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 zone = start.time_zone() 256 if zone: 257 request.write(";TZID=%s" % zone) 258 259 request.write(";VALUE=DATE-TIME:%04d%02d%02dT%02d%02d%02d\r\n" % start.padded().as_tuple()[:-1]) 260 else: 261 request.write("DTSTART;VALUE=DATE:%04d%02d%02d\r\n" % start.as_date().as_tuple()) 262 263 if end.has_time(): 264 request.write("DTEND") 265 zone = end.time_zone() 266 if zone: 267 request.write(";TZID=%s" % zone) 268 269 request.write(";VALUE=DATE-TIME:%04d%02d%02dT%02d%02d%02d\r\n" % end.padded().as_tuple()[:-1]) 270 else: 271 request.write("DTEND;VALUE=DATE:%04d%02d%02d\r\n" % end.next_day().as_date().as_tuple()) 272 273 request.write("SUMMARY:%s\r\n" % getQuotedText(event_summary)) 274 275 # Optional details. 276 277 if event_details.has_key("topics") or event_details.has_key("categories"): 278 request.write("CATEGORIES:%s\r\n" % ",".join( 279 [getQuotedText(topic) for topic in event_details.get("topics") or event_details.get("categories")] 280 )) 281 if event_details.has_key("location"): 282 request.write("LOCATION:%s\r\n" % getQuotedText(event_details["location"])) 283 284 request.write("END:VEVENT\r\n") 285 286 request.write("END:VCALENDAR\r\n") 287 288 elif format == "RSS": 289 290 # Using the page name and the page URL in the title, link and 291 # description. 292 293 request.write('<rss version="2.0">\r\n') 294 request.write('<channel>\r\n') 295 request.write('<title>%s</title>\r\n' % request.getPathinfo()[1:]) 296 request.write('<link>%s%s</link>\r\n' % (request.getBaseURL(), request.getPathinfo())) 297 request.write('<description>Events published on %s%s</description>\r\n' % (request.getBaseURL(), request.getPathinfo())) 298 request.write('<lastBuildDate>%s</lastBuildDate>\r\n' % EventAggregatorSupport.getHTTPTimeString(latest_timestamp)) 299 300 # Sort all_shown_events by start date, reversed: 301 # 302 # * event_details are dictionaries, with the "start" entry providing 303 # the start date 304 # 305 # So we use as sorting key the "start" key from the event details. 306 307 ordered_events = EventAggregatorSupport.getOrderedEvents(all_shown_events) 308 ordered_events.reverse() 309 310 for event in ordered_events: 311 event_page = event.getPage() 312 event_details = event.getDetails() 313 314 # Get the summary details. 315 316 event_summary = event.getSummary(parent) 317 link = event_page.getPageURL(request) 318 319 request.write('<item>\r\n') 320 request.write('<title>%s</title>\r\n' % wikiutil.escape(event_summary)) 321 request.write('<link>%s</link>\r\n' % link) 322 323 # Write a description according to the preferred source of 324 # descriptions. 325 326 if descriptions == "page": 327 description = event_details.get("description", "") 328 else: 329 description = event_details["last-comment"] 330 331 request.write('<description>%s</description>\r\n' % wikiutil.escape(description)) 332 333 for topic in event_details.get("topics") or event_details.get("categories") or []: 334 request.write('<category>%s</category>\r\n' % topic) 335 336 request.write('<pubDate>%s</pubDate>\r\n' % EventAggregatorSupport.getHTTPTimeString(event_details["created"])) 337 request.write('<guid>%s#%s</guid>\r\n' % (link, event_details["sequence"])) 338 request.write('</item>\r\n') 339 340 request.write('</channel>\r\n') 341 request.write('</rss>\r\n') 342 343 if EventAggregatorSupport.isMoin15(): 344 raise MoinMoin.util.MoinMoinNoFooter 345 346 # Action function. 347 348 def execute(pagename, request): 349 EventAggregatorSummary(pagename, request).render() 350 351 # vim: tabstop=4 expandtab shiftwidth=4