[FIX] search count
[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
22 from datetime import datetime, timedelta, date
23 from dateutil import parser
24 from dateutil import rrule
25 from osv import fields, osv
26 from service import web_services
27 from tools.translate import _
28 import pytz
29 import re
30 import time
31 import tools
32
33 months = {
34     1: "January", 2: "February", 3: "March", 4: "April", \
35     5: "May", 6: "June", 7: "July", 8: "August", 9: "September", \
36     10: "October", 11: "November", 12: "December"
37 }
38
39 def get_recurrent_dates(rrulestring, exdate, startdate=None, exrule=None):
40     """
41     Get recurrent dates based on Rule string considering exdate and start date
42     @param rrulestring: Rulestring
43     @param exdate: List of exception dates for rrule
44     @param startdate: Startdate for computing recurrent dates
45     @return: List of Recurrent dates
46     """
47     def todate(date):
48         val = parser.parse(''.join((re.compile('\d')).findall(date)))
49         return val
50
51     if not startdate:
52         startdate = datetime.now()
53     if not exdate:
54         exdate = []
55     rset1 = rrule.rrulestr(str(rrulestring), dtstart=startdate, forceset=True)
56
57     for date in exdate:
58         datetime_obj = todate(date)
59         rset1._exdate.append(datetime_obj)
60     if exrule:
61         rset1.exrule(rrule.rrulestr(str(exrule), dtstart=startdate))
62
63
64     return list(rset1._iter())
65
66 def base_calendar_id2real_id(base_calendar_id=None, with_date=False):
67     """
68     This function converts virtual event id into real id of actual event
69     @param base_calendar_id: Id of calendar
70     @param with_date: If value passed to this param it will return dates based on value of withdate + base_calendar_id
71     """
72
73     if base_calendar_id and isinstance(base_calendar_id, (str, unicode)):
74         res = base_calendar_id.split('-')
75
76         if len(res) >= 2:
77             real_id = res[0]
78             if with_date:
79                 real_date = time.strftime("%Y-%m-%d %H:%M:%S", \
80                                  time.strptime(res[1], "%Y%m%d%H%M%S"))
81                 start = datetime.strptime(real_date, "%Y-%m-%d %H:%M:%S")
82                 end = start + timedelta(hours=with_date)
83                 return (int(real_id), real_date, end.strftime("%Y-%m-%d %H:%M:%S"))
84             return int(real_id)
85
86     return base_calendar_id and int(base_calendar_id) or base_calendar_id
87
88 def real_id2base_calendar_id(real_id, recurrent_date):
89     """
90     Convert  real id of record into virtual id using recurrent_date
91     e.g. real id is 1 and recurrent_date is 01-12-2009 10:00:00 then it will return
92         1-20091201100000
93     @return: real id with recurrent date.
94     """
95
96     if real_id and recurrent_date:
97         recurrent_date = time.strftime("%Y%m%d%H%M%S", \
98                             time.strptime(recurrent_date, "%Y-%m-%d %H:%M:%S"))
99         return '%d-%s' % (real_id, recurrent_date)
100     return real_id
101
102 def _links_get(self, cr, uid, context=None):
103     """
104     Get request link.
105     @param cr: the current row, from the database cursor,
106     @param uid: the current user’s ID for security checks,
107     @param context: A standard dictionary for contextual values
108     @return: list of dictionary which contain object and name and id.
109     """
110     obj = self.pool.get('res.request.link')
111     ids = obj.search(cr, uid, [])
112     res = obj.read(cr, uid, ids, ['object', 'name'], context=context)
113     return [(r['object'], r['name']) for r in res]
114
115 html_invitation = """
116 <html>
117 <head>
118 <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
119 <title>%(name)s</title>
120 </head>
121 <body>
122 <table border="0" cellspacing="10" cellpadding="0" width="100%%"
123     style="font-family: Arial, Sans-serif; font-size: 14">
124     <tr>
125         <td width="100%%">Hello,</td>
126     </tr>
127     <tr>
128         <td width="100%%">You are invited for <i>%(company)s</i> Event.</td>
129     </tr>
130     <tr>
131         <td width="100%%">Below are the details of event:</td>
132     </tr>
133 </table>
134
135 <table cellspacing="0" cellpadding="5" border="0" summary=""
136     style="width: 90%%; font-family: Arial, Sans-serif; border: 1px Solid #ccc; background-color: #f6f6f6">
137     <tr valign="center" align="center">
138         <td bgcolor="DFDFDF">
139         <h3>%(name)s</h3>
140         </td>
141     </tr>
142     <tr>
143         <td>
144         <table cellpadding="8" cellspacing="0" border="0"
145             style="font-size: 14" summary="Eventdetails" bgcolor="f6f6f6"
146             width="90%%">
147             <tr>
148                 <td width="21%%">
149                 <div><b>Start Date</b></div>
150                 </td>
151                 <td><b>:</b></td>
152                 <td>%(start_date)s</td>
153                 <td width="15%%">
154                 <div><b>End Date</b></div>
155                 </td>
156                 <td><b>:</b></td>
157                 <td width="25%%">%(end_date)s</td>
158             </tr>
159             <tr valign="top">
160                 <td><b>Description</b></td>
161                 <td><b>:</b></td>
162                 <td colspan="3">%(description)s</td>
163             </tr>
164             <tr valign="top">
165                 <td>
166                 <div><b>Location</b></div>
167                 </td>
168                 <td><b>:</b></td>
169                 <td colspan="3">%(location)s</td>
170             </tr>
171             <tr valign="top">
172                 <td>
173                 <div><b>Event Attendees</b></div>
174                 </td>
175                 <td><b>:</b></td>
176                 <td colspan="3">
177                 <div>
178                 <div>%(attendees)s</div>
179                 </div>
180                 </td>
181             </tr>
182         </table>
183         </td>
184     </tr>
185 </table>
186 <table border="0" cellspacing="10" cellpadding="0" width="100%%"
187     style="font-family: Arial, Sans-serif; font-size: 14">
188     <tr>
189         <td width="100%%">From:</td>
190     </tr>
191     <tr>
192         <td width="100%%">%(user)s</td>
193     </tr>
194     <tr valign="top">
195         <td width="100%%">-<font color="a7a7a7">-------------------------</font></td>
196     </tr>
197     <tr>
198         <td width="100%%"> <font color="a7a7a7">%(sign)s</font></td>
199     </tr>
200 </table>
201 </body>
202 </html>
203 """
204
205 class calendar_attendee(osv.osv):
206     """
207     Calendar Attendee Information
208     """
209     _name = 'calendar.attendee'
210     _description = 'Attendee information'
211     _rec_name = 'cutype'
212
213     __attribute__ = {}
214
215     def _get_address(self, name=None, email=None):
216         """
217         Gives email information in ical CAL-ADDRESS type format
218         @param name: Name for CAL-ADDRESS value
219         @param email: Email address for CAL-ADDRESS value
220         """
221         if name and email:
222             name += ':'
223         return (name or '') + (email and ('MAILTO:' + email) or '')
224
225     def _compute_data(self, cr, uid, ids, name, arg, context=None):
226         """
227         Compute data on function fields for attendee values .
228         @param cr: the current row, from the database cursor,
229         @param uid: the current user’s ID for security checks,
230         @param ids: List of calendar attendee’s IDs.
231         @param name: name of field.
232         @param context: A standard dictionary for contextual values
233         @return: Dictionary of form {id: {'field Name': value'}}.
234         """
235         name = name[0]
236         result = {}
237         for attdata in self.browse(cr, uid, ids, context=context):
238             id = attdata.id
239             result[id] = {}
240             if name == 'sent_by':
241                 if not attdata.sent_by_uid:
242                     result[id][name] = ''
243                     continue
244                 else:
245                     result[id][name] = self._get_address(attdata.sent_by_uid.name, \
246                                         attdata.sent_by_uid.address_id.email)
247
248             if name == 'cn':
249                 if attdata.user_id:
250                     result[id][name] = attdata.user_id.name
251                 elif attdata.partner_address_id:
252                     result[id][name] = attdata.partner_address_id.name or attdata.partner_id.name
253                 else:
254                     result[id][name] = attdata.email or ''
255
256             if name == 'delegated_to':
257                 todata = []
258                 for child in attdata.child_ids:
259                     if child.email:
260                         todata.append('MAILTO:' + child.email)
261                 result[id][name] = ', '.join(todata)
262
263             if name == 'delegated_from':
264                 fromdata = []
265                 for parent in attdata.parent_ids:
266                     if parent.email:
267                         fromdata.append('MAILTO:' + parent.email)
268                 result[id][name] = ', '.join(fromdata)
269
270             if name == 'event_date':
271                 if attdata.ref:
272                     result[id][name] = attdata.ref.date
273                 else:
274                     result[id][name] = False
275
276             if name == 'event_end_date':
277                 if attdata.ref:
278                     result[id][name] = attdata.ref.date_deadline
279                 else:
280                     result[id][name] = False
281
282             if name == 'sent_by_uid':
283                 if attdata.ref:
284                     result[id][name] = (attdata.ref.user_id.id, attdata.ref.user_id.name)
285                 else:
286                     result[id][name] = uid
287
288             if name == 'language':
289                 user_obj = self.pool.get('res.users')
290                 lang = user_obj.read(cr, uid, uid, ['context_lang'], context=context)['context_lang']
291                 result[id][name] = lang.replace('_', '-')
292
293         return result
294
295     def _links_get(self, cr, uid, context=None):
296         """
297         Get request link for ref field in calendar attendee.
298         @param cr: the current row, from the database cursor,
299         @param uid: the current user’s ID for security checks,
300         @param context: A standard dictionary for contextual values
301         @return: list of dictionary which contain object and name and id.
302         """
303         obj = self.pool.get('res.request.link')
304         ids = obj.search(cr, uid, [])
305         res = obj.read(cr, uid, ids, ['object', 'name'], context=context)
306         return [(r['object'], r['name']) for r in res]
307
308     def _lang_get(self, cr, uid, context=None):
309         """
310         Get language for language selection field.
311         @param cr: the current row, from the database cursor,
312         @param uid: the current user’s ID for security checks,
313         @param context: A standard dictionary for contextual values
314         @return: list of dictionary which contain code and name and id.
315         """
316         obj = self.pool.get('res.lang')
317         ids = obj.search(cr, uid, [])
318         res = obj.read(cr, uid, ids, ['code', 'name'], context=context)
319         res = [((r['code']).replace('_', '-').lower(), r['name']) for r in res]
320         return res
321
322     _columns = {
323         'cutype': fields.selection([('individual', 'Individual'), \
324                     ('group', 'Group'), ('resource', 'Resource'), \
325                     ('room', 'Room'), ('unknown', '') ], \
326                     'Invite Type', help="Specify the type of Invitation"),
327         'member': fields.char('Member', size=124,
328                     help="Indicate the groups that the attendee belongs to"),
329         'role': fields.selection([('req-participant', 'Participation required'), \
330                     ('chair', 'Chair Person'), \
331                     ('opt-participant', 'Optional Participation'), \
332                     ('non-participant', 'For information Purpose')], 'Role', \
333                     help='Participation role for the calendar user'),
334         'state': fields.selection([('tentative', 'Tentative'),
335                         ('needs-action', 'Needs Action'),
336                         ('accepted', 'Accepted'),
337                         ('declined', 'Declined'),
338                         ('delegated', 'Delegated')], 'State', readonly=True, \
339                         help="Status of the attendee's participation"),
340         'rsvp':  fields.boolean('Required Reply?',
341                     help="Indicats whether the favor of a reply is requested"),
342         'delegated_to': fields.function(_compute_data, method=True, \
343                 string='Delegated To', type="char", size=124, store=True, \
344                 multi='delegated_to', help="The users that the original \
345 request was delegated to"),
346         'delegated_from': fields.function(_compute_data, method=True, string=\
347             'Delegated From', type="char", store=True, size=124, multi='delegated_from'),
348         'parent_ids': fields.many2many('calendar.attendee', 'calendar_attendee_parent_rel', \
349                                     'attendee_id', 'parent_id', 'Delegrated From'),
350         'child_ids': fields.many2many('calendar.attendee', 'calendar_attendee_child_rel', \
351                                       'attendee_id', 'child_id', 'Delegrated To'),
352         'sent_by': fields.function(_compute_data, method=True, string='Sent By', \
353                         type="char", multi='sent_by', store=True, size=124, \
354                         help="Specify the user that is acting on behalf of the calendar user"),
355         'sent_by_uid': fields.function(_compute_data, method=True, string='Sent By User', \
356                             type="many2one", relation="res.users", multi='sent_by_uid'),
357         'cn': fields.function(_compute_data, method=True, string='Common name', \
358                             type="char", size=124, multi='cn', store=True),
359         'dir': fields.char('URI Reference', size=124, help="Reference to the URI\
360 that points to the directory information corresponding to the attendee."),
361         'language': fields.function(_compute_data, method=True, string='Language', \
362                     type="selection", selection=_lang_get, multi='language', \
363                     store=True, help="To specify the language for text values in a\
364 property or property parameter."),
365         'user_id': fields.many2one('res.users', 'User'),
366         'partner_address_id': fields.many2one('res.partner.address', 'Contact'),
367         'partner_id': fields.related('partner_address_id', 'partner_id', type='many2one', \
368                         relation='res.partner', string='Partner', help="Partner related to contact"),
369         'email': fields.char('Email', size=124, help="Email of Invited Person"),
370         'event_date': fields.function(_compute_data, method=True, string='Event Date', \
371                             type="datetime", multi='event_date'),
372         'event_end_date': fields.function(_compute_data, method=True, \
373                             string='Event End Date', type="datetime", \
374                             multi='event_end_date'),
375         'ref': fields.reference('Event Ref', selection=_links_get, size=128),
376         'availability': fields.selection([('free', 'Free'), ('busy', 'Busy')], 'Free/Busy', readonly="True"),
377     }
378
379     _defaults = {
380         'state': 'needs-action',
381         'role': 'req-participant',
382         'rsvp':  True,
383         'cutype': 'individual',
384     }
385
386     def copy(self, cr, uid, id, default=None, context=None):
387         raise osv.except_osv(_('Warning!'), _('Can not Duplicate'))
388
389     def get_ics_file(self, cr, uid, event_obj, context=None):
390         """
391         Returns iCalendar file for the event invitation
392         @param self: The object pointer
393         @param cr: the current row, from the database cursor,
394         @param uid: the current user’s ID for security checks,
395         @param event_obj: Event object (browse record)
396         @param context: A standard dictionary for contextual values
397         @return: .ics file content
398         """
399         res = None
400         def ics_datetime(idate, short=False):
401             if idate:
402                 if short or len(idate)<=10:
403                     return date.fromtimestamp(time.mktime(time.strptime(idate, '%Y-%m-%d')))
404                 else:
405                     return datetime.strptime(idate, '%Y-%m-%d %H:%M:%S')
406             else:
407                 return False
408         try:
409             # FIXME: why isn't this in CalDAV?
410             import vobject
411         except ImportError:
412             return res
413         cal = vobject.iCalendar()
414         event = cal.add('vevent')
415         if not event_obj.date_deadline or not event_obj.date:
416               raise osv.except_osv(_('Warning !'),_("Couldn't Invite because date is not specified!"))
417         event.add('created').value = ics_datetime(time.strftime('%Y-%m-%d %H:%M:%S'))
418         event.add('dtstart').value = ics_datetime(event_obj.date)
419         event.add('dtend').value = ics_datetime(event_obj.date_deadline)
420         event.add('summary').value = event_obj.name
421         if  event_obj.description:
422             event.add('description').value = event_obj.description
423         if event_obj.location:
424             event.add('location').value = event_obj.location
425         if event_obj.rrule:
426             event.add('rrule').value = event_obj.rrule
427         if event_obj.organizer:
428             event_org = event.add('organizer')
429             event_org.params['CN'] = [event_obj.organizer]
430             event_org.value = 'MAILTO:' + (event_obj.organizer)
431         elif event_obj.user_id or event_obj.organizer_id:
432             event_org = event.add('organizer')
433             organizer = event_obj.organizer_id
434             if not organizer:
435                 organizer = event_obj.user_id
436             event_org.params['CN'] = [organizer.name]
437             event_org.value = 'MAILTO:' + (organizer.user_email or organizer.name)
438
439         if event_obj.alarm_id:
440             # computes alarm data
441             valarm = event.add('valarm')
442             alarm_object = self.pool.get('res.alarm')
443             alarm_data = alarm_object.read(cr, uid, event_obj.alarm_id.id, context=context)
444             # Compute trigger data
445             interval = alarm_data['trigger_interval']
446             occurs = alarm_data['trigger_occurs']
447             duration = (occurs == 'after' and alarm_data['trigger_duration']) \
448                                             or -(alarm_data['trigger_duration'])
449             related = alarm_data['trigger_related']
450             trigger = valarm.add('TRIGGER')
451             trigger.params['related'] = [related.upper()]
452             if interval == 'days':
453                 delta = timedelta(days=duration)
454             if interval == 'hours':
455                 delta = timedelta(hours=duration)
456             if interval == 'minutes':
457                 delta = timedelta(minutes=duration)
458             trigger.value = delta
459             # Compute other details
460             valarm.add('DESCRIPTION').value = alarm_data['name'] or 'OpenERP'
461
462         for attendee in event_obj.attendee_ids:
463             attendee_add = event.add('attendee')
464             attendee_add.params['CUTYPE'] = [str(attendee.cutype)]
465             attendee_add.params['ROLE'] = [str(attendee.role)]
466             attendee_add.params['RSVP'] = [str(attendee.rsvp)]
467             attendee_add.value = 'MAILTO:' + (attendee.email or '')
468         res = cal.serialize()
469         return res
470
471     def _send_mail(self, cr, uid, ids, mail_to, email_from=tools.config.get('email_from', False), context=None):
472         """
473         Send mail for event invitation to event attendees.
474         @param cr: the current row, from the database cursor,
475         @param uid: the current user’s ID for security checks,
476         @param ids: List of attendee’s IDs.
477         @param email_from: Email address for user sending the mail
478         @param context: A standard dictionary for contextual values
479         @return: True
480         """
481         if context is None:
482             context = {}
483
484         company = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.name
485         for att in self.browse(cr, uid, ids, context=context):
486             sign = att.sent_by_uid and att.sent_by_uid.signature or ''
487             sign = '<br>'.join(sign and sign.split('\n') or [])
488             res_obj = att.ref
489             if res_obj:
490                 att_infos = []
491                 sub = res_obj.name
492                 other_invitation_ids = self.search(cr, uid, [('ref', '=', res_obj._name + ',' + str(res_obj.id))])
493
494                 for att2 in self.browse(cr, uid, other_invitation_ids):
495                     att_infos.append(((att2.user_id and att2.user_id.name) or \
496                                  (att2.partner_id and att2.partner_id.name) or \
497                                     att2.email) + ' - Status: ' + att2.state.title())
498                 body_vals = {'name': res_obj.name,
499                             'start_date': res_obj.date,
500                             'end_date': res_obj.date_deadline or False,
501                             'description': res_obj.description or '-',
502                             'location': res_obj.location or '-',
503                             'attendees': '<br>'.join(att_infos),
504                             'user': res_obj.user_id and res_obj.user_id.name or 'OpenERP User',
505                             'sign': sign,
506                             'company': company
507                 }
508                 body = html_invitation % body_vals
509                 if mail_to and email_from:
510                     attach = self.get_ics_file(cr, uid, res_obj, context=context)
511                     tools.email_send(
512                         email_from,
513                         mail_to,
514                         sub,
515                         body,
516                         attach=attach and [('invitation.ics', attach)] or None,
517                         subtype='html',
518                         reply_to=email_from
519                     )
520             return True
521
522     def onchange_user_id(self, cr, uid, ids, user_id, *args, **argv):
523         """
524         Make entry on email and availbility on change of user_id field.
525         @param cr: the current row, from the database cursor,
526         @param uid: the current user’s ID for security checks,
527         @param ids: List of calendar attendee’s IDs.
528         @param user_id: Changed value of User id
529         @return: dictionary of value. which put value in email and availability fields.
530         """
531
532         if not user_id:
533             return {'value': {'email': ''}}
534         usr_obj = self.pool.get('res.users')
535         user = usr_obj.browse(cr, uid, user_id, *args)
536         return {'value': {'email': user.address_id.email, 'availability':user.availability}}
537
538     def do_tentative(self, cr, uid, ids, context=None, *args):
539         """ Makes event invitation as Tentative
540         @param self: The object pointer
541         @param cr: the current row, from the database cursor,
542         @param uid: the current user’s ID for security checks,
543         @param ids: List of calendar attendee’s IDs
544         @param *args: Get Tupple value
545         @param context: A standard dictionary for contextual values
546         """
547         return self.write(cr, uid, ids, {'state': 'tentative'}, context)
548
549     def do_accept(self, cr, uid, ids, context=None, *args):
550         """
551         Update state of invitation as Accepted and
552         if the invited user is other then event user it will make a copy of this event for invited user
553         @param cr: the current row, from the database cursor,
554         @param uid: the current user’s ID for security checks,
555         @param ids: List of calendar attendee’s IDs.
556         @param context: A standard dictionary for contextual values
557         @return: True
558         """
559         if context is None:
560             context = {}
561
562         for vals in self.browse(cr, uid, ids, context=context):
563             if vals.ref and vals.ref.user_id:
564                 mod_obj = self.pool.get(vals.ref._name)
565                 defaults = {'user_id': vals.user_id.id, 'organizer_id': vals.ref.user_id.id}
566                 mod_obj.copy(cr, uid, vals.ref.id, default=defaults, context=context)
567             self.write(cr, uid, vals.id, {'state': 'accepted'}, context)
568
569         return True
570
571     def do_decline(self, cr, uid, ids, context=None, *args):
572         """ Marks event invitation as Declined
573         @param self: The object pointer
574         @param cr: the current row, from the database cursor,
575         @param uid: the current user’s ID for security checks,
576         @param ids: List of calendar attendee’s IDs
577         @param *args: Get Tupple value
578         @param context: A standard dictionary for contextual values """
579         if context is None:
580             context = {}
581         return self.write(cr, uid, ids, {'state': 'declined'}, context)
582
583     def create(self, cr, uid, vals, context=None):
584         """ Overrides orm create method.
585         @param self: The object pointer
586         @param cr: the current row, from the database cursor,
587         @param uid: the current user’s ID for security checks,
588         @param vals: Get Values
589         @param context: A standard dictionary for contextual values """
590
591         if context is None:
592             context = {}
593         if not vals.get("email") and vals.get("cn"):
594             cnval = vals.get("cn").split(':')
595             email = filter(lambda x:x.__contains__('@'), cnval)
596             vals['email'] = email and email[0] or ''
597             vals['cn'] = vals.get("cn")
598         res = super(calendar_attendee, self).create(cr, uid, vals, context)
599         return res
600 calendar_attendee()
601
602 class res_alarm(osv.osv):
603     """Resource Alarm """
604     _name = 'res.alarm'
605     _description = 'Basic Alarm Information'
606
607     _columns = {
608         'name':fields.char('Name', size=256, required=True),
609         'trigger_occurs': fields.selection([('before', 'Before'), \
610                                             ('after', 'After')], \
611                                         'Triggers', required=True),
612         'trigger_interval': fields.selection([('minutes', 'Minutes'), \
613                                                 ('hours', 'Hours'), \
614                                                 ('days', 'Days')], 'Interval', \
615                                                 required=True),
616         'trigger_duration': fields.integer('Duration', required=True),
617         'trigger_related': fields.selection([('start', 'The event starts'), \
618                                             ('end', 'The event ends')], \
619                                             'Related to', required=True),
620         'duration': fields.integer('Duration', help="""Duration' and 'Repeat' \
621 are both optional, but if one occurs, so MUST the other"""),
622         'repeat': fields.integer('Repeat'),
623         'active': fields.boolean('Active', help="If the active field is set to \
624 true, it will allow you to hide the event alarm information without removing it.")
625     }
626     _defaults = {
627         'trigger_interval': 'minutes',
628         'trigger_duration': 5,
629         'trigger_occurs': 'before',
630         'trigger_related': 'start',
631         'active': 1,
632     }
633
634     def do_alarm_create(self, cr, uid, ids, model, date, context=None):
635         """
636         Create Alarm for event.
637         @param cr: the current row, from the database cursor,
638         @param uid: the current user’s ID for security checks,
639         @param ids: List of res alarm’s IDs.
640         @param model: Model name.
641         @param date: Event date
642         @param context: A standard dictionary for contextual values
643         @return: True
644         """
645         if context is None:
646             context = {}
647         alarm_obj = self.pool.get('calendar.alarm')
648         res_alarm_obj = self.pool.get('res.alarm')
649         ir_obj = self.pool.get('ir.model')
650         model_id = ir_obj.search(cr, uid, [('model', '=', model)])[0]
651
652         model_obj = self.pool.get(model)
653         for data in model_obj.browse(cr, uid, ids, context=context):
654
655             basic_alarm = data.alarm_id
656             cal_alarm = data.base_calendar_alarm_id
657             if (not basic_alarm and cal_alarm) or (basic_alarm and cal_alarm):
658                 new_res_alarm = None
659                 # Find for existing res.alarm
660                 duration = cal_alarm.trigger_duration
661                 interval = cal_alarm.trigger_interval
662                 occurs = cal_alarm.trigger_occurs
663                 related = cal_alarm.trigger_related
664                 domain = [('trigger_duration', '=', duration), ('trigger_interval', '=', interval), ('trigger_occurs', '=', occurs), ('trigger_related', '=', related)]
665                 alarm_ids = res_alarm_obj.search(cr, uid, domain, context=context)
666                 if not alarm_ids:
667                     val = {
668                             'trigger_duration': duration,
669                             'trigger_interval': interval,
670                             'trigger_occurs': occurs,
671                             'trigger_related': related,
672                             'name': str(duration) + ' ' + str(interval) + ' '  + str(occurs)
673                            }
674                     new_res_alarm = res_alarm_obj.create(cr, uid, val, context=context)
675                 else:
676                     new_res_alarm = alarm_ids[0]
677                 cr.execute('UPDATE %s ' % model_obj._table + \
678                             ' SET base_calendar_alarm_id=%s, alarm_id=%s ' \
679                             ' WHERE id=%s',
680                             (cal_alarm.id, new_res_alarm, data.id))
681
682             self.do_alarm_unlink(cr, uid, [data.id], model)
683             if basic_alarm:
684                 vals = {
685                     'action': 'display',
686                     'description': data.description,
687                     'name': data.name,
688                     'attendee_ids': [(6, 0, map(lambda x:x.id, data.attendee_ids))],
689                     'trigger_related': basic_alarm.trigger_related,
690                     'trigger_duration': basic_alarm.trigger_duration,
691                     'trigger_occurs': basic_alarm.trigger_occurs,
692                     'trigger_interval': basic_alarm.trigger_interval,
693                     'duration': basic_alarm.duration,
694                     'repeat': basic_alarm.repeat,
695                     'state': 'run',
696                     'event_date': data[date],
697                     'res_id': data.id,
698                     'model_id': model_id,
699                     'user_id': uid
700                  }
701                 alarm_id = alarm_obj.create(cr, uid, vals)
702                 cr.execute('UPDATE %s ' % model_obj._table + \
703                             ' SET base_calendar_alarm_id=%s, alarm_id=%s '
704                             ' WHERE id=%s', \
705                             ( alarm_id, basic_alarm.id, data.id) )
706         return True
707
708     def do_alarm_unlink(self, cr, uid, ids, model, context=None):
709         """
710         Delete alarm specified in ids
711         @param cr: the current row, from the database cursor,
712         @param uid: the current user’s ID for security checks,
713         @param ids: List of res alarm’s IDs.
714         @param model: Model name for which alarm is to be cleared.
715         @return: True
716         """
717         if context is None:
718             context = {}
719         alarm_obj = self.pool.get('calendar.alarm')
720         ir_obj = self.pool.get('ir.model')
721         model_id = ir_obj.search(cr, uid, [('model', '=', model)])[0]
722         model_obj = self.pool.get(model)
723         for datas in model_obj.browse(cr, uid, ids, context=context):
724             alarm_ids = alarm_obj.search(cr, uid, [('model_id', '=', model_id), ('res_id', '=', datas.id)])
725             if alarm_ids:
726                 alarm_obj.unlink(cr, uid, alarm_ids)
727                 cr.execute('Update %s set base_calendar_alarm_id=NULL, alarm_id=NULL\
728                             where id=%%s' % model_obj._table,(datas.id,))
729         return True
730
731 res_alarm()
732
733 class calendar_alarm(osv.osv):
734     _name = 'calendar.alarm'
735     _description = 'Event alarm information'
736     _inherit = 'res.alarm'
737     __attribute__ = {}
738
739     _columns = {
740         'alarm_id': fields.many2one('res.alarm', 'Basic Alarm', ondelete='cascade'),
741         'name': fields.char('Summary', size=124, help="""Contains the text to be \
742                      used as the message subject for email \
743                      or contains the text to be used for display"""),
744         'action': fields.selection([('audio', 'Audio'), ('display', 'Display'), \
745                 ('procedure', 'Procedure'), ('email', 'Email') ], 'Action', \
746                 required=True, help="Defines the action to be invoked when an alarm is triggered"),
747         'description': fields.text('Description', help='Provides a more complete \
748                             description of the calendar component, than that \
749                             provided by the "SUMMARY" property'),
750         'attendee_ids': fields.many2many('calendar.attendee', 'alarm_attendee_rel', \
751                                       'alarm_id', 'attendee_id', 'Attendees', readonly=True),
752         'attach': fields.binary('Attachment', help="""* Points to a sound resource,\
753                      which is rendered when the alarm is triggered for audio,
754                     * File which is intended to be sent as message attachments for email,
755                     * Points to a procedure resource, which is invoked when\
756                       the alarm is triggered for procedure."""),
757         'res_id': fields.integer('Resource ID'),
758         'model_id': fields.many2one('ir.model', 'Model'),
759         'user_id': fields.many2one('res.users', 'Owner'),
760         'event_date': fields.datetime('Event Date'),
761         'event_end_date': fields.datetime('Event End Date'),
762         'trigger_date': fields.datetime('Trigger Date', readonly="True"),
763         'state':fields.selection([
764                     ('draft', 'Draft'),
765                     ('run', 'Run'),
766                     ('stop', 'Stop'),
767                     ('done', 'Done'),
768                 ], 'State', select=True, readonly=True),
769      }
770
771     _defaults = {
772         'action': 'email',
773         'state': 'run',
774      }
775
776     def create(self, cr, uid, vals, context=None):
777         """
778         Overrides orm create method.
779         @param self: The object pointer
780         @param cr: the current row, from the database cursor,
781         @param vals: dictionary of fields value.{‘name_of_the_field’: value, ...}
782         @param context: A standard dictionary for contextual values
783         @return: new record id for calendar_alarm.
784         """
785         if context is None:
786             context = {}
787         event_date = vals.get('event_date', False)
788         if event_date:
789             dtstart = datetime.strptime(vals['event_date'], "%Y-%m-%d %H:%M:%S")
790             if vals['trigger_interval'] == 'days':
791                 delta = timedelta(days=vals['trigger_duration'])
792             if vals['trigger_interval'] == 'hours':
793                 delta = timedelta(hours=vals['trigger_duration'])
794             if vals['trigger_interval'] == 'minutes':
795                 delta = timedelta(minutes=vals['trigger_duration'])
796             trigger_date = dtstart + (vals['trigger_occurs'] == 'after' and delta or -delta)
797             vals['trigger_date'] = trigger_date
798         res = super(calendar_alarm, self).create(cr, uid, vals, context=context)
799         return res
800
801     def do_run_scheduler(self, cr, uid, automatic=False, use_new_cursor=False, \
802                        context=None):
803         """Scheduler for event reminder
804         @param self: The object pointer
805         @param cr: the current row, from the database cursor,
806         @param uid: the current user’s ID for security checks,
807         @param ids: List of calendar alarm’s IDs.
808         @param use_new_cursor: False or the dbname
809         @param context: A standard dictionary for contextual values
810         """
811         return True # XXX FIXME REMOVE THIS AFTER FIXING get_recurrent_dates!!
812         if context is None:
813             context = {}
814         current_datetime = datetime.now()
815         request_obj = self.pool.get('res.request')
816         alarm_ids = self.search(cr, uid, [('state', '!=', 'done')], context=context)
817
818         mail_to = []
819
820         for alarm in self.browse(cr, uid, alarm_ids, context=context):
821             next_trigger_date = None
822             update_vals = {}
823             model_obj = self.pool.get(alarm.model_id.model)
824             res_obj = model_obj.browse(cr, uid, alarm.res_id, context=context)
825             re_dates = []
826
827             if res_obj.rrule:
828                 event_date = datetime.strptime(res_obj.date, '%Y-%m-%d %H:%M:%S')
829                 recurrent_dates = get_recurrent_dates(res_obj.rrule, res_obj.exdate, event_date, res_obj.exrule)
830
831                 trigger_interval = alarm.trigger_interval
832                 if trigger_interval == 'days':
833                     delta = timedelta(days=alarm.trigger_duration)
834                 if trigger_interval == 'hours':
835                     delta = timedelta(hours=alarm.trigger_duration)
836                 if trigger_interval == 'minutes':
837                     delta = timedelta(minutes=alarm.trigger_duration)
838                 delta = alarm.trigger_occurs == 'after' and delta or -delta
839
840                 for rdate in recurrent_dates:
841                     if rdate + delta > current_datetime:
842                         break
843                     if rdate + delta <= current_datetime:
844                         re_dates.append(rdate.strftime("%Y-%m-%d %H:%M:%S"))
845                 rest_dates = recurrent_dates[len(re_dates):]
846                 next_trigger_date = rest_dates and rest_dates[0] or None
847
848             else:
849                 re_dates = [alarm.trigger_date]
850
851             for r_date in re_dates:
852                 ref = alarm.model_id.model + ',' + str(alarm.res_id)
853
854                 # search for alreay sent requests
855                 if request_obj.search(cr, uid, [('trigger_date', '=', r_date), ('ref_doc1', '=', ref)], context=context):
856                     continue
857
858                 if alarm.action == 'display':
859                     value = {
860                        'name': alarm.name,
861                        'act_from': alarm.user_id.id,
862                        'act_to': alarm.user_id.id,
863                        'body': alarm.description,
864                        'trigger_date': r_date,
865                        'ref_doc1': ref
866                     }
867                     request_id = request_obj.create(cr, uid, value)
868                     request_ids = [request_id]
869                     for attendee in res_obj.attendee_ids:
870                         if attendee.user_id:
871                             value['act_to'] = attendee.user_id.id
872                             request_id = request_obj.create(cr, uid, value)
873                             request_ids.append(request_id)
874                     request_obj.request_send(cr, uid, request_ids)
875
876                 if alarm.action == 'email':
877                     sub = '[Openobject Reminder] %s' % (alarm.name)
878                     body = """
879 Event: %s
880 Event Date: %s
881 Description: %s
882
883 From:
884       %s
885
886 ----
887 %s
888
889 """  % (alarm.name, alarm.trigger_date, alarm.description, \
890                         alarm.user_id.name, alarm.user_id.signature)
891                     mail_to = [alarm.user_id.address_id.email]
892                     for att in alarm.attendee_ids:
893                         mail_to.append(att.user_id.address_id.email)
894                     if mail_to:
895                         tools.email_send(
896                             tools.config.get('email_from', False),
897                             mail_to,
898                             sub,
899                             body
900                         )
901             if next_trigger_date:
902                 update_vals.update({'trigger_date': next_trigger_date})
903             else:
904                 update_vals.update({'state': 'done'})
905             self.write(cr, uid, [alarm.id], update_vals)
906         return True
907
908 calendar_alarm()
909
910
911 class calendar_event(osv.osv):
912     _name = "calendar.event"
913     _description = "Calendar Event"
914     __attribute__ = {}
915
916     def _tz_get(self, cr, uid, context=None):
917         return [(x.lower(), x) for x in pytz.all_timezones]
918
919     def onchange_dates(self, cr, uid, ids, start_date, duration=False, end_date=False, allday=False, context=None):
920         """Returns duration and/or end date based on values passed
921         @param self: The object pointer
922         @param cr: the current row, from the database cursor,
923         @param uid: the current user’s ID for security checks,
924         @param ids: List of calendar event’s IDs.
925         @param start_date: Starting date
926         @param duration: Duration between start date and end date
927         @param end_date: Ending Datee
928         @param context: A standard dictionary for contextual values
929         """
930         if context is None:
931             context = {}
932
933         value = {}
934         if not start_date:
935             return value
936         if not end_date and not duration:
937             duration = 1.00
938             value['duration'] = duration
939
940         if allday: # For all day event
941             value = {'duration': 24}
942             duration = 24.0
943             if start_date:
944                 start = datetime.strptime(start_date, "%Y-%m-%d %H:%M:%S")
945                 start_date = datetime.strftime(datetime(start.year, start.month, start.day, 0,0,0), "%Y-%m-%d %H:%M:%S")
946                 value['date'] = start_date
947                 print start_date
948
949
950         start = datetime.strptime(start_date, "%Y-%m-%d %H:%M:%S")
951         if end_date and not duration:
952             end = datetime.strptime(end_date, "%Y-%m-%d %H:%M:%S")
953             diff = end - start
954             duration = float(diff.days)* 24 + (float(diff.seconds) / 3600)
955             value['duration'] = round(duration, 2)
956         elif not end_date:
957             end = start + timedelta(hours=duration)
958             value['date_deadline'] = end.strftime("%Y-%m-%d %H:%M:%S")
959         elif end_date and duration and not allday:
960             # we have both, keep them synchronized:
961             # set duration based on end_date (arbitrary decision: this avoid
962             # getting dates like 06:31:48 instead of 06:32:00)
963             end = datetime.strptime(end_date, "%Y-%m-%d %H:%M:%S")
964             diff = end - start
965             duration = float(diff.days)* 24 + (float(diff.seconds) / 3600)
966             value['duration'] = round(duration, 2)
967
968         return {'value': value}
969
970     def unlink_events(self, cr, uid, ids, context=None):
971         """
972         This function deletes event which are linked with the event with recurrent_uid
973                 (Removes the events which refers to the same UID value)
974         """
975         if context is None:
976             context = {}
977         for event_id in ids:
978             cr.execute("select id from %s where recurrent_uid=%%s" % (self._table), (event_id,))
979             r_ids = map(lambda x: x[0], cr.fetchall())
980             self.unlink(cr, uid, r_ids, context=context)
981         return True
982
983     def _set_rrulestring(self, cr, uid, id, name, value, arg, context=None):
984         """
985         Sets values of fields that defines event recurrence from the value of rrule string
986         @param self: The object pointer
987         @param cr: the current row, from the database cursor,
988         @param id: List of calendar event's ids.
989         @param context: A standard dictionary for contextual values
990         @return: dictionary of rrule value.
991         """
992         if context is None:
993             context = {}
994         cr.execute("UPDATE %s set freq='None',interval=0,count=0,end_date=Null,\
995                     mo=False,tu=False,we=False,th=False,fr=False,sa=False,su=False,\
996                     day=0,select1='date',month_list=Null ,byday=Null where id=%%s" % (self._table), (id,))
997
998         if not value:
999             cr.execute("UPDATE %s set rrule_type='none' where id=%%s" % self._table,(id,))
1000             return True
1001         val = {}
1002         for part in value.split(';'):
1003             if part.lower().__contains__('freq') and len(value.split(';')) <=2:
1004                 rrule_type = part.lower()[5:]
1005                 break
1006             else:
1007                 rrule_type = 'custom'
1008                 break
1009         ans = value.split(';')
1010         for i in ans:
1011             val[i.split('=')[0].lower()] = i.split('=')[1].lower()
1012         if not val.get('interval'):
1013             rrule_type = 'custom'
1014         elif int(val.get('interval')) > 1: #If interval is other than 1 rule is custom
1015             rrule_type = 'custom'
1016
1017         qry = "UPDATE \"%s\" set rrule_type=%%s " % self._table
1018         qry_args = [ rrule_type, ]
1019         new_val = val.copy()
1020         for k, v in val.items():
1021             if  val['freq'] == 'weekly' and val.get('byday'):
1022                 for day in val['byday'].split(','):
1023                     new_val[day] = True
1024                 val.pop('byday')
1025
1026             if val.get('until'):
1027                 until = parser.parse(''.join((re.compile('\d')).findall(val.get('until'))))
1028                 new_val['end_date'] = until.strftime('%Y-%m-%d')
1029                 val.pop('until')
1030                 new_val.pop('until')
1031
1032             if val.get('bymonthday'):
1033                 new_val['day'] = val.get('bymonthday')
1034                 val.pop('bymonthday')
1035                 new_val['select1'] = 'date'
1036                 new_val.pop('bymonthday')
1037
1038             if val.get('byday'):
1039                 d = val.get('byday')
1040                 if '-' in d:
1041                     new_val['byday'] = d[:2]
1042                     new_val['week_list'] = d[2:4].upper()
1043                 else:
1044                     new_val['byday'] = d[:1]
1045                     new_val['week_list'] = d[1:3].upper()
1046                 new_val['select1'] = 'day'
1047
1048             if val.get('bymonth'):
1049                 new_val['month_list'] = val.get('bymonth')
1050                 val.pop('bymonth')
1051                 new_val.pop('bymonth')
1052
1053         for k, v in new_val.items():
1054             qry += ", %s=%%s" % k
1055             qry_args.append(v)
1056
1057         qry = qry + " where id=%s"
1058         qry_args.append(id)
1059         cr.execute(qry, qry_args)
1060         return True
1061
1062     def _get_rulestring(self, cr, uid, ids, name, arg, context=None):
1063         """
1064         Gets Recurrence rule string according to value type RECUR of iCalendar from the values given.
1065         @param self: The object pointer
1066         @param cr: the current row, from the database cursor,
1067         @param id: List of calendar event's ids.
1068         @param context: A standard dictionary for contextual values
1069         @return: dictionary of rrule value.
1070         """
1071         
1072         result = {}
1073         for datas in self.read(cr, uid, ids, context=context):
1074             event = datas['id']
1075             if datas.get('rrule_type'):
1076                 if datas.get('rrule_type') == 'none':
1077                     result[event] = False
1078                     cr.execute("UPDATE %s set exrule=Null where id=%%s" % self._table,( event,))
1079                 if datas.get('rrule_type') :
1080                     if datas.get('interval', 0) < 0:
1081                         raise osv.except_osv(_('Warning!'), _('Interval can not be Negative'))
1082                     if datas.get('count', 0) < 0:
1083                         raise osv.except_osv(_('Warning!'), _('Count can not be Negative'))
1084                     rrule_custom = self.compute_rule_string(cr, uid, datas, \
1085                                                          context=context)
1086                     result[event] = rrule_custom
1087                 else:
1088                     result[event] = self.compute_rule_string(cr, uid, {'freq': datas.get('rrule_type').upper(), 'interval': 1}, context=context)
1089
1090         for id, myrule in result.items():
1091             #Remove the events generated from recurrent event
1092             if not myrule:
1093                 self.unlink_events(cr, uid, [id], context=context)
1094         print 'rrule creation', result
1095         return result
1096
1097     _columns = {
1098         'id': fields.integer('ID'),
1099         'sequence': fields.integer('Sequence'),
1100         'name': fields.char('Description', size=64, required=False, states={'done': [('readonly', True)]}),
1101         'date': fields.datetime('Date', states={'done': [('readonly', True)]}),
1102         'date_deadline': fields.datetime('Deadline', states={'done': [('readonly', True)]}),
1103         'create_date': fields.datetime('Created', readonly=True),
1104         'duration': fields.float('Duration', states={'done': [('readonly', True)]}),
1105         'description': fields.text('Description', states={'done': [('readonly', True)]}),
1106         'class': fields.selection([('public', 'Public'), ('private', 'Private'), \
1107              ('confidential', 'Confidential')], 'Mark as', states={'done': [('readonly', True)]}),
1108         'location': fields.char('Location', size=264, help="Location of Event", states={'done': [('readonly', True)]}),
1109         'show_as': fields.selection([('free', 'Free'), ('busy', 'Busy')], \
1110                                                 'Show as', states={'done': [('readonly', True)]}),
1111         'base_calendar_url': fields.char('Caldav URL', size=264),
1112         'state': fields.selection([('tentative', 'Tentative'),
1113                         ('confirmed', 'Confirmed'),
1114                         ('cancelled', 'Cancelled')], 'State', readonly=True),
1115         'exdate': fields.text('Exception Date/Times', help="This property \
1116 defines the list of date/time exceptions for a recurring calendar component."),
1117         'exrule': fields.char('Exception Rule', size=352, help="Defines a \
1118 rule or repeating pattern of time to exclude from the recurring rule."),
1119         'rrule': fields.function(_get_rulestring, type='char', size=124, method=True, \
1120                                     string='Recurrent Rule', store=True, \
1121                                     fnct_inv=_set_rrulestring, help='Defines a\
1122  rule or repeating pattern for recurring events\n\
1123 e.g.: Every other month on the last Sunday of the month for 10 occurrences:\
1124         FREQ=MONTHLY;INTERVAL=2;COUNT=10;BYDAY=-1SU'),
1125         'rrule_type': fields.selection([('none', ''), ('daily', 'Daily'), \
1126                             ('weekly', 'Weekly'), ('monthly', 'Monthly'), \
1127                             ('yearly', 'Yearly'),],
1128                             'Recurrency', states={'done': [('readonly', True)]},
1129                             help="Let the event automatically repeat at that interval"),
1130         'alarm_id': fields.many2one('res.alarm', 'Alarm', states={'done': [('readonly', True)]},
1131                         help="Set an alarm at this time, before the event occurs" ),
1132         'base_calendar_alarm_id': fields.many2one('calendar.alarm', 'Alarm'),
1133         'recurrent_uid': fields.integer('Recurrent ID'),
1134         'recurrent_id': fields.datetime('Recurrent ID date'),
1135         'vtimezone': fields.selection(_tz_get, size=64, string='Timezone'),
1136         'user_id': fields.many2one('res.users', 'Responsible', states={'done': [('readonly', True)]}),
1137         'organizer': fields.char("Organizer", size=256, states={'done': [('readonly', True)]}), # Map with Organizer Attribure of VEvent.
1138         'organizer_id': fields.many2one('res.users', 'Organizer', states={'done': [('readonly', True)]}),
1139         'freq': fields.selection([('None', 'No Repeat'),
1140                                 ('hourly', 'Hours'),
1141                                 ('daily', 'Days'),
1142                                 ('weekly', 'Weeks'),
1143                                 ('monthly', 'Months'),
1144                                 ('yearly', 'Years'), ], 'Frequency'),
1145
1146         'end_type' : fields.selection([('forever', 'Forever'), ('count', 'Fix amout of times'), ('end_date','End date')], 'Way to end reccurency'),
1147         'interval': fields.integer('Repeat every', help="Repeat every (Days/Week/Month/Year)"),
1148         'count': fields.integer('Repeat', help="Repeat x times"),
1149         'mo': fields.boolean('Mon'),
1150         'tu': fields.boolean('Tue'),
1151         'we': fields.boolean('Wed'),
1152         'th': fields.boolean('Thu'),
1153         'fr': fields.boolean('Fri'),
1154         'sa': fields.boolean('Sat'),
1155         'su': fields.boolean('Sun'),
1156         'select1': fields.selection([('date', 'Date of month'),
1157                                     ('day', 'Day of month')], 'Option'),
1158         'day': fields.integer('Date of month'),
1159         'week_list': fields.selection([('MO', 'Monday'), ('TU', 'Tuesday'), \
1160                                    ('WE', 'Wednesday'), ('TH', 'Thursday'), \
1161                                    ('FR', 'Friday'), ('SA', 'Saturday'), \
1162                                    ('SU', 'Sunday')], 'Weekday'),
1163         'byday': fields.selection([('1', 'First'), ('2', 'Second'), \
1164                                    ('3', 'Third'), ('4', 'Fourth'), \
1165                                    ('5', 'Fifth'), ('-1', 'Last')], 'By day'),
1166         'month_list': fields.selection(months.items(), 'Month'),
1167         'end_date': fields.date('Repeat Until'),
1168         'attendee_ids': fields.many2many('calendar.attendee', 'event_attendee_rel', \
1169                                  'event_id', 'attendee_id', 'Attendees'),
1170         'allday': fields.boolean('All Day', states={'done': [('readonly', True)]}),
1171         'active': fields.boolean('Active', help="If the active field is set to \
1172          true, it will allow you to hide the event alarm information without removing it."),
1173         'recurrency': fields.boolean('Recurrent', help="Recurrent Meeting"),
1174         'edit_all': fields.boolean('Edit All', help="Edit all Occurrences  of recurrent Meeting."),
1175     }
1176     def default_organizer(self, cr, uid, context=None):
1177         user_pool = self.pool.get('res.users')
1178         user = user_pool.browse(cr, uid, uid, context=context)
1179         res = user.name
1180         if user.user_email:
1181             res += " <%s>" %(user.user_email)
1182         return res
1183
1184     _defaults = {
1185             'end_type' : 'forever',
1186             'state': 'tentative',
1187             'class': 'public',
1188             'show_as': 'busy',
1189             'freq': 'None',
1190             'select1': 'date',
1191             'interval': 1,
1192             'active': 1,
1193             'user_id': lambda self, cr, uid, ctx: uid,
1194             'organizer': default_organizer,
1195             'edit_all' : False,
1196     }
1197
1198     def onchange_edit_all(self, cr, uid, ids, rrule_type,edit_all, context=None):
1199         if not context:
1200             context = {}
1201
1202         value = {}
1203         if edit_all and rrule_type:
1204             for id in ids:
1205               base_calendar_id2real_id(id)
1206         return value
1207
1208     def modify_all(self, cr, uid, event_ids, defaults, context=None, *args):
1209         """
1210         Modifies the recurring event
1211         @param cr: the current row, from the database cursor,
1212         @param uid: the current user’s ID for security checks,
1213         @param event_ids: List of crm meeting’s IDs.
1214         @param context: A standard dictionary for contextual values
1215         @return: True
1216         """
1217         for event_id in event_ids:
1218             event_id = base_calendar_id2real_id(event_id)
1219
1220             defaults.pop('id')
1221             defaults.update({'table': self._table})
1222
1223             qry = "UPDATE %(table)s set name = '%(name)s', \
1224                             date = '%(date)s', date_deadline = '%(date_deadline)s'"
1225             if defaults.get('alarm_id'):
1226                 qry += ", alarm_id = %(alarm_id)s"
1227             if defaults.get('location'):
1228                 qry += ", location = '%(location)s'"
1229             qry += "WHERE id = %s" % (event_id)
1230             cr.execute(qry, defaults)
1231
1232         return True
1233
1234     def get_recurrent_ids(self, cr, uid, select, base_start_date, base_until_date, limit=100, context=None):
1235         """Gives virtual event ids for recurring events based on value of Recurrence Rule
1236         This method gives ids of dates that comes between start date and end date of calendar views
1237         @param self: The object pointer
1238         @param cr: the current row, from the database cursor,
1239         @param uid: the current user’s ID for security checks,
1240         @param base_start_date: Get Start Date
1241         @param base_until_date: Get End Date
1242         @param limit: The Number of Results to Return """
1243         if not context:
1244             context = {}
1245
1246         limit = limit == 0 and 100 or limit
1247         virtual_id = context and context.get('virtual_id', False) or False
1248         print 'context', context
1249
1250         if isinstance(select, (str, int, long)):
1251             ids = [select]
1252         else:
1253             ids = select
1254         result = []
1255         recur_dict = []
1256         if ids and virtual_id:
1257             cr.execute("select m.id, m.rrule, m.date, m.date_deadline, m.duration, \
1258                             m.exdate, m.exrule, m.recurrent_id, m.recurrent_uid from " + self._table + \
1259                             " m where m.id = ANY(%s)", (ids,) )
1260
1261             print "ids", ids
1262             count = 0
1263             for data in cr.dictfetchall():
1264                 print 'data', data
1265                 print 'count', count
1266                 print 'limit', limit
1267                 start_date = base_start_date and datetime.strptime(base_start_date[:10]+ ' 00:00:00' , "%Y-%m-%d %H:%M:%S") or False
1268                 until_date = base_until_date and datetime.strptime(base_until_date[:10]+ ' 23:59:59', "%Y-%m-%d %H:%M:%S") or False
1269                 print count
1270                 if count > limit:
1271                     break
1272                 event_date = datetime.strptime(data['date'], "%Y-%m-%d %H:%M:%S")
1273 #                To check: If the start date is replace by event date .. the event date will be changed by that of calendar code
1274                 start_date = event_date
1275                 if not data['rrule']:
1276                     if start_date and (event_date < start_date):
1277                         continue
1278                     if until_date and (event_date > until_date):
1279                         continue
1280                     idval = data['id']
1281                     if not data['recurrent_id']:
1282                         result.append(idval)
1283                         count += 1
1284                     else:
1285                         ex_id = real_id2base_calendar_id(data['recurrent_uid'], data['recurrent_id'])
1286                         ls = base_calendar_id2real_id(ex_id, with_date=data and data.get('duration', 0) or 0)
1287                         if not isinstance(ls, (str, int, long)) and len(ls) >= 2:
1288                             if ls[1] == data['recurrent_id']:
1289                                 result.append(idval)
1290                         recur_dict.append(ex_id)
1291                 else:
1292                     print 'rrule', data['rrule'], data['id']
1293                     exdate = data['exdate'] and data['exdate'].split(',') or []
1294                     rrule_str = data['rrule']
1295                     new_rrule_str = []
1296                     rrule_until_date = False
1297                     is_until = False
1298                     for rule in rrule_str.split(';'):
1299                         name, value = rule.split('=')
1300                         if name == "UNTIL":
1301                             is_until = True
1302                             value = parser.parse(value)
1303                             rrule_until_date = parser.parse(value.strftime("%Y-%m-%d"))
1304                             if until_date and until_date >= rrule_until_date:
1305                                 until_date = rrule_until_date
1306                             if until_date:
1307                                 value = until_date.strftime("%Y%m%d%H%M%S")
1308                         new_rule = '%s=%s' % (name, value)
1309                         new_rrule_str.append(new_rule)
1310                     if not is_until and until_date:
1311                         value = until_date.strftime("%Y%m%d%H%M%S")
1312                         name = "UNTIL"
1313                         new_rule = '%s=%s' % (name, value)
1314                         new_rrule_str.append(new_rule)
1315                     new_rrule_str = ';'.join(new_rrule_str)
1316                     rdates = get_recurrent_dates(str(new_rrule_str), exdate, start_date, data['exrule'])
1317                     for r_date in rdates:
1318                         if start_date and r_date < start_date:
1319                             continue
1320                         if until_date and r_date > until_date:
1321                             continue
1322                         idval = real_id2base_calendar_id(data['id'], r_date.strftime("%Y-%m-%d %H:%M:%S"))
1323                         result.append(idval)
1324                         print "count", count
1325                         count += 1
1326                         if count > limit:
1327                             break
1328
1329         print "result", result
1330         if result:
1331             ids = list(set(result)-set(recur_dict))
1332         if isinstance(select, (str, int, long)):
1333             return ids and ids[0] or False
1334         print "return ids", ids
1335         return ids
1336
1337     def compute_rule_string(self, cr, uid, datas, context=None, *args):
1338         """
1339         Compute rule string according to value type RECUR of iCalendar from the values given.
1340         @param self: the object pointer
1341         @param cr: the current row, from the database cursor,
1342         @param uid: the current user’s ID for security checks,
1343         @param datas: dictionary of freq and interval value.
1344         @param context: A standard dictionary for contextual values
1345         @return: String value of the format RECUR of iCalendar
1346         """
1347
1348         weekdays = ['mo', 'tu', 'we', 'th', 'fr', 'sa', 'su']
1349         weekstring = ''
1350         monthstring = ''
1351         yearstring = ''
1352         freq=datas.get('rrule_type')
1353         if  freq == 'none':
1354             return ''
1355
1356         interval_srting = datas.get('interval') and (';INTERVAL=' + str(datas.get('interval'))) or ''
1357
1358         if freq == 'weekly':
1359             byday = map(lambda x: x.upper(), filter(lambda x: datas.get(x) and x in weekdays, datas))
1360             if byday:
1361                 weekstring = ';BYDAY=' + ','.join(byday)
1362
1363         elif freq == 'monthly':
1364             if datas.get('select1')=='date' and (datas.get('day') < 1 or datas.get('day') > 31):
1365                 raise osv.except_osv(_('Error!'), ("Please select proper Day of month"))
1366             if datas.get('select1')=='day':
1367                 monthstring = ';BYDAY=' + datas.get('byday') + datas.get('week_list')
1368             elif datas.get('select1')=='date':
1369                 monthstring = ';BYMONTHDAY=' + str(datas.get('day'))
1370
1371
1372         if datas.get('end_date'):
1373             datas['end_date'] = ''.join((re.compile('\d')).findall(datas.get('end_date'))) + 'T235959Z'
1374         enddate = (datas.get('count') and (';COUNT=' + str(datas.get('count'))) or '') +\
1375                              ((datas.get('end_date') and (';UNTIL=' + datas.get('end_date'))) or '')
1376
1377         rrule_string = 'FREQ=' + freq.upper() + weekstring + interval_srting \
1378                             + enddate + monthstring + yearstring
1379
1380         return rrule_string
1381
1382     def search(self, cr, uid, args, offset=0, limit=100, order=None,
1383             context=None, count=False):
1384
1385         args_without_date = []
1386         start_date = False
1387         until_date = False
1388
1389         for arg in args:
1390             if arg[0] not in ('date', unicode('date'), 'date_deadline', unicode('date_deadline')):
1391                 args_without_date.append(arg)
1392             else:
1393                 if arg[1] in ('>', '>='):
1394                     if start_date:
1395                         continue
1396                     start_date = arg[2]
1397                 elif arg[1] in ('<', '<='):
1398                     if until_date:
1399                         continue
1400                     until_date = arg[2]
1401         res = super(calendar_event, self).search(cr, uid, args_without_date, \
1402                                  offset, limit, order, context, count=False)
1403         print "result normaux", res
1404         res = self.get_recurrent_ids(cr, uid, res, start_date, until_date, limit, context=context)
1405         print "result de merde", res
1406         print 'len', len(res)
1407         return count and len(res) or res
1408
1409
1410     def get_edit_all(self, cr, uid, id, vals=None):
1411         """
1412             return true if we have to edit all meeting from the same recurrent
1413             or only on occurency
1414         """
1415         meeting = self.read(cr,uid, id, ['edit_all', 'recurrency'] )
1416         if(vals and 'edit_all' in vals): #we jsut check edit_all
1417             return vals['edit_all']
1418         else: #it's a recurrent event and edit_all is already check
1419             return meeting['recurrency'] and meeting['edit_all']
1420
1421
1422
1423
1424     def write(self, cr, uid, ids, vals, context=None, check=True, update_check=True):
1425         """
1426         Overrides orm write method.
1427         @param self: the object pointer
1428         @param cr: the current row, from the database cursor,
1429         @param uid: the current user’s ID for security checks,
1430         @param ids: List of crm meeting's ids
1431         @param vals: Dictionary of field value.
1432         @param context: A standard dictionary for contextual values
1433         @return: True
1434         """
1435         if context is None:
1436             context = {}
1437         if isinstance(ids, (str, int, long)):
1438             select = [ids]
1439         else:
1440             select = ids
1441         new_ids = []
1442         res = False
1443         for event_id in select:
1444             real_event_id = base_calendar_id2real_id(event_id)
1445
1446
1447             if(self.get_edit_all(cr, uid, event_id, vals=vals)):
1448                 event_id = real_event_id
1449
1450
1451             if len(str(event_id).split('-')) > 1:
1452                 data = self.read(cr, uid, event_id, ['date', 'date_deadline', \
1453                                                     'rrule', 'duration', 'exdate'])
1454                 if data.get('rrule'):
1455                     data.update(vals)
1456                     data.update({
1457                         'recurrent_uid': real_event_id,
1458                         'recurrent_id': data.get('date'),
1459                         'rrule_type': 'none',
1460                         'rrule': '',
1461                         'edit_all': False,
1462                         'recurrency' : False,
1463                         })
1464
1465                     new_id = self.copy(cr, uid, real_event_id, default=data, context=context)
1466
1467                     date_new = event_id.split('-')[1]
1468                     date_new = time.strftime("%Y%m%dT%H%M%S", \
1469                                  time.strptime(date_new, "%Y%m%d%H%M%S"))
1470                     exdate = (data['exdate'] and (data['exdate'] + ',')  or '') + date_new
1471                     res = self.write(cr, uid, [real_event_id], {'exdate': exdate})
1472
1473                     context.update({'active_id': new_id, 'active_ids': [new_id]})
1474                     continue
1475             if not real_event_id in new_ids:
1476                 new_ids.append(real_event_id)
1477
1478         if vals.get('vtimezone', '') and vals.get('vtimezone', '').startswith('/freeassociation.sourceforge.net/tzfile/'):
1479             vals['vtimezone'] = vals['vtimezone'][40:]
1480
1481         updated_vals = self.onchange_dates(cr, uid, new_ids,
1482             vals.get('date', False),
1483             vals.get('duration', False),
1484             vals.get('date_deadline', False),
1485             vals.get('allday', False),
1486             context=context)
1487         vals.update(updated_vals.get('value', {}))
1488
1489         if not 'edit_all' in vals:
1490             vals['edit_all'] = False
1491
1492         if new_ids:
1493             res = super(calendar_event, self).write(cr, uid, new_ids, vals, context=context)
1494
1495         if ('alarm_id' in vals or 'base_calendar_alarm_id' in vals)\
1496                 or ('date' in vals or 'duration' in vals or 'date_deadline' in vals):
1497             # change alarm details
1498             alarm_obj = self.pool.get('res.alarm')
1499             alarm_obj.do_alarm_create(cr, uid, new_ids, self._name, 'date', context=context)
1500         return res
1501
1502     def browse(self, cr, uid, ids, context=None, list_class=None, fields_process=None):
1503         """
1504         Overrides orm browse method.
1505         @param self: the object pointer
1506         @param cr: the current row, from the database cursor,
1507         @param uid: the current user’s ID for security checks,
1508         @param ids: List of crm meeting's ids
1509         @param context: A standard dictionary for contextual values
1510         @return: the object list.
1511         """
1512         if isinstance(ids, (str, int, long)):
1513             select = [ids]
1514         else:
1515             select = ids
1516         select = map(lambda x: base_calendar_id2real_id(x), select)
1517         res = super(calendar_event, self).browse(cr, uid, select, context, \
1518                                                     list_class, fields_process)
1519         if isinstance(ids, (str, int, long)):
1520             return res and res[0] or False
1521
1522         return res
1523
1524     def read(self, cr, uid, ids, fields=None, context=None, load='_classic_read'):
1525         """
1526         Overrides orm Read method.Read List of fields for calendar event.
1527         @param cr: the current row, from the database cursor,
1528         @param user: the current user’s ID for security checks,
1529         @param ids: List of calendar event's id.
1530         @param fields: List of fields.
1531         @param context: A standard dictionary for contextual values
1532         @return: List of Dictionary of form [{‘name_of_the_field’: value, ...}, ...]
1533         """
1534         # FIXME This whole id mangling has to go!
1535         if context is None:
1536             context = {}
1537         
1538         print 'read ids', ids
1539
1540
1541         if isinstance(ids, (str, int, long)):
1542             select = [ids]
1543         else:
1544             select = ids
1545         select = map(lambda x: (x, base_calendar_id2real_id(x)), select)
1546         print 'selected', select
1547         result = []
1548         if fields and 'date' not in fields:
1549             fields.append('date')
1550         if fields and 'duration' not in fields:
1551             fields.append('duration')
1552
1553
1554         for base_calendar_id, real_id in select:
1555             #REVET: Revision ID: olt@tinyerp.com-20100924131709-cqsd1ut234ni6txn
1556             res = super(calendar_event, self).read(cr, uid, real_id, fields=fields, context=context, load=load)
1557            
1558             if not res :
1559                 continue
1560             ls = base_calendar_id2real_id(base_calendar_id, with_date=res and res.get('duration', 0) or 0)
1561             if not isinstance(ls, (str, int, long)) and len(ls) >= 2:
1562                 res['date'] = ls[1]
1563                 res['date_deadline'] = ls[2]
1564             res['id'] = base_calendar_id
1565
1566             result.append(res)
1567         if isinstance(ids, (str, int, long)):
1568             return result and result[0] or False
1569         
1570         print 'resultat', result 
1571         return result
1572
1573     def copy(self, cr, uid, id, default=None, context=None):
1574         """
1575         Duplicate record on specified id.
1576         @param self: the object pointer.
1577         @param cr: the current row, from the database cursor,
1578         @param id: id of record from which we duplicated.
1579         @param context: A standard dictionary for contextual values
1580         @return: Duplicate record id.
1581         """
1582         if context is None:
1583             context = {}
1584         res = super(calendar_event, self).copy(cr, uid, base_calendar_id2real_id(id), default, context)
1585         alarm_obj = self.pool.get('res.alarm')
1586         alarm_obj.do_alarm_create(cr, uid, [res], self._name, 'date', context=context)
1587
1588         return res
1589
1590     def unlink(self, cr, uid, ids, context=None):
1591         """
1592         Deletes records specified in ids.
1593         @param self: the object pointer.
1594         @param cr: the current row, from the database cursor,
1595         @param id: List of calendar event's id.
1596         @param context: A standard dictionary for contextual values
1597         @return: True
1598         """
1599         res = False
1600         for event_datas in self.read(cr, uid, ids, ['date', 'rrule', 'exdate'], context=context):
1601             event_id = event_datas['id']
1602
1603             if self.get_edit_all(cr, uid, event_id, vals=None):
1604                 event_id = base_calendar_id2real_id(event_id)
1605
1606             if isinstance(event_id, (int, long)):
1607                 res = super(calendar_event, self).unlink(cr, uid, event_id, context=context)
1608                 self.pool.get('res.alarm').do_alarm_unlink(cr, uid, [event_id], self._name)
1609                 self.unlink_events(cr, uid, [event_id], context=context)
1610             else:
1611                 str_event, date_new = event_id.split('-')
1612                 event_id = int(str_event)
1613                 if event_datas['rrule']:
1614                     # Remove one of the recurrent event
1615                     date_new = time.strftime("%Y%m%dT%H%M%S", \
1616                                  time.strptime(date_new, "%Y%m%d%H%M%S"))
1617                     exdate = (event_datas['exdate'] and (event_datas['exdate'] + ',')  or '') + date_new
1618                     res = self.write(cr, uid, [event_id], {'exdate': exdate})
1619                 else:
1620                     res = super(calendar_event, self).unlink(cr, uid, [event_id], context=context)
1621                     self.pool.get('res.alarm').do_alarm_unlink(cr, uid, [event_id], self._name)
1622                     self.unlink_events(cr, uid, [event_id], context=context)
1623         return res
1624
1625     def create(self, cr, uid, vals, context=None):
1626         """
1627         Create new record.
1628         @param self: the object pointer
1629         @param cr: the current row, from the database cursor,
1630         @param uid: the current user’s ID for security checks,
1631         @param vals: dictionary of every field value.
1632         @param context: A standard dictionary for contextual values
1633         @return: new created record id.
1634         """
1635         if context is None:
1636             context = {}
1637
1638         virtual_id = context.get('virtual_id', False)
1639
1640         if vals.get('vtimezone', '') and vals.get('vtimezone', '').startswith('/freeassociation.sourceforge.net/tzfile/'):
1641             vals['vtimezone'] = vals['vtimezone'][40:]
1642
1643         updated_vals = self.onchange_dates(cr, uid, [],
1644             vals.get('date', False),
1645             vals.get('duration', False),
1646             vals.get('date_deadline', False),
1647             vals.get('allday', False),
1648             context=context)
1649         vals.update(updated_vals.get('value', {}))
1650
1651         res = super(calendar_event, self).create(cr, uid, vals, context)
1652         alarm_obj = self.pool.get('res.alarm')
1653         alarm_obj.do_alarm_create(cr, uid, [res], self._name, 'date', context=context)
1654
1655
1656
1657         if vals.get('rrule_type') != 'none' and virtual_id:
1658             res = real_id2base_calendar_id(res, vals.get('date', False))
1659         print "id", res
1660         return res
1661
1662     def do_tentative(self, cr, uid, ids, context=None, *args):
1663         """ Makes event invitation as Tentative
1664         @param self: The object pointer
1665         @param cr: the current row, from the database cursor,
1666         @param uid: the current user’s ID for security checks,
1667         @param ids: List of Event IDs
1668         @param *args: Get Tupple value
1669         @param context: A standard dictionary for contextual values
1670         """
1671         return self.write(cr, uid, ids, {'state': 'tentative'}, context)
1672
1673     def do_cancel(self, cr, uid, ids, context=None, *args):
1674         """ Makes event invitation as Tentative
1675         @param self: The object pointer
1676         @param cr: the current row, from the database cursor,
1677         @param uid: the current user’s ID for security checks,
1678         @param ids: List of Event IDs
1679         @param *args: Get Tupple value
1680         @param context: A standard dictionary for contextual values
1681         """
1682         return self.write(cr, uid, ids, {'state': 'cancelled'}, context)
1683
1684     def do_confirm(self, cr, uid, ids, context=None, *args):
1685         """ Makes event invitation as Tentative
1686         @param self: The object pointer
1687         @param cr: the current row, from the database cursor,
1688         @param uid: the current user’s ID for security checks,
1689         @param ids: List of Event IDs
1690         @param *args: Get Tupple value
1691         @param context: A standard dictionary for contextual values
1692         """
1693         return self.write(cr, uid, ids, {'state': 'confirmed'}, context)
1694
1695 calendar_event()
1696
1697 class calendar_todo(osv.osv):
1698     """ Calendar Task """
1699
1700     _name = "calendar.todo"
1701     _inherit = "calendar.event"
1702     _description = "Calendar Task"
1703
1704     def _get_date(self, cr, uid, ids, name, arg, context=None):
1705         """
1706         Get Date
1707         @param self: The object pointer
1708         @param cr: the current row, from the database cursor,
1709         @param uid: the current user’s ID for security checks,
1710         @param ids: List of calendar todo's IDs.
1711         @param args: list of tuples of form [(‘name_of_the_field’, ‘operator’, value), ...].
1712         @param context: A standard dictionary for contextual values
1713         """
1714
1715         res = {}
1716         for event in self.browse(cr, uid, ids, context=context):
1717             res[event.id] = event.date_start
1718         return res
1719
1720     def _set_date(self, cr, uid, id, name, value, arg, context=None):
1721         """
1722         Set Date
1723         @param self: The object pointer
1724         @param cr: the current row, from the database cursor,
1725         @param uid: the current user’s ID for security checks,
1726         @param id: calendar's ID.
1727         @param value: Get Value
1728         @param args: list of tuples of form [(‘name_of_the_field’, ‘operator’, value), ...].
1729         @param context: A standard dictionary for contextual values
1730         """
1731
1732         assert name == 'date'
1733         return self.write(cr, uid, id, { 'date_start': value }, context=context)
1734
1735     _columns = {
1736         'date': fields.function(_get_date, method=True, fnct_inv=_set_date, \
1737                             string='Duration', store=True, type='datetime'),
1738         'duration': fields.integer('Duration'),
1739     }
1740
1741     __attribute__ = {}
1742
1743
1744 calendar_todo()
1745
1746 class ir_attachment(osv.osv):
1747     _name = 'ir.attachment'
1748     _inherit = 'ir.attachment'
1749
1750     def search_count(self, cr, user, args, context=None):
1751         """
1752         @param self: The object pointer
1753         @param cr: the current row, from the database cursor,
1754         @param user: the current user’s ID for security checks,
1755         @param args: list of tuples of form [(‘name_of_the_field’, ‘operator’, value), ...].
1756         @param context: A standard dictionary for contextual values
1757         """
1758
1759         args1 = []
1760         for arg in args:
1761             args1.append(map(lambda x:str(x).split('-')[0], arg))
1762         return super(ir_attachment, self).search_count(cr, user, args1, context)
1763
1764
1765
1766     def create(self, cr, uid, vals, context=None):
1767         if context:
1768             id = context.get('default_res_id', False)
1769             context.update({'default_res_id' : base_calendar_id2real_id(id)})
1770         return super(ir_attachment, self).create(cr, uid, vals, context=context)
1771
1772     def search(self, cr, uid, args, offset=0, limit=None, order=None,
1773             context=None, count=False):
1774         """
1775         @param self: The object pointer
1776         @param cr: the current row, from the database cursor,
1777         @param uid: the current user’s ID for security checks,
1778         @param args: list of tuples of form [(‘name_of_the_field’, ‘operator’, value), ...].
1779         @param offset: The Number of Results to pass,
1780         @param limit: The Number of Results to Return,
1781         @param context: A standard dictionary for contextual values
1782         """
1783
1784         new_args = args
1785         for i, arg in enumerate(new_args):
1786             if arg[0] == 'res_id':
1787                 new_args[i] = (arg[0], arg[1], base_calendar_id2real_id(arg[2]))
1788
1789         return super(ir_attachment, self).search(cr, uid, new_args, offset=offset,
1790                             limit=limit, order=order, context=context, count=False)
1791 ir_attachment()
1792
1793 class ir_values(osv.osv):
1794     _inherit = 'ir.values'
1795
1796     def set(self, cr, uid, key, key2, name, models, value, replace=True, \
1797             isobject=False, meta=False, preserve_user=False, company=False):
1798         """
1799         Set IR Values
1800         @param self: The object pointer
1801         @param cr: the current row, from the database cursor,
1802         @param uid: the current user’s ID for security checks,
1803         @param model: Get The Model
1804         """
1805
1806         new_model = []
1807         for data in models:
1808             if type(data) in (list, tuple):
1809                 new_model.append((data[0], base_calendar_id2real_id(data[1])))
1810             else:
1811                 new_model.append(data)
1812         return super(ir_values, self).set(cr, uid, key, key2, name, new_model, \
1813                     value, replace, isobject, meta, preserve_user, company)
1814
1815     def get(self, cr, uid, key, key2, models, meta=False, context=None, \
1816              res_id_req=False, without_user=True, key2_req=True):
1817         """
1818         Get IR Values
1819         @param self: The object pointer
1820         @param cr: the current row, from the database cursor,
1821         @param uid: the current user’s ID for security checks,
1822         @param model: Get The Model
1823         """
1824         if context is None:
1825             context = {}
1826         new_model = []
1827         for data in models:
1828             if type(data) in (list, tuple):
1829                 new_model.append((data[0], base_calendar_id2real_id(data[1])))
1830             else:
1831                 new_model.append(data)
1832         return super(ir_values, self).get(cr, uid, key, key2, new_model, \
1833                          meta, context, res_id_req, without_user, key2_req)
1834
1835 ir_values()
1836
1837 class ir_model(osv.osv):
1838
1839     _inherit = 'ir.model'
1840
1841     def read(self, cr, uid, ids, fields=None, context=None,
1842             load='_classic_read'):
1843         """
1844         Overrides orm read method.
1845         @param self: The object pointer
1846         @param cr: the current row, from the database cursor,
1847         @param uid: the current user’s ID for security checks,
1848         @param ids: List of IR Model’s IDs.
1849         @param context: A standard dictionary for contextual values
1850         """
1851         new_ids = isinstance(ids, (str, int, long)) and [ids] or ids
1852         if context is None:
1853             context = {}
1854         data = super(ir_model, self).read(cr, uid, new_ids, fields=fields, \
1855                         context=context, load=load)
1856         if data:
1857             for val in data:
1858                 val['id'] = base_calendar_id2real_id(val['id'])
1859         return isinstance(ids, (str, int, long)) and data[0] or data
1860
1861 ir_model()
1862
1863 class virtual_report_spool(web_services.report_spool):
1864
1865     def exp_report(self, db, uid, object, ids, datas=None, context=None):
1866         """
1867         Export Report
1868         @param self: The object pointer
1869         @param db: get the current database,
1870         @param uid: the current user’s ID for security checks,
1871         @param context: A standard dictionary for contextual values
1872         """
1873
1874         if object == 'printscreen.list':
1875             return super(virtual_report_spool, self).exp_report(db, uid, \
1876                             object, ids, datas, context)
1877         new_ids = []
1878         for id in ids:
1879             new_ids.append(base_calendar_id2real_id(id))
1880         if datas.get('id', False):
1881             datas['id'] = base_calendar_id2real_id(datas['id'])
1882         return super(virtual_report_spool, self).exp_report(db, uid, object, new_ids, datas, context)
1883
1884 virtual_report_spool()
1885
1886 class res_users(osv.osv):
1887     _inherit = 'res.users'
1888
1889     def _get_user_avail(self, cr, uid, ids, context=None):
1890         """
1891         Get User Availability
1892         @param self: The object pointer
1893         @param cr: the current row, from the database cursor,
1894         @param uid: the current user’s ID for security checks,
1895         @param ids: List of res user’s IDs.
1896         @param context: A standard dictionary for contextual values
1897         """
1898
1899         current_datetime = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
1900         res = {}
1901         attendee_obj = self.pool.get('calendar.attendee')
1902         attendee_ids = attendee_obj.search(cr, uid, [
1903                     ('event_date', '<=', current_datetime), ('event_end_date', '<=', current_datetime),
1904                     ('state', '=', 'accepted'), ('user_id', 'in', ids)
1905                     ])
1906
1907         for attendee_data in attendee_obj.read(cr, uid, attendee_ids, ['user_id']):
1908             user_id = attendee_data['user_id']
1909             status = 'busy'
1910             res.update({user_id:status})
1911
1912         #TOCHECK: Delegated Event
1913         for user_id in ids:
1914             if user_id not in res:
1915                 res[user_id] = 'free'
1916
1917         return res
1918
1919     def _get_user_avail_fun(self, cr, uid, ids, name, args, context=None):
1920         """
1921         Get User Availability Function
1922         @param self: The object pointer
1923         @param cr: the current row, from the database cursor,
1924         @param uid: the current user’s ID for security checks,
1925         @param ids: List of res user’s IDs.
1926         @param context: A standard dictionary for contextual values
1927         """
1928
1929         return self._get_user_avail(cr, uid, ids, context=context)
1930
1931     _columns = {
1932             'availability': fields.function(_get_user_avail_fun, type='selection', \
1933                     selection=[('free', 'Free'), ('busy', 'Busy')], \
1934                     string='Free/Busy', method=True),
1935     }
1936
1937 res_users()
1938
1939
1940 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: