1.1 --- a/imipweb/event.py Tue Sep 12 20:28:08 2017 +0200
1.2 +++ b/imipweb/event.py Tue Sep 12 22:09:24 2017 +0200
1.3 @@ -65,7 +65,8 @@
1.4 without notification.
1.5 """
1.6
1.7 - return (self.can_edit_recurrence(recurrence) or not self.is_organiser()) and \
1.8 + return (not self.is_organiser() or
1.9 + self.can_edit_recurrence(recurrence)) and \
1.10 recurrence.origin != "RRULE"
1.11
1.12 def can_edit_recurrence(self, recurrence):
1.13 @@ -87,6 +88,7 @@
1.14 notification.
1.15 """
1.16
1.17 + attendee = get_uri(attendee)
1.18 return self.can_edit_attendee(attendee) or attendee == self.user and self.is_organiser()
1.19
1.20 def can_edit_attendee(self, attendee):
1.21 @@ -412,7 +414,7 @@
1.22
1.23 remove_type = self.can_remove_attendee(attendee_uri) and "submit" or "checkbox"
1.24 self.control("remove", remove_type, str(i),
1.25 - str(i) in self.get_state("remove", list),
1.26 + attendee in self.get_state("remove", list),
1.27 id="remove-%d" % i, class_="remove")
1.28
1.29 page.label(_("Remove"), for_="remove-%d" % i, class_="remove")
1.30 @@ -510,7 +512,7 @@
1.31 remove_type = self.can_remove_recurrence(period) and "submit" or "checkbox"
1.32
1.33 self.control("recur-remove", remove_type, str(index),
1.34 - str(index) in self.get_state("recur-remove", list),
1.35 + period in self.get_state("recur-remove", list),
1.36 id="recur-remove-%d" % index, class_="remove")
1.37
1.38 page.label(_("Remove"), for_="recur-remove-%d" % index, class_="remove")
1.39 @@ -852,10 +854,9 @@
1.40 # modified period information.
1.41 # NOTE: Currently, rules are not updated.
1.42
1.43 - to_unschedule, to_exclude = self.get_removed_periods(periods)
1.44 + active_periods, to_unschedule, to_exclude = self.get_removed_periods(periods)
1.45 +
1.46 periods = set(periods)
1.47 - active_periods = [p for p in periods if not p.replaced]
1.48 -
1.49 changed = self.obj.set_period(period) or changed
1.50 changed = self.obj.set_periods(periods) or changed
1.51
1.52 @@ -879,7 +880,7 @@
1.53 # Obtain any new participants and those to be removed.
1.54
1.55 attendees = self.get_current_attendees()
1.56 - removed = self.get_removed_attendees(attendees)
1.57 + removed = self.get_removed_attendees()
1.58
1.59 added, to_cancel = self.update_attendees(attendees, removed)
1.60 single_user = not attendees or uri_values(attendees) == [self.user]
1.61 @@ -1003,6 +1004,19 @@
1.62 periods.append(p.as_event_period(i))
1.63 return periods
1.64
1.65 + def get_active_periods(self, periods):
1.66 +
1.67 + "Return a mapping of non-replaced periods to counts, given 'periods'."
1.68 +
1.69 + active_periods = {}
1.70 + for p in periods:
1.71 + if not p.replaced:
1.72 + if not active_periods.has_key(p):
1.73 + active_periods[p] = 1
1.74 + else:
1.75 + active_periods[p] += 1
1.76 + return active_periods
1.77 +
1.78 # Access to form-originating object information.
1.79
1.80 def get_main_period_from_page(self):
1.81 @@ -1051,6 +1065,50 @@
1.82 attendee_map = self.obj.get_value_map("ATTENDEE")
1.83 return [get_verbose_address(value, attendee_map.get(value)) for value in attendees]
1.84
1.85 + def filter_duplicates(self, l):
1.86 +
1.87 + """
1.88 + Return collection 'l' filtered for duplicate values, retaining the given
1.89 + element ordering.
1.90 + """
1.91 +
1.92 + s = set()
1.93 + f = []
1.94 +
1.95 + for value in l:
1.96 + if value not in s:
1.97 + s.add(value)
1.98 + f.append(value)
1.99 +
1.100 + return f
1.101 +
1.102 + def remove_from_collection(self, l, indexes, fn):
1.103 +
1.104 + """
1.105 + Remove from collection 'l' all values having 'indexes' where 'fn'
1.106 + applied to each referenced value returns a true value. Values where 'fn'
1.107 + returns a false value are added to a list of deferred removals which is
1.108 + returned.
1.109 + """
1.110 +
1.111 + still_to_remove = []
1.112 + correction = 0
1.113 +
1.114 + for i in indexes:
1.115 + try:
1.116 + i = int(i) - correction
1.117 + value = l[i]
1.118 + except (IndexError, ValueError):
1.119 + continue
1.120 +
1.121 + if fn(value):
1.122 + del l[i]
1.123 + correction += 1
1.124 + else:
1.125 + still_to_remove.append(value)
1.126 +
1.127 + return still_to_remove
1.128 +
1.129 def update_attendees_from_page(self):
1.130
1.131 "Add or remove attendees. This does not affect the stored object."
1.132 @@ -1059,7 +1117,9 @@
1.133
1.134 attendees = self.get_attendees_from_page()
1.135
1.136 - if args.has_key("add"):
1.137 + add = args.has_key("add")
1.138 +
1.139 + if add:
1.140 attendees.append("")
1.141
1.142 # Add attendees suggested in counter-proposals.
1.143 @@ -1078,24 +1138,15 @@
1.144 # Only actually remove attendees if the event is unsent, if the attendee
1.145 # is new, or if it is the current user being removed.
1.146
1.147 - if args.has_key("remove"):
1.148 - still_to_remove = []
1.149 - correction = 0
1.150 + remove = args.has_key("remove")
1.151
1.152 - for i in args["remove"]:
1.153 - try:
1.154 - i = int(i) - correction
1.155 - attendee = attendees[i]
1.156 - except (IndexError, ValueError):
1.157 - continue
1.158 + if remove:
1.159 + still_to_remove = self.remove_from_collection(attendees,
1.160 + args["remove"], self.can_remove_attendee)
1.161 + self.set_state("remove", still_to_remove)
1.162
1.163 - if self.can_remove_attendee(get_uri(attendee)):
1.164 - del attendees[i]
1.165 - correction += 1
1.166 - else:
1.167 - still_to_remove.append(str(i))
1.168 -
1.169 - self.set_state("remove", still_to_remove)
1.170 + if add or add_suggested or remove:
1.171 + attendees = self.filter_duplicates(attendees)
1.172
1.173 return attendees
1.174
1.175 @@ -1107,7 +1158,9 @@
1.176
1.177 recurrences = self.get_recurrences_from_page()
1.178
1.179 - if args.has_key("recur-add"):
1.180 + add = args.has_key("recur-add")
1.181 +
1.182 + if add:
1.183 period = self.get_current_main_period().as_form_period()
1.184 period.origin = "RDATE"
1.185 recurrences.append(period)
1.186 @@ -1115,24 +1168,15 @@
1.187 # Only actually remove recurrences if the event is unsent, or if the
1.188 # recurrence is new, but only for explicit recurrences.
1.189
1.190 - if args.has_key("recur-remove"):
1.191 - still_to_remove = []
1.192 - correction = 0
1.193 + remove = args.has_key("recur-remove")
1.194
1.195 - for i in args["recur-remove"]:
1.196 - try:
1.197 - i = int(i) - correction
1.198 - recurrence = recurrences[i]
1.199 - except (IndexError, ValueError):
1.200 - continue
1.201 + if remove:
1.202 + still_to_remove = self.remove_from_collection(recurrences,
1.203 + args["recur-remove"], self.can_remove_recurrence)
1.204 + self.set_state("recur-remove", still_to_remove)
1.205
1.206 - if self.can_remove_recurrence(recurrence):
1.207 - del recurrences[i]
1.208 - correction += 1
1.209 - else:
1.210 - still_to_remove.append(str(i))
1.211 -
1.212 - self.set_state("recur-remove", still_to_remove)
1.213 + if add or remove:
1.214 + recurrences = self.filter_duplicates(recurrences)
1.215
1.216 return recurrences
1.217
1.218 @@ -1207,17 +1251,14 @@
1.219 self.get_stored_attendees or self.update_attendees_from_page,
1.220 overwrite=True)
1.221
1.222 - def get_removed_attendees(self, attendees):
1.223 + def get_removed_attendees(self):
1.224
1.225 """
1.226 - Return details of 'attendees' to be removed according to previously
1.227 + Return details of attendees to be removed according to previously
1.228 determined removal information.
1.229 """
1.230
1.231 - removed = []
1.232 - for i in self.get_state("remove", list):
1.233 - removed.append(attendees[int(i)])
1.234 - return removed
1.235 + return self.get_state("remove", list)
1.236
1.237 def get_removed_periods(self, periods):
1.238
1.239 @@ -1227,21 +1268,33 @@
1.240 excluded).
1.241 """
1.242
1.243 - to_unschedule = []
1.244 - to_exclude = []
1.245 + to_unschedule = set()
1.246 + to_exclude = set()
1.247 +
1.248 + # Get all periods that are not replaced.
1.249
1.250 - for i in self.get_state("recur-remove", list):
1.251 - try:
1.252 - period = periods[int(i)]
1.253 - except (IndexError, ValueError):
1.254 - continue
1.255 + active_periods = self.get_active_periods(periods)
1.256 +
1.257 + for period in self.get_state("recur-remove", list):
1.258 + active_periods[period] -= 1
1.259
1.260 if not self.can_edit_recurrence(period) and self.is_organiser():
1.261 - to_unschedule.append(period)
1.262 + l = to_unschedule
1.263 else:
1.264 - to_exclude.append(period)
1.265 + l = to_exclude
1.266 + l.add(period)
1.267 +
1.268 + # Determine whether some periods are both removed and added.
1.269
1.270 - return to_unschedule, to_exclude
1.271 + remaining = []
1.272 + for period, n in active_periods.items():
1.273 + if n > 0:
1.274 + remaining.append(period)
1.275 +
1.276 + to_unschedule.difference_update(remaining)
1.277 + to_exclude.difference_update(remaining)
1.278 +
1.279 + return remaining, to_unschedule, to_exclude
1.280
1.281 # Full page output methods.
1.282