# HG changeset patch # User Paul Boddie # Date 1444149809 -7200 # Node ID a32c6487cdaa82d2a48e1fde38c6feac87582ad5 # Parent 058e27a598ae02bbeac389e1e482de6de8376189 Added basic counter-proposal acceptance support. Added a common form utility method for getting prefixed form field names as values. Moved request/event removal methods to the common client functionality, simplifying them to work on the current object. diff -r 058e27a598ae -r a32c6487cdaa imiptools/client.py --- a/imiptools/client.py Tue Oct 06 18:38:45 2015 +0200 +++ b/imiptools/client.py Tue Oct 06 18:43:29 2015 +0200 @@ -867,6 +867,12 @@ # Convenience methods for removing counter-proposals and updating the # request queue. + def remove_request(self): + return self.store.dequeue_request(self.user, self.uid, self.recurrenceid) + + def remove_event(self): + return self.store.remove_event(self.user, self.uid, self.recurrenceid) + def remove_counter(self, attendee): self.remove_counters([attendee]) diff -r 058e27a598ae -r a32c6487cdaa imipweb/event.py --- a/imipweb/event.py Tue Oct 06 18:38:45 2015 +0200 +++ b/imipweb/event.py Tue Oct 06 18:43:29 2015 +0200 @@ -574,8 +574,9 @@ if first: page.td(rowspan=len(periods)) + self.control("accept-%d" % i, "submit", "Accept") self.control("decline-%d" % i, "submit", "Decline") - self.control("decline", "hidden", attendee) + self.control("counter", "hidden", attendee) page.td.close() page.tr.close() @@ -714,9 +715,10 @@ cancel = args.has_key("cancel") ignore = args.has_key("ignore") save = args.has_key("save") - decline = filter(None, [(arg.startswith("decline-") and arg[len("decline-"):]) for arg in args.keys()]) + accept = self.prefixed_args("accept-", int) + decline = self.prefixed_args("decline-", int) - have_action = reply or discard or create or cancel or ignore or save or decline + have_action = reply or discard or create or cancel or ignore or save or accept or decline if not have_action: return ["action"] @@ -787,7 +789,7 @@ # Process the object and remove it from the list of requests. if reply and self.process_received_request(): - self.remove_request(self.uid, self.recurrenceid) + self.remove_request() elif self.is_organiser() and (invite or cancel): @@ -796,39 +798,53 @@ if self.process_created_request( invite and "REQUEST" or "CANCEL", to_cancel, to_unschedule): - self.remove_request(self.uid, self.recurrenceid) + self.remove_request() # Save single user events. elif save: 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) + self.remove_request() # Remove the request and the object. elif discard: self.remove_event_from_freebusy() - self.remove_event(self.uid, self.recurrenceid) - self.remove_request(self.uid, self.recurrenceid) + self.remove_event() + self.remove_request() + + # Update counter-proposal records synchronously instead of assuming + # that the outgoing handler will have done so before the form is + # refreshed. + + # Accept a counter-proposal and decline all others, sending a new + # request to all attendees. + + elif accept: - # Decline a counter-proposal. + # Take the first accepted proposal, although there should be only + # one anyway. + + for i in accept: + attendee_uri = get_uri(args.get("counter", [])[i]) + obj = self.get_stored_object(self.uid, self.recurrenceid, "counters", attendee_uri) + self.obj.set_periods(self.get_periods(obj)) + break + + # Remove counter-proposals and issue a new invitation. + + attendees = uri_values(args.get("counter", [])) + self.remove_counters(attendees) + self.process_created_request("REQUEST") + + # Decline a counter-proposal individually. elif decline: - for s in decline: - try: - i = int(s) - except (IndexError, ValueError): - pass - else: - attendee_uri = get_uri(args.get("decline", [])[i]) - self.process_declined_counter(attendee_uri) - - # Update the counter-proposals synchronously instead of - # assuming that the outgoing handler will have done so - # before the form is refreshed. - - self.remove_counter(attendee_uri) + for i in decline: + attendee_uri = get_uri(args.get("counter", [])[i]) + self.process_declined_counter(attendee_uri) + self.remove_counter(attendee_uri) # Redirect to the event. @@ -870,7 +886,7 @@ start = self.get_date_control_values("dtstart") end = self.get_date_control_values("dtend") - period = FormPeriod(start, end, dtend_enabled, dttimes_enabled, self.get_tzid()) + period = FormPeriod(start, end, dtend_enabled, dttimes_enabled, self.get_tzid(), "DTSTART") # Handle absent main period details. diff -r 058e27a598ae -r a32c6487cdaa imipweb/resource.py --- a/imipweb/resource.py Tue Oct 06 18:38:45 2015 +0200 +++ b/imipweb/resource.py Tue Oct 06 18:43:29 2015 +0200 @@ -195,14 +195,6 @@ def _format_datetime(self, fn, dt, format): return fn(dt, format=format, locale=self.get_user_locale()) - # Data management methods. - - def remove_request(self, uid, recurrenceid=None): - return self.store.dequeue_request(self.user, uid, recurrenceid) - - def remove_event(self, uid, recurrenceid=None): - return self.store.remove_event(self.user, uid, recurrenceid) - class ResourceClient(Resource, Client): "A Web application resource and calendar client." @@ -366,6 +358,29 @@ "Utility methods resource mix-in." + def prefixed_args(self, prefix, convert=None): + + """ + Return values for all arguments having the given 'prefix' in their + names, removing the prefix to obtain each value from the argument name + itself. The 'convert' callable can be specified to perform a conversion + (to int, for example). + """ + + args = self.env.get_args() + + values = [] + for name in args.keys(): + if name.startswith(prefix): + value = name[len(prefix):] + if convert: + try: + value = convert(value) + except ValueError: + pass + values.append(value) + return values + def control(self, name, type, value, selected=False, **kw): """