# HG changeset patch # User Paul Boddie # Date 1443471376 -7200 # Node ID ef83320e1a3796e42058e6a18386e12a5949bd4f # Parent 2c7a4ff3b8a80a46e1c4368f3f82c2e00985e263 Added support for excluding recurrences of unshared events. diff -r 2c7a4ff3b8a8 -r ef83320e1a37 htdocs/styles.css --- a/htdocs/styles.css Mon Sep 28 17:04:03 2015 +0200 +++ b/htdocs/styles.css Mon Sep 28 22:16:16 2015 +0200 @@ -113,6 +113,7 @@ text-decoration: line-through; } +.objectvalue.dtstart.excluded, .objectvalue.dtstart.replaced { vertical-align: top; } diff -r 2c7a4ff3b8a8 -r ef83320e1a37 imiptools/data.py --- a/imiptools/data.py Mon Sep 28 17:04:03 2015 +0200 +++ b/imiptools/data.py Mon Sep 28 22:16:16 2015 +0200 @@ -409,6 +409,25 @@ return old_values != set(self.get_date_values("RDATE") or []) + def update_exceptions(self, excluded): + + """ + Update the exceptions to any rule by applying the list of 'excluded' + periods. + """ + + to_exclude = set(excluded).difference(self.get_date_values("EXDATE") or []) + if not to_exclude: + return False + + if not self.has_key("EXDATE"): + self["EXDATE"] = [] + + for p in to_exclude: + self["EXDATE"].append(get_period_item(p.get_start(), p.get_end())) + + return True + def correct_object(self, tzid, permitted_values): "Correct the object's period details." diff -r 2c7a4ff3b8a8 -r ef83320e1a37 imipweb/event.py --- a/imipweb/event.py Mon Sep 28 17:04:03 2015 +0200 +++ b/imipweb/event.py Mon Sep 28 22:16:16 2015 +0200 @@ -60,6 +60,12 @@ without notification. """ + return self.can_edit_recurrence(recurrence) and not recurrence.origin + + def can_edit_recurrence(self, recurrence): + + "Return whether 'recurrence' can be edited." + return self.recurrence_is_new(recurrence) or not self.obj.is_shared() def recurrence_is_new(self, recurrence): @@ -193,6 +199,7 @@ recurrenceids = self._get_active_recurrences(self.uid) replaced = not self.recurrenceid and period.is_replaced(recurrenceids) + excluded = period not in self.get_periods(self.obj) # Provide a summary of the object. @@ -221,7 +228,7 @@ # Handle datetimes specially. if name in ["DTSTART", "DTEND"]: - if not replaced: + if not replaced and not excluded: # Obtain the datetime. @@ -234,9 +241,22 @@ self.show_datetime_controls(is_start and period.get_form_start() or period.get_form_end(), is_start) elif name == "DTSTART": - page.td(class_="objectvalue %s replaced" % field, rowspan=2) - page.a("First occurrence replaced by a separate event", href=self.link_to(self.uid, replaced)) - page.td.close() + + # Replaced occurrences link to their replacements. + + if replaced: + page.td(class_="objectvalue %s replaced" % field, rowspan=2) + page.a("First occurrence replaced by a separate event", href=self.link_to(self.uid, replaced)) + page.td.close() + + # NOTE: Should provide a way of editing recurrences when the + # NOTE: first occurrence is excluded, plus a way of + # NOTE: reinstating the occurrence. + + elif excluded: + page.td(class_="objectvalue %s excluded" % field, rowspan=2) + page.add("First occurrence excluded") + page.td.close() page.tr.close() @@ -474,7 +494,8 @@ page.th("") page.td() - remove_type = (not self.obj.is_shared() or not period.origin) and "submit" or "checkbox" + remove_type = self.can_remove_recurrence(period) and "submit" or "checkbox" + self.control("recur-remove", remove_type, str(index), str(index) in args.get("recur-remove", []), id="recur-remove-%d" % index, class_="remove") @@ -715,10 +736,11 @@ # Set the periods in the object, first obtaining removed and # modified period information. - to_unschedule = self.get_removed_periods(periods) + to_unschedule, to_exclude = self.get_removed_periods(periods) self.obj.set_period(period) self.obj.set_periods(periods) + self.obj.update_exceptions(to_exclude) # Update summary. @@ -841,14 +863,26 @@ """ Return those from the recurrence 'periods' to remove upon updating an - event. + event along with those to exclude in a tuple of the form (unscheduled, + excluded). """ + args = self.env.get_args() to_unschedule = [] - args = self.env.get_args() + to_exclude = [] + for i in args.get("recur-remove", []): - to_unschedule.append(periods[int(i)]) - return to_unschedule + try: + period = periods[int(i)] + except (IndexError, ValueError): + continue + + if not self.can_edit_recurrence(period): + to_unschedule.append(period) + else: + to_exclude.append(period) + + return to_unschedule, to_exclude def get_attendees_from_page(self): @@ -916,7 +950,7 @@ # NOTE: Addition of recurrences to be supported. # Only actually remove recurrences if the event is unsent, or if the - # recurrence is new. + # recurrence is new, but only for explicit recurrences. if args.has_key("recur-remove"): still_to_remove = []