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