2 * Miscellaneous functions which handle calendar components.
4 * Copyright (c) 1996-2010 by the citadel.org team
6 * This program is open source software. You can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 3 of the
9 * License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include "webserver.h"
28 "12am", "1am", "2am", "3am", "4am", "5am", "6am",
29 "7am", "8am", "9am", "10am", "11am", "12pm",
30 "1pm", "2pm", "3pm", "4pm", "5pm", "6pm",
31 "7pm", "8pm", "9pm", "10pm", "11pm"
35 * The display_icaltimetype_as_webform() and icaltime_from_webform() functions
36 * handle the display and editing of date/time properties in web pages. The
37 * first one converts an icaltimetype into valid HTML markup -- a series of form
38 * fields for editing the date and time. When the user submits the form, the
39 * results can be fed back into the second function, which turns it back into
40 * an icaltimetype. The "prefix" string required by both functions is prepended
41 * to all field names. This allows a form to contain more than one date/time
42 * property (for example, a start and end time) by ensuring the field names are
43 * unique within the form.
45 * NOTE: These functions assume that the icaltimetype being edited is in UTC, and
46 * will convert to/from local time for editing. "local" in this case is assumed
47 * to be the time zone in which the WebCit server is running. A future improvement
48 * might be to allow the user to specify his/her timezone.
51 void display_icaltimetype_as_webform(struct icaltimetype *t, char *prefix, int date_only) {
59 int all_day_event = 0;
63 time_format = get_time_format_cached ();
66 localtime_r(&now, &tm_now);
67 this_year = tm_now.tm_year + 1900;
69 if (t == NULL) return;
70 if (t->is_date) all_day_event = 1;
71 tt = icaltime_as_timet(*t);
76 localtime_r(&tt, &tm);
79 wc_printf("<input type=\"text\" name=\"");
80 StrBufAppendBufPlain(WCC->WBuf, prefix, -1, 0);
81 wc_printf("\" id=\"");
82 StrBufAppendBufPlain(WCC->WBuf, prefix, -1, 0);
83 wc_printf("\" size=\"10\" maxlength=\"10\" value=\"");
84 wc_strftime(timebuf, 32, "%Y-%m-%d", &tm);
85 StrBufAppendBufPlain(WCC->WBuf, timebuf, -1, 0);
88 StrBufAppendPrintf(WC->trailing_javascript, "attachDatePicker('");
89 StrBufAppendPrintf(WC->trailing_javascript, prefix);
90 StrBufAppendPrintf(WC->trailing_javascript, "', '%s');\n", get_selected_language());
92 /* If we're editing a date only, we still generate the time boxes, but we hide them.
93 * This keeps the data model consistent.
96 wc_printf("<div style=\"display:none\">");
99 wc_printf("<span ID=\"");
100 StrBufAppendBufPlain(WCC->WBuf, prefix, -1, 0);
101 wc_printf("_time\">");
102 wc_printf(_("Hour: "));
103 wc_printf("<SELECT NAME=\"%s_hour\" SIZE=\"1\">\n", prefix);
104 for (i=0; i<=23; ++i) {
106 if (time_format == WC_TIMEFORMAT_24) {
107 wc_printf("<OPTION %s VALUE=\"%d\">%d</OPTION>\n",
108 ((tm.tm_hour == i) ? "SELECTED" : ""),
113 wc_printf("<OPTION %s VALUE=\"%d\">%s</OPTION>\n",
114 ((tm.tm_hour == i) ? "SELECTED" : ""),
120 wc_printf("</SELECT>\n");
122 wc_printf(_("Minute: "));
123 wc_printf("<SELECT NAME=\"%s_minute\" SIZE=\"1\">\n", prefix);
124 for (i=0; i<=59; ++i) {
125 if ( (i % 5 == 0) || (tm.tm_min == i) ) {
126 wc_printf("<OPTION %s VALUE=\"%d\">:%02d</OPTION>\n",
127 ((tm.tm_min == i) ? "SELECTED" : ""),
132 wc_printf("</SELECT></span>\n");
140 * Get date/time from a web form and convert it into an icaltimetype struct.
142 void icaltime_from_webform(struct icaltimetype *t, char *prefix) {
147 /* Stuff with zero values */
148 memset(t, 0, sizeof(struct icaltimetype));
150 /* Get the year/month/date all in one shot -- it will be in ISO YYYY-MM-DD format */
151 sscanf((char*)BSTR(prefix), "%04d-%02d-%02d", &t->year, &t->month, &t->day);
154 sprintf(vname, "%s_hour", prefix);
155 t->hour = IBSTR(vname);
158 sprintf(vname, "%s_minute", prefix);
159 t->minute = IBSTR(vname);
161 /* time zone is set to the default zone for this server */
164 t->zone = get_default_icaltimezone();
169 * Get date (no time) from a web form and convert it into an icaltimetype struct.
171 void icaltime_from_webform_dateonly(struct icaltimetype *t, char *prefix) {
174 /* Stuff with zero values */
175 memset(t, 0, sizeof(struct icaltimetype));
177 /* Get the year/month/date all in one shot -- it will be in ISO YYYY-MM-DD format */
178 sscanf((char*)BSTR(prefix), "%04d-%02d-%02d", &t->year, &t->month, &t->day);
180 /* time zone is set to the default zone for this server */
187 * Render a PARTSTAT parameter as a string (and put it in parentheses)
189 void partstat_as_string(char *buf, icalproperty *attendee) {
190 icalparameter *partstat_param;
191 icalparameter_partstat partstat;
193 strcpy(buf, _("(status unknown)"));
195 partstat_param = icalproperty_get_first_parameter(
197 ICAL_PARTSTAT_PARAMETER
199 if (partstat_param == NULL) {
203 partstat = icalparameter_get_partstat(partstat_param);
205 case ICAL_PARTSTAT_X:
208 case ICAL_PARTSTAT_NEEDSACTION:
209 strcpy(buf, _("(needs action)"));
211 case ICAL_PARTSTAT_ACCEPTED:
212 strcpy(buf, _("(accepted)"));
214 case ICAL_PARTSTAT_DECLINED:
215 strcpy(buf, _("(declined)"));
217 case ICAL_PARTSTAT_TENTATIVE:
218 strcpy(buf, _("(tenative)"));
220 case ICAL_PARTSTAT_DELEGATED:
221 strcpy(buf, _("(delegated)"));
223 case ICAL_PARTSTAT_COMPLETED:
224 strcpy(buf, _("(completed)"));
226 case ICAL_PARTSTAT_INPROCESS:
227 strcpy(buf, _("(in process)"));
229 case ICAL_PARTSTAT_NONE:
230 strcpy(buf, _("(none)"));
236 * Utility function to encapsulate a subcomponent into a full VCALENDAR.
238 * We also scan for any date/time properties that reference timezones, and attach
239 * those timezones along with the supplied subcomponent. (Increase the size of the array if you need to.)
241 * Note: if you change anything here, change it in Citadel server's ical_send_out_invitations() too.
243 icalcomponent *ical_encapsulate_subcomponent(icalcomponent *subcomp) {
244 icalcomponent *encaps;
246 struct icaltimetype t;
247 const icaltimezone *attached_zones[5] = { NULL, NULL, NULL, NULL, NULL };
249 const icaltimezone *z;
250 int num_zones_attached = 0;
251 int zone_already_attached;
253 if (subcomp == NULL) {
254 syslog(3, "ERROR: ical_encapsulate_subcomponent() called with NULL argument\n");
259 * If we're already looking at a full VCALENDAR component, this is probably an error.
261 if (icalcomponent_isa(subcomp) == ICAL_VCALENDAR_COMPONENT) {
262 syslog(3, "ERROR: component sent to ical_encapsulate_subcomponent() already top level\n");
267 for (p = icalcomponent_get_first_property(subcomp, ICAL_ANY_PROPERTY);
269 p = icalcomponent_get_next_property(subcomp, ICAL_ANY_PROPERTY))
271 if ( (icalproperty_isa(p) == ICAL_COMPLETED_PROPERTY)
272 || (icalproperty_isa(p) == ICAL_CREATED_PROPERTY)
273 || (icalproperty_isa(p) == ICAL_DATEMAX_PROPERTY)
274 || (icalproperty_isa(p) == ICAL_DATEMIN_PROPERTY)
275 || (icalproperty_isa(p) == ICAL_DTEND_PROPERTY)
276 || (icalproperty_isa(p) == ICAL_DTSTAMP_PROPERTY)
277 || (icalproperty_isa(p) == ICAL_DTSTART_PROPERTY)
278 || (icalproperty_isa(p) == ICAL_DUE_PROPERTY)
279 || (icalproperty_isa(p) == ICAL_EXDATE_PROPERTY)
280 || (icalproperty_isa(p) == ICAL_LASTMODIFIED_PROPERTY)
281 || (icalproperty_isa(p) == ICAL_MAXDATE_PROPERTY)
282 || (icalproperty_isa(p) == ICAL_MINDATE_PROPERTY)
283 || (icalproperty_isa(p) == ICAL_RECURRENCEID_PROPERTY)
285 t = icalproperty_get_dtstart(p); /*/ it's safe to use dtstart for all of them */
286 if ((icaltime_is_valid_time(t)) && (z=icaltime_get_timezone(t), z)) {
288 zone_already_attached = 0;
289 for (i=0; i<5; ++i) {
290 if (z == attached_zones[i]) {
291 ++zone_already_attached;
292 syslog(9, "zone already attached!!\n");
295 if ((!zone_already_attached) && (num_zones_attached < 5)) {
296 syslog(9, "attaching zone %d!\n", num_zones_attached);
297 attached_zones[num_zones_attached++] = z;
300 icalproperty_set_parameter(p,
301 icalparameter_new_tzid(icaltimezone_get_tzid((icaltimezone *)z))
307 /* Encapsulate the VEVENT component into a complete VCALENDAR */
308 encaps = icalcomponent_new(ICAL_VCALENDAR_COMPONENT);
309 if (encaps == NULL) {
310 syslog(3, "ERROR: ical_encapsulate_subcomponent() could not allocate component\n");
314 /* Set the Product ID */
315 icalcomponent_add_property(encaps, icalproperty_new_prodid(PRODID));
317 /* Set the Version Number */
318 icalcomponent_add_property(encaps, icalproperty_new_version("2.0"));
320 /* Attach any timezones we need */
321 if (num_zones_attached > 0) for (i=0; i<num_zones_attached; ++i) {
323 zc = icalcomponent_new_clone(icaltimezone_get_component((icaltimezone *)attached_zones[i]));
324 icalcomponent_add_component(encaps, zc);
327 /* Encapsulate the subcomponent inside */
328 icalcomponent_add_component(encaps, subcomp);
330 /* Return the object we just created. */