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 formatEventsForOutputType(events, request, mimetype, parent=None, descriptions=None, latest_timestamp=None, write=None): 83 84 """ 85 Format the given 'events' using the 'request' for the given 'mimetype'. 86 87 The optional 'parent' indicates the "natural" parent page of the events. Any 88 event pages residing beneath the parent page will have their names 89 reproduced as relative to the parent page. 90 91 The optional 'descriptions' indicates the nature of any description given 92 for events in the output resource. 93 94 The optional 'latest_timestamp' indicates the timestamp of the latest edit 95 of the page or event collection. 96 97 If the 'write' parameter is specified, use it to write output. 98 """ 99 100 write = write or request.write 101 102 # Start the collection. 103 104 if mimetype == "text/calendar" and vCalendar is not None: 105 _write = vCalendar.iterwrite(write=write).write 106 _write("BEGIN", {}, "VCALENDAR") 107 _write("PRODID", {}, "-//MoinMoin//EventAggregatorSummary") 108 _write("VERSION", {}, "2.0") 109 110 elif mimetype == "application/rss+xml": 111 112 # Using the page name and the page URL in the title, link and 113 # description. 114 115 path_info = getPathInfo(request) 116 117 write('<rss version="2.0">\n') 118 write('<channel>\n') 119 write('<title>%s</title>\n' % path_info[1:]) 120 write('<link>%s%s</link>\n' % (request.getBaseURL(), path_info)) 121 write('<description>Events published on %s%s</description>\n' % (request.getBaseURL(), path_info)) 122 123 if latest_timestamp is not None: 124 write('<lastBuildDate>%s</lastBuildDate>\n' % latest_timestamp.as_HTTP_datetime_string()) 125 126 # Sort the events by start date, reversed. 127 128 ordered_events = getOrderedEvents(events) 129 ordered_events.reverse() 130 events = ordered_events 131 132 elif mimetype == "text/html": 133 write('<html>') 134 write('<body>') 135 136 # Output the collection one by one. 137 138 for event in events: 139 formatEventForOutputType(event, request, mimetype, parent, descriptions, write) 140 141 # End the collection. 142 143 if mimetype == "text/calendar" and vCalendar is not None: 144 _write("END", {}, "VCALENDAR") 145 146 elif mimetype == "application/rss+xml": 147 write('</channel>\n') 148 write('</rss>\n') 149 150 elif mimetype == "text/html": 151 write('</body>') 152 write('</html>') 153 154 def formatEventForOutputType(event, request, mimetype, parent=None, descriptions=None, write=None): 155 156 """ 157 Format the given 'event' using the 'request' for the given 'mimetype'. 158 159 The optional 'parent' indicates the "natural" parent page of the events. Any 160 event pages residing beneath the parent page will have their names 161 reproduced as relative to the parent page. 162 163 The optional 'descriptions' indicates the nature of any description given 164 for events in the output resource. 165 166 If the 'write' parameter is specified, use it to write output. 167 """ 168 169 write = write or request.write 170 event_details = event.getDetails() 171 event_metadata = event.getMetadata() 172 173 if mimetype == "text/calendar" and vCalendar is not None: 174 175 # NOTE: A custom formatter making attributes for links and plain 176 # NOTE: text for values could be employed here. 177 178 _write = vCalendar.iterwrite(write=write).write 179 180 # Get the summary details. 181 182 event_summary = event.getSummary(parent) 183 link = event.getEventURL() 184 185 # Output the event details. 186 187 _write("BEGIN", {}, "VEVENT") 188 _write("UID", {}, link) 189 _write("URL", {}, link) 190 _write("DTSTAMP", {}, "%04d%02d%02dT%02d%02d%02dZ" % event_metadata["created"].as_tuple()[:6]) 191 _write("LAST-MODIFIED", {}, "%04d%02d%02dT%02d%02d%02dZ" % event_metadata["last-modified"].as_tuple()[:6]) 192 _write("SEQUENCE", {}, "%d" % event_metadata["sequence"]) 193 194 start = event_details["start"] 195 end = event_details["end"] 196 197 if isinstance(start, DateTime): 198 params, value = getCalendarDateTime(start) 199 else: 200 params, value = {"VALUE" : "DATE"}, "%04d%02d%02d" % start.as_date().as_tuple() 201 _write("DTSTART", params, value) 202 203 if isinstance(end, DateTime): 204 params, value = getCalendarDateTime(end) 205 else: 206 params, value = {"VALUE" : "DATE"}, "%04d%02d%02d" % end.next_day().as_date().as_tuple() 207 _write("DTEND", params, value) 208 209 _write("SUMMARY", {}, event_summary) 210 211 # Optional details. 212 213 if event_details.get("topics") or event_details.get("categories"): 214 _write("CATEGORIES", {}, event_details.get("topics") or event_details.get("categories")) 215 if event_details.has_key("location"): 216 _write("LOCATION", {}, event_details["location"]) 217 if event_details.has_key("geo"): 218 _write("GEO", {}, tuple([str(ref.to_degrees()) for ref in event_details["geo"]])) 219 220 _write("END", {}, "VEVENT") 221 222 elif mimetype == "application/rss+xml": 223 224 event_page = event.getPage() 225 event_details = event.getDetails() 226 227 # Get a parser and formatter for the formatting of some attributes. 228 229 fmt = request.html_formatter 230 231 # Get the summary details. 232 233 event_summary = event.getSummary(parent) 234 link = event.getEventURL() 235 236 write('<item>\n') 237 write('<title>%s</title>\n' % escape(event_summary)) 238 write('<link>%s</link>\n' % link) 239 240 # Write a description according to the preferred source of 241 # descriptions. 242 243 if descriptions == "page": 244 description = event_details.get("description", "") 245 else: 246 description = event_metadata["last-comment"] 247 248 write('<description>%s</description>\n' % 249 fmt.text(event_page.formatText(description, fmt))) 250 251 for topic in event_details.get("topics") or event_details.get("categories") or []: 252 write('<category>%s</category>\n' % 253 fmt.text(event_page.formatText(topic, fmt))) 254 255 write('<pubDate>%s</pubDate>\n' % event_metadata["created"].as_HTTP_datetime_string()) 256 write('<guid>%s#%s</guid>\n' % (link, event_metadata["sequence"])) 257 write('</item>\n') 258 259 elif mimetype == "text/html": 260 fmt = request.html_formatter 261 fmt.setPage(request.page) 262 formatEvent(event, request, fmt, write=write) 263 264 # iCalendar format helper functions. 265 266 def getCalendarDateTime(datetime): 267 268 """ 269 Write to the given 'request' the 'datetime' using appropriate time zone 270 information. 271 """ 272 273 utc_datetime = datetime.to_utc() 274 if utc_datetime: 275 return {"VALUE" : "DATE-TIME"}, "%04d%02d%02dT%02d%02d%02dZ" % utc_datetime.padded().as_tuple()[:-1] 276 else: 277 zone = datetime.time_zone() 278 params = {"VALUE" : "DATE-TIME"} 279 if zone: 280 params["TZID"] = zone 281 return params, "%04d%02d%02dT%02d%02d%02d" % datetime.padded().as_tuple()[:-1] 282 283 # Helper functions. 284 285 def getOrderedEvents(events): 286 287 """ 288 Return a list with the given 'events' ordered according to their start and 289 end dates. 290 """ 291 292 ordered_events = events[:] 293 ordered_events.sort() 294 return ordered_events 295 296 # vim: tabstop=4 expandtab shiftwidth=4