Back to index

webcit  8.12-dfsg
calendar_tools.c
Go to the documentation of this file.
00001 /*
00002  * Miscellaneous functions which handle calendar components.
00003  *
00004  * Copyright (c) 1996-2012 by the citadel.org team
00005  *
00006  * This program is open source software.  You can redistribute it and/or
00007  * modify it under the terms of the GNU General Public License, version 3.
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  */
00014 
00015 #include "webcit.h"
00016 #include "webserver.h"
00017 #include "time.h"
00018 #include "calendar.h"
00019 
00020 /* Hour strings */
00021 char *hourname[] = {
00022        "12am", "1am", "2am", "3am", "4am", "5am", "6am",
00023        "7am", "8am", "9am", "10am", "11am", "12pm",
00024        "1pm", "2pm", "3pm", "4pm", "5pm", "6pm",
00025        "7pm", "8pm", "9pm", "10pm", "11pm"
00026 };
00027 
00028 /*
00029  * The display_icaltimetype_as_webform() and icaltime_from_webform() functions
00030  * handle the display and editing of date/time properties in web pages.  The
00031  * first one converts an icaltimetype into valid HTML markup -- a series of form
00032  * fields for editing the date and time.  When the user submits the form, the
00033  * results can be fed back into the second function, which turns it back into
00034  * an icaltimetype.  The "prefix" string required by both functions is prepended
00035  * to all field names.  This allows a form to contain more than one date/time
00036  * property (for example, a start and end time) by ensuring the field names are
00037  * unique within the form.
00038  *
00039  * NOTE: These functions assume that the icaltimetype being edited is in UTC, and
00040  * will convert to/from local time for editing.  "local" in this case is assumed
00041  * to be the time zone in which the WebCit server is running.  A future improvement
00042  * might be to allow the user to specify his/her timezone.
00043  */
00044 
00045 void display_icaltimetype_as_webform(struct icaltimetype *t, char *prefix, int date_only) {
00046        wcsession *WCC = WC;
00047        int i;
00048        time_t now;
00049        struct tm tm_now;
00050        time_t tt;
00051        struct tm tm;
00052        int all_day_event = 0;
00053        int time_format;
00054        char timebuf[32];
00055        
00056        time_format = get_time_format_cached ();
00057 
00058        now = time(NULL);
00059        localtime_r(&now, &tm_now);
00060 
00061        if (t == NULL) return;
00062        if (t->is_date) all_day_event = 1;
00063        tt = icaltime_as_timet(*t);
00064        if (all_day_event) {
00065               gmtime_r(&tt, &tm);
00066        }
00067        else {
00068               localtime_r(&tt, &tm);
00069        }
00070 
00071        wc_printf("<input type=\"text\" name=\"");
00072        StrBufAppendBufPlain(WCC->WBuf, prefix, -1, 0);
00073        wc_printf("\" id=\"");
00074        StrBufAppendBufPlain(WCC->WBuf, prefix, -1, 0);
00075        wc_printf("\" size=\"10\" maxlength=\"10\" value=\"");
00076        wc_strftime(timebuf, 32, "%Y-%m-%d", &tm);
00077        StrBufAppendBufPlain(WCC->WBuf, timebuf, -1, 0);
00078        wc_printf("\">");
00079 
00080        StrBufAppendPrintf(WC->trailing_javascript, "attachDatePicker('");
00081        StrBufAppendPrintf(WC->trailing_javascript, prefix);
00082        StrBufAppendPrintf(WC->trailing_javascript, "', '%s');\n", get_selected_language());
00083 
00084        /* If we're editing a date only, we still generate the time boxes, but we hide them.
00085         * This keeps the data model consistent.
00086         */
00087        if (date_only) {
00088               wc_printf("<div style=\"display:none\">");
00089        }
00090 
00091        wc_printf("<span ID=\"");
00092        StrBufAppendBufPlain(WCC->WBuf, prefix, -1, 0);
00093        wc_printf("_time\">");
00094        wc_printf(_("Hour: "));
00095        wc_printf("<SELECT NAME=\"%s_hour\" SIZE=\"1\">\n", prefix);
00096        for (i=0; i<=23; ++i) {
00097 
00098               if (time_format == WC_TIMEFORMAT_24) {
00099                      wc_printf("<OPTION %s VALUE=\"%d\">%d</OPTION>\n",
00100                             ((tm.tm_hour == i) ? "SELECTED" : ""),
00101                             i, i
00102                             );
00103               }
00104               else {
00105                      wc_printf("<OPTION %s VALUE=\"%d\">%s</OPTION>\n",
00106                             ((tm.tm_hour == i) ? "SELECTED" : ""),
00107                             i, hourname[i]
00108                             );
00109               }
00110 
00111        }
00112        wc_printf("</SELECT>\n");
00113 
00114        wc_printf(_("Minute: "));
00115        wc_printf("<SELECT NAME=\"%s_minute\" SIZE=\"1\">\n", prefix);
00116        for (i=0; i<=59; ++i) {
00117               if ( (i % 5 == 0) || (tm.tm_min == i) ) {
00118                      wc_printf("<OPTION %s VALUE=\"%d\">:%02d</OPTION>\n",
00119                             ((tm.tm_min == i) ? "SELECTED" : ""),
00120                             i, i
00121                             );
00122               }
00123        }
00124        wc_printf("</SELECT></span>\n");
00125 
00126        if (date_only) {
00127               wc_printf("</div>");
00128        }
00129 }
00130 
00131 /*
00132  * Get date/time from a web form and convert it into an icaltimetype struct.
00133  */
00134 void icaltime_from_webform(struct icaltimetype *t, char *prefix) {
00135        char vname[32];
00136 
00137        if (!t) return;
00138 
00139        /* Stuff with zero values */
00140        memset(t, 0, sizeof(struct icaltimetype));
00141 
00142        /* Get the year/month/date all in one shot -- it will be in ISO YYYY-MM-DD format */
00143        sscanf((char*)BSTR(prefix), "%04d-%02d-%02d", &t->year, &t->month, &t->day);
00144 
00145        /* hour */
00146        sprintf(vname, "%s_hour", prefix);
00147        t->hour = IBSTR(vname);
00148 
00149        /* minute */
00150        sprintf(vname, "%s_minute", prefix);
00151        t->minute = IBSTR(vname);
00152 
00153        /* time zone is set to the default zone for this server */
00154        t->is_utc = 0;
00155        t->is_date = 0;
00156        t->zone = get_default_icaltimezone();
00157 }
00158 
00159 
00160 /*
00161  * Get date (no time) from a web form and convert it into an icaltimetype struct.
00162  */
00163 void icaltime_from_webform_dateonly(struct icaltimetype *t, char *prefix) {
00164        if (!t) return;
00165 
00166        /* Stuff with zero values */
00167        memset(t, 0, sizeof(struct icaltimetype));
00168 
00169        /* Get the year/month/date all in one shot -- it will be in ISO YYYY-MM-DD format */
00170        sscanf((char*)BSTR(prefix), "%04d-%02d-%02d", &t->year, &t->month, &t->day);
00171 
00172        /* time zone is set to the default zone for this server */
00173        t->is_utc = 1;
00174        t->is_date = 1;
00175 }
00176 
00177 
00178 /*
00179  * Render a PARTSTAT parameter as a string (and put it in parentheses)
00180  */
00181 void partstat_as_string(char *buf, icalproperty *attendee) {
00182        icalparameter *partstat_param;
00183        icalparameter_partstat partstat;
00184 
00185        strcpy(buf, _("(status unknown)"));
00186 
00187        partstat_param = icalproperty_get_first_parameter(
00188               attendee,
00189               ICAL_PARTSTAT_PARAMETER
00190               );
00191        if (partstat_param == NULL) {
00192               return;
00193        }
00194 
00195        partstat = icalparameter_get_partstat(partstat_param);
00196        switch(partstat) {
00197        case ICAL_PARTSTAT_X:
00198               strcpy(buf, "(x)");
00199               break;
00200        case ICAL_PARTSTAT_NEEDSACTION:
00201               strcpy(buf, _("(needs action)"));
00202               break;
00203        case ICAL_PARTSTAT_ACCEPTED:
00204               strcpy(buf, _("(accepted)"));
00205               break;
00206        case ICAL_PARTSTAT_DECLINED:
00207               strcpy(buf, _("(declined)"));
00208               break;
00209        case ICAL_PARTSTAT_TENTATIVE:
00210               strcpy(buf, _("(tenative)"));
00211               break;
00212        case ICAL_PARTSTAT_DELEGATED:
00213               strcpy(buf, _("(delegated)"));
00214               break;
00215        case ICAL_PARTSTAT_COMPLETED:
00216               strcpy(buf, _("(completed)"));
00217               break;
00218        case ICAL_PARTSTAT_INPROCESS:
00219               strcpy(buf, _("(in process)"));
00220               break;
00221        case ICAL_PARTSTAT_NONE:
00222               strcpy(buf, _("(none)"));
00223               break;
00224        }
00225 }
00226 
00227 /*
00228  * Utility function to encapsulate a subcomponent into a full VCALENDAR.
00229  *
00230  * We also scan for any date/time properties that reference timezones, and attach
00231  * those timezones along with the supplied subcomponent.  (Increase the size of the array if you need to.)
00232  *
00233  * Note: if you change anything here, change it in Citadel server's ical_send_out_invitations() too.
00234  */
00235 icalcomponent *ical_encapsulate_subcomponent(icalcomponent *subcomp) {
00236        icalcomponent *encaps;
00237        icalproperty *p;
00238        struct icaltimetype t;
00239        const icaltimezone *attached_zones[5] = { NULL, NULL, NULL, NULL, NULL };
00240        int i;
00241        const icaltimezone *z;
00242        int num_zones_attached = 0;
00243        int zone_already_attached;
00244 
00245        if (subcomp == NULL) {
00246               syslog(3, "ERROR: ical_encapsulate_subcomponent() called with NULL argument\n");
00247               return NULL;
00248        }
00249 
00250        /*
00251         * If we're already looking at a full VCALENDAR component, this is probably an error.
00252         */
00253        if (icalcomponent_isa(subcomp) == ICAL_VCALENDAR_COMPONENT) {
00254               syslog(3, "ERROR: component sent to ical_encapsulate_subcomponent() already top level\n");
00255               return subcomp;
00256        }
00257 
00258        /* search for... */
00259        for (p = icalcomponent_get_first_property(subcomp, ICAL_ANY_PROPERTY);
00260             p != NULL;
00261             p = icalcomponent_get_next_property(subcomp, ICAL_ANY_PROPERTY))
00262        {
00263               if ( (icalproperty_isa(p) == ICAL_COMPLETED_PROPERTY)
00264                 || (icalproperty_isa(p) == ICAL_CREATED_PROPERTY)
00265                 || (icalproperty_isa(p) == ICAL_DATEMAX_PROPERTY)
00266                 || (icalproperty_isa(p) == ICAL_DATEMIN_PROPERTY)
00267                 || (icalproperty_isa(p) == ICAL_DTEND_PROPERTY)
00268                 || (icalproperty_isa(p) == ICAL_DTSTAMP_PROPERTY)
00269                 || (icalproperty_isa(p) == ICAL_DTSTART_PROPERTY)
00270                 || (icalproperty_isa(p) == ICAL_DUE_PROPERTY)
00271                 || (icalproperty_isa(p) == ICAL_EXDATE_PROPERTY)
00272                 || (icalproperty_isa(p) == ICAL_LASTMODIFIED_PROPERTY)
00273                 || (icalproperty_isa(p) == ICAL_MAXDATE_PROPERTY)
00274                 || (icalproperty_isa(p) == ICAL_MINDATE_PROPERTY)
00275                 || (icalproperty_isa(p) == ICAL_RECURRENCEID_PROPERTY)
00276               ) {
00277                      t = icalproperty_get_dtstart(p);   /*/ it's safe to use dtstart for all of them */
00278                      if ((icaltime_is_valid_time(t)) && (z=icaltime_get_timezone(t), z)) {
00279                      
00280                             zone_already_attached = 0;
00281                             for (i=0; i<5; ++i) {
00282                                    if (z == attached_zones[i]) {
00283                                           ++zone_already_attached;
00284                                           syslog(9, "zone already attached!!\n");
00285                                    }
00286                             }
00287                             if ((!zone_already_attached) && (num_zones_attached < 5)) {
00288                                    syslog(9, "attaching zone %d!\n", num_zones_attached);
00289                                    attached_zones[num_zones_attached++] = z;
00290                             }
00291 
00292                             icalproperty_set_parameter(p,
00293                                    icalparameter_new_tzid(icaltimezone_get_tzid((icaltimezone *)z))
00294                             );
00295                      }
00296               }
00297        }
00298 
00299        /* Encapsulate the VEVENT component into a complete VCALENDAR */
00300        encaps = icalcomponent_new(ICAL_VCALENDAR_COMPONENT);
00301        if (encaps == NULL) {
00302               syslog(3, "ERROR: ical_encapsulate_subcomponent() could not allocate component\n");
00303               return NULL;
00304        }
00305 
00306        /* Set the Product ID */
00307        icalcomponent_add_property(encaps, icalproperty_new_prodid(PRODID));
00308 
00309        /* Set the Version Number */
00310        icalcomponent_add_property(encaps, icalproperty_new_version("2.0"));
00311 
00312        /* Attach any timezones we need */
00313        if (num_zones_attached > 0) for (i=0; i<num_zones_attached; ++i) {
00314               icalcomponent *zc;
00315               zc = icalcomponent_new_clone(icaltimezone_get_component((icaltimezone *)attached_zones[i]));
00316               icalcomponent_add_component(encaps, zc);
00317        }
00318 
00319        /* Encapsulate the subcomponent inside */
00320        icalcomponent_add_component(encaps, subcomp);
00321 
00322        /* Return the object we just created. */
00323        return(encaps);
00324 }