# HG changeset patch # User Paul Boddie # Date 1423355193 -3600 # Node ID 6c15dc009c701b2df609052889dc291a52ea46f1 # Parent b7edaea101758cc19b3c7d5254470da4c870a41d Introduced control over end datetime usage together with round-trip editing support for events, rejecting erroneous datetime details. diff -r b7edaea10175 -r 6c15dc009c70 htdocs/styles.css --- a/htdocs/styles.css Sun Feb 08 01:24:49 2015 +0100 +++ b/htdocs/styles.css Sun Feb 08 01:26:33 2015 +0100 @@ -137,8 +137,22 @@ display: block; } +/* Hiding/showing end datetimes. */ + +input#dtend-disable, +input#dtend-enable, +input#dtend-enable:not(:checked) ~ .object td.objectvalue.dtend .enabled, +input#dtend-enable:checked ~ .object td.objectvalue.dtend .disabled { + display: none; +} + /* Style the labels. */ +div.enabled label.disable { + display: block; + margin-top: 0.25em; +} + label.enable, label.disable { padding-left: 0.25em; diff -r b7edaea10175 -r 6c15dc009c70 imip_manager.py --- a/imip_manager.py Sun Feb 08 01:24:49 2015 +0100 +++ b/imip_manager.py Sun Feb 08 01:26:33 2015 +0100 @@ -307,11 +307,6 @@ isinstance(dt, datetime) and babel.dates.format_datetime or babel.dates.format_date, dt, format) - def format_end_datetime(self, dt, format): - if isinstance(dt, date) and not isinstance(dt, datetime): - dt = dt - timedelta(1) - return self.format_datetime(dt, format) - def _format_datetime(self, fn, dt, format): return fn(dt, format=format, locale=self.get_user_locale()) @@ -485,27 +480,40 @@ # Obtain the user's timezone and process datetime values. update = False - error = False if is_organiser: t = self.handle_date_controls("dtstart") if t: - dtstart, tzid = t - update = update or self.set_datetime_in_object(dtstart, tzid, "DTSTART", obj) + dtstart, attr = t + update = update or self.set_datetime_in_object(dtstart, attr["TZID"], "DTSTART", obj) else: - error = True + return False + + # Handle specified end datetimes. + + if args.get("dtend-control", [None])[0] == "enable": + t = self.handle_date_controls("dtend") + if t: + dtend, attr = t + + # Convert end dates to iCalendar "next day" dates. - t = self.handle_date_controls("dtend") - if t: - dtend, tzid = t - update = update or self.set_datetime_in_object(dtend, tzid, "DTEND", obj) + if not isinstance(dtend, datetime): + dtend += timedelta(1) + update = update or self.set_datetime_in_object(dtend, attr["TZID"], "DTEND", obj) + else: + return False + + # Otherwise, treat the end date as the start date. Datetimes cannot + # be duplicated in such a way. + else: - error = True + if isinstance(dtstart, datetime): + return False + dtend = dtstart + timedelta(1) + update = update or self.set_datetime_in_object(dtend, attr["TZID"], "DTEND", obj) - if not error: - error = dtstart > dtend - - if error: + if dtstart >= dtend: return False # Process any action. @@ -554,7 +562,7 @@ """ Handle date control information for fields starting with 'name', - returning a (datetime, tzid) tuple or None if the fields cannot be used + returning a (datetime, attr) tuple or None if the fields cannot be used to construct a datetime object. """ @@ -570,9 +578,10 @@ time = (hour or minute or second) and "T%s%s%s" % (hour, minute, second) or "" value = "%s%s" % (date, time) - dt = get_datetime(value, {"TZID" : tzid}) + attr = {"TZID" : tzid} + dt = get_datetime(value, attr) if dt: - return dt, tzid + return dt, attr return None @@ -668,6 +677,38 @@ tzid = self.get_tzid() + # Provide controls to change the displayed object. + + args = self.env.get_args() + + t = self.handle_date_controls("dtstart") + if t: + dtstart, dtstart_attr = t + else: + dtstart, dtstart_attr = obj.get_datetime_item("DTSTART") + + dtend, dtend_attr = None, None + + if args.get("dtend-control", [None])[0] == "enable": + t = self.handle_date_controls("dtend") + if t: + dtend, dtend_attr = t + elif not args.has_key("dtend-control"): + dtend, dtend_attr = obj.get_datetime_item("DTEND") + + # Change end dates to refer to the actual dates, not the iCalendar + # "next day" dates. + + if dtend and not isinstance(dtend, datetime): + dtend -= timedelta(1) + + if isinstance(dtend, datetime) or dtstart != dtend: + page.input(name="dtend-control", type="radio", value="enable", id="dtend-enable", checked="checked") + page.input(name="dtend-control", type="radio", value="disable", id="dtend-disable") + else: + page.input(name="dtend-control", type="radio", value="enable", id="dtend-enable") + page.input(name="dtend-control", type="radio", value="disable", id="dtend-disable", checked="checked") + # Provide a summary of the object. page.table(class_="object", cellspacing=5, cellpadding=5) @@ -686,16 +727,30 @@ # Handle datetimes specially. if name in ["DTSTART", "DTEND"]: - value, attr = obj.get_item(name) - event_tzid = attr.get("TZID", tzid) - strvalue = ( - name == "DTSTART" and self.format_datetime or self.format_end_datetime - )(to_timezone(get_datetime(value), event_tzid), "full") - page.th(label, class_="objectheading") + + page.th(label, class_="objectheading %s" % name.lower()) + + if name == "DTSTART": + dt, attr, event_tzid = dtstart, dtstart_attr, dtstart_attr.get("TZID", tzid) + else: + dt, attr, event_tzid = dtend, dtend_attr, dtend_attr.get("TZID", tzid) + + strvalue = self.format_datetime(dt, "full") + value = format_datetime(dt) if is_organiser: - page.td() + page.td(class_="objectvalue %s" % name.lower()) + if name == "DTEND": + page.div(class_="disabled") + page.label("Specify end date", for_="dtend-enable", class_="enable") + page.div.close() + + page.div(class_="enabled") self._show_date_controls(name.lower(), value, attr, tzid) + if name == "DTEND": + page.label("End on same day", for_="dtend-disable", class_="disable") + page.div.close() + page.td.close() else: page.td(strvalue) @@ -705,7 +760,8 @@ # Handle the summary specially. elif name == "SUMMARY": - value = obj.get_value(name) + value = args.get("summary", [obj.get_value(name)])[0] + page.th(label, class_="objectheading") page.td() if is_organiser: