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 _record(self, from_organiser=True): 42 43 """ 44 Record details from the current object given a message originating 45 from an organiser if 'from_organiser' is set to a true value. 46 """ 47 48 self.set_identity(from_organiser) 49 50 # Check for a new event, tolerating not-strictly-new events if the 51 # attendee is responding. 52 53 if not self.have_new_object(strict=from_organiser): 54 return False 55 56 # Update the object. 57 58 if from_organiser: 59 60 # Set the complete event or an additional occurrence. 61 62 self.store.set_event(self.user, self.uid, self.recurrenceid, self.obj.to_node()) 63 64 # Remove additional recurrences if handling a complete event. 65 66 if not self.recurrenceid: 67 self.store.remove_recurrences(self.user, self.uid) 68 69 else: 70 # Obtain valid attendees, merging their attendance with the stored 71 # object. 72 73 attendees = self.require_attendees(from_organiser) 74 self.merge_attendance(attendees) 75 76 # Remove any associated request. 77 78 self.store.dequeue_request(self.user, self.uid, self.recurrenceid) 79 80 # Update free/busy information. 81 82 self.update_event_in_freebusy(from_organiser) 83 84 return True 85 86 def _remove(self, from_organiser=True): 87 88 """ 89 Remove details from the current object given a message originating 90 from an organiser if 'from_organiser' is set to a true value. 91 """ 92 93 self.set_identity(from_organiser) 94 95 # Check for event using UID. 96 97 if not self.have_new_object(): 98 return False 99 100 # Obtain any stored object, using parent object details if a newly- 101 # indicated occurrence is referenced. 102 103 obj = self.get_stored_object_version() 104 old = not obj and self.get_parent_object() or obj 105 106 if not old: 107 return False 108 109 # Only cancel the event completely if all attendees are given. 110 111 attendees = uri_dict(old.get_value_map("ATTENDEE")) 112 all_attendees = set(attendees.keys()) 113 given_attendees = set(uri_values(self.obj.get_values("ATTENDEE"))) 114 cancel_entire_event = given_attendees == all_attendees 115 116 # Keep the event for the organiser. 117 118 if cancel_entire_event: 119 self.store.cancel_event(self.user, self.uid, self.recurrenceid) 120 121 # Otherwise, remove the given attendees and update the event. 122 123 elif obj: 124 for attendee in given_attendees: 125 if attendees.has_key(attendee): 126 del attendees[attendee] 127 obj["ATTENDEE"] = attendees.items() 128 129 # Update the stored object with sequence information. 130 131 if obj: 132 obj["SEQUENCE"] = self.obj.get_items("SEQUENCE") or [] 133 obj["DTSTAMP"] = self.obj.get_items("DTSTAMP") or [] 134 135 # Update free/busy information. 136 137 if cancel_entire_event or self.user in given_attendees: 138 self.remove_event_from_freebusy() 139 140 # Set the complete event if not an additional occurrence. For any newly- 141 # indicated occurrence, use the received event details. 142 143 self.store.set_event(self.user, self.uid, self.recurrenceid, (obj or self.obj).to_node()) 144 145 # Remove any associated request. 146 147 self.store.dequeue_request(self.user, self.uid, self.recurrenceid) 148 149 return True 150 151 class Event(PersonHandler): 152 153 "An event handler." 154 155 def add(self): 156 pass 157 158 def cancel(self): 159 self._remove(True) 160 161 def counter(self): 162 pass 163 164 def declinecounter(self): 165 pass 166 167 def publish(self): 168 self._record(True) 169 170 def refresh(self): 171 pass 172 173 def reply(self): 174 self._record(False) 175 176 def request(self): 177 self._record(True) 178 179 # Handler registry. 180 181 handlers = [ 182 ("VEVENT", Event), 183 ] 184 185 # vim: tabstop=4 expandtab shiftwidth=4