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): 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 = request.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.request.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 = request.form 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 EventAggregatorSupport.isMoin15(): 207 send_headers = request.http_headers 208 else: 209 send_headers = request.emit_http_headers 210 211 # Define headers. 212 213 if format == "iCalendar": 214 headers = ["Content-Type: text/calendar; charset=%s" % config.charset] 215 elif format == "RSS": 216 headers = ["Content-Type: application/rss+xml; charset=%s" % config.charset] 217 218 # Define the last modified time. 219 220 if latest_timestamp is not None: 221 headers.append("Last-Modified: %s" % EventAggregatorSupport.getHTTPTimeString(latest_timestamp)) 222 223 send_headers(headers) 224 225 # iCalendar output... 226 227 if format == "iCalendar": 228 request.write("BEGIN:VCALENDAR\r\n") 229 request.write("PRODID:-//MoinMoin//EventAggregatorSummary\r\n") 230 request.write("VERSION:2.0\r\n") 231 232 for event in all_shown_events: 233 event_page = event.getPage() 234 event_details = event.getDetails() 235 236 # Get the summary details. 237 238 event_summary = event.getSummary(parent) 239 link = event_page.getPageURL(request) 240 241 # Output the event details. 242 243 request.write("BEGIN:VEVENT\r\n") 244 request.write("UID:%s\r\n" % link) 245 request.write("URL:%s\r\n" % link) 246 request.write("DTSTAMP:%04d%02d%02dT%02d%02d%02dZ\r\n" % event_details["created"][:6]) 247 request.write("LAST-MODIFIED:%04d%02d%02dT%02d%02d%02dZ\r\n" % event_details["last-modified"][:6]) 248 request.write("SEQUENCE:%d\r\n" % event_details["sequence"]) 249 250 start = event_details["start"] 251 end = event_details["end"] 252 253 if start.has_time(): 254 request.write("DTSTART") 255 write_calendar_datetime(request, start) 256 else: 257 request.write("DTSTART;VALUE=DATE:%04d%02d%02d\r\n" % start.as_date().as_tuple()) 258 259 if end.has_time(): 260 request.write("DTEND") 261 write_calendar_datetime(request, end) 262 else: 263 request.write("DTEND;VALUE=DATE:%04d%02d%02d\r\n" % end.next_day().as_date().as_tuple()) 264 265 request.write("SUMMARY:%s\r\n" % getQuotedText(event_summary)) 266 267 # Optional details. 268 269 if event_details.get("topics") or event_details.get("categories"): 270 request.write("CATEGORIES:%s\r\n" % ",".join( 271 [getQuotedText(topic) for topic in event_details.get("topics") or event_details.get("categories")] 272 )) 273 if event_details.has_key("location"): 274 request.write("LOCATION:%s\r\n" % getQuotedText(event_details["location"])) 275 276 request.write("END:VEVENT\r\n") 277 278 request.write("END:VCALENDAR\r\n") 279 280 elif format == "RSS": 281 282 # Using the page name and the page URL in the title, link and 283 # description. 284 285 request.write('<rss version="2.0">\r\n') 286 request.write('<channel>\r\n') 287 request.write('<title>%s</title>\r\n' % request.getPathinfo()[1:]) 288 request.write('<link>%s%s</link>\r\n' % (request.getBaseURL(), request.getPathinfo())) 289 request.write('<description>Events published on %s%s</description>\r\n' % (request.getBaseURL(), request.getPathinfo())) 290 request.write('<lastBuildDate>%s</lastBuildDate>\r\n' % EventAggregatorSupport.getHTTPTimeString(latest_timestamp)) 291 292 # Sort all_shown_events by start date, reversed: 293 # 294 # * event_details are dictionaries, with the "start" entry providing 295 # the start date 296 # 297 # So we use as sorting key the "start" key from the event details. 298 299 ordered_events = EventAggregatorSupport.getOrderedEvents(all_shown_events) 300 ordered_events.reverse() 301 302 for event in ordered_events: 303 event_page = event.getPage() 304 event_details = event.getDetails() 305 306 # Get the summary details. 307 308 event_summary = event.getSummary(parent) 309 link = event_page.getPageURL(request) 310 311 request.write('<item>\r\n') 312 request.write('<title>%s</title>\r\n' % wikiutil.escape(event_summary)) 313 request.write('<link>%s</link>\r\n' % link) 314 315 # Write a description according to the preferred source of 316 # descriptions. 317 318 if descriptions == "page": 319 description = event_details.get("description", "") 320 else: 321 description = event_details["last-comment"] 322 323 request.write('<description>%s</description>\r\n' % wikiutil.escape(description)) 324 325 for topic in event_details.get("topics") or event_details.get("categories") or []: 326 request.write('<category>%s</category>\r\n' % topic) 327 328 request.write('<pubDate>%s</pubDate>\r\n' % EventAggregatorSupport.getHTTPTimeString(event_details["created"])) 329 request.write('<guid>%s#%s</guid>\r\n' % (link, event_details["sequence"])) 330 request.write('</item>\r\n') 331 332 request.write('</channel>\r\n') 333 request.write('</rss>\r\n') 334 335 if EventAggregatorSupport.isMoin15(): 336 raise MoinMoin.util.MoinMoinNoFooter 337 338 def write_calendar_datetime(request, datetime): 339 340 """ 341 Write to the given 'request' the 'datetime' using appropriate time zone 342 information. 343 """ 344 345 utc_datetime = datetime.to_utc() 346 if utc_datetime: 347 request.write(";VALUE=DATE-TIME:%04d%02d%02dT%02d%02d%02dZ\r\n" % utc_datetime.padded().as_tuple()[:-1]) 348 else: 349 zone = datetime.time_zone() 350 if zone: 351 request.write(";TZID=/%s" % zone) 352 request.write(";VALUE=DATE-TIME:%04d%02d%02dT%02d%02d%02d\r\n" % datetime.padded().as_tuple()[:-1]) 353 354 # Action function. 355 356 def execute(pagename, request): 357 EventAggregatorSummary(pagename, request).render() 358 359 # vim: tabstop=4 expandtab shiftwidth=4