def updateVPOLLData(from_component, to_component, attendee): """ Update VPOLL sub-components with voter's response. @param from_component: component to copy from @type from_component: L{Component} @param to_component: component to copy to @type to_component: L{Component} @param attendee: attendee being processed @type attendee: L{Property} """ responses = {} for prop in from_component.properties("POLL-ITEM-ID"): responses[prop.value()] = prop for component in to_component.subcomponents(): if component.name() in ignoredComponents: continue poll_item_id = component.propertyValue("POLL-ITEM-ID") if poll_item_id is None: continue voter = component.getVoterProperty((attendee.value(),)) # If no response - remove if poll_item_id not in responses or not responses[poll_item_id].hasParameter("RESPONSE"): if voter is not None: component.removeProperty(voter) continue # Add or update voter if voter is None: voter = Property("VOTER", attendee.value()) component.addProperty(voter) voter.setParameter("RESPONSE", responses[poll_item_id].parameterValue("RESPONSE"))
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