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