WikiCalendarMacro: WikiCalendarMacro.5.py

File WikiCalendarMacro.5.py, 10.0 kB (added by Andy, 4 months ago)

updated version 4's CSS slightly; tested on FF2, IE6

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 by: Andy Schlaikjer <andrew.schlaikjer@gmail.com>
15 # python 2.4 fix by: JasonWinnebeck
16 # more CSS tweaks by: Andy Schlaikjer <andrew.schlaikjer@gmail.com>
17
18 import time
19 import calendar
20 from cStringIO import StringIO
21 from trac.wiki.api import WikiSystem
22 from trac.wiki.macros import WikiMacroBase
23 from trac.util import *
24
25 class WikiCalendarMacro(WikiMacroBase):
26    
27     """Inserts a small calendar where each day links to a wiki page whose name
28     matches `wiki-page-format`. The current day is highlighted, and days with
29     Milestones are marked in bold. This version makes heavy use of CSS for
30     formatting. Tested in Firefox 2 and IE 6.
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         thispageURL = formatter.context.href
104         # for the prev/next navigation links
105         prevMonth = month-1
106         prevYear  = year
107         nextMonth = month+1
108         nextYear  = year
109         # check for year change (KISS version)
110         if prevMonth == 0:
111             prevMonth = 12
112             prevYear -= 1
113         if nextMonth == 13:
114             nextMonth = 1
115             nextYear += 1
116        
117         # 9-tuple for use with time.* functions requiring a struct_time
118         date = list( today )
119        
120         # building the output
121         buff = StringIO()
122         buff.write('''\
123 <style type="text/css">
124 <!--
125 table.wiki-calendar { margin: 0; border: none; padding: 0; border-collapse: collapse; }
126 table.wiki-calendar caption { font-size: 120%; white-space: nowrap; }
127 table.wiki-calendar caption a { display: inline; margin: 0; border: 0; padding: 0; background-color: transparent; color: #b00; text-decoration: none;}
128 table.wiki-calendar caption a.prev { padding-right: 5px; }
129 table.wiki-calendar caption a.next { padding-left: 5px; }
130 table.wiki-calendar caption a:hover { background-color: #eee; }
131 table.wiki-calendar caption a:visited  {}
132 table.wiki-calendar caption a:active {}
133 table.wiki-calendar th { border: none; border-bottom: 2px solid #000; text-align: center; font-weight: bold; }
134 table.wiki-calendar td { padding: 0; border: none; text-align: right; }
135 table.wiki-calendar a { display: block; width: 2em; height: 100%; margin: 0; border: 2px solid #fff; padding: 0; background-color: #fff; color: #888; text-decoration: none; }
136 table.wiki-calendar a:hover { border-color: #eee !important; background-color: #eee !important; color: #000; }
137 table.wiki-calendar a:visited {}
138 table.wiki-calendar a:active  {}
139 table.wiki-calendar a.today { border-color: #b00; }
140 table.wiki-calendar a.today:hover { border-color: #b00 !important; }
141 table.wiki-calendar a.empty { border-color: #f0f0f0; background-color: #f0f0f0; }
142 table.wiki-calendar a.milestone { border-color: #888; font-weight: bold; }
143 table.wiki-calendar a.haspage { color: #b00 !important; }
144 table.wiki-calendar a.haspage:hover {}
145 //-->
146 </style>
147 <table class="wiki-calendar"><caption>
148 ''')
149        
150         if showbuttons:
151             # prev year link
152             date[0:2] = [year-1, month]
153             buff.write('<a class="prev" href="%(url)s" title="%(title)s">&lt;&lt;</a>' % {
154                 'url': thispageURL(month=month, year=year-1),
155                 'title': time.strftime('%B %Y', tuple(date))
156                 })
157             # prev month link
158             date[0:2] = [prevYear, prevMonth]
159             buff.write('<a class="prev" href="%(url)s" title="%(title)s">&lt;</a>' % {
160                 'url': thispageURL(month=prevMonth, year=prevYear),
161                 'title': time.strftime('%B %Y', tuple(date))
162                 })
163        
164         # the caption
165         date[0:2] = [year, month]
166         buff.write(time.strftime('%B %Y', tuple(date)))
167        
168         if showbuttons:
169             # next month link
170             date[0:2] = [nextYear, nextMonth]
171             buff.write('<a class="next" href="%(url)s" title="%(title)s">&gt;</a>' % {
172                 'url': thispageURL(month=nextMonth, year=nextYear),
173                 'title': time.strftime('%B %Y', tuple(date))
174                 })
175             # next year link
176             date[0:2] = [year+1, month]
177             buff.write('<a class="next" href="%(url)s" title="%(title)s">&gt;&gt;</a>' % {
178                 'url': thispageURL(month=month, year=year+1),
179                 'title': time.strftime('%B %Y', tuple(date))
180                 })
181            
182         buff.write('</caption>\n<thead>\n<tr>')
183         for day in calendar.weekheader(2).split():
184             buff.write('<th scope="col">%s</th>' % day)
185         buff.write('</tr>\n</thead>\n<tbody>\n')
186        
187         last_week_prev_month = calendar.monthcalendar(prevYear, prevMonth)[-1];
188         first_week_next_month = calendar.monthcalendar(nextYear, nextMonth)[0];
189         w = -1
190         for week in calendar.monthcalendar(year, month):
191             buff.write('<tr>\n')
192             w = w+1
193             d = -1
194             for day in week:
195                 d = d+1
196                
197                 # calc date and update CSS classes
198                 date[0:3] = [year, month, day]
199                 classes = ''
200                 if not day:
201                     classes = 'empty'
202                     if w == 0:
203                         day = last_week_prev_month[d]
204                         date[0:3] = [prevYear, prevMonth, day]
205                     else:
206                         day = first_week_next_month[d]
207                         date[0:3] = [nextYear, nextMonth, day]
208                 else:
209                     classes = day == curr_day and 'today' or ''
210                 url = ''
211                 title = ''
212                
213                 # check for milestone
214                 db = self.env.get_db_cnx()
215                 cursor = db.cursor()
216                 duedate = time.mktime(tuple(date))
217                 cursor.execute("SELECT name FROM milestone WHERE due=%s", (duedate,))
218                 row = cursor.fetchone()
219                 if row:
220                     milestone_name = row[0]
221                     classes += ' milestone'
222                     url = self.env.href.milestone(milestone_name)
223                     title = milestone_name + ' - '
224                    
225                 # check for wikipage with name specified in 'wiki_page_format'
226                 wiki = time.strftime(wiki_page_format, tuple(date))
227                 url = self.env.href.wiki(wiki)
228                 if WikiSystem(self.env).has_page(wiki):
229                     classes += ' haspage'
230                     title += 'Go to page %s' % wiki
231                 else:
232                     url += "?action=edit"
233                     title += 'Create page %s' % wiki
234                    
235                 # buffer output
236                 buff.write('<td><a class="%(classes)s" href="%(url)s" title="%(title)s">%(day)s</a></td>' % {
237                     'classes': classes.strip(),
238                     'url': url,
239                     'title': title,
240                     'day': day
241                     })
242             buff.write('</tr>\n')
243         buff.write('</tbody>\n</table>\n')
244         table = buff.getvalue()
245         buff.close()
246         return table