"""
    EventCalendar.py  Version 0.80  2005. 10. 31.
                                                                                                           
    This macro gives a list of the events recorded in the sub-pages in the form of monthly view and list view.
                                                                                                           
    @copyright: 2005 by Seungik Lee <seungiklee<at>gmail.com>  http://cds.icu.ac.kr/~silee/
    @license: GPL

    Usage: 
        To list the events in a page, just insert [[EventCalendar]]
        To insert an event, enclose the information about ONE event with 'eventcal' in the page or its subpages.
        
            E.g., 
                {{{#!eventcal
                    = [Title] =
                    [Start Date] ... [End Date]
                    [Start Time] ... [End Time]
                }}}
            
            [Title] should be enclosed with heading marker ('='), double quatation mark ("), wiki italic or bold ('', ''')
                Title can be omitted and it will be titled as 'No Title'
                e.g., == Title ==, === Title ===, "Title", ''Title'', '''Title'''
            
            [Start|End Date] should be in YYYY/MM/DD or YYYY-MM-DD. End date can be omitted.
                e.g, 2005/10/20, 2005-10-20
            
            [Start|End Time] should be in HH:MM in 24-hour format. Both of start|end Time can be omitted but not either of them.
                e.g., 08:00, 12:00, 18:00
            
            In the parsing block of eventcal, it pops out first two date formatted text (startdate and enddate), 
            two time formatted text (starttime, endtime), and quoted or heading titie. 
            
            It ignores further occurrence of the targeted string. 
            The order of each fields (date, time, title) does not matter .
            The start date|time should precede the end date|time in the occurrence respectively.
        
        Use 'eventcal.py' to get a parsed format of the event which will be shown at the EventCalendar macro.
        To insert an administrative event, just click the [New Event] and input the information. It will be stored in a text file.

        There is no input parameter yet.

    Features:
        Monthly, List, Item view of events 
        Handles daily and timely events 
        Uses text files for storing administrative event data
        Uses wiki subpages for storing normal event data 
        Add/edit/remove events (in text files only)
        Fields included: Title, Start/End Date/Time, Reference Page in wiki 

    Change Log:
        Oct. 31, 2005 - Version 0.80 
            - The initial version is released.

    To do list:
        Clean the ugly codes!!! (need help)
            raw html codes
        Daily view of events 
        Weekly view of events (is it useful?)
        Date/time Format customization (e.g., 2005/10, Oct. 2005, 18:00, 06:00pm)
        Custom background color per event 
        Custom icon per event 
        Upcoming event list 
        More input parameters
        Parsing description field
        Using page cache
        Update page log at add/edit/remove event in the text data file
        Sort events by various options in list view
        Load partial events only in the specified date range

    Notes:
        'MonthCalendar.py' developed by Thomas Waldmann <ThomasWaldmann@gmx.de> has inspired this macro.
        Much buggy.. :please report bugs and suggest your ideas.
        If you missed to add css for EventCalender, monthly view may not readable.
            Insert the EventCalendar css classes into the common.css of appropriate theme.

"""

from MoinMoin import wikiutil, config
from MoinMoin.Page import Page
import re, calendar, time, datetime
import codecs, os, urllib

# The following line sets the calendar to have either Sunday or Monday as
# the first day of the week. Only SUNDAY or MONDAY (case sensitive) are
# valid here.  All other values will not make good calendars.
# XXX change here ----------------vvvvvv
calendar.setfirstweekday(calendar.SUNDAY)

class Globs:
    month_style_us = 1  # 1: October 2005; 2: 2005 / 10
    adminmsg=''
    datafiledir=''
    pagename=''
    pagepath = ''
    admin=''
    baseurl=''
    subname=''
    cal_delimeter = ''
    wkend = ''
    months = ''
    wkdays = ''
    today = ''
    lastaction = ''
    depth=1 # depth of sub-pages to read
    request=''
    out_fname=''
    inx_fname=''

def execute(macro, args):
    
    # INITIALIZATION ----------------------------------------
    setglobalvalues(macro)
    
    # allowed actions to guests
    guest_allowed_action = ['calshow', 'evtlist', 'evtview']
    guest_default_action = 'calshow'
    
    # Internal variables
    cal_action = ''
    form_vals = {}

    # PROCESSING ARGUEMENTS ----------------------------------------
    if args:
        args=macro.request.getText(args)

    for item in macro.form.items():
        if not form_vals.has_key(item[0]):
	    try:
		    form_vals[item[0]]=item[1][0]
	    except AttributeError:
	        pass
    
    # PROCESSING ACTIONS ----------------------------------------
    cal_action = form_vals.get('calaction', guest_default_action)
    form_vals['calaction'] = cal_action

    # CONTROL FUNCTIONS ----------------------------------------
    
    html = []
    html_result = ''
    
    request = macro.request
    formatter = macro.formatter
    
    # check previlege
    if (not Globs.admin) and (not cal_action in guest_allowed_action):
        message('You have no previlege for this action')
        cal_action = guest_default_action
    
    # redirect to the appropriate view
    if cal_action == 'calshow':
        html_result = showcalendar(macro, form_vals)
        
    if cal_action == 'evtlist':
        html_result = showeventlist(macro, form_vals)
        
    if cal_action == 'evtadd':
        html_result = addeventform(macro, form_vals)

    if cal_action == 'evtaddnow':
        html_result = addevent(macro, form_vals)
        
    if cal_action == 'evtdel':
        html_result = deleteevent(macro, form_vals)
    
    if cal_action == 'evtedit':
        html_result = editevent(macro, form_vals)
        
    if cal_action == 'evteditnow':
        html_result = updateevent(macro, form_vals)
        
    if cal_action == 'evtview':
        html_result = viewevent(macro, form_vals)
    
    # format output
    html.append( showmessage() )
    html.append( html_result )
    html.append( showmenubar(form_vals) )
    
    return formatter.rawHTML(u''.join(html))


def setglobalvalues(macro):
    
    request = macro.request
    
    # Useful variables
    Globs.baseurl = macro.request.getBaseURL() + '/'
    Globs.pagename = macro.formatter.page.page_name
    Globs.request = request
    
    # This fixes the subpages bug. subname is now used instead of pagename when creating certain urls
    Globs.subname = Globs.pagename.split('/')[-1]

    pagepath = macro.formatter.page.getPagePath()
    Globs.datafiledir = pagepath + '/eventcalendardata'
    Globs.pagepath = macro.formatter.page.getPagePath()
    Globs.cal_delimeter = u'\t'
    
    Globs.inx_fname = os.path.join(Globs.datafiledir, 'eventindex.txt')
    Globs.out_fname = os.path.join(Globs.datafiledir, 'events.txt')
    
    # Figure out if we have delete privs
    try:
        # If a user can delete the page containing the EventCalendar, then they are considered a EventCalendar administrator
        if macro.request.user.may.delete(macro.formatter.page.page_name):
            Globs.admin = 'true'
    except AttributeError:
        pass
    
    # european / US differences
    months = ('January','February','March','April','May','June','July','August','September','October','November','December')
    
    # Set things up for Monday or Sunday as the first day of the week
    if calendar.firstweekday() == calendar.MONDAY:
        wkend = 6
        wkdays = ('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun')
    elif calendar.firstweekday() == calendar.SUNDAY:
        wkend = 0
        wkdays = ('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat')
    
    Globs.months = months
    Globs.wkdays = wkdays
    Globs.wkend = wkend

    year, month, day, h, m, s, wd, yd, ds = request.user.getTime(time.time())
    Globs.today = datetime.date(year, month, day)

