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