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 298 if latest_timestamp is not None: 299 request.write('<lastBuildDate>%s</lastBuildDate>\r\n' % EventAggregatorSupport.getHTTPTimeString(latest_timestamp)) 300 301 # Sort all_shown_events by start date, reversed: 302 # 303 # * event_details are dictionaries, with the "start" entry providing 304 # the start date 305 # 306 # So we use as sorting key the "start" key from the event details. 307 308 ordered_events = EventAggregatorSupport.getOrderedEvents(all_shown_events) 309 ordered_events.reverse() 310 311 for event in ordered_events: 312 event_page = event.getPage() 313 event_details = event.getDetails() 314 315 # Get the summary details. 316 317 event_summary = event.getSummary(parent) 318 link = event_page.getPageURL(request) 319 320 request.write('<item>\r\n') 321 request.write('<title>%s</title>\r\n' % wikiutil.escape(event_summary)) 322 request.write('<link>%s</link>\r\n' % link) 323 324 # Write a description according to the preferred source of 325 # descriptions. 326 327 if descriptions == "page": 328 description = event_details.get("description", "") 329 else: 330 description = event_details["last-comment"] 331 332 request.write('<description>%s</description>\r\n' % wikiutil.escape(description)) 333 334 for topic in event_details.get("topics") or event_details.get("categories") or []: 335 request.write('<category>%s</category>\r\n' % topic) 336 337 request.write('<pubDate>%s</pubDate>\r\n' % EventAggregatorSupport.getHTTPTimeString(event_details["created"])) 338 request.write('<guid>%s#%s</guid>\r\n' % (link, event_details["sequence"])) 339 request.write('</item>\r\n') 340 341 request.write('</channel>\r\n') 342 request.write('</rss>\r\n') 343 344 if EventAggregatorSupport.isMoin15(): 345 raise MoinMoin.util.MoinMoinNoFooter 346 347 def write_calendar_datetime(request, datetime): 348 349 """ 350 Write to the given 'request' the 'datetime' using appropriate time zone 351 information. 352 """ 353 354 utc_datetime = datetime.to_utc() 355 if utc_datetime: 356 request.write(";VALUE=DATE-TIME:%04d%02d%02dT%02d%02d%02dZ\r\n" % utc_datetime.padded().as_tuple()[:-1]) 357 else: 358 zone = datetime.time_zone() 359 if zone: 360 request.write(";TZID=/%s" % zone) 361 request.write(";VALUE=DATE-TIME:%04d%02d%02dT%02d%02d%02d\r\n" % datetime.padded().as_tuple()[:-1]) 362 363 # Action function. 364 365 def execute(pagename, request): 366 EventAggregatorSummary(pagename, request).render() 367 368 # vim: tabstop=4 expandtab shiftwidth=4