EventAggregator

macros/EventAggregator.py

64:a86bb9c82190
2010-01-06 Paul Boddie Made substituted title/summary text use only the specified title, not the full page title.
     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             span = EventAggregatorSupport.span(first, last)    57             self.number_of_months = span[0] * 12 + span[1] + 1    58     59             self.previous_month_start = EventAggregatorSupport.prevmonth(first)    60             self.next_month_start = EventAggregatorSupport.nextmonth(first)    61             self.previous_month_end = EventAggregatorSupport.prevmonth(last)    62             self.next_month_end = EventAggregatorSupport.nextmonth(last)    63     64             self.previous_set_start = EventAggregatorSupport.monthupdate(first, -self.number_of_months)    65             self.next_set_start = EventAggregatorSupport.monthupdate(first, self.number_of_months)    66             self.previous_set_end = EventAggregatorSupport.monthupdate(last, -self.number_of_months)    67             self.next_set_end = EventAggregatorSupport.monthupdate(last, self.number_of_months)    68     69     def getQualifiedParameterName(self, argname):    70         return EventAggregatorSupport.getQualifiedParameterName(self.calendar_name, argname)    71     72     def getMonthQueryString(self, argname, month, prefix=1):    73         if month is not None:    74             if prefix:    75                 argname = self.getQualifiedParameterName(argname)    76             return "%s=%04d-%02d" % ((argname,) + month)    77         else:    78             return ""    79     80     def getNavigationLink(self, start, end, mode=None):    81         return "%s&%s&%s=%s" % (    82             self.getMonthQueryString("start", start),    83             self.getMonthQueryString("end", end),    84             self.getQualifiedParameterName("mode"), mode or self.mode    85             )    86     87     def writeDownloadControls(self):    88         page = self.page    89         request = page.request    90         fmt = page.formatter    91         _ = request.getText    92     93         output = []    94     95         # Generate the links.    96     97         download_all_link = "action=EventAggregatorSummary&doit=1&%s" % self.category_name_parameters    98         download_link = download_all_link + ("&%s&%s" % (    99             self.getMonthQueryString("start", self.calendar_start, prefix=0),   100             self.getMonthQueryString("end", self.calendar_end, prefix=0)   101             ))   102         subscribe_all_link = download_all_link + "&format=RSS"   103         subscribe_link = download_link + "&format=RSS"   104    105         # Write the controls.   106    107         output.append(fmt.div(on=1, css_class="event-download-controls"))   108         output.append(fmt.span(on=1, css_class="event-download"))   109         output.append(linkToPage(request, page, _("Download this view"), download_link))   110         output.append(fmt.span(on=0))   111         output.append(fmt.span(on=1, css_class="event-download"))   112         output.append(linkToPage(request, page, _("Download this calendar"), download_all_link))   113         output.append(fmt.span(on=0))   114         output.append(fmt.span(on=1, css_class="event-download"))   115         output.append(linkToPage(request, page, _("Subscribe to this view"), subscribe_link))   116         output.append(fmt.span(on=0))   117         output.append(fmt.span(on=1, css_class="event-download"))   118         output.append(linkToPage(request, page, _("Subscribe to this calendar"), subscribe_all_link))   119         output.append(fmt.span(on=0))   120         output.append(fmt.div(on=0))   121    122         return "".join(output)   123    124     def writeViewControls(self):   125         page = self.page   126         request = page.request   127         fmt = page.formatter   128         _ = request.getText   129    130         output = []   131    132         calendar_link = self.getNavigationLink(   133             self.calendar_start, self.calendar_end, "calendar"   134             )   135         list_link = self.getNavigationLink(   136             self.calendar_start, self.calendar_end, "list"   137             )   138         table_link = self.getNavigationLink(   139             self.calendar_start, self.calendar_end, "table"   140             )   141    142         # Write the controls.   143    144         output.append(fmt.div(on=1, css_class="event-view-controls"))   145         output.append(fmt.span(on=1, css_class="event-view"))   146         output.append(linkToPage(request, page, _("View as calendar"), calendar_link))   147         output.append(fmt.span(on=0))   148         output.append(fmt.span(on=1, css_class="event-view"))   149         output.append(linkToPage(request, page, _("View as list"), list_link))   150         output.append(fmt.span(on=0))   151         output.append(fmt.span(on=1, css_class="event-view"))   152         output.append(linkToPage(request, page, _("View as table"), table_link))   153         output.append(fmt.span(on=0))   154         output.append(fmt.div(on=0))   155    156         return "".join(output)   157    158     def writeMonthHeading(self, year, month):   159         page = self.page   160         request = page.request   161         fmt = page.formatter   162         _ = request.getText   163    164         output = []   165    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 = EventAggregatorSupport.monthupdate((year, month), self.number_of_months - 1)   197    198             month_link = self.getNavigationLink(   199                 (year, month), end_month   200                 )   201    202             output.append(fmt.span(on=1, css_class="previous-month"))   203             output.append(linkToPage(request, page, "<<", previous_set_link))   204             output.append(fmt.text(" "))   205             output.append(linkToPage(request, page, "<", previous_month_link))   206             output.append(fmt.span(on=0))   207    208             output.append(fmt.span(on=1, css_class="next-month"))   209             output.append(linkToPage(request, page, ">", next_month_link))   210             output.append(fmt.text(" "))   211             output.append(linkToPage(request, page, ">>", next_set_link))   212             output.append(fmt.span(on=0))   213    214             output.append(linkToPage(request, page, full_month_label, month_link))   215    216         else:   217             output.append(fmt.span(on=1))   218             output.append(fmt.text(month_label))   219             output.append(fmt.span(on=0))   220             output.append(fmt.text(" "))   221             output.append(fmt.span(on=1))   222             output.append(fmt.text(unicode(year)))   223             output.append(fmt.span(on=0))   224    225         return "".join(output)   226    227     def writeDayNumberLinked(self, day, month, year):   228         page = self.page   229         request = page.request   230         fmt = page.formatter   231         _ = request.getText   232    233         output = []   234    235         # Prepare navigation details for the calendar shown with the new event   236         # form.   237    238         navigation_link = self.getNavigationLink(   239             self.calendar_start, self.calendar_end, self.mode   240             )   241    242         # Prepare the link to the new event form, incorporating the above   243         # calendar parameters.   244    245         new_event_link = "action=EventAggregatorNewEvent&start-day=%d&start-month=%d&start-year=%d" \   246             "&%s&template=%s&parent=%s&%s" % (   247             day, month, year, self.category_name_parameters, self.template_name, self.parent_name or "",   248             navigation_link)   249    250         output.append(fmt.div(on=1))   251         output.append(fmt.span(on=1, css_class="event-day-number"))   252         output.append(linkToPage(request, page, unicode(day), new_event_link))   253         output.append(fmt.span(on=0))   254         output.append(fmt.div(on=0))   255    256         return "".join(output)   257    258 # HTML-related functions.   259    260 def getColour(s):   261     colour = [0, 0, 0]   262     digit = 0   263     for c in s:   264         colour[digit] += ord(c)   265         colour[digit] = colour[digit] % 256   266         digit += 1   267         digit = digit % 3   268     return tuple(colour)   269    270 def getBlackOrWhite(colour):   271     if sum(colour) / 3.0 > 127:   272         return (0, 0, 0)   273     else:   274         return (255, 255, 255)   275    276 # Macro functions.   277    278 def execute(macro, args):   279    280     """   281     Execute the 'macro' with the given 'args': an optional list of selected   282     category names (categories whose pages are to be shown), together with   283     optional named arguments of the following forms:   284    285       start=YYYY-MM     shows event details starting from the specified month   286       start=current-N   shows event details relative to the current month   287       end=YYYY-MM       shows event details ending at the specified month   288       end=current+N     shows event details relative to the current month   289    290       mode=calendar     shows a calendar view of events   291       mode=list         shows a list of events by month   292       mode=ics          provides iCalendar data for the events   293    294       names=daily       shows the name of an event on every day of that event   295       names=weekly      shows the name of an event once per week   296    297       calendar=NAME     uses the given NAME to provide request parameters which   298                         can be used to control the calendar view   299    300       template=PAGE     uses the given PAGE as the default template for new   301                         events (or the default template from the configuration   302                         if not specified)   303    304       parent=PAGE       uses the given PAGE as the parent of any new event page   305     """   306    307     request = macro.request   308     fmt = macro.formatter   309     page = fmt.page   310     _ = request.getText   311    312     # Interpret the arguments.   313    314     try:   315         parsed_args = args and wikiutil.parse_quoted_separated(args, name_value=False) or []   316     except AttributeError:   317         parsed_args = args.split(",")   318    319     parsed_args = [arg for arg in parsed_args if arg]   320    321     # Get special arguments.   322    323     category_names = []   324     calendar_start = None   325     calendar_end = None   326     mode = None   327     name_usage = "weekly"   328     calendar_name = None   329     template_name = getattr(request.cfg, "event_aggregator_new_event_template", "EventTemplate")   330     parent_name = None   331    332     for arg in parsed_args:   333         if arg.startswith("start="):   334             calendar_start = EventAggregatorSupport.getParameterMonth(arg[6:])   335    336         elif arg.startswith("end="):   337             calendar_end = EventAggregatorSupport.getParameterMonth(arg[4:])   338    339         elif arg.startswith("mode="):   340             mode = arg[5:]   341    342         elif arg.startswith("names="):   343             name_usage = arg[6:]   344    345         elif arg.startswith("calendar="):   346             calendar_name = arg[9:]   347    348         elif arg.startswith("template="):   349             template_name = arg[9:]   350    351         elif arg.startswith("parent="):   352             parent_name = arg[7:]   353    354         else:   355             category_names.append(arg)   356    357     # Find request parameters to override settings.   358    359     if calendar_name is not None:   360         calendar_start = EventAggregatorSupport.getFormMonth(request, calendar_name, "start") or calendar_start   361         calendar_end = EventAggregatorSupport.getFormMonth(request, calendar_name, "end") or calendar_end   362    363     mode = EventAggregatorSupport.getQualifiedParameter(request, calendar_name, "mode", mode or "calendar")   364    365     # Get the events.   366    367     events, shown_events, all_shown_events, earliest, latest = \   368         EventAggregatorSupport.getEvents(request, category_names, calendar_start, calendar_end)   369    370     # Get a concrete period of time.   371    372     first, last = EventAggregatorSupport.getConcretePeriod(calendar_start, calendar_end, earliest, latest)   373    374     # Define a view of the calendar, retaining useful navigational information.   375    376     view = View(page, calendar_name, calendar_start, calendar_end, first, last, category_names, template_name, parent_name, mode)   377    378     # Make a calendar.   379    380     output = []   381    382     # Output download controls.   383    384     output.append(fmt.div(on=1, css_class="event-controls"))   385     output.append(view.writeDownloadControls())   386     output.append(fmt.div(on=0))   387    388     # Output a table.   389    390     if mode == "table":   391    392         # Start of table view output.   393    394         output.append(fmt.table(on=1, attrs={"tableclass" : "event-table"}))   395    396         output.append(fmt.table_row(on=1))   397         output.append(fmt.table_cell(on=1, attrs={"class" : "event-table-heading"}))   398         output.append(fmt.text(_("Event dates")))   399         output.append(fmt.table_cell(on=0))   400         output.append(fmt.table_cell(on=1, attrs={"class" : "event-table-heading"}))   401         output.append(fmt.text(_("Event location")))   402         output.append(fmt.table_cell(on=0))   403         output.append(fmt.table_cell(on=1, attrs={"class" : "event-table-heading"}))   404         output.append(fmt.text(_("Event details")))   405         output.append(fmt.table_cell(on=0))   406         output.append(fmt.table_row(on=0))   407    408         # Get the events in order.   409    410         ordered_events = EventAggregatorSupport.getOrderedEvents(all_shown_events)   411    412         # Show the events in order.   413    414         for event_page, event_details in ordered_events:   415             event_summary = EventAggregatorSupport.getEventSummary(event_page, event_details, parent_name)   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("%04d-%02d-%02d" % 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("%04d-%02d-%02d" % 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(linkToPage(request, event_page, 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 year, month in EventAggregatorSupport.daterange(first, 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(year, 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 = calendar.monthrange(year, month)   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 = (year, month, max(first_day, 1))   529                     week_end = (year, month, min(first_day + 6, number_of_days))   530    531                     week_coverage, week_events = EventAggregatorSupport.getCoverage(   532                         week_start, week_end, shown_events.get((year, 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 = (year, month, 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(day, month, year))   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                             date = (year, month, day)   580    581                             # Output out-of-month days.   582    583                             if day < 1 or day > number_of_days:   584                                 output.append(fmt.table_cell(on=1,   585                                     attrs={"class" : "event-day-content event-day-excluded", "colspan" : "3"}))   586                                 output.append(fmt.table_cell(on=0))   587    588                             # Output empty days.   589    590                             else:   591                                 output.append(fmt.table_cell(on=1,   592                                     attrs={"class" : "event-day-content event-day-empty", "colspan" : "3"}))   593    594                         output.append(fmt.table_row(on=0))   595    596                     # Or visit each set of scheduled events...   597    598                     else:   599                         for coverage, events in week_events:   600    601                             # Output each set.   602    603                             output.append(fmt.table_row(on=1))   604    605                             # Then, output day details.   606    607                             for weekday in range(0, 7):   608                                 day = first_day + weekday   609                                 date = (year, month, day)   610    611                                 # Skip out-of-month days.   612    613                                 if day < 1 or day > number_of_days:   614                                     output.append(fmt.table_cell(on=1,   615                                         attrs={"class" : "event-day-content event-day-excluded", "colspan" : "3"}))   616                                     output.append(fmt.table_cell(on=0))   617                                     continue   618    619                                 # Output the day.   620    621                                 if date not in coverage:   622                                     output.append(fmt.table_cell(on=1,   623                                         attrs={"class" : "event-day-content event-day-empty", "colspan" : "3"}))   624    625                                 # Get event details for the current day.   626    627                                 for event_page, event_details in events:   628                                     if not (event_details["start"] <= date <= event_details["end"]):   629                                         continue   630    631                                     # Get basic properties of the event.   632    633                                     starts_today = event_details["start"] == date   634                                     ends_today = event_details["end"] == date   635                                     event_summary = EventAggregatorSupport.getEventSummary(event_page, event_details, parent_name)   636    637                                     # Generate a colour for the event.   638    639                                     bg = getColour(event_page.page_name)   640                                     fg = getBlackOrWhite(bg)   641                                     style = ("background-color: rgb(%d, %d, %d); color: rgb(%d, %d, %d);" % (bg + fg))   642    643                                     # Determine if the event name should be shown.   644    645                                     start_of_period = starts_today or weekday == 0 or day == 1   646    647                                     if name_usage == "daily" or start_of_period:   648                                         hide_text = 0   649                                     else:   650                                         hide_text = 1   651    652                                     # Output start of day gap and determine whether   653                                     # any event content should be explicitly output   654                                     # for this day.   655    656                                     if starts_today:   657    658                                         # Single day events...   659    660                                         if ends_today:   661                                             colspan = 3   662                                             event_day_type = "event-day-single"   663    664                                         # Events starting today...   665    666                                         else:   667                                             output.append(fmt.table_cell(on=1, attrs={"class" : "event-day-start-gap"}))   668                                             output.append(fmt.table_cell(on=0))   669    670                                             # Calculate the span of this cell.   671                                             # Events whose names appear on every day...   672    673                                             if name_usage == "daily":   674                                                 colspan = 2   675                                                 event_day_type = "event-day-starting"   676    677                                             # Events whose names appear once per week...   678    679                                             else:   680                                                 if event_details["end"] <= week_end:   681                                                     event_length = event_details["end"][2] - day + 1   682                                                     colspan = (event_length - 2) * 3 + 4   683                                                 else:   684                                                     event_length = week_end[2] - day + 1   685                                                     colspan = (event_length - 1) * 3 + 2   686    687                                                 event_day_type = "event-day-multiple"   688    689                                     # Events continuing from a previous week...   690    691                                     elif start_of_period:   692    693                                         # End of continuing event...   694    695                                         if ends_today:   696                                             colspan = 2   697                                             event_day_type = "event-day-ending"   698    699                                         # Events continuing for at least one more day...   700    701                                         else:   702    703                                             # Calculate the span of this cell.   704                                             # Events whose names appear on every day...   705    706                                             if name_usage == "daily":   707                                                 colspan = 3   708                                                 event_day_type = "event-day-full"   709    710                                             # Events whose names appear once per week...   711    712                                             else:   713                                                 if event_details["end"] <= week_end:   714                                                     event_length = event_details["end"][2] - day + 1   715                                                     colspan = (event_length - 1) * 3 + 2   716                                                 else:   717                                                     event_length = week_end[2] - day + 1   718                                                     colspan = event_length * 3   719    720                                                 event_day_type = "event-day-multiple"   721    722                                     # Continuing events whose names appear on every day...   723    724                                     elif name_usage == "daily":   725                                         if ends_today:   726                                             colspan = 2   727                                             event_day_type = "event-day-ending"   728                                         else:   729                                             colspan = 3   730                                             event_day_type = "event-day-full"   731    732                                     # Continuing events whose names appear once per week...   733    734                                     else:   735                                         colspan = None   736    737                                     # Output the main content only if it is not   738                                     # continuing from a previous day.   739    740                                     if colspan is not None:   741    742                                         # Colour the cell for continuing events.   743    744                                         attrs={   745                                             "class" : "event-day-content event-day-busy %s" % event_day_type,   746                                             "colspan" : str(colspan)   747                                             }   748    749                                         if not (starts_today and ends_today):   750                                             attrs["style"] = style   751    752                                         output.append(fmt.table_cell(on=1, attrs=attrs))   753    754                                         # Output the event.   755    756                                         if starts_today and ends_today or not hide_text:   757    758                                             output.append(fmt.div(on=1, css_class="event-summary-box"))   759                                             output.append(fmt.div(on=1, css_class="event-summary", style=style))   760                                             output.append(linkToPage(request, event_page, event_summary))   761                                             output.append(fmt.div(on=0))   762    763                                             # Add a pop-up element for long summaries.   764    765                                             output.append(fmt.div(on=1, css_class="event-summary-popup", style=style))   766                                             output.append(linkToPage(request, event_page, event_summary))   767                                             output.append(fmt.div(on=0))   768    769                                             output.append(fmt.div(on=0))   770    771                                         # Output end of day content.   772    773                                         output.append(fmt.div(on=0))   774    775                                     # Output end of day gap.   776    777                                     if ends_today and not starts_today:   778                                         output.append(fmt.table_cell(on=1, attrs={"class" : "event-day-end-gap"}))   779                                         output.append(fmt.table_cell(on=0))   780    781                                 # End of day.   782    783                                 output.append(fmt.table_cell(on=0))   784    785                             # End of set.   786    787                             output.append(fmt.table_row(on=0))   788    789                             # Add a spacer.   790    791                             output.append(fmt.table_row(on=1))   792    793                             for weekday in range(0, 7):   794                                 day = first_day + weekday   795                                 css_classes = "event-day-spacer"   796    797                                 # Skip out-of-month days.   798    799                                 if day < 1 or day > number_of_days:   800                                     css_classes += " event-day-excluded"   801    802                                 output.append(fmt.table_cell(on=1, attrs={"class" : css_classes, "colspan" : "3"}))   803                                 output.append(fmt.table_cell(on=0))   804    805                             output.append(fmt.table_row(on=0))   806    807                     # Process the next week...   808    809                     first_day += 7   810    811                 # End of month.   812    813                 output.append(fmt.table(on=0))   814    815             # Or output a summary view...   816    817             elif mode == "list":   818    819                 # Output a list.   820    821                 output.append(fmt.listitem(on=1, attr={"class" : "event-listings-month"}))   822                 output.append(fmt.div(on=1, attr={"class" : "event-listings-month-heading"}))   823    824                 # Either write a month heading or produce links for navigable   825                 # calendars.   826    827                 output.append(view.writeMonthHeading(year, month))   828    829                 output.append(fmt.div(on=0))   830    831                 output.append(fmt.bullet_list(on=1, attr={"class" : "event-month-listings"}))   832    833                 # Get the events in order.   834    835                 ordered_events = EventAggregatorSupport.getOrderedEvents(shown_events.get((year, month), []))   836    837                 # Show the events in order.   838    839                 for event_page, event_details in ordered_events:   840                     event_summary = EventAggregatorSupport.getEventSummary(event_page, event_details, parent_name)   841    842                     output.append(fmt.listitem(on=1, attr={"class" : "event-listing"}))   843    844                     # Link to the page using the summary.   845    846                     output.append(fmt.paragraph(on=1))   847                     output.append(linkToPage(request, event_page, event_summary))   848                     output.append(fmt.paragraph(on=0))   849    850                     # Start and end dates.   851    852                     output.append(fmt.paragraph(on=1))   853                     output.append(fmt.span(on=1))   854                     output.append(fmt.text("%04d-%02d-%02d" % event_details["start"]))   855                     output.append(fmt.span(on=0))   856                     output.append(fmt.text(" - "))   857                     output.append(fmt.span(on=1))   858                     output.append(fmt.text("%04d-%02d-%02d" % event_details["end"]))   859                     output.append(fmt.span(on=0))   860                     output.append(fmt.paragraph(on=0))   861    862                     # Location.   863    864                     if event_details.has_key("location"):   865                         output.append(fmt.paragraph(on=1))   866                         output.append(fmt.text(event_details["location"]))   867                         output.append(fmt.paragraph(on=1))   868    869                     # Topics.   870    871                     if event_details.has_key("topics") or event_details.has_key("categories"):   872                         output.append(fmt.bullet_list(on=1, attr={"class" : "event-topics"}))   873    874                         for topic in event_details.get("topics") or event_details.get("categories") or []:   875                             output.append(fmt.listitem(on=1))   876                             output.append(fmt.text(topic))   877                             output.append(fmt.listitem(on=0))   878    879                         output.append(fmt.bullet_list(on=0))   880    881                     output.append(fmt.listitem(on=0))   882    883                 output.append(fmt.bullet_list(on=0))   884    885         # Output top-level information.   886    887         # End of list view output.   888    889         if mode == "list":   890             output.append(fmt.bullet_list(on=0))   891    892     # Output view controls.   893    894     output.append(fmt.div(on=1, css_class="event-controls"))   895     output.append(view.writeViewControls())   896     output.append(fmt.div(on=0))   897    898     return ''.join(output)   899    900 # vim: tabstop=4 expandtab shiftwidth=4