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