Caldav node: use .ics extension, re-enable unlink
[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 from document import nodes
28 import StringIO
29
30 class node_database(nodes.node_database):
31     def _child_get(self, cr, name=False, parent_id=False, domain=None):
32         dirobj = self.context._dirobj
33         uid = self.context.uid
34         ctx = self.context.context.copy()
35         ctx.update(self.dctx)
36         if not domain:
37             domain = []
38         domain2 = domain + [('calendar_collection','=', False)]
39         res = super(node_database, self)._child_get(cr, name=name, parent_id=parent_id, domain=domain2)
40         where = [('parent_id','=',parent_id)]
41         domain2 = domain + [('calendar_collection','=', True)]
42         if name:
43             where.append(('name','=',name))
44         if domain2:
45             where += domain2
46
47         where2 = where + [('type', '=', 'directory')]
48         ids = dirobj.search(cr, uid, where2, context=ctx)
49         for dirr in dirobj.browse(cr,uid,ids,context=ctx):
50             res.append(node_calendar_collection(dirr.name,self,self.context,dirr))
51         return res
52
53 class node_calendar_collection(nodes.node_dir):
54     DAV_PROPS = {
55             "http://calendarserver.org/ns/" : ('getctag',),
56             }
57     DAV_M_NS = {
58            "http://calendarserver.org/ns/" : '_get_dav',
59            }
60
61     http_options = { 'DAV': ['calendar-access'] }
62
63     def _file_get(self,cr, nodename=False):
64         return []
65
66     def _child_get(self, cr, name=False, parent_id=False, domain=None):
67         dirobj = self.context._dirobj
68         uid = self.context.uid
69         ctx = self.context.context.copy()
70         ctx.update(self.dctx)
71         where = [('collection_id','=',self.dir_id)]
72         ext = False
73         if name and name.endswith('.ics'):
74             name = name[-4]
75             ext = '.ics'
76         if name:
77             where.append(('name','=',name))
78         if not domain:
79             domain = []
80         where = where + domain
81         fil_obj = dirobj.pool.get('basic.calendar')
82         ids = fil_obj.search(cr,uid,where,context=ctx)
83         res = []
84         for calender in fil_obj.browse(cr, uid, ids, context=ctx):
85             if not ext:
86                 res.append(node_calendar(calender.name, self, self.context, calender))
87             else:
88                 res.append(res_node_calendar(name, self, self.context, calender))
89         return res
90
91     def _get_dav_owner(self, cr):
92         # Todo?
93         return False
94
95     def _get_ttag(self, cr):
96         return 'calen-dir-%d' % self.dir_id
97
98     def _get_dav_getctag(self, cr):
99         result = self.get_etag(cr)
100         return str(result)
101
102 class node_calendar(nodes.node_class):
103     our_type = 'collection'
104     DAV_PROPS = {
105             "http://calendarserver.org/ns/" : ('getctag',),
106             'http://groupdav.org/': ('resourcetype',),
107             "urn:ietf:params:xml:ns:caldav" : (
108                     'calendar-description',
109                     'calendar-data',
110                     'calendar-home-set',
111                     'calendar-user-address-set',
112                     'schedule-inbox-URL',
113                     'schedule-outbox-URL',)}
114     DAV_M_NS = {
115            "DAV:" : '_get_dav',
116            'http://groupdav.org/': '_get_gdav',
117            "http://calendarserver.org/ns/" : '_get_dav',
118            "urn:ietf:params:xml:ns:caldav" : '_get_caldav'}
119
120     http_options = { 'DAV': ['calendar-access'] }
121
122     def __init__(self,path, parent, context, calendar):
123         super(node_calendar,self).__init__(path, parent,context)
124         self.calendar_id = calendar.id
125         self.mimetype = 'application/x-directory'
126         self.create_date = calendar.create_date
127         self.write_date = calendar.write_date or calendar.create_date
128         self.content_length = 0
129         self.displayname = calendar.name
130         self.cal_type = calendar.type
131
132     def _get_dav_getctag(self, cr):
133         result = self._get_ttag(cr) + ':' + str(time.time())
134         return str(result)
135
136     def _get_gdav_resourcetype(self, cr):
137         return (str(self.cal_type + '-collection'), 'http://groupdav.org/')
138
139     def removeme_match_dav_eprop(self, cr, match, ns, prop):
140         # Why?
141         if ns == "DAV:" and prop == "getetag":
142             dirobj = self.context._dirobj
143             uid = self.context.uid
144             ctx = self.context.context.copy()
145             tem, dav_time = tuple(match.split(':'))
146             model, res_id = tuple(tem.split('_'))
147             model_obj = dirobj.pool.get(model)
148             model = model_obj.browse(cr, uid, res_id, context=ctx)
149             write_time = model.write_date or model.create_date
150             wtime = time.mktime(time.strptime(write_time,'%Y-%m-%d %H:%M:%S'))
151             if float(dav_time) == float(wtime):
152                 return True
153             return False
154         res = super(node_calendar, self).match_dav_eprop(cr, match, ns, prop)
155         return res
156
157     def get_domain(self, cr, filters):
158         # TODO: doc.
159         res = []
160         # dirobj = self.context._dirobj
161         #uid = self.context.uid
162         #ctx = self.context.context.copy()
163         #ctx.update(self.dctx)
164         # calendar_obj = dirobj.pool.get('basic.calendar')
165         if not filters:
166             return res
167         if filters.localName == 'calendar-query':
168             res = []
169             for filter_child in filters.childNodes:
170                 if filter_child.nodeType == filter_child.TEXT_NODE:
171                     continue
172                 if filter_child.localName == 'filter':
173                     for vcalendar_filter in filter_child.childNodes:
174                         if vcalendar_filter.nodeType == vcalendar_filter.TEXT_NODE:
175                             continue
176                         if vcalendar_filter.localName == 'comp-filter':
177                             if vcalendar_filter.getAttribute('name') == 'VCALENDAR':
178                                 for vevent_filter in vcalendar_filter.childNodes:
179                                     if vevent_filter.nodeType == vevent_filter.TEXT_NODE:
180                                         continue
181                                     if vevent_filter.localName == 'comp-filter':
182                                         if vevent_filter.getAttribute('name') == 'VEVENT':
183                                             res = [('type','=','vevent')]
184                                         if vevent_filter.getAttribute('name') == 'VTODO':
185                                             res = [('type','=','vtodo')]
186             return res
187         elif filters.localName == 'calendar-multiget':
188             names = []
189             for filter_child in filters.childNodes:
190                 if filter_child.nodeType == filter_child.TEXT_NODE:
191                     continue
192                 if filter_child.localName == 'href':
193                     if not filter_child.firstChild:
194                         continue
195                     uri = filter_child.firstChild.data
196                     caluri = uri.split('/')
197                     if len(caluri):
198                         caluri = caluri[-2]
199                         if caluri not in names : names.append(caluri)
200             res = [('name','in',names)]
201             return res
202         return res
203
204     def children(self, cr, domain=None):
205         return self._child_get(cr, domain=domain)
206
207     def child(self,cr, name, domain=None):
208         res = self._child_get(cr, name, domain=domain)
209         if res:
210             return res[0]
211         return None
212
213
214     def _child_get(self, cr, name=False, parent_id=False, domain=None):
215         dirobj = self.context._dirobj
216         uid = self.context.uid
217         ctx = self.context.context.copy()
218         ctx.update(self.dctx)
219         where = []
220         if name:
221             if name.endswith('.ics'):
222                 name = name[:-4]
223             where.append(('id','=',int(name)))
224         if not domain:
225             domain = []
226         #for opr1, opt, opr2 in domain:
227         #    if opr1 == 'type' and opr2 != self.cal_type:
228         #        return []
229
230         fil_obj = dirobj.pool.get('basic.calendar')
231         ids = fil_obj.search(cr, uid, domain)
232         res = []
233         if self.calendar_id in ids:
234             res = fil_obj.get_calendar_objects(cr, uid, [self.calendar_id], self, domain=where, context=ctx)
235         return res
236
237     def create_child(self, cr, path, data):
238         """ API function to create a child file object and node
239             Return the node_* created
240         """
241         # we ignore the path, it will be re-generated automatically
242         res = self.set_data(cr, data)
243         
244         # TODO: use the res to create at least one node
245         return None
246
247
248     def set_data(self, cr, data, fil_obj = None):
249         uid = self.context.uid
250         calendar_obj = self.context._dirobj.pool.get('basic.calendar')
251         res = calendar_obj.import_cal(cr, uid, data, self.calendar_id)
252         return res
253
254     def get_data_len(self, cr, fil_obj = None):
255         return self.content_length
256
257     def _get_ttag(self,cr):
258         return 'calendar-%d' % (self.calendar_id,)
259
260     def rmcol(self, cr):
261         return False
262
263
264 class res_node_calendar(nodes.node_class):
265     our_type = 'file'
266     DAV_PROPS = {
267             "http://calendarserver.org/ns/" : ('getctag'),
268             "urn:ietf:params:xml:ns:caldav" : (
269                     'calendar-description',
270                     'calendar-data',
271                     'calendar-home-set',
272                     'calendar-user-address-set',
273                     'schedule-inbox-URL',
274                     'schedule-outbox-URL',)}
275     DAV_M_NS = {
276            "http://calendarserver.org/ns/" : '_get_dav',
277            "urn:ietf:params:xml:ns:caldav" : '_get_caldav'}
278
279     http_options = { 'DAV': ['calendar-access'] }
280
281     def __init__(self,path, parent, context, res_obj, res_model=None, res_id=None):
282         super(res_node_calendar,self).__init__(path, parent, context)
283         self.mimetype = 'text/calendar'
284         self.create_date = parent.create_date
285         self.write_date = parent.write_date or parent.create_date
286         self.calendar_id = hasattr(parent, 'calendar_id') and parent.calendar_id or False
287         if res_obj:
288             if not self.calendar_id: self.calendar_id = res_obj.id
289             pr = res_obj.perm_read()[0]
290             self.create_date = pr.get('create_date')
291             self.write_date = pr.get('write_date') or pr.get('create_date')
292             self.displayname = res_obj.name
293
294         self.content_length = 0
295
296         self.model = res_model
297         self.res_id = res_id
298
299     def open(self, cr, mode=False):
300         uid = self.context.uid
301         if self.type in ('collection','database'):
302             return False
303         s = StringIO.StringIO(self.get_data(cr))
304         s.name = self
305         return s
306
307     def get_data(self, cr, fil_obj = None):
308         uid = self.context.uid
309         calendar_obj = self.context._dirobj.pool.get('basic.calendar')
310         context = self.context.context.copy()
311         context.update({'model': self.model, 'res_id':self.res_id})
312         res = calendar_obj.export_cal(cr, uid, [self.calendar_id], context=context)
313         return res
314
315     def get_data_len(self, cr, fil_obj = None):
316         return self.content_length
317
318     def set_data(self, cr, data, fil_obj = None):
319         uid = self.context.uid
320         calendar_obj = self.context._dirobj.pool.get('basic.calendar')
321         res =  calendar_obj.import_cal(cr, uid, data, self.calendar_id)
322         return res
323
324     def _get_ttag(self,cr):
325         res = False
326         if self.model and self.res_id:
327             res = '%s_%d' % (self.model, self.res_id)
328         elif self.calendar_id:
329             res = '%d' % (self.calendar_id)
330         return res
331
332     def _get_caldav_calendar_data(self, cr):
333         return self.get_data(cr)
334
335
336     def _get_caldav_calendar_description(self, cr):
337         uid = self.context.uid
338         calendar_obj = self.context._dirobj.pool.get('basic.calendar')
339         ctx = self.context.context.copy()
340         ctx.update(self.dctx)
341         calendar = calendar_obj.browse(cr, uid, self.calendar_id, context=ctx)
342         return calendar.description
343
344
345     def _get_caldav_calendar_home_set(self, cr):
346         import xml.dom.minidom
347         import urllib
348         uid = self.context.uid
349         ctx = self.context.context.copy()
350         ctx.update(self.dctx)
351         doc = xml.dom.minidom.getDOMImplementation().createDocument(None, 'href', None)
352
353         calendar_obj = self.context._dirobj.pool.get('basic.calendar')
354         calendar = calendar_obj.browse(cr, uid, self.calendar_id, context=ctx)
355         huri = doc.createTextNode(urllib.quote('/%s/%s' % (cr.dbname, calendar.collection_id.name)))
356         href = doc.documentElement
357         href.tagName = 'D:href'
358         href.appendChild(huri)
359         return href
360
361     def _get_caldav_calendar_user_address_set(self, cr):
362         import xml.dom.minidom
363         dirobj = self.context._dirobj
364         uid = self.context.uid
365         ctx = self.context.context.copy()
366         ctx.update(self.dctx)
367         user_obj = self.context._dirobj.pool.get('res.users')
368         user = user_obj.browse(cr, uid, uid, context=ctx)
369         doc = xml.dom.minidom.getDOMImplementation().createDocument(None, 'href', None)
370         href = doc.documentElement
371         href.tagName = 'D:href'
372         huri = doc.createTextNode('MAILTO:' + user.email)
373         href.appendChild(huri)
374         return href
375
376
377     def _get_caldav_schedule_inbox_URL(self, cr):
378         import xml.dom.minidom
379         import urllib
380         uid = self.context.uid
381         ctx = self.context.context.copy()
382         ctx.update(self.dctx)
383         calendar_obj = self.context._dirobj.pool.get('basic.calendar')
384         calendar = calendar_obj.browse(cr, uid, self.calendar_id, context=ctx)
385         res = '%s/%s' %(calendar.name, calendar.collection_id.name)
386         doc = xml.dom.minidom.getDOMImplementation().createDocument(None, 'href', None)
387         href = doc.documentElement
388         href.tagName = 'D:href'
389         huri = doc.createTextNode(urllib.quote('/%s/%s' % (cr.dbname, res)))
390         href.appendChild(huri)
391         return href
392
393
394     def rm(self, cr):
395         uid = self.context.uid
396         res = False
397         if self.type in ('collection','database'):
398             return False
399         if self.model and self.res_id:
400             document_obj = self.context._dirobj.pool.get(self.model)
401             if document_obj:
402                 res =  document_obj.unlink(cr, uid, [self.res_id])
403
404         return res
405
406     def _get_caldav_schedule_outbox_URL(self, cr):
407         return self._get_caldav_schedule_inbox_URL(cr)
408
409 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4