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 top-level information. 389 390 # Start of list view output. 391 392 if mode == "list": 393 output.append(fmt.bullet_list(on=1, attr={"class" : "event-listings"})) 394 395 # Start of table view output. 396 397 elif mode == "table": 398 399 # Output a table. 400 401 output.append(fmt.table(on=1, attrs={"tableclass" : "event-table"})) 402 403 output.append(fmt.table_row(on=1)) 404 output.append(fmt.table_cell(on=1, attrs={"class" : "event-table-heading"})) 405 output.append(fmt.text(_("Event dates"))) 406 output.append(fmt.table_cell(on=0)) 407 output.append(fmt.table_cell(on=1, attrs={"class" : "event-table-heading"})) 408 output.append(fmt.text(_("Event location"))) 409 output.append(fmt.table_cell(on=0)) 410 output.append(fmt.table_cell(on=1, attrs={"class" : "event-table-heading"})) 411 output.append(fmt.text(_("Event details"))) 412 output.append(fmt.table_cell(on=0)) 413 output.append(fmt.table_row(on=0)) 414 415 # Visit all months in the requested range, or across known events. 416 417 for year, month in EventAggregatorSupport.daterange(first, last): 418 419 # Either output a calendar view... 420 421 if mode == "calendar": 422 423 # Output a month. 424 425 output.append(fmt.table(on=1, attrs={"tableclass" : "event-month"})) 426 427 output.append(fmt.table_row(on=1)) 428 output.append(fmt.table_cell(on=1, attrs={"class" : "event-month-heading", "colspan" : "21"})) 429 430 # Either write a month heading or produce links for navigable 431 # calendars. 432 433 output.append(view.writeMonthHeading(year, month)) 434 435 output.append(fmt.table_cell(on=0)) 436 output.append(fmt.table_row(on=0)) 437 438 # Weekday headings. 439 440 output.append(fmt.table_row(on=1)) 441 442 for weekday in range(0, 7): 443 output.append(fmt.table_cell(on=1, attrs={"class" : "event-weekday-heading", "colspan" : "3"})) 444 output.append(fmt.text(_(EventAggregatorSupport.getDayLabel(weekday)))) 445 output.append(fmt.table_cell(on=0)) 446 447 output.append(fmt.table_row(on=0)) 448 449 # Process the days of the month. 450 451 start_weekday, number_of_days = calendar.monthrange(year, month) 452 453 # The start weekday is the weekday of day number 1. 454 # Find the first day of the week, counting from below zero, if 455 # necessary, in order to land on the first day of the month as 456 # day number 1. 457 458 first_day = 1 - start_weekday 459 460 while first_day <= number_of_days: 461 462 # Find events in this week and determine how to mark them on the 463 # calendar. 464 465 week_start = (year, month, max(first_day, 1)) 466 week_end = (year, month, min(first_day + 6, number_of_days)) 467 468 week_coverage, week_events = EventAggregatorSupport.getCoverage( 469 week_start, week_end, shown_events.get((year, month), [])) 470 471 # Output a week, starting with the day numbers. 472 473 output.append(fmt.table_row(on=1)) 474 475 for weekday in range(0, 7): 476 day = first_day + weekday 477 date = (year, month, day) 478 479 # Output out-of-month days. 480 481 if day < 1 or day > number_of_days: 482 output.append(fmt.table_cell(on=1, attrs={"class" : "event-day-heading event-day-excluded", "colspan" : "3"})) 483 output.append(fmt.table_cell(on=0)) 484 485 # Output normal days. 486 487 else: 488 if date in week_coverage: 489 output.append(fmt.table_cell(on=1, attrs={"class" : "event-day-heading event-day-busy", "colspan" : "3"})) 490 else: 491 output.append(fmt.table_cell(on=1, attrs={"class" : "event-day-heading event-day-empty", "colspan" : "3"})) 492 493 # Output the day number, making a link to a new event 494 # action. 495 496 output.append(view.writeDayNumberLinked(day, month, year)) 497 498 # End of day. 499 500 output.append(fmt.table_cell(on=0)) 501 502 # End of day numbers. 503 504 output.append(fmt.table_row(on=0)) 505 506 # Either generate empty days... 507 508 if not week_events: 509 output.append(fmt.table_row(on=1)) 510 511 for weekday in range(0, 7): 512 day = first_day + weekday 513 date = (year, month, day) 514 515 # Output out-of-month days. 516 517 if day < 1 or day > number_of_days: 518 output.append(fmt.table_cell(on=1, 519 attrs={"class" : "event-day-content event-day-excluded", "colspan" : "3"})) 520 output.append(fmt.table_cell(on=0)) 521 522 # Output empty days. 523 524 else: 525 output.append(fmt.table_cell(on=1, 526 attrs={"class" : "event-day-content event-day-empty", "colspan" : "3"})) 527 528 output.append(fmt.table_row(on=0)) 529 530 # Or visit each set of scheduled events... 531 532 else: 533 for coverage, events in week_events: 534 535 # Output each set. 536 537 output.append(fmt.table_row(on=1)) 538 539 # Then, output day details. 540 541 for weekday in range(0, 7): 542 day = first_day + weekday 543 date = (year, month, day) 544 545 # Skip out-of-month days. 546 547 if day < 1 or day > number_of_days: 548 output.append(fmt.table_cell(on=1, 549 attrs={"class" : "event-day-content event-day-excluded", "colspan" : "3"})) 550 output.append(fmt.table_cell(on=0)) 551 continue 552 553 # Output the day. 554 555 if date not in coverage: 556 output.append(fmt.table_cell(on=1, 557 attrs={"class" : "event-day-content event-day-empty", "colspan" : "3"})) 558 559 # Get event details for the current day. 560 561 for event_page, event_details in events: 562 if not (event_details["start"] <= date <= event_details["end"]): 563 continue 564 565 # Get basic properties of the event. 566 567 starts_today = event_details["start"] == date 568 ends_today = event_details["end"] == date 569 event_summary = EventAggregatorSupport.getEventSummary(event_page, event_details, parent_name) 570 571 # Generate a colour for the event. 572 573 bg = getColour(event_page.page_name) 574 fg = getBlackOrWhite(bg) 575 style = ("background-color: rgb(%d, %d, %d); color: rgb(%d, %d, %d);" % (bg + fg)) 576 577 # Determine if the event name should be shown. 578 579 start_of_period = starts_today or weekday == 0 or day == 1 580 581 if name_usage == "daily" or start_of_period: 582 hide_text = 0 583 else: 584 hide_text = 1 585 586 # Output start of day gap and determine whether 587 # any event content should be explicitly output 588 # for this day. 589 590 if starts_today: 591 592 # Single day events... 593 594 if ends_today: 595 colspan = 3 596 event_day_type = "event-day-single" 597 598 # Events starting today... 599 600 else: 601 output.append(fmt.table_cell(on=1, attrs={"class" : "event-day-start-gap"})) 602 output.append(fmt.table_cell(on=0)) 603 604 # Calculate the span of this cell. 605 # Events whose names appear on every day... 606 607 if name_usage == "daily": 608 colspan = 2 609 event_day_type = "event-day-starting" 610 611 # Events whose names appear once per week... 612 613 else: 614 if event_details["end"] <= week_end: 615 event_length = event_details["end"][2] - day + 1 616 colspan = (event_length - 2) * 3 + 4 617 else: 618 event_length = week_end[2] - day + 1 619 colspan = (event_length - 1) * 3 + 2 620 621 event_day_type = "event-day-multiple" 622 623 # Events continuing from a previous week... 624 625 elif start_of_period: 626 627 # End of continuing event... 628 629 if ends_today: 630 colspan = 2 631 event_day_type = "event-day-ending" 632 633 # Events continuing for at least one more day... 634 635 else: 636 637 # Calculate the span of this cell. 638 # Events whose names appear on every day... 639 640 if name_usage == "daily": 641 colspan = 3 642 event_day_type = "event-day-full" 643 644 # Events whose names appear once per week... 645 646 else: 647 if event_details["end"] <= week_end: 648 event_length = event_details["end"][2] - day + 1 649 colspan = (event_length - 1) * 3 + 2 650 else: 651 event_length = week_end[2] - day + 1 652 colspan = event_length * 3 653 654 event_day_type = "event-day-multiple" 655 656 # Continuing events whose names appear on every day... 657 658 elif name_usage == "daily": 659 if ends_today: 660 colspan = 2 661 event_day_type = "event-day-ending" 662 else: 663 colspan = 3 664 event_day_type = "event-day-full" 665 666 # Continuing events whose names appear once per week... 667 668 else: 669 colspan = None 670 671 # Output the main content only if it is not 672 # continuing from a previous day. 673 674 if colspan is not None: 675 676 # Colour the cell for continuing events. 677 678 attrs={ 679 "class" : "event-day-content event-day-busy %s" % event_day_type, 680 "colspan" : str(colspan) 681 } 682 683 if not (starts_today and ends_today): 684 attrs["style"] = style 685 686 output.append(fmt.table_cell(on=1, attrs=attrs)) 687 688 # Output the event. 689 690 if starts_today and ends_today or not hide_text: 691 692 output.append(fmt.div(on=1, css_class="event-summary-box")) 693 output.append(fmt.div(on=1, css_class="event-summary", style=style)) 694 output.append(linkToPage(request, event_page, event_summary)) 695 output.append(fmt.div(on=0)) 696 697 # Add a pop-up element for long summaries. 698 699 output.append(fmt.div(on=1, css_class="event-summary-popup", style=style)) 700 output.append(linkToPage(request, event_page, event_summary)) 701 output.append(fmt.div(on=0)) 702 703 output.append(fmt.div(on=0)) 704 705 # Output end of day content. 706 707 output.append(fmt.div(on=0)) 708 709 # Output end of day gap. 710 711 if ends_today and not starts_today: 712 output.append(fmt.table_cell(on=1, attrs={"class" : "event-day-end-gap"})) 713 output.append(fmt.table_cell(on=0)) 714 715 # End of day. 716 717 output.append(fmt.table_cell(on=0)) 718 719 # End of set. 720 721 output.append(fmt.table_row(on=0)) 722 723 # Add a spacer. 724 725 output.append(fmt.table_row(on=1)) 726 727 for weekday in range(0, 7): 728 day = first_day + weekday 729 css_classes = "event-day-spacer" 730 731 # Skip out-of-month days. 732 733 if day < 1 or day > number_of_days: 734 css_classes += " event-day-excluded" 735 736 output.append(fmt.table_cell(on=1, attrs={"class" : css_classes, "colspan" : "3"})) 737 output.append(fmt.table_cell(on=0)) 738 739 output.append(fmt.table_row(on=0)) 740 741 # Process the next week... 742 743 first_day += 7 744 745 # End of month. 746 747 output.append(fmt.table(on=0)) 748 749 # Or output a summary view... 750 751 elif mode == "list": 752 753 # Output a list. 754 755 output.append(fmt.listitem(on=1, attr={"class" : "event-listings-month"})) 756 output.append(fmt.div(on=1, attr={"class" : "event-listings-month-heading"})) 757 758 # Either write a month heading or produce links for navigable 759 # calendars. 760 761 output.append(view.writeMonthHeading(year, month)) 762 763 output.append(fmt.div(on=0)) 764 765 output.append(fmt.bullet_list(on=1, attr={"class" : "event-month-listings"})) 766 767 # Get the events in order. 768 769 ordered_events = EventAggregatorSupport.getOrderedEvents(shown_events.get((year, month), [])) 770 771 # Show the events in order. 772 773 for event_page, event_details in ordered_events: 774 event_summary = EventAggregatorSupport.getEventSummary(event_page, event_details, parent_name) 775 776 output.append(fmt.listitem(on=1, attr={"class" : "event-listing"})) 777 778 # Link to the page using the summary. 779 780 output.append(fmt.paragraph(on=1)) 781 output.append(linkToPage(request, event_page, event_summary)) 782 output.append(fmt.paragraph(on=0)) 783 784 # Start and end dates. 785 786 output.append(fmt.paragraph(on=1)) 787 output.append(fmt.span(on=1)) 788 output.append(fmt.text("%04d-%02d-%02d" % event_details["start"])) 789 output.append(fmt.span(on=0)) 790 output.append(fmt.text(" - ")) 791 output.append(fmt.span(on=1)) 792 output.append(fmt.text("%04d-%02d-%02d" % event_details["end"])) 793 output.append(fmt.span(on=0)) 794 output.append(fmt.paragraph(on=0)) 795 796 # Location. 797 798 if event_details.has_key("location"): 799 output.append(fmt.paragraph(on=1)) 800 output.append(fmt.text(event_details["location"])) 801 output.append(fmt.paragraph(on=1)) 802 803 # Topics. 804 805 if event_details.has_key("topics") or event_details.has_key("categories"): 806 output.append(fmt.bullet_list(on=1, attr={"class" : "event-topics"})) 807 808 for topic in event_details.get("topics") or event_details.get("categories") or []: 809 output.append(fmt.listitem(on=1)) 810 output.append(fmt.text(topic)) 811 output.append(fmt.listitem(on=0)) 812 813 output.append(fmt.bullet_list(on=0)) 814 815 output.append(fmt.listitem(on=0)) 816 817 output.append(fmt.bullet_list(on=0)) 818 819 # Or output a table of events... 820 821 elif mode == "table": 822 823 # Get the events in order. 824 825 ordered_events = EventAggregatorSupport.getOrderedEvents(shown_events.get((year, month), [])) 826 827 # Show the events in order. 828 829 for event_page, event_details in ordered_events: 830 event_summary = EventAggregatorSupport.getEventSummary(event_page, event_details, parent_name) 831 832 # Prepare CSS classes with category-related styling. 833 834 css_classes = ["event-table-details"] 835 836 for topic in event_details.get("topics") or event_details.get("categories") or []: 837 838 # Filter the category text to avoid illegal characters. 839 840 css_classes.append("event-table-category-%s" % "".join(filter(lambda c: c.isalnum(), topic))) 841 842 attrs = {"class" : " ".join(css_classes)} 843 844 output.append(fmt.table_row(on=1)) 845 846 # Start and end dates. 847 848 output.append(fmt.table_cell(on=1, attrs=attrs)) 849 output.append(fmt.span(on=1)) 850 output.append(fmt.text("%04d-%02d-%02d" % event_details["start"])) 851 output.append(fmt.span(on=0)) 852 853 if event_details["start"] != event_details["end"]: 854 output.append(fmt.text(" - ")) 855 output.append(fmt.span(on=1)) 856 output.append(fmt.text("%04d-%02d-%02d" % event_details["end"])) 857 output.append(fmt.span(on=0)) 858 859 output.append(fmt.table_cell(on=0)) 860 861 # Location. 862 863 output.append(fmt.table_cell(on=1, attrs=attrs)) 864 865 if event_details.has_key("location"): 866 output.append(fmt.text(event_details["location"])) 867 868 output.append(fmt.table_cell(on=0)) 869 870 # Link to the page using the summary. 871 872 output.append(fmt.table_cell(on=1, attrs=attrs)) 873 output.append(linkToPage(request, event_page, event_summary)) 874 output.append(fmt.table_cell(on=0)) 875 876 output.append(fmt.table_row(on=0)) 877 878 # Output top-level information. 879 880 # End of list view output. 881 882 if mode == "list": 883 output.append(fmt.bullet_list(on=0)) 884 885 # End of table view output. 886 887 elif mode == "table": 888 output.append(fmt.table(on=0)) 889 890 # Output view controls. 891 892 output.append(fmt.div(on=1, css_class="event-controls")) 893 output.append(view.writeViewControls()) 894 output.append(fmt.div(on=0)) 895 896 return ''.join(output) 897 898 # vim: tabstop=4 expandtab shiftwidth=4