1.1 --- a/imiptools/client.py Wed Jul 29 16:40:28 2015 +0200
1.2 +++ b/imiptools/client.py Wed Jul 29 18:41:11 2015 +0200
1.3 @@ -32,49 +32,6 @@
1.4 from imiptools.profile import Preferences
1.5 import imip_store
1.6
1.7 -def update_attendees(obj, attendees, removed):
1.8 -
1.9 - """
1.10 - Update the attendees in 'obj' with the given 'attendees' and 'removed'
1.11 - attendee lists. A list is returned containing the attendees whose
1.12 - attendance should be cancelled.
1.13 - """
1.14 -
1.15 - to_cancel = []
1.16 -
1.17 - existing_attendees = uri_values(obj.get_values("ATTENDEE") or [])
1.18 - added = set(attendees).difference(existing_attendees)
1.19 -
1.20 - if added or removed:
1.21 - attendees = uri_items(obj.get_items("ATTENDEE") or [])
1.22 - sequence = obj.get_value("SEQUENCE")
1.23 -
1.24 - if removed:
1.25 - remaining = []
1.26 -
1.27 - for attendee, attendee_attr in attendees:
1.28 - if attendee in removed:
1.29 -
1.30 - # Without a sequence number, assume that the event has not
1.31 - # been published and that attendees can be silently removed.
1.32 -
1.33 - if sequence is not None:
1.34 - to_cancel.append((attendee, attendee_attr))
1.35 - else:
1.36 - remaining.append((attendee, attendee_attr))
1.37 -
1.38 - attendees = remaining
1.39 -
1.40 - if added:
1.41 - for attendee in added:
1.42 - attendee = attendee.strip()
1.43 - if attendee:
1.44 - attendees.append((get_uri(attendee), {"PARTSTAT" : "NEEDS-ACTION", "RSVP" : "TRUE"}))
1.45 -
1.46 - obj["ATTENDEE"] = attendees
1.47 -
1.48 - return to_cancel
1.49 -
1.50 class Client:
1.51
1.52 "Common handler and manager methods."
1.53 @@ -126,6 +83,49 @@
1.54
1.55 # Common operations on calendar data.
1.56
1.57 + def update_attendees(self, obj, attendees, removed):
1.58 +
1.59 + """
1.60 + Update the attendees in 'obj' with the given 'attendees' and 'removed'
1.61 + attendee lists. A list is returned containing the attendees whose
1.62 + attendance should be cancelled.
1.63 + """
1.64 +
1.65 + to_cancel = []
1.66 +
1.67 + existing_attendees = uri_values(obj.get_values("ATTENDEE") or [])
1.68 + added = set(attendees).difference(existing_attendees)
1.69 +
1.70 + if added or removed:
1.71 + attendees = uri_items(obj.get_items("ATTENDEE") or [])
1.72 + sequence = obj.get_value("SEQUENCE")
1.73 +
1.74 + if removed:
1.75 + remaining = []
1.76 +
1.77 + for attendee, attendee_attr in attendees:
1.78 + if attendee in removed:
1.79 +
1.80 + # Without a sequence number, assume that the event has not
1.81 + # been published and that attendees can be silently removed.
1.82 +
1.83 + if sequence is not None:
1.84 + to_cancel.append((attendee, attendee_attr))
1.85 + else:
1.86 + remaining.append((attendee, attendee_attr))
1.87 +
1.88 + attendees = remaining
1.89 +
1.90 + if added:
1.91 + for attendee in added:
1.92 + attendee = attendee.strip()
1.93 + if attendee:
1.94 + attendees.append((get_uri(attendee), {"PARTSTAT" : "NEEDS-ACTION", "RSVP" : "TRUE"}))
1.95 +
1.96 + obj["ATTENDEE"] = attendees
1.97 +
1.98 + return to_cancel
1.99 +
1.100 def update_participation(self, obj, partstat=None):
1.101
1.102 """
3.1 --- a/imipweb/event.py Wed Jul 29 16:40:28 2015 +0200
3.2 +++ b/imipweb/event.py Wed Jul 29 18:41:11 2015 +0200
3.3 @@ -20,7 +20,6 @@
3.4 """
3.5
3.6 from datetime import date, timedelta
3.7 -from imiptools.client import update_attendees
3.8 from imiptools.data import get_uri, uri_dict, uri_values
3.9 from imiptools.dates import format_datetime, get_datetime_item, \
3.10 get_period_item, to_date, to_timezone
3.11 @@ -60,11 +59,51 @@
3.12 (None, "Not indicated"),
3.13 ]
3.14
3.15 + # Access to stored object information.
3.16 +
3.17 def is_organiser(self, obj):
3.18 return get_uri(obj.get_value("ORGANIZER")) == self.user
3.19
3.20 + def get_stored_attendees(self, obj):
3.21 + return uri_values(obj.get_values("ATTENDEE") or [])
3.22 +
3.23 + def get_stored_main_period(self, obj):
3.24 +
3.25 + """
3.26 + Return the main event period for the given 'obj'.
3.27 + """
3.28 +
3.29 + dtstart, dtstart_attr = obj.get_datetime_item("DTSTART")
3.30 +
3.31 + if obj.has_key("DTEND"):
3.32 + dtend, dtend_attr = obj.get_datetime_item("DTEND")
3.33 + elif obj.has_key("DURATION"):
3.34 + duration = obj.get_duration("DURATION")
3.35 + dtend = dtstart + duration
3.36 + dtend_attr = dtstart_attr
3.37 + else:
3.38 + dtend, dtend_attr = dtstart, dtstart_attr
3.39 +
3.40 + return EventPeriod(dtstart, dtend, self.get_tzid(), None, dtstart_attr, dtend_attr)
3.41 +
3.42 + def get_stored_recurrences(self, obj):
3.43 +
3.44 + "Return recurrences computed using the given 'obj'."
3.45 +
3.46 + recurrences = []
3.47 + for period in self.get_periods(obj):
3.48 + if period.origin != "DTSTART":
3.49 + recurrences.append(period)
3.50 + return recurrences
3.51 +
3.52 # Request logic methods.
3.53
3.54 + def is_initial_load(self):
3.55 +
3.56 + "Return whether the event is being loaded and shown for the first time."
3.57 +
3.58 + return not self.env.get_args().has_key("editing")
3.59 +
3.60 def handle_request(self, obj):
3.61
3.62 """
3.63 @@ -124,9 +163,7 @@
3.64 # Set the periods in the object, first obtaining removed and
3.65 # modified period information.
3.66
3.67 - to_unschedule = []
3.68 - for i in args.get("recur-remove", []):
3.69 - to_unschedule.append(periods[int(i)])
3.70 + to_unschedule = self.get_removed_periods()
3.71
3.72 self.set_period_in_object(obj, period)
3.73 self.set_periods_in_object(obj, periods)
3.74 @@ -138,9 +175,9 @@
3.75
3.76 # Obtain any participants and those to be removed.
3.77
3.78 - attendees = self.get_attendees()
3.79 + attendees = self.get_attendees_from_page()
3.80 removed = [attendees[int(i)] for i in args.get("remove", [])]
3.81 - to_cancel = update_attendees(obj, attendees, removed)
3.82 + to_cancel = self.update_attendees(obj, attendees, removed)
3.83 single_user = not attendees or attendees == [self.user]
3.84
3.85 # Update attendee participation for the current user.
3.86 @@ -291,29 +328,16 @@
3.87 return all_values
3.88
3.89 def get_current_main_period(self, obj):
3.90 - if self.is_initial_load() or not self.is_organiser(obj):
3.91 - return self.get_existing_main_period(obj)
3.92 - else:
3.93 - return self.get_main_period()
3.94 -
3.95 - def get_existing_main_period(self, obj):
3.96
3.97 """
3.98 - Return the main event period for the given 'obj'.
3.99 + Return the currently active main period for 'obj' depending on whether
3.100 + editing has begun or whether the object has just been loaded.
3.101 """
3.102
3.103 - dtstart, dtstart_attr = obj.get_datetime_item("DTSTART")
3.104 -
3.105 - if obj.has_key("DTEND"):
3.106 - dtend, dtend_attr = obj.get_datetime_item("DTEND")
3.107 - elif obj.has_key("DURATION"):
3.108 - duration = obj.get_duration("DURATION")
3.109 - dtend = dtstart + duration
3.110 - dtend_attr = dtstart_attr
3.111 + if self.is_initial_load() or not self.is_organiser(obj):
3.112 + return self.get_stored_main_period(obj)
3.113 else:
3.114 - dtend, dtend_attr = dtstart, dtstart_attr
3.115 -
3.116 - return EventPeriod(dtstart, dtend, self.get_tzid(), None, dtstart_attr, dtend_attr)
3.117 + return self.get_main_period()
3.118
3.119 def get_main_period(self):
3.120
3.121 @@ -336,20 +360,10 @@
3.122 """
3.123
3.124 if self.is_initial_load() or not self.is_organiser(obj):
3.125 - return self.get_existing_recurrences(obj)
3.126 + return self.get_stored_recurrences(obj)
3.127 else:
3.128 return self.get_recurrences()
3.129
3.130 - def get_existing_recurrences(self, obj):
3.131 -
3.132 - "Return recurrences computed using the given 'obj'."
3.133 -
3.134 - recurrences = []
3.135 - for period in self.get_periods(obj):
3.136 - if period.origin != "DTSTART":
3.137 - recurrences.append(period)
3.138 - return recurrences
3.139 -
3.140 def get_recurrences(self):
3.141
3.142 "Return the recurrences defined in the event form."
3.143 @@ -374,11 +388,15 @@
3.144
3.145 return periods
3.146
3.147 - def is_initial_load(self):
3.148 + def get_removed_periods(self):
3.149 +
3.150 + "Return a list of recurrence periods to remove upon updating an event."
3.151
3.152 - "Return whether the event is being loaded and shown for the first time."
3.153 -
3.154 - return not self.env.get_args().has_key("editing")
3.155 + to_unschedule = []
3.156 + args = self.env.get_args()
3.157 + for i in args.get("recur-remove", []):
3.158 + to_unschedule.append(periods[int(i)])
3.159 + return to_unschedule
3.160
3.161 def get_current_attendees(self, obj):
3.162
3.163 @@ -388,14 +406,11 @@
3.164 """
3.165
3.166 if self.is_initial_load() or not self.is_organiser(obj):
3.167 - return self.get_existing_attendees(obj)
3.168 + return self.get_stored_attendees(obj)
3.169 else:
3.170 - return self.get_attendees()
3.171 + return self.get_attendees_from_page()
3.172
3.173 - def get_existing_attendees(self, obj):
3.174 - return uri_values(obj.get_values("ATTENDEE") or [])
3.175 -
3.176 - def get_attendees(self):
3.177 + def get_attendees_from_page(self):
3.178
3.179 """
3.180 Return attendees from the request, normalised for iCalendar purposes,
3.181 @@ -418,15 +433,14 @@
3.182
3.183 return ordered_attendees
3.184
3.185 - def update_attendees(self, obj):
3.186 + def update_attendees_from_page(self, obj):
3.187
3.188 "Add or remove attendees. This does not affect the stored object."
3.189
3.190 args = self.env.get_args()
3.191
3.192 - attendees = self.get_attendees()
3.193 - existing_attendees = self.get_existing_attendees(obj)
3.194 - sequence = obj.get_value("SEQUENCE")
3.195 + attendees = self.get_attendees_from_page()
3.196 + existing_attendees = self.get_stored_attendees(obj)
3.197
3.198 if args.has_key("add"):
3.199 attendees.append("")
3.200 @@ -443,7 +457,7 @@
3.201
3.202 existing = attendee in existing_attendees
3.203
3.204 - if not existing or sequence is None or attendee == self.user:
3.205 + if not existing or not obj.is_shared() or attendee == self.user:
3.206 attendees.remove(attendee)
3.207
3.208 return attendees
3.209 @@ -460,7 +474,6 @@
3.210 attendees = self.get_current_attendees(obj)
3.211 is_attendee = self.user in attendees
3.212 is_request = (obj.get_uid(), obj.get_recurrenceid()) in self._get_requests()
3.213 - sequence = obj.get_value("SEQUENCE")
3.214
3.215 # Show appropriate options depending on the role of the user.
3.216
3.217 @@ -479,10 +492,10 @@
3.218 page.p("As organiser, you can perform the following:")
3.219
3.220 page.p()
3.221 - self._control("create", "submit", sequence is None and "Create event" or "Update event")
3.222 + self._control("create", "submit", not obj.is_shared() and "Create event" or "Update event")
3.223 page.add(" ")
3.224
3.225 - if sequence is not None and not is_request:
3.226 + if obj.is_shared() and not is_request:
3.227 self._control("cancel", "submit", "Cancel event")
3.228 else:
3.229 self._control("discard", "submit", "Discard event")
3.230 @@ -512,9 +525,9 @@
3.231 # Obtain basic event information, generating any necessary editing controls.
3.232
3.233 if self.is_initial_load() or not self.is_organiser(obj):
3.234 - attendees = self.get_existing_attendees(obj)
3.235 + attendees = self.get_stored_attendees(obj)
3.236 else:
3.237 - attendees = self.update_attendees(obj)
3.238 + attendees = self.update_attendees_from_page(obj)
3.239
3.240 p = self.get_current_main_period(obj)
3.241 self.show_object_datetime_controls(p)
3.242 @@ -650,13 +663,12 @@
3.243
3.244 existing = attendee_attr is not None
3.245 partstat = attendee_attr and attendee_attr.get("PARTSTAT")
3.246 - sequence = obj.get_value("SEQUENCE")
3.247
3.248 page.td(class_="objectvalue")
3.249
3.250 # Show a form control as organiser for new attendees.
3.251
3.252 - if self.is_organiser(obj) and (not existing or sequence is None):
3.253 + if self.is_organiser(obj) and (not existing or not obj.is_shared()):
3.254 self._control("attendee", "value", attendee, size="40")
3.255 else:
3.256 self._control("attendee", "hidden", attendee)
3.257 @@ -684,7 +696,7 @@
3.258
3.259 # Permit the removal of newly-added attendees.
3.260
3.261 - remove_type = (not existing or sequence is None or attendee == self.user) and "submit" or "checkbox"
3.262 + remove_type = (not existing or not obj.is_shared() or attendee == self.user) and "submit" or "checkbox"
3.263 self._control("remove", remove_type, str(i), str(i) in args.get("remove", []), id="remove-%d" % i, class_="remove")
3.264
3.265 page.label("Remove", for_="remove-%d" % i, class_="remove")
3.266 @@ -773,8 +785,6 @@
3.267 page = self.page
3.268 args = self.env.get_args()
3.269
3.270 - sequence = obj.get_value("SEQUENCE")
3.271 -
3.272 p = event_period_from_period(period)
3.273 replaced = not recurrenceid and self.is_replaced(p, recurrenceids)
3.274
3.275 @@ -806,7 +816,7 @@
3.276 page.th("")
3.277 page.td()
3.278
3.279 - remove_type = sequence is None or not period.origin and "submit" or "checkbox"
3.280 + remove_type = not obj.is_shared() or not period.origin and "submit" or "checkbox"
3.281 self._control("recur-remove", remove_type, str(index),
3.282 str(index) in args.get("recur-remove", []),
3.283 id="recur-remove-%d" % index, class_="remove")