# HG changeset patch # User Paul Boddie # Date 1505246313 -7200 # Node ID 47093230ddb8418efcb4b46fc70ad01925a7cc99 # Parent 333740ca50b667e726056f9c8137635ec1f5ad6d Introduced common object removal for attendees and recurrences. Remove duplicate values when attendees and recurrences are modified. Prevent values from being removed if they have been explicitly added when both removed and added values are submitted for sending. diff -r 333740ca50b6 -r 47093230ddb8 imipweb/event.py --- a/imipweb/event.py Tue Sep 12 20:27:38 2017 +0200 +++ b/imipweb/event.py Tue Sep 12 21:58:33 2017 +0200 @@ -65,7 +65,8 @@ without notification. """ - return (self.can_edit_recurrence(recurrence) or not self.is_organiser()) and \ + return (not self.is_organiser() or + self.can_edit_recurrence(recurrence)) and \ recurrence.origin != "RRULE" def can_edit_recurrence(self, recurrence): @@ -87,6 +88,7 @@ notification. """ + attendee = get_uri(attendee) return self.can_edit_attendee(attendee) or attendee == self.user and self.is_organiser() def can_edit_attendee(self, attendee): @@ -848,10 +850,9 @@ # modified period information. # NOTE: Currently, rules are not updated. - to_unschedule, to_exclude = self.get_removed_periods(periods) + active_periods, to_unschedule, to_exclude = self.get_removed_periods(periods) + periods = set(periods) - active_periods = [p for p in periods if not p.replaced] - changed = self.obj.set_period(period) or changed changed = self.obj.set_periods(periods) or changed @@ -995,6 +996,19 @@ return [p.as_event_period(i) for i, p in enumerate(self.get_recurrences_from_page())] + def get_active_periods(self, periods): + + "Return a mapping of non-replaced periods to counts, given 'periods'." + + active_periods = {} + for p in periods: + if not p.replaced: + if not active_periods.has_key(p): + active_periods[p] = 1 + else: + active_periods[p] += 1 + return active_periods + # Access to form-originating object information. def get_main_period_from_page(self): @@ -1042,8 +1056,12 @@ """ args = self.env.get_args() - to_unschedule = [] - to_exclude = [] + to_unschedule = set() + to_exclude = set() + + # Get all periods that are not replaced. + + active_periods = self.get_active_periods(periods) for i in args.get("recur-remove", []): try: @@ -1051,12 +1069,26 @@ except (IndexError, ValueError): continue + active_periods[period] -= 1 + if not self.can_edit_recurrence(period) and self.is_organiser(): - to_unschedule.append(period) + l = to_unschedule else: - to_exclude.append(period) + l = to_exclude + + l.add(period) + + # Determine whether some periods are both removed and added. - return to_unschedule, to_exclude + remaining = [] + for period, n in active_periods.items(): + if n > 0: + remaining.append(period) + + to_unschedule.difference_update(remaining) + to_exclude.difference_update(remaining) + + return remaining, to_unschedule, to_exclude def get_attendees_from_page(self): @@ -1077,6 +1109,50 @@ attendee_map = self.obj.get_value_map("ATTENDEE") return [get_verbose_address(value, attendee_map.get(value)) for value in attendees] + def filter_duplicates(self, l): + + """ + Return collection 'l' filtered for duplicate values, retaining the given + element ordering. + """ + + s = set() + f = [] + + for value in l: + if value not in s: + s.add(value) + f.append(value) + + return f + + def remove_from_collection(self, l, indexes, fn): + + """ + Remove from collection 'l' all values having 'indexes' where 'fn' + applied to each referenced value returns a true value. Values where 'fn' + returns a false value are added to a list of deferred removals which is + returned. + """ + + still_to_remove = [] + correction = 0 + + for i in indexes: + try: + i = int(i) - correction + value = l[i] + except (IndexError, ValueError): + continue + + if fn(value): + del l[i] + correction += 1 + else: + still_to_remove.append(str(i)) + + return still_to_remove + def update_attendees_from_page(self): "Add or remove attendees. This does not affect the stored object." @@ -1085,7 +1161,9 @@ attendees = self.get_attendees_from_page() - if args.has_key("add"): + add = args.has_key("add") + + if add: attendees.append("") # Add attendees suggested in counter-proposals. @@ -1104,24 +1182,15 @@ # Only actually remove attendees if the event is unsent, if the attendee # is new, or if it is the current user being removed. - if args.has_key("remove"): - still_to_remove = [] - correction = 0 + remove = args.has_key("remove") - for i in args["remove"]: - try: - i = int(i) - correction - attendee = attendees[i] - except (IndexError, ValueError): - continue + if remove: + still_to_remove = self.remove_from_collection(attendees, + args["remove"], self.can_remove_attendee) + args["remove"] = still_to_remove - if self.can_remove_attendee(get_uri(attendee)): - del attendees[i] - correction += 1 - else: - still_to_remove.append(str(i)) - - args["remove"] = still_to_remove + if add or add_suggested or remove: + attendees = self.filter_duplicates(attendees) args["attendee"] = attendees return attendees @@ -1134,7 +1203,9 @@ recurrences = self.get_recurrences_from_page() - if args.has_key("recur-add"): + add = args.has_key("recur-add") + + if add: period = self.get_current_main_period().as_form_period() period.origin = "RDATE" recurrences.append(period) @@ -1142,24 +1213,15 @@ # Only actually remove recurrences if the event is unsent, or if the # recurrence is new, but only for explicit recurrences. - if args.has_key("recur-remove"): - still_to_remove = [] - correction = 0 + remove = args.has_key("recur-remove") - for i in args["recur-remove"]: - try: - i = int(i) - correction - recurrence = recurrences[i] - except (IndexError, ValueError): - continue + if remove: + still_to_remove = self.remove_from_collection(recurrences, + args["recur-remove"], self.can_remove_recurrence) + args["recur-remove"] = still_to_remove - if self.can_remove_recurrence(recurrence): - del recurrences[i] - correction += 1 - else: - still_to_remove.append(str(i)) - - args["recur-remove"] = still_to_remove + if add or remove: + recurrences = self.filter_duplicates(recurrences) self.set_recurrences_in_page(recurrences) return recurrences