# HG changeset patch # User Paul Boddie # Date 1432122083 -7200 # Node ID 1f47884f8d96978c22dbd23cfb73ac16c8d2a186 # Parent ec3432dd46866a01dc8026f0f279573f16be2121 Introduced common outgoing message handling to update free/busy records. diff -r ec3432dd4686 -r 1f47884f8d96 imiptools/handlers/__init__.py --- a/imiptools/handlers/__init__.py Tue May 19 23:40:57 2015 +0200 +++ b/imiptools/handlers/__init__.py Wed May 20 13:41:23 2015 +0200 @@ -84,6 +84,9 @@ except OSError: self.publisher = None + def get_definitive_object(self, from_organiser): + return from_organiser and self.obj or self.get_object() + def set_object(self, obj): self.obj = obj self.uid = self.obj.get_uid() @@ -197,12 +200,18 @@ # Organisers employ a special transparency if not attending. - if for_organiser or not attr or attr.get("PARTSTAT") != "DECLINED": - self.update_freebusy(freebusy, periods, transp=( - for_organiser and not (attr and attr.get("PARTSTAT")) and "ORG" or None)) + if self.is_participating(attr, for_organiser): + self.update_freebusy(freebusy, periods, + transp=self.get_transparency(attr, for_organiser)) else: self.remove_from_freebusy(freebusy) + def is_participating(self, attr, as_organiser=False): + return as_organiser or not attr or attr.get("PARTSTAT") != "DECLINED" + + def get_transparency(self, attr, as_organiser=False): + return as_organiser and not (attr and attr.get("PARTSTAT")) and "ORG" or None + # Convenience methods for updating stored free/busy information. def update_freebusy_from_participant(self, participant_item, for_organiser): @@ -215,6 +224,9 @@ participant, participant_attr = participant_item + # A user does not store free/busy information for themself as another + # party. + if participant == self.user: return @@ -223,7 +235,7 @@ # Obtain the stored object if the current object is not issued by the # organiser. - obj = for_organiser and self.obj or self.get_object() + obj = self.get_definitive_object(for_organiser) if not obj: return diff -r ec3432dd4686 -r 1f47884f8d96 imiptools/handlers/common.py --- a/imiptools/handlers/common.py Tue May 19 23:40:57 2015 +0200 +++ b/imiptools/handlers/common.py Wed May 20 13:41:23 2015 +0200 @@ -19,7 +19,8 @@ this program. If not, see . """ -from imiptools.data import get_address, get_uri, make_freebusy, to_part +from imiptools.data import get_address, get_uri, make_freebusy, to_part, \ + uri_dict from imiptools.dates import format_datetime from imiptools.period import Period @@ -66,4 +67,57 @@ self.add_result("REPLY", [get_address(organiser)], to_part("REPLY", responses)) +class Outgoing: + + "Common outgoing message handling functionality." + + def update_event_in_freebusy(self, from_organiser=True): + + "Update free/busy information when handling an object." + + freebusy = self.store.get_freebusy(self.user) + + # Use the stored event in case the reply is incomplete, as is seen + # when Claws sends a REPLY for an object originally employing + # recurrence information. + + obj = self.get_definitive_object(from_organiser) + if not obj: + return False # although this should not happen + + # If newer than any old version, discard old details from the + # free/busy record and check for suitability. + + # Interpretation of periods can depend on the time zone. + + periods = obj.get_periods(self.get_tzid(), self.get_window_end()) + + # Obtain the attendance attributes for this user, if available. + + attendees = uri_dict(self.obj.get_value_map("ATTENDEE")) + self.update_freebusy_for_participant(freebusy, periods, attendees.get(self.user), from_organiser) + + # Remove original recurrence details replaced by additional + # recurrences, as well as obsolete additional recurrences. + + self.remove_freebusy_for_recurrences(freebusy, self.store.get_recurrences(self.user, self.uid)) + self.store.set_freebusy(self.user, freebusy) + + if self.publisher and self.is_sharing(): + self.publisher.set_freebusy(self.user, freebusy) + + return True + + def remove_event_from_freebusy(self): + + "Remove free/busy information when handling an object." + + freebusy = self.store.get_freebusy(self.user) + self.remove_from_freebusy(freebusy) + self.remove_freebusy_for_recurrences(freebusy) + self.store.set_freebusy(self.user, freebusy) + + if self.publisher and self.is_sharing(): + self.publisher.set_freebusy(self.user, freebusy) + # vim: tabstop=4 expandtab shiftwidth=4 diff -r ec3432dd4686 -r 1f47884f8d96 imiptools/handlers/person_outgoing.py --- a/imiptools/handlers/person_outgoing.py Tue May 19 23:40:57 2015 +0200 +++ b/imiptools/handlers/person_outgoing.py Wed May 20 13:41:23 2015 +0200 @@ -22,25 +22,27 @@ from imiptools.data import uri_dict, uri_item, uri_values from imiptools.handlers import Handler +from imiptools.handlers.common import Outgoing -class PersonHandler(Handler): +class PersonHandler(Handler, Outgoing): "Handling mechanisms specific to people." def set_identity(self, from_organiser=True): """ - Set the current user for the current object. + Set the current user for the current object. It is usually set when + initialising the handler, using the recipient details, but outgoing + messages do not reference the recipient in this way. """ self.user, attr = uri_item(self.obj.get_item(from_organiser and "ORGANIZER" or "ATTENDEE")) - def _record(self, from_organiser=True, update_freebusy=False): + def _record(self, from_organiser=True): """ Record details from the current object given a message originating - from an organiser if 'from_organiser' is set to a true value, updating - free/busy information if 'update_freebusy' is set to a true value. + from an organiser if 'from_organiser' is set to a true value. """ self.set_identity(from_organiser) @@ -76,44 +78,16 @@ # Update free/busy information. - if update_freebusy: - - freebusy = self.store.get_freebusy(self.user) - - # Use the stored event in case the reply is incomplete, as is seen - # when Claws sends a REPLY for an object originally employing - # recurrence information. - - obj = self.get_object() - if not obj: - return False # although this should not happen - - # If newer than any old version, discard old details from the - # free/busy record and check for suitability. - - # Interpretation of periods can depend on the time zone. - - periods = obj.get_periods(self.get_tzid(), self.get_window_end()) - - # Obtain the attendance attributes for this user, if available. - - attendees = uri_dict(self.obj.get_value_map("ATTENDEE")) - self.update_freebusy_for_participant(freebusy, periods, attendees.get(self.user), from_organiser) - - # Remove original recurrence details replaced by additional - # recurrences, as well as obsolete additional recurrences. - - self.remove_freebusy_for_recurrences(freebusy, self.store.get_recurrences(self.user, self.uid)) - self.store.set_freebusy(self.user, freebusy) - - if self.publisher and self.is_sharing(): - self.publisher.set_freebusy(self.user, freebusy) + self.update_event_in_freebusy() return True - def _remove(self, from_organiser=True, update_freebusy=False): + def _remove(self, from_organiser=True): - "Remove free/busy information for any unprocessed object." + """ + Remove details from the current object given a message originating + from an organiser if 'from_organiser' is set to a true value. + """ self.set_identity(from_organiser) @@ -168,16 +142,8 @@ # Update free/busy information. - if update_freebusy: - freebusy = self.store.get_freebusy(self.user) - self.remove_from_freebusy(freebusy) - self.remove_freebusy_for_recurrences(freebusy) - - if cancel_entire_event or self.user in given_attendees: - self.store.set_freebusy(self.user, freebusy) - - if self.publisher and self.is_sharing(): - self.publisher.set_freebusy(self.user, freebusy) + if cancel_entire_event or self.user in given_attendees: + self.remove_event_from_freebusy() return True @@ -189,7 +155,7 @@ pass def cancel(self): - self._remove(True, True) + self._remove(True) def counter(self): pass @@ -198,16 +164,16 @@ pass def publish(self): - self._record(True, True) + self._record(True) def refresh(self): pass def reply(self): - self._record(False, True) + self._record(False) def request(self): - self._record(True, True) + self._record(True) # Handler registry. diff -r ec3432dd4686 -r 1f47884f8d96 imiptools/handlers/resource.py --- a/imiptools/handlers/resource.py Tue May 19 23:40:57 2015 +0200 +++ b/imiptools/handlers/resource.py Wed May 20 13:41:23 2015 +0200 @@ -21,13 +21,24 @@ from imiptools.data import get_address, get_uri, to_part from imiptools.handlers import Handler -from imiptools.handlers.common import CommonFreebusy -from imiptools.period import remove_affected_period +from imiptools.handlers.common import CommonFreebusy, Outgoing -class ResourceHandler(Handler): +class ResourceHandler(Handler, Outgoing): "Handling mechanisms specific to resources." + def set_participation(self, scheduled): + + "Set the user's participation in the current object if 'scheduled'." + + attendee_attr = self.obj.get_value_map("ATTENDEE").get(self.user) + attendee_attr["PARTSTAT"] = scheduled and "ACCEPTED" or "DECLINED" + if attendee_attr.has_key("RSVP"): + del attendee_attr["RSVP"] + if self.messenger and self.messenger.sender != get_address(self.user): + attendee_attr["SENT-BY"] = get_uri(self.messenger.sender) + return attendee_attr + def _record_and_respond(self, handle_for_attendee): """ @@ -76,13 +87,9 @@ freebusy = self.store.get_freebusy(self.user) scheduled = self.can_schedule(freebusy, periods) - attendee_attr = self.obj.get_value_map("ATTENDEE").get(self.user) + # Update the participation of the resource in the object. - attendee_attr["PARTSTAT"] = scheduled and "ACCEPTED" or "DECLINED" - if attendee_attr.has_key("RSVP"): - del attendee_attr["RSVP"] - if self.messenger and self.messenger.sender != get_address(self.user): - attendee_attr["SENT-BY"] = get_uri(self.messenger.sender) + attendee_attr = self.set_participation(scheduled) # Set the complete event or an additional occurrence. @@ -94,28 +101,14 @@ if not self.recurrenceid: self.store.remove_recurrences(self.user, self.uid) - # Only update free/busy details if the event is scheduled. - - if scheduled: - self.update_freebusy(freebusy, periods) - else: - self.remove_from_freebusy(freebusy) + # Update free/busy information. - # Remove original recurrence details replaced by additional - # recurrences, as well as obsolete additional recurrences. + self.update_event_in_freebusy(from_organiser=False) - self.remove_freebusy_for_recurrences(freebusy, self.store.get_recurrences(self.user, self.uid)) - self.store.set_freebusy(self.user, freebusy) - - if self.publisher and self.is_sharing(): - self.publisher.set_freebusy(self.user, freebusy) - - # Make a version of the request with just this attendee. + # Make a version of the object with just this attendee, update the + # DTSTAMP in the response, and return the object for sending. self.obj["ATTENDEE"] = [(self.user, attendee_attr)] - - # Update the DTSTAMP in the response. - self.update_dtstamp() return event @@ -129,13 +122,11 @@ self.store.set_event(self.user, self.uid, self.recurrenceid, self.obj.to_node()) self.store.cancel_event(self.user, self.uid, self.recurrenceid) - freebusy = self.store.get_freebusy(self.user) - self.remove_from_freebusy(freebusy) - self.remove_freebusy_for_recurrences(freebusy) - self.store.set_freebusy(self.user, freebusy) + # Update free/busy information. - if self.publisher and self.is_sharing(): - self.publisher.set_freebusy(self.user, freebusy) + self.remove_event_from_freebusy() + + # No response is required. return None