def occurrences(self, start=None, end=None): # XXX fit-to-period tz = self.item.get_value(PidLidTimeZoneStruct) recurrences = self.recurrences if start and end: recurrences = recurrences.between(_utils._from_gmt(start, tz), _utils._from_gmt(end, tz)) start_exc_ext = {} for exc, ext in zip(self.exceptions, self.extended_exceptions): start_exc_ext[exc['start_datetime']] = exc, ext for d in recurrences: startdatetime_val = _utils.unixtime_to_rectime( time.mktime(d.timetuple())) subject = self.item.subject location = self.item.location if startdatetime_val in start_exc_ext: exc, ext = start_exc_ext[startdatetime_val] minutes = exc['end_datetime'] - startdatetime_val subject = ext.get('subject', subject) location = ext.get('location', location) else: minutes = self.endtime_offset - self.starttime_offset d = _utils._to_gmt(d, tz) occ = Occurrence(self.item, d, d + datetime.timedelta(minutes=minutes), subject, location) if (not start or occ.end > start) and (not end or occ.start < end): yield occ
def delete_exception(self, basedate, item, copytags): tz = item.get(PidLidTimeZoneStruct) basedate2 = _utils._from_gmt(basedate, tz) basedate_val = _utils.unixtime_to_rectime( time.mktime(basedate2.timetuple())) if self.is_exception(basedate): self.modify_exception(basedate, item, copytags) for i, exc in enumerate(self.exceptions): if exc['original_start_date'] == basedate_val: break self.modified_instance_count -= 1 self.modified_instance_dates = [ m for m in self.modified_instance_dates if m != exc['start_datetime'] ] del self.exceptions[i] del self.extended_exceptions[i] else: self.deleted_instance_count += 1 self.deleted_instance_dates.append(basedate_val) self.deleted_instance_dates.sort() self._save() self._update_calitem()
def create_exception2(self, basedate): # TODO merge with create_exception # TODO create embedded item tz = self.item.get(PidLidTimeZoneStruct) basedate = _utils._from_gmt(basedate, tz) basedate_val = _utils.unixtime_to_rectime( time.mktime(basedate.timetuple())) - self.starttime_offset # update blob self.deleted_instance_count += 1 self.deleted_instance_dates.append(basedate_val) self.deleted_instance_dates.sort() self.modified_instance_count += 1 self.modified_instance_dates.append(basedate_val) self.modified_instance_dates.sort() exception = { 'start_datetime': basedate_val + self.starttime_offset, 'end_datetime': basedate_val + self.starttime_offset + 30, # TODO 'original_start_date': basedate_val, 'override_flags': 0, } self.exceptions.append(exception) # no evidence of sorting self.extended_exceptions.append({}) self._save() # update calitem self._update_calitem()
def create_exception(self, basedate, item, copytags=None, merge=False): tz = item.get(PidLidTimeZoneStruct) cal_item = self.item # create embedded item message_flags = MSGFLAG_READ if item.get(PR_MESSAGE_FLAGS) == 0: # XXX wut/php compat message_flags |= MSGFLAG_UNSENT message = cal_item.create_item(message_flags) self._update_embedded(basedate, message, item, copytags, create=True) message[ PidLidResponseStatus] = respDeclined | respOrganized # XXX php bug for merge case? if copytags: message[PidLidBusyStatus] = 0 # copy over recipients (XXX check php delta stuff..) table = item.mapiobj.OpenProperty(PR_MESSAGE_RECIPIENTS, IID_IMAPITable, MAPI_UNICODE, 0) table.SetColumns(_meetingrequest.RECIP_PROPS, 0) recips = list(table.QueryRows(-1, 0)) for recip in recips: flags = PpropFindProp(recip, PR_RECIPIENT_FLAGS) if not flags: recip.append( SPropValue(PR_RECIPIENT_FLAGS, recipExceptionalResponse | recipSendable)) if copytags: for recip in recips: recip.append( SPropValue(PR_RECIPIENT_FLAGS, recipExceptionalDeleted | recipSendable)) recip.append(SPropValue(PR_RECIPIENT_TRACKSTATUS, 0)) organiser = _meetingrequest._organizer_props(message, item) if organiser and not merge: # XXX merge -> initialize? recips.insert(0, organiser) message.mapiobj.ModifyRecipients(MODRECIP_ADD, recips) message.mapiobj.SaveChanges(KEEP_OPEN_READWRITE) message._attobj.SaveChanges(KEEP_OPEN_READWRITE) cal_item.mapiobj.SaveChanges(KEEP_OPEN_READWRITE) # XXX attachments? # update blob self.deleted_instance_count += 1 deldate = _utils._from_gmt(basedate, tz) deldate_val = _utils.unixtime_to_rectime( time.mktime(deldate.timetuple())) self.deleted_instance_dates.append(deldate_val) self.deleted_instance_dates.sort() self.modified_instance_count += 1 moddate = message.prop(PidLidAppointmentStartWhole).value daystart = moddate - datetime.timedelta( hours=moddate.hour, minutes=moddate.minute) # XXX different approach in php? seconds? localdaystart = _utils._from_gmt(daystart, tz) moddate_val = _utils.unixtime_to_rectime( time.mktime(localdaystart.timetuple())) self.modified_instance_dates.append(moddate_val) self.modified_instance_dates.sort() startdate = _utils._from_gmt( message.prop(PidLidAppointmentStartWhole).value, tz) startdate_val = _utils.unixtime_to_rectime( time.mktime(startdate.timetuple())) enddate = _utils._from_gmt( message.prop(PidLidAppointmentEndWhole).value, tz) enddate_val = _utils.unixtime_to_rectime( time.mktime(enddate.timetuple())) exception = {} extended_exception = {} self._update_exceptions(cal_item, message, startdate_val, enddate_val, deldate_val, exception, extended_exception, copytags, create=True) self.exceptions.append(exception) # no evidence of sorting self.extended_exceptions.append(extended_exception) self._save() # update calitem self._update_calitem()
def _update_embedded(self, basedate, message, item, copytags=None, create=False): basetime = basedate + datetime.timedelta(minutes=self.starttime_offset) cal_item = self.item if copytags: props = item.mapiobj.GetProps(copytags, 0) else: # XXX remove? props = [ p.mapiobj for p in item.props() if p.proptag != PR_ICON_INDEX ] message.mapiobj.SetProps(props) message[ PR_MESSAGE_CLASS_W] = u'IPM.OLE.CLASS.{00061055-0000-0000-C000-000000000046}' message[PidLidExceptionReplaceTime] = basetime intended_busystatus = cal_item.get( PidLidIntendedBusyStatus) # XXX tentative? merge with modify_exc? if intended_busystatus is not None: message[PidLidBusyStatus] = intended_busystatus sub_type = cal_item.get(PidLidAppointmentSubType) if sub_type is not None: message[PidLidAppointmentSubType] = sub_type props = [ SPropValue(PR_ATTACHMENT_FLAGS, 2), # XXX cannot find spec SPropValue(PR_ATTACHMENT_HIDDEN, True), SPropValue(PR_ATTACHMENT_LINKID, 0), SPropValue(PR_ATTACH_FLAGS, 0), SPropValue(PR_ATTACH_METHOD, ATTACH_EMBEDDED_MSG), SPropValue(PR_DISPLAY_NAME_W, u'Exception'), ] start = message.get(PidLidAppointmentStartWhole) if start is not None: start_local = unixtime( time.mktime(_utils._from_gmt( start, self.tz).timetuple())) # XXX why local?? props.append(SPropValue(PR_EXCEPTION_STARTTIME, start_local)) end = message.prop(PidLidAppointmentEndWhole).value if end is not None: end_local = unixtime( time.mktime(_utils._from_gmt( end, self.tz).timetuple())) # XXX why local?? props.append(SPropValue(PR_EXCEPTION_ENDTIME, end_local)) message._attobj.SetProps(props) if not create: # XXX php bug? props = props[:-2] message.mapiobj.SetProps(props) message.mapiobj.SaveChanges(KEEP_OPEN_READWRITE) message._attobj.SaveChanges(KEEP_OPEN_READWRITE)
def modify_exception2(self, basedate, subject=None, start=None, end=None, location=None): # TODO merge with modify_exception tz = self.item.get(PidLidTimeZoneStruct) # update embedded item for message in self.item.items(): # XXX no cal_item? to helper replacetime = message.get(PidLidExceptionReplaceTime) if replacetime and replacetime.date() == basedate.date(): if subject is not None: message.subject = subject # TODO set other args message._attobj.SaveChanges(KEEP_OPEN_READWRITE) break # update blob basedate_val = _utils.unixtime_to_rectime( time.mktime(_utils._from_gmt(basedate, tz).timetuple())) for i, exception in enumerate(self.exceptions): if exception['original_start_date'] in ( basedate_val, basedate_val - self.starttime_offset): # TODO pick one extended_exception = self.extended_exceptions[i] break if subject is not None: exception['override_flags'] |= ARO_SUBJECT exception['subject'] = subject.encode('cp1252', 'replace') extended_exception['subject'] = subject if location is not None: exception['override_flags'] |= ARO_LOCATION exception['location'] = location.encode('cp1252', 'replace') extended_exception['location'] = location if start: startdate_val = _utils.unixtime_to_rectime( time.mktime(_utils._from_gmt(start, tz).timetuple())) exception['start_datetime'] = startdate_val extended_exception['start_datetime'] = startdate_val if end: enddate_val = _utils.unixtime_to_rectime( time.mktime(_utils._from_gmt(end, tz).timetuple())) exception['end_datetime'] = enddate_val extended_exception['end_datetime'] = enddate_val extended_exception['start_datetime'] = exception[ 'start_datetime'] # TODO on creation? extended_exception['end_datetime'] = exception['end_datetime'] extended_exception['original_start_date'] = exception[ 'original_start_date'] self._save() # update calitem self._update_calitem()
def modify_exception(self, basedate, item, copytags=None): # XXX 'item' too MR specific tz = item.get(PidLidTimeZoneStruct) cal_item = self.item # update embedded item for message in cal_item.items(): # XXX no cal_item? to helper replacetime = message.get(PidLidExceptionReplaceTime) if replacetime and replacetime.date() == basedate.date(): self._update_embedded(basedate, message, item, copytags) icon_index = item.get(PR_ICON_INDEX) if not copytags and icon_index is not None: message[PR_ICON_INDEX] = icon_index message._attobj.SaveChanges(KEEP_OPEN_READWRITE) break else: return # XXX exception if copytags: # XXX bug in php code? (setallrecipients, !empty..) message[PidLidBusyStatus] = 0 table = message.mapiobj.OpenProperty(PR_MESSAGE_RECIPIENTS, IID_IMAPITable, MAPI_UNICODE, 0) table.SetColumns(_meetingrequest.RECIP_PROPS, 0) recips = list(table.QueryRows(-1, 0)) for recip in recips: flags = PpropFindProp(recip, PR_RECIPIENT_FLAGS) if flags and flags.Value != (recipOrganizer | recipSendable): flags.Value = recipExceptionalDeleted | recipSendable trackstatus = PpropFindProp(recip, PR_RECIPIENT_TRACKSTATUS) recip.append(SPropValue(PR_RECIPIENT_TRACKSTATUS, 0)) message.mapiobj.ModifyRecipients(MODRECIP_MODIFY, recips) message.mapiobj.SaveChanges(KEEP_OPEN_READWRITE) message._attobj.SaveChanges(KEEP_OPEN_READWRITE) cal_item.mapiobj.SaveChanges(KEEP_OPEN_READWRITE) # update blob basedate_val = _utils.unixtime_to_rectime( time.mktime(_utils._from_gmt(basedate, tz).timetuple())) startdate = _utils._from_gmt( message.prop(PidLidAppointmentStartWhole).value, tz) startdate_val = _utils.unixtime_to_rectime( time.mktime(startdate.timetuple())) enddate = _utils._from_gmt( message.prop(PidLidAppointmentEndWhole).value, tz) enddate_val = _utils.unixtime_to_rectime( time.mktime(enddate.timetuple())) for i, exception in enumerate(self.exceptions): if exception[ 'original_start_date'] == basedate_val: # TODO offset, as below? current_startdate_val = exception[ 'start_datetime'] - self.starttime_offset for j, val in enumerate(self.modified_instance_dates): if val == current_startdate_val: self.modified_instance_dates[ j] = startdate_val - self.starttime_offset self.modified_instance_dates.sort() break extended_exception = self.extended_exceptions[i] self._update_exceptions(cal_item, message, startdate_val, enddate_val, basedate_val, exception, extended_exception, copytags, create=False) self._save() # update calitem self._update_calitem()