1.1 --- a/htdocs/styles.css Mon Sep 28 17:04:03 2015 +0200
1.2 +++ b/htdocs/styles.css Mon Sep 28 22:16:16 2015 +0200
1.3 @@ -113,6 +113,7 @@
1.4 text-decoration: line-through;
1.5 }
1.6
1.7 +.objectvalue.dtstart.excluded,
1.8 .objectvalue.dtstart.replaced {
1.9 vertical-align: top;
1.10 }
2.1 --- a/imiptools/data.py Mon Sep 28 17:04:03 2015 +0200
2.2 +++ b/imiptools/data.py Mon Sep 28 22:16:16 2015 +0200
2.3 @@ -409,6 +409,25 @@
2.4
2.5 return old_values != set(self.get_date_values("RDATE") or [])
2.6
2.7 + def update_exceptions(self, excluded):
2.8 +
2.9 + """
2.10 + Update the exceptions to any rule by applying the list of 'excluded'
2.11 + periods.
2.12 + """
2.13 +
2.14 + to_exclude = set(excluded).difference(self.get_date_values("EXDATE") or [])
2.15 + if not to_exclude:
2.16 + return False
2.17 +
2.18 + if not self.has_key("EXDATE"):
2.19 + self["EXDATE"] = []
2.20 +
2.21 + for p in to_exclude:
2.22 + self["EXDATE"].append(get_period_item(p.get_start(), p.get_end()))
2.23 +
2.24 + return True
2.25 +
2.26 def correct_object(self, tzid, permitted_values):
2.27
2.28 "Correct the object's period details."
3.1 --- a/imipweb/event.py Mon Sep 28 17:04:03 2015 +0200
3.2 +++ b/imipweb/event.py Mon Sep 28 22:16:16 2015 +0200
3.3 @@ -60,6 +60,12 @@
3.4 without notification.
3.5 """
3.6
3.7 + return self.can_edit_recurrence(recurrence) and not recurrence.origin
3.8 +
3.9 + def can_edit_recurrence(self, recurrence):
3.10 +
3.11 + "Return whether 'recurrence' can be edited."
3.12 +
3.13 return self.recurrence_is_new(recurrence) or not self.obj.is_shared()
3.14
3.15 def recurrence_is_new(self, recurrence):
3.16 @@ -193,6 +199,7 @@
3.17
3.18 recurrenceids = self._get_active_recurrences(self.uid)
3.19 replaced = not self.recurrenceid and period.is_replaced(recurrenceids)
3.20 + excluded = period not in self.get_periods(self.obj)
3.21
3.22 # Provide a summary of the object.
3.23
3.24 @@ -221,7 +228,7 @@
3.25 # Handle datetimes specially.
3.26
3.27 if name in ["DTSTART", "DTEND"]:
3.28 - if not replaced:
3.29 + if not replaced and not excluded:
3.30
3.31 # Obtain the datetime.
3.32
3.33 @@ -234,9 +241,22 @@
3.34 self.show_datetime_controls(is_start and period.get_form_start() or period.get_form_end(), is_start)
3.35
3.36 elif name == "DTSTART":
3.37 - page.td(class_="objectvalue %s replaced" % field, rowspan=2)
3.38 - page.a("First occurrence replaced by a separate event", href=self.link_to(self.uid, replaced))
3.39 - page.td.close()
3.40 +
3.41 + # Replaced occurrences link to their replacements.
3.42 +
3.43 + if replaced:
3.44 + page.td(class_="objectvalue %s replaced" % field, rowspan=2)
3.45 + page.a("First occurrence replaced by a separate event", href=self.link_to(self.uid, replaced))
3.46 + page.td.close()
3.47 +
3.48 + # NOTE: Should provide a way of editing recurrences when the
3.49 + # NOTE: first occurrence is excluded, plus a way of
3.50 + # NOTE: reinstating the occurrence.
3.51 +
3.52 + elif excluded:
3.53 + page.td(class_="objectvalue %s excluded" % field, rowspan=2)
3.54 + page.add("First occurrence excluded")
3.55 + page.td.close()
3.56
3.57 page.tr.close()
3.58
3.59 @@ -474,7 +494,8 @@
3.60 page.th("")
3.61 page.td()
3.62
3.63 - remove_type = (not self.obj.is_shared() or not period.origin) and "submit" or "checkbox"
3.64 + remove_type = self.can_remove_recurrence(period) and "submit" or "checkbox"
3.65 +
3.66 self.control("recur-remove", remove_type, str(index),
3.67 str(index) in args.get("recur-remove", []),
3.68 id="recur-remove-%d" % index, class_="remove")
3.69 @@ -715,10 +736,11 @@
3.70 # Set the periods in the object, first obtaining removed and
3.71 # modified period information.
3.72
3.73 - to_unschedule = self.get_removed_periods(periods)
3.74 + to_unschedule, to_exclude = self.get_removed_periods(periods)
3.75
3.76 self.obj.set_period(period)
3.77 self.obj.set_periods(periods)
3.78 + self.obj.update_exceptions(to_exclude)
3.79
3.80 # Update summary.
3.81
3.82 @@ -841,14 +863,26 @@
3.83
3.84 """
3.85 Return those from the recurrence 'periods' to remove upon updating an
3.86 - event.
3.87 + event along with those to exclude in a tuple of the form (unscheduled,
3.88 + excluded).
3.89 """
3.90
3.91 + args = self.env.get_args()
3.92 to_unschedule = []
3.93 - args = self.env.get_args()
3.94 + to_exclude = []
3.95 +
3.96 for i in args.get("recur-remove", []):
3.97 - to_unschedule.append(periods[int(i)])
3.98 - return to_unschedule
3.99 + try:
3.100 + period = periods[int(i)]
3.101 + except (IndexError, ValueError):
3.102 + continue
3.103 +
3.104 + if not self.can_edit_recurrence(period):
3.105 + to_unschedule.append(period)
3.106 + else:
3.107 + to_exclude.append(period)
3.108 +
3.109 + return to_unschedule, to_exclude
3.110
3.111 def get_attendees_from_page(self):
3.112
3.113 @@ -916,7 +950,7 @@
3.114 # NOTE: Addition of recurrences to be supported.
3.115
3.116 # Only actually remove recurrences if the event is unsent, or if the
3.117 - # recurrence is new.
3.118 + # recurrence is new, but only for explicit recurrences.
3.119
3.120 if args.has_key("recur-remove"):
3.121 still_to_remove = []