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