# HG changeset patch # User Paul Boddie # Date 1442676196 -7200 # Node ID 07be124ea8244b843b3d3d0a1ef025cbdac78dd7 # Parent d40e26697850eceb4324d53407b3a6e2999dc029 Introduced a resource class for the event page having a current object. Moved various client functions into the common client from the common handler, attempting to eliminate duplicated Web functionality. diff -r d40e26697850 -r 07be124ea824 imip_manager.py --- a/imip_manager.py Sat Sep 19 01:08:17 2015 +0200 +++ b/imip_manager.py Sat Sep 19 17:23:16 2015 +0200 @@ -29,9 +29,9 @@ from imipweb.calendar import CalendarPage from imipweb.event import EventPage -from imipweb.resource import Resource +from imipweb.resource import ResourceClient -class Manager(Resource): +class Manager(ResourceClient): "A simple manager application." diff -r d40e26697850 -r 07be124ea824 imiptools/client.py --- a/imiptools/client.py Sat Sep 19 01:08:17 2015 +0200 +++ b/imiptools/client.py Sat Sep 19 17:23:16 2015 +0200 @@ -795,4 +795,85 @@ for attendee in attendees.keys(): self.update_freebusy_from_participant(attendee, False, self.remove_freebusy_for_participant) + # Convenience methods for updating free/busy details at the event level. + + def update_event_in_freebusy(self, for_organiser=True): + + """ + Update free/busy information when handling an object, doing so for the + organiser of an event if 'for_organiser' is set to a true value. + """ + + freebusy = self.store.get_freebusy(self.user) + + # Obtain the attendance attributes for this user, if available. + + self.update_freebusy_for_participant(freebusy, self.user, for_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() and self.is_publishing(): + self.publisher.set_freebusy(self.user, freebusy) + + # Update free/busy provider information if the event may recur + # indefinitely. + + if self.possibly_recurring_indefinitely(): + self.store.append_freebusy_provider(self.user, self.obj) + + 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() and self.is_publishing(): + self.publisher.set_freebusy(self.user, freebusy) + + # Update free/busy provider information if the event may recur + # indefinitely. + + if self.possibly_recurring_indefinitely(): + self.store.remove_freebusy_provider(self.user, self.obj) + + def update_event_in_freebusy_offers(self): + + "Update free/busy offers when handling an object." + + freebusy = self.store.get_freebusy_offers(self.user) + + # Obtain the attendance attributes for this user, if available. + + self.update_freebusy_for_participant(freebusy, self.user, offer=True) + + # 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_offers(self.user, freebusy) + + return True + + def remove_event_from_freebusy_offers(self): + + "Remove free/busy offers when handling an object." + + freebusy = self.store.get_freebusy_offers(self.user) + + self.remove_from_freebusy(freebusy) + self.remove_freebusy_for_recurrences(freebusy) + self.store.set_freebusy_offers(self.user, freebusy) + + return True + # vim: tabstop=4 expandtab shiftwidth=4 diff -r d40e26697850 -r 07be124ea824 imiptools/handlers/common.py --- a/imiptools/handlers/common.py Sat Sep 19 01:08:17 2015 +0200 +++ b/imiptools/handlers/common.py Sat Sep 19 17:23:16 2015 +0200 @@ -110,83 +110,4 @@ self.add_result("REFRESH", [get_address(organiser)], obj.to_part("REFRESH")) - def update_event_in_freebusy(self, for_organiser=True): - - """ - Update free/busy information when handling an object, doing so for the - organiser of an event if 'for_organiser' is set to a true value. - """ - - freebusy = self.store.get_freebusy(self.user) - - # Obtain the attendance attributes for this user, if available. - - self.update_freebusy_for_participant(freebusy, self.user, for_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() and self.is_publishing(): - self.publisher.set_freebusy(self.user, freebusy) - - # Update free/busy provider information if the event may recur - # indefinitely. - - if self.possibly_recurring_indefinitely(): - self.store.append_freebusy_provider(self.user, self.obj) - - 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() and self.is_publishing(): - self.publisher.set_freebusy(self.user, freebusy) - - # Update free/busy provider information if the event may recur - # indefinitely. - - if self.possibly_recurring_indefinitely(): - self.store.remove_freebusy_provider(self.user, self.obj) - - def update_event_in_freebusy_offers(self): - - "Update free/busy offers when handling an object." - - freebusy = self.store.get_freebusy_offers(self.user) - - # Obtain the attendance attributes for this user, if available. - - self.update_freebusy_for_participant(freebusy, self.user, offer=True) - - # 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_offers(self.user, freebusy) - - return True - - def remove_event_from_freebusy_offers(self): - - "Remove free/busy offers when handling an object." - - freebusy = self.store.get_freebusy_offers(self.user) - - self.remove_from_freebusy(freebusy) - self.remove_freebusy_for_recurrences(freebusy) - self.store.set_freebusy_offers(self.user, freebusy) - - return True - # vim: tabstop=4 expandtab shiftwidth=4 diff -r d40e26697850 -r 07be124ea824 imipweb/calendar.py --- a/imipweb/calendar.py Sat Sep 19 01:08:17 2015 +0200 +++ b/imipweb/calendar.py Sat Sep 19 17:23:16 2015 +0200 @@ -27,9 +27,9 @@ to_timezone from imiptools.period import add_day_start_points, add_empty_days, add_slots, \ get_scale, get_slots, get_spans, partition_by_day, Point -from imipweb.resource import Resource +from imipweb.resource import ResourceClient -class CalendarPage(Resource): +class CalendarPage(ResourceClient): "A request handler for the calendar page." diff -r d40e26697850 -r 07be124ea824 imipweb/event.py --- a/imipweb/event.py Sat Sep 19 01:08:17 2015 +0200 +++ b/imipweb/event.py Sat Sep 19 17:23:16 2015 +0200 @@ -29,15 +29,15 @@ event_period_from_period, form_period_from_period, \ FormDate, FormPeriod, PeriodError from imipweb.client import ManagerClient -from imipweb.resource import Resource +from imipweb.resource import ResourceClientForObject import pytz -class EventPage(Resource): +class EventPage(ResourceClientForObject): "A request handler for the event page." def __init__(self, resource=None, messenger=None): - Resource.__init__(self, resource) + ResourceClientForObject.__init__(self, resource) self.messenger = messenger or Messenger() # Various property values and labels. @@ -61,37 +61,25 @@ # Access to stored object information. - def is_organiser(self, obj): - return get_uri(obj.get_value("ORGANIZER")) == self.user - - def get_stored_attendees(self, obj): - return uri_values(obj.get_values("ATTENDEE") or []) + def is_organiser(self): + return get_uri(self.obj.get_value("ORGANIZER")) == self.user - def get_stored_main_period(self, obj): - - """ - Return the main event period for the given 'obj'. - """ + def get_stored_attendees(self): + return uri_values(self.obj.get_values("ATTENDEE") or []) - dtstart, dtstart_attr = obj.get_datetime_item("DTSTART") + def get_stored_main_period(self): - if obj.has_key("DTEND"): - dtend, dtend_attr = obj.get_datetime_item("DTEND") - elif obj.has_key("DURATION"): - duration = obj.get_duration("DURATION") - dtend = dtstart + duration - dtend_attr = dtstart_attr - else: - dtend, dtend_attr = dtstart, dtstart_attr + "Return the main event period for the current object." + (dtstart, dtstart_attr), (dtend, dtend_attr) = self.obj.get_main_period_items(self.get_tzid()) return EventPeriod(dtstart, dtend, self.get_tzid(), None, dtstart_attr, dtend_attr) - def get_stored_recurrences(self, obj): + def get_stored_recurrences(self): - "Return recurrences computed using the given 'obj'." + "Return recurrences computed using the current object." recurrences = [] - for period in self.get_periods(obj): + for period in self.get_periods(self.obj): if period.origin != "DTSTART": recurrences.append(period) return recurrences @@ -104,19 +92,16 @@ return not self.env.get_args().has_key("editing") - def handle_request(self, obj): + def handle_request(self): """ - Handle actions involving the given 'obj' as an object's representation, - returning an error if one occurred, or None if the request was - successfully handled. + Handle actions involving the current object, returning an error if one + occurred, or None if the request was successfully handled. """ # Handle a submitted form. args = self.env.get_args() - uid = obj.get_uid() - recurrenceid = obj.get_recurrenceid() # Get the possible actions. @@ -146,7 +131,7 @@ # Update principal event details if organiser. - if self.is_organiser(obj): + if self.is_organiser(): # Update time periods (main and recurring). @@ -165,25 +150,25 @@ to_unschedule = self.get_removed_periods() - obj.set_period(period) - obj.set_periods(periods) + self.obj.set_period(period) + self.obj.set_periods(periods) # Update summary. if args.has_key("summary"): - obj["SUMMARY"] = [(args["summary"][0], {})] + self.obj["SUMMARY"] = [(args["summary"][0], {})] # Obtain any participants and those to be removed. attendees = self.get_attendees_from_page() removed = [attendees[int(i)] for i in args.get("remove", [])] - to_cancel = self.update_attendees(obj, attendees, removed) + to_cancel = self.update_attendees(self.obj, attendees, removed) single_user = not attendees or attendees == [self.user] # Update attendee participation for the current user. if args.has_key("partstat"): - self.update_participation(obj, args["partstat"][0]) + self.update_participation(self.obj, args["partstat"][0]) # Process any action. @@ -194,35 +179,35 @@ if reply or invite or cancel: - client = ManagerClient(obj, self.user, self.messenger) + client = ManagerClient(self.obj, self.user, self.messenger) # Process the object and remove it from the list of requests. if reply and client.process_received_request(): - self.remove_request(uid, recurrenceid) + self.remove_request(self.uid, self.recurrenceid) - elif self.is_organiser(obj) and (invite or cancel): + elif self.is_organiser() and (invite or cancel): # Invitation, uninvitation and unscheduling... if client.process_created_request( invite and "REQUEST" or "CANCEL", to_cancel, to_unschedule): - self.remove_request(uid, recurrenceid) + self.remove_request(self.uid, self.recurrenceid) # Save single user events. elif save: - self.store.set_event(self.user, uid, recurrenceid, node=obj.to_node()) - self.update_freebusy(uid, recurrenceid, obj) - self.remove_request(uid, recurrenceid) + self.store.set_event(self.user, self.uid, self.recurrenceid, node=self.obj.to_node()) + self.update_event_in_freebusy() + self.remove_request(self.uid, self.recurrenceid) # Remove the request and the object. elif discard: - self.remove_from_freebusy(uid, recurrenceid) - self.remove_event(uid, recurrenceid) - self.remove_request(uid, recurrenceid) + self.remove_event_from_freebusy() + self.remove_event(self.uid, self.recurrenceid) + self.remove_request(self.uid, self.recurrenceid) else: handled = False @@ -284,15 +269,15 @@ return all_values - def get_current_main_period(self, obj): + def get_current_main_period(self): """ - Return the currently active main period for 'obj' depending on whether - editing has begun or whether the object has just been loaded. + Return the currently active main period for the current object depending + on whether editing has begun or whether the object has just been loaded. """ - if self.is_initial_load() or not self.is_organiser(obj): - return self.get_stored_main_period(obj) + if self.is_initial_load() or not self.is_organiser(): + return self.get_stored_main_period() else: return self.get_main_period() @@ -309,15 +294,15 @@ return FormPeriod(start, end, dtend_enabled, dttimes_enabled, self.get_tzid()) - def get_current_recurrences(self, obj): + def get_current_recurrences(self): """ - Return recurrences for 'obj' using the original object where no editing - is in progress, using form data otherwise. + Return recurrences for the current object using the original object + details where no editing is in progress, using form data otherwise. """ - if self.is_initial_load() or not self.is_organiser(obj): - return self.get_stored_recurrences(obj) + if self.is_initial_load() or not self.is_organiser(): + return self.get_stored_recurrences() else: return self.get_recurrences() @@ -355,15 +340,16 @@ to_unschedule.append(periods[int(i)]) return to_unschedule - def get_current_attendees(self, obj): + def get_current_attendees(self): """ - Return attendees for 'obj' depending on whether the object is being - edited. + Return attendees for the current object depending on whether the object + has been edited or instead provides such information from its stored + form. """ - if self.is_initial_load() or not self.is_organiser(obj): - return self.get_stored_attendees(obj) + if self.is_initial_load() or not self.is_organiser(): + return self.get_stored_attendees() else: return self.get_attendees_from_page() @@ -390,14 +376,14 @@ return ordered_attendees - def update_attendees_from_page(self, obj): + def update_attendees_from_page(self): "Add or remove attendees. This does not affect the stored object." args = self.env.get_args() attendees = self.get_attendees_from_page() - existing_attendees = self.get_stored_attendees(obj) + existing_attendees = self.get_stored_attendees() if args.has_key("add"): attendees.append("") @@ -414,27 +400,27 @@ existing = attendee in existing_attendees - if not existing or not obj.is_shared() or attendee == self.user: + if not existing or not self.obj.is_shared() or attendee == self.user: attendees.remove(attendee) return attendees # Page fragment methods. - def show_request_controls(self, obj): + def show_request_controls(self): - "Show form controls for a request concerning 'obj'." + "Show form controls for a request." page = self.page args = self.env.get_args() - attendees = self.get_current_attendees(obj) + attendees = self.get_current_attendees() is_attendee = self.user in attendees - is_request = self._have_request(obj.get_uid(), obj.get_recurrenceid()) + is_request = self._have_request(self.uid, self.recurrenceid) # Show appropriate options depending on the role of the user. - if is_attendee and not self.is_organiser(obj): + if is_attendee and not self.is_organiser(): page.p("An action is required for this request:") page.p() @@ -445,14 +431,14 @@ self._control("ignore", "submit", "Do nothing for now") page.p.close() - if self.is_organiser(obj): + if self.is_organiser(): page.p("As organiser, you can perform the following:") page.p() - self._control("create", "submit", not obj.is_shared() and "Create event" or "Update event") + self._control("create", "submit", not self.obj.is_shared() and "Create event" or "Update event") page.add(" ") - if obj.is_shared() and not is_request: + if self.obj.is_shared() and not is_request: self._control("cancel", "submit", "Cancel event") else: self._control("discard", "submit", "Discard event") @@ -461,12 +447,11 @@ self._control("save", "submit", "Save without sending") page.p.close() - def show_object_on_page(self, obj, errors=None): + def show_object_on_page(self, errors=None): """ - Show the calendar object with the representation 'obj' on the current - page. If 'errors' is given, show a suitable message for the different - errors provided. + Show the calendar object on the current page. If 'errors' is given, show + a suitable message for the different errors provided. """ page = self.page @@ -476,24 +461,22 @@ self._control("editing", "hidden", "true") - uid = obj.get_uid() args = self.env.get_args() # Obtain basic event information, generating any necessary editing controls. - if self.is_initial_load() or not self.is_organiser(obj): - attendees = self.get_stored_attendees(obj) + if self.is_initial_load() or not self.is_organiser(): + attendees = self.get_stored_attendees() else: - attendees = self.update_attendees_from_page(obj) + attendees = self.update_attendees_from_page() - p = self.get_current_main_period(obj) + p = self.get_current_main_period() self.show_object_datetime_controls(p) # Obtain any separate recurrences for this event. - recurrenceid = obj.get_recurrenceid() - recurrenceids = self._get_active_recurrences(uid) - replaced = not recurrenceid and p.is_replaced(recurrenceids) + recurrenceids = self._get_active_recurrences(self.uid) + replaced = not self.recurrenceid and p.is_replaced(recurrenceids) # Provide a summary of the object. @@ -508,7 +491,7 @@ for name, label in self.property_items: field = name.lower() - items = obj.get_items(name) or [] + items = self.obj.get_items(name) or [] rowspan = len(items) if name == "ATTENDEE": @@ -532,11 +515,11 @@ # basis of any potential datetime specified if dt-control is # set. - self.show_datetime_controls(obj, is_start and p.get_form_start() or p.get_form_end(), is_start) + self.show_datetime_controls(is_start and p.get_form_start() or p.get_form_end(), is_start) elif name == "DTSTART": page.td(class_="objectvalue %s replaced" % field, rowspan=2) - page.a("First occurrence replaced by a separate event", href=self.link_to(uid, replaced)) + page.a("First occurrence replaced by a separate event", href=self.link_to(self.uid, replaced)) page.td.close() page.tr.close() @@ -544,10 +527,10 @@ # Handle the summary specially. elif name == "SUMMARY": - value = args.get("summary", [obj.get_value(name)])[0] + value = args.get("summary", [self.obj.get_value(name)])[0] page.td(class_="objectvalue summary") - if self.is_organiser(obj): + if self.is_organiser(): self._control("summary", "text", value, size=80) else: page.add(value) @@ -568,12 +551,12 @@ # Obtain details of attendees to supply attributes. - self.show_attendee(obj, i, value, attendee_map.get(value)) + self.show_attendee(i, value, attendee_map.get(value)) page.tr.close() # Allow more attendees to be specified. - if self.is_organiser(obj): + if self.is_organiser(): if not first: page.tr() @@ -602,17 +585,17 @@ page.tbody.close() page.table.close() - self.show_recurrences(obj, errors) - self.show_conflicting_events(obj) - self.show_request_controls(obj) + self.show_recurrences(errors) + self.show_conflicting_events() + self.show_request_controls() page.form.close() - def show_attendee(self, obj, i, attendee, attendee_attr): + def show_attendee(self, i, attendee, attendee_attr): """ - For the given object 'obj', show the attendee in position 'i' with the - given 'attendee' value, having 'attendee_attr' as any stored attributes. + For the current object, show the attendee in position 'i' with the given + 'attendee' value, having 'attendee_attr' as any stored attributes. """ page = self.page @@ -625,7 +608,7 @@ # Show a form control as organiser for new attendees. - if self.is_organiser(obj) and (not existing or not obj.is_shared()): + if self.is_organiser() and (not existing or not self.obj.is_shared()): self._control("attendee", "value", attendee, size="40") else: self._control("attendee", "hidden", attendee) @@ -641,7 +624,7 @@ # button in order to refresh the page and show a control for # the current user, if indicated. - elif self.is_organiser(obj) and not existing: + elif self.is_organiser() and not existing: self._control("partstat-refresh", "submit", "refresh", id="partstat-%d" % i, class_="refresh") page.label(dict(self.partstat_items).get(partstat, ""), for_="partstat-%s" % i, class_="partstat") else: @@ -649,11 +632,11 @@ # Permit organisers to remove attendees. - if self.is_organiser(obj): + if self.is_organiser(): # Permit the removal of newly-added attendees. - remove_type = (not existing or not obj.is_shared() or attendee == self.user) and "submit" or "checkbox" + remove_type = (not existing or not self.obj.is_shared() or attendee == self.user) and "submit" or "checkbox" self._control("remove", remove_type, str(i), str(i) in args.get("remove", []), id="remove-%d" % i, class_="remove") page.label("Remove", for_="remove-%d" % i, class_="remove") @@ -664,48 +647,44 @@ page.td.close() - def show_recurrences(self, obj, errors=None): + def show_recurrences(self, errors=None): """ - Show recurrences for the object having the given representation 'obj'. - If 'errors' is given, show a suitable message for the different errors - provided. + Show recurrences for the current object. If 'errors' is given, show a + suitable message for the different errors provided. """ page = self.page # Obtain any parent object if this object is a specific recurrence. - uid = obj.get_uid() - recurrenceid = obj.get_recurrenceid() - - if recurrenceid: - parent = self.get_stored_object(uid, None) + if self.recurrenceid: + parent = self.get_stored_object(self.uid, None) if not parent: return page.p() - page.a("This event modifies a recurring event.", href=self.link_to(uid)) + page.a("This event modifies a recurring event.", href=self.link_to(self.uid)) page.p.close() # Obtain the periods associated with the event. # NOTE: Add a control to add recurrences here. - recurrences = self.get_current_recurrences(obj) + recurrences = self.get_current_recurrences() if len(recurrences) < 1: return - recurrenceids = self._get_recurrences(uid) + recurrenceids = self._get_recurrences(self.uid) page.p("This event occurs on the following occasions within the next %d days:" % self.get_window_size()) # Show each recurrence in a separate table if editable. - if self.is_organiser(obj) and recurrences: + if self.is_organiser() and recurrences: for index, period in enumerate(recurrences): - self.show_recurrence(obj, index, period, recurrenceid, recurrenceids, errors) + self.show_recurrence(index, period, self.recurrenceid, recurrenceids, errors) # Otherwise, use a compact single table. @@ -722,21 +701,21 @@ for index, period in enumerate(recurrences): page.tr() - self.show_recurrence_label(period, recurrenceid, recurrenceids, True) - self.show_recurrence_label(period, recurrenceid, recurrenceids, False) + self.show_recurrence_label(period, self.recurrenceid, recurrenceids, True) + self.show_recurrence_label(period, self.recurrenceid, recurrenceids, False) page.tr.close() page.tbody.close() page.table.close() - def show_recurrence(self, obj, index, period, recurrenceid, recurrenceids, errors=None): + def show_recurrence(self, index, period, recurrenceid, recurrenceids, errors=None): """ - Show recurrence controls for a recurrence provided by 'obj' with the - given 'index' position in the list of periods, the given 'period' - details, where a 'recurrenceid' indicates any specific recurrence, and - where 'recurrenceids' indicates all known additional recurrences for the - object. + Show recurrence controls for a recurrence provided by the current object + with the given 'index' position in the list of periods, the given + 'period' details, where a 'recurrenceid' indicates any specific + recurrence, and where 'recurrenceids' indicates all known additional + recurrences for the object. If 'errors' is given, show a suitable message for the different errors provided. @@ -761,12 +740,12 @@ page.tr() error = errors and ("dtstart", index) in errors and " error" or "" page.th("Start", class_="objectheading start%s" % error) - self.show_recurrence_controls(obj, index, period, recurrenceid, recurrenceids, True) + self.show_recurrence_controls(index, period, recurrenceid, recurrenceids, True) page.tr.close() page.tr() error = errors and ("dtend", index) in errors and " error" or "" page.th("End", class_="objectheading end%s" % error) - self.show_recurrence_controls(obj, index, period, recurrenceid, recurrenceids, False) + self.show_recurrence_controls(index, period, recurrenceid, recurrenceids, False) page.tr.close() # Permit the removal of recurrences. @@ -776,7 +755,7 @@ page.th("") page.td() - remove_type = not obj.is_shared() or not period.origin and "submit" or "checkbox" + remove_type = not self.obj.is_shared() or not period.origin and "submit" or "checkbox" self._control("recur-remove", remove_type, str(index), str(index) in args.get("recur-remove", []), id="recur-remove-%d" % index, class_="remove") @@ -795,28 +774,24 @@ page.div.close() - def show_conflicting_events(self, obj): + def show_conflicting_events(self): - """ - Show conflicting events for the object having the representation 'obj'. - """ + "Show conflicting events for the current object." page = self.page - uid = obj.get_uid() - recurrenceid = obj.get_recurrenceid() - recurrenceids = self._get_active_recurrences(uid) + recurrenceids = self._get_active_recurrences(self.uid) # Obtain the user's timezone. tzid = self.get_tzid() - periods = self.get_periods(obj) + periods = self.get_periods(self.obj) # Indicate whether there are conflicting events. conflicts = [] - attendee_map = uri_dict(obj.get_value_map("ATTENDEE")) + attendee_map = uri_dict(self.obj.get_value_map("ATTENDEE")) - for participant in self.get_current_attendees(obj): + for participant in self.get_current_attendees(): if participant == self.user: freebusy = self.store.get_freebusy(participant) else: @@ -827,21 +802,21 @@ # Obtain any time zone details from the suggested event. - _dtstart, attr = obj.get_item("DTSTART") + _dtstart, attr = self.obj.get_item("DTSTART") tzid = attr.get("TZID", tzid) # Show any conflicts with periods of actual attendance. participant_attr = attendee_map.get(participant) partstat = participant_attr and participant_attr.get("PARTSTAT") - recurrences = obj.get_recurrence_start_points(recurrenceids, tzid) + recurrences = self.obj.get_recurrence_start_points(recurrenceids, tzid) for p in have_conflict(freebusy, periods, True): - if not recurrenceid and p.is_replaced(recurrences): + if not self.recurrenceid and p.is_replaced(recurrences): continue if ( # Unidentified or different event - (p.uid != uid or recurrenceid and p.recurrenceid and p.recurrenceid != recurrenceid) and + (p.uid != self.uid or self.recurrenceid and p.recurrenceid and p.recurrenceid != self.recurrenceid) and # Different period or unclear participation with the same period (p not in periods or not partstat in ("ACCEPTED", "TENTATIVE")) and # Participant not limited to organising @@ -945,19 +920,19 @@ id=_id("dttimes-enable", index) ) - def show_datetime_controls(self, obj, formdate, show_start): + def show_datetime_controls(self, formdate, show_start): """ - Show datetime details from the given 'obj' for the 'formdate', showing - start details if 'show_start' is set to a true value. Details will - appear as controls for organisers and labels for attendees. + Show datetime details from the current object for the 'formdate', + showing start details if 'show_start' is set to a true value. Details + will appear as controls for organisers and labels for attendees. """ page = self.page # Show controls for editing as organiser. - if self.is_organiser(obj): + if self.is_organiser(): page.td(class_="objectvalue dt%s" % (show_start and "start" or "end")) if show_start: @@ -989,11 +964,11 @@ else: page.td("(Unrecognised date)") - def show_recurrence_controls(self, obj, index, period, recurrenceid, recurrenceids, show_start): + def show_recurrence_controls(self, index, period, recurrenceid, recurrenceids, show_start): """ - Show datetime details from the given 'obj' for the recurrence having the - given 'index', with the recurrence period described by 'period', + Show datetime details from the current object for the recurrence having + the given 'index', with the recurrence period described by 'period', indicating a start, end and origin of the period from the event details, employing any 'recurrenceid' and 'recurrenceids' for the object to configure the displayed information. @@ -1011,7 +986,7 @@ # Show controls for editing as organiser. - if self.is_organiser(obj) and not replaced: + if self.is_organiser() and not replaced: page.td(class_="objectvalue dt%s" % (show_start and "start" or "end")) read_only = period.origin == "RRULE" @@ -1084,17 +1059,18 @@ uid, recurrenceid, section = self.get_identifiers(path_info) obj = self.get_stored_object(uid, recurrenceid, section) + self.set_object(obj) if not obj: return False - errors = self.handle_request(obj) + errors = self.handle_request() if not errors: return True self.new_page(title="Event") - self.show_object_on_page(obj, errors) + self.show_object_on_page(errors) return True diff -r d40e26697850 -r 07be124ea824 imipweb/resource.py --- a/imipweb/resource.py Sat Sep 19 01:08:17 2015 +0200 +++ b/imipweb/resource.py Sat Sep 19 17:23:16 2015 +0200 @@ -20,7 +20,7 @@ """ from datetime import datetime -from imiptools.client import Client +from imiptools.client import Client, ClientForObject from imiptools.data import get_uri, uri_values from imiptools.dates import get_recurrence_start_point from imiptools.period import remove_period, remove_affected_period @@ -29,17 +29,21 @@ import imip_store import markup -class Resource(Client): +class Resource: - "A Web application resource and calendar client." + "A Web application resource." def __init__(self, resource=None): + + """ + Initialise a resource, allowing it to share the environment of any given + existing 'resource'. + """ + self.encoding = "utf-8" self.env = CGIEnvironment(self.encoding) - user = self.env.get_user() - Client.__init__(self, user and get_uri(user) or None) - + self.objects = {} self.locale = None self.requests = None @@ -47,14 +51,6 @@ self.page = resource and resource.page or markup.page() self.html_ids = None - self.store = imip_store.FileStore() - self.objects = {} - - try: - self.publisher = imip_store.FilePublisher() - except OSError: - self.publisher = None - # Presentation methods. def new_page(self, title): @@ -208,55 +204,22 @@ def remove_event(self, uid, recurrenceid=None): return self.store.remove_event(self.user, uid, recurrenceid) - def update_freebusy(self, uid, recurrenceid, obj): +class ResourceClient(Resource, Client): - """ - Update stored free/busy details for the event with the given 'uid' and - 'recurrenceid' having a representation of 'obj'. - """ - - is_only_organiser = self.user not in uri_values(obj.get_values("ATTENDEE")) - - freebusy = self.store.get_freebusy(self.user) + "A Web application resource and calendar client." - 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"), - obj.get_value("ORGANIZER")) - - # Subtract any recurrences from the free/busy details of a parent - # object. - - for recurrenceid in self._get_recurrences(uid): - remove_affected_period(freebusy, uid, obj.get_recurrence_start_point(recurrenceid, self.get_tzid())) + def __init__(self, resource=None): + Resource.__init__(self, resource) + user = self.env.get_user() + Client.__init__(self, user and get_uri(user) or None) - self.store.set_freebusy(self.user, freebusy) - self.publish_freebusy(freebusy) +class ResourceClientForObject(Resource, ClientForObject): - # Update free/busy provider information if the event may recur - # indefinitely. - - if obj.possibly_recurring_indefinitely(): - self.store.append_freebusy_provider(self.user, obj) + "A Web application resource and calendar client for a specific object." - def remove_from_freebusy(self, uid, recurrenceid=None): - freebusy = self.store.get_freebusy(self.user) - remove_period(freebusy, uid, recurrenceid) - self.store.set_freebusy(self.user, freebusy) - self.publish_freebusy(freebusy) - - # Update free/busy provider information if the event may recur - # indefinitely. - - if obj.possibly_recurring_indefinitely(): - self.store.remove_freebusy_provider(self.user, obj) - - def publish_freebusy(self, freebusy): - - "Publish the details if configured to share them." - - if self.publisher and self.is_sharing() and self.is_publishing(): - self.publisher.set_freebusy(self.user, freebusy) + def __init__(self, resource=None): + Resource.__init__(self, resource) + user = self.env.get_user() + ClientForObject.__init__(self, None, user and get_uri(user) or None) # vim: tabstop=4 expandtab shiftwidth=4