1.1 --- a/.hgtags Sun Feb 06 02:38:17 2011 +0100
1.2 +++ b/.hgtags Sat Feb 19 03:06:03 2011 +0100
1.3 @@ -5,4 +5,5 @@
1.4 f1f552421d5e3d23b6fe2d53625d264cfe1d182c rel-0-3
1.5 27922520e51f07b289abe18b66537c7481556868 rel-0-4
1.6 0c8421ec5a70dc70e93a773f9812564d837cc575 rel-0-5
1.7 +e1684a6e8a821ac67ae6d591b703edaefbb66b15 rel-0-6
1.8 067b30223eb3db64971ace7dae263e4eae09dce6 rel-0-6-1
2.1 --- a/EventAggregatorSupport.py Sun Feb 06 02:38:17 2011 +0100
2.2 +++ b/EventAggregatorSupport.py Sat Feb 19 03:06:03 2011 +0100
2.3 @@ -31,7 +31,7 @@
2.4 def escattr(s):
2.5 return escape(s, 1)
2.6
2.7 -__version__ = "0.6.1"
2.8 +__version__ = "0.6.2"
2.9
2.10 # Date labels.
2.11
2.12 @@ -80,9 +80,6 @@
2.13
2.14 # Utility functions.
2.15
2.16 -def isMoin15():
2.17 - return version.release.startswith("1.5.")
2.18 -
2.19 def getCategoryPattern(request):
2.20 global category_regexp
2.21
2.22 @@ -102,6 +99,147 @@
2.23 else:
2.24 return int(x)
2.25
2.26 +def sort_none_first(x, y):
2.27 + if x is None:
2.28 + return -1
2.29 + elif y is None:
2.30 + return 1
2.31 + else:
2.32 + return cmp(x, y)
2.33 +
2.34 +# Utility classes and associated functions.
2.35 +
2.36 +class Form:
2.37 +
2.38 + """
2.39 + A wrapper preserving MoinMoin 1.8.x (and earlier) behaviour in a 1.9.x
2.40 + environment.
2.41 + """
2.42 +
2.43 + def __init__(self, form):
2.44 + self.form = form
2.45 +
2.46 + def get(self, name, default=None):
2.47 + values = self.form.getlist(name)
2.48 + if not values:
2.49 + return default
2.50 + else:
2.51 + return values
2.52 +
2.53 + def __getitem__(self, name):
2.54 + return self.form.getlist(name)
2.55 +
2.56 +class ActionSupport:
2.57 +
2.58 + """
2.59 + Work around disruptive MoinMoin changes in 1.9, and also provide useful
2.60 + convenience methods.
2.61 + """
2.62 +
2.63 + def get_form(self):
2.64 + return get_form(self.request)
2.65 +
2.66 + def _get_selected(self, value, input_value):
2.67 +
2.68 + """
2.69 + Return the HTML attribute text indicating selection of an option (or
2.70 + otherwise) if 'value' matches 'input_value'.
2.71 + """
2.72 +
2.73 + return input_value is not None and value == input_value and 'selected="selected"' or ''
2.74 +
2.75 + def _get_selected_for_list(self, value, input_values):
2.76 +
2.77 + """
2.78 + Return the HTML attribute text indicating selection of an option (or
2.79 + otherwise) if 'value' matches one of the 'input_values'.
2.80 + """
2.81 +
2.82 + return value in input_values and 'selected="selected"' or ''
2.83 +
2.84 + def _get_input(self, form, name, default=None):
2.85 +
2.86 + """
2.87 + Return the input from 'form' having the given 'name', returning either
2.88 + the input converted to an integer or the given 'default' (optional, None
2.89 + if not specified).
2.90 + """
2.91 +
2.92 + value = form.get(name, [None])[0]
2.93 + if not value: # true if 0 obtained
2.94 + return default
2.95 + else:
2.96 + return int(value)
2.97 +
2.98 + def get_month_lists(self, default_as_current=0):
2.99 +
2.100 + """
2.101 + Return two lists of HTML element definitions corresponding to the start
2.102 + and end month selection controls, with months selected according to any
2.103 + values that have been specified via request parameters.
2.104 + """
2.105 +
2.106 + _ = self._
2.107 + form = self.get_form()
2.108 +
2.109 + # Initialise month lists.
2.110 +
2.111 + start_month_list = []
2.112 + end_month_list = []
2.113 +
2.114 + start_month = self._get_input(form, "start-month", default_as_current and getCurrentMonth().month() or None)
2.115 + end_month = self._get_input(form, "end-month", start_month)
2.116 +
2.117 + # Prepare month lists, selecting specified months.
2.118 +
2.119 + if not default_as_current:
2.120 + start_month_list.append('<option value=""></option>')
2.121 + end_month_list.append('<option value=""></option>')
2.122 +
2.123 + for month in range(1, 13):
2.124 + month_label = _(getMonthLabel(month))
2.125 + selected = self._get_selected(month, start_month)
2.126 + start_month_list.append('<option value="%02d" %s>%s</option>' % (month, selected, month_label))
2.127 + selected = self._get_selected(month, end_month)
2.128 + end_month_list.append('<option value="%02d" %s>%s</option>' % (month, selected, month_label))
2.129 +
2.130 + return start_month_list, end_month_list
2.131 +
2.132 + def get_year_defaults(self, default_as_current=0):
2.133 +
2.134 + "Return defaults for the start and end years."
2.135 +
2.136 + form = self.get_form()
2.137 +
2.138 + start_year_default = form.get("start-year", [default_as_current and getCurrentYear() or ""])[0]
2.139 + end_year_default = form.get("end-year", [default_as_current and start_year_default or ""])[0]
2.140 +
2.141 + return start_year_default, end_year_default
2.142 +
2.143 +def get_form(request):
2.144 +
2.145 + "Work around disruptive MoinMoin changes in 1.9."
2.146 +
2.147 + if hasattr(request, "values"):
2.148 + return Form(request.values)
2.149 + else:
2.150 + return request.form
2.151 +
2.152 +class send_headers:
2.153 +
2.154 + """
2.155 + A wrapper to preserve MoinMoin 1.8.x (and earlier) request behaviour in a
2.156 + 1.9.x environment.
2.157 + """
2.158 +
2.159 + def __init__(self, request):
2.160 + self.request = request
2.161 +
2.162 + def __call__(self, headers):
2.163 + for header in headers:
2.164 + parts = header.split(":")
2.165 + self.request.headers.add(parts[0], ":".join(parts[1:]))
2.166 +
2.167 # Textual representations.
2.168
2.169 def getHTTPTimeString(tmtuple):
2.170 @@ -214,11 +352,7 @@
2.171 """
2.172
2.173 query = search.QueryParser().parse_query('category:%s' % pagename)
2.174 - if isMoin15():
2.175 - results = search.searchPages(request, query)
2.176 - results.sortByPagename()
2.177 - else:
2.178 - results = search.searchPages(request, query, "page_name")
2.179 + results = search.searchPages(request, query, "page_name")
2.180
2.181 cat_pattern = getCategoryPattern(request)
2.182 pages = []
2.183 @@ -249,21 +383,13 @@
2.184
2.185 "Using 'request', return the URL of this page."
2.186
2.187 - page = self.page
2.188 -
2.189 - if isMoin15():
2.190 - return request.getQualifiedURL(page.url(request))
2.191 - else:
2.192 - return request.getQualifiedURL(page.url(request, relative=0))
2.193 + return request.getQualifiedURL(self.page.url(request, relative=0))
2.194
2.195 def getFormat(self):
2.196
2.197 "Get the format used on this page."
2.198
2.199 - if isMoin15():
2.200 - return "wiki" # page.pi_format
2.201 - else:
2.202 - return self.page.pi["format"]
2.203 + return self.page.pi["format"]
2.204
2.205 def getRevisions(self):
2.206
2.207 @@ -658,7 +784,7 @@
2.208 Using 'request', set timestamp details in the details dictionary of each of
2.209 the 'events'.
2.210
2.211 - Retutn the latest timestamp found.
2.212 + Return the latest timestamp found.
2.213 """
2.214
2.215 latest = None
2.216 @@ -728,6 +854,10 @@
2.217
2.218 return min(first, last), last
2.219
2.220 +# NOTE: Support coverage using times within days. This will involve timespan
2.221 +# NOTE: objects which can be compared in such a way that set operations will be
2.222 +# NOTE: able to detect overlapping periods.
2.223 +
2.224 def getCoverage(start, end, events):
2.225
2.226 """
2.227 @@ -737,7 +867,7 @@
2.228 of the form (set of covered days, events).
2.229 """
2.230
2.231 - all_events = []
2.232 + all_events = {}
2.233 full_coverage = set()
2.234
2.235 # Get event details.
2.236 @@ -754,28 +884,39 @@
2.237 event_start = max(event_details["start"], start)
2.238 event_end = min(event_details["end"], end)
2.239 event_coverage = set(event_start.days_until(event_end))
2.240 + event_location = event_details.get("location")
2.241
2.242 # Update the overall coverage.
2.243
2.244 full_coverage.update(event_coverage)
2.245
2.246 - # Try and fit the event into the events list.
2.247 -
2.248 - for i, (coverage, covered_events) in enumerate(all_events):
2.249 -
2.250 - # Where the event does not overlap with the current
2.251 - # element, add it alongside existing events.
2.252 + # Add a new events list for a new location.
2.253 + # Locations can be unspecified, thus None refers to all unlocalised
2.254 + # events.
2.255
2.256 - if not coverage.intersection(event_coverage):
2.257 - covered_events.append(event)
2.258 - all_events[i] = coverage.union(event_coverage), covered_events
2.259 - break
2.260 + if not all_events.has_key(event_location):
2.261 + all_events[event_location] = [(event_coverage, [event])]
2.262
2.263 - # Make a new element in the list if the event cannot be
2.264 - # marked alongside existing events.
2.265 + # Try and fit the event into an events list.
2.266
2.267 else:
2.268 - all_events.append((event_coverage, [event]))
2.269 + slot = all_events[event_location]
2.270 +
2.271 + for i, (coverage, covered_events) in enumerate(slot):
2.272 +
2.273 + # Where the event does not overlap with the current
2.274 + # element, add it alongside existing events.
2.275 +
2.276 + if not coverage.intersection(event_coverage):
2.277 + covered_events.append(event)
2.278 + slot[i] = coverage.union(event_coverage), covered_events
2.279 + break
2.280 +
2.281 + # Make a new element in the list if the event cannot be
2.282 + # marked alongside existing events.
2.283 +
2.284 + else:
2.285 + slot.append((event_coverage, [event]))
2.286
2.287 return full_coverage, all_events
2.288
2.289 @@ -1251,7 +1392,7 @@
2.290 in the 'request'.
2.291 """
2.292
2.293 - return request.form.get(name, [default])[0]
2.294 + return get_form(request).get(name, [default])[0]
2.295
2.296 def getQualifiedParameter(request, calendar_name, argname, default=None):
2.297
2.298 @@ -1333,17 +1474,25 @@
2.299 else:
2.300 return None
2.301
2.302 +def getFullMonthLabel(request, year_month):
2.303 +
2.304 + """
2.305 + Return the full month plus year label using the given 'request' and
2.306 + 'year_month'.
2.307 + """
2.308 +
2.309 + _ = request.getText
2.310 + year, month = year_month.as_tuple()
2.311 + month_label = _(getMonthLabel(month))
2.312 + return "%s %s" % (month_label, year)
2.313 +
2.314 # Page-related functions.
2.315
2.316 def getPrettyPageName(page):
2.317
2.318 "Return a nicely formatted title/name for the given 'page'."
2.319
2.320 - if isMoin15():
2.321 - title = page.split_title(page.request, force=1)
2.322 - else:
2.323 - title = page.split_title(force=1)
2.324 -
2.325 + title = page.split_title(force=1)
2.326 return getPrettyTitle(title)
2.327
2.328 def linkToPage(request, page, text, query_string=None):
2.329 @@ -1354,14 +1503,7 @@
2.330 """
2.331
2.332 text = wikiutil.escape(text)
2.333 -
2.334 - if isMoin15():
2.335 - url = wikiutil.quoteWikinameURL(page.page_name)
2.336 - if query_string is not None:
2.337 - url = "%s?%s" % (url, query_string)
2.338 - return wikiutil.link_tag(request, url, text, getattr(page, "formatter", None))
2.339 - else:
2.340 - return page.link_to_raw(request, text, query_string)
2.341 + return page.link_to_raw(request, text, query_string)
2.342
2.343 def getFullPageName(parent, title):
2.344
3.1 --- a/PKG-INFO Sun Feb 06 02:38:17 2011 +0100
3.2 +++ b/PKG-INFO Sat Feb 19 03:06:03 2011 +0100
3.3 @@ -1,12 +1,12 @@
3.4 Metadata-Version: 1.1
3.5 Name: EventAggregator
3.6 -Version: 0.6.1
3.7 +Version: 0.6.2
3.8 Author: Paul Boddie
3.9 Author-email: paul at boddie org uk
3.10 Maintainer: Paul Boddie
3.11 Maintainer-email: paul at boddie org uk
3.12 Home-page: http://moinmo.in/MacroMarket/EventAggregator
3.13 -Download-url: http://moinmo.in/MacroMarket/EventAggregator?action=AttachFile&do=view&target=EventAggregator-0.6.tar.gz
3.14 +Download-url: http://moinmo.in/MacroMarket/EventAggregator?action=AttachFile&do=view&target=EventAggregator-0.7.tar.gz
3.15 Summary: Aggregate event data and display it in an event calendar (or summarise it in iCalendar and RSS resources)
3.16 License: GPL (version 2 or later)
3.17 Description: The EventAggregator macro for MoinMoin can be used to display event
4.1 --- a/README.txt Sun Feb 06 02:38:17 2011 +0100
4.2 +++ b/README.txt Sat Feb 19 03:06:03 2011 +0100
4.3 @@ -26,9 +26,14 @@
4.4 Important Notices
4.5 -----------------
4.6
4.7 -Release 0.6.1 fixes various bugs in HTML production done by the actions. It is
4.8 +Release 0.6.2 fixes various bugs in HTML production done by the actions. It is
4.9 strongly recommended to upgrade from earlier versions to this release.
4.10
4.11 +In release 0.6.2, support for MoinMoin 1.5.x has been dropped. Since usage of
4.12 +the Xapian search software is practically a necessary part of deploying this
4.13 +solution, and yet Xapian only became integrated with MoinMoin from version 1.6
4.14 +onwards, few deployments should have involved MoinMoin 1.5.x.
4.15 +
4.16 In release 0.6, support for event times has been introduced. Due to the
4.17 complicated nature of times, time zones, time regimes, and so on, the
4.18 behaviour of the software may change in future versions to support common
4.19 @@ -112,12 +117,7 @@
4.20 Using the Macro
4.21 ---------------
4.22
4.23 -It should now be possible to edit pages and use the macro as follows. For
4.24 -MoinMoin 1.5:
4.25 -
4.26 - [[EventAggregator(CategoryEvents)]]
4.27 -
4.28 -For MoinMoin 1.6 and above:
4.29 +It should now be possible to edit pages and use the macro as follows:
4.30
4.31 <<EventAggregator(CategoryEvents)>>
4.32
4.33 @@ -227,6 +227,22 @@
4.34 time zone information for the correct interpretation of time information in
4.35 those summaries. Thus, it is highly recommended that pytz be installed.
4.36
4.37 +New in EventAggregator 0.6.2 (Changes since EventAggregator 0.6.1)
4.38 +------------------------------------------------------------------
4.39 +
4.40 + * Dropped MoinMoin 1.5.x support, since Xapian search is not available for
4.41 + that version and is virtually a necessity.
4.42 + * Fixed form handling to be compatible with MoinMoin 1.9.x, since that
4.43 + particular release series introduced an incompatible request API that
4.44 + breaks existing code (no longer providing access to query string
4.45 + parameters via the form attribute, and only returning single values
4.46 + unless the new getlist method on form-like objects is used).
4.47 + * Fixed the direct writing of requests to be compatible with MoinMoin 1.9.
4.48 + * Added pop-up elements showing information about the calendar/view
4.49 + resources available for download or subscription.
4.50 + * Added download/subscription links which open the form associated with the
4.51 + EventAggregatorSummary action and permit editing of the supplied values.
4.52 +
4.53 New in EventAggregator 0.6.1 (Changes since EventAggregator 0.6)
4.54 ----------------------------------------------------------------
4.55
5.1 --- a/actions/EventAggregatorNewEvent.py Sun Feb 06 02:38:17 2011 +0100
5.2 +++ b/actions/EventAggregatorNewEvent.py Sat Feb 19 03:06:03 2011 +0100
5.3 @@ -27,27 +27,14 @@
5.4
5.5 # Action class and supporting functions.
5.6
5.7 -class EventAggregatorNewEvent(ActionBase):
5.8 +class EventAggregatorNewEvent(ActionBase, EventAggregatorSupport.ActionSupport):
5.9
5.10 "An event creation dialogue requesting various parameters."
5.11
5.12 - def _get_selected(self, value, input_value):
5.13 - return input_value is not None and value == input_value and 'selected="selected"' or ''
5.14 -
5.15 - def _get_selected_for_list(self, value, input_values):
5.16 - return value in input_values and 'selected="selected"' or ''
5.17 -
5.18 - def _get_input(self, form, name, default=None):
5.19 - value = form.get(name, [None])[0]
5.20 - if not value: # true if 0 obtained
5.21 - return default
5.22 - else:
5.23 - return int(value)
5.24 -
5.25 def get_form_html(self, buttons_html):
5.26 _ = self._
5.27 request = self.request
5.28 - form = request.form
5.29 + form = self.get_form()
5.30
5.31 # Handle advanced and basic forms, and enable/disable certain fields.
5.32
5.33 @@ -99,20 +86,8 @@
5.34
5.35 # Initialise month lists.
5.36
5.37 - start_month_list = []
5.38 - end_month_list = []
5.39 -
5.40 - start_month = self._get_input(form, "start-month", EventAggregatorSupport.getCurrentMonth().month())
5.41 - end_month = self._get_input(form, "end-month", start_month)
5.42 -
5.43 - # Prepare month lists, selecting specified months.
5.44 -
5.45 - for month in range(1, 13):
5.46 - month_label = _(EventAggregatorSupport.getMonthLabel(month))
5.47 - selected = self._get_selected(month, start_month)
5.48 - start_month_list.append('<option value="%02d" %s>%s</option>' % (month, selected, escape(month_label)))
5.49 - selected = self._get_selected(month, end_month)
5.50 - end_month_list.append('<option value="%02d" %s>%s</option>' % (month, selected, escape(month_label)))
5.51 + start_month_list, end_month_list = self.get_month_lists(default_as_current=1)
5.52 + start_year_default, end_year_default = self.get_year_defaults(default_as_current=1)
5.53
5.54 # Initialise regime lists.
5.55
5.56 @@ -155,7 +130,7 @@
5.57
5.58 "start_label" : escape(_("Start date (day, month, year)")),
5.59 "start_day_default" : escattr(form.get("start-day", [""])[0]),
5.60 - "start_year_default" : escattr(form.get("start-year", [""])[0] or EventAggregatorSupport.getCurrentYear()),
5.61 + "start_year_default" : escattr(start_year_default),
5.62 "start_time_label" : escape(_("Start time (hour, minute, second)")),
5.63 "start_hour_default" : escattr(form.get("start-hour", [""])[0]),
5.64 "start_minute_default" : escattr(form.get("start-minute", [""])[0]),
5.65 @@ -164,7 +139,7 @@
5.66
5.67 "end_label" : escape(_("End date (day, month, year) - if different")),
5.68 "end_day_default" : escattr(form.get("end-day", [""])[0] or form.get("start-day", [""])[0]),
5.69 - "end_year_default" : escattr(form.get("end-year", [""])[0] or form.get("start-year", [""])[0]),
5.70 + "end_year_default" : escattr(end_year_default),
5.71 "end_time_label" : escape(_("End time (hour, minute, second)")),
5.72 "end_hour_default" : escattr(form.get("end-hour", [""])[0]),
5.73 "end_minute_default" : escattr(form.get("end-minute", [""])[0]),
5.74 @@ -469,7 +444,7 @@
5.75 "Create the new event."
5.76
5.77 _ = self._
5.78 - form = self.request.form
5.79 + form = self.get_form()
5.80
5.81 # If no title exists in the request, an error message is returned.
5.82
5.83 @@ -499,7 +474,7 @@
5.84 "Create an event page using the 'request'."
5.85
5.86 _ = request.getText
5.87 - form = request.form
5.88 + form = self.get_form()
5.89
5.90 category_pagenames = form.get("category", [])
5.91 description = form.get("description", [None])[0]
6.1 --- a/actions/EventAggregatorSummary.py Sun Feb 06 02:38:17 2011 +0100
6.2 +++ b/actions/EventAggregatorSummary.py Sat Feb 19 03:06:03 2011 +0100
6.3 @@ -14,7 +14,6 @@
6.4 from MoinMoin.action import ActionBase
6.5 from MoinMoin import config
6.6 from MoinMoin.Page import Page
6.7 -import MoinMoin.util # for MoinMoin 1.5.x
6.8 from MoinMoin import wikiutil
6.9 import EventAggregatorSupport
6.10
6.11 @@ -25,50 +24,82 @@
6.12
6.13 # Action class and supporting functions.
6.14
6.15 -class EventAggregatorSummary(ActionBase):
6.16 +class EventAggregatorSummary(ActionBase, EventAggregatorSupport.ActionSupport):
6.17
6.18 "A summary dialogue requesting various parameters."
6.19
6.20 def get_form_html(self, buttons_html):
6.21 _ = self._
6.22 request = self.request
6.23 - form = request.form
6.24 + form = self.get_form()
6.25
6.26 category_list = []
6.27 + category_pagenames = form.get("category", [])
6.28
6.29 for category_name, category_pagename in \
6.30 EventAggregatorSupport.getCategoryMapping(
6.31 EventAggregatorSupport.getCategories(request),
6.32 request):
6.33
6.34 - category_list.append('<option value="%s">%s</option>' % (escattr(category_pagename), escape(category_name)))
6.35 + selected = self._get_selected_for_list(category_pagename, category_pagenames)
6.36 + category_list.append('<option value="%s" %s>%s</option>' % (escattr(category_pagename), selected, escape(category_name)))
6.37 +
6.38 + # Initialise month lists.
6.39 +
6.40 + start_month_list, end_month_list = self.get_month_lists()
6.41 + start_year_default, end_year_default = self.get_year_defaults()
6.42 +
6.43 + # Criteria instead of months and years.
6.44
6.45 - month_list = []
6.46 - month_list.append('<option value=""></option>')
6.47 + start_criteria_default = form.get("start", [""])[0]
6.48 + end_criteria_default = form.get("end", [""])[0]
6.49 +
6.50 + start_criteria_evaluated = EventAggregatorSupport.getParameterMonth(start_criteria_default)
6.51 + end_criteria_evaluated = EventAggregatorSupport.getParameterMonth(end_criteria_default)
6.52
6.53 - for month in range(1, 13):
6.54 - month_label = _(EventAggregatorSupport.getMonthLabel(month))
6.55 - month_list.append('<option value="%02d">%s</option>' % (month, escape(month_label)))
6.56 + start_criteria_evaluated = start_criteria_evaluated and \
6.57 + EventAggregatorSupport.getFullMonthLabel(request, start_criteria_evaluated) or ""
6.58 + end_criteria_evaluated = end_criteria_evaluated and \
6.59 + EventAggregatorSupport.getFullMonthLabel(request, end_criteria_evaluated) or ""
6.60 +
6.61 + # Descriptions.
6.62 +
6.63 + descriptions = form.get("descriptions", [None])[0]
6.64
6.65 descriptions_list = [
6.66 - '<option value="%s">%s</option>' % ("page", escape(_("page"))),
6.67 - '<option value="%s">%s</option>' % ("comment", escape(_("comment")))
6.68 + '<option value="%s" %s>%s</option>' % ("page", self._get_selected("page", descriptions), escape(_("page"))),
6.69 + '<option value="%s" %s>%s</option>' % ("comment", self._get_selected("comment", descriptions), escape(_("comment")))
6.70 ]
6.71
6.72 + # Format.
6.73 +
6.74 + format = form.get("format", [None])[0]
6.75 +
6.76 format_list = [
6.77 - '<option value="%s">%s</option>' % ("iCalendar", escape(_("iCalendar"))),
6.78 - '<option value="%s">%s</option>' % ("RSS", escape(_("RSS 2.0")))
6.79 + '<option value="%s" %s>%s</option>' % ("iCalendar", self._get_selected("iCalendar", format), escape(_("iCalendar"))),
6.80 + '<option value="%s" %s>%s</option>' % ("RSS", self._get_selected("RSS", format), escape(_("RSS 2.0")))
6.81 ]
6.82
6.83 + right_arrow = unicode('\xe2\x86\x92', "utf-8")
6.84 +
6.85 d = {
6.86 "buttons_html" : buttons_html,
6.87 "category_label" : escape(_("Categories")),
6.88 "category_list" : "\n".join(category_list),
6.89 - "month_list" : "\n".join(month_list),
6.90 + "start_month_list" : "\n".join(start_month_list),
6.91 "start_label" : escape(_("Start year and month")),
6.92 - "start_year_default" : "",
6.93 + "start_year_default" : escattr(start_year_default),
6.94 + "start_criteria_label" : escape(_("or special criteria")),
6.95 + "start_criteria_default": escattr(start_criteria_default),
6.96 + "start_eval_label" : escape(right_arrow),
6.97 + "start_criteria_eval" : escape(start_criteria_evaluated),
6.98 + "end_month_list" : "\n".join(end_month_list),
6.99 "end_label" : escape(_("End year and month")),
6.100 - "end_year_default" : "",
6.101 + "end_year_default" : escattr(end_year_default),
6.102 + "end_criteria_label" : escape(_("or special criteria")),
6.103 + "end_criteria_default" : escattr(end_criteria_default),
6.104 + "end_eval_label" : escape(right_arrow),
6.105 + "end_criteria_eval" : escape(end_criteria_evaluated),
6.106 "descriptions_label" : escape(_("Use descriptions from...")),
6.107 "descriptions_list" : "\n".join(descriptions_list),
6.108 "format_label" : escape(_("Summary format")),
6.109 @@ -91,21 +122,37 @@
6.110 <td class="label"><label>%(start_label)s</label></td>
6.111 <td>
6.112 <select name="start-month">
6.113 - %(month_list)s
6.114 + %(start_month_list)s
6.115 </select>
6.116 <input name="start-year" type="text" value="%(start_year_default)s" size="4" />
6.117 </td>
6.118 </tr>
6.119 <tr>
6.120 + <td class="label"><label>%(start_criteria_label)s</label></td>
6.121 + <td>
6.122 + <input name="start" type="text" value="%(start_criteria_default)s" size="12" />
6.123 + <input name="start-eval" type="submit" value="%(start_eval_label)s" />
6.124 + %(start_criteria_eval)s
6.125 + </td>
6.126 + </tr>
6.127 + <tr>
6.128 <td class="label"><label>%(end_label)s</label></td>
6.129 <td>
6.130 <select name="end-month">
6.131 - %(month_list)s
6.132 + %(end_month_list)s
6.133 </select>
6.134 <input name="end-year" type="text" value="%(end_year_default)s" size="4" />
6.135 </td>
6.136 </tr>
6.137 <tr>
6.138 + <td class="label"><label>%(end_criteria_label)s</label></td>
6.139 + <td>
6.140 + <input name="end" type="text" value="%(end_criteria_default)s" size="12" />
6.141 + <input name="end-eval" type="submit" value="%(end_eval_label)s" />
6.142 + %(end_criteria_eval)s
6.143 + </td>
6.144 + </tr>
6.145 + <tr>
6.146 <td class="label"><label>%(descriptions_label)s</label></td>
6.147 <td class="content">
6.148 <select name="descriptions">
6.149 @@ -141,7 +188,7 @@
6.150 "Write the iCalendar resource."
6.151
6.152 _ = self._
6.153 - form = self.request.form
6.154 + form = self.get_form()
6.155
6.156 # If no category names exist in the request, an error message is
6.157 # returned.
6.158 @@ -179,7 +226,7 @@
6.159 can be specified.
6.160 """
6.161
6.162 - form = request.form
6.163 + form = EventAggregatorSupport.get_form(request)
6.164
6.165 category_names = form.get("category", [])
6.166 format = form.get("format", ["iCalendar"])[0]
6.167 @@ -206,10 +253,12 @@
6.168
6.169 # Output summary data...
6.170
6.171 - if EventAggregatorSupport.isMoin15():
6.172 + if hasattr(request, "http_headers"):
6.173 send_headers = request.http_headers
6.174 + elif hasattr(request, "emit_http_headers"):
6.175 + send_headers = request.emit_http_headers
6.176 else:
6.177 - send_headers = request.emit_http_headers
6.178 + send_headers = EventAggregatorSupport.send_headers(request)
6.179
6.180 # Define headers.
6.181
6.182 @@ -285,12 +334,19 @@
6.183 # Using the page name and the page URL in the title, link and
6.184 # description.
6.185
6.186 + if hasattr(request, "getPathinfo"):
6.187 + path_info = request.getPathinfo()
6.188 + else:
6.189 + path_info = request.path
6.190 +
6.191 request.write('<rss version="2.0">\r\n')
6.192 request.write('<channel>\r\n')
6.193 - request.write('<title>%s</title>\r\n' % request.getPathinfo()[1:])
6.194 - request.write('<link>%s%s</link>\r\n' % (request.getBaseURL(), request.getPathinfo()))
6.195 - request.write('<description>Events published on %s%s</description>\r\n' % (request.getBaseURL(), request.getPathinfo()))
6.196 - request.write('<lastBuildDate>%s</lastBuildDate>\r\n' % EventAggregatorSupport.getHTTPTimeString(latest_timestamp))
6.197 + request.write('<title>%s</title>\r\n' % path_info[1:])
6.198 + request.write('<link>%s%s</link>\r\n' % (request.getBaseURL(), path_info))
6.199 + request.write('<description>Events published on %s%s</description>\r\n' % (request.getBaseURL(), path_info))
6.200 +
6.201 + if latest_timestamp is not None:
6.202 + request.write('<lastBuildDate>%s</lastBuildDate>\r\n' % EventAggregatorSupport.getHTTPTimeString(latest_timestamp))
6.203
6.204 # Sort all_shown_events by start date, reversed:
6.205 #
6.206 @@ -335,9 +391,6 @@
6.207 request.write('</channel>\r\n')
6.208 request.write('</rss>\r\n')
6.209
6.210 - if EventAggregatorSupport.isMoin15():
6.211 - raise MoinMoin.util.MoinMoinNoFooter
6.212 -
6.213 def write_calendar_datetime(request, datetime):
6.214
6.215 """
7.1 --- a/css/event-aggregator.css Sun Feb 06 02:38:17 2011 +0100
7.2 +++ b/css/event-aggregator.css Sat Feb 19 03:06:03 2011 +0100
7.3 @@ -21,6 +21,36 @@
7.4
7.5 .event-download {
7.6 padding-right: 2em;
7.7 + position: relative;
7.8 +}
7.9 +
7.10 +.event-download a {
7.11 + font-size: smaller;
7.12 +}
7.13 +
7.14 +.event-download-popup {
7.15 + display: none;
7.16 +}
7.17 +
7.18 +.event-download:hover .event-download-popup {
7.19 + display: block;
7.20 + position: absolute;
7.21 + top: 1.5em;
7.22 + left: 0;
7.23 + z-index: 3;
7.24 + background-color: #fff;
7.25 + color: #000;
7.26 + padding: 0.5em;
7.27 + border: 1px solid #000;
7.28 +}
7.29 +
7.30 +.event-download-period {
7.31 + display: block;
7.32 +}
7.33 +
7.34 +.event-download-period-raw {
7.35 + display: block;
7.36 + font-size: smaller;
7.37 }
7.38
7.39 .event-view-controls {
7.40 @@ -31,6 +61,10 @@
7.41 padding-right: 2em;
7.42 }
7.43
7.44 +.event-view a {
7.45 + font-size: smaller;
7.46 +}
7.47 +
7.48 /* Calendar view. */
7.49
7.50 .event-month {
8.1 --- a/macros/EventAggregator.py Sun Feb 06 02:38:17 2011 +0100
8.2 +++ b/macros/EventAggregator.py Sat Feb 19 03:06:03 2011 +0100
8.3 @@ -24,7 +24,7 @@
8.4
8.5 def __init__(self, page, calendar_name, raw_calendar_start, raw_calendar_end,
8.6 calendar_start, calendar_end, first, last, category_names, template_name,
8.7 - parent_name, mode):
8.8 + parent_name, mode, name_usage):
8.9
8.10 """
8.11 Initialise the view with the current 'page', a 'calendar_name' (which
8.12 @@ -35,6 +35,9 @@
8.13
8.14 The additional 'category_names', 'template_name', 'parent_name' and
8.15 'mode' parameters are used to configure the links employed by the view.
8.16 +
8.17 + The 'name_usage' parameter controls how names are shown on calendar mode
8.18 + events.
8.19 """
8.20
8.21 self.page = page
8.22 @@ -46,6 +49,7 @@
8.23 self.template_name = template_name
8.24 self.parent_name = parent_name
8.25 self.mode = mode
8.26 + self.name_usage = name_usage
8.27
8.28 self.category_name_parameters = "&".join([("category=%s" % name) for name in category_names])
8.29
8.30 @@ -66,9 +70,46 @@
8.31 self.next_set_end = last.month_update(self.number_of_months)
8.32
8.33 def getQualifiedParameterName(self, argname):
8.34 +
8.35 + "Return the 'argname' qualified using the calendar name."
8.36 +
8.37 return EventAggregatorSupport.getQualifiedParameterName(self.calendar_name, argname)
8.38
8.39 + def getMonthYearQueryString(self, argname, year_month, prefix=1):
8.40 +
8.41 + """
8.42 + Return a query string fragment for the given 'argname', referring to the
8.43 + month given by the specified 'year_month' object, appropriate for this
8.44 + calendar.
8.45 +
8.46 + If 'prefix' is specified and set to a false value, the parameters in the
8.47 + query string will not be calendar-specific, but could be used with the
8.48 + summary action.
8.49 + """
8.50 +
8.51 + if year_month is not None:
8.52 + year, month = year_month.as_tuple()
8.53 + month_argname = "%s-month" % argname
8.54 + year_argname = "%s-year" % argname
8.55 + if prefix:
8.56 + month_argname = self.getQualifiedParameterName(month_argname)
8.57 + year_argname = self.getQualifiedParameterName(year_argname)
8.58 + return "%s=%s&%s=%s" % (month_argname, month, year_argname, year)
8.59 + else:
8.60 + return ""
8.61 +
8.62 def getMonthQueryString(self, argname, month, prefix=1):
8.63 +
8.64 + """
8.65 + Return a query string fragment for the given 'argname', referring to the
8.66 + month given by the specified 'month' value, appropriate for this
8.67 + calendar.
8.68 +
8.69 + If 'prefix' is specified and set to a false value, the parameters in the
8.70 + query string will not be calendar-specific, but could be used with the
8.71 + summary action.
8.72 + """
8.73 +
8.74 if month is not None:
8.75 if prefix:
8.76 argname = self.getQualifiedParameterName(argname)
8.77 @@ -77,12 +118,24 @@
8.78 return ""
8.79
8.80 def getNavigationLink(self, start, end, mode=None):
8.81 +
8.82 + """
8.83 + Return a query string fragment for navigation to a view showing months
8.84 + from 'start' to 'end' inclusive, with the optional 'mode' indicating the
8.85 + view style.
8.86 + """
8.87 +
8.88 return "%s&%s&%s=%s" % (
8.89 self.getMonthQueryString("start", start),
8.90 self.getMonthQueryString("end", end),
8.91 self.getQualifiedParameterName("mode"), mode or self.mode
8.92 )
8.93
8.94 + def getFullMonthLabel(self, year_month):
8.95 + page = self.page
8.96 + request = page.request
8.97 + return EventAggregatorSupport.getFullMonthLabel(request, year_month)
8.98 +
8.99 def writeDownloadControls(self):
8.100 page = self.page
8.101 request = page.request
8.102 @@ -93,13 +146,18 @@
8.103
8.104 # Generate the links.
8.105
8.106 - download_all_link = "action=EventAggregatorSummary&doit=1&parent=%s&%s" % (
8.107 + download_dialogue_link = "action=EventAggregatorSummary&parent=%s&%s" % (
8.108 self.parent_name or "", self.category_name_parameters
8.109 )
8.110 + download_all_link = download_dialogue_link + "&doit=1"
8.111 download_link = download_all_link + ("&%s&%s" % (
8.112 - self.getMonthQueryString("start", self.calendar_start, prefix=0),
8.113 - self.getMonthQueryString("end", self.calendar_end, prefix=0)
8.114 + self.getMonthYearQueryString("start", self.calendar_start, prefix=0),
8.115 + self.getMonthYearQueryString("end", self.calendar_end, prefix=0)
8.116 ))
8.117 +
8.118 + # Subscription links just explicitly select the RSS format.
8.119 +
8.120 + subscribe_dialogue_link = download_dialogue_link + "&format=RSS"
8.121 subscribe_all_link = download_all_link + "&format=RSS"
8.122 subscribe_link = download_link + "&format=RSS"
8.123
8.124 @@ -119,23 +177,76 @@
8.125
8.126 period_limits = "".join(period_limits)
8.127
8.128 + download_dialogue_link += period_limits
8.129 download_all_link += period_limits
8.130 + subscribe_dialogue_link += period_limits
8.131 subscribe_all_link += period_limits
8.132
8.133 + # Pop-up descriptions of the downloadable calendars.
8.134 +
8.135 + calendar_period = "%s - %s" % (
8.136 + self.getFullMonthLabel(self.calendar_start),
8.137 + self.getFullMonthLabel(self.calendar_end)
8.138 + )
8.139 + raw_calendar_period = "%s - %s" % (self.raw_calendar_start, self.raw_calendar_end)
8.140 +
8.141 # Write the controls.
8.142
8.143 + # Download controls.
8.144 +
8.145 output.append(fmt.div(on=1, css_class="event-download-controls"))
8.146 output.append(fmt.span(on=1, css_class="event-download"))
8.147 output.append(linkToPage(request, page, _("Download this view"), download_link))
8.148 + output.append(fmt.span(on=1, css_class="event-download-popup"))
8.149 + output.append(fmt.text(calendar_period))
8.150 output.append(fmt.span(on=0))
8.151 + output.append(fmt.span(on=0))
8.152 +
8.153 output.append(fmt.span(on=1, css_class="event-download"))
8.154 output.append(linkToPage(request, page, _("Download this calendar"), download_all_link))
8.155 + output.append(fmt.span(on=1, css_class="event-download-popup"))
8.156 + output.append(fmt.span(on=1, css_class="event-download-period"))
8.157 + output.append(fmt.text(calendar_period))
8.158 output.append(fmt.span(on=0))
8.159 + output.append(fmt.span(on=1, css_class="event-download-period-raw"))
8.160 + output.append(fmt.text(raw_calendar_period))
8.161 + output.append(fmt.span(on=0))
8.162 + output.append(fmt.span(on=0))
8.163 + output.append(fmt.span(on=0))
8.164 +
8.165 + output.append(fmt.span(on=1, css_class="event-download"))
8.166 + output.append(linkToPage(request, page, _("Download..."), download_dialogue_link))
8.167 + output.append(fmt.span(on=1, css_class="event-download-popup"))
8.168 + output.append(fmt.text(_("Edit download options")))
8.169 + output.append(fmt.span(on=0))
8.170 + output.append(fmt.span(on=0))
8.171 +
8.172 + # Subscription controls.
8.173 +
8.174 output.append(fmt.span(on=1, css_class="event-download"))
8.175 output.append(linkToPage(request, page, _("Subscribe to this view"), subscribe_link))
8.176 + output.append(fmt.span(on=1, css_class="event-download-popup"))
8.177 + output.append(fmt.text(calendar_period))
8.178 output.append(fmt.span(on=0))
8.179 + output.append(fmt.span(on=0))
8.180 +
8.181 output.append(fmt.span(on=1, css_class="event-download"))
8.182 output.append(linkToPage(request, page, _("Subscribe to this calendar"), subscribe_all_link))
8.183 + output.append(fmt.span(on=1, css_class="event-download-popup"))
8.184 + output.append(fmt.span(on=1, css_class="event-download-period"))
8.185 + output.append(fmt.text(calendar_period))
8.186 + output.append(fmt.span(on=0))
8.187 + output.append(fmt.span(on=1, css_class="event-download-period-raw"))
8.188 + output.append(fmt.text(raw_calendar_period))
8.189 + output.append(fmt.span(on=0))
8.190 + output.append(fmt.span(on=0))
8.191 + output.append(fmt.span(on=0))
8.192 +
8.193 + output.append(fmt.span(on=1, css_class="event-download"))
8.194 + output.append(linkToPage(request, page, _("Subscribe..."), subscribe_dialogue_link))
8.195 + output.append(fmt.span(on=1, css_class="event-download-popup"))
8.196 + output.append(fmt.text(_("Edit subscription options")))
8.197 + output.append(fmt.span(on=0))
8.198 output.append(fmt.span(on=0))
8.199 output.append(fmt.div(on=0))
8.200
8.201 @@ -180,12 +291,10 @@
8.202 request = page.request
8.203 fmt = page.formatter
8.204 _ = request.getText
8.205 + full_month_label = self.getFullMonthLabel(year_month)
8.206
8.207 output = []
8.208
8.209 - year, month = year_month.as_tuple()
8.210 - month_label = _(EventAggregatorSupport.getMonthLabel(month))
8.211 -
8.212 # Prepare navigation links.
8.213
8.214 if self.calendar_name is not None:
8.215 @@ -213,7 +322,6 @@
8.216
8.217 # A link leading to this month being at the top of the calendar.
8.218
8.219 - full_month_label = "%s %s" % (month_label, year)
8.220 end_month = year_month.month_update(self.number_of_months - 1)
8.221
8.222 month_link = self.getNavigationLink(year_month, end_month)
8.223 @@ -234,11 +342,7 @@
8.224
8.225 else:
8.226 output.append(fmt.span(on=1))
8.227 - output.append(fmt.text(month_label))
8.228 - output.append(fmt.span(on=0))
8.229 - output.append(fmt.text(" "))
8.230 - output.append(fmt.span(on=1))
8.231 - output.append(fmt.text(unicode(year)))
8.232 + output.append(fmt.text(full_month_label))
8.233 output.append(fmt.span(on=0))
8.234
8.235 return "".join(output)
8.236 @@ -275,6 +379,330 @@
8.237
8.238 return "".join(output)
8.239
8.240 + # Calendar layout methods.
8.241 +
8.242 + def writeDayNumbers(self, first_day, number_of_days, month, busy_dates):
8.243 + page = self.page
8.244 + fmt = page.formatter
8.245 +
8.246 + output = []
8.247 + output.append(fmt.table_row(on=1))
8.248 +
8.249 + for weekday in range(0, 7):
8.250 + day = first_day + weekday
8.251 + date = month.as_date(day)
8.252 +
8.253 + # Output out-of-month days.
8.254 +
8.255 + if day < 1 or day > number_of_days:
8.256 + output.append(fmt.table_cell(on=1,
8.257 + attrs={"class" : "event-day-heading event-day-excluded", "colspan" : "3"}))
8.258 + output.append(fmt.table_cell(on=0))
8.259 +
8.260 + # Output normal days.
8.261 +
8.262 + else:
8.263 + if date in busy_dates:
8.264 + output.append(fmt.table_cell(on=1,
8.265 + attrs={"class" : "event-day-heading event-day-busy", "colspan" : "3"}))
8.266 + else:
8.267 + output.append(fmt.table_cell(on=1,
8.268 + attrs={"class" : "event-day-heading event-day-empty", "colspan" : "3"}))
8.269 +
8.270 + # Output the day number, making a link to a new event
8.271 + # action.
8.272 +
8.273 + output.append(self.writeDayNumberLinked(date))
8.274 +
8.275 + # End of day.
8.276 +
8.277 + output.append(fmt.table_cell(on=0))
8.278 +
8.279 + # End of day numbers.
8.280 +
8.281 + output.append(fmt.table_row(on=0))
8.282 + return "".join(output)
8.283 +
8.284 + def writeEmptyWeek(self, first_day, number_of_days):
8.285 + page = self.page
8.286 + fmt = page.formatter
8.287 +
8.288 + output = []
8.289 + output.append(fmt.table_row(on=1))
8.290 +
8.291 + for weekday in range(0, 7):
8.292 + day = first_day + weekday
8.293 +
8.294 + # Output out-of-month days.
8.295 +
8.296 + if day < 1 or day > number_of_days:
8.297 + output.append(fmt.table_cell(on=1,
8.298 + attrs={"class" : "event-day-content event-day-excluded", "colspan" : "3"}))
8.299 + output.append(fmt.table_cell(on=0))
8.300 +
8.301 + # Output empty days.
8.302 +
8.303 + else:
8.304 + output.append(fmt.table_cell(on=1,
8.305 + attrs={"class" : "event-day-content event-day-empty", "colspan" : "3"}))
8.306 +
8.307 + output.append(fmt.table_row(on=0))
8.308 + return "".join(output)
8.309 +
8.310 + def writeWeekSlots(self, first_day, number_of_days, month, week_end, week_slots):
8.311 + output = []
8.312 +
8.313 + locations = week_slots.keys()
8.314 + locations.sort(EventAggregatorSupport.sort_none_first)
8.315 +
8.316 + # Visit each slot corresponding to a location (or no location).
8.317 +
8.318 + for location in locations:
8.319 +
8.320 + # Visit each coverage span, presenting the events in the span.
8.321 +
8.322 + for coverage, events in week_slots[location]:
8.323 +
8.324 + # Output each set.
8.325 +
8.326 + output.append(self.writeWeekSlot(first_day, number_of_days, month, week_end, coverage, events))
8.327 +
8.328 + # Add a spacer.
8.329 +
8.330 + output.append(self.writeSpacer(first_day, number_of_days))
8.331 +
8.332 + return "".join(output)
8.333 +
8.334 + def writeWeekSlot(self, first_day, number_of_days, month, week_end, coverage, events):
8.335 + page = self.page
8.336 + request = page.request
8.337 + fmt = page.formatter
8.338 +
8.339 + output = []
8.340 + output.append(fmt.table_row(on=1))
8.341 +
8.342 + # Then, output day details.
8.343 +
8.344 + for weekday in range(0, 7):
8.345 + day = first_day + weekday
8.346 + date = month.as_date(day)
8.347 +
8.348 + # Skip out-of-month days.
8.349 +
8.350 + if day < 1 or day > number_of_days:
8.351 + output.append(fmt.table_cell(on=1,
8.352 + attrs={"class" : "event-day-content event-day-excluded", "colspan" : "3"}))
8.353 + output.append(fmt.table_cell(on=0))
8.354 + continue
8.355 +
8.356 + # Output the day.
8.357 +
8.358 + if date not in coverage:
8.359 + output.append(fmt.table_cell(on=1,
8.360 + attrs={"class" : "event-day-content event-day-empty", "colspan" : "3"}))
8.361 +
8.362 + # Get event details for the current day.
8.363 +
8.364 + for event in events:
8.365 + event_page = event.getPage()
8.366 + event_details = event.getDetails()
8.367 +
8.368 + if not (event_details["start"] <= date <= event_details["end"]):
8.369 + continue
8.370 +
8.371 + # Get basic properties of the event.
8.372 +
8.373 + starts_today = event_details["start"] == date
8.374 + ends_today = event_details["end"] == date
8.375 + event_summary = event.getSummary(self.parent_name)
8.376 + is_ambiguous = event_details["start"].ambiguous() or event_details["end"].ambiguous()
8.377 +
8.378 + # Generate a colour for the event.
8.379 +
8.380 + bg = getColour(event_summary)
8.381 + fg = getBlackOrWhite(bg)
8.382 + style = ("background-color: rgb(%d, %d, %d); color: rgb(%d, %d, %d);" % (bg + fg))
8.383 +
8.384 + # Determine if the event name should be shown.
8.385 +
8.386 + start_of_period = starts_today or weekday == 0 or day == 1
8.387 +
8.388 + if self.name_usage == "daily" or start_of_period:
8.389 + hide_text = 0
8.390 + else:
8.391 + hide_text = 1
8.392 +
8.393 + # Output start of day gap and determine whether
8.394 + # any event content should be explicitly output
8.395 + # for this day.
8.396 +
8.397 + if starts_today:
8.398 +
8.399 + # Single day events...
8.400 +
8.401 + if ends_today:
8.402 + colspan = 3
8.403 + event_day_type = "event-day-single"
8.404 +
8.405 + # Events starting today...
8.406 +
8.407 + else:
8.408 + output.append(fmt.table_cell(on=1, attrs={"class" : "event-day-start-gap"}))
8.409 + output.append(fmt.table_cell(on=0))
8.410 +
8.411 + # Calculate the span of this cell.
8.412 + # Events whose names appear on every day...
8.413 +
8.414 + if self.name_usage == "daily":
8.415 + colspan = 2
8.416 + event_day_type = "event-day-starting"
8.417 +
8.418 + # Events whose names appear once per week...
8.419 +
8.420 + else:
8.421 + if event_details["end"] <= week_end:
8.422 + event_length = event_details["end"].day() - day + 1
8.423 + colspan = (event_length - 2) * 3 + 4
8.424 + else:
8.425 + event_length = week_end.day() - day + 1
8.426 + colspan = (event_length - 1) * 3 + 2
8.427 +
8.428 + event_day_type = "event-day-multiple"
8.429 +
8.430 + # Events continuing from a previous week...
8.431 +
8.432 + elif start_of_period:
8.433 +
8.434 + # End of continuing event...
8.435 +
8.436 + if ends_today:
8.437 + colspan = 2
8.438 + event_day_type = "event-day-ending"
8.439 +
8.440 + # Events continuing for at least one more day...
8.441 +
8.442 + else:
8.443 +
8.444 + # Calculate the span of this cell.
8.445 + # Events whose names appear on every day...
8.446 +
8.447 + if self.name_usage == "daily":
8.448 + colspan = 3
8.449 + event_day_type = "event-day-full"
8.450 +
8.451 + # Events whose names appear once per week...
8.452 +
8.453 + else:
8.454 + if event_details["end"] <= week_end:
8.455 + event_length = event_details["end"].day() - day + 1
8.456 + colspan = (event_length - 1) * 3 + 2
8.457 + else:
8.458 + event_length = week_end.day() - day + 1
8.459 + colspan = event_length * 3
8.460 +
8.461 + event_day_type = "event-day-multiple"
8.462 +
8.463 + # Continuing events whose names appear on every day...
8.464 +
8.465 + elif self.name_usage == "daily":
8.466 + if ends_today:
8.467 + colspan = 2
8.468 + event_day_type = "event-day-ending"
8.469 + else:
8.470 + colspan = 3
8.471 + event_day_type = "event-day-full"
8.472 +
8.473 + # Continuing events whose names appear once per week...
8.474 +
8.475 + else:
8.476 + colspan = None
8.477 +
8.478 + # Output the main content only if it is not
8.479 + # continuing from a previous day.
8.480 +
8.481 + if colspan is not None:
8.482 +
8.483 + # Colour the cell for continuing events.
8.484 +
8.485 + attrs={
8.486 + "class" : "event-day-content event-day-busy %s" % event_day_type,
8.487 + "colspan" : str(colspan)
8.488 + }
8.489 +
8.490 + if not (starts_today and ends_today):
8.491 + attrs["style"] = style
8.492 +
8.493 + output.append(fmt.table_cell(on=1, attrs=attrs))
8.494 +
8.495 + # Output the event.
8.496 +
8.497 + if starts_today and ends_today or not hide_text:
8.498 +
8.499 + # The event box contains the summary, alongside
8.500 + # other elements.
8.501 +
8.502 + output.append(fmt.div(on=1, css_class="event-summary-box"))
8.503 + output.append(fmt.div(on=1, css_class="event-summary", style=style))
8.504 +
8.505 + if is_ambiguous:
8.506 + output.append(fmt.icon("/!\\"))
8.507 +
8.508 + output.append(event_page.linkToPage(request, event_summary))
8.509 + output.append(fmt.div(on=0))
8.510 +
8.511 + # Add a pop-up element for long summaries.
8.512 +
8.513 + output.append(fmt.div(on=1, css_class="event-summary-popup", style=style))
8.514 +
8.515 + if is_ambiguous:
8.516 + output.append(fmt.icon("/!\\"))
8.517 +
8.518 + output.append(event_page.linkToPage(request, event_summary))
8.519 + output.append(fmt.div(on=0))
8.520 +
8.521 + output.append(fmt.div(on=0))
8.522 +
8.523 + # Output end of day content.
8.524 +
8.525 + output.append(fmt.div(on=0))
8.526 +
8.527 + # Output end of day gap.
8.528 +
8.529 + if ends_today and not starts_today:
8.530 + output.append(fmt.table_cell(on=1, attrs={"class" : "event-day-end-gap"}))
8.531 + output.append(fmt.table_cell(on=0))
8.532 +
8.533 + # End of day.
8.534 +
8.535 + output.append(fmt.table_cell(on=0))
8.536 +
8.537 + # End of set.
8.538 +
8.539 + output.append(fmt.table_row(on=0))
8.540 + return "".join(output)
8.541 +
8.542 + def writeSpacer(self, first_day, number_of_days):
8.543 + page = self.page
8.544 + fmt = page.formatter
8.545 +
8.546 + output = []
8.547 + output.append(fmt.table_row(on=1))
8.548 +
8.549 + for weekday in range(0, 7):
8.550 + day = first_day + weekday
8.551 + css_classes = "event-day-spacer"
8.552 +
8.553 + # Skip out-of-month days.
8.554 +
8.555 + if day < 1 or day > number_of_days:
8.556 + css_classes += " event-day-excluded"
8.557 +
8.558 + output.append(fmt.table_cell(on=1, attrs={"class" : css_classes, "colspan" : "3"}))
8.559 + output.append(fmt.table_cell(on=0))
8.560 +
8.561 + output.append(fmt.table_row(on=0))
8.562 + return "".join(output)
8.563 +
8.564 # HTML-related functions.
8.565
8.566 def getColour(s):
8.567 @@ -309,7 +737,7 @@
8.568
8.569 mode=calendar shows a calendar view of events
8.570 mode=list shows a list of events by month
8.571 - mode=ics provides iCalendar data for the events
8.572 + mode=table shows a table of events
8.573
8.574 names=daily shows the name of an event on every day of that event
8.575 names=weekly shows the name of an event once per week
8.576 @@ -398,7 +826,7 @@
8.577 # Define a view of the calendar, retaining useful navigational information.
8.578
8.579 view = View(page, calendar_name, raw_calendar_start, raw_calendar_end, calendar_start, calendar_end,
8.580 - first, last, category_names, template_name, parent_name, mode)
8.581 + first, last, category_names, template_name, parent_name, mode, name_usage)
8.582
8.583 # Make a calendar.
8.584
8.585 @@ -555,295 +983,22 @@
8.586 week_start = month.as_date(max(first_day, 1))
8.587 week_end = month.as_date(min(first_day + 6, number_of_days))
8.588
8.589 - week_coverage, week_events = EventAggregatorSupport.getCoverage(
8.590 + full_coverage, week_slots = EventAggregatorSupport.getCoverage(
8.591 week_start, week_end, shown_events.get(month, []))
8.592
8.593 # Output a week, starting with the day numbers.
8.594
8.595 - output.append(fmt.table_row(on=1))
8.596 -
8.597 - for weekday in range(0, 7):
8.598 - day = first_day + weekday
8.599 - date = month.as_date(day)
8.600 -
8.601 - # Output out-of-month days.
8.602 -
8.603 - if day < 1 or day > number_of_days:
8.604 - output.append(fmt.table_cell(on=1,
8.605 - attrs={"class" : "event-day-heading event-day-excluded", "colspan" : "3"}))
8.606 - output.append(fmt.table_cell(on=0))
8.607 -
8.608 - # Output normal days.
8.609 -
8.610 - else:
8.611 - if date in week_coverage:
8.612 - output.append(fmt.table_cell(on=1,
8.613 - attrs={"class" : "event-day-heading event-day-busy", "colspan" : "3"}))
8.614 - else:
8.615 - output.append(fmt.table_cell(on=1,
8.616 - attrs={"class" : "event-day-heading event-day-empty", "colspan" : "3"}))
8.617 -
8.618 - # Output the day number, making a link to a new event
8.619 - # action.
8.620 -
8.621 - output.append(view.writeDayNumberLinked(date))
8.622 -
8.623 - # End of day.
8.624 -
8.625 - output.append(fmt.table_cell(on=0))
8.626 -
8.627 - # End of day numbers.
8.628 -
8.629 - output.append(fmt.table_row(on=0))
8.630 + output.append(view.writeDayNumbers(first_day, number_of_days, month, full_coverage))
8.631
8.632 # Either generate empty days...
8.633
8.634 - if not week_events:
8.635 - output.append(fmt.table_row(on=1))
8.636 -
8.637 - for weekday in range(0, 7):
8.638 - day = first_day + weekday
8.639 -
8.640 - # Output out-of-month days.
8.641 + if not week_slots:
8.642 + output.append(view.writeEmptyWeek(first_day, number_of_days))
8.643
8.644 - if day < 1 or day > number_of_days:
8.645 - output.append(fmt.table_cell(on=1,
8.646 - attrs={"class" : "event-day-content event-day-excluded", "colspan" : "3"}))
8.647 - output.append(fmt.table_cell(on=0))
8.648 -
8.649 - # Output empty days.
8.650 -
8.651 - else:
8.652 - output.append(fmt.table_cell(on=1,
8.653 - attrs={"class" : "event-day-content event-day-empty", "colspan" : "3"}))
8.654 -
8.655 - output.append(fmt.table_row(on=0))
8.656 -
8.657 - # Or visit each set of scheduled events...
8.658 + # Or generate each set of scheduled events...
8.659
8.660 else:
8.661 - for coverage, events in week_events:
8.662 -
8.663 - # Output each set.
8.664 -
8.665 - output.append(fmt.table_row(on=1))
8.666 -
8.667 - # Then, output day details.
8.668 -
8.669 - for weekday in range(0, 7):
8.670 - day = first_day + weekday
8.671 - date = month.as_date(day)
8.672 -
8.673 - # Skip out-of-month days.
8.674 -
8.675 - if day < 1 or day > number_of_days:
8.676 - output.append(fmt.table_cell(on=1,
8.677 - attrs={"class" : "event-day-content event-day-excluded", "colspan" : "3"}))
8.678 - output.append(fmt.table_cell(on=0))
8.679 - continue
8.680 -
8.681 - # Output the day.
8.682 -
8.683 - if date not in coverage:
8.684 - output.append(fmt.table_cell(on=1,
8.685 - attrs={"class" : "event-day-content event-day-empty", "colspan" : "3"}))
8.686 -
8.687 - # Get event details for the current day.
8.688 -
8.689 - for event in events:
8.690 - event_page = event.getPage()
8.691 - event_details = event.getDetails()
8.692 -
8.693 - if not (event_details["start"] <= date <= event_details["end"]):
8.694 - continue
8.695 -
8.696 - # Get basic properties of the event.
8.697 -
8.698 - starts_today = event_details["start"] == date
8.699 - ends_today = event_details["end"] == date
8.700 - event_summary = event.getSummary(parent_name)
8.701 - is_ambiguous = event_details["start"].ambiguous() or event_details["end"].ambiguous()
8.702 -
8.703 - # Generate a colour for the event.
8.704 -
8.705 - bg = getColour(event_summary)
8.706 - fg = getBlackOrWhite(bg)
8.707 - style = ("background-color: rgb(%d, %d, %d); color: rgb(%d, %d, %d);" % (bg + fg))
8.708 -
8.709 - # Determine if the event name should be shown.
8.710 -
8.711 - start_of_period = starts_today or weekday == 0 or day == 1
8.712 -
8.713 - if name_usage == "daily" or start_of_period:
8.714 - hide_text = 0
8.715 - else:
8.716 - hide_text = 1
8.717 -
8.718 - # Output start of day gap and determine whether
8.719 - # any event content should be explicitly output
8.720 - # for this day.
8.721 -
8.722 - if starts_today:
8.723 -
8.724 - # Single day events...
8.725 -
8.726 - if ends_today:
8.727 - colspan = 3
8.728 - event_day_type = "event-day-single"
8.729 -
8.730 - # Events starting today...
8.731 -
8.732 - else:
8.733 - output.append(fmt.table_cell(on=1, attrs={"class" : "event-day-start-gap"}))
8.734 - output.append(fmt.table_cell(on=0))
8.735 -
8.736 - # Calculate the span of this cell.
8.737 - # Events whose names appear on every day...
8.738 -
8.739 - if name_usage == "daily":
8.740 - colspan = 2
8.741 - event_day_type = "event-day-starting"
8.742 -
8.743 - # Events whose names appear once per week...
8.744 -
8.745 - else:
8.746 - if event_details["end"] <= week_end:
8.747 - event_length = event_details["end"].day() - day + 1
8.748 - colspan = (event_length - 2) * 3 + 4
8.749 - else:
8.750 - event_length = week_end.day() - day + 1
8.751 - colspan = (event_length - 1) * 3 + 2
8.752 -
8.753 - event_day_type = "event-day-multiple"
8.754 -
8.755 - # Events continuing from a previous week...
8.756 -
8.757 - elif start_of_period:
8.758 -
8.759 - # End of continuing event...
8.760 -
8.761 - if ends_today:
8.762 - colspan = 2
8.763 - event_day_type = "event-day-ending"
8.764 -
8.765 - # Events continuing for at least one more day...
8.766 -
8.767 - else:
8.768 -
8.769 - # Calculate the span of this cell.
8.770 - # Events whose names appear on every day...
8.771 -
8.772 - if name_usage == "daily":
8.773 - colspan = 3
8.774 - event_day_type = "event-day-full"
8.775 -
8.776 - # Events whose names appear once per week...
8.777 -
8.778 - else:
8.779 - if event_details["end"] <= week_end:
8.780 - event_length = event_details["end"].day() - day + 1
8.781 - colspan = (event_length - 1) * 3 + 2
8.782 - else:
8.783 - event_length = week_end.day() - day + 1
8.784 - colspan = event_length * 3
8.785 -
8.786 - event_day_type = "event-day-multiple"
8.787 -
8.788 - # Continuing events whose names appear on every day...
8.789 -
8.790 - elif name_usage == "daily":
8.791 - if ends_today:
8.792 - colspan = 2
8.793 - event_day_type = "event-day-ending"
8.794 - else:
8.795 - colspan = 3
8.796 - event_day_type = "event-day-full"
8.797 -
8.798 - # Continuing events whose names appear once per week...
8.799 -
8.800 - else:
8.801 - colspan = None
8.802 -
8.803 - # Output the main content only if it is not
8.804 - # continuing from a previous day.
8.805 -
8.806 - if colspan is not None:
8.807 -
8.808 - # Colour the cell for continuing events.
8.809 -
8.810 - attrs={
8.811 - "class" : "event-day-content event-day-busy %s" % event_day_type,
8.812 - "colspan" : str(colspan)
8.813 - }
8.814 -
8.815 - if not (starts_today and ends_today):
8.816 - attrs["style"] = style
8.817 -
8.818 - output.append(fmt.table_cell(on=1, attrs=attrs))
8.819 -
8.820 - # Output the event.
8.821 -
8.822 - if starts_today and ends_today or not hide_text:
8.823 -
8.824 - # The event box contains the summary, alongside
8.825 - # other elements.
8.826 -
8.827 - output.append(fmt.div(on=1, css_class="event-summary-box"))
8.828 - output.append(fmt.div(on=1, css_class="event-summary", style=style))
8.829 -
8.830 - if is_ambiguous:
8.831 - output.append(fmt.icon("/!\\"))
8.832 -
8.833 - output.append(event_page.linkToPage(request, event_summary))
8.834 - output.append(fmt.div(on=0))
8.835 -
8.836 - # Add a pop-up element for long summaries.
8.837 -
8.838 - output.append(fmt.div(on=1, css_class="event-summary-popup", style=style))
8.839 -
8.840 - if is_ambiguous:
8.841 - output.append(fmt.icon("/!\\"))
8.842 -
8.843 - output.append(event_page.linkToPage(request, event_summary))
8.844 - output.append(fmt.div(on=0))
8.845 -
8.846 - output.append(fmt.div(on=0))
8.847 -
8.848 - # Output end of day content.
8.849 -
8.850 - output.append(fmt.div(on=0))
8.851 -
8.852 - # Output end of day gap.
8.853 -
8.854 - if ends_today and not starts_today:
8.855 - output.append(fmt.table_cell(on=1, attrs={"class" : "event-day-end-gap"}))
8.856 - output.append(fmt.table_cell(on=0))
8.857 -
8.858 - # End of day.
8.859 -
8.860 - output.append(fmt.table_cell(on=0))
8.861 -
8.862 - # End of set.
8.863 -
8.864 - output.append(fmt.table_row(on=0))
8.865 -
8.866 - # Add a spacer.
8.867 -
8.868 - output.append(fmt.table_row(on=1))
8.869 -
8.870 - for weekday in range(0, 7):
8.871 - day = first_day + weekday
8.872 - css_classes = "event-day-spacer"
8.873 -
8.874 - # Skip out-of-month days.
8.875 -
8.876 - if day < 1 or day > number_of_days:
8.877 - css_classes += " event-day-excluded"
8.878 -
8.879 - output.append(fmt.table_cell(on=1, attrs={"class" : css_classes, "colspan" : "3"}))
8.880 - output.append(fmt.table_cell(on=0))
8.881 -
8.882 - output.append(fmt.table_row(on=0))
8.883 + output.append(view.writeWeekSlots(first_day, number_of_days, month, week_end, week_slots))
8.884
8.885 # Process the next week...
8.886
9.1 --- a/pages/HelpOnEventAggregator Sun Feb 06 02:38:17 2011 +0100
9.2 +++ b/pages/HelpOnEventAggregator Sat Feb 19 03:06:03 2011 +0100
9.3 @@ -108,6 +108,8 @@
9.4
9.5 To start a distinct event, just define a property that has already been recorded for the previous event on the page, if any. Usually, the start and end dates will be the most suitable properties for this purpose.
9.6
9.7 +Be careful ''not'' to start a distinct event with a property that was ''not'' recorded for the previous event: the result will be the property being added to the previous event, even if the property appears adjacent to the rest of the new event definition.
9.8 +
9.9 <<Anchor(TimeProblems)>>
9.10 === Occasional Problems with Times ===
9.11
10.1 --- a/setup.py Sun Feb 06 02:38:17 2011 +0100
10.2 +++ b/setup.py Sat Feb 19 03:06:03 2011 +0100
10.3 @@ -8,6 +8,6 @@
10.4 author = "Paul Boddie",
10.5 author_email = "paul@boddie.org.uk",
10.6 url = "http://moinmo.in/MacroMarket/EventAggregator",
10.7 - version = "0.6.1",
10.8 + version = "0.6.2",
10.9 py_modules = ["EventAggregatorSupport", "MoinMoin.script.import.eventfeed"]
10.10 )