X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=webcit%2Fevent.c;h=6a72c0b973499a8e4bc15356e9d30cbf34514c6e;hb=2362c3d4de86f20822ab81015222a196137fd20e;hp=93632f6a21dac0a322891c65cc3daf6a6e8e0ec2;hpb=c6f1a8ea5b3950cc06c4e649ebd7d0bc59cb948f;p=citadel.git diff --git a/webcit/event.c b/webcit/event.c index 93632f6a2..6a72c0b97 100644 --- a/webcit/event.c +++ b/webcit/event.c @@ -2,71 +2,99 @@ * $Id$ * * Editing calendar events. - * */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include "webcit.h" #include "webserver.h" -#ifdef HAVE_ICAL_H - - /* - * 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) { +void display_edit_individual_event(icalcomponent *supplied_vevent, long msgnum, char *from, int unread) { 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; + + lprintf(9, "display_edit_individual_event(%ld) calview=%s year=%s month=%s day=%s\n", + msgnum, bstr("calview"), bstr("year"), bstr("month"), bstr("day") + ); 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, from, unread + ); + 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("

"); + wprintf(_("Add or edit an event")); + wprintf("

"); + wprintf("
\n"); + wprintf("
\n"); + + wprintf("
" + ""); - 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"); + + /** Transparency */ + wprintf("\n"); + + /** Attendees */ + wprintf("\n"); + + /** Done with properties. */ + wprintf("
\n"); + + /************************************************************ + * Uncomment this to see the UID in calendar events for debugging wprintf("UID == "); p = icalcomponent_get_first_property(vevent, ICAL_UID_PROPERTY); if (p != NULL) { escputs((char *)icalproperty_get_comment(p)); } - wprintf(" (FIXME remove this when done)
\n"); + wprintf("
\n"); + wprintf("SEQUENCE == %d
\n", sequence); + *************************************************************/ + + wprintf("
\n"); + wprintf("\n", WC->nonce); - wprintf("\n"); wprintf("\n", msgnum); wprintf("\n", @@ -78,10 +106,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 (havebstr("year")) { + tm_now.tm_year = ibstr("year") - 1900; + tm_now.tm_mon = ibstr("month") - 1; + tm_now.tm_mday = ibstr("day"); + } + if (havebstr("hour")) { + tm_now.tm_hour = ibstr("hour"); + tm_now.tm_min = ibstr("minute"); + tm_now.tm_sec = 0; + } + else { + tm_now.tm_hour = 0; + tm_now.tm_min = 0; + tm_now.tm_sec = 0; + } + + t_start = icaltime_from_timet_with_zone( + mktime(&tm_now), + ((yesbstr("alldayevent")) ? 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=\"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"); + + /** Pop open an address book -- begin **/ + wprintf( + " " + "" + "", + _("Attendees"), + _("Contacts") + ); + /** Pop open an address book -- end **/ + + wprintf("
" + "
\n
" + "" + "  " + "\n" "  " - "\n" + "\n" "  " - "\n" - "
\n" + "\n" + "\n", + _("Save"), + _("Delete"), + _("Check attendee availability"), + _("Cancel") ); wprintf("\n"); - + + wprintf("
\n"); + wprintf("\n" + ); + + address_book_popup(); wDumpContent(1); if (created_new_vevent) { @@ -149,120 +390,327 @@ 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) { +void save_individual_event(icalcomponent *supplied_vevent, long msgnum, char *from, int unread) { 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, j; + 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, from, unread + ); + return; + } } else { vevent = icalcomponent_new(ICAL_VEVENT_COMPONENT); created_new_vevent = 1; } - if (!strcasecmp(bstr("sc"), "Save")) { + if ( (havebstr("save_button")) + || (havebstr("check_button")) ) { - /* 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 (havebstr("summary")) { + + 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 (havebstr("location")) { + 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 (havebstr("description")) { + 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 (yesbstr("alldayevent")) { 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 (havebstr("transp")) { + 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); + } + + icalcomponent_add_property(vevent, icalproperty_new_transp(formtransp)); + } + + /** Give this event a UID if it doesn't have one. */ 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)); + } + + /** Increment the sequence ID */ + while (prop = icalcomponent_get_first_property(vevent, + ICAL_SEQUENCE_PROPERTY), (prop != NULL) ) { + i = icalproperty_get_sequence(prop); + if (i > sequence) sequence = i; + icalcomponent_remove_property(vevent, prop); + icalproperty_free(prop); + } + ++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 + */ + strcpy(buf, bstr("organizer")); + if ( (icalcomponent_get_first_property(vevent, + ICAL_ORGANIZER_PROPERTY) == NULL) + && (!IsEmptyStr(buf)) ) { + + /** set new organizer */ + sprintf(organizer_string, "MAILTO:%s", buf); icalcomponent_add_property(vevent, - icalproperty_new_uid(buf) + icalproperty_new_organizer(organizer_string) ); + } - - /* 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; + + /** + * Add any new attendees listed in the web form + */ + + /* First, strip out the parenthesized partstats. */ + strcpy(form_attendees, bstr("attendees")); + stripout(form_attendees, '(', ')'); + + /* Next, change any commas to newlines, because we want newline-separated attendees. */ + j = strlen(form_attendees); + for (i=0; iImportantMessage, &buf[4]); + } + icalmemory_free_ring (); + icalcomponent_free(encaps); + } + + /** Or, check attendee availability if the user asked for that. */ + if ( (encaps != NULL) && (havebstr("check_button")) ) { + + /** 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, from, unread); + + 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) ) { - serv_printf("DELE %ld", atol(bstr("msgnum"))); - serv_gets(buf); + /** + * If the user clicked 'Delete' then delete it. + */ + if ( (havebstr("delete_button")) && (msgnum > 0L) ) { + serv_printf("DELE %ld", lbstr("msgnum")); + 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 (!havebstr("check_button")) { + readloop("readfwd"); + } } -#endif /* HAVE_ICAL_H */