# HG changeset patch # User Paul Boddie # Date 1443120118 -7200 # Node ID a92517218628c05b4d8b406dd100166812f2cc51 # Parent 3ee9193569d837671b92ca50d61b3f2772d47b9a Introduced a mix-in class for form control generation. diff -r 3ee9193569d8 -r a92517218628 imipweb/event.py --- a/imipweb/event.py Thu Sep 24 19:40:03 2015 +0200 +++ b/imipweb/event.py Thu Sep 24 20:41:58 2015 +0200 @@ -19,20 +19,17 @@ this program. If not, see . """ -from datetime import date, timedelta from imiptools.data import get_uri, uri_dict, uri_items, uri_values -from imiptools.dates import format_datetime, get_datetime_item, \ - to_date, to_timezone +from imiptools.dates import to_timezone from imiptools.mail import Messenger from imiptools.period import have_conflict from imipweb.data import EventPeriod, \ event_period_from_period, form_period_from_period, \ FormDate, FormPeriod, PeriodError from imipweb.client import ManagerClient -from imipweb.resource import ResourceClientForObject -import pytz +from imipweb.resource import FormUtilities, ResourceClientForObject -class EventPage(ResourceClientForObject): +class EventPage(ResourceClientForObject, FormUtilities): "A request handler for the event page." @@ -448,27 +445,27 @@ page.p("An action is required for this request:") page.p() - self._control("reply", "submit", "Send reply") + self.control("reply", "submit", "Send reply") page.add(" ") - self._control("discard", "submit", "Discard event") + self.control("discard", "submit", "Discard event") page.add(" ") - self._control("ignore", "submit", "Do nothing for now") + self.control("ignore", "submit", "Do nothing for now") page.p.close() if self.is_organiser(): page.p("As organiser, you can perform the following:") page.p() - self._control("create", "submit", not self.obj.is_shared() and "Create event" or "Update event") + self.control("create", "submit", not self.obj.is_shared() and "Create event" or "Update event") page.add(" ") if self.obj.is_shared() and not is_request: - self._control("cancel", "submit", "Cancel event") + self.control("cancel", "submit", "Cancel event") else: - self._control("discard", "submit", "Discard event") + self.control("discard", "submit", "Discard event") page.add(" ") - self._control("save", "submit", "Save without sending") + self.control("save", "submit", "Save without sending") page.p.close() def show_object_on_page(self, errors=None): @@ -483,7 +480,7 @@ # Add a hidden control to help determine whether editing has already begun. - self._control("editing", "hidden", "true") + self.control("editing", "hidden", "true") args = self.env.get_args() @@ -555,7 +552,7 @@ page.td(class_="objectvalue summary") if self.is_organiser(): - self._control("summary", "text", value, size=80) + self.control("summary", "text", value, size=80) else: page.add(value) page.td.close() @@ -585,7 +582,7 @@ page.tr() page.td() - self._control("add", "submit", "add", id="add", class_="add") + self.control("add", "submit", "add", id="add", class_="add") page.label("Add attendee", for_="add", class_="add") page.td.close() page.tr.close() @@ -632,23 +629,23 @@ # Show a form control as organiser for new attendees. if self.is_organiser() and self.can_edit_attendee(attendee): - self._control("attendee", "value", attendee, size="40") + self.control("attendee", "value", attendee, size="40") else: - self._control("attendee", "hidden", attendee) + self.control("attendee", "hidden", attendee) page.add(attendee) page.add(" ") # Show participation status, editable for the current user. if attendee == self.user: - self._show_menu("partstat", partstat, self.partstat_items, "partstat") + self.menu("partstat", partstat, self.partstat_items, "partstat") # Allow the participation indicator to act as a submit # button in order to refresh the page and show a control for # the current user, if indicated. elif self.is_organiser() and self.attendee_is_new(attendee): - self._control("partstat-refresh", "submit", "refresh", id="partstat-%d" % i, class_="refresh") + self.control("partstat-refresh", "submit", "refresh", id="partstat-%d" % i, class_="refresh") page.label(dict(self.partstat_items).get(partstat, ""), for_="partstat-%s" % i, class_="partstat") # Otherwise, just show a label with the participation status. @@ -663,7 +660,7 @@ # Permit the removal of newly-added attendees. remove_type = self.can_remove_attendee(attendee) and "submit" or "checkbox" - self._control("remove", remove_type, str(i), str(i) in args.get("remove", []), id="remove-%d" % i, class_="remove") + self.control("remove", remove_type, str(i), str(i) in args.get("remove", []), id="remove-%d" % i, class_="remove") page.label("Remove", for_="remove-%d" % i, class_="remove") page.label(for_="remove-%d" % i, class_="removed") @@ -782,7 +779,7 @@ page.td() remove_type = not self.obj.is_shared() or not period.origin and "submit" or "checkbox" - self._control("recur-remove", remove_type, str(index), + self.control("recur-remove", remove_type, str(index), str(index) in args.get("recur-remove", []), id="recur-remove-%d" % index, class_="remove") @@ -934,13 +931,13 @@ page.style.close() - self._control( + self.control( _name("dtend-control", "recur", index), "checkbox", _enable(index), p.end_enabled, id=_id("dtend-enable", index) ) - self._control( + self.control( _name("dttimes-control", "recur", index), "checkbox", _enable(index), p.times_enabled, id=_id("dttimes-enable", index) @@ -963,7 +960,7 @@ if show_start: page.div(class_="dt enabled") - self._show_date_controls("dtstart", formdate) + self.date_controls("dtstart", formdate) page.br() page.label("Specify times", for_="dttimes-enable", class_="time disabled enable") page.label("Specify dates only", for_="dttimes-enable", class_="time enabled disable") @@ -974,7 +971,7 @@ page.label("Specify end date", for_="dtend-enable", class_="enable") page.div.close() page.div(class_="dt enabled") - self._show_date_controls("dtend", formdate) + self.date_controls("dtend", formdate) page.br() page.label("End on same day", for_="dtend-enable", class_="disable") page.div.close() @@ -1019,7 +1016,7 @@ if show_start: page.div(class_="dt enabled") - self._show_date_controls(_name("dtstart", "recur", index), p.get_form_start(), index=index, read_only=read_only) + self.date_controls(_name("dtstart", "recur", index), p.get_form_start(), index=index, read_only=read_only) if not read_only: page.br() page.label("Specify times", for_=_id("dttimes-enable", index), class_="time disabled enable") @@ -1028,7 +1025,7 @@ # Put the origin somewhere. - self._control("recur-origin", "hidden", p.origin or "") + self.control("recur-origin", "hidden", p.origin or "") else: page.div(class_="dt disabled") @@ -1036,7 +1033,7 @@ page.label("Specify end date", for_=_id("dtend-enable", index), class_="enable") page.div.close() page.div(class_="dt enabled") - self._show_date_controls(_name("dtend", "recur", index), p.get_form_end(), index=index, show_tzid=False, read_only=read_only) + self.date_controls(_name("dtend", "recur", index), p.get_form_end(), index=index, show_tzid=False, read_only=read_only) if not read_only: page.br() page.label("End on same day", for_=_id("dtend-enable", index), class_="disable") @@ -1100,130 +1097,4 @@ return True - # Utility methods. - - def _control(self, name, type, value, selected=False, **kw): - - """ - Show a control with the given 'name', 'type' and 'value', with - 'selected' indicating whether it should be selected (checked or - equivalent), and with keyword arguments setting other properties. - """ - - page = self.page - if selected: - page.input(name=name, type=type, value=value, checked=selected, **kw) - else: - page.input(name=name, type=type, value=value, **kw) - - def _show_menu(self, name, default, items, class_="", index=None): - - """ - Show a select menu having the given 'name', set to the given 'default', - providing the given (value, label) 'items', and employing the given CSS - 'class_' if specified. - """ - - page = self.page - values = self.env.get_args().get(name, [default]) - if index is not None: - values = values[index:] - values = values and values[0:1] or [default] - - page.select(name=name, class_=class_) - for v, label in items: - if v is None: - continue - if v in values: - page.option(label, value=v, selected="selected") - else: - page.option(label, value=v) - page.select.close() - - def _show_date_controls(self, name, default, index=None, show_tzid=True, read_only=False): - - """ - Show date controls for a field with the given 'name' and 'default' form - date value. - - If 'index' is specified, default field values will be overridden by the - element from a collection of existing form values with the specified - index; otherwise, field values will be overridden by a single form - value. - - If 'show_tzid' is set to a false value, the time zone menu will not be - provided. - - If 'read_only' is set to a true value, the controls will be hidden and - labels will be employed instead. - """ - - page = self.page - - # Show dates for up to one week around the current date. - - dt = default.as_datetime() - if not dt: - dt = date.today() - - base = to_date(dt) - - # Show a date label with a hidden field if read-only. - - if read_only: - self._control("%s-date" % name, "hidden", format_datetime(base)) - page.span(self.format_date(base, "long")) - - # Show dates for up to one week around the current date. - # NOTE: Support paging to other dates. - - else: - items = [] - for i in range(-7, 8): - d = base + timedelta(i) - items.append((format_datetime(d), self.format_date(d, "full"))) - self._show_menu("%s-date" % name, format_datetime(base), items, index=index) - - # Show time details. - - page.span(class_="time enabled") - - if read_only: - page.span("%s:%s:%s" % (default.get_hour(), default.get_minute(), default.get_second())) - self._control("%s-hour" % name, "hidden", default.get_hour()) - self._control("%s-minute" % name, "hidden", default.get_minute()) - self._control("%s-second" % name, "hidden", default.get_second()) - else: - self._control("%s-hour" % name, "text", default.get_hour(), maxlength=2, size=2) - page.add(":") - self._control("%s-minute" % name, "text", default.get_minute(), maxlength=2, size=2) - page.add(":") - self._control("%s-second" % name, "text", default.get_second(), maxlength=2, size=2) - - # Show time zone details. - - if show_tzid: - page.add(" ") - tzid = default.get_tzid() or self.get_tzid() - - # Show a label if read-only or a menu otherwise. - - if read_only: - self._control("%s-tzid" % name, "hidden", tzid) - page.span(tzid) - else: - self._show_timezone_menu("%s-tzid" % name, tzid, index) - - page.span.close() - - def _show_timezone_menu(self, name, default, index=None): - - """ - Show timezone controls using a menu with the given 'name', set to the - given 'default' unless a field of the given 'name' provides a value. - """ - - entries = [(tzid, tzid) for tzid in pytz.all_timezones] - self._show_menu(name, default, entries, index=index) - # vim: tabstop=4 expandtab shiftwidth=4 diff -r 3ee9193569d8 -r a92517218628 imipweb/resource.py --- a/imipweb/resource.py Thu Sep 24 19:40:03 2015 +0200 +++ b/imipweb/resource.py Thu Sep 24 20:41:58 2015 +0200 @@ -19,15 +19,16 @@ this program. If not, see . """ -from datetime import datetime +from datetime import datetime, timedelta from imiptools.client import Client, ClientForObject from imiptools.data import get_uri, uri_values -from imiptools.dates import get_recurrence_start_point +from imiptools.dates import format_datetime, get_recurrence_start_point, to_date from imiptools.period import remove_period, remove_affected_period from imipweb.env import CGIEnvironment import babel.dates import imip_store import markup +import pytz class Resource: @@ -212,4 +213,132 @@ user = self.env.get_user() ClientForObject.__init__(self, None, user and get_uri(user) or None) +class FormUtilities: + + "Utility methods." + + def control(self, name, type, value, selected=False, **kw): + + """ + Show a control with the given 'name', 'type' and 'value', with + 'selected' indicating whether it should be selected (checked or + equivalent), and with keyword arguments setting other properties. + """ + + page = self.page + if selected: + page.input(name=name, type=type, value=value, checked=selected, **kw) + else: + page.input(name=name, type=type, value=value, **kw) + + def menu(self, name, default, items, class_="", index=None): + + """ + Show a select menu having the given 'name', set to the given 'default', + providing the given (value, label) 'items', and employing the given CSS + 'class_' if specified. + """ + + page = self.page + values = self.env.get_args().get(name, [default]) + if index is not None: + values = values[index:] + values = values and values[0:1] or [default] + + page.select(name=name, class_=class_) + for v, label in items: + if v is None: + continue + if v in values: + page.option(label, value=v, selected="selected") + else: + page.option(label, value=v) + page.select.close() + + def date_controls(self, name, default, index=None, show_tzid=True, read_only=False): + + """ + Show date controls for a field with the given 'name' and 'default' form + date value. + + If 'index' is specified, default field values will be overridden by the + element from a collection of existing form values with the specified + index; otherwise, field values will be overridden by a single form + value. + + If 'show_tzid' is set to a false value, the time zone menu will not be + provided. + + If 'read_only' is set to a true value, the controls will be hidden and + labels will be employed instead. + """ + + page = self.page + + # Show dates for up to one week around the current date. + + dt = default.as_datetime() + if not dt: + dt = date.today() + + base = to_date(dt) + + # Show a date label with a hidden field if read-only. + + if read_only: + self.control("%s-date" % name, "hidden", format_datetime(base)) + page.span(self.format_date(base, "long")) + + # Show dates for up to one week around the current date. + # NOTE: Support paging to other dates. + + else: + items = [] + for i in range(-7, 8): + d = base + timedelta(i) + items.append((format_datetime(d), self.format_date(d, "full"))) + self.menu("%s-date" % name, format_datetime(base), items, index=index) + + # Show time details. + + page.span(class_="time enabled") + + if read_only: + page.span("%s:%s:%s" % (default.get_hour(), default.get_minute(), default.get_second())) + self.control("%s-hour" % name, "hidden", default.get_hour()) + self.control("%s-minute" % name, "hidden", default.get_minute()) + self.control("%s-second" % name, "hidden", default.get_second()) + else: + self.control("%s-hour" % name, "text", default.get_hour(), maxlength=2, size=2) + page.add(":") + self.control("%s-minute" % name, "text", default.get_minute(), maxlength=2, size=2) + page.add(":") + self.control("%s-second" % name, "text", default.get_second(), maxlength=2, size=2) + + # Show time zone details. + + if show_tzid: + page.add(" ") + tzid = default.get_tzid() or self.get_tzid() + + # Show a label if read-only or a menu otherwise. + + if read_only: + self.control("%s-tzid" % name, "hidden", tzid) + page.span(tzid) + else: + self.timezone_menu("%s-tzid" % name, tzid, index) + + page.span.close() + + def timezone_menu(self, name, default, index=None): + + """ + Show timezone controls using a menu with the given 'name', set to the + given 'default' unless a field of the given 'name' provides a value. + """ + + entries = [(tzid, tzid) for tzid in pytz.all_timezones] + self.menu(name, default, entries, index=index) + # vim: tabstop=4 expandtab shiftwidth=4