3 * Handles the HTML display of calendar items.
5 * Copyright (c) 1996-2012 by the citadel.org team
7 * This program is open source software. You can redistribute it and/or
8 * modify it under the terms of the GNU General Public License, version 3.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
17 #include "webserver.h"
20 /* These define how high the hour rows are in the day view */
22 #define EXTRATIMELINE 22
24 void embeddable_mini_calendar(int year, int month) {
25 struct tm starting_tm;
29 time_t previous_month;
31 time_t colheader_time;
32 struct tm colheader_tm;
33 char colheader_label[32];
38 snprintf(div_id, sizeof div_id, "mini_calendar_%d", rand());
40 /* Determine what day to start. If an impossible value is found, start on Sunday.
42 get_pref_long("weekstart", &weekstart, 17);
47 * Now back up to the 1st of the month...
49 memset(&starting_tm, 0, sizeof(struct tm));
51 starting_tm.tm_year = year - 1900;
52 starting_tm.tm_mon = month - 1;
53 starting_tm.tm_mday = 1;
54 thetime = mktime(&starting_tm);
56 memcpy(&tm, &starting_tm, sizeof(struct tm));
57 while (tm.tm_mday != 1) {
58 thetime = thetime - (time_t) 86400; /* go back 24 hours */
59 localtime_r(&thetime, &tm);
62 /* Determine previous and next months ... for links */
63 previous_month = thetime - (time_t) 864000L; /* back 10 days */
64 next_month = thetime + (time_t) (31L * 86400L); /* ahead 31 days */
66 /* Now back up until we're on the user's preferred start day */
67 localtime_r(&thetime, &tm);
68 while (tm.tm_wday != weekstart) {
69 thetime = thetime - (time_t) 86400; /* go back 24 hours */
70 localtime_r(&thetime, &tm);
73 wc_printf("<div class=\"mini_calendar\" id=\"%s\">\n", div_id);
75 /* Previous month link */
76 localtime_r(&previous_month, &tm);
77 wc_printf("<a href=\"javascript:minical_change_month(%d,%d);\">«</a>", (int) (tm.tm_year) + 1900, tm.tm_mon + 1);
79 wc_strftime(colheader_label, sizeof colheader_label, "%B", &starting_tm);
80 wc_printf(" "
81 "<span class=\"mini_calendar_month_label\">" "%s %d" "</span>" " ", colheader_label, year);
84 localtime_r(&next_month, &tm);
85 wc_printf("<a href=\"javascript:minical_change_month(%d,%d);\">»</a>", (int) (tm.tm_year) + 1900, tm.tm_mon + 1);
87 wc_printf("<table border=\"0\" cellpadding=\"1\" cellspacing=\"1\" class=\"mini_calendar_days\">" "<tr>");
88 colheader_time = thetime;
89 for (i = 0; i < 7; ++i) {
90 colheader_time = thetime + (i * 86400);
91 localtime_r(&colheader_time, &colheader_tm);
92 wc_strftime(colheader_label, sizeof colheader_label, "%A", &colheader_tm);
93 wc_printf("<th>%c</th>", colheader_label[0]);
99 /* Now do 35 or 42 days */
100 for (i = 0; i < 42; ++i) {
101 localtime_r(&thetime, &tm);
105 /* Before displaying Sunday, start a new row */
110 if (tm.tm_mon == month - 1) {
111 snprintf(url, sizeof url, "readfwd?calview=day?year=%d?month=%d?day=%d",
112 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
113 wc_printf("<td><a href=\"%s\">%d</a></td>", url, tm.tm_mday);
116 wc_printf("<td> </td>");
119 /* After displaying one week, end the row */
121 wc_printf("</tr>\n");
126 thetime += (time_t) 86400; /* ahead 24 hours */
129 wc_printf("</table>" /* end of inner table */
132 StrBufAppendPrintf(WC->trailing_javascript,
133 " function minical_change_month(year, month) { \n"
134 " p = 'year=' + year + '&month=' + month \n"
135 " + '&r=' + ctdlRandomString(); \n"
136 " new Ajax.Updater('%s', 'mini_calendar', \n"
137 " { method: 'get', parameters: p, evalScripts: true } ); \n"
143 * ajax embedder for the above mini calendar
145 void ajax_mini_calendar(void) {
146 embeddable_mini_calendar(ibstr("year"), ibstr("month"));
151 * Display one day of a whole month view of a calendar
153 void calendar_month_view_display_events(int year, int month, int day) {
159 icalproperty *p = NULL;
160 icalproperty *q = NULL;
161 struct icaltimetype t;
162 struct icaltimetype end_t;
163 struct icaltimetype today_start_t;
164 struct icaltimetype today_end_t;
165 struct icaltimetype today_t;
166 struct tm starting_tm;
168 int all_day_event = 0;
174 if (GetCount(WCC->disp_cal_items) == 0) {
180 * Create an imaginary event which spans the 24 hours of today. Any events which
181 * overlap with this one take place at least partially in this day. We have to
182 * convert it from a struct tm in order to make it UTC.
184 memset(&starting_tm, 0, sizeof(struct tm));
185 starting_tm.tm_year = year - 1900;
186 starting_tm.tm_mon = month - 1;
187 starting_tm.tm_mday = day;
188 starting_tm.tm_hour = 0;
189 starting_tm.tm_min = 0;
190 today_start_t = icaltime_from_timet_with_zone(mktime(&starting_tm), 0, icaltimezone_get_utc_timezone());
192 memset(&ending_tm, 0, sizeof(struct tm));
193 ending_tm.tm_year = year - 1900;
194 ending_tm.tm_mon = month - 1;
195 ending_tm.tm_mday = day;
196 ending_tm.tm_hour = 23;
197 ending_tm.tm_min = 59;
198 today_end_t = icaltime_from_timet_with_zone(mktime(&ending_tm), 0, icaltimezone_get_utc_timezone());
201 * Create another one without caring about the timezone for all day events.
203 today_t = icaltime_null_date();
205 today_t.month = month;
209 * Now loop through our list of events to see which ones occur today.
211 Pos = GetNewHashPos(WCC->disp_cal_items, 0);
212 while (GetNextHashPos(WCC->disp_cal_items, Pos, &hklen, &HashKey, &vCal)) {
213 Cal = (disp_cal *) vCal;
215 q = icalcomponent_get_first_property(Cal->cal, ICAL_DTSTART_PROPERTY);
217 t = icalproperty_get_dtstart(q);
220 memset(&t, 0, sizeof t);
222 q = icalcomponent_get_first_property(Cal->cal, ICAL_DTEND_PROPERTY);
224 end_t = icalproperty_get_dtend(q);
227 memset(&end_t, 0, sizeof end_t);
233 show_event = ical_ctdl_is_overlap(t, end_t, today_t, icaltime_null_time());
236 show_event = ical_ctdl_is_overlap(t, end_t, today_start_t, today_end_t);
240 * If we determined that this event occurs today, then display it.
244 /* time_t logtt = icaltime_as_timet(t);
245 syslog(LOG_DEBUG, "Match on %04d-%02d-%02d for event %x%s on %s",
247 (int)Cal, ((all_day_event) ? " (all day)" : ""),
251 p = icalcomponent_get_first_property(Cal->cal, ICAL_SUMMARY_PROPERTY);
253 p = icalproperty_new_summary(_("Untitled Event"));
254 icalcomponent_add_property(Cal->cal, p);
259 wc_printf("<table border=\"0\" cellpadding=\"2\"><TR>" "<td bgcolor=\"#CCCCDD\">");
263 wc_printf("<font size=\"-1\">"
264 "<a class=\"event%s\" href=\"display_edit_event?"
265 "msgnum=%ld?calview=month?year=%d?month=%d?day=%d\">",
266 (Cal->unread) ? "_unread" : "_read", Cal->cal_msgnum, year, month, day);
268 escputs((char *) icalproperty_get_comment(p));
270 wc_printf("<span class=\"tooltip\"><span class=\"btttop\"></span><span class=\"bttmiddle\">");
272 wc_printf("<i>%s: %s</i><br>", _("From"), Cal->from);
273 wc_printf("<i>%s</i> ", _("Summary:"));
274 escputs((char *) icalproperty_get_comment(p));
277 q = icalcomponent_get_first_property(Cal->cal, ICAL_LOCATION_PROPERTY);
279 wc_printf("<i>%s</i> ", _("Location:"));
280 escputs((char *) icalproperty_get_comment(q));
285 * Only show start/end times if we're actually looking at the VEVENT
286 * component. Otherwise it shows bogus dates for e.g. timezones
288 if (icalcomponent_isa(Cal->cal) == ICAL_VEVENT_COMPONENT) {
290 q = icalcomponent_get_first_property(Cal->cal, ICAL_DTSTART_PROPERTY);
294 t = icalproperty_get_dtstart(q);
295 q = icalcomponent_get_first_property(Cal->cal, ICAL_DTEND_PROPERTY);
297 end_t = icalproperty_get_dtend(q);
301 * events with starting date/time equal
302 * ending date/time might get only
303 * DTSTART but no DTEND
313 /* end given, have to adjust it */
314 icaltime_adjust(&end_t, -1, 0, 0, 0);
316 memset(&d_tm, 0, sizeof d_tm);
317 d_tm.tm_year = t.year - 1900;
318 d_tm.tm_mon = t.month - 1;
319 d_tm.tm_mday = t.day;
320 wc_strftime(buf, sizeof buf, "%x", &d_tm);
322 if (no_end || !icaltime_compare(t, end_t)) {
323 wc_printf("<i>%s</i> %s<br>", _("Date:"), buf);
326 wc_printf("<i>%s</i> %s<br>", _("Starting date:"), buf);
327 d_tm.tm_year = end_t.year - 1900;
328 d_tm.tm_mon = end_t.month - 1;
329 d_tm.tm_mday = end_t.day;
330 wc_strftime(buf, sizeof buf, "%x", &d_tm);
331 wc_printf("<i>%s</i> %s<br>", _("Ending date:"), buf);
335 tt = icaltime_as_timet(t);
336 webcit_fmt_date(buf, 256, tt, DATEFMT_BRIEF);
337 if (no_end || !icaltime_compare(t, end_t)) {
338 wc_printf("<i>%s</i> %s<br>", _("Date/time:"), buf);
341 wc_printf("<i>%s</i> %s<br>", _("Starting date/time:"), buf);
342 tt = icaltime_as_timet(end_t);
343 webcit_fmt_date(buf, 256, tt, DATEFMT_BRIEF);
344 wc_printf("<i>%s</i> %s<br>", _("Ending date/time:"), buf);
352 q = icalcomponent_get_first_property(Cal->cal, ICAL_DESCRIPTION_PROPERTY);
354 wc_printf("<i>%s</i> ", _("Notes:"));
355 escputs((char *) icalproperty_get_comment(q));
359 wc_printf("</span><span class=\"bttbottom\"></span></span>");
360 wc_printf("</a></font><br>\n");
363 wc_printf("</td></tr></table>");
377 * Display one day of a whole month view of a calendar
379 void calendar_month_view_brief_events(time_t thetime, const char *daycolor) {
393 struct icaltimetype t;
395 int all_day_event = 0;
399 time_format = get_time_format_cached();
401 if (time_format == WC_TIMEFORMAT_24)
402 timeformat = "%k:%M";
404 timeformat = "%I:%M %p";
406 localtime_r(&thetime, &today_tm);
408 Pos = GetNewHashPos(WCC->disp_cal_items, 0);
409 while (GetNextHashPos(WCC->disp_cal_items, Pos, &hklen, &HashKey, &vCal)) {
410 Cal = (disp_cal *) vCal;
411 p = icalcomponent_get_first_property(Cal->cal, ICAL_DTSTART_PROPERTY);
413 t = icalproperty_get_dtstart(p);
414 event_tt = icaltime_as_timet(t);
415 event_tts = event_tt;
422 gmtime_r(&event_tts, &event_tms);
425 localtime_r(&event_tts, &event_tms);
427 /* \todo epoch &! daymask */
428 if ((event_tms.tm_year == today_tm.tm_year)
429 && (event_tms.tm_mon == today_tm.tm_mon)
430 && (event_tms.tm_mday == today_tm.tm_mday)) {
436 p = icalcomponent_get_first_property(Cal->cal, ICAL_SUMMARY_PROPERTY);
438 p = icalproperty_new_summary(_("Untitled Event"));
439 icalcomponent_add_property(Cal->cal, p);
441 e = icalcomponent_get_first_property(Cal->cal, ICAL_DTEND_PROPERTY);
442 if ((p != NULL) && (e != NULL)) {
445 t = icalproperty_get_dtend(e);
446 event_tte = icaltime_as_timet(t);
447 localtime_r(&event_tte, &event_tme);
448 difftime = (event_tte - event_tts) / 60;
449 hours = (int) (difftime / 60);
450 minutes = difftime % 60;
451 wc_printf("<tr><td bgcolor='%s'>%i:%2i</td><td bgcolor='%s'>"
453 "<a class=\"event%s\" href=\"display_edit_event?msgnum=%ld?calview=calbrief?year=%s?month=%s?day=%s\">",
456 (Cal->unread) ? "_unread" : "_read",
457 daycolor, Cal->cal_msgnum, bstr("year"), bstr("month"), bstr("day")
461 icalproperty_get_comment(p));
462 /* \todo: allso ammitime format */
463 wc_strftime(&sbuf[0], sizeof(sbuf), timeformat, &event_tms);
464 wc_strftime(&ebuf[0], sizeof(sbuf), timeformat, &event_tme);
466 wc_printf("</a></font></td>"
467 "<td bgcolor='%s'>%s</td><td bgcolor='%s'>%s</td></tr>",
468 daycolor, sbuf, daycolor, ebuf);
481 * view one month. pretty view
483 void calendar_month_view(int year, int month, int day) {
484 struct tm starting_tm;
489 time_t previous_month;
491 time_t colheader_time;
493 struct tm colheader_tm;
494 char colheader_label[32];
498 * Make sure we know which day is today.
500 today_timet = time(NULL);
501 localtime_r(&today_timet, &today_tm);
504 * Determine what day to start. If an impossible value is found, start on Sunday.
506 get_pref_long("weekstart", &weekstart, 17);
511 * Now back up to the 1st of the month...
513 memset(&starting_tm, 0, sizeof(struct tm));
515 starting_tm.tm_year = year - 1900;
516 starting_tm.tm_mon = month - 1;
517 starting_tm.tm_mday = day;
518 thetime = mktime(&starting_tm);
520 memcpy(&tm, &starting_tm, sizeof(struct tm));
521 while (tm.tm_mday != 1) {
522 thetime = thetime - (time_t) 86400; /* go back 24 hours */
523 localtime_r(&thetime, &tm);
526 /* Determine previous and next months ... for links */
527 previous_month = thetime - (time_t) 864000L; /* back 10 days */
528 next_month = thetime + (time_t) (31L * 86400L); /* ahead 31 days */
530 /* Now back up until we're on the user's preferred start day */
531 localtime_r(&thetime, &tm);
532 while (tm.tm_wday != weekstart) {
533 thetime = thetime - (time_t) 86400; /* go back 24 hours */
534 localtime_r(&thetime, &tm);
537 /* Outer table (to get the background color) */
538 wc_printf("<table class=\"calendar\"> \n <tr><td>");
540 wc_printf("<table width=\"100%%\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\"><tr>\n");
542 wc_printf("<td align=\"center\">");
544 localtime_r(&previous_month, &tm);
545 wc_printf("<a href=\"readfwd?calview=month?year=%d?month=%d?day=1\">", (int) (tm.tm_year) + 1900, tm.tm_mon + 1);
546 wc_printf("<img alt=\"%s\" align=\"middle\" src=\"static/webcit_icons/essen/32x32/back.png\" border=\"0\"></a>\n",
549 wc_strftime(colheader_label, sizeof colheader_label, "%B", &starting_tm);
550 wc_printf(" " "<font size=\"+1\" color=\"#FFFFFF\">" "%s %d" "</font>" " ", colheader_label, year);
552 localtime_r(&next_month, &tm);
553 wc_printf("<a href=\"readfwd?calview=month?year=%d?month=%d?day=1\">", (int) (tm.tm_year) + 1900, tm.tm_mon + 1);
554 wc_printf("<img alt=\"%s\" align=\"middle\" src=\"static/webcit_icons/essen/32x32/forward.png\" border=\"0\"></A>\n",
557 wc_printf("</td></tr></table>\n");
559 /* Inner table (the real one) */
560 wc_printf("<table width=\"100%%\" border=\"0\" cellpadding=\"1\" cellspacing=\"1\" "
561 "bgcolor='#204B78' id=\"inner_month\"><tr>");
562 wc_printf("<th align=\"center\" width=\"2%%\"></th>");
563 colheader_time = thetime;
564 for (i = 0; i < 7; ++i) {
565 colheader_time = thetime + (i * 86400);
566 localtime_r(&colheader_time, &colheader_tm);
567 wc_strftime(colheader_label, sizeof colheader_label, "%A", &colheader_tm);
568 wc_printf("<th align=\"center\" width=\"14%%\">" "<font color=\"#FFFFFF\">%s</font></th>", colheader_label);
571 wc_printf("</tr>\n");
574 /* Now do 35 or 42 days */
575 localtime_r(&thetime, &tm);
576 for (i = 0; i < 42; ++i) {
578 /* Before displaying the first day of the week, start a new row */
580 wc_printf("<tr><td class=\"week_of_year\">");
581 wc_strftime(colheader_label, sizeof colheader_label, "%V", &tm);
582 wc_printf("%s ", colheader_label);
585 wc_printf("<td class=\"cal%s\"><div class=\"day\">",
586 ((tm.tm_mon != month - 1) ? "out" :
587 (((tm.tm_year == today_tm.tm_year) && (tm.tm_mon == today_tm.tm_mon)
588 && (tm.tm_mday == today_tm.tm_mday)) ? "today" : ((tm.tm_wday == 0
589 || tm.tm_wday == 6) ? "weekend" : "day")))
591 if ((i == 0) || (tm.tm_mday == 1)) {
592 wc_strftime(colheader_label, sizeof colheader_label, "%B", &tm);
593 wc_printf("%s ", colheader_label);
595 wc_printf("<a href=\"readfwd?calview=day?year=%d?month=%d?day=%d\">"
596 "%d</a></div>", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_mday);
598 /* put the data here, stupid */
599 calendar_month_view_display_events(tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
603 /* After displaying the last day of the week, end the row */
605 wc_printf("</tr>\n");
608 thetime += (time_t) 86400; /* ahead 24 hours */
609 localtime_r(&thetime, &tm);
611 if (((i % 7) == 6) && (tm.tm_mon != month - 1) && (tm.tm_mday < 15)) {
612 i = 100; /* break out of the loop */
616 wc_printf("</table>" /* end of inner table */
617 "</td></tr></table>\n" /* end of outer table */
622 * view one month. brief view
624 void calendar_brief_month_view(int year, int month, int day) {
625 struct tm starting_tm;
629 time_t previous_month;
631 char month_label[32];
633 /* Determine what day to start.
634 * First, back up to the 1st of the month...
636 memset(&starting_tm, 0, sizeof(struct tm));
637 starting_tm.tm_year = year - 1900;
638 starting_tm.tm_mon = month - 1;
639 starting_tm.tm_mday = day;
640 thetime = mktime(&starting_tm);
642 memcpy(&tm, &starting_tm, sizeof(struct tm));
643 while (tm.tm_mday != 1) {
644 thetime = thetime - (time_t) 86400; /* go back 24 hours */
645 localtime_r(&thetime, &tm);
648 /* Determine previous and next months ... for links */
649 previous_month = thetime - (time_t) 864000L; /* back 10 days */
650 next_month = thetime + (time_t) (31L * 86400L); /* ahead 31 days */
652 /* Now back up until we're on a Sunday */
653 localtime_r(&thetime, &tm);
654 while (tm.tm_wday != 0) {
655 thetime = thetime - (time_t) 86400; /* go back 24 hours */
656 localtime_r(&thetime, &tm);
659 /* Outer table (to get the background color) */
660 wc_printf("<table width=\"100%%\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" " "bgcolor=#204B78><tr><td>\n");
662 wc_printf("<table width=\"100%%\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\"><tr>\n");
664 wc_printf("<td align=\"center\">");
666 localtime_r(&previous_month, &tm);
667 wc_printf("<a href=\"readfwd?calview=month?year=%d?month=%d?day=1\">", (int) (tm.tm_year) + 1900, tm.tm_mon + 1);
668 wc_printf("<img alt=\"%s\" align=\"middle\" src=\"static/webcit_icons/essen/32x32/back.png\" border=\"0\"></a>\n",
671 wc_strftime(month_label, sizeof month_label, "%B", &tm);
672 wc_printf(" " "<font size=\"+1\" color=\"#FFFFFF\">" "%s %d" "</font>" " ", month_label, year);
674 localtime_r(&next_month, &tm);
675 wc_printf("<a href=\"readfwd?calview=month?year=%d?month=%d?day=1\">", (int) (tm.tm_year) + 1900, tm.tm_mon + 1);
676 wc_printf("<img alt=\"%s\" align=\"middle\" src=\"static/webcit_icons/essen/32x32/forward.png\" border=\"0\"></a>\n",
679 wc_printf("</td></tr></table>\n");
681 /* Inner table (the real one) */
682 wc_printf("<table width=\"100%%\" border=\"0\" cellpadding=\"1\" cellspacing=\"1\" " "bgcolor=#EEEECC><TR>");
683 wc_printf("</tr>\n");
684 wc_printf("<tr><td colspan=\"100%%\">\n");
687 for (i = 0; i < 35; ++i) {
688 char weeknumber[255];
689 char weekday_name[32];
691 localtime_r(&thetime, &tm);
694 /* Before displaying Sunday, start a new CELL */
696 wc_strftime(&weeknumber[0], sizeof(weeknumber), "%U", &tm);
697 wc_printf("<table border='0' bgcolor=\"#EEEECC\" width='100%%'> <tr><th colspan='4'>%s %s</th></tr>"
698 " <tr><td>%s</td><td width='70%%'>%s</td><td>%s</td><td>%s</td></tr>\n",
699 _("Week"), weeknumber, _("Hours"), _("Subject"), _("Start"), _("End")
703 daycolor = ((tm.tm_mon != month - 1) ? "DDDDDD" : ((tm.tm_wday == 0 || tm.tm_wday == 6) ? "EEEECC" : "FFFFFF"));
706 wc_strftime(weekday_name, sizeof weekday_name, "%A", &tm);
707 wc_printf("<tr><td bgcolor='%s' colspan='1' align='left'> %s,%i."
708 "</td><td bgcolor='%s' colspan='3'><hr></td></tr>\n", daycolor, weekday_name, tm.tm_mday, daycolor);
710 /* put the data of one day here, stupid */
711 calendar_month_view_brief_events(thetime, daycolor);
714 /* After displaying Saturday, end the row */
716 wc_printf("</td></tr></table>\n");
719 thetime += (time_t) 86400; /* ahead 24 hours */
722 wc_printf("</table>" /* end of inner table */
723 "</td></tr></table>\n" /* end of outer table */
728 * Calendar week view -- not implemented yet, this is a stub function
730 void calendar_week_view(int year, int month, int day) {
731 wc_printf("<center><i>week view FIXME</i></center><br>\n");
737 * Display events for a particular hour of a particular day.
738 * (Specify hour < 0 to show "all day" events)
740 * dstart and dend indicate which hours our "daytime" begins and end
742 void calendar_day_view_display_events(time_t thetime, int year, int month, int day, int notime_events, int dstart, int dend) {
747 icalproperty *p = NULL;
748 icalproperty *q = NULL;
754 int all_day_event = 0;
755 int ongoing_event = 0;
758 struct icaltimetype t;
759 struct icaltimetype end_t;
760 struct icaltimetype today_start_t;
761 struct icaltimetype today_end_t;
762 struct icaltimetype today_t;
763 struct tm starting_tm;
774 if (GetCount(WCC->disp_cal_items) == 0) {
775 /* nothing to display */
779 /* Create an imaginary event which spans the current day. Any events which
780 * overlap with this one take place at least partially in this day.
782 memset(&starting_tm, 0, sizeof(struct tm));
783 starting_tm.tm_year = year - 1900;
784 starting_tm.tm_mon = month - 1;
785 starting_tm.tm_mday = day;
786 starting_tm.tm_hour = 0;
787 starting_tm.tm_min = 0;
788 today_start_t = icaltime_from_timet_with_zone(mktime(&starting_tm), 0, icaltimezone_get_utc_timezone());
790 memset(&ending_tm, 0, sizeof(struct tm));
791 ending_tm.tm_year = year - 1900;
792 ending_tm.tm_mon = month - 1;
793 ending_tm.tm_mday = day;
794 ending_tm.tm_hour = 23;
795 ending_tm.tm_min = 59;
796 today_end_t = icaltime_from_timet_with_zone(mktime(&ending_tm), 0, icaltimezone_get_utc_timezone());
799 * Create another one without caring about the timezone for all day events.
801 today_t = icaltime_null_date();
803 today_t.month = month;
806 /* Now loop through our list of events to see which ones occur today.
808 Pos = GetNewHashPos(WCC->disp_cal_items, 0);
809 while (GetNextHashPos(WCC->disp_cal_items, Pos, &hklen, &HashKey, &vCal)) {
810 Cal = (disp_cal *) vCal;
815 q = icalcomponent_get_first_property(Cal->cal, ICAL_DTSTART_PROPERTY);
817 t = icalproperty_get_dtstart(q);
818 event_tt = icaltime_as_timet(t);
819 localtime_r(&event_tt, &event_te);
822 memset(&t, 0, sizeof t);
828 q = icalcomponent_get_first_property(Cal->cal, ICAL_DTEND_PROPERTY);
830 end_t = icalproperty_get_dtend(q);
833 /* no end given means end = start */
834 memcpy(&end_t, &t, sizeof(struct icaltimetype));
838 show_event = ical_ctdl_is_overlap(t, end_t, today_t, icaltime_null_time());
839 if (icaltime_compare(t, end_t)) {
841 * the end date is non-inclusive so adjust it by one
842 * day because our test is inclusive, note that a day is
843 * not to much because we are talking about all day
846 icaltime_adjust(&end_t, -1, 0, 0, 0);
850 show_event = ical_ctdl_is_overlap(t, end_t, today_start_t, today_end_t);
853 event_tte = icaltime_as_timet(end_t);
854 localtime_r(&event_tte, &event_tm);
856 /* If we determined that this event occurs today, then display it.
858 p = icalcomponent_get_first_property(Cal->cal, ICAL_SUMMARY_PROPERTY);
860 p = icalproperty_new_summary(_("Untitled Event"));
861 icalcomponent_add_property(Cal->cal, p);
864 if ((show_event) && (p != NULL)) {
866 if ((event_te.tm_mday != day) || (event_tm.tm_mday != day))
869 if (all_day_event && notime_events) {
870 wc_printf("<li class=\"event_framed%s\"> "
871 "<a href=\"display_edit_event?"
872 "msgnum=%ld?calview=day?year=%d?month=%d?day=%d\" "
873 " class=\"event_title\">",
874 (Cal->unread) ? "_unread" : "_read", Cal->cal_msgnum, year, month, day);
875 escputs((char *) icalproperty_get_comment(p));
876 wc_printf("<span class=\"tooltip\"><span class=\"btttop\"></span><span class=\"bttmiddle\">");
877 wc_printf("<i>%s</i><br>", _("All day event"));
878 wc_printf("<i>%s: %s</i><br>", _("From"), Cal->from);
879 wc_printf("<i>%s</i> ", _("Summary:"));
880 escputs((char *) icalproperty_get_comment(p));
882 q = icalcomponent_get_first_property(Cal->cal, ICAL_LOCATION_PROPERTY);
884 wc_printf("<i>%s</i> ", _("Location:"));
885 escputs((char *) icalproperty_get_comment(q));
888 if (!icaltime_compare(t, end_t)) { /* one day only */
889 webcit_fmt_date(buf, 256, event_tt, DATEFMT_LOCALEDATE);
890 wc_printf("<i>%s</i> %s<br>", _("Date:"), buf);
893 webcit_fmt_date(buf, 256, event_tt, DATEFMT_LOCALEDATE);
894 wc_printf("<i>%s</i> %s<br>", _("Starting date:"), buf);
895 webcit_fmt_date(buf, 256, event_tte, DATEFMT_LOCALEDATE);
896 wc_printf("<i>%s</i> %s<br>", _("Ending date:"), buf);
898 q = icalcomponent_get_first_property(Cal->cal, ICAL_DESCRIPTION_PROPERTY);
900 wc_printf("<i>%s</i> ", _("Notes:"));
901 escputs((char *) icalproperty_get_comment(q));
904 wc_printf("</span><span class=\"bttbottom\"></span></span>");
905 wc_printf("</a> <span>(");
906 wc_printf(_("All day event"));
907 wc_printf(")</span></li>\n");
909 else if (ongoing_event && notime_events) {
910 wc_printf("<li class=\"event_framed%s\"> "
911 "<a href=\"display_edit_event?"
912 "msgnum=%ld&calview=day?year=%d?month=%d?day=%d\" "
913 " class=\"event_title\">",
914 (Cal->unread) ? "_unread" : "_read", Cal->cal_msgnum, year, month, day);
915 escputs((char *) icalproperty_get_comment(p));
916 wc_printf("<span class=\"tooltip\"><span class=\"btttop\"></span><span class=\"bttmiddle\">");
917 wc_printf("<i>%s</i><br>", _("Ongoing event"));
918 wc_printf("<i>%s: %s</i><br>", _("From"), Cal->from);
919 wc_printf("<i>%s</i> ", _("Summary:"));
920 escputs((char *) icalproperty_get_comment(p));
922 q = icalcomponent_get_first_property(Cal->cal, ICAL_LOCATION_PROPERTY);
924 wc_printf("<i>%s</i> ", _("Location:"));
925 escputs((char *) icalproperty_get_comment(q));
928 webcit_fmt_date(buf, 256, event_tt, DATEFMT_BRIEF);
929 wc_printf("<i>%s</i> %s<br>", _("Starting date/time:"), buf);
930 webcit_fmt_date(buf, 256, event_tte, DATEFMT_BRIEF);
931 wc_printf("<i>%s</i> %s<br>", _("Ending date/time:"), buf);
932 q = icalcomponent_get_first_property(Cal->cal, ICAL_DESCRIPTION_PROPERTY);
934 wc_printf("<i>%s</i> ", _("Notes:"));
935 escputs((char *) icalproperty_get_comment(q));
938 wc_printf("</span><span class=\"bttbottom\"></span></span>");
939 wc_printf("</a> <span>(");
940 wc_printf(_("Ongoing event"));
941 wc_printf(")</span></li>\n");
943 else if (!all_day_event && !notime_events) {
946 if (event_te.tm_mday != day)
947 event_te.tm_hour = 0;
948 if (event_tm.tm_mday != day)
949 event_tm.tm_hour = 24;
951 /* Calculate the location of the top of the box */
952 if (event_te.tm_hour < dstart) {
953 startmin = diffmin = event_te.tm_min / 6;
954 top = (event_te.tm_hour * EXTRATIMELINE) + startmin;
956 else if ((event_te.tm_hour >= dstart) && (event_te.tm_hour <= dend)) {
957 startmin = diffmin = (event_te.tm_min / 2);
958 top = (dstart * EXTRATIMELINE) + ((event_te.tm_hour - dstart) * TIMELINE) + startmin;
960 else if (event_te.tm_hour > dend) {
961 startmin = diffmin = event_te.tm_min / 6;
963 (dstart * EXTRATIMELINE) + ((dend - dstart - 1) * TIMELINE) +
964 ((event_tm.tm_hour - dend + 1) * EXTRATIMELINE) + startmin;
967 /* should never get here */
970 /* Calculate the location of the bottom of the box */
971 if (event_tm.tm_hour < dstart) {
972 endmin = diffmin = event_tm.tm_min / 6;
973 bottom = (event_tm.tm_hour * EXTRATIMELINE) + endmin;
975 else if ((event_tm.tm_hour >= dstart) && (event_tm.tm_hour <= dend)) {
976 endmin = diffmin = (event_tm.tm_min / 2);
977 bottom = (dstart * EXTRATIMELINE) + ((event_tm.tm_hour - dstart) * TIMELINE) + endmin;
979 else if (event_tm.tm_hour > dend) {
980 endmin = diffmin = event_tm.tm_min / 6;
982 (dstart * EXTRATIMELINE) + ((dend - dstart + 1) * TIMELINE) +
983 ((event_tm.tm_hour - dend - 1) * EXTRATIMELINE) + endmin;
986 /* should never get here */
989 wc_printf("<dd class=\"event_framed%s\" "
990 "style=\"position: absolute; "
991 "top:%dpx; left:%dpx; "
992 "height:%dpx; \" >", (Cal->unread) ? "_unread" : "_read", top, (gap * 40), (bottom - top)
994 wc_printf("<a href=\"display_edit_event?"
995 "msgnum=%ld?calview=day?year=%d?month=%d?day=%d?hour=%d\" "
996 "class=\"event_title\">", Cal->cal_msgnum, year, month, day, t.hour);
997 escputs((char *) icalproperty_get_comment(p));
998 wc_printf("<span class=\"tooltip\"><span class=\"btttop\"></span><span class=\"bttmiddle\">");
999 wc_printf("<i>%s: %s</i><br>", _("From"), Cal->from);
1000 wc_printf("<i>%s</i> ", _("Summary:"));
1001 escputs((char *) icalproperty_get_comment(p));
1003 q = icalcomponent_get_first_property(Cal->cal, ICAL_LOCATION_PROPERTY);
1005 wc_printf("<i>%s</i> ", _("Location:"));
1006 escputs((char *) icalproperty_get_comment(q));
1009 if (!icaltime_compare(t, end_t)) { /* one day only */
1010 webcit_fmt_date(buf, 256, event_tt, DATEFMT_BRIEF);
1011 wc_printf("<i>%s</i> %s<br>", _("Date/time:"), buf);
1014 webcit_fmt_date(buf, 256, event_tt, DATEFMT_BRIEF);
1015 wc_printf("<i>%s</i> %s<br>", _("Starting date/time:"), buf);
1016 webcit_fmt_date(buf, 256, event_tte, DATEFMT_BRIEF);
1017 wc_printf("<i>%s</i> %s<br>", _("Ending date/time:"), buf);
1019 q = icalcomponent_get_first_property(Cal->cal, ICAL_DESCRIPTION_PROPERTY);
1021 wc_printf("<i>%s</i> ", _("Notes:"));
1022 escputs((char *) icalproperty_get_comment(q));
1025 wc_printf("</span><span class=\"bttbottom\"></span></span>");
1026 wc_printf("</a></dd>\n");
1030 DeleteHashPos(&Pos);
1036 void calendar_day_view(int year, int month, int day) {
1038 struct icaltimetype today, yesterday, tomorrow;
1045 int timeline = TIMELINE;
1046 int extratimeline = EXTRATIMELINE;
1051 time_format = get_time_format_cached();
1052 get_pref_long("daystart", &daystart, 8);
1053 get_pref_long("dayend", &dayend, 17);
1055 /* when loading daystart/dayend, replace missing, corrupt, or impossible values with defaults */
1056 if ((daystart < 0) || (dayend < 2) || (daystart >= 23) || (dayend > 23) || (dayend <= daystart)) {
1062 memset(&d_tm, 0, sizeof d_tm);
1063 d_tm.tm_year = year - 1900;
1064 d_tm.tm_mon = month - 1;
1066 today_t = mktime(&d_tm);
1068 /* Figure out the dates for "yesterday" and "tomorrow" links */
1070 memset(&today, 0, sizeof(struct icaltimetype));
1072 today.month = month;
1076 memcpy(&yesterday, &today, sizeof(struct icaltimetype));
1078 yesterday = icaltime_normalize(yesterday);
1080 memcpy(&tomorrow, &today, sizeof(struct icaltimetype));
1082 tomorrow = icaltime_normalize(tomorrow);
1084 /* Inner table (the real one) */
1085 wc_printf("<table class=\"calendar\" id=\"inner_day\"><tr> \n");
1087 /* Innermost cell (contains hours etc.) */
1088 wc_printf("<td class=\"events_of_the_day\" >");
1089 wc_printf("<dl class=\"events\" >");
1091 /* Now the middle of the day... */
1093 extrahourlabel = extratimeline - 2;
1094 hourlabel = extrahourlabel * 150 / 100;
1095 if (hourlabel > (timeline - 2))
1096 hourlabel = timeline - 2;
1098 for (hour = 0; hour < daystart; ++hour) { /* could do HEIGHT=xx */
1099 wc_printf("<dt class=\"extrahour\">"
1100 "<a href=\"display_edit_event?msgnum=0" "?calview=day?year=%d?month=%d?day=%d?hour=%d?minute=0\">",
1102 /* TODO: what have these been used for?
1103 (hour * extratimeline ),
1107 year, month, day, hour);
1109 if (time_format == WC_TIMEFORMAT_24) {
1110 wc_printf("%2d:00</a> ", hour);
1113 wc_printf("%d:00%s</a> ", ((hour == 0) ? 12 : (hour <= 12 ? hour : hour - 12)), (hour < 12 ? "am" : "pm")
1120 gap = daystart * extratimeline;
1122 for (hour = daystart; hour <= dayend; ++hour) { /* could do HEIGHT=xx */
1123 wc_printf("<dt class=\"hour\">"
1124 "<a href=\"display_edit_event?msgnum=0?calview=day" "?year=%d?month=%d?day=%d?hour=%d?minute=0\">",
1126 /*TODO: what have these been used for?
1127 gap + ((hour - daystart) * timeline ),
1131 year, month, day, hour);
1133 if (time_format == WC_TIMEFORMAT_24) {
1134 wc_printf("%2d:00</a> ", hour);
1137 wc_printf("%d:00%s</a> ", (hour <= 12 ? hour : hour - 12), (hour < 12 ? "am" : "pm")
1144 gap = gap + ((dayend - daystart + 1) * timeline);
1146 for (hour = (dayend + 1); hour < 24; ++hour) { /* could do HEIGHT=xx */
1147 wc_printf("<dt class=\"extrahour\">"
1148 "<a href=\"display_edit_event?msgnum=0?calview=day" "?year=%d?month=%d?day=%d?hour=%d?minute=0\">",
1150 /*TODO: what have these been used for?
1151 gap + ((hour - dayend - 1) * extratimeline ),
1155 year, month, day, hour);
1157 if (time_format == WC_TIMEFORMAT_24) {
1158 wc_printf("%2d:00</a> ", hour);
1161 wc_printf("%d:00%s</a> ", (hour <= 12 ? hour : hour - 12), (hour < 12 ? "am" : "pm")
1168 /* Display events with start and end times on this day */
1169 calendar_day_view_display_events(today_t, year, month, day, 0, daystart, dayend);
1172 wc_printf("</td>"); /* end of innermost table */
1174 /* Display extra events (start/end times not present or not today) in the middle column */
1175 wc_printf("<td class=\"extra_events\">");
1179 /* Display all-day events */
1180 calendar_day_view_display_events(today_t, year, month, day, 1, daystart, dayend);
1184 wc_printf("</td>"); /* end extra on the middle */
1186 wc_printf("<td width='20%%' align=\"center\" valign=top>"); /* begin stuff-on-the-right */
1188 /* Begin todays-date-with-left-and-right-arrows */
1189 wc_printf("<table border=\"0\" width=\"100%%\" " "cellspacing=\"0\" cellpadding=\"0\" bgcolor=\"#FFFFFF\">\n");
1193 wc_printf("<td align=\"center\">");
1194 wc_printf("<a href=\"readfwd?calview=day?year=%d?month=%d?day=%d\">", yesterday.year, yesterday.month, yesterday.day);
1195 wc_printf("<img alt=\"previous\" align=\"middle\" src=\"static/webcit_icons/essen/32x32/back.png\" border=\"0\"></a>");
1198 wc_strftime(d_str, sizeof d_str,
1199 "<td align=\"center\">"
1200 "<font size='+2'>%A</font><br>"
1201 "<font size='+2'>%B</font><br>" "<font size='+3'>%d</font><br>" "<font size='+2'>%Y</font><br>" "</td>", &d_tm);
1202 wc_printf("%s", d_str);
1205 wc_printf("<td align=\"center\">");
1206 wc_printf("<a href=\"readfwd?calview=day?year=%d?month=%d?day=%d\">", tomorrow.year, tomorrow.month, tomorrow.day);
1207 wc_printf("<img alt=\"%s\" align=\"middle\" src=\"static/webcit_icons/essen/32x32/forward.png\""
1208 " border=\"0\"></a>\n", _("next"));
1211 wc_printf("</tr></table>\n");
1212 /* End todays-date-with-left-and-right-arrows */
1214 /* Embed a mini month calendar in this space */
1215 wc_printf("<br>\n");
1216 embeddable_mini_calendar(year, month);
1218 wc_printf("</td></tr>"); /* end stuff-on-the-right */
1219 wc_printf("</table>\n"); /* end of inner table */
1224 * Display today's events. Returns the number of items displayed.
1226 int calendar_summary_view(void) {
1228 const char *HashKey;
1233 struct icaltimetype t;
1238 int all_day_event = 0;
1239 char timestring[SIZ];
1240 wcsession *WCC = WC;
1241 int num_displayed = 0;
1243 if (GetCount(WC->disp_cal_items) == 0) {
1248 localtime_r(&now, &today_tm);
1250 Pos = GetNewHashPos(WCC->disp_cal_items, 0);
1251 while (GetNextHashPos(WCC->disp_cal_items, Pos, &hklen, &HashKey, &vCal)) {
1252 Cal = (disp_cal *) vCal;
1253 p = icalcomponent_get_first_property(Cal->cal, ICAL_DTSTART_PROPERTY);
1255 t = icalproperty_get_dtstart(p);
1256 event_tt = icaltime_as_timet(t);
1263 fmt_time(timestring, SIZ, event_tt);
1265 if (all_day_event) {
1266 gmtime_r(&event_tt, &event_tm);
1269 localtime_r(&event_tt, &event_tm);
1272 if ((event_tm.tm_year == today_tm.tm_year)
1273 && (event_tm.tm_mon == today_tm.tm_mon)
1274 && (event_tm.tm_mday == today_tm.tm_mday)
1277 p = icalcomponent_get_first_property(Cal->cal, ICAL_SUMMARY_PROPERTY);
1279 p = icalproperty_new_summary(_("Untitled Task"));
1280 icalcomponent_add_property(Cal->cal, p);
1283 if (WCC->CurRoom.view == VIEW_TASKS) {
1284 wc_printf("<a href=\"display_edit_task"
1285 "?msgnum=%ld" "?return_to_summary=1" "?go=", Cal->cal_msgnum);
1286 escputs(ChrPtr(WCC->CurRoom.name));
1290 wc_printf("<a href=\"display_edit_event"
1298 today_tm.tm_year + 1900, today_tm.tm_mon + 1, today_tm.tm_mday);
1299 escputs(ChrPtr(WCC->CurRoom.name));
1302 escputs((char *) icalproperty_get_comment(p));
1303 if (!all_day_event) {
1304 wc_printf(" (%s)", timestring);
1306 wc_printf("</a><br>\n");
1312 DeleteHashPos(&Pos);
1313 DeleteHash(&WC->disp_cal_items);
1314 return (num_displayed);
1318 * Parse the URL variables in order to determine the scope and display of a calendar view
1320 int calendar_GetParamsGetServerCall(SharedMessageStatus * Stat,
1321 void **ViewSpecific, long oper, char *cmd, long len, char *filter, long flen) {
1322 wcsession *WCC = WC;
1330 c = (calview *) malloc(sizeof(calview));
1331 memset(c, 0, sizeof(calview));
1332 *ViewSpecific = (void *) c;
1334 Stat->load_seen = 1;
1335 strcpy(cmd, "MSGS ALL");
1336 Stat->maxmsgs = 32767;
1338 /* In case no date was specified, go with today */
1340 localtime_r(&now, &tm);
1341 c->year = tm.tm_year + 1900;
1342 c->month = tm.tm_mon + 1;
1343 c->day = tm.tm_mday;
1345 /* Now see if a date was specified */
1346 if (havebstr("year"))
1347 c->year = ibstr("year");
1348 if (havebstr("month"))
1349 c->month = ibstr("month");
1350 if (havebstr("day"))
1351 c->day = ibstr("day");
1353 /* How would you like that cooked? */
1354 if (havebstr("calview")) {
1355 strcpy(cv, bstr("calview"));
1358 strcpy(cv, "month");
1361 /* Display the selected view */
1362 if (!strcasecmp(cv, "day")) {
1363 c->view = calview_day;
1365 else if (!strcasecmp(cv, "week")) {
1366 c->view = calview_week;
1368 else if (!strcasecmp(cv, "summary")) { /* shouldn't ever happen, but just in case */
1369 c->view = calview_day;
1372 if (WCC->CurRoom.view == VIEW_CALBRIEF) {
1373 c->view = calview_brief;
1376 c->view = calview_month;
1380 /* Now try and set the lower and upper bounds so that we don't
1381 * burn too many cpu cycles parsing data way in the past or future
1384 tm.tm_year = c->year - 1900;
1385 tm.tm_mon = c->month - 1;
1386 tm.tm_mday = c->day;
1389 if (c->view == calview_month)
1391 if (c->view == calview_brief)
1393 if (c->view == calview_week)
1395 if (c->view == calview_day)
1397 if (c->view == calview_summary)
1400 c->lower_bound = now - span;
1401 c->upper_bound = now + span;
1408 * Render a calendar view from data previously loaded into memory
1410 int calendar_RenderView_or_Tail(SharedMessageStatus * Stat, void **ViewSpecific, long oper) {
1411 wcsession *WCC = WC;
1412 calview *c = (calview *) * ViewSpecific;
1414 if (c->view == calview_day) {
1415 calendar_day_view(c->year, c->month, c->day);
1417 else if (c->view == calview_week) {
1418 calendar_week_view(c->year, c->month, c->day);
1421 if (WCC->CurRoom.view == VIEW_CALBRIEF) {
1422 calendar_brief_month_view(c->year, c->month, c->day);
1425 calendar_month_view(c->year, c->month, c->day);
1429 /* Free the in-memory list of calendar items */
1430 DeleteHash(&WC->disp_cal_items);
1434 void InitModule_CALENDAR_VIEW(void) {
1435 WebcitAddUrlHandler(HKEY("mini_calendar"), "", 0, ajax_mini_calendar, AJAX);