diff --git a/addons/base_calendar/base_calendar.py b/addons/base_calendar/base_calendar.py index 27b68003aa2..7bb7d61d8ee 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,11 @@ 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 = 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: event_org = event.add('organizer') organizer = event_obj.organizer_id if not organizer: @@ -1111,6 +1114,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 +1151,14 @@ 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) + res = user.name + if user.user_email: + res += " <%s>" %(user.user_email) + return res + _defaults = { 'state': 'tentative', 'class': 'public', @@ -1156,7 +1168,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): @@ -1199,33 +1211,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) - 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, - '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 - def modify_all(self, cr, uid, event_ids, defaults, context=None, *args): """ Modifies the recurring event @@ -1235,7 +1220,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) @@ -1270,9 +1254,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 + \ + 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))+")") @@ -1293,8 +1278,16 @@ 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']) + 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 [] rrule_str = data['rrule'] @@ -1329,7 +1322,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 @@ -1390,7 +1383,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): """ @@ -1424,8 +1416,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. @@ -1444,22 +1434,27 @@ 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 = 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']) 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 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 @@ -1559,13 +1554,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", \ + 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)) + 'Z' - 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/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/caldav_node.py b/addons/caldav/caldav_node.py index b931b0f0328..c6e21c46e77 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,15 +81,16 @@ 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): - # Todo? + # Todo? return False def _get_ttag(self, cr): @@ -205,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: @@ -224,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 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 04bcd6eaa22..b4f7c03e5aa 100644 --- a/addons/caldav/calendar.py +++ b/addons/caldav/calendar.py @@ -83,7 +83,61 @@ 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 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, @param uid: the current user’s ID for security checks, @@ -220,6 +274,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 @@ -228,9 +286,16 @@ class CalDAV(object): @param context: A standard dictionary for contextual values """ 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(), {'name': cal_data.params.get('CN') and 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() @@ -249,13 +314,20 @@ 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) - self.ical_set(cal_data.name.lower(), ','.join(exval), 'value') + exdates += cal_data.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'): - self.ical_set('vtimezone', cal_data.params.get('X-VOBJ-ORIGINAL-TZID'), '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) return vals @@ -272,6 +344,8 @@ class CalDAV(object): timezones = [] 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') @@ -305,30 +379,53 @@ 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]) + 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]: + organizer = str2mailto(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['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'): - vevent.add(field).value = map(parser.parse, (data[map_field]).split(',')) + exfield = vevent.add(field) + exdates = (data[map_field]).split(',') + if tzval: + 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) + 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) - dtfield.value = parser.parse(data[map_field]) if tzval: + dest_date = self.format_date_tz(data[map_field], tzval.title()) dtfield.params['TZID'] = [tzval.title()] + dtfield.value = parser.parse(dest_date) + 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": @@ -344,7 +441,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, @@ -352,7 +449,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 = {} @@ -379,8 +477,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): @@ -451,6 +549,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): @@ -467,7 +566,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)] @@ -497,7 +596,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) @@ -580,7 +679,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, @@ -662,7 +761,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, @@ -739,7 +838,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, @@ -792,7 +891,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, @@ -842,7 +941,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, @@ -859,7 +958,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, @@ -867,7 +966,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') @@ -876,7 +976,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 @@ -915,7 +1016,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, @@ -924,7 +1025,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, []) @@ -1035,7 +1137,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, @@ -1044,7 +1146,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/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.py b/addons/crm_caldav/crm_caldav.py index 3c3e8b8ae2e..a9430a47225 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 = {} @@ -96,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() 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 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") 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)) 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..0ca322fca12 100644 --- a/addons/document_ftp/ftpserver/ftpserver.py +++ b/addons/document_ftp/ftpserver/ftpserver.py @@ -121,15 +121,9 @@ import tempfile import warnings import random import stat +from collections import deque from tarfile import filemode -try: - import pwd - import grp -except ImportError: - pwd = grp = None - - LOG_ACTIVE = True __all__ = ['proto_cmds', 'Error', 'log', 'logline', 'logerror', 'DummyAuthorizer', @@ -192,24 +186,6 @@ proto_cmds = { } -# hack around format_exc function of traceback module to grant -# backward compatibility with python < 2.4 -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). @@ -590,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 @@ -1227,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: @@ -1438,7 +1392,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] @@ -1469,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 @@ -1866,7 +1817,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 +1954,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() diff --git a/addons/document_webdav/dav_fs.py b/addons/document_webdav/dav_fs.py index af8738ef0da..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") @@ -522,7 +523,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 +535,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.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 diff --git a/addons/document_webdav/webdav_server.py b/addons/document_webdav/webdav_server.py index 34c255a2b3e..8a83e25456d 100644 --- a/addons/document_webdav/webdav_server.py +++ b/addons/document_webdav/webdav_server.py @@ -214,14 +214,21 @@ 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: - 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) diff --git a/addons/project_caldav/project_caldav.py b/addons/project_caldav/project_caldav.py index 65e61c6e4cd..ea6c02bc2cb 100644 --- a/addons/project_caldav/project_caldav.py +++ b/addons/project_caldav/project_caldav.py @@ -21,13 +21,19 @@ 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,