| [13632] | 1 | # -*- coding: utf-8 -*- |
|---|
| 2 | |
|---|
| [17664] | 3 | from __future__ import division |
|---|
| 4 | |
|---|
| [13632] | 5 | import datetime |
|---|
| [16113] | 6 | import math |
|---|
| [17664] | 7 | from functools import reduce |
|---|
| [13632] | 8 | |
|---|
| 9 | from trac.core import * |
|---|
| [14089] | 10 | from trac.util.datefmt import parse_date, utc |
|---|
| [17477] | 11 | from trac.util.html import tag |
|---|
| [13632] | 12 | from trac.web.chrome import (Chrome, add_script, add_script_data, |
|---|
| 13 | add_stylesheet, ITemplateProvider) |
|---|
| 14 | from trac.wiki.api import parse_args |
|---|
| 15 | from trac.wiki.macros import WikiMacroBase |
|---|
| 16 | |
|---|
| [14175] | 17 | from weekplan.core import WeekPlanModule |
|---|
| [13632] | 18 | |
|---|
| [14175] | 19 | |
|---|
| [13632] | 20 | class WeekPlanMacro(WikiMacroBase): |
|---|
| 21 | """Show a week-by-week calendar and allow planning events. |
|---|
| 22 | |
|---|
| 23 | The arguments are: |
|---|
| 24 | * `plan`: Id of the shown collection of events. (required) |
|---|
| 25 | * Can be a `|`-separated list of multiple plans. |
|---|
| 26 | * `start`: A date in the first week shown. (Defaults to today.) |
|---|
| 27 | * `weeks`: Number of weeks shown. (Defaults to one.) |
|---|
| [16113] | 28 | * `weeksperrow`: Number of weeks per row. (Defaults to one.) |
|---|
| [13768] | 29 | * `width`: Width of the calendar. (Defaults to 400.) |
|---|
| 30 | * `rowheight`: Height of one row of the calendar. (Defaults to 100.) |
|---|
| 31 | * `showweekends`: Show Saturdays and Sundays (Defaults to hidden.) |
|---|
| 32 | * `color`: Color of the events. (Defaults to `#3A87AD|#39AC60|#D7A388|#88BDD7|#9939AC|#AC9939`.) |
|---|
| [13632] | 33 | * Can be a `|`-separated list of multiple colors. Each plan uses a different colors if multiple plans are specified. |
|---|
| [13770] | 34 | * `label`: Labels shown instead of the plan ids. (Defaults to the plan ids.) |
|---|
| 35 | * Can be a `|`-separated list of multiple labels. Each plan uses a different label if multiple plans are specified. |
|---|
| [13769] | 36 | * `hidelegend`: Show a legend below the calendar. (Defaults to shown.) |
|---|
| [13632] | 37 | Example: |
|---|
| 38 | {{{ |
|---|
| 39 | [[WeekPlan(plan=example, start=1.1.2014,weeks=10)]] |
|---|
| 40 | }}} |
|---|
| 41 | """ |
|---|
| 42 | |
|---|
| 43 | def expand_macro(self, formatter, name, content): |
|---|
| 44 | args, kw = parse_args(content) |
|---|
| 45 | args = [arg.strip() for arg in args] |
|---|
| 46 | |
|---|
| 47 | plan_ids = kw.get('plan', '').split('|') |
|---|
| 48 | if not plan_ids: |
|---|
| 49 | raise TracError('Missing plan id') |
|---|
| 50 | |
|---|
| [14175] | 51 | colors = kw.get('color', '#38A|#4B6|#DA8|#9CD|#CAD|#CC7').split('|') |
|---|
| [17664] | 52 | colors = dict(zip(plan_ids, colors*((len(plan_ids)-1)//len(colors)+1))) |
|---|
| [13632] | 53 | |
|---|
| [13770] | 54 | labels = [label for label in kw.get('label', '').split('|') if label] |
|---|
| 55 | labels = dict(zip(plan_ids, labels + plan_ids[len(labels):])) |
|---|
| 56 | |
|---|
| [13632] | 57 | start = kw.get('start') |
|---|
| 58 | if start is None: |
|---|
| [14089] | 59 | start = datetime.date.today() |
|---|
| [13632] | 60 | else: |
|---|
| [14089] | 61 | start = parse_date(start, utc).date() |
|---|
| [13632] | 62 | weeks = int(kw.get('weeks', 1)) |
|---|
| [16113] | 63 | weeksperrow = int(kw.get('weeksperrow', 1)) |
|---|
| [13632] | 64 | |
|---|
| 65 | width = int(kw.get('width', 400)) |
|---|
| 66 | rowheight = int(kw.get('rowheight', 100)) |
|---|
| 67 | |
|---|
| 68 | req = formatter.req |
|---|
| 69 | context = formatter.context |
|---|
| [14175] | 70 | core = WeekPlanModule(self.env) |
|---|
| [13632] | 71 | |
|---|
| 72 | plan_data = { |
|---|
| 73 | 'form_token': req.form_token, |
|---|
| 74 | 'api_url': formatter.href('weekplan'), |
|---|
| 75 | 'plans': plan_ids, |
|---|
| [14175] | 76 | 'plans_with_add_event': [plan_id for plan_id in plan_ids if core.can_plan_add_event(plan_id)], |
|---|
| 77 | 'plans_with_update_event': [plan_id for plan_id in plan_ids if core.can_plan_update_event(plan_id)], |
|---|
| 78 | 'plans_with_delete_event': [plan_id for plan_id in plan_ids if core.can_plan_delete_event(plan_id)], |
|---|
| [13632] | 79 | 'colors': colors, |
|---|
| [13770] | 80 | 'labels': labels, |
|---|
| [13632] | 81 | 'calendar_data': { |
|---|
| 82 | 'firstDay': 1, # Start weeks on Monday |
|---|
| 83 | 'weekends': 'showweekends' in args, # Hide Saturdays and Sundays by default |
|---|
| 84 | 'columnFormat': 'ddd', # Show column headers "Mon Tue Wed Thu Fri" |
|---|
| 85 | 'defaultView': 'multiWeek', # Custom extension of "basicWeek" view! |
|---|
| [14089] | 86 | 'defaultDate': start.isoformat(), |
|---|
| [13632] | 87 | 'weeks': weeks, # New option for custom "multiWeek" view |
|---|
| [16113] | 88 | 'weeksperrow': weeksperrow, # New option for custom "multiWeek" view |
|---|
| 89 | 'contentHeight': math.ceil(weeks / weeksperrow) * rowheight, |
|---|
| [13632] | 90 | 'header': { # Title and navigation buttons |
|---|
| 91 | 'left': '', |
|---|
| 92 | 'center': 'title', |
|---|
| 93 | 'right': 'prev,next' |
|---|
| 94 | }, |
|---|
| [14089] | 95 | 'eventSources': [ |
|---|
| 96 | { |
|---|
| 97 | 'url': formatter.href('weekplan'), |
|---|
| 98 | 'data': { |
|---|
| 99 | 'format': 'json', |
|---|
| 100 | 'plan': '|'.join(plan_ids), |
|---|
| 101 | }, |
|---|
| 102 | }, |
|---|
| 103 | ], |
|---|
| [13632] | 104 | }, |
|---|
| 105 | } |
|---|
| 106 | plan_data_id = '%012x' % id(plan_data) |
|---|
| 107 | |
|---|
| [14089] | 108 | add_stylesheet(req, 'weekplan/css/fullcalendar.css') |
|---|
| 109 | add_script(req, 'weekplan/js/moment.min.js') |
|---|
| [13632] | 110 | add_script(req, 'weekplan/js/fullcalendar.js') |
|---|
| 111 | add_script(req, 'weekplan/js/weekplan.js') |
|---|
| 112 | add_script_data(req, {'weekplan_%s' % plan_data_id: plan_data}) |
|---|
| 113 | Chrome(self.env).add_jquery_ui(req) |
|---|
| 114 | |
|---|
| [13769] | 115 | calendar_element = tag.div("Enable JavaScript to display the week plan.", |
|---|
| 116 | class_='trac-weekplan system-message', |
|---|
| 117 | id='trac-weekplan-%s' % plan_data_id, |
|---|
| 118 | style='width:%spx' % width) |
|---|
| 119 | if 'hidelegend' in args: |
|---|
| 120 | return calendar_element |
|---|
| 121 | else: |
|---|
| 122 | return tag.div( |
|---|
| 123 | calendar_element, |
|---|
| [13770] | 124 | self._render_legend(plan_ids, labels, colors)) |
|---|
| [13769] | 125 | |
|---|
| [13770] | 126 | def _render_legend(self, names, labels, colors): |
|---|
| [13769] | 127 | def merge(a, b): |
|---|
| 128 | return a + u' | ' + b |
|---|
| [13770] | 129 | items = [tag.span(u'■', style='color:%s' % colors[name]) + ' ' + labels[name] for name in names] |
|---|
| [13769] | 130 | return tag.p( |
|---|
| 131 | reduce(merge, items), |
|---|
| 132 | class_='trac-weekplan-legend') |
|---|