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