1 # -*- coding: iso-8859-1 -*- 2 """ 3 MoinMoin - EventAggregator resource acquisition and access 4 5 @copyright: 2008, 2009, 2010, 2011, 2012, 2013, 2014 by Paul Boddie <paul@boddie.org.uk> 6 @license: GNU GPL (v2 or later), see COPYING.txt for details. 7 """ 8 9 from EventAggregatorSupport.Filter import * 10 from EventAggregatorSupport.Types import * 11 12 from DateSupport import Date, Month 13 from MoinSupport import * 14 from MoinRemoteSupport import getCachedResource, getCachedResourceMetadata, imapreader 15 from ItemSupport import ItemStore 16 17 from MoinMoin.Page import Page 18 19 import urllib 20 21 try: 22 from cStringIO import StringIO 23 except ImportError: 24 from StringIO import StringIO 25 26 # Obtaining event containers and events from such containers. 27 28 def getEventPages(pages): 29 30 "Return a list of events found on the given 'pages'." 31 32 # Get event pages instead of result pages. 33 34 return EventResourceCollection(map(EventPage, pages)) 35 36 def getAllEventSources(request): 37 38 "Return all event sources defined in the Wiki using the 'request'." 39 40 sources_page = getattr(request.cfg, "event_aggregator_sources_page", "EventSourcesDict") 41 42 # Remote sources are accessed via dictionary page definitions. 43 44 return getWikiDict(sources_page, request) 45 46 def getEventResources(sources, calendar_start, calendar_end, request): 47 48 """ 49 Return resource objects for the given 'sources' using the given 50 'calendar_start' and 'calendar_end' to parameterise requests to the sources, 51 and the 'request' to access configuration settings in the Wiki. 52 """ 53 54 sources_dict = getAllEventSources(request) 55 if not sources_dict: 56 return [] 57 58 # Use dates for the calendar limits. 59 60 if isinstance(calendar_start, Date): 61 pass 62 elif isinstance(calendar_start, Month): 63 calendar_start = calendar_start.as_date(1) 64 65 if isinstance(calendar_end, Date): 66 pass 67 elif isinstance(calendar_end, Month): 68 calendar_end = calendar_end.as_date(-1) 69 70 resources = EventResourceCollection() 71 72 for source in sources: 73 details = sources_dict[source].split() 74 type = details[0] 75 resource = None 76 77 # Support non-URL sources. 78 79 if type == "store": 80 details = details[1:] 81 82 details.extend([None] * 3) 83 page_name, store_name, lockdir_name = details[:3] 84 85 resource = getEventResourcesFromStore(page_name, store_name, lockdir_name, request) 86 87 # Unrecognised types are treated as URLs. 88 89 else: 90 if type == "url": 91 details = details[1:] 92 93 details.extend([None] * 3) 94 url, format, expected_content_type = details[:3] 95 format = format or "ical" 96 97 resource = getEventResourcesFromSource(url, format, expected_content_type, calendar_start, calendar_end, request) 98 99 if resource: 100 resources.append(resource) 101 102 return resources 103 104 def getEventResourcesFromStore(page_name, store_name, lockdir_name, request): 105 106 """ 107 Return a resource object for the store indicated by the given 'page_name' 108 and 'store_name' (with the accompanying 'lockdir_name', specified as None 109 for a computed name to be used). 110 """ 111 112 page = Page(request, page_name) 113 store = ItemStore(page, store_name, lockdir_name) 114 return parseEventsInStore(store, getPageURL(page), request) 115 116 def getEventResourcesFromSource(url, format, expected_content_type, calendar_start, calendar_end, request): 117 118 """ 119 Return a resource object for the given 'url' providing content in the 120 specified 'format', insisting on the 'expected_content_type', and using the 121 given 'calendar_start' and 'calendar_end' to parameterise requests to the 122 sources and the 'request' to access configuration settings in the Wiki. 123 """ 124 125 # Prevent local file access. 126 127 if url.startswith("file:"): 128 return None 129 130 # Support IMAP access. 131 132 elif url.startswith("imap"): 133 reader = imapreader 134 135 # Otherwise, use the default access mechanism. 136 137 else: 138 reader = None 139 140 # Parameterise the URL. 141 # Where other parameters are used, care must be taken to encode them 142 # properly. 143 144 url = url.replace("{start}", urllib.quote_plus(calendar_start and str(calendar_start) or "")) 145 url = url.replace("{end}", urllib.quote_plus(calendar_end and str(calendar_end) or "")) 146 147 # Get a parser. 148 # NOTE: This could be done reactively by choosing a parser based on 149 # NOTE: the content type provided by the URL. 150 151 parser, default_content_type = getParserForFormat(format) 152 required_content_type = expected_content_type or default_content_type 153 154 # Obtain the resource, using a cached version if appropriate. 155 156 max_cache_age = int(getattr(request.cfg, "event_aggregator_max_cache_age", "300")) 157 data = getCachedResource(request, url, "EventAggregator", "wiki", max_cache_age, reader) 158 if not data: 159 return None 160 161 # Process the entry, parsing the content. 162 163 f = StringIO(data) 164 try: 165 # Get the content type and encoding, making sure that the data 166 # can be parsed. 167 168 url, content_type, encoding, metadata = getCachedResourceMetadata(f) 169 170 if content_type != required_content_type: 171 return None 172 173 # Send the data to the parser. 174 175 return parser(f, encoding, url, metadata) 176 177 finally: 178 f.close() 179 180 def getParserForFormat(format): 181 182 "Return a parser and default content type for the given 'format'." 183 184 if format == "ical": 185 return parseEventsInCalendarFromResource, "text/calendar" 186 elif format == "xcal": 187 return parseEventsInXMLCalendarFromResource, "application/calendar+xml" 188 elif format == "mbox": 189 return parseEventsInMailboxFromResource, "multipart/mixed" 190 else: 191 return None, None 192 193 # Page-related functions. 194 195 def fillEventPageFromTemplate(template_page, new_page, event_details, category_pagenames): 196 197 """ 198 Using the given 'template_page', complete the 'new_page' by copying the 199 template and adding the given 'event_details' (a dictionary of event 200 fields), setting also the 'category_pagenames' to define category 201 membership. 202 """ 203 204 event_page = EventPage(template_page) 205 new_event_page = EventPage(new_page) 206 new_event_page.copyPage(event_page) 207 208 if new_event_page.getFormat() == "wiki": 209 new_event = Event(new_event_page, event_details) 210 new_event_page.setEvents([new_event]) 211 new_event_page.setCategoryMembership(category_pagenames) 212 new_event_page.flushEventDetails() 213 214 return new_event_page.getBody() 215 216 # Event selection from request parameters. 217 218 def getEventsUsingParameters(category_names, search_pattern, remote_sources, 219 calendar_start, calendar_end, resolution, request): 220 221 "Get the events according to the resolution of the calendar." 222 223 if search_pattern: 224 results = getPagesForSearch(search_pattern, request) 225 else: 226 results = [] 227 228 results += getAllCategoryPages(category_names, request) 229 pages = getPagesFromResults(results, request) 230 events = getEventPages(pages).getEvents() 231 events += getEventResources(remote_sources, calendar_start, calendar_end, request).getEvents() 232 all_shown_events = getEventsInPeriod(events, getCalendarPeriod(calendar_start, calendar_end)) 233 earliest, latest = getEventLimits(all_shown_events) 234 235 # Get a concrete period of time. 236 237 first, last = getConcretePeriod(calendar_start, calendar_end, earliest, latest, resolution) 238 239 return all_shown_events, first, last 240 241 # vim: tabstop=4 expandtab shiftwidth=4