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