From 369cae941e34f0be498e1786814381a129f52751 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Tue, 12 Oct 2010 13:40:37 +0300 Subject: [PATCH] CalDAV: properly implement the CTag feature Instead of using the ETag (which looks at the modification time of the base.calendar object), aggregate over the contained calendar entries to find their maximum modification. bzr revid: p_christ@hol.gr-20101012104037-847evso9mryqom5f --- addons/caldav/caldav_node.py | 45 +++++++++++------------------- addons/caldav/calendar.py | 53 ++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 29 deletions(-) diff --git a/addons/caldav/caldav_node.py b/addons/caldav/caldav_node.py index 510e43eabd0..52da451b567 100644 --- a/addons/caldav/caldav_node.py +++ b/addons/caldav/caldav_node.py @@ -21,33 +21,14 @@ import time from document_webdav import nodes +from document.nodes import _str2time import logging import StringIO +from tools.dict_tools import dict_merge, dict_merge2 + # TODO: implement DAV-aware errors, inherit from IOError -def dict_merge(*dicts): - """ Return a dict with all values of dicts - """ - res = {} - for d in dicts: - res.update(d) - return res - -def dict_merge2(*dicts): - """ Return a dict with all values of dicts. - If some key appears twice and contains iterable objects, the values - are merged (instead of overwritten). - """ - res = {} - for d in dicts: - for k in d.keys(): - if k in res and isinstance(res[k], (list, tuple)): - res[k] = res[k] + d[k] - else: - res[k] = d[k] - return res - # Assuming that we have set global properties right, we mark *all* # directories as having calendar-access. nodes.node_dir.http_options = dict_merge2(nodes.node_dir.http_options, @@ -186,10 +167,18 @@ class node_calendar(nodes.node_class): self.content_length = 0 self.displayname = calendar.name self.cal_type = calendar.type + # TODO owner + def _get_dav_getctag(self, cr): - result = self._get_ttag(cr) + ':' + str(time.time()) - return str(result) + dirobj = self.context._dirobj + uid = self.context.uid + ctx = self.context.context.copy() + ctx.update(self.dctx) + + bc_obj = dirobj.pool.get('basic.calendar') + res = bc_obj.get_cal_max_modified(cr, uid, [self.calendar_id], self, domain=[], context=ctx) + return _str2time(res) def _get_dav_user_state(self, cr): #TODO @@ -295,11 +284,9 @@ class node_calendar(nodes.node_class): if not domain: domain = [] - fil_obj = dirobj.pool.get('basic.calendar') - ids = fil_obj.search(cr, uid, domain) - res = [] - if self.calendar_id in ids: - res = fil_obj.get_calendar_objects(cr, uid, [self.calendar_id], self, domain=where, context=ctx) + bc_obj = dirobj.pool.get('basic.calendar') + # we /could/ be supplying an invalid calendar id to bc_obj, it has to check + res = bc_obj.get_calendar_objects(cr, uid, [self.calendar_id], self, domain=where, context=ctx) return res def create_child(self, cr, path, data): diff --git a/addons/caldav/calendar.py b/addons/caldav/calendar.py index 2f7d978b5a6..2133e3be293 100644 --- a/addons/caldav/calendar.py +++ b/addons/caldav/calendar.py @@ -552,6 +552,33 @@ 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' @@ -610,6 +637,32 @@ class Calendar(CalDAV, osv.osv): node = res_node_calendar('%s.ics' %data.id, parent, ctx, data, line.object_id.model, data.id) res.append(node) return res + + + def get_cal_max_modified(self, cr, uid, ids, parent=None, domain=None, context=None): + if not context: + context = {} + if not domain: + domain = [] + res = None + ctx_res_id = context.get('res_id', None) + ctx_model = context.get('model', None) + for cal in self.browse(cr, uid, ids): + for line in cal.line_ids: + if ctx_model and ctx_model != line.object_id.model: + continue + if line.name in ('valarm', 'attendee'): + continue + line_domain = eval(line.domain or '[]', context) + line_domain += domain + if ctx_res_id: + line_domain += [('id','=',ctx_res_id)] + mod_obj = self.pool.get(line.object_id.model) + max_data = get_last_modified(mod_obj, cr, uid, line_domain, context=context) + if res and res > max_data: + continue + res = max_data + return res def export_cal(self, cr, uid, ids, vobj='vevent', context=None): """ Export Calendar