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