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(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 # Get the summary details. 311 312 event_summary = event.getSummary(parent) 313 link = event_page.getPageURL(request) 314 315 # Output the event details. 316 317 request.write("BEGIN:VEVENT\r\n") 318 request.write("UID:%s\r\n" % link) 319 request.write("URL:%s\r\n" % link) 320 request.write("DTSTAMP:%04d%02d%02dT%02d%02d%02dZ\r\n" % event_details["created"][:6]) 321 request.write("LAST-MODIFIED:%04d%02d%02dT%02d%02d%02dZ\r\n" % event_details["last-modified"][:6]) 322 request.write("SEQUENCE:%d\r\n" % event_details["sequence"]) 323 324 start = event_details["start"] 325 end = event_details["end"] 326 327 if isinstance(start, DateTime): 328 request.write("DTSTART") 329 write_calendar_datetime(request, start) 330 else: 331 request.write("DTSTART;VALUE=DATE:%04d%02d%02d\r\n" % start.as_date().as_tuple()) 332 333 if isinstance(end, DateTime): 334 request.write("DTEND") 335 write_calendar_datetime(request, end) 336 else: 337 request.write("DTEND;VALUE=DATE:%04d%02d%02d\r\n" % end.next_day().as_date().as_tuple()) 338 339 request.write("SUMMARY:%s\r\n" % getQuotedText(event_summary)) 340 341 # Optional details. 342 343 if event_details.get("topics") or event_details.get("categories"): 344 request.write("CATEGORIES:%s\r\n" % ",".join( 345 [getQuotedText(topic) for topic in event_details.get("topics") or event_details.get("categories")] 346 )) 347 if event_details.has_key("location"): 348 request.write("LOCATION:%s\r\n" % getQuotedText(event_details["location"])) 349 350 request.write("END:VEVENT\r\n") 351 352 request.write("END:VCALENDAR\r\n") 353 354 # RSS output... 355 356 elif format == "RSS": 357 358 # Using the page name and the page URL in the title, link and 359 # description. 360 361 if hasattr(request, "getPathinfo"): 362 path_info = request.getPathinfo() 363 else: 364 path_info = request.path 365 366 request.write('<rss version="2.0">\r\n') 367 request.write('<channel>\r\n') 368 request.write('<title>%s</title>\r\n' % path_info[1:]) 369 request.write('<link>%s%s</link>\r\n' % (request.getBaseURL(), path_info)) 370 request.write('<description>Events published on %s%s</description>\r\n' % (request.getBaseURL(), path_info)) 371 372 if latest_timestamp is not None: 373 request.write('<lastBuildDate>%s</lastBuildDate>\r\n' % getHTTPTimeString(latest_timestamp)) 374 375 # Sort all_shown_events by start date, reversed. 376 377 ordered_events = getOrderedEvents(all_shown_events) 378 ordered_events.reverse() 379 380 for event in ordered_events: 381 event_page = event.getPage() 382 event_details = event.getDetails() 383 384 # Get the summary details. 385 386 event_summary = event.getSummary(parent) 387 link = event_page.getPageURL(request) 388 389 request.write('<item>\r\n') 390 request.write('<title>%s</title>\r\n' % wikiutil.escape(event_summary)) 391 request.write('<link>%s</link>\r\n' % link) 392 393 # Write a description according to the preferred source of 394 # descriptions. 395 396 if descriptions == "page": 397 description = event_details.get("description", "") 398 else: 399 description = event_details["last-comment"] 400 401 request.write('<description>%s</description>\r\n' % wikiutil.escape(description)) 402 403 for topic in event_details.get("topics") or event_details.get("categories") or []: 404 request.write('<category>%s</category>\r\n' % topic) 405 406 request.write('<pubDate>%s</pubDate>\r\n' % getHTTPTimeString(event_details["created"])) 407 request.write('<guid>%s#%s</guid>\r\n' % (link, event_details["sequence"])) 408 request.write('</item>\r\n') 409 410 request.write('</channel>\r\n') 411 request.write('</rss>\r\n') 412 413 def write_calendar_datetime(request, datetime): 414 415 """ 416 Write to the given 'request' the 'datetime' using appropriate time zone 417 information. 418 """ 419 420 utc_datetime = datetime.to_utc() 421 if utc_datetime: 422 request.write(";VALUE=DATE-TIME:%04d%02d%02dT%02d%02d%02dZ\r\n" % utc_datetime.padded().as_tuple()[:-1]) 423 else: 424 zone = datetime.time_zone() 425 if zone: 426 request.write(";TZID=/%s" % zone) 427 request.write(";VALUE=DATE-TIME:%04d%02d%02dT%02d%02d%02d\r\n" % datetime.padded().as_tuple()[:-1]) 428 429 # Action function. 430 431 def execute(pagename, request): 432 EventAggregatorSummary(pagename, request).render() 433 434 # vim: tabstop=4 expandtab shiftwidth=4