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) + '&lt;' + formatter.url(0)
 315     nextmonth = formatter.url(1, nextlink, 'cal-link', **attrs) + '&gt;' + formatter.url(0)
 316     prevyear = formatter.url(1, prevylink, 'cal-link', **attrs) + '&lt;&lt;' + formatter.url(0)
 317     nextyear = formatter.url(1, nextylink, 'cal-link', **attrs) + '&gt;&gt;' + 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&nbsp;%s&nbsp;<b>&nbsp;%s&nbsp;%s</b>&nbsp;%s\n&nbsp;%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&nbsp;%s&nbsp;<b>&nbsp;%s&nbsp;/&nbsp;%s</b>&nbsp;%s\n&nbsp;%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">&nbsp;</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.
  • [get | view] (2008-08-29 23:44:58, 20.3 KB) [[attachment:MonthCalendar.py]]
  • [get | view] (2008-08-29 23:43:14, 3.1 KB) [[attachment:infobox.js]]
  • [get | view] (2008-08-29 23:45:28, 89.7 KB) [[attachment:screenshot.png]]
 All files | Selected Files: delete move to page copy to page

You are not allowed to attach a file to this page.