38294f0013b10d9aa32450b98bea1e3b61272bde
[citadel.git] / webcit / calendar_view.c
1 // Handles the HTML display of calendar items.
2 //
3 // Copyright (c) 1996-2023 by the citadel.org team
4 //
5 // This program is open source software.  Use, duplication, or disclosure
6 // are subject to the terms of the GNU General Public License, version 3.
7
8 #include "webcit.h"
9 #include "webserver.h"
10 #include "calendar.h"
11
12 // These define how high the hour rows are in the day view
13 #define TIMELINE        22
14 #define EXTRATIMELINE   22
15
16 void embeddable_mini_calendar(int year, int month) {
17         struct tm starting_tm;
18         struct tm tm;
19         time_t thetime;
20         int i;
21         time_t previous_month;
22         time_t next_month;
23         time_t colheader_time;
24         struct tm colheader_tm;
25         char colheader_label[32];
26         long weekstart = 0;
27         char url[256];
28         char div_id[256];
29
30         snprintf(div_id, sizeof div_id, "mini_calendar_%d", rand() );
31
32         // Determine what day to start.  If an impossible value is found, start on Sunday.
33         get_pref_long("weekstart", &weekstart, 17);
34         if (weekstart > 6) weekstart = 0;
35
36         // Now back up to the 1st of the month...
37         memset(&starting_tm, 0, sizeof(struct tm));
38
39         starting_tm.tm_year = year - 1900;
40         starting_tm.tm_mon = month - 1;
41         starting_tm.tm_mday = 1;
42         thetime = mktime(&starting_tm);
43
44         memcpy(&tm, &starting_tm, sizeof(struct tm));
45         while (tm.tm_mday != 1) {
46                 thetime = thetime - (time_t)86400;      // go back 24 hours
47                 localtime_r(&thetime, &tm);
48         }
49
50         // Determine previous and next months ... for links
51         previous_month = thetime - (time_t)864000;      // back 10 days
52         next_month = thetime + (time_t)(31L * 86400);   // ahead 31 days
53
54         // Now back up until we're on the user's preferred start day
55         localtime_r(&thetime, &tm);
56         while (tm.tm_wday != weekstart) {
57                 thetime = thetime - (time_t)86400;      // go back 24 hours
58                 localtime_r(&thetime, &tm);
59         }
60
61         wc_printf("<div class=\"mini_calendar\" id=\"%s\">\n", div_id);
62
63         // Previous month link
64         localtime_r(&previous_month, &tm);
65         wc_printf("<a href=\"javascript:minical_change_month(%d,%d);\">&laquo;</a>", (int)(tm.tm_year)+1900, tm.tm_mon + 1);
66
67         wc_strftime(colheader_label, sizeof colheader_label, "%B", &starting_tm);
68         wc_printf("&nbsp;&nbsp;"
69                 "<span class=\"mini_calendar_month_label\">"
70                 "%s %d"
71                 "</span>"
72                 "&nbsp;&nbsp;", colheader_label, year);
73
74         // Next month link
75         localtime_r(&next_month, &tm);
76         wc_printf("<a href=\"javascript:minical_change_month(%d,%d);\">&raquo;</a>", (int)(tm.tm_year)+1900, tm.tm_mon + 1);
77
78         wc_printf("<table border=\"0\" cellpadding=\"1\" cellspacing=\"1\" class=\"mini_calendar_days\"><tr>");
79         colheader_time = thetime;
80         for (i=0; i<7; ++i) {
81                 colheader_time = thetime + (i * 86400) ;
82                 localtime_r(&colheader_time, &colheader_tm);
83                 wc_strftime(colheader_label, sizeof colheader_label, "%A", &colheader_tm);
84                 wc_printf("<th>%c</th>", colheader_label[0]);
85
86         }
87         wc_printf("</tr>\n");
88
89         // Now do 35 or 42 days
90         for (i = 0; i < 42; ++i) {
91                 localtime_r(&thetime, &tm);
92
93                 if (i < 35) {
94
95                         // Before displaying Sunday, start a new row
96                         if ((i % 7) == 0) {
97                                 wc_printf("<tr>");
98                         }
99
100                         if (tm.tm_mon == month-1) {
101                                 snprintf(url, sizeof url, "readfwd?calview=day?year=%d?month=%d?day=%d",
102                                         tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday);
103                                 wc_printf("<td><a href=\"%s\">%d</a></td>", url, tm.tm_mday);
104                         }
105                         else {
106                                 wc_printf("<td> </td>");
107                         }
108
109                         // After displaying one week, end the row
110                         if ((i % 7) == 6) {
111                                 wc_printf("</tr>\n");
112                         }
113
114                 }
115
116                 thetime += (time_t)86400;               // ahead 24 hours
117         }
118
119         wc_printf("</table>"                    // end of inner table
120                 "</div>\n");
121
122         StrBufAppendPrintf(WC->trailing_javascript,
123                 "       function minical_change_month(year, month) {                                    \n"
124                 "               p = 'year=' + year + '&month=' + month                                  \n"
125                 "                       + '&r=' + ctdlRandomString();                                   \n"
126                 "               new Ajax.Updater('%s', 'mini_calendar',                                 \n"
127                 "                       { method: 'get', parameters: p, evalScripts: true } );          \n"
128                 "       }                                                                               \n"
129                 "",
130                 div_id
131         );
132
133 }
134
135
136 // ajax embedder for the above mini calendar 
137 void ajax_mini_calendar(void) {
138         embeddable_mini_calendar( ibstr("year"), ibstr("month"));
139 }
140
141
142 // Display one day of a whole month view of a calendar
143 void calendar_month_view_display_events(int year, int month, int day) {
144         long hklen;
145         const char *HashKey;
146         void *vCal;
147         HashPos *Pos;
148         disp_cal *Cal;
149         icalproperty *p = NULL;
150         icalproperty *q = NULL;
151         struct icaltimetype t;
152         struct icaltimetype end_t;
153         struct icaltimetype today_start_t;
154         struct icaltimetype today_end_t;
155         struct icaltimetype today_t;
156         struct tm starting_tm;
157         struct tm ending_tm;
158         int all_day_event = 0;
159         int show_event = 0;
160         char buf[256];
161         time_t tt;
162
163         if (GetCount(WC->disp_cal_items) == 0) {
164                 wc_printf("<br>\n");
165                 return;
166         }
167
168         // Create an imaginary event which spans the 24 hours of today.  Any events which
169         // overlap with this one take place at least partially in this day.  We have to
170         // convert it from a struct tm in order to make it UTC.
171         memset(&starting_tm, 0, sizeof(struct tm));
172         starting_tm.tm_year = year - 1900;
173         starting_tm.tm_mon = month - 1;
174         starting_tm.tm_mday = day;
175         starting_tm.tm_hour = 0;
176         starting_tm.tm_min = 0;
177         today_start_t = icaltime_from_timet_with_zone(mktime(&starting_tm), 0, icaltimezone_get_utc_timezone());
178
179         memset(&ending_tm, 0, sizeof(struct tm));
180         ending_tm.tm_year = year - 1900;
181         ending_tm.tm_mon = month - 1;
182         ending_tm.tm_mday = day;
183         ending_tm.tm_hour = 23;
184         ending_tm.tm_min = 59;
185         today_end_t = icaltime_from_timet_with_zone(mktime(&ending_tm), 0, icaltimezone_get_utc_timezone());
186
187         // Create another one without caring about the timezone for all day events.
188         today_t = icaltime_null_date();
189         today_t.year = year;
190         today_t.month = month;
191         today_t.day = day;
192
193         // Now loop through our list of events to see which ones occur today.
194         Pos = GetNewHashPos(WC->disp_cal_items, 0);
195         while (GetNextHashPos(WC->disp_cal_items, Pos, &hklen, &HashKey, &vCal)) {
196                 Cal = (disp_cal*)vCal;
197                 all_day_event = 0;
198                 q = icalcomponent_get_first_property(Cal->cal, ICAL_DTSTART_PROPERTY);
199                 if (q != NULL) {
200                         t = icalproperty_get_dtstart(q);
201                 }
202                 else {
203                         memset(&t, 0, sizeof t);
204                 }
205                 q = icalcomponent_get_first_property(Cal->cal, ICAL_DTEND_PROPERTY);
206                 if (q != NULL) {
207                         end_t = icalproperty_get_dtend(q);
208                 }
209                 else {
210                         memset(&end_t, 0, sizeof end_t);
211                 }
212                 if (t.is_date) {
213                         all_day_event = 1;
214                 }
215
216                 if (all_day_event) {
217                         show_event = ical_ctdl_is_overlap(t, end_t, today_t, icaltime_null_time());
218                 }
219                 else {
220                         show_event = ical_ctdl_is_overlap(t, end_t, today_start_t, today_end_t);
221                 }
222
223                 // If we determined that this event occurs today, then display it.
224                 if (show_event) {
225
226                         //time_t logtt = icaltime_as_timet(t);
227                         //syslog(LOG_DEBUG, "Match on %04d-%02d-%02d for event %x%s on %s",
228                                 //year, month, day,
229                                 //(int)Cal, ((all_day_event) ? " (all day)" : ""),
230                                 //ctime(&logtt)
231                         //);
232
233                         p = icalcomponent_get_first_property(Cal->cal, ICAL_SUMMARY_PROPERTY);
234                         if (p == NULL) {
235                                 p = icalproperty_new_summary(_("Untitled Event"));
236                                 icalcomponent_add_property(Cal->cal, p);
237                         }
238                         if (p != NULL) {
239
240                                 if (all_day_event) {
241                                         wc_printf("<table border=\"0\" cellpadding=\"2\"><TR>"
242                                                 "<td bgcolor=\"#CCCCDD\">"
243                                                 );
244                                 }
245
246                                 wc_printf("<font size=\"-1\">"
247                                         "<a class=\"event%s\" href=\"display_edit_event?"
248                                         "msgnum=%ld?calview=month?year=%d?month=%d?day=%d\">"
249                                         ,
250                                         (Cal->unread)?"_unread":"_read",
251                                         Cal->cal_msgnum,
252                                         year, month, day
253                                 );
254
255                                 escputs((char *) icalproperty_get_comment(p));
256
257                                 wc_printf("<span class=\"tooltip\"><span class=\"btttop\"></span><span class=\"bttmiddle\">");
258
259                                 wc_printf("<i>%s: %s</i><br>", _("From"), Cal->from);
260                                 wc_printf("<i>%s</i> ",          _("Summary:"));
261                                 escputs((char *)icalproperty_get_comment(p));
262                                 wc_printf("<br>");
263
264                                 q = icalcomponent_get_first_property(
265                                         Cal->cal,
266                                         ICAL_LOCATION_PROPERTY);
267                                 if (q) {
268                                         wc_printf("<i>%s</i> ", _("Location:"));
269                                         escputs((char *)icalproperty_get_comment(q));
270                                         wc_printf("<br>");
271                                 }
272
273                                 // Only show start/end times if we're actually looking at the VEVENT
274                                 // component.  Otherwise it shows bogus dates for e.g. timezones
275                                 if (icalcomponent_isa(Cal->cal) == ICAL_VEVENT_COMPONENT) {
276
277                                         q = icalcomponent_get_first_property(Cal->cal, ICAL_DTSTART_PROPERTY);
278                                         if (q != NULL) {
279                                                 int no_end = 0;
280
281                                                 t = icalproperty_get_dtstart(q);
282                                                 q = icalcomponent_get_first_property(Cal->cal, ICAL_DTEND_PROPERTY);
283                                                 if (q != NULL) {
284                                                         end_t = icalproperty_get_dtend(q);
285                                                 }
286                                                 else {
287                                                         // events with starting date/time equal
288                                                         // ending date/time might get only
289                                                         // DTSTART but no DTEND
290                                                         no_end = 1;
291                                                 }
292
293                                                 if (t.is_date) {
294                                                         // all day event
295                                                         struct tm d_tm;
296
297                                                         if (!no_end) {
298                                                                 // end given, have to adjust it
299                                                                 icaltime_adjust(&end_t, -1, 0, 0, 0);
300                                                         }
301                                                         memset(&d_tm, 0, sizeof d_tm);
302                                                         d_tm.tm_year = t.year - 1900;
303                                                         d_tm.tm_mon = t.month - 1;
304                                                         d_tm.tm_mday = t.day;
305                                                         wc_strftime(buf, sizeof buf, "%x", &d_tm);
306
307                                                         if (no_end || !icaltime_compare(t, end_t)) {
308                                                                 wc_printf("<i>%s</i> %s<br>", _("Date:"), buf);
309                                                         }
310                                                         else {
311                                                                 wc_printf("<i>%s</i> %s<br>", _("Starting date:"), buf);
312                                                                 d_tm.tm_year = end_t.year - 1900;
313                                                                 d_tm.tm_mon = end_t.month - 1;
314                                                                 d_tm.tm_mday = end_t.day;
315                                                                 wc_strftime(buf, sizeof buf, "%x", &d_tm);
316                                                                 wc_printf("<i>%s</i> %s<br>", _("Ending date:"), buf);
317                                                         }
318                                                 }
319                                                 else {
320                                                         tt = icaltime_as_timet(t);
321                                                         webcit_fmt_date(buf, 256, tt, DATEFMT_BRIEF);
322                                                         if (no_end || !icaltime_compare(t, end_t)) {
323                                                                 wc_printf("<i>%s</i> %s<br>", _("Date/time:"), buf);
324                                                         }
325                                                         else {
326                                                                 wc_printf("<i>%s</i> %s<br>", _("Starting date/time:"), buf);
327                                                                 tt = icaltime_as_timet(end_t);
328                                                                 webcit_fmt_date(buf, 256, tt, DATEFMT_BRIEF);
329                                                                 wc_printf("<i>%s</i> %s<br>", _("Ending date/time:"), buf);
330                                                         }
331                                                 }
332                                         }
333                                 }
334
335                                 q = icalcomponent_get_first_property(Cal->cal, ICAL_DESCRIPTION_PROPERTY);
336                                 if (q) {
337                                         wc_printf("<i>%s</i> ", _("Notes:"));
338                                         escputs((char *)icalproperty_get_comment(q));
339                                         wc_printf("<br>");
340                                 }
341
342                                 wc_printf("</span><span class=\"bttbottom\"></span></span>");
343                                 wc_printf("</a></font><br>\n");
344
345                                 if (all_day_event) {
346                                         wc_printf("</td></tr></table>");
347                                 }
348                         }
349                 }
350         }
351         DeleteHashPos(&Pos);
352 }
353
354
355 // Display one day of a whole month view of a calendar
356 void calendar_month_view_brief_events(time_t thetime, const char *daycolor) {
357         long hklen;
358         const char *HashKey;
359         void *vCal;
360         HashPos *Pos;
361         time_t event_tt;
362         time_t event_tts;
363         time_t event_tte;
364         struct tm event_tms;
365         struct tm event_tme;
366         struct tm today_tm;
367         icalproperty *p;
368         icalproperty *e;
369         struct icaltimetype t;
370         disp_cal *Cal;
371         int all_day_event = 0;
372         char *timeformat;
373         int time_format;
374
375         time_format = get_time_format_cached ();
376
377         if (time_format == WC_TIMEFORMAT_24) timeformat="%k:%M";
378         else timeformat="%I:%M %p";
379
380         localtime_r(&thetime, &today_tm);
381
382         Pos = GetNewHashPos(WC->disp_cal_items, 0);
383         while (GetNextHashPos(WC->disp_cal_items, Pos, &hklen, &HashKey, &vCal)) {
384                 Cal = (disp_cal*)vCal;
385                 p = icalcomponent_get_first_property(Cal->cal, ICAL_DTSTART_PROPERTY);
386                 if (p != NULL) {
387                         t = icalproperty_get_dtstart(p);
388                         event_tt = icaltime_as_timet(t);
389                         event_tts=event_tt;
390                         if (t.is_date) all_day_event = 1;
391                         else all_day_event = 0;
392
393                         if (all_day_event) {
394                                 gmtime_r(&event_tts, &event_tms);
395                         }
396                         else {
397                                 localtime_r(&event_tts, &event_tms);
398                         }
399                         // todo: epoch &! daymask
400                         if (    (event_tms.tm_year == today_tm.tm_year)
401                                 && (event_tms.tm_mon == today_tm.tm_mon)
402                                 && (event_tms.tm_mday == today_tm.tm_mday))
403                         {
404
405                                 char sbuf[255];
406                                 char ebuf[255];
407         
408                                 p = icalcomponent_get_first_property(Cal->cal, ICAL_SUMMARY_PROPERTY);
409                                 if (p == NULL) {
410                                         p = icalproperty_new_summary(_("Untitled Event"));
411                                         icalcomponent_add_property(Cal->cal, p);
412                                 }
413                                 e = icalcomponent_get_first_property( Cal->cal, ICAL_DTEND_PROPERTY);
414                                 if ((p != NULL) && (e != NULL)) {
415                                         time_t difftime;
416                                         int hours, minutes;
417                                         t = icalproperty_get_dtend(e);
418                                         event_tte = icaltime_as_timet(t);
419                                         localtime_r(&event_tte, &event_tme);
420                                         difftime=(event_tte-event_tts)/60;
421                                         hours=(int)(difftime / 60);
422                                         minutes=difftime % 60;
423                                         wc_printf("<tr><td bgcolor='%s'>%i:%2i</td><td bgcolor='%s'>"
424                                                 "<font size=\"-1\">"
425                                                 "<a class=\"event%s\" href=\"display_edit_event?msgnum=%ld?calview=calbrief?year=%s?month=%s?day=%s\">",
426                                                 daycolor,
427                                                 hours, minutes,
428                                                 (Cal->unread)?"_unread":"_read",
429                                                 daycolor,
430                                                 Cal->cal_msgnum,
431                                                 bstr("year"),
432                                                 bstr("month"),
433                                                 bstr("day")
434                                         );
435         
436                                         escputs((char *) icalproperty_get_comment(p));
437                                         // todo: also ammitime format
438                                         wc_strftime(&sbuf[0], sizeof(sbuf), timeformat, &event_tms);
439                                         wc_strftime(&ebuf[0], sizeof(sbuf), timeformat, &event_tme);
440         
441                                         wc_printf("</a></font></td>"
442                                                 "<td bgcolor='%s'>%s</td><td bgcolor='%s'>%s</td></tr>",
443                                                 daycolor,
444                                                 sbuf,
445                                                 daycolor,
446                                                 ebuf
447                                         );
448                                 }
449                         }
450                 }
451         }
452         DeleteHashPos(&Pos);
453 }
454
455
456 // view one month. pretty view
457 void calendar_month_view(int year, int month, int day) {
458         struct tm starting_tm;
459         struct tm tm;
460         struct tm today_tm;
461         time_t thetime;
462         int i;
463         time_t previous_month;
464         time_t next_month;
465         time_t colheader_time;
466         time_t today_timet;
467         struct tm colheader_tm;
468         char colheader_label[32];
469         long weekstart = 0;
470
471         // Make sure we know which day is today.
472         today_timet = time(NULL);
473         localtime_r(&today_timet, &today_tm);
474
475         // Determine what day to start.  If an impossible value is found, start on Sunday.
476         get_pref_long("weekstart", &weekstart, 17);
477         if (weekstart > 6) weekstart = 0;
478
479         // Now back up to the 1st of the month...
480         memset(&starting_tm, 0, sizeof(struct tm));
481
482         starting_tm.tm_year = year - 1900;
483         starting_tm.tm_mon = month - 1;
484         starting_tm.tm_mday = day;
485         thetime = mktime(&starting_tm);
486
487         memcpy(&tm, &starting_tm, sizeof(struct tm));
488         while (tm.tm_mday != 1) {
489                 thetime = thetime - (time_t)86400;      // go back 24 hours
490                 localtime_r(&thetime, &tm);
491         }
492
493         // Determine previous and next months ... for links
494         previous_month = thetime - (time_t)864000;      // back 10 days
495         next_month = thetime + (time_t)(31L * 86400);   // ahead 31 days
496
497         // Now back up until we're on the user's preferred start day
498         localtime_r(&thetime, &tm);
499         while (tm.tm_wday != weekstart) {
500                 thetime = thetime - (time_t)86400;      // go back 24 hours
501                 localtime_r(&thetime, &tm);
502         }
503
504         // Outer table (to get the background color)
505         wc_printf("<table class=\"calendar\"> \n <tr><td>"); 
506
507         wc_printf("<table width=\"100%%\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\"><tr>\n");
508
509         wc_printf("<td align=\"center\">");
510
511         localtime_r(&previous_month, &tm);
512         wc_printf("<a href=\"readfwd?calview=month?year=%d?month=%d?day=1\">",
513                 (int)(tm.tm_year)+1900, tm.tm_mon + 1);
514         wc_printf("<img alt=\"%s\" align=\"middle\" src=\"static/webcit_icons/essen/32x32/back.png\" border=\"0\"></a>\n", _("previous"));
515
516         wc_strftime(colheader_label, sizeof colheader_label, "%B", &starting_tm);
517         wc_printf("&nbsp;&nbsp;"
518                 "<font size=\"+1\" color=\"#FFFFFF\">"
519                 "%s %d"
520                 "</font>"
521                 "&nbsp;&nbsp;", colheader_label, year);
522
523         localtime_r(&next_month, &tm);
524         wc_printf("<a href=\"readfwd?calview=month?year=%d?month=%d?day=1\">",
525                 (int)(tm.tm_year)+1900, tm.tm_mon + 1);
526         wc_printf("<img alt=\"%s\" align=\"middle\" src=\"static/webcit_icons/essen/32x32/forward.png\" border=\"0\"></A>\n", _("next"));
527
528         wc_printf("</td></tr></table>\n");
529
530         // Inner table (the real one)
531         wc_printf("<table width=\"100%%\" border=\"0\" cellpadding=\"1\" cellspacing=\"1\" "
532                 "bgcolor='#204B78' id=\"inner_month\"><tr>");
533         wc_printf("<th align=\"center\" width=\"2%%\"></th>");
534         colheader_time = thetime;
535         for (i=0; i<7; ++i) {
536                 colheader_time = thetime + (i * 86400) ;
537                 localtime_r(&colheader_time, &colheader_tm);
538                 wc_strftime(colheader_label, sizeof colheader_label, "%A", &colheader_tm);
539                 wc_printf("<th align=\"center\" width=\"14%%\">"
540                         "<font color=\"#FFFFFF\">%s</font></th>", colheader_label);
541
542         }
543         wc_printf("</tr>\n");
544
545         // Now do 35 or 42 days
546         localtime_r(&thetime, &tm);
547         for (i = 0; i<42; ++i) {
548
549                 // Before displaying the first day of the week, start a new row
550                 if ((i % 7) == 0) {
551                         wc_printf("<tr><td class=\"week_of_year\">");
552                         wc_strftime(colheader_label, sizeof colheader_label, "%V", &tm);
553                         wc_printf("%s ", colheader_label);
554                 }
555
556                 wc_printf("<td class=\"cal%s\"><div class=\"day\">",
557                         ((tm.tm_mon != month-1) ? "out" :
558                                 (((tm.tm_year == today_tm.tm_year) && (tm.tm_mon == today_tm.tm_mon) && (tm.tm_mday == today_tm.tm_mday)) ? "today" :
559                                 ((tm.tm_wday==0 || tm.tm_wday==6) ? "weekend" :
560                                         "day")))
561                         );
562                 if ((i==0) || (tm.tm_mday == 1)) {
563                         wc_strftime(colheader_label, sizeof colheader_label, "%B", &tm);
564                         wc_printf("%s ", colheader_label);
565                 }
566                 wc_printf("<a href=\"readfwd?calview=day?year=%d?month=%d?day=%d\">"
567                         "%d</a></div>",
568                         tm.tm_year + 1900,
569                         tm.tm_mon + 1,
570                         tm.tm_mday,
571                         tm.tm_mday);
572
573                 // put the data here, stupid
574                 calendar_month_view_display_events(
575                         tm.tm_year + 1900,
576                         tm.tm_mon + 1,
577                         tm.tm_mday
578                         );
579
580                 wc_printf("</td>");
581
582                 // After displaying the last day of the week, end the row
583                 if ((i % 7) == 6) {
584                         wc_printf("</tr>\n");
585                 }
586
587                 thetime += (time_t)86400;               // ahead 24 hours
588                 localtime_r(&thetime, &tm);
589
590                 if ( ((i % 7) == 6) && (tm.tm_mon != month-1) && (tm.tm_mday < 15) ) {
591                         i = 100;        // break out of the loop
592                 }
593         }
594
595         wc_printf("</table>"                    // end of inner table
596                 "</td></tr></table>\n"          // end of outer table
597         );
598 }
599
600
601 // view one month. brief view
602 void calendar_brief_month_view(int year, int month, int day) {
603         struct tm starting_tm;
604         struct tm tm;
605         time_t thetime;
606         int i;
607         time_t previous_month;
608         time_t next_month;
609         char month_label[32];
610
611         // Determine what day to start.
612         // First, back up to the 1st of the month...
613         memset(&starting_tm, 0, sizeof(struct tm));
614         starting_tm.tm_year = year - 1900;
615         starting_tm.tm_mon = month - 1;
616         starting_tm.tm_mday = day;
617         thetime = mktime(&starting_tm);
618
619         memcpy(&tm, &starting_tm, sizeof(struct tm));
620         while (tm.tm_mday != 1) {
621                 thetime = thetime - (time_t)86400;      // go back 24 hours
622                 localtime_r(&thetime, &tm);
623         }
624
625         // Determine previous and next months ... for links
626         previous_month = thetime - (time_t)864000;      // back 10 days
627         next_month = thetime + (time_t)(31L * 86400);   // ahead 31 days
628
629         // Now back up until we're on a Sunday
630         localtime_r(&thetime, &tm);
631         while (tm.tm_wday != 0) {
632                 thetime = thetime - (time_t)86400;      // go back 24 hours
633                 localtime_r(&thetime, &tm);
634         }
635
636         // Outer table (to get the background color)
637         wc_printf("<table width=\"100%%\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" "
638                 "bgcolor=#204B78><tr><td>\n");
639
640         wc_printf("<table width=\"100%%\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\"><tr>\n");
641
642         wc_printf("<td align=\"center\">");
643
644         localtime_r(&previous_month, &tm);
645         wc_printf("<a href=\"readfwd?calview=month?year=%d?month=%d?day=1\">",
646                 (int)(tm.tm_year)+1900, tm.tm_mon + 1);
647         wc_printf("<img alt=\"%s\" align=\"middle\" src=\"static/webcit_icons/essen/32x32/back.png\" border=\"0\"></a>\n", _("previous"));
648
649         wc_strftime(month_label, sizeof month_label, "%B", &tm);
650         wc_printf("&nbsp;&nbsp;"
651                 "<font size=\"+1\" color=\"#FFFFFF\">"
652                 "%s %d"
653                 "</font>"
654                 "&nbsp;&nbsp;", month_label, year);
655
656         localtime_r(&next_month, &tm);
657         wc_printf("<a href=\"readfwd?calview=month?year=%d?month=%d?day=1\">",
658                 (int)(tm.tm_year)+1900, tm.tm_mon + 1);
659         wc_printf("<img alt=\"%s\" align=\"middle\" src=\"static/webcit_icons/essen/32x32/forward.png\" border=\"0\"></a>\n", _("next"));
660
661         wc_printf("</td></tr></table>\n");
662
663         // Inner table (the real one)
664         wc_printf("<table width=\"100%%\" border=\"0\" cellpadding=\"1\" cellspacing=\"1\" "
665                 "bgcolor=#EEEECC><TR>");
666         wc_printf("</tr>\n");
667         wc_printf("<tr><td colspan=\"100%%\">\n");
668
669         // Now do 35 days
670         for (i = 0; i < 35; ++i) {
671                 char weeknumber[255];
672                 char weekday_name[32];
673                 char *daycolor;
674                 localtime_r(&thetime, &tm);
675
676
677                 // Before displaying Sunday, start a new CELL
678                 if ((i % 7) == 0) {
679                         wc_strftime(&weeknumber[0], sizeof(weeknumber), "%U", &tm);
680                         wc_printf("<table border='0' bgcolor=\"#EEEECC\" width='100%%'> <tr><th colspan='4'>%s %s</th></tr>"
681                                 "   <tr><td>%s</td><td width='70%%'>%s</td><td>%s</td><td>%s</td></tr>\n",
682                                 _("Week"),
683                                 weeknumber,
684                                 _("Hours"),
685                                 _("Subject"),
686                                 _("Start"),
687                                 _("End")
688                                 );
689                 }
690
691                 daycolor=((tm.tm_mon != month-1) ? "DDDDDD" :
692                         ((tm.tm_wday==0 || tm.tm_wday==6) ? "EEEECC" :
693                                 "FFFFFF"));
694
695                 // Day Header
696                 wc_strftime(weekday_name, sizeof weekday_name, "%A", &tm);
697                 wc_printf("<tr><td bgcolor='%s' colspan='1' align='left'> %s,%i."
698                         "</td><td bgcolor='%s' colspan='3'><hr></td></tr>\n",
699                         daycolor,
700                         weekday_name,tm.tm_mday,
701                         daycolor);
702
703                 // put the data of one day here, stupid
704                 calendar_month_view_brief_events(thetime, daycolor);
705
706                 // After displaying Saturday, end the row
707                 if ((i % 7) == 6) {
708                         wc_printf("</td></tr></table>\n");
709                 }
710
711                 thetime += (time_t)86400;               // ahead 24 hours
712         }
713
714         wc_printf("</table>"                    // end of inner table
715                 "</td></tr></table>\n"          // end of outer table
716         );
717 }
718
719
720 // Calendar week view -- not implemented yet, this is a stub function
721 void calendar_week_view(int year, int month, int day) {
722         wc_printf("<center><i>week view FIXME</i></center><br>\n");
723 }
724
725
726 // display one day
727 // Display events for a particular hour of a particular day.
728 // (Specify hour < 0 to show "all day" events)
729 //
730 // dstart and dend indicate which hours our "daytime" begins and end
731 void calendar_day_view_display_events(time_t thetime, int year, int month, int day, int notime_events, int dstart, int dend) {
732
733         long hklen;
734         const char *HashKey;
735         void *vCal;
736         HashPos *Pos;
737         icalproperty *p = NULL;
738         icalproperty *q = NULL;
739         time_t event_tt;
740         time_t event_tte;
741         struct tm event_te;
742         struct tm event_tm;
743         int show_event = 0;
744         int all_day_event = 0;
745         int ongoing_event = 0;
746         disp_cal *Cal;
747         struct icaltimetype t;
748         struct icaltimetype end_t;
749         struct icaltimetype today_start_t;
750         struct icaltimetype today_end_t;
751         struct icaltimetype today_t;
752         struct tm starting_tm;
753         struct tm ending_tm;
754         int top = 0;
755         int bottom = 0;
756         int gap = 1;
757         int startmin = 0;
758         int diffmin = 0;
759         int endmin = 0;
760
761         char buf[256];
762
763         if (GetCount(WC->disp_cal_items) == 0) {
764                 // nothing to display
765                 return;
766         }
767
768         // Create an imaginary event which spans the current day.  Any events which
769         // overlap with this one take place at least partially in this day.
770         memset(&starting_tm, 0, sizeof(struct tm));
771         starting_tm.tm_year = year - 1900;
772         starting_tm.tm_mon = month - 1;
773         starting_tm.tm_mday = day;
774         starting_tm.tm_hour = 0;
775         starting_tm.tm_min = 0;
776         today_start_t = icaltime_from_timet_with_zone(mktime(&starting_tm), 0, icaltimezone_get_utc_timezone());
777
778         memset(&ending_tm, 0, sizeof(struct tm));
779         ending_tm.tm_year = year - 1900;
780         ending_tm.tm_mon = month - 1;
781         ending_tm.tm_mday = day;
782         ending_tm.tm_hour = 23;
783         ending_tm.tm_min = 59;
784         today_end_t = icaltime_from_timet_with_zone(mktime(&ending_tm), 0, icaltimezone_get_utc_timezone());
785
786         // Create another one without caring about the timezone for all day events.
787         today_t = icaltime_null_date();
788         today_t.year = year;
789         today_t.month = month;
790         today_t.day = day;
791
792         // Now loop through our list of events to see which ones occur today.
793         Pos = GetNewHashPos(WC->disp_cal_items, 0);
794         while (GetNextHashPos(WC->disp_cal_items, Pos, &hklen, &HashKey, &vCal)) {
795                 Cal = (disp_cal*)vCal;
796
797                 all_day_event = 0;
798                 ongoing_event=0;
799
800                 q = icalcomponent_get_first_property(Cal->cal, ICAL_DTSTART_PROPERTY);
801                 if (q != NULL) {
802                         t = icalproperty_get_dtstart(q);
803                         event_tt = icaltime_as_timet(t);
804                         localtime_r(&event_tt, &event_te);
805                 }
806                 else {
807                         memset(&t, 0, sizeof t);
808                 }
809
810                 if (t.is_date) all_day_event = 1;
811
812                 q = icalcomponent_get_first_property(Cal->cal, ICAL_DTEND_PROPERTY);
813                 if (q != NULL) {
814                         end_t = icalproperty_get_dtend(q);
815                 }
816                 else {
817                         // no end given means end = start
818                         memcpy(&end_t, &t, sizeof(struct icaltimetype));
819                 }
820
821                 if (all_day_event) {
822                         show_event = ical_ctdl_is_overlap(t, end_t, today_t, icaltime_null_time());
823                         if (icaltime_compare(t, end_t)) {
824                                 // the end date is non-inclusive so adjust it by one day because our test is inclusive,
825                                 // note that a day is not too much because we are talking about all day events
826                                 icaltime_adjust(&end_t, -1, 0, 0, 0);
827                         }
828                 }
829                 else {
830                         show_event = ical_ctdl_is_overlap(t, end_t, today_start_t, today_end_t);
831                 }
832
833                 event_tte = icaltime_as_timet(end_t);
834                 localtime_r(&event_tte, &event_tm);
835
836                 // If we determined that this event occurs today, then display it.
837                 p = icalcomponent_get_first_property(Cal->cal,ICAL_SUMMARY_PROPERTY);
838                 if (p == NULL) {
839                         p = icalproperty_new_summary(_("Untitled Event"));
840                         icalcomponent_add_property(Cal->cal, p);
841                 }
842
843                 if ((show_event) && (p != NULL)) {
844
845                         if ((event_te.tm_mday != day) || (event_tm.tm_mday != day)) ongoing_event = 1; 
846
847                         if (all_day_event && notime_events) {
848                                 wc_printf("<li class=\"event_framed%s\"> "
849                                         "<a href=\"display_edit_event?"
850                                         "msgnum=%ld?calview=day?year=%d?month=%d?day=%d\" "
851                                         " class=\"event_title\">"
852                                         ,
853                                         (Cal->unread)?"_unread":"_read",
854                                         Cal->cal_msgnum, year, month, day
855                                 );
856                                 escputs((char *) icalproperty_get_comment(p));
857                                 wc_printf("<span class=\"tooltip\"><span class=\"btttop\"></span><span class=\"bttmiddle\">");
858                                 wc_printf("<i>%s</i><br>",      _("All day event"));
859                                 wc_printf("<i>%s: %s</i><br>",  _("From"), Cal->from);
860                                 wc_printf("<i>%s</i> ",           _("Summary:"));
861                                 escputs((char *) icalproperty_get_comment(p));
862                                 wc_printf("<br>");
863                                 q = icalcomponent_get_first_property(Cal->cal,ICAL_LOCATION_PROPERTY);
864                                 if (q) {
865                                         wc_printf("<i>%s</i> ", _("Location:"));
866                                         escputs((char *)icalproperty_get_comment(q));
867                                         wc_printf("<br>");
868                                 }
869                                 if (!icaltime_compare(t, end_t)) { // one day only
870                                         webcit_fmt_date(buf, 256, event_tt, DATEFMT_LOCALEDATE);
871                                         wc_printf("<i>%s</i> %s<br>", _("Date:"), buf);
872                                 }
873                                 else {
874                                         webcit_fmt_date(buf, 256, event_tt, DATEFMT_LOCALEDATE);
875                                         wc_printf("<i>%s</i> %s<br>", _("Starting date:"), buf);
876                                         webcit_fmt_date(buf, 256, event_tte, DATEFMT_LOCALEDATE);
877                                         wc_printf("<i>%s</i> %s<br>", _("Ending date:"), buf);
878                                 }
879                                 q = icalcomponent_get_first_property(Cal->cal,ICAL_DESCRIPTION_PROPERTY);
880                                 if (q) {
881                                         wc_printf("<i>%s</i> ", _("Notes:"));
882                                         escputs((char *)icalproperty_get_comment(q));
883                                         wc_printf("<br>");
884                                 }
885                                 wc_printf("</span><span class=\"bttbottom\"></span></span>");
886                                 wc_printf("</a> <span>(");
887                                 wc_printf(_("All day event"));
888                                 wc_printf(")</span></li>\n");
889                         }
890
891                         else if (ongoing_event && notime_events) {
892
893                                 wc_printf("<li class=\"event_framed%s\"> "
894                                         "<a href=\"display_edit_event?"
895                                         "msgnum=%ld&calview=day?year=%d?month=%d?day=%d\" "
896                                         " class=\"event_title\">" 
897                                         ,
898                                         (Cal->unread)?"_unread":"_read",
899                                         Cal->cal_msgnum, year, month, day
900                                 );
901                                 escputs((char *) icalproperty_get_comment(p));
902                                 wc_printf("<span class=\"tooltip\"><span class=\"btttop\"></span><span class=\"bttmiddle\">");
903                                 wc_printf("<i>%s</i><br>",     _("Ongoing event"));
904                                 wc_printf("<i>%s: %s</i><br>", _("From"), Cal->from);
905                                 wc_printf("<i>%s</i> ",          _("Summary:"));
906                                 escputs((char *) icalproperty_get_comment(p));
907                                 wc_printf("<br>");
908                                 q = icalcomponent_get_first_property(Cal->cal,ICAL_LOCATION_PROPERTY);
909                                 if (q) {
910                                         wc_printf("<i>%s</i> ", _("Location:"));
911                                         escputs((char *)icalproperty_get_comment(q));
912                                         wc_printf("<br>");
913                                 }
914                                 webcit_fmt_date(buf, 256, event_tt, DATEFMT_BRIEF);
915                                 wc_printf("<i>%s</i> %s<br>", _("Starting date/time:"), buf);
916                                 webcit_fmt_date(buf, 256, event_tte, DATEFMT_BRIEF);
917                                 wc_printf("<i>%s</i> %s<br>", _("Ending date/time:"), buf);
918                                 q = icalcomponent_get_first_property(Cal->cal,ICAL_DESCRIPTION_PROPERTY);
919                                 if (q) {
920                                         wc_printf("<i>%s</i> ", _("Notes:"));
921                                         escputs((char *)icalproperty_get_comment(q));
922                                         wc_printf("<br>");
923                                 }
924                                 wc_printf("</span><span class=\"bttbottom\"></span></span>");
925                                 wc_printf("</a> <span>(");
926                                 wc_printf(_("Ongoing event"));
927                                 wc_printf(")</span></li>\n");
928                         }
929
930                         else if (!all_day_event && !notime_events) {
931                                 gap++;
932
933                                 if (event_te.tm_mday != day) event_te.tm_hour = 0;
934                                 if (event_tm.tm_mday != day) event_tm.tm_hour = 24;
935
936                                 // Calculate the location of the top of the box
937                                 if (event_te.tm_hour < dstart) {
938                                         startmin = diffmin = event_te.tm_min / 6;
939                                         top = (event_te.tm_hour * EXTRATIMELINE) + startmin;
940                                 }
941                                 else if ((event_te.tm_hour >= dstart) && (event_te.tm_hour <= dend)) {
942                                         startmin = diffmin = (event_te.tm_min / 2);
943                                         top = (dstart * EXTRATIMELINE) + ((event_te.tm_hour - dstart) * TIMELINE) + startmin;
944                                 }
945                                 else if (event_te.tm_hour >dend) {
946                                         startmin = diffmin = event_te.tm_min / 6;
947                                         top = (dstart * EXTRATIMELINE) + ((dend - dstart - 1) * TIMELINE) + ((event_tm.tm_hour - dend + 1) * EXTRATIMELINE) + startmin ;
948                                 }
949                                 else {
950                                         // should never get here
951                                 }
952
953                                 // Calculate the location of the bottom of the box
954                                 if (event_tm.tm_hour < dstart) {
955                                         endmin = diffmin = event_tm.tm_min / 6;
956                                         bottom = (event_tm.tm_hour * EXTRATIMELINE) + endmin;
957                                 }
958                                 else if ((event_tm.tm_hour >= dstart) && (event_tm.tm_hour <= dend)) {
959                                         endmin = diffmin = (event_tm.tm_min / 2);
960                                         bottom = (dstart * EXTRATIMELINE) + ((event_tm.tm_hour - dstart) * TIMELINE) + endmin ;
961                                 }
962                                 else if (event_tm.tm_hour >dend) {
963                                         endmin = diffmin = event_tm.tm_min / 6;
964                                         bottom = (dstart * EXTRATIMELINE) + ((dend - dstart + 1) * TIMELINE) + ((event_tm.tm_hour - dend - 1) * EXTRATIMELINE) + endmin;
965                                 }
966                                 else {
967                                         // should never get here
968                                 }
969
970                                 wc_printf("<dd  class=\"event_framed%s\" "
971                                         "style=\"position: absolute; "
972                                         "top:%dpx; left:%dpx; "
973                                         "height:%dpx; \" >",
974                                         (Cal->unread)?"_unread":"_read",
975                                         top, (gap * 40), (bottom-top)
976                                         );
977                                 wc_printf("<a href=\"display_edit_event?"
978                                         "msgnum=%ld?calview=day?year=%d?month=%d?day=%d?hour=%d\" "
979                                         "class=\"event_title\">"
980                                         ,
981                                         Cal->cal_msgnum, year, month, day, t.hour
982                                 );
983                                 escputs((char *) icalproperty_get_comment(p));
984                                 wc_printf("<span class=\"tooltip\"><span class=\"btttop\"></span><span class=\"bttmiddle\">");
985                                 wc_printf("<i>%s: %s</i><br>", _("From"), Cal->from);
986                                 wc_printf("<i>%s</i> ",          _("Summary:"));
987                                 escputs((char *) icalproperty_get_comment(p));
988                                 wc_printf("<br>");
989                                 q = icalcomponent_get_first_property(Cal->cal,ICAL_LOCATION_PROPERTY);
990                                 if (q) {
991                                         wc_printf("<i>%s</i> ", _("Location:"));
992                                         escputs((char *)icalproperty_get_comment(q));
993                                         wc_printf("<br>");
994                                                                 }
995                                 if (!icaltime_compare(t, end_t)) { // one day only
996                                         webcit_fmt_date(buf, 256, event_tt, DATEFMT_BRIEF);
997                                         wc_printf("<i>%s</i> %s<br>", _("Date/time:"), buf);
998                                 }
999                                 else {
1000                                         webcit_fmt_date(buf, 256, event_tt, DATEFMT_BRIEF);
1001                                         wc_printf("<i>%s</i> %s<br>", _("Starting date/time:"), buf);
1002                                         webcit_fmt_date(buf, 256, event_tte, DATEFMT_BRIEF);
1003                                         wc_printf("<i>%s</i> %s<br>", _("Ending date/time:"), buf);
1004                                 }
1005                                 q = icalcomponent_get_first_property(Cal->cal,ICAL_DESCRIPTION_PROPERTY);
1006                                 if (q) {
1007                                         wc_printf("<i>%s</i> ", _("Notes:"));
1008                                         escputs((char *)icalproperty_get_comment(q));
1009                                         wc_printf("<br>");
1010                                 }
1011                                 wc_printf("</span><span class=\"bttbottom\"></span></span>");
1012                                 wc_printf("</a></dd>\n");
1013                         }
1014                 }
1015         }
1016         DeleteHashPos(&Pos);
1017 }
1018
1019
1020 // view one day
1021 void calendar_day_view(int year, int month, int day) {
1022         int hour;
1023         struct icaltimetype today, yesterday, tomorrow;
1024         long daystart;
1025         long dayend;
1026         struct tm d_tm;
1027         char d_str[160];
1028         int time_format;
1029         time_t today_t;
1030         int timeline = TIMELINE;
1031         int extratimeline = EXTRATIMELINE;
1032         int gap = 0;
1033         int hourlabel;
1034         int extrahourlabel;
1035
1036         time_format = get_time_format_cached ();
1037         get_pref_long("daystart", &daystart, 8);
1038         get_pref_long("dayend", &dayend, 17);
1039
1040         // when loading daystart/dayend, replace missing, corrupt, or impossible values with defaults
1041         if ((daystart < 0) || (dayend < 2) || (daystart >= 23) || (dayend > 23) || (dayend <= daystart)) {
1042                 daystart = 9;
1043                 dayend = 17;
1044         }
1045
1046         // Today's date
1047         memset(&d_tm, 0, sizeof d_tm);
1048         d_tm.tm_year = year - 1900;
1049         d_tm.tm_mon = month - 1;
1050         d_tm.tm_mday = day;
1051         today_t = mktime(&d_tm); 
1052
1053         // Figure out the dates for "yesterday" and "tomorrow" links
1054
1055         memset(&today, 0, sizeof(struct icaltimetype));
1056         today.year = year;
1057         today.month = month;
1058         today.day = day;
1059         today.is_date = 1;
1060
1061         memcpy(&yesterday, &today, sizeof(struct icaltimetype));
1062         --yesterday.day;
1063         yesterday = icaltime_normalize(yesterday);
1064
1065         memcpy(&tomorrow, &today, sizeof(struct icaltimetype));
1066         ++tomorrow.day;
1067         tomorrow = icaltime_normalize(tomorrow);
1068
1069         // Inner table (the real one)
1070         wc_printf("<table class=\"calendar\" id=\"inner_day\"><tr> \n");
1071
1072         // Innermost cell (contains hours etc.)
1073         wc_printf("<td class=\"events_of_the_day\" >");
1074         wc_printf("<dl class=\"events\" >");
1075
1076         // Now the middle of the day...
1077
1078         extrahourlabel = extratimeline - 2;
1079         hourlabel = extrahourlabel * 150 / 100;
1080         if (hourlabel > (timeline - 2)) hourlabel = timeline - 2;
1081
1082         for (hour = 0; hour < daystart; ++hour) {       // could do HEIGHT=xx
1083                 wc_printf("<dt class=\"extrahour\">"
1084                         "<a href=\"display_edit_event?msgnum=0"
1085                         "?calview=day?year=%d?month=%d?day=%d?hour=%d?minute=0\">",
1086                         year, month, day, hour
1087                 );
1088
1089                 if (time_format == WC_TIMEFORMAT_24) {
1090                         wc_printf("%2d:00</a> ", hour);
1091                 }
1092                 else {
1093                         wc_printf("%d:00%s</a> ",
1094                                 ((hour == 0) ? 12 : (hour <= 12 ? hour : hour-12)),
1095                                 (hour < 12 ? "am" : "pm")
1096                         );
1097                 }
1098
1099                 wc_printf("</dt>");
1100         }
1101
1102         gap = daystart * extratimeline;
1103
1104         for (hour = daystart; hour <= dayend; ++hour) {       // could do HEIGHT=xx
1105                 wc_printf("<dt class=\"hour\">"
1106                         "<a href=\"display_edit_event?msgnum=0?calview=day"
1107                         "?year=%d?month=%d?day=%d?hour=%d?minute=0\">",
1108                         year, month, day, hour
1109                 );
1110
1111                 if (time_format == WC_TIMEFORMAT_24) {
1112                         wc_printf("%2d:00</a> ", hour);
1113                 }
1114                 else {
1115                         wc_printf("%d:00%s</a> ",
1116                                 (hour <= 12 ? hour : hour-12),
1117                                 (hour < 12 ? "am" : "pm")
1118                         );
1119                 }
1120
1121                 wc_printf("</dt>");
1122         }
1123
1124         gap = gap + ((dayend - daystart + 1) * timeline);
1125
1126         for (hour = (dayend + 1); hour < 24; ++hour) {       // could do HEIGHT=xx
1127                 wc_printf("<dt class=\"extrahour\">"
1128                         "<a href=\"display_edit_event?msgnum=0?calview=day"
1129                         "?year=%d?month=%d?day=%d?hour=%d?minute=0\">",
1130                         year, month, day, hour
1131                 );
1132
1133                 if (time_format == WC_TIMEFORMAT_24) {
1134                         wc_printf("%2d:00</a> ", hour);
1135                 }
1136                 else {
1137                         wc_printf("%d:00%s</a> ",
1138                                 (hour <= 12 ? hour : hour-12),
1139                                 (hour < 12 ? "am" : "pm")
1140                         );
1141                 }
1142
1143                 wc_printf("</dt>");
1144         }
1145
1146         // Display events with start and end times on this day
1147         calendar_day_view_display_events(today_t, year, month, day, 0, daystart, dayend);
1148
1149         wc_printf("</dl>");
1150         wc_printf("</td>");                     // end of innermost table
1151
1152         // Display extra events (start/end times not present or not today) in the middle column
1153         wc_printf("<td class=\"extra_events\">");
1154
1155         wc_printf("<ul>");
1156
1157         // Display all-day events
1158         calendar_day_view_display_events(today_t, year, month, day, 1, daystart, dayend);
1159
1160         wc_printf("</ul>");
1161
1162         wc_printf("</td>");     // end extra on the middle
1163
1164         wc_printf("<td width='20%%' align=\"center\" valign=top>");     // begin stuff-on-the-right
1165
1166         // Begin todays-date-with-left-and-right-arrows
1167         wc_printf("<table border=\"0\" width=\"100%%\" "
1168                 "cellspacing=\"0\" cellpadding=\"0\" bgcolor=\"#FFFFFF\">\n");
1169         wc_printf("<tr>");
1170
1171         // Left arrow
1172         wc_printf("<td align=\"center\">");
1173         wc_printf("<a href=\"readfwd?calview=day?year=%d?month=%d?day=%d\">", yesterday.year, yesterday.month, yesterday.day);
1174         wc_printf("<img alt=\"previous\" align=\"middle\" src=\"static/webcit_icons/essen/32x32/back.png\" border=\"0\"></a>");
1175         wc_printf("</td>");
1176
1177         wc_strftime(d_str, sizeof d_str,
1178                 "<td align=\"center\">"
1179                 "<font size='+2'>%A</font><br>"
1180                 "<font size='+2'>%B</font><br>"
1181                 "<font size='+3'>%d</font><br>"
1182                 "<font size='+2'>%Y</font><br>"
1183                 "</td>",
1184                 &d_tm
1185         );
1186         wc_printf("%s", d_str);
1187
1188         // Right arrow
1189         wc_printf("<td align=\"center\">");
1190         wc_printf("<a href=\"readfwd?calview=day?year=%d?month=%d?day=%d\">",
1191                 tomorrow.year, tomorrow.month, tomorrow.day);
1192         wc_printf("<img alt=\"%s\" align=\"middle\" src=\"static/webcit_icons/essen/32x32/forward.png\""
1193                 " border=\"0\"></a>\n", _("next"));
1194         wc_printf("</td>");
1195
1196         wc_printf("</tr></table>\n");
1197         // End todays-date-with-left-and-right-arrows
1198
1199         // Embed a mini month calendar in this space
1200         wc_printf("<br>\n");
1201         embeddable_mini_calendar(year, month);
1202
1203         wc_printf("</td></tr>");                        // end stuff-on-the-right
1204         wc_printf("</table>\n");                        // end of inner table
1205 }
1206
1207
1208 // Display today's events.  Returns the number of items displayed.
1209 int calendar_summary_view(void) {
1210         long hklen;
1211         const char *HashKey;
1212         void *vCal;
1213         HashPos *Pos;
1214         disp_cal *Cal;
1215         icalproperty *p;
1216         struct icaltimetype t;
1217         time_t event_tt;
1218         struct tm event_tm;
1219         struct tm today_tm;
1220         time_t now;
1221         int all_day_event = 0;
1222         char timestring[SIZ];
1223         int num_displayed = 0;
1224
1225         if (GetCount(WC->disp_cal_items) == 0) {
1226                 return(0);
1227         }
1228
1229         now = time(NULL);
1230         localtime_r(&now, &today_tm);
1231
1232         Pos = GetNewHashPos(WC->disp_cal_items, 0);
1233         while (GetNextHashPos(WC->disp_cal_items, Pos, &hklen, &HashKey, &vCal)) {
1234                 Cal = (disp_cal*)vCal;
1235                 p = icalcomponent_get_first_property(Cal->cal, ICAL_DTSTART_PROPERTY);
1236                 if (p != NULL) {
1237                         t = icalproperty_get_dtstart(p);
1238                         event_tt = icaltime_as_timet(t);
1239                         if (t.is_date) {
1240                                 all_day_event = 1;
1241                         }
1242                         else {
1243                                 all_day_event = 0;
1244                         }
1245                         fmt_time(timestring, SIZ, event_tt);
1246
1247                         if (all_day_event) {
1248                                 gmtime_r(&event_tt, &event_tm);
1249                         }
1250                         else {
1251                                 localtime_r(&event_tt, &event_tm);
1252                         }
1253
1254                         if ( (event_tm.tm_year == today_tm.tm_year)
1255                                 && (event_tm.tm_mon == today_tm.tm_mon)
1256                                 && (event_tm.tm_mday == today_tm.tm_mday)
1257                         ) {
1258                                 p = icalcomponent_get_first_property(Cal->cal, ICAL_SUMMARY_PROPERTY);
1259                                 if (p == NULL) {
1260                                         p = icalproperty_new_summary(_("Untitled Task"));
1261                                         icalcomponent_add_property(Cal->cal, p);
1262                                 }
1263                                 if (p != NULL) {
1264                                         if (WC->CurRoom.view == VIEW_TASKS) {
1265                                                 wc_printf("<a href=\"display_edit_task"
1266                                                         "?msgnum=%ld"
1267                                                         "?return_to_summary=1"
1268                                                         "?go=",
1269                                                         Cal->cal_msgnum
1270                                                 );
1271                                                 escputs(ChrPtr(WC->CurRoom.name));
1272                                                 wc_printf("\">");
1273                                         }
1274                                         else {
1275                                                 wc_printf("<a href=\"display_edit_event"
1276                                                         "?msgnum=%ld"
1277                                                         "?calview=summary"
1278                                                         "?year=%d"
1279                                                         "?month=%d"
1280                                                         "?day=%d"
1281                                                         "?go=",
1282                                                         Cal->cal_msgnum,
1283                                                         today_tm.tm_year + 1900,
1284                                                         today_tm.tm_mon + 1,
1285                                                         today_tm.tm_mday
1286                                                 );
1287                                                 escputs(ChrPtr(WC->CurRoom.name));
1288                                                 wc_printf("\">");
1289                                         }
1290                                         escputs((char *) icalproperty_get_comment(p));
1291                                         if (!all_day_event) {
1292                                                 wc_printf(" (%s)", timestring);
1293                                         }
1294                                         wc_printf("</a><br>\n");
1295                                         ++num_displayed;
1296                                 }
1297                         }
1298                 }
1299         }
1300         DeleteHashPos(&Pos);
1301         DeleteHash(&WC->disp_cal_items);
1302         return(num_displayed);
1303 }
1304
1305
1306 // Parse the URL variables in order to determine the scope and display of a calendar view
1307 int calendar_GetParamsGetServerCall(SharedMessageStatus *Stat,
1308         void **ViewSpecific,
1309         long oper,
1310         char *cmd,
1311         long len,
1312         char *filter,
1313         long flen)
1314 {
1315         calview *c;
1316         time_t now;
1317         struct tm tm;
1318         char cv[32];
1319
1320         int span = 3888000;
1321
1322         c = (calview*) malloc(sizeof(calview));
1323         memset(c, 0, sizeof(calview));
1324         *ViewSpecific = (void*)c;
1325
1326         Stat->load_seen = 1;
1327         strcpy(cmd, "MSGS ALL");
1328         Stat->maxmsgs = 32767;
1329
1330         // In case no date was specified, go with today
1331         now = time(NULL);
1332         localtime_r(&now, &tm);
1333         c->year = tm.tm_year + 1900;
1334         c->month = tm.tm_mon + 1;
1335         c->day = tm.tm_mday;
1336
1337         // Now see if a date was specified
1338         if (havebstr("year")) c->year = ibstr("year");
1339         if (havebstr("month")) c->month = ibstr("month");
1340         if (havebstr("day")) c->day = ibstr("day");
1341
1342         // How would you like that cooked?
1343         if (havebstr("calview")) {
1344                 strcpy(cv, bstr("calview"));
1345         }
1346         else {
1347                 strcpy(cv, "month");
1348         }
1349
1350         // Display the selected view
1351         if (!strcasecmp(cv, "day")) {
1352                 c->view = calview_day;
1353         }
1354         else if (!strcasecmp(cv, "week")) {
1355                 c->view = calview_week;
1356         }
1357         else if (!strcasecmp(cv, "summary")) {  // shouldn't ever happen, but just in case
1358                 c->view = calview_day;
1359         }
1360         else {
1361                 if (WC->CurRoom.view == VIEW_CALBRIEF) {
1362                         c->view = calview_brief;
1363                 }
1364                 else {
1365                         c->view = calview_month;
1366                 }
1367         }
1368
1369         // Now try and set the lower and upper bounds so that we don't
1370         // burn too many cpu cycles parsing data way in the past or future
1371         tm.tm_year = c->year - 1900;
1372         tm.tm_mon = c->month - 1;
1373         tm.tm_mday = c->day;
1374         now = mktime(&tm);
1375
1376         if (c->view == calview_month)   span = 3888000;
1377         if (c->view == calview_brief)   span = 3888000;
1378         if (c->view == calview_week)    span = 604800;
1379         if (c->view == calview_day)     span = 86400;
1380         if (c->view == calview_summary) span = 86400;
1381
1382         c->lower_bound = now - span;
1383         c->upper_bound = now + span;
1384         return 200;
1385 }
1386
1387
1388 // Render a calendar view from data previously loaded into memory
1389 int calendar_RenderView_or_Tail(SharedMessageStatus *Stat, void **ViewSpecific, long oper) {
1390
1391         calview *c = (calview*) *ViewSpecific;
1392
1393         if (c->view == calview_day) {
1394                 calendar_day_view(c->year, c->month, c->day);
1395         }
1396         else if (c->view == calview_week) {
1397                 calendar_week_view(c->year, c->month, c->day);
1398         }
1399         else {
1400                 if (WC->CurRoom.view == VIEW_CALBRIEF) {
1401                         calendar_brief_month_view(c->year, c->month, c->day);
1402                 }
1403                 else {
1404                         calendar_month_view(c->year, c->month, c->day);
1405                 }
1406         }
1407
1408         // Free the in-memory list of calendar items
1409         DeleteHash(&WC->disp_cal_items);
1410         return 0;
1411 }
1412
1413 void 
1414 InitModule_CALENDAR_VIEW
1415 (void)
1416 {
1417         WebcitAddUrlHandler(HKEY("mini_calendar"), "", 0, ajax_mini_calendar, AJAX);
1418 }