CalDAV: Use the new API for DAV properties.
[odoo/odoo.git] / addons / caldav / caldav_node.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
6 #
7 #    This program is free software: you can redistribute it and/or modify
8 #    it under the terms of the GNU Affero General Public License as
9 #    published by the Free Software Foundation, either version 3 of the
10 #    License, or (at your option) any later version.
11 #
12 #    This program is distributed in the hope that it will be useful,
13 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 #    GNU Affero General Public License for more details.
16 #
17 #    You should have received a copy of the GNU Affero General Public License
18 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 #
20 ##############################################################################
21
22 from osv import osv, fields
23 from tools.translate import _
24 import pooler
25 import tools
26 import time
27 import base64
28 from document import nodes
29 import StringIO
30
31 class node_database(nodes.node_database):
32     def _child_get(self, cr, name=False, parent_id=False, domain=None):
33         dirobj = self.context._dirobj
34         uid = self.context.uid
35         ctx = self.context.context.copy()
36         ctx.update(self.dctx)
37         if not domain:
38             domain = []
39         domain2 = domain + [('calendar_collection','=', False)]
40         res = super(node_database, self)._child_get(cr, name=name, parent_id=parent_id, domain=domain2)
41         where = [('parent_id','=',parent_id)]
42         domain2 = domain + [('calendar_collection','=', True)]
43         if name:
44             where.append(('name','=',name))
45         if domain2:
46             where += domain2
47
48         where2 = where + [('type', '=', 'directory')]
49         ids = dirobj.search(cr, uid, where2, context=ctx)
50         for dirr in dirobj.browse(cr,uid,ids,context=ctx):
51             res.append(node_calendar_collection(dirr.name,self,self.context,dirr))
52         return res
53
54 class node_calendar_collection(nodes.node_dir):
55     DAV_PROPS = {
56             "http://calendarserver.org/ns/" : ('getctag'),
57             }
58     DAV_M_NS = {
59            "http://calendarserver.org/ns/" : '_get_dav',
60            }
61
62     http_options = { 'DAV': ['calendar-access'] }
63
64     def _file_get(self,cr, nodename=False):
65         return []
66
67     def _child_get(self, cr, name=False, parent_id=False, domain=None):
68         dirobj = self.context._dirobj
69         uid = self.context.uid
70         ctx = self.context.context.copy()
71         ctx.update(self.dctx)
72         where = [('collection_id','=',self.dir_id)]
73         ext = False
74         if name:
75             res = name.split('.ics')
76             if len(res) > 1:
77                 name = res[0]
78                 ext = '.ics'
79         if name:
80             where.append(('name','=',name))
81         if not domain:
82             domain = []
83         where = where + domain
84         fil_obj = dirobj.pool.get('basic.calendar')
85         ids = fil_obj.search(cr,uid,where,context=ctx)
86         res = []
87         for calender in fil_obj.browse(cr, uid, ids, context=ctx):
88             if not ext:
89                 res.append(node_calendar(calender.name, self, self.context, calender))
90             else:
91                 res.append(res_node_calendar(name, self, self.context, calender))
92         return res
93
94     def _get_dav_owner(self, cr):
95         # Todo?
96         return False
97
98     def get_etag(self, cr):
99         """ Get a tag, unique per object + modification.
100
101             see. http://tools.ietf.org/html/rfc2616#section-13.3.3 """
102         return self._get_ttag(cr) + ':' + self._get_wtag(cr)
103
104     def _get_wtag(self, cr):
105         """ Return the modification time as a unique, compact string """
106         if self.write_date:
107             wtime = time.mktime(time.strptime(self.write_date, '%Y-%m-%d %H:%M:%S'))
108         else: wtime = time.time()
109         return str(wtime)
110
111     def _get_ttag(self, cr):
112         return 'calendar collection-%d' % self.dir_id
113
114     def _get_dav_getctag(self, cr):
115         result = self.get_etag(cr)
116         return str(result)
117
118
119 class node_calendar(nodes.node_class):
120     our_type = 'collection'
121     DAV_PROPS = {
122             "http://calendarserver.org/ns/" : ('getctag'),
123             "urn:ietf:params:xml:ns:caldav" : (
124                     'calendar-description',
125                     'calendar-data',
126                     'calendar-home-set',
127                     'calendar-user-address-set',
128                     'schedule-inbox-URL',
129                     'schedule-outbox-URL',)}
130     DAV_M_NS = {
131            "DAV:" : '_get_dav',
132            "http://calendarserver.org/ns/" : '_get_dav',
133            "urn:ietf:params:xml:ns:caldav" : '_get_caldav'}
134
135     http_options = { 'DAV': ['calendar-access'] }
136
137     def __init__(self,path, parent, context, calendar):
138         super(node_calendar,self).__init__(path, parent,context)
139         self.calendar_id = calendar.id
140         self.mimetype = 'application/x-directory'
141         self.create_date = calendar.create_date
142         self.write_date = calendar.write_date or calendar.create_date
143         self.content_length = 0
144         self.displayname = calendar.name
145         self.cal_type = calendar.type
146
147     def _get_dav_getctag(self, cr):
148         result = self._get_ttag(cr) + ':' + str(time.time())
149         return str(result)
150
151     def removeme_match_dav_eprop(self, cr, match, ns, prop):
152         # Why?
153         if ns == "DAV:" and prop == "getetag":
154             dirobj = self.context._dirobj
155             uid = self.context.uid
156             ctx = self.context.context.copy()
157             tem, dav_time = tuple(match.split(':'))
158             model, res_id = tuple(tem.split('_'))
159             model_obj = dirobj.pool.get(model)
160             model = model_obj.browse(cr, uid, res_id, context=ctx)
161             write_time = model.write_date or model.create_date
162             wtime = time.mktime(time.strptime(write_time,'%Y-%m-%d %H:%M:%S'))
163             if float(dav_time) == float(wtime):
164                 return True
165             return False
166         res = super(node_calendar, self).match_dav_eprop(cr, match, ns, prop)
167         return res
168
169     def get_domain(self, cr, filters):
170         res = []
171         dirobj = self.context._dirobj
172         uid = self.context.uid
173         ctx = self.context.context.copy()
174         ctx.update(self.dctx)
175         calendar_obj = dirobj.pool.get('basic.calendar')
176         if not filters:
177             return res
178         if filters.localName == 'calendar-query':
179             res = []
180             for filter_child in filters.childNodes:
181                 if filter_child.nodeType == filter_child.TEXT_NODE:
182                     continue
183                 if filter_child.localName == 'filter':
184                     for vcalendar_filter in filter_child.childNodes:
185                         if vcalendar_filter.nodeType == vcalendar_filter.TEXT_NODE:
186                             continue
187                         if vcalendar_filter.localName == 'comp-filter':
188                             if vcalendar_filter.getAttribute('name') == 'VCALENDAR':
189                                 for vevent_filter in vcalendar_filter.childNodes:
190                                     if vevent_filter.nodeType == vevent_filter.TEXT_NODE:
191                                         continue
192                                     if vevent_filter.localName == 'comp-filter':
193                                         if vevent_filter.getAttribute('name') == 'VEVENT':
194                                             res = [('type','=','vevent')]
195                                         if vevent_filter.getAttribute('name') == 'VTODO':
196                                             res = [('type','=','vtodo')]
197             return res
198         elif filters.localName == 'calendar-multiget':
199             names = []
200             for filter_child in filters.childNodes:
201                 if filter_child.nodeType == filter_child.TEXT_NODE:
202                     continue
203                 if filter_child.localName == 'href':
204                     if not filter_child.firstChild:
205                         continue
206                     uri = filter_child.firstChild.data
207                     caluri = uri.split('/')
208                     if len(caluri):
209                         caluri = caluri[-2]
210                         if caluri not in names : names.append(caluri)
211             res = [('name','in',names)]
212             return res
213         return res
214
215     def children(self, cr, domain=None):
216         return self._child_get(cr, domain=domain)
217
218     def child(self,cr, name, domain=None):
219         res = self._child_get(cr, name, domain=domain)
220         if res:
221             return res[0]
222         return None
223
224
225     def _child_get(self, cr, name=False, parent_id=False, domain=None):
226         dirobj = self.context._dirobj
227         uid = self.context.uid
228         ctx = self.context.context.copy()
229         ctx.update(self.dctx)
230         where = []
231         if name:
232             where.append(('id','=',int(name)))
233         if not domain:
234             domain = []
235         #for opr1, opt, opr2 in domain:
236         #    if opr1 == 'type' and opr2 != self.cal_type:
237         #        return []
238
239         fil_obj = dirobj.pool.get('basic.calendar')
240         ids = fil_obj.search(cr, uid, domain)
241         res = []
242         if self.calendar_id in ids:
243             res = fil_obj.get_calendar_objects(cr, uid, [self.calendar_id], self, domain=where, context=ctx)
244         return res
245
246     def create_child(self,cr,path,data):
247         """ API function to create a child file object and node
248             Return the node_* created
249         """
250         return self.set_data(cr, data)
251
252
253     def set_data(self, cr, data, fil_obj = None):
254         uid = self.context.uid
255         calendar_obj = self.context._dirobj.pool.get('basic.calendar')
256         return calendar_obj.import_cal(cr, uid, base64.encodestring(data), self.calendar_id)
257
258     def get_data_len(self, cr, fil_obj = None):
259         return self.content_length
260
261
262     def _get_ttag(self,cr):
263         return 'calendar-%d' % (self.calendar_id,)
264
265
266
267     def get_etag(self, cr):
268         """ Get a tag, unique per object + modification.
269
270             see. http://tools.ietf.org/html/rfc2616#section-13.3.3 """
271         return self._get_ttag(cr) + ':' + self._get_wtag(cr)
272
273     def _get_wtag(self, cr):
274         """ Return the modification time as a unique, compact string """
275         if self.write_date:
276             wtime = time.mktime(time.strptime(self.write_date, '%Y-%m-%d %H:%M:%S'))
277         else: wtime = time.time()
278         return str(wtime)
279
280     def rmcol(self, cr):
281         return False
282
283
284 class res_node_calendar(nodes.node_class):
285     our_type = 'file'
286     DAV_PROPS = {
287             "http://calendarserver.org/ns/" : ('getctag'),
288             "urn:ietf:params:xml:ns:caldav" : (
289                     'calendar-description',
290                     'calendar-data',
291                     'calendar-home-set',
292                     'calendar-user-address-set',
293                     'schedule-inbox-URL',
294                     'schedule-outbox-URL',)}
295     DAV_M_NS = {
296            "http://calendarserver.org/ns/" : '_get_dav',
297            "urn:ietf:params:xml:ns:caldav" : '_get_caldav'}
298
299     http_options = { 'DAV': ['calendar-access'] }
300
301     def __init__(self,path, parent, context, res_obj, res_model=None, res_id=None):
302         super(res_node_calendar,self).__init__(path, parent, context)
303         self.mimetype = 'text/calendar'
304         self.create_date = parent.create_date
305         self.write_date = parent.write_date or parent.create_date
306         self.calendar_id = hasattr(parent, 'calendar_id') and parent.calendar_id or False
307         if res_obj:
308             if not self.calendar_id: self.calendar_id = res_obj.id
309             self.create_date = res_obj.create_date
310             self.write_date = res_obj.write_date or res_obj.create_date
311             self.displayname = res_obj.name
312
313         self.content_length = 0
314
315         self.model = res_model
316         self.res_id = res_id
317
318     def open(self, cr, mode=False):
319         uid = self.context.uid
320         if self.type in ('collection','database'):
321             return False
322         s = StringIO.StringIO(self.get_data(cr))
323         s.name = self
324         return s
325
326     def get_data(self, cr, fil_obj = None):
327         uid = self.context.uid
328         calendar_obj = self.context._dirobj.pool.get('basic.calendar')
329         context = self.context.context.copy()
330         context.update({'model': self.model, 'res_id':self.res_id})
331         res = calendar_obj.export_cal(cr, uid, [self.calendar_id], context=context)
332         return res
333
334     def get_data_len(self, cr, fil_obj = None):
335         return self.content_length
336
337     def set_data(self, cr, data, fil_obj = None):
338         uid = self.context.uid
339         calendar_obj = self.context._dirobj.pool.get('basic.calendar')
340         return calendar_obj.import_cal(cr, uid, base64.encodestring(data), self.calendar_id)
341
342     def _get_ttag(self,cr):
343         res = False
344         if self.model and self.res_id:
345             res = '%s_%d' % (self.model, self.res_id)
346         elif self.calendar_id:
347             res = '%d' % (self.calendar_id)
348         return res
349
350     def _get_caldav_calendar_data(self, cr):
351         return self.get_data(cr)
352
353
354     def _get_caldav_calendar_description(self, cr):
355         uid = self.context.uid
356         calendar_obj = self.context._dirobj.pool.get('basic.calendar')
357         ctx = self.context.context.copy()
358         ctx.update(self.dctx)
359         calendar = calendar_obj.browse(cr, uid, self.calendar_id, context=ctx)
360         return calendar.description
361
362
363     def _get_caldav_calendar_home_set(self, cr):
364         import xml.dom.minidom
365         import urllib
366         uid = self.context.uid
367         ctx = self.context.context.copy()
368         ctx.update(self.dctx)
369         doc = xml.dom.minidom.getDOMImplementation().createDocument(None, 'href', None)
370
371         calendar_obj = self.context._dirobj.pool.get('basic.calendar')
372         calendar = calendar_obj.browse(cr, uid, self.calendar_id, context=ctx)
373         huri = doc.createTextNode(urllib.quote('/%s/%s' % (cr.dbname, calendar.collection_id.name)))
374         href = doc.documentElement
375         href.tagName = 'D:href'
376         href.appendChild(huri)
377         return href
378
379     def _get_caldav_calendar_user_address_set(self, cr):
380         import xml.dom.minidom
381         dirobj = self.context._dirobj
382         uid = self.context.uid
383         ctx = self.context.context.copy()
384         ctx.update(self.dctx)
385         user_obj = self.context._dirobj.pool.get('res.users')
386         user = user_obj.browse(cr, uid, uid, context=ctx)
387         doc = xml.dom.minidom.getDOMImplementation().createDocument(None, 'href', None)
388         href = doc.documentElement
389         href.tagName = 'D:href'
390         huri = doc.createTextNode('MAILTO:' + user.email)
391         href.appendChild(huri)
392         return href
393
394
395     def _get_caldav_schedule_inbox_URL(self, cr):
396         import xml.dom.minidom
397         import urllib
398         uid = self.context.uid
399         ctx = self.context.context.copy()
400         ctx.update(self.dctx)
401         calendar_obj = self.context._dirobj.pool.get('basic.calendar')
402         calendar = calendar_obj.browse(cr, uid, self.calendar_id, context=ctx)
403         res = '%s/%s' %(calendar.name, calendar.collection_id.name)
404         doc = xml.dom.minidom.getDOMImplementation().createDocument(None, 'href', None)
405         href = doc.documentElement
406         href.tagName = 'D:href'
407         huri = doc.createTextNode(urllib.quote('/%s/%s' % (cr.dbname, res)))
408         href.appendChild(huri)
409         return href
410
411
412     def rm(self, cr):
413         uid = self.context.uid
414         res = False
415         if self.type in ('collection','database'):
416             return False
417         if self.model and self.res_id:
418             document_obj = self.context._dirobj.pool.get(self.model)
419             if document_obj:
420                 res = False
421                 #res = document_obj.unlink(cr, uid, [self.res_id]) #TOFIX
422
423         return res
424
425
426
427     def _get_caldav_schedule_outbox_URL(self, cr):
428         return self._get_caldav_schedule_inbox_URL(cr)
429
430
431     def get_etag(self, cr):
432         """ Get a tag, unique per object + modification.
433
434             see. http://tools.ietf.org/html/rfc2616#section-13.3.3 """
435         return self._get_ttag(cr) + ':' + self._get_wtag(cr)
436
437     def _get_wtag(self, cr):
438         """ Return the modification time as a unique, compact string """
439         if self.write_date:
440             wtime = time.mktime(time.strptime(self.write_date, '%Y-%m-%d %H:%M:%S'))
441         else: wtime = time.time()
442         return str(wtime)
443
444 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4