def deleteevent(macro, form_vals):
    
    Globs.lastaction = 'evtdel'
    request = macro.request
    
    required_fields = {'id': 'Event ID'}
    
    missing_fields = []
    for field in required_fields.keys():
        if not form_vals.has_key(field):
            missing_fields.append(required_fields[field])

    if missing_fields:
        message('Failed to delete the event (insufficient input: %s)' % u','.join(missing_fields) )
        return redirectPreviousAction(macro, form_vals)

    eid = form_vals['id']
    
    return deleteeventnow(macro, form_vals, eid)
    

def editevent(macro, form_vals):
    
    Globs.lastaction = 'evtedit'
    request = macro.request
    
    required_fields = {'id': 'Event ID'}
    
    missing_fields = []
    for field in required_fields.keys():
        if not form_vals.has_key(field):
            missing_fields.append(required_fields[field])

    if missing_fields:
        message('Failed to edit the event (insufficient input: %s)' % u','.join(missing_fields) )
        return redirectPreviousAction(macro, form_vals)

    try:
        eid = form_vals['id']
        event = getEventById(macro, eid)
    except KeyError:
        message('Failed to edit the event (no such event, id: %s)' % eid )
        return redirectPreviousAction(macro, form_vals)
        
    return editeventform(macro, form_vals, event)
    
# input form to edit events
def editeventform(macro, form_vals, event):
    
    Globs.lastaction = 'evtedit'
    prev_calaction = ''
    cal_date = ''
    cal_action = ''
    
    request = macro.request
    formatter = macro.formatter
    
    form_fields = ('id', 'title', 'startdate', 'enddate', 'starttime', 'endtime', 'description', 'refer')
    event_form = {}
    event_form = event_form.fromkeys(form_fields, '')

    if form_vals.has_key('calaction'):
        cal_action = form_vals['calaction']
        
    if cal_action == 'evteditnow' or (not event):
        for field in event_form.keys():
            if form_vals.has_key(field):
                event_form[field] = form_vals[field]

    else:
        for field in event_form.keys():
            if event.has_key(field):
                event_form[field] = event[field]
                
    prev_calaction = form_vals.get('prevcalaction', '')
    cal_date = form_vals.get('caldate', '')

    html = [
        u'<div id="editeventform">',
        u'<table id="editeventform" width="90%%">',
        u'<form action="%s" name="editeventform" METHOD="POST">' % Globs.subname,

		u'<tr><td><b>Title</b></td><td><input type="text" size="30" maxlength="100" name="title" value="%s"></td></tr>' % converttext(event_form['title']),
		u'<tr><td><b>Start Date</b></td><td><input type="text" size="8" maxlength="8" name="startdate" value="%s"> (YYYYMMDD)</td></tr>' % event_form['startdate'],
		u'<tr><td>Start Time</td><td><input type="text" size="4" maxlength="4" name="starttime" value="%s"> (HHMM)</td></tr>' % event_form['starttime'],
		u'<tr><td><b>End Date</b></td><td><input type="text" size="8" maxlength="8" name="enddate" value="%s"> (YYYYMMDD)</td></tr>' % event_form['enddate'],
		u'<tr><td>End Time</td><td><input type="text" size="4" maxlength="4" name="endtime" value="%s"> (HHMM)</td></tr>' % event_form['endtime'],
		
		u'<tr><td>Description</td><td><input type="text" size="50" maxlength="255" name="description" value="%s"></td></tr>' % converttext(event_form['description']),
		#u'<tr><td>Reference</td><td><input type="text" size="50" maxlength="255" name="refer" value="%s"> (ReferPageName)</td></tr>' % converttext(event_form['refer']),
		
		u'<tr><td colspan="2" align="right"><input type="submit" value="POST" class="postbutton"></td></tr>',
		u'<input type="hidden" value="%s" name="id">' % event_form['id'],
		u'<input type="hidden" value="evteditnow" name="calaction">',
		u'<input type="hidden" value="%s" name="prevcalaction">' % prev_calaction,
		u'<input type="hidden" value="%s" name="caldate">' % cal_date,
		u'</form>',
		u'</table>',
		u'</div>',
        ]
    
    return u'\r\n'.join(html)


def viewevent(macro, form_vals):
    
    Globs.lastaction = 'evtview'
    
    request = macro.request

    required_fields = {'id': 'Event ID'}
    
    missing_fields = []
    for field in required_fields.keys():
        if not form_vals.has_key(field):
            missing_fields.append(required_fields[field])

    if missing_fields:
        message('Failed to view the event (insufficient input: %s)' % u','.join(missing_fields) )
        return redirectPreviousAction(macro, form_vals)

    try:
        eid = form_vals['id']
        event = getEventById(macro, eid)
    except KeyError:
        message('Failed to view the event (no such event, id: %s)' % eid )
        return redirectPreviousAction(macro, form_vals)
    
    return showeventdetail(event, form_vals)
    

def showeventdetail(event, form_vals):
    Globs.lastaction = 'evtview'
    
    html = [
        u'<div id="viewevent">',
        u'<table id="viewevent" width="90%%">',
        u'<tr><td width="20%%">Title</td><td>%s</td></tr>' % converttext(event['title']),
		u'<tr><td>Start Date</td><td>%s</td></tr>' % event['startdate'],
		u'<tr><td>Start Time</td><td>%s</td></tr>' % event['starttime'],
		u'<tr><td>End Date</td><td>%s</td></tr>' % event['enddate'],
		u'<tr><td>End Time</td><td>%s</td></tr>' % event['endtime'],
		u'<tr><td>Description</td><td>%s</td></tr>' % converttext(event['description']),
		u'<tr><td>Reference</td><td>%s</td></tr>' % showReferPageParsed(event),
		u'</table>',
		u'</div>',
        ]
    
    return u'\r\n'.join(html)


def showReferPageParsed(event):
    request = Globs.request
    pagename = Globs.pagename
    
    refer = event['refer']
    title = event['title']
    startdate = event['startdate']
    
    bookmark = u'%s%s' % (title.replace(' ', ''), startdate)
    bookmark = urllib.quote_plus(bookmark.encode(config.charset))
    targetlink = Page(request, refer).link_to(request, text=refer, querystr='action=show', anchor=bookmark)
    
    return targetlink


def redirectPreviousAction(macro, form_vals):

    prev_calaction = form_vals.get('prevcalaction', 'calshow')

    if prev_calaction == 'evtlist':
        return showeventlist(macro, form_vals)
    else:
        return showcalendar(macro, form_vals)


def getquerystring(form_vals, req_fields):
    
    m_query = []
    tmp_form_vals = form_vals
    
    # format querystring
    # action should be poped out
    for field in req_fields:
        if tmp_form_vals.has_key(field):
            m_query.append(u'%s=%s' % (field, tmp_form_vals[field]) )
    
    if 'prevcalaction' in req_fields:
        if not tmp_form_vals.has_key('prevcalaction'):
            m_query.append(u'%s=%s' % ('prevcalaction', tmp_form_vals['calaction']) )
            
    m_query = u'&'.join(m_query)
    return m_query

