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