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 Dependencies = ['pages'] 21 22 # Action class and supporting functions. 23 24 class EventAggregatorSummary(ActionBase, EventAggregatorSupport.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 \ 39 EventAggregatorSupport.getCategoryMapping( 40 EventAggregatorSupport.getCategories(request), 41 request): 42 43 selected = self._get_selected_for_list(category_pagename, category_pagenames) 44 45 category_list.append('<option value="%s" %s>%s</option>' % (category_pagename, selected, category_name)) 46 47 # Initialise month lists and defaults. 48 49 start_month_list, end_month_list = self.get_month_lists() 50 start_day_default, end_day_default = self.get_day_defaults() 51 start_year_default, end_year_default = self.get_year_defaults() 52 53 # Criteria instead of months and years. 54 55 start_criteria_default = form.get("start", [""])[0] 56 end_criteria_default = form.get("end", [""])[0] 57 58 if resolution == "date": 59 get_parameter = EventAggregatorSupport.getParameterDate 60 get_label = EventAggregatorSupport.getFullDateLabel 61 else: 62 get_parameter = EventAggregatorSupport.getParameterMonth 63 get_label = EventAggregatorSupport.getFullMonthLabel 64 65 start_criteria_evaluated = get_parameter(start_criteria_default) 66 end_criteria_evaluated = get_parameter(end_criteria_default) 67 68 start_criteria_evaluated = get_label(request, start_criteria_evaluated) 69 end_criteria_evaluated = get_label(request, end_criteria_evaluated) 70 71 # Descriptions. 72 73 descriptions = form.get("descriptions", [None])[0] 74 75 descriptions_list = [ 76 '<option value="%s" %s>%s</option>' % ("page", self._get_selected("page", descriptions), _("page")), 77 '<option value="%s" %s>%s</option>' % ("comment", self._get_selected("comment", descriptions), _("comment")) 78 ] 79 80 # Format. 81 82 format = form.get("format", [None])[0] 83 84 format_list = [ 85 '<option value="%s" %s>%s</option>' % ("iCalendar", self._get_selected("iCalendar", format), _("iCalendar")), 86 '<option value="%s" %s>%s</option>' % ("RSS", self._get_selected("RSS", format), _("RSS 2.0")) 87 ] 88 89 right_arrow = unicode('\xe2\x86\x92', "utf-8") 90 91 d = { 92 "buttons_html" : buttons_html, 93 "category_label" : _("Categories"), 94 "category_list" : "\n".join(category_list), 95 "start_month_list" : "\n".join(start_month_list), 96 "start_label" : _("Start day (optional), month and year"), 97 "start_day_default" : start_day_default, 98 "start_year_default" : start_year_default, 99 "start_criteria_label" : _("or special criteria"), 100 "start_criteria_default": start_criteria_default, 101 "start_eval_label" : right_arrow, 102 "start_criteria_eval" : start_criteria_evaluated, 103 "end_month_list" : "\n".join(end_month_list), 104 "end_label" : _("End day (optional), month and year"), 105 "end_day_default" : end_day_default, 106 "end_year_default" : end_year_default, 107 "end_criteria_label" : _("or special criteria"), 108 "end_criteria_default" : end_criteria_default, 109 "end_eval_label" : right_arrow, 110 "end_criteria_eval" : end_criteria_evaluated, 111 "descriptions_label" : _("Use descriptions from..."), 112 "descriptions_list" : "\n".join(descriptions_list), 113 "format_label" : _("Summary format"), 114 "format_list" : "\n".join(format_list), 115 "parent_label" : _("Parent page"), 116 "parent_name" : form.get("parent", [""])[0], 117 "resolution" : resolution, 118 } 119 120 return ''' 121 <input name="resolution" type="hidden" value="%(resolution)s" /> 122 <table> 123 <tr> 124 <td class="label"><label>%(category_label)s</label></td> 125 <td class="content"> 126 <select multiple="multiple" name="category"> 127 %(category_list)s 128 </select> 129 </td> 130 </tr> 131 <tr> 132 <td class="label"><label>%(start_label)s</label></td> 133 <td> 134 <input name="start-day" type="text" value="%(start_day_default)s" size="2" /> 135 <select name="start-month"> 136 %(start_month_list)s 137 </select> 138 <input name="start-year" type="text" value="%(start_year_default)s" size="4" /> 139 </td> 140 </tr> 141 <tr> 142 <td class="label"><label>%(start_criteria_label)s</label></td> 143 <td> 144 <input name="start" type="text" value="%(start_criteria_default)s" size="12" /> 145 <input name="start-eval" type="submit" value="%(start_eval_label)s" /> 146 %(start_criteria_eval)s 147 </td> 148 </tr> 149 <tr> 150 <td class="label"><label>%(end_label)s</label></td> 151 <td> 152 <input name="end-day" type="text" value="%(end_day_default)s" size="2" /> 153 <select name="end-month"> 154 %(end_month_list)s 155 </select> 156 <input name="end-year" type="text" value="%(end_year_default)s" size="4" /> 157 </td> 158 </tr> 159 <tr> 160 <td class="label"><label>%(end_criteria_label)s</label></td> 161 <td> 162 <input name="end" type="text" value="%(end_criteria_default)s" size="12" /> 163 <input name="end-eval" type="submit" value="%(end_eval_label)s" /> 164 %(end_criteria_eval)s 165 </td> 166 </tr> 167 <tr> 168 <td class="label"><label>%(descriptions_label)s</label></td> 169 <td class="content"> 170 <select name="descriptions"> 171 %(descriptions_list)s 172 </select> 173 </td> 174 </tr> 175 <tr> 176 <td class="label"><label>%(format_label)s</label></td> 177 <td class="content"> 178 <select name="format"> 179 %(format_list)s 180 </select> 181 </td> 182 </tr> 183 <tr> 184 <td class="label"><label>%(parent_label)s</label></td> 185 <td class="content"> 186 <input name="parent" type="text" size="40" value="%(parent_name)s" /> 187 </td> 188 </tr> 189 <tr> 190 <td></td> 191 <td class="buttons"> 192 %(buttons_html)s 193 </td> 194 </tr> 195 </table> 196 ''' % d 197 198 def do_action(self): 199 200 "Write the iCalendar resource." 201 202 _ = self._ 203 form = self.get_form() 204 205 # If no category names exist in the request, an error message is 206 # returned. 207 208 category_names = form.get("category", []) 209 210 if not category_names: 211 return 0, _("No categories specified.") 212 213 write_resource(self.request) 214 return 1, None 215 216 def render_success(self, msg, msgtype=None): 217 218 """ 219 Render neither 'msg' nor 'msgtype' since a resource has already been 220 produced. 221 NOTE: msgtype is optional because MoinMoin 1.5.x does not support it. 222 """ 223 224 pass 225 226 def getQuotedText(text): 227 228 "Return the 'text' quoted for iCalendar purposes." 229 230 return text.replace(";", r"\;").replace(",", r"\,") 231 232 def write_resource(request): 233 234 """ 235 For the given 'request', write an iCalendar summary of the event data found 236 in the categories specified via the "category" request parameter, using the 237 "start" and "end" parameters (if specified). Multiple "category" parameters 238 can be specified. 239 """ 240 241 form = EventAggregatorSupport.get_form(request) 242 243 category_names = form.get("category", []) 244 format = form.get("format", ["iCalendar"])[0] 245 descriptions = form.get("descriptions", ["page"])[0] 246 parent = form.get("parent", [""])[0] 247 248 calendar_start = EventAggregatorSupport.getFormDate(request, None, "start") or \ 249 EventAggregatorSupport.getFormMonth(request, None, "start") 250 calendar_end = EventAggregatorSupport.getFormDate(request, None, "end") or \ 251 EventAggregatorSupport.getFormMonth(request, None, "end") 252 253 # Look for separate start and end parameters, either for complete dates 254 # or for years and months. 255 256 if calendar_start is None: 257 calendar_start = EventAggregatorSupport.getFormDateTriple(request, "start-year", "start-month", "start-day") or \ 258 EventAggregatorSupport.getFormMonthPair(request, "start-year", "start-month") 259 260 if calendar_end is None: 261 calendar_end = EventAggregatorSupport.getFormDateTriple(request, "end-year", "end-month", "end-day") or \ 262 EventAggregatorSupport.getFormMonthPair(request, "end-year", "end-month") 263 264 # Determine the period and get the events involved. 265 266 if isinstance(calendar_start, EventAggregatorSupport.Date): 267 if isinstance(calendar_end, EventAggregatorSupport.Date): 268 resolution = "date" 269 else: 270 calendar_start = calendar_start.as_month() 271 resolution = "month" 272 else: 273 resolution = "month" 274 if isinstance(calendar_end, EventAggregatorSupport.Date): 275 calendar_end = calendar_end.as_month() 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 start.has_time(): 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 end.has_time(): 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