def updateAttendeeData(from_component, to_component): """ Called when processing a REPLY only. Copy the PARTSTAT of the Attendee in the from_component to the matching ATTENDEE in the to_component. Ignore if no match found. Also update the private comments. For VPOLL we need to copy POLL-ITEM-ID response values into the actual matching polled sub-components as VOTER properties. @param from_component: component to copy from @type from_component: L{Component} @param to_component: component to copy to @type to_component: L{Component} """ # Track what changed partstat_changed = False private_comment_changed = False # Get REQUEST-STATUS as we need to write that into the saved ATTENDEE property reqstatus = tuple(from_component.properties("REQUEST-STATUS")) if reqstatus: reqstatus = ",".join(status.value()[0] for status in reqstatus) else: reqstatus = "2.0" # Get attendee in from_component - there MUST be only one attendees = tuple(from_component.properties(from_component.recipientPropertyName())) if len(attendees) != 1: log.error("There must be one and only one ATTENDEE property in a REPLY\n%s" % (str(from_component),)) return None, False, False attendee = attendees[0] partstat = attendee.parameterValue("PARTSTAT", "NEEDS-ACTION") # Now find matching ATTENDEE in to_component existing_attendee = to_component.getAttendeeProperty((attendee.value(),)) if existing_attendee: oldpartstat = existing_attendee.parameterValue("PARTSTAT", "NEEDS-ACTION") existing_attendee.setParameter("PARTSTAT", partstat) existing_attendee.setParameter("SCHEDULE-STATUS", reqstatus) partstat_changed = (oldpartstat != partstat) # Always delete RSVP on PARTSTAT change if partstat_changed: try: existing_attendee.removeParameter("RSVP") except KeyError: pass # Handle attendee comments if config.Scheduling.CalDAV.get("EnablePrivateComments", True): # Look for X-CALENDARSERVER-PRIVATE-COMMENT property in iTIP component (State 1 in spec) attendee_comment = tuple(from_component.properties("X-CALENDARSERVER-PRIVATE-COMMENT")) attendee_comment = attendee_comment[0] if len(attendee_comment) else None # Look for matching X-CALENDARSERVER-ATTENDEE-COMMENT property in existing data (State 2 in spec) private_comments = tuple(to_component.properties("X-CALENDARSERVER-ATTENDEE-COMMENT")) for comment in private_comments: attendeeref = comment.parameterValue("X-CALENDARSERVER-ATTENDEE-REF") if attendeeref == attendee.value(): private_comment = comment break else: private_comment = None else: attendee_comment = None private_comment = None # Now do update logic if attendee_comment is None and private_comment is None: # Nothing to do pass elif attendee_comment is None and private_comment is not None: # We now remove the private comment on the organizer's side if the attendee removed it to_component.removeProperty(private_comment) private_comment_changed = True elif attendee_comment is not None and private_comment is None: # Add new property private_comment = Property( "X-CALENDARSERVER-ATTENDEE-COMMENT", attendee_comment.value(), params={ "X-CALENDARSERVER-ATTENDEE-REF": attendee.value(), "X-CALENDARSERVER-DTSTAMP": DateTime.getNowUTC().getText(), } ) to_component.addProperty(private_comment) private_comment_changed = True else: # Only change if different if private_comment.value() != attendee_comment.value(): # Remove all property parameters private_comment.removeAllParameters() # Add default parameters private_comment.setParameter("X-CALENDARSERVER-ATTENDEE-REF", attendee.value()) private_comment.setParameter("X-CALENDARSERVER-DTSTAMP", DateTime.getNowUTC().getText()) # Set new value private_comment.setValue(attendee_comment.value()) private_comment_changed = True # Do VPOLL transfer if from_component.name() == "VPOLL": # TODO: figure out how to report changes back iTipProcessing.updateVPOLLData(from_component, to_component, attendee) return attendee.value(), partstat_changed, private_comment_changed
def updateAttendeeData(from_component, to_component): """ Copy the PARTSTAT of the Attendee in the from_component to the matching ATTENDEE in the to_component. Ignore if no match found. Also update the private comments. @param from_component: @type from_component: @param to_component: @type to_component: """ # Track what changed partstat_changed = False private_comment_changed = False # Get REQUEST-STATUS as we need to write that into the saved ATTENDEE property reqstatus = tuple(from_component.properties("REQUEST-STATUS")) if reqstatus: reqstatus = ",".join(status.value()[0] for status in reqstatus) else: reqstatus = "2.0" # Get attendee in from_component - there MUST be only one attendees = tuple(from_component.properties("ATTENDEE")) if len(attendees) != 1: log.error("There must be one and only one ATTENDEE property in a REPLY\n%s" % (str(from_component),)) return None, False, False attendee = attendees[0] partstat = attendee.params().get("PARTSTAT", ("NEEDS-ACTION",))[0] # Now find matching ATTENDEE in to_component existing_attendee = to_component.getAttendeeProperty((attendee.value(),)) if existing_attendee: oldpartstat = existing_attendee.params().get("PARTSTAT", ("NEEDS-ACTION",))[0] existing_attendee.params()["PARTSTAT"] = [partstat] existing_attendee.params()["SCHEDULE-STATUS"] = [reqstatus] partstat_changed = (oldpartstat != partstat) # Always delete RSVP on PARTSTAT change if partstat_changed: try: del existing_attendee.params()["RSVP"] except KeyError: pass # Handle attendee comments if config.Scheduling.CalDAV.get("EnablePrivateComments", True): # Look for X-CALENDARSERVER-PRIVATE-COMMENT property in iTIP component (State 1 in spec) attendee_comment = tuple(from_component.properties("X-CALENDARSERVER-PRIVATE-COMMENT")) attendee_comment = attendee_comment[0] if len(attendee_comment) else None # Look for matching X-CALENDARSERVER-ATTENDEE-COMMENT property in existing data (State 2 in spec) private_comments = tuple(to_component.properties("X-CALENDARSERVER-ATTENDEE-COMMENT")) for comment in private_comments: params = comment.params()["X-CALENDARSERVER-ATTENDEE-REF"] if len(params) != 1: log.error("Must be one and only one X-CALENDARSERVER-ATTENDEE-REF parameter in X-CALENDARSERVER-ATTENDEE-COMMENT") params = (None,) param = params[0] if param == attendee.value(): private_comment = comment break else: private_comment = None else: attendee_comment = None private_comment = None # Now do update logic if attendee_comment is None and private_comment is None: # Nothing to do pass elif attendee_comment is None and private_comment is not None: # Remove all property parameters private_comment.params().clear() # Add default parameters private_comment.params()["X-CALENDARSERVER-ATTENDEE-REF"] = [attendee.value()] private_comment.params()["X-CALENDARSERVER-DTSTAMP"] = [dateTimeToString(datetime.datetime.now(tz=utc))] # Set value empty private_comment.setValue("") private_comment_changed = True elif attendee_comment is not None and private_comment is None: # Add new property private_comment = Property( "X-CALENDARSERVER-ATTENDEE-COMMENT", attendee_comment.value(), params = { "X-CALENDARSERVER-ATTENDEE-REF": [attendee.value()], "X-CALENDARSERVER-DTSTAMP": [dateTimeToString(datetime.datetime.now(tz=utc))], } ) to_component.addProperty(private_comment) private_comment_changed = True else: # Only change if different if private_comment.value() != attendee_comment.value(): # Remove all property parameters private_comment.params().clear() # Add default parameters private_comment.params()["X-CALENDARSERVER-ATTENDEE-REF"] = [attendee.value()] private_comment.params()["X-CALENDARSERVER-DTSTAMP"] = [dateTimeToString(datetime.datetime.now(tz=utc))] # Set new value private_comment.setValue(attendee_comment.value()) private_comment_changed = True return attendee.value(), partstat_changed, private_comment_changed