1.1 --- a/imiptools/handlers/resource.py Wed Dec 10 21:41:56 2014 +0100
1.2 +++ b/imiptools/handlers/resource.py Wed Dec 10 23:19:20 2014 +0100
1.3 @@ -8,7 +8,85 @@
1.4 from imiptools.handlers.common import CommonFreebusy
1.5 from vCalendar import to_node
1.6
1.7 -class Event(Handler):
1.8 +class ResourceHandler(Handler):
1.9 +
1.10 + "Handling mechanisms specific to resources."
1.11 +
1.12 + def _record_and_respond(self, handle_for_attendee):
1.13 +
1.14 + oa = self.require_organiser_and_attendees()
1.15 + if not oa:
1.16 + return None
1.17 +
1.18 + organiser_item, attendees = oa
1.19 +
1.20 + # Validate the organiser, ignoring spoofed requests.
1.21 +
1.22 + if not self.validate_identities([organiser_item]):
1.23 + return None
1.24 +
1.25 + # Process each attendee separately.
1.26 +
1.27 + calendar = []
1.28 +
1.29 + for attendee, attendee_attr in attendees.items():
1.30 +
1.31 + # Check for event using UID.
1.32 +
1.33 + if not self.have_new_object(attendee, "VEVENT"):
1.34 + continue
1.35 +
1.36 + # Collect response objects produced when handling the request.
1.37 +
1.38 + response = handle_for_attendee(attendee, attendee_attr)
1.39 + if response:
1.40 + calendar.append(response)
1.41 +
1.42 + return calendar
1.43 +
1.44 + def _schedule_for_attendee(self, attendee, attendee_attr):
1.45 +
1.46 + # If newer than any old version, discard old details from the
1.47 + # free/busy record and check for suitability.
1.48 +
1.49 + periods = self.get_periods()
1.50 + freebusy = self.store.get_freebusy(attendee) or []
1.51 + scheduled = self.can_schedule(freebusy, periods)
1.52 +
1.53 + attendee_attr["PARTSTAT"] = scheduled and "ACCEPTED" or "DECLINED"
1.54 + if self.messenger and self.messenger.sender != get_address(attendee):
1.55 + attendee_attr["SENT-BY"] = get_uri(self.messenger.sender)
1.56 +
1.57 + # Make a version of the request with just this attendee.
1.58 +
1.59 + self.details["ATTENDEE"] = [(attendee, attendee_attr)]
1.60 +
1.61 + event = to_node({"VEVENT" : [(self.details, {})]})
1.62 + self.store.set_event(attendee, self.uid, event)
1.63 +
1.64 + # Only update free/busy details if the event is scheduled.
1.65 +
1.66 + if scheduled:
1.67 + self.update_freebusy(freebusy, attendee, periods)
1.68 + else:
1.69 + self.remove_from_freebusy(freebusy, attendee)
1.70 +
1.71 + if self.publisher:
1.72 + self.publisher.set_freebusy(attendee, freebusy)
1.73 +
1.74 + return event
1.75 +
1.76 + def _cancel_for_attendee(self, attendee, attendee_attr):
1.77 +
1.78 + freebusy = self.store.get_freebusy(attendee) or []
1.79 + self.remove_from_freebusy(freebusy, attendee)
1.80 +
1.81 + if self.publisher:
1.82 + self.publisher.set_freebusy(attendee, freebusy)
1.83 +
1.84 + return None
1.85 +
1.86 +class Event(ResourceHandler):
1.87
1.88 "An event handler."
1.89
1.90 @@ -16,7 +94,11 @@
1.91 pass
1.92
1.93 def cancel(self):
1.94 - pass
1.95 +
1.96 + "Cancel attendance for attendees."
1.97 +
1.98 + self._record_and_respond(self._cancel_for_attendee)
1.99 + return None
1.100
1.101 def counter(self):
1.102
1.103 @@ -54,57 +136,13 @@
1.104 No support for countering requests is implemented.
1.105 """
1.106
1.107 - oa = self.require_organiser_and_attendees()
1.108 - if not oa:
1.109 - return None
1.110 -
1.111 - organiser_item, attendees = oa
1.112 -
1.113 - # Validate the organiser, ignoring spoofed requests.
1.114 -
1.115 - if not self.validate_identities([organiser_item]):
1.116 + response = self._record_and_respond(self._schedule_for_attendee)
1.117 + if response:
1.118 + return "REPLY", to_part("REPLY", response)
1.119 + else:
1.120 return None
1.121
1.122 - # Process each attendee separately.
1.123 -
1.124 - calendar = []
1.125 -
1.126 - for attendee, attendee_attr in attendees.items():
1.127 -
1.128 - # Check for event using UID.
1.129 -
1.130 - if not self.have_new_object(attendee, "VEVENT"):
1.131 - continue
1.132 -
1.133 - # If newer than any old version, discard old details from the
1.134 - # free/busy record and check for suitability.
1.135 -
1.136 - periods = self.get_periods()
1.137 - freebusy = self.store.get_freebusy(attendee) or []
1.138 - scheduled = self.can_schedule(freebusy, periods)
1.139 -
1.140 - attendee_attr["PARTSTAT"] = scheduled and "ACCEPTED" or "DECLINED"
1.141 - if self.messenger and self.messenger.sender != get_address(attendee):
1.142 - attendee_attr["SENT-BY"] = get_uri(self.messenger.sender)
1.143 - self.details["ATTENDEE"] = [(attendee, attendee_attr)]
1.144 -
1.145 - event = to_node({"VEVENT" : [(self.details, {})]})
1.146 - calendar.append(event)
1.147 - self.store.set_event(attendee, self.uid, event)
1.148 -
1.149 - # Only update free/busy details if the event is scheduled.
1.150 -
1.151 - if scheduled:
1.152 - self.update_freebusy(freebusy, attendee, periods)
1.153 - else:
1.154 - self.remove_from_freebusy(freebusy, attendee)
1.155 -
1.156 - if self.publisher:
1.157 - self.publisher.set_freebusy(attendee, freebusy)
1.158 -
1.159 - return "REPLY", to_part("REPLY", calendar)
1.160 -
1.161 -class Freebusy(Handler, CommonFreebusy):
1.162 +class Freebusy(ResourceHandler, CommonFreebusy):
1.163
1.164 "A free/busy handler."
1.165
1.166 @@ -119,7 +157,7 @@
1.167
1.168 # request provided by CommonFreeBusy.request
1.169
1.170 -class Journal(Handler):
1.171 +class Journal(ResourceHandler):
1.172
1.173 "A journal entry handler."
1.174
1.175 @@ -132,7 +170,7 @@
1.176 def publish(self):
1.177 pass
1.178
1.179 -class Todo(Handler):
1.180 +class Todo(ResourceHandler):
1.181
1.182 "A to-do item handler."
1.183