1 #!/usr/bin/env python 2 3 """ 4 Handlers for a person for whom scheduling is performed. 5 """ 6 7 from email.mime.text import MIMEText 8 from imiptools.config import MANAGER_PATH, MANAGER_URL 9 from imiptools.content import Handler, to_part 10 from imiptools.handlers.common import CommonFreebusy 11 from socket import gethostname 12 from vCalendar import to_node 13 14 def get_manager_url(): 15 url_base = MANAGER_URL or "http://%s/" % gethostname() 16 return "%s/%s" % (url_base.rstrip("/"), MANAGER_PATH.lstrip("/")) 17 18 class PersonHandler(Handler): 19 20 "Handling mechanisms specific to people." 21 22 def _record_and_deliver(self, objtype, from_organiser=True, queue=False): 23 24 oa = self.require_organiser_and_attendees(from_organiser) 25 if not oa: 26 return False 27 28 (organiser, organiser_attr), attendees = organiser_item, attendees = oa 29 30 # Validate the organiser or attendee, ignoring spoofed requests. 31 32 if not self.validate_identities(from_organiser and [organiser_item] or attendees.items()): 33 return False 34 35 # Process each attendee separately. 36 37 if from_organiser: 38 for attendee, attendee_attr in attendees.items(): 39 40 if not self.have_new_object(attendee, objtype): 41 continue 42 43 # Store the object and queue any request. 44 45 self.store.set_event(attendee, self.uid, to_node( 46 {objtype : [(self.details, {})]} 47 )) 48 49 if queue: 50 self.store.queue_request(attendee, self.uid) 51 52 # As organiser, update attendance. 53 54 else: 55 obj = self.get_object(organiser, objtype) 56 57 if obj and self.have_new_object(organiser, objtype, obj): 58 attendee_map = self.get_value_map("ATTENDEE") 59 60 for attendee, attendee_attr in attendees.items(): 61 62 # Update attendance in the loaded object. 63 64 attendee_map[attendee] = attendee_attr 65 66 # Set the new details and store the object. 67 68 obj["ATTENDEE"] = attendee_map.items() 69 70 self.store.set_event(organiser, self.uid, to_node( 71 {objtype : [(obj, {})]} 72 )) 73 74 return True 75 76 def reply(self): 77 78 "Wrap any valid message and pass it on to the recipient." 79 80 attendee = self.get_value("ATTENDEE") 81 if attendee: 82 return "REPLY", MIMEText("A reply has been received from %s." % attendee) 83 84 class Event(PersonHandler): 85 86 "An event handler." 87 88 def add(self): 89 90 # NOTE: Queue a suggested modification to any active event. 91 92 # The message is now wrapped and passed on to the recipient. 93 94 return "ADD", MIMEText("An addition to an event has been received.") 95 96 def cancel(self): 97 98 # NOTE: Queue a suggested modification to any active event. 99 100 # The message is now wrapped and passed on to the recipient. 101 102 return "CANCEL", MIMEText("A cancellation has been received.") 103 104 def counter(self): 105 106 # NOTE: Queue a suggested modification to any active event. 107 108 # The message is now wrapped and passed on to the recipient. 109 110 return "COUNTER", MIMEText("A counter proposal has been received.") 111 112 def declinecounter(self): 113 114 # NOTE: Queue a suggested modification to any active event. 115 116 # The message is now wrapped and passed on to the recipient. 117 118 return "DECLINECOUNTER", MIMEText("A declining counter proposal has been received.") 119 120 def publish(self): 121 122 # NOTE: Register details of any relevant event. 123 124 # The message is now wrapped and passed on to the recipient. 125 126 return "PUBLISH", MIMEText("Details of an event have been received.") 127 128 def refresh(self): 129 130 # NOTE: Update details of any active event. 131 132 # The message is now wrapped and passed on to the recipient. 133 134 return "REFRESH", MIMEText("An event update has been received.") 135 136 def reply(self): 137 138 "Record replies and notify the recipient." 139 140 self._record_and_deliver("VEVENT", from_organiser=False, queue=False) 141 return PersonHandler.reply(self) 142 143 def request(self): 144 145 "Hold requests and notify the recipient." 146 147 self._record_and_deliver("VEVENT", from_organiser=True, queue=True) 148 149 # The message is now wrapped and passed on to the recipient. 150 151 url = "%s/%s" % (get_manager_url().rstrip("/"), self.uid) 152 return "REQUEST", MIMEText("A request has been queued and can be viewed here: %s" % url) 153 154 class Freebusy(PersonHandler, CommonFreebusy): 155 156 "A free/busy handler." 157 158 def publish(self): 159 160 # NOTE: Register free/busy information. 161 162 # The message is now wrapped and passed on to the recipient. 163 164 return "PUBLISH", MIMEText("Details of a contact's availability have been received.") 165 166 def reply(self): 167 168 "Record replies and notify the recipient." 169 170 self._record_and_deliver("VFREEBUSY", from_organiser=False, queue=False) 171 return PersonHandler.reply(self) 172 173 def request(self): 174 175 """ 176 Respond to a request by preparing a reply containing free/busy 177 information for each indicated attendee. 178 """ 179 180 # NOTE: This should be subject to policy/preferences. 181 182 return CommonFreebusy.request(self) 183 184 class Journal(PersonHandler): 185 186 "A journal entry handler." 187 188 def add(self): 189 190 # NOTE: Queue a suggested modification to any active entry. 191 192 # The message is now wrapped and passed on to the recipient. 193 194 return "ADD", MIMEText("An addition to a journal entry has been received.") 195 196 def cancel(self): 197 198 # NOTE: Queue a suggested modification to any active entry. 199 200 # The message is now wrapped and passed on to the recipient. 201 202 return "CANCEL", MIMEText("A cancellation has been received.") 203 204 def publish(self): 205 206 # NOTE: Register details of any relevant entry. 207 208 # The message is now wrapped and passed on to the recipient. 209 210 return "PUBLISH", MIMEText("Details of a journal entry have been received.") 211 212 class Todo(PersonHandler): 213 214 "A to-do item handler." 215 216 def add(self): 217 218 # NOTE: Queue a suggested modification to any active item. 219 220 # The message is now wrapped and passed on to the recipient. 221 222 return "ADD", MIMEText("An addition to an item has been received.") 223 224 def cancel(self): 225 226 # NOTE: Queue a suggested modification to any active item. 227 228 # The message is now wrapped and passed on to the recipient. 229 230 return "CANCEL", MIMEText("A cancellation has been received.") 231 232 def counter(self): 233 234 # NOTE: Queue a suggested modification to any active item. 235 236 # The message is now wrapped and passed on to the recipient. 237 238 return "COUNTER", MIMEText("A counter proposal has been received.") 239 240 def declinecounter(self): 241 242 # NOTE: Queue a suggested modification to any active item. 243 244 # The message is now wrapped and passed on to the recipient. 245 246 return "DECLINECOUNTER", MIMEText("A declining counter proposal has been received.") 247 248 def publish(self): 249 250 # NOTE: Register details of any relevant item. 251 252 # The message is now wrapped and passed on to the recipient. 253 254 return "PUBLISH", MIMEText("Details of an item have been received.") 255 256 def refresh(self): 257 258 # NOTE: Update details of any active item. 259 260 # The message is now wrapped and passed on to the recipient. 261 262 return "REFRESH", MIMEText("An item update has been received.") 263 264 def reply(self): 265 266 "Record replies and notify the recipient." 267 268 self._record_and_deliver("VTODO", from_organiser=False, queue=False) 269 return PersonHandler.reply(self) 270 271 def request(self): 272 273 "Hold requests and notify the recipient." 274 275 self._record_and_deliver("VTODO", from_organiser=True, queue=True) 276 277 # The message is now wrapped and passed on to the recipient. 278 279 return "REQUEST", MIMEText("A request has been queued.") 280 281 # Handler registry. 282 283 handlers = [ 284 ("VFREEBUSY", Freebusy), 285 ("VEVENT", Event), 286 ("VTODO", Todo), 287 ("VJOURNAL", Journal), 288 ] 289 290 # vim: tabstop=4 expandtab shiftwidth=4