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