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 calendar_period = getCalendarPeriod(calendar_start, calendar_end, resolution) 274 all_shown_events = getEventsInPeriod(events, calendar_period, resolution) 275 276 latest_timestamp = setEventTimestamps(request, all_shown_events) 277 278 # Output summary data... 279 280 if hasattr(request, "http_headers"): 281 send_headers = request.http_headers 282 elif hasattr(request, "emit_http_headers"): 283 send_headers = request.emit_http_headers 284 else: 285 send_headers = send_headers(request) 286 287 # Define headers. 288 289 if format == "iCalendar": 290 headers = ["Content-Type: text/calendar; charset=%s" % config.charset] 291 elif format == "RSS": 292 headers = ["Content-Type: application/rss+xml; charset=%s" % config.charset] 293 294 # Define the last modified time. 295 296 if latest_timestamp is not None: 297 headers.append("Last-Modified: %s" % getHTTPTimeString(latest_timestamp)) 298 299 send_headers(headers) 300 301 # iCalendar output... 302 303 if format == "iCalendar": 304 request.write("BEGIN:VCALENDAR\r\n") 305 request.write("PRODID:-//MoinMoin//EventAggregatorSummary\r\n") 306 request.write("VERSION:2.0\r\n") 307 308 for event in all_shown_events: 309 event_page = event.getPage() 310 event_details = event.getDetails() 311 312 # Get the summary details. 313 314 event_summary = event.getSummary(parent) 315 link = event_page.getPageURL(request) 316 317 # Output the event details. 318 319 request.write("BEGIN:VEVENT\r\n") 320 request.write("UID:%s\r\n" % link) 321 request.write("URL:%s\r\n" % link) 322 request.write("DTSTAMP:%04d%02d%02dT%02d%02d%02dZ\r\n" % event_details["created"][:6]) 323 request.write("LAST-MODIFIED:%04d%02d%02dT%02d%02d%02dZ\r\n" % event_details["last-modified"][:6]) 324 request.write("SEQUENCE:%d\r\n" % event_details["sequence"]) 325 326 start = event_details["start"] 327 end = event_details["end"] 328 329 if isinstance(start, DateTime): 330 request.write("DTSTART") 331 write_calendar_datetime(request, start) 332 else: 333 request.write("DTSTART;VALUE=DATE:%04d%02d%02d\r\n" % start.as_date().as_tuple()) 334 335 if isinstance(end, DateTime): 336 request.write("DTEND") 337 write_calendar_datetime(request, end) 338 else: 339 request.write("DTEND;VALUE=DATE:%04d%02d%02d\r\n" % end.next_day().as_date().as_tuple()) 340 341 request.write("SUMMARY:%s\r\n" % getQuotedText(event_summary)) 342 343 # Optional details. 344 345 if event_details.get("topics") or event_details.get("categories"): 346 request.write("CATEGORIES:%s\r\n" % ",".join( 347 [getQuotedText(topic) for topic in event_details.get("topics") or event_details.get("categories")] 348 )) 349 if event_details.has_key("location"): 350 request.write("LOCATION:%s\r\n" % getQuotedText(event_details["location"])) 351 352 request.write("END:VEVENT\r\n") 353 354 request.write("END:VCALENDAR\r\n") 355 356 # RSS output... 357 358 elif format == "RSS": 359 360 # Using the page name and the page URL in the title, link and 361 # description. 362 363 if hasattr(request, "getPathinfo"): 364 path_info = request.getPathinfo() 365 else: 366 path_info = request.path 367 368 request.write('<rss version="2.0">\r\n') 369 request.write('<channel>\r\n') 370 request.write('<title>%s</title>\r\n' % path_info[1:]) 371 request.write('<link>%s%s</link>\r\n' % (request.getBaseURL(), path_info)) 372 request.write('<description>Events published on %s%s</description>\r\n' % (request.getBaseURL(), path_info)) 373 374 if latest_timestamp is not None: 375 request.write('<lastBuildDate>%s</lastBuildDate>\r\n' % getHTTPTimeString(latest_timestamp)) 376 377 # Sort all_shown_events by start date, reversed: 378 # 379 # * event_details are dictionaries, with the "start" entry providing 380 # the start date 381 # 382 # So we use as sorting key the "start" key from the event details. 383 384 ordered_events = getOrderedEvents(all_shown_events) 385 ordered_events.reverse() 386 387 for event in ordered_events: 388 event_page = event.getPage() 389 event_details = event.getDetails() 390 391 # Get the summary details. 392 393 event_summary = event.getSummary(parent) 394 link = event_page.getPageURL(request) 395 396 request.write('<item>\r\n') 397 request.write('<title>%s</title>\r\n' % wikiutil.escape(event_summary)) 398 request.write('<link>%s</link>\r\n' % link) 399 400 # Write a description according to the preferred source of 401 # descriptions. 402 403 if descriptions == "page": 404 description = event_details.get("description", "") 405 else: 406 description = event_details["last-comment"] 407 408 request.write('<description>%s</description>\r\n' % wikiutil.escape(description)) 409 410 for topic in event_details.get("topics") or event_details.get("categories") or []: 411 request.write('<category>%s</category>\r\n' % topic) 412 413 request.write('<pubDate>%s</pubDate>\r\n' % getHTTPTimeString(event_details["created"])) 414 request.write('<guid>%s#%s</guid>\r\n' % (link, event_details["sequence"])) 415 request.write('</item>\r\n') 416 417 request.write('</channel>\r\n') 418 request.write('</rss>\r\n') 419 420 def write_calendar_datetime(request, datetime): 421 422 """ 423 Write to the given 'request' the 'datetime' using appropriate time zone 424 information. 425 """ 426 427 utc_datetime = datetime.to_utc() 428 if utc_datetime: 429 request.write(";VALUE=DATE-TIME:%04d%02d%02dT%02d%02d%02dZ\r\n" % utc_datetime.padded().as_tuple()[:-1]) 430 else: 431 zone = datetime.time_zone() 432 if zone: 433 request.write(";TZID=/%s" % zone) 434 request.write(";VALUE=DATE-TIME:%04d%02d%02dT%02d%02d%02d\r\n" % datetime.padded().as_tuple()[:-1]) 435 436 # Action function. 437 438 def execute(pagename, request): 439 EventAggregatorSummary(pagename, request).render() 440 441 # vim: tabstop=4 expandtab shiftwidth=4