17202c3ba83ca1431dfbdafe8c77d1f686d5e404
[odoo/odoo.git] / addons / base_calendar / base_calendar.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #    
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2004-2010 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 from datetime import datetime, timedelta
22 from dateutil import parser
23 from dateutil import rrule
24 from osv import fields, osv
25 from service import web_services
26 from tools.translate import _
27 import base64
28 import pooler
29 import pytz
30 import re
31 import time
32 import tools
33
34 months = {
35         1:"January", 2:"February", 3:"March", 4:"April", \
36         5:"May", 6:"June", 7:"July", 8:"August", 9:"September", \
37         10:"October", 11:"November", 12:"December"}
38
39 def get_recurrent_dates(rrulestring, exdate, startdate=None):
40     def todate(date):
41         val = parser.parse(''.join((re.compile('\d')).findall(date)))
42         return val
43     if not startdate:
44         startdate = datetime.now()
45     rset1 = rrule.rrulestr(rrulestring, dtstart=startdate, forceset=True)
46
47     for date in exdate:
48         datetime_obj = todate(date)
49         rset1._exdate.append(datetime_obj)
50     re_dates = map(lambda x:x.strftime('%Y-%m-%d %H:%M:%S'), rset1._iter())
51     return re_dates
52
53 def base_calendar_id2real_id(base_calendar_id=None, with_date=False):
54     if base_calendar_id and isinstance(base_calendar_id, (str, unicode)):
55         res = base_calendar_id.split('-')
56         if len(res) >= 2:
57             real_id = res[0]
58             if with_date:
59                 real_date = time.strftime("%Y-%m-%d %H:%M:%S", \
60                                  time.strptime(res[1], "%Y%m%d%H%M%S"))
61                 start = datetime.strptime(real_date, "%Y-%m-%d %H:%M:%S")
62                 end = start + timedelta(hours=with_date)
63                 return (int(real_id), real_date, end.strftime("%Y-%m-%d %H:%M:%S"))
64             return int(real_id)
65     return base_calendar_id and int(base_calendar_id) or base_calendar_id
66
67 def real_id2base_calendar_id(real_id, recurrent_date):
68     if real_id and recurrent_date:
69         recurrent_date = time.strftime("%Y%m%d%H%M%S", \
70                          time.strptime(recurrent_date, "%Y-%m-%d %H:%M:%S"))
71         return '%d-%s' % (real_id, recurrent_date)
72     return real_id
73
74
75 def _links_get(self, cr, uid, context={}):
76     obj = self.pool.get('res.request.link')
77     ids = obj.search(cr, uid, [])
78     res = obj.read(cr, uid, ids, ['object', 'name'], context)
79     return [(r['object'], r['name']) for r in res]
80
81 html_invitation = """
82 <html>
83 <head>
84 <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
85 <title>%(name)s</title>
86 </head>
87 <body>
88 <table border="0" cellspacing="10" cellpadding="0" width="100%%"
89     style="font-family: Arial, Sans-serif; font-size: 14">
90     <tr>
91         <td width="100%%">Hello,</td>
92     </tr>
93     <tr>
94         <td width="100%%">You are invited for <i>%(company)s</i> Event.</td>
95     </tr>
96     <tr>
97         <td width="100%%">Below are the details of event:</td>
98     </tr>
99 </table>
100
101 <table cellspacing="0" cellpadding="5" border="0" summary=""
102     style="width: 90%%; font-family: Arial, Sans-serif; border: 1px Solid #ccc; background-color: #f6f6f6">
103     <tr valign="center" align="center">
104         <td bgcolor="DFDFDF">
105         <h3>%(name)s</h3>
106         </td>
107     </tr>
108     <tr>
109         <td>
110         <table cellpadding="8" cellspacing="0" border="0"
111             style="font-size: 14" summary="Eventdetails" bgcolor="f6f6f6"
112             width="90%%">
113             <tr>
114                 <td width="21%%">
115                 <div><b>Start Date</b></div>
116                 </td>
117                 <td><b>:</b></td>
118                 <td>%(start_date)s</td>
119                 <td width="15%%">
120                 <div><b>End Date</b></div>
121                 </td>
122                 <td><b>:</b></td>
123                 <td width="25%%">%(end_date)s</td>
124             </tr>
125             <tr valign="top">
126                 <td><b>Description</b></td>
127                 <td><b>:</b></td>
128                 <td colspan="3">%(description)s</td>
129             </tr>
130             <tr valign="top">
131                 <td>
132                 <div><b>Location</b></div>
133                 </td>
134                 <td><b>:</b></td>
135                 <td colspan="3">%(location)s</td>
136             </tr>
137             <tr valign="top">
138                 <td>
139                 <div><b>Event Attendees</b></div>
140                 </td>
141                 <td><b>:</b></td>
142                 <td colspan="3">
143                 <div>
144                 <div>%(attendees)s</div>
145                 </div>
146                 </td>
147             </tr>
148             <tr valign="top">
149                 <td><b>Are you coming?</b></td>
150                 <td><b>:</b></td>
151                 <td colspan="3">
152                 <UL>
153                     <LI>YES</LI>
154                     <LI>NO</LI>
155                     <LI>MAYBE</LI>
156                 </UL>
157                 </td>
158             </tr>
159         </table>
160         </td>
161     </tr>
162 </table>
163 <table border="0" cellspacing="10" cellpadding="0" width="100%%"
164     style="font-family: Arial, Sans-serif; font-size: 14">
165     <tr>
166         <td width="100%%"><b>Note:</b> If you are interested please reply this
167         mail and keep only your response from options <i>YES, NO</i>
168         and <i>MAYBE</i>.</td>
169     </tr>
170     <tr>
171         <td width="100%%">From:</td>
172     </tr>
173     <tr>
174         <td width="100%%">%(user)s</td>
175     </tr>
176     <tr valign="top">
177         <td width="100%%">-<font color="a7a7a7">-------------------------</font></td>
178     </tr>
179     <tr>
180         <td width="100%%"> <font color="a7a7a7">%(sign)s</font></td>
181     </tr>
182 </table>
183 </body>
184 </html>
185 """
186
187 class invite_attendee_wizard(osv.osv_memory):
188     _name = "base_calendar.invite.attendee"
189     _description = "Invite Attendees"
190
191     _columns = {
192         'type': fields.selection([('internal', 'Internal User'), \
193               ('external', 'External Email'), \
194               ('partner', 'Partner Contacts')], 'Type', required=True), 
195         'user_ids': fields.many2many('res.users', 'invite_user_rel', 
196                                   'invite_id', 'user_id', 'Users'), 
197         'partner_id': fields.many2one('res.partner', 'Partner'), 
198         'email': fields.char('Email', size=124), 
199         'contact_ids': fields.many2many('res.partner.address', 'invite_contact_rel', 
200                                   'invite_id', 'contact_id', 'Contacts'), 
201         'send_mail': fields.boolean('Send mail?', help='Check this if you want\
202  to send an Email to Invited Person')
203               }
204
205     _defaults = {
206                  'type': lambda *x: 'internal'
207                  }
208
209     def do_invite(self, cr, uid, ids, context={}):
210         for att_id in ids:
211             datas = self.read(cr, uid, att_id)
212             model = False
213             model_field = False
214             if not context or not context.get('model'):
215                 return {}
216             else:
217                 model = context.get('model')
218             model_field = context.get('attendee_field', False)
219             obj = self.pool.get(model)
220             res_obj = obj.browse(cr, uid, context['active_id'])
221             type = datas.get('type')
222             att_obj = self.pool.get('calendar.attendee')
223             vals = {}
224             mail_to = []
225             if not model == 'calendar.attendee':
226                 vals = {'ref': '%s,%s' % (model, base_calendar_id2real_id(context['active_id']))}
227     
228             if type == 'internal':
229                 user_obj = self.pool.get('res.users')
230                 if not datas.get('user_ids'):
231                     raise osv.except_osv(_('Error!'), ("Please select any User"))
232                 for user_id in datas.get('user_ids'):
233                     user = user_obj.browse(cr, uid, user_id)
234                     vals.update({'user_id': user_id, 
235                                          'email': user.address_id.email})
236                     if user.address_id.email:
237                         mail_to.append(user.address_id.email)
238                     
239             elif  type == 'external' and datas.get('email'):
240                 vals.update({'email': datas['email']})
241                 mail_to.append(datas['email'])
242             elif  type == 'partner':
243                 add_obj = self.pool.get('res.partner.address')
244                 for contact in  add_obj.browse(cr, uid, datas['contact_ids']):
245                     vals.update({
246                                  'partner_address_id': contact.id, 
247                                  'email': contact.email})
248                     if contact.email:
249                         mail_to.append(contact.email)
250     
251             if model == 'calendar.attendee':
252                 att = att_obj.browse(cr, uid, context['active_id'])
253                 vals.update({
254                     'parent_ids' : [(4, att.id)],
255                     'ref': att.ref
256                 })
257             if datas.get('send_mail'):
258                 if not mail_to:
259                     name =  map(lambda x: x[1], filter(lambda x: type==x[0], \
260                                        self._columns['type'].selection))
261                     raise osv.except_osv(_('Error!'), ("%s must have an email \
262     Address to send mail") % (name[0]))
263                 att_obj._send_mail(cr, uid, [att_id], mail_to, \
264                        email_from=tools.config.get('email_from', False))
265             att_id = att_obj.create(cr, uid, vals)
266             if model_field:
267                 obj.write(cr, uid, res_obj.id, {model_field: [(4, att_id)]})
268         return {}
269
270
271     def onchange_partner_id(self, cr, uid, ids, partner_id, *args, **argv):
272         if not partner_id:
273             return {'value': {'contact_ids': []}}
274         cr.execute('select id from res_partner_address \
275                          where partner_id=%s' % (partner_id))
276         contacts = map(lambda x: x[0], cr.fetchall())
277         return {'value': {'contact_ids': contacts}}
278
279 invite_attendee_wizard()
280
281 class calendar_attendee(osv.osv):
282     _name = 'calendar.attendee'
283     _description = 'Attendee information'
284     _rec_name = 'cutype'
285
286     __attribute__ = {}
287
288     def _get_address(self, name=None, email=None):
289         if name and email:
290             name += ':'
291         return (name or '') + (email and ('MAILTO:' + email) or '')
292
293     def _compute_data(self, cr, uid, ids, name, arg, context):
294         name = name[0]
295         result = {}
296
297         for attdata in self.browse(cr, uid, ids, context=context):
298             id = attdata.id
299             result[id] = {}
300             if name == 'sent_by':
301                 if not attdata.sent_by_uid:
302                     result[id][name] = ''
303                     continue
304                 else:
305                     result[id][name] =  self._get_address(attdata.sent_by_uid.name, \
306                                         attdata.sent_by_uid.address_id.email)
307             if name == 'cn':
308                 if attdata.user_id:
309                     result[id][name] = self._get_address(attdata.user_id.name, attdata.email)
310                 elif attdata.partner_address_id:
311                     result[id][name] = self._get_address(attdata.partner_id.name, attdata.email)
312                 else:
313                     result[id][name] = self._get_address(None, attdata.email)
314             if name == 'delegated_to':
315                 todata = []
316                 for parent in attdata.parent_ids:
317                     if parent.email:
318                         todata.append('MAILTO:' + parent.email)
319                 result[id][name] = ', '.join(todata)
320             if name == 'delegated_from':
321                 fromdata = []
322                 for child in attdata.child_ids:
323                     if child.email:
324                         fromdata.append('MAILTO:' + child.email)
325                 result[id][name] = ', '.join(fromdata)
326             if name == 'event_date':
327                 if attdata.ref:
328                     result[id][name] = attdata.ref.date
329                 else:
330                     result[id][name] = False
331             if name == 'event_end_date':
332                 if attdata.ref:
333                      result[id][name] = attdata.ref.date_deadline
334                 else:
335                     result[id][name] = False
336             if name == 'sent_by_uid':
337                 if attdata.ref:
338                     result[id][name] = (attdata.ref.user_id.id,attdata.ref.user_id.name)
339                 else:
340                     result[id][name] = uid
341             if name == 'language':
342                 user_obj = self.pool.get('res.users')
343                 lang = user_obj.read(cr, uid, uid, ['context_lang'])['context_lang']
344                 result[id][name] = lang.replace('_', '-')
345         return result
346
347     def _links_get(self, cr, uid, context={}):
348         obj = self.pool.get('res.request.link')
349         ids = obj.search(cr, uid, [])
350         res = obj.read(cr, uid, ids, ['object', 'name'], context)
351         return [(r['object'], r['name']) for r in res]
352
353     def _lang_get(self, cr, uid, context={}):
354         obj = self.pool.get('res.lang')
355         ids = obj.search(cr, uid, [])
356         res = obj.read(cr, uid, ids, ['code', 'name'], context)
357         res = [((r['code']).replace('_', '-'), r['name']) for r in res]
358         return res
359
360     _columns = {
361         'cutype': fields.selection([('individual', 'Individual'), \
362                     ('group', 'Group'), ('resource', 'Resource'), \
363                     ('room', 'Room'), ('unknown', '') ], \
364                     'Invite Type', help="Specify the type of Invitation"), 
365         'member': fields.char('Member', size=124, 
366                     help="Indicate the groups that the attendee belongs to"), 
367         'role': fields.selection([('req-participant', 'Participation required'), \
368                     ('chair', 'Chair Person'), \
369                     ('opt-participant', 'Optional Participation'), \
370                     ('non-participant', 'For information Purpose')], 'Role', \
371                     help='Participation role for the calendar user'), 
372         'state': fields.selection([('tentative', 'Tentative'), 
373                         ('needs-action', 'Needs Action'), 
374                         ('accepted', 'Accepted'), 
375                         ('declined', 'Declined'), 
376                         ('delegated', 'Delegated')], 'State', readonly=True, 
377                         help="Status of the attendee's participation"), 
378         'rsvp':  fields.boolean('Required Reply?', 
379                     help="Indicats whether the favor of a reply is requested"), 
380         'delegated_to': fields.function(_compute_data, method=True, \
381                 string='Delegated To', type="char", size=124, store=True, \
382                 multi='delegated_to', help="The users that the original \
383 request was delegated to"),         
384         'delegated_from': fields.function(_compute_data, method=True, string=\
385             'Delegated From', type="char", store=True, size=124, multi='delegated_from'),        
386         'parent_ids': fields.many2many('calendar.attendee', 'calendar_attendee_parent_rel', 'attendee_id', 'parent_id', 'Delegrated From'),
387         'child_ids': fields.many2many('calendar.attendee', 'calendar_attendee_child_rel', 'attendee_id', 'child_id', 'Delegrated To'),  
388         'sent_by': fields.function(_compute_data, method=True, string='Sent By', type="char", multi='sent_by', store=True, size=124, help="Specify the user that is acting on behalf of the calendar user"), 
389         'sent_by_uid': fields.function(_compute_data, method=True, string='Sent By User', type="many2one", relation="res.users", multi='sent_by_uid'), 
390         'cn': fields.function(_compute_data, method=True, string='Common name', type="char", size=124, multi='cn', store=True), 
391         'dir': fields.char('URI Reference', size=124, help="Reference to the URI that points to the directory information corresponding to the attendee."), 
392         'language':  fields.function(_compute_data, method=True, string='Language', type="selection", selection=_lang_get, multi='language', store=True, help="To specify the language for text values in a property or property parameter."), 
393         'user_id': fields.many2one('res.users', 'User'), 
394         'partner_address_id': fields.many2one('res.partner.address', 'Contact'), 
395         'partner_id': fields.related('partner_address_id', 'partner_id', type='many2one', relation='res.partner', string='Partner', help="Partner related to contact"), 
396         'email': fields.char('Email', size=124, help="Email of Invited Person"), 
397         'event_date': fields.function(_compute_data, method=True, string='Event Date', type="datetime", multi='event_date'), 
398         'event_end_date': fields.function(_compute_data, method=True, string='Event End Date', type="datetime", multi='event_end_date'), 
399         'ref': fields.reference('Event Ref', selection=_links_get, size=128), 
400         'availability': fields.selection([('free', 'Free'), ('busy', 'Busy')], 'Free/Busy', readonly="True"), 
401      }
402     _defaults = {
403         'state':  lambda *x: 'needs-action', 
404     }
405     
406     response_re = re.compile("Are you coming\?.*\n*.*(YES|NO|MAYBE).*", re.UNICODE)
407     
408     def msg_new(self, cr, uid, msg):        
409         return False
410         
411     def msg_act_get(self, msg):
412         mailgate_obj = self.pool.get('mail.gateway')
413         body = mailgate_obj.msg_body_get(msg)
414         actions = {}
415         res = self.response_re.findall(body['body'])
416         if res:
417                 actions['state'] = res[0]
418         return actions
419
420     def msg_update(self, cr, uid, ids, msg, data={}, default_act='None'):
421         msg_actions = self.msg_act_get(msg)
422         if msg_actions.get('state'):
423             if msg_actions['state'] in ['YES', 'NO', 'MAYBE']:
424                 mapping = {'YES': 'accepted', 'NO': 'declined', 'MAYBE': 'tentative'}
425                 status = mapping[msg_actions['state']]
426                 print 'Got response for invitation id: %s as %s'  % (ids, status)
427                 self.write(cr, uid, ids, {'state': status})
428         return True
429
430     def _send_mail(self, cr, uid, ids, mail_to, email_from=tools.config.get('email_from', False), context={}):
431         company = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.name
432         for att in self.browse(cr, uid, ids, context=context):
433             sign = att.sent_by_uid and att.sent_by_uid.signature or ''
434             sign = '<br>'.join(sign and sign.split('\n') or [])
435             res_obj = att.ref
436             if res_obj and len(res_obj):
437                 res_obj = res_obj[0]
438             sub = '[%s Invitation][%d] %s'  % (company, att.id, res_obj.name)
439             att_infos = []
440             other_invitaion_ids = self.search(cr, uid, [('ref','=',att.ref)])
441             for att2 in self.browse(cr, uid, other_invitaion_ids):
442                 att_infos.append(((att2.user_id and att2.user_id.name) or \
443                              (att2.partner_id and att2.partner_id.name) or \
444                                 att2.email) +  ' - Status: ' + att2.state.title())
445             body_vals = {'name': res_obj.name, 
446                         'start_date': res_obj.date, 
447                         'end_date': res_obj.date_deadline or False, 
448                         'description': res_obj.description or '-', 
449                         'location': res_obj.location or '-', 
450                         'attendees': '<br>'.join(att_infos), 
451                         'user': res_obj.user_id and res_obj.user_id.name or 'OpenERP User', 
452                         'sign': sign, 
453                         'company': company
454             }
455             body = html_invitation % body_vals
456             if mail_to and email_from:
457                 tools.email_send(
458                         email_from, 
459                         mail_to, 
460                         sub, 
461                         body, 
462                         subtype='html', 
463                         reply_to=email_from
464                     ) 
465             return True
466     def onchange_user_id(self, cr, uid, ids, user_id, *args, **argv):
467         if not user_id:
468             return {'value': {'email': ''}}
469         usr_obj = self.pool.get('res.users')
470         user = usr_obj.browse(cr, uid, user_id, *args)
471         return {'value': {'email': user.address_id.email, 'availability':user.availability}}
472
473     def do_tentative(self, cr, uid, ids, context=None, *args):
474         self.write(cr, uid, ids, {'state': 'tentative'}, context)
475
476     def do_accept(self, cr, uid, ids, context=None, *args):
477         for invite in ids:
478             vals = self.read(cr, uid, invite, context=context)
479             user = vals.get('user_id')
480             if user:
481                 ref = vals.get('ref', None)
482                 if ref:
483                     event_ref = ref
484                     if event_ref.user_id.id != user[0]:
485                         defaults = {'user_id':  user[0]}
486                         new_event = model_obj.copy(cr, uid, event, default=defaults, context=context)
487             self.write(cr, uid, invite, {'state': 'accepted'}, context)
488         return True
489
490     def do_decline(self, cr, uid, ids, context=None, *args):
491         self.write(cr, uid, ids, {'state': 'declined'}, context)
492
493     def create(self, cr, uid, vals, context={}):
494         if not vals.get("email") and vals.get("cn"):
495             cnval = vals.get("cn").split(':')
496             email =  filter(lambda x:x.__contains__('@'), cnval)
497             vals['email'] = email[0]
498             vals['cn'] = vals.get("cn")
499         res = super(calendar_attendee, self).create(cr, uid, vals, context)
500         return res
501     
502 calendar_attendee()
503
504 class res_alarm(osv.osv):
505     _name = 'res.alarm'
506     _description = 'Basic Alarm Information'
507     _columns = {
508         'name':fields.char('Name', size=256, required=True), 
509         'trigger_occurs': fields.selection([('before', 'Before'), ('after', 'After')], \
510                                         'Triggers', required=True), 
511         'trigger_interval': fields.selection([('minutes', 'Minutes'), ('hours', 'Hours'), \
512                 ('days', 'Days')], 'Interval', required=True), 
513         'trigger_duration':  fields.integer('Duration', required=True), 
514         'trigger_related':  fields.selection([('start', 'The event starts'), ('end', \
515                                        'The event ends')], 'Related to', required=True), 
516         'duration': fields.integer('Duration', help="""Duration' and 'Repeat' \
517 are both optional, but if one occurs, so MUST the other"""), 
518         'repeat': fields.integer('Repeat'), 
519         'active': fields.boolean('Active', help="If the active field is set to true, it will allow you to hide the event alarm information without removing it."), 
520
521
522     }
523     _defaults = {
524         'trigger_interval':  lambda *x: 'minutes', 
525         'trigger_duration': lambda *x: 5, 
526         'trigger_occurs': lambda *x: 'before', 
527         'trigger_related': lambda *x: 'start', 
528         'active': lambda *x: 1, 
529     }
530
531     def do_alarm_create(self, cr, uid, ids, model, date, context={}):
532         alarm_obj = self.pool.get('calendar.alarm')
533         ir_obj = self.pool.get('ir.model')
534         model_id = ir_obj.search(cr, uid, [('model', '=', model)])[0]
535         
536         model_obj = self.pool.get(model)
537         for data in model_obj.browse(cr, uid, ids):
538             basic_alarm = data.alarm_id
539             if not context.get('alarm_id'):
540                 self.do_alarm_unlink(cr, uid, [data.id], model)
541                 return True
542             self.do_alarm_unlink(cr, uid, [data.id], model)
543             if basic_alarm:
544                 vals = {
545                     'action': 'display', 
546                     'description': data.description, 
547                     'name': data.name, 
548                     'attendee_ids': [(6, 0, map(lambda x:x.id, data.attendee_ids))], 
549                     'trigger_related': basic_alarm.trigger_related, 
550                     'trigger_duration': basic_alarm.trigger_duration, 
551                     'trigger_occurs': basic_alarm.trigger_occurs, 
552                     'trigger_interval': basic_alarm.trigger_interval, 
553                     'duration': basic_alarm.duration, 
554                     'repeat': basic_alarm.repeat, 
555                     'state': 'run', 
556                     'event_date': data[date], 
557                     'res_id': data.id, 
558                     'model_id': model_id, 
559                     'user_id': uid
560                  }
561                 alarm_id = alarm_obj.create(cr, uid, vals)
562                 cr.execute('Update %s set base_calendar_alarm_id=%s, alarm_id=%s \
563                                         where id=%s' % (model_obj._table, \
564                                         alarm_id, basic_alarm.id, data.id))
565         cr.commit()
566         return True
567
568     def do_alarm_unlink(self, cr, uid, ids, model, context={}):
569         alarm_obj = self.pool.get('calendar.alarm')
570         ir_obj = self.pool.get('ir.model')
571         model_id = ir_obj.search(cr, uid, [('model', '=', model)])[0]
572         model_obj = self.pool.get(model)
573         for datas in model_obj.browse(cr, uid, ids):
574             alarm_ids = alarm_obj.search(cr, uid, [('model_id', '=', model_id), ('res_id', '=', datas.id)])
575             if alarm_ids:
576                 alarm_obj.unlink(cr, uid, alarm_ids)
577                 cr.execute('Update %s set base_calendar_alarm_id=NULL, alarm_id=NULL\
578                              where id=%s' % (model_obj._table, datas.id))
579         cr.commit()
580         return True
581
582 res_alarm()
583
584 class calendar_alarm(osv.osv):
585     _name = 'calendar.alarm'
586     _description = 'Event alarm information'
587     _inherit = 'res.alarm'
588     __attribute__ = {}
589
590     _columns = {
591             'alarm_id': fields.many2one('res.alarm', 'Basic Alarm', ondelete='cascade'), 
592             'name': fields.char('Summary', size=124, help="""Contains the text to be used as the message subject for email
593 or contains the text to be used for display"""), 
594             'action': fields.selection([('audio', 'Audio'), ('display', 'Display'), \
595                     ('procedure', 'Procedure'), ('email', 'Email') ], 'Action', \
596                     required=True, help="Defines the action to be invoked when an alarm is triggered"), 
597             'description': fields.text('Description', help='Provides a more complete description of the calendar component, than that provided by the "SUMMARY" property'), 
598             'attendee_ids': fields.many2many('calendar.attendee', 'alarm_attendee_rel', \
599                                           'alarm_id', 'attendee_id', 'Attendees', readonly=True), 
600             'attach': fields.binary('Attachment', help="""* Points to a sound resource, which is rendered when the alarm is triggered for audio,
601 * File which is intended to be sent as message attachments for email,
602 * Points to a procedure resource, which is invoked when the alarm is triggered for procedure."""), 
603             'res_id': fields.integer('Resource ID'), 
604             'model_id': fields.many2one('ir.model', 'Model'), 
605             'user_id': fields.many2one('res.users', 'Owner'), 
606             'event_date': fields.datetime('Event Date'), 
607             'event_end_date': fields.datetime('Event End Date'), 
608             'trigger_date': fields.datetime('Trigger Date', readonly="True"), 
609             'state':fields.selection([
610                         ('draft', 'Draft'), 
611                         ('run', 'Run'), 
612                         ('stop', 'Stop'), 
613                         ('done', 'Done'), 
614                     ], 'State', select=True, readonly=True), 
615      }
616
617     _defaults = {
618         'action':  lambda *x: 'email', 
619         'state': lambda *x: 'run', 
620      }
621
622     def create(self, cr, uid, vals, context={}):
623         event_date = vals.get('event_date', False)
624         if event_date:
625             dtstart = datetime.strptime(vals['event_date'], "%Y-%m-%d %H:%M:%S")
626             if vals['trigger_interval'] == 'days':
627                 delta = timedelta(days=vals['trigger_duration'])
628             if vals['trigger_interval'] == 'hours':
629                 delta = timedelta(hours=vals['trigger_duration'])
630             if vals['trigger_interval'] == 'minutes':
631                 delta = timedelta(minutes=vals['trigger_duration'])
632             trigger_date =  dtstart + (vals['trigger_occurs'] == 'after' and delta or -delta)
633             vals['trigger_date'] = trigger_date
634         res = super(calendar_alarm, self).create(cr, uid, vals, context)
635         return res
636
637     def do_run_scheduler(self, cr, uid, automatic=False, use_new_cursor=False,\
638                        context=None):
639         if not context:
640             context = {}
641         current_datetime = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
642         cr.execute("select alarm.id as id \
643                     from calendar_alarm alarm \
644                     where alarm.state = %s and alarm.trigger_date <= %s", ('run', current_datetime))
645         res = cr.dictfetchall()
646         alarm_ids = map(lambda x: x['id'], res)
647         attendee_obj = self.pool.get('calendar.attendee')
648         request_obj = self.pool.get('res.request')
649         mail_to = []
650         for alarm in self.browse(cr, uid, alarm_ids):
651             if alarm.action == 'display':
652                 value = {
653                    'name': alarm.name, 
654                    'act_from': alarm.user_id.id, 
655                    'act_to': alarm.user_id.id, 
656                    'body': alarm.description, 
657                    'trigger_date': alarm.trigger_date, 
658                    'ref_doc1':  '%s,%s'  % (alarm.model_id.model, alarm.res_id)
659                 }
660                 request_id = request_obj.create(cr, uid, value)
661                 request_ids = [request_id]
662                 for attendee in alarm.attendee_ids:
663                     if attendee.user_id:
664                         value['act_to'] = attendee.user_id.id
665                         request_id = request_obj.create(cr, uid, value)
666                         request_ids.append(request_id)
667                 request_obj.request_send(cr, uid, request_ids)
668
669             if alarm.action == 'email':
670                 sub = '[Openobject Remainder] %s'  % (alarm.name)
671                 body = """
672                 Name: %s
673                 Date: %s
674                 Description: %s
675
676                 From:
677                       %s
678                       %s
679
680                 """  % (alarm.name, alarm.trigger_date, alarm.description, \
681                     alarm.user_id.name, alarm.user_id.signature)
682                 mail_to = [alarm.user_id.address_id.email]
683                 for att in alarm.attendee_ids:
684                     mail_to.append(att.user_id.address_id.email)
685                 if mail_to:
686                     tools.email_send(
687                         tools.config.get('email_from', False), 
688                         mail_to, 
689                         sub, 
690                         body
691                     )
692             self.write(cr, uid, [alarm.id], {'state':'done'})
693         return True
694
695 calendar_alarm()
696
697
698 class calendar_event(osv.osv):
699     _name = "calendar.event"
700     _description = "Calendar Event"
701     __attribute__ = {}
702     
703     def _tz_get(self, cr, uid, context={}):
704         return [(x.lower(), x) for x in pytz.all_timezones]
705
706     
707     def onchange_dates(self, cr, uid, ids, start_date, duration=False, end_date=False, context={}):
708         if not start_date:
709             return {}
710         start = datetime.strptime(start_date, "%Y-%m-%d %H:%M:%S")
711         value = {}
712         if end_date and not duration:
713             end = datetime.strptime(end_date, "%Y-%m-%d %H:%M:%S")
714             diff = end - start
715             duration =  float(diff.days)* 24 + (float(diff.seconds) / 3600)
716             value['duration'] = round(duration, 2)
717         elif not end_date:
718             end = start + timedelta(hours=duration)
719             value['date_deadline'] = end.strftime("%Y-%m-%d %H:%M:%S")
720         return {'value': value}
721
722     def _get_rulestring(self, cr, uid, ids, name, arg, context=None):
723         result = {}
724         for event in ids:
725             datas = self.read(cr, uid, event)
726             if datas.get('rrule_type'):
727                 if datas.get('rrule_type') == 'none':
728                     result[event] = False
729                 elif datas.get('rrule_type') == 'custom':
730                     rrule_custom = self.compute_rule_string(cr, uid, datas)
731                     result[event] = rrule_custom
732                 else:
733                     result[event] = self.compute_rule_string(cr, uid, {'freq':\
734                                         datas.get('rrule_type').upper(), \
735                                         'interval': 1}, context=context)
736         return result
737
738     _columns = {
739         'id': fields.integer('ID'), 
740         'sequence': fields.integer('Sequence'), 
741         'name': fields.char('Description', size=64, required=True), 
742         'date': fields.datetime('Date'), 
743         'date_deadline': fields.datetime('Deadline'), 
744         'create_date': fields.datetime('Created', readonly=True), 
745         'duration': fields.float('Duration'), 
746         'description': fields.text('Your action'), 
747         'class': fields.selection([('public', 'Public'), ('private', 'Private'), \
748                  ('confidential', 'Confidential')], 'Mark as'), 
749         'location': fields.char('Location', size=264, help="Location of Event"), 
750         'show_as': fields.selection([('free', 'Free'), \
751                                   ('busy', 'Busy')], 
752                                    'Show as'), 
753         'base_calendar_url': fields.char('Caldav URL', size=264), 
754         'exdate': fields.text('Exception Date/Times', help="This property \
755 defines the list of date/time exceptions for arecurring calendar component."), 
756         'exrule': fields.char('Exception Rule', size=352, help="defines a \
757 rule or repeating pattern for anexception to a recurrence set"), 
758         'rrule': fields.function(_get_rulestring, type='char', size=124, method=True, string='Recurrent Rule', store=True), 
759         'rrule_type': fields.selection([('none', ''), ('daily', 'Daily'), \
760                             ('weekly', 'Weekly'), ('monthly', 'Monthly'), \
761                             ('yearly', 'Yearly'), ('custom', 'Custom')], 'Recurrency'), 
762         'alarm_id': fields.many2one('res.alarm', 'Alarm'), 
763         'base_calendar_alarm_id': fields.many2one('calendar.alarm', 'Alarm'), 
764         'recurrent_uid': fields.integer('Recurrent ID'), 
765         'recurrent_id': fields.datetime('Recurrent ID date'), 
766         'vtimezone': fields.related('user_id', 'context_tz', type='char', size=24, string='Timezone'), 
767         'user_id': fields.many2one('res.users', 'Responsible'), 
768         'freq': fields.selection([('None', 'No Repeat'), \
769                             ('secondly', 'Secondly'), \
770                             ('minutely', 'Minutely'), \
771                             ('hourly', 'Hourly'), \
772                             ('daily', 'Daily'), \
773                             ('weekly', 'Weekly'), \
774                             ('monthly', 'Monthly'), \
775                             ('yearly', 'Yearly')], 'Frequency'), 
776         'interval': fields.integer('Interval'), 
777         'count': fields.integer('Count'), 
778         'mo': fields.boolean('Mon'), 
779         'tu': fields.boolean('Tue'), 
780         'we': fields.boolean('Wed'), 
781         'th': fields.boolean('Thu'), 
782         'fr': fields.boolean('Fri'), 
783         'sa': fields.boolean('Sat'), 
784         'su': fields.boolean('Sun'), 
785         'select1': fields.selection([('date', 'Date of month'), \
786                             ('day', 'Day of month')], 'Option'), 
787         'day': fields.integer('Date of month'), 
788         'week_list': fields.selection([('MO', 'Monday'), ('TU', 'Tuesday'), \
789                                    ('WE', 'Wednesday'), ('TH', 'Thursday'), \
790                                    ('FR', 'Friday'), ('SA', 'Saturday'), \
791                                    ('SU', 'Sunday')], 'Weekday'), 
792         'byday': fields.selection([('1', 'First'), ('2', 'Second'), \
793                                    ('3', 'Third'), ('4', 'Fourth'), \
794                                    ('5', 'Fifth'), ('-1', 'Last')], 'By day'), 
795         'month_list': fields.selection(months.items(), 'Month'), 
796         'end_date': fields.date('Repeat Until')
797     }
798
799     _defaults = {
800          'class': lambda *a: 'public', 
801          'show_as': lambda *a: 'busy', 
802          'freq':  lambda *x: 'None', 
803          'select1':  lambda *x: 'date', 
804          'interval':  lambda *x: 1, 
805     }
806     
807     def modify_this(self, cr, uid, event_id, defaults, real_date, context=None, *args):
808         event_id = base_calendar_id2real_id(event_id)
809         datas = self.read(cr, uid, event_id, context=context)
810         defaults.update({
811                'recurrent_uid': base_calendar_id2real_id(datas['id']), 
812                'recurrent_id': defaults.get('date') or real_date, 
813                'rrule_type': 'none', 
814                'rrule': ''
815                     })
816         exdate = datas['exdate'] and datas['exdate'].split(',') or []
817         if real_date and defaults.get('date'):
818             exdate.append(real_date)
819         self.write(cr, uid, event_id, {'exdate': ','.join(exdate)}, context=context)
820         new_id = self.copy(cr, uid, event_id, default=defaults, context=context)
821         return new_id
822
823     def modify_all(self, cr, uid, event_id, defaults, context=None, *args):
824         event_id = base_calendar_id2real_id(event_id)
825         defaults.pop('id')
826         defaults.update({'table': self._table})
827
828         qry = "UPDATE %(table)s set name='%(name)s', \
829                         date='%(date)s', date_deadline='%(date_deadline)s'"
830         if defaults.get('alarm_id'):
831             qry += ", alarm_id=%(alarm_id)s"
832         if defaults.get('location'):
833             qry += ", location='%(location)s'"
834         qry += "WHERE id=%s" % (event_id)
835         cr.execute(qry % (defaults))
836         return True
837
838     def get_recurrent_ids(self, cr, uid, select, base_start_date, base_until_date, limit=100):
839         if not limit:
840             limit = 100
841         if isinstance(select, (str, int, long)):
842             ids = [select]
843         else:
844             ids = select
845         result = []
846         if ids and (base_start_date or base_until_date):
847             cr.execute("select m.id, m.rrule, m.date, m.date_deadline, \
848                             m.exdate  from "  + self._table + \
849                             " m where m.id in ("\
850                             + ','.join(map(lambda x: str(x), ids))+")")
851
852             count = 0
853             for data in cr.dictfetchall():
854                 start_date = base_start_date and datetime.strptime(base_start_date, "%Y-%m-%d") or False
855                 until_date = base_until_date and datetime.strptime(base_until_date, "%Y-%m-%d") or False
856                 if count > limit:
857                     break
858                 event_date = datetime.strptime(data['date'], "%Y-%m-%d %H:%M:%S")
859                 if start_date and start_date <= event_date:
860                     start_date = event_date
861                 if not data['rrule']:
862                     if start_date and (event_date < start_date):
863                         continue
864                     if until_date and (event_date > until_date):
865                         continue
866                     idval = real_id2base_calendar_id(data['id'], data['date'])
867                     result.append(idval)
868                     count += 1
869                 else:
870                     exdate = data['exdate'] and data['exdate'].split(',') or []                    
871                     rrule_str = data['rrule']
872                     new_rrule_str = []
873                     rrule_until_date = False
874                     is_until = False
875                     for rule in rrule_str.split(';'):
876                         name, value = rule.split('=')
877                         if name == "UNTIL":
878                             is_until = True
879                             value = parser.parse(value)
880                             rrule_until_date = parser.parse(value.strftime("%Y-%m-%d"))
881                             if until_date and until_date >= rrule_until_date:
882                                 until_date = rrule_until_date
883                             if until_date:
884                                 value = until_date.strftime("%Y%m%d%H%M%S")
885                         new_rule = '%s=%s' % (name, value)
886                         new_rrule_str.append(new_rule)
887                     if not is_until and until_date:
888                         value = until_date.strftime("%Y%m%d%H%M%S")
889                         name = "UNTIL"
890                         new_rule = '%s=%s' % (name, value)
891                         new_rrule_str.append(new_rule)
892                     new_rrule_str = ';'.join(new_rrule_str)
893                     start_date = datetime.strptime(data['date'], "%Y-%m-%d %H:%M:%S")
894                     rdates = get_recurrent_dates(str(new_rrule_str), exdate, start_date)
895                     for rdate in rdates:
896                         r_date = datetime.strptime(rdate, "%Y-%m-%d %H:%M:%S")
897                         if start_date and r_date < start_date:
898                             continue
899                         if until_date and r_date > until_date:
900                             continue
901                         idval = real_id2base_calendar_id(data['id'], rdate)
902                         result.append(idval)
903                         count += 1
904         if result:
905             ids = result
906         if isinstance(select, (str, int, long)):
907             return ids and ids[0] or False
908         return ids
909
910     def compute_rule_string(self, cr, uid, datas, context=None, *args):
911         weekdays = ['mo', 'tu', 'we', 'th', 'fr', 'sa', 'su']
912         weekstring = ''
913         monthstring = ''
914         yearstring = ''
915 #    logic for computing rrule string
916         freq = datas.get('freq')
917         if freq == 'None':
918             return ''
919
920         if freq == 'weekly':
921             byday = map(lambda x: x.upper(), filter(lambda x: datas.get(x) and x in weekdays, datas))
922             if byday:
923                 weekstring = ';BYDAY=' + ','.join(byday)
924
925         elif freq == 'monthly':
926             if datas.get('select1')=='date' and (datas.get('day') < 1 or datas.get('day') > 31):
927                 raise osv.except_osv(_('Error!'), ("Please select proper Day of month"))
928             if datas.get('select1')=='day':
929                 monthstring = ';BYDAY=' + datas.get('byday') + datas.get('week_list')
930             elif datas.get('select1')=='date':
931                 monthstring = ';BYMONTHDAY=' + str(datas.get('day'))
932
933         elif freq == 'yearly':
934             if datas.get('select1')=='date'  and (datas.get('day') < 1 or datas.get('day') > 31):
935                 raise osv.except_osv(_('Error!'), ("Please select proper Day of month"))
936             bymonth = ';BYMONTH=' + str(datas.get('month_list'))
937             if datas.get('select1')=='day':
938                 bystring = ';BYDAY=' + datas.get('byday') + datas.get('week_list')
939             elif datas.get('select1')=='date':
940                 bystring = ';BYMONTHDAY=' + str(datas.get('day'))
941             yearstring = bymonth + bystring
942
943         if datas.get('end_date'):
944             datas['end_date'] = ''.join((re.compile('\d')).findall(datas.get('end_date'))) + '235959Z'
945         enddate = (datas.get('count') and (';COUNT=' +  str(datas.get('count'))) or '') +\
946                              ((datas.get('end_date') and (';UNTIL=' + datas.get('end_date'))) or '')
947
948         rrule_string = 'FREQ=' + freq.upper() +  weekstring + ';INTERVAL=' + \
949                 str(datas.get('interval')) + enddate + monthstring + yearstring
950
951 #        End logic
952         return rrule_string
953
954     def search(self, cr, uid, args, offset=0, limit=100, order=None, 
955             context=None, count=False):
956         args_without_date = []
957         start_date = False
958         until_date = False
959         for arg in args:
960             if arg[0] not in ('date', unicode('date')):
961                 args_without_date.append(arg)
962             else:
963                 if arg[1] in ('>', '>='):
964                     start_date = arg[2]
965                 elif arg[1] in ('<', '<='):
966                     until_date = arg[2]
967         res = super(calendar_event, self).search(cr, uid, args_without_date, \
968                                  offset, limit, order, context, count)
969         return self.get_recurrent_ids(cr, uid, res, start_date, until_date, limit)
970
971
972     def write(self, cr, uid, ids, vals, context=None, check=True, update_check=True):
973         if not context:
974             context = {}
975         if isinstance(ids, (str, int, long)):
976             select = [ids]
977         else:
978             select = ids
979         new_ids = []
980         for event_id in select:
981             if len(str(event_id).split('-')) > 1:
982                 data = self.read(cr, uid, event_id, ['date', 'date_deadline', \
983                                                     'rrule', 'duration'])
984                 if data.get('rrule'):
985                     real_date = data.get('date')
986                     data.update(vals)
987                     new_id = self.modify_this(cr, uid, event_id, data, \
988                                                 real_date, context)
989                     context.update({'active_id': new_id,'active_ids': [new_id]})
990                     continue
991             event_id = base_calendar_id2real_id(event_id)
992             if not event_id in new_ids:
993                 new_ids.append(event_id)
994         res = super(calendar_event, self).write(cr, uid, new_ids, vals, context=context)
995         if vals.has_key('alarm_id') or vals.has_key('base_calendar_alarm_id'):
996             alarm_obj = self.pool.get('res.alarm')
997             context.update({'alarm_id': vals.get('alarm_id')})
998             alarm_obj.do_alarm_create(cr, uid, new_ids, self._name, 'date', \
999                                             context=context)
1000         return res
1001
1002     def browse(self, cr, uid, ids, context=None, list_class=None, fields_process={}):
1003         if isinstance(ids, (str, int, long)):
1004             select = [ids]
1005         else:
1006             select = ids
1007         select = map(lambda x: base_calendar_id2real_id(x), select)
1008         res = super(calendar_event, self).browse(cr, uid, select, context, \
1009                                                     list_class, fields_process)
1010         if isinstance(ids, (str, int, long)):
1011             return res and res[0] or False
1012         return res
1013
1014     def read(self, cr, uid, ids, fields=None, context={}, load='_classic_read'):
1015         if isinstance(ids, (str, int, long)):
1016             select = [ids]
1017         else:
1018             select = ids
1019         select = map(lambda x: (x, base_calendar_id2real_id(x)), select)
1020         result = []
1021         if fields and 'date' not in fields:
1022             fields.append('date')
1023         for base_calendar_id, real_id in select:
1024             res = super(calendar_event, self).read(cr, uid, real_id, fields=fields, context=context, \
1025                                               load=load)
1026             ls = base_calendar_id2real_id(base_calendar_id, with_date=res.get('duration', 0))
1027             if not isinstance(ls, (str, int, long)) and len(ls) >= 2:
1028                 res['date'] = ls[1]
1029                 res['date_deadline'] = ls[2]
1030             res['id'] = base_calendar_id
1031
1032             result.append(res)
1033         if isinstance(ids, (str, int, long)):
1034             return result and result[0] or False
1035         return result
1036
1037     def copy(self, cr, uid, id, default=None, context={}):
1038         res = super(calendar_event, self).copy(cr, uid, base_calendar_id2real_id(id), default, context)
1039         alarm_obj = self.pool.get('res.alarm')
1040         alarm_obj.do_alarm_create(cr, uid, [res], self._name, 'date')
1041         return res
1042
1043     def unlink(self, cr, uid, ids, context=None):
1044         res = False
1045         for id in ids:
1046             ls = base_calendar_id2real_id(id)
1047             if not isinstance(ls, (str, int, long)) and len(ls) >= 2:
1048                 date_new = ls[1]
1049                 for record in self.read(cr, uid, [base_calendar_id2real_id(id)], \
1050                                             ['date', 'rrule', 'exdate']):
1051                     if record['rrule']:
1052                         exdate = (record['exdate'] and (record['exdate'] + ',')  or '') + ''.join((re.compile('\d')).findall(date_new)) + 'Z'
1053                         if record['date'] == date_new:
1054                             res = self.write(cr, uid, [base_calendar_id2real_id(id)], {'exdate': exdate})
1055                     else:
1056                         ids = map(lambda x: base_calendar_id2real_id(x), ids)
1057                         res = super(calendar_event, self).unlink(cr, uid, \
1058                                                 base_calendar_id2real_id(ids))
1059                         alarm_obj = self.pool.get('res.alarm')
1060                         alarm_obj.do_alarm_unlink(cr, uid, ids, self._name)
1061             else:
1062                 ids = map(lambda x: base_calendar_id2real_id(x), ids)
1063                 res = super(calendar_event, self).unlink(cr, uid, ids)
1064                 alarm_obj = self.pool.get('res.alarm')
1065                 alarm_obj.do_alarm_unlink(cr, uid, ids, self._name)
1066         return res
1067
1068     def create(self, cr, uid, vals, context={}):
1069         res = super(calendar_event, self).create(cr, uid, vals, context)
1070         alarm_obj = self.pool.get('res.alarm')
1071         alarm_obj.do_alarm_create(cr, uid, [res], self._name, 'date')
1072         return res
1073
1074 calendar_event()
1075
1076 class calendar_todo(osv.osv):
1077     _name = "calendar.todo"
1078     _inherit = "calendar.event"
1079     _description = "Calendar Task"
1080
1081     def _get_date(self, cr, uid, ids, name, arg, context):
1082         res = {}
1083         for event in self.browse(cr, uid, ids, context=context):
1084             res[event.id] = event.date_start
1085         return res
1086
1087     def _set_date(self, cr, uid, id, name, value, arg, context):
1088         event = self.browse(cr, uid, id, context=context)
1089         cr.execute("UPDATE %s set date_start='%s' where id=%s"  \
1090                            % (self._table, value, id))
1091         return True
1092
1093     _columns = {
1094         'date': fields.function(_get_date, method=True, fnct_inv=_set_date, \
1095                             string='Duration', store=True, type='datetime'), 
1096         'duration': fields.integer('Duration'), 
1097     }
1098     
1099     __attribute__ = {}
1100     
1101     
1102 calendar_todo()
1103  
1104 class ir_attachment(osv.osv):
1105     _name = 'ir.attachment'
1106     _inherit = 'ir.attachment'
1107
1108     def search_count(self, cr, user, args, context=None):
1109         args1 = []
1110         for arg in args:
1111             args1.append(map(lambda x:str(x).split('-')[0], arg))
1112         return super(ir_attachment, self).search_count(cr, user, args1, context)
1113
1114     def search(self, cr, uid, args, offset=0, limit=None, order=None, 
1115             context=None, count=False):
1116         new_args = args
1117         for i, arg in enumerate(new_args):
1118             if arg[0] == 'res_id':
1119                 new_args[i] = (arg[0], arg[1], base_calendar_id2real_id(arg[2]))
1120         return super(ir_attachment, self).search(cr, uid, new_args, offset=offset, 
1121                             limit=limit, order=order, 
1122                             context=context, count=False)
1123 ir_attachment()
1124
1125 class ir_values(osv.osv):
1126     _inherit = 'ir.values'
1127
1128     def set(self, cr, uid, key, key2, name, models, value, replace=True, \
1129             isobject=False, meta=False, preserve_user=False, company=False):
1130         new_model = []
1131         for data in models:
1132             if type(data) in (list, tuple):
1133                 new_model.append((data[0], base_calendar_id2real_id(data[1])))
1134             else:
1135                 new_model.append(data)
1136         return super(ir_values, self).set(cr, uid, key, key2, name, new_model,\
1137                     value, replace, isobject, meta, preserve_user, company)
1138
1139     def get(self, cr, uid, key, key2, models, meta=False, context={}, \
1140              res_id_req=False, without_user=True, key2_req=True):
1141         new_model = []
1142         for data in models:
1143             if type(data) in (list, tuple):
1144                 new_model.append((data[0], base_calendar_id2real_id(data[1])))
1145             else:
1146                 new_model.append(data)
1147         return super(ir_values, self).get(cr, uid, key, key2, new_model, \
1148                          meta, context, res_id_req, without_user, key2_req)
1149
1150 ir_values()
1151
1152 class ir_model(osv.osv):
1153
1154     _inherit = 'ir.model'
1155
1156     def read(self, cr, uid, ids, fields=None, context={}, 
1157             load='_classic_read'):
1158         data = super(ir_model, self).read(cr, uid, ids, fields=fields, \
1159                         context=context, load=load)
1160         if data:
1161             for val in data:
1162                 val['id'] = base_calendar_id2real_id(val['id'])
1163         return data
1164
1165 ir_model()
1166
1167 class virtual_report_spool(web_services.report_spool):
1168
1169     def exp_report(self, db, uid, object, ids, datas=None, context=None):
1170         if object == 'printscreen.list':
1171             return super(virtual_report_spool, self).exp_report(db, uid, \
1172                             object, ids, datas, context)
1173         new_ids = []
1174         for id in ids:
1175             new_ids.append(base_calendar_id2real_id(id))
1176         if datas is None:
1177             datas = {}
1178         if datas.get('id',False):
1179             datas['id'] = base_calendar_id2real_id(datas['id'])        
1180         return super(virtual_report_spool, self).exp_report(db, uid, object, new_ids, datas, context)
1181
1182 virtual_report_spool()
1183
1184 class res_users(osv.osv):
1185     _inherit = 'res.users'
1186
1187     def _get_user_avail(self, cr, uid, ids, context=None):
1188         current_datetime = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
1189         res = {}
1190         attendee_obj = self.pool.get('calendar.attendee')
1191         attendee_ids = attendee_obj.search(cr, uid, [
1192                     ('event_date', '<=', current_datetime), ('event_end_date', '<=', current_datetime), 
1193                     ('state', '=', 'accepted'), ('user_id', 'in', ids)
1194                     ])
1195
1196         result = cr.dictfetchall()
1197         for attendee_data in attendee_obj.read(cr, uid, attendee_ids, ['user_id']):
1198             user_id = attendee_data['user_id']
1199             status = 'busy'
1200             res.update({user_id:status})
1201
1202         #TOCHECK: Delegated Event        
1203         for user_id in ids:
1204             if user_id not in res:
1205                 res[user_id] = 'free'
1206
1207         return res
1208
1209     def _get_user_avail_fun(self, cr, uid, ids, name, args, context=None):
1210         return self._get_user_avail(cr, uid, ids, context=context)
1211
1212     _columns = {
1213             'availability': fields.function(_get_user_avail_fun, type='selection', \
1214                     selection=[('free', 'Free'), ('busy', 'Busy')], \
1215                     string='Free/Busy', method=True), 
1216     }
1217 res_users()
1218
1219 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
1220