Attachment 'MonthCalendar.py'
Download 1 """
2 MoinMoin - MonthCalendar Macro
3
4 You can use this macro to put a month's calendar page on a Wiki page.
5
6 The days are links to Wiki pages following this naming convention:
7 BasePageName/year-month-day
8
9 @copyright: 2002-2007 MoinMoin:ThomasWaldmann
10 @license: GNU GPL, see COPYING for details.
11
12 Revisions:
13 * first revision without a number (=1.0):
14 * was only online for a few hours and then replaced by 1.1
15 * 1.1:
16 * changed name to MonthCalendar to avoid conflict with "calendar" under case-insensitive OSes like Win32
17 * days as subpages
18 * basepage argument
19 * change order of year/month argument
20 * browsing links to prev/next month/year
21 * current limitation: you can only browse one calendar on the same
22 page/url, if you try to browse another calendar on the same page,
23 the first one jumps back to its original display
24 * show basepage in calendar header if basepage<>currentpage
25 * 1.2:
26 * minor fixes in argument parsing
27 * cosmetic fix for netscape, other cosmetic changes, changed css
28 * i18n support (weekday short names)
29 * 1.3:
30 * fixes to run with MoinMoin 0.11, thanks to JuergenHermann
31 * fix: withspace before "," allowed in argument list
32 * BasePage in calendar header (if present) is a link now
33 * more i18n
34 * HTML cleanup, generating code avoids bracketing errors
35 * colour cosmetics
36 * 1.4:
37 * new parameter for enabling fixed height of 6 "calendar weeks",
38 if you want to show a whole year's calendar, this just looks
39 better than having some months with 4, some with 5 and some with 6.
40 * group calendaring functions:
41 * you can give mutliple BasePages UserName1*UserName2*UserName3
42 * first BasePage is considered "your" Basepage,
43 used days are bright red
44 * 2nd and all other BasePages are considered "others" BasePages
45 and lead to an increasing green component the more "used" days
46 the others have. So white gets greener and red gets more yellowish.
47 * in the head part of the calendar, you can click on each name
48 to get to the Page of the same name
49 * colouring of my and others BasePage is done in a way to show
50 the colouring used in the calendar:
51 * others use green colouring (increasingly green if multiply used)
52 * I use red colouring, which gets more and more yellowish as
53 the day is used by more and more others, too
54 * 1.5:
55 * fixed username colouring when using a BasePage
56 * fixed navigation header of MonthCalendar not to get broken into
57 multiple lines
58 * fixed SubPage handling (please do not use relative SubPages like
59 /SubPage yet. Use MyName/SubPage.)
60 * 1.6:
61 * syntactic cleanup
62 * removed i18n compatibility for moin<1.1 or cvs<2003-06-10
63 * integrated Scott Chapman's changes:
64 * Made it configurable for Sunday or Monday as the first day of the week.
65 Search for "change here".
66 * Made it so that today is not only set to a seperate css style, but also boldfaced.
67 Some browsers don't show the other css style (Netscape).
68 * Made it so weekend dates have different color.
69 * 1.7:
70 * added request parameter where needed
71 * 1.8:
72 * some fixes for moin 1.2 (use this version ONLY if you run moin 1.2, too):
73 * .value backtrace fixed when selecting next/prev month/year
74 * request param added to macro function
75 * 1.9:
76 * adapted to moin 1.3
77 * 2.0:
78 * integrated into moin 1.3
79 * added some nice JS (thanks to Klaus Knopper) to show nice mouseovers
80 showing a preview of the day page linked (use first level headlines
81 to make entries)
82 * merged "common navigation" change of OliverGraf
83 * merged AnnualMonthlyCalendar change of JonathanDietrich
84 * 2.1:
85 * fixed CSS for IE users
86 * fix javascript for IE4
87 * do a correct calculation of "today" using user's timezone
88 * 2.2:
89 * added template argument for specifying an edit template for new pages
90
91 Usage:
92 <<MonthCalendar(BasePage,year,month,monthoffset,monthoffset2,height6)>>
93
94 each parameter can be empty and then defaults to currentpage or currentdate or monthoffset=0
95
96 Samples (paste that to one of your pages for a first try):
97
98 Calendar of current month for current page:
99 <<MonthCalendar>>
100
101 Calendar of last month:
102 <<MonthCalendar(,,,-1)>>
103
104 Calendar of next month:
105 <<MonthCalendar(,,,+1)>>
106
107 Calendar of Page SampleUser, this years december:
108 <<MonthCalendar(SampleUser,,12)>>
109
110 Calendar of current Page, this years december:
111 <<MonthCalendar(,,12)>>
112
113 Calendar of December, 2001:
114 <<MonthCalendar(,2001,12)>>
115
116 Calendar of the month two months after December, 2001
117 (maybe doesn't make much sense, but is possible)
118 <<MonthCalendar(,2001,12,+2)>>
119
120 Calendar of year 2002 (every month padded to height of 6):
121 ||||||Year 2002||
122 ||<<MonthCalendar(,2002,1,,,1)>>||<<MonthCalendar(,2002,2,,,1)>>||<<MonthCalendar(,2002,3,,,1)>>||
123 ||<<MonthCalendar(,2002,4,,,1)>>||<<MonthCalendar(,2002,5,,,1)>>||<<MonthCalendar(,2002,6,,,1)>>||
124 ||<<MonthCalendar(,2002,7,,,1)>>||<<MonthCalendar(,2002,8,,,1)>>||<<MonthCalendar(,2002,9,,,1)>>||
125 ||<<MonthCalendar(,2002,10,,,1)>>||<<MonthCalendar(,2002,11,,,1)>>||<<MonthCalendar(,2002,12,,,1)>>||
126
127 Current calendar of me, also showing entries of A and B:
128 <<MonthCalendar(MyPage*TestUserA*TestUserB)>>
129
130 SubPage calendars:
131 <<MonthCalendar(MyName/CalPrivate)>>
132 <<MonthCalendar(MyName/CalBusiness)>>
133 <<MonthCalendar(MyName/CalBusiness*MyName/CalPrivate)>>
134
135
136 Anniversary Calendars: (no year data)
137 <<MonthCalendar(Yearly,,,+1,,6,1)>>
138
139 This creates calendars of the format Yearly/MM-DD
140 By leaving out the year, you can set birthdays, and anniversaries in this
141 calendar and not have to re-enter each year.
142
143 This creates a calendar which uses MonthCalendarTemplate for directly editing
144 nonexisting day pages:
145 <<MonthCalendar(,,,,,,MonthCalendarTemplate)>>
146 """
147
148 Dependencies = ['namespace', 'time', ]
149
150 import re, calendar, time
151
152 from MoinMoin import wikiutil
153 from MoinMoin.Page import Page
154
155 # The following line sets the calendar to have either Sunday or Monday as
156 # the first day of the week. Only SUNDAY or MONDAY (case sensitive) are
157 # valid here. All other values will not make good calendars.
158 # If set to Sunday, the calendar is displayed at "March 2003" vs. "2003 / 3" also.
159 # XXX change here ----------------vvvvvv
160 calendar.setfirstweekday(calendar.MONDAY)
161
162 def cliprgb(r, g, b):
163 """ clip r,g,b values into range 0..254 """
164 def clip(x):
165 """ clip x value into range 0..254 """
166 if x < 0:
167 x = 0
168 elif x > 254:
169 x = 254
170 return x
171 return clip(r), clip(g), clip(b)
172
173 def yearmonthplusoffset(year, month, offset):
174 """ calculate new year/month from year/month and offset """
175 month += offset
176 # handle offset and under/overflows - quick and dirty, yes!
177 while month < 1:
178 month += 12
179 year -= 1
180 while month > 12:
181 month -= 12
182 year += 1
183 return year, month
184
185 def parseargs(args, defpagename, defyear, defmonth, defoffset, defoffset2, defheight6, defanniversary, deftemplate):
186 """ parse macro arguments """
187 strpagename = args.group('basepage')
188 if strpagename:
189 parmpagename = wikiutil.unquoteWikiname(strpagename)
190 else:
191 parmpagename = defpagename
192 # multiple pagenames separated by "*" - split into list of pagenames
193 parmpagename = re.split(r'\*', parmpagename)
194
195 strtemplate = args.group('template')
196 if strtemplate:
197 parmtemplate = wikiutil.unquoteWikiname(strtemplate)
198 else:
199 parmtemplate = deftemplate
200
201 def getint(args, name, default):
202 s = args.group(name)
203 i = default
204 if s:
205 try:
206 i = int(s)
207 except:
208 pass
209 return i
210
211 parmyear = getint(args, 'year', defyear)
212 parmmonth = getint(args, 'month', defmonth)
213 parmoffset = getint(args, 'offset', defoffset)
214 parmoffset2 = getint(args, 'offset2', defoffset2)
215 parmheight6 = getint(args, 'height6', defheight6)
216 parmanniversary = getint(args, 'anniversary', defanniversary)
217
218 return parmpagename, parmyear, parmmonth, parmoffset, parmoffset2, parmheight6, parmanniversary, parmtemplate
219
220 # FIXME: vvvvvv is there a better way for matching a pagename ?
221 _arg_basepage = r'\s*(?P<basepage>[^, ]+)?\s*'
222 _arg_year = r',\s*(?P<year>\d+)?\s*'
223 _arg_month = r',\s*(?P<month>\d+)?\s*'
224 _arg_offset = r',\s*(?P<offset>[+-]?\d+)?\s*'
225 _arg_offset2 = r',\s*(?P<offset2>[+-]?\d+)?\s*'
226 _arg_height6 = r',\s*(?P<height6>[+-]?\d+)?\s*'
227 _arg_anniversary = r',\s*(?P<anniversary>[+-]?\d+)?\s*'
228 _arg_template = r',\s*(?P<template>[^, ]+)?\s*' # XXX see basepage comment
229 _args_re_pattern = r'^(%s)?(%s)?(%s)?(%s)?(%s)?(%s)?(%s)?(%s)?$' % \
230 (_arg_basepage, _arg_year, _arg_month,
231 _arg_offset, _arg_offset2, _arg_height6, _arg_anniversary, _arg_template)
232
233
234 def execute(macro, text):
235 request = macro.request
236 formatter = macro.formatter
237 _ = request.getText
238
239 # return immediately if getting links for the current page
240 if request.mode_getpagelinks:
241 return ''
242
243 args_re = re.compile(_args_re_pattern)
244
245 currentyear, currentmonth, currentday, h, m, s, wd, yd, ds = request.user.getTime(time.time())
246 thispage = formatter.page.page_name
247 # does the url have calendar params (= somebody has clicked on prev/next links in calendar) ?
248 if 'calparms' in macro.form:
249 text2 = macro.form['calparms'][0]
250 args2 = args_re.match(text2)
251 if not args2:
252 return ('<p><strong class="error">%s</strong></p>' % _('Invalid MonthCalendar calparms "%s"!', formatted=False)) % (text2,)
253 else:
254 has_calparms = 1 # yes!
255 cparmpagename, cparmyear, cparmmonth, cparmoffset, cparmoffset2, cparmheight6, cparmanniversary, cparmtemplate = \
256 parseargs(args2, thispage, currentyear, currentmonth, 0, 0, 0, 0, '')
257 else:
258 has_calparms = 0
259
260 if text is None: # macro call without parameters
261 parmpagename, parmyear, parmmonth, parmoffset, parmoffset2, parmheight6, anniversary, parmtemplate = \
262 [thispage], currentyear, currentmonth, 0, 0, 0, 0, ''
263 else:
264 # parse and check arguments
265 args = args_re.match(text)
266 if not args:
267 return ('<p><strong class="error">%s</strong></p>' % _('Invalid MonthCalendar arguments "%s"!', formatted=False)) % (text,)
268 else:
269 parmpagename, parmyear, parmmonth, parmoffset, parmoffset2, parmheight6, anniversary, parmtemplate = \
270 parseargs(args, thispage, currentyear, currentmonth, 0, 0, 0, 0, '')
271
272 # does url have calendar params and is THIS the right calendar to modify (we can have multiple
273 # calendars on the same page)?
274 #if has_calparms and (cparmpagename,cparmyear,cparmmonth,cparmoffset) == (parmpagename,parmyear,parmmonth,parmoffset):
275
276 # move all calendars when using the navigation:
277 if has_calparms and cparmpagename == parmpagename:
278 year, month = yearmonthplusoffset(parmyear, parmmonth, parmoffset + cparmoffset2)
279 parmoffset2 = cparmoffset2
280 parmtemplate = cparmtemplate
281 else:
282 year, month = yearmonthplusoffset(parmyear, parmmonth, parmoffset)
283
284 if request.isSpiderAgent and abs(currentyear - year) > 1:
285 return '' # this is a bot and it didn't follow the rules (see below)
286 if currentyear == year:
287 attrs = {}
288 else:
289 attrs = {'rel': 'nofollow' } # otherwise even well-behaved bots will index forever
290
291 # get the calendar
292 monthcal = calendar.monthcalendar(year, month)
293
294 # european / US differences
295 months = ('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December')
296 # Set things up for Monday or Sunday as the first day of the week
297 if calendar.firstweekday() == calendar.MONDAY:
298 wkend = (5, 6)
299 wkdays = ('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun')
300 if calendar.firstweekday() == calendar.SUNDAY:
301 wkend = (0, 6)
302 wkdays = ('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat')
303
304 colorstep = 85
305 p = Page(request, thispage)
306 qpagenames = '*'.join([wikiutil.quoteWikinameURL(pn) for pn in parmpagename])
307 qtemplate = wikiutil.quoteWikinameURL(parmtemplate)
308 querystr = "calparms=%%s,%d,%d,%d,%%d,%%s" % (parmyear, parmmonth, parmoffset)
309 prevlink = p.url(request, querystr % (qpagenames, parmoffset2 - 1, qtemplate), relative=False)
310 nextlink = p.url(request, querystr % (qpagenames, parmoffset2 + 1, qtemplate), relative=False)
311 prevylink = p.url(request, querystr % (qpagenames, parmoffset2 - 12, qtemplate), relative=False)
312 nextylink = p.url(request, querystr % (qpagenames, parmoffset2 + 12, qtemplate), relative=False)
313
314 prevmonth = formatter.url(1, prevlink, 'cal-link', **attrs) + '<' + formatter.url(0)
315 nextmonth = formatter.url(1, nextlink, 'cal-link', **attrs) + '>' + formatter.url(0)
316 prevyear = formatter.url(1, prevylink, 'cal-link', **attrs) + '<<' + formatter.url(0)
317 nextyear = formatter.url(1, nextylink, 'cal-link', **attrs) + '>>' + formatter.url(0)
318
319 if parmpagename != [thispage]:
320 pagelinks = ''
321 r, g, b = (255, 0, 0)
322 l = len(parmpagename[0])
323 steps = len(parmpagename)
324 maxsteps = (255 / colorstep)
325 if steps > maxsteps:
326 steps = maxsteps
327 chstep = int(l / steps)
328 st = 0
329 while st < l:
330 ch = parmpagename[0][st:st+chstep]
331 r, g, b = cliprgb(r, g, b)
332 link = Page(request, parmpagename[0]).link_to(request, ch,
333 rel='nofollow',
334 style='background-color:#%02x%02x%02x;color:#000000;text-decoration:none' % (r, g, b))
335 pagelinks = pagelinks + link
336 r, g, b = (r, g+colorstep, b)
337 st = st + chstep
338 r, g, b = (255-colorstep, 255, 255-colorstep)
339 for page in parmpagename[1:]:
340 link = Page(request, page).link_to(request, page,
341 rel='nofollow',
342 style='background-color:#%02x%02x%02x;color:#000000;text-decoration:none' % (r, g, b))
343 pagelinks = pagelinks + '*' + link
344 showpagename = ' %s<BR>\n' % pagelinks
345 else:
346 showpagename = ''
347 if calendar.firstweekday() == calendar.SUNDAY:
348 resth1 = ' <th colspan="7" class="cal-header">\n' \
349 '%s' \
350 ' %s %s <b> %s %s</b> %s\n %s\n' \
351 ' </th>\n' % (showpagename, prevyear, prevmonth, months[month-1], str(year), nextmonth, nextyear)
352 if calendar.firstweekday() == calendar.MONDAY:
353 resth1 = ' <th colspan="7" class="cal-header">\n' \
354 '%s' \
355 ' %s %s <b> %s / %s</b> %s\n %s\n' \
356 ' </th>\n' % (showpagename, prevyear, prevmonth, str(year), month, nextmonth, nextyear)
357 restr1 = ' <tr>\n%s </tr>\n' % resth1
358
359 r7 = range(7)
360 restd2 = []
361 for wkday in r7:
362 wday = _(wkdays[wkday], formatted=False)
363 if wkday in wkend:
364 cssday = "cal-weekend"
365 else:
366 cssday = "cal-workday"
367 restd2.append(' <td class="%s" width="14%%">%s</td>\n' % (cssday, wday))
368 restr2 = ' <tr>\n%s </tr>\n' % "".join(restd2)
369
370 if parmheight6:
371 while len(monthcal) < 6:
372 monthcal = monthcal + [[0, 0, 0, 0, 0, 0, 0]]
373
374 maketip_js = []
375 restrn = []
376 for week in monthcal:
377 restdn = []
378 for wkday in r7:
379 day = week[wkday]
380 if not day:
381 restdn.append(' <td class="cal-invalidday"> </td>\n')
382 else:
383 page = parmpagename[0]
384 if anniversary:
385 link = "%s/%02d-%02d" % (page, month, day)
386 else:
387 link = "%s/%4d-%02d-%02d" % (page, year, month, day)
388 daypage = Page(request, link)
389 #Rough hack
390 tipname = None
391 tipList = []
392 if daypage.exists() and request.user.may.read(link):
393 csslink = "cal-usedday"
394 query = {}
395 r, g, b, u = (255, 0, 0, 1)
396 daycontent = daypage.get_raw_body()
397 header1_re = re.compile(r'^\s*(=+\s.*\s=+)$', re.MULTILINE) # re.UNICODE
398 titletext = []
399 for match in header1_re.finditer(daycontent):
400 if match:
401 title = match.group(1)
402 title = wikiutil.escape(title).replace("'", "\\'")
403 titletext.append(title)
404 tipname = link
405 tipList.append(link)
406 tipList.append('<br>'.join(titletext))
407 # maketip_js.append("maketip('%s','%s','%s');" % (tipname, tiptitle, tiptext))
408 # attrs = {'onMouseOver': "tip('%s')" % tipname,
409 # 'onMouseOut': "untip()"}
410 else:
411 csslink = "cal-emptyday"
412 if parmtemplate:
413 query = {'action': 'edit', 'template': parmtemplate}
414 else:
415 query = {}
416 r, g, b, u = (255, 255, 255, 0)
417 if wkday in wkend:
418 csslink = "cal-weekend"
419 attrs = {'rel': 'nofollow'}
420 for otherpage in parmpagename[1:]:
421 otherlink = "%s/%4d-%02d-%02d" % (otherpage, year, month, day)
422 otherdaypage = Page(request, otherlink)
423 if otherdaypage.exists():
424 csslink = "cal-usedday"
425 if u == 0:
426 r, g, b = (r-colorstep, g, b-colorstep)
427 else:
428 r, g, b = (r, g+colorstep, b)
429 daycontent = otherdaypage.get_raw_body()
430 header1_re = re.compile(r'^\s*(=+\s.*\s=+)$', re.MULTILINE) # re.UNICODE
431 titletext = []
432 for match in header1_re.finditer(daycontent):
433 if match:
434 title = match.group(1)
435 title = wikiutil.escape(title).replace("'", "\\'")
436 titletext.append(title)
437 if not tipname:
438 tipname = otherlink
439 tipList.append(otherlink)
440 tipList.append('<br>'.join(titletext))
441 if tipname:
442 tipArgs = ["'%s'" % value for value in tipList]
443 maketip_js.append("maketip('%s',%s);" % (tipname, ','.join(tipArgs)))
444 attrs = {'onMouseOver': "tip('%s')" % tipname,
445 'onMouseOut': "untip()"}
446 r, g, b = cliprgb(r, g, b)
447 style = 'background-color:#%02x%02x%02x' % (r, g, b)
448 fmtlink = formatter.url(1, daypage.url(request, query, relative=False), csslink, **attrs) + str(day) + formatter.url(0)
449 if day == currentday and month == currentmonth and year == currentyear:
450 cssday = "cal-today"
451 fmtlink = "<b>%s</b>" % fmtlink # for browser with CSS probs
452 else:
453 cssday = "cal-nottoday"
454 restdn.append(' <td style="%s" class="%s">%s</td>\n' % (style, cssday, fmtlink))
455 restrn.append(' <tr>\n%s </tr>\n' % "".join(restdn))
456
457 restable = '<table border="2" cellspacing="2" cellpadding="2">\n%s%s%s</table>\n'
458 restable = restable % (restr1, restr2, "".join(restrn))
459
460 result = """\
461 <script language="JavaScript" type="text/javascript" src="%s/common/js/infobox.js"></script>
462 <div id="infodiv" style="position:absolute; visibility:hidden; z-index:20; top:-999em; left:0px;"></div>
463 <script language="JavaScript" type="text/javascript">
464 <!--
465 %s
466 // -->
467 </script>
468 %s
469 """ % (request.cfg.url_prefix_static, "\n".join(maketip_js), restable)
470 return formatter.rawHTML(result)
471
472 # EOF
Attached Files
To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.You are not allowed to attach a file to this page.