paul@10 | 1 | # -*- coding: iso-8859-1 -*- |
paul@10 | 2 | """ |
paul@10 | 3 | MoinMoin - EventAggregatorSummary Action |
paul@10 | 4 | |
paul@137 | 5 | @copyright: 2008, 2009, 2010, 2011 by Paul Boddie <paul@boddie.org.uk> |
paul@10 | 6 | @copyright: 2000-2004 Juergen Hermann <jh@web.de>, |
paul@24 | 7 | 2003-2008 MoinMoin:ThomasWaldmann, |
paul@24 | 8 | 2004-2006 MoinMoin:AlexanderSchremmer, |
paul@19 | 9 | 2007 MoinMoin:ReimarBauer. |
paul@60 | 10 | 2009 Cristian Rigamonti <rigamonti@fsfeurope.org> |
paul@10 | 11 | @license: GNU GPL (v2 or later), see COPYING.txt for details. |
paul@10 | 12 | """ |
paul@10 | 13 | |
paul@19 | 14 | from MoinMoin.action import ActionBase |
paul@10 | 15 | from MoinMoin import config |
paul@24 | 16 | from MoinMoin.Page import Page |
paul@29 | 17 | from MoinMoin import wikiutil |
paul@168 | 18 | from EventAggregatorSupport import * |
paul@137 | 19 | |
paul@10 | 20 | Dependencies = ['pages'] |
paul@10 | 21 | |
paul@19 | 22 | # Action class and supporting functions. |
paul@19 | 23 | |
paul@168 | 24 | class EventAggregatorSummary(ActionBase, ActionSupport): |
paul@19 | 25 | |
paul@19 | 26 | "A summary dialogue requesting various parameters." |
paul@19 | 27 | |
paul@19 | 28 | def get_form_html(self, buttons_html): |
paul@19 | 29 | _ = self._ |
paul@19 | 30 | request = self.request |
paul@109 | 31 | form = self.get_form() |
paul@19 | 32 | |
paul@128 | 33 | resolution = form.get("resolution", ["month"])[0] |
paul@128 | 34 | |
paul@19 | 35 | category_list = [] |
paul@112 | 36 | category_pagenames = form.get("category", []) |
paul@19 | 37 | |
paul@168 | 38 | for category_name, category_pagename in getCategoryMapping(getCategories(request), request): |
paul@19 | 39 | |
paul@112 | 40 | selected = self._get_selected_for_list(category_pagename, category_pagenames) |
paul@112 | 41 | |
paul@136 | 42 | category_list.append('<option value="%s" %s>%s</option>' % ( |
paul@136 | 43 | escattr(category_pagename), selected, escape(category_name))) |
paul@112 | 44 | |
paul@127 | 45 | # Initialise month lists and defaults. |
paul@19 | 46 | |
paul@112 | 47 | start_month_list, end_month_list = self.get_month_lists() |
paul@127 | 48 | start_day_default, end_day_default = self.get_day_defaults() |
paul@112 | 49 | start_year_default, end_year_default = self.get_year_defaults() |
paul@23 | 50 | |
paul@114 | 51 | # Criteria instead of months and years. |
paul@114 | 52 | |
paul@114 | 53 | start_criteria_default = form.get("start", [""])[0] |
paul@114 | 54 | end_criteria_default = form.get("end", [""])[0] |
paul@114 | 55 | |
paul@128 | 56 | if resolution == "date": |
paul@168 | 57 | get_parameter = getParameterDate |
paul@168 | 58 | get_label = getFullDateLabel |
paul@128 | 59 | else: |
paul@168 | 60 | get_parameter = getParameterMonth |
paul@168 | 61 | get_label = getFullMonthLabel |
paul@114 | 62 | |
paul@128 | 63 | start_criteria_evaluated = get_parameter(start_criteria_default) |
paul@128 | 64 | end_criteria_evaluated = get_parameter(end_criteria_default) |
paul@128 | 65 | |
paul@128 | 66 | start_criteria_evaluated = get_label(request, start_criteria_evaluated) |
paul@128 | 67 | end_criteria_evaluated = get_label(request, end_criteria_evaluated) |
paul@114 | 68 | |
paul@112 | 69 | # Descriptions. |
paul@112 | 70 | |
paul@112 | 71 | descriptions = form.get("descriptions", [None])[0] |
paul@23 | 72 | |
paul@45 | 73 | descriptions_list = [ |
paul@148 | 74 | '<option value="%s" %s>%s</option>' % ("page", self._get_selected("page", descriptions), escape(_("page"))), |
paul@148 | 75 | '<option value="%s" %s>%s</option>' % ("comment", self._get_selected("comment", descriptions), escape(_("comment"))) |
paul@45 | 76 | ] |
paul@45 | 77 | |
paul@112 | 78 | # Format. |
paul@112 | 79 | |
paul@112 | 80 | format = form.get("format", [None])[0] |
paul@112 | 81 | |
paul@45 | 82 | format_list = [ |
paul@148 | 83 | '<option value="%s" %s>%s</option>' % ("iCalendar", self._get_selected("iCalendar", format), escape(_("iCalendar"))), |
paul@148 | 84 | '<option value="%s" %s>%s</option>' % ("RSS", self._get_selected("RSS", format), escape(_("RSS 2.0"))) |
paul@45 | 85 | ] |
paul@23 | 86 | |
paul@115 | 87 | right_arrow = unicode('\xe2\x86\x92', "utf-8") |
paul@115 | 88 | |
paul@19 | 89 | d = { |
paul@74 | 90 | "buttons_html" : buttons_html, |
paul@137 | 91 | "category_label" : escape(_("Categories")), |
paul@74 | 92 | "category_list" : "\n".join(category_list), |
paul@112 | 93 | "start_month_list" : "\n".join(start_month_list), |
paul@136 | 94 | "start_label" : escape(_("Start day (optional), month and year")), |
paul@136 | 95 | "start_day_default" : escattr(start_day_default), |
paul@148 | 96 | "start_year_default" : escattr(start_year_default), |
paul@148 | 97 | "start_criteria_label" : escape(_("or special criteria")), |
paul@148 | 98 | "start_criteria_default": escattr(start_criteria_default), |
paul@136 | 99 | "start_eval_label" : escattr(right_arrow), |
paul@148 | 100 | "start_criteria_eval" : escape(start_criteria_evaluated), |
paul@112 | 101 | "end_month_list" : "\n".join(end_month_list), |
paul@136 | 102 | "end_label" : escape(_("End day (optional), month and year")), |
paul@136 | 103 | "end_day_default" : escattr(end_day_default), |
paul@148 | 104 | "end_year_default" : escattr(end_year_default), |
paul@148 | 105 | "end_criteria_label" : escape(_("or special criteria")), |
paul@148 | 106 | "end_criteria_default" : escattr(end_criteria_default), |
paul@151 | 107 | "end_eval_label" : escattr(right_arrow), |
paul@148 | 108 | "end_criteria_eval" : escape(end_criteria_evaluated), |
paul@137 | 109 | "descriptions_label" : escape(_("Use descriptions from...")), |
paul@74 | 110 | "descriptions_list" : "\n".join(descriptions_list), |
paul@137 | 111 | "format_label" : escape(_("Summary format")), |
paul@74 | 112 | "format_list" : "\n".join(format_list), |
paul@137 | 113 | "parent_label" : escape(_("Parent page")), |
paul@137 | 114 | "parent_name" : escattr(form.get("parent", [""])[0]), |
paul@136 | 115 | "resolution" : escattr(resolution), |
paul@19 | 116 | } |
paul@10 | 117 | |
paul@19 | 118 | return ''' |
paul@131 | 119 | <input name="resolution" type="hidden" value="%(resolution)s" /> |
paul@19 | 120 | <table> |
paul@19 | 121 | <tr> |
paul@19 | 122 | <td class="label"><label>%(category_label)s</label></td> |
paul@19 | 123 | <td class="content"> |
paul@19 | 124 | <select multiple="multiple" name="category"> |
paul@19 | 125 | %(category_list)s |
paul@19 | 126 | </select> |
paul@19 | 127 | </td> |
paul@19 | 128 | </tr> |
paul@19 | 129 | <tr> |
paul@19 | 130 | <td class="label"><label>%(start_label)s</label></td> |
paul@22 | 131 | <td> |
paul@127 | 132 | <input name="start-day" type="text" value="%(start_day_default)s" size="2" /> |
paul@23 | 133 | <select name="start-month"> |
paul@112 | 134 | %(start_month_list)s |
paul@23 | 135 | </select> |
paul@23 | 136 | <input name="start-year" type="text" value="%(start_year_default)s" size="4" /> |
paul@19 | 137 | </td> |
paul@19 | 138 | </tr> |
paul@19 | 139 | <tr> |
paul@114 | 140 | <td class="label"><label>%(start_criteria_label)s</label></td> |
paul@114 | 141 | <td> |
paul@114 | 142 | <input name="start" type="text" value="%(start_criteria_default)s" size="12" /> |
paul@114 | 143 | <input name="start-eval" type="submit" value="%(start_eval_label)s" /> |
paul@114 | 144 | %(start_criteria_eval)s |
paul@114 | 145 | </td> |
paul@114 | 146 | </tr> |
paul@114 | 147 | <tr> |
paul@19 | 148 | <td class="label"><label>%(end_label)s</label></td> |
paul@22 | 149 | <td> |
paul@127 | 150 | <input name="end-day" type="text" value="%(end_day_default)s" size="2" /> |
paul@23 | 151 | <select name="end-month"> |
paul@112 | 152 | %(end_month_list)s |
paul@23 | 153 | </select> |
paul@23 | 154 | <input name="end-year" type="text" value="%(end_year_default)s" size="4" /> |
paul@19 | 155 | </td> |
paul@19 | 156 | </tr> |
paul@19 | 157 | <tr> |
paul@114 | 158 | <td class="label"><label>%(end_criteria_label)s</label></td> |
paul@114 | 159 | <td> |
paul@114 | 160 | <input name="end" type="text" value="%(end_criteria_default)s" size="12" /> |
paul@114 | 161 | <input name="end-eval" type="submit" value="%(end_eval_label)s" /> |
paul@114 | 162 | %(end_criteria_eval)s |
paul@114 | 163 | </td> |
paul@114 | 164 | </tr> |
paul@114 | 165 | <tr> |
paul@45 | 166 | <td class="label"><label>%(descriptions_label)s</label></td> |
paul@45 | 167 | <td class="content"> |
paul@45 | 168 | <select name="descriptions"> |
paul@45 | 169 | %(descriptions_list)s |
paul@45 | 170 | </select> |
paul@45 | 171 | </td> |
paul@45 | 172 | </tr> |
paul@45 | 173 | <tr> |
paul@45 | 174 | <td class="label"><label>%(format_label)s</label></td> |
paul@45 | 175 | <td class="content"> |
paul@45 | 176 | <select name="format"> |
paul@45 | 177 | %(format_list)s |
paul@45 | 178 | </select> |
paul@45 | 179 | </td> |
paul@45 | 180 | </tr> |
paul@45 | 181 | <tr> |
paul@74 | 182 | <td class="label"><label>%(parent_label)s</label></td> |
paul@74 | 183 | <td class="content"> |
paul@74 | 184 | <input name="parent" type="text" size="40" value="%(parent_name)s" /> |
paul@74 | 185 | </td> |
paul@74 | 186 | </tr> |
paul@74 | 187 | <tr> |
paul@19 | 188 | <td></td> |
paul@19 | 189 | <td class="buttons"> |
paul@19 | 190 | %(buttons_html)s |
paul@19 | 191 | </td> |
paul@19 | 192 | </tr> |
paul@19 | 193 | </table> |
paul@19 | 194 | ''' % d |
paul@19 | 195 | |
paul@19 | 196 | def do_action(self): |
paul@19 | 197 | |
paul@19 | 198 | "Write the iCalendar resource." |
paul@19 | 199 | |
paul@19 | 200 | _ = self._ |
paul@109 | 201 | form = self.get_form() |
paul@19 | 202 | |
paul@19 | 203 | # If no category names exist in the request, an error message is |
paul@19 | 204 | # returned. |
paul@19 | 205 | |
paul@23 | 206 | category_names = form.get("category", []) |
paul@19 | 207 | |
paul@19 | 208 | if not category_names: |
paul@19 | 209 | return 0, _("No categories specified.") |
paul@19 | 210 | |
paul@19 | 211 | write_resource(self.request) |
paul@19 | 212 | return 1, None |
paul@19 | 213 | |
paul@27 | 214 | def render_success(self, msg, msgtype=None): |
paul@19 | 215 | |
paul@19 | 216 | """ |
paul@19 | 217 | Render neither 'msg' nor 'msgtype' since a resource has already been |
paul@19 | 218 | produced. |
paul@27 | 219 | NOTE: msgtype is optional because MoinMoin 1.5.x does not support it. |
paul@19 | 220 | """ |
paul@19 | 221 | |
paul@19 | 222 | pass |
paul@19 | 223 | |
paul@24 | 224 | def getQuotedText(text): |
paul@24 | 225 | |
paul@24 | 226 | "Return the 'text' quoted for iCalendar purposes." |
paul@24 | 227 | |
paul@24 | 228 | return text.replace(";", r"\;").replace(",", r"\,") |
paul@24 | 229 | |
paul@19 | 230 | def write_resource(request): |
paul@10 | 231 | |
paul@10 | 232 | """ |
paul@19 | 233 | For the given 'request', write an iCalendar summary of the event data found |
paul@19 | 234 | in the categories specified via the "category" request parameter, using the |
paul@19 | 235 | "start" and "end" parameters (if specified). Multiple "category" parameters |
paul@19 | 236 | can be specified. |
paul@10 | 237 | """ |
paul@10 | 238 | |
paul@168 | 239 | form = get_form(request) |
paul@46 | 240 | |
paul@46 | 241 | category_names = form.get("category", []) |
paul@46 | 242 | format = form.get("format", ["iCalendar"])[0] |
paul@46 | 243 | descriptions = form.get("descriptions", ["page"])[0] |
paul@74 | 244 | parent = form.get("parent", [""])[0] |
paul@142 | 245 | resolution = form.get("resolution", ["month"])[0] |
paul@10 | 246 | |
paul@143 | 247 | # Look first for a single start and end parameter. If that fails to provide |
paul@143 | 248 | # dates, look for separate start and end parameters, either for complete |
paul@143 | 249 | # dates or for years and months. |
paul@10 | 250 | |
paul@143 | 251 | if resolution == "date": |
paul@168 | 252 | calendar_start = getFormDate(request, None, "start") |
paul@168 | 253 | calendar_end = getFormDate(request, None, "end") |
paul@10 | 254 | |
paul@143 | 255 | if calendar_start is None: |
paul@168 | 256 | calendar_start = getFormDateTriple(request, "start-year", "start-month", "start-day") |
paul@143 | 257 | if calendar_end is None: |
paul@168 | 258 | calendar_end = getFormDateTriple(request, "end-year", "end-month", "end-day") |
paul@23 | 259 | |
paul@143 | 260 | elif resolution == "month": |
paul@168 | 261 | calendar_start = getFormMonth(request, None, "start") |
paul@168 | 262 | calendar_end = getFormMonth(request, None, "end") |
paul@23 | 263 | |
paul@143 | 264 | if calendar_start is None: |
paul@168 | 265 | calendar_start = getFormMonthPair(request, "start-year", "start-month") |
paul@143 | 266 | if calendar_end is None: |
paul@168 | 267 | calendar_end = getFormMonthPair(request, "end-year", "end-month") |
paul@127 | 268 | |
paul@127 | 269 | # Determine the period and get the events involved. |
paul@23 | 270 | |
paul@168 | 271 | event_pages = getPagesFromResults(getAllCategoryPages(category_names, request), request) |
paul@168 | 272 | events = getEventsFromPages(event_pages) |
paul@171 | 273 | all_shown_events = getEventsInPeriod(events, getCalendarPeriod(calendar_start, calendar_end)) |
paul@168 | 274 | latest_timestamp = setEventTimestamps(request, all_shown_events) |
paul@29 | 275 | |
paul@29 | 276 | # Output summary data... |
paul@10 | 277 | |
paul@110 | 278 | if hasattr(request, "http_headers"): |
paul@27 | 279 | send_headers = request.http_headers |
paul@110 | 280 | elif hasattr(request, "emit_http_headers"): |
paul@110 | 281 | send_headers = request.emit_http_headers |
paul@27 | 282 | else: |
paul@180 | 283 | send_headers = send_headers_cls(request) |
paul@27 | 284 | |
paul@29 | 285 | # Define headers. |
paul@29 | 286 | |
paul@29 | 287 | if format == "iCalendar": |
paul@29 | 288 | headers = ["Content-Type: text/calendar; charset=%s" % config.charset] |
paul@29 | 289 | elif format == "RSS": |
paul@29 | 290 | headers = ["Content-Type: application/rss+xml; charset=%s" % config.charset] |
paul@29 | 291 | |
paul@29 | 292 | # Define the last modified time. |
paul@10 | 293 | |
paul@38 | 294 | if latest_timestamp is not None: |
paul@168 | 295 | headers.append("Last-Modified: %s" % getHTTPTimeString(latest_timestamp)) |
paul@38 | 296 | |
paul@29 | 297 | send_headers(headers) |
paul@29 | 298 | |
paul@29 | 299 | # iCalendar output... |
paul@10 | 300 | |
paul@29 | 301 | if format == "iCalendar": |
paul@29 | 302 | request.write("BEGIN:VCALENDAR\r\n") |
paul@29 | 303 | request.write("PRODID:-//MoinMoin//EventAggregatorSummary\r\n") |
paul@29 | 304 | request.write("VERSION:2.0\r\n") |
paul@24 | 305 | |
paul@69 | 306 | for event in all_shown_events: |
paul@69 | 307 | event_page = event.getPage() |
paul@69 | 308 | event_details = event.getDetails() |
paul@29 | 309 | |
paul@180 | 310 | # NOTE: A custom formatter making attributes for links and plain |
paul@180 | 311 | # NOTE: text for values could be employed here. |
paul@180 | 312 | |
paul@29 | 313 | # Get the summary details. |
paul@10 | 314 | |
paul@74 | 315 | event_summary = event.getSummary(parent) |
paul@67 | 316 | link = event_page.getPageURL(request) |
paul@29 | 317 | |
paul@29 | 318 | # Output the event details. |
paul@24 | 319 | |
paul@29 | 320 | request.write("BEGIN:VEVENT\r\n") |
paul@37 | 321 | request.write("UID:%s\r\n" % link) |
paul@37 | 322 | request.write("URL:%s\r\n" % link) |
paul@29 | 323 | request.write("DTSTAMP:%04d%02d%02dT%02d%02d%02dZ\r\n" % event_details["created"][:6]) |
paul@29 | 324 | request.write("LAST-MODIFIED:%04d%02d%02dT%02d%02d%02dZ\r\n" % event_details["last-modified"][:6]) |
paul@29 | 325 | request.write("SEQUENCE:%d\r\n" % event_details["sequence"]) |
paul@92 | 326 | |
paul@92 | 327 | start = event_details["start"] |
paul@92 | 328 | end = event_details["end"] |
paul@92 | 329 | |
paul@168 | 330 | if isinstance(start, DateTime): |
paul@92 | 331 | request.write("DTSTART") |
paul@93 | 332 | write_calendar_datetime(request, start) |
paul@92 | 333 | else: |
paul@92 | 334 | request.write("DTSTART;VALUE=DATE:%04d%02d%02d\r\n" % start.as_date().as_tuple()) |
paul@92 | 335 | |
paul@168 | 336 | if isinstance(end, DateTime): |
paul@92 | 337 | request.write("DTEND") |
paul@93 | 338 | write_calendar_datetime(request, end) |
paul@92 | 339 | else: |
paul@92 | 340 | request.write("DTEND;VALUE=DATE:%04d%02d%02d\r\n" % end.next_day().as_date().as_tuple()) |
paul@92 | 341 | |
paul@29 | 342 | request.write("SUMMARY:%s\r\n" % getQuotedText(event_summary)) |
paul@24 | 343 | |
paul@29 | 344 | # Optional details. |
paul@24 | 345 | |
paul@98 | 346 | if event_details.get("topics") or event_details.get("categories"): |
paul@29 | 347 | request.write("CATEGORIES:%s\r\n" % ",".join( |
paul@180 | 348 | [getQuotedText(topic) |
paul@180 | 349 | for topic in event_details.get("topics") or event_details.get("categories")] |
paul@29 | 350 | )) |
paul@29 | 351 | if event_details.has_key("location"): |
paul@29 | 352 | request.write("LOCATION:%s\r\n" % getQuotedText(event_details["location"])) |
paul@29 | 353 | |
paul@29 | 354 | request.write("END:VEVENT\r\n") |
paul@10 | 355 | |
paul@29 | 356 | request.write("END:VCALENDAR\r\n") |
paul@29 | 357 | |
paul@127 | 358 | # RSS output... |
paul@127 | 359 | |
paul@29 | 360 | elif format == "RSS": |
paul@60 | 361 | |
paul@60 | 362 | # Using the page name and the page URL in the title, link and |
paul@60 | 363 | # description. |
paul@60 | 364 | |
paul@110 | 365 | if hasattr(request, "getPathinfo"): |
paul@110 | 366 | path_info = request.getPathinfo() |
paul@110 | 367 | else: |
paul@110 | 368 | path_info = request.path |
paul@110 | 369 | |
paul@29 | 370 | request.write('<rss version="2.0">\r\n') |
paul@29 | 371 | request.write('<channel>\r\n') |
paul@110 | 372 | request.write('<title>%s</title>\r\n' % path_info[1:]) |
paul@110 | 373 | request.write('<link>%s%s</link>\r\n' % (request.getBaseURL(), path_info)) |
paul@110 | 374 | request.write('<description>Events published on %s%s</description>\r\n' % (request.getBaseURL(), path_info)) |
paul@111 | 375 | |
paul@111 | 376 | if latest_timestamp is not None: |
paul@168 | 377 | request.write('<lastBuildDate>%s</lastBuildDate>\r\n' % getHTTPTimeString(latest_timestamp)) |
paul@60 | 378 | |
paul@171 | 379 | # Sort all_shown_events by start date, reversed. |
paul@60 | 380 | |
paul@168 | 381 | ordered_events = getOrderedEvents(all_shown_events) |
paul@67 | 382 | ordered_events.reverse() |
paul@24 | 383 | |
paul@69 | 384 | for event in ordered_events: |
paul@69 | 385 | event_page = event.getPage() |
paul@69 | 386 | event_details = event.getDetails() |
paul@29 | 387 | |
paul@180 | 388 | # Get a parser and formatter for the formatting of some attributes. |
paul@180 | 389 | |
paul@180 | 390 | parser_cls = getParserClass(request, event_page.getFormat()) |
paul@180 | 391 | fmt = getFormatter(request, "text/html", event_page.page) |
paul@180 | 392 | |
paul@29 | 393 | # Get the summary details. |
paul@29 | 394 | |
paul@74 | 395 | event_summary = event.getSummary(parent) |
paul@67 | 396 | link = event_page.getPageURL(request) |
paul@24 | 397 | |
paul@29 | 398 | request.write('<item>\r\n') |
paul@29 | 399 | request.write('<title>%s</title>\r\n' % wikiutil.escape(event_summary)) |
paul@29 | 400 | request.write('<link>%s</link>\r\n' % link) |
paul@45 | 401 | |
paul@45 | 402 | # Write a description according to the preferred source of |
paul@45 | 403 | # descriptions. |
paul@45 | 404 | |
paul@45 | 405 | if descriptions == "page": |
paul@45 | 406 | description = event_details.get("description", "") |
paul@45 | 407 | else: |
paul@45 | 408 | description = event_details["last-comment"] |
paul@45 | 409 | |
paul@180 | 410 | request.write('<description>%s</description>\r\n' % |
paul@181 | 411 | fmt.text(formatText(description, request, fmt, parser_cls))) |
paul@29 | 412 | |
paul@29 | 413 | for topic in event_details.get("topics") or event_details.get("categories") or []: |
paul@180 | 414 | request.write('<category>%s</category>\r\n' % |
paul@181 | 415 | fmt.text(formatText(topic, request, fmt, parser_cls))) |
paul@24 | 416 | |
paul@168 | 417 | request.write('<pubDate>%s</pubDate>\r\n' % getHTTPTimeString(event_details["created"])) |
paul@29 | 418 | request.write('<guid>%s#%s</guid>\r\n' % (link, event_details["sequence"])) |
paul@29 | 419 | request.write('</item>\r\n') |
paul@10 | 420 | |
paul@29 | 421 | request.write('</channel>\r\n') |
paul@29 | 422 | request.write('</rss>\r\n') |
paul@10 | 423 | |
paul@93 | 424 | def write_calendar_datetime(request, datetime): |
paul@93 | 425 | |
paul@93 | 426 | """ |
paul@93 | 427 | Write to the given 'request' the 'datetime' using appropriate time zone |
paul@93 | 428 | information. |
paul@93 | 429 | """ |
paul@93 | 430 | |
paul@93 | 431 | utc_datetime = datetime.to_utc() |
paul@93 | 432 | if utc_datetime: |
paul@93 | 433 | request.write(";VALUE=DATE-TIME:%04d%02d%02dT%02d%02d%02dZ\r\n" % utc_datetime.padded().as_tuple()[:-1]) |
paul@93 | 434 | else: |
paul@93 | 435 | zone = datetime.time_zone() |
paul@93 | 436 | if zone: |
paul@93 | 437 | request.write(";TZID=/%s" % zone) |
paul@93 | 438 | request.write(";VALUE=DATE-TIME:%04d%02d%02dT%02d%02d%02d\r\n" % datetime.padded().as_tuple()[:-1]) |
paul@93 | 439 | |
paul@19 | 440 | # Action function. |
paul@19 | 441 | |
paul@19 | 442 | def execute(pagename, request): |
paul@19 | 443 | EventAggregatorSummary(pagename, request).render() |
paul@19 | 444 | |
paul@10 | 445 | # vim: tabstop=4 expandtab shiftwidth=4 |