1.1 --- a/htdocs/styles.css Tue Feb 10 19:01:12 2015 +0100
1.2 +++ b/htdocs/styles.css Tue Feb 10 19:10:57 2015 +0100
1.3 @@ -72,6 +72,15 @@
1.4 background-color: #faa;
1.5 }
1.6
1.7 +.partstat {
1.8 + margin-left: 1em;
1.9 + padding: 0.25em;
1.10 +}
1.11 +
1.12 +span.partstat {
1.13 + background-color: #aaa;
1.14 +}
1.15 +
1.16 /* Selection of slots/periods for new events. */
1.17
1.18 input.newevent.selector {
1.19 @@ -147,6 +156,7 @@
1.20
1.21 /* Hiding/showing remove/uninvite labels. */
1.22
1.23 +input.add,
1.24 input.remove,
1.25 input.remove:checked ~ label.remove,
1.26 input.remove:not(:checked) ~ label.removed {
1.27 @@ -161,6 +171,7 @@
1.28
1.29 /* Style the labels. */
1.30
1.31 +label.add,
1.32 label.remove,
1.33 label.removed {
1.34 float: right;
1.35 @@ -168,6 +179,7 @@
1.36
1.37 .dt.disabled label,
1.38 .dt.enabled label,
1.39 +label.add,
1.40 label.remove,
1.41 label.removed,
1.42 label.hidebusy,
2.1 --- a/imip_manager.py Tue Feb 10 19:01:12 2015 +0100
2.2 +++ b/imip_manager.py Tue Feb 10 19:10:57 2015 +0100
2.3 @@ -229,7 +229,7 @@
2.4
2.5 return False
2.6
2.7 - def process_created_request(self, method, update=False, removed=None):
2.8 + def process_created_request(self, method, update=False, removed=None, added=None):
2.9
2.10 """
2.11 Process the current request for the given 'user', sending a created
2.12 @@ -241,6 +241,8 @@
2.13
2.14 If 'removed' is specified, a list of participants to be removed is
2.15 provided.
2.16 +
2.17 + If 'added' is specified, a list of participants to be added is provided.
2.18 """
2.19
2.20 organiser, organiser_attr = uri_item(self.obj.get_item("ORGANIZER"))
2.21 @@ -250,17 +252,25 @@
2.22
2.23 to_cancel = []
2.24
2.25 - if removed:
2.26 + if added or removed:
2.27 attendees = uri_items(self.obj.get_items("ATTENDEE"))
2.28 - remaining = []
2.29 +
2.30 + if removed:
2.31 + remaining = []
2.32
2.33 - for attendee, attendee_attr in attendees:
2.34 - if attendee in removed:
2.35 - to_cancel.append((attendee, attendee_attr))
2.36 - else:
2.37 - remaining.append((attendee, attendee_attr))
2.38 + for attendee, attendee_attr in attendees:
2.39 + if attendee in removed:
2.40 + to_cancel.append((attendee, attendee_attr))
2.41 + else:
2.42 + remaining.append((attendee, attendee_attr))
2.43
2.44 - self.obj["ATTENDEE"] = remaining
2.45 + attendees = remaining
2.46 +
2.47 + if added:
2.48 + for attendee in added:
2.49 + attendees.append((attendee, {"PARTSTAT" : "NEEDS-ACTION", "RSVP" : "TRUE"}))
2.50 +
2.51 + self.obj["ATTENDEE"] = attendees
2.52
2.53 self.update_dtstamp()
2.54 self.set_sequence(update)
2.55 @@ -551,10 +561,6 @@
2.56
2.57 is_organiser = get_uri(obj.get_value("ORGANIZER")) == self.user
2.58
2.59 - # Obtain any participants to be removed.
2.60 -
2.61 - removed = args.get("remove")
2.62 -
2.63 # Obtain the user's timezone and process datetime values.
2.64
2.65 update = False
2.66 @@ -597,6 +603,11 @@
2.67 if dtstart >= dtend:
2.68 return ["dtstart", "dtend"]
2.69
2.70 + # Obtain any participants to be added or removed.
2.71 +
2.72 + removed = args.get("remove")
2.73 + added = args.get("added")
2.74 +
2.75 # Process any action.
2.76
2.77 handled = True
2.78 @@ -609,7 +620,7 @@
2.79
2.80 if reply and handler.process_received_request(update) or \
2.81 is_organiser and (invite or cancel) and \
2.82 - handler.process_created_request(invite and "REQUEST" or "CANCEL", update, removed):
2.83 + handler.process_created_request(invite and "REQUEST" or "CANCEL", update, removed, added):
2.84
2.85 self.remove_request(uid)
2.86
2.87 @@ -764,6 +775,28 @@
2.88 # Provide controls to change the displayed object.
2.89
2.90 args = self.env.get_args()
2.91 +
2.92 + # Add or remove new attendees.
2.93 + # This does not affect the stored object.
2.94 +
2.95 + existing_attendees = uri_values(obj.get_values("ATTENDEE"))
2.96 + new_attendees = args.get("added", [])
2.97 + new_attendee = args.get("attendee", [""])[0]
2.98 +
2.99 + if args.has_key("add"):
2.100 + if new_attendee.strip():
2.101 + new_attendee = get_uri(new_attendee.strip())
2.102 + if new_attendee not in new_attendees and new_attendee not in existing_attendees:
2.103 + new_attendees.append(new_attendee)
2.104 + new_attendee = ""
2.105 +
2.106 + if args.has_key("removenew"):
2.107 + removed_attendee = args["removenew"][0]
2.108 + if removed_attendee in new_attendees:
2.109 + new_attendees.remove(removed_attendee)
2.110 +
2.111 + # Configure the start and end datetimes.
2.112 +
2.113 dtend_control = args.get("dtend-control", [None])[0]
2.114 dttimes_control = args.get("dttimes-control", [None])[0]
2.115 with_time = dttimes_control == "enable"
2.116 @@ -895,7 +928,12 @@
2.117 if not items:
2.118 continue
2.119
2.120 - page.th(label, class_="objectheading", rowspan=len(items))
2.121 + rowspan = len(items)
2.122 +
2.123 + if name == "ATTENDEE":
2.124 + rowspan += len(new_attendees) + 1
2.125 +
2.126 + page.th(label, class_="objectheading", rowspan=rowspan)
2.127
2.128 first = True
2.129
2.130 @@ -914,12 +952,15 @@
2.131
2.132 partstat = attr.get("PARTSTAT")
2.133 if value == self.user and (not is_organiser or name == "ORGANIZER"):
2.134 - self._show_menu("partstat", partstat, self.partstat_items)
2.135 + self._show_menu("partstat", partstat, self.partstat_items, "partstat")
2.136 else:
2.137 page.span(dict(self.partstat_items).get(partstat, ""), class_="partstat")
2.138
2.139 if is_organiser and name == "ATTENDEE":
2.140 - page.input(name="remove", type="checkbox", value=value, id="remove-%d" % i, class_="remove")
2.141 + if value in args.get("remove", []):
2.142 + page.input(name="remove", type="checkbox", value=value, id="remove-%d" % i, class_="remove", checked="checked")
2.143 + else:
2.144 + page.input(name="remove", type="checkbox", value=value, id="remove-%d" % i, class_="remove")
2.145 page.label("Remove", for_="remove-%d" % i, class_="remove")
2.146 page.label("Uninvited", for_="remove-%d" % i, class_="removed")
2.147
2.148 @@ -930,6 +971,25 @@
2.149 page.td.close()
2.150 page.tr.close()
2.151
2.152 + # Allow more attendees to be specified.
2.153 +
2.154 + if is_organiser and name == "ATTENDEE":
2.155 + for i, attendee in enumerate(new_attendees):
2.156 + page.tr()
2.157 + page.td()
2.158 + page.input(name="added", type="value", value=attendee)
2.159 + page.input(name="removenew", type="submit", value=attendee, id="removenew-%d" % i, class_="remove")
2.160 + page.label("Remove", for_="removenew-%d" % i, class_="remove")
2.161 + page.td.close()
2.162 + page.tr.close()
2.163 + page.tr()
2.164 + page.td()
2.165 + page.input(name="attendee", type="value", value=new_attendee)
2.166 + page.input(name="add", type="submit", value="add", id="add-%d" % i, class_="add")
2.167 + page.label("Add", for_="add-%d" % i, class_="add")
2.168 + page.td.close()
2.169 + page.tr.close()
2.170 +
2.171 page.tbody.close()
2.172 page.table.close()
2.173
2.174 @@ -1566,15 +1626,15 @@
2.175 identifier = "slot-%s" % value
2.176 return value, identifier
2.177
2.178 - def _show_menu(self, name, default, items):
2.179 + def _show_menu(self, name, default, items, class_=""):
2.180 page = self.page
2.181 values = self.env.get_args().get(name, [default])
2.182 page.select(name=name)
2.183 for v, label in items:
2.184 if v in values:
2.185 - page.option(label, value=v, selected="selected")
2.186 + page.option(label, value=v, selected="selected", class_=class_)
2.187 else:
2.188 - page.option(label, value=v)
2.189 + page.option(label, value=v, class_=class_)
2.190 page.select.close()
2.191
2.192 def _show_date_controls(self, name, default, attr, tzid):