1 # -*- coding: iso-8859-1 -*- 2 """ 3 MoinMoin - EventAggregator event formatting 4 5 @copyright: 2008, 2009, 2010, 2011, 2012, 2013, 2014 by Paul Boddie <paul@boddie.org.uk> 6 @copyright: 2000-2004 Juergen Hermann <jh@web.de>, 7 2005-2008 MoinMoin:ThomasWaldmann. 8 @license: GNU GPL (v2 or later), see COPYING.txt for details. 9 """ 10 11 from MoinSupport import * 12 from MoinMoin.wikiutil import escape 13 14 try: 15 import vCalendar 16 except ImportError: 17 vCalendar = None 18 19 # Event-only formatting. 20 21 def formatEvent(event, request, fmt, write=None, parser_cls=None): 22 23 """ 24 Format the given 'event' using the 'request' and formatter 'fmt'. If the 25 'write' parameter is specified, use it to write output. 26 27 Where 'parser_cls' is specified, override the parser used to format text. 28 This is essential when dealing with calendar format pages since the page 29 parser will be unable to handle arbitrary fragments of text. 30 """ 31 32 details = event.getDetails() 33 raw_details = event.getRawDetails() 34 write = write or request.write 35 36 if details.has_key("fragment"): 37 write(fmt.anchordef(details["fragment"])) 38 39 # Promote any title to a heading above the event details. 40 41 if raw_details.has_key("title"): 42 write(formatText(raw_details["title"], request, fmt, parser_cls=parser_cls)) 43 elif details.has_key("title"): 44 write(fmt.heading(on=1, depth=1)) 45 write(fmt.text(details["title"])) 46 write(fmt.heading(on=0, depth=1)) 47 48 # Produce a definition list for the rest of the details. 49 50 write(fmt.definition_list(on=1)) 51 52 for term in event.all_terms: 53 if term == "title": 54 continue 55 56 raw_value = raw_details.get(term) 57 value = details.get(term) 58 59 if raw_value or value: 60 write(fmt.definition_term(on=1)) 61 write(fmt.text(term)) 62 write(fmt.definition_term(on=0)) 63 write(fmt.definition_desc(on=1)) 64 65 # Try and use the raw details, if available. 66 67 if raw_value: 68 write(formatText(raw_value, request, fmt, parser_cls=parser_cls)) 69 70 # Otherwise, format the processed details. 71 72 else: 73 if term in event.list_terms: 74 write(", ".join([formatText(unicode(v), request, fmt, parser_cls=parser_cls) for v in value])) 75 else: 76 write(fmt.text(unicode(value))) 77 78 write(fmt.definition_desc(on=0)) 79 80 write(fmt.definition_list(on=0)) 81 82 def formatMethod(resource, request, fmt, write=None): 83 84 "Where the 'resource' has a method, offer iTIP actions." 85 86 method = resource.getMethod() 87 88 if method: 89 _ = request.getText 90 write = write or request.write 91 write(fmt.paragraph(on=1)) 92 write(fmt.span(on=1)) 93 write(fmt.text(_("iTIP method"))) 94 write(fmt.span(on=0)) 95 write(" ") 96 write(fmt.span(on=1)) 97 write(fmt.text(method)) 98 write(fmt.span(on=0)) 99 write(fmt.paragraph(on=0)) 100 101 def formatEventsForOutputType(events, request, mimetype, parent=None, descriptions=None, latest_timestamp=None, write=None, resource=None): 102 103 """ 104 Format the given 'events' using the 'request' for the given 'mimetype'. 105 106 The optional 'parent' indicates the "natural" parent page of the events. Any 107 event pages residing beneath the parent page will have their names 108 reproduced as relative to the parent page. 109 110 The optional 'descriptions' indicates the nature of any description given 111 for events in the output resource. 112 113 The optional 'latest_timestamp' indicates the timestamp of the latest edit 114 of the page or event collection. 115 116 If the 'write' parameter is specified, use it to write output. 117 118 If the 'resource' parameter is specified, use it to write any resource-level 119 information such as iTIP method information. 120 """ 121 122 write = write or request.write 123 124 # Start the collection. 125 126 if mimetype == "text/calendar" and vCalendar is not None: 127 _write = vCalendar.iterwrite(write=write).write 128 _write("BEGIN", {}, "VCALENDAR") 129 _write("PRODID", {}, "-//MoinMoin//EventAggregatorSummary") 130 _write("VERSION", {}, "2.0") 131 132 elif mimetype == "application/rss+xml": 133 134 # Using the page name and the page URL in the title, link and 135 # description. 136 137 path_info = getPathInfo(request) 138 139 write('<rss version="2.0">\n') 140 write('<channel>\n') 141 write('<title>%s</title>\n' % path_info[1:]) 142 write('<link>%s%s</link>\n' % (request.getBaseURL(), path_info)) 143 write('<description>Events published on %s%s</description>\n' % (request.getBaseURL(), path_info)) 144 145 if latest_timestamp is not None: 146 write('<lastBuildDate>%s</lastBuildDate>\n' % latest_timestamp.as_HTTP_datetime_string()) 147 148 # Sort the events by start date, reversed. 149 150 ordered_events = getOrderedEvents(events) 151 ordered_events.reverse() 152 events = ordered_events 153 154 elif mimetype == "text/html": 155 write('<html>') 156 write('<body>') 157 158 # Output the collection one by one. 159 160 for event in events: 161 formatEventForOutputType(event, request, mimetype, parent, descriptions, write, resource) 162 163 # End the collection. 164 165 if mimetype == "text/calendar" and vCalendar is not None: 166 _write("END", {}, "VCALENDAR") 167 168 elif mimetype == "application/rss+xml": 169 write('</channel>\n') 170 write('</rss>\n') 171 172 elif mimetype == "text/html": 173 write('</body>') 174 write('</html>') 175 176 def formatEventForOutputType(event, request, mimetype, parent=None, descriptions=None, write=None, resource=None): 177 178 """ 179 Format the given 'event' using the 'request' for the given 'mimetype'. 180 181 The optional 'parent' indicates the "natural" parent page of the events. Any 182 event pages residing beneath the parent page will have their names 183 reproduced as relative to the parent page. 184 185 The optional 'descriptions' indicates the nature of any description given 186 for events in the output resource. 187 188 If the 'write' parameter is specified, use it to write output. 189 190 If the 'resource' parameter is specified, use it to write any resource-level 191 information such as iTIP method information. 192 """ 193 194 write = write or request.write 195 event_details = event.getDetails() 196 event_metadata = event.getMetadata() 197 198 if mimetype == "text/calendar" and vCalendar is not None: 199 200 # NOTE: A custom formatter making attributes for links and plain 201 # NOTE: text for values could be employed here. 202 203 _write = vCalendar.iterwrite(write=write).write 204 205 # Get the summary details. 206 207 event_summary = event.getSummary(parent) 208 link = event.getEventURL() 209 210 # Output the event details. 211 212 _write("BEGIN", {}, "VEVENT") 213 _write("UID", {}, link) 214 _write("URL", {}, link) 215 _write("DTSTAMP", {}, "%04d%02d%02dT%02d%02d%02dZ" % event_metadata["created"].as_tuple()[:6]) 216 _write("LAST-MODIFIED", {}, "%04d%02d%02dT%02d%02d%02dZ" % event_metadata["last-modified"].as_tuple()[:6]) 217 _write("SEQUENCE", {}, "%d" % event_metadata["sequence"]) 218 219 start = event_details["start"] 220 end = event_details["end"] 221 222 if isinstance(start, DateTime): 223 params, value = getCalendarDateTime(start) 224 else: 225 params, value = {"VALUE" : "DATE"}, "%04d%02d%02d" % start.as_date().as_tuple() 226 _write("DTSTART", params, value) 227 228 if isinstance(end, DateTime): 229 params, value = getCalendarDateTime(end) 230 else: 231 params, value = {"VALUE" : "DATE"}, "%04d%02d%02d" % end.next_day().as_date().as_tuple() 232 _write("DTEND", params, value) 233 234 _write("SUMMARY", {}, event_summary) 235 236 # Optional details. 237 238 if event_details.get("topics") or event_details.get("categories"): 239 _write("CATEGORIES", {}, event_details.get("topics") or event_details.get("categories")) 240 if event_details.has_key("location"): 241 _write("LOCATION", {}, event_details["location"]) 242 if event_details.has_key("geo"): 243 _write("GEO", {}, tuple([str(ref.to_degrees()) for ref in event_details["geo"]])) 244 245 _write("END", {}, "VEVENT") 246 247 elif mimetype == "application/rss+xml": 248 249 event_page = event.getPage() 250 event_details = event.getDetails() 251 252 # Get a parser and formatter for the formatting of some attributes. 253 254 fmt = request.html_formatter 255 256 # Get the summary details. 257 258 event_summary = event.getSummary(parent) 259 link = event.getEventURL() 260 261 write('<item>\n') 262 write('<title>%s</title>\n' % escape(event_summary)) 263 write('<link>%s</link>\n' % link) 264 265 # Write a description according to the preferred source of 266 # descriptions. 267 268 if descriptions == "page": 269 description = event_details.get("description", "") 270 else: 271 description = event_metadata["last-comment"] 272 273 write('<description>%s</description>\n' % 274 fmt.text(event_page.formatText(description, fmt))) 275 276 for topic in event_details.get("topics") or event_details.get("categories") or []: 277 write('<category>%s</category>\n' % 278 fmt.text(event_page.formatText(topic, fmt))) 279 280 write('<pubDate>%s</pubDate>\n' % event_metadata["created"].as_HTTP_datetime_string()) 281 write('<guid>%s#%s</guid>\n' % (link, event_metadata["sequence"])) 282 write('</item>\n') 283 284 elif mimetype == "text/html": 285 fmt = request.html_formatter 286 fmt.setPage(request.page) 287 formatEvent(event, request, fmt, write=write) 288 if resource: 289 formatMethod(resource, request, fmt, write) 290 291 # iCalendar format helper functions. 292 293 def getCalendarDateTime(datetime): 294 295 """ 296 Write to the given 'request' the 'datetime' using appropriate time zone 297 information. 298 """ 299 300 utc_datetime = datetime.to_utc() 301 if utc_datetime: 302 return {"VALUE" : "DATE-TIME"}, "%04d%02d%02dT%02d%02d%02dZ" % utc_datetime.padded().as_tuple()[:-1] 303 else: 304 zone = datetime.time_zone() 305 params = {"VALUE" : "DATE-TIME"} 306 if zone: 307 params["TZID"] = zone 308 return params, "%04d%02d%02dT%02d%02d%02d" % datetime.padded().as_tuple()[:-1] 309 310 # Helper functions. 311 312 def getOrderedEvents(events): 313 314 """ 315 Return a list with the given 'events' ordered according to their start and 316 end dates. 317 """ 318 319 ordered_events = events[:] 320 ordered_events.sort() 321 return ordered_events 322 323 # vim: tabstop=4 expandtab shiftwidth=4