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