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