1.1 --- a/imip_manager.py Thu Feb 12 22:35:16 2015 +0100
1.2 +++ b/imip_manager.py Thu Feb 12 22:41:50 2015 +0100
1.3 @@ -320,12 +320,12 @@
1.4 def _get_uid(self, path_info):
1.5 return path_info.lstrip("/").split("/", 1)[0]
1.6
1.7 - def _get_object(self, uid):
1.8 - if self.objects.has_key(uid):
1.9 - return self.objects[uid]
1.10 + def _get_object(self, uid, recurrenceid=None):
1.11 + if self.objects.has_key((uid, recurrenceid)):
1.12 + return self.objects[(uid, recurrenceid)]
1.13
1.14 - fragment = uid and self.store.get_event(self.user, uid) or None
1.15 - obj = self.objects[uid] = fragment and Object(fragment)
1.16 + fragment = uid and self.store.get_event(self.user, uid, recurrenceid) or None
1.17 + obj = self.objects[(uid, recurrenceid)] = fragment and Object(fragment)
1.18 return obj
1.19
1.20 def _get_requests(self):
1.21 @@ -337,11 +337,11 @@
1.22
1.23 def _get_request_summary(self):
1.24 summary = []
1.25 - for uid in self._get_requests():
1.26 - obj = self._get_object(uid)
1.27 + for uid, recurrenceid in self._get_requests():
1.28 + obj = self._get_object(uid, recurrenceid)
1.29 if obj:
1.30 for start, end in obj.get_periods_for_freebusy(self.get_tzid()):
1.31 - summary.append((start, end, uid))
1.32 + summary.append((start, end, uid, obj.get_value("TRANSP"), recurrenceid))
1.33 return summary
1.34
1.35 # Preference methods.
1.36 @@ -369,21 +369,21 @@
1.37
1.38 # Data management methods.
1.39
1.40 - def remove_request(self, uid):
1.41 - return self.store.dequeue_request(self.user, uid)
1.42 + def remove_request(self, uid, recurrenceid=None):
1.43 + return self.store.dequeue_request(self.user, uid, recurrenceid)
1.44
1.45 - def remove_event(self, uid):
1.46 - return self.store.remove_event(self.user, uid)
1.47 + def remove_event(self, uid, recurrenceid=None):
1.48 + return self.store.remove_event(self.user, uid, recurrenceid)
1.49
1.50 - def update_freebusy(self, uid, obj):
1.51 + def update_freebusy(self, uid, recurrenceid, obj):
1.52 tzid = self.get_tzid()
1.53 freebusy = self.store.get_freebusy(self.user)
1.54 update_freebusy(freebusy, self.user, obj.get_periods_for_freebusy(tzid),
1.55 - obj.get_value("TRANSP"), uid, self.store)
1.56 + obj.get_value("TRANSP"), uid, recurrenceid, self.store)
1.57
1.58 - def remove_from_freebusy(self, uid):
1.59 + def remove_from_freebusy(self, uid, recurrenceid=None):
1.60 freebusy = self.store.get_freebusy(self.user)
1.61 - remove_from_freebusy(freebusy, self.user, uid, self.store)
1.62 + remove_from_freebusy(freebusy, self.user, uid, recurrenceid, self.store)
1.63
1.64 # Presentation methods.
1.65
1.66 @@ -509,9 +509,9 @@
1.67 participant = get_uri(participant)
1.68 rwrite(("ATTENDEE", {"RSVP" : "TRUE", "PARTSTAT" : "NEEDS-ACTION"}, participant))
1.69
1.70 - obj = ("VEVENT", {}, record)
1.71 + node = ("VEVENT", {}, record)
1.72
1.73 - self.store.set_event(self.user, this_uid, obj)
1.74 + self.store.set_event(self.user, this_uid, node=node)
1.75 self.store.queue_request(self.user, this_uid)
1.76
1.77 # Redirect to the object (or the first of the objects), where instead of
1.78 @@ -626,8 +626,8 @@
1.79 # Save single user events.
1.80
1.81 elif save:
1.82 - self.store.set_event(self.user, uid, obj.to_node())
1.83 - self.update_freebusy(uid, obj)
1.84 + self.store.set_event(self.user, uid, node=obj.to_node())
1.85 + self.update_freebusy(uid, None, obj=obj)
1.86 self.remove_request(uid)
1.87
1.88 # Remove the request and the object.
1.89 @@ -707,7 +707,7 @@
1.90 attendees = uri_values((obj.get_values("ATTENDEE") or []) + args.get("attendee", []))
1.91 is_attendee = self.user in attendees
1.92
1.93 - is_request = obj.get_value("UID") in self._get_requests()
1.94 + is_request = (obj.get_value("UID"), obj.get_value("RECURRENCE-ID")) in self._get_requests()
1.95
1.96 have_other_attendees = len(attendees) > (is_attendee and 1 or 0)
1.97
1.98 @@ -1088,7 +1088,7 @@
1.99 page.tbody()
1.100
1.101 for t in conflicts:
1.102 - start, end, found_uid = t[:3]
1.103 + start, end, found_uid, transp, found_recurrenceid = t[:5]
1.104
1.105 # Provide details of any conflicting event.
1.106
1.107 @@ -1101,7 +1101,7 @@
1.108
1.109 page.td()
1.110
1.111 - found_obj = self._get_object(found_uid)
1.112 + found_obj = self._get_object(found_uid, found_recurrenceid)
1.113 if found_obj:
1.114 page.a(found_obj.get_value("SUMMARY"), href=self.env.new_url(found_uid))
1.115 else:
1.116 @@ -1133,11 +1133,11 @@
1.117
1.118 self.page.ul()
1.119
1.120 - for request in requests:
1.121 - obj = self._get_object(request)
1.122 + for uid, recurrenceid in requests:
1.123 + obj = self._get_object(uid, recurrenceid)
1.124 if obj:
1.125 self.page.li()
1.126 - self.page.a(obj.get_value("SUMMARY"), href="#request-%s" % request)
1.127 + self.page.a(obj.get_value("SUMMARY"), href="#request-%s-%s" % (uid, recurrenceid or ""))
1.128 self.page.li.close()
1.129
1.130 self.page.ul.close()
1.131 @@ -1197,7 +1197,7 @@
1.132 "Show an object request using the given 'path_info' for the current user."
1.133
1.134 uid = self._get_uid(path_info)
1.135 - obj = self._get_object(uid)
1.136 + obj = self._get_object(uid, None)
1.137
1.138 if not obj:
1.139 return False
1.140 @@ -1559,7 +1559,7 @@
1.141 page.td.close()
1.142 empty = 0
1.143
1.144 - start, end, uid, key = get_freebusy_details(t)
1.145 + start, end, uid, recurrenceid, key = get_freebusy_details(t)
1.146 span = spans[key]
1.147
1.148 # Produce a table cell only at the start of the period
1.149 @@ -1567,7 +1567,7 @@
1.150
1.151 if point == start or continuation:
1.152
1.153 - obj = self._get_object(uid)
1.154 + obj = self._get_object(uid, recurrenceid)
1.155
1.156 has_continued = continuation and point != start
1.157 will_continue = not ends_on_same_day(point, end, tzid)
1.158 @@ -1581,9 +1581,11 @@
1.159 )
1.160
1.161 # Only anchor the first cell of events.
1.162 + # NOTE: Need to only anchor the first period for a
1.163 + # NOTE: recurring event.
1.164
1.165 if point == start:
1.166 - page.td(class_=css, rowspan=span, id="%s-%s" % (group_type, uid))
1.167 + page.td(class_=css, rowspan=span, id="%s-%s-%s" % (group_type, uid, recurrenceid or ""))
1.168 else:
1.169 page.td(class_=css, rowspan=span)
1.170
1.171 @@ -1595,7 +1597,7 @@
1.172 # Only link to events if they are not being
1.173 # updated by requests.
1.174
1.175 - if uid in self._get_requests() and group_type != "request":
1.176 + if (uid, recurrenceid) in self._get_requests() and group_type != "request":
1.177 page.span(summary)
1.178 else:
1.179 href = "%s/%s" % (self.env.get_url().rstrip("/"), uid)
2.1 --- a/imip_store.py Thu Feb 12 22:35:16 2015 +0100
2.2 +++ b/imip_store.py Thu Feb 12 22:41:50 2015 +0100
2.3 @@ -24,7 +24,7 @@
2.4 from imiptools.data import make_calendar, parse_object, to_stream
2.5 from imiptools.filesys import fix_permissions, FileBase
2.6 from os.path import exists, isfile, join
2.7 -from os import listdir, remove
2.8 +from os import listdir, remove, rmdir
2.9 from time import sleep
2.10
2.11 class FileStore(FileBase):
2.12 @@ -40,6 +40,66 @@
2.13 def release_lock(self, user):
2.14 FileBase.release_lock(self, user)
2.15
2.16 + def _set_defaults(self, t, empty_defaults):
2.17 + for i, default in empty_defaults:
2.18 + if i >= len(t):
2.19 + t += [None] * (i - len(t) + 1)
2.20 + if not t[i]:
2.21 + t[i] = default
2.22 + return t
2.23 +
2.24 + def _get_table(self, user, filename, empty_defaults=None):
2.25 +
2.26 + """
2.27 + From the file for the given 'user' having the given 'filename', return
2.28 + a list of tuples representing the file's contents.
2.29 +
2.30 + The 'empty_defaults' is a list of (index, value) tuples indicating the
2.31 + default value where a column either does not exist or provides an empty
2.32 + value.
2.33 + """
2.34 +
2.35 + self.acquire_lock(user)
2.36 + try:
2.37 + f = open(filename, "rb")
2.38 + try:
2.39 + l = []
2.40 + for line in f.readlines():
2.41 + t = line.strip().split("\t")
2.42 + if empty_defaults:
2.43 + t = self._set_defaults(t, empty_defaults)
2.44 + l.append(tuple(t))
2.45 + return l
2.46 + finally:
2.47 + f.close()
2.48 + finally:
2.49 + self.release_lock(user)
2.50 +
2.51 + def _set_table(self, user, filename, items, empty_defaults=None):
2.52 +
2.53 + """
2.54 + For the given 'user', write to the file having the given 'filename' the
2.55 + 'items'.
2.56 +
2.57 + The 'empty_defaults' is a list of (index, value) tuples indicating the
2.58 + default value where a column either does not exist or provides an empty
2.59 + value.
2.60 + """
2.61 +
2.62 + self.acquire_lock(user)
2.63 + try:
2.64 + f = open(filename, "wb")
2.65 + try:
2.66 + for item in items:
2.67 + if empty_defaults:
2.68 + item = self._set_defaults(list(item), empty_defaults)
2.69 + f.write("\t".join(item) + "\n")
2.70 + finally:
2.71 + f.close()
2.72 + fix_permissions(filename)
2.73 + finally:
2.74 + self.release_lock(user)
2.75 +
2.76 def _get_object(self, user, filename):
2.77
2.78 """
2.79 @@ -88,6 +148,17 @@
2.80
2.81 return True
2.82
2.83 + def _remove_collection(self, filename):
2.84 +
2.85 + "Remove the collection with the given 'filename'."
2.86 +
2.87 + try:
2.88 + rmdir(filename)
2.89 + except OSError:
2.90 + return False
2.91 +
2.92 + return True
2.93 +
2.94 def get_events(self, user):
2.95
2.96 "Return a list of event identifiers."
2.97 @@ -98,7 +169,20 @@
2.98
2.99 return [name for name in listdir(filename) if isfile(join(filename, name))]
2.100
2.101 - def get_event(self, user, uid):
2.102 + def get_event(self, user, uid, recurrenceid=None):
2.103 +
2.104 + """
2.105 + Get the event for the given 'user' with the given 'uid'. If
2.106 + the optional 'recurrenceid' is specified, a specific instance or
2.107 + occurrence of an event is returned.
2.108 + """
2.109 +
2.110 + if recurrenceid:
2.111 + return self.get_recurrence(user, uid, recurrenceid)
2.112 + else:
2.113 + return self.get_complete_event(user, uid)
2.114 +
2.115 + def get_complete_event(self, user, uid):
2.116
2.117 "Get the event for the given 'user' with the given 'uid'."
2.118
2.119 @@ -108,7 +192,20 @@
2.120
2.121 return self._get_object(user, filename)
2.122
2.123 - def set_event(self, user, uid, node):
2.124 + def set_event(self, user, uid, recurrenceid, node):
2.125 +
2.126 + """
2.127 + Set an event for 'user' having the given 'uid' and 'recurrenceid' (which
2.128 + if the latter is specified, a specific instance or occurrence of an
2.129 + event is referenced), using the given 'node' description.
2.130 + """
2.131 +
2.132 + if recurrenceid:
2.133 + return self.set_recurrence(user, uid, recurrenceid, node)
2.134 + else:
2.135 + return self.set_complete_event(user, uid, node)
2.136 +
2.137 + def set_complete_event(self, user, uid, node):
2.138
2.139 "Set an event for 'user' having the given 'uid' and 'node'."
2.140
2.141 @@ -120,12 +217,31 @@
2.142
2.143 def remove_event(self, user, uid):
2.144
2.145 + """
2.146 + Remove an event for 'user' having the given 'uid'. If the optional
2.147 + 'recurrenceid' is specified, a specific instance or occurrence of an
2.148 + event is removed.
2.149 + """
2.150 +
2.151 + if recurrenceid:
2.152 + return self.remove_recurrence(user, uid, recurrenceid)
2.153 + else:
2.154 + for recurrenceid in self.get_recurrences(user, uid) or []:
2.155 + self.remove_recurrence(user, uid, recurrenceid)
2.156 + return self.remove_complete_event(user, uid)
2.157 +
2.158 + def remove_complete_event(self, user, uid):
2.159 +
2.160 "Remove an event for 'user' having the given 'uid'."
2.161
2.162 filename = self.get_object_in_store(user, "objects", uid)
2.163 if not filename:
2.164 return False
2.165
2.166 + recurrences = self.get_object_in_store(user, "recurrences", uid)
2.167 + if recurrences:
2.168 + self._remove_collection(recurrences)
2.169 +
2.170 return self._remove_object(filename)
2.171
2.172 def get_recurrences(self, user, uid):
2.173 @@ -168,7 +284,7 @@
2.174
2.175 "Remove an event for 'user' having the given 'uid'."
2.176
2.177 - filename = self.get_object_in_store(user, "objects", uid)
2.178 + filename = self.get_object_in_store(user, "recurrences", uid)
2.179 if not filename:
2.180 return False
2.181
2.182 @@ -182,7 +298,7 @@
2.183 if not filename or not exists(filename):
2.184 return []
2.185 else:
2.186 - return self._get_freebusy(user, filename)
2.187 + return self._get_table(user, filename, [(4, None)])
2.188
2.189 def get_freebusy_for_other(self, user, other):
2.190
2.191 @@ -192,24 +308,7 @@
2.192 if not filename or not exists(filename):
2.193 return []
2.194 else:
2.195 - return self._get_freebusy(user, filename)
2.196 -
2.197 - def _get_freebusy(self, user, filename):
2.198 -
2.199 - "For the given 'user', get the free/busy details from 'filename'."
2.200 -
2.201 - self.acquire_lock(user)
2.202 - try:
2.203 - f = open(filename)
2.204 - try:
2.205 - l = []
2.206 - for line in f.readlines():
2.207 - l.append(tuple(line.strip().split("\t")))
2.208 - return l
2.209 - finally:
2.210 - f.close()
2.211 - finally:
2.212 - self.release_lock(user)
2.213 + return self._get_table(user, filename, [(4, None)])
2.214
2.215 def set_freebusy(self, user, freebusy):
2.216
2.217 @@ -219,7 +318,7 @@
2.218 if not filename:
2.219 return False
2.220
2.221 - self._set_freebusy(user, filename, freebusy)
2.222 + self._set_table(user, filename, freebusy, [(3, "OPAQUE"), (4, "")])
2.223 return True
2.224
2.225 def set_freebusy_for_other(self, user, freebusy, other):
2.226 @@ -230,28 +329,9 @@
2.227 if not filename:
2.228 return False
2.229
2.230 - self._set_freebusy(user, filename, freebusy)
2.231 + self._set_table(user, filename, freebusy, [(2, ""), (3, "OPAQUE"), (4, "")])
2.232 return True
2.233
2.234 - def _set_freebusy(self, user, filename, freebusy):
2.235 -
2.236 - """
2.237 - For the given 'user', write to the file having the given 'filename' the
2.238 - 'freebusy' details.
2.239 - """
2.240 -
2.241 - self.acquire_lock(user)
2.242 - try:
2.243 - f = open(filename, "w")
2.244 - try:
2.245 - for item in freebusy:
2.246 - f.write("\t".join([(value or "OPAQUE") for value in item]) + "\n")
2.247 - finally:
2.248 - f.close()
2.249 - fix_permissions(filename)
2.250 - finally:
2.251 - self.release_lock(user)
2.252 -
2.253 def _get_requests(self, user, queue):
2.254
2.255 "Get requests for the given 'user' from the given 'queue'."
2.256 @@ -260,15 +340,7 @@
2.257 if not filename or not exists(filename):
2.258 return None
2.259
2.260 - self.acquire_lock(user)
2.261 - try:
2.262 - f = open(filename)
2.263 - try:
2.264 - return [line.strip() for line in f.readlines()]
2.265 - finally:
2.266 - f.close()
2.267 - finally:
2.268 - self.release_lock(user)
2.269 + return self._get_table(user, filename, [(1, None)])
2.270
2.271 def get_requests(self, user):
2.272
2.273 @@ -298,7 +370,7 @@
2.274 f = open(filename, "w")
2.275 try:
2.276 for request in requests:
2.277 - print >>f, request
2.278 + print >>f, "\t".join([value or "" for value in request])
2.279 finally:
2.280 f.close()
2.281 fix_permissions(filename)
2.282 @@ -319,9 +391,12 @@
2.283
2.284 return self._set_requests(user, cancellations, "cancellations")
2.285
2.286 - def _set_request(self, user, request, queue):
2.287 + def _set_request(self, user, uid, recurrenceid, queue):
2.288
2.289 - "For the given 'user', set the queued 'request' in the given 'queue'."
2.290 + """
2.291 + For the given 'user', set the queued 'uid' and 'recurrenceid' in the
2.292 + given 'queue'.
2.293 + """
2.294
2.295 filename = self.get_object_in_store(user, queue)
2.296 if not filename:
2.297 @@ -331,7 +406,7 @@
2.298 try:
2.299 f = open(filename, "a")
2.300 try:
2.301 - print >>f, request
2.302 + print >>f, "\t".join([uid, recurrenceid or ""])
2.303 finally:
2.304 f.close()
2.305 fix_permissions(filename)
2.306 @@ -340,51 +415,63 @@
2.307
2.308 return True
2.309
2.310 - def set_request(self, user, request):
2.311 + def set_request(self, user, uid, recurrenceid=None):
2.312
2.313 - "For the given 'user', set the queued 'request'."
2.314 + "For the given 'user', set the queued 'uid' and 'recurrenceid'."
2.315
2.316 - return self._set_request(user, request, "requests")
2.317 + return self._set_request(user, uid, recurrenceid, "requests")
2.318
2.319 - def set_cancellation(self, user, cancellation):
2.320 + def set_cancellation(self, user, uid, recurrenceid=None):
2.321 +
2.322 + "For the given 'user', set the queued 'uid' and 'recurrenceid'."
2.323
2.324 - "For the given 'user', set the queued 'cancellation'."
2.325 + return self._set_request(user, uid, recurrenceid, "cancellations")
2.326
2.327 - return self._set_request(user, cancellation, "cancellations")
2.328 + def queue_request(self, user, uid, recurrenceid=None):
2.329
2.330 - def queue_request(self, user, uid):
2.331 -
2.332 - "Queue a request for 'user' having the given 'uid'."
2.333 + """
2.334 + Queue a request for 'user' having the given 'uid'. If the optional
2.335 + 'recurrenceid' is specified, the request refers to a specific instance
2.336 + or occurrence of an event.
2.337 + """
2.338
2.339 requests = self.get_requests(user) or []
2.340
2.341 - if uid not in requests:
2.342 - return self.set_request(user, uid)
2.343 + if (uid, recurrenceid) not in requests:
2.344 + return self.set_request(user, uid, recurrenceid)
2.345
2.346 return False
2.347
2.348 - def dequeue_request(self, user, uid):
2.349 + def dequeue_request(self, user, uid, recurrenceid=None):
2.350
2.351 - "Dequeue a request for 'user' having the given 'uid'."
2.352 + """
2.353 + Dequeue a request for 'user' having the given 'uid'. If the optional
2.354 + 'recurrenceid' is specified, the request refers to a specific instance
2.355 + or occurrence of an event.
2.356 + """
2.357
2.358 requests = self.get_requests(user) or []
2.359
2.360 try:
2.361 - requests.remove(uid)
2.362 + requests.remove((uid, recurrenceid))
2.363 self.set_requests(user, requests)
2.364 except ValueError:
2.365 return False
2.366 else:
2.367 return True
2.368
2.369 - def cancel_event(self, user, uid):
2.370 + def cancel_event(self, user, uid, recurrenceid=None):
2.371
2.372 - "Queue an event for cancellation for 'user' having the given 'uid'."
2.373 + """
2.374 + Queue an event for cancellation for 'user' having the given 'uid'. If
2.375 + the optional 'recurrenceid' is specified, a specific instance or
2.376 + occurrence of an event is cancelled.
2.377 + """
2.378
2.379 cancellations = self.get_cancellations(user) or []
2.380
2.381 - if uid not in cancellations:
2.382 - return self.set_cancellation(user, uid)
2.383 + if (uid, recurrenceid) not in cancellations:
2.384 + return self.set_cancellation(user, uid, recurrenceid)
2.385
2.386 return False
2.387
2.388 @@ -410,7 +497,7 @@
2.389 rwrite(("UID", {}, user))
2.390 rwrite(("DTSTAMP", {}, datetime.utcnow().strftime("%Y%m%dT%H%M%SZ")))
2.391
2.392 - for start, end, uid, transp in freebusy:
2.393 + for start, end, uid, transp, recurrenceid in freebusy:
2.394 if not transp or transp == "OPAQUE":
2.395 rwrite(("FREEBUSY", {"FBTYPE" : "BUSY"}, "/".join([start, end])))
2.396
3.1 --- a/imiptools/content.py Thu Feb 12 22:35:16 2015 +0100
3.2 +++ b/imiptools/content.py Thu Feb 12 22:41:50 2015 +0100
3.3 @@ -115,6 +115,7 @@
3.4
3.5 self.obj = None
3.6 self.uid = None
3.7 + self.recurrenceid = None
3.8 self.sequence = None
3.9 self.dtstamp = None
3.10
3.11 @@ -128,6 +129,7 @@
3.12 def set_object(self, obj):
3.13 self.obj = obj
3.14 self.uid = self.obj.get_value("UID")
3.15 + self.recurrenceid = self.obj.get_value("RECURRENCE-ID")
3.16 self.sequence = self.obj.get_value("SEQUENCE")
3.17 self.dtstamp = self.obj.get_value("DTSTAMP")
3.18
3.19 @@ -166,14 +168,14 @@
3.20 # Access to calendar structures and other data.
3.21
3.22 def remove_from_freebusy(self, freebusy, attendee):
3.23 - remove_from_freebusy(freebusy, attendee, self.uid, self.store)
3.24 + remove_from_freebusy(freebusy, attendee, self.uid, self.recurrenceid, self.store)
3.25
3.26 def remove_from_freebusy_for_other(self, freebusy, user, other):
3.27 - remove_from_freebusy_for_other(freebusy, user, other, self.uid, self.store)
3.28 + remove_from_freebusy_for_other(freebusy, user, other, self.uid, self.recurrenceid, self.store)
3.29
3.30 def update_freebusy(self, freebusy, attendee, periods):
3.31 update_freebusy(freebusy, attendee, periods, self.obj.get_value("TRANSP"),
3.32 - self.uid, self.store)
3.33 + self.uid, self.recurrenceid, self.store)
3.34
3.35 def update_freebusy_from_participant(self, user, participant_item):
3.36
3.37 @@ -192,7 +194,7 @@
3.38 update_freebusy_for_other(freebusy, user, participant,
3.39 self.obj.get_periods_for_freebusy(tzid=None),
3.40 self.obj.get_value("TRANSP"),
3.41 - self.uid, self.store)
3.42 + self.uid, self.recurrenceid, self.store)
3.43 else:
3.44 self.remove_from_freebusy_for_other(freebusy, user, participant)
3.45
3.46 @@ -213,7 +215,7 @@
3.47 self.update_freebusy_from_participant(organiser, attendee_item)
3.48
3.49 def can_schedule(self, freebusy, periods):
3.50 - return can_schedule(freebusy, periods, self.uid)
3.51 + return can_schedule(freebusy, periods, self.uid, self.recurrenceid)
3.52
3.53 def filter_by_senders(self, mapping):
3.54
3.55 @@ -335,7 +337,7 @@
3.56 given 'user' and for the given 'objtype'.
3.57 """
3.58
3.59 - fragment = self.store.get_event(user, self.uid)
3.60 + fragment = self.store.get_event(user, self.uid, self.recurrenceid)
3.61 return fragment and Object(fragment)
3.62
3.63 def have_new_object(self, attendee, obj=None):
3.64 @@ -418,10 +420,7 @@
3.65 event = obj.to_node()
3.66 recurrenceid = obj.get_value("RECURRENCE-ID")
3.67
3.68 - if not recurrenceid:
3.69 - self.store.set_event(identity, self.uid, event)
3.70 - else:
3.71 - self.store.set_recurrence(identity, self.uid, recurrenceid, event)
3.72 + self.store.set_event(identity, self.uid, self.recurrenceid, event)
3.73
3.74 return True
3.75
4.1 --- a/imiptools/handlers/person.py Thu Feb 12 22:35:16 2015 +0100
4.2 +++ b/imiptools/handlers/person.py Thu Feb 12 22:41:50 2015 +0100
4.3 @@ -52,19 +52,14 @@
4.4 # Set the complete event if not an additional occurrence.
4.5
4.6 event = self.obj.to_node()
4.7 - recurrenceid = self.obj.get_value("RECURRENCE-ID")
4.8 -
4.9 - if not recurrenceid:
4.10 - self.store.set_event(attendee, self.uid, event)
4.11 - else:
4.12 - self.store.set_recurrence(attendee, self.uid, recurrenceid, event)
4.13 + self.store.set_event(attendee, self.uid, self.recurrenceid, event)
4.14
4.15 # Queue any request.
4.16
4.17 if queue:
4.18 - self.store.queue_request(attendee, self.uid)
4.19 + self.store.queue_request(attendee, self.uid, self.recurrenceid)
4.20 elif cancel:
4.21 - self.store.cancel_event(attendee, self.uid)
4.22 + self.store.cancel_event(attendee, self.uid, self.recurrenceid)
4.23
4.24 # No return message will occur to update the free/busy
4.25 # information, so this is done here.
5.1 --- a/imiptools/handlers/person_outgoing.py Thu Feb 12 22:35:16 2015 +0100
5.2 +++ b/imiptools/handlers/person_outgoing.py Thu Feb 12 22:41:50 2015 +0100
5.3 @@ -62,12 +62,7 @@
5.4 # Set the complete event if not an additional occurrence.
5.5
5.6 event = self.obj.to_node()
5.7 - recurrenceid = self.obj.get_value("RECURRENCE-ID")
5.8 -
5.9 - if not recurrenceid:
5.10 - self.store.set_event(identity, self.uid, event)
5.11 - else:
5.12 - self.store.set_recurrence(identity, self.uid, recurrenceid, event)
5.13 + self.store.set_event(identity, self.uid, self.recurrenceid, event)
5.14
5.15 else:
5.16 organiser_item, attendees = self.require_organiser_and_attendees(from_organiser)
5.17 @@ -75,7 +70,7 @@
5.18
5.19 # Remove any associated request.
5.20
5.21 - self.store.dequeue_request(identity, self.uid)
5.22 + self.store.dequeue_request(identity, self.uid, self.recurrenceid)
5.23
5.24 # Update free/busy information.
5.25
5.26 @@ -123,7 +118,7 @@
5.27 given_attendees = set(uri_values(self.obj.get_values("ATTENDEE")))
5.28
5.29 if given_attendees == all_attendees:
5.30 - self.store.cancel_event(identity, self.uid)
5.31 + self.store.cancel_event(identity, self.uid, self.recurrenceid)
5.32
5.33 # Otherwise, remove the given attendees and update the event.
5.34
5.35 @@ -141,16 +136,11 @@
5.36 # Set the complete event if not an additional occurrence.
5.37
5.38 event = obj.to_node()
5.39 - recurrenceid = obj.get_value("RECURRENCE-ID")
5.40 -
5.41 - if not recurrenceid:
5.42 - self.store.set_event(identity, self.uid, event)
5.43 - else:
5.44 - self.store.set_recurrence(identity, self.uid, recurrenceid, event)
5.45 + self.store.set_event(identity, self.uid, self.recurrenceid, event)
5.46
5.47 # Remove any associated request.
5.48
5.49 - self.store.dequeue_request(identity, self.uid)
5.50 + self.store.dequeue_request(identity, self.uid, self.recurrenceid)
5.51
5.52 # Update free/busy information.
5.53
6.1 --- a/imiptools/handlers/resource.py Thu Feb 12 22:35:16 2015 +0100
6.2 +++ b/imiptools/handlers/resource.py Thu Feb 12 22:41:50 2015 +0100
6.3 @@ -87,12 +87,7 @@
6.4 # Set the complete event if not an additional occurrence.
6.5
6.6 event = self.obj.to_node()
6.7 - recurrenceid = self.obj.get_value("RECURRENCE-ID")
6.8 -
6.9 - if not recurrenceid:
6.10 - self.store.set_event(attendee, self.uid, event)
6.11 - else:
6.12 - self.store.set_recurrence(attendee, self.uid, recurrenceid, event)
6.13 + self.store.set_event(attendee, self.uid, self.recurrenceid, event)
6.14
6.15 # Only update free/busy details if the event is scheduled.
6.16
6.17 @@ -108,7 +103,7 @@
6.18
6.19 def _cancel_for_attendee(self, attendee, attendee_attr):
6.20
6.21 - self.store.cancel_event(attendee, self.uid)
6.22 + self.store.cancel_event(attendee, self.uid, self.recurrenceid)
6.23
6.24 freebusy = self.store.get_freebusy(attendee)
6.25 self.remove_from_freebusy(freebusy, attendee)
7.1 --- a/imiptools/period.py Thu Feb 12 22:35:16 2015 +0100
7.2 +++ b/imiptools/period.py Thu Feb 12 22:41:50 2015 +0100
7.3 @@ -25,16 +25,16 @@
7.4
7.5 # Time management with datetime strings in the UTC time zone.
7.6
7.7 -def can_schedule(freebusy, periods, uid):
7.8 +def can_schedule(freebusy, periods, uid, recurrenceid):
7.9
7.10 """
7.11 Return whether the 'freebusy' list can accommodate the given 'periods'
7.12 - employing the specified 'uid'.
7.13 + employing the specified 'uid' and 'recurrenceid'.
7.14 """
7.15
7.16 for conflict in have_conflict(freebusy, periods, True):
7.17 - start, end, found_uid, found_transp = conflict
7.18 - if found_uid != uid:
7.19 + start, end, found_uid, found_transp, found_recurrenceid = conflict
7.20 + if found_uid != uid and found_recurrenceid != recurrenceid:
7.21 return False
7.22
7.23 return True
7.24 @@ -64,11 +64,11 @@
7.25 def insert_period(freebusy, period):
7.26 insort_left(freebusy, period)
7.27
7.28 -def remove_period(freebusy, uid):
7.29 +def remove_period(freebusy, uid, recurrenceid=None):
7.30 i = 0
7.31 while i < len(freebusy):
7.32 t = freebusy[i]
7.33 - if len(t) >= 3 and t[2] == uid:
7.34 + if len(t) >= 5 and t[2] == uid and t[4] == recurrenceid:
7.35 del freebusy[i]
7.36 else:
7.37 i += 1
7.38 @@ -332,7 +332,7 @@
7.39 for point, active in slots:
7.40 for t in active:
7.41 if t and len(t) >= 2:
7.42 - start, end, uid, key = get_freebusy_details(t)
7.43 + start, end, uid, recurrenceid, key = get_freebusy_details(t)
7.44
7.45 try:
7.46 start_slot = points.index(start)
7.47 @@ -348,73 +348,74 @@
7.48
7.49 def get_freebusy_details(t):
7.50
7.51 - "Return a tuple of the form (start, end, uid, key) from 't'."
7.52 + "Return a tuple of the form (start, end, uid, recurrenceid, key) from 't'."
7.53
7.54 # Handle both complete free/busy details...
7.55
7.56 - if len(t) > 2:
7.57 - start, end, uid = t[:3]
7.58 - key = uid
7.59 + if len(t) > 4:
7.60 + start, end, uid, transp, recurrenceid = t[:5]
7.61 + key = uid, recurrenceid
7.62
7.63 # ...and published details without specific event details.
7.64
7.65 else:
7.66 start, end = t[:2]
7.67 uid = None
7.68 + recurrenceid = None
7.69 key = (start, end)
7.70
7.71 - return start, end, uid, key
7.72 + return start, end, uid, recurrenceid, key
7.73
7.74 -def remove_from_freebusy(freebusy, attendee, uid, store):
7.75 +def remove_from_freebusy(freebusy, attendee, uid, recurrenceid, store):
7.76
7.77 """
7.78 For the given 'attendee', remove periods from 'freebusy' that are associated
7.79 - with 'uid' in the 'store'.
7.80 + with 'uid' and 'recurrenceid' in the 'store'.
7.81 """
7.82
7.83 - remove_period(freebusy, uid)
7.84 + remove_period(freebusy, uid, recurrenceid)
7.85 store.set_freebusy(attendee, freebusy)
7.86
7.87 -def remove_from_freebusy_for_other(freebusy, user, other, uid, store):
7.88 +def remove_from_freebusy_for_other(freebusy, user, other, uid, recurrenceid, store):
7.89
7.90 """
7.91 For the given 'user', remove for the 'other' party periods from 'freebusy'
7.92 - that are associated with 'uid' in the 'store'.
7.93 + that are associated with 'uid' and 'recurrenceid' in the 'store'.
7.94 """
7.95
7.96 - remove_period(freebusy, uid)
7.97 + remove_period(freebusy, uid, recurrenceid)
7.98 store.set_freebusy_for_other(user, freebusy, other)
7.99
7.100 -def _update_freebusy(freebusy, periods, transp, uid):
7.101 +def _update_freebusy(freebusy, periods, transp, uid, recurrenceid):
7.102
7.103 """
7.104 Update the free/busy details with the given 'periods', 'transp' setting and
7.105 - 'uid'.
7.106 + 'uid' plus 'recurrenceid'.
7.107 """
7.108
7.109 - remove_period(freebusy, uid)
7.110 + remove_period(freebusy, uid, recurrenceid)
7.111
7.112 for start, end in periods:
7.113 - insert_period(freebusy, (start, end, uid, transp))
7.114 + insert_period(freebusy, (start, end, uid, transp, recurrenceid))
7.115
7.116 -def update_freebusy(freebusy, attendee, periods, transp, uid, store):
7.117 +def update_freebusy(freebusy, attendee, periods, transp, uid, recurrenceid, store):
7.118
7.119 """
7.120 For the given 'attendee', update the free/busy details with the given
7.121 - 'periods', 'transp' setting and 'uid' in the 'store'.
7.122 + 'periods', 'transp' setting and 'uid' plus 'recurrenceid' in the 'store'.
7.123 """
7.124
7.125 - _update_freebusy(freebusy, periods, transp, uid)
7.126 + _update_freebusy(freebusy, periods, transp, uid, recurrenceid)
7.127 store.set_freebusy(attendee, freebusy)
7.128
7.129 -def update_freebusy_for_other(freebusy, user, other, periods, transp, uid, store):
7.130 +def update_freebusy_for_other(freebusy, user, other, periods, transp, uid, recurrenceid, store):
7.131
7.132 """
7.133 For the given 'user', update the free/busy details of 'other' with the given
7.134 - 'periods', 'transp' setting and 'uid' in the 'store'.
7.135 + 'periods', 'transp' setting and 'uid' plus 'recurrenceid' in the 'store'.
7.136 """
7.137
7.138 - _update_freebusy(freebusy, periods, transp, uid)
7.139 + _update_freebusy(freebusy, periods, transp, uid, recurrenceid)
7.140 store.set_freebusy_for_other(user, freebusy, other)
7.141
7.142 # vim: tabstop=4 expandtab shiftwidth=4