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