From 3f284dd7e9d77fb480f46eb1d039e5b7f3109d77 Mon Sep 17 00:00:00 2001 From: "rpa (Open ERP)" Date: Wed, 4 Aug 2010 16:35:03 +0530 Subject: [PATCH 01/34] [FIX]: caldav: Corrected exdate imported from sunbird, [IMP]: caldav: Event with timezone should exported with date having related timezone bzr revid: rpa@tinyerp.com-20100804110503-j80r9ke95o28v18s --- addons/caldav/calendar.py | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/addons/caldav/calendar.py b/addons/caldav/calendar.py index 04bcd6eaa22..8a40d7c253d 100644 --- a/addons/caldav/calendar.py +++ b/addons/caldav/calendar.py @@ -228,6 +228,7 @@ class CalDAV(object): @param context: A standard dictionary for contextual values """ att_data = [] + exdates = [] for cal_data in child.getChildren(): if cal_data.name.lower() == 'organizer': self.ical_set(cal_data.name.lower(), {'name': cal_data.params.get('CN') and cal_data.params.get('CN')[0]}, 'value') @@ -249,11 +250,11 @@ class CalDAV(object): self.ical_set(cal_data.name.lower(), vals, 'value') continue if cal_data.name.lower() == 'exdate': - exval = map(lambda x: str(x), cal_data.value) + exdates += cal_data.value + exval = map(lambda x: x.strftime('%Y%m%dT%H%M%SZ'), exdates) self.ical_set(cal_data.name.lower(), ','.join(exval), 'value') continue if cal_data.name.lower() in self.__attribute__: - if cal_data.params.get('X-VOBJ-ORIGINAL-TZID'): self.ical_set('vtimezone', cal_data.params.get('X-VOBJ-ORIGINAL-TZID'), 'value') self.ical_set(cal_data.name.lower(), cal_data.value, 'value') @@ -305,12 +306,13 @@ class CalDAV(object): alarm_obj = self.pool.get('basic.calendar.alarm') vevent = alarm_obj.export_cal(cr, uid, model, \ data[map_field][0], vevent, context=ctx) - elif field == 'vtimezone' and data[map_field] and data[map_field] not in timezones: + elif field == 'vtimezone' and data[map_field]: tzval = data[map_field] - tz_obj = self.pool.get('basic.calendar.timezone') - ical = tz_obj.export_cal(cr, uid, None, \ - data[map_field], ical, context=context) - timezones.append(data[map_field]) + if tzval not in timezones: + tz_obj = self.pool.get('basic.calendar.timezone') + ical = tz_obj.export_cal(cr, uid, None, \ + data[map_field], ical, context=context) + timezones.append(data[map_field]) elif field == 'organizer' and data[map_field]: event_org = vevent.add('organizer') organizer_id = data[map_field][0] @@ -326,9 +328,15 @@ class CalDAV(object): vevent.add(field).value = tools.ustr(data[map_field]) elif map_type in ('datetime', 'date') and data[map_field]: dtfield = vevent.add(field) - dtfield.value = parser.parse(data[map_field]) if tzval: + date_1 = datetime.strptime(data[map_field], '%Y-%m-%d %H:%M:%S') + desttz = pytz.timezone(tzval.title()) + srctz = pytz.timezone('GMT') + date_gmt = date_1.replace(tzinfo=srctz).astimezone(desttz).strftime('%Y-%m-%d %H:%M:%S') dtfield.params['TZID'] = [tzval.title()] + dtfield.value = parser.parse(date_gmt) + else: + dtfield.value = parser.parse(data[map_field]) elif map_type == "timedelta": vevent.add(field).value = timedelta(hours=data[map_field]) elif map_type == "many2one": From bd76d0a66a42c534bfa21fa9e408b13c13e60ae3 Mon Sep 17 00:00:00 2001 From: "rpa (Open ERP)" Date: Wed, 4 Aug 2010 19:07:58 +0530 Subject: [PATCH 02/34] [FIX]: caldav: Fixed problem of Importing date with timezone information(converted into utc) bzr revid: rpa@tinyerp.com-20100804133758-a0u50if8z3rol31l --- addons/caldav/calendar.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/addons/caldav/calendar.py b/addons/caldav/calendar.py index 8a40d7c253d..c60bccad036 100644 --- a/addons/caldav/calendar.py +++ b/addons/caldav/calendar.py @@ -257,6 +257,9 @@ class CalDAV(object): if cal_data.name.lower() in self.__attribute__: if cal_data.params.get('X-VOBJ-ORIGINAL-TZID'): self.ical_set('vtimezone', cal_data.params.get('X-VOBJ-ORIGINAL-TZID'), 'value') + date_utc = cal_data.value.astimezone(pytz.utc) + self.ical_set(cal_data.name.lower(), date_utc, 'value') + continue self.ical_set(cal_data.name.lower(), cal_data.value, 'value') vals = map_data(cr, uid, self, context=context) return vals @@ -331,8 +334,7 @@ class CalDAV(object): if tzval: date_1 = datetime.strptime(data[map_field], '%Y-%m-%d %H:%M:%S') desttz = pytz.timezone(tzval.title()) - srctz = pytz.timezone('GMT') - date_gmt = date_1.replace(tzinfo=srctz).astimezone(desttz).strftime('%Y-%m-%d %H:%M:%S') + date_gmt = date_1.replace(tzinfo=pytz.utc).astimezone(desttz).strftime('%Y-%m-%d %H:%M:%S') dtfield.params['TZID'] = [tzval.title()] dtfield.value = parser.parse(date_gmt) else: From 90d7a02691d1c3af713dbd8eefc5c9acfa220761 Mon Sep 17 00:00:00 2001 From: "rpa (Open ERP)" Date: Thu, 5 Aug 2010 17:41:52 +0530 Subject: [PATCH 03/34] [FIX]: caldav: Fixed problem of exdate for the event with timezone bzr revid: rpa@tinyerp.com-20100805121152-0x9z33v4e1ql81la --- addons/base_calendar/base_calendar.py | 4 ++-- addons/caldav/calendar.py | 8 +++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/addons/base_calendar/base_calendar.py b/addons/base_calendar/base_calendar.py index 27b68003aa2..45a5c8d9f62 100644 --- a/addons/base_calendar/base_calendar.py +++ b/addons/base_calendar/base_calendar.py @@ -1559,12 +1559,12 @@ true, it will allow you to hide the event alarm information without removing it. continue event_id, date_new = event_id.split('-') event_id = [int(event_id)] - for record in self.read(cr, uid, event_id, ['date', 'rrule', 'exdate']): + for record in self.read(cr, uid, event_id, ['date', 'rrule', 'exdate'], context=context): if record['rrule']: # Remove one of the recurrent event date_new = time.strftime("%Y-%m-%d %H:%M:%S", \ time.strptime(date_new, "%Y%m%d%H%M%S")) - exdate = (record['exdate'] and (record['exdate'] + ',') or '') + ''.join((re.compile('\d')).findall(date_new)) + 'Z' + exdate = (record['exdate'] and (record['exdate'] + ',') or '') + ''.join((re.compile('\d')).findall(date_new)) res = self.write(cr, uid, event_id, {'exdate': exdate}) else: res = super(calendar_event, self).unlink(cr, uid, event_id) diff --git a/addons/caldav/calendar.py b/addons/caldav/calendar.py index c60bccad036..6937248e828 100644 --- a/addons/caldav/calendar.py +++ b/addons/caldav/calendar.py @@ -276,6 +276,7 @@ class CalDAV(object): timezones = [] for data in datas: tzval = None + exfield = None vevent = ical.add(name) for field in self.__attribute__.keys(): map_field = self.ical_get(field, 'field') @@ -316,6 +317,8 @@ class CalDAV(object): ical = tz_obj.export_cal(cr, uid, None, \ data[map_field], ical, context=context) timezones.append(data[map_field]) + if exfield: + exfield.params['TZID'] = [tzval.title()] elif field == 'organizer' and data[map_field]: event_org = vevent.add('organizer') organizer_id = data[map_field][0] @@ -326,7 +329,10 @@ class CalDAV(object): elif data[map_field]: if map_type in ("char", "text"): if field in ('exdate'): - vevent.add(field).value = map(parser.parse, (data[map_field]).split(',')) + exfield = vevent.add(field) + if tzval: + exfield.params['TZID'] = [tzval.title()] + exfield.value = map(parser.parse, (data[map_field]).split(',')) else: vevent.add(field).value = tools.ustr(data[map_field]) elif map_type in ('datetime', 'date') and data[map_field]: From b9d92c406175ec1210bd348691142d37395028ae Mon Sep 17 00:00:00 2001 From: "rpa (Open ERP)" Date: Fri, 6 Aug 2010 15:11:06 +0530 Subject: [PATCH 04/34] [FIX]: caldav: Exported exdate with timezone information bzr revid: rpa@tinyerp.com-20100806094106-cf65m4647j3gtf7c --- addons/base_calendar/base_calendar.py | 5 ++--- addons/caldav/calendar.py | 30 +++++++++++++++++++++------ 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/addons/base_calendar/base_calendar.py b/addons/base_calendar/base_calendar.py index 45a5c8d9f62..69f048cba71 100644 --- a/addons/base_calendar/base_calendar.py +++ b/addons/base_calendar/base_calendar.py @@ -1562,10 +1562,9 @@ true, it will allow you to hide the event alarm information without removing it. for record in self.read(cr, uid, event_id, ['date', 'rrule', 'exdate'], context=context): if record['rrule']: # Remove one of the recurrent event - date_new = time.strftime("%Y-%m-%d %H:%M:%S", \ + date_new = time.strftime("%Y%m%dT%H%M%S", \ time.strptime(date_new, "%Y%m%d%H%M%S")) - exdate = (record['exdate'] and (record['exdate'] + ',') or '') + ''.join((re.compile('\d')).findall(date_new)) - res = self.write(cr, uid, event_id, {'exdate': exdate}) + res = self.write(cr, uid, event_id, {'exdate': date_new}) else: res = super(calendar_event, self).unlink(cr, uid, event_id) self.pool.get('res.alarm').do_alarm_unlink(cr, uid, event_id, self._name) diff --git a/addons/caldav/calendar.py b/addons/caldav/calendar.py index 6937248e828..cb6860188e8 100644 --- a/addons/caldav/calendar.py +++ b/addons/caldav/calendar.py @@ -220,6 +220,10 @@ class CalDAV(object): self.__attribute__[name][type] = None return True + def format_date_tz(self, date, tz=None): + format = tools.DEFAULT_SERVER_DATETIME_FORMAT + return tools.server_to_local_timestamp(date, format, format, tz) + def parse_ics(self, cr, uid, child, cal_children=None, context=None): """ parse calendaring and scheduling information @param self: The object pointer @@ -251,7 +255,7 @@ class CalDAV(object): continue if cal_data.name.lower() == 'exdate': exdates += cal_data.value - exval = map(lambda x: x.strftime('%Y%m%dT%H%M%SZ'), exdates) + exval = map(lambda x: x.strftime('%Y%m%dT%H%M%S'), exdates) self.ical_set(cal_data.name.lower(), ','.join(exval), 'value') continue if cal_data.name.lower() in self.__attribute__: @@ -277,6 +281,7 @@ class CalDAV(object): for data in datas: tzval = None exfield = None + exdates = [] vevent = ical.add(name) for field in self.__attribute__.keys(): map_field = self.ical_get(field, 'field') @@ -319,6 +324,13 @@ class CalDAV(object): timezones.append(data[map_field]) if exfield: exfield.params['TZID'] = [tzval.title()] + exdates_updated = [] + for exdate in exdates: + date1 = (datetime.strptime(exdate, "%Y%m%dT%H%M%S")).strftime('%Y-%m-%d %H:%M:%S') + dest_date = self.format_date_tz(date1, tzval.title()) + ex_date = (datetime.strptime(dest_date, "%Y-%m-%d %H:%M:%S")).strftime('%Y%m%dT%H%M%S') + exdates_updated.append(ex_date) + exfield.value = map(parser.parse, exdates_updated) elif field == 'organizer' and data[map_field]: event_org = vevent.add('organizer') organizer_id = data[map_field][0] @@ -330,19 +342,25 @@ class CalDAV(object): if map_type in ("char", "text"): if field in ('exdate'): exfield = vevent.add(field) + exdates = (data[map_field]).split(',') if tzval: exfield.params['TZID'] = [tzval.title()] - exfield.value = map(parser.parse, (data[map_field]).split(',')) + exdates_updated = [] + for exdate in exdates: + date1 = (datetime.strptime(exdate, "%Y%m%dT%H%M%S")).strftime('%Y-%m-%d %H:%M:%S') + dest_date = self.format_date_tz(date1, tzval.title()) + ex_date = (datetime.strptime(dest_date, "%Y-%m-%d %H:%M:%S")).strftime('%Y%m%dT%H%M%S') + exdates_updated.append(ex_date) + exdates = exdates_updated + exfield.value = map(parser.parse, exdates) else: vevent.add(field).value = tools.ustr(data[map_field]) elif map_type in ('datetime', 'date') and data[map_field]: dtfield = vevent.add(field) if tzval: - date_1 = datetime.strptime(data[map_field], '%Y-%m-%d %H:%M:%S') - desttz = pytz.timezone(tzval.title()) - date_gmt = date_1.replace(tzinfo=pytz.utc).astimezone(desttz).strftime('%Y-%m-%d %H:%M:%S') + dest_date = self.format_date_tz(data[map_field], tzval.title()) dtfield.params['TZID'] = [tzval.title()] - dtfield.value = parser.parse(date_gmt) + dtfield.value = parser.parse(dest_date) else: dtfield.value = parser.parse(data[map_field]) elif map_type == "timedelta": From 22a3019e2c4225a6959bd30dd9b843a493dcf7b2 Mon Sep 17 00:00:00 2001 From: "rpa (Open ERP)" Date: Fri, 6 Aug 2010 17:32:32 +0530 Subject: [PATCH 05/34] [REF]: caldav: Code improvement regarding context bzr revid: rpa@tinyerp.com-20100806120232-b5nhbgowc1ii28ug --- addons/caldav/calendar.py | 32 +++++++++++-------- addons/caldav/wizard/calendar_event_export.py | 7 ++-- addons/crm_caldav/crm_caldav.py | 19 +++++++---- 3 files changed, 34 insertions(+), 24 deletions(-) diff --git a/addons/caldav/calendar.py b/addons/caldav/calendar.py index cb6860188e8..61dd3c25c9f 100644 --- a/addons/caldav/calendar.py +++ b/addons/caldav/calendar.py @@ -83,7 +83,7 @@ def openobjectid2uid(cr, uidval, oomodel): value = 'OpenObject-%s_%s@%s' % (oomodel, uidval, cr.dbname) return value -def get_attribute_mapping(cr, uid, calname, context={}): +def get_attribute_mapping(cr, uid, calname, context=None): """ Attribute Mapping with Basic calendar fields and lines @param cr: the current row, from the database cursor, @param uid: the current user’s ID for security checks, @@ -378,7 +378,7 @@ class CalDAV(object): vevent.add(field).value = key1 return vevent - def check_import(self, cr, uid, vals, context={}): + def check_import(self, cr, uid, vals, context=None): """ @param self: The object pointer @param cr: the current row, from the database cursor, @@ -386,7 +386,8 @@ class CalDAV(object): @param vals: Get Values @param context: A standard dictionary for contextual values """ - + if not context: + context = {} ids = [] model_obj = self.pool.get(context.get('model')) recur_pool = {} @@ -614,7 +615,7 @@ class basic_calendar_line(osv.osv): 'domain': lambda *a: '[]', } - def create(self, cr, uid, vals, context={}): + def create(self, cr, uid, vals, context=None): """ create calendar's line @param self: The object pointer @param cr: the current row, from the database cursor, @@ -696,7 +697,7 @@ class basic_calendar_fields(osv.osv): raise osv.except_osv(_('Warning !'), _('Please provide proper configuration of "%s" in Calendar Lines' % (name))) return True - def create(self, cr, uid, vals, context={}): + def create(self, cr, uid, vals, context=None): """ Create Calendar's fields @param self: The object pointer @param cr: the current row, from the database cursor, @@ -773,7 +774,7 @@ class Event(CalDAV, osv.osv_memory): 'dtend': None, # Use: O-1, Type: DATE-TIME, Specifies the date and time that a calendar component ends. } - def export_cal(self, cr, uid, datas, vobj='vevent', context={}): + def export_cal(self, cr, uid, datas, vobj='vevent', context=None): """ Export calendar @param self: The object pointer @param cr: the current row, from the database cursor, @@ -826,7 +827,7 @@ class ToDo(CalDAV, osv.osv_memory): 'rrule': None, } - def export_cal(self, cr, uid, datas, vobj='vevent', context={}): + def export_cal(self, cr, uid, datas, vobj='vevent', context=None): """ Export Calendar @param self: The object pointer @param cr: the current row, from the database cursor, @@ -876,7 +877,7 @@ class Timezone(CalDAV, osv.osv_memory): 'x-prop': None, # Use: O-n, Type: Text, } - def get_name_offset(self, cr, uid, tzid, context={}): + def get_name_offset(self, cr, uid, tzid, context=None): """ Get Name Offset value @param self: The object pointer @param cr: the current row, from the database cursor, @@ -893,7 +894,7 @@ class Timezone(CalDAV, osv.osv_memory): realoffset = (val < 0 and ('-' + realoffset) or ('+' + realoffset)) return (mydt.tzname(), realoffset) - def export_cal(self, cr, uid, model, tzid, ical, context={}): + def export_cal(self, cr, uid, model, tzid, ical, context=None): """ Export Calendar @param self: The object pointer @param cr: the current row, from the database cursor, @@ -901,7 +902,8 @@ class Timezone(CalDAV, osv.osv_memory): @param model: Get Model's name @param context: A standard dictionary for contextual values """ - + if not context: + context = {} ctx = context.copy() ctx.update({'model': model}) cal_tz = ical.add('vtimezone') @@ -949,7 +951,7 @@ class Alarm(CalDAV, osv.osv_memory): 'x-prop': None, } - def export_cal(self, cr, uid, model, alarm_id, vevent, context={}): + def export_cal(self, cr, uid, model, alarm_id, vevent, context=None): """ Export Calendar @param self: The object pointer @param cr: the current row, from the database cursor, @@ -958,7 +960,8 @@ class Alarm(CalDAV, osv.osv_memory): @param alarm_id: Get Alarm's Id @param context: A standard dictionary for contextual values """ - + if not context: + context = {} valarm = vevent.add('valarm') alarm_object = self.pool.get(model) alarm_data = alarm_object.read(cr, uid, alarm_id, []) @@ -1069,7 +1072,7 @@ class Attendee(CalDAV, osv.osv_memory): vals = map_data(cr, uid, self, context=context) return vals - def export_cal(self, cr, uid, model, attendee_ids, vevent, context={}): + def export_cal(self, cr, uid, model, attendee_ids, vevent, context=None): """ Export Calendar @param self: The object pointer @param cr: the current row, from the database cursor, @@ -1078,7 +1081,8 @@ class Attendee(CalDAV, osv.osv_memory): @param attendee_ids: Get Attendee's Id @param context: A standard dictionary for contextual values """ - + if not context: + context = {} attendee_object = self.pool.get(model) ctx = context.copy() ctx.update({'model': model}) diff --git a/addons/caldav/wizard/calendar_event_export.py b/addons/caldav/wizard/calendar_event_export.py index 0be54df9f56..ea90686b347 100644 --- a/addons/caldav/wizard/calendar_event_export.py +++ b/addons/caldav/wizard/calendar_event_export.py @@ -33,19 +33,20 @@ class calendar_event_export(osv.osv_memory): Export Calendar Event. """ - def default_get(self, cr, uid, fields, context): + def default_get(self, cr, uid, fields, context=None): """ Get Default value for Name field. """ + if not context: + context = {} model = context.get('model', 'basic.calendar') model_obj = self.pool.get(model) - calendar = model_obj.export_cal(cr, uid, context['active_ids'], context) res = super(calendar_event_export, self).default_get( cr, uid, fields, context=context) name = 'OpenERP %s.ics' % (model_obj._description) if 'name' in fields: res.update({'name': name}) if 'file_path' in fields: - calendar = model_obj.export_cal(cr, uid, context['active_ids'], context) + calendar = model_obj.export_cal(cr, uid, context['active_ids'], context=context) res.update({'file_path': base64.encodestring(calendar)}) return res diff --git a/addons/crm_caldav/crm_caldav.py b/addons/crm_caldav/crm_caldav.py index 3c3e8b8ae2e..58b9e4911fb 100644 --- a/addons/crm_caldav/crm_caldav.py +++ b/addons/crm_caldav/crm_caldav.py @@ -27,7 +27,7 @@ from datetime import datetime class crm_meeting(osv.osv): _inherit = 'crm.meeting' - def export_cal(self, cr, uid, ids, context={}): + def export_cal(self, cr, uid, ids, context=None): """ @param self: The object pointer @param cr: the current row, from the database cursor, @@ -35,14 +35,16 @@ class crm_meeting(osv.osv): @param ids: List of CRM Meeting’s IDs @param context: A standard dictionary for contextual values """ - + if not context: + context = {} ids = map(lambda x: base_calendar.base_calendar_id2real_id(x), ids) - event_data = self.read(cr, uid, ids) + event_data = self.read(cr, uid, ids, context=context) event_obj = self.pool.get('basic.calendar.event') - ical = event_obj.export_cal(cr, uid, event_data, context={'model': self._name}) + context.update({'model': self._name}) + ical = event_obj.export_cal(cr, uid, event_data, context=context) return ical.serialize() - def import_cal(self, cr, uid, data, data_id=None, context={}): + def import_cal(self, cr, uid, data, data_id=None, context=None): """ @param self: The object pointer @param cr: the current row, from the database cursor, @@ -51,12 +53,13 @@ class crm_meeting(osv.osv): @param data_id: calendar's Id @param context: A standard dictionary for contextual values """ - + if not context: + context = {} event_obj = self.pool.get('basic.calendar.event') vals = event_obj.import_cal(cr, uid, data, context=context) return self.check_import(cr, uid, vals, context=context) - def check_import(self, cr, uid, vals, context={}): + def check_import(self, cr, uid, vals, context=None): """ @param self: The object pointer @param cr: the current row, from the database cursor, @@ -64,6 +67,8 @@ class crm_meeting(osv.osv): @param vals: Get Values @param context: A standard dictionary for contextual values """ + if not context: + context = {} ids = [] model_obj = self.pool.get(context.get('model')) recur_pool = {} From 8058b7ff54c722c9aa3b735cb437d7f9d352139b Mon Sep 17 00:00:00 2001 From: "rpa (Open ERP)" Date: Fri, 6 Aug 2010 17:57:02 +0530 Subject: [PATCH 06/34] [FIX]: caldav: Importing calendar with timezone works perfect now bzr revid: rpa@tinyerp.com-20100806122702-dy4t0atc6jnggrmt --- addons/caldav/calendar.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/addons/caldav/calendar.py b/addons/caldav/calendar.py index 61dd3c25c9f..42f7e830311 100644 --- a/addons/caldav/calendar.py +++ b/addons/caldav/calendar.py @@ -255,8 +255,10 @@ class CalDAV(object): continue if cal_data.name.lower() == 'exdate': exdates += cal_data.value - exval = map(lambda x: x.strftime('%Y%m%dT%H%M%S'), exdates) - self.ical_set(cal_data.name.lower(), ','.join(exval), 'value') + exvals = [] + for exdate in exdates: + exvals.append(datetime.fromtimestamp(time.mktime(exdate.utctimetuple())).strftime('%Y%m%dT%H%M%S')) + self.ical_set(cal_data.name.lower(), ','.join(exvals), 'value') continue if cal_data.name.lower() in self.__attribute__: if cal_data.params.get('X-VOBJ-ORIGINAL-TZID'): From 4620bb07198166ff31eea17d260f1101f46715cb Mon Sep 17 00:00:00 2001 From: "rpa (Open ERP)" Date: Mon, 9 Aug 2010 11:39:55 +0530 Subject: [PATCH 07/34] [IMP]: caldav: Added minimum date for DTSTART of timezone instead of current date bzr revid: rpa@tinyerp.com-20100809060955-122w0fjk3qk5iy56 --- addons/caldav/calendar.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addons/caldav/calendar.py b/addons/caldav/calendar.py index 42f7e830311..21176656142 100644 --- a/addons/caldav/calendar.py +++ b/addons/caldav/calendar.py @@ -914,7 +914,8 @@ class Timezone(CalDAV, osv.osv_memory): tzname, offset = self.get_name_offset(cr, uid, tzid) tz_std.add("TZOFFSETFROM").value = offset tz_std.add("TZOFFSETTO").value = offset - tz_std.add("DTSTART").value = datetime.now() # TODO + #TODO: Get start date for timezone + tz_std.add("DTSTART").value = datetime.strptime('1970-01-01 00:00:00', '%Y-%m-%d %H:%M:%S') tz_std.add("TZNAME").value = tzname return ical From 5b6dcd8ffedc2ffd718d82097ca719c968a3df63 Mon Sep 17 00:00:00 2001 From: "rpa (Open ERP)" Date: Mon, 9 Aug 2010 12:42:17 +0530 Subject: [PATCH 08/34] [FIX]: caldav: Minor fix for evaluting domain bzr revid: rpa@tinyerp.com-20100809071217-kgmba32lb2xblkpc --- addons/caldav/calendar.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/caldav/calendar.py b/addons/caldav/calendar.py index 21176656142..6414afd7d48 100644 --- a/addons/caldav/calendar.py +++ b/addons/caldav/calendar.py @@ -504,7 +504,7 @@ class Calendar(CalDAV, osv.osv): continue if line.name in ('valarm', 'attendee'): continue - line_domain = eval(line.domain) + line_domain = eval(line.domain or '[]') line_domain += domain if ctx_res_id: line_domain += [('id','=',ctx_res_id)] @@ -534,7 +534,7 @@ class Calendar(CalDAV, osv.osv): continue if line.name in ('valarm', 'attendee'): continue - domain = eval(line.domain) + domain = eval(line.domain or '[]') if ctx_res_id: domain += [('id','=',ctx_res_id)] mod_obj = self.pool.get(line.object_id.model) From 5f2a464a8c47b341c102ff2536c24bca3c7f90d1 Mon Sep 17 00:00:00 2001 From: "rpa (Open ERP)" Date: Mon, 9 Aug 2010 19:18:54 +0530 Subject: [PATCH 09/34] [FIX]: base_calendar: Corrected wrong interpretation of editing event with recurrence bzr revid: rpa@tinyerp.com-20100809134854-6cu1rm7bsuif3fcn --- addons/base_calendar/base_calendar.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/addons/base_calendar/base_calendar.py b/addons/base_calendar/base_calendar.py index 69f048cba71..05f957ce2b0 100644 --- a/addons/base_calendar/base_calendar.py +++ b/addons/base_calendar/base_calendar.py @@ -1212,17 +1212,12 @@ true, it will allow you to hide the event alarm information without removing it. """ event_id = base_calendar_id2real_id(event_id) - datas = self.read(cr, uid, event_id, context=context) defaults.update({ - 'recurrent_uid': base_calendar_id2real_id(datas['id']), - 'recurrent_id': defaults.get('date') or real_date, + 'recurrent_uid': base_calendar_id2real_id(event_id), + 'recurrent_id': real_date, 'rrule_type': 'none', 'rrule': '' }) - exdate = datas['exdate'] and datas['exdate'].split(',') or [] - if real_date and defaults.get('date'): - exdate.append(real_date) - self.write(cr, uid, event_id, {'exdate': ','.join(exdate)}, context=context) new_id = self.copy(cr, uid, event_id, default=defaults, context=context) return new_id From ffda52ad1f8351ce141330630071bee2af29f3d9 Mon Sep 17 00:00:00 2001 From: "rpa (Open ERP)" Date: Tue, 10 Aug 2010 10:52:52 +0530 Subject: [PATCH 10/34] [IMP]: project_caldav: Added undefined import bzr revid: rpa@tinyerp.com-20100810052252-yvwh1n2jpx2s00xd --- addons/project_caldav/project_caldav.py | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/project_caldav/project_caldav.py b/addons/project_caldav/project_caldav.py index 65e61c6e4cd..70789d89dec 100644 --- a/addons/project_caldav/project_caldav.py +++ b/addons/project_caldav/project_caldav.py @@ -21,6 +21,7 @@ from osv import fields, osv from caldav import calendar +from datetime import datetime class project_task(osv.osv): _name = "project.task" From d53b3d9c693a99a0ae03bd7d21fb2ab9c8727117 Mon Sep 17 00:00:00 2001 From: "rpa (Open ERP)" Date: Tue, 10 Aug 2010 12:52:55 +0530 Subject: [PATCH 11/34] [IMP,FIX]: base_calendar: code cleaning and fixed problem of editing one of recurrent events bzr revid: rpa@tinyerp.com-20100810072255-1z3d0mkd51mqh8u5 --- addons/base_calendar/base_calendar.py | 43 +++++++-------------------- 1 file changed, 10 insertions(+), 33 deletions(-) diff --git a/addons/base_calendar/base_calendar.py b/addons/base_calendar/base_calendar.py index 05f957ce2b0..56d2370e772 100644 --- a/addons/base_calendar/base_calendar.py +++ b/addons/base_calendar/base_calendar.py @@ -1199,28 +1199,6 @@ true, it will allow you to hide the event alarm information without removing it. return value - def modify_this(self, cr, uid, event_id, defaults, real_date, context=None, *args): - """Modifies only one event record out of virtual recurrent events - and creates new event as a specific instance of a Recurring Event", - @param self: The object pointer - @param cr: the current row, from the database cursor, - @param uid: the current user’s ID for security checks, - @param event_id: Id of Recurring Event - @param real_date: Date of event recurrence that is being modified - @param context: A standard dictionary for contextual values - @param *args: Get Tupple Value - """ - - event_id = base_calendar_id2real_id(event_id) - defaults.update({ - 'recurrent_uid': base_calendar_id2real_id(event_id), - 'recurrent_id': real_date, - 'rrule_type': 'none', - 'rrule': '' - }) - new_id = self.copy(cr, uid, event_id, default=defaults, context=context) - return new_id - def modify_all(self, cr, uid, event_ids, defaults, context=None, *args): """ Modifies the recurring event @@ -1230,7 +1208,6 @@ true, it will allow you to hide the event alarm information without removing it. @param context: A standard dictionary for contextual values @return: True """ - for event_id in event_ids: event_id = base_calendar_id2real_id(event_id) @@ -1385,7 +1362,6 @@ true, it will allow you to hide the event alarm information without removing it. return rrule_string - def search(self, cr, uid, args, offset=0, limit=100, order=None, context=None, count=False): """ @@ -1419,8 +1395,6 @@ true, it will allow you to hide the event alarm information without removing it. return self.get_recurrent_ids(cr, uid, res, start_date, until_date, limit) - - def write(self, cr, uid, ids, vals, context=None, check=True, update_check=True): """ Overrides orm write method. @@ -1443,16 +1417,19 @@ true, it will allow you to hide the event alarm information without removing it. if len(str(event_id).split('-')) > 1: data = self.read(cr, uid, event_id, ['date', 'date_deadline', \ 'rrule', 'duration']) + real_event_id = base_calendar_id2real_id(event_id) if data.get('rrule'): - real_date = data.get('date') - data.update(vals) - new_id = self.modify_this(cr, uid, event_id, data, \ - real_date, context) + vals.update({ + 'recurrent_uid': real_event_id, + 'recurrent_id': data.get('date'), + 'rrule_type': 'none', + 'rrule': '' + }) + new_id = self.copy(cr, uid, real_event_id, default=vals, context=context) context.update({'active_id': new_id, 'active_ids': [new_id]}) continue - event_id = base_calendar_id2real_id(event_id) - if not event_id in new_ids: - new_ids.append(event_id) + if not real_event_id in new_ids: + new_ids.append(real_event_id) res = super(calendar_event, self).write(cr, uid, new_ids, vals, context=context) if (vals.has_key('alarm_id') or vals.has_key('base_calendar_alarm_id'))\ From f2f6ec0423735558b24522506c0b1ace9216c4e8 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Tue, 10 Aug 2010 15:29:57 +0300 Subject: [PATCH 12/34] Document: cleanup code for lint checking After pyflakes output, cleanup some unused imports, old code, fix names for undefined vars. bzr revid: p_christ@hol.gr-20100810122957-v01nqfq9omzdcs6e --- addons/document/directory_content.py | 9 ++--- addons/document/directory_report.py | 6 --- addons/document/document.py | 8 +--- addons/document/document_directory.py | 55 ++------------------------- addons/document/document_storage.py | 23 ++++++----- addons/document/nodes.py | 16 ++++---- addons/document/test_cindex.py | 2 +- 7 files changed, 26 insertions(+), 93 deletions(-) diff --git a/addons/document/directory_content.py b/addons/document/directory_content.py index bec4f25e45d..685df7910f3 100644 --- a/addons/document/directory_content.py +++ b/addons/document/directory_content.py @@ -18,16 +18,13 @@ # along with this program. If not, see . # ############################################################################## -import base64 from osv import osv, fields -from osv.orm import except_orm -import urlparse import netsvc -import os +# import os import nodes -import StringIO +# import StringIO class document_directory_content_type(osv.osv): _name = 'document.directory.content.type' @@ -83,7 +80,7 @@ class document_directory_content(osv.osv): tname = '' if content.include_name: content_name = node.displayname or '' - obj = node.context._dirobj.pool.get(model) + # obj = node.context._dirobj.pool.get(model) if content_name: tname = (content.prefix or '') + content_name + (content.suffix or '') + (content.extension or '') else: diff --git a/addons/document/directory_report.py b/addons/document/directory_report.py index a4cd2f45068..657fd39d9a9 100644 --- a/addons/document/directory_report.py +++ b/addons/document/directory_report.py @@ -20,13 +20,7 @@ # ############################################################################## -import base64 - from osv import osv, fields -from osv.orm import except_orm -import urlparse - -import os class ir_action_report_xml(osv.osv): _name="ir.actions.report.xml" diff --git a/addons/document/document.py b/addons/document/document.py index c62f62da2d9..46cf6d189c1 100644 --- a/addons/document/document.py +++ b/addons/document/document.py @@ -22,16 +22,10 @@ import base64 from osv import osv, fields -import urlparse import os -import pooler -import netsvc -from osv.orm import except_orm -#import StringIO - -from psycopg2 import Binary +# from psycopg2 import Binary #from tools import config import tools from tools.translate import _ diff --git a/addons/document/document_directory.py b/addons/document/document_directory.py index f5b9202fc8e..18544b54eac 100644 --- a/addons/document/document_directory.py +++ b/addons/document/document_directory.py @@ -19,11 +19,8 @@ # ############################################################################## -import base64 from osv import osv, fields -from osv.orm import except_orm -import urlparse import os import nodes @@ -141,30 +138,6 @@ class document_directory(osv.osv): _parent(dir_id, path) return path - def ol_get_resource_path(self, cr, uid, dir_id, res_model, res_id): - # this method will be used in process module - # to be need test and Improvement if resource dir has parent resource (link resource) - path=[] - def _parent(dir_id,path): - parent=self.browse(cr, uid, dir_id) - if parent.parent_id and not parent.ressource_parent_type_id: - _parent(parent.parent_id.id,path) - path.append(parent.name) - else: - path.append(parent.name) - return path - - directory=self.browse(cr,uid,dir_id) - model_ids=self.pool.get('ir.model').search(cr, uid, [('model','=',res_model)]) - if directory: - _parent(dir_id,path) - path.append(self.pool.get(directory.ressource_type_id.model).browse(cr, uid, res_id).name) - #user=self.pool.get('res.users').browse(cr,uid,uid) - #return "ftp://%s:%s@localhost:%s/%s/%s"%(user.login,user.password,config.get('ftp_server_port',8021),cr.dbname,'/'.join(path)) - # No way we will return the password! - return "ftp://user:pass@host:port/test/this" - return False - def _check_recursion(self, cr, uid, ids): level = 100 while len(ids): @@ -178,8 +151,9 @@ class document_directory(osv.osv): _constraints = [ (_check_recursion, 'Error! You can not create recursive Directories.', ['parent_id']) ] + def __init__(self, *args, **kwargs): - res = super(document_directory, self).__init__(*args, **kwargs) + super(document_directory, self).__init__(*args, **kwargs) #self._cache = {} def onchange_content_id(self, cr, uid, ids, ressource_type_id): @@ -223,31 +197,8 @@ class document_directory(osv.osv): """ try to locate the node in uri, Return a tuple (node_dir, remaining_path) """ - return (nodes.node_database(context=ncontext), uri) + return (nodes.node_database(context=ncontext), uri) - def old_code(): - if not uri: - return node_database(cr, uid, context=context) - turi = tuple(uri) - node = node_class(cr, uid, '/', False, context=context, type='database') - for path in uri[:]: - if path: - node = node.child(path) - if not node: - return False - oo = node.object and (node.object._name, node.object.id) or False - oo2 = node.object2 and (node.object2._name, node.object2.id) or False - return node - - def ol_get_childs(self, cr, uid, uri, context={}): - node = self.get_object(cr, uid, uri, context) - if uri: - children = node.children() - else: - children= [node] - result = map(lambda node: node.path_get(), children) - return result - def copy(self, cr, uid, id, default=None, context=None): if not default: default ={} diff --git a/addons/document/document_storage.py b/addons/document/document_storage.py index 419d6c03e37..0ecc01941b4 100644 --- a/addons/document/document_storage.py +++ b/addons/document/document_storage.py @@ -37,7 +37,6 @@ from osv.orm import except_orm import random import string import pooler -import netsvc import nodes from content_index import cntIndex @@ -189,7 +188,7 @@ class nodefd_db(StringIO, nodes.node_descriptor): # we now open a *separate* cursor, to update the data. # FIXME: this may be improved, for concurrency handling par = self._get_parent() - uid = par.context.uid + # uid = par.context.uid cr = pooler.get_db(par.context.dbname).cursor() try: if self.mode in ('w', 'w+', 'r+'): @@ -227,7 +226,7 @@ class nodefd_db(StringIO, nodes.node_descriptor): " WHERE id = %s", (out, len(data), par.file_id)) cr.commit() - except Exception, e: + except Exception: logging.getLogger('document.storage').exception('Cannot update db file #%d for close:', par.file_id) raise finally: @@ -262,7 +261,7 @@ class nodefd_db64(StringIO, nodes.node_descriptor): # we now open a *separate* cursor, to update the data. # FIXME: this may be improved, for concurrency handling par = self._get_parent() - uid = par.context.uid + # uid = par.context.uid cr = pooler.get_db(par.context.dbname).cursor() try: if self.mode in ('w', 'w+', 'r+'): @@ -288,18 +287,18 @@ class nodefd_db64(StringIO, nodes.node_descriptor): cr.execute('UPDATE ir_attachment SET db_datas = %s::bytea, file_size=%s, ' \ 'index_content = %s, file_type = %s ' \ 'WHERE id = %s', - (base64.encodestring(out), len(out), icont_u, mime, par.file_id)) + (base64.encodestring(data), len(data), icont_u, mime, par.file_id)) elif self.mode == 'a': - out = self.getvalue() + data = self.getvalue() # Yes, we're obviously using the wrong representation for storing our # data as base64-in-bytea cr.execute("UPDATE ir_attachment " \ "SET db_datas = encode( (COALESCE(decode(encode(db_datas,'escape'),'base64'),'') || decode(%s, 'base64')),'base64')::bytea , " \ " file_size = COALESCE(file_size, 0) + %s " \ " WHERE id = %s", - (base64.encodestring(out), len(out), par.file_id)) + (base64.encodestring(data), len(data), par.file_id)) cr.commit() - except Exception, e: + except Exception: logging.getLogger('document.storage').exception('Cannot update db file #%d for close:', par.file_id) raise finally: @@ -500,7 +499,7 @@ class document_storage(osv.osv): # try to fix their directory. if ira.file_size: self._doclog.warning("ir.attachment #%d does not have a filename, trying the name." %ira.id) - sfname = ira.name + # sfname = ira.name fpath = os.path.join(boo.path,ira.store_fname or ira.name) if os.path.exists(fpath): return file(fpath,'rb').read() @@ -695,7 +694,7 @@ class document_storage(osv.osv): store_fname = os.path.join(*store_path) return { 'name': new_name, 'datas_fname': new_name, 'store_fname': store_fname } else: - raise TypeError("No %s storage" % boo.type) + raise TypeError("No %s storage" % sbro.type) def simple_move(self, cr, uid, file_node, ndir_bro, context=None): """ A preparation for a file move. @@ -734,7 +733,7 @@ class document_storage(osv.osv): fname = fil_bo.store_fname if not fname: return ValueError("Tried to rename a non-stored file") - path = storage_bo.path + path = sbro.path oldpath = os.path.join(path, fname) for ch in ('*', '|', "\\", '/', ':', '"', '<', '>', '?', '..'): @@ -751,7 +750,7 @@ class document_storage(osv.osv): os.rename(oldpath, newpath) return { 'name': new_name, 'datas_fname': new_name, 'store_fname': new_name } else: - raise TypeError("No %s storage" % boo.type) + raise TypeError("No %s storage" % sbro.type) document_storage() diff --git a/addons/document/nodes.py b/addons/document/nodes.py index 228f104574d..cf590dafdad 100644 --- a/addons/document/nodes.py +++ b/addons/document/nodes.py @@ -19,17 +19,14 @@ # ############################################################################## -# import base64 -# import StringIO -from osv import osv, fields -from osv.orm import except_orm # import urlparse import pooler from tools.safe_eval import safe_eval import errno -import os +# import os import time +import logging from StringIO import StringIO @@ -44,6 +41,8 @@ from StringIO import StringIO # root: if we are at the first directory of a ressource # +logger = logging.getLogger('doc2.nodes') + def _str2time(cre): """ Convert a string with time representation (from db) into time (float) @@ -902,9 +901,8 @@ class node_res_obj(node_class): def get_dav_eprop(self, cr, ns, prop): if ns != 'http://groupdav.org/' or prop != 'resourcetype': - print "Who asked for %s:%s?" % (ns, prop) + logger.warning("Who asked for %s:%s?" % (ns, prop)) return None - res = {} cntobj = self.context._dirobj.pool.get('document.directory.content') uid = self.context.uid ctx = self.context.context.copy() @@ -1374,11 +1372,11 @@ class nodefd_content(StringIO, node_descriptor): if self.mode in ('w', 'w+', 'r+'): data = self.getvalue() cntobj = par.context._dirobj.pool.get('document.directory.content') - cntobj.process_write(cr, uid, parent, data, ctx) + cntobj.process_write(cr, uid, par, data, par.context.context) elif self.mode == 'a': raise NotImplementedError cr.commit() - except Exception, e: + except Exception: logging.getLogger('document.content').exception('Cannot update db content #%d for close:', par.cnt_id) raise finally: diff --git a/addons/document/test_cindex.py b/addons/document/test_cindex.py index 267e0497950..d7ae7c4d160 100644 --- a/addons/document/test_cindex.py +++ b/addons/document/test_cindex.py @@ -35,7 +35,7 @@ for fname in args: for line in res[:5]: print line except Exception,e: - import traceback,sys + import traceback tb_s = reduce(lambda x, y: x+y, traceback.format_exception( sys.exc_type, sys.exc_value, sys.exc_traceback)) From 559df1377083dd2463fedb7eb594533bfd89fa29 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Tue, 10 Aug 2010 15:29:57 +0300 Subject: [PATCH 13/34] Document_ftp: cleanup code for lint bzr revid: p_christ@hol.gr-20100810122957-y0wqscm0xcfppbqd --- addons/document_ftp/ftpserver/abstracted_fs.py | 17 +++++++---------- addons/document_ftp/ftpserver/ftpserver.py | 8 +++++--- addons/document_ftp/wizard/ftp_browse.py | 11 ++++++----- addons/document_ftp/wizard/ftp_configuration.py | 10 ++-------- 4 files changed, 20 insertions(+), 26 deletions(-) diff --git a/addons/document_ftp/ftpserver/abstracted_fs.py b/addons/document_ftp/ftpserver/abstracted_fs.py index 2ddfcc2bb7d..952b75d49e1 100644 --- a/addons/document_ftp/ftpserver/abstracted_fs.py +++ b/addons/document_ftp/ftpserver/abstracted_fs.py @@ -1,9 +1,8 @@ # -*- encoding: utf-8 -*- + import os import time from tarfile import filemode -import StringIO -import base64 import logging import errno @@ -12,12 +11,11 @@ import fnmatch import pooler import netsvc -import os + from service import security from osv import osv #from document.nodes import node_res_dir, node_res_obj from document.nodes import get_node_context -import stat def _get_month_name(month): month=int(month) @@ -165,7 +163,7 @@ class abstracted_fs(object): return ret except EnvironmentError: raise - except Exception,e: + except Exception: self._log.exception('Cannot create item %s at node %s', objname, repr(node)) raise OSError(1, 'Operation not permited.') @@ -177,7 +175,7 @@ class abstracted_fs(object): try: res = node.open_data(cr, mode) cr.commit() - except TypeError, e: + except TypeError: raise IOError(errno.EINVAL, "No data") return res @@ -188,12 +186,11 @@ class abstracted_fs(object): name. Unlike mkstemp it returns an object with a file-like interface. """ - raise NotImplementedError + raise NotImplementedError # TODO text = not 'b' in mode # for unique file , maintain version if duplicate file if dir: - # TODO cr = dir.cr uid = dir.uid pool = pooler.get_pool(node.context.dbname) @@ -234,7 +231,7 @@ class abstracted_fs(object): cdir = node.create_child_collection(cr, basename) self._log.debug("Created child dir: %r", cdir) cr.commit() - except Exception,e: + except Exception: self._log.exception('Cannot create dir "%s" at node %s', basename, repr(node)) raise OSError(1, 'Operation not permited.') @@ -405,7 +402,7 @@ class abstracted_fs(object): cr.commit() except EnvironmentError: raise - except Exception, err: + except Exception: self._log.exception('Cannot rename "%s" to "%s" at "%s"', src, datacr[2], datacr[1]) raise OSError(1,'Operation not permited.') diff --git a/addons/document_ftp/ftpserver/ftpserver.py b/addons/document_ftp/ftpserver/ftpserver.py index 692abc4aef4..681282e81e6 100644 --- a/addons/document_ftp/ftpserver/ftpserver.py +++ b/addons/document_ftp/ftpserver/ftpserver.py @@ -124,6 +124,7 @@ import stat from tarfile import filemode try: + # TODO: remove, no Unix credentials import pwd import grp except ImportError: @@ -194,6 +195,7 @@ proto_cmds = { # hack around format_exc function of traceback module to grant # backward compatibility with python < 2.4 +# TODO: remove, we're not 2.4 compatible anyway if not hasattr(traceback, 'format_exc'): try: import cStringIO as StringIO @@ -1438,7 +1440,7 @@ class FTPHandler(asynchat.async_chat): """ try: asynchat.async_chat.__init__(self, conn=conn) # python2.5 - except TypeError, e: + except TypeError: asynchat.async_chat.__init__(self, sock=conn) # python2.6 self.server = server self.remote_ip, self.remote_port = self.socket.getpeername()[:2] @@ -1866,7 +1868,7 @@ class FTPHandler(asynchat.async_chat): self.respond('%s %s.' % (str(ret_code), why)) raise FTPExceptionSent(why) - except Exception, e: + except Exception, err: cmdname = function.__name__ try: logerror(traceback.format_exc()) @@ -2003,7 +2005,7 @@ class FTPHandler(asynchat.async_chat): assert len(octs) == 4 for x in octs: assert 0 <= x <= 255 - except (AssertionError, ValueError, OverflowError), err: + except (AssertionError, ValueError, OverflowError): self.respond("501 Invalid EPRT format.") else: self._make_eport(ip, port) diff --git a/addons/document_ftp/wizard/ftp_browse.py b/addons/document_ftp/wizard/ftp_browse.py index d033302f22a..37aba5158f1 100644 --- a/addons/document_ftp/wizard/ftp_browse.py +++ b/addons/document_ftp/wizard/ftp_browse.py @@ -20,8 +20,9 @@ ############################################################################## from osv import osv, fields -from tools.translate import _ +# from tools.translate import _ from document_ftp import ftpserver + class document_ftp_browse(osv.osv_memory): _name = 'document.ftp.browse' _description = 'Document FTP Browse' @@ -35,8 +36,8 @@ class document_ftp_browse(osv.osv_memory): if 'url' in fields: user_pool = self.pool.get('res.users') current_user = user_pool.browse(cr, uid, uid, context=context) - dir_pool = self.pool.get('document.directory') - data_pool = self.pool.get('ir.model.data') + # dir_pool = self.pool.get('document.directory') + data_pool = self.pool.get('ir.model.data') aid = data_pool._get_id(cr, uid, 'document_ftp', 'action_document_browse') aid = data_pool.browse(cr, uid, aid, context=context).res_id ftp_url = self.pool.get('ir.actions.url').browse(cr, uid, aid, context=context) @@ -50,12 +51,12 @@ class document_ftp_browse(osv.osv_memory): def browse_ftp(self, cr, uid, ids, context): data_id = ids and ids[0] or False - data = self.browse(cr, uid, data_id, context) + data = self.browse(cr, uid, data_id, context) final_url = data.url return { 'type': 'ir.actions.act_url', 'url':final_url, 'target': 'new' } + document_ftp_browse() - diff --git a/addons/document_ftp/wizard/ftp_configuration.py b/addons/document_ftp/wizard/ftp_configuration.py index 40926bdf7c9..0522e0348b8 100644 --- a/addons/document_ftp/wizard/ftp_configuration.py +++ b/addons/document_ftp/wizard/ftp_configuration.py @@ -19,14 +19,8 @@ # ############################################################################## -import base64 - from osv import osv, fields -from osv.orm import except_orm from tools import config -import urlparse - -import os class document_ftp_configuration(osv.osv_memory): @@ -46,10 +40,10 @@ class document_ftp_configuration(osv.osv_memory): def execute(self, cr, uid, ids, context=None): conf = self.browse(cr, uid, ids[0], context) - dir_pool = self.pool.get('document.directory') - data_pool = self.pool.get('ir.model.data') + data_pool = self.pool.get('ir.model.data') # Update the action for FTP browse. aid = data_pool._get_id(cr, uid, 'document_ftp', 'action_document_browse') aid = data_pool.browse(cr, uid, aid, context=context).res_id self.pool.get('ir.actions.url').write(cr, uid, [aid], {'url': 'ftp://'+(conf.host or 'localhost:8021')+'/'}) + document_ftp_configuration() From 97f6f059e1aa85ba8beb3e7d5bc52dea6e66bf21 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Tue, 10 Aug 2010 15:29:57 +0300 Subject: [PATCH 14/34] Document_ftp: second round of lint checks These are the functional ones. Don't mess with the real os, because we're just an abstraction layer of nodes, drop support for Python < 2.4, because the server wouldn't work anyway. bzr revid: p_christ@hol.gr-20100810122957-41n7lq0o287j62vw --- addons/document_ftp/ftpserver/ftpserver.py | 65 +++------------------- 1 file changed, 7 insertions(+), 58 deletions(-) diff --git a/addons/document_ftp/ftpserver/ftpserver.py b/addons/document_ftp/ftpserver/ftpserver.py index 681282e81e6..0ca322fca12 100644 --- a/addons/document_ftp/ftpserver/ftpserver.py +++ b/addons/document_ftp/ftpserver/ftpserver.py @@ -121,16 +121,9 @@ import tempfile import warnings import random import stat +from collections import deque from tarfile import filemode -try: - # TODO: remove, no Unix credentials - import pwd - import grp -except ImportError: - pwd = grp = None - - LOG_ACTIVE = True __all__ = ['proto_cmds', 'Error', 'log', 'logline', 'logerror', 'DummyAuthorizer', @@ -193,25 +186,6 @@ proto_cmds = { } -# hack around format_exc function of traceback module to grant -# backward compatibility with python < 2.4 -# TODO: remove, we're not 2.4 compatible anyway -if not hasattr(traceback, 'format_exc'): - try: - import cStringIO as StringIO - except ImportError: - import StringIO - - def _format_exc(): - f = StringIO.StringIO() - traceback.print_exc(file=f) - data = f.getvalue() - f.close() - return data - - traceback.format_exc = _format_exc - - def _strerror(err): """A wrap around os.strerror() which may be not available on all platforms (e.g. pythonCE). @@ -592,16 +566,6 @@ class ActiveDTP(asyncore.dispatcher): self.cmd_channel.respond("425 Can't connect to specified address.") self.close() - -try: - from collections import deque -except ImportError: - # backward compatibility with Python < 2.4 by replacing deque with a list - class deque(list): - def appendleft(self, obj): - list.insert(self, 0, obj) - - class DTPHandler(asyncore.dispatcher): """Class handling server-data-transfer-process (server-DTP, see RFC-959) managing data-transfer operations involving sending @@ -1229,21 +1193,9 @@ class AbstractedFS: if not nlinks: # non-posix system, let's use a bogus value nlinks = 1 size = st.st_size # file size - if pwd and grp: - # get user and group name, else just use the raw uid/gid - try: - uname = pwd.getpwuid(st.st_uid).pw_name - except KeyError: - uname = st.st_uid - try: - gname = grp.getgrgid(st.st_gid).gr_name - except KeyError: - gname = st.st_gid - else: - # on non-posix systems the only chance we use default - # bogus values for owner and group - uname = "owner" - gname = "group" + uname = st.st_uid or "owner" + gname = st.st_gid or "group" + # stat.st_mtime could fail (-1) if last mtime is too old # in which case we return the local time as last mtime try: @@ -1471,13 +1423,10 @@ class FTPHandler(asynchat.async_chat): # mlsx facts attributes self.current_facts = ['type', 'perm', 'size', 'modify'] - if os.name == 'posix': - self.current_facts.append('unique') + self.current_facts.append('unique') self.available_facts = self.current_facts[:] - if pwd and grp: - self.available_facts += ['unix.mode', 'unix.uid', 'unix.gid'] - if os.name == 'nt': - self.available_facts.append('create') + self.available_facts += ['unix.mode', 'unix.uid', 'unix.gid'] + self.available_facts.append('create') # dtp attributes self.data_server = None From 6ad4fbfc80f644b3f6dffa15560076d26074a777 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Tue, 10 Aug 2010 15:29:57 +0300 Subject: [PATCH 15/34] CalDAV: Fix support for WebCal CalDAV+GroupDAV is a collection of .ics files (resources). WebCal is a single .ics resource with all calendar data in it. So, support both of those nodes for every calendar instance, eg: /dbname/Calendars/Meetings : collection /dbname/Calendars/Meetings.ics : vcalendar file bzr revid: p_christ@hol.gr-20100810122957-a1691wv8ultbki55 --- addons/caldav/caldav_node.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/addons/caldav/caldav_node.py b/addons/caldav/caldav_node.py index b931b0f0328..5ebe38cc308 100644 --- a/addons/caldav/caldav_node.py +++ b/addons/caldav/caldav_node.py @@ -71,8 +71,8 @@ class node_calendar_collection(nodes.node_dir): where = [('collection_id','=',self.dir_id)] ext = False if name and name.endswith('.ics'): - name = name[-4] - ext = '.ics' + name = name[:-4] + ext = True if name: where.append(('name','=',name)) if not domain: @@ -81,11 +81,12 @@ class node_calendar_collection(nodes.node_dir): fil_obj = dirobj.pool.get('basic.calendar') ids = fil_obj.search(cr,uid,where,context=ctx) res = [] - for calender in fil_obj.browse(cr, uid, ids, context=ctx): - if not ext: - res.append(node_calendar(calender.name, self, self.context, calender)) - else: - res.append(res_node_calendar(name, self, self.context, calender)) + for cal in fil_obj.browse(cr, uid, ids, context=ctx): + if (not name) or not ext: + res.append(node_calendar(cal.name, self, self.context, cal)) + if (not name) or ext: + res.append(res_node_calendar(cal.name+'.ics', self, self.context, cal)) + # May be both of them! return res def _get_dav_owner(self, cr): From bcecd56c9eff5c5d9bb07088f2996c88c2166d3f Mon Sep 17 00:00:00 2001 From: "Harry (OpenERP)" Date: Tue, 10 Aug 2010 18:01:08 +0530 Subject: [PATCH 16/34] [IMP] calendar: add new char field for organizer in event and use it to map with Organizer attribute. bzr revid: hmo@tinyerp.com-20100810123108-p870zh9vrbfl9644 --- addons/base_calendar/base_calendar.py | 14 +++++++++++--- .../wizard/base_calendar_invite_attendee_view.xml | 1 + addons/caldav/calendar.py | 10 ++++------ addons/crm/crm_meeting_view.xml | 2 +- addons/crm_caldav/crm_caldav_data.xml | 2 +- 5 files changed, 18 insertions(+), 11 deletions(-) diff --git a/addons/base_calendar/base_calendar.py b/addons/base_calendar/base_calendar.py index 56d2370e772..bd91e98169b 100644 --- a/addons/base_calendar/base_calendar.py +++ b/addons/base_calendar/base_calendar.py @@ -378,7 +378,6 @@ property or property parameter."), } _defaults = { 'state': 'needs-action', - 'user_id': lambda self, cr, uid, ctx: uid, 'role': 'req-participant', 'rsvp': True, 'cutype': 'individual', @@ -419,7 +418,10 @@ property or property parameter."), event.add('location').value = event_obj.location if event_obj.rrule: event.add('rrule').value = event_obj.rrule - if event_obj.user_id or event_obj.organizer_id: + if event_obj.organizer: + event_org.params['CN'] = [event_obj.organizer] + event_org.value = 'MAILTO:' + (event_obj.organizer) + elif event_obj.user_id or event_obj.organizer_id: event_org = event.add('organizer') organizer = event_obj.organizer_id if not organizer: @@ -1111,6 +1113,7 @@ e.g.: Every other month on the last Sunday of the month for 10 occurrences:\ 'vtimezone': fields.related('user_id', 'context_tz', type='char', size=24, \ string='Timezone', store=True), 'user_id': fields.many2one('res.users', 'Responsible', states={'done': [('readonly', True)]}), + 'organizer': fields.char("Organizer", size=256, states={'done': [('readonly', True)]}), # Map with Organizer Attribure of VEvent. 'organizer_id': fields.many2one('res.users', 'Organizer', states={'done': [('readonly', True)]}), 'freq': fields.selection([('None', 'No Repeat'), \ ('secondly', 'Secondly'), \ @@ -1147,6 +1150,11 @@ e.g.: Every other month on the last Sunday of the month for 10 occurrences:\ 'active': fields.boolean('Active', help="If the active field is set to \ true, it will allow you to hide the event alarm information without removing it.") } + def default_organizer(self, cr, uid, context=None): + user_pool = self.pool.get('res.users') + user = user_pool.browse(cr, uid, uid, context=context) + return user.user_email or user.name + _defaults = { 'state': 'tentative', 'class': 'public', @@ -1156,7 +1164,7 @@ true, it will allow you to hide the event alarm information without removing it. 'interval': 1, 'active': 1, 'user_id': lambda self, cr, uid, ctx: uid, - 'organizer_id': lambda self, cr, uid, ctx: uid, + 'organizer': default_organizer, } def open_event(self, cr, uid, ids, context=None): diff --git a/addons/base_calendar/wizard/base_calendar_invite_attendee_view.xml b/addons/base_calendar/wizard/base_calendar_invite_attendee_view.xml index bea5ea66a84..494787bbf97 100644 --- a/addons/base_calendar/wizard/base_calendar_invite_attendee_view.xml +++ b/addons/base_calendar/wizard/base_calendar_invite_attendee_view.xml @@ -10,6 +10,7 @@ form
+ diff --git a/addons/caldav/calendar.py b/addons/caldav/calendar.py index 6414afd7d48..ab247cd9090 100644 --- a/addons/caldav/calendar.py +++ b/addons/caldav/calendar.py @@ -235,7 +235,7 @@ class CalDAV(object): exdates = [] for cal_data in child.getChildren(): if cal_data.name.lower() == 'organizer': - self.ical_set(cal_data.name.lower(), {'name': cal_data.params.get('CN') and cal_data.params.get('CN')[0]}, 'value') + self.ical_set(cal_data.name.lower(), cal_data.params.get('CN') and cal_data.params.get('CN')[0], 'value') continue if cal_data.name.lower() == 'attendee': ctx = context.copy() @@ -334,12 +334,10 @@ class CalDAV(object): exdates_updated.append(ex_date) exfield.value = map(parser.parse, exdates_updated) elif field == 'organizer' and data[map_field]: + organizer = data[map_field] event_org = vevent.add('organizer') - organizer_id = data[map_field][0] - user_obj = self.pool.get('res.users') - organizer = user_obj.browse(cr, uid, organizer_id, context=context) - event_org.params['CN'] = [organizer.name] - event_org.value = 'MAILTO:' + (organizer.user_email or organizer.name) + event_org.params['CN'] = [organizer] + event_org.value = 'MAILTO:' + (organizer) elif data[map_field]: if map_type in ("char", "text"): if field in ('exdate'): diff --git a/addons/crm/crm_meeting_view.xml b/addons/crm/crm_meeting_view.xml index 3c04006447f..af843666435 100644 --- a/addons/crm/crm_meeting_view.xml +++ b/addons/crm/crm_meeting_view.xml @@ -107,7 +107,7 @@ - + diff --git a/addons/crm_caldav/crm_caldav_data.xml b/addons/crm_caldav/crm_caldav_data.xml index 3b74eef123c..692b7e7389e 100644 --- a/addons/crm_caldav/crm_caldav_data.xml +++ b/addons/crm_caldav/crm_caldav_data.xml @@ -32,7 +32,7 @@ - + field From a58c266d2cc1b9ea62859c12fec2dfb23ed9417b Mon Sep 17 00:00:00 2001 From: "Harry (OpenERP)" Date: Tue, 10 Aug 2010 18:16:52 +0530 Subject: [PATCH 17/34] [FIX] calendar: regression on write event and invite attendee bzr revid: hmo@tinyerp.com-20100810124652-9ba18o9s3sjgwugc --- addons/base_calendar/base_calendar.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/addons/base_calendar/base_calendar.py b/addons/base_calendar/base_calendar.py index bd91e98169b..43aedb13777 100644 --- a/addons/base_calendar/base_calendar.py +++ b/addons/base_calendar/base_calendar.py @@ -419,6 +419,7 @@ property or property parameter."), if event_obj.rrule: event.add('rrule').value = event_obj.rrule if event_obj.organizer: + event_org = event.add('organizer') event_org.params['CN'] = [event_obj.organizer] event_org.value = 'MAILTO:' + (event_obj.organizer) elif event_obj.user_id or event_obj.organizer_id: @@ -1421,7 +1422,9 @@ true, it will allow you to hide the event alarm information without removing it. else: select = ids new_ids = [] + res = False for event_id in select: + real_event_id = event_id if len(str(event_id).split('-')) > 1: data = self.read(cr, uid, event_id, ['date', 'date_deadline', \ 'rrule', 'duration']) @@ -1436,10 +1439,10 @@ true, it will allow you to hide the event alarm information without removing it. new_id = self.copy(cr, uid, real_event_id, default=vals, context=context) context.update({'active_id': new_id, 'active_ids': [new_id]}) continue - if not real_event_id in new_ids: + if real_event_id and not real_event_id in new_ids: new_ids.append(real_event_id) - - res = super(calendar_event, self).write(cr, uid, new_ids, vals, context=context) + if new_ids: + res = super(calendar_event, self).write(cr, uid, new_ids, vals, context=context) if (vals.has_key('alarm_id') or vals.has_key('base_calendar_alarm_id'))\ or (vals.has_key('date') or vals.has_key('duration') or vals.has_key('date_deadline')): # change alarm details From c8f907d5e5f7034a6f406676c31351087bb5f9ee Mon Sep 17 00:00:00 2001 From: "rpa (Open ERP)" Date: Tue, 10 Aug 2010 19:04:18 +0530 Subject: [PATCH 18/34] [FIX]: base_calendar: Event with recurrence_uid is needed to be excluded in calendar view bzr revid: rpa@tinyerp.com-20100810133418-xp83r412mmhcwse6 --- addons/base_calendar/base_calendar.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/addons/base_calendar/base_calendar.py b/addons/base_calendar/base_calendar.py index 43aedb13777..15735f849ca 100644 --- a/addons/base_calendar/base_calendar.py +++ b/addons/base_calendar/base_calendar.py @@ -1251,9 +1251,10 @@ true, it will allow you to hide the event alarm information without removing it. else: ids = select result = [] + recur_dict = [] if ids and (base_start_date or base_until_date): cr.execute("select m.id, m.rrule, m.date, m.date_deadline, \ - m.exdate, m.exrule from " + self._table + \ + m.exdate, m.exrule, m.recurrent_id, m.recurrent_uid from " + self._table + \ " m where m.id in ("\ + ','.join(map(lambda x: str(x), ids))+")") @@ -1274,8 +1275,12 @@ true, it will allow you to hide the event alarm information without removing it. if until_date and (event_date > until_date): continue idval = real_id2base_calendar_id(data['id'], data['date']) - result.append(idval) - count += 1 + if not data['recurrent_id']: + result.append(idval) + count += 1 + else: + ex_id = real_id2base_calendar_id(data['recurrent_uid'], data['recurrent_id']) + recur_dict.append(ex_id) else: exdate = data['exdate'] and data['exdate'].split(',') or [] rrule_str = data['rrule'] @@ -1310,7 +1315,7 @@ true, it will allow you to hide the event alarm information without removing it. result.append(idval) count += 1 if result: - ids = result + ids = list(set(result)-set(recur_dict)) if isinstance(select, (str, int, long)): return ids and ids[0] or False return ids From d44cd8ca622666b945c2e56d0c8b27c10a0970a0 Mon Sep 17 00:00:00 2001 From: "rpa (Open ERP)" Date: Wed, 11 Aug 2010 12:06:18 +0530 Subject: [PATCH 19/34] [FIX]: base_calendar: Fixed problem of missing dates for recurrent event in calendar view bzr revid: rpa@tinyerp.com-20100811063618-np9qqmok9nydj612 --- addons/base_calendar/base_calendar.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/addons/base_calendar/base_calendar.py b/addons/base_calendar/base_calendar.py index 15735f849ca..0a4bb59d41b 100644 --- a/addons/base_calendar/base_calendar.py +++ b/addons/base_calendar/base_calendar.py @@ -1253,7 +1253,7 @@ true, it will allow you to hide the event alarm information without removing it. result = [] recur_dict = [] if ids and (base_start_date or base_until_date): - cr.execute("select m.id, m.rrule, m.date, m.date_deadline, \ + cr.execute("select m.id, m.rrule, m.date, m.date_deadline, m.duration, \ m.exdate, m.exrule, m.recurrent_id, m.recurrent_uid from " + self._table + \ " m where m.id in ("\ + ','.join(map(lambda x: str(x), ids))+")") @@ -1280,6 +1280,10 @@ true, it will allow you to hide the event alarm information without removing it. count += 1 else: ex_id = real_id2base_calendar_id(data['recurrent_uid'], data['recurrent_id']) + ls = base_calendar_id2real_id(ex_id, with_date=data and data.get('duration', 0) or 0) + if not isinstance(ls, (str, int, long)) and len(ls) >= 2: + if ls[1] == data['recurrent_id']: + result.append(idval) recur_dict.append(ex_id) else: exdate = data['exdate'] and data['exdate'].split(',') or [] From 47298c7670d36472bc58711fc10f0cce4258f58b Mon Sep 17 00:00:00 2001 From: "rpa (Open ERP)" Date: Wed, 11 Aug 2010 14:19:46 +0530 Subject: [PATCH 20/34] [IMP]: base_calendar: Minor code improvement bzr revid: rpa@tinyerp.com-20100811084946-4iy1gh6skd0lt6xm --- addons/base_calendar/base_calendar.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/base_calendar/base_calendar.py b/addons/base_calendar/base_calendar.py index 0a4bb59d41b..769bf0321c0 100644 --- a/addons/base_calendar/base_calendar.py +++ b/addons/base_calendar/base_calendar.py @@ -1433,11 +1433,10 @@ true, it will allow you to hide the event alarm information without removing it. new_ids = [] res = False for event_id in select: - real_event_id = event_id + real_event_id = base_calendar_id2real_id(event_id) if len(str(event_id).split('-')) > 1: data = self.read(cr, uid, event_id, ['date', 'date_deadline', \ 'rrule', 'duration']) - real_event_id = base_calendar_id2real_id(event_id) if data.get('rrule'): vals.update({ 'recurrent_uid': real_event_id, @@ -1448,8 +1447,9 @@ true, it will allow you to hide the event alarm information without removing it. new_id = self.copy(cr, uid, real_event_id, default=vals, context=context) context.update({'active_id': new_id, 'active_ids': [new_id]}) continue - if real_event_id and not real_event_id in new_ids: + if not real_event_id in new_ids: new_ids.append(real_event_id) + if new_ids: res = super(calendar_event, self).write(cr, uid, new_ids, vals, context=context) if (vals.has_key('alarm_id') or vals.has_key('base_calendar_alarm_id'))\ From 780d30e4ea19c97ee00b88fd4953af8657d88624 Mon Sep 17 00:00:00 2001 From: "rpa (Open ERP)" Date: Wed, 11 Aug 2010 15:02:23 +0530 Subject: [PATCH 21/34] [IMP]: caldav: Added description field in calendar bzr revid: rpa@tinyerp.com-20100811093223-lixibo7upw4ebubk --- addons/caldav/caldav_view.xml | 68 +++++++++++++++++++---------------- addons/caldav/calendar.py | 1 + 2 files changed, 39 insertions(+), 30 deletions(-) diff --git a/addons/caldav/caldav_view.xml b/addons/caldav/caldav_view.xml index 9184a0aadd2..2e9719efead 100644 --- a/addons/caldav/caldav_view.xml +++ b/addons/caldav/caldav_view.xml @@ -74,37 +74,45 @@ - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - + + + + + + + + + + + + + + diff --git a/addons/caldav/calendar.py b/addons/caldav/calendar.py index ab247cd9090..ab768f86c51 100644 --- a/addons/caldav/calendar.py +++ b/addons/caldav/calendar.py @@ -486,6 +486,7 @@ class Calendar(CalDAV, osv.osv): 'line_ids': fields.one2many('basic.calendar.lines', 'calendar_id', 'Calendar Lines'), 'create_date': fields.datetime('Created Date', readonly=True), 'write_date': fields.datetime('Modifided Date', readonly=True), + 'description': fields.text("description"), } def get_calendar_objects(self, cr, uid, ids, parent=None, domain=None, context=None): From a566e35d20dad3ed5ac743b3411fefcb2ae21863 Mon Sep 17 00:00:00 2001 From: "rpa (Open ERP)" Date: Thu, 12 Aug 2010 12:17:53 +0530 Subject: [PATCH 22/34] [IMP]: document_webdav: Raise warning when PyWebDAV is not installed bzr revid: rpa@tinyerp.com-20100812064753-xfcxcbs726oa3qtw --- addons/document_webdav/webdav.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/addons/document_webdav/webdav.py b/addons/document_webdav/webdav.py index 1fd530cbdca..10a7f9db4e6 100644 --- a/addons/document_webdav/webdav.py +++ b/addons/document_webdav/webdav.py @@ -24,8 +24,15 @@ import xml.dom.minidom domimpl = xml.dom.minidom.getDOMImplementation() import urlparse import urllib -from DAV import utils -from DAV.propfind import PROPFIND +from osv import osv + +try: + from DAV import utils + from DAV.propfind import PROPFIND +except ImportError: + raise osv.except_osv('PyWebDAV Import Error!','Please install PyWebDAV \ +from http://code.google.com/p/pywebdav/downloads/detail?name=PyWebDAV-0.9.4.tar.gz&can=2&q=/') + import tools From e8562fc40c6c43bbee3968a1448fc2cc402e77db Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Thu, 12 Aug 2010 14:08:38 +0300 Subject: [PATCH 23/34] CalDAV: minor rewrite of previous improvement make it a little more readable. bzr revid: p_christ@hol.gr-20100812110838-vmjlnnwopn0pr0j1 --- addons/caldav/calendar.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/addons/caldav/calendar.py b/addons/caldav/calendar.py index ab768f86c51..38367936541 100644 --- a/addons/caldav/calendar.py +++ b/addons/caldav/calendar.py @@ -235,7 +235,9 @@ class CalDAV(object): exdates = [] for cal_data in child.getChildren(): if cal_data.name.lower() == 'organizer': - self.ical_set(cal_data.name.lower(), cal_data.params.get('CN') and cal_data.params.get('CN')[0], 'value') + self.ical_set(cal_data.name.lower(), + {'name': cal_data.params.get('CN', ['',])[0]}, + 'value') continue if cal_data.name.lower() == 'attendee': ctx = context.copy() From 2bd49b9d27e0d612df3ca14d358ac06ad631cc92 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Thu, 12 Aug 2010 14:09:14 +0300 Subject: [PATCH 24/34] project_caldav: Fix column inheritance of project.task We explicitly want project.task.active to come from project.task. bzr revid: p_christ@hol.gr-20100812110914-bj27asf9rjj08qxh --- addons/project_caldav/project_caldav.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/addons/project_caldav/project_caldav.py b/addons/project_caldav/project_caldav.py index 70789d89dec..ea6c02bc2cb 100644 --- a/addons/project_caldav/project_caldav.py +++ b/addons/project_caldav/project_caldav.py @@ -23,12 +23,17 @@ from osv import fields, osv from caldav import calendar from datetime import datetime +from project.project import task as base_project_task + class project_task(osv.osv): _name = "project.task" _inherit = ["calendar.todo", "project.task"] _columns = { + # force inherit from project.project_task so that + # calendar.todo.active is masked oute + 'active': base_project_task._columns['active'], 'write_date': fields.datetime('Write Date'), - 'create_date': fields.datetime('Create Date'), + 'create_date': fields.datetime('Create Date', readonly=True), 'attendee_ids': fields.many2many('calendar.attendee', \ 'task_attendee_rel', 'task_id', 'attendee_id', 'Attendees'), 'state': fields.selection([('draft', 'Draft'),('open', 'In Progress'),('pending', 'Pending'), ('cancelled', 'Cancelled'), ('done', 'Done')], 'State', readonly=True, required=True, From f13ef7607467b367752872062e8ca5013023225b Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Thu, 12 Aug 2010 14:09:29 +0300 Subject: [PATCH 25/34] Document: Better dbg message of content indexers. bzr revid: p_christ@hol.gr-20100812110929-hyy3oe0dmxkwxja4 --- addons/document/content_index.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/addons/document/content_index.py b/addons/document/content_index.py index d6012e60091..e3b37914f8c 100644 --- a/addons/document/content_index.py +++ b/addons/document/content_index.py @@ -98,7 +98,9 @@ class indexer(object): def _doIndexFile(self,fpath): raise NhException("Content not handled here") - + + def __repr__(self): + return "" %(self.__module__, self.__class__.__name__) def mime_match(mime, mdict): @@ -128,7 +130,7 @@ class contentIndex(object): f = True if f: - self.__logger.debug('Register content indexer: %s', obj) + self.__logger.debug('Register content indexer: %r', obj) if not f: raise Exception("Your indexer should at least suport a mimetype or extension") From 05cdd7507de56d8c1f87c43113d7b90c86b718b8 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Thu, 12 Aug 2010 14:10:14 +0300 Subject: [PATCH 26/34] crm_caldav: allow exception to propagate at check_import() bzr revid: p_christ@hol.gr-20100812111014-jrzkxaiibodbllor --- addons/crm_caldav/crm_caldav.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/crm_caldav/crm_caldav.py b/addons/crm_caldav/crm_caldav.py index 58b9e4911fb..a9430a47225 100644 --- a/addons/crm_caldav/crm_caldav.py +++ b/addons/crm_caldav/crm_caldav.py @@ -101,8 +101,8 @@ class crm_meeting(osv.osv): event_id = model_obj.create(cr, uid, val) recur_pool[u_id] = event_id ids.append(event_id) - except Exception, e: - raise osv.except_osv(('Error !'), (str(e))) + except Exception: + raise return ids crm_meeting() From 71c5192b080c2ee424c524e5a2b14d03b76be72b Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Thu, 12 Aug 2010 14:10:18 +0300 Subject: [PATCH 27/34] caldav: don't bork if client requests foobar.ics bzr revid: p_christ@hol.gr-20100812111018-tzpw0yixyuwp4dg0 --- addons/caldav/caldav_node.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/addons/caldav/caldav_node.py b/addons/caldav/caldav_node.py index 5ebe38cc308..700caafa0f0 100644 --- a/addons/caldav/caldav_node.py +++ b/addons/caldav/caldav_node.py @@ -86,11 +86,11 @@ class node_calendar_collection(nodes.node_dir): res.append(node_calendar(cal.name, self, self.context, cal)) if (not name) or ext: res.append(res_node_calendar(cal.name+'.ics', self, self.context, cal)) - # May be both of them! + # May be both of them! return res def _get_dav_owner(self, cr): - # Todo? + # Todo? return False def _get_ttag(self, cr): @@ -206,7 +206,15 @@ class node_calendar(nodes.node_class): if name: if name.endswith('.ics'): name = name[:-4] - where.append(('id','=',int(name))) + try: + where.append(('id','=',int(name))) + except ValueError: + # if somebody requests any other name than the ones we + # generate (non-numeric), it just won't exist + # FIXME: however, this confuses Evolution (at least), which + # thinks the .ics node hadn't been saved. + return [] + if not domain: domain = [] #for opr1, opt, opr2 in domain: From 5df0b8cf8cc0ca5eb6af8428e5ba6a8c4bb38f34 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Thu, 12 Aug 2010 14:10:23 +0300 Subject: [PATCH 28/34] Doc Webdav: at PUT, return feedback about created node. Used in caldav, the final location of the node and its ETag must be returned after creation. bzr revid: p_christ@hol.gr-20100812111023-s6dv0g24enfghki9 --- addons/document_webdav/dav_fs.py | 29 ++++++++++++++++++++++--- addons/document_webdav/webdav_server.py | 13 ++++++++--- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/addons/document_webdav/dav_fs.py b/addons/document_webdav/dav_fs.py index af8738ef0da..fe88906c557 100644 --- a/addons/document_webdav/dav_fs.py +++ b/addons/document_webdav/dav_fs.py @@ -522,7 +522,6 @@ class openerp_dav_handler(dav_interface): def put(self, uri, data, content_type=None): """ put the object into the filesystem """ self.parent.log_message('Putting %s (%d), %s'%( misc.ustr(uri), data and len(data) or 0, content_type)) - parent='/'.join(uri.split('/')[:-1]) cr, uid, pool,dbname, uri2 = self.get_cr(uri) if not dbname: if cr: cr.close() @@ -535,20 +534,44 @@ class openerp_dav_handler(dav_interface): objname = uri2[-1] ext = objname.find('.') >0 and objname.split('.')[1] or False + ret = None if not node: dir_node = self.uri2object(cr, uid, pool, uri2[:-1]) if not dir_node: cr.close() raise DAV_NotFound('Parent folder not found') - self._try_function(dir_node.create_child, (cr, objname, data), + newchild = self._try_function(dir_node.create_child, (cr, objname, data), "create %s" % objname, cr=cr) + if not newchild: + cr.commit() + cr.close() + raise DAV_Error(400, "Failed to create resource") + + uparts=urlparse.urlparse(uri) + fileloc = '/'.join(newchild.full_path()) + if isinstance(fileloc, unicode): + fileloc = fileloc.encode('utf-8') + # the uri we get is a mangled one, where the davpath has been removed + davpath = self.parent.get_davpath() + + surl = '%s://%s' % (uparts[0], uparts[1]) + uloc = urllib.quote(fileloc) + hurl = False + if uri != ('/'+uloc) and uri != (surl + '/' + uloc): + hurl = '%s%s/%s/%s' %(surl, davpath, dbname, uloc) + etag = False + try: + etag = str(newchild.get_etag(cr)) + except Exception, e: + self.parent.log_error("Cannot get etag for node: %s" % e) + ret = (hurl, etag) else: self._try_function(node.set_data, (cr, data), "save %s" % objname, cr=cr) cr.commit() cr.close() - return 201 + return ret def rmcol(self,uri): """ delete a collection """ diff --git a/addons/document_webdav/webdav_server.py b/addons/document_webdav/webdav_server.py index 34c255a2b3e..3cda2ccc6fe 100644 --- a/addons/document_webdav/webdav_server.py +++ b/addons/document_webdav/webdav_server.py @@ -214,13 +214,20 @@ class DAVHandler(HttpOptions, FixSendError, DAVRequestHandler): return self.send_status(ec) headers = {} + etag = None + if location and isinstance(location, tuple): + etag = location[1] + location = location[0] + # note that we have allowed for > 2 elems if location: headers['Location'] = location try: - etag = dc.get_prop(location or uri, "DAV:", "getetag") - headers['ETag'] = etag - except: + if not etag: + etag = dc.get_prop(location or uri, "DAV:", "getetag") + if etag: + headers['ETag'] = str(etag) + except Exception: pass self.send_body(None, '201', 'Created', '', headers=headers) From bcba71f7798296a11d232c5d2b91bfe91405a015 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Thu, 12 Aug 2010 14:10:25 +0300 Subject: [PATCH 29/34] Doc Webdav: Don't send both ETag and Location after PUT An ETag should refer to the requested uri, so a Location header will render it invalid anyway. Gnome's evolution has an if-else block that first checks ETag and then Location. bzr revid: p_christ@hol.gr-20100812111025-xoilqlb956moovgv --- addons/document_webdav/webdav_server.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/addons/document_webdav/webdav_server.py b/addons/document_webdav/webdav_server.py index 3cda2ccc6fe..8a83e25456d 100644 --- a/addons/document_webdav/webdav_server.py +++ b/addons/document_webdav/webdav_server.py @@ -221,14 +221,14 @@ class DAVHandler(HttpOptions, FixSendError, DAVRequestHandler): # note that we have allowed for > 2 elems if location: headers['Location'] = location - - try: - if not etag: - etag = dc.get_prop(location or uri, "DAV:", "getetag") - if etag: - headers['ETag'] = str(etag) - except Exception: - pass + else: + try: + if not etag: + etag = dc.get_prop(location or uri, "DAV:", "getetag") + if etag: + headers['ETag'] = str(etag) + except Exception: + pass self.send_body(None, '201', 'Created', '', headers=headers) From c2fee9e69119addd84897005f4eb3ce7ad34a732 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Thu, 12 Aug 2010 14:10:28 +0300 Subject: [PATCH 30/34] caldav: when creating a calendar child, return a node bzr revid: p_christ@hol.gr-20100812111028-tntn2b1uspc64cyd --- addons/caldav/caldav_node.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/addons/caldav/caldav_node.py b/addons/caldav/caldav_node.py index 700caafa0f0..c6e21c46e77 100644 --- a/addons/caldav/caldav_node.py +++ b/addons/caldav/caldav_node.py @@ -233,9 +233,21 @@ class node_calendar(nodes.node_class): Return the node_* created """ # we ignore the path, it will be re-generated automatically + fil_obj = self.context._dirobj.pool.get('basic.calendar') + ctx = self.context.context.copy() + ctx.update(self.dctx) + uid = self.context.uid + res = self.set_data(cr, data) - - # TODO: use the res to create at least one node + + if res and len(res): + # We arbitrarily construct only the first node of the data + # that have been imported. ICS may have had more elements, + # but only one node can be returned here. + assert isinstance(res[0], (int, long)) + fnodes = fil_obj.get_calendar_objects(cr, uid, [self.calendar_id], self, + domain=[('id','=',res[0])], context=ctx) + return fnodes[0] return None From 59c236eff1e4fb8a93d2ba2072b641a3d4331ce3 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Thu, 12 Aug 2010 14:10:31 +0300 Subject: [PATCH 31/34] caldav: Store times at server's timezone, not utc. The values stored from caldav should be at the server's timezone, like all other timestamps of openerp. We accept any timezone in the iCalendar files, we export our (server's) timezone by default. bzr revid: p_christ@hol.gr-20100812111031-m14103hzl9q7ff9j --- addons/caldav/calendar.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/addons/caldav/calendar.py b/addons/caldav/calendar.py index 38367936541..3f7661a15fd 100644 --- a/addons/caldav/calendar.py +++ b/addons/caldav/calendar.py @@ -233,6 +233,8 @@ class CalDAV(object): att_data = [] exdates = [] + _server_tzinfo = pytz.timezone(tools.get_server_timezone()) + for cal_data in child.getChildren(): if cal_data.name.lower() == 'organizer': self.ical_set(cal_data.name.lower(), @@ -264,9 +266,11 @@ class CalDAV(object): continue if cal_data.name.lower() in self.__attribute__: if cal_data.params.get('X-VOBJ-ORIGINAL-TZID'): - self.ical_set('vtimezone', cal_data.params.get('X-VOBJ-ORIGINAL-TZID'), 'value') - date_utc = cal_data.value.astimezone(pytz.utc) - self.ical_set(cal_data.name.lower(), date_utc, 'value') + # since we do convert, do we also need to save the original tzid? + # self.ical_set('vtimezone', cal_data.params.get('X-VOBJ-ORIGINAL-TZID'), 'value') + + date_local = cal_data.value.astimezone(_server_tzinfo) + self.ical_set(cal_data.name.lower(), date_local, 'value') continue self.ical_set(cal_data.name.lower(), cal_data.value, 'value') vals = map_data(cr, uid, self, context=context) From ddae08c774b67429feca92aa5aac09a28128a694 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Thu, 12 Aug 2010 14:10:34 +0300 Subject: [PATCH 32/34] caldav: propagate exceptions. There is no need to hide them behind an osv.except_osv bzr revid: p_christ@hol.gr-20100812111034-j627h6ixp6ldf2ha --- addons/caldav/calendar.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/caldav/calendar.py b/addons/caldav/calendar.py index 3f7661a15fd..fd533c098a1 100644 --- a/addons/caldav/calendar.py +++ b/addons/caldav/calendar.py @@ -420,8 +420,8 @@ class CalDAV(object): event_id = model_obj.create(cr, uid, val) recur_pool[u_id] = event_id ids.append(event_id) - except Exception, e: - raise osv.except_osv(('Error !'), (str(e))) + except Exception: + raise return ids def export_cal(self, cr, uid, datas, vobj=None, context=None): From 5b31947d8861c2dd1fc857bde580a41f50a74260 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Thu, 12 Aug 2010 19:21:13 +0300 Subject: [PATCH 33/34] Caldav: store the organizer as email address, retrieve that I hope that these functions can be reused in more places. bzr revid: p_christ@hol.gr-20100812162113-jrotrwtn492qggkm --- addons/caldav/calendar.py | 69 +++++++++++++++++++++++++++++++++++---- 1 file changed, 63 insertions(+), 6 deletions(-) diff --git a/addons/caldav/calendar.py b/addons/caldav/calendar.py index fd533c098a1..b4f7c03e5aa 100644 --- a/addons/caldav/calendar.py +++ b/addons/caldav/calendar.py @@ -83,6 +83,60 @@ def openobjectid2uid(cr, uidval, oomodel): value = 'OpenObject-%s_%s@%s' % (oomodel, uidval, cr.dbname) return value +def mailto2str(arg): + """Take a dict of mail and convert to string. + """ + ret = [] + if isinstance(arg, dict): + args = [arg,] + else: + args = arg + + for ard in args: + rstr = ard.get('name','') + if ard.get('company',False): + rstr += ' (%s)' % ard.get('company') + if ard.get('email'): + rstr += ' <%s>' % ard.get('email') + ret.append(rstr) + return ', '.join(ret) + +def str2mailto(emailstr, multi=False): + """Split one email string to a dict of name, company, mail parts + + @param multi Return an array, recognize comma-sep + """ + # TODO: move to tools or sth. + mege = re.compile(r'([^\(\<]+) *(\((.*?)\))? *(\< ?(.*?) ?\>)? ?(\((.*?)\))? *$') + + mailz= [emailstr,] + retz = [] + if multi: + mailz = emailstr.split(',') + + for mas in mailz: + m = mege.match(mas.strip()) + if not m: + # one of the rare non-matching strings is "sad" :( + # retz.append({ 'name': mas.strip() }) + # continue + raise ValueError("Invalid email address %r" % mas) + rd = { 'name': m.group(1).strip(), + 'email': m.group(5), } + if m.group(2): + rd['company'] = m.group(3).strip() + elif m.group(6): + rd['company'] = m.group(7).strip() + + if rd['name'].startswith('"') and rd['name'].endswith('"'): + rd['name'] = rd['name'][1:-1] + retz.append(rd) + + if multi: + return retz + else: + return retz[0] + def get_attribute_mapping(cr, uid, calname, context=None): """ Attribute Mapping with Basic calendar fields and lines @param cr: the current row, from the database cursor, @@ -237,9 +291,11 @@ class CalDAV(object): for cal_data in child.getChildren(): if cal_data.name.lower() == 'organizer': - self.ical_set(cal_data.name.lower(), - {'name': cal_data.params.get('CN', ['',])[0]}, - 'value') + dmail = { 'name': cal_data.params.get('CN', ['',])[0], + 'email': cal_data.value.replace('MAILTO:',''), + # TODO: company? + } + self.ical_set(cal_data.name.lower(), mailto2str(dmail), 'value') continue if cal_data.name.lower() == 'attendee': ctx = context.copy() @@ -340,10 +396,11 @@ class CalDAV(object): exdates_updated.append(ex_date) exfield.value = map(parser.parse, exdates_updated) elif field == 'organizer' and data[map_field]: - organizer = data[map_field] + organizer = str2mailto(data[map_field]) event_org = vevent.add('organizer') - event_org.params['CN'] = [organizer] - event_org.value = 'MAILTO:' + (organizer) + event_org.params['CN'] = [organizer['name']] + event_org.value = 'MAILTO:' + (organizer.get('email') or '') + # TODO: company? elif data[map_field]: if map_type in ("char", "text"): if field in ('exdate'): From 2777bd4d5c54f3a50f2b50ac0adec95739088e2f Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Thu, 12 Aug 2010 19:21:20 +0300 Subject: [PATCH 34/34] Doc Webdav: close cursor on misc exceptions bzr revid: p_christ@hol.gr-20100812162120-q6q2z3tqqy1nsr95 --- addons/document_webdav/dav_fs.py | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/document_webdav/dav_fs.py b/addons/document_webdav/dav_fs.py index fe88906c557..5d641cb39c0 100644 --- a/addons/document_webdav/dav_fs.py +++ b/addons/document_webdav/dav_fs.py @@ -129,6 +129,7 @@ class openerp_dav_handler(dav_interface): raise default_exc(err.strerror) except Exception,e: import traceback + if cr: cr.close() self.parent.log_error("Cannot %s: %s", opname, str(e)) self.parent.log_message("Exc: %s",traceback.format_exc()) raise default_exc("Operation failed")