1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/EventAggregatorSupport/Formatting.py Tue Apr 30 23:49:32 2013 +0200
1.3 @@ -0,0 +1,292 @@
1.4 +# -*- coding: iso-8859-1 -*-
1.5 +"""
1.6 + MoinMoin - EventAggregator event formatting
1.7 +
1.8 + @copyright: 2008, 2009, 2010, 2011, 2012, 2013 by Paul Boddie <paul@boddie.org.uk>
1.9 + @copyright: 2000-2004 Juergen Hermann <jh@web.de>,
1.10 + 2005-2008 MoinMoin:ThomasWaldmann.
1.11 + @license: GNU GPL (v2 or later), see COPYING.txt for details.
1.12 +"""
1.13 +
1.14 +from MoinSupport import *
1.15 +from MoinMoin.wikiutil import escape
1.16 +
1.17 +try:
1.18 + import vCalendar
1.19 +except ImportError:
1.20 + vCalendar = None
1.21 +
1.22 +# Event-only formatting.
1.23 +
1.24 +def formatEvent(event, request, fmt, write=None):
1.25 +
1.26 + """
1.27 + Format the given 'event' using the 'request' and formatter 'fmt'. If the
1.28 + 'write' parameter is specified, use it to write output.
1.29 + """
1.30 +
1.31 + details = event.getDetails()
1.32 + raw_details = event.getRawDetails()
1.33 + write = write or request.write
1.34 +
1.35 + if details.has_key("fragment"):
1.36 + write(fmt.anchordef(details["fragment"]))
1.37 +
1.38 + # Promote any title to a heading above the event details.
1.39 +
1.40 + if raw_details.has_key("title"):
1.41 + write(formatText(raw_details["title"], request, fmt))
1.42 + elif details.has_key("title"):
1.43 + write(fmt.heading(on=1, depth=1))
1.44 + write(fmt.text(details["title"]))
1.45 + write(fmt.heading(on=0, depth=1))
1.46 +
1.47 + # Produce a definition list for the rest of the details.
1.48 +
1.49 + write(fmt.definition_list(on=1))
1.50 +
1.51 + for term in event.all_terms:
1.52 + if term == "title":
1.53 + continue
1.54 +
1.55 + raw_value = raw_details.get(term)
1.56 + value = details.get(term)
1.57 +
1.58 + if raw_value or value:
1.59 + write(fmt.definition_term(on=1))
1.60 + write(fmt.text(term))
1.61 + write(fmt.definition_term(on=0))
1.62 + write(fmt.definition_desc(on=1))
1.63 +
1.64 + # Try and use the raw details, if available.
1.65 +
1.66 + if raw_value:
1.67 + write(formatText(raw_value, request, fmt))
1.68 +
1.69 + # Otherwise, format the processed details.
1.70 +
1.71 + else:
1.72 + if term in event.list_terms:
1.73 + write(", ".join([formatText(str(v), request, fmt) for v in value]))
1.74 + else:
1.75 + write(fmt.text(str(value)))
1.76 +
1.77 + write(fmt.definition_desc(on=0))
1.78 +
1.79 + write(fmt.definition_list(on=0))
1.80 +
1.81 +def formatEventsForOutputType(events, request, mimetype, parent=None, descriptions=None, latest_timestamp=None, write=None):
1.82 +
1.83 + """
1.84 + Format the given 'events' using the 'request' for the given 'mimetype'.
1.85 +
1.86 + The optional 'parent' indicates the "natural" parent page of the events. Any
1.87 + event pages residing beneath the parent page will have their names
1.88 + reproduced as relative to the parent page.
1.89 +
1.90 + The optional 'descriptions' indicates the nature of any description given
1.91 + for events in the output resource.
1.92 +
1.93 + The optional 'latest_timestamp' indicates the timestamp of the latest edit
1.94 + of the page or event collection.
1.95 +
1.96 + If the 'write' parameter is specified, use it to write output.
1.97 + """
1.98 +
1.99 + write = write or request.write
1.100 +
1.101 + # Start the collection.
1.102 +
1.103 + if mimetype == "text/calendar" and vCalendar is not None:
1.104 + write = vCalendar.iterwrite(write=write).write
1.105 + write("BEGIN", {}, "VCALENDAR")
1.106 + write("PRODID", {}, "-//MoinMoin//EventAggregatorSummary")
1.107 + write("VERSION", {}, "2.0")
1.108 +
1.109 + elif mimetype == "application/rss+xml":
1.110 +
1.111 + # Using the page name and the page URL in the title, link and
1.112 + # description.
1.113 +
1.114 + path_info = getPathInfo(request)
1.115 +
1.116 + write('<rss version="2.0">\r\n')
1.117 + write('<channel>\r\n')
1.118 + write('<title>%s</title>\r\n' % path_info[1:])
1.119 + write('<link>%s%s</link>\r\n' % (request.getBaseURL(), path_info))
1.120 + write('<description>Events published on %s%s</description>\r\n' % (request.getBaseURL(), path_info))
1.121 +
1.122 + if latest_timestamp is not None:
1.123 + write('<lastBuildDate>%s</lastBuildDate>\r\n' % latest_timestamp.as_HTTP_datetime_string())
1.124 +
1.125 + # Sort the events by start date, reversed.
1.126 +
1.127 + ordered_events = getOrderedEvents(events)
1.128 + ordered_events.reverse()
1.129 + events = ordered_events
1.130 +
1.131 + elif mimetype == "text/html":
1.132 + write('<html>')
1.133 + write('<body>')
1.134 +
1.135 + # Output the collection one by one.
1.136 +
1.137 + for event in events:
1.138 + formatEventForOutputType(event, request, mimetype, parent, descriptions)
1.139 +
1.140 + # End the collection.
1.141 +
1.142 + if mimetype == "text/calendar" and vCalendar is not None:
1.143 + write("END", {}, "VCALENDAR")
1.144 +
1.145 + elif mimetype == "application/rss+xml":
1.146 + write('</channel>\r\n')
1.147 + write('</rss>\r\n')
1.148 +
1.149 + elif mimetype == "text/html":
1.150 + write('</body>')
1.151 + write('</html>')
1.152 +
1.153 +def formatEventForOutputType(event, request, mimetype, parent=None, descriptions=None, write=None):
1.154 +
1.155 + """
1.156 + Format the given 'event' using the 'request' for the given 'mimetype'.
1.157 +
1.158 + The optional 'parent' indicates the "natural" parent page of the events. Any
1.159 + event pages residing beneath the parent page will have their names
1.160 + reproduced as relative to the parent page.
1.161 +
1.162 + The optional 'descriptions' indicates the nature of any description given
1.163 + for events in the output resource.
1.164 +
1.165 + If the 'write' parameter is specified, use it to write output.
1.166 + """
1.167 +
1.168 + write = write or request.write
1.169 + event_details = event.getDetails()
1.170 + event_metadata = event.getMetadata()
1.171 +
1.172 + if mimetype == "text/calendar" and vCalendar is not None:
1.173 +
1.174 + # NOTE: A custom formatter making attributes for links and plain
1.175 + # NOTE: text for values could be employed here.
1.176 +
1.177 + write = vCalendar.iterwrite(write=write).write
1.178 +
1.179 + # Get the summary details.
1.180 +
1.181 + event_summary = event.getSummary(parent)
1.182 + link = event.getEventURL()
1.183 +
1.184 + # Output the event details.
1.185 +
1.186 + write("BEGIN", {}, "VEVENT")
1.187 + write("UID", {}, link)
1.188 + write("URL", {}, link)
1.189 + write("DTSTAMP", {}, "%04d%02d%02dT%02d%02d%02dZ" % event_metadata["created"].as_tuple()[:6])
1.190 + write("LAST-MODIFIED", {}, "%04d%02d%02dT%02d%02d%02dZ" % event_metadata["last-modified"].as_tuple()[:6])
1.191 + write("SEQUENCE", {}, "%d" % event_metadata["sequence"])
1.192 +
1.193 + start = event_details["start"]
1.194 + end = event_details["end"]
1.195 +
1.196 + if isinstance(start, DateTime):
1.197 + params, value = getCalendarDateTime(start)
1.198 + else:
1.199 + params, value = {"VALUE" : "DATE"}, "%04d%02d%02d" % start.as_date().as_tuple()
1.200 + write("DTSTART", params, value)
1.201 +
1.202 + if isinstance(end, DateTime):
1.203 + params, value = getCalendarDateTime(end)
1.204 + else:
1.205 + params, value = {"VALUE" : "DATE"}, "%04d%02d%02d" % end.next_day().as_date().as_tuple()
1.206 + write("DTEND", params, value)
1.207 +
1.208 + write("SUMMARY", {}, event_summary)
1.209 +
1.210 + # Optional details.
1.211 +
1.212 + if event_details.get("topics") or event_details.get("categories"):
1.213 + write("CATEGORIES", {}, event_details.get("topics") or event_details.get("categories"))
1.214 + if event_details.has_key("location"):
1.215 + write("LOCATION", {}, event_details["location"])
1.216 + if event_details.has_key("geo"):
1.217 + write("GEO", {}, tuple([str(ref.to_degrees()) for ref in event_details["geo"]]))
1.218 +
1.219 + write("END", {}, "VEVENT")
1.220 +
1.221 + elif mimetype == "application/rss+xml":
1.222 +
1.223 + event_page = event.getPage()
1.224 + event_details = event.getDetails()
1.225 +
1.226 + # Get a parser and formatter for the formatting of some attributes.
1.227 +
1.228 + fmt = request.html_formatter
1.229 +
1.230 + # Get the summary details.
1.231 +
1.232 + event_summary = event.getSummary(parent)
1.233 + link = event.getEventURL()
1.234 +
1.235 + write('<item>\r\n')
1.236 + write('<title>%s</title>\r\n' % escape(event_summary))
1.237 + write('<link>%s</link>\r\n' % link)
1.238 +
1.239 + # Write a description according to the preferred source of
1.240 + # descriptions.
1.241 +
1.242 + if descriptions == "page":
1.243 + description = event_details.get("description", "")
1.244 + else:
1.245 + description = event_metadata["last-comment"]
1.246 +
1.247 + write('<description>%s</description>\r\n' %
1.248 + fmt.text(event_page.formatText(description, fmt)))
1.249 +
1.250 + for topic in event_details.get("topics") or event_details.get("categories") or []:
1.251 + write('<category>%s</category>\r\n' %
1.252 + fmt.text(event_page.formatText(topic, fmt)))
1.253 +
1.254 + write('<pubDate>%s</pubDate>\r\n' % event_metadata["created"].as_HTTP_datetime_string())
1.255 + write('<guid>%s#%s</guid>\r\n' % (link, event_metadata["sequence"]))
1.256 + write('</item>\r\n')
1.257 +
1.258 + elif mimetype == "text/html":
1.259 + fmt = request.html_formatter
1.260 + fmt.setPage(request.page)
1.261 + formatEvent(event, request, fmt, write=write)
1.262 +
1.263 +# iCalendar format helper functions.
1.264 +
1.265 +def getCalendarDateTime(datetime):
1.266 +
1.267 + """
1.268 + Write to the given 'request' the 'datetime' using appropriate time zone
1.269 + information.
1.270 + """
1.271 +
1.272 + utc_datetime = datetime.to_utc()
1.273 + if utc_datetime:
1.274 + return {"VALUE" : "DATE-TIME"}, "%04d%02d%02dT%02d%02d%02dZ" % utc_datetime.padded().as_tuple()[:-1]
1.275 + else:
1.276 + zone = datetime.time_zone()
1.277 + params = {"VALUE" : "DATE-TIME"}
1.278 + if zone:
1.279 + params["TZID"] = zone
1.280 + return params, "%04d%02d%02dT%02d%02d%02d" % datetime.padded().as_tuple()[:-1]
1.281 +
1.282 +# Helper functions.
1.283 +
1.284 +def getOrderedEvents(events):
1.285 +
1.286 + """
1.287 + Return a list with the given 'events' ordered according to their start and
1.288 + end dates.
1.289 + """
1.290 +
1.291 + ordered_events = events[:]
1.292 + ordered_events.sort()
1.293 + return ordered_events
1.294 +
1.295 +# vim: tabstop=4 expandtab shiftwidth=4