CalDAV: more properties, hide WebCal support
authorP. Christeas <p_christ@hol.gr>
Tue, 12 Oct 2010 10:40:44 +0000 (13:40 +0300)
committerP. Christeas <p_christ@hol.gr>
Tue, 12 Oct 2010 10:40:44 +0000 (13:40 +0300)
WebCal entries, although permitted by the protocol, confuse some badly
implemented CalDAV clients, so hide them with an option at the calendar
definition.

bzr revid: p_christ@hol.gr-20101012104044-rjx63ucwjk0ys98r

addons/caldav/caldav_node.py
addons/caldav/caldav_view.xml
addons/caldav/calendar.py
addons/caldav/orm_utils.py [new file with mode: 0644]

index 52da451..f104b27 100644 (file)
@@ -24,6 +24,7 @@ from document_webdav import nodes
 from document.nodes import _str2time
 import logging
 import StringIO
+from orm_utils import get_last_modified
 
 from tools.dict_tools import dict_merge, dict_merge2
 
@@ -64,21 +65,24 @@ class node_calendar_collection(nodes.node_dir):
         for cal in fil_obj.browse(cr, uid, ids, context=ctx):
             if (not name) or not ext:
                 res.append(node_calendar(cal.name, self, self.context, cal))
-            if (not name) or ext:
+            if cal.has_webcal and (not name) or ext:
                 res.append(res_node_calendar(cal.name+'.ics', self, self.context, cal))
             # May be both of them!
         return res
 
-    def _get_dav_owner(self, cr):
-        # Todo?
-        return False
-
     def _get_ttag(self, cr):
         return 'calen-dir-%d' % self.dir_id
 
     def _get_dav_getctag(self, cr):
