1.1 --- a/imiptools/handlers/__init__.py Tue May 19 23:40:57 2015 +0200
1.2 +++ b/imiptools/handlers/__init__.py Wed May 20 13:41:23 2015 +0200
1.3 @@ -84,6 +84,9 @@
1.4 except OSError:
1.5 self.publisher = None
1.6
1.7 + def get_definitive_object(self, from_organiser):
1.8 + return from_organiser and self.obj or self.get_object()
1.9 +
1.10 def set_object(self, obj):
1.11 self.obj = obj
1.12 self.uid = self.obj.get_uid()
1.13 @@ -197,12 +200,18 @@
1.14
1.15 # Organisers employ a special transparency if not attending.
1.16
1.17 - if for_organiser or not attr or attr.get("PARTSTAT") != "DECLINED":
1.18 - self.update_freebusy(freebusy, periods, transp=(
1.19 - for_organiser and not (attr and attr.get("PARTSTAT")) and "ORG" or None))
1.20 + if self.is_participating(attr, for_organiser):
1.21 + self.update_freebusy(freebusy, periods,
1.22 + transp=self.get_transparency(attr, for_organiser))
1.23 else:
1.24 self.remove_from_freebusy(freebusy)
1.25
1.26 + def is_participating(self, attr, as_organiser=False):
1.27 + return as_organiser or not attr or attr.get("PARTSTAT") != "DECLINED"
1.28 +
1.29 + def get_transparency(self, attr, as_organiser=False):
1.30 + return as_organiser and not (attr and attr.get("PARTSTAT")) and "ORG" or None
1.31 +
1.32 # Convenience methods for updating stored free/busy information.
1.33
1.34 def update_freebusy_from_participant(self, participant_item, for_organiser):
1.35 @@ -215,6 +224,9 @@
1.36
1.37 participant, participant_attr = participant_item
1.38
1.39 + # A user does not store free/busy information for themself as another
1.40 + # party.
1.41 +
1.42 if participant == self.user:
1.43 return
1.44
1.45 @@ -223,7 +235,7 @@
1.46 # Obtain the stored object if the current object is not issued by the
1.47 # organiser.
1.48
1.49 - obj = for_organiser and self.obj or self.get_object()
1.50 + obj = self.get_definitive_object(for_organiser)
1.51 if not obj:
1.52 return
1.53
2.1 --- a/imiptools/handlers/common.py Tue May 19 23:40:57 2015 +0200
2.2 +++ b/imiptools/handlers/common.py Wed May 20 13:41:23 2015 +0200
2.3 @@ -19,7 +19,8 @@
2.4 this program. If not, see <http://www.gnu.org/licenses/>.
2.5 """
2.6
2.7 -from imiptools.data import get_address, get_uri, make_freebusy, to_part
2.8 +from imiptools.data import get_address, get_uri, make_freebusy, to_part, \
2.9 + uri_dict
2.10 from imiptools.dates import format_datetime
2.11 from imiptools.period import Period
2.12
2.13 @@ -66,4 +67,57 @@
2.14
2.15 self.add_result("REPLY", [get_address(organiser)], to_part("REPLY", responses))
2.16
2.17 +class Outgoing:
2.18 +
2.19 + "Common outgoing message handling functionality."
2.20 +
2.21 + def update_event_in_freebusy(self, from_organiser=True):
2.22 +
2.23 + "Update free/busy information when handling an object."
2.24 +
2.25 + freebusy = self.store.get_freebusy(self.user)
2.26 +
2.27 + # Use the stored event in case the reply is incomplete, as is seen
2.28 + # when Claws sends a REPLY for an object originally employing
2.29 + # recurrence information.
2.30 +
2.31 + obj = self.get_definitive_object(from_organiser)
2.32 + if not obj:
2.33 + return False # although this should not happen
2.34 +
2.35 + # If newer than any old version, discard old details from the
2.36 + # free/busy record and check for suitability.
2.37 +
2.38 + # Interpretation of periods can depend on the time zone.
2.39 +
2.40 + periods = obj.get_periods(self.get_tzid(), self.get_window_end())
2.41 +
2.42 + # Obtain the attendance attributes for this user, if available.
2.43 +
2.44 + attendees = uri_dict(self.obj.get_value_map("ATTENDEE"))
2.45 + self.update_freebusy_for_participant(freebusy, periods, attendees.get(self.user), from_organiser)
2.46 +
2.47 + # Remove original recurrence details replaced by additional
2.48 + # recurrences, as well as obsolete additional recurrences.
2.49 +
2.50 + self.remove_freebusy_for_recurrences(freebusy, self.store.get_recurrences(self.user, self.uid))
2.51 + self.store.set_freebusy(self.user, freebusy)
2.52 +
2.53 + if self.publisher and self.is_sharing():
2.54 + self.publisher.set_freebusy(self.user, freebusy)
2.55 +
2.56 + return True
2.57 +
2.58 + def remove_event_from_freebusy(self):
2.59 +
2.60 + "Remove free/busy information when handling an object."
2.61 +
2.62 + freebusy = self.store.get_freebusy(self.user)
2.63 + self.remove_from_freebusy(freebusy)
2.64 + self.remove_freebusy_for_recurrences(freebusy)
2.65 + self.store.set_freebusy(self.user, freebusy)
2.66 +
2.67 + if self.publisher and self.is_sharing():
2.68 + self.publisher.set_freebusy(self.user, freebusy)
2.69 +
2.70 # vim: tabstop=4 expandtab shiftwidth=4
3.1 --- a/imiptools/handlers/person_outgoing.py Tue May 19 23:40:57 2015 +0200
3.2 +++ b/imiptools/handlers/person_outgoing.py Wed May 20 13:41:23 2015 +0200
3.3 @@ -22,25 +22,27 @@
3.4
3.5 from imiptools.data import uri_dict, uri_item, uri_values
3.6 from imiptools.handlers import Handler
3.7 +from imiptools.handlers.common import Outgoing
3.8
3.9 -class PersonHandler(Handler):
3.10 +class PersonHandler(Handler, Outgoing):
3.11
3.12 "Handling mechanisms specific to people."
3.13
3.14 def set_identity(self, from_organiser=True):
3.15
3.16 """
3.17 - Set the current user for the current object.
3.18 + Set the current user for the current object. It is usually set when
3.19 + initialising the handler, using the recipient details, but outgoing
3.20 + messages do not reference the recipient in this way.
3.21 """
3.22
3.23 self.user, attr = uri_item(self.obj.get_item(from_organiser and "ORGANIZER" or "ATTENDEE"))
3.24
3.25 - def _record(self, from_organiser=True, update_freebusy=False):
3.26 + def _record(self, from_organiser=True):
3.27
3.28 """
3.29 Record details from the current object given a message originating
3.30 - from an organiser if 'from_organiser' is set to a true value, updating
3.31 - free/busy information if 'update_freebusy' is set to a true value.
3.32 + from an organiser if 'from_organiser' is set to a true value.
3.33 """
3.34
3.35 self.set_identity(from_organiser)
3.36 @@ -76,44 +78,16 @@
3.37
3.38 # Update free/busy information.
3.39
3.40 - if update_freebusy:
3.41 -
3.42 - freebusy = self.store.get_freebusy(self.user)
3.43 -
3.44 - # Use the stored event in case the reply is incomplete, as is seen
3.45 - # when Claws sends a REPLY for an object originally employing
3.46 - # recurrence information.
3.47 -
3.48 - obj = self.get_object()
3.49 - if not obj:
3.50 - return False # although this should not happen
3.51 -
3.52 - # If newer than any old version, discard old details from the
3.53 - # free/busy record and check for suitability.
3.54 -
3.55 - # Interpretation of periods can depend on the time zone.
3.56 -
3.57 - periods = obj.get_periods(self.get_tzid(), self.get_window_end())
3.58 -
3.59 - # Obtain the attendance attributes for this user, if available.
3.60 -
3.61 - attendees = uri_dict(self.obj.get_value_map("ATTENDEE"))
3.62 - self.update_freebusy_for_participant(freebusy, periods, attendees.get(self.user), from_organiser)
3.63 -
3.64 - # Remove original recurrence details replaced by additional
3.65 - # recurrences, as well as obsolete additional recurrences.
3.66 -
3.67 - self.remove_freebusy_for_recurrences(freebusy, self.store.get_recurrences(self.user, self.uid))
3.68 - self.store.set_freebusy(self.user, freebusy)
3.69 -
3.70 - if self.publisher and self.is_sharing():
3.71 - self.publisher.set_freebusy(self.user, freebusy)
3.72 + self.update_event_in_freebusy()
3.73
3.74 return True
3.75
3.76 - def _remove(self, from_organiser=True, update_freebusy=False):
3.77 + def _remove(self, from_organiser=True):
3.78
3.79 - "Remove free/busy information for any unprocessed object."
3.80 + """
3.81 + Remove details from the current object given a message originating
3.82 + from an organiser if 'from_organiser' is set to a true value.
3.83 + """
3.84
3.85 self.set_identity(from_organiser)
3.86
3.87 @@ -168,16 +142,8 @@
3.88
3.89 # Update free/busy information.
3.90
3.91 - if update_freebusy:
3.92 - freebusy = self.store.get_freebusy(self.user)
3.93 - self.remove_from_freebusy(freebusy)
3.94 - self.remove_freebusy_for_recurrences(freebusy)
3.95 -
3.96 - if cancel_entire_event or self.user in given_attendees:
3.97 - self.store.set_freebusy(self.user, freebusy)
3.98 -
3.99 - if self.publisher and self.is_sharing():
3.100 - self.publisher.set_freebusy(self.user, freebusy)
3.101 + if cancel_entire_event or self.user in given_attendees:
3.102 + self.remove_event_from_freebusy()
3.103
3.104 return True
3.105
3.106 @@ -189,7 +155,7 @@
3.107 pass
3.108
3.109 def cancel(self):
3.110 - self._remove(True, True)
3.111 + self._remove(True)
3.112
3.113 def counter(self):
3.114 pass
3.115 @@ -198,16 +164,16 @@
3.116 pass
3.117
3.118 def publish(self):
3.119 - self._record(True, True)
3.120 + self._record(True)
3.121
3.122 def refresh(self):
3.123 pass
3.124
3.125 def reply(self):
3.126 - self._record(False, True)
3.127 + self._record(False)
3.128
3.129 def request(self):
3.130 - self._record(True, True)
3.131 + self._record(True)
3.132
3.133 # Handler registry.
3.134
4.1 --- a/imiptools/handlers/resource.py Tue May 19 23:40:57 2015 +0200
4.2 +++ b/imiptools/handlers/resource.py Wed May 20 13:41:23 2015 +0200
4.3 @@ -21,13 +21,24 @@
4.4
4.5 from imiptools.data import get_address, get_uri, to_part
4.6 from imiptools.handlers import Handler
4.7 -from imiptools.handlers.common import CommonFreebusy
4.8 -from imiptools.period import remove_affected_period
4.9 +from imiptools.handlers.common import CommonFreebusy, Outgoing
4.10
4.11 -class ResourceHandler(Handler):
4.12 +class ResourceHandler(Handler, Outgoing):
4.13
4.14 "Handling mechanisms specific to resources."
4.15
4.16 + def set_participation(self, scheduled):
4.17 +
4.18 + "Set the user's participation in the current object if 'scheduled'."
4.19 +
4.20 + attendee_attr = self.obj.get_value_map("ATTENDEE").get(self.user)
4.21 + attendee_attr["PARTSTAT"] = scheduled and "ACCEPTED" or "DECLINED"
4.22 + if attendee_attr.has_key("RSVP"):
4.23 + del attendee_attr["RSVP"]
4.24 + if self.messenger and self.messenger.sender != get_address(self.user):
4.25 + attendee_attr["SENT-BY"] = get_uri(self.messenger.sender)
4.26 + return attendee_attr
4.27 +
4.28 def _record_and_respond(self, handle_for_attendee):
4.29
4.30 """
4.31 @@ -76,13 +87,9 @@
4.32 freebusy = self.store.get_freebusy(self.user)
4.33 scheduled = self.can_schedule(freebusy, periods)
4.34
4.35 - attendee_attr = self.obj.get_value_map("ATTENDEE").get(self.user)
4.36 + # Update the participation of the resource in the object.
4.37
4.38 - attendee_attr["PARTSTAT"] = scheduled and "ACCEPTED" or "DECLINED"
4.39 - if attendee_attr.has_key("RSVP"):
4.40 - del attendee_attr["RSVP"]
4.41 - if self.messenger and self.messenger.sender != get_address(self.user):
4.42 - attendee_attr["SENT-BY"] = get_uri(self.messenger.sender)
4.43 + attendee_attr = self.set_participation(scheduled)
4.44
4.45 # Set the complete event or an additional occurrence.
4.46
4.47 @@ -94,28 +101,14 @@
4.48 if not self.recurrenceid:
4.49 self.store.remove_recurrences(self.user, self.uid)
4.50
4.51 - # Only update free/busy details if the event is scheduled.
4.52 -
4.53 - if scheduled:
4.54 - self.update_freebusy(freebusy, periods)
4.55 - else:
4.56 - self.remove_from_freebusy(freebusy)
4.57 + # Update free/busy information.
4.58
4.59 - # Remove original recurrence details replaced by additional
4.60 - # recurrences, as well as obsolete additional recurrences.
4.61 + self.update_event_in_freebusy(from_organiser=False)
4.62
4.63 - self.remove_freebusy_for_recurrences(freebusy, self.store.get_recurrences(self.user, self.uid))
4.64 - self.store.set_freebusy(self.user, freebusy)
4.65 -
4.66 - if self.publisher and self.is_sharing():
4.67 - self.publisher.set_freebusy(self.user, freebusy)
4.68 -
4.69 - # Make a version of the request with just this attendee.
4.70 + # Make a version of the object with just this attendee, update the
4.71 + # DTSTAMP in the response, and return the object for sending.
4.72
4.73 self.obj["ATTENDEE"] = [(self.user, attendee_attr)]
4.74 -
4.75 - # Update the DTSTAMP in the response.
4.76 -
4.77 self.update_dtstamp()
4.78 return event
4.79
4.80 @@ -129,13 +122,11 @@
4.81 self.store.set_event(self.user, self.uid, self.recurrenceid, self.obj.to_node())
4.82 self.store.cancel_event(self.user, self.uid, self.recurrenceid)
4.83
4.84 - freebusy = self.store.get_freebusy(self.user)
4.85 - self.remove_from_freebusy(freebusy)
4.86 - self.remove_freebusy_for_recurrences(freebusy)
4.87 - self.store.set_freebusy(self.user, freebusy)
4.88 + # Update free/busy information.
4.89
4.90 - if self.publisher and self.is_sharing():
4.91 - self.publisher.set_freebusy(self.user, freebusy)
4.92 + self.remove_event_from_freebusy()
4.93 +
4.94 + # No response is required.
4.95
4.96 return None
4.97