WikiCalendarMacro: WikiCalendarMacro.4.1.py

File WikiCalendarMacro.4.1.py, 10.1 kB (added by JasonWinnebeck, 3 months ago)

Fix to my version 4, to fix prev/next button link generation, tested in Trac 0.11-r7405 and python 2.4

Line 
1 # Copyright (C) 2005 Matthew Good <trac@matt-good.net>
2 # Copyright (C) 2005 Jan Finell <finell@cenix-bioscience.com>
3 #
4 # "THE BEER-WARE LICENSE" (Revision 42):
5 # <trac@matt-good.net> wrote this file.  As long as you retain this notice you
6 # can do whatever you want with this stuff.  If we meet some day, and you think
7 # this stuff is worth it, you can buy me a beer in return.  Matthew Good
8 # (Beer-ware license written by Poul-Henning Kamp
9 #  http://people.freebsd.org/~phk/)
10 #
11 # Author: Matthew Good <trac@matt-good.net>
12 # Month/Year navigation by: Jan Finell <finell@cenix-bioscience.com>
13 # trac 0.11 compatibility by: Vlad Sukhoy <vladimir.sukhoy@gmail.com>
14 # refactored; CSS added: Andy Schlaikjer <andrew.schlaikjer@gmail.com>
15
16 import time
17 import calendar
18 from cStringIO import StringIO
19 from trac.wiki.api import WikiSystem
20 from trac.wiki.macros import WikiMacroBase
21 from trac.web.href import Href
22 from trac.util import *
23
24 class WikiCalendarMacro(WikiMacroBase):
25    
26     """Inserts a small calendar where each day links to a wiki page
27     whose name matches `wiki-page-format`. The current day is
28     highlighted, and days with Milestones are marked in bold. This
29     version makes heavy use of CSS for formatting. Tested in Firefox
30     2.0 only.
31     
32     Usage:
33     {{{
34     [[WikiCalendar([year, [month, [show-buttons, [wiki-page-format]]]])]]
35     }}}
36     
37     Arguments:
38      1. `year` (4-digit year) - defaults to `*` (current year)
39      1. `month` (2-digit month) - defaults to `*` (current month)
40      1. `show-buttons` (boolean) - defaults to `true`
41      1. `wiki-page-format` (string) - defaults to `%Y-%m-%d`
42     
43     Examples:
44     {{{
45     [[WikiCalendar(2006,07)]]
46     [[WikiCalendar(2006,07,false)]]
47     [[WikiCalendar(*,*,true,Meeting-%Y-%m-%d)]]
48     [[WikiCalendar(2006,07,false,Meeting-%Y-%m-%d)]]
49     }}}
50     """
51    
52     def expand_macro(self, formatter, name, content):
53         today = time.localtime()
54         # VS: The hdf is gone in 0.11, using request object instead
55         http_param_year = formatter.req.args.get('year', '')
56         http_param_month = formatter.req.args.get('month', '')
57         if content == "":
58             args = []
59         else:
60             args = content.split(',', 3)
61        
62         # find out whether use http param, current or macro param year/month
63        
64         if http_param_year == "":
65             # not clicked on a prev or next button
66             if len(args) >= 1 and args[0] <> "*":
67                 # year given in macro parameters
68                 year = int(args[0])
69             else:
70                 # use current year
71                 year = today.tm_year
72         else:
73             # year in http params (clicked by user) overrides everything
74             year = int(http_param_year)
75        
76         if http_param_month == "":
77             # not clicked on a prev or next button
78             if len(args) >= 2 and args[1] <> "*":
79                 # month given in macro parameters
80                 month = int(args[1])
81             else:
82                 # use current month
83                 month = today.tm_mon
84         else:
85             # month in http params (clicked by user) overrides everything
86             month = int(http_param_month)
87        
88         showbuttons = 1
89         if len(args) >= 3:
90             showbuttons = bool(args[2]=="True" or args[2]=="true" or args[2]=="no" or args[2]=="0")
91        
92         wiki_page_format = "%Y-%m-%d"
93         if len(args) >= 4:
94             wiki_page_format = args[3]
95        
96         curr_day = None
97         if year == today.tm_year and month == today.tm_mon:
98             curr_day = today.tm_mday
99        
100         # url to the current page (used in the navigation links)
101         # VS: hdf is gone in 0.11, using the new "Context" object instead
102         # AS: trac.web.Href object used instead of basic strings
103         # JW: context doesn't seem to have the right href, so we get it from
104         #     the req; hopefully should work regardless of Trac base_url
105         #     setting or anything like that.
106         thispageURL = Href( formatter.req.base_path + formatter.req.path_info )
107
108         # for the prev/next navigation links
109         prevMonth = month-1
110         prevYear  = year
111         nextMonth = month+1
112         nextYear  = year
113         # check for year change (KISS version)
114         if prevMonth == 0:
115             prevMonth = 12
116             prevYear -= 1
117         if nextMonth == 13:
118             nextMonth = 1
119             nextYear += 1
120        
121         # 9-tuple for use with time.* functions requiring a struct_time
122         # argument. The ninth value of -1 signals "do the right thing"
123         # w.r.t. daylight savings time.
124         date = list( today )
125        
126         # building the output
127         buff = StringIO()
128         buff.write('''\
129 <style type="text/css">
130 <!--
131 table.wiki-calendar { border: none; border-collapse: separate; border-spacing: 0px; }
132 table.wiki-calendar caption { font-size: 120%; white-space: nowrap; }
133 table.wiki-calendar caption a { display: inline; margin: 0; border: 0; padding: 0; text-decoration: none; color: #b00; }
134 table.wiki-calendar caption a.prev { padding-right: 5px; }
135 table.wiki-calendar caption a.next { padding-left: 5px; }
136 table.wiki-calendar caption a:hover { background-color: #eee; }
137 table.wiki-calendar caption a:visited  {}
138 table.wiki-calendar caption a:active {}
139 table.wiki-calendar th { border: none; border-bottom: 2px solid black; text-align: center; font-weight: bold; }
140 table.wiki-calendar td { padding: 0; border: 2px; text-align: right; }
141 table.wiki-calendar a { display: block; width: 2em; height: 100%; margin: 0; border: 2px solid transparent; padding: 0; text-decoration: none; color: #888; }
142 table.wiki-calendar a:hover { background-color: #eee !important; color: #000; }
143 table.wiki-calendar a:visited {}
144 table.wiki-calendar a:active {}
145 table.wiki-calendar a.empty { background-color: #fafafa; }
146 table.wiki-calendar a.milestone { border-color: #888; font-weight: bold; }
147 table.wiki-calendar a.haspage { color: #b00; }
148 table.wiki-calendar a.haspage:hover {}
149 table.wiki-calendar a.today { border-color: #b00; }
150 //-->
151 </style>
152 <table class="wiki-calendar"><caption>
153 ''')
154        
155         if showbuttons:
156             # prev year link
157             date[0:2] = [year-1, month]
158             buff.write('<a class="prev" href="%(url)s" title="%(title)s">&lt;&lt;</a>' % {
159                 'url': thispageURL(month=month, year=year-1),
160                 'title': time.strftime('%B %Y', tuple(date))
161                 })
162             # prev month link
163             date[0:2] = [prevYear, prevMonth]
164             buff.write('<a class="prev" href="%(url)s" title="%(title)s">&lt;</a>' % {
165                 'url': thispageURL(month=prevMonth, year=prevYear),
166                 'title': time.strftime('%B %Y', tuple(date))
167                 })
168        
169         # the caption
170         date[0:2] = [year, month]
171         buff.write(time.strftime('%B %Y', tuple(date)))
172        
173         if showbuttons:
174             # next month link
175             date[0:2] = [nextYear, nextMonth]
176             buff.write('<a class="next" href="%(url)s" title="%(title)s">&gt;</a>' % {
177                 'url': thispageURL(month=nextMonth, year=nextYear),
178                 'title': time.strftime('%B %Y', tuple(date))
179                 })
180             # next year link
181             date[0:2] = [year+1, month]
182             buff.write('<a class="next" href="%(url)s" title="%(title)s">&gt;&gt;</a>' % {
183                 'url': thispageURL(month=month, year=year+1),
184                 'title': time.strftime('%B %Y', tuple(date))
185                 })
186            
187         buff.write('</caption>\n<thead>\n<tr>')
188         for day in calendar.weekheader(2).split():
189             buff.write('<th scope="col">%s</th>' % day)
190         buff.write('</tr>\n</thead>\n<tbody>\n')
191        
192         last_week_prev_month = calendar.monthcalendar(prevYear, prevMonth)[-1];
193         first_week_next_month = calendar.monthcalendar(nextYear, nextMonth)[0];
194         w = -1
195         for week in calendar.monthcalendar(year, month):
196             buff.write('<tr>\n')
197             w = w+1
198             d = -1
199             for day in week:
200                 d = d+1
201                
202                 # calc date and update CSS classes
203                 date[0:3] = [year, month, day]
204                 classes = ''
205                 if not day:
206                     classes = 'empty'
207                     if w == 0:
208                         day = last_week_prev_month[d]
209                         date[0:3] = [prevYear, prevMonth, day]
210                     else:
211                         day = first_week_next_month[d]
212                         date[0:3] = [nextYear, nextMonth, day]
213                 else:
214                     classes = day == curr_day and 'today' or ''
215                 url = ''
216                 title = ''
217                
218                 # check for milestone
219                 db = self.env.get_db_cnx()
220                 cursor = db.cursor()
221                 duedate = time.mktime(tuple(date))
222                 cursor.execute("SELECT name FROM milestone WHERE due=%s", (duedate,))
223                 row = cursor.fetchone()
224                 if row:
225                     milestone_name = row[0]
226                     classes += ' milestone'
227                     url = self.env.href.milestone(milestone_name)
228                     title = milestone_name + ' - '
229                    
230                 # check for wikipage with name specified in 'wiki_page_format'
231                 wiki = time.strftime(wiki_page_format, tuple(date))
232                 url = self.env.href.wiki(wiki)
233                 if WikiSystem(self.env).has_page(wiki):
234                     classes += ' haspage'
235                     title += 'Go to page %s' % wiki
236                 else:
237                     url += "?action=edit"
238                     title += 'Create page %s' % wiki
239                    
240                 # buffer output
241                 buff.write('<td><a class="%(classes)s" href="%(url)s" title="%(title)s">%(day)s</a></td>' % {
242                     'classes': classes.strip(),
243                     'url': url,
244                     'title': title,
245                     'day': day
246                     })
247             buff.write('</tr>\n')
248         buff.write('</tbody>\n</table>\n')
249         table = buff.getvalue()
250         buff.close()
251         return table