-        result = self.get_etag(cr)
-        return str(result)
+        dirobj = self.context._dirobj
+        uid = self.context.uid
+        ctx = self.context.context.copy()
+        ctx.update(self.dctx)
+        where = [('collection_id','=',self.dir_id)]
+        bc_obj = dirobj.pool.get('basic.calendar')
+        
+        res = get_last_modified(bc_obj, cr, uid, where, context=ctx)
+        return _str2time(res)
 
 class node_calendar_res_col(nodes.node_res_obj):
     """ Calendar collection, as a dynamically created node
@@ -116,7 +120,7 @@ class node_calendar_res_col(nodes.node_res_obj):
         for cal in fil_obj.browse(cr, uid, ids, context=ctx):
             if (not name) or not ext:
                 res.append(node_calendar(cal.name, self, self.context, cal))
-            if (not name) or ext:
+            if cal.has_webcal and (not name) or ext:
                 res.append(res_node_calendar(cal.name+'.ics', self, self.context, cal))
             # May be both of them!
         return res
@@ -125,8 +129,15 @@ class node_calendar_res_col(nodes.node_res_obj):
         return 'calen-dir-%d' % self.dir_id
 
     def _get_dav_getctag(self, cr):
-        result = self.get_etag(cr)
-        return str(result)
+        dirobj = self.context._dirobj
+        uid = self.context.uid
+        ctx = self.context.context.copy()
+        ctx.update(self.dctx)
+        where = [('collection_id','=',self.dir_id)]
+        bc_obj = dirobj.pool.get('basic.calendar')
+        
+        res = get_last_modified(bc_obj, cr, uid, where, context=ctx)
+        return _str2time(res)
 
 class node_calendar(nodes.node_class):
     our_type = 'collection'
@@ -138,7 +149,9 @@ class node_calendar(nodes.node_class):
             "urn:ietf:params:xml:ns:caldav" : (
                     'calendar-description', 
                     'supported-calendar-component-set',
-                    )}
+                    ),
+            "http://apple.com/ns/ical/": ("calendar-color", "calendar-order"),
+            }
     DAV_PROPS_HIDDEN = {
             "urn:ietf:params:xml:ns:caldav" : (
                     'calendar-data',
@@ -154,7 +167,9 @@ class node_calendar(nodes.node_class):
            # "http://cal.me.com/_namespace/": '_get_dav', 
            'http://groupdav.org/': '_get_gdav',
            "http://calendarserver.org/ns/" : '_get_dav',
-           "urn:ietf:params:xml:ns:caldav" : '_get_caldav'}
+           "urn:ietf:params:xml:ns:caldav" : '_get_caldav',
+           "http://apple.com/ns/ical/": '_get_apple_cal',
+           }
 
     http_options = { 'DAV': ['calendar-access'] }
 
@@ -167,8 +182,12 @@ class node_calendar(nodes.node_class):
         self.content_length = 0
         self.displayname = calendar.name
         self.cal_type = calendar.type
-        # TODO owner
-        
+        self.cal_color = calendar.calendar_color or None
+        self.cal_order = calendar.calendar_order or None
+        try:
+            self.uuser = (calendar.user_id and calendar.user_id.login) or 'nobody'
+        except Exception:
+            self.uuser = 'nobody'
 
     def _get_dav_getctag(self, cr):
         dirobj = self.context._dirobj
@@ -186,8 +205,9 @@ class node_calendar(nodes.node_class):
 
     def get_dav_resourcetype(self, cr):
         res = [ ('collection', 'DAV:'),
+                ('calendar', 'urn:ietf:params:xml:ns:caldav'),
                 (str(self.cal_type + '-collection'), 'http://groupdav.org/'),
-                ('calendar', 'urn:ietf:params:xml:ns:caldav') ]
+                ]
         return res
 
     def get_domain(self, cr, filters):
@@ -373,6 +393,12 @@ class node_calendar(nodes.node_class):
 
     def _get_caldav_max_date_time(self, cr):
         return "21001231T235959Z" # I will be dead by then
+    
+    def _get_apple_cal_calendar_color(self, cr):
+        return self.cal_color
+
+    def _get_apple_cal_calendar_order(self, cr):
+        return self.cal_order
 
 class res_node_calendar(nodes.node_class):
     our_type = 'file'
@@ -445,7 +471,6 @@ class res_node_calendar(nodes.node_class):
             res = '%d' % (self.calendar_id)
         return res
 
-
     def rm(self, cr):
         uid = self.context.uid
         res = False
index 2e9719e..97656a9 100644 (file)
@@ -74,6 +74,9 @@
                     <field name="type"/>
                     <field name="user_id"/>
                     <field name="collection_id" required="1"/>
+                    <field name="has_webcal" groups="base.group_extended" />
+                    <field name="calendar_color" groups="base.group_extended" />
+                    <field name="calendar_order" groups="base.group_extended" />
                     <notebook colspan="4">
                         <page string="Calendar Lines">
                             <field name="line_ids" mode="form,tree" colspan="4" nolabel="1">
index 2133e3b..276f08c 100644 (file)
@@ -32,6 +32,7 @@ import tools
 import time
 import logging
 from caldav_node import res_node_calendar
+from orm_utils import get_last_modified
 from tools.safe_eval import safe_eval as eval
 
 try:
@@ -552,33 +553,6 @@ class CalDAV(object):
             self.ical_reset('value')
         return res
 
-if True: # we need this indentation level ;)
-
-    def get_last_modified(self, cr, user, args, context=None, access_rights_uid=None):
-        """Return the last modification date of objects in 'domain'
-        This function has similar semantics to orm.search(), apart from the
-        limit, offset and order arguments, which make no sense here.
-        It is useful when we want to find if the table (aka set of records)
-        has any modifications we should update at the client.
-        """
-        if context is None:
-            context = {}
-        self.pool.get('ir.model.access').check(cr, access_rights_uid or user, self._name, 'read', context=context)
-
-        query = self._where_calc(cr, user, args, context=context)
-        self._apply_ir_rules(cr, user, query, 'read', context=context)
-        from_clause, where_clause, where_clause_params = query.get_sql()
-
-        where_str = where_clause and (" WHERE %s" % where_clause) or ''
-
-        cr.execute('SELECT MAX(COALESCE("%s".write_date, "%s".create_date)) FROM ' % (self._table, self._table) + 
-                    from_clause + where_str ,
-                    where_clause_params,
-                    debug=self._debug)
-        res = cr.fetchall()
-        return res[0][0]
-
-
 class Calendar(CalDAV, osv.osv):
     _name = 'basic.calendar'
     _calname = 'calendar'
@@ -607,6 +581,13 @@ class Calendar(CalDAV, osv.osv):
             'create_date': fields.datetime('Created Date', readonly=True),
             'write_date': fields.datetime('Modifided Date', readonly=True),
             'description': fields.text("description"),
+            'calendar_color': fields.char('Color', size=20, help="For supporting clients, the color of the calendar entries"),
+            'calendar_order': fields.integer('Order', help="For supporting clients, the order of this folder among the calendars"),
+            'has_webcal': fields.boolean('WebCal', required=True, help="Also export a <name>.ics entry next to the calendar folder, with WebCal content."),
+    }
+    
+    _defaults = {
+        'has_webcal': False,
     }
 
     def get_calendar_objects(self, cr, uid, ids, parent=None, domain=None, context=None):
diff --git a/addons/caldav/orm_utils.py b/addons/caldav/orm_utils.py
new file mode 100644 (file)
index 0000000..badacc8
--- /dev/null
@@ -0,0 +1,49 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    Copyright (C) 2010 OpenERP SA (www.openerp.com)
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+
+if True: # we need this indentation level ;)
+
+    def get_last_modified(self, cr, user, args, context=None, access_rights_uid=None):
+        """Return the last modification date of objects in 'domain'
+        This function has similar semantics to orm.search(), apart from the
+        limit, offset and order arguments, which make no sense here.
+        It is useful when we want to find if the table (aka set of records)
+        has any modifications we should update at the client.
+        """
+        if context is None:
+            context = {}
+        self.pool.get('ir.model.access').check(cr, access_rights_uid or user, self._name, 'read', context=context)
+
+        query = self._where_calc(cr, user, args, context=context)
+        self._apply_ir_rules(cr, user, query, 'read', context=context)
+        from_clause, where_clause, where_clause_params = query.get_sql()
+
+        where_str = where_clause and (" WHERE %s" % where_clause) or ''
+
+        cr.execute('SELECT MAX(COALESCE("%s".write_date, "%s".create_date)) FROM ' % (self._table, self._table) + 
+                    from_clause + where_str ,
+                    where_clause_params,
+                    debug=self._debug)
+        res = cr.fetchall()
+        return res[0][0]
+
+#eof