1.1 --- a/EventAggregatorSupport.py Sat Jun 06 02:17:51 2009 +0200
1.2 +++ b/EventAggregatorSupport.py Sat Jun 06 23:37:14 2009 +0200
1.3 @@ -27,7 +27,14 @@
1.4 # Regular expressions where MoinMoin does not provide the required support.
1.5
1.6 category_regexp = None
1.7 -definition_list_regexp = re.compile(ur'^\s+(?P<term>.*?)::\s(?P<desc>.*?)$', re.UNICODE | re.MULTILINE)
1.8 +
1.9 +# Page parsing.
1.10 +
1.11 +definition_list_regexp = re.compile(ur'(?P<wholeterm>^(?P<optcomment>#*)\s+(?P<term>.*?)::\s)(?P<desc>.*?)$', re.UNICODE | re.MULTILINE)
1.12 +category_membership_regexp = re.compile(ur"^\s*((Category\S+)(\s+Category\S+)*)\s*$", re.MULTILINE | re.UNICODE)
1.13 +
1.14 +# Value parsing.
1.15 +
1.16 date_regexp = re.compile(ur'(?P<year>[0-9]{4})-(?P<month>[0-9]{2})-(?P<day>[0-9]{2})', re.UNICODE)
1.17 month_regexp = re.compile(ur'(?P<year>[0-9]{4})-(?P<month>[0-9]{2})', re.UNICODE)
1.18 verbatim_regexp = re.compile(ur'(?:'
1.19 @@ -144,6 +151,12 @@
1.20
1.21 return "".join([s for s in verbatim_regexp.split(text) if s is not None])
1.22
1.23 +def getEncodedWikiText(text):
1.24 +
1.25 + "Encode the given 'text' in a verbatim representation."
1.26 +
1.27 + return "<<Verbatim(%s)>>" % text
1.28 +
1.29 def getFormat(page):
1.30
1.31 "Get the format used on 'page'."
1.32 @@ -162,6 +175,11 @@
1.33 if getFormat(page) == "wiki":
1.34 for match in definition_list_regexp.finditer(page.get_raw_body()):
1.35
1.36 + # Skip commented-out items.
1.37 +
1.38 + if match.group("optcomment"):
1.39 + continue
1.40 +
1.41 # Permit case-insensitive list terms.
1.42
1.43 term = match.group("term").lower()
1.44 @@ -189,6 +207,71 @@
1.45
1.46 return event_details
1.47
1.48 +def setEventDetails(body, event_details):
1.49 +
1.50 + """
1.51 + Set the event details in the given page 'body' using the 'event_details'
1.52 + dictionary, returning the new body text.
1.53 + """
1.54 +
1.55 + new_body_parts = []
1.56 + end_of_last_match = 0
1.57 +
1.58 + for match in definition_list_regexp.finditer(body):
1.59 +
1.60 + # Add preceding text to the new body.
1.61 +
1.62 + new_body_parts.append(body[end_of_last_match:match.start()])
1.63 + end_of_last_match = match.end()
1.64 +
1.65 + # Get the matching regions, adding the term to the new body.
1.66 +
1.67 + new_body_parts.append(match.group("wholeterm"))
1.68 +
1.69 + # Permit case-insensitive list terms.
1.70 +
1.71 + term = match.group("term").lower()
1.72 + desc = match.group("desc")
1.73 +
1.74 + # Special value type handling.
1.75 +
1.76 + if event_details.has_key(term):
1.77 +
1.78 + # Dates.
1.79 +
1.80 + if term in ("start", "end"):
1.81 + desc = desc.replace("YYYY-MM-DD", event_details[term])
1.82 +
1.83 + # Lists (whose elements may be quoted).
1.84 +
1.85 + elif term in ("topics", "categories"):
1.86 + desc = ", ".join(getEncodedWikiText(event_details[term]))
1.87 +
1.88 + # Labels which may well be quoted.
1.89 +
1.90 + elif term in ("title", "summary", "description"):
1.91 + desc = getEncodedWikiText(event_details[term])
1.92 +
1.93 + new_body_parts.append(desc)
1.94 +
1.95 + else:
1.96 + new_body_parts.append(body[end_of_last_match:])
1.97 +
1.98 + return "".join(new_body_parts)
1.99 +
1.100 +def setCategoryMembership(body, category_names):
1.101 +
1.102 + """
1.103 + Set the category membership in the given page 'body' using the specified
1.104 + 'category_names' and returning the new body text.
1.105 + """
1.106 +
1.107 + match = category_membership_regexp.search(body)
1.108 + if match:
1.109 + return "".join([body[:match.start()], " ".join(category_names), body[match.end():]])
1.110 + else:
1.111 + return body
1.112 +
1.113 def getEventSummary(event_page, event_details):
1.114
1.115 """
3.1 --- a/README.txt Sat Jun 06 02:17:51 2009 +0200
3.2 +++ b/README.txt Sat Jun 06 23:37:14 2009 +0200
3.3 @@ -14,6 +14,12 @@
3.4 the request as URL or form parameters: these restrict the extent of each
3.5 generated summary.
3.6
3.7 +The EventAggregatorNewEvent action can be used to conveniently create new
3.8 +event pages, displaying a simple form which can be filled out in order to
3.9 +provide elementary event details such as the event title or summary, the
3.10 +categories to which the page will be assigned, and the start and end dates of
3.11 +the event.
3.12 +
3.13 Installation
3.14 ------------
3.15
3.16 @@ -54,7 +60,7 @@
3.17
3.18 This ensures that the styles are made available to the browser.
3.19
3.20 -To install the action in a Wiki, consider using the instactions script provided:
3.21 +To install the actions in a Wiki, consider using the instactions script provided:
3.22
3.23 ./instactions path-to-wiki
3.24
3.25 @@ -93,8 +99,8 @@
3.26
3.27 See pages/HelpOnEventAggregator for more detailed information.
3.28
3.29 -Using the Action
3.30 -----------------
3.31 +Using the Actions
3.32 +-----------------
3.33
3.34 To obtain an iCalendar summary, the EventAggregatorSummary action can be
3.35 selected from the actions menu on any page. Alternatively, a collection of
3.36 @@ -102,6 +108,13 @@
3.37
3.38 See pages/HelpOnEventAggregatorSummary for more detailed information.
3.39
3.40 +To create new events using the EventAggregatorNewEvent action, the appropriate
3.41 +menu entry can be selected in the actions menu. Alternatively, clicking on a
3.42 +day number in a calendar view will invoke the action and pre-fill the form
3.43 +with the start date set to the selected day from the calendar.
3.44 +
3.45 +See pages/HelpOnEventAggregatorNewEvent for more detailed information.
3.46 +
3.47 Recommended Software
3.48 --------------------
3.49
3.50 @@ -145,6 +158,7 @@
3.51 * Added a parameter to the EventAggregatorSummary action to select the
3.52 source of event descriptions for the RSS feed.
3.53 * Updated the documentation to cover the RSS support.
3.54 + * Added the EventAggregatorNewEvent action.
3.55
3.56 New in EventAggregator 0.2 (Changes since EventAggregator 0.1)
3.57 --------------------------------------------------------------
4.1 --- a/actions/EventAggregatorNewEvent.py Sat Jun 06 02:17:51 2009 +0200
4.2 +++ b/actions/EventAggregatorNewEvent.py Sat Jun 06 23:37:14 2009 +0200
4.3 @@ -25,17 +25,14 @@
4.4
4.5 "An event creation dialogue requesting various parameters."
4.6
4.7 - # NOTE: These patterns, used to replace text in the template, must
4.8 - # NOTE: correspond with the template and the recognised labels found in
4.9 - # NOTE: EventAggregatorSummary.getEventDetails.
4.10 + def _get_selected(self, month, input_month):
4.11 + return input_month is not None and month == input_month and 'selected="selected"' or ''
4.12
4.13 - start_pattern = re.compile("(?<= start:: ).*?(?P<date>yyyy-mm-dd)")
4.14 - end_pattern = re.compile("(?<= end:: ).*?(?P<date>yyyy-mm-dd)")
4.15 - topics_pattern = re.compile(r"(?<= categories:: )\s*(.*?)$|(?<= topics:: )\s*(.*?)$", re.MULTILINE | re.UNICODE)
4.16 - category_pattern = re.compile(r"^\s*((Category\S+)(\s+Category\S+)*)\s*$", re.MULTILINE | re.UNICODE)
4.17 -
4.18 - def _get_selected(self, month, input_month):
4.19 - return (("%02d" % month) == input_month) and 'selected="selected"' or ''
4.20 + def _get_input(self, form, name, default, error):
4.21 + try:
4.22 + return int(form.get(name, [None])[0] or default)
4.23 + except ValueError:
4.24 + return error
4.25
4.26 def get_form_html(self, buttons_html):
4.27 _ = self._
4.28 @@ -58,8 +55,10 @@
4.29 end_month_list = []
4.30 end_month_list.append('<option value=""></option>')
4.31
4.32 - start_month = form.get("start-month", [None])[0]
4.33 - end_month = form.get("end-month", [None])[0]
4.34 + start_month = self._get_input(form, "start-month", 0, None)
4.35 + end_month = self._get_input(form, "end-month", 0, None)
4.36 + start_year = self._get_input(form, "start-year", EventAggregatorSupport.getCurrentYear(), None)
4.37 + end_year = self._get_input(form, "end-year", 0, None)
4.38
4.39 # Prepare month lists, selecting specified months.
4.40
4.41 @@ -78,7 +77,7 @@
4.42 "end_month_list" : "\n".join(end_month_list),
4.43 "start_label" : _("Start date (day, month, year)"),
4.44 "start_day_default" : form.get("start-day", [""])[0],
4.45 - "start_year_default" : form.get("start-year", [""])[0],
4.46 + "start_year_default" : start_year,
4.47 "end_label" : _("End date (day, month, year)"),
4.48 "end_day_default" : form.get("end-day", [""])[0],
4.49 "end_year_default" : form.get("end-year", [""])[0],
4.50 @@ -159,17 +158,18 @@
4.51 "Create an event page using the 'request'."
4.52
4.53 _ = request.getText
4.54 + form = request.form
4.55
4.56 - category_names = request.form.get("category", [])
4.57 - title = request.form.get("title", [None])[0]
4.58 + category_names = form.get("category", [])
4.59 + title = form.get("title", [None])[0]
4.60
4.61 try:
4.62 - start_day = int(request.form.get("start-day", [None])[0] or 0)
4.63 - start_month = int(request.form.get("start-month", [None])[0] or 0)
4.64 - start_year = int(request.form.get("start-year", [None])[0] or 0)
4.65 - end_day = int(request.form.get("end-day", [None])[0] or start_day)
4.66 - end_month = int(request.form.get("end-month", [None])[0] or start_month)
4.67 - end_year = int(request.form.get("end-year", [None])[0] or start_year)
4.68 + start_day = self._get_input(form, "start-day", 0, None)
4.69 + start_month = self._get_input(form, "start-month", 0, None)
4.70 + start_year = self._get_input(form, "start-year", 0, None)
4.71 + end_day = self._get_input(form, "end-day", start_day, None)
4.72 + end_month = self._get_input(form, "end-month", start_month, None)
4.73 + end_year = self._get_input(form, "end-year", start_year, None)
4.74
4.75 start_year, start_month, start_day = self.constrain_date(start_year, start_month, start_day)
4.76 end_year, end_month, end_day = self.constrain_date(end_year, end_month, end_day)
4.77 @@ -186,35 +186,25 @@
4.78 page = PageEditor(request, getattr(request.cfg, "event_aggregator_new_event_template", "EventTemplate"))
4.79 body = page.get_raw_body()
4.80
4.81 - # Load the new page and replace the placeholders.
4.82 + # Load the new page and replace the event details in the body.
4.83
4.84 new_page = PageEditor(request, title)
4.85
4.86 if new_page.exists():
4.87 return 0, _("The specified page already exists. Please choose another name.")
4.88
4.89 - # NOTE: The placeholders must match the template.
4.90 -
4.91 - body = self.replace_placeholder(body, self.start_pattern, start_date, 1)
4.92 - body = self.replace_placeholder(body, self.end_pattern, end_date, 1)
4.93 - body = self.replace_placeholder(body, self.category_pattern, " ".join(category_names))
4.94 -
4.95 - new_page.saveText(body, 0)
4.96 + if EventAggregatorSupport.getFormat(page) == "wiki":
4.97 + event_details = {"start" : start_date, "end" : end_date, "title" : title, "summary" : title}
4.98 + body = EventAggregatorSupport.setEventDetails(body, event_details)
4.99 + body = EventAggregatorSupport.setCategoryMembership(body, category_names)
4.100 + new_page.saveText(body, 0)
4.101
4.102 # Redirect and return success.
4.103
4.104 - query = {'action' : 'edit', 'backto' : self.pagename}
4.105 + query = {'action' : 'edit'}
4.106 request.http_redirect(new_page.url(request, query))
4.107 return 1, None
4.108
4.109 - def replace_placeholder(self, body, pattern, value, lower=0):
4.110 - match = pattern.search(lower and body.lower() or body)
4.111 - if match:
4.112 - start, end = match.span()
4.113 - return body[:start] + value + body[end:]
4.114 - else:
4.115 - return body
4.116 -
4.117 def make_date_string(self, year, month, day):
4.118 return "%s-%s-%s" % (
4.119 year and ("%04d" % year) or "YYYY",
6.1 --- a/pages/HelpOnEventAggregator Sat Jun 06 02:17:51 2009 +0200
6.2 +++ b/pages/HelpOnEventAggregator Sat Jun 06 23:37:14 2009 +0200
6.3 @@ -13,9 +13,13 @@
6.4
6.5 <<NewPage(CategoryTemplate,Add a new category,,Category%s)>>
6.6
6.7 -Each event must be created on a new page belonging to the appropriate event category. For CategoryEvents, you can do this by filling out and submitting this form:
6.8 +Each event must be created on a new page belonging to the appropriate event category. The following action can be used to create a new event page (using !EventAggregatorNewEvent):
6.9 +
6.10 +(!) <<Action(EventAggregatorNewEvent,Add an event)>>
6.11
6.12 -<<NewPage(EventTemplate,Add an event)>>
6.13 +Since each event is represented by a page, creating a new page based on an appropriate template is also sufficient. For pages belonging to CategoryEvents, you can do this by filling out and submitting this form (which uses EventTemplate):
6.14 +
6.15 +<<NewPage(EventTemplate,Add an event page)>>
6.16
6.17 The event page describes the event in more detail, and the start and end dates of the event must be specified in a definition list so that they can be read from the page and displayed by the !EventAggregator. The EventTemplate provides some guidance, and all you need to do is to replace the `YYYY-MM-DD` placeholders with actual year, month and day values. For example:
6.18
6.19 @@ -124,4 +128,5 @@
6.20
6.21 == See Also ==
6.22
6.23 + * HelpOnEventAggregatorNewEvent - an action providing a form for creating new events conveniently
6.24 * HelpOnEventAggregatorSummary - an action producing iCalendar event summaries