Attachment 'EventCalendar-easytime2.py'
Download 1 """
2 EventCalendar.py Version 0.94 February 06, 2006
3
4 This macro gives a list of the events recorded in the sub-pages in the form of monthly view and list view.
5
6 @copyright: 2006 by Seungik Lee <seungiklee<at>gmail.com> http://www.silee.net/
7 @license: GPL
8
9 For more information, please visit http://moinmoin.wikiwikiweb.de/MacroMarket/EventCalendar
10
11 <Usage>
12
13 * To list the events in a page, just insert [[EventCalendar]]
14 * To insert an event, insert the event information in any pages of specified category (CategoryEventCalendar by default).
15
16 <Parameters>
17
18 * category: the category of the event data to be used in the calendar. default: 'CategoryEventCalendar'
19 * menubar: shows menubar or not (1: show, 0: no menubar). default: 1
20 * monthlywidth: calendar width in pixel or percent (monthly view). default: '600' (pixel)
21 * simplewidth: calendar width in pixel or percent (simpleview). default: '150' (pixel)
22 * firstview: initial calendar view: monthly, list, simple, upcoming. default: 'monthly'
23 * curdate: initial calendar date (in YYYYMM format). default: current month
24 * upcomingrange: # of days for the range of upcoming event list. default: 7 (days)
25
26 <Event Data Format>
27
28 * Example:
29
30 [default_description:: [default_description_text]]
31 [default_bgcolor:: [default_custom_background_color]]
32
33 == <title> ==
34 start:: <startdate> [starttime]
35 [end:: [enddate] [endtime]]
36 [description:: [description_text]]
37 [bgcolor:: [custom_background_color]]
38 [recur:: <recur_freq> <recur_type> [until <recur_until>]]
39
40 ...
41
42 ----
43 CategoryEventCalendar
44
45
46 * title: event title. required
47 * should be enclosed with heading marker ('='), Title cannot be omitted.
48
49 * startdate: date of start. required
50 * should be in YYYY/MM/DD or YYYY-MM-DD
51
52 * starttime: time of start. optional
53 * should be in HH:MM in 24-hour format
54
55 * enddate: date of end. optional
56 * should be in YYYY/MM/DD or YYYY-MM-DD. If omitted, it will be assigned equal to <startdate>.
57
58 * endtime: time of end. optional
59 * should be in HH:MM in 24-hour format. Both of start|end Time can be omitted but not either of them.
60
61 * description: description of the event. optional
62 * any text with no markup. should be in a line.
63
64 * bgcolor: custom background color of the event in monthly view. optional
65 * e.g., #abcdef
66
67 * recur: recurrence information of the event, optional
68 * recur_freq: how many intervals, digit, required
69 * recur_type: [day|week|weekday|month|year], required
70 * day: every [recur_freq] days
71 * week: every [recur_freq] weeks
72 * weekday: on the same weekday of [recur_freq]-th week of the month
73 * month: on the same day of [recur_freq]-th month
74 * year: on the same day of [recur_freq]-th year
75 * recur_until: recurred until when, YYYY/MM/DD or YYYY-MM-DD, optional
76
77 * e.g., 10 day, 2 week until 2006-06-31, 3 weekday, 6 month until 2007-12-31, 1 year
78
79 * default_bgcolor, default_description: default values of bgcolor and description in the page if unavailable
80
81 * The order of the fields after an event title does not matter.
82
83 <Event Data Examples>
84
85 = Default values =
86 default_bgcolor:: #c0c0c0
87 default_description:: testing...
88
89 === Test event ===
90 start:: 2006-01-10 14:00
91 end:: 2006-01-12 17:00
92 description:: test event
93 bgcolor:: #cfcfcf
94
95 === Jinah's Birthday ===
96 start:: 1977-10-20
97 recur:: 1 year
98
99 === Weekly meeting ===
100 start:: 2006-01-17 19:00
101 end:: 21:00
102 recur:: 1 week until 2006-12-31
103
104 ----
105 CategoryEventCalendar
106
107
108 <Notes>
109
110 * It caches all the page list of the specified category and the event information.
111 * If you added/removed a page into/from a category, you need to do 'Delete cache' in the macro page.
112
113 * 'MonthCalendar.py' developed by Thomas Waldmann <ThomasWaldmann@gmx.de> has inspired this macro.
114 * Much buggy.. : please report bugs and suggest your ideas.
115 * If you missed to add css for EventCalender, monthly view may not be readable.
116 * Insert the EventCalendar css classes into the screen.css of an appropriate theme.
117
118
119
120 """
121
122 from MoinMoin import wikiutil, config, search, caching
123 from MoinMoin.Page import Page
124 from MoinMoin.parser import wiki
125 import re, calendar, time, datetime
126 import codecs, os, urllib, sha
127
128 try:
129 import cPickle as pickle
130 except ImportError:
131 import pickle
132
133 # Set pickle protocol, see http://docs.python.org/lib/node64.html
134 PICKLE_PROTOCOL = pickle.HIGHEST_PROTOCOL
135
136
137 # The following line sets the calendar to have either Sunday or Monday as
138 # the first day of the week. Only SUNDAY or MONDAY (case sensitive) are
139 # valid here. All other values will not make good calendars.
140 # XXX change here ----------------vvvvvv
141 calendar.setfirstweekday(calendar.SUNDAY)
142
143
144 class Globs:
145 month_style_us = 1 # 1: October 2005; 2: 2005 / 10
146 defaultcategory = 'CategoryEventCalendar'
147 upcomingrange = 7 # days
148 pagename = ''
149 baseurl = ''
150 subname = ''
151 wkend = ''
152 months = ''
153 wkdays = ''
154 today = ''
155 request = None
156 formatter = None
157 cal_action = ''
158 debugmsg = ''
159 page_action = ''
160 events = None
161
162
163 class Params:
164 menubar = 0
165 monthlywidth = ''
166 simplewidth = ''
167 firstview = ''
168 curdate = ''
169 bgcolor = ''
170 category = ''
171 upcomingrange = 0
172 debug = 0
173
174
175 def execute(macro, args):
176
177 request = macro.request
178 formatter = macro.formatter
179
180 # INITIALIZATION ----------------------------------------
181 setglobalvalues(macro)
182 getparams(args)
183
184 # allowed actions
185 allowed_action = ['monthly', 'list', 'simple', 'upcoming']
186 default_action = Params.firstview
187
188 # Internal variables
189 cal_action = ''
190 form_vals = {}
191
192 # PROCESSING ARGUEMENTS ----------------------------------------
193 if args:
194 args=request.getText(args)
195
196 for item in macro.form.items():
197 if not form_vals.has_key(item[0]):
198 try:
199 form_vals[item[0]]=item[1][0]
200 except AttributeError:
201 pass
202
203 # PROCESSING ACTIONS ----------------------------------------
204 cal_action = form_vals.get('calaction', default_action)
205 page_action = form_vals.get('action', 'show')
206
207 if not cal_action in allowed_action:
208 cal_action = default_action
209
210 form_vals['calaction'] = cal_action
211
212 # CONTROL FUNCTIONS ----------------------------------------
213
214 html = []
215 html_result = ''
216
217 Globs.cal_action = cal_action
218 Globs.page_action = page_action
219
220
221 # redirect to the appropriate view
222 if cal_action == 'monthly':
223 html_result = showcalendar(form_vals)
224
225 if cal_action == 'list':
226 html_result = showeventlist(form_vals)
227
228 if cal_action == 'simple':
229 html_result = showsimplecalendar(form_vals)
230
231 if cal_action == 'upcoming':
232 html_result = showupcomingeventlist(form_vals)
233
234
235 # format output
236 html.append( html_result )
237 html.append( showmenubar(form_vals) )
238
239 if Params.debug and Globs.debugmsg:
240 html.append(u'<p><b>Debug messages:</b><font color="#aa0000"><ol>%s</ol></font>' % Globs.debugmsg)
241
242 return formatter.rawHTML(u''.join(html))
243
244
245
246 def getparams(args):
247 # process arguments
248
249 params = {}
250 if args:
251 # Arguments are comma delimited key=value pairs
252 sargs = args.split(',')
253
254 for item in sargs:
255 sitem = item.split('=')
256
257 if len(sitem) == 2:
258 key, value = sitem[0], sitem[1]
259 params[key.strip()] = value.strip()
260
261 # category name:
262 # default: 'CategoryEventCalendar'
263 Params.category = params.get('category', Globs.defaultcategory)
264
265 # menu bar: shows menubar or not (1: show, 0: no menubar)
266 # default: 1
267 try:
268 Params.menubar = int(params.get('menubar', 1))
269 except (TypeError, ValueError):
270 Params.menubar = 1
271
272 # calendar width in pixel or percent (monthly)
273 # default: 600px
274 Params.monthlywidth = params.get('monthlywidth', '600')
275 if Params.monthlywidth:
276 # Params.monthlywidth = Params.monthlywidth.replace('%', '%%')
277 Params.monthlywidth = ' width="%s" ' % Params.monthlywidth
278
279 # calendar width in pixel or percent (simply)
280 # default: 150px
281 Params.simplewidth = params.get('simplewidth', '150')
282 if Params.simplewidth:
283 # Params.simplewidth = Params.simplewidth.replace('%', '%%')
284 Params.simplewidth = ' width="%s" ' % Params.simplewidth
285
286 # calendar view: monthly, list, simple
287 # default: 'monthly'
288 Params.firstview = params.get('firstview', 'monthly')
289
290 # calendar date: in YYYYMM format (in monthly, simple view)
291 # default: current month
292 Params.curdate = params.get('curdate', '')
293
294 # upcoming range: # of days for upcoming event list
295 # default: 7
296 try:
297 Params.upcomingrange = int(params.get('upcomingrange', Globs.upcomingrange))
298 except (TypeError, ValueError):
299 Params.upcomingrange = Globs.upcomingrange
300
301 # default bgcolor
302 Params.bgcolor = '#ddffdd'
303
304
305 def setglobalvalues(macro):
306
307 request = macro.request
308 formatter = macro.formatter
309
310 # Useful variables
311 Globs.baseurl = request.getBaseURL() + '/'
312 Globs.pagename = formatter.page.page_name
313 Globs.request = request
314 Globs.formatter = formatter
315
316 # This fixes the subpages bug. subname is now used instead of pagename when creating certain urls
317 Globs.subname = Globs.pagename.split('/')[-1]
318
319 pagepath = formatter.page.getPagePath()
320 Globs.pagepath = formatter.page.getPagePath()
321
322 # european / US differences
323 months = ('January','February','March','April','May','June','July','August','September','October','November','December')
324
325 # Set things up for Monday or Sunday as the first day of the week
326 if calendar.firstweekday() == calendar.MONDAY:
327 wkend = 6
328 wkdays = ('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun')
329 elif calendar.firstweekday() == calendar.SUNDAY:
330 wkend = 0
331 wkdays = ('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat')
332
333 Globs.months = months
334 Globs.wkdays = wkdays
335 Globs.wkend = wkend
336
337 year, month, day, h, m, s, wd, yd, ds = request.user.getTime(time.time())
338 Globs.today = datetime.date(year, month, day)
339
340 Globs.debugmsg = ''
341
342
343 def showReferPageParsed(event, targettext='title', showdesc=0):
344 request = Globs.request
345 pagename = Globs.pagename
346
347 refer = event['refer']
348 targettext = event[targettext]
349 startdate = event['startdate']
350 enddate = event['enddate']
351 description = event['description']
352 starttime = event['starttime']
353 endtime = event['endtime']
354 hid = event['hid']
355
356 if showdesc:
357 if (startdate == enddate) and (starttime and endtime):
358 timedescription = '%s:%s ~ %s:%s' % (starttime[:2], starttime[2:], endtime[:2], endtime[2:])
359 if description:
360 timedescription = '%s | ' % timedescription
361 else:
362 timedescription = ''
363
364 targetlink = '<a href="%s#%s" title="%s%s">%s</a>' % ( refer, hid, timedescription, wikiutil.escape(description), wikiutil.escape(targettext) )
365 else:
366 targetlink = '<a href="%s#%s">%s</a>' % ( refer, hid, wikiutil.escape(targettext))
367
368 return targetlink
369
370
371 def getheadingid(request, referpage, title):
372
373 pntt = (referpage + title).encode(config.charset)
374 hid = "head-" + sha.new(pntt).hexdigest()
375 request._page_headings.setdefault(pntt, 0)
376 request._page_headings[pntt] += 1
377 if request._page_headings[pntt] > 1:
378 hid += '-%d'%(request._page_headings[pntt],)
379
380 return hid
381
382
383 def getquerystring(form_vals, req_fields):
384
385 m_query = []
386 tmp_form_vals = form_vals
387
388 # format querystring
389 # action should be poped out
390 for field in req_fields:
391 if tmp_form_vals.has_key(field):
392 m_query.append(u'%s=%s' % (field, tmp_form_vals[field]) )
393
394 if 'prevcalaction' in req_fields:
395 if not tmp_form_vals.has_key('prevcalaction'):
396 m_query.append(u'%s=%s' % ('prevcalaction', tmp_form_vals['calaction']) )
397
398 m_query = u'&'.join(m_query)
399 return m_query
400
401
402 # bottom menu bar
403 def showmenubar(form_vals):
404
405 cal_action = Globs.cal_action
406 page_name = Globs.pagename
407
408 if not Params.menubar: return ''
409
410 if cal_action == 'simple':
411 menuwidth = Params.simplewidth
412 elif cal_action == 'monthly':
413 menuwidth = Params.monthlywidth
414 else:
415 menuwidth = ''
416
417 left_menu_selected = []
418 right_menu_selected = []
419
420 # Go Today
421 year, month, day = gettodaydate()
422 mnu_curmonthcal = u'<a href="%s?calaction=%s&caldate=%d%02d" title="Go Today">[Today]</a>' % (page_name, cal_action, year, month)
423
424 # List View
425 mnu_listview = u'<a href="%s?calaction=list" title="List of all events">[List]</a>' % page_name
426
427 # Monthly View
428 mnu_monthview = u'<a href="%s?calaction=monthly&%s" title="Monthly view">[Monthly]</a>' % (page_name, getquerystring(form_vals, ['caldate']) )
429
430 # Simple Calendar View
431 mnu_simpleview = u'<a href="%s?calaction=simple&%s" title="Simple calendar view">[Simple]</a>' % (page_name, getquerystring(form_vals, ['caldate']) )
432
433 # Upcoming Event List
434 mnu_upcomingview = u'<a href="%s?calaction=upcoming&%s" title="Upcoming event list">[Upcoming]</a>' % (page_name, getquerystring(form_vals, ['caldate']) )
435
436 html = [
437 u'\r\n',
438 u'<table class="eventcalendar_menubar" %s>',
439 u' <tr>',
440 u' <td class="eventcalendar_menubar" align="left">%s</td>',
441 u' <td class="eventcalendar_menubar" align="right">%s</td>',
442 u' </tr>',
443 u'</table>',
444 ]
445
446 if cal_action == 'list':
447 left_menu_selected.append(mnu_monthview)
448 left_menu_selected.append(mnu_simpleview)
449 right_menu_selected.append(mnu_upcomingview)
450
451 elif cal_action == 'simple':
452 left_menu_selected.append(mnu_monthview)
453 left_menu_selected.append(mnu_listview)
454 right_menu_selected.append(mnu_upcomingview)
455 right_menu_selected.append(mnu_curmonthcal)
456
457 elif cal_action == 'upcoming':
458 left_menu_selected.append(mnu_monthview)
459 left_menu_selected.append(mnu_simpleview)
460 left_menu_selected.append(mnu_listview)
461
462 else:
463 left_menu_selected.append(mnu_listview)
464 left_menu_selected.append(mnu_simpleview)
465 right_menu_selected.append(mnu_upcomingview)
466 right_menu_selected.append(mnu_curmonthcal)
467
468 left_menu_selected = u'\r\n'.join(left_menu_selected)
469 right_menu_selected = u'\r\n'.join(right_menu_selected)
470
471 html = u'\r\n'.join(html)
472 html = html % (menuwidth, left_menu_selected, right_menu_selected)
473
474 return html
475
476
477 def getdatefield(str_date):
478 str_year = ''
479 str_month = ''
480 str_day = ''
481
482 if len(str_date) == 6:
483 # year+month
484 str_year = str_date[:4]
485 str_month = str_date[4:]
486 str_day = '1'
487
488 elif len(str_date) == 8:
489 # year+month+day
490 str_year = str_date[:4]
491 str_month = str_date[4:6]
492 str_day = str_date[6:]
493
494 elif len(str_date) == 10:
495 # year+?+month+?+day
496 str_year = str_date[:4]
497 str_month = str_date[5:7]
498 str_day = str_date[8:]
499
500 else:
501 raise ValueError
502
503 # It raises exception if the input date is incorrect
504 temp = datetime.date(int(str_year), int(str_month), int(str_day))
505
506 return temp.year, temp.month, temp.day
507
508
509 def gettimefield(str_time):
510 str_hour = ''
511 str_min = ''
512
513 regex_time = '(?P<hour>\\d{1,2})[:]?(?P<minute>\\d{0,2})(?P<pm>[pP][mM]?)?(?P<am>[aA][mM]?)?'
514 pattern = re.compile(regex_time)
515 match = pattern.search(str_time)
516
517 if match:
518 str_hour = match.group('hour')
519 str_min = match.group('minute')
520 str_pm = match.group('pm')
521 str_am = match.group('am')
522
523 else:
524 raise ValueError
525
526 int_hour = int(str_hour)
527
528 # convert to 24 hour format
529 if int_hour == 12 and (str_am or str_pm):
530 int_hour = 0
531
532 # check for pm
533 if str_pm:
534 int_hour = int_hour + 12
535
536 #handle null minutes
537 try:
538 int_min = int(str_min)
539 except (TypeError, ValueError):
540 int_min = 0
541
542 # It raises exception if the input date is incorrect
543 temp = datetime.time(int_hour, int_min)
544
545 return temp.hour, temp.minute
546
547
548 def gettodaydate():
549 today = Globs.today
550 return today.year, today.month, today.day
551
552
553 def cal_listhead():
554
555 html = [
556 u' <tr>',
557 u' <td class="list_head">Title</td>',
558 u' <td class="list_head">Start Date</td>',
559 u' <td class="list_head">End Date</td>',
560 u' <td class="list_head">Recurrence</td>',
561 u' <td class="list_head">Description</td>',
562 u' <td class="list_head">Reference</td>',
563 u' </tr>',
564 ]
565
566 return u'\r\n'.join(html)
567
568
569 def showeventlist(form_vals):
570
571 debug('Show Calendar: List view')
572
573 request = Globs.request
574 formatter = Globs.formatter
575
576 html_event_rows = []
577 html_list_header = cal_listhead()
578
579 # read all the events
580 events, cal_events = loadEvents()
581
582 # sort events
583 sorted_eventids = events.keys()
584 sorted_eventids.sort(cmp=lambda x,y: cmp(events[x]['startdate'], events[y]['startdate']))
585
586 for eid in sorted_eventids:
587 if not events[eid]['clone']:
588 html_event_rows.append( listshow_event(events[eid], form_vals) )
589
590 html_event_rows = u'\r\n'.join(html_event_rows)
591
592 html_list_table = [
593 u'\r\n<div id="eventlist">',
594 u'<table class="eventlist">',
595 u'%s' % html_list_header,
596 u'%s' % html_event_rows,
597 u'</table>',
598 u'</div>',
599 ]
600 html_list_table = u'\r\n'.join(html_list_table)
601
602 return html_list_table
603
604
605 def listshow_event(event, form_vals):
606
607 if event['recur_freq']:
608 recur_desc = 'every %d %s' % (event['recur_freq'], event['recur_type'])
609 if event['recur_until']:
610 recur_desc = '%s until %s' % (recur_desc, formatcfgdatetime(event['recur_until']))
611 else:
612 recur_desc = ''
613
614 html = [
615 u' <tr>',
616 u' <td class="list_entry">%s</td>' % converttext(event['title']),
617 u' <td class="list_entry">%s</td>' % formatcfgdatetime(event['startdate'], event['starttime']),
618 u' <td class="list_entry">%s</td>' % formatcfgdatetime(event['enddate'], event['endtime']),
619 u' <td class="list_entry">%s</td>' % recur_desc,
620 u' <td class="list_entry">%s</td>' % converttext(event['description']),
621 u' <td class="list_entry">%s</td>' % showReferPageParsed(event, 'refer'),
622 u' </tr>',
623 ]
624
625 return u'\r\n'.join(html)
626
627
628 def showupcomingeventlist(form_vals):
629
630 debug('Show Calendar: Upcoming Event View')
631
632 request = Globs.request
633 formatter = Globs.formatter
634
635 html_event_rows = []
636 html_list_header = cal_listhead()
637
638 year, month, day = gettodaydate()
639 day_delta = datetime.timedelta(days=Params.upcomingrange)
640 cur_date = datetime.date(year, month, day)
641 next_range = cur_date + day_delta
642
643 # set ranges of events
644 datefrom = u'%04d%02d%02d' % (year, month, day)
645 dateto = u'%04d%02d%02d' % (next_range.year, next_range.month, next_range.day)
646
647 # read all the events (no cache)
648 events, cal_events = loadEvents(datefrom, dateto, 1)
649
650 datefrom = u'%04d-%02d-%02d' % (year, month, day)
651 dateto = u'%04d-%02d-%02d' % (next_range.year, next_range.month, next_range.day)
652
653 # sort events
654 sorted_eventids = events.keys()
655 sorted_eventids.sort(cmp=lambda x,y: cmp(events[x]['startdate'], events[y]['startdate']))
656
657 for eid in sorted_eventids:
658 html_event_rows.append( listshow_event(events[eid], form_vals) )
659
660 html_event_rows = u'\r\n'.join(html_event_rows)
661
662 html_list_table = [
663 u'\r\n<div id="eventlist">',
664 u'<table class="eventlist">',
665 u'<tr><td colspan="7" class="list_entry" style="border-width: 0px;"><b>Upcoming Event List: %s ~ %s</b><p><br><p></td></tr>' % (datefrom, dateto),
666 u'%s' % html_list_header,
667 u'%s' % html_event_rows,
668 u'</table>',
669 u'</div>',
670 ]
671 html_list_table = u'\r\n'.join(html_list_table)
672
673 return html_list_table
674
675
676
677
678 def showcalendar(form_vals):
679
680 request = Globs.request
681 formatter = Globs.formatter
682
683 if form_vals.has_key('caldate'):
684 try:
685 year, month, str_temp = getdatefield(form_vals['caldate'])
686 except (TypeError, ValueError):
687 debug('Invalid target date: e.g., "200510"')
688 year, month, dy = gettodaydate()
689 elif Params.curdate:
690 try:
691 year, month, str_temp = getdatefield(Params.curdate)
692 except (TypeError, ValueError):
693 debug('Invalid target date: e.g., "200510"')
694 year, month, dy = gettodaydate()
695
696 else:
697 year, month, dy = gettodaydate()
698
699 html = showeventcalendar(year, month)
700
701 return u''.join(html)
702
703
704
705
706 def showsimplecalendar(form_vals):
707
708 request = Globs.request
709 formatter = Globs.formatter
710
711 if form_vals.has_key('caldate'):
712 try:
713 year, month, str_temp = getdatefield(form_vals['caldate'])
714 except (TypeError, ValueError):
715 debug('Invalid target date: e.g., "200510"')
716 year, month, dy = gettodaydate()
717 elif Params.curdate:
718 try:
719 year, month, str_temp = getdatefield(Params.curdate)
720 except (TypeError, ValueError):
721 debug('Invalid target date: e.g., "200510"')
722 year, month, dy = gettodaydate()
723 else:
724 year, month, dy = gettodaydate()
725
726 html = showsimpleeventcalendar(year, month)
727
728 return u''.join(html)
729
730
731
732 # sort events in cal_events by length of days of the event
733 def comp_cal_events(xid, yid):
734 events = Globs.events
735
736 if events[xid]['date_len'] > events[yid]['date_len']:
737 return -1
738 elif events[xid]['date_len'] == events[yid]['date_len']:
739 if events[xid]['date_len'] == 1:
740 return cmp(events[xid]['starttime'], events[yid]['starttime'])
741 else:
742 return 0
743 else:
744 return 1
745
746
747 # load events from wiki pages
748 def loadEvents(datefrom='', dateto='', nocache=0):
749
750 request = Globs.request
751
752 debug('Loading event information.')
753
754 events = {}
755 cal_events = {}
756 raw_events = loadEventsFromWikiPages()
757
758 # handling cal_events
759 if datefrom or dateto:
760
761 # cache configurations
762 arena = Page(request, Globs.pagename)
763 eventkey = 'events'
764 filteredeventkey = 'events_%s-%s' % (datefrom, dateto)
765 caleventkey = 'calevents_%s-%s' % (datefrom, dateto)
766
767 cache_events = caching.CacheEntry(request, arena, eventkey)
768 cache_filteredevents = caching.CacheEntry(request, arena, filteredeventkey)
769 cache_calevents = caching.CacheEntry(request, arena, caleventkey)
770
771 dirty = 1
772
773 debug('Checking cal_events cache')
774
775 if not (cache_calevents.needsUpdate(cache_events._filename()) or cache_filteredevents.needsUpdate(cache_events._filename())):
776
777 try:
778 events = pickle.loads(cache_filteredevents.content())
779 cal_events = pickle.loads(cache_calevents.content())
780 debug('Cached event (filtered) information is used: total %d events' % len(events))
781 dirty = 0
782 except (pickle.UnpicklingError, IOError, EOFError, ValueError):
783 debug('Picke error at fetching cached events (filtered)')
784 events = {}
785 cal_events = {}
786
787
788 # if cache is dirty, update the cache
789 if dirty:
790
791 debug('Checking event cache: it\'s dirty or requested to refresh')
792 debug('Building new cal_event information')
793
794 try:
795 datefrom, dateto = int(datefrom), int(dateto)
796 except (TypeError, ValueError):
797 datefrom, dateto = 0, 0
798
799 clone_num = 0
800
801 for e_id in raw_events.keys():
802
803 cur_event = raw_events[e_id]
804
805 # handling event recurrence
806 recur_freq = cur_event['recur_freq']
807
808 if recur_freq:
809
810 if not (cur_event['recur_until'] and int(cur_event['recur_until']) < datefrom) or int(cur_event['startdate']) > dateto:
811
812 if not (int(cur_event['enddate']) < datefrom or int(cur_event['startdate']) > dateto):
813 # generating cal_events for iteself
814 events[e_id] = cur_event.copy()
815 insertcalevents(cal_events, datefrom, dateto, e_id, cur_event['startdate'], cur_event['enddate'])
816
817 delta_date_len = datetime.timedelta(days = int(cur_event['date_len']) - 1 )
818
819 if cur_event['recur_type'] == 'day':
820
821 day_delta = int(recur_freq)
822 startdate = getdatetimefromstring(cur_event['startdate'])
823 datefrom_date = getdatetimefromstring(datefrom)
824
825 if int(datefrom) > int(cur_event['startdate']):
826 diffs = datefrom_date - startdate
827 q_delta = diffs.days / day_delta
828 if diffs.days % day_delta > 0:
829 q_delta += 1
830 else:
831 q_delta = 1
832
833 while 1:
834
835 if q_delta == 0:
836 q_delta += 1
837 continue
838
839 recurred_startdate = startdate + datetime.timedelta(days = q_delta * day_delta )
840 recurred_enddate = recurred_startdate + delta_date_len
841
842 new_startdate = formatdateobject(recurred_startdate)
843 new_enddate = formatdateobject(recurred_enddate)
844
845 if int(new_startdate) > dateto or (cur_event['recur_until'] and int(cur_event['recur_until']) < int(new_startdate)):
846 break
847
848 clone_num += 1
849 clone_id = 'c%d' % clone_num
850
851 events[clone_id] = cur_event.copy()
852 events[clone_id]['id'] = clone_id
853 events[clone_id]['startdate'] = new_startdate
854 events[clone_id]['enddate'] = new_enddate
855 events[clone_id]['clone'] = 1
856
857 insertcalevents(cal_events, datefrom, dateto, clone_id, new_startdate, new_enddate)
858
859 q_delta += 1
860
861 elif cur_event['recur_type'] == 'week':
862
863 day_delta = int(recur_freq) * 7
864
865 startdate = getdatetimefromstring(cur_event['startdate'])
866 datefrom_date = getdatetimefromstring(datefrom)
867
868 if int(datefrom) > int(cur_event['startdate']):
869 diffs = datefrom_date - startdate
870 q_delta = diffs.days / day_delta
871 if diffs.days % day_delta > 0:
872 q_delta += 1
873 else:
874 q_delta = 1
875
876 while 1:
877
878 if q_delta == 0:
879 q_delta += 1
880 continue
881
882 recurred_startdate = startdate + datetime.timedelta(days = q_delta * day_delta )
883 recurred_enddate = recurred_startdate + delta_date_len
884
885 new_startdate = formatdateobject(recurred_startdate)
886 new_enddate = formatdateobject(recurred_enddate)
887
888 if int(new_startdate) > dateto or (cur_event['recur_until'] and int(cur_event['recur_until']) < int(new_startdate)):
889 break
890
891 clone_num += 1
892 clone_id = 'c%d' % clone_num
893
894 events[clone_id] = cur_event.copy()
895 events[clone_id]['id'] = clone_id
896 events[clone_id]['startdate'] = new_startdate
897 events[clone_id]['enddate'] = new_enddate
898 events[clone_id]['clone'] = 1
899
900 insertcalevents(cal_events, datefrom, dateto, clone_id, new_startdate, new_enddate)
901
902 q_delta += 1
903
904
905 elif cur_event['recur_type'] == 'weekday':
906
907 syear, smonth, sday = getdatefield(cur_event['startdate'])
908 cyear, cmonth, cday = getdatefield(str(datefrom))
909
910 recur_weekday = calendar.weekday(syear, smonth, sday)
911
912
913 while 1:
914
915 firstweekday, daysinmonth = calendar.monthrange(cyear, cmonth)
916 firstmatch = (recur_weekday - firstweekday) % 7 + 1
917
918 #XXX should handle error
919 try:
920 therecur_day = xrange(firstmatch, daysinmonth + 1, 7)[recur_freq-1]
921 except IndexError:
922 therecur_day = xrange(firstmatch, daysinmonth + 1, 7)[-1]
923
924 recurred_startdate = datetime.date(cyear, cmonth, therecur_day)
925 recurred_enddate = recurred_startdate + delta_date_len
926
927 new_startdate = formatdateobject(recurred_startdate)
928 new_enddate = formatdateobject(recurred_enddate)
929
930 if int(new_startdate) < int(datefrom):
931 cyear, cmonth = yearmonthplusoffset(cyear, cmonth, 1)
932 continue
933
934 if int(new_startdate) > dateto or (cur_event['recur_until'] and int(cur_event['recur_until']) < int(new_startdate)):
935 break
936
937 clone_num += 1
938 clone_id = 'c%d' % clone_num
939
940 events[clone_id] = cur_event.copy()
941 events[clone_id]['id'] = clone_id
942 events[clone_id]['startdate'] = new_startdate
943 events[clone_id]['enddate'] = new_enddate
944 events[clone_id]['clone'] = 1
945
946 insertcalevents(cal_events, datefrom, dateto, clone_id, new_startdate, new_enddate)
947
948 cyear, cmonth = yearmonthplusoffset(cyear, cmonth, 1)
949
950
951 elif cur_event['recur_type'] == 'month':
952
953 cyear, cmonth, therecurday = getdatefield(cur_event['startdate'])
954
955 while 1:
956
957 cyear, cmonth = yearmonthplusoffset(cyear, cmonth, recur_freq)
958 firstweekday, daysinmonth = calendar.monthrange(cyear, cmonth)
959 recur_day = therecurday
960 if daysinmonth < recur_day:
961 recur_day = daysinmonth
962 new_startdate = formatDate(cyear, cmonth, recur_day)
963
964 if int(new_startdate) < int(datefrom):
965 continue
966
967 recurred_startdate = datetime.date(cyear, cmonth, recur_day)
968 recurred_enddate = recurred_startdate + delta_date_len
969
970 new_startdate = formatdateobject(recurred_startdate)
971 new_enddate = formatdateobject(recurred_enddate)
972
973 if int(new_startdate) > dateto or (cur_event['recur_until'] and int(cur_event['recur_until']) < int(new_startdate)):
974 break
975
976 clone_num += 1
977 clone_id = 'c%d' % clone_num
978
979 events[clone_id] = cur_event.copy()
980 events[clone_id]['id'] = clone_id
981 events[clone_id]['startdate'] = new_startdate
982 events[clone_id]['enddate'] = new_enddate
983 events[clone_id]['clone'] = 1
984
985 insertcalevents(cal_events, datefrom, dateto, clone_id, new_startdate, new_enddate)
986
987 elif cur_event['recur_type'] == 'year':
988
989 ryear, rmonth, rday = getdatefield(cur_event['startdate'])
990 cyear, cmonth, cday = getdatefield(str(datefrom))
991
992 while 1:
993
994 ryear += recur_freq
995 new_startdate = formatDate(ryear, rmonth, rday)
996
997 if int(new_startdate) < int(datefrom):
998 continue
999
1000 if int(new_startdate) > dateto or (cur_event['recur_until'] and int(cur_event['recur_until']) < int(new_startdate)):
1001 break
1002
1003 recurred_startdate = datetime.date(ryear, rmonth, rday)
1004 recurred_enddate = recurred_startdate + delta_date_len
1005
1006 new_startdate = formatdateobject(recurred_startdate)
1007 new_enddate = formatdateobject(recurred_enddate)
1008
1009 clone_num += 1
1010 clone_id = 'c%d' % clone_num
1011
1012 events[clone_id] = cur_event.copy()
1013 events[clone_id]['id'] = clone_id
1014 events[clone_id]['startdate'] = new_startdate
1015 events[clone_id]['enddate'] = new_enddate
1016 events[clone_id]['clone'] = 1
1017
1018 insertcalevents(cal_events, datefrom, dateto, clone_id, new_startdate, new_enddate)
1019
1020 else:
1021
1022 if not (int(cur_event['enddate']) < datefrom or int(cur_event['startdate']) > dateto):
1023 events[e_id] = cur_event.copy()
1024 insertcalevents(cal_events, datefrom, dateto, e_id, cur_event['startdate'], cur_event['enddate'])
1025
1026 # sort cal_events
1027 # store event list into global variables in order to sort them
1028 Globs.events = events
1029
1030 for eachdate in cal_events.keys():
1031 cal_events[eachdate].sort(comp_cal_events)
1032
1033 # cache update
1034 if not nocache:
1035 cache_filteredevents.update(pickle.dumps(events, PICKLE_PROTOCOL))
1036 cache_calevents.update(pickle.dumps(cal_events, PICKLE_PROTOCOL))
1037
1038 else:
1039 events = raw_events
1040
1041 debug(u'Total %d of events are loaded finally.' % len(events))
1042
1043 #debug('Events:')
1044 #for key in events.keys():
1045 # debug('__ %s' % events[key])
1046
1047 return events, cal_events
1048
1049
1050
1051 def loadEventsFromWikiPages():
1052
1053 events = {}
1054
1055 eventrecord_list = []
1056 eventpages = []
1057
1058 request = Globs.request
1059 category = Params.category
1060
1061 # cache configurations
1062 arena = Page(request, Globs.pagename)
1063 eventkey = 'events'
1064 pagelistkey = 'eventpages'
1065
1066 cache_events = caching.CacheEntry(request, arena, eventkey)
1067 cache_pages = caching.CacheEntry(request, arena, pagelistkey)
1068
1069
1070 # page list cache
1071
1072 debug('Checking page list cache')
1073
1074 # check the time at which page list cache has been created
1075
1076 cp_mtime = cache_pages.mtime()
1077 timedelta_days = 9999
1078
1079 if cp_mtime:
1080 cp_date = datetime.datetime.fromtimestamp(cp_mtime)
1081 today = datetime.datetime.fromtimestamp(time.time())
1082 datediff = today - cp_date
1083 timedelta_days = datediff.days
1084 debug('Time from page list cache built = %s' % datediff)
1085
1086
1087 if Globs.page_action == 'refresh' or cache_pages.needsUpdate(arena._text_filename()) or timedelta_days >= 1:
1088 categorypages = searchPages(request, category)
1089 for page in categorypages:
1090 eventpages.append(page.page_name)
1091 cache_pages.update('\n'.join(eventpages), True)
1092 debug('New page list is built: %d pages' % len(eventpages))
1093 else:
1094 eventpages = cache_pages.content(True).split('\n')
1095 debug('Cached page list is used: %d pages' % len(eventpages))
1096
1097
1098 # generating events
1099
1100 e_num = 0
1101 dirty = 0
1102 debug_records = {}
1103
1104 # fetch event records from each page in the category
1105 for page_name in eventpages:
1106
1107 p = Page(request, page_name)
1108 e_ref = page_name
1109
1110 eventrecordkey = 'eventrecords'
1111 cache_eventrecords = caching.CacheEntry(request, p, eventrecordkey)
1112
1113 if cache_eventrecords.needsUpdate(p._text_filename()) or Globs.page_action == 'refresh':
1114 dirty = 1
1115 page_content = p.get_raw_body()
1116 eventrecords, e_num = getEventRecordFromPage(page_content, e_ref, e_num)
1117 debug_records[e_ref] = '%d eventrecords are fetched from %s' % (len(eventrecords), e_ref)
1118 cache_eventrecords.update(pickle.dumps(eventrecords, PICKLE_PROTOCOL))
1119 else:
1120 try:
1121 eventrecords = pickle.loads(cache_eventrecords.content())
1122 debug_records[e_ref] = '%d cached eventrecords are used from %s' % (len(eventrecords), e_ref)
1123 except (pickle.UnpicklingError, IOError, EOFError, ValueError):
1124 dirty = 1
1125 page_content = p.get_raw_body()
1126 eventrecords, e_num = getEventRecordFromPage(page_content, e_ref, e_num)
1127 debug_records[e_ref] = '%d eventrecords are fetched from %s due to pickle error' % (len(eventrecords), e_ref)
1128 cache_eventrecords.update(pickle.dumps(eventrecords, PICKLE_PROTOCOL))
1129
1130 eventrecord_list.append(eventrecords)
1131
1132 # if no dirty, just fetch the cache
1133 if not (dirty or Globs.page_action == 'refresh'):
1134
1135 debug('Checking event cache: still valid')
1136
1137 try:
1138 events = pickle.loads(cache_events.content())
1139 debug('Cached event information is used: total %d events' % len(events))
1140 except (pickle.UnpicklingError, IOError, EOFError, ValueError):
1141 debug('Picke error at fetching cached events')
1142 events = {}
1143
1144 else:
1145
1146 debug('Checking event cache: it\'s dirty or requested to refresh')
1147
1148 # if there is no events (if it needs refreshed), generate events dictionary
1149 if not len(events.keys()):
1150
1151 # XXX: just debugging
1152 debug('Bulding new event information')
1153 for page_name in eventpages:
1154 debug(debug_records[page_name])
1155
1156
1157 day_delta = datetime.timedelta(days=1)
1158
1159 for eventrecords in eventrecord_list:
1160
1161 for evtrecord in eventrecords:
1162
1163 e_id = evtrecord['id']
1164
1165 # generating events
1166 events[e_id] = evtrecord
1167 #debug('event %s: %s' % (evtrecord['id'], evtrecord))
1168
1169 # after generating updated events, update the cache
1170 cache_events.update(pickle.dumps(events, PICKLE_PROTOCOL))
1171
1172 debug('Event information is newly built: total %d events' % len(events))
1173
1174 # end of updating events block
1175
1176
1177 return events
1178
1179
1180
1181 def getEventRecordFromPage(pagecontent, referpage, e_num):
1182
1183 request = Globs.request
1184
1185 eventrecords = []
1186 page_bgcolor = ''
1187 page_description = ''
1188
1189 # fetch the page default bgcolor
1190 regex_page_bgcolor = r"""
1191 ^\s+default_bgcolor::\s*
1192 (?P<pagebgcolor>\#[0-9a-fA-F]{6})
1193 \s*
1194 $
1195 """
1196
1197 pattern = re.compile(regex_page_bgcolor, re.UNICODE + re.MULTILINE + re.IGNORECASE + re.DOTALL + re.VERBOSE)
1198 match = pattern.search(pagecontent)
1199
1200 if match:
1201 page_bgcolor = match.group('pagebgcolor')
1202
1203 # fetch the page default description
1204 regex_page_description = r"""
1205 ^\s+default_description::\s*
1206 (?P<pagedescription>.*?)
1207 $
1208 """
1209
1210 pattern = re.compile(regex_page_description, re.UNICODE + re.MULTILINE + re.IGNORECASE + re.DOTALL + re.VERBOSE)
1211 match = pattern.search(pagecontent)
1212
1213 if match:
1214 page_description = match.group('pagedescription')
1215
1216
1217 # fetch event item
1218 regex_eventitem = r"""
1219 (?P<eventitem>
1220 (?P<heading>^\s*(?P<hmarker>=+)\s(?P<eventtitle>.*?)\s(?P=hmarker) $)
1221 (?P<eventdetail>.*?
1222 (?=
1223 ^\s*(?P<nexthmarker>=+)\s(?P<nexteventtitle>.*?)\s(?P=nexthmarker) $
1224 | \Z
1225 )
1226 )
1227 )
1228 """
1229
1230 pattern = re.compile(regex_eventitem, re.UNICODE + re.MULTILINE + re.IGNORECASE + re.DOTALL + re.VERBOSE)
1231 match = pattern.findall(pagecontent)
1232
1233 if match:
1234
1235 for matchitem in match:
1236
1237 eventitem = {}
1238
1239 eventtitle = matchitem[3]
1240 eventdetail = matchitem[4]
1241
1242 try:
1243 e_start_date, e_start_time, e_end_date, e_end_time, e_bgcolor, e_description, e_recur_freq, e_recur_type, e_recur_until = geteventfield(eventdetail)
1244 except (TypeError, ValueError):
1245 #debug('An event data is corrupted: invalid event format')
1246 continue
1247
1248 # set default values
1249 if not e_bgcolor:
1250 e_bgcolor = page_bgcolor
1251
1252 if not e_description:
1253 e_description = page_description
1254
1255 e_num += 1
1256 e_id = 'e%d' % e_num
1257
1258 eventitem['id'] = e_id
1259 eventitem['title'] = eventtitle
1260 eventitem['startdate'] = e_start_date
1261 eventitem['starttime'] = e_start_time
1262 eventitem['enddate'] = e_end_date
1263 eventitem['endtime'] = e_end_time
1264 eventitem['title'] = eventtitle
1265 eventitem['refer'] = referpage
1266 eventitem['bgcolor'] = e_bgcolor
1267 eventitem['description'] = e_description
1268 eventitem['recur_freq'] = e_recur_freq
1269 eventitem['recur_type'] = e_recur_type
1270 eventitem['recur_until'] = e_recur_until
1271
1272 eventitem['date_len'] = diffday(e_start_date, e_end_date) + 1
1273 eventitem['clone'] = 0
1274 eventitem['hid'] = getheadingid(request, referpage, eventtitle)
1275
1276 eventrecords.append(eventitem)
1277
1278 #debug('matched records: %d' % len(match))
1279
1280 return eventrecords, e_num
1281
1282
1283
1284 def geteventfield(detail):
1285
1286 regex_startdate = r"""
1287 ^\s+start::\s*
1288 (?P<startdate>\d{4}[/-]\d{2}[/-]\d{2})
1289 \s*
1290 (?P<starttime>\d{1,2}[:]?\d{0,2}([AP][M]?)?)*
1291 \s*
1292 $
1293 """
1294
1295 regex_enddate = r"""
1296 ^\s+end::\s*
1297 (?P<enddate>\d{4}[/-]\d{2}[/-]\d{2})*
1298 \s*
1299 (?P<endtime>\d{1,2}[:]?\d{0,2}([AP][M]?)?)*
1300 \s*
1301 $
1302 """
1303
1304 regex_bgcolor = r"""
1305 ^\s+bgcolor::\s*
1306 (?P<bgcolor>\#[0-9a-fA-F]{6})
1307 \s*
1308 $
1309 """
1310
1311 regex_description = r"""
1312 ^\s+description::\s*
1313 (?P<description>.*?)
1314 $
1315 """
1316
1317 regex_recur = r"""
1318 ^\s+recur::\s*
1319 (?P<recur_freq>\d+)
1320 \s*
1321 (?P<recur_type>day|week|weekday|month|year)
1322 \s*
1323 (
1324 until
1325 \s*
1326 (?P<recur_until>\d{4}[/-]\d{2}[/-]\d{2})
1327 )*
1328 $
1329 """
1330
1331 # need help on regular expressions for more efficient/flexible form
1332
1333 # compile regular expression objects
1334
1335 pattern_startdate = re.compile(regex_startdate, re.UNICODE + re.MULTILINE + re.IGNORECASE + re.DOTALL + re.VERBOSE)
1336 pattern_enddate = re.compile(regex_enddate, re.UNICODE + re.MULTILINE + re.IGNORECASE + re.DOTALL + re.VERBOSE)
1337 pattern_bgcolor = re.compile(regex_bgcolor, re.UNICODE + re.MULTILINE + re.IGNORECASE + re.DOTALL + re.VERBOSE)
1338 pattern_description = re.compile(regex_description, re.UNICODE + re.MULTILINE + re.IGNORECASE + re.DOTALL + re.VERBOSE)
1339 pattern_recur = re.compile(regex_recur, re.UNICODE + re.MULTILINE + re.IGNORECASE + re.DOTALL + re.VERBOSE)
1340
1341 ##################### retrieve startdate
1342 match = pattern_startdate.search(detail)
1343
1344 if match:
1345 startdate = match.group('startdate')
1346 starttime = match.group('starttime')
1347 else:
1348 startdate = ''
1349 starttime = ''
1350
1351 ##################### retrieve enddate
1352 match = pattern_enddate.search(detail)
1353
1354 if match:
1355 enddate = match.group('enddate')
1356 endtime = match.group('endtime')
1357 else:
1358 enddate = ''
1359 endtime = ''
1360
1361 ##################### retrieve bgcolor
1362 match = pattern_bgcolor.search(detail)
1363
1364 if match:
1365 bgcolor = match.group('bgcolor')
1366 else:
1367 bgcolor = ''
1368
1369 ##################### retrieve description
1370 match = pattern_description.search(detail)
1371
1372 if match:
1373 description = match.group('description')
1374 else:
1375 description = ''
1376
1377 ##################### retrieve recurrence
1378 match = pattern_recur.search(detail)
1379
1380 if match:
1381 recur_freq = int(match.group('recur_freq'))
1382 recur_type = match.group('recur_type')
1383 recur_until = match.group('recur_until')
1384
1385 else:
1386 recur_freq = 0
1387 recur_type = ''
1388 recur_until = ''
1389
1390
1391 # check validity of each fields
1392
1393 if not startdate:
1394 #debug('start date is not specified')
1395 # self.printoutput('Parse Error', msg, '')
1396 # ERROR
1397 return '','','','','','','','','',''
1398
1399 if (endtime) and not (starttime):
1400 #debug('endtime without starttime')
1401 # ERROR
1402 return '','','','','','','','','',''
1403
1404 # if no time, it's 1-day event
1405 if not enddate:
1406 enddate = startdate
1407
1408 try:
1409 syear, smonth, sday = getdatefield(startdate)
1410 eyear, emonth, eday = getdatefield(enddate)
1411 except (TypeError, ValueError):
1412 #debug('invalid date format: %s, %s' % (startdate, enddate))
1413 return '','','','','','','','','',''
1414
1415 if datetime.date(syear, smonth, sday) > datetime.date(eyear, emonth, eday):
1416 #debug('startdate should precede enddate')
1417 return '','','','','','','','','',''
1418
1419 # format date
1420 startdate = formatDate(syear, smonth, sday)
1421 enddate = formatDate(eyear, emonth, eday)
1422
1423 if (starttime and endtime):
1424 try:
1425 shour, smin = gettimefield(starttime)
1426 ehour, emin = gettimefield(endtime)
1427 except (TypeError, ValueError):
1428 #debug('invalid time format: %s, %s' % (startdate, enddate))
1429 return '','','','','','','','','',''
1430
1431 if startdate == enddate:
1432 if datetime.time(shour, smin) > datetime.time(ehour, emin):
1433 #debug('starttime should precede endtime')
1434 return '','','','','','','','','',''
1435
1436 # format time
1437 starttime = u'%02d%02d' %(shour, smin)
1438 endtime = u'%02d%02d' %(ehour, emin)
1439 elif (starttime):
1440 try:
1441 shour, smin = gettimefield(starttime)
1442 except (TypeError, ValueError):
1443 #debug('invalid time format: %s, %s' % (startdate, enddate))
1444 return '','','','','','','','','',''
1445
1446 starttime = u'%02d%02d' %(shour, smin)
1447 endtime = u''
1448
1449 # check recurrent data
1450 event_len = diffday(startdate, enddate)
1451 if recur_freq:
1452
1453 if recur_type == 'day':
1454 if event_len > int(recur_freq):
1455 debug('event length should be smaller than recurrence interval')
1456 return '','','','','','','','','',''
1457
1458 elif recur_type == 'week':
1459 if event_len > int(recur_freq) * 7:
1460 debug('event length should be smaller than recurrence interval')
1461 return '','','','','','','','','',''
1462
1463 elif recur_type == 'weekday':
1464 if event_len > 25:
1465 debug('event length should be smaller than recurrence interval')
1466 return '','','','','','','','','',''
1467
1468 elif recur_type == 'month':
1469 if event_len > int(recur_freq) * 25:
1470 debug('event length should be smaller than recurrence interval')
1471 return '','','','','','','','','',''
1472
1473 elif recur_type == 'year':
1474 if event_len > int(recur_freq) * 365:
1475 debug('event length should be smaller than recurrence interval')
1476 return '','','','','','','','','',''
1477
1478 if recur_until:
1479 try:
1480 ryear, rmonth, rday = getdatefield(recur_until)
1481 except (TypeError, ValueError):
1482 debug('invalid date format: %s' % recur_until)
1483 return '','','','','','','','','',''
1484
1485 recur_until = formatDate(ryear, rmonth, rday)
1486
1487 if int(recur_until) < int(enddate):
1488 debug('recur_until should precede enddate')
1489 return '','','','','','','','','',''
1490
1491
1492 return startdate, starttime, enddate, endtime, bgcolor, description, recur_freq, recur_type, recur_until
1493
1494
1495
1496 def converttext(targettext):
1497 # Converts some special characters of html to plain-text style
1498 # What else to handle?
1499
1500 targettext = targettext.replace(u'&', '&')
1501 targettext = targettext.replace(u'>', '>')
1502 targettext = targettext.replace(u'<', '<')
1503 targettext = targettext.replace(u'\n', '<br>')
1504 targettext = targettext.replace(u'"', '"')
1505 targettext = targettext.replace(u'\t', '  ')
1506 targettext = targettext.replace(u' ', ' ')
1507
1508 return targettext
1509
1510
1511 # monthly view
1512 def showeventcalendar(year, month):
1513
1514 debug('Show Calendar: Monthly View')
1515
1516 request = Globs.request
1517 formatter = Globs.formatter
1518 _ = request.getText
1519
1520 wkend = Globs.wkend
1521 months= Globs.months
1522 wkdays = Globs.wkdays
1523
1524 # get the calendar
1525 monthcal = calendar.monthcalendar(year, month)
1526
1527 # shows current year & month
1528 html_header_curyearmonth = calhead_yearmonth(year, month, 'head_yearmonth')
1529
1530 r7 = range(7)
1531
1532 # shows header of week days
1533 html_header_weekdays = []
1534
1535 for wkday in r7:
1536 wday = _(wkdays[wkday])
1537 html_header_weekdays.append( calhead_weekday(wday, 'head_weekday') )
1538 html_header_weekdays = ' <tr>\r\n%s\r\n</tr>\r\n' % u'\r\n'.join(html_header_weekdays)
1539
1540 # pending events for next row
1541 next_pending = []
1542
1543 # gets previous, next month
1544 day_delta = datetime.timedelta(days=-1)
1545 cur_month = datetime.date(year, month, 1)
1546 prev_month = cur_month + day_delta
1547
1548 day_delta = datetime.timedelta(days=15)
1549 cur_month_end = datetime.date(year, month, 25)
1550 next_month = cur_month_end + day_delta
1551
1552 prev_monthcal = calendar.monthcalendar(prev_month.year, prev_month.month)
1553 next_monthcal = calendar.monthcalendar(next_month.year, next_month.month)
1554
1555 # shows days
1556 html_week_rows = []
1557
1558 # set ranges of events
1559 datefrom = u'%04d%02d21' % (prev_month.year, prev_month.month)
1560 dateto = u'%04d%02d06' % (next_month.year, next_month.month)
1561
1562 # read all the events
1563 events, cal_events = loadEvents(datefrom, dateto)
1564
1565 #debug(u' events: %s' % events)
1566 #debug(u' cal_events: %s' % cal_events)
1567
1568 for week in monthcal:
1569
1570 # day head rows
1571 html_headday_cols = []
1572 html_events_rows = []
1573
1574 for wkday in r7:
1575
1576 day = week[wkday]
1577
1578 if not day:
1579 if week == monthcal[0]:
1580 nb_day = prev_monthcal[-1][wkday]
1581 else:
1582 nb_day = next_monthcal[0][wkday]
1583
1584 html_headday_cols.append( calhead_day_nbmonth(nb_day) )
1585 else:
1586 html_headday_cols.append( calhead_day(year, month, day, wkday) )
1587
1588 html_headday_row = ' <tr>\r\n%s\r\n</tr>\r\n' % u'\r\n'.join(html_headday_cols)
1589 html_week_rows.append(html_headday_row)
1590
1591 # dummy rows
1592 html_headdummy_cols = []
1593
1594 for wkday in r7:
1595 day = week[wkday]
1596 if not day:
1597 html_headdummy_cols.append( calshow_blankbox('head_dummy_nbmonth') )
1598 else:
1599 html_headdummy_cols.append( calshow_blankbox('head_dummy') )
1600
1601 html_headdummy_cols = u'\r\n'.join(html_headdummy_cols)
1602 html_week_rows.append(' <tr>\r\n%s </tr>\r\n' % html_headdummy_cols)
1603
1604 # pending events for next row
1605 pending = next_pending
1606 next_pending = []
1607
1608 # show events
1609 while 1:
1610 event_left = 7
1611 colspan = -1
1612 html_events_cols = []
1613
1614 for wkday in r7:
1615
1616 day = week[wkday]
1617
1618 if not day:
1619 if week == monthcal[0]:
1620 cur_date = formatDate(prev_month.year, prev_month.month, prev_monthcal[-1][wkday])
1621 else:
1622 cur_date = formatDate(next_month.year, next_month.month, next_monthcal[0][wkday])
1623 else:
1624 cur_date = formatDate(year, month, day)
1625
1626 # if an event is already displayed with colspan
1627 if colspan > 0:
1628 colspan -= 1
1629 if cal_events.has_key(cur_date) and lastevent in cal_events[cur_date]:
1630 cal_events[cur_date].remove(lastevent)
1631
1632 continue
1633
1634 # if there is any event for this date
1635 if cal_events.has_key(cur_date):
1636 if len(cal_events[cur_date]) > 0:
1637
1638 # if there is any pending event in the previous week
1639 if wkday == 0 and len(pending) > 0:
1640 todo_event_id = pending.pop(0)
1641 if todo_event_id in cal_events[cur_date]:
1642 cur_event = events[todo_event_id]
1643 temp_len = diffday(cur_date, cur_event['enddate']) + 1
1644
1645 # calculate colspan value
1646 if (7-wkday) < temp_len:
1647 colspan = 7 - wkday
1648 next_pending.append(cur_event['id'])
1649 html_events_cols.append( calshow_eventbox(cur_event, colspan, 'append_pending', cur_date) )
1650
1651 else:
1652 colspan = temp_len
1653 html_events_cols.append( calshow_eventbox(cur_event, colspan, 'append', cur_date) )
1654
1655
1656 cal_events[cur_date].remove(todo_event_id)
1657
1658 colspan -= 1
1659 lastevent = todo_event_id
1660 else:
1661 debug('Warning: no such event in cal_events')
1662
1663 continue
1664
1665 # if there is no pending event in the previous week, start a new event
1666 event_found = 0
1667 for e_id in cal_events[cur_date]:
1668
1669 # if the start date of the event is current date
1670 if events[e_id]['startdate'] == cur_date:
1671
1672 cur_event = events[cal_events[cur_date].pop(cal_events[cur_date].index(e_id))]
1673
1674 # calculate colspan value
1675 if (7-wkday) < cur_event['date_len']:
1676 colspan = 7 - wkday
1677 next_pending.append(cur_event['id'])
1678 html_events_cols.append( calshow_eventbox(cur_event, colspan, 'pending', cur_date) )
1679
1680 else:
1681 colspan = cur_event['date_len']
1682 html_events_cols.append( calshow_eventbox(cur_event, colspan, '', cur_date) )
1683
1684 colspan -= 1
1685 lastevent = cur_event['id']
1686 event_found = 1
1687 break
1688
1689 # if the start date of the event is NOT current date
1690 else:
1691
1692 # pending event from previous month
1693 if wkday == 0 and week == monthcal[0]:
1694
1695 cur_event = events[cal_events[cur_date].pop(0)]
1696 temp_len = diffday(cur_date, cur_event['enddate']) + 1
1697
1698 # calculate colspan value
1699 if (7-wkday) < temp_len:
1700 colspan = 7 - wkday
1701 next_pending.append(cur_event['id'])
1702 html_events_cols.append( calshow_eventbox(cur_event, colspan, 'append_pending', cur_date) )
1703 else:
1704 colspan = temp_len
1705 html_events_cols.append( calshow_eventbox(cur_event, colspan, 'append', cur_date) )
1706
1707 colspan -= 1
1708 lastevent = cur_event['id']
1709 event_found = 1
1710 break
1711
1712 # if there is no event to start
1713 if not event_found:
1714 if not day:
1715 html_events_cols.append( calshow_blankbox('cal_nbmonth') )
1716 else:
1717 html_events_cols.append( calshow_blankbox('cal_noevent') )
1718 event_left -= 1
1719
1720 else:
1721 if not day:
1722 html_events_cols.append( calshow_blankbox('cal_nbmonth') )
1723 else:
1724 html_events_cols.append( calshow_blankbox('cal_noevent') )
1725
1726 event_left -= 1
1727
1728 # if there is NO event for this date
1729 else:
1730 if not day:
1731 html_events_cols.append( calshow_blankbox('cal_nbmonth') )
1732 else:
1733 html_events_cols.append( calshow_blankbox('cal_noevent') )
1734
1735 event_left -= 1
1736
1737 # if no event for this entry
1738 if not event_left:
1739 # ignore the previous entry
1740 break
1741 else:
1742 html_events_rows.append(' <tr>\r\n%s </tr>\r\n' % u'\r\n'.join(html_events_cols))
1743
1744 # show dummy blank slots for week height
1745 left_blank_rows = 2 - len(html_events_rows)
1746
1747 # remove the followings
1748 if left_blank_rows > 0 and 0:
1749 for i in range(left_blank_rows):
1750 html_events_cols = []
1751 for wkday in r7:
1752 day = week[wkday]
1753 if not day:
1754 html_events_cols.append( calshow_blankbox('cal_nbmonth') )
1755 else:
1756 html_events_cols.append( calshow_blankbox('cal_noevent') )
1757
1758 html_events_rows.append(' <tr>\r\n%s </tr>\r\n' % u'\r\n'.join(html_events_cols))
1759
1760
1761 # close the week slots
1762 html_events_cols = []
1763 for wkday in r7:
1764 day = week[wkday]
1765 if not day:
1766 html_events_cols.append( calshow_blankbox('cal_last_nbmonth') )
1767 else:
1768 html_events_cols.append( calshow_blankbox('cal_last_noevent') )
1769
1770 html_events_rows.append(' <tr>\r\n%s </tr>\r\n' % u'\r\n'.join(html_events_cols))
1771
1772 html_events_rows = u'\r\n'.join(html_events_rows)
1773 html_week_rows.append(html_events_rows)
1774
1775 html_calendar_rows = u'\r\n'.join(html_week_rows)
1776
1777 html_cal_table = [
1778 u'\r\n<div id="eventcalendar">',
1779 u'<table class="eventcalendar" %s>' % Params.monthlywidth,
1780 u'%s' % html_header_curyearmonth,
1781 u'%s' % html_header_weekdays,
1782 u'%s' % html_calendar_rows,
1783 u'</table>',
1784 u'</div>',
1785 ]
1786 html_cal_table = u'\r\n'.join(html_cal_table)
1787
1788 return html_cal_table
1789
1790 # simple view
1791 def showsimpleeventcalendar(year, month):
1792
1793 debug('Show Calendar: Simple View')
1794
1795 request = Globs.request
1796 formatter = Globs.formatter
1797 _ = request.getText
1798
1799 wkend = Globs.wkend
1800 months= Globs.months
1801 wkdays = Globs.wkdays
1802
1803 # get the calendar
1804 monthcal = calendar.monthcalendar(year, month)
1805
1806 # shows current year & month
1807 html_header_curyearmonth = calhead_yearmonth(year, month, 'simple_yearmonth')
1808
1809 r7 = range(7)
1810
1811 # shows header of week days
1812 html_header_weekdays = []
1813
1814 for wkday in r7:
1815 wday = wkdays[wkday]
1816 html_header_weekdays.append( calhead_weekday(wday, 'simple_weekday') )
1817 html_header_weekdays = ' <tr>\r\n%s\r\n</tr>\r\n' % u'\r\n'.join(html_header_weekdays)
1818
1819 # gets previous, next month
1820 day_delta = datetime.timedelta(days=-1)
1821 cur_month = datetime.date(year, month, 1)
1822 prev_month = cur_month + day_delta
1823
1824 day_delta = datetime.timedelta(days=15)
1825 cur_month_end = datetime.date(year, month, 25)
1826 next_month = cur_month_end + day_delta
1827
1828 prev_monthcal = calendar.monthcalendar(prev_month.year, prev_month.month)
1829 next_monthcal = calendar.monthcalendar(next_month.year, next_month.month)
1830
1831 # shows days
1832 html_week_rows = []
1833
1834 # set ranges of events
1835 datefrom = u'%04d%02d21' % (prev_month.year, prev_month.month)
1836 dateto = u'%04d%02d06' % (next_month.year, next_month.month)
1837
1838 # read all the events
1839 events, cal_events = loadEvents(datefrom, dateto)
1840
1841 for week in monthcal:
1842
1843 # day head rows
1844 html_headday_cols = []
1845 html_events_rows = []
1846
1847 for wkday in r7:
1848
1849 day = week[wkday]
1850
1851 if not day:
1852 if week == monthcal[0]:
1853 nb_day = prev_monthcal[-1][wkday]
1854 else:
1855 nb_day = next_monthcal[0][wkday]
1856
1857 html_headday_cols.append( simple_eventbox(year, month, day, nb_day, 'simple_nb') )
1858 else:
1859 cur_date = formatDate(year, month, day)
1860 if cal_events.has_key(cur_date):
1861 html_headday_cols.append( simple_eventbox(year, month, day, wkday, 'simple_event') )
1862 else:
1863 html_headday_cols.append( simple_eventbox(year, month, day, wkday, 'simple_noevent') )
1864
1865 html_headday_row = ' <tr>\r\n%s\r\n</tr>\r\n' % u'\r\n'.join(html_headday_cols)
1866 html_week_rows.append(html_headday_row)
1867
1868 html_calendar_rows = u'\r\n'.join(html_week_rows)
1869
1870 html_cal_table = [
1871 u'\r\n<div id="eventcalendar">',
1872 u'<table class="simplecalendar" %s>' % Params.simplewidth,
1873 u'%s' % html_header_curyearmonth,
1874 u'%s' % html_header_weekdays,
1875 u'%s' % html_calendar_rows,
1876 u'</table>',
1877 u'</div>',
1878 ]
1879 html_cal_table = u'\r\n'.join(html_cal_table)
1880
1881 return html_cal_table
1882
1883
1884 # show weekday
1885 def calhead_yearmonth(year, month, headclass):
1886
1887 months = Globs.months
1888 monthstyle_us = Globs.month_style_us
1889 cal_action = Globs.cal_action
1890 page_name = Globs.pagename
1891
1892 nextyear, nextmonth = yearmonthplusoffset(year, month, 1)
1893 prevyear, prevmonth = yearmonthplusoffset(year, month, -1)
1894
1895 prevlink = u'%s?calaction=%s&caldate=%d%02d' % (page_name, cal_action, prevyear, prevmonth)
1896 nextlink = u'%s?calaction=%s&caldate=%d%02d' % (page_name, cal_action, nextyear, nextmonth)
1897 curlink = u'%s?calaction=%s&caldate=%d%02d' % (page_name, cal_action, year, month)
1898
1899 if monthstyle_us:
1900 stryearmonth = u'%s %d' % (months[month-1], year)
1901 strnextmonth = u'%s %d' % (months[nextmonth-1], nextyear)
1902 strprevmonth = u'%s %d' % (months[prevmonth-1], prevyear)
1903 else:
1904 stryearmonth = u'%d / %02d' % (year, month)
1905 strnextmonth = u'%d / %02d' % (nextyear, nextmonth)
1906 strprevmonth = u'%d / %02d' % (prevyear, prevmonth)
1907
1908 html = [
1909 u' <tr>',
1910 u' <td class="%s"><a href="%s" title="%s"><</a></td>' % (headclass, prevlink, strprevmonth),
1911 u' <td colspan="5" class="%s"><a href="%s" title="Refresh">%s</a></td>' % (headclass, curlink, stryearmonth),
1912 u' <td class="%s"><a href="%s" title="%s">></a></td>' % (headclass, nextlink, strnextmonth),
1913 u' </tr>',
1914 ]
1915
1916 return u'\r\n'.join(html)
1917
1918 # show days in simple
1919 def simple_eventbox(year, month, day, wkday, boxclass):
1920 wkend = Globs.wkend
1921 if wkday == wkend:
1922 html_text = u'<font color="#aa7744">%s</font>' % day
1923 else:
1924 html_text = u'%s' % day
1925
1926 cyear, cmonth, cday = gettodaydate()
1927
1928 if boxclass == 'simple_nb':
1929 html = u' <td class="%s"> </td>\r\n' % boxclass
1930 else:
1931 if cyear == year and cmonth == month and cday == day:
1932 html = u' <td class="%s_today">%s</td>\r\n' % (boxclass, html_text)
1933 else:
1934 html = u' <td class="%s">%s</td>\r\n' % (boxclass, html_text)
1935
1936 return html
1937
1938
1939 # show weekday
1940 def calhead_weekday(wday, headclass):
1941 if headclass == 'simple_weekday':
1942 html = u' <td class="%s">%s</td>\r\n' % (headclass, wday[0])
1943 else:
1944 html = u' <td class="%s">%s</td>\r\n' % (headclass, wday)
1945
1946 return html
1947
1948
1949 # show days of current month
1950 def calhead_day(year, month, day, wkday):
1951 wkend = Globs.wkend
1952 if wkday == wkend:
1953 html_text = u'<font color="#FF3300">%s</font>' % day
1954 else:
1955 html_text = u'%s' % day
1956
1957 cyear, cmonth, cday = gettodaydate()
1958
1959 if cyear == year and cmonth == month and cday == day:
1960 html = u' <td class="head_day_today"> %s</td>\r\n' % html_text
1961 else:
1962 html = u' <td class="head_day"> %s</td>\r\n' % html_text
1963
1964 return html
1965
1966
1967 # show days of previous or next month
1968 def calhead_day_nbmonth(day):
1969 html = u' <td class="head_day_nbmonth"> %s</td>\r\n' % day
1970 return html
1971
1972
1973 # show blank calendar box
1974 def calshow_blankbox(classname):
1975 html = u' <td class="%s"> </td>' % classname
1976 return html
1977
1978 # show eventbox
1979 def calshow_eventbox(event, colspan, status, cur_date):
1980 if status:
1981 status = u'_%s' % status
1982
1983 title = event['title']
1984 eid = event['id']
1985 startdate = event['startdate']
1986 enddate = event['enddate']
1987 starttime = event['starttime']
1988 endtime = event['endtime']
1989 description = event['description']
1990 bgcolor = event['bgcolor']
1991
1992 year, month, day = getdatefield(cur_date)
1993
1994 if bgcolor:
1995 bgcolor = 'background-color: %s;' % bgcolor
1996 else:
1997 bgcolor = 'background-color: %s;' % Params.bgcolor
1998
1999 if (startdate == enddate) and starttime:
2000 shour, smin = gettimefield(starttime)
2001
2002 link = [
2003 u'<table width="100%%" style="border-width: 0px; padding: 0px; margin: 0px;"><tr>\r\n',
2004 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 </td>\r\n' % (shour, smin),
2005 u'<td style="border-width: 0px; padding: 0px; margin: 0px; text-align: left; vertical-align: top;font-size: 8pt;">',
2006 u'%s' % showReferPageParsed(event, 'title', 1),
2007 u'</td>\r\n</tr></table>',
2008 ]
2009 link = u''.join(link)
2010 else:
2011 link = u'%s' % showReferPageParsed(event, 'title', 1)
2012
2013
2014 html = [
2015 u' <td class="cal_eventbox" colspan="%d"><table class="cal_event">' % colspan,
2016 u' <tr><td id="cal_event_%s" class="cal_event%s" style="%s">%s</td></tr>' % (eid, status, bgcolor, link),
2017 u' </table></td>',
2018 ]
2019
2020 return u'\r\n'.join(html)
2021
2022
2023 def insertcalevents(cal_events, datefrom, dateto, e_id, e_start_date, e_end_date):
2024
2025
2026 if not (int(e_start_date) > dateto or int(e_end_date) < datefrom):
2027
2028 e_start_date = str(max(int(e_start_date), datefrom))
2029 e_end_date = str(min(int(e_end_date), dateto))
2030
2031 day_delta = datetime.timedelta(days=1)
2032 e_start_year, e_start_month, e_start_day = getdatefield(e_start_date)
2033 cur_datetime = datetime.date(e_start_year, e_start_month, e_start_day)
2034
2035 while 1:
2036 tmp_record_date = formatdateobject(cur_datetime)
2037
2038 if not cal_events.has_key(tmp_record_date):
2039 cal_events[tmp_record_date] = []
2040 cal_events[tmp_record_date].append(e_id)
2041
2042 if tmp_record_date == e_end_date:
2043 break
2044
2045 cur_datetime = cur_datetime + day_delta
2046
2047
2048 # date format should be like '20051004' for 2005, Oct., 04
2049 def diffday(date1, date2):
2050
2051 try:
2052 year1, month1, day1 = getdatefield(date1)
2053 year2, month2, day2 = getdatefield(date2)
2054 tmp_diff = datetime.date(year2, month2, day2) - datetime.date(year1, month1, day1)
2055 except (TypeError, ValueError):
2056 debug('An event data is corrupted: invalid date format')
2057 return 0
2058
2059 return tmp_diff.days
2060
2061
2062 def formatDate(year, month, day):
2063 # returns like: '20051004'
2064 return u'%4d%02d%02d' % (year, month, day)
2065
2066 def formatdateobject(obj_date):
2067
2068 return formatDate(obj_date.year, obj_date.month, obj_date.day)
2069
2070
2071 def cliprgb(r,g,b): # don't use 255!
2072 if r < 0: r=0
2073 if r > 254: r=254
2074 if b < 0: b=0
2075 if b > 254: b=254
2076 if g < 0: g=0
2077 if g > 254: g=254
2078 return r, g, b
2079
2080
2081 def debug(astring):
2082 Globs.debugmsg += u'<li>%s\n' % astring
2083
2084
2085 def yearmonthplusoffset(year, month, offset):
2086 month = month+offset
2087 # handle offset and under/overflows - quick and dirty, yes!
2088 while month < 1:
2089 month = month + 12
2090 year = year - 1
2091 while month > 12:
2092 month = month - 12
2093 year = year + 1
2094 return year, month
2095
2096
2097 def formatcfgdatetime(strdate, strtime=''):
2098
2099 if not strdate:
2100 return ''
2101
2102 request = Globs.request
2103
2104 if request.user.date_fmt:
2105 date_fmt = request.user.date_fmt
2106 else:
2107 date_fmt = request.cfg.date_fmt
2108
2109 if request.user.datetime_fmt:
2110 datetime_fmt = request.user.datetime_fmt
2111 else:
2112 datetime_fmt = request.cfg.datetime_fmt
2113
2114 ## XXX HACK
2115 datetime_fmt = datetime_fmt.replace(':%S', '')
2116
2117 date_fmt = str(date_fmt)
2118 datetime_fmt = str(datetime_fmt)
2119
2120 year, month, day = getdatefield(str(strdate))
2121 if strtime:
2122 hour, min = gettimefield(str(strtime))
2123 objdatetime = datetime.datetime(year, month, day, hour, min)
2124 return objdatetime.strftime(datetime_fmt)
2125 else:
2126 objdate = getdatetimefromstring(strdate)
2127 return objdate.strftime(date_fmt)
2128
2129
2130 def getdatetimefromstring(strdate):
2131 year, month, day = getdatefield(str(strdate))
2132 return datetime.date( year, month, day )
2133
2134
2135 def searchPages(request, needle):
2136 # Search the pages and return the results
2137 query = search.QueryParser().parse_query(needle)
2138 results = search.searchPages(request, query)
2139 #results.sortByPagename()
2140
2141 return results.hits
2142
2143 html = []
2144 for page in results.hits:
2145 html.append(page.page_name)
2146
2147 html = u',<br>'.join(html)
2148 return u'%s<p>%s' % (Params.category, html)
2149
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.