# HG changeset patch # User Paul Boddie # Date 1437947006 -7200 # Node ID d75510f99c062055b79f63602dcb69915e820036 # Parent f75086354aa76f9c6fe1b76dd39045296e406af2 Moved various handler methods to the client classes. Attempted to simplify various methods and to remove the need to obtain information from some methods only to pass it straight to other methods that could have obtained such information themselves. diff -r f75086354aa7 -r d75510f99c06 imiptools/client.py --- a/imiptools/client.py Sun Jul 26 02:01:24 2015 +0200 +++ b/imiptools/client.py Sun Jul 26 23:43:26 2015 +0200 @@ -20,12 +20,15 @@ """ from datetime import datetime -from imiptools.data import get_address, get_uri, get_window_end, \ - make_freebusy, to_part, \ +from imiptools.data import Object, get_address, get_uri, get_window_end, \ + is_new_object, make_freebusy, to_part, \ uri_dict, uri_items, uri_values from imiptools.dates import format_datetime, get_default_timezone, \ - get_timestamp, to_timezone -from imiptools.period import update_freebusy + get_recurrence_start_point, get_timestamp, \ + to_timezone +from imiptools.period import can_schedule, remove_period, \ + remove_additional_periods, remove_affected_period, \ + update_freebusy from imiptools.profile import Preferences import imip_store @@ -123,10 +126,30 @@ # Common operations on calendar data. - def is_participating(self, attr, as_organiser=False): + def is_participating(self, user, as_organiser=False): + + """ + Return whether, subject to the 'user' indicating an identity and the + 'as_organiser' status of that identity, the user concerned is actually + participating in the current object event. + """ + + attr = self.get_attendance(user) return as_organiser or not attr or attr.get("PARTSTAT") != "DECLINED" - def get_overriding_transparency(self, attr, as_organiser=False): + def get_overriding_transparency(self, user, as_organiser=False): + + """ + Return the overriding transparency to be associated with the free/busy + records for an event, subject to the 'user' indicating an identity and + the 'as_organiser' status of that identity. + + Where an identity is only an organiser and not attending, "ORG" is + returned. Otherwise, no overriding transparency is defined and None is + returned. + """ + + attr = self.get_attendance(user) return as_organiser and not (attr and attr.get("PARTSTAT")) and "ORG" or None def update_participation(self, obj, partstat=None): @@ -152,12 +175,34 @@ if self.messenger and self.messenger.sender != get_address(self.user): attr["SENT-BY"] = get_uri(self.messenger.sender) + def get_periods(self, obj): + + """ + Return periods for the given 'obj'. Interpretation of periods can depend + on the time zone, which is obtained for the current user. + """ + + return obj.get_periods(self.get_tzid(), self.get_window_end()) + + # Store operations. + + def get_stored_object(self, uid, recurrenceid): + + """ + Return the stored object for the current user, with the given 'uid' and + 'recurrenceid'. + """ + + fragment = self.store.get_event(self.user, uid, recurrenceid) + return fragment and Object(fragment) + # Free/busy operations. - def get_freebusy_part(self): + def get_freebusy_part(self, freebusy=None): """ - Return a message part containing free/busy information for the user. + Return a message part containing free/busy information for the user, + either specified as 'freebusy' or obtained from the store directly. """ if self.is_sharing() and self.is_bundling(): @@ -167,7 +212,7 @@ utcnow = get_timestamp() uid = "imip-agent-%s-%s" % (utcnow, get_address(self.user)) - freebusy = self.store.get_freebusy(self.user) + freebusy = freebusy or self.store.get_freebusy(self.user) user_attr = {} self.update_sender(user_attr) @@ -175,6 +220,17 @@ return None + def update_freebusy(self, freebusy, periods, transp, uid, recurrenceid, summary, organiser): + + """ + Update the 'freebusy' collection with the given 'periods', indicating a + 'transp' status, explicit 'uid' and 'recurrenceid' to indicate either a + recurrence or the parent event. The 'summary' and 'organiser' must also + be provided. + """ + + update_freebusy(freebusy, periods, transp, self.uid, recurrenceid, summary, organiser) + class ClientForObject(Client): "A client maintaining a specific object." @@ -184,51 +240,15 @@ self.set_object(obj) def set_object(self, obj): + + "Set the current object to 'obj', obtaining metadata details." + self.obj = obj self.uid = obj and self.obj.get_uid() self.recurrenceid = obj and self.obj.get_recurrenceid() self.sequence = obj and self.obj.get_value("SEQUENCE") self.dtstamp = obj and self.obj.get_value("DTSTAMP") - def _update_freebusy(self, freebusy, periods, recurrenceid, transp=None): - - """ - Update the 'freebusy' collection with the given 'periods', indicating an - explicit 'recurrenceid' to affect either a recurrence or the parent - event. - """ - - update_freebusy(freebusy, periods, - transp or self.obj.get_value("TRANSP") or "OPAQUE", - self.uid, recurrenceid, - self.obj.get_value("SUMMARY"), - self.obj.get_value("ORGANIZER")) - - def update_freebusy(self, freebusy, periods, transp=None): - - """ - Update the 'freebusy' collection for this event with the given - 'periods'. - """ - - self._update_freebusy(freebusy, periods, self.recurrenceid, transp) - - def update_freebusy_for_participant(self, freebusy, periods, attr, for_organiser=False): - - """ - Update the 'freebusy' collection using the given 'periods', subject to - the 'attr' provided for the participant, indicating whether this is - being generated 'for_organiser' or not. - """ - - # Organisers employ a special transparency if not attending. - - if self.is_participating(attr, for_organiser): - self.update_freebusy(freebusy, periods, - transp=self.get_overriding_transparency(attr, for_organiser)) - else: - self.remove_from_freebusy(freebusy) - # Object update methods. def update_dtstamp(self): @@ -246,4 +266,278 @@ sequence = self.obj.get_value("SEQUENCE") or "0" self.obj["SEQUENCE"] = [(str(int(sequence) + (increment and 1 or 0)), {})] + def merge_attendance(self, attendees): + + """ + Merge attendance from the current object's 'attendees' into the version + stored for the current user. + """ + + obj = self.get_stored_object_version() + + if not obj or not self.have_new_object(obj): + return False + + # Get attendee details in a usable form. + + attendee_map = uri_dict(obj.get_value_map("ATTENDEE")) + + for attendee, attendee_attr in attendees.items(): + + # Update attendance in the loaded object. + + attendee_map[attendee] = attendee_attr + + # Set the new details and store the object. + + obj["ATTENDEE"] = attendee_map.items() + + # Set the complete event if not an additional occurrence. + + event = obj.to_node() + self.store.set_event(self.user, self.uid, self.recurrenceid, event) + + return True + + # Object-related tests. + + def get_attendance(self, user=None): + + """ + Return the attendance attributes for 'user', or the current user if + 'user' is not specified. + """ + + attendees = uri_dict(self.obj.get_value_map("ATTENDEE")) + return attendees.get(user or self.user) or {} + + def is_attendee(self, identity, obj=None): + + """ + Return whether 'identity' is an attendee in the current object, or in + 'obj' if specified. + """ + + return identity in uri_values((obj or self.obj).get_values("ATTENDEE")) + + def can_schedule(self, freebusy, periods): + + """ + Indicate whether within 'freebusy' the given 'periods' can be scheduled. + """ + + return can_schedule(freebusy, periods, self.uid, self.recurrenceid) + + def have_new_object(self, obj=None): + + """ + Return whether the current object is new to the current user (or if the + given 'obj' is new). + """ + + obj = obj or self.get_stored_object_version() + + # If found, compare SEQUENCE and potentially DTSTAMP. + + if obj: + sequence = obj.get_value("SEQUENCE") + dtstamp = obj.get_value("DTSTAMP") + + # If the request refers to an older version of the object, ignore + # it. + + return is_new_object(sequence, self.sequence, dtstamp, self.dtstamp, + self.is_partstat_updated(obj)) + + return True + + def is_partstat_updated(self, obj): + + """ + Return whether the participant status has been updated in the current + object in comparison to the given 'obj'. + + NOTE: Some clients like Claws Mail erase time information from DTSTAMP + NOTE: and make it invalid. Thus, such attendance information may also be + NOTE: incorporated into any new object assessment. + """ + + old_attendees = uri_dict(obj.get_value_map("ATTENDEE")) + new_attendees = uri_dict(self.obj.get_value_map("ATTENDEE")) + + for attendee, attr in old_attendees.items(): + old_partstat = attr.get("PARTSTAT") + new_attr = new_attendees.get(attendee) + new_partstat = new_attr and new_attr.get("PARTSTAT") + + if old_partstat == "NEEDS-ACTION" and new_partstat and \ + new_partstat != old_partstat: + + return True + + return False + + # Object retrieval. + + def get_stored_object_version(self): + + """ + Return the stored object to which the current object refers for the + current user. + """ + + return self.get_stored_object(self.uid, self.recurrenceid) + + def get_definitive_object(self, from_organiser): + + """ + Return an object considered definitive for the current transaction, + using 'from_organiser' to select the current transaction's object if + true, or selecting a stored object if false. + """ + + return from_organiser and self.obj or self.get_stored_object_version() + + def get_parent_object(self): + + """ + Return the parent object to which the current object refers for the + current user. + """ + + return self.recurrenceid and self.get_stored_object(self.uid, None) or None + + # Convenience methods for modifying free/busy collections. + + def get_recurrence_start_point(self, recurrenceid): + + "Get 'recurrenceid' in a form suitable for matching free/busy entries." + + tzid = self.obj.get_tzid() or self.get_tzid() + return get_recurrence_start_point(recurrenceid, tzid) + + def remove_from_freebusy(self, freebusy): + + "Remove this event from the given 'freebusy' collection." + + if not remove_period(freebusy, self.uid, self.recurrenceid) and self.recurrenceid: + remove_affected_period(freebusy, self.uid, self.get_recurrence_start_point(self.recurrenceid)) + + def remove_freebusy_for_recurrences(self, freebusy, recurrenceids=None): + + """ + Remove from 'freebusy' any original recurrence from parent free/busy + details for the current object, if the current object is a specific + additional recurrence. Otherwise, remove all additional recurrence + information corresponding to 'recurrenceids', or if omitted, all + recurrences. + """ + + if self.recurrenceid: + recurrenceid = self.get_recurrence_start_point(self.recurrenceid) + remove_affected_period(freebusy, self.uid, recurrenceid) + else: + # Remove obsolete recurrence periods. + + remove_additional_periods(freebusy, self.uid, recurrenceids) + + # Remove original periods affected by additional recurrences. + + if recurrenceids: + for recurrenceid in recurrenceids: + recurrenceid = self.get_recurrence_start_point(recurrenceid) + remove_affected_period(freebusy, self.uid, recurrenceid) + + def update_freebusy(self, freebusy, user, for_organiser): + + """ + Update the 'freebusy' collection for this event with the periods and + transparency associated with the current object, subject to the 'user' + identity and the attendance details provided for them, indicating + whether the update is 'for_organiser' or not. + """ + + # Obtain the stored object if the current object is not issued by the + # organiser. Attendees do not have the opportunity to redefine the + # periods. + + obj = self.get_definitive_object(for_organiser) + if not obj: + return + + # Obtain the affected periods. + + periods = self.get_periods(obj) + + # Define an overriding transparency, the indicated event transparency, + # or the default transparency for the free/busy entry. + + transp = self.get_overriding_transparency(user, for_organiser) or \ + obj.get_value("TRANSP") or \ + "OPAQUE" + + # Perform the low-level update. + + Client.update_freebusy(self, freebusy, periods, transp, + self.uid, self.recurrenceid, + obj.get_value("SUMMARY"), + obj.get_value("ORGANIZER")) + + def update_freebusy_for_participant(self, freebusy, user, for_organiser=False, + updating_other=False): + + """ + Update the 'freebusy' collection using the given 'periods', involving + the given 'user', indicating whether the update is 'for_organiser' or + not, and whether it is 'updating_other' (meaning another user's + details). + """ + + # Record in the free/busy details unless a non-participating attendee. + # Use any attendee information for an organiser, not the organiser's own + # attributes. + + if self.is_participating(user, for_organiser and not updating_other): + self.update_freebusy(freebusy, user, for_organiser) + else: + self.remove_from_freebusy(freebusy) + + # Convenience methods for updating stored free/busy information received + # from other users. + + def update_freebusy_from_participant(self, user, for_organiser): + + """ + For the current user, record the free/busy information for another + 'user', indicating whether the update is 'for_organiser' or not, thus + maintaining a separate record of their free/busy details. + """ + + # A user does not store free/busy information for themself as another + # party. + + if user == self.user: + return + + freebusy = self.store.get_freebusy_for_other(self.user, user) + self.update_freebusy_for_participant(freebusy, user, for_organiser, True) + + # Tidy up any obsolete recurrences. + + self.remove_freebusy_for_recurrences(freebusy, self.store.get_recurrences(self.user, self.uid)) + self.store.set_freebusy_for_other(self.user, freebusy, user) + + def update_freebusy_from_organiser(self, organiser): + + "For the current user, record free/busy information from 'organiser'." + + self.update_freebusy_from_participant(organiser, True) + + def update_freebusy_from_attendees(self, attendees): + + "For the current user, record free/busy information from 'attendees'." + + for attendee in attendees.keys(): + self.update_freebusy_from_participant(attendee, False) + # vim: tabstop=4 expandtab shiftwidth=4 diff -r f75086354aa7 -r d75510f99c06 imiptools/data.py --- a/imiptools/data.py Sun Jul 26 02:01:24 2015 +0200 +++ b/imiptools/data.py Sun Jul 26 23:43:26 2015 +0200 @@ -418,12 +418,6 @@ return is_same_sequence and partstat_set or not is_old_sequence -# NOTE: Need to expose the 100 day window for recurring events in the -# NOTE: configuration. - -def get_window_end(tzid, window_size=100): - return to_timezone(datetime.now(), tzid) + timedelta(window_size) - def get_periods(obj, tzid, window_end, inclusive=False): """ @@ -504,4 +498,34 @@ return periods +def get_sender_identities(mapping): + + """ + Return a mapping from actual senders to the identities for which they + have provided data, extracting this information from the given + 'mapping'. + """ + + senders = {} + + for value, attr in mapping.items(): + sent_by = attr.get("SENT-BY") + if sent_by: + sender = get_uri(sent_by) + else: + sender = value + + if not senders.has_key(sender): + senders[sender] = [] + + senders[sender].append(value) + + return senders + +# NOTE: Need to expose the 100 day window for recurring events in the +# NOTE: configuration. + +def get_window_end(tzid, window_size=100): + return to_timezone(datetime.now(), tzid) + timedelta(window_size) + # vim: tabstop=4 expandtab shiftwidth=4 diff -r f75086354aa7 -r d75510f99c06 imiptools/handlers/__init__.py --- a/imiptools/handlers/__init__.py Sun Jul 26 02:01:24 2015 +0200 +++ b/imiptools/handlers/__init__.py Sun Jul 26 23:43:26 2015 +0200 @@ -22,13 +22,8 @@ from email.mime.text import MIMEText from imiptools.client import ClientForObject from imiptools.config import MANAGER_PATH, MANAGER_URL -from imiptools.data import Object, get_address, get_uri, \ - is_new_object, uri_dict, uri_item, uri_values -from imiptools.dates import format_datetime, get_recurrence_start_point, \ - to_timezone -from imiptools.period import can_schedule, remove_period, \ - remove_additional_periods, remove_affected_period -from imiptools.profile import Preferences +from imiptools.data import get_address, get_uri, get_sender_identities, \ + uri_dict, uri_item from socket import gethostname # References to the Web interface. @@ -101,128 +96,8 @@ def get_outgoing_methods(self): return self.outgoing_methods - # Convenience methods for modifying free/busy collections. - - def get_recurrence_start_point(self, recurrenceid): - - "Get 'recurrenceid' in a form suitable for matching free/busy entries." - - tzid = self.obj.get_tzid() or self.get_tzid() - return get_recurrence_start_point(recurrenceid, tzid) - - def remove_from_freebusy(self, freebusy): - - "Remove this event from the given 'freebusy' collection." - - if not remove_period(freebusy, self.uid, self.recurrenceid) and self.recurrenceid: - remove_affected_period(freebusy, self.uid, self.get_recurrence_start_point(self.recurrenceid)) - - def remove_freebusy_for_recurrences(self, freebusy, recurrenceids=None): - - """ - Remove from 'freebusy' any original recurrence from parent free/busy - details for the current object, if the current object is a specific - additional recurrence. Otherwise, remove all additional recurrence - information corresponding to 'recurrenceids', or if omitted, all - recurrences. - """ - - if self.recurrenceid: - recurrenceid = self.get_recurrence_start_point(self.recurrenceid) - remove_affected_period(freebusy, self.uid, recurrenceid) - else: - # Remove obsolete recurrence periods. - - remove_additional_periods(freebusy, self.uid, recurrenceids) - - # Remove original periods affected by additional recurrences. - - if recurrenceids: - for recurrenceid in recurrenceids: - recurrenceid = self.get_recurrence_start_point(recurrenceid) - remove_affected_period(freebusy, self.uid, recurrenceid) - - # Convenience methods for updating stored free/busy information. - - def update_freebusy_from_participant(self, participant_item, for_organiser): - - """ - For the calendar user, record the free/busy information for the - 'participant_item' (a value plus attributes) representing a different - identity, thus maintaining a separate record of their free/busy details. - """ - - participant, participant_attr = participant_item - - # A user does not store free/busy information for themself as another - # party. - - if participant == self.user: - return - - freebusy = self.store.get_freebusy_for_other(self.user, participant) - - # Obtain the stored object if the current object is not issued by the - # organiser. - - obj = self.get_definitive_object(for_organiser) - if not obj: - return - - # Obtain the affected periods. - - periods = obj.get_periods(self.get_tzid(), self.get_window_end()) - - # Record in the free/busy details unless a non-participating attendee. - # Use any attendee information for an organiser, not the organiser's own - # attributes. - - if for_organiser: - participant_attr = obj.get_value_map("ATTENDEE").get(participant) - - self.update_freebusy_for_participant(freebusy, periods, participant_attr, - for_organiser and not self.is_attendee(participant)) - - # Tidy up any obsolete recurrences. - - self.remove_freebusy_for_recurrences(freebusy, self.store.get_recurrences(self.user, self.uid)) - self.store.set_freebusy_for_other(self.user, freebusy, participant) - - def update_freebusy_from_organiser(self, organiser_item): - - """ - For the current user, record free/busy information from the - 'organiser_item' (a value plus attributes). - """ - - self.update_freebusy_from_participant(organiser_item, True) - - def update_freebusy_from_attendees(self, attendees): - - "For the current user, record free/busy information from 'attendees'." - - for attendee_item in attendees.items(): - self.update_freebusy_from_participant(attendee_item, False) - # Logic, filtering and access to calendar structures and other data. - def is_attendee(self, identity, obj=None): - - """ - Return whether 'identity' is an attendee in the current object, or in - 'obj' if specified. - """ - - return identity in uri_values((obj or self.obj).get_values("ATTENDEE")) - - def can_schedule(self, freebusy, periods): - - """ - Indicate whether within 'freebusy' the given 'periods' can be scheduled. - """ - - return can_schedule(freebusy, periods, self.uid, self.recurrenceid) - def filter_by_senders(self, mapping): """ @@ -233,7 +108,7 @@ # Get a mapping from senders to identities. - identities = self.get_sender_identities(mapping) + identities = get_sender_identities(mapping) # Find the senders that are valid. @@ -242,7 +117,7 @@ # Return the true identities. - return reduce(lambda a, b: a + b, [identities[get_uri(address)] for address in valid]) + return reduce(lambda a, b: a + b, [identities[get_uri(address)] for address in valid], []) else: return mapping @@ -317,148 +192,4 @@ return organiser_item, attendees - def get_sender_identities(self, mapping): - - """ - Return a mapping from actual senders to the identities for which they - have provided data, extracting this information from the given - 'mapping'. - """ - - senders = {} - - for value, attr in mapping.items(): - sent_by = attr.get("SENT-BY") - if sent_by: - sender = get_uri(sent_by) - else: - sender = value - - if not senders.has_key(sender): - senders[sender] = [] - - senders[sender].append(value) - - return senders - - def _get_object(self, uid, recurrenceid): - - """ - Return the stored object for the current user, with the given 'uid' and - 'recurrenceid'. - """ - - fragment = self.store.get_event(self.user, uid, recurrenceid) - return fragment and Object(fragment) - - def get_object(self): - - """ - Return the stored object to which the current object refers for the - current user. - """ - - return self._get_object(self.uid, self.recurrenceid) - - def get_definitive_object(self, from_organiser): - - """ - Return an object considered definitive for the current transaction, - using 'from_organiser' to select the current transaction's object if - true, or selecting a stored object if false. - """ - - return from_organiser and self.obj or self.get_object() - - def get_parent_object(self): - - """ - Return the parent object to which the current object refers for the - current user. - """ - - return self.recurrenceid and self._get_object(self.uid, None) or None - - def have_new_object(self, obj=None): - - """ - Return whether the current object is new to the current user (or if the - given 'obj' is new). - """ - - obj = obj or self.get_object() - - # If found, compare SEQUENCE and potentially DTSTAMP. - - if obj: - sequence = obj.get_value("SEQUENCE") - dtstamp = obj.get_value("DTSTAMP") - - # If the request refers to an older version of the object, ignore - # it. - - return is_new_object(sequence, self.sequence, dtstamp, self.dtstamp, - self.is_partstat_updated(obj)) - - return True - - def is_partstat_updated(self, obj): - - """ - Return whether the participant status has been updated in the current - object in comparison to the given 'obj'. - - NOTE: Some clients like Claws Mail erase time information from DTSTAMP - NOTE: and make it invalid. Thus, such attendance information may also be - NOTE: incorporated into any new object assessment. - """ - - old_attendees = uri_dict(obj.get_value_map("ATTENDEE")) - new_attendees = uri_dict(self.obj.get_value_map("ATTENDEE")) - - for attendee, attr in old_attendees.items(): - old_partstat = attr.get("PARTSTAT") - new_attr = new_attendees.get(attendee) - new_partstat = new_attr and new_attr.get("PARTSTAT") - - if old_partstat == "NEEDS-ACTION" and new_partstat and \ - new_partstat != old_partstat: - - return True - - return False - - def merge_attendance(self, attendees): - - """ - Merge attendance from the current object's 'attendees' into the version - stored for the current user. - """ - - obj = self.get_object() - - if not obj or not self.have_new_object(obj): - return False - - # Get attendee details in a usable form. - - attendee_map = uri_dict(obj.get_value_map("ATTENDEE")) - - for attendee, attendee_attr in attendees.items(): - - # Update attendance in the loaded object. - - attendee_map[attendee] = attendee_attr - - # Set the new details and store the object. - - obj["ATTENDEE"] = attendee_map.items() - - # Set the complete event if not an additional occurrence. - - event = obj.to_node() - self.store.set_event(self.user, self.uid, self.recurrenceid, event) - - return True - # vim: tabstop=4 expandtab shiftwidth=4 diff -r f75086354aa7 -r d75510f99c06 imiptools/handlers/common.py --- a/imiptools/handlers/common.py Sun Jul 26 02:01:24 2015 +0200 +++ b/imiptools/handlers/common.py Sun Jul 26 23:43:26 2015 +0200 @@ -69,7 +69,7 @@ class Outgoing: - "Common outgoing message handling functionality." + "Common outgoing message handling functionality mix-in." def update_event_in_freebusy(self, from_organiser=True): @@ -77,25 +77,9 @@ 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) + self.update_freebusy_for_participant(freebusy, self.user, from_organiser) # Remove original recurrence details replaced by additional # recurrences, as well as obsolete additional recurrences. diff -r f75086354aa7 -r d75510f99c06 imiptools/handlers/person.py --- a/imiptools/handlers/person.py Sun Jul 26 02:01:24 2015 +0200 +++ b/imiptools/handlers/person.py Sun Jul 26 23:43:26 2015 +0200 @@ -42,7 +42,7 @@ if not oa: return False - (organiser, organiser_attr), attendees = organiser_item, attendees = oa + (organiser, organiser_attr), attendees = oa # Handle notifications and invitations. @@ -93,7 +93,7 @@ # Update the recipient's record of the organiser's schedule. - self.update_freebusy_from_organiser(organiser_item) + self.update_freebusy_from_organiser(organiser) # As organiser, update attendance from valid attendees. diff -r f75086354aa7 -r d75510f99c06 imiptools/handlers/person_outgoing.py --- a/imiptools/handlers/person_outgoing.py Sun Jul 26 02:01:24 2015 +0200 +++ b/imiptools/handlers/person_outgoing.py Sun Jul 26 23:43:26 2015 +0200 @@ -99,7 +99,7 @@ # Obtain any stored object, using parent object details if a newly- # indicated occurrence is referenced. - obj = self.get_object() + obj = self.get_stored_object_version() old = not obj and self.get_parent_object() or obj if not old: diff -r f75086354aa7 -r d75510f99c06 imipweb/calendar.py --- a/imipweb/calendar.py Sun Jul 26 02:01:24 2015 +0200 +++ b/imipweb/calendar.py Sun Jul 26 23:43:26 2015 +0200 @@ -177,7 +177,7 @@ page.ul() for uid, recurrenceid in requests: - obj = self._get_object(uid, recurrenceid) + obj = self.get_stored_object(uid, recurrenceid) if obj: page.li() page.a(obj.get_value("SUMMARY"), href="#request-%s-%s" % (uid, recurrenceid or "")) diff -r f75086354aa7 -r d75510f99c06 imipweb/client.py --- a/imipweb/client.py Sun Jul 26 02:01:24 2015 +0200 +++ b/imipweb/client.py Sun Jul 26 23:43:26 2015 +0200 @@ -56,22 +56,20 @@ else: recipients = [get_address(organiser)] + # Since the outgoing handler updates this user's free/busy details, + # the stored details will probably not have the updated details at + # this point, so we update our copy for serialisation as the bundled + # free/busy object. + + freebusy = self.store.get_freebusy(self.user) + self.update_freebusy(freebusy, self.user, from_organiser) + # Bundle free/busy information if appropriate. - part = self.get_freebusy_part() + part = self.get_freebusy_part(freebusy) if part: parts.append(part) - # Since the outgoing handler updates this user's free/busy details, - # the stored details will probably not have the updated details at - # this point, so we update our copy for serialisation as the bundled - # free/busy object. - - freebusy = self.store.get_freebusy(self.user) - - self.update_freebusy(freebusy, - self.obj.get_periods(self.get_tzid(), self.get_window_end())) - # Explicitly specify the outgoing BCC recipient since we are sending as # the generic calendar user. diff -r f75086354aa7 -r d75510f99c06 imipweb/event.py --- a/imipweb/event.py Sun Jul 26 02:01:24 2015 +0200 +++ b/imipweb/event.py Sun Jul 26 23:43:26 2015 +0200 @@ -713,7 +713,7 @@ recurrenceid = obj.get_recurrenceid() if recurrenceid: - parent = self._get_object(uid) + parent = self.get_stored_object(uid) if not parent: return @@ -1101,7 +1101,7 @@ "Show an object request using the given 'path_info' for the current user." uid, recurrenceid = self._get_identifiers(path_info) - obj = self._get_object(uid, recurrenceid) + obj = self.get_stored_object(uid, recurrenceid) if not obj: return False diff -r f75086354aa7 -r d75510f99c06 imipweb/resource.py --- a/imipweb/resource.py Sun Jul 26 02:01:24 2015 +0200 +++ b/imipweb/resource.py Sun Jul 26 23:43:26 2015 +0200 @@ -110,8 +110,7 @@ if self.objects.has_key((uid, recurrenceid)): return self.objects[(uid, recurrenceid)] - fragment = uid and self.store.get_event(self.user, uid, recurrenceid) or None - obj = self.objects[(uid, recurrenceid)] = fragment and Object(fragment) + obj = self.objects[(uid, recurrenceid)] = self.get_stored_object(uid, recurrenceid) return obj def _get_recurrences(self, uid): @@ -127,7 +126,7 @@ def _get_request_summary(self): summary = [] for uid, recurrenceid in self._get_requests(): - obj = self._get_object(uid, recurrenceid) + obj = self.get_stored_object(uid, recurrenceid) if obj: periods = obj.get_periods(self.get_tzid(), self.get_window_end()) recurrenceids = self._get_recurrences(uid) @@ -203,8 +202,7 @@ freebusy = self.store.get_freebusy(self.user) - update_freebusy(freebusy, - obj.get_periods(self.get_tzid(), self.get_window_end()), + Client.update_freebusy(self, freebusy, self.get_periods(obj), is_only_organiser and "ORG" or obj.get_value("TRANSP"), uid, recurrenceid, obj.get_value("SUMMARY"),