EventAggregator

macros/EventAggregator.py

68:e3ab5b5e9464
2010-01-28 Paul Boddie Made the compareEvents function the EventPage.__cmp__ method with minor alterations.
     1 # -*- coding: iso-8859-1 -*-     2 """     3     MoinMoin - EventAggregator Macro     4      5     @copyright: 2008, 2009 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 MoinMoin import wikiutil    12 import EventAggregatorSupport    13 import calendar    14     15 linkToPage = EventAggregatorSupport.linkToPage    16     17 try:    18     set    19 except NameError:    20     from sets import Set as set    21     22 Dependencies = ['pages']    23     24 # Abstractions.    25     26 class View:    27     28     "A view of the event calendar."    29     30     def __init__(self, page, calendar_name, calendar_start, calendar_end,    31         first, last, category_names, template_name, parent_name, mode):    32     33         """    34         Initialise the view with the current 'page', a 'calendar_name' (which    35         may be None), the requested 'calendar_start' and 'calendar_end', and the    36         'first' and 'last' months of event coverage.    37     38         The additional 'category_names', 'template_name', 'parent_name' and    39         'mode' parameters are used to configure the links employed by the view.    40         """    41     42         self.page = page    43         self.calendar_name = calendar_name    44         self.calendar_start = calendar_start    45         self.calendar_end = calendar_end    46         self.template_name = template_name    47         self.parent_name = parent_name    48         self.mode = mode    49     50         self.category_name_parameters = "&".join([("category=%s" % name) for name in category_names])    51     52         if self.calendar_name is not None:    53     54             # Store the view parameters.    55     56             self.number_of_months = (last - first).months() + 1    57     58             self.previous_month_start = first.previous_month()    59             self.next_month_start = first.next_month()    60             self.previous_month_end = last.previous_month()    61             self.next_month_end = last.next_month()    62     63             self.previous_set_start = first.month_update(-self.number_of_months)    64             self.next_set_start = first.month_update(self.number_of_months)    65             self.previous_set_end = last.month_update(-self.number_of_months)    66             self.next_set_end = last.month_update(self.number_of_months)    67     68     def getQualifiedParameterName(self, argname):    69         return EventAggregatorSupport.getQualifiedParameterName(self.calendar_name, argname)    70     71     def getMonthQueryString(self, argname, month, prefix=1):    72         if month is not None:    73             if prefix:    74                 argname = self.getQualifiedParameterName(argname)    75             return "%s=%s" % (argname, month)    76         else:    77             return ""    78     79     def getNavigationLink(self, start, end, mode=None):    80         return "%s&%s&%s=%s" % (    81             self.getMonthQueryString("start", start),    82             self.getMonthQueryString("end", end),    83             self.getQualifiedParameterName("mode"), mode or self.mode    84             )    85     86     def writeDownloadControls(self):    87         page = self.page    88         request = page.request    89         fmt = page.formatter    90         _ = request.getText    91     92         output = []    93     94         # Generate the links.    95     96         download_all_link = "action=EventAggregatorSummary&doit=1&%s" % self.category_name_parameters    97         download_link = download_all_link + ("&%s&%s" % (    98             self.getMonthQueryString("start", self.calendar_start, prefix=0),    99             self.getMonthQueryString("end", self.calendar_end, prefix=0)   100             ))   101         subscribe_all_link = download_all_link + "&format=RSS"   102         subscribe_link = download_link + "&format=RSS"   103    104         # Write the controls.   105    106         output.append(fmt.div(on=1, css_class="event-download-controls"))   107         output.append(fmt.span(on=1, css_class="event-download"))   108         output.append(linkToPage(request, page, _("Download this view"), download_link))   109         output.append(fmt.span(on=0))   110         output.append(fmt.span(on=1, css_class="event-download"))   111         output.append(linkToPage(request, page, _("Download this calendar"), download_all_link))   112         output.append(fmt.span(on=0))   113         output.append(fmt.span(on=1, css_class="event-download"))   114         output.append(linkToPage(request, page, _("Subscribe to this view"), subscribe_link))   115         output.append(fmt.span(on=0))   116         output.append(fmt.span(on=1, css_class="event-download"))   117         output.append(linkToPage(request, page, _("Subscribe to this calendar"), subscribe_all_link))   118         output.append(fmt.span(on=0))   119         output.append(fmt.div(on=0))   120    121         return "".join(output)   122    123     def writeViewControls(self):   124         page = self.page   125         request = page.request   126         fmt = page.formatter   127         _ = request.getText   128    129         output = []   130    131         calendar_link = self.getNavigationLink(   132             self.calendar_start, self.calendar_end, "calendar"   133             )   134         list_link = self.getNavigationLink(   135             self.calendar_start, self.calendar_end, "list"   136             )   137         table_link = self.getNavigationLink(   138             self.calendar_start, self.calendar_end, "table"   139             )   140    141         # Write the controls.   142    143         output.append(fmt.div(on=1, css_class="event-view-controls"))   144         output.append(fmt.span(on=1, css_class="event-view"))   145         output.append(linkToPage(request, page, _("View as calendar"), calendar_link))   146         output.append(fmt.span(on=0))   147         output.append(fmt.span(on=1, css_class="event-view"))   148         output.append(linkToPage(request, page, _("View as list"), list_link))   149         output.append(fmt.span(on=0))   150         output.append(fmt.span(on=1, css_class="event-view"))   151         output.append(linkToPage(request, page, _("View as table"), table_link))   152         output.append(fmt.span(on=0))   153         output.append(fmt.div(on=0))   154    155         return "".join(output)   156    157     def writeMonthHeading(self, year_month):   158         page = self.page   159         request = page.request   160         fmt = page.formatter   161         _ = request.getText   162    163         output = []   164    165         year, month = year_month.as_tuple()   166         month_label = _(EventAggregatorSupport.getMonthLabel(month))   167    168         # Prepare navigation links.   169    170         if self.calendar_name is not None:   171             calendar_name = self.calendar_name   172    173             # Links to the previous set of months and to a calendar shifted   174             # back one month.   175    176             previous_set_link = self.getNavigationLink(   177                 self.previous_set_start, self.previous_set_end   178                 )   179             previous_month_link = self.getNavigationLink(   180                 self.previous_month_start, self.previous_month_end   181                 )   182    183             # Links to the next set of months and to a calendar shifted   184             # forward one month.   185    186             next_set_link = self.getNavigationLink(   187                 self.next_set_start, self.next_set_end   188                 )   189             next_month_link = self.getNavigationLink(   190                 self.next_month_start, self.next_month_end   191                 )   192    193             # A link leading to this month being at the top of the calendar.   194    195             full_month_label = "%s %s" % (month_label, year)   196             end_month = year_month.month_update(self.number_of_months - 1)   197    198             month_link = self.getNavigationLink(year_month, end_month)   199    200             output.append(fmt.span(on=1, css_class="previous-month"))   201             output.append(linkToPage(request, page, "<<", previous_set_link))   202             output.append(fmt.text(" "))   203             output.append(linkToPage(request, page, "<", previous_month_link))   204             output.append(fmt.span(on=0))   205    206             output.append(fmt.span(on=1, css_class="next-month"))   207             output.append(linkToPage(request, page, ">", next_month_link))   208             output.append(fmt.text(" "))   209             output.append(linkToPage(request, page, ">>", next_set_link))   210             output.append(fmt.span(on=0))   211    212             output.append(linkToPage(request, page, full_month_label, month_link))   213    214         else:   215             output.append(fmt.span(on=1))   216             output.append(fmt.text(month_label))   217             output.append(fmt.span(on=0))   218             output.append(fmt.text(" "))   219             output.append(fmt.span(on=1))   220             output.append(fmt.text(unicode(year)))   221             output.append(fmt.span(on=0))   222    223         return "".join(output)   224    225     def writeDayNumberLinked(self, date):   226         page = self.page   227         request = page.request   228         fmt = page.formatter   229         _ = request.getText   230    231         year, month, day = date.as_tuple()   232         output = []   233    234         # Prepare navigation details for the calendar shown with the new event   235         # form.   236    237         navigation_link = self.getNavigationLink(   238             self.calendar_start, self.calendar_end, self.mode   239             )   240    241         # Prepare the link to the new event form, incorporating the above   242         # calendar parameters.   243    244         new_event_link = "action=EventAggregatorNewEvent&start-day=%d&start-month=%d&start-year=%d" \   245             "&%s&template=%s&parent=%s&%s" % (   246             day, month, year, self.category_name_parameters, self.template_name, self.parent_name or "",   247             navigation_link)   248    249         output.append(fmt.div(on=1))   250         output.append(fmt.span(on=1, css_class="event-day-number"))   251         output.append(linkToPage(request, page, unicode(day), new_event_link))   252         output.append(fmt.span(on=0))   253         output.append(fmt.div(on=0))   254    255         return "".join(output)   256    257 # HTML-related functions.   258    259 def getColour(s):   260     colour = [0, 0, 0]   261     digit = 0   262     for c in s:   263         colour[digit] += ord(c)   264         colour[digit] = colour[digit] % 256   265         digit += 1   266         digit = digit % 3   267     return tuple(colour)   268    269 def getBlackOrWhite(colour):   270     if sum(colour) / 3.0 > 127:   271         return (0, 0, 0)   272     else:   273         return (255, 255, 255)   274    275 # Macro functions.   276    277 def execute(macro, args):   278    279     """   280     Execute the 'macro' with the given 'args': an optional list of selected   281     category names (categories whose pages are to be shown), together with   282     optional named arguments of the following forms:   283    284       start=YYYY-MM     shows event details starting from the specified month   285       start=current-N   shows event details relative to the current month   286       end=YYYY-MM       shows event details ending at the specified month   287       end=current+N     shows event details relative to the current month   288    289       mode=calendar     shows a calendar view of events   290       mode=list         shows a list of events by month   291       mode=ics          provides iCalendar data for the events   292    293       names=daily       shows the name of an event on every day of that event   294       names=weekly      shows the name of an event once per week   295    296       calendar=NAME     uses the given NAME to provide request parameters which   297                         can be used to control the calendar view   298    299       template=PAGE     uses the given PAGE as the default template for new   300                         events (or the default template from the configuration   301                         if not specified)   302    303       parent=PAGE       uses the given PAGE as the parent of any new event page   304     """   305    306     request = macro.request   307     fmt = macro.formatter   308     page = fmt.page   309     _ = request.getText   310    311     # Interpret the arguments.   312    313     try:   314         parsed_args = args and wikiutil.parse_quoted_separated(args, name_value=False) or []   315     except AttributeError:   316         parsed_args = args.split(",")   317    318     parsed_args = [arg for arg in parsed_args if arg]   319    320     # Get special arguments.   321    322     category_names = []   323     calendar_start = None   324     calendar_end = None   325     mode = None   326     name_usage = "weekly"   327     calendar_name = None   328     template_name = getattr(request.cfg, "event_aggregator_new_event_template", "EventTemplate")   329     parent_name = None   330    331     for arg in parsed_args:   332         if arg.startswith("start="):   333             calendar_start = EventAggregatorSupport.getParameterMonth(arg[6:])   334    335         elif arg.startswith("end="):   336             calendar_end = EventAggregatorSupport.getParameterMonth(arg[4:])   337    338         elif arg.startswith("mode="):   339             mode = arg[5:]   340    341         elif arg.startswith("names="):   342             name_usage = arg[6:]   343    344         elif arg.startswith("calendar="):   345             calendar_name = arg[9:]   346    347         elif arg.startswith("template="):   348             template_name = arg[9:]   349    350         elif arg.startswith("parent="):   351             parent_name = arg[7:]   352    353         else:   354             category_names.append(arg)   355    356     # Find request parameters to override settings.   357    358     if calendar_name is not None:   359         calendar_start = EventAggregatorSupport.getFormMonth(request, calendar_name, "start") or calendar_start   360         calendar_end = EventAggregatorSupport.getFormMonth(request, calendar_name, "end") or calendar_end   361    362     mode = EventAggregatorSupport.getQualifiedParameter(request, calendar_name, "mode", mode or "calendar")   363    364     # Get the events.   365    366     events, shown_events, all_shown_events, earliest, latest = \   367         EventAggregatorSupport.getEvents(request, category_names, calendar_start, calendar_end)   368    369     # Get a concrete period of time.   370    371     first, last = EventAggregatorSupport.getConcretePeriod(calendar_start, calendar_end, earliest, latest)   372    373     # Define a view of the calendar, retaining useful navigational information.   374    375     view = View(page, calendar_name, calendar_start, calendar_end, first, last, category_names, template_name, parent_name, mode)   376    377     # Make a calendar.   378    379     output = []   380    381     # Output download controls.   382    383     output.append(fmt.div(on=1, css_class="event-controls"))   384     output.append(view.writeDownloadControls())   385     output.append(fmt.div(on=0))   386    387     # Output a table.   388    389     if mode == "table":   390    391         # Start of table view output.   392    393         output.append(fmt.table(on=1, attrs={"tableclass" : "event-table"}))   394    395         output.append(fmt.table_row(on=1))   396         output.append(fmt.table_cell(on=1, attrs={"class" : "event-table-heading"}))   397         output.append(fmt.text(_("Event dates")))   398         output.append(fmt.table_cell(on=0))   399         output.append(fmt.table_cell(on=1, attrs={"class" : "event-table-heading"}))   400         output.append(fmt.text(_("Event location")))   401         output.append(fmt.table_cell(on=0))   402         output.append(fmt.table_cell(on=1, attrs={"class" : "event-table-heading"}))   403         output.append(fmt.text(_("Event details")))   404         output.append(fmt.table_cell(on=0))   405         output.append(fmt.table_row(on=0))   406    407         # Get the events in order.   408    409         ordered_events = EventAggregatorSupport.getOrderedEvents(all_shown_events)   410    411         # Show the events in order.   412    413         for event_page in ordered_events:   414             event_summary = event_page.getEventSummary(parent_name)   415             event_details = event_page.getEventDetails()   416    417             # Prepare CSS classes with category-related styling.   418    419             css_classes = ["event-table-details"]   420    421             for topic in event_details.get("topics") or event_details.get("categories") or []:   422    423                 # Filter the category text to avoid illegal characters.   424    425                 css_classes.append("event-table-category-%s" % "".join(filter(lambda c: c.isalnum(), topic)))   426    427             attrs = {"class" : " ".join(css_classes)}   428    429             output.append(fmt.table_row(on=1))   430    431             # Start and end dates.   432    433             output.append(fmt.table_cell(on=1, attrs=attrs))   434             output.append(fmt.span(on=1))   435             output.append(fmt.text(str(event_details["start"])))   436             output.append(fmt.span(on=0))   437    438             if event_details["start"] != event_details["end"]:   439                 output.append(fmt.text(" - "))   440                 output.append(fmt.span(on=1))   441                 output.append(fmt.text(str(event_details["end"])))   442                 output.append(fmt.span(on=0))   443    444             output.append(fmt.table_cell(on=0))   445    446             # Location.   447    448             output.append(fmt.table_cell(on=1, attrs=attrs))   449    450             if event_details.has_key("location"):   451                 output.append(fmt.text(event_details["location"]))   452    453             output.append(fmt.table_cell(on=0))   454    455             # Link to the page using the summary.   456    457             output.append(fmt.table_cell(on=1, attrs=attrs))   458             output.append(event_page.linkToPage(request, event_summary))   459             output.append(fmt.table_cell(on=0))   460    461             output.append(fmt.table_row(on=0))   462    463         # End of table view output.   464    465         output.append(fmt.table(on=0))   466    467     # Output a list or calendar.   468    469     elif mode in ("list", "calendar"):   470    471         # Output top-level information.   472    473         # Start of list view output.   474    475         if mode == "list":   476             output.append(fmt.bullet_list(on=1, attr={"class" : "event-listings"}))   477    478         # Visit all months in the requested range, or across known events.   479    480         for month in first.months_until(last):   481    482             # Either output a calendar view...   483    484             if mode == "calendar":   485    486                 # Output a month.   487    488                 output.append(fmt.table(on=1, attrs={"tableclass" : "event-month"}))   489    490                 output.append(fmt.table_row(on=1))   491                 output.append(fmt.table_cell(on=1, attrs={"class" : "event-month-heading", "colspan" : "21"}))   492    493                 # Either write a month heading or produce links for navigable   494                 # calendars.   495    496                 output.append(view.writeMonthHeading(month))   497    498                 output.append(fmt.table_cell(on=0))   499                 output.append(fmt.table_row(on=0))   500    501                 # Weekday headings.   502    503                 output.append(fmt.table_row(on=1))   504    505                 for weekday in range(0, 7):   506                     output.append(fmt.table_cell(on=1, attrs={"class" : "event-weekday-heading", "colspan" : "3"}))   507                     output.append(fmt.text(_(EventAggregatorSupport.getDayLabel(weekday))))   508                     output.append(fmt.table_cell(on=0))   509    510                 output.append(fmt.table_row(on=0))   511    512                 # Process the days of the month.   513    514                 start_weekday, number_of_days = month.month_properties()   515    516                 # The start weekday is the weekday of day number 1.   517                 # Find the first day of the week, counting from below zero, if   518                 # necessary, in order to land on the first day of the month as   519                 # day number 1.   520    521                 first_day = 1 - start_weekday   522    523                 while first_day <= number_of_days:   524    525                     # Find events in this week and determine how to mark them on the   526                     # calendar.   527    528                     week_start = month.as_date(max(first_day, 1))   529                     week_end = month.as_date(min(first_day + 6, number_of_days))   530    531                     week_coverage, week_events = EventAggregatorSupport.getCoverage(   532                         week_start, week_end, shown_events.get(month, []))   533    534                     # Output a week, starting with the day numbers.   535    536                     output.append(fmt.table_row(on=1))   537    538                     for weekday in range(0, 7):   539                         day = first_day + weekday   540                         date = month.as_date(day)   541    542                         # Output out-of-month days.   543    544                         if day < 1 or day > number_of_days:   545                             output.append(fmt.table_cell(on=1,   546                                 attrs={"class" : "event-day-heading event-day-excluded", "colspan" : "3"}))   547                             output.append(fmt.table_cell(on=0))   548    549                         # Output normal days.   550    551                         else:   552                             if date in week_coverage:   553                                 output.append(fmt.table_cell(on=1,   554                                     attrs={"class" : "event-day-heading event-day-busy", "colspan" : "3"}))   555                             else:   556                                 output.append(fmt.table_cell(on=1,   557                                     attrs={"class" : "event-day-heading event-day-empty", "colspan" : "3"}))   558    559                             # Output the day number, making a link to a new event   560                             # action.   561    562                             output.append(view.writeDayNumberLinked(date))   563    564                             # End of day.   565    566                             output.append(fmt.table_cell(on=0))   567    568                     # End of day numbers.   569    570                     output.append(fmt.table_row(on=0))   571    572                     # Either generate empty days...   573    574                     if not week_events:   575                         output.append(fmt.table_row(on=1))   576    577                         for weekday in range(0, 7):   578                             day = first_day + weekday   579    580                             # Output out-of-month days.   581    582                             if day < 1 or day > number_of_days:   583                                 output.append(fmt.table_cell(on=1,   584                                     attrs={"class" : "event-day-content event-day-excluded", "colspan" : "3"}))   585                                 output.append(fmt.table_cell(on=0))   586    587                             # Output empty days.   588    589                             else:   590                                 output.append(fmt.table_cell(on=1,   591                                     attrs={"class" : "event-day-content event-day-empty", "colspan" : "3"}))   592    593                         output.append(fmt.table_row(on=0))   594    595                     # Or visit each set of scheduled events...   596    597                     else:   598                         for coverage, events in week_events:   599    600                             # Output each set.   601    602                             output.append(fmt.table_row(on=1))   603    604                             # Then, output day details.   605    606                             for weekday in range(0, 7):   607                                 day = first_day + weekday   608                                 date = month.as_date(day)   609    610                                 # Skip out-of-month days.   611    612                                 if day < 1 or day > number_of_days:   613                                     output.append(fmt.table_cell(on=1,   614                                         attrs={"class" : "event-day-content event-day-excluded", "colspan" : "3"}))   615                                     output.append(fmt.table_cell(on=0))   616                                     continue   617    618                                 # Output the day.   619    620                                 if date not in coverage:   621                                     output.append(fmt.table_cell(on=1,   622                                         attrs={"class" : "event-day-content event-day-empty", "colspan" : "3"}))   623    624                                 # Get event details for the current day.   625    626                                 for event_page in events:   627                                     event_details = event_page.getEventDetails()   628    629                                     if not (event_details["start"] <= date <= event_details["end"]):   630                                         continue   631    632                                     # Get basic properties of the event.   633    634                                     starts_today = event_details["start"] == date   635                                     ends_today = event_details["end"] == date   636                                     event_summary = event_page.getEventSummary(parent_name)   637    638                                     # Generate a colour for the event.   639    640                                     bg = getColour(event_summary)   641                                     fg = getBlackOrWhite(bg)   642                                     style = ("background-color: rgb(%d, %d, %d); color: rgb(%d, %d, %d);" % (bg + fg))   643    644                                     # Determine if the event name should be shown.   645    646                                     start_of_period = starts_today or weekday == 0 or day == 1   647    648                                     if name_usage == "daily" or start_of_period:   649                                         hide_text = 0   650                                     else:   651                                         hide_text = 1   652    653                                     # Output start of day gap and determine whether   654                                     # any event content should be explicitly output   655                                     # for this day.   656    657                                     if starts_today:   658    659                                         # Single day events...   660    661                                         if ends_today:   662                                             colspan = 3   663                                             event_day_type = "event-day-single"   664    665                                         # Events starting today...   666    667                                         else:   668                                             output.append(fmt.table_cell(on=1, attrs={"class" : "event-day-start-gap"}))   669                                             output.append(fmt.table_cell(on=0))   670    671                                             # Calculate the span of this cell.   672                                             # Events whose names appear on every day...   673    674                                             if name_usage == "daily":   675                                                 colspan = 2   676                                                 event_day_type = "event-day-starting"   677    678                                             # Events whose names appear once per week...   679    680                                             else:   681                                                 if event_details["end"] <= week_end:   682                                                     event_length = event_details["end"].day() - day + 1   683                                                     colspan = (event_length - 2) * 3 + 4   684                                                 else:   685                                                     event_length = week_end.day() - day + 1   686                                                     colspan = (event_length - 1) * 3 + 2   687    688                                                 event_day_type = "event-day-multiple"   689    690                                     # Events continuing from a previous week...   691    692                                     elif start_of_period:   693    694                                         # End of continuing event...   695    696                                         if ends_today:   697                                             colspan = 2   698                                             event_day_type = "event-day-ending"   699    700                                         # Events continuing for at least one more day...   701    702                                         else:   703    704                                             # Calculate the span of this cell.   705                                             # Events whose names appear on every day...   706    707                                             if name_usage == "daily":   708                                                 colspan = 3   709                                                 event_day_type = "event-day-full"   710    711                                             # Events whose names appear once per week...   712    713                                             else:   714                                                 if event_details["end"] <= week_end:   715                                                     event_length = event_details["end"].day() - day + 1   716                                                     colspan = (event_length - 1) * 3 + 2   717                                                 else:   718                                                     event_length = week_end.day() - day + 1   719                                                     colspan = event_length * 3   720    721                                                 event_day_type = "event-day-multiple"   722    723                                     # Continuing events whose names appear on every day...   724    725                                     elif name_usage == "daily":   726                                         if ends_today:   727                                             colspan = 2   728                                             event_day_type = "event-day-ending"   729                                         else:   730                                             colspan = 3   731                                             event_day_type = "event-day-full"   732    733                                     # Continuing events whose names appear once per week...   734    735                                     else:   736                                         colspan = None   737    738                                     # Output the main content only if it is not   739                                     # continuing from a previous day.   740    741                                     if colspan is not None:   742    743                                         # Colour the cell for continuing events.   744    745                                         attrs={   746                                             "class" : "event-day-content event-day-busy %s" % event_day_type,   747                                             "colspan" : str(colspan)   748                                             }   749    750                                         if not (starts_today and ends_today):   751                                             attrs["style"] = style   752    753                                         output.append(fmt.table_cell(on=1, attrs=attrs))   754    755                                         # Output the event.   756    757                                         if starts_today and ends_today or not hide_text:   758    759                                             output.append(fmt.div(on=1, css_class="event-summary-box"))   760                                             output.append(fmt.div(on=1, css_class="event-summary", style=style))   761                                             output.append(event_page.linkToPage(request, event_summary))   762                                             output.append(fmt.div(on=0))   763    764                                             # Add a pop-up element for long summaries.   765    766                                             output.append(fmt.div(on=1, css_class="event-summary-popup", style=style))   767                                             output.append(event_page.linkToPage(request, event_summary))   768                                             output.append(fmt.div(on=0))   769    770                                             output.append(fmt.div(on=0))   771    772                                         # Output end of day content.   773    774                                         output.append(fmt.div(on=0))   775    776                                     # Output end of day gap.   777    778                                     if ends_today and not starts_today:   779                                         output.append(fmt.table_cell(on=1, attrs={"class" : "event-day-end-gap"}))   780                                         output.append(fmt.table_cell(on=0))   781    782                                 # End of day.   783    784                                 output.append(fmt.table_cell(on=0))   785    786                             # End of set.   787    788                             output.append(fmt.table_row(on=0))   789    790                             # Add a spacer.   791    792                             output.append(fmt.table_row(on=1))   793    794                             for weekday in range(0, 7):   795                                 day = first_day + weekday   796                                 css_classes = "event-day-spacer"   797    798                                 # Skip out-of-month days.   799    800                                 if day < 1 or day > number_of_days:   801                                     css_classes += " event-day-excluded"   802    803                                 output.append(fmt.table_cell(on=1, attrs={"class" : css_classes, "colspan" : "3"}))   804                                 output.append(fmt.table_cell(on=0))   805    806                             output.append(fmt.table_row(on=0))   807    808                     # Process the next week...   809    810                     first_day += 7   811    812                 # End of month.   813    814                 output.append(fmt.table(on=0))   815    816             # Or output a summary view...   817    818             elif mode == "list":   819    820                 # Output a list.   821    822                 output.append(fmt.listitem(on=1, attr={"class" : "event-listings-month"}))   823                 output.append(fmt.div(on=1, attr={"class" : "event-listings-month-heading"}))   824    825                 # Either write a month heading or produce links for navigable   826                 # calendars.   827    828                 output.append(view.writeMonthHeading(month))   829    830                 output.append(fmt.div(on=0))   831    832                 output.append(fmt.bullet_list(on=1, attr={"class" : "event-month-listings"}))   833    834                 # Get the events in order.   835    836                 ordered_events = EventAggregatorSupport.getOrderedEvents(shown_events.get(month, []))   837    838                 # Show the events in order.   839    840                 for event_page in ordered_events:   841                     event_details = event_page.getEventDetails()   842                     event_summary = event_page.getEventSummary(parent_name)   843    844                     output.append(fmt.listitem(on=1, attr={"class" : "event-listing"}))   845    846                     # Link to the page using the summary.   847    848                     output.append(fmt.paragraph(on=1))   849                     output.append(event_page.linkToPage(request, event_summary))   850                     output.append(fmt.paragraph(on=0))   851    852                     # Start and end dates.   853    854                     output.append(fmt.paragraph(on=1))   855                     output.append(fmt.span(on=1))   856                     output.append(fmt.text(str(event_details["start"])))   857                     output.append(fmt.span(on=0))   858                     output.append(fmt.text(" - "))   859                     output.append(fmt.span(on=1))   860                     output.append(fmt.text(str(event_details["end"])))   861                     output.append(fmt.span(on=0))   862                     output.append(fmt.paragraph(on=0))   863    864                     # Location.   865    866                     if event_details.has_key("location"):   867                         output.append(fmt.paragraph(on=1))   868                         output.append(fmt.text(event_details["location"]))   869                         output.append(fmt.paragraph(on=1))   870    871                     # Topics.   872    873                     if event_details.has_key("topics") or event_details.has_key("categories"):   874                         output.append(fmt.bullet_list(on=1, attr={"class" : "event-topics"}))   875    876                         for topic in event_details.get("topics") or event_details.get("categories") or []:   877                             output.append(fmt.listitem(on=1))   878                             output.append(fmt.text(topic))   879                             output.append(fmt.listitem(on=0))   880    881                         output.append(fmt.bullet_list(on=0))   882    883                     output.append(fmt.listitem(on=0))   884    885                 output.append(fmt.bullet_list(on=0))   886    887         # Output top-level information.   888    889         # End of list view output.   890    891         if mode == "list":   892             output.append(fmt.bullet_list(on=0))   893    894     # Output view controls.   895    896     output.append(fmt.div(on=1, css_class="event-controls"))   897     output.append(view.writeViewControls())   898     output.append(fmt.div(on=0))   899    900     return ''.join(output)   901    902 # vim: tabstop=4 expandtab shiftwidth=4