[CLEAN]: Removed commented codes, unnecessary spaces.
[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 = True
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 cal in fil_obj.browse(cr, uid, ids, context=ctx):
85             if (not name) or not ext:
86                 res.append(node_calendar(cal.name, self, self.context, cal))
87             if (not name) or ext:
88                 res.append(res_node_calendar(cal.name+'.ics', self, self.context, cal))
89             # May be both of them!
90         return res
91
92     def _get_dav_owner(self, cr):
93         # Todo?
94         return False
95
96     def _get_ttag(self, cr):
97         return 'calen-dir-%d' % self.dir_id
98
99     def _get_dav_getctag(self, cr):
100         result = self.get_etag(cr)
101         return str(result)
102
103 class node_calendar(nodes.node_class):
104     our_type = 'collection'
105     DAV_PROPS = {
106             "http://calendarserver.org/ns/" : ('getctag',),
107             'http://groupdav.org/': ('resourcetype',),
108             "urn:ietf:params:xml:ns:caldav" : (
109                     'calendar-description',
110                     'calendar-data',
111                     'calendar-home-set',
112                     'calendar-user-address-set',
113                     'schedule-inbox-URL',
114                     'schedule-outbox-URL',)}
115     DAV_M_NS = {
116            "DAV:" : '_get_dav',
117            'http://groupdav.org/': '_get_gdav',
118            "http://calendarserver.org/ns/" : '_get_dav',
119            "urn:ietf:params:xml:ns:caldav" : '_get_caldav'}
120
121     http_options = { 'DAV': ['calendar-access'] }
122
123     def __init__(self,path, parent, context, calendar):
124         super(node_calendar,self).__init__(path, parent,context)
125         self.calendar_id = calendar.id
126         self.mimetype = 'application/x-directory'
127         self.create_date = calendar.create_date
128         self.write_date = calendar.write_date or calendar.create_date
129         self.content_length = 0
130         self.displayname = calendar.name
131         self.cal_type = calendar.type
132
133     def _get_dav_getctag(self, cr):
134         result = self._get_ttag(cr) + ':' + str(time.time())
135         return str(result)
136
137     def get_dav_resourcetype(self, cr):
138         res = [ ('collection', 'DAV:'),
139                 (str(self.cal_type + '-collection'), 'http://groupdav.org/'),
140                 ('calendar', 'urn:ietf:params:xml:ns:caldav') ]
141         return res
142
143     def get_domain(self, cr, filters):
144         # TODO: doc.
145         res = []
146         if not filters:
147             return res
148         if filters.localName == 'calendar-query':
149             res = []
150             for filter_child in filters.childNodes:
151                 if filter_child.nodeType == filter_child.TEXT_NODE:
152                     continue
153                 if filter_child.localName == 'filter':
154                     for vcalendar_filter in filter_child.childNodes:
155                         if vcalendar_filter.nodeType == vcalendar_filter.TEXT_NODE:
156                             continue
157                         if vcalendar_filter.localName == 'comp-filter':
158                             if vcalendar_filter.getAttribute('name') == 'VCALENDAR':
159                                 for vevent_filter in vcalendar_filter.childNodes:
160                                     if vevent_filter.nodeType == vevent_filter.TEXT_NODE:
161                                         continue
162                                     if vevent_filter.localName == 'comp-filter':
163                                         if vevent_filter.getAttribute('name') == 'VEVENT':
164                                             res = [('type','=','vevent')]
165                                         if vevent_filter.getAttribute('name') == 'VTODO':
166                                             res = [('type','=','vtodo')]
167             return res
168         elif filters.localName == 'calendar-multiget':
169             names = []
170             for filter_child in filters.childNodes:
171                 if filter_child.nodeType == filter_child.TEXT_NODE:
172                     continue
173                 if filter_child.localName == 'href':
174                     if not filter_child.firstChild:
175                         continue
176                     uri = filter_child.firstChild.data
177                     caluri = uri.split('/')
178                     if len(caluri):
179                         caluri = caluri[-2]
180                         if caluri not in names : names.append(caluri)
181             res = [('name','in',names)]
182             return res
183         return res
184
185     def children(self, cr, domain=None):
186         return self._child_get(cr, domain=domain)
187
188     def child(self,cr, name, domain=None):
189         res = self._child_get(cr, name, domain=domain)
190         if res:
191             return res[0]
192         return None
193
194
195     def _child_get(self, cr, name=False, parent_id=False, domain=None):
196         dirobj = self.context._dirobj
197         uid = self.context.uid
198         ctx = self.context.context.copy()
199         ctx.update(self.dctx)
200         where = []
201         if name:
202             if name.endswith('.ics'):
203                 name = name[:-4]
204             try:
205                 where.append(('id','=',int(name)))
206             except ValueError:
207                 # if somebody requests any other name than the ones we
208                 # generate (non-numeric), it just won't exist
209                 # FIXME: however, this confuses Evolution (at least), which
210                 # thinks the .ics node hadn't been saved.
211                 return []
212
213         if not domain:
214             domain = []
215
216         fil_obj = dirobj.pool.get('basic.calendar')
217         ids = fil_obj.search(cr, uid, domain)
218         res = []
219         if self.calendar_id in ids:
220             res = fil_obj.get_calendar_objects(cr, uid, [self.calendar_id], self, domain=where, context=ctx)
221         return res
222
223     def create_child(self, cr, path, data):
224         """ API function to create a child file object and node
225             Return the node_* created
226         """
227         # we ignore the path, it will be re-generated automatically
228         fil_obj = self.context._dirobj.pool.get('basic.calendar')
229         ctx = self.context.context.copy()
230         ctx.update(self.dctx)
231         uid = self.context.uid
232
233         res = self.set_data(cr, data)
234
235         if res and len(res):
236             # We arbitrarily construct only the first node of the data
237             # that have been imported. ICS may have had more elements,
238             # but only one node can be returned here.
239             assert isinstance(res[0], (int, long))
240             fnodes = fil_obj.get_calendar_objects(cr, uid, [self.calendar_id], self,
241                     domain=[('id','=',res[0])], context=ctx)
242             return fnodes[0]
243         return None
244
245
246     def set_data(self, cr, data, fil_obj = None):
247         uid = self.context.uid
248         calendar_obj = self.context._dirobj.pool.get('basic.calendar')
249         res = calendar_obj.import_cal(cr, uid, data, self.calendar_id)
250         return res
251
252     def get_data_len(self, cr, fil_obj = None):
253         return self.content_length
254
255     def _get_ttag(self,cr):
256         return 'calendar-%d' % (self.calendar_id,)
257
258     def rmcol(self, cr):
259         return False
260
261
262 class res_node_calendar(nodes.node_class):
263     our_type = 'file'
264     DAV_PROPS = {
265             "http://calendarserver.org/ns/" : ('getctag'),
266             "urn:ietf:params:xml:ns:caldav" : (
267                     'calendar-description',
268                     'calendar-data',
269                     'calendar-home-set',
270                     'calendar-user-address-set',
271                     'schedule-inbox-URL',
272                     'schedule-outbox-URL',)}
273     DAV_M_NS = {
274            "http://calendarserver.org/ns/" : '_get_dav',
275            "urn:ietf:params:xml:ns:caldav" : '_get_caldav'}
276
277     http_options = { 'DAV': ['calendar-access'] }
278
279     def __init__(self,path, parent, context, res_obj, res_model=None, res_id=None):
280         super(res_node_calendar,self).__init__(path, parent, context)
281         self.mimetype = 'text/calendar'
282         self.create_date = parent.create_date
283         self.write_date = parent.write_date or parent.create_date
284         self.calendar_id = hasattr(parent, 'calendar_id') and parent.calendar_id or False
285         if res_obj:
286             if not self.calendar_id: self.calendar_id = res_obj.id
287             pr = res_obj.perm_read()[0]
288             self.create_date = pr.get('create_date')
289             self.write_date = pr.get('write_date') or pr.get('create_date')
290             self.displayname = res_obj.name
291
292         self.content_length = 0
293
294         self.model = res_model
295         self.res_id = res_id
296
297     def open(self, cr, mode=False):
298         uid = self.context.uid
299         if self.type in ('collection','database'):
300             return False
301         s = StringIO.StringIO(self.get_data(cr))
302         s.name = self
303         return s
304
305     def get_data(self, cr, fil_obj = None):
306         uid = self.context.uid
307         calendar_obj = self.context._dirobj.pool.get('basic.calendar')
308         context = self.context.context.copy()
309         context.update({'model': self.model, 'res_id':self.res_id})
310         res = calendar_obj.export_cal(cr, uid, [self.calendar_id], context=context)
311         return res
312
313     def get_data_len(self, cr, fil_obj = None):
314         return self.content_length
315
316     def set_data(self, cr, data, fil_obj = None):
317         uid = self.context.uid
318         calendar_obj = self.context._dirobj.pool.get('basic.calendar')
319         res =  calendar_obj.import_cal(cr, uid, data, self.calendar_id)
320         return res
321
322     def _get_ttag(self,cr):
323         res = False
324         if self.model and self.res_id:
325             res = '%s_%d' % (self.model, self.res_id)
326         elif self.calendar_id:
327             res = '%d' % (self.calendar_id)
328         return res
329
330     def _get_caldav_calendar_data(self, cr):
331         return self.get_data(cr)
332
333
334     def _get_caldav_calendar_description(self, cr):
335         uid = self.context.uid
336         calendar_obj = self.context._dirobj.pool.get('basic.calendar')
337         ctx = self.context.context.copy()
338         ctx.update(self.dctx)
339         calendar = calendar_obj.browse(cr, uid, self.calendar_id, context=ctx)
340         return calendar.description
341
342
343     def _get_caldav_calendar_home_set(self, cr):
344         import xml.dom.minidom
345         import urllib
346         uid = self.context.uid
347         ctx = self.context.context.copy()
348         ctx.update(self.dctx)
349         doc = xml.dom.minidom.getDOMImplementation().createDocument(None, 'href', None)
350
351         calendar_obj = self.context._dirobj.pool.get('basic.calendar')
352         calendar = calendar_obj.browse(cr, uid, self.calendar_id, context=ctx)
353         huri = doc.createTextNode(urllib.quote('/%s/%s' % (cr.dbname, calendar.collection_id.name)))
354         href = doc.documentElement
355         href.tagName = 'D:href'
356         href.appendChild(huri)
357         return href
358
359     def _get_caldav_calendar_user_address_set(self, cr):
360         import xml.dom.minidom
361         dirobj = self.context._dirobj
362         uid = self.context.uid
363         ctx = self.context.context.copy()
364         ctx.update(self.dctx)
365         user_obj = self.context._dirobj.pool.get('res.users')
366         user = user_obj.browse(cr, uid, uid, context=ctx)
367         doc = xml.dom.minidom.getDOMImplementation().createDocument(None, 'href', None)
368         href = doc.documentElement
369         href.tagName = 'D:href'
370         huri = doc.createTextNode('MAILTO:' + user.email)
371         href.appendChild(huri)
372         return href
373
374
375     def _get_caldav_schedule_inbox_URL(self, cr):
376         import xml.dom.minidom
377         import urllib
378         uid = self.context.uid
379         ctx = self.context.context.copy()
380         ctx.update(self.dctx)
381         calendar_obj = self.context._dirobj.pool.get('basic.calendar')
382         calendar = calendar_obj.browse(cr, uid, self.calendar_id, context=ctx)
383         res = '%s/%s' %(calendar.name, calendar.collection_id.name)
384         doc = xml.dom.minidom.getDOMImplementation().createDocument(None, 'href', None)
385         href = doc.documentElement
386         href.tagName = 'D:href'
387         huri = doc.createTextNode(urllib.quote('/%s/%s' % (cr.dbname, res)))
388         href.appendChild(huri)
389         return href
390
391
392     def rm(self, cr):
393         uid = self.context.uid
394         res = False
395         if self.type in ('collection','database'):
396             return False
397         if self.model and self.res_id:
398             document_obj = self.context._dirobj.pool.get(self.model)
399             if document_obj:
400                 res =  document_obj.unlink(cr, uid, [self.res_id])
401
402         return res
403
404     def _get_caldav_schedule_outbox_URL(self, cr):
405         return self._get_caldav_schedule_inbox_URL(cr)
406
407 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4