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