X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=webcit%2Fevent.c;h=af9ebe893149a611db52565dbda9e4d007625fbf;hb=8d8c5af38e5026933ea3b9d5eb1b75f276df1d99;hp=11ece54543922a827954ffa17bd6cd490db3b8ca;hpb=d236455ba4508a1b9c9f0f65878d0cf784c186b8;p=citadel.git diff --git a/webcit/event.c b/webcit/event.c index 11ece5454..af9ebe893 100644 --- a/webcit/event.c +++ b/webcit/event.c @@ -1,64 +1,116 @@ /* * $Id$ - * - * Editing calendar events. - * */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +/** + * \defgroup EditCal Editing calendar events. + * \ingroup Calendaring + */ +/*@{*/ #include "webcit.h" #include "webserver.h" -#ifdef HAVE_ICAL_H +#ifdef WEBCIT_WITH_CALENDAR_SERVICE - -/* - * Display an event by itself (for editing) +/** + * \brief Display an event by itself (for editing) + * \param supplied_vevent the event to edit + * \param msgnum reference on the citserver */ void display_edit_individual_event(icalcomponent *supplied_vevent, long msgnum) { icalcomponent *vevent; icalproperty *p; - struct icaltimetype t; + icalvalue *v; + struct icaltimetype t_start, t_end; time_t now; + struct tm tm_now; int created_new_vevent = 0; + icalproperty *organizer = NULL; + char organizer_string[SIZ]; + icalproperty *attendee = NULL; + char attendee_string[SIZ]; + char buf[SIZ]; + int organizer_is_me = 0; + int i; + int sequence = 0; now = time(NULL); + strcpy(organizer_string, ""); + strcpy(attendee_string, ""); if (supplied_vevent != NULL) { vevent = supplied_vevent; + /** + * If we're looking at a fully encapsulated VCALENDAR + * rather than a VEVENT component, attempt to use the first + * relevant VEVENT subcomponent. If there is none, the + * NULL returned by icalcomponent_get_first_component() will + * tell the next iteration of this function to create a + * new one. + */ + if (icalcomponent_isa(vevent) == ICAL_VCALENDAR_COMPONENT) { + display_edit_individual_event( + icalcomponent_get_first_component( + vevent, ICAL_VEVENT_COMPONENT + ), msgnum + ); + return; + } } else { vevent = icalcomponent_new(ICAL_VEVENT_COMPONENT); created_new_vevent = 1; } - output_headers(3); - wprintf("
" - "" - "Edit event" - "

