# HG changeset patch # User Paul Boddie # Date 1423591857 -3600 # Node ID edec6e155c64c6514e02b5071e3af4ce81d6f828 # Parent 191c6eda1a2df6c7c4b2b7631af072ea6e47ee9e Added support for adding participants to an existing event as organiser. Adjusted participant status presentation slightly. diff -r 191c6eda1a2d -r edec6e155c64 htdocs/styles.css --- a/htdocs/styles.css Tue Feb 10 19:01:12 2015 +0100 +++ b/htdocs/styles.css Tue Feb 10 19:10:57 2015 +0100 @@ -72,6 +72,15 @@ background-color: #faa; } +.partstat { + margin-left: 1em; + padding: 0.25em; +} + +span.partstat { + background-color: #aaa; +} + /* Selection of slots/periods for new events. */ input.newevent.selector { @@ -147,6 +156,7 @@ /* Hiding/showing remove/uninvite labels. */ +input.add, input.remove, input.remove:checked ~ label.remove, input.remove:not(:checked) ~ label.removed { @@ -161,6 +171,7 @@ /* Style the labels. */ +label.add, label.remove, label.removed { float: right; @@ -168,6 +179,7 @@ .dt.disabled label, .dt.enabled label, +label.add, label.remove, label.removed, label.hidebusy, diff -r 191c6eda1a2d -r edec6e155c64 imip_manager.py --- a/imip_manager.py Tue Feb 10 19:01:12 2015 +0100 +++ b/imip_manager.py Tue Feb 10 19:10:57 2015 +0100 @@ -229,7 +229,7 @@ return False - def process_created_request(self, method, update=False, removed=None): + def process_created_request(self, method, update=False, removed=None, added=None): """ Process the current request for the given 'user', sending a created @@ -241,6 +241,8 @@ If 'removed' is specified, a list of participants to be removed is provided. + + If 'added' is specified, a list of participants to be added is provided. """ organiser, organiser_attr = uri_item(self.obj.get_item("ORGANIZER")) @@ -250,17 +252,25 @@ to_cancel = [] - if removed: + if added or removed: attendees = uri_items(self.obj.get_items("ATTENDEE")) - remaining = [] + + if removed: + remaining = [] - for attendee, attendee_attr in attendees: - if attendee in removed: - to_cancel.append((attendee, attendee_attr)) - else: - remaining.append((attendee, attendee_attr)) + for attendee, attendee_attr in attendees: + if attendee in removed: + to_cancel.append((attendee, attendee_attr)) + else: + remaining.append((attendee, attendee_attr)) - self.obj["ATTENDEE"] = remaining + attendees = remaining + + if added: + for attendee in added: + attendees.append((attendee, {"PARTSTAT" : "NEEDS-ACTION", "RSVP" : "TRUE"})) + + self.obj["ATTENDEE"] = attendees self.update_dtstamp() self.set_sequence(update) @@ -551,10 +561,6 @@ is_organiser = get_uri(obj.get_value("ORGANIZER")) == self.user - # Obtain any participants to be removed. - - removed = args.get("remove") - # Obtain the user's timezone and process datetime values. update = False @@ -597,6 +603,11 @@ if dtstart >= dtend: return ["dtstart", "dtend"] + # Obtain any participants to be added or removed. + + removed = args.get("remove") + added = args.get("added") + # Process any action. handled = True @@ -609,7 +620,7 @@ if reply and handler.process_received_request(update) or \ is_organiser and (invite or cancel) and \ - handler.process_created_request(invite and "REQUEST" or "CANCEL", update, removed): + handler.process_created_request(invite and "REQUEST" or "CANCEL", update, removed, added): self.remove_request(uid) @@ -764,6 +775,28 @@ # Provide controls to change the displayed object. args = self.env.get_args() + + # Add or remove new attendees. + # This does not affect the stored object. + + existing_attendees = uri_values(obj.get_values("ATTENDEE")) + new_attendees = args.get("added", []) + new_attendee = args.get("attendee", [""])[0] + + if args.has_key("add"): + if new_attendee.strip(): + new_attendee = get_uri(new_attendee.strip()) + if new_attendee not in new_attendees and new_attendee not in existing_attendees: + new_attendees.append(new_attendee) + new_attendee = "" + + if args.has_key("removenew"): + removed_attendee = args["removenew"][0] + if removed_attendee in new_attendees: + new_attendees.remove(removed_attendee) + + # Configure the start and end datetimes. + dtend_control = args.get("dtend-control", [None])[0] dttimes_control = args.get("dttimes-control", [None])[0] with_time = dttimes_control == "enable" @@ -895,7 +928,12 @@ if not items: continue - page.th(label, class_="objectheading", rowspan=len(items)) + rowspan = len(items) + + if name == "ATTENDEE": + rowspan += len(new_attendees) + 1 + + page.th(label, class_="objectheading", rowspan=rowspan) first = True @@ -914,12 +952,15 @@ partstat = attr.get("PARTSTAT") if value == self.user and (not is_organiser or name == "ORGANIZER"): - self._show_menu("partstat", partstat, self.partstat_items) + self._show_menu("partstat", partstat, self.partstat_items, "partstat") else: page.span(dict(self.partstat_items).get(partstat, ""), class_="partstat") if is_organiser and name == "ATTENDEE": - page.input(name="remove", type="checkbox", value=value, id="remove-%d" % i, class_="remove") + if value in args.get("remove", []): + page.input(name="remove", type="checkbox", value=value, id="remove-%d" % i, class_="remove", checked="checked") + else: + page.input(name="remove", type="checkbox", value=value, id="remove-%d" % i, class_="remove") page.label("Remove", for_="remove-%d" % i, class_="remove") page.label("Uninvited", for_="remove-%d" % i, class_="removed") @@ -930,6 +971,25 @@ page.td.close() page.tr.close() + # Allow more attendees to be specified. + + if is_organiser and name == "ATTENDEE": + for i, attendee in enumerate(new_attendees): + page.tr() + page.td() + page.input(name="added", type="value", value=attendee) + page.input(name="removenew", type="submit", value=attendee, id="removenew-%d" % i, class_="remove") + page.label("Remove", for_="removenew-%d" % i, class_="remove") + page.td.close() + page.tr.close() + page.tr() + page.td() + page.input(name="attendee", type="value", value=new_attendee) + page.input(name="add", type="submit", value="add", id="add-%d" % i, class_="add") + page.label("Add", for_="add-%d" % i, class_="add") + page.td.close() + page.tr.close() + page.tbody.close() page.table.close() @@ -1566,15 +1626,15 @@ identifier = "slot-%s" % value return value, identifier - def _show_menu(self, name, default, items): + def _show_menu(self, name, default, items, class_=""): page = self.page values = self.env.get_args().get(name, [default]) page.select(name=name) for v, label in items: if v in values: - page.option(label, value=v, selected="selected") + page.option(label, value=v, selected="selected", class_=class_) else: - page.option(label, value=v) + page.option(label, value=v, class_=class_) page.select.close() def _show_date_controls(self, name, default, attr, tzid):