1.1 --- a/imiptools/client.py Sun Oct 15 21:34:23 2017 +0200
1.2 +++ b/imiptools/client.py Sun Oct 15 22:16:05 2017 +0200
1.3 @@ -804,12 +804,8 @@
1.4 if to_set:
1.5 self.obj.set_periods(to_set)
1.6
1.7 - # Exclude only the main period, if appropriate.
1.8 -
1.9 - if to_exclude:
1.10 - main = get_main_period(to_exclude)
1.11 - if main:
1.12 - self.obj.update_exceptions([main], [])
1.13 + if to_exclude:
1.14 + self.obj.update_exceptions(to_exclude, to_set or [])
1.15
1.16 # General message generation methods.
1.17
2.1 --- a/imipweb/data.py Sun Oct 15 21:34:23 2017 +0200
2.2 +++ b/imipweb/data.py Sun Oct 15 22:16:05 2017 +0200
2.3 @@ -412,8 +412,9 @@
2.4 "Return whether an update can be prepared and sent."
2.5
2.6 return not self.is_organiser() or \
2.7 + not self.obj.is_shared() or \
2.8 self.obj.is_shared() and self.state.get("changed") and \
2.9 - self.have_other_attendees()
2.10 + self.have_other_attendees()
2.11
2.12 def have_other_attendees(self):
2.13
2.14 @@ -471,7 +472,7 @@
2.15 "Add a blank attendee."
2.16
2.17 attendees = self.state.get("attendees")
2.18 - attendees[uri or ""] = {}
2.19 + attendees[uri or ""] = {"PARTSTAT" : "NEEDS-ACTION"}
2.20
2.21 def add_suggested_attendee(self, index):
2.22
2.23 @@ -511,9 +512,9 @@
2.24 # Cancel any removed periods.
2.25
2.26 if operation == "remove":
2.27 - for p in current:
2.28 + for index, p in enumerate(current):
2.29 if p == period:
2.30 - p.cancelled = True
2.31 + self.cancel_periods([index])
2.32 break
2.33
2.34 # Add or replace any other suggestions.
2.35 @@ -573,6 +574,12 @@
2.36 for index in to_remove:
2.37 del periods[index]
2.38
2.39 + def can_edit_attendance(self):
2.40 +
2.41 + "Return whether the organiser's attendance can be edited."
2.42 +
2.43 + return self.state.get("attendees").has_key(self.user)
2.44 +
2.45 def edit_attendance(self, partstat):
2.46
2.47 "Set the 'partstat' of the current user, if attending."
2.48 @@ -580,7 +587,9 @@
2.49 attendees = self.state.get("attendees")
2.50 attr = attendees.get(self.user)
2.51
2.52 - if attr:
2.53 + # Set the attendance for the user, if attending.
2.54 +
2.55 + if attr is not None:
2.56 new_attr = {}
2.57 new_attr.update(attr)
2.58 new_attr["PARTSTAT"] = partstat
2.59 @@ -1287,6 +1296,14 @@
2.60
2.61 cancelled_removed = select_recurrences(cancelled, removed).values()
2.62
2.63 + # Cancelled periods originating from rules must be excluded since there are
2.64 + # no explicit instances to be deleted.
2.65 +
2.66 + cancelled_rule = []
2.67 + for p in cancelled_removed:
2.68 + if p.origin == "RRULE":
2.69 + cancelled_rule.append(p)
2.70 +
2.71 # Reinstated periods are previously-cancelled periods that are now modified
2.72 # periods, and they appear in updates.
2.73
2.74 @@ -1300,7 +1317,6 @@
2.75 # As organiser...
2.76
2.77 if is_organiser:
2.78 - to_exclude = []
2.79
2.80 # For unshared events...
2.81 # All modifications redefine the event.
2.82 @@ -1309,9 +1325,11 @@
2.83 # New periods should cause the event to be redefined.
2.84 # Other changes should also cause event redefinition.
2.85 # Event redefinition should only occur if no replacement periods exist.
2.86 + # Cancelled rule-originating periods must be excluded.
2.87
2.88 if not is_shared or new and not replaced:
2.89 to_set = active_periods
2.90 + to_exclude = cancelled_rule
2.91 to_unschedule = []
2.92 to_reschedule = []
2.93 to_add = []
2.94 @@ -1323,6 +1341,7 @@
2.95
2.96 else:
2.97 to_set = []
2.98 + to_exclude = []
2.99 to_unschedule = cancelled_removed
2.100 to_reschedule = list(chain(replaced_modified, retained_modified, reinstated))
2.101 to_add = new