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