[IMP] caldav
[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     PROPS = {
56             "http://calendarserver.org/ns/" : ('getctag'),
57             }          
58     M_NS = { 
59            "http://calendarserver.org/ns/" : '_get_dav',
60            }    
61    
62     def get_dav_props(self, cr):                
63         return self.PROPS
64
65     def get_dav_eprop(self,cr, ns, propname):         
66         if self.M_NS.has_key(ns):
67             prefix = self.M_NS[ns]
68         else:
69             print "No namespace:",ns, "( for prop:", propname,")"
70             return None
71
72         mname = prefix + "_" + propname
73
74         if not hasattr(self, mname):
75             return None
76
77         try:
78             m = getattr(self, mname)
79             r = m(cr)
80             return r
81         except AttributeError, e:
82             print 'Property %s not supported' % propname
83             print "Exception:", e            
84         return None
85
86     def _file_get(self,cr, nodename=False):
87         return []   
88
89
90     def get_domain(self, cr, filters):        
91         res = []
92         dirobj = self.context._dirobj
93         uid = self.context.uid
94         ctx = self.context.context.copy()
95         ctx.update(self.dctx)
96         calendar_obj = dirobj.pool.get('basic.calendar')
97         if not filters:
98             return res
99         if filters.localName == 'calendar-query':      
100             res = []
101             for filter_child in filters.childNodes:
102                 if filter_child.nodeType == filter_child.TEXT_NODE:
103                     continue                
104                 if filter_child.localName == 'filter':                    
105                     for vcalendar_filter in filter_child.childNodes:
106                         if vcalendar_filter.nodeType == vcalendar_filter.TEXT_NODE:                            
107                             continue
108                         if vcalendar_filter.localName == 'comp-filter':
109                             if vcalendar_filter.getAttribute('name') == 'VCALENDAR':
110                                 for vevent_filter in vcalendar_filter.childNodes:
111                                     if vevent_filter.nodeType == vevent_filter.TEXT_NODE:
112                                         continue
113                                     if vevent_filter.localName == 'comp-filter':
114                                         if vevent_filter.getAttribute('name') == 'VEVENT':
115                                             res = [('type','=','vevent')]
116                                         if vevent_filter.getAttribute('name') == 'VTODO':
117                                             res = [('type','=','vtodo')]                               
118             
119             return res
120         elif filters.localName == 'calendar-multiget':
121             ids = []
122             for filter_child in filters.childNodes:
123                 if filter_child.nodeType == filter_child.TEXT_NODE:
124                     continue
125                 if filter_child.localName == 'href':
126                     if not filter_child.firstChild:
127                         continue
128                     uri = filter_child.firstChild.data  
129                     caluri = uri.split('/')
130                     if len(caluri):
131                         caluri = caluri[-1]
132                         calendar = calendar_obj.name_search(cr, uid, caluri)
133                         if calendar:                            
134                             calendar_id, calendar_name = calendar[0]                            
135                             ids.append(calendar_id)
136             return [('id', 'in', ids)]
137         return res
138
139     def _child_get(self, cr, name=False, parent_id=False, domain=None):
140         dirobj = self.context._dirobj
141         uid = self.context.uid
142         ctx = self.context.context.copy()
143         ctx.update(self.dctx)
144         where = [('collection_id','=',self.dir_id)]                              
145         if name:
146             where.append(('name','=',name))
147         if not domain:
148             domain = []       
149         where = where + domain
150         fil_obj = dirobj.pool.get('basic.calendar')        
151         ids = fil_obj.search(cr,uid,where,context=ctx)
152         res = []
153         if ids:
154             for fil in fil_obj.browse(cr,uid,ids,context=ctx):
155                 res.append(node_calendar(fil.name,self,self.context,fil))
156         return res
157
158     def _get_dav_owner(self, cr):
159         return False
160
161     
162     def get_etag(self, cr):
163         """ Get a tag, unique per object + modification.
164
165             see. http://tools.ietf.org/html/rfc2616#section-13.3.3 """
166         return self._get_ttag(cr) + ':' + self._get_wtag(cr)
167
168     def _get_wtag(self, cr):
169         """ Return the modification time as a unique, compact string """
170         if self.write_date:
171             wtime = time.mktime(time.strptime(self.write_date, '%Y-%m-%d %H:%M:%S'))
172         else: wtime = time.time()
173         return str(wtime)
174
175     def _get_ttag(self, cr):
176         return 'calendar collection-%d' % self.dir_id
177
178     def _get_dav_getctag(self, cr):
179         result = self.get_etag(cr)        
180         return str(result)   
181         
182
183 class node_calendar(nodes.node_class):
184     our_type = 'file'
185     PROPS = {
186             "http://calendarserver.org/ns/" : ('getctag'),
187             "urn:ietf:params:xml:ns:caldav" : (
188                     'calendar-description',
189                     'calendar-data',
190                     'calendar-home-set',
191                     'calendar-user-address-set',
192                     'schedule-inbox-URL',
193                     'schedule-outbox-URL',)}
194     M_NS = { 
195            "http://calendarserver.org/ns/" : '_get_dav',
196            "urn:ietf:params:xml:ns:caldav" : '_get_caldav'} 
197
198     def __init__(self,path, parent, context, calendar):
199         super(node_calendar,self).__init__(path, parent,context)
200         self.calendar_id = calendar.id
201         self.mimetype = 'ics'
202         self.create_date = calendar.create_date
203         self.write_date = calendar.write_date or calendar.create_date
204         self.content_length = 0
205         self.displayname = calendar.name        
206          
207     def open(self, cr, mode=False):
208         uid = self.context.uid        
209         if self.type in ('collection','database'):
210             return False            
211         fobj = self.context._dirobj.pool.get('basic.calendar').browse(cr, uid, self.calendar_id, context=self.context.context)        
212         s = StringIO.StringIO(self.get_data(cr, fobj))        
213         s.name = self
214         return s           
215
216        
217    
218     def get_dav_props(self, cr):        
219         return self.PROPS
220
221     def get_dav_eprop(self,cr, ns, propname):        
222         if self.M_NS.has_key(ns):
223             prefix = self.M_NS[ns]
224         else:
225             print "No namespace:",ns, "( for prop:", propname,")"
226             return None
227         propname = propname.replace('-','_')
228         mname = prefix + "_" + propname
229         if not hasattr(self, mname):
230             return None
231
232         try:
233             m = getattr(self, mname)
234             r = m(cr)            
235             return r
236         except AttributeError, e:
237             print 'Property %s not supported' % propname
238             print "Exception:", e            
239         return None
240
241
242     def get_data(self, cr, fil_obj = None):            
243         uid = self.context.uid
244         calendar_obj = self.context._dirobj.pool.get('basic.calendar')
245         res = calendar_obj.export_cal(cr, uid, [self.calendar_id])        
246         return res
247
248     def get_data_len(self, cr, fil_obj = None):        
249         return self.content_length
250
251     def set_data(self, cr, data, fil_obj = None):
252         uid = self.context.uid
253         calendar_obj = self.context._dirobj.pool.get('basic.calendar')
254         return calendar_obj.import_cal(cr, uid, base64.encodestring(data), self.calendar_id)
255
256     def _get_ttag(self,cr):
257         return 'calendar-%d' % self.calendar_id
258
259
260     
261     def _get_caldav_calendar_data(self, cr):        
262         return self.get_data(cr)
263         
264
265     def _get_caldav_calendar_description(self, cr):
266         uid = self.context.uid
267         calendar_obj = self.context._dirobj.pool.get('basic.calendar')
268         ctx = self.context.context.copy()
269         ctx.update(self.dctx)        
270         calendar = calendar_obj.browse(cr, uid, self.calendar_id, context=ctx)
271         return calendar.description
272     
273
274     def _get_caldav_calendar_home_set(self, cr):
275         import xml.dom.minidom
276         import urllib       
277         uid = self.context.uid
278         ctx = self.context.context.copy()
279         ctx.update(self.dctx)
280         doc = xml.dom.minidom.getDOMImplementation().createDocument(None, 'href', None)        
281         
282         calendar_obj = self.context._dirobj.pool.get('basic.calendar')             
283         calendar = calendar_obj.browse(cr, uid, self.calendar_id, context=ctx)
284         huri = doc.createTextNode(urllib.quote('/%s/%s' % (cr.dbname, calendar.collection_id.name)))
285         href = doc.documentElement
286         href.tagName = 'D:href'
287         href.appendChild(huri)
288         return href
289
290     def _get_caldav_calendar_user_address_set(self, cr):
291         import xml.dom.minidom
292         dirobj = self.context._dirobj
293         uid = self.context.uid
294         ctx = self.context.context.copy()
295         ctx.update(self.dctx)
296         user_obj = self.context._dirobj.pool.get('res.users')
297         user = user_obj.browse(cr, uid, uid, context=ctx)        
298         doc = xml.dom.minidom.getDOMImplementation().createDocument(None, 'href', None)
299         href = doc.documentElement
300         href.tagName = 'D:href'
301         huri = doc.createTextNode('MAILTO:' + user.email)
302         href.appendChild(huri)
303         return href
304
305
306     def _get_caldav_schedule_inbox_URL(self, cr):
307         import xml.dom.minidom
308         import urllib        
309         uid = self.context.uid
310         ctx = self.context.context.copy()
311         ctx.update(self.dctx)
312         calendar_obj = self.context._dirobj.pool.get('basic.calendar')             
313         calendar = calendar_obj.browse(cr, uid, self.calendar_id, context=ctx)
314         res = '%s/%s' %(calendar.name, calendar.collection_id.name)
315         doc = xml.dom.minidom.getDOMImplementation().createDocument(None, 'href', None)
316         href = doc.documentElement
317         href.tagName = 'D:href'
318         huri = doc.createTextNode(urllib.quote('/%s/%s' % (cr.dbname, res)))
319         href.appendChild(huri)
320         return href
321
322
323
324     def _get_caldav_schedule_outbox_URL(self, cr):
325         return _get_caldav_schedule_inbox_URL(cr)
326     
327
328     def get_etag(self, cr):
329         """ Get a tag, unique per object + modification.
330
331             see. http://tools.ietf.org/html/rfc2616#section-13.3.3 """
332         return self._get_ttag(cr) + ':' + self._get_wtag(cr)
333
334     def _get_wtag(self, cr):
335         """ Return the modification time as a unique, compact string """
336         if self.write_date:
337             wtime = time.mktime(time.strptime(self.write_date, '%Y-%m-%d %H:%M:%S'))
338         else: wtime = time.time()
339         return str(wtime)             
340 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4