1 #!/usr/bin/env python 2 3 """ 4 Handlers for a person for whom scheduling is performed, inspecting outgoing 5 messages to obtain scheduling done externally. 6 7 Copyright (C) 2014, 2015 Paul Boddie <paul@boddie.org.uk> 8 9 This program is free software; you can redistribute it and/or modify it under 10 the terms of the GNU General Public License as published by the Free Software 11 Foundation; either version 3 of the License, or (at your option) any later 12 version. 13 14 This program is distributed in the hope that it will be useful, but WITHOUT 15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 16 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 17 details. 18 19 You should have received a copy of the GNU General Public License along with 20 this program. If not, see <http://www.gnu.org/licenses/>. 21 """ 22 23 from imiptools.data import uri_dict, uri_item, uri_values 24 from imiptools.handlers import Handler 25 from imiptools.handlers.common import CommonEvent 26 27 class PersonHandler(Handler, CommonEvent): 28 29 "Handling mechanisms specific to people." 30 31 def set_identity(self, from_organiser=True): 32 33 """ 34 Set the current user for the current object. It is usually set when 35 initialising the handler, using the recipient details, but outgoing 36 messages do not reference the recipient in this way. 37 """ 38 39 self.user, attr = uri_item(self.obj.get_item(from_organiser and "ORGANIZER" or "ATTENDEE")) 40 41 def _add(self): 42 43 "Add a recurrence for the current object." 44 45 self.set_identity() 46 47 # Obtain valid organiser and attendee details. 48 49 oa = self.require_organiser_and_attendees() 50 if not oa: 51 return False 52 53 (organiser, organiser_attr), attendees = oa 54 55 # Ignore unknown objects. 56 57 if not self.get_stored_object_version(): 58 return 59 60 # Record the event as a recurrence of the parent object. 61 62 self.update_recurrenceid() 63 64 # Update free/busy information. 65 66 self.update_event_in_freebusy() 67 68 # Set the additional occurrence. 69 70 self.store.set_event(self.user, self.uid, self.recurrenceid, self.obj.to_node()) 71 72 return True 73 74 def _record(self, from_organiser=True): 75 76 """ 77 Record details from the current object given a message originating 78 from an organiser if 'from_organiser' is set to a true value. 79 """ 80 81 self.set_identity(from_organiser) 82 83 # Check for a new event, tolerating not-strictly-new events if the 84 # attendee is responding. 85 86 if not self.have_new_object(strict=from_organiser): 87 return False 88 89 # Update the object. 90 91 if from_organiser: 92 93 # Set the complete event or an additional occurrence. 94 95 self.store.set_event(self.user, self.uid, self.recurrenceid, self.obj.to_node()) 96 97 # Remove additional recurrences if handling a complete event. 98 99 if not self.recurrenceid: 100 self.store.remove_recurrences(self.user, self.uid) 101 102 else: 103 # Obtain valid attendees, merging their attendance with the stored 104 # object. 105 106 attendees = self.require_attendees(from_organiser) 107 self.merge_attendance(attendees) 108 109 # Remove any associated request. 110 111 self.store.dequeue_request(self.user, self.uid, self.recurrenceid) 112 113 # Update free/busy information. 114 115 self.update_event_in_freebusy(from_organiser) 116 117 return True 118 119 def _remove(self, from_organiser=True): 120 121 """ 122 Remove details from the current object given a message originating 123 from an organiser if 'from_organiser' is set to a true value. 124 """ 125 126 self.set_identity(from_organiser) 127 128 # Check for event using UID. 129 130 if not self.have_new_object(): 131 return False 132 133 # Obtain any stored object, using parent object details if a newly- 134 # indicated occurrence is referenced. 135 136 obj = self.get_stored_object_version() 137 old = not obj and self.get_parent_object() or obj 138 139 if not old: 140 return False 141 142 # Only cancel the event completely if all attendees are given. 143 144 attendees = uri_dict(old.get_value_map("ATTENDEE")) 145 all_attendees = set(attendees.keys()) 146 given_attendees = set(uri_values(self.obj.get_values("ATTENDEE"))) 147 cancel_entire_event = given_attendees == all_attendees 148 149 # Keep the event for the organiser. 150 151 if cancel_entire_event: 152 self.store.cancel_event(self.user, self.uid, self.recurrenceid) 153 154 # Otherwise, remove the given attendees and update the event. 155 156 elif obj: 157 for attendee in given_attendees: 158 if attendees.has_key(attendee): 159 del attendees[attendee] 160 obj["ATTENDEE"] = attendees.items() 161 162 # Update the stored object with sequence information. 163 164 if obj: 165 obj["SEQUENCE"] = self.obj.get_items("SEQUENCE") or [] 166 obj["DTSTAMP"] = self.obj.get_items("DTSTAMP") or [] 167 168 # Update free/busy information. 169 170 if cancel_entire_event or self.user in given_attendees: 171 self.remove_event_from_freebusy() 172 173 # Set the complete event if not an additional occurrence. For any newly- 174 # indicated occurrence, use the received event details. 175 176 self.store.set_event(self.user, self.uid, self.recurrenceid, (obj or self.obj).to_node()) 177 178 # Remove any associated request. 179 180 self.store.dequeue_request(self.user, self.uid, self.recurrenceid) 181 182 return True 183 184 class Event(PersonHandler): 185 186 "An event handler." 187 188 def add(self): 189 self._add() 190 191 def cancel(self): 192 self._remove(True) 193 194 def counter(self): 195 pass 196 197 def declinecounter(self): 198 pass 199 200 def publish(self): 201 self._record(True) 202 203 def refresh(self): 204 pass 205 206 def reply(self): 207 self._record(False) 208 209 def request(self): 210 self._record(True) 211 212 # Handler registry. 213 214 handlers = [ 215 ("VEVENT", Event), 216 ] 217 218 # vim: tabstop=4 expandtab shiftwidth=4