\n" + /** Learn the sequence */ + p = icalcomponent_get_first_property(vevent, ICAL_SEQUENCE_PROPERTY); + if (p != NULL) { + sequence = icalproperty_get_sequence(p); + } + + /** Begin output */ + output_headers(1, 1, 2, 0, 0, 0); + wprintf("
\n" + "
" + ""); + wprintf(_("Add or edit an event")); + wprintf("" + "
\n" + "
\n
\n" + ); + + wprintf("\n" ); + + wprintf("
" + ""); + + /** + * For a new event, the user creating the event should be the + * organizer. Set this field accordingly. + */ + if (icalcomponent_get_first_property(vevent, ICAL_ORGANIZER_PROPERTY) + == NULL) { + sprintf(organizer_string, "MAILTO:%s", WC->cs_inet_email); + icalcomponent_add_property(vevent, + icalproperty_new_organizer(organizer_string) + ); + } + + /** + * Determine who is the organizer of this event. + * We need to determine "me" or "not me." + */ + organizer = icalcomponent_get_first_property(vevent, ICAL_ORGANIZER_PROPERTY); + if (organizer != NULL) { + strcpy(organizer_string, icalproperty_get_organizer(organizer)); + if (!strncasecmp(organizer_string, "MAILTO:", 7)) { + strcpy(organizer_string, &organizer_string[7]); + striplt(organizer_string); + serv_printf("ISME %s", organizer_string); + serv_getln(buf, sizeof buf); + if (buf[0] == '2') { + organizer_is_me = 1; + } + } + } + + wprintf("\n"); - wprintf("
" - "" + /** Transparency */ + wprintf("
\n"); + + /** Attendees */ + wprintf("\n"); + + /** Done with properties. */ + wprintf("
\n"); + /************************************************************ * Uncomment this to see the UID in calendar events for debugging wprintf("UID == "); @@ -66,9 +118,12 @@ void display_edit_individual_event(icalcomponent *supplied_vevent, long msgnum) if (p != NULL) { escputs((char *)icalproperty_get_comment(p)); } + wprintf("
\n"); + wprintf("SEQUENCE == %d
\n", sequence); *************************************************************/ - wprintf("
\n"); + wprintf("\n"); + wprintf("\n", msgnum); wprintf("\n", @@ -80,10 +135,12 @@ void display_edit_individual_event(icalcomponent *supplied_vevent, long msgnum) wprintf("\n", bstr("day")); - /* Put it in a borderless table so it lines up nicely */ + /** Put it in a borderless table so it lines up nicely */ wprintf("\n"); - wprintf("\n"); - wprintf("\n"); - wprintf("\n"); - wprintf("\n"); - wprintf("
Summary\n" + wprintf("
"); + wprintf(_("Summary")); + wprintf("\n" "
Location\n" + wprintf("
"); + wprintf(_("Location")); + wprintf("\n" "
Start\n"); + wprintf("
"); + wprintf(_("Start")); + wprintf("\n"); p = icalcomponent_get_first_property(vevent, ICAL_DTSTART_PROPERTY); if (p != NULL) { - t = icalproperty_get_dtstart(p); + t_start = icalproperty_get_dtstart(p); + if (t_start.is_date) { + t_start.hour = 0; + t_start.minute = 0; + t_start.second = 0; + } } else { - t = icaltime_from_timet(now, 0); + localtime_r(&now, &tm_now); + if (strlen(bstr("year")) > 0) { + tm_now.tm_year = atoi(bstr("year")) - 1900; + tm_now.tm_mon = atoi(bstr("month")) - 1; + tm_now.tm_mday = atoi(bstr("day")); + } + if (strlen(bstr("hour")) > 0) { + tm_now.tm_hour = atoi(bstr("hour")); + tm_now.tm_min = atoi(bstr("minute")); + tm_now.tm_sec = 0; + } + else { + tm_now.tm_hour = 9; + tm_now.tm_min = 0; + tm_now.tm_sec = 0; + } + + t_start = icaltime_from_timet_with_zone( + mktime(&tm_now), + ((!strcasecmp(bstr("alldayevent"), "yes")) ? 1 : 0), + icaltimezone_get_utc_timezone() + ); + t_start.is_utc = 1; + } - display_icaltimetype_as_webform(&t, "dtstart"); + display_icaltimetype_as_webform(&t_start, "dtstart"); + + wprintf("%s", + (t_start.is_date ? "CHECKED" : "" ), + _("All day event") + ); + wprintf("
End\n"); - p = icalcomponent_get_first_property(vevent, ICAL_DTEND_PROPERTY); - if (p != NULL) { - t = icalproperty_get_dtend(p); + /** + * If this is an all-day-event, set the end time to be identical to + * the start time (the hour/minute/second will be set to midnight). + * Otherwise extract or create it. + */ + wprintf("
"); + wprintf(_("End")); + wprintf("\n"); + if (t_start.is_date) { + t_end = t_start; } else { - t = icaltime_from_timet(now, 0); + p = icalcomponent_get_first_property(vevent, + ICAL_DTEND_PROPERTY); + if (p != NULL) { + t_end = icalproperty_get_dtend(p); + } + else { + /** + * If this is not an all-day event and there is no + * end time specified, make the default one hour + * from the start time. + */ + t_end = t_start; + t_end.hour += 1; + t_end.second = 0; + t_end = icaltime_normalize(t_end); + /* t_end = icaltime_from_timet(now, 0); */ + } } - display_icaltimetype_as_webform(&t, "dtend"); + display_icaltimetype_as_webform(&t_end, "dtend"); wprintf("
Notes\n" + wprintf("
"); + wprintf(_("Notes")); + wprintf("\n" "
\n"); + wprintf("
"); + wprintf(_("Organizer")); + wprintf(""); + escputs(organizer_string); + if (organizer_is_me) { + wprintf(" "); + wprintf(_("(you are the organizer)")); + wprintf("\n"); + } + + /** + * Transmit the organizer as a hidden field. We don't want the user + * to be able to change it, but we do want it fed back to the server, + * especially if this is a new event and there is no organizer already + * in the calendar object. + */ + wprintf(""); + + wprintf("
"); + wprintf(_("Show time as:")); + wprintf(""); + + p = icalcomponent_get_first_property(vevent, ICAL_TRANSP_PROPERTY); + if (p == NULL) { + /** No transparency found. Default to opaque (busy). */ + p = icalproperty_new_transp(ICAL_TRANSP_OPAQUE); + if (p != NULL) { + icalcomponent_add_property(vevent, p); + } + } + if (p != NULL) { + v = icalproperty_get_value(p); + } + else { + v = NULL; + } + + wprintf(""); + wprintf(_("Free")); + wprintf("  "); + + wprintf(""); + wprintf(_("Busy")); + + wprintf("
"); + wprintf(_("Attendees")); + wprintf("
" + ""); + wprintf(_("(One per line)")); + wprintf("
" + "
\n
" + "" "  " - "\n" + "\n" "  " - "\n" - "
\n" + "\n" + "  " + "\n" + "\n", + _("Save"), + _("Delete"), + _("Check attendee availability"), + _("Cancel") ); wprintf("\n"); - + + wprintf("
\n"); + wprintf("\n" + ); wDumpContent(1); if (created_new_vevent) { @@ -151,120 +404,333 @@ void display_edit_individual_event(icalcomponent *supplied_vevent, long msgnum) } } -/* - * Save an edited event +/** + * \brief Save an edited event + * \param supplied_vevent the event to save + * \param msgnum the index on the citserver */ void save_individual_event(icalcomponent *supplied_vevent, long msgnum) { char buf[SIZ]; - int delete_existing = 0; icalproperty *prop; - icalcomponent *vevent; + icalcomponent *vevent, *encaps; int created_new_vevent = 0; int all_day_event = 0; - struct icaltimetype event_start; + struct icaltimetype event_start, t; + icalproperty *attendee = NULL; + char attendee_string[SIZ]; + int i; + int foundit; + char form_attendees[SIZ]; + char organizer_string[SIZ]; + int sequence = 0; + enum icalproperty_transp formtransp = ICAL_TRANSP_NONE; if (supplied_vevent != NULL) { vevent = supplied_vevent; + /** + * If we're looking at a fully encapsulated VCALENDAR + * rather than a VEVENT component, attempt to use the first + * relevant VEVENT subcomponent. If there is none, the + * NULL returned by icalcomponent_get_first_component() will + * tell the next iteration of this function to create a + * new one. + */ + if (icalcomponent_isa(vevent) == ICAL_VCALENDAR_COMPONENT) { + save_individual_event( + icalcomponent_get_first_component( + vevent, ICAL_VEVENT_COMPONENT + ), msgnum + ); + return; + } } else { vevent = icalcomponent_new(ICAL_VEVENT_COMPONENT); created_new_vevent = 1; } - if (!strcasecmp(bstr("sc"), "Save")) { + if ( (strlen(bstr("save_button")) > 0) + || (strlen(bstr("check_button")) > 0) ) { - /* Replace values in the component with ones from the form */ + /** Replace values in the component with ones from the form */ while (prop = icalcomponent_get_first_property(vevent, ICAL_SUMMARY_PROPERTY), prop != NULL) { icalcomponent_remove_property(vevent, prop); + icalproperty_free(prop); } - icalcomponent_add_property(vevent, - icalproperty_new_summary(bstr("summary"))); - - while (prop = icalcomponent_get_first_property(vevent, - ICAL_LOCATION_PROPERTY), prop != NULL) { - icalcomponent_remove_property(vevent, prop); - } - icalcomponent_add_property(vevent, - icalproperty_new_location(bstr("location"))); - - while (prop = icalcomponent_get_first_property(vevent, - ICAL_DESCRIPTION_PROPERTY), prop != NULL) { - icalcomponent_remove_property(vevent, prop); - } - icalcomponent_add_property(vevent, - icalproperty_new_description(bstr("description"))); + + if (strlen(bstr("summary")) > 0) { + + icalcomponent_add_property(vevent, + icalproperty_new_summary(bstr("summary"))); + } else { + icalcomponent_add_property(vevent, + icalproperty_new_summary("Untitled Event")); + } + while (prop = icalcomponent_get_first_property(vevent, + ICAL_LOCATION_PROPERTY), prop != NULL) { + icalcomponent_remove_property(vevent, prop); + icalproperty_free(prop); + } + if (strlen(bstr("location")) > 0) { + icalcomponent_add_property(vevent, + icalproperty_new_location(bstr("location"))); + } + while (prop = icalcomponent_get_first_property(vevent, + ICAL_DESCRIPTION_PROPERTY), prop != NULL) { + icalcomponent_remove_property(vevent, prop); + icalproperty_free(prop); + } + if (strlen(bstr("description")) > 0) { + icalcomponent_add_property(vevent, + icalproperty_new_description(bstr("description"))); + } + while (prop = icalcomponent_get_first_property(vevent, ICAL_DTSTART_PROPERTY), prop != NULL) { icalcomponent_remove_property(vevent, prop); + icalproperty_free(prop); } - event_start = icaltime_from_webform("dtstart"); - if (event_start.is_date) { - lprintf(9, "*** all day event ***\n"); + + if (!strcmp(bstr("alldayevent"), "yes")) { all_day_event = 1; } - icalcomponent_add_property(vevent, - icalproperty_new_dtstart(event_start) - ); - + else { + all_day_event = 0; + } + + if (all_day_event) { + icaltime_from_webform_dateonly(&event_start, "dtstart"); + } + else { + icaltime_from_webform(&event_start, "dtstart"); + } + + /** + * The following odd-looking snippet of code looks like it + * takes some unnecessary steps. It is done this way because + * libical incorrectly turns an "all day event" into a normal + * event starting at midnight (i.e. it serializes as date/time + * instead of just date) unless icalvalue_new_date() is used. + * So we force it, if this is an all day event. + */ + prop = icalproperty_new_dtstart(event_start); + if (all_day_event) { + icalproperty_set_value(prop, + icalvalue_new_date(event_start) + ); + } + + if (prop) icalcomponent_add_property(vevent, prop); + else icalproperty_free(prop); + while (prop = icalcomponent_get_first_property(vevent, ICAL_DTEND_PROPERTY), prop != NULL) { icalcomponent_remove_property(vevent, prop); + icalproperty_free(prop); } while (prop = icalcomponent_get_first_property(vevent, ICAL_DURATION_PROPERTY), prop != NULL) { icalcomponent_remove_property(vevent, prop); + icalproperty_free(prop); } + if (all_day_event == 0) { + icaltime_from_webform(&t, "dtend"); icalcomponent_add_property(vevent, - icalproperty_new_dtend( - icaltime_from_webform("dtend") + icalproperty_new_dtend(icaltime_normalize(t) ) ); } - /* Give this event a UID if it doesn't have one. */ + /** See if transparency is indicated */ + if (strlen(bstr("transp")) > 0) { + if (!strcasecmp(bstr("transp"), "opaque")) { + formtransp = ICAL_TRANSP_OPAQUE; + } + else if (!strcasecmp(bstr("transp"), "transparent")) { + formtransp = ICAL_TRANSP_TRANSPARENT; + } + + while (prop = icalcomponent_get_first_property(vevent, ICAL_TRANSP_PROPERTY), + (prop != NULL)) { + icalcomponent_remove_property(vevent, prop); + icalproperty_free(prop); + } + + lprintf(9, "adding new property...\n"); + icalcomponent_add_property(vevent, icalproperty_new_transp(formtransp)); + lprintf(9, "...added it.\n"); + } + + /** Give this event a UID if it doesn't have one. */ + lprintf(9, "Give this event a UID if it doesn't have one.\n"); if (icalcomponent_get_first_property(vevent, ICAL_UID_PROPERTY) == NULL) { - generate_new_uid(buf); + generate_uuid(buf); icalcomponent_add_property(vevent, icalproperty_new_uid(buf) ); } - - /* Serialize it and save it to the message base */ - serv_puts("ENT0 1|||4"); - serv_gets(buf); - if (buf[0] == '4') { - serv_puts("Content-type: text/calendar"); - serv_puts(""); - serv_puts(icalcomponent_as_ical_string(vevent)); - serv_puts("000"); - delete_existing = 1; + + /** Increment the sequence ID */ + lprintf(9, "Increment the sequence ID\n"); + while (prop = icalcomponent_get_first_property(vevent, + ICAL_SEQUENCE_PROPERTY), (prop != NULL) ) { + i = icalproperty_get_sequence(prop); + lprintf(9, "Sequence was %d\n", i); + if (i > sequence) sequence = i; + icalcomponent_remove_property(vevent, prop); + icalproperty_free(prop); + } + ++sequence; + lprintf(9, "New sequence is %d. Adding...\n", sequence); + icalcomponent_add_property(vevent, + icalproperty_new_sequence(sequence) + ); + + /** + * Set the organizer, only if one does not already exist *and* + * the form is supplying one + */ + lprintf(9, "Setting the organizer...\n"); + strcpy(buf, bstr("organizer")); + if ( (icalcomponent_get_first_property(vevent, + ICAL_ORGANIZER_PROPERTY) == NULL) + && (strlen(buf) > 0) ) { + + /** set new organizer */ + sprintf(organizer_string, "MAILTO:%s", buf); + icalcomponent_add_property(vevent, + icalproperty_new_organizer(organizer_string) + ); + + } + + /** + * Add any new attendees listed in the web form + */ + lprintf(9, "Add any new attendees\n"); + + /* First, strip out the parenthesized partstats. */ + strcpy(form_attendees, bstr("attendees")); + stripout(form_attendees, '(', ')'); + + /** Now iterate! */ + for (i=0; i 0) { + lprintf(9, "Attendee: <%s>\n", buf); + sprintf(attendee_string, "MAILTO:%s", buf); + foundit = 0; + + for (attendee = icalcomponent_get_first_property(vevent, ICAL_ATTENDEE_PROPERTY); attendee != NULL; attendee = icalcomponent_get_next_property(vevent, ICAL_ATTENDEE_PROPERTY)) { + if (!strcasecmp(attendee_string, + icalproperty_get_attendee(attendee))) + ++foundit; + } + + + if (foundit == 0) { + icalcomponent_add_property(vevent, + icalproperty_new_attendee(attendee_string) + ); + } + } + } + + /** + * Remove any attendees *not* listed in the web form + */ +STARTOVER: lprintf(9, "Remove unlisted attendees\n"); + for (attendee = icalcomponent_get_first_property(vevent, ICAL_ATTENDEE_PROPERTY); attendee != NULL; attendee = icalcomponent_get_next_property(vevent, ICAL_ATTENDEE_PROPERTY)) { + strcpy(attendee_string, icalproperty_get_attendee(attendee)); + if (!strncasecmp(attendee_string, "MAILTO:", 7)) { + strcpy(attendee_string, &attendee_string[7]); + striplt(attendee_string); + foundit = 0; + for (i=0; i 0) ) { + serv_puts("ENT0 1|||4|||1|"); + serv_getln(buf, sizeof buf); + if (buf[0] == '8') { + serv_puts("Content-type: text/calendar"); + serv_puts(""); + serv_puts(icalcomponent_as_ical_string(encaps)); + serv_puts("000"); + } + if ( (buf[0] == '8') || (buf[0] == '4') ) { + while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { + lprintf(9, "ENT0 REPLY: %s\n", buf); + } + } + if (buf[0] == '2') { + strcpy(WC->ImportantMessage, &buf[4]); + } + icalcomponent_free(encaps); + } + + /** Or, check attendee availability if the user asked for that. */ + if ( (encaps != NULL) && (strlen(bstr("check_button")) > 0) ) { + + /** Call this function, which does the real work */ + check_attendee_availability(encaps); + + /** This displays the form again, with our annotations */ + display_edit_individual_event(encaps, msgnum); + + icalcomponent_free(encaps); } - } - /* - * If the user clicked 'Delete' then delete it, period. - */ - if (!strcasecmp(bstr("sc"), "Delete")) { - delete_existing = 1; } - if ( (delete_existing) && (msgnum > 0L) ) { + /** + * If the user clicked 'Delete' then delete it. + */ + lprintf(9, "Checking to see if we have to delete an old event\n"); + if ( (strlen(bstr("delete_button")) > 0) && (msgnum > 0L) ) { serv_printf("DELE %ld", atol(bstr("msgnum"))); - serv_gets(buf); + serv_getln(buf, sizeof buf); } if (created_new_vevent) { icalcomponent_free(vevent); } - /* Go back to the event list */ - readloop("readfwd"); + /** If this was a save or delete, go back to the calendar view. */ + if (strlen(bstr("check_button")) == 0) { + readloop("readfwd"); + } } -#endif /* HAVE_ICAL_H */ +#endif /* WEBCIT_WITH_CALENDAR_SERVICE */ + +/*@}*/