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