1 # -*- coding: utf-8 -*-
2 ##############################################################################
4 # OpenERP, Open Source Management Solution
5 # Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
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.
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.
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/>.
20 ##############################################################################
22 from datetime import datetime, timedelta
23 from dateutil import parser
24 from dateutil.rrule import *
25 from osv import osv, fields
26 from tools.translate import _
32 # O-1 Optional and can come only once
33 # O-n Optional and can come more than once
34 # R-1 Required and can come only once
35 # R-n Required and can come more than once
37 def uid2openobjectid(cr, uidval, oomodel, rdate):
38 __rege = re.compile(r'OpenObject-([\w|\.]+)_([0-9]+)@(\w+)$')
39 wematch = __rege.match(uidval.encode('utf8'))
43 model, id, dbname = wematch.groups()
44 model_obj = pooler.get_pool(cr.dbname).get(model)
45 if (not model == oomodel) or (not dbname == cr.dbname):
47 qry = 'select distinct(id) from %s' % model_obj._table
49 qry += " where recurrent_id='%s'" % (rdate)
55 ids = map(lambda x: str(x[0]), cr.fetchall())
60 def openobjectid2uid(cr, uidval, oomodel):
61 value = 'OpenObject-%s_%s@%s' % (oomodel, uidval, cr.dbname)
64 def get_attribute_mapping(cr, uid, calname, context={}):
67 pool = pooler.get_pool(cr.dbname)
68 field_obj = pool.get('basic.calendar.fields')
69 type_obj = pool.get('basic.calendar.lines')
70 type_id = type_obj.search(cr, uid, [('object_id.model', '=', context.get('model'))])
71 fids = field_obj.search(cr, uid, [('type_id', '=', type_id[0])])
73 for field in field_obj.browse(cr, uid, fids):
74 attr = field.name.name
76 res[attr]['field'] = field.field_id.name
77 res[attr]['type'] = field.field_id.ttype
78 if field.fn == 'hours':
79 res[attr]['type'] = "timedelta"
80 if res[attr]['type'] in ('one2many', 'many2many', 'many2one'):
81 res[attr]['object'] = field.field_id.relation
82 elif res[attr]['type'] in ('selection') and field.mapping:
83 res[attr]['mapping'] = eval(field.mapping)
84 if not res.get('uid', None):
86 res['uid']['field'] = 'id'
87 res[attr]['type'] = "integer"
90 def map_data(cr, uid, obj):
92 for map_dict in obj.__attribute__:
93 map_val = obj.ical_get(map_dict, 'value')
94 field = obj.ical_get(map_dict, 'field')
95 field_type = obj.ical_get(map_dict, 'type')
97 if field_type == 'selection':
100 mapping = obj.__attribute__[map_dict].get('mapping', False)
102 map_val = mapping[map_val.lower()]
104 map_val = map_val.lower()
105 if field_type == 'many2many':
110 model = obj.__attribute__[map_dict].get('object', False)
111 modobj = obj.pool.get(model)
112 for map_vall in map_val:
113 id = modobj.create(cr, uid, map_vall)
115 vals[field] = [(6, 0, ids)]
117 if field_type == 'many2one':
119 if not map_val or not isinstance(map_val, dict):
122 model = obj.__attribute__[map_dict].get('object', False)
123 modobj = obj.pool.get(model)
124 id = modobj.create(cr, uid, map_val)
127 if field_type == 'timedelta':
129 vals[field] = (map_val.seconds/float(86400) + map_val.days)
131 vals[field] = map_val
134 class CalDAV(object):
137 def get_recurrent_dates(self, rrulestring, exdate, startdate=None):
139 startdate = datetime.now()
140 rset1 = rrulestr(rrulestring, dtstart=startdate, forceset=True)
143 datetime_obj = todate(date)
144 rset1._exdate.append(datetime_obj)
145 re_dates = map(lambda x:x.strftime('%Y-%m-%d %H:%M:%S'), rset1._iter())
148 def ical_set(self, name, value, type):
149 if name in self.__attribute__ and self.__attribute__[name]:
150 self.__attribute__[name][type] = value
153 def ical_get(self, name, type):
154 if self.__attribute__.get(name):
155 val = self.__attribute__.get(name).get(type, None)
156 valtype = self.__attribute__.get(name).get('type', None)
158 if valtype and valtype == 'datetime' and val:
159 if isinstance(val, list):
160 val = ','.join(map(lambda x: x.strftime('%Y-%m-%d %H:%M:%S'), val))
162 val = val.strftime('%Y-%m-%d %H:%M:%S')
165 return self.__attribute__.get(name, None)
167 def ical_reset(self, type):
168 for name in self.__attribute__:
169 if self.__attribute__[name]:
170 self.__attribute__[name][type] = None
173 def parse_ics(self, cr, uid, child):
175 for cal_data in child.getChildren():
176 if cal_data.name.lower() == 'attendee':
177 attendee = self.pool.get('basic.calendar.attendee')
178 att_data.append(attendee.import_cal(cr, uid, cal_data))
179 self.ical_set(cal_data.name.lower(), att_data, 'value')
181 if cal_data.name.lower() == 'valarm':
182 alarm = self.pool.get('basic.calendar.alarm')
183 vals = alarm.import_cal(cr, uid, cal_data)
184 self.ical_set(cal_data.name.lower(), vals, 'value')
186 if cal_data.name.lower() in self.__attribute__:
187 self.ical_set(cal_data.name.lower(), cal_data.value, 'value')
188 vals = map_data(cr, uid, self)
191 def create_ics(self, cr, uid, datas, name, ical, context=None):
193 model = context.get('model', None)
194 war_str = "No data available" + (model and " for " + model) or ""
195 raise osv.except_osv(_('Warning !'), _(war_str))
197 vevent = ical.add(name)
198 for field in self.__attribute__.keys():
199 map_field = self.ical_get(field, 'field')
200 map_type = self.ical_get(field, 'type')
201 if map_field in data.keys():
203 model = context.get('model', None)
206 uidval = openobjectid2uid(cr, data[map_field], model)
207 model_obj = self.pool.get(model)
209 if model_obj._columns.get('recurrent_uid', None):
210 cr.execute('select id from %s where recurrent_uid=%s'
211 % (model_obj._table, data[map_field]))
212 r_ids = map(lambda x: x[0], cr.fetchall())
214 rdata = self.pool.get(model).read(cr, uid, r_ids)
215 rcal = self.export_cal(cr, uid, rdata, context=context)
216 for revents in rcal.contents['vevent']:
217 ical.contents['vevent'].append(revents)
218 if data.get('recurrent_uid', None):
219 uidval = openobjectid2uid(cr, data['recurrent_uid'], model)
220 vevent.add('uid').value = uidval
221 elif field == 'attendee' and data[map_field]:
222 model = self.__attribute__[field].get('object', False)
223 attendee_obj = self.pool.get('basic.calendar.attendee')
224 vevent = attendee_obj.export_cal(cr, uid, model, \
225 data[map_field], vevent, context=context)
226 elif field == 'valarm' and data[map_field]:
227 model = self.__attribute__[field].get('object', False)
228 alarm_obj = self.pool.get('basic.calendar.alarm')
229 vevent = alarm_obj.export_cal(cr, uid, model, \
230 data[map_field][0], vevent, context=context)
231 elif data[map_field]:
232 if map_type in ("char", "text"):
233 vevent.add(field).value = str(data[map_field].encode('utf8'))
234 elif map_type in ('datetime', 'date') and data[map_field]:
235 if field in ('exdate'):
236 vevent.add(field).value = [parser.parse(data[map_field])]
238 vevent.add(field).value = parser.parse(data[map_field])
239 elif map_type == "timedelta":
240 vevent.add(field).value = timedelta(hours=data[map_field])
241 elif map_type == "many2one":
242 vevent.add(field).value = (data.get(map_field)[1]).encode('utf8')
243 elif map_type in ("float", "integer"):
244 vevent.add(field).value = str(data.get(map_field))
245 elif map_type == "selection":
246 if not self.ical_get(field, 'mapping'):
247 vevent.add(field).value = (data[map_field].encode('utf8')).upper()
249 for key1, val1 in self.ical_get(field, 'mapping').items():
250 if val1 == data[map_field]:
251 vevent.add(field).value = key1
254 def check_import(self, cr, uid, vals, context={}):
256 model_obj = self.pool.get(context.get('model'))
259 exists, r_id = uid2openobjectid(cr, val['id'], context.get('model'), \
260 val.get('recurrent_id'))
261 if val.has_key('create_date'): val.pop('create_date')
264 val.update({'recurrent_uid': exists})
265 model_obj.write(cr, uid, [r_id], val)
268 model_obj.write(cr, uid, [exists], val)
271 event_id = model_obj.create(cr, uid, val)
274 raise osv.except_osv(('Error !'), (str(e)))
277 def export_cal(self, cr, uid, datas, vobj=None, context={}):
279 self.__attribute__ = get_attribute_mapping(cr, uid, self._calname, context)
280 ical = vobject.iCalendar()
281 self.create_ics(cr, uid, datas, vobj, ical, context=context)
284 raise osv.except_osv(('Error !'), (str(e)))
286 def import_cal(self, cr, uid, content, data_id=None, context=None):
287 ical_data = base64.decodestring(content)
288 self.__attribute__ = get_attribute_mapping(cr, uid, self._calname, context)
289 parsedCal = vobject.readOne(ical_data)
292 for child in parsedCal.getChildren():
293 if child.name.lower() in ('vevent', 'vtodo'):
294 vals = self.parse_ics(cr, uid, child)
298 if vals: res.append(vals)
299 self.ical_reset('value')
303 class Calendar(CalDAV, osv.osv):
304 _name = 'basic.calendar'
305 _description = 'Calendar'
306 _calname = 'calendar'
309 'prodid': None, # Use: R-1, Type: TEXT, Specifies the identifier for the product that created the iCalendar object.
310 'version': None, # Use: R-1, Type: TEXT, Specifies the identifier corresponding to the highest version number
311 # or the minimum and maximum range of the iCalendar specification
312 # that is required in order to interpret the iCalendar object.
313 'calscale': None, # Use: O-1, Type: TEXT, Defines the calendar scale used for the calendar information specified in the iCalendar object.
314 'method': None, # Use: O-1, Type: TEXT, Defines the iCalendar object method associated with the calendar object.
315 'vevent': None, # Use: O-n, Type: Collection of Event class
316 'vtodo': None, # Use: O-n, Type: Collection of ToDo class
317 'vjournal': None, # Use: O-n, Type: Collection of Journal class
318 'vfreebusy': None, # Use: O-n, Type: Collection of FreeBusy class
319 'vtimezone': None, # Use: O-n, Type: Collection of Timezone class
322 'name': fields.char("Name", size=64),
323 'line_ids': fields.one2many('basic.calendar.lines', 'calendar_id', 'Calendar Lines'),
324 'active': fields.boolean('Active'),
328 'active': lambda *a: True,
331 def export_cal(self, cr, uid, datas, vobj='vevent', context={}):
333 cal = self.browse(cr, uid, datas[0])
334 ical = vobject.iCalendar()
335 for line in cal.line_ids:
336 if line.name in ('alarm', 'attendee'):
338 mod_obj = self.pool.get(line.object_id.model)
339 data_ids = mod_obj.search(cr, uid, eval(line.domain), context=context)
340 datas = mod_obj.read(cr, uid, data_ids, context=context)
341 context.update({'model': line.object_id.model})
342 self.__attribute__ = get_attribute_mapping(cr, uid, line.name, context)
343 self.create_ics(cr, uid, datas, line.name, ical, context=context)
344 return ical.serialize()
346 raise osv.except_osv(('Error !'), (str(e)))
348 def import_cal(self, cr, uid, content, data_id=None, context=None):
349 ical_data = base64.decodestring(content)
350 parsedCal = vobject.readOne(ical_data)
352 data_id = self.search(cr, uid, [])[0]
353 cal = self.browse(cr, uid, data_id)
356 for line in cal.line_ids:
357 cal_children[line.name] = line.object_id.model
358 for child in parsedCal.getChildren():
359 if child.name.lower() in cal_children:
360 context.update({'model': cal_children[child.name.lower()]})
361 self.__attribute__ = get_attribute_mapping(cr, uid, child.name.lower(), context=context)
362 val = self.parse_ics(cr, uid, child)
363 obj = self.pool.get(cal_children[child.name.lower()])
364 if hasattr(obj, 'check_import'):
365 obj.check_import(cr, uid, [val], context=context)
367 self.check_import(cr, uid, [val], context=context)
372 class basic_calendar_line(osv.osv):
373 _name = 'basic.calendar.lines'
374 _description = 'Calendar Lines'
376 'name': fields.selection([('vevent', 'Event'), ('vtodo', 'TODO'), \
377 ('alarm', 'Alarm'), \
378 ('attendee', 'Attendee')], \
379 string="Type", size=64),
380 'object_id': fields.many2one('ir.model', 'Object'),
381 'calendar_id': fields.many2one('basic.calendar', 'Calendar', \
382 required=True, ondelete='cascade'),
383 'domain': fields.char('Domain', size=124),
384 'mapping_ids': fields.one2many('basic.calendar.fields', 'type_id', 'Fields Mapping')
388 'domain': lambda *a: '[]',
391 basic_calendar_line()
393 class basic_calendar_attribute(osv.osv):
394 _name = 'basic.calendar.attributes'
395 _description = 'Calendar attributes'
397 'name': fields.char("Name", size=64, required=True),
398 'type': fields.selection([('vevent', 'Event'), ('vtodo', 'TODO'), \
399 ('alarm', 'Alarm'), \
400 ('attendee', 'Attendee')], \
401 string="Type", size=64, required=True),
404 basic_calendar_attribute()
406 class basic_calendar_fields(osv.osv):
407 _name = 'basic.calendar.fields'
408 _description = 'Calendar fields'
411 'field_id': fields.many2one('ir.model.fields', 'OpenObject Field'),
412 'name': fields.many2one('basic.calendar.attributes', 'Name', required=True),
413 'type_id': fields.many2one('basic.calendar.lines', 'Type', \
414 required=True, ondelete='cascade'),
415 'expr': fields.char("Expression", size=64),
416 'fn': fields.selection( [('field', 'Use the field'),
417 ('const', 'Expression as constant'),
418 ('hours', 'Interval in hours'),
420 'mapping': fields.text('Mapping'),
424 'fn': lambda *a: 'field',
427 basic_calendar_fields()
429 class Event(CalDAV, osv.osv_memory):
430 _name = 'basic.calendar.event'
433 'class': None, # Use: O-1, Type: TEXT, Defines the access classification for a calendar component like "PUBLIC" / "PRIVATE" / "CONFIDENTIAL"
434 'created': None, # Use: O-1, Type: DATE-TIME, Specifies the date and time that the calendar information was created by the calendar user agent in the calendar store.
435 'description': None, # Use: O-1, Type: TEXT, Provides a more complete description of the calendar component, than that provided by the "SUMMARY" property.
436 'dtstart': None, # Use: O-1, Type: DATE-TIME, Specifies when the calendar component begins.
437 'geo': None, # Use: O-1, Type: FLOAT, Specifies information related to the global position for the activity specified by a calendar component.
438 'last-mod': None, # Use: O-1, Type: DATE-TIME Specifies the date and time that the information associated with the calendar component was last revised in the calendar store.
439 'location': None, # Use: O-1, Type: TEXT Defines the intended venue for the activity defined by a calendar component.
440 'organizer': None, # Use: O-1, Type: CAL-ADDRESS, Defines the organizer for a calendar component.
441 'priority': None, # Use: O-1, Type: INTEGER, Defines the relative priority for a calendar component.
442 'dtstamp': None, # Use: O-1, Type: DATE-TIME, Indicates the date/time that the instance of the iCalendar object was created.
443 'seq': None, # Use: O-1, Type: INTEGER, Defines the revision sequence number of the calendar component within a sequence of revision.
444 'status': None, # Use: O-1, Type: TEXT, Defines the overall status or confirmation for the calendar component.
445 'summary': None, # Use: O-1, Type: TEXT, Defines a short summary or subject for the calendar component.
446 'transp': None, # Use: O-1, Type: TEXT, Defines whether an event is transparent or not to busy time searches.
447 'uid': None, # Use: O-1, Type: TEXT, Defines the persistent, globally unique identifier for the calendar component.
448 'url': None, # Use: O-1, Type: URL, Defines a Uniform Resource Locator (URL) associated with the iCalendar object.
450 'attach': None, # Use: O-n, Type: BINARY, Provides the capability to associate a document object with a calendar component.
451 'attendee': None, # Use: O-n, Type: CAL-ADDRESS, Defines an "Attendee" within a calendar component.
452 'categories': None, # Use: O-n, Type: TEXT, Defines the categories for a calendar component.
453 'comment': None, # Use: O-n, Type: TEXT, Specifies non-processing information intended to provide a comment to the calendar user.
454 'contact': None, # Use: O-n, Type: TEXT, Used to represent contact information or alternately a reference to contact information associated with the calendar component.
455 'exdate': None, # Use: O-n, Type: DATE-TIME, Defines the list of date/time exceptions for a recurring calendar component.
456 'exrule': None, # Use: O-n, Type: RECUR, Defines a rule or repeating pattern for an exception to a recurrence set.
458 'related': None, # Use: O-n, Specify the relationship of the alarm trigger with respect to the start or end of the calendar component.
459 # like A trigger set 5 minutes after the end of the event or to-do.---> TRIGGER;related=END:PT5M
460 'resources': None, # Use: O-n, Type: TEXT, Defines the equipment or resources anticipated for an activity specified by a calendar entity like RESOURCES:EASEL,PROJECTOR,VCR, LANGUAGE=fr:1 raton-laveur
461 'rdate': None, # Use: O-n, Type: DATE-TIME, Defines the list of date/times for a recurrence set.
462 'rrule': None, # Use: O-n, Type: RECUR, Defines a rule or repeating pattern for recurring events, to-dos, or time zone definitions.
464 'duration': None, # Use: O-1, Type: DURATION, Specifies a positive duration of time.
465 'dtend': None, # Use: O-1, Type: DATE-TIME, Specifies the date and time that a calendar component ends.
467 def export_cal(self, cr, uid, datas, vobj='vevent', context={}):
468 return super(Event, self).export_cal(cr, uid, datas, 'vevent', context=context)
472 class ToDo(CalDAV, osv.osv_memory):
473 _name = 'basic.calendar.todo'
511 def export_cal(self, cr, uid, datas, vobj='vevent', context={}):
512 return super(ToDo, self).export_cal(cr, uid, datas, 'vtodo', context=context)
516 class Journal(CalDAV):
520 class FreeBusy(CalDAV):
522 'contact': None, # Use: O-1, Type: Text, Represent contact information or alternately a reference to contact information associated with the calendar component.
523 'dtstart': None, # Use: O-1, Type: DATE-TIME, Specifies when the calendar component begins.
524 'dtend': None, # Use: O-1, Type: DATE-TIME, Specifies the date and time that a calendar component ends.
525 'duration': None, # Use: O-1, Type: DURATION, Specifies a positive duration of time.
526 'dtstamp': None, # Use: O-1, Type: DATE-TIME, Indicates the date/time that the instance of the iCalendar object was created.
527 'organizer': None, # Use: O-1, Type: CAL-ADDRESS, Defines the organizer for a calendar component.
528 'uid': None, # Use: O-1, Type: Text, Defines the persistent, globally unique identifier for the calendar component.
529 'url': None, # Use: O-1, Type: URL, Defines a Uniform Resource Locator (URL) associated with the iCalendar object.
530 'attendee': None, # Use: O-n, Type: CAL-ADDRESS, Defines an "Attendee" within a calendar component.
531 'comment': None, # Use: O-n, Type: TEXT, Specifies non-processing information intended to provide a comment to the calendar user.
532 'freebusy': None, # Use: O-n, Type: PERIOD, Defines one or more free or busy time intervals.
538 class Timezone(CalDAV):
540 'tzid': None, # Use: R-1, Type: Text, Specifies the text value that uniquely identifies the "VTIMEZONE" calendar component.
541 'last-mod': None, # Use: O-1, Type: DATE-TIME, Specifies the date and time that the information associated with the calendar component was last revised in the calendar store.
542 'tzurl': None, # Use: O-1, Type: URI, Provides a means for a VTIMEZONE component to point to a network location that can be used to retrieve an up-to-date version of itself.
543 'standardc': {'tzprop': None}, # Use: R-1,
544 'daylightc': {'tzprop': None}, # Use: R-1,
545 'x-prop': None, # Use: O-n, Type: Text,
549 class Alarm(CalDAV, osv.osv_memory):
550 _name = 'basic.calendar.alarm'
554 'action': None, # Use: R-1, Type: Text, defines the action to be invoked when an alarm is triggered LIKE "AUDIO" / "DISPLAY" / "EMAIL" / "PROCEDURE"
555 'description': None, # Type: Text, Provides a more complete description of the calendar component, than that provided by the "SUMMARY" property. Use:- R-1 for DISPLAY,Use:- R-1 for EMAIL,Use:- R-1 for PROCEDURE
556 'summary': None, # Use: R-1, Type: Text Which contains the text to be used as the message subject. Use for EMAIL
557 'attendee': None, # Use: R-n, Type: CAL-ADDRESS, Contain the email address of attendees to receive the message. It can also include one or more. Use for EMAIL
558 'trigger': None, # Use: R-1, Type: DURATION, The "TRIGGER" property specifies a duration prior to the start of an event or a to-do. The "TRIGGER" edge may be explicitly set to be relative to the "START" or "END" of the event or to-do with the "related" parameter of the "TRIGGER" property. The "TRIGGER" property value type can alternatively be set to an absolute calendar date and time of day value. Use for all action like AUDIO, DISPLAY, EMAIL and PROCEDURE
559 'duration': None, # Type: DURATION, Duration' and 'repeat' are both optional, and MUST NOT occur more than once each, but if one occurs, so MUST the other. Use:- 0-1 for AUDIO, EMAIL and PROCEDURE, Use:- 0-n for DISPLAY
560 'repeat': None, # Type: INTEGER, Duration' and 'repeat' are both optional, and MUST NOT occur more than once each, but if one occurs, so MUST the other. Use:- 0-1 for AUDIO, EMAIL and PROCEDURE, Use:- 0-n for DISPLAY
561 'attach': None, # Use:- O-n: which MUST point to a sound resource, which is rendered when the alarm is triggered for AUDIO, Use:- O-n: which are intended to be sent as message attachments for EMAIL, Use:- R-1:which MUST point to a procedure resource, which is invoked when the alarm is triggered for PROCEDURE.
565 def export_cal(self, cr, uid, model, alarm_id, vevent, context={}):
566 valarm = vevent.add('valarm')
567 alarm_object = self.pool.get(model)
568 alarm_data = alarm_object.read(cr, uid, alarm_id, [])
570 # Compute trigger data
571 interval = alarm_data['trigger_interval']
572 occurs = alarm_data['trigger_occurs']
573 duration = (occurs == 'after' and alarm_data['trigger_duration']) \
574 or -(alarm_data['trigger_duration'])
575 related = alarm_data['trigger_related']
576 trigger = valarm.add('TRIGGER')
577 trigger.params['related'] = [related.upper()]
578 if interval == 'days':
579 delta = timedelta(days=duration)
580 if interval == 'hours':
581 delta = timedelta(hours=duration)
582 if interval == 'minutes':
583 delta = timedelta(minutes=duration)
584 trigger.value = delta
586 # Compute other details
587 valarm.add('DESCRIPTION').value = alarm_data['name']
588 valarm.add('ACTION').value = alarm_data['action']
591 def import_cal(self, cr, uid, ical_data):
592 for child in ical_data.getChildren():
593 if child.name.lower() == 'trigger':
594 seconds = child.value.seconds
595 days = child.value.days
596 diff = (days * 86400) + seconds
601 related = days > 0 and 'after' or 'before'
602 elif (abs(diff) / 3600) == 0:
603 duration = abs(diff / 60)
605 related = days >= 0 and 'after' or 'before'
607 duration = abs(diff / 3600)
609 related = days >= 0 and 'after' or 'before'
610 self.ical_set('trigger_interval', interval, 'value')
611 self.ical_set('trigger_duration', duration, 'value')
612 self.ical_set('trigger_occurs', related.lower(), 'value')
614 if child.params.get('related'):
615 self.ical_set('trigger_related', child.params.get('related')[0].lower(), 'value')
617 self.ical_set(child.name.lower(), child.value.lower(), 'value')
618 vals = map_data(cr, uid, self)
623 class Attendee(CalDAV, osv.osv_memory):
624 _name = 'basic.calendar.attendee'
625 _calname = 'attendee'
628 'cutype': None, # Use: 0-1 Specify the type of calendar user specified by the property like "INDIVIDUAL"/"GROUP"/"RESOURCE"/"ROOM"/"UNKNOWN".
629 'member': None, # Use: 0-1 Specify the group or list membership of the calendar user specified by the property.
630 'role': None, # Use: 0-1 Specify the participation role for the calendar user specified by the property like "CHAIR"/"REQ-PARTICIPANT"/"OPT-PARTICIPANT"/"NON-PARTICIPANT"
631 'partstat': None, # Use: 0-1 Specify the participation status for the calendar user specified by the property. like use for VEVENT:- "NEEDS-ACTION"/"ACCEPTED"/"DECLINED"/"TENTATIVE"/"DELEGATED", use for VTODO:-"NEEDS-ACTION"/"ACCEPTED"/"DECLINED"/"TENTATIVE"/"DELEGATED"/"COMPLETED"/"IN-PROCESS" and use for VJOURNAL:- "NEEDS-ACTION"/"ACCEPTED"/"DECLINED".
632 'rsvp': None, # Use: 0-1 Specify whether there is an expectation of a favor of a reply from the calendar user specified by the property value like TRUE / FALSE.
633 'delegated-to': None, # Use: 0-1 Specify the calendar users to whom the calendar user specified by the property has delegated participation.
634 'delegated-from': None, # Use: 0-1 Specify the calendar users that have delegated their participation to the calendar user specified by the property.
635 'sent-by': None, # Use: 0-1 Specify the calendar user that is acting on behalf of the calendar user specified by the property.
636 'cn': None, # Use: 0-1 Specify the common name to be associated with the calendar user specified by the property.
637 'dir': None, # Use: 0-1 Specify reference to a directory entry associated with the calendar user specified by the property.
638 'language': None, # Use: 0-1 Specify the language for text values in a property or property parameter.
641 def import_cal(self, cr, uid, ical_data):
642 for para in ical_data.params:
643 if para.lower() == 'cn':
644 self.ical_set(para.lower(), ical_data.params[para][0]+':'+ \
645 ical_data.value, 'value')
647 self.ical_set(para.lower(), ical_data.params[para][0].lower(), 'value')
648 if not ical_data.params.get('CN'):
649 self.ical_set('cn', ical_data.value, 'value')
650 vals = map_data(cr, uid, self)
653 def export_cal(self, cr, uid, model, attendee_ids, vevent, context={}):
654 attendee_object = self.pool.get(model)
655 self.__attribute__ = get_attribute_mapping(cr, uid, self._calname, context)
656 for attendee in attendee_object.read(cr, uid, attendee_ids, []):
657 attendee_add = vevent.add('attendee')
659 for a_key, a_val in self.__attribute__.items():
660 if attendee[a_val['field']] and a_val['field'] != 'cn':
661 if a_val['type'] == 'text':
662 attendee_add.params[a_key] = [str(attendee[a_val['field']])]
663 elif a_val['type'] == 'boolean':
664 attendee_add.params[a_key] = [str(attendee[a_val['field']])]
665 if a_val['field'] == 'cn':
666 cn_val = [str(attendee[a_val['field']])]
667 attendee_add.params['CN'] = cn_val
673 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: