1.1 --- a/imiptools/editing.py Fri Jan 12 19:35:17 2018 +0100
1.2 +++ b/imiptools/editing.py Fri Jan 12 21:54:14 2018 +0100
1.3 @@ -3,7 +3,7 @@
1.4 """
1.5 User interface data abstractions.
1.6
1.7 -Copyright (C) 2014, 2015, 2017 Paul Boddie <paul@boddie.org.uk>
1.8 +Copyright (C) 2014, 2015, 2017, 2018 Paul Boddie <paul@boddie.org.uk>
1.9
1.10 This program is free software; you can redistribute it and/or modify it under
1.11 the terms of the GNU General Public License as published by the Free Software
1.12 @@ -30,6 +30,7 @@
1.13 to_date, to_utc_datetime, to_timezone
1.14 from imiptools.period import get_overlapping_members, RecurringPeriod
1.15 from itertools import chain
1.16 +import vRecurrence
1.17
1.18 # General editing abstractions.
1.19
1.20 @@ -153,6 +154,7 @@
1.21 "attendees" : lambda: OrderedDict(self.obj.get_items("ATTENDEE") or []),
1.22 "organiser" : lambda: self.obj.get_value("ORGANIZER"),
1.23 "periods" : lambda: CopiableList(form_periods_from_periods(self.get_unedited_periods())),
1.24 + "rule" : lambda: CopiableList(vRecurrence.get_selectors_for_rule(self.obj.get_value("RRULE"))),
1.25 "suggested_attendees" : self.get_suggested_attendees,
1.26 "suggested_periods" : self.get_suggested_periods,
1.27 "summary" : lambda: self.obj.get_value("SUMMARY"),
1.28 @@ -429,7 +431,7 @@
1.29
1.30 is_changed = []
1.31
1.32 - for name in ["summary"]:
1.33 + for name in ["rule", "summary"]:
1.34 if self.state.has_changed(name):
1.35 is_changed.append(name)
1.36
1.37 @@ -475,6 +477,10 @@
1.38 self.update_attendees(to_invite, to_cancel, to_modify)
1.39 self.update_event_from_periods(to_set, to_exclude)
1.40
1.41 + if self.state.has_changed("rule"):
1.42 + rule = vRecurrence.to_property(self.state.get("rule"))
1.43 + self.obj.set_rule((rule, {}))
1.44 +
1.45 # Classify the nature of any update.
1.46
1.47 if is_changed:
1.48 @@ -736,7 +742,7 @@
1.49
1.50 def remove_attendees(self, indexes):
1.51
1.52 - "Remove attendee at 'index'."
1.53 + "Remove attendee at 'indexes'."
1.54
1.55 attendees = self.state.get("attendees")
1.56 to_remove = []
1.57 @@ -767,7 +773,31 @@
1.58
1.59 return True
1.60
1.61 + def can_edit_rule_selector(self, index):
1.62
1.63 + "Return whether the recurrence rule selector at 'index' can be edited."
1.64 +
1.65 + try:
1.66 + rule = self.state.get("rule")
1.67 + return rule and rule[index] or None
1.68 + except IndexError:
1.69 + return None
1.70 +
1.71 + def remove_rule_selectors(self, indexes):
1.72 +
1.73 + "Remove rule selectors at 'indexes'."
1.74 +
1.75 + rule = self.state.get("rule")
1.76 + to_remove = []
1.77 + removed = 0
1.78 +
1.79 + for index in indexes:
1.80 + if self.can_edit_rule_selector(index):
1.81 + to_remove.append(index - removed)
1.82 + removed += 1
1.83 +
1.84 + for index in to_remove:
1.85 + del rule[index]
1.86
1.87 # Period-related abstractions.
1.88
1.89 @@ -1457,12 +1487,13 @@
1.90 # All modifications redefine the event.
1.91
1.92 # For shared events...
1.93 - # New periods should cause the event to be redefined.
1.94 - # Other changes should also cause event redefinition.
1.95 - # Event redefinition should only occur if no replacement periods exist.
1.96 + # Property changes should cause event redefinition.
1.97 # Cancelled rule-originating periods must be excluded.
1.98 + # NOTE: New periods where no replacement periods exist might also cause
1.99 + # NOTE: complete redefinition, especially if ADD requests are not
1.100 + # NOTE: desired.
1.101
1.102 - if not is_shared or new and not replaced:
1.103 + if not is_shared or is_changed:
1.104 to_set = active_non_rule
1.105 to_exclude = list(chain(cancelled_rule, obsolete_rule, cancelled_main))
1.106 to_unschedule = []