1.1 --- a/htdocs/styles.css Mon Feb 09 23:30:01 2015 +0100
1.2 +++ b/htdocs/styles.css Tue Feb 10 00:40:51 2015 +0100
1.3 @@ -132,15 +132,7 @@
1.4
1.5 input#hidebusy:checked ~ .calendar tr.slot.busy,
1.6 input#showdays:not(:checked) ~ .calendar thead.separator.empty,
1.7 -input#showdays:not(:checked) ~ .calendar tbody.points.empty {
1.8 - display: none;
1.9 -}
1.10 -
1.11 -/* Show slot endpoints when hiding adjacent busy periods. */
1.12 -
1.13 -input#hidebusy:checked ~ .calendar th.timeslot span.endpoint {
1.14 - display: block;
1.15 -}
1.16 +input#showdays:not(:checked) ~ .calendar tbody.points.empty,
1.17
1.18 /* Hiding/showing end datetimes and start/end times. */
1.19
1.20 @@ -151,14 +143,33 @@
1.21 input#dttimes-disable:checked ~ .object td.objectvalue .time.enabled,
1.22 input#dttimes-enable:checked ~ .object td.objectvalue .time.disabled,
1.23 input#dtend-disable:checked ~ .object td.objectvalue.dtend .dt.enabled,
1.24 -input#dtend-enable:checked ~ .object td.objectvalue.dtend .dt.disabled {
1.25 +input#dtend-enable:checked ~ .object td.objectvalue.dtend .dt.disabled,
1.26 +
1.27 +/* Hiding/showing remove/uninvite labels. */
1.28 +
1.29 +input.remove,
1.30 +input.remove:checked ~ label.remove,
1.31 +input.remove:not(:checked) ~ label.removed {
1.32 display: none;
1.33 }
1.34
1.35 +/* Show slot endpoints when hiding adjacent busy periods. */
1.36 +
1.37 +input#hidebusy:checked ~ .calendar th.timeslot span.endpoint {
1.38 + display: block;
1.39 +}
1.40 +
1.41 /* Style the labels. */
1.42
1.43 +label.remove,
1.44 +label.removed {
1.45 + float: right;
1.46 +}
1.47 +
1.48 .dt.disabled label,
1.49 .dt.enabled label,
1.50 +label.remove,
1.51 +label.removed,
1.52 label.hidebusy,
1.53 label.showdays {
1.54 color: #009;
2.1 --- a/imip_manager.py Mon Feb 09 23:30:01 2015 +0100
2.2 +++ b/imip_manager.py Tue Feb 10 00:40:51 2015 +0100
2.3 @@ -138,9 +138,6 @@
2.4
2.5 self.set_object(obj)
2.6
2.7 - self.organiser = self.obj.get_value("ORGANIZER")
2.8 - self.attendees = self.obj.get_values("ATTENDEE")
2.9 -
2.10 # Communication methods.
2.11
2.12 def send_message(self, method, sender, for_organiser):
2.13 @@ -158,10 +155,13 @@
2.14 # also attending. The updated event will be saved by the outgoing
2.15 # handler.
2.16
2.17 + organiser = self.obj.get_value("ORGANIZER")
2.18 + attendees = self.obj.get_values("ATTENDEE")
2.19 +
2.20 if for_organiser:
2.21 - recipients = [get_address(attendee) for attendee in self.attendees if attendee != self.user]
2.22 + recipients = [get_address(attendee) for attendee in attendees if attendee != self.user]
2.23 else:
2.24 - recipients = [get_address(self.organiser)]
2.25 + recipients = [get_address(organiser)]
2.26
2.27 # Bundle free/busy information if appropriate.
2.28
2.29 @@ -228,7 +228,7 @@
2.30
2.31 return False
2.32
2.33 - def process_created_request(self, method, update=False):
2.34 + def process_created_request(self, method, update=False, removed=None):
2.35
2.36 """
2.37 Process the current request for the given 'user', sending a created
2.38 @@ -237,6 +237,9 @@
2.39
2.40 If 'update' is given, the sequence number will be incremented in order
2.41 to override any previous message.
2.42 +
2.43 + If 'removed' is specified, a list of participants to be removed is
2.44 + provided.
2.45 """
2.46
2.47 organiser, organiser_attr = self.obj.get_item("ORGANIZER")
2.48 @@ -244,10 +247,17 @@
2.49 if self.messenger and self.messenger.sender != get_address(organiser):
2.50 organiser_attr["SENT-BY"] = get_uri(self.messenger.sender)
2.51
2.52 + if removed:
2.53 + attendee_map = self.obj.get_value_map("ATTENDEE")
2.54 + self.obj["ATTENDEE"] = [(attendee, attendee_attr)
2.55 + for (attendee, attendee_attr) in attendee_map.items()
2.56 + if attendee not in removed]
2.57 +
2.58 self.update_dtstamp()
2.59 self.set_sequence(update)
2.60
2.61 - self.send_message(method, get_address(self.organiser), for_organiser=True)
2.62 + self.send_message(method, get_address(organiser), for_organiser=True)
2.63 +
2.64 return True
2.65
2.66 class Manager(Common):
2.67 @@ -508,9 +518,10 @@
2.68 if args.has_key("summary"):
2.69 obj["SUMMARY"] = [(args["summary"][0], {})]
2.70
2.71 + organisers = obj.get_value_map("ORGANIZER")
2.72 + attendees = obj.get_value_map("ATTENDEE")
2.73 +
2.74 if args.has_key("partstat"):
2.75 - organisers = obj.get_value_map("ORGANIZER")
2.76 - attendees = obj.get_value_map("ATTENDEE")
2.77 for d in attendees, organisers:
2.78 if d.has_key(self.user):
2.79 d[self.user]["PARTSTAT"] = args["partstat"][0]
2.80 @@ -519,6 +530,10 @@
2.81
2.82 is_organiser = obj.get_value("ORGANIZER") == self.user
2.83
2.84 + # Obtain any participants to be removed.
2.85 +
2.86 + removed = args.get("remove")
2.87 +
2.88 # Obtain the user's timezone and process datetime values.
2.89
2.90 update = False
2.91 @@ -572,7 +587,8 @@
2.92 # Process the object and remove it from the list of requests.
2.93
2.94 if reply and handler.process_received_request(update) or \
2.95 - (invite or cancel) and handler.process_created_request(invite and "REQUEST" or "CANCEL", update):
2.96 + is_organiser and (invite or cancel) and \
2.97 + handler.process_created_request(invite and "REQUEST" or "CANCEL", update, removed):
2.98
2.99 self.remove_request(uid)
2.100
2.101 @@ -862,7 +878,7 @@
2.102
2.103 first = True
2.104
2.105 - for value, attr in items:
2.106 + for i, (value, attr) in enumerate(items):
2.107 if not first:
2.108 page.tr()
2.109 else:
2.110 @@ -878,6 +894,12 @@
2.111 self._show_menu("partstat", partstat, self.partstat_items)
2.112 else:
2.113 page.span(dict(self.partstat_items).get(partstat, ""), class_="partstat")
2.114 +
2.115 + if is_organiser and name == "ATTENDEE":
2.116 + page.input(name="remove", type="checkbox", value=value, id="remove-%d" % i, class_="remove")
2.117 + page.label("Remove", for_="remove-%d" % i, class_="remove")
2.118 + page.label("Uninvited", for_="remove-%d" % i, class_="removed")
2.119 +
2.120 else:
2.121 page.td(class_="objectattribute")
2.122 page.add(value)