# bottom menu bar
def showmenubar(form_vals):
    
    lastaction = Globs.lastaction
    
    left_menu_selected = []
    right_menu_selected = []
    
    # New Event
    mnu_newevent = u'<a href="?calaction=evtadd&%s">[New Event]</a>' % getquerystring(form_vals, ['prevcalaction', 'caldate'])
    
    # Go Today
    year, month, day = gettodaydate()
    mnu_curmonthcal = u'<a href="?calaction=calshow&caldate=%d%02d">[Go Today]</a>' % (year, month)
    
    # View Event
    mnu_viewevent = u'<a href="?calaction=evtview&%s">[View Event]</a>' % getquerystring(form_vals, ['prevcalaction', 'caldate', 'id'])
    
    # Edit Event
    mnu_editevent = u'<a href="?calaction=evtedit&%s">[Edit Event]</a>' % getquerystring(form_vals, ['prevcalaction', 'caldate', 'id'])
    
    # Delete Event
    mnu_deleteevent = u'<a href="?calaction=evtdel&%s" onClick="return confirm(\'Are you sure to delete?\');">[Delete Event]</a>' % getquerystring(form_vals, ['prevcalaction', 'caldate', 'id'])
    
    # List View
    mnu_listview = u'<a href="?calaction=evtlist">[List View]</a>'
    
    # Monthly View
    mnu_monthview = u'<a href="?calaction=calshow&%s">[Monthly View]</a>' % getquerystring(form_vals, ['caldate'])
    
    html = [
        u'\r\n',
        u'<table class="eventcalendar_menubar" width="90%%" align="center">',
        u'  <tr>',
        u'  <td class="eventcalendar_menubar" align="left">%s</td>',
        u'  <td class="eventcalendar_menubar" align="right">%s</td>',
        u'  </tr>',
        u'</table>',
        ]
        
    if lastaction == 'evtview':
        if form_vals.has_key('id') and form_vals['id'].startswith('w'):
            left_menu_selected.append(mnu_monthview)
            left_menu_selected.append(mnu_listview)
            left_menu_selected.append(mnu_curmonthcal)
                   
            right_menu_selected.append(mnu_newevent)
        else:
            left_menu_selected.append(mnu_monthview)
            left_menu_selected.append(mnu_listview)
            left_menu_selected.append(mnu_curmonthcal)
                   
            if Globs.admin:
                right_menu_selected.append(mnu_editevent)
                right_menu_selected.append(mnu_deleteevent)
            
            if Globs.admin:
                right_menu_selected.append(mnu_newevent)

    elif lastaction == 'evtedit':
        left_menu_selected.append(mnu_monthview)
        left_menu_selected.append(mnu_listview)
        left_menu_selected.append(mnu_curmonthcal)
               
        right_menu_selected.append(mnu_viewevent)
        
        if Globs.admin:
            right_menu_selected.append(mnu_deleteevent)
            right_menu_selected.append(mnu_newevent)
        
    elif lastaction == 'evtlist':
        left_menu_selected.append(mnu_monthview)
        left_menu_selected.append(mnu_curmonthcal)
               
        if Globs.admin:
            right_menu_selected.append(mnu_newevent)
        
    elif lastaction == 'evtadd':
        left_menu_selected.append(mnu_monthview)
        left_menu_selected.append(mnu_listview)
        left_menu_selected.append(mnu_curmonthcal)

    else:
        left_menu_selected.append(mnu_listview)
        left_menu_selected.append(mnu_curmonthcal)
        
        if Globs.admin:       
            right_menu_selected.append(mnu_newevent)

    left_menu_selected = u'\r\n'.join(left_menu_selected)
    right_menu_selected = u'\r\n'.join(right_menu_selected)
    
    html = u'\r\n'.join(html)
    html = html % (left_menu_selected, right_menu_selected)

    return html
        

def getdatefield(str_date):
    str_year = ''
    str_month = ''
    str_day = ''
    
    if len(str_date) == 6:
        # year+month
        str_year = str_date[:4]
        str_month = str_date[4:]
        str_day = '1'

    elif len(str_date) == 8:
        # year+month+day
        str_year = str_date[:4]
        str_month = str_date[4:6]
        str_day = str_date[6:]
    
    elif len(str_date) == 10:
        # year+?+month+?+day
        str_year = str_date[:4]
        str_month = str_date[5:7]
        str_day = str_date[8:]
    
    else:
        raise ValueError
    
    # It raises exception if the input date is incorrect
    temp = datetime.date(int(str_year), int(str_month), int(str_day))

    return temp.year, temp.month, temp.day


def gettimefield(str_time):
    str_hour = ''
    str_min = ''
    
    if len(str_time) == 4:
        # hour+minute
        str_hour = str_time[:2]
        str_min = str_time[2:]
    
    elif len(str_time) == 5:
        # hour+?+minute
        str_hour = str_time[:2]
        str_min = str_time[3:]
        
    else:
        raise ValueError
    
    # It raises exception if the input date is incorrect
    temp = datetime.time(int(str_hour), int(str_min))

    return temp.hour, temp.minute

def showmessage():
    if Globs.adminmsg:
        return u'<table class="eventcalendar_msg" align="center"><tr><td class="eventcalendar_msg">%s</td></tr></table>' % Globs.adminmsg
    else:
        return ''

def gettodaydate():
    today = Globs.today
    return today.year, today.month, today.day    


def cal_listhead():

    html = [
        u'  <tr>',
        u'      <td class="list_head">Title</td>',
        u'      <td class="list_head">Start Date</td>',
        u'      <td class="list_head">Start Time</td>',
        u'      <td class="list_head">End Date</td>',
        u'      <td class="list_head">End Time</td>',
        u'      <td class="list_head">Description</td>',
        u'      <td class="list_head">Reference</td>',
        u'      <td class="list_head">Action</td>',
        u'  </tr>',
        ]
        
    return u'\r\n'.join(html)


def showeventlist(macro, form_vals):
    Globs.lastaction = 'evtlist'
    
    request = macro.request
    formatter = macro.formatter
    
    html_event_rows = []
    html_list_header = cal_listhead()
    
    # read all the events from data file
    events, cal_events = loadEvents(macro, 1)
    
    # sort events
    sorted_eventids = events.keys()
    sorted_eventids.sort(cmp=lambda x,y: cmp(events[x]['startdate'], events[y]['startdate']))
    
    for eid in sorted_eventids:
        html_event_rows.append( listshow_event(events[eid], form_vals) )
    
    html_event_rows = u'\r\n'.join(html_event_rows)
    
    html_list_table = [
        u'\r\n<div id="eventlist">',
        u'<table width="90%%" align="center">',
        u'%s' % html_list_header,
        u'%s' % html_event_rows,
        u'</table>',
        u'</div>',
        ]
    html_list_table = u'\r\n'.join(html_list_table)
        
    return html_list_table
        

def listshow_event(event, form_vals):
    
    if event['id'][0] != 'w':
        html_editlink = u'  <a href="?calaction=evtedit&id=%s&prevcalaction=evtlist">[Edit]</a>' % event['id']
        html_dellink = u'  <a href="?calaction=evtdel&id=%s&prevcalaction=evtlist" onClick="return confirm(\'Are you sure to delete?\');">[Delete]</a>' % event['id']
    else:
        html_editlink = ''
        html_dellink = ''
        
    syear, smonth, sday = getdatefield(event['startdate'])
    eyear, emonth, eday = getdatefield(event['enddate'])
    
    time_info = 1
    try:
        shour, smin = gettimefield(event['starttime'])
        ehour, emin = gettimefield(event['endtime'])
    except ValueError:
        time_info = 0
    
    if time_info:
        html = [
            u'  <tr>',
            u'  <td class="list_entry">%s</td>' % converttext(event['title']),
            u'  <td class="list_entry">%d %02d/%02d</td>' % (syear, smonth, sday),
            u'  <td class="list_entry">%02d:%02d</td>' % (shour, smin),
            u'  <td class="list_entry">%d %02d/%02d</td>' % (eyear, emonth, eday),
            u'  <td class="list_entry">%02d:%02d</td>' % (ehour, emin),
            u'  <td class="list_entry">%s</td>' % converttext(event['description']),
            u'  <td class="list_entry">%s</td>' % showReferPageParsed(event),
            u'  <td class="list_entry">%s%s</td>' % (html_editlink, html_dellink),
            u'  </tr>',
            ]
    
    else:
        html = [
            u'  <tr>',
            u'  <td class="list_entry">%s</td>' % converttext(event['title']),
            u'  <td class="list_entry">%d %02d/%02d</td>' % (syear, smonth, sday),
            u'  <td class="list_entry">&nbsp;</td>',
            u'  <td class="list_entry">%d %02d/%02d</td>' % (eyear, emonth, eday),
            u'  <td class="list_entry">&nbsp;</td>',
            u'  <td class="list_entry">%s</td>' % converttext(event['description']),
            u'  <td class="list_entry">%s</td>' % showReferPageParsed(event),
            u'  <td class="list_entry">%s%s</td>' % (html_editlink, html_dellink),
            u'  </tr>',
            ]
        
    return u'\r\n'.join(html)

def showcalendar(macro, form_vals):
    Globs.lastaction = 'calshow'
    
    request = macro.request
    formatter = macro.formatter
    
    if form_vals.has_key('caldate'):
        try:
            year, month, str_temp = getdatefield(form_vals['caldate'])
        except ValueError:
            message('Invalid target date: e.g., "200510"')
            year, month, dy = gettodaydate()
    else:
        year, month, dy = gettodaydate()
    
    html = showeventcalendar(macro, year, month)
    
    return u''.join(html)

def getEventById(macro, e_id):

    events, cal_events = loadEvents(macro, 1)
        
    return events[e_id]

# load events from text files and wiki pages
def loadEvents(macro, readsubpages=1):
    
    events, cal_events = loadEventsFromTextFile(macro)
    
    if readsubpages:
        # reads events from child pages----------------------
        events, cal_events = loadEventsFromWikiPage(macro, events, cal_events)
    
    Globs.events = events
    Globs.cal_events = cal_events
    
    return events, cal_events

# sort cal_events by length of days
def comp_cal_events(xid, yid):
    events = Globs.events
    
    if events[xid]['date_len'] > events[yid]['date_len']:
        return -1
    elif events[xid]['date_len'] == events[yid]['date_len']:
        if events[xid]['date_len'] == 1:
            return cmp(events[xid]['starttime'], events[yid]['starttime'])
        else:
            return 0
    else:
        return 1
        
# load events from text files
def loadEventsFromTextFile(macro):

    # delimeter
    cal_delimeter = Globs.cal_delimeter
    
    # event information
    events = {}
    
    # event information per date
    cal_events = {}
    
    if not os.path.isfile(Globs.out_fname):
        # message('Failed to load event data file (no event or data file is removed)')
        return events, cal_events

    try:
        event_file = codecs.open(Globs.out_fname,'r', encoding='utf-8')
    except IOError:
        message('Failed to load event data file (unable to read data file)')
        return events, cal_events
    
    while 1:
        in_line = event_file.readline()
                
        if not in_line:
            break
        
        if in_line == '\r\n':
            continue
        
        try:
            [e_id, e_start_date, e_start_time, e_end_date, e_end_time, e_title, e_description, e_ref] = in_line.split(cal_delimeter)
        except ValueError:
            message('Failed to parse the events (the data file may be corrupt)')
            return events, cal_events
        
        try:
            e_start_year, e_start_month, e_start_day = getdatefield(e_start_date)
            e_end_year, e_end_month, e_end_day = getdatefield(e_end_date)
        except ValueError:
            message('An event data is corrupted: invalid date format')

        if e_start_time and e_end_time:
	        try:
	            e_start_time_hour, e_start_time_min = gettimefield(e_start_time)
	            e_end_time_min, e_end_time_min = gettimefield(e_end_time)
	        except ValueError:
	            message('An event data is corrupted: invalid time format')
        
        # store event information
        events[e_id] = {}
        events[e_id]['id'] = e_id
        events[e_id]['startdate'] = e_start_date
        events[e_id]['starttime'] = e_start_time
        events[e_id]['enddate'] = e_end_date
        events[e_id]['endtime'] = e_end_time
        events[e_id]['title'] = e_title
        events[e_id]['description'] = e_description
        events[e_id]['refer'] = e_ref
        events[e_id]['date_len'] = diffday(e_start_date, e_end_date) + 1

        # records event information for each date b/w start and end
        tmp_record_date = e_start_date
        cur_datetime = datetime.date(e_start_year, e_start_month, e_start_day)
        offset = 1
        
        while 1:
            if not cal_events.has_key(tmp_record_date):
                cal_events[tmp_record_date] = []
            cal_events[tmp_record_date].append(e_id)
            
            if tmp_record_date == e_end_date:
                break
            
            day_delta = datetime.timedelta(days=offset)
            tmp_datetime = cur_datetime + day_delta
            
            tmp_record_date = formatDate(tmp_datetime.year, tmp_datetime.month, tmp_datetime.day)
            offset += 1
    
    event_file.close()

    return events, cal_events

def loadEventsFromWikiPage(macro, events, cal_events):

    request = macro.request
    depth = Globs.depth
    parent = Globs.pagename
    
    # reads events from child pages
    children = getPages(request, '^%s/' % parent)
    
    # append the page itself
    children.append(parent)
    
    id = 0
    for child in children:
        shortname = child[len(parent):]
        # possibly limit depth
        if depth and shortname.count('/') > depth:
            continue
        
        p = Page(request, child)
        page_content = p.get_raw_body()
        
        for lines in getEventListFromPage(page_content):
            try:
                e_start_date, e_start_time, e_end_date, e_end_time, e_title, e_description = geteventfield(lines)
            except ValueError:
                # message('An event data is corrupted: invalid event format')
                continue
            
            try:
                e_start_year, e_start_month, e_start_day = getdatefield(e_start_date)
                e_end_year, e_end_month, e_end_day = getdatefield(e_end_date)
            except ValueError:
                # message('An event data is corrupted: invalid date format')
                continue
            
            if e_start_time and e_end_time:
                try:
                    e_start_time_hour, e_start_time_min = gettimefield(e_start_time)
                    e_end_time_min, e_end_time_min = gettimefield(e_end_time)
                except ValueError:
                    # message('An event data is corrupted: invalid time format')
                    continue
            
            e_ref = p.page_name
            
            if not e_start_date:
                # message('An event data is corrupted: invalid time format')
                continue
            
            id += 1
            e_id = u'w%d' % id
            
            # store event information
            events[e_id] = {}
            events[e_id]['id'] = e_id
            events[e_id]['startdate'] = e_start_date
            events[e_id]['starttime'] = e_start_time
            events[e_id]['enddate'] = e_end_date
            events[e_id]['endtime'] = e_end_time
            events[e_id]['title'] = e_title
            events[e_id]['description'] = e_description
            events[e_id]['refer'] = e_ref
            events[e_id]['date_len'] = diffday(e_start_date, e_end_date) + 1
            
            # message('event %s: %s' % (e_id, events[e_id]))
            
            # records event information for each date b/w start and end
            tmp_record_date = e_start_date
            cur_datetime = datetime.date(e_start_year, e_start_month, e_start_day)
            offset = 1
            
            while 1:
                if not cal_events.has_key(tmp_record_date):
                    cal_events[tmp_record_date] = []
                cal_events[tmp_record_date].append(e_id)
                
                if tmp_record_date == e_end_date:
                    break
                
                day_delta = datetime.timedelta(days=offset)
                tmp_datetime = cur_datetime + day_delta
                
                tmp_record_date = formatDate(tmp_datetime.year, tmp_datetime.month, tmp_datetime.day)
                offset += 1
    
    return events, cal_events


def getEventListFromPage(lines):
    regex_eventcal = r'[\{]{3}#!eventcal(\s*.*?[^}]+)[\}]{3}'
    
    pattern = re.compile(regex_eventcal, re.UNICODE + re.MULTILINE)
    match = pattern.findall(lines)
    return match

def geteventfield(lines):
    # need help on regular expressions for more efficient/flexible form
    regex_date = r'(\d{4}[/-]\d{2}[/-]\d{2})'
    regex_time = r'(\d{2}[:]\d{2})'
    # regex_title = r'["\'=-]+(\s*.*[^"\'=-]+)["\'=-]+'
    regex_title = r'["]+(\s*.*?)["]+|[\']{2,}(\s*.*?)[\']{2,}|[=]+(\s*.*?)[=]+' 
    
    # retrieve date
    pattern = re.compile(regex_date, re.UNICODE + re.MULTILINE)
    
    match = pattern.findall(lines)
    
    if not len(match):
        # message('at least one date field should be specified')
        # self.printoutput('Parse Error', msg, '')
        # ERROR
        return '','','','','',''
    
    # month, day only should be handled
    
    startdate = match[0]
    if len(match) > 1:
        enddate = match[1]
    else:
        enddate = ''
    
    # retrieve time
    pattern = re.compile(regex_time, re.UNICODE + re.MULTILINE)
    
    match = pattern.findall(lines)
    
    if len(match) >= 2:
        starttime = match[0]
        endtime = match[1]
    elif len(match) == 0:
        starttime = ''
        endtime = ''
    else:
        # message('no or 2 time field should be specified')
        # ERROR
        return '','','','','',''
        
    # retrieve title
    pattern = re.compile(regex_title, re.UNICODE + re.MULTILINE + re.DOTALL)
    
    match = pattern.search(lines)
    
    if not match:
        title = 'No title'
    else:
        for item in match.groups():
            if item:
                title = item
                break
    
    # if no time, it's 1-day event
    if not enddate:
        enddate = startdate
    
    # check the validity of date/time
    try:
        syear, smonth, sday = getdatefield(startdate)
        eyear, emonth, eday = getdatefield(enddate)
    except ValueError:
        # message('invalid date format: %s, %s' % (startdate, enddate))
        return '','','','','',''
    
    if startdate and enddate:
        if datetime.date(syear, smonth, sday) > datetime.date(eyear, emonth, eday):
            # message('startdate should precede enddate')
            return '','','','','',''
    
    # format date
    startdate = u'%4d%02d%02d' %(syear, smonth, sday)
    enddate = u'%4d%02d%02d' %(eyear, emonth, eday)
    
    if (starttime and endtime):
        try:
            shour, smin = gettimefield(starttime)
            ehour, emin = gettimefield(endtime)
        except ValueError:
            # message('invalid time format: %s, %s' % (startdate, enddate))
            return '','','','','',''
        
        if startdate == enddate:
            if datetime.time(shour, smin) > datetime.time(ehour, emin):
                # message('starttime should precede endtime')
                return '','','','','',''
                
        # format time
        starttime = u'%02d%02d' %(shour, smin)
        endtime = u'%02d%02d' %(ehour, emin)
    
    return startdate, starttime, enddate, endtime, title, ''


def addevent(macro, form_vals):
    Globs.lastaction = 'evtadd'
    
    request = macro.request
    formatter = macro.formatter
        
    form_fields = ('title', 'startdate', 'starttime', 'enddate', 'endtime', 'description', 'refer')
    required_fields = {'title': 'Title', 'startdate': 'Start Date', 'enddate': 'End Date'}
    
    event_form = {}
    event_form = event_form.fromkeys(form_fields, '')
    
    for field in event_form.keys():
        if form_vals.has_key(field):
            event_form[field] = form_vals[field]
            
    missing_fields = []
    for field in required_fields.keys():
        if not event_form[field]:
            missing_fields.append(required_fields[field])
            
    if missing_fields:
        message('Failed to add the event (insufficient input: %s)' % u','.join(missing_fields) )
        return addeventform(macro, form_vals)
        
    if (event_form['starttime'] and (not event_form['endtime'])) or (not event_form['starttime'] and event_form['endtime']):
        message('Failed to add the event (insufficient input: start/end time)' )
        return addeventform(macro, form_vals)
    
    else:
        if (event_form['starttime'] and event_form['endtime']):
	        try:
	            hour, min = gettimefield( event_form['starttime'] )
	            hour, min = gettimefield( event_form['endtime'] )
	        except ValueError:
	            message('Failed to add the event (invalid time format)' )
	            return addeventform(macro, form_vals)
    
    try:
        year, month, day = getdatefield( event_form['startdate'] )
        year, month, day = getdatefield( event_form['enddate'] )
    
    except ValueError:
        message('Failed to add the event (invalid date format)' )
        return addeventform(macro, form_vals)
    
    return addeventnow(macro, event_form, form_vals)


def updateevent(macro, form_vals):
    Globs.lastaction = 'evtedit'
    
    request = macro.request
    formatter = macro.formatter
    
    form_fields = ('id', 'title', 'startdate', 'starttime', 'enddate', 'endtime', 'description', 'refer')
    required_fields = {'id': 'Event ID', 'title': 'Title', 'startdate': 'Start Date', 'enddate': 'End Date'}
    
    event_form = {}
    event_form = event_form.fromkeys(form_fields, '')
    
    for field in event_form.keys():
        if form_vals.has_key(field):
            event_form[field] = form_vals[field]
            
    missing_fields = []
    for field in required_fields.keys():
        if not event_form[field]:
            missing_fields.append(required_fields[field])
            
    if missing_fields:
        message('Failed to update the event (insufficient input: %s)' % u','.join(missing_fields) )
        return editeventform(macro, form_vals, event_form)
        
    if (event_form['starttime'] and (not event_form['endtime'])) or (not event_form['starttime'] and event_form['endtime']):
        message('Failed to update the event (insufficient input: start/end time)' )
        return addeventform(macro, form_vals)

    else:
        if event_form['starttime'] and event_form['endtime']:
            try:
                hour, min = gettimefield( event_form['starttime'] )
                hour, min = gettimefield( event_form['endtime'] )
            except ValueError:
                message('Failed to update the event (invalid time format)' )
                return editeventform(macro, form_vals, event_form)
    try:
        year, month, day = getdatefield( event_form['startdate'] )
        year, month, day = getdatefield( event_form['enddate'] )
    
    except ValueError:
        message('Failed to update the event (invalid date format)' )
        return editeventform(macro, form_vals, event_form)
    
    try:
        tempid = int(event_form['id']) + 1
    except ValueError:
        message('Failed to update the event (invalid id format)' )
        return editeventform(macro, form_vals, event_form)
    
    return updateeventnow(macro, event_form, form_vals)

# input form to add events
def addeventform(macro, form_vals):
    Globs.lastaction = 'evtadd'
    prev_calaction = ''
    cal_date = ''
    
    request = macro.request
    formatter = macro.formatter
    
    form_fields = ('title', 'startdate', 'enddate', 'starttime', 'endtime', 'description', 'refer')
    event_form = {}
    event_form = event_form.fromkeys(form_fields, '')
    
    for field in event_form.keys():
        if form_vals.has_key(field):
            event_form[field] = form_vals[field]
    
    if form_vals.has_key('prevcalaction'):
        prev_calaction = form_vals['prevcalaction']
    
    if form_vals.has_key('caldate'):
        cal_date = form_vals['caldate']
    
    year, month, day = gettodaydate()
    syear = 0

    if cal_date:
        try:
            syear, smonth, sday = getdatefield(cal_date)
        except  ValueError:
            message('Invalid target date: e.g., "200510"')
        
        if syear == year and smonth == month:
            sday = day

    if not event_form['startdate']:
        if syear:
            event_form['startdate'] = u'%d%02d%02d' % (syear, smonth, sday)
        else:
            event_form['startdate'] = u'%d%02d%02d' % (year, month, day)

    if not event_form['enddate']:
        if syear:
            event_form['enddate'] = u'%d%02d%02d' % (syear, smonth, sday)
        else:
            event_form['enddate'] = u'%d%02d%02d' % (year, month, day)
    
    if not event_form['starttime']:
        event_form['starttime'] = u'0900'
        
    if not event_form['endtime']:
        event_form['endtime'] = u'1800'
    
    html = [
        u'<div id=addeventform>',
        u'<table id=addeventform width=90%%>',
        u'<form action="%s" name=a"ddeventform" METHOD=POST>' % Globs.subname,

		u'<tr><td><b>Title</b></td><td><input type="text" size="30" maxlength="100" name="title" value="%s"></td></tr>' % event_form['title'],
		u'<tr><td><b>Start Date</b></td><td><input type="text" size="8" maxlength="8" name="startdate" value="%s"> (YYYYMMDD)</td></tr>' % event_form['startdate'],
		u'<tr><td>Start Time</td><td><input type="text" size="4" maxlength="4" name="starttime" value="%s"> (HHMM)</td></tr>' % event_form['starttime'],
		u'<tr><td><b>End Date</b></td><td><input type="text" size="8" maxlength="8" name="enddate" value="%s"> (YYYYMMDD)</td></tr>' % event_form['enddate'],
		u'<tr><td>End Time</td><td><input type="text" size="4" maxlength="4" name="endtime" value="%s"> (HHMM)</td></tr>' % event_form['endtime'],
		
		u'<tr><td>Description</td><td><input type="text" size="50" maxlength="255" name="description" value="%s"></td></tr>' % event_form['description'],
		#u'<tr><td>Reference</td><td><input type="text" size="50" maxlength="255" name="refer" value="%s"> (ReferPageName)</td></tr>' % event_form['refer'],
		
		u'<tr><td colspan="2" align="right"><input type="submit" value="POST" class="postbutton"></td></tr>',
		u'<input type="hidden" value="evtaddnow" name="calaction">',
		u'<input type="hidden" value="%s" name="prevcalaction">' % prev_calaction,
		u'<input type="hidden" value="%s" name="caldate">' % cal_date,
		u'</form>',
		u'</table>',
		u'</div>',
        ]

    
    return u'\r\n'.join(html)

def addeventnow(macro, event_form, form_vals):
    Globs.lastaction = 'evtadd'
    
    # Add an event with inputs
    
    request = macro.request
    formatter = macro.formatter
    
    cal_delimeter = Globs.cal_delimeter
    
    try:
        # If the data file exists
        inx_file = codecs.open(Globs.inx_fname,'r+',encoding='utf-8')
    except IOError:
        try:
            # If the directory does not exist
            if not os.path.isdir(Globs.datafiledir):
                # Create a attachments directory first
                # Is conflict with moinmoin?
                os.mkdir(Globs.datafiledir)
            
            inx_file = codecs.open(Globs.inx_fname,'w',encoding='utf-8')

        except IOError:
            message('Failed to add the event (unable to create an index file)')
            return addeventform(macro, form_vals)
    
    try:
        cur_index = inx_file.readline()
        cur_index = int(cur_index)
    except ValueError:
        cur_index = 0

    cur_index = cur_index + 1
    cur_index = str(cur_index)
    
    try:
        out_file = codecs.open(Globs.out_fname,'a+',encoding='utf-8')
    except IOError:
        message('Failed to add the event (unable to create a data file)')
        return addeventform(macro, form_vals)
    
    eventitem = [
        cur_index,
        cal_delimeter,
        event_form['startdate'],
        cal_delimeter,
        event_form['starttime'],
        cal_delimeter,
        event_form['enddate'], 
        cal_delimeter,
        event_form['endtime'],
        cal_delimeter,
        convertdelimiter(event_form['title']),
        cal_delimeter,
        convertdelimiter(event_form['description']),
        cal_delimeter,
        convertdelimiter(event_form['refer']),
        ]
    
    eventitem = u''.join(eventitem)
    
    out_file.write(eventitem + u'\r\n')
    out_file.close()
    
    inx_file.seek(0)
    inx_file.write(cur_index)
    inx_file.close()
    
    # add log entry
    # addLogEntry(macro.request, 'EVTNEW', Globs.pagename, 'titled \'%s\'' % event_form['title'])
    
    message('The event is added')
    return redirectPreviousAction(macro, form_vals)


def updateeventnow(macro, event_form, form_vals):
    Globs.lastaction = 'evtedit'
    
    # Update an event with inputs
    
    request = macro.request
    formatter = macro.formatter
    
    cal_delimeter = Globs.cal_delimeter
    
    # read all the events from data file
    events, cal_events = loadEvents(macro, 0)
    
    # update the selected event item
    try:
        events[event_form['id']].update(event_form.items())
    except KeyError:
        message('Failed to update the event (no such event)')
        return editeventform(macro, form_vals, event_form)
    
    # format the data fit to data file
    event_items = []
    for eid in events.keys():
        eventitem = [
            eid,
            cal_delimeter,
            events[eid]['startdate'],
            cal_delimeter,
            events[eid]['starttime'],
            cal_delimeter,
            events[eid]['enddate'],
            cal_delimeter,
            events[eid]['endtime'],
            cal_delimeter,
            convertdelimiter(events[eid]['title']),
            cal_delimeter,
            convertdelimiter(events[eid]['description']),
            cal_delimeter,
            convertdelimiter(events[eid]['refer']),
            ]
    
        eventitem = u''.join(eventitem)
        event_items.append(eventitem)
    
    event_items = u'\r\n'.join(event_items) + u'\r\n'

    # writes the data file back
    try:
        out_file = codecs.open(Globs.out_fname,'w',encoding='utf-8')
    except IOError:
        message('Failed to update the event (unable to open a data file)')
        return editeventform(macro, form_vals, event_form)
        
    out_file.writelines(event_items)
    out_file.close()
    
    # add log entry
    # addLogEntry(macro.request, 'EVTUPD', Globs.pagename, 'titled \'%s\'' % events[eid]['title'])
    
    message('The event is updated')
    return redirectPreviousAction(macro, form_vals)

def deleteeventnow(macro, form_vals, eid):
    
    # Remove an event with inputs
    
    request = macro.request
    formatter = macro.formatter
    
    cal_delimeter = Globs.cal_delimeter
    
    # read all the events from data file
    events, cal_events = loadEvents(macro, 0)
    
    # remove the selected event item
    try:
        deletedevent = events.pop(eid)
    except KeyError:
        message('Failed to delete the event (no such event, id: %s)' % eid )
        return redirectPreviousAction(macro, form_vals)
    
    # format the data fit to data file
    event_items = []
    for eid in events.keys():
        eventitem = [
            eid,
            cal_delimeter,
            events[eid]['startdate'], 
            cal_delimeter,
            events[eid]['starttime'],
            cal_delimeter,
            events[eid]['enddate'], 
            cal_delimeter,
            events[eid]['endtime'],
            cal_delimeter,
            convertdelimiter(events[eid]['title']),
            cal_delimeter,
            convertdelimiter(events[eid]['description']),
            cal_delimeter,
            convertdelimiter(events[eid]['refer']),
            ]
            
        eventitem = u''.join(eventitem)
        event_items.append(eventitem)
    
    if event_items:
        event_items = u'\r\n'.join(event_items) + u'\r\n'
    else:
        event_items = u'\r\n'.join(event_items)
    
    # writes the data file back
    try:
        out_file = codecs.open(Globs.out_fname,'w',encoding='utf-8')
    except IOError:
        message('Failed to delete the event (unable to open a data file)')
        return editeventform(macro, form_vals, event_form)
        
    out_file.writelines(event_items)
    out_file.close()
    
    # add log entry
    # addLogEntry(macro.request, 'EVTDEL', Globs.pagename, 'titled \'%s\'' % events[eid]['title'])
    
    message('The event is deleted')
    return redirectPreviousAction(macro, form_vals)


def converttext(targettext):
    # Converts some special characters of html to plain-text style
    # What else to handle?

    targettext = targettext.replace(u'&', '&amp')
    targettext = targettext.replace(u'>', '&gt;')
    targettext = targettext.replace(u'<', '&lt;')
    targettext = targettext.replace(u'\n', '<br>')
    targettext = targettext.replace(u'"', '&quot;')
    targettext = targettext.replace(u'\t', '&nbsp;&nbsp;&nbsp;&nbsp')
    targettext = targettext.replace(u'  ', '&nbsp;&nbsp;')
        
    return targettext

def convertdelimiter(targettext):
    # Converts delimeter to other string to avoid a crash
    
    targettext = targettext.replace(Globs.cal_delimeter, '    ')
    
    return targettext



def showeventcalendar(macro, year, month):
    Globs.lastaction = 'calshow'
    
    request = macro.request
    formatter = macro.formatter
    _ = request.getText
    
    wkend = Globs.wkend
    months= Globs.months
    wkdays = Globs.wkdays
    
    # get the calendar
    monthcal = calendar.monthcalendar(year, month)

    # shows current year & month
    html_header_curyearmonth = calhead_yearmonth(year, month)
    
    r7 = range(7)
    
    # shows header of week days
    html_header_weekdays = []
    
    for wkday in r7:
        wday = _(wkdays[wkday])
        html_header_weekdays.append( calhead_weekday(wday) )
    html_header_weekdays = '    <tr>\r\n%s\r\n</tr>\r\n' % u'\r\n'.join(html_header_weekdays)
 
    # read all the events from data file
    events, cal_events = loadEvents(macro, 1)
    # message(u'total # of events: %d' % len(events))
    
    # sort cal_events
    for eachdate in cal_events.keys():
        # cal_events[eachdate].sort(cmp=lambda x,y: cmp(events[y]['date_len'], events[x]['date_len']))
        cal_events[eachdate].sort(comp_cal_events)
            
    # pending events for next row
    next_pending = []
    
    # gets previous, next month
    day_delta = datetime.timedelta(days=-1)
    cur_month = datetime.date(year, month, 1)
    prev_month = cur_month + day_delta
    
    day_delta = datetime.timedelta(days=15)
    cur_month_end = datetime.date(year, month, 25)
    next_month = cur_month_end + day_delta
    
    prev_monthcal = calendar.monthcalendar(prev_month.year, prev_month.month)
    next_monthcal = calendar.monthcalendar(next_month.year, next_month.month)
    
    # shows days
    html_week_rows = []
    
    for week in monthcal:
        
        # day head rows
        html_headday_cols = []
        html_events_rows = []
        
        for wkday in r7:
             
            day = week[wkday]
            
            if not day:
                if week == monthcal[0]:
                    nb_day = prev_monthcal[-1][wkday]
                else:
                    nb_day = next_monthcal[0][wkday]
                    
                html_headday_cols.append( calhead_day_nbmonth(nb_day) )
            else:
                html_headday_cols.append( calhead_day(year, month, day, wkday) )
        
        html_headday_row = '    <tr>\r\n%s\r\n</tr>\r\n' % u'\r\n'.join(html_headday_cols)
        html_week_rows.append(html_headday_row)
        
        # dummy rows
        html_headdummy_cols = []
        
        for wkday in r7:
            day = week[wkday]
            if not day:
                html_headdummy_cols.append( calshow_blankbox('head_dummy_nbmonth') )
            else:
                html_headdummy_cols.append( calshow_blankbox('head_dummy') )
        
        html_headdummy_cols = u'\r\n'.join(html_headdummy_cols)
        html_week_rows.append(' <tr>\r\n%s </tr>\r\n' % html_headdummy_cols)
        
        # pending events for next row
        pending = next_pending
        next_pending = []
        
        # show events
        while 1: 
            event_left = 7
            colspan = -1
            html_events_cols = []

            for wkday in r7:
             
                day = week[wkday]
                
                if not day:
                    if week == monthcal[0]:
                        cur_date = formatDate(prev_month.year, prev_month.month, prev_monthcal[-1][wkday])
                    else:
                        cur_date = formatDate(next_month.year, next_month.month, next_monthcal[0][wkday])
                else:
                    cur_date = formatDate(year, month, day)

                # if an event is already displayed with colspan
                if colspan > 0:
                    colspan -= 1
                    if cal_events.has_key(cur_date) and lastevent in cal_events[cur_date]:
                        cal_events[cur_date].remove(lastevent)
                    
                    continue
                    
                # if there is any event for this date
                if cal_events.has_key(cur_date):
                    if len(cal_events[cur_date]) > 0:
                        
                        if wkday == 0 and len(pending) > 0:
                            todo_event_id = pending.pop(0)
                            if todo_event_id in cal_events[cur_date]:
                                cur_event = events[todo_event_id]
                                temp_len = diffday(cur_date, cur_event['enddate']) + 1
                                
                                # calculate colspan value
                                if (7-wkday) < temp_len:
                                    colspan = 7 - wkday
                                    next_pending.append(cur_event['id'])
                                    html_events_cols.append( calshow_eventbox(cur_event, colspan, 'append_pending', cur_date) )

                                else:
                                    colspan = temp_len
                                    html_events_cols.append( calshow_eventbox(cur_event, colspan, 'append', cur_date) )
                                
                                
                                cal_events[cur_date].remove(todo_event_id)

                                colspan -= 1
                                lastevent = todo_event_id
                            else:
                                message('FATAL ERROR')
                            continue
                            
                        # if the start date of the event is current date    
                        
                        for e_id in cal_events[cur_date]:
                            if events[e_id]['startdate'] == cur_date:
                                cur_event = events[cal_events[cur_date].pop(cal_events[cur_date].index(e_id))]
                                
                                # calculate colspan value
                                if (7-wkday) < cur_event['date_len']:
                                    colspan = 7 - wkday
                                    next_pending.append(cur_event['id'])
                                    html_events_cols.append( calshow_eventbox(cur_event, colspan, 'pending', cur_date) )

                                else:
                                    colspan = cur_event['date_len']
                                    html_events_cols.append( calshow_eventbox(cur_event, colspan, '', cur_date) )
                                
                                colspan -= 1
                                lastevent = cur_event['id']
                                break
                            
                        # if the start date of the event is NOT current date
                        else:
                            if day == 0 and wkday == 0 and week == monthcal[0]:
                                cur_event = events[cal_events[cur_date].pop(0)]
                                temp_len = diffday(cur_date, cur_event['enddate']) + 1
                                
                                # calculate colspan value
                                if (7-wkday) < temp_len:
                                    colspan = 7 - wkday
                                    next_pending.append(cur_event['id'])
                                    html_events_cols.append( calshow_eventbox(cur_event, colspan, 'append_pending', cur_date) )
                                else:
                                    colspan = temp_len
                                    html_events_cols.append( calshow_eventbox(cur_event, colspan, 'append', cur_date) )
                                
                                
                                colspan -= 1
                                lastevent = cur_event['id']
                            else:
                                if not day:
                                    html_events_cols.append( calshow_blankbox('cal_nbmonth') )
                                else:
                                    html_events_cols.append( calshow_blankbox('cal_noevent') )
                                event_left -= 1
                                
                    else:
                        if not day:
                            html_events_cols.append( calshow_blankbox('cal_nbmonth') )
                        else:
                            html_events_cols.append( calshow_blankbox('cal_noevent') )
                        
                        event_left -= 1        
                
                # if there is NO event for this date
                else:
                    if not day:
                        html_events_cols.append( calshow_blankbox('cal_nbmonth') )
                    else:
                        html_events_cols.append( calshow_blankbox('cal_noevent') )
                        
                    event_left -= 1
            
            # if no event for this entry
            if not event_left:
                # ignore the previous entry
                break
            else:
                html_events_rows.append(' <tr>\r\n%s </tr>\r\n' % u'\r\n'.join(html_events_cols))
            
        # show dummy blank slots for week height
        left_blank_rows = 2 - len(html_events_rows)
        
        # message('%d: left_blank_rows=%d' % (day, left_blank_rows))
        
        if left_blank_rows > 0:
            for i in range(left_blank_rows):
                html_events_cols = []
                for wkday in r7:
                    day = week[wkday]
                    if not day:
                        html_events_cols.append( calshow_blankbox('cal_nbmonth') )
                    else:
                        html_events_cols.append( calshow_blankbox('cal_noevent') )
                
                html_events_rows.append(' <tr>\r\n%s </tr>\r\n' % u'\r\n'.join(html_events_cols))
        
        
        # close the week slots
        html_events_cols = []
        for wkday in r7:
            day = week[wkday]
            if not day:
                html_events_cols.append( calshow_blankbox('cal_last_nbmonth') )
            else:
                html_events_cols.append( calshow_blankbox('cal_last_noevent') )
    
        html_events_rows.append(' <tr>\r\n%s </tr>\r\n' % u'\r\n'.join(html_events_cols))
        
        html_events_rows = u'\r\n'.join(html_events_rows)
        html_week_rows.append(html_events_rows)
            
    html_calendar_rows = u'\r\n'.join(html_week_rows)
    
    html_cal_table = [
        u'\r\n<div id="eventcalendar">',
        u'<table width="90%%" class="eventcalendar">',
        u'%s' % html_header_curyearmonth,
        u'%s' % html_header_weekdays,
        u'%s' % html_calendar_rows,
        u'</table>',
        u'</div>',
        ]
    html_cal_table = u'\r\n'.join(html_cal_table)
        
    return html_cal_table

# show weekday
def calhead_yearmonth(year, month):
    
    months = Globs.months
    monthstyle_us = Globs.month_style_us
    
    nextyear, nextmonth = yearmonthplusoffset(year, month, 1)
    prevyear, prevmonth = yearmonthplusoffset(year, month, -1)
    
    prevlink = u'?calaction=calshow&caldate=%d%02d' % (prevyear, prevmonth)
    nextlink = u'?calaction=calshow&caldate=%d%02d' % (nextyear, nextmonth)
    
    if monthstyle_us:
        stryearmonth = u'%s %d' % (months[month-1], year)
    else:
        stryearmonth = u'%d / %02d' % (year, month)
    
    html = [
        u'  <tr>',
        u'      <td class="head_yearmonth"><a href="%s">&lt;</a></td>' % prevlink,
        u'      <td colspan="5" class="head_yearmonth">%s</td>' % stryearmonth,
        u'      <td class="head_yearmonth"><a href="%s">&gt;</a></td>' % nextlink,
        u'  </tr>',
        ]
        
    return u'\r\n'.join(html)


# show weekday
def calhead_weekday(wday):
    html = u'       <td class="head_weekday">%s</td>\r\n' % wday
    return html


# show days of current month
def calhead_day(year, month, day, wkday):
    wkend = Globs.wkend
    if wkday == wkend:
        html_text = u'<font color="#FF3300">%s</font>' % day
    else:
        html_text = u'%s' % day
    
    cyear, cmonth, cday = gettodaydate()
    
    if cyear == year and cmonth == month and cday == day:
        html = u'  <td class="head_day_today">&nbsp;%s</td>\r\n' % html_text
    else:
        html = u'  <td class="head_day">&nbsp;%s</td>\r\n' % html_text
       
    return html


# show days of previous or next month
def calhead_day_nbmonth(day):
    html = u'  <td class="head_day_nbmonth">&nbsp;%s</td>\r\n' % day
    return html

    
# show blank calendar box
def calshow_blankbox(classname):
    html = u'  <td class="%s">&nbsp;</td>' % classname
    return html

# show eventbox
def calshow_eventbox(event, colspan, status, cur_date):
    if status:
        status = u'_%s' % status
    
    title = event['title']
    eid = event['id']
    startdate = event['startdate']
    enddate = event['enddate']
    starttime = event['starttime']
    endtime = event['endtime']
    description = event['description']
    
    year, month, day = getdatefield(cur_date)
    
    if eid[0] == 'w':
        eventfromsubpage = 'background-color: #ddffdd;'
    else:
        eventfromsubpage = ''
    
    if (startdate == enddate) and starttime:
        shour, smin = gettimefield(starttime)
        
        link = [
            u'<table width="100%%" style="border-width: 0px; padding: 0px; margin: 0px;"><tr>\r\n',
            u'<td width="10" nowrap style="border-width: 0px; padding: 0px; margin: 0px; text-align: left; vertical-align: top; font-size: 7pt; color: #000000;">%02d:%02d&nbsp;</td>\r\n' % (shour, smin),
            # u'<td>&nbsp;</td>',
            u'<td style="border-width: 0px; padding: 0px; margin: 0px; text-align: left; vertical-align: top;font-size: 8pt;">',
            u'<a href="?calaction=evtview&id=%s&caldate=%d%02d" title="%s">%s</a>' % (eid, year, month, converttext(description), converttext(title)),
            u'</td>\r\n</tr></table>',
            ]
        link = u''.join(link)
    else:
        link = u'<a href="?calaction=evtview&id=%s&caldate=%d%02d" title="%s">%s</a>' % (eid, year, month, converttext(description), converttext(title))
    
    
    html = [
        u'  <td class="cal_eventbox" colspan="%d"><table class="cal_event">' % colspan,
        u'      <tr><td id="cal_event_%s" class="cal_event%s" style="%s">%s</td></tr>' % (eid, status, eventfromsubpage, link),
        u'      </table></td>',
        ]
        
    return u'\r\n'.join(html)
        


# date format should be like '20051004' for 2005, Oct., 04
def diffday(date1, date2):
    
    try:
        year1, month1, day1 = getdatefield(date1)
        year2, month2, day2 = getdatefield(date2)
        tmp_diff = datetime.date(year2, month2, day2) - datetime.date(year1, month1, day1)
    except ValueError:
        message('An event data is corrupted: invalid date format')
        return 0

    return tmp_diff.days


def formatDate(year, month, day):
    # returns like: '20051004'
    return u'%4d%02d%02d' % (year, month, day)

def cliprgb(r,g,b): # don't use 255!
    if r < 0:   r=0
    if r > 254: r=254
    if b < 0:   b=0
    if b > 254: b=254
    if g < 0:   g=0
    if g > 254: g=254
    return r, g, b


def message(astring):
    Globs.adminmsg += u'%s<br>\r\n' % astring

def yearmonthplusoffset(year, month, offset):
    month = month+offset
    # handle offset and under/overflows - quick and dirty, yes!
    while month < 1:
        month = month + 12
        year = year - 1
    while month > 12:
        month = month - 12
        year = year + 1
    return year, month

def getPages(request, filter_regex=None):
    """ Return a (filtered) list of pages names.
    """
    filter = None
    if filter_regex:
        filter = re.compile(filter_regex).match
    pages = request.rootpage.getPageList(filter=filter)
    return pages
    
    
def cutstring(strcut, intlen):
    if len(strcut) > intlen:
        newlen = intlen - 3
        strtemp = strcut[:newlen] + '...'
    else:
        strtemp = strcut
        
    return strtemp