1.1 --- a/imipweb/event.py Wed Apr 01 01:31:39 2015 +0200
1.2 +++ b/imipweb/event.py Sat Apr 04 21:07:29 2015 +0200
1.3 @@ -39,6 +39,25 @@
1.4 Resource.__init__(self, resource)
1.5 self.messenger = messenger or Messenger()
1.6
1.7 + # Various property values and labels.
1.8 +
1.9 + property_items = [
1.10 + ("SUMMARY", "Summary"),
1.11 + ("DTSTART", "Start"),
1.12 + ("DTEND", "End"),
1.13 + ("ORGANIZER", "Organiser"),
1.14 + ("ATTENDEE", "Attendee"),
1.15 + ]
1.16 +
1.17 + partstat_items = [
1.18 + ("NEEDS-ACTION", "Not confirmed"),
1.19 + ("ACCEPTED", "Attending"),
1.20 + ("TENTATIVE", "Tentatively attending"),
1.21 + ("DECLINED", "Not attending"),
1.22 + ("DELEGATED", "Delegated"),
1.23 + (None, "Not indicated"),
1.24 + ]
1.25 +
1.26 # Request logic methods.
1.27
1.28 def handle_request(self, uid, recurrenceid, obj):
1.29 @@ -460,23 +479,6 @@
1.30 page.input(name="ignore", type="submit", value="Do nothing for now")
1.31 page.p.close()
1.32
1.33 - property_items = [
1.34 - ("SUMMARY", "Summary"),
1.35 - ("DTSTART", "Start"),
1.36 - ("DTEND", "End"),
1.37 - ("ORGANIZER", "Organiser"),
1.38 - ("ATTENDEE", "Attendee"),
1.39 - ]
1.40 -
1.41 - partstat_items = [
1.42 - ("NEEDS-ACTION", "Not confirmed"),
1.43 - ("ACCEPTED", "Attending"),
1.44 - ("TENTATIVE", "Tentatively attending"),
1.45 - ("DECLINED", "Not attending"),
1.46 - ("DELEGATED", "Delegated"),
1.47 - (None, "Not indicated"),
1.48 - ]
1.49 -
1.50 def show_object_on_page(self, uid, obj, error=None):
1.51
1.52 """
1.53 @@ -614,10 +616,7 @@
1.54
1.55 remove_type = partstat and "checkbox" or "submit"
1.56
1.57 - if value in args.get("remove", []):
1.58 - page.input(name="remove", type=remove_type, value=value, id="remove-%d" % i, class_="remove", checked="checked")
1.59 - else:
1.60 - page.input(name="remove", type=remove_type, value=value, id="remove-%d" % i, class_="remove")
1.61 + self._control("remove", remove_type, value, value in args.get("remove", []), id="remove-%d" % i, class_="remove")
1.62
1.63 page.label("Remove", for_="remove-%d" % i, class_="remove")
1.64 page.label("Uninvited", for_="remove-%d" % i, class_="removed")
1.65 @@ -664,176 +663,6 @@
1.66
1.67 page.form.close()
1.68
1.69 - def show_object_datetime_controls(self, start, end, index=None):
1.70 -
1.71 - """
1.72 - Show datetime-related controls if already active or if an object needs
1.73 - them for the given 'start' to 'end' period. The given 'index' is used to
1.74 - parameterise individual controls for dynamic manipulation.
1.75 - """
1.76 -
1.77 - page = self.page
1.78 - args = self.env.get_args()
1.79 - sn = self._suffixed_name
1.80 - ssn = self._simple_suffixed_name
1.81 -
1.82 - # Add a dynamic stylesheet to permit the controls to modify the display.
1.83 - # NOTE: The style details need to be coordinated with the static
1.84 - # NOTE: stylesheet.
1.85 -
1.86 - if index is not None:
1.87 - page.style(type="text/css")
1.88 -
1.89 - # Unlike the rules for object properties, these affect recurrence
1.90 - # properties.
1.91 -
1.92 - page.add("""\
1.93 -input#dttimes-enable-%(index)d,
1.94 -input#dtend-enable-%(index)d,
1.95 -input#dttimes-enable-%(index)d:not(:checked) ~ .recurrence td.objectvalue .time.enabled,
1.96 -input#dttimes-enable-%(index)d:checked ~ .recurrence td.objectvalue .time.disabled,
1.97 -input#dtend-enable-%(index)d:not(:checked) ~ .recurrence td.objectvalue.dtend .dt.enabled,
1.98 -input#dtend-enable-%(index)d:checked ~ .recurrence td.objectvalue.dtend .dt.disabled {
1.99 - display: none;
1.100 -}""" % {"index" : index})
1.101 -
1.102 - page.style.close()
1.103 -
1.104 - dtend_control = args.get(ssn("dtend-control", "recur", index), [])
1.105 - dttimes_control = args.get(ssn("dttimes-control", "recur", index), [])
1.106 -
1.107 - dtend_enabled = index is not None and str(index) in dtend_control or index is None and dtend_control
1.108 - dttimes_enabled = index is not None and str(index) in dttimes_control or index is None and dttimes_control
1.109 -
1.110 - initial_load = not args.has_key("editing")
1.111 -
1.112 - dtend_enabled = dtend_enabled or initial_load and (isinstance(end, datetime) or start != end - timedelta(1))
1.113 - dttimes_enabled = dttimes_enabled or initial_load and (isinstance(start, datetime) or isinstance(end, datetime))
1.114 -
1.115 - if dtend_enabled:
1.116 - page.input(name=ssn("dtend-control", "recur", index), type="checkbox",
1.117 - value=(index is not None and str(index) or "enable"), id=sn("dtend-enable", index), checked="checked")
1.118 - else:
1.119 - page.input(name=ssn("dtend-control", "recur", index), type="checkbox",
1.120 - value=(index is not None and str(index) or "enable"), id=sn("dtend-enable", index))
1.121 -
1.122 - if dttimes_enabled:
1.123 - page.input(name=ssn("dttimes-control", "recur", index), type="checkbox",
1.124 - value=(index is not None and str(index) or "enable"), id=sn("dttimes-enable", index), checked="checked")
1.125 - else:
1.126 - page.input(name=ssn("dttimes-control", "recur", index), type="checkbox",
1.127 - value=(index is not None and str(index) or "enable"), id=sn("dttimes-enable", index))
1.128 -
1.129 - def show_datetime_controls(self, obj, dt, attr, show_start):
1.130 -
1.131 - """
1.132 - Show datetime details from the given 'obj' for the datetime 'dt' and
1.133 - attributes 'attr', showing start details if 'show_start' is set
1.134 - to a true value. Details will appear as controls for organisers and
1.135 - labels for attendees.
1.136 - """
1.137 -
1.138 - page = self.page
1.139 - is_organiser = get_uri(obj.get_value("ORGANIZER")) == self.user
1.140 -
1.141 - # Change end dates to refer to the actual dates, not the iCalendar
1.142 - # "next day" dates.
1.143 -
1.144 - if not show_start and not isinstance(dt, datetime):
1.145 - dt -= timedelta(1)
1.146 -
1.147 - # Show controls for editing as organiser.
1.148 -
1.149 - if is_organiser:
1.150 - page.td(class_="objectvalue dt%s" % (show_start and "start" or "end"))
1.151 -
1.152 - if show_start:
1.153 - page.div(class_="dt enabled")
1.154 - self._show_date_controls("dtstart", dt, attr.get("TZID"))
1.155 - page.br()
1.156 - page.label("Specify times", for_="dttimes-enable", class_="time disabled enable")
1.157 - page.label("Specify dates only", for_="dttimes-enable", class_="time enabled disable")
1.158 - page.div.close()
1.159 -
1.160 - else:
1.161 - page.div(class_="dt disabled")
1.162 - page.label("Specify end date", for_="dtend-enable", class_="enable")
1.163 - page.div.close()
1.164 - page.div(class_="dt enabled")
1.165 - self._show_date_controls("dtend", dt, attr.get("TZID"))
1.166 - page.br()
1.167 - page.label("End on same day", for_="dtend-enable", class_="disable")
1.168 - page.div.close()
1.169 -
1.170 - page.td.close()
1.171 -
1.172 - # Show a label as attendee.
1.173 -
1.174 - else:
1.175 - page.td(self.format_datetime(dt, "full"))
1.176 -
1.177 - def show_recurrence_controls(self, obj, index, start, end, origin, recurrenceid, recurrenceids, show_start):
1.178 -
1.179 - """
1.180 - Show datetime details from the given 'obj' for the recurrence having the
1.181 - given 'index', with the recurrence period described by the datetimes
1.182 - 'start' and 'end', indicating the 'origin' of the period from the event
1.183 - details, employing any 'recurrenceid' and 'recurrenceids' for the object
1.184 - to configure the displayed information.
1.185 -
1.186 - If 'show_start' is set to a true value, the start details will be shown;
1.187 - otherwise, the end details will be shown.
1.188 - """
1.189 -
1.190 - page = self.page
1.191 - sn = self._suffixed_name
1.192 - ssn = self._simple_suffixed_name
1.193 -
1.194 - is_organiser = get_uri(obj.get_value("ORGANIZER")) == self.user
1.195 -
1.196 - # Change end dates to refer to the actual dates, not the iCalendar
1.197 - # "next day" dates.
1.198 -
1.199 - if not isinstance(end, datetime):
1.200 - end -= timedelta(1)
1.201 -
1.202 - start_utc = format_datetime(to_timezone(start, "UTC"))
1.203 - replaced = recurrenceids and start_utc in recurrenceids and "replaced" or ""
1.204 - css = " ".join([
1.205 - replaced,
1.206 - recurrenceid and start_utc == recurrenceid and "affected" or ""
1.207 - ])
1.208 -
1.209 - # Show controls for editing as organiser.
1.210 -
1.211 - if is_organiser and not replaced and origin != "RRULE":
1.212 - page.td(class_="objectvalue dt%s" % (show_start and "start" or "end"))
1.213 -
1.214 - if show_start:
1.215 - page.div(class_="dt enabled")
1.216 - self._show_date_controls(ssn("dtstart", "recur", index), start, index=index)
1.217 - page.br()
1.218 - page.label("Specify times", for_=sn("dttimes-enable", index), class_="time disabled enable")
1.219 - page.label("Specify dates only", for_=sn("dttimes-enable", index), class_="time enabled disable")
1.220 - page.div.close()
1.221 -
1.222 - else:
1.223 - page.div(class_="dt disabled")
1.224 - page.label("Specify end date", for_=sn("dtend-enable", index), class_="enable")
1.225 - page.div.close()
1.226 - page.div(class_="dt enabled")
1.227 - self._show_date_controls(ssn("dtend", "recur", index), end, index=index, show_tzid=False)
1.228 - page.br()
1.229 - page.label("End on same day", for_=sn("dtend-enable", index), class_="disable")
1.230 - page.div.close()
1.231 -
1.232 - page.td.close()
1.233 -
1.234 - # Show label as attendee.
1.235 -
1.236 - else:
1.237 - page.td(self.format_datetime(show_start and start or end, "long"), class_=css)
1.238 -
1.239 def show_recurrences(self, obj):
1.240
1.241 "Show recurrences for the object having the given representation 'obj'."
1.242 @@ -992,6 +821,176 @@
1.243 page.tbody.close()
1.244 page.table.close()
1.245
1.246 + # Generation of controls within page fragments.
1.247 +
1.248 + def show_object_datetime_controls(self, start, end, index=None):
1.249 +
1.250 + """
1.251 + Show datetime-related controls if already active or if an object needs
1.252 + them for the given 'start' to 'end' period. The given 'index' is used to
1.253 + parameterise individual controls for dynamic manipulation.
1.254 + """
1.255 +
1.256 + page = self.page
1.257 + args = self.env.get_args()
1.258 + sn = self._suffixed_name
1.259 + ssn = self._simple_suffixed_name
1.260 +
1.261 + # Add a dynamic stylesheet to permit the controls to modify the display.
1.262 + # NOTE: The style details need to be coordinated with the static
1.263 + # NOTE: stylesheet.
1.264 +
1.265 + if index is not None:
1.266 + page.style(type="text/css")
1.267 +
1.268 + # Unlike the rules for object properties, these affect recurrence
1.269 + # properties.
1.270 +
1.271 + page.add("""\
1.272 +input#dttimes-enable-%(index)d,
1.273 +input#dtend-enable-%(index)d,
1.274 +input#dttimes-enable-%(index)d:not(:checked) ~ .recurrence td.objectvalue .time.enabled,
1.275 +input#dttimes-enable-%(index)d:checked ~ .recurrence td.objectvalue .time.disabled,
1.276 +input#dtend-enable-%(index)d:not(:checked) ~ .recurrence td.objectvalue.dtend .dt.enabled,
1.277 +input#dtend-enable-%(index)d:checked ~ .recurrence td.objectvalue.dtend .dt.disabled {
1.278 + display: none;
1.279 +}""" % {"index" : index})
1.280 +
1.281 + page.style.close()
1.282 +
1.283 + dtend_control = args.get(ssn("dtend-control", "recur", index), [])
1.284 + dttimes_control = args.get(ssn("dttimes-control", "recur", index), [])
1.285 +
1.286 + dtend_enabled = index is not None and str(index) in dtend_control or index is None and dtend_control
1.287 + dttimes_enabled = index is not None and str(index) in dttimes_control or index is None and dttimes_control
1.288 +
1.289 + initial_load = not args.has_key("editing")
1.290 +
1.291 + dtend_enabled = dtend_enabled or initial_load and (isinstance(end, datetime) or start != end - timedelta(1))
1.292 + dttimes_enabled = dttimes_enabled or initial_load and (isinstance(start, datetime) or isinstance(end, datetime))
1.293 +
1.294 + self._control(
1.295 + ssn("dtend-control", "recur", index), "checkbox",
1.296 + index is not None and str(index) or "enable", dtend_enabled,
1.297 + id=sn("dtend-enable", index)
1.298 + )
1.299 +
1.300 + self._control(
1.301 + ssn("dttimes-control", "recur", index), "checkbox",
1.302 + index is not None and str(index) or "enable", dttimes_enabled,
1.303 + id=sn("dttimes-enable", index)
1.304 + )
1.305 +
1.306 + def show_datetime_controls(self, obj, dt, attr, show_start):
1.307 +
1.308 + """
1.309 + Show datetime details from the given 'obj' for the datetime 'dt' and
1.310 + attributes 'attr', showing start details if 'show_start' is set
1.311 + to a true value. Details will appear as controls for organisers and
1.312 + labels for attendees.
1.313 + """
1.314 +
1.315 + page = self.page
1.316 + is_organiser = get_uri(obj.get_value("ORGANIZER")) == self.user
1.317 +
1.318 + # Change end dates to refer to the actual dates, not the iCalendar
1.319 + # "next day" dates.
1.320 +
1.321 + if not show_start and not isinstance(dt, datetime):
1.322 + dt -= timedelta(1)
1.323 +
1.324 + # Show controls for editing as organiser.
1.325 +
1.326 + if is_organiser:
1.327 + page.td(class_="objectvalue dt%s" % (show_start and "start" or "end"))
1.328 +
1.329 + if show_start:
1.330 + page.div(class_="dt enabled")
1.331 + self._show_date_controls("dtstart", dt, attr.get("TZID"))
1.332 + page.br()
1.333 + page.label("Specify times", for_="dttimes-enable", class_="time disabled enable")
1.334 + page.label("Specify dates only", for_="dttimes-enable", class_="time enabled disable")
1.335 + page.div.close()
1.336 +
1.337 + else:
1.338 + page.div(class_="dt disabled")
1.339 + page.label("Specify end date", for_="dtend-enable", class_="enable")
1.340 + page.div.close()
1.341 + page.div(class_="dt enabled")
1.342 + self._show_date_controls("dtend", dt, attr.get("TZID"))
1.343 + page.br()
1.344 + page.label("End on same day", for_="dtend-enable", class_="disable")
1.345 + page.div.close()
1.346 +
1.347 + page.td.close()
1.348 +
1.349 + # Show a label as attendee.
1.350 +
1.351 + else:
1.352 + page.td(self.format_datetime(dt, "full"))
1.353 +
1.354 + def show_recurrence_controls(self, obj, index, start, end, origin, recurrenceid, recurrenceids, show_start):
1.355 +
1.356 + """
1.357 + Show datetime details from the given 'obj' for the recurrence having the
1.358 + given 'index', with the recurrence period described by the datetimes
1.359 + 'start' and 'end', indicating the 'origin' of the period from the event
1.360 + details, employing any 'recurrenceid' and 'recurrenceids' for the object
1.361 + to configure the displayed information.
1.362 +
1.363 + If 'show_start' is set to a true value, the start details will be shown;
1.364 + otherwise, the end details will be shown.
1.365 + """
1.366 +
1.367 + page = self.page
1.368 + sn = self._suffixed_name
1.369 + ssn = self._simple_suffixed_name
1.370 +
1.371 + is_organiser = get_uri(obj.get_value("ORGANIZER")) == self.user
1.372 +
1.373 + # Change end dates to refer to the actual dates, not the iCalendar
1.374 + # "next day" dates.
1.375 +
1.376 + if not isinstance(end, datetime):
1.377 + end -= timedelta(1)
1.378 +
1.379 + start_utc = format_datetime(to_timezone(start, "UTC"))
1.380 + replaced = recurrenceids and start_utc in recurrenceids and "replaced" or ""
1.381 + css = " ".join([
1.382 + replaced,
1.383 + recurrenceid and start_utc == recurrenceid and "affected" or ""
1.384 + ])
1.385 +
1.386 + # Show controls for editing as organiser.
1.387 +
1.388 + if is_organiser and not replaced and origin != "RRULE":
1.389 + page.td(class_="objectvalue dt%s" % (show_start and "start" or "end"))
1.390 +
1.391 + if show_start:
1.392 + page.div(class_="dt enabled")
1.393 + self._show_date_controls(ssn("dtstart", "recur", index), start, index=index)
1.394 + page.br()
1.395 + page.label("Specify times", for_=sn("dttimes-enable", index), class_="time disabled enable")
1.396 + page.label("Specify dates only", for_=sn("dttimes-enable", index), class_="time enabled disable")
1.397 + page.div.close()
1.398 +
1.399 + else:
1.400 + page.div(class_="dt disabled")
1.401 + page.label("Specify end date", for_=sn("dtend-enable", index), class_="enable")
1.402 + page.div.close()
1.403 + page.div(class_="dt enabled")
1.404 + self._show_date_controls(ssn("dtend", "recur", index), end, index=index, show_tzid=False)
1.405 + page.br()
1.406 + page.label("End on same day", for_=sn("dtend-enable", index), class_="disable")
1.407 + page.div.close()
1.408 +
1.409 + page.td.close()
1.410 +
1.411 + # Show label as attendee.
1.412 +
1.413 + else:
1.414 + page.td(self.format_datetime(show_start and start or end, "long"), class_=css)
1.415 +
1.416 # Full page output methods.
1.417
1.418 def show(self, path_info):
1.419 @@ -1016,6 +1015,20 @@
1.420
1.421 # Utility methods.
1.422
1.423 + def _control(self, name, type, value, selected, **kw):
1.424 +
1.425 + """
1.426 + Show a control with the given 'name', 'type' and 'value', with
1.427 + 'selected' indicating whether it should be selected (checked or
1.428 + equivalent), and with keyword arguments setting other properties.
1.429 + """
1.430 +
1.431 + page = self.page
1.432 + if selected:
1.433 + page.input(name=name, type=type, value=value, checked=selected, **kw)
1.434 + else:
1.435 + page.input(name=name, type=type, value=value, **kw)
1.436 +
1.437 def _show_menu(self, name, default, items, class_="", index=None):
1.438
1.439 """