From 72015d547deb7e4632f70ad8dcae2d56ed8bfcca Mon Sep 17 00:00:00 2001 From: =?utf8?q?Wilfried=20G=C3=B6esgens?= Date: Thu, 20 Aug 2009 20:13:41 +0000 Subject: [PATCH] * split tasks view into its own file * re-arange readloop() so it presents a stable API to its views --- webcit/Makefile.in | 4 +- webcit/availability.c | 1 + webcit/calendar.c | 507 +++---------------------------- webcit/calendar_view.c | 200 ++---------- webcit/event.c | 5 +- webcit/messages.c | 490 +++++++---------------------- webcit/messages.h | 104 ++++++- webcit/msg_renderers.c | 328 +++++++++++++++++++- webcit/notes.c | 42 ++- webcit/smtpqueue.c | 4 +- webcit/summary.c | 24 +- webcit/tasks.c | 676 +++++++++++++++++++++++++++++++++++++++++ webcit/useredit.c | 5 +- webcit/vcard_edit.c | 92 +++++- webcit/webcit.h | 32 -- webcit/wiki.c | 23 ++ 16 files changed, 1472 insertions(+), 1065 deletions(-) create mode 100644 webcit/tasks.c diff --git a/webcit/Makefile.in b/webcit/Makefile.in index f6a90d416..4940c49aa 100644 --- a/webcit/Makefile.in +++ b/webcit/Makefile.in @@ -46,7 +46,7 @@ webcit: webserver.o context_loop.o ical_dezonify.o \ roomops.o messages.o msg_renderers.o userlist.o paging.o sysmsgs.o \ useredit.o vcard_edit.o preferences.o html2html.o listsub.o \ graphics.o netconf.o siteconfig.o subst.o \ - calendar.o calendar_tools.o calendar_view.o event.o smtpqueue.o \ + calendar.o calendar_tools.o calendar_view.o tasks.o event.o smtpqueue.o \ availability.o iconbar.o crypto.o inetconf.o notes.o wiki.o \ groupdav_main.o groupdav_get.o groupdav_propfind.o fmt_date.o \ groupdav_options.o autocompletion.o gettext.o tabs.o sieve.o \ @@ -61,7 +61,7 @@ webcit: webserver.o context_loop.o ical_dezonify.o \ roomops.o messages.o msg_renderers.o userlist.o paging.o sysmsgs.o \ useredit.o locate_host.o siteconfig.o subst.o vcard_edit.o floors.o \ graphics.o netconf.o preferences.o html2html.o openid.o \ - summary.o calendar.o calendar_tools.o calendar_view.o event.o wiki.o \ + summary.o calendar.o calendar_tools.o calendar_view.o tasks.o event.o wiki.o \ availability.o ical_dezonify.o iconbar.o crypto.o inetconf.o notes.o \ groupdav_main.o groupdav_get.o groupdav_propfind.o groupdav_delete.o \ groupdav_options.o autocompletion.o tabs.o smtpqueue.o sieve.o \ diff --git a/webcit/availability.c b/webcit/availability.c index 67b5139d3..8f1e39950 100644 --- a/webcit/availability.c +++ b/webcit/availability.c @@ -6,6 +6,7 @@ #include "webcit.h" #include "webserver.h" +#include "calendar.h" /* * Utility function to fetch a VFREEBUSY type of thing for any specified user. diff --git a/webcit/calendar.c b/webcit/calendar.c index 539c530fa..f0baec2dc 100644 --- a/webcit/calendar.c +++ b/webcit/calendar.c @@ -6,7 +6,7 @@ #include "webcit.h" #include "webserver.h" - +#include "calendar.h" /* * Process a calendar object. At this point it's already been deserialized by cal_process_attachment() @@ -396,7 +396,7 @@ void delete_cal(void *vCal) * any iCalendar objects and store them in a hash table. Later on, the second phase will * use this hash table to render the calendar for display. */ -void display_individual_cal(icalcomponent *cal, long msgnum, char *from, int unread, struct calview *calv) +void display_individual_cal(icalcomponent *cal, long msgnum, char *from, int unread, calview *calv) { icalproperty *ps = NULL; struct icaltimetype dtstart, dtend; @@ -579,415 +579,6 @@ void display_individual_cal(icalcomponent *cal, long msgnum, char *from, int unr -/* - * Display a task by itself (for editing) - */ -void display_edit_individual_task(icalcomponent *supplied_vtodo, long msgnum, char *from, - int unread, struct calview *calv) -{ - icalcomponent *vtodo; - icalproperty *p; - struct icaltimetype IcalTime; - time_t now; - int created_new_vtodo = 0; - icalproperty_status todoStatus; - - now = time(NULL); - - if (supplied_vtodo != NULL) { - vtodo = supplied_vtodo; - - /* - * It's safe to convert to UTC here because there are no recurrences to worry about. - */ - ical_dezonify(vtodo); - - /* - * If we're looking at a fully encapsulated VCALENDAR - * rather than a VTODO component, attempt to use the first - * relevant VTODO 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(vtodo) == ICAL_VCALENDAR_COMPONENT) { - display_edit_individual_task( - icalcomponent_get_first_component( - vtodo, ICAL_VTODO_COMPONENT - ), - msgnum, from, unread, calv - ); - return; - } - } - else { - vtodo = icalcomponent_new(ICAL_VTODO_COMPONENT); - created_new_vtodo = 1; - } - - /* TODO: Can we take all this and move it into a template? */ - output_headers(1, 1, 1, 0, 0, 0); - wprintf(""); - p = icalcomponent_get_first_property(vtodo, ICAL_SUMMARY_PROPERTY); - /* Get summary early for title */ - wprintf("
\n"); - wprintf("
"); - wprintf(_("Edit task")); - wprintf("- "); - if (p != NULL) { - escputs((char *)icalproperty_get_comment(p)); - } - wprintf("
"); - - wprintf("
\n"); - wprintf("
\n"); - wprintf("
\n "); - wprintf("\n", WC->nonce); - wprintf("\n", msgnum); - wprintf("\n", - ibstr("return_to_summary")); - wprintf("
"); - wprintf("
"); - wprintf("\n"); - - wprintf("\n"); - - wprintf("\n"); - - wprintf("\n"); - todoStatus = icalcomponent_get_status(vtodo); - wprintf(""); - /* start category field */ - p = icalcomponent_get_first_property(vtodo, ICAL_CATEGORIES_PROPERTY); - wprintf("\n "); - /* end category field */ - wprintf("
"); - wprintf(_("Summary:")); - wprintf("" - "
"); - wprintf(_("Start date:")); - wprintf(""); - p = icalcomponent_get_first_property(vtodo, ICAL_DTSTART_PROPERTY); - wprintf(""); - wprintf(_("No date")); - - wprintf(" "); - wprintf(""); - wprintf(_("or")); - wprintf(" "); - if (p != NULL) { - IcalTime = icalproperty_get_dtstart(p); - } - else - IcalTime = icaltime_current_time_with_zone(get_default_icaltimezone()); - display_icaltimetype_as_webform(&IcalTime, "dtstart", 0); - - wprintf(""); - wprintf(_("Time associated")); - wprintf("
"); - wprintf(_("Due date:")); - wprintf(""); - p = icalcomponent_get_first_property(vtodo, ICAL_DUE_PROPERTY); - wprintf(""); - wprintf(_("No date")); - wprintf(" "); - wprintf("\n"); - wprintf(_("or")); - wprintf(" "); - if (p != NULL) { - IcalTime = icalproperty_get_due(p); - } - else - IcalTime = icaltime_current_time_with_zone(get_default_icaltimezone()); - display_icaltimetype_as_webform(&IcalTime, "due", 0); - - wprintf(""); - wprintf(_("Time associated")); - wprintf("
\n"); - wprintf(_("Completed:")); - wprintf(""); - wprintf(""); - wprintf("
"); - wprintf(_("Category:")); - wprintf(""); - wprintf(""); - wprintf("
"); - wprintf(_("Description:")); - wprintf(""); - wprintf("
\n"); - - wprintf("" - "" - "  " - "\n" - "  " - "\n" - "\n", - _("Save"), - _("Delete"), - _("Cancel") - ); - wprintf("
"); - wprintf("
\n"); - wprintf("
\n"); - wprintf(""); - wDumpContent(1); - - if (created_new_vtodo) { - icalcomponent_free(vtodo); - } -} - -/* - * Save an edited task - * - * supplied_vtodo the task to save - * msgnum number of the mesage in our db - */ -void save_individual_task(icalcomponent *supplied_vtodo, long msgnum, char* from, int unread, - struct calview *calv) -{ - char buf[SIZ]; - int delete_existing = 0; - icalproperty *prop; - icalcomponent *vtodo, *encaps; - int created_new_vtodo = 0; - int i; - int sequence = 0; - struct icaltimetype t; - - if (supplied_vtodo != NULL) { - vtodo = supplied_vtodo; - /** - * If we're looking at a fully encapsulated VCALENDAR - * rather than a VTODO component, attempt to use the first - * relevant VTODO 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(vtodo) == ICAL_VCALENDAR_COMPONENT) { - save_individual_task( - icalcomponent_get_first_component( - vtodo, ICAL_VTODO_COMPONENT), - msgnum, from, unread, calv - ); - return; - } - } - else { - vtodo = icalcomponent_new(ICAL_VTODO_COMPONENT); - created_new_vtodo = 1; - } - - if (havebstr("save_button")) { - - /** Replace values in the component with ones from the form */ - - while (prop = icalcomponent_get_first_property(vtodo, - ICAL_SUMMARY_PROPERTY), prop != NULL) { - icalcomponent_remove_property(vtodo, prop); - icalproperty_free(prop); - } - if (havebstr("summary")) { - - icalcomponent_add_property(vtodo, - icalproperty_new_summary(bstr("summary"))); - } else { - icalcomponent_add_property(vtodo, - icalproperty_new_summary(_("Untitled Task"))); - } - - while (prop = icalcomponent_get_first_property(vtodo, - ICAL_DESCRIPTION_PROPERTY), prop != NULL) { - icalcomponent_remove_property(vtodo, prop); - icalproperty_free(prop); - } - if (havebstr("description")) { - icalcomponent_add_property(vtodo, - icalproperty_new_description(bstr("description"))); - } - - while (prop = icalcomponent_get_first_property(vtodo, - ICAL_DTSTART_PROPERTY), prop != NULL) { - icalcomponent_remove_property(vtodo, prop); - icalproperty_free(prop); - } - if (IsEmptyStr(bstr("nodtstart"))) { - if (yesbstr("dtstart_time")) { - icaltime_from_webform(&t, "dtstart"); - } - else { - icaltime_from_webform_dateonly(&t, "dtstart"); - } - icalcomponent_add_property(vtodo, - icalproperty_new_dtstart(t) - ); - } - while(prop = icalcomponent_get_first_property(vtodo, - ICAL_STATUS_PROPERTY), prop != NULL) { - icalcomponent_remove_property(vtodo,prop); - icalproperty_free(prop); - } - while(prop = icalcomponent_get_first_property(vtodo, - ICAL_PERCENTCOMPLETE_PROPERTY), prop != NULL) { - icalcomponent_remove_property(vtodo,prop); - icalproperty_free(prop); - } - - if (havebstr("status")) { - icalproperty_status taskStatus = icalproperty_string_to_status(bstr("status")); - icalcomponent_set_status(vtodo, taskStatus); - icalcomponent_add_property(vtodo, - icalproperty_new_percentcomplete( - (strcasecmp(bstr("status"), "completed") ? 0 : 100) - ) - ); - } - else { - icalcomponent_add_property(vtodo, icalproperty_new_percentcomplete(0)); - } - while (prop = icalcomponent_get_first_property(vtodo, - ICAL_CATEGORIES_PROPERTY), prop != NULL) { - icalcomponent_remove_property(vtodo,prop); - icalproperty_free(prop); - } - if (!IsEmptyStr(bstr("category"))) { - prop = icalproperty_new_categories(bstr("category")); - icalcomponent_add_property(vtodo,prop); - } - while (prop = icalcomponent_get_first_property(vtodo, - ICAL_DUE_PROPERTY), prop != NULL) { - icalcomponent_remove_property(vtodo, prop); - icalproperty_free(prop); - } - if (IsEmptyStr(bstr("nodue"))) { - if (yesbstr("due_time")) { - icaltime_from_webform(&t, "due"); - } - else { - icaltime_from_webform_dateonly(&t, "due"); - } - icalcomponent_add_property(vtodo, - icalproperty_new_due(t) - ); - } - /** Give this task a UID if it doesn't have one. */ - lprintf(9, "Give this task a UID if it doesn't have one.\n"); - if (icalcomponent_get_first_property(vtodo, - ICAL_UID_PROPERTY) == NULL) { - generate_uuid(buf); - icalcomponent_add_property(vtodo, - icalproperty_new_uid(buf) - ); - } - - /* Increment the sequence ID */ - lprintf(9, "Increment the sequence ID\n"); - while (prop = icalcomponent_get_first_property(vtodo, - 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(vtodo, prop); - icalproperty_free(prop); - } - ++sequence; - lprintf(9, "New sequence is %d. Adding...\n", sequence); - icalcomponent_add_property(vtodo, - icalproperty_new_sequence(sequence) - ); - - /* - * Encapsulate event into full VCALENDAR component. Clone it first, - * for two reasons: one, it's easier to just free the whole thing - * when we're done instead of unbundling, but more importantly, we - * can't encapsulate something that may already be encapsulated - * somewhere else. - */ - lprintf(9, "Encapsulating into a full VCALENDAR component\n"); - encaps = ical_encapsulate_subcomponent(icalcomponent_new_clone(vtodo)); - - /* Serialize it and save it to the message base */ - serv_puts("ENT0 1|||4"); - serv_getln(buf, sizeof buf); - if (buf[0] == '4') { - serv_puts("Content-type: text/calendar"); - serv_puts(""); - serv_puts(icalcomponent_as_ical_string(encaps)); - serv_puts("000"); - - /* - * Probably not necessary; the server will see the UID - * of the object and delete the old one anyway, but - * just in case... - */ - delete_existing = 1; - } - icalcomponent_free(encaps); - } - - /** - * If the user clicked 'Delete' then explicitly delete the message. - */ - if (havebstr("delete_button")) { - delete_existing = 1; - } - - if ( (delete_existing) && (msgnum > 0L) ) { - serv_printf("DELE %ld", lbstr("msgnum")); - serv_getln(buf, sizeof buf); - } - - if (created_new_vtodo) { - icalcomponent_free(vtodo); - } - - /* Go back to wherever we came from */ - if (ibstr("return_to_summary") == 1) { - summary(); - } - else { - readloop(readfwd); - } -} @@ -996,7 +587,7 @@ void process_ical_object(long msgnum, int unread, char *FlatIcal, icalcomponent_kind which_kind, IcalCallbackFunc CallBack, - struct calview *calv + calview *calv ) { icalcomponent *cal, *c; @@ -1039,7 +630,7 @@ void process_ical_object(long msgnum, int unread, void load_ical_object(long msgnum, int unread, icalcomponent_kind which_kind, IcalCallbackFunc CallBack, - struct calview *calv, + calview *calv, int RenderAsync ) { @@ -1169,55 +760,15 @@ void load_ical_object(long msgnum, int unread, /* * Display a calendar item */ -void load_calendar_item(message_summary *Msg, int unread, struct calview *c) { - load_ical_object(Msg->msgnum, unread, (-1), display_individual_cal, c, 1); -} - -/* - * Display task view - */ -void display_task(message_summary *Msg, int unread) { - load_ical_object(Msg->msgnum, unread, ICAL_VTODO_COMPONENT, display_individual_cal, NULL, 0); -} - -/* - * Display the editor component for a task - */ -void display_edit_task(void) { - long msgnum = 0L; - - /* Force change the room if we have to */ - if (havebstr("taskrm")) { - gotoroom(sbstr("taskrm")); - } - - msgnum = lbstr("msgnum"); - if (msgnum > 0L) { - /* existing task */ - load_ical_object(msgnum, 0, - ICAL_VTODO_COMPONENT, - display_edit_individual_task, - NULL, 0 - ); - } - else { - /* new task */ - display_edit_individual_task(NULL, 0L, "", 0, NULL); - } -} - -/* - * save an edited task - */ -void save_task(void) { - long msgnum = 0L; - msgnum = lbstr("msgnum"); - if (msgnum > 0L) { - load_ical_object(msgnum, 0, ICAL_VTODO_COMPONENT, save_individual_task, NULL, 0); - } - else { - save_individual_task(NULL, 0L, "", 0, NULL); - } +int calendar_LoadMsgFromServer(SharedMessageStatus *Stat, + void **ViewSpecific, + message_summary* Msg, + int is_new, + int i) +{ + calview *c = (calview*) *ViewSpecific; + load_ical_object(Msg->msgnum, is_new, (-1), display_individual_cal, c, 1); + return 0; } /* @@ -1300,18 +851,48 @@ void do_freebusy(void) +int calendar_Cleanup(void **ViewSpecific) +{ + calview *c; + + c = (calview *) *ViewSpecific; + + wDumpContent(1); + free (c); + *ViewSpecific = NULL; + + return 0; +} + void InitModule_CALENDAR (void) { + RegisterReadLoopHandlerset( + VIEW_CALENDAR, + calendar_GetParamsGetServerCall, + NULL, + calendar_LoadMsgFromServer, + calendar_RenderView_or_Tail, + calendar_Cleanup); + + RegisterReadLoopHandlerset( + VIEW_CALBRIEF, + calendar_GetParamsGetServerCall, + NULL, + calendar_LoadMsgFromServer, + calendar_RenderView_or_Tail, + calendar_Cleanup); + + + RegisterPreference("daystart", _("Calendar day view begins at:"), PRF_INT, NULL); RegisterPreference("dayend", _("Calendar day view ends at:"), PRF_INT, NULL); RegisterPreference("weekstart", _("Week starts on:"), PRF_INT, NULL); WebcitAddUrlHandler(HKEY("freebusy"), do_freebusy, COOKIEUNNEEDED|ANONYMOUS|FORCE_SESSIONCLOSE); WebcitAddUrlHandler(HKEY("display_edit_task"), display_edit_task, 0); - WebcitAddUrlHandler(HKEY("save_task"), save_task, 0); WebcitAddUrlHandler(HKEY("display_edit_event"), display_edit_event, 0); WebcitAddUrlHandler(HKEY("save_event"), save_event, 0); WebcitAddUrlHandler(HKEY("respond_to_request"), respond_to_request, 0); diff --git a/webcit/calendar_view.c b/webcit/calendar_view.c index 6d71049a9..48ac99466 100644 --- a/webcit/calendar_view.c +++ b/webcit/calendar_view.c @@ -6,6 +6,7 @@ #include "webcit.h" #include "webserver.h" +#include "calendar.h" /* These define how high the hour rows are in the day view */ #define TIMELINE 30 @@ -1425,12 +1426,27 @@ int calendar_summary_view(void) { /* * Parse the URL variables in order to determine the scope and display of a calendar view */ -void parse_calendar_view_request(struct calview *c) { +int calendar_GetParamsGetServerCall(SharedMessageStatus *Stat, + void **ViewSpecific, + long oper, + char *cmd, + long len) +{ + calview *c; time_t now; struct tm tm; - char calview[32]; + char cv[32]; + int span = 3888000; + c = (calview*) malloc(sizeof(calview)); + memset(c, 0, sizeof(calview)); + *ViewSpecific = (void*)c; + + Stat->load_seen = 1; + strcpy(cmd, "MSGS ALL"); + Stat->maxmsgs = 32767; + /* In case no date was specified, go with today */ now = time(NULL); localtime_r(&now, &tm); @@ -1445,20 +1461,20 @@ void parse_calendar_view_request(struct calview *c) { /* How would you like that cooked? */ if (havebstr("calview")) { - strcpy(calview, bstr("calview")); + strcpy(cv, bstr("calview")); } else { - strcpy(calview, "month"); + strcpy(cv, "month"); } /* Display the selected view */ - if (!strcasecmp(calview, "day")) { + if (!strcasecmp(cv, "day")) { c->view = calview_day; } - else if (!strcasecmp(calview, "week")) { + else if (!strcasecmp(cv, "week")) { c->view = calview_week; } - else if (!strcasecmp(calview, "summary")) { /* shouldn't ever happen, but just in case */ + else if (!strcasecmp(cv, "summary")) { /* shouldn't ever happen, but just in case */ c->view = calview_day; } else { @@ -1487,6 +1503,7 @@ void parse_calendar_view_request(struct calview *c) { c->lower_bound = now - span; c->upper_bound = now + span; + return 200; } @@ -1494,8 +1511,12 @@ void parse_calendar_view_request(struct calview *c) { /* * Render a calendar view from data previously loaded into memory */ -void render_calendar_view(struct calview *c) +int calendar_RenderView_or_Tail(SharedMessageStatus *Stat, + void **ViewSpecific, + long oper) { + calview *c = (calview*) *ViewSpecific; + if (c->view == calview_day) { calendar_day_view(c->year, c->month, c->day); } @@ -1513,173 +1534,10 @@ void render_calendar_view(struct calview *c) /* Free the in-memory list of calendar items */ DeleteHash(&WC->disp_cal_items); -} - - -/* - * Helper function for do_tasks_view(). Returns the due date/time of a vtodo. - */ -time_t get_task_due_date(icalcomponent *vtodo, int *is_date) { - icalproperty *p; - - if (vtodo == NULL) { - return(0L); - } - - /* - * If we're looking at a fully encapsulated VCALENDAR - * rather than a VTODO component, recurse into the data - * structure until we get a VTODO. - */ - if (icalcomponent_isa(vtodo) == ICAL_VCALENDAR_COMPONENT) { - return get_task_due_date( - icalcomponent_get_first_component( - vtodo, ICAL_VTODO_COMPONENT - ), is_date - ); - } - - p = icalcomponent_get_first_property(vtodo, ICAL_DUE_PROPERTY); - if (p != NULL) { - struct icaltimetype t = icalproperty_get_due(p); - - if (is_date) - *is_date = t.is_date; - return(icaltime_as_timet(t)); - } - else { - return(0L); - } -} - - -/* - * Compare the due dates of two tasks (this is for sorting) - */ -int task_due_cmp(const void *vtask1, const void *vtask2) { - disp_cal * Task1 = (disp_cal *)GetSearchPayload(vtask1); - disp_cal * Task2 = (disp_cal *)GetSearchPayload(vtask2); - - time_t t1; - time_t t2; - - t1 = get_task_due_date(Task1->cal, NULL); - t2 = get_task_due_date(Task2->cal, NULL); - if (t1 < t2) return(-1); - if (t1 > t2) return(1); - return(0); -} - -/* - * qsort filter to move completed tasks to bottom of task list - */ -int task_completed_cmp(const void *vtask1, const void *vtask2) { - disp_cal * Task1 = (disp_cal *)GetSearchPayload(vtask1); -/* disp_cal * Task2 = (disp_cal *)GetSearchPayload(vtask2); */ - - icalproperty_status t1 = icalcomponent_get_status((Task1)->cal); - /* icalproperty_status t2 = icalcomponent_get_status(((struct disp_cal *)task2)->cal); */ - - if (t1 == ICAL_STATUS_COMPLETED) - return 1; return 0; } -/* - * do the whole task view stuff - */ -void do_tasks_view(void) { - long hklen; - const char *HashKey; - void *vCal; - disp_cal *Cal; - HashPos *Pos; - int nItems; - time_t due; - char buf[SIZ]; - icalproperty *p; - wcsession *WCC = WC; - - wprintf("
" - "\n\n" - "\n", - _("Show All")); - - nItems = GetCount(WC->disp_cal_items); - - /* Sort them if necessary - if (nItems > 1) { - SortByPayload(WC->disp_cal_items, task_due_cmp); - } - * this shouldn't be neccessary, since we sort by the start time. - */ - - /* And then again, by completed */ - if (nItems > 1) { - SortByPayload(WC->disp_cal_items, - task_completed_cmp); - } - - Pos = GetNewHashPos(WCC->disp_cal_items, 0); - while (GetNextHashPos(WCC->disp_cal_items, Pos, &hklen, &HashKey, &vCal)) { - icalproperty_status todoStatus; - int is_date; - Cal = (disp_cal*)vCal; - wprintf("\n"); - - due = get_task_due_date(Cal->cal, &is_date); - wprintf(""); - wprintf(""); - wprintf(""); - } - - wprintf("
"); - wprintf(_("Completed?")); - wprintf(""); - wprintf(_("Name of task")); - wprintf(""); - wprintf(_("Date due")); - wprintf(""); - wprintf(_("Category")); - wprintf(" ()
"); - todoStatus = icalcomponent_get_status(Cal->cal); - wprintf("\n"); - p = icalcomponent_get_first_property(Cal->cal, - ICAL_SUMMARY_PROPERTY); - wprintf("cal_msgnum); - urlescputs(ChrPtr(WC->wc_roomname)); - wprintf("\">"); - /* wprintf(" "); */ - if (p != NULL) { - escputs((char *)icalproperty_get_comment(p)); - } - wprintf("\n"); - wprintf(" 0) { - webcit_fmt_date(buf, SIZ, due, is_date ? DATEFMT_RAWDATE : DATEFMT_FULL); - wprintf(">%s",buf); - } - else { - wprintf(">"); - } - wprintf(""); - p = icalcomponent_get_first_property(Cal->cal, - ICAL_CATEGORIES_PROPERTY); - if (p != NULL) { - escputs((char *)icalproperty_get_categories(p)); - } - wprintf("
\n"); - - /* Free the list */ - DeleteHash(&WC->disp_cal_items); - DeleteHashPos(&Pos); -} diff --git a/webcit/event.c b/webcit/event.c index 120091765..d1c7bbc6b 100644 --- a/webcit/event.c +++ b/webcit/event.c @@ -6,6 +6,7 @@ #include "webcit.h" #include "webserver.h" +#include "calendar.h" /* * Display an event by itself (for editing) @@ -13,7 +14,7 @@ * msgnum reference on the citserver */ void display_edit_individual_event(icalcomponent *supplied_vevent, long msgnum, char *from, - int unread, struct calview *calv) + int unread, calview *calv) { icalcomponent *vevent; icalproperty *p; @@ -765,7 +766,7 @@ void display_edit_individual_event(icalcomponent *supplied_vevent, long msgnum, * msgnum: the index on the citserver */ void save_individual_event(icalcomponent *supplied_vevent, long msgnum, char *from, - int unread, struct calview *calv) { + int unread, calview *calv) { char buf[SIZ]; icalproperty *prop; icalcomponent *vevent, *encaps; diff --git a/webcit/messages.c b/webcit/messages.c index 73fb0f37c..3c5439345 100644 --- a/webcit/messages.c +++ b/webcit/messages.c @@ -12,6 +12,7 @@ HashList *MsgHeaderHandler = NULL; HashList *MsgEvaluators = NULL; HashList *MimeRenderHandler = NULL; +HashList *ReadLoopHandler = NULL; int dbg_analyze_msg = 0; #define SUBJ_COL_WIDTH_PCT 50 /* Mailbox view column width */ @@ -569,41 +570,37 @@ message_summary *ReadOneMessageSummary(StrBuf *RawMessage, const char *DefaultSu * load message pointers from the server for a "read messages" operation * * servcmd: the citadel command to send to the citserver - * with_headers: also include some of the headers with the message numbers (more expensive) */ -int load_msg_ptrs(const char *servcmd, int with_headers, long *lowest_found, long *highest_found) +int load_msg_ptrs(const char *servcmd, SharedMessageStatus *Stat) { StrBuf* FoundCharset = NULL; wcsession *WCC = WC; message_summary *Msg; StrBuf *Buf, *Buf2; - int nummsgs = 0; - int maxload = 0; long len; int n; int skipit; const char *Ptr = NULL; - if (lowest_found) *lowest_found = LONG_MAX; - if (highest_found) *highest_found = LONG_MIN; + Stat->lowest_found = LONG_MAX; + Stat->highest_found = LONG_MIN; if (WCC->summ != NULL) { DeleteHash(&WCC->summ); } WCC->summ = NewHash(1, Flathash); - maxload = 10000; Buf = NewStrBuf(); serv_puts(servcmd); StrBuf_ServGetln(Buf); if (GetServerStatus(Buf, NULL) != 1) { FreeStrBuf(&Buf); - return (nummsgs); + return (Stat->nummsgs); } Buf2 = NewStrBuf(); while (len = StrBuf_ServGetln(Buf), ((len != 3) || strcmp(ChrPtr(Buf), "000")!= 0)) { - if (nummsgs < maxload) { + if (Stat->nummsgs < Stat->maxload) { skipit = 0; Ptr = NULL; Msg = (message_summary*)malloc(sizeof(message_summary)); @@ -612,12 +609,12 @@ int load_msg_ptrs(const char *servcmd, int with_headers, long *lowest_found, lon Msg->msgnum = StrBufExtractNext_long(Buf, &Ptr, '|'); Msg->date = StrBufExtractNext_long(Buf, &Ptr, '|'); - if (nummsgs == 0) { - if ((lowest_found) && (Msg->msgnum < *lowest_found)) { - *lowest_found = Msg->msgnum; + if (Stat->nummsgs == 0) { + if (Msg->msgnum < Stat->lowest_found) { + Stat->lowest_found = Msg->msgnum; } - if ((highest_found) && (Msg->msgnum > *highest_found)) { - *highest_found = Msg->msgnum; + if (Msg->msgnum > Stat->highest_found) { + Stat->highest_found = Msg->msgnum; } } @@ -631,7 +628,7 @@ int load_msg_ptrs(const char *servcmd, int with_headers, long *lowest_found, lon * nummsgs should be the same order as the message date. */ if (Msg->date == 0) { - Msg->date = nummsgs; + Msg->date = Stat->nummsgs; if (StrLength(Buf) < 32) skipit = 1; } @@ -680,11 +677,11 @@ int load_msg_ptrs(const char *servcmd, int with_headers, long *lowest_found, lon n = Msg->msgnum; Put(WCC->summ, (const char *)&n, sizeof(n), Msg, DestroyMessageSummary); } - nummsgs++; + Stat->nummsgs++; } FreeStrBuf(&Buf2); FreeStrBuf(&Buf); - return (nummsgs); + return (Stat->nummsgs); } @@ -701,97 +698,6 @@ inline message_summary* GetMessagePtrAt(int n, HashList *Summ) } -/* startmsg is an index within the message list. - * starting_from is the Citadel message number to be supplied to a "MSGS GT" operation - */ -long DrawMessageDropdown(StrBuf *Selector, long maxmsgs, long startmsg, int nMessages, long starting_from) -{ - StrBuf *TmpBuf; - wcsession *WCC = WC; - void *vMsg; - int lo, hi; - long ret; - long hklen; - const char *key; - int done = 0; - int nItems; - HashPos *At; - long vector[16]; - WCTemplputParams SubTP; - - memset(&SubTP, 0, sizeof(WCTemplputParams)); - SubTP.Filter.ContextType = CTX_LONGVECTOR; - SubTP.Context = &vector; - TmpBuf = NewStrBufPlain(NULL, SIZ); - At = GetNewHashPos(WCC->summ, nMessages); - nItems = GetCount(WCC->summ); - ret = nMessages; - vector[0] = 7; - vector[2] = 1; - vector[1] = startmsg; - vector[3] = 0; - vector[7] = starting_from; - - while (!done) { - vector[3] = abs(nMessages); - lo = GetHashPosCounter(At); - if (nMessages > 0) { - if (lo + nMessages >= nItems) { - hi = nItems - 1; - vector[3] = nItems - lo; - if (startmsg == lo) - ret = vector[3]; - } - else { - hi = lo + nMessages - 1; - } - } else { - if (lo + nMessages < -1) { - hi = 0; - } - else { - if ((lo % abs(nMessages)) != 0) { - int offset = (lo % abs(nMessages) * - (nMessages / abs(nMessages))); - hi = lo + offset; - vector[3] = abs(offset); - if (startmsg == lo) - ret = offset; - } - else - hi = lo + nMessages; - } - } - done = !GetNextHashPos(WCC->summ, At, &hklen, &key, &vMsg); - - /* - * Bump these because although we're thinking in zero base, the user - * is a drooling idiot and is thinking in one base. - */ - vector[4] = lo + 1; - vector[5] = hi + 1; - vector[6] = lo; - FlushStrBuf(TmpBuf); - dbg_print_longvector(vector); - DoTemplate(HKEY("select_messageindex"), TmpBuf, &SubTP); - StrBufAppendBuf(Selector, TmpBuf, 0); - } - vector[6] = 0; - FlushStrBuf(TmpBuf); - if (maxmsgs == 9999999) { - vector[1] = 1; - ret = maxmsgs; - } - else - vector[1] = 0; - vector[2] = 0; - dbg_print_longvector(vector); - DoTemplate(HKEY("select_messageindex_all"), TmpBuf, &SubTP); - StrBufAppendBuf(Selector, TmpBuf, 0); - FreeStrBuf(&TmpBuf); - DeleteHashPos(&At); - return ret; -} void load_seen_flags(void) { @@ -830,6 +736,17 @@ void load_seen_flags(void) extern readloop_struct rlid[]; +typedef struct _RoomRenderer{ + int RoomType; + + GetParamsGetServerCall_func GetParamsGetServerCall; + PrintViewHeader_func PrintViewHeader; + LoadMsgFromServer_func LoadMsgFromServer; + RenderView_or_Tail_func RenderView_or_Tail; + View_Cleanup_func ViewCleanup; +} RoomRenderer; + + /* * command loop for reading messages * @@ -837,328 +754,124 @@ extern readloop_struct rlid[]; */ void readloop(long oper) { - StrBuf *MessageDropdown = NULL; - StrBuf *BBViewToolBar = NULL; + RoomRenderer *ViewMsg; + void *vViewMsg; void *vMsg; message_summary *Msg; char cmd[256] = ""; - char buf[SIZ]; - int a = 0; - int with_headers = 0; - int nummsgs; - long startmsg = 0; - int maxmsgs = 0; - long *displayed_msgs = NULL; - int num_displayed = 0; - int is_singlecard = 0; - struct calview calv; int i; - int lowest_displayed = (-1); - int highest_displayed = 0; - addrbookent *addrbook = NULL; - int num_ab = 0; - int bbs_reverse = 0; wcsession *WCC = WC; HashPos *at; const char *HashKey; long HKLen; - int care_for_empty_list = 0; - int load_seen = 0; - int sortit = 0; - int defaultsortorder = 0; WCTemplputParams SubTP; - char *ab_name; - const StrBuf *Mime; - long lowest_found = (-1); - long highest_found = (-1); + SharedMessageStatus Stat; + void *ViewSpecific; if (havebstr("is_summary") && (1 == (ibstr("is_summary")))) WCC->wc_view = VIEW_MAILBOX; + memset(&Stat, 0, sizeof(SharedMessageStatus)); + Stat.maxload = 10000; + Stat.lowest_found = (-1); + Stat.highest_found = (-1); + GetHash(ReadLoopHandler, IKEY(WCC->wc_view), &vViewMsg); + if (vViewMsg == NULL) { + WCC->wc_view = VIEW_BBS; + GetHash(ReadLoopHandler, IKEY(WCC->wc_view), &vViewMsg); + } + if (vViewMsg == NULL) + return;///TODO: print message + + ViewMsg = (RoomRenderer*) vViewMsg; if (!WCC->is_ajax) { output_headers(1, 1, 1, 0, 0, 0); } else if (WCC->wc_view == VIEW_MAILBOX) { jsonMessageListHdr(); } - switch (WCC->wc_view) { - case VIEW_WIKI: - sprintf(buf, "wiki?room=%s&page=home", ChrPtr(WCC->wc_roomname)); - http_redirect(buf); + switch(ViewMsg->GetParamsGetServerCall( + &Stat, + &ViewSpecific, + oper, + cmd, sizeof(cmd))) + { + case 400: + case 404: + return; - case VIEW_CALBRIEF: - case VIEW_CALENDAR: - load_seen = 1; - strcpy(cmd, "MSGS ALL"); - maxmsgs = 32767; - parse_calendar_view_request(&calv); - break; - case VIEW_TASKS: - strcpy(cmd, "MSGS ALL"); - maxmsgs = 32767; - break; - case VIEW_NOTES: - strcpy(cmd, "MSGS ALL"); - maxmsgs = 32767; - wprintf("
\n"); - break; - case VIEW_ADDRESSBOOK: - is_singlecard = ibstr("is_singlecard"); - if (is_singlecard != 1) { - if (oper == do_search) { - snprintf(cmd, sizeof(cmd), "MSGS SEARCH|%s", bstr("query")); - } - else { - strcpy(cmd, "MSGS ALL"); - } - maxmsgs = 9999999; - break; - } - break; - case VIEW_MAILBOX: - if (!WCC->is_ajax) { - new_summary_view(); - return; - } else { - defaultsortorder = 2; - sortit = 1; - load_seen = 1; - care_for_empty_list = 0; - with_headers = 1; - /* Generally using maxmsgs|startmsg is not required - in mailbox view, but we have a 'safemode' for clients - (*cough* Exploder) that simply can't handle too many */ - if (havebstr("maxmsgs")) maxmsgs = ibstr("maxmsgs"); - else maxmsgs = 9999999; - if (havebstr("startmsg")) startmsg = lbstr("startmsg"); - snprintf(cmd, sizeof(cmd), "MSGS %s|%s||1", - (oper == do_search) ? "SEARCH" : "ALL", - (oper == do_search) ? bstr("query") : "" - ); - } - break; - case VIEW_BBS: + case 300: /* the callback hook should do the work for us here, since he knows what to do. */ + return; + case 200: default: - defaultsortorder = 1; - startmsg = -1; - sortit = 1; - care_for_empty_list = 1; - - rlid[oper].cmd(cmd, sizeof(cmd)); - - if (havebstr("maxmsgs")) - maxmsgs = ibstr("maxmsgs"); - if (maxmsgs == 0) maxmsgs = DEFAULT_MAXMSGS; - - if (havebstr("startmsg")) { - startmsg = lbstr("startmsg"); - } - - } - - nummsgs = load_msg_ptrs(cmd, with_headers, &lowest_found, &highest_found); - if (nummsgs == 0) { - if (care_for_empty_list) { - wprintf("

"); - switch (oper) { - case readnew: - wprintf(_("No new messages.")); - break; - case readold: - wprintf(_("No old messages.")); - break; - default: - wprintf(_("No messages here.")); - } - wprintf("
\n"); - goto DONE; - } - + break; } + if (!IsEmptyStr(cmd)) + Stat.nummsgs = load_msg_ptrs(cmd, &Stat); - if (sortit) { + if (Stat.sortit) { CompareFunc SortIt; memset(&SubTP, 0, sizeof(WCTemplputParams)); SubTP.Filter.ContextType = CTX_NONE; SubTP.Context = NULL; SortIt = RetrieveSort(&SubTP, NULL, 0, - HKEY("date"), defaultsortorder); + HKEY("date"), Stat.defaultsortorder); if (SortIt != NULL) SortByPayload(WCC->summ, SortIt); - if (WCC->wc_view == VIEW_BBS) { - if (lbstr("SortOrder") == 2) { - bbs_reverse = 1; - num_displayed = -DEFAULT_MAXMSGS; - } - else { - bbs_reverse = 0; - num_displayed = DEFAULT_MAXMSGS; - } - } } - if (startmsg < 0) startmsg = (bbs_reverse) ? nummsgs - 1 : 0; + if (Stat.startmsg < 0) + Stat.startmsg = (Stat.reverse) ? Stat.nummsgs - 1 : 0; - if (load_seen) load_seen_flags(); + if (Stat.load_seen) load_seen_flags(); /* * Print any inforation above the message list... */ - switch (WCC->wc_view) { - case VIEW_BBS: - BBViewToolBar = NewStrBufPlain(NULL, SIZ); - MessageDropdown = NewStrBufPlain(NULL, SIZ); - - maxmsgs = DrawMessageDropdown(MessageDropdown, maxmsgs, startmsg, - num_displayed, lowest_found-1); - if (num_displayed < 0) { - startmsg += maxmsgs; - if (num_displayed != maxmsgs) - maxmsgs = abs(maxmsgs) + 1; - else - maxmsgs = abs(maxmsgs); + if (ViewMsg->PrintViewHeader != NULL) + ViewMsg->PrintViewHeader(&Stat, &ViewSpecific); - } - memset(&SubTP, 0, sizeof(WCTemplputParams)); - SubTP.Filter.ContextType = CTX_STRBUF; - SubTP.Context = MessageDropdown; - DoTemplate(HKEY("msg_listselector_top"), BBViewToolBar, &SubTP); - StrBufAppendBuf(WCC->WBuf, BBViewToolBar, 0); - FlushStrBuf(BBViewToolBar); - break; - } - WCC->startmsg = startmsg; - WCC->maxmsgs = maxmsgs; + WCC->startmsg = Stat.startmsg; + WCC->maxmsgs = Stat.maxmsgs; WCC->num_displayed = 0; /* Put some helpful data in vars for mailsummary_json */ - svputlong("READLOOP:TOTALMSGS", nummsgs); - svputlong("READLOOP:STARTMSG", startmsg); + svputlong("READLOOP:TOTALMSGS", Stat.nummsgs); + svputlong("READLOOP:STARTMSG", Stat.startmsg); svputlong("WCVIEW", WCC->wc_view); /* * iterate over each message. if we need to load an attachment, do it here. */ - if (WCC->wc_view == VIEW_MAILBOX) goto NO_MSG_LOOP; - at = GetNewHashPos(WCC->summ, 0); - num_displayed = i = 0; - while (GetNextHashPos(WCC->summ, at, &HKLen, &HashKey, &vMsg)) { - Msg = (message_summary*) vMsg; - if ((Msg->msgnum >= startmsg) && (num_displayed <= maxmsgs)) { - switch (WCC->wc_view) { - case VIEW_WIKI: - break; - case VIEW_CALBRIEF: /* load the mime attachments for special tasks... */ - case VIEW_CALENDAR: - load_calendar_item(Msg, Msg->is_new, &calv); - break; - case VIEW_TASKS: - display_task(Msg, Msg->is_new); - break; - case VIEW_NOTES: - display_note(Msg, Msg->is_new); - break; - case VIEW_ADDRESSBOOK: - ab_name = NULL; - fetch_ab_name(Msg, &ab_name); - if (ab_name == NULL) - break; - ++num_ab; - addrbook = realloc(addrbook, - (sizeof(addrbookent) * num_ab) ); - safestrncpy(addrbook[num_ab-1].ab_name, ab_name, - sizeof(addrbook[num_ab-1].ab_name)); - addrbook[num_ab-1].ab_msgnum = Msg->msgnum; - free(ab_name); - break; - case VIEW_BBS: /* Tag the mails we want to show in bbview... */ - default: - if (displayed_msgs == NULL) { - displayed_msgs = malloc(sizeof(long) * - (maxmsgs= startmsg) && (i < startmsg + maxmsgs)) { - displayed_msgs[num_displayed] = Msg->msgnum; - if (lowest_displayed < 0) lowest_displayed = a; - highest_displayed = a; - - num_displayed++; - } - } - } - i++; + + if ((ViewMsg->LoadMsgFromServer != NULL) && + (!IsEmptyStr(cmd))) + { + at = GetNewHashPos(WCC->summ, 0); + Stat.num_displayed = i = 0; + while ( GetNextHashPos(WCC->summ, at, &HKLen, &HashKey, &vMsg)) { + Msg = (message_summary*) vMsg; + if ((Msg->msgnum >= Stat.startmsg) && (Stat.num_displayed <= Stat.maxmsgs)) { + ViewMsg->LoadMsgFromServer(&Stat, &ViewSpecific, Msg, Msg->is_new, i); + } + i++; + } + DeleteHashPos(&at); } - DeleteHashPos(&at); -NO_MSG_LOOP: /* * Done iterating the message list. now tasks we want to do after. */ - switch (WCC->wc_view) { - case VIEW_MAILBOX: - DoTemplate(HKEY("mailsummary_json"),NULL, &SubTP); - break; - case VIEW_BBS: - if (displayed_msgs != NULL) { - /* if we do a split bbview in the future, begin messages div here */ - - for (a=0; aWBuf, HKEY("view_message"), displayed_msgs[a], NULL, &Mime); - } - - /* if we do a split bbview in the future, end messages div here */ - - free(displayed_msgs); - displayed_msgs = NULL; - } - memset(&SubTP, 0, sizeof(WCTemplputParams)); - SubTP.Filter.ContextType = CTX_STRBUF; - SubTP.Context = MessageDropdown; - DoTemplate(HKEY("msg_listselector_bottom"), BBViewToolBar, &SubTP); - StrBufAppendBuf(WCC->WBuf, BBViewToolBar, 0); + if (ViewMsg->RenderView_or_Tail != NULL) + ViewMsg->RenderView_or_Tail(&Stat, &ViewSpecific, oper); - FreeStrBuf(&BBViewToolBar); - FreeStrBuf(&MessageDropdown); - } + if (ViewMsg->ViewCleanup != NULL) + ViewMsg->ViewCleanup(&ViewSpecific); - -DONE: - switch (WCC->wc_view) { - case VIEW_WIKI: - break; - case VIEW_CALBRIEF: - case VIEW_CALENDAR: - render_calendar_view(&calv); - break; - case VIEW_TASKS: - do_tasks_view(); /* Render the task list */ - break; - case VIEW_NOTES: - break; - case VIEW_ADDRESSBOOK: - if (is_singlecard) - read_message(WC->WBuf, HKEY("view_message"), lbstr("startmsg"), NULL, &Mime); - else - do_addrbook_view(addrbook, num_ab); /* Render the address book */ - break; - case VIEW_MAILBOX: - case VIEW_BBS: - default: - break; - } - /* Note: wDumpContent() will output one additional tag. */ - if (WCC->wc_view != VIEW_MAILBOX) { - /* We ought to move this out into template */ - wDumpContent(1); - } else { - end_burst(); - } WCC->startmsg = 0; WCC->maxmsgs = 0; if (WCC->summ != NULL) { DeleteHash(&WCC->summ); } - if (addrbook != NULL) free(addrbook); - FreeStrBuf(&BBViewToolBar); } @@ -1940,13 +1653,6 @@ void jsonMessageListHdr(void) begin_burst(); } -/* Spit out the new summary view. This is basically a static page, so clients can cache the layout, all the dirty work is javascript :) */ -void new_summary_view(void) { - begin_burst(); - DoTemplate(HKEY("msg_listview"),NULL,&NoCtx); - DoTemplate(HKEY("trailing"),NULL,&NoCtx); - end_burst(); -} /* Output message list in JSON format */ void jsonMessageList(void) { @@ -1958,6 +1664,30 @@ void jsonMessageList(void) { WC->is_ajax = 0; } +void RegisterReadLoopHandlerset( + int RoomType, + GetParamsGetServerCall_func GetParamsGetServerCall, + PrintViewHeader_func PrintViewHeader, + LoadMsgFromServer_func LoadMsgFromServer, + RenderView_or_Tail_func RenderView_or_Tail, + View_Cleanup_func ViewCleanup + ) +{ + RoomRenderer *Handler; + + Handler = (RoomRenderer*) malloc(sizeof(RoomRenderer)); + + Handler->RoomType = RoomType; + Handler->GetParamsGetServerCall = GetParamsGetServerCall; + Handler->PrintViewHeader = PrintViewHeader; + Handler->LoadMsgFromServer = LoadMsgFromServer; + Handler->RenderView_or_Tail = RenderView_or_Tail; + Handler->ViewCleanup = ViewCleanup; + + Put(ReadLoopHandler, IKEY(RoomType), Handler, NULL); + +} + void InitModule_MSG (void) diff --git a/webcit/messages.h b/webcit/messages.h index 29ed7b3b5..69f5e90ae 100644 --- a/webcit/messages.h +++ b/webcit/messages.h @@ -1,7 +1,7 @@ extern HashList *MsgHeaderHandler; extern HashList *MimeRenderHandler; - +extern HashList *ReadLoopHandler; typedef struct wc_mime_attachment wc_mime_attachment; typedef void (*RenderMimeFunc)(wc_mime_attachment *Mime, StrBuf *RawData, StrBuf *FoundCharset); typedef struct _RenderMimeFuncStruct { @@ -93,4 +93,104 @@ int load_message(message_summary *Msg, StrBuf **Error); -int load_msg_ptrs(const char *servcmd, int with_headers, long *lowest_found, long *highest_found); + + +typedef struct _SharedMessageStatus{ + long load_seen; /** should read information be loaded */ + long sortit; /** should we sort it using the standard sort API? */ + long defaultsortorder; /** if we should sort it, which direction should be the default? */ + + long maxload; /** how many headers should we accept from the server? defaults to 10k */ + long maxmsgs; /** how many message bodies do you want to load at most?*/ + long reverse; /** should the load-range be reversed? */ + + long startmsg; /** which is the start message ????? */ + long nummsgs; /** How many messages are available to your view? */ + long num_displayed; /** counted up for LoadMsgFromServer */ /* TODO: unclear who should access this and why */ + + long lowest_found; /** smallest Message ID found; */ + long highest_found; /** highest Message ID found; */ + +}SharedMessageStatus; + +int load_msg_ptrs(const char *servcmd, SharedMessageStatus *Stat); + +typedef int (*GetParamsGetServerCall_func)(SharedMessageStatus *Stat, + void **ViewSpecific, + long oper, + char *cmd, + long len); + +typedef int (*PrintViewHeader_func)(SharedMessageStatus *Stat, void **ViewSpecific); + +typedef int (*LoadMsgFromServer_func)(SharedMessageStatus *Stat, + void **ViewSpecific, + message_summary* Msg, + int is_new, + int i); + +typedef int (*RenderView_or_Tail_func)(SharedMessageStatus *Stat, + void **ViewSpecific, + long oper); +typedef int (*View_Cleanup_func)(void **ViewSpecific); + +void RegisterReadLoopHandlerset( + /** + * RoomType: which View definition are you going to be called for + */ + int RoomType, + + /** + * GetParamsGetServerCall should do the following: + * * allocate your private context structure + * * evaluate your commandline arguments, put results to your private struct. + * * fill cmd with the command to load the message pointer list: + * * might depend on bstr/oper depending on your needs + * * might stay empty if no list should loaded and LoadMsgFromServer + * is skipped. + * * influence the behaviour by presetting values on SharedMessageStatus + */ + GetParamsGetServerCall_func GetParamsGetServerCall, + + /** + * PrintViewHeader is here to print informations infront of your messages. + * The message list is already loaded & sorted (if) so you can evaluate + * its result on the SharedMessageStatus struct. + */ + PrintViewHeader_func PrintViewHeader, + + /** + * LoadMsgFromServer is called for every message in the message list: + * * which is + * * after 'startmsg' + * * up to 'maxmsgs' after your 'startmsg' + * * it should load and parse messages from citserer. + * * depending on your needs you might want to print your message here... + * * if cmd was empty, its skipped alltogether. + */ + LoadMsgFromServer_func LoadMsgFromServer, + + /** + * RenderView_or_Tail is called last; + * * if you used PrintViewHeader to print messages, you might want to print + * trailing information here + * * if you just pre-loaded your messages, put your render code here. + */ + RenderView_or_Tail_func RenderView_or_Tail, + + /** + * ViewCleanup should just clear your private data so all your mem can go back to + * VALgrindHALLA. + * it also should release the content for delivery via end_burst() or wDumpContent(1); + */ + View_Cleanup_func ViewCleanup + ); +/* +GetParamsGetServerCall + +PrintViewHeader + +LoadMsgFromServer + +RenderView_or_Tail +*/ diff --git a/webcit/msg_renderers.c b/webcit/msg_renderers.c index f17ae5415..55411e228 100644 --- a/webcit/msg_renderers.c +++ b/webcit/msg_renderers.c @@ -1070,6 +1070,98 @@ void tmplput_MIME_Length(StrBuf *Target, WCTemplputParams *TP) StrBufAppendPrintf(Target, "%ld", mime->length); } +/* startmsg is an index within the message list. + * starting_from is the Citadel message number to be supplied to a "MSGS GT" operation + */ +long DrawMessageDropdown(StrBuf *Selector, long maxmsgs, long startmsg, int nMessages, long starting_from) +{ + StrBuf *TmpBuf; + wcsession *WCC = WC; + void *vMsg; + int lo, hi; + long ret; + long hklen; + const char *key; + int done = 0; + int nItems; + HashPos *At; + long vector[16]; + WCTemplputParams SubTP; + + memset(&SubTP, 0, sizeof(WCTemplputParams)); + SubTP.Filter.ContextType = CTX_LONGVECTOR; + SubTP.Context = &vector; + TmpBuf = NewStrBufPlain(NULL, SIZ); + At = GetNewHashPos(WCC->summ, nMessages); + nItems = GetCount(WCC->summ); + ret = nMessages; + vector[0] = 7; + vector[2] = 1; + vector[1] = startmsg; + vector[3] = 0; + vector[7] = starting_from; + + while (!done) { + vector[3] = abs(nMessages); + lo = GetHashPosCounter(At); + if (nMessages > 0) { + if (lo + nMessages >= nItems) { + hi = nItems - 1; + vector[3] = nItems - lo; + if (startmsg == lo) + ret = vector[3]; + } + else { + hi = lo + nMessages - 1; + } + } else { + if (lo + nMessages < -1) { + hi = 0; + } + else { + if ((lo % abs(nMessages)) != 0) { + int offset = (lo % abs(nMessages) * + (nMessages / abs(nMessages))); + hi = lo + offset; + vector[3] = abs(offset); + if (startmsg == lo) + ret = offset; + } + else + hi = lo + nMessages; + } + } + done = !GetNextHashPos(WCC->summ, At, &hklen, &key, &vMsg); + + /* + * Bump these because although we're thinking in zero base, the user + * is a drooling idiot and is thinking in one base. + */ + vector[4] = lo + 1; + vector[5] = hi + 1; + vector[6] = lo; + FlushStrBuf(TmpBuf); + dbg_print_longvector(vector); + DoTemplate(HKEY("select_messageindex"), TmpBuf, &SubTP); + StrBufAppendBuf(Selector, TmpBuf, 0); + } + vector[6] = 0; + FlushStrBuf(TmpBuf); + if (maxmsgs == 9999999) { + vector[1] = 1; + ret = maxmsgs; + } + else + vector[1] = 0; + vector[2] = 0; + dbg_print_longvector(vector); + DoTemplate(HKEY("select_messageindex_all"), TmpBuf, &SubTP); + StrBufAppendBuf(Selector, TmpBuf, 0); + FreeStrBuf(&TmpBuf); + DeleteHashPos(&At); + return ret; +} + HashList *iterate_get_registered_Attachments(StrBuf *Target, WCTemplputParams *TP) { return WC->attachments; @@ -1112,15 +1204,247 @@ readloop_struct rlid[] = { { {HKEY("readfwd")}, servcmd_readfwd}, { {HKEY("readnew")}, servcmd_readnew}, { {HKEY("readold")}, servcmd_readold}, - { {HKEY("readgt")}, servcmd_readgt} + { {HKEY("readgt")}, servcmd_readgt} }; +/* Spit out the new summary view. This is basically a static page, so clients can cache the layout, all the dirty work is javascript :) */ +void new_summary_view(void) { + begin_burst(); + DoTemplate(HKEY("msg_listview"),NULL,&NoCtx); + DoTemplate(HKEY("trailing"),NULL,&NoCtx); + end_burst(); +} + + +int mailview_GetParamsGetServerCall(SharedMessageStatus *Stat, + void **ViewSpecific, + long oper, + char *cmd, + long len) +{ + if (!WC->is_ajax) { + new_summary_view(); + return 200; + } else { + Stat->defaultsortorder = 2; + Stat->sortit = 1; + Stat->load_seen = 1; + /* Generally using maxmsgs|startmsg is not required + in mailbox view, but we have a 'safemode' for clients + (*cough* Exploder) that simply can't handle too many */ + if (havebstr("maxmsgs")) Stat->maxmsgs = ibstr("maxmsgs"); + else Stat->maxmsgs = 9999999; + if (havebstr("startmsg")) Stat->startmsg = lbstr("startmsg"); + snprintf(cmd, len, "MSGS %s|%s||1", + (oper == do_search) ? "SEARCH" : "ALL", + (oper == do_search) ? bstr("query") : "" + ); + } + return 200; +} + +int mailview_RenderView_or_Tail(SharedMessageStatus *Stat, + void **ViewSpecific, + long oper) +{ + WCTemplputParams SubTP; + + DoTemplate(HKEY("mailsummary_json"),NULL, &SubTP); + return 0; +} + +int mailview_Cleanup(void **ViewSpecific) +{ + /* Note: wDumpContent() will output one additional tag. */ + /* We ought to move this out into template */ + if (WC->is_ajax) + end_burst(); + else + wDumpContent(1); + return 0; +} + + + +typedef struct _bbsview_stuct { + StrBuf *BBViewToolBar; + StrBuf *MessageDropdown; + long *displayed_msgs; + int a; +}bbsview_struct; + +int bbsview_GetParamsGetServerCall(SharedMessageStatus *Stat, + void **ViewSpecific, + long oper, + char *cmd, + long len) +{ + bbsview_struct *VS; + + VS = (bbsview_struct*) malloc(sizeof(bbsview_struct)); + memset(VS, 0, sizeof(bbsview_struct)); + *ViewSpecific = (void*)VS; + Stat->defaultsortorder = 1; + Stat->startmsg = -1; + Stat->sortit = 1; + + rlid[oper].cmd(cmd, len); + + if (havebstr("maxmsgs")) + Stat->maxmsgs = ibstr("maxmsgs"); + if (Stat->maxmsgs == 0) Stat->maxmsgs = DEFAULT_MAXMSGS; + + if (havebstr("startmsg")) { + Stat->startmsg = lbstr("startmsg"); + } + if (lbstr("SortOrder") == 2) { + Stat->reverse = 1; + Stat->num_displayed = -DEFAULT_MAXMSGS; + } + else { + Stat->reverse = 0; + Stat->num_displayed = DEFAULT_MAXMSGS; + } + + return 200; +} + +int bbsview_PrintViewHeader(SharedMessageStatus *Stat, void **ViewSpecific) +{ + bbsview_struct *VS; + WCTemplputParams SubTP; + + VS = (bbsview_struct*)*ViewSpecific; + + VS->BBViewToolBar = NewStrBufPlain(NULL, SIZ); + VS->MessageDropdown = NewStrBufPlain(NULL, SIZ); + + /*** startmsg->maxmsgs = **/DrawMessageDropdown(VS->MessageDropdown, + Stat->maxmsgs, + Stat->startmsg, + Stat->num_displayed, + Stat->lowest_found-1); + if (Stat->num_displayed < 0) { + Stat->startmsg += Stat->maxmsgs; + if (Stat->num_displayed != Stat->maxmsgs) + Stat->maxmsgs = abs(Stat->maxmsgs) + 1; + else + Stat->maxmsgs = abs(Stat->maxmsgs); + + } + memset(&SubTP, 0, sizeof(WCTemplputParams)); + SubTP.Filter.ContextType = CTX_STRBUF; + SubTP.Context = VS->MessageDropdown; + DoTemplate(HKEY("msg_listselector_top"), VS->BBViewToolBar, &SubTP); + StrBufAppendBuf(WC->WBuf, VS->BBViewToolBar, 0); + FlushStrBuf(VS->BBViewToolBar); + return 200; +} + +int bbsview_LoadMsgFromServer(SharedMessageStatus *Stat, + void **ViewSpecific, + message_summary* Msg, + int is_new, + int i) +{ + bbsview_struct *VS; + + VS = (bbsview_struct*)*ViewSpecific; + if (VS->displayed_msgs == NULL) { + VS->displayed_msgs = malloc(sizeof(long) * + ((Stat->maxmsgs < Stat->nummsgs) ? + Stat->maxmsgs + 1 : + Stat->nummsgs + 1)); + } + if ((i >= Stat->startmsg) && (i < Stat->startmsg + Stat->maxmsgs)) { + VS->displayed_msgs[Stat->num_displayed] = Msg->msgnum; + Stat->num_displayed++; + } + return 200; +} +int bbsview_RenderView_or_Tail(SharedMessageStatus *Stat, + void **ViewSpecific, + long oper) +{ + wcsession *WCC = WC; + bbsview_struct *VS; + WCTemplputParams SubTP; + const StrBuf *Mime; + + VS = (bbsview_struct*)*ViewSpecific; + if (Stat->nummsgs == 0) { + wprintf("

"); + switch (oper) { + case readnew: + wprintf(_("No new messages.")); + break; + case readold: + wprintf(_("No old messages.")); + break; + default: + wprintf(_("No messages here.")); + } + wprintf("
\n"); + } + else + { + if (VS->displayed_msgs != NULL) { + /* if we do a split bbview in the future, begin messages div here */ + int a;/// todo + for (a=0; a < Stat->num_displayed; ++a) { + read_message(WCC->WBuf, HKEY("view_message"), VS->displayed_msgs[a], NULL, &Mime); + } + + /* if we do a split bbview in the future, end messages div here */ + + free(VS->displayed_msgs); + VS->displayed_msgs = NULL; + } + memset(&SubTP, 0, sizeof(WCTemplputParams)); + SubTP.Filter.ContextType = CTX_STRBUF; + SubTP.Context = VS->MessageDropdown; + DoTemplate(HKEY("msg_listselector_bottom"), VS->BBViewToolBar, &SubTP); + StrBufAppendBuf(WCC->WBuf, VS->BBViewToolBar, 0); + } + return 0; + +} + + +int bbsview_Cleanup(void **ViewSpecific) +{ + bbsview_struct *VS; + + VS = (bbsview_struct*)*ViewSpecific; + end_burst(); + FreeStrBuf(&VS->BBViewToolBar); + FreeStrBuf(&VS->MessageDropdown); + free(VS); + return 0; +} + void InitModule_MSGRENDERERS (void) { + RegisterReadLoopHandlerset( + VIEW_MAILBOX, + mailview_GetParamsGetServerCall, + NULL, /// TODO: is this right? + NULL, //// "" + mailview_RenderView_or_Tail, + mailview_Cleanup); + + RegisterReadLoopHandlerset( + VIEW_BBS, + bbsview_GetParamsGetServerCall, + bbsview_PrintViewHeader, + bbsview_LoadMsgFromServer, + bbsview_RenderView_or_Tail, + bbsview_Cleanup); + RegisterSortFunc(HKEY("date"), NULL, 0, summcmp_date, @@ -1253,6 +1577,7 @@ ServerStartModule_MSGRENDERERS { MsgHeaderHandler = NewHash(1, NULL); MimeRenderHandler = NewHash(1, NULL); + ReadLoopHandler = NewHash(1, NULL); } void @@ -1261,6 +1586,7 @@ ServerShutdownModule_MSGRENDERERS { DeleteHash(&MsgHeaderHandler); DeleteHash(&MimeRenderHandler); + DeleteHash(&ReadLoopHandler); } diff --git a/webcit/notes.c b/webcit/notes.c index a8e02b04c..d9a50bc68 100644 --- a/webcit/notes.c +++ b/webcit/notes.c @@ -296,13 +296,19 @@ void ajax_update_note(void) { * * msgnum = Message number on the local server of the note to be displayed */ -void display_note(message_summary *Msg, int unread) { +////TODO: falscher hook +int notes_LoadMsgFromServer(SharedMessageStatus *Stat, + void **ViewSpecific, + message_summary* Msg, + int is_new, + int i) +{ struct vnote *v; WCTemplputParams TP; memset(&TP, 0, sizeof(WCTemplputParams)); TP.Filter.ContextType = CTX_VNOTE; - v = vnote_new_from_msg(Msg->msgnum, unread); + v = vnote_new_from_msg(Msg->msgnum, is_new); if (v) { TP.Context = v; DoTemplate(HKEY("vnoteitem"), @@ -322,6 +328,7 @@ void display_note(message_summary *Msg, int unread) { vnote_free(v); } + return 0; } @@ -408,10 +415,41 @@ void tmpl_vcard_put_uid(StrBuf *Target, WCTemplputParams *TP) StrBufAppendBufPlain(Target, v->uid, -1, 0); } + + + +int notes_GetParamsGetServerCall(SharedMessageStatus *Stat, + void **ViewSpecific, + long oper, + char *cmd, + long len) +{ + strcpy(cmd, "MSGS ALL"); + Stat->maxmsgs = 32767; + wprintf("
\n"); + return 200; + +} + +int notes_Cleanup(void **ViewSpecific) +{ + end_burst(); + return 0; +} + + void InitModule_NOTES (void) { + RegisterReadLoopHandlerset( + VIEW_NOTES, + notes_GetParamsGetServerCall, + NULL, + notes_LoadMsgFromServer, + NULL, + notes_Cleanup); + WebcitAddUrlHandler(HKEY("add_new_note"), add_new_note, 0); WebcitAddUrlHandler(HKEY("ajax_update_note"), ajax_update_note, 0); diff --git a/webcit/smtpqueue.c b/webcit/smtpqueue.c index 2e74064ec..503dc6393 100644 --- a/webcit/smtpqueue.c +++ b/webcit/smtpqueue.c @@ -169,7 +169,9 @@ void display_smtpqueue_inner_div(void) { int i; int num_msgs; StrBuf *Buf; + SharedMessageStatus Stat; + memset(&Stat, 0, sizeof(SharedMessageStatus)); /* Check to see if we can go to the __CitadelSMTPspoolout__ room. * If not, we don't have access to the queue. */ @@ -178,7 +180,7 @@ void display_smtpqueue_inner_div(void) { FreeStrBuf(&Buf); if (!strcasecmp(ChrPtr(WCC->wc_roomname), "__CitadelSMTPspoolout__")) { - num_msgs = load_msg_ptrs("MSGS ALL", 0, NULL, NULL); + num_msgs = load_msg_ptrs("MSGS ALL", &Msg); if (num_msgs > 0) { wprintf("" diff --git a/webcit/summary.c b/webcit/summary.c index ae2588da2..fb34a8583 100644 --- a/webcit/summary.c +++ b/webcit/summary.c @@ -5,6 +5,7 @@ */ #include "webcit.h" +#include "calendar.h" /* * Display today's date in a friendly format @@ -84,7 +85,9 @@ void tasks_section(void) { message_summary *Msg; wcsession *WCC = WC; StrBuf *Buf; + SharedMessageStatus Stat; + memset(&Stat, 0, sizeof(SharedMessageStatus)); Buf = NewStrBufPlain(HKEY("_TASKS_")); gotoroom(Buf); FreeStrBuf(&Buf); @@ -92,14 +95,14 @@ void tasks_section(void) { num_msgs = 0; } else { - num_msgs = load_msg_ptrs("MSGS ALL", 0, NULL, NULL); + num_msgs = load_msg_ptrs("MSGS ALL", &Stat); } if (num_msgs > 0) { at = GetNewHashPos(WCC->summ, 0); while (GetNextHashPos(WCC->summ, at, &HKLen, &HashKey, &vMsg)) { Msg = (message_summary*) vMsg; - display_task(Msg, 0); + tasks_LoadMsgFromServer(NULL, NULL, Msg, 0, 0); } DeleteHashPos(&at); } @@ -116,6 +119,7 @@ void tasks_section(void) { * Calendar section */ void calendar_section(void) { + char cmd[SIZ]; int num_msgs = 0; HashPos *at; const char *HashKey; @@ -123,9 +127,12 @@ void calendar_section(void) { void *vMsg; message_summary *Msg; wcsession *WCC = WC; - struct calview c; + calview c; StrBuf *Buf; + void *v = &c; + SharedMessageStatus Stat; + memset(&Stat, 0, sizeof(SharedMessageStatus)); Buf = NewStrBufPlain(HKEY("_CALENDAR_")); gotoroom(Buf); FreeStrBuf(&Buf); @@ -133,16 +140,19 @@ void calendar_section(void) { num_msgs = 0; } else { - num_msgs = load_msg_ptrs("MSGS ALL", 0, NULL, NULL); + num_msgs = load_msg_ptrs("MSGS ALL", &Stat); } - - parse_calendar_view_request(&c); + calendar_GetParamsGetServerCall(&Stat, + &c, + readnew, + cmd, + sizeof(cmd)); if (num_msgs > 0) { at = GetNewHashPos(WCC->summ, 0); while (GetNextHashPos(WCC->summ, at, &HKLen, &HashKey, &vMsg)) { Msg = (message_summary*) vMsg; - load_calendar_item(Msg, 0, &c); + calendar_LoadMsgFromServer(NULL, &v, Msg, 0, 0); } DeleteHashPos(&at); } diff --git a/webcit/tasks.c b/webcit/tasks.c new file mode 100644 index 000000000..fecdb67cf --- /dev/null +++ b/webcit/tasks.c @@ -0,0 +1,676 @@ +#include "webcit.h" +#include "calendar.h" +#include "webserver.h" + +/* + * qsort filter to move completed tasks to bottom of task list + */ +int task_completed_cmp(const void *vtask1, const void *vtask2) { + disp_cal * Task1 = (disp_cal *)GetSearchPayload(vtask1); +/* disp_cal * Task2 = (disp_cal *)GetSearchPayload(vtask2); */ + + icalproperty_status t1 = icalcomponent_get_status((Task1)->cal); + /* icalproperty_status t2 = icalcomponent_get_status(((struct disp_cal *)task2)->cal); */ + + if (t1 == ICAL_STATUS_COMPLETED) + return 1; + return 0; +} + + +/* + * Helper function for do_tasks_view(). Returns the due date/time of a vtodo. + */ +time_t get_task_due_date(icalcomponent *vtodo, int *is_date) { + icalproperty *p; + + if (vtodo == NULL) { + return(0L); + } + + /* + * If we're looking at a fully encapsulated VCALENDAR + * rather than a VTODO component, recurse into the data + * structure until we get a VTODO. + */ + if (icalcomponent_isa(vtodo) == ICAL_VCALENDAR_COMPONENT) { + return get_task_due_date( + icalcomponent_get_first_component( + vtodo, ICAL_VTODO_COMPONENT + ), is_date + ); + } + + p = icalcomponent_get_first_property(vtodo, ICAL_DUE_PROPERTY); + if (p != NULL) { + struct icaltimetype t = icalproperty_get_due(p); + + if (is_date) + *is_date = t.is_date; + return(icaltime_as_timet(t)); + } + else { + return(0L); + } +} + +/* + * Compare the due dates of two tasks (this is for sorting) + */ +int task_due_cmp(const void *vtask1, const void *vtask2) { + disp_cal * Task1 = (disp_cal *)GetSearchPayload(vtask1); + disp_cal * Task2 = (disp_cal *)GetSearchPayload(vtask2); + + time_t t1; + time_t t2; + + t1 = get_task_due_date(Task1->cal, NULL); + t2 = get_task_due_date(Task2->cal, NULL); + if (t1 < t2) return(-1); + if (t1 > t2) return(1); + return(0); +} + +/* + * do the whole task view stuff + */ +int tasks_RenderView_or_Tail(SharedMessageStatus *Stat, + void **ViewSpecific, + long oper) +{ + long hklen; + const char *HashKey; + void *vCal; + disp_cal *Cal; + HashPos *Pos; + int nItems; + time_t due; + char buf[SIZ]; + icalproperty *p; + wcsession *WCC = WC; + + wprintf("
" + "
\n\n" + "\n", + _("Show All")); + + nItems = GetCount(WC->disp_cal_items); + + /* Sort them if necessary + if (nItems > 1) { + SortByPayload(WC->disp_cal_items, task_due_cmp); + } + * this shouldn't be neccessary, since we sort by the start time. + */ + + /* And then again, by completed */ + if (nItems > 1) { + SortByPayload(WC->disp_cal_items, + task_completed_cmp); + } + + Pos = GetNewHashPos(WCC->disp_cal_items, 0); + while (GetNextHashPos(WCC->disp_cal_items, Pos, &hklen, &HashKey, &vCal)) { + icalproperty_status todoStatus; + int is_date; + + Cal = (disp_cal*)vCal; + wprintf("\n"); + + due = get_task_due_date(Cal->cal, &is_date); + wprintf(""); + wprintf(""); + wprintf(""); + } + + wprintf("
"); + wprintf(_("Completed?")); + wprintf(""); + wprintf(_("Name of task")); + wprintf(""); + wprintf(_("Date due")); + wprintf(""); + wprintf(_("Category")); + wprintf(" ()
"); + todoStatus = icalcomponent_get_status(Cal->cal); + wprintf("\n"); + p = icalcomponent_get_first_property(Cal->cal, + ICAL_SUMMARY_PROPERTY); + wprintf("cal_msgnum); + urlescputs(ChrPtr(WC->wc_roomname)); + wprintf("\">"); + /* wprintf(" "); */ + if (p != NULL) { + escputs((char *)icalproperty_get_comment(p)); + } + wprintf("\n"); + wprintf(" 0) { + webcit_fmt_date(buf, SIZ, due, is_date ? DATEFMT_RAWDATE : DATEFMT_FULL); + wprintf(">%s",buf); + } + else { + wprintf(">"); + } + wprintf(""); + p = icalcomponent_get_first_property(Cal->cal, + ICAL_CATEGORIES_PROPERTY); + if (p != NULL) { + escputs((char *)icalproperty_get_categories(p)); + } + wprintf("
\n"); + + /* Free the list */ + DeleteHash(&WC->disp_cal_items); + DeleteHashPos(&Pos); + return 0; +} + + +/* + * Display a task by itself (for editing) + */ +void display_edit_individual_task(icalcomponent *supplied_vtodo, long msgnum, char *from, + int unread, calview *calv) +{ + icalcomponent *vtodo; + icalproperty *p; + struct icaltimetype IcalTime; + time_t now; + int created_new_vtodo = 0; + icalproperty_status todoStatus; + + now = time(NULL); + + if (supplied_vtodo != NULL) { + vtodo = supplied_vtodo; + + /* + * It's safe to convert to UTC here because there are no recurrences to worry about. + */ + ical_dezonify(vtodo); + + /* + * If we're looking at a fully encapsulated VCALENDAR + * rather than a VTODO component, attempt to use the first + * relevant VTODO 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(vtodo) == ICAL_VCALENDAR_COMPONENT) { + display_edit_individual_task( + icalcomponent_get_first_component( + vtodo, ICAL_VTODO_COMPONENT + ), + msgnum, from, unread, calv + ); + return; + } + } + else { + vtodo = icalcomponent_new(ICAL_VTODO_COMPONENT); + created_new_vtodo = 1; + } + + /* TODO: Can we take all this and move it into a template? */ + output_headers(1, 1, 1, 0, 0, 0); + wprintf(""); + p = icalcomponent_get_first_property(vtodo, ICAL_SUMMARY_PROPERTY); + /* Get summary early for title */ + wprintf("
\n"); + wprintf("
"); + wprintf(_("Edit task")); + wprintf("- "); + if (p != NULL) { + escputs((char *)icalproperty_get_comment(p)); + } + wprintf("
"); + + wprintf("
\n"); + wprintf("
\n"); + wprintf("
\n "); + wprintf("\n", WC->nonce); + wprintf("\n", msgnum); + wprintf("\n", + ibstr("return_to_summary")); + wprintf("
"); + wprintf("
"); + wprintf("\n"); + + wprintf("\n"); + + wprintf("\n"); + + wprintf("\n"); + todoStatus = icalcomponent_get_status(vtodo); + wprintf(""); + /* start category field */ + p = icalcomponent_get_first_property(vtodo, ICAL_CATEGORIES_PROPERTY); + wprintf("\n "); + /* end category field */ + wprintf("
"); + wprintf(_("Summary:")); + wprintf("" + "
"); + wprintf(_("Start date:")); + wprintf(""); + p = icalcomponent_get_first_property(vtodo, ICAL_DTSTART_PROPERTY); + wprintf(""); + wprintf(_("No date")); + + wprintf(" "); + wprintf(""); + wprintf(_("or")); + wprintf(" "); + if (p != NULL) { + IcalTime = icalproperty_get_dtstart(p); + } + else + IcalTime = icaltime_current_time_with_zone(get_default_icaltimezone()); + display_icaltimetype_as_webform(&IcalTime, "dtstart", 0); + + wprintf(""); + wprintf(_("Time associated")); + wprintf("
"); + wprintf(_("Due date:")); + wprintf(""); + p = icalcomponent_get_first_property(vtodo, ICAL_DUE_PROPERTY); + wprintf(""); + wprintf(_("No date")); + wprintf(" "); + wprintf("\n"); + wprintf(_("or")); + wprintf(" "); + if (p != NULL) { + IcalTime = icalproperty_get_due(p); + } + else + IcalTime = icaltime_current_time_with_zone(get_default_icaltimezone()); + display_icaltimetype_as_webform(&IcalTime, "due", 0); + + wprintf(""); + wprintf(_("Time associated")); + wprintf("
\n"); + wprintf(_("Completed:")); + wprintf(""); + wprintf(""); + wprintf("
"); + wprintf(_("Category:")); + wprintf(""); + wprintf(""); + wprintf("
"); + wprintf(_("Description:")); + wprintf(""); + wprintf("
\n"); + + wprintf("" + "" + "  " + "\n" + "  " + "\n" + "\n", + _("Save"), + _("Delete"), + _("Cancel") + ); + wprintf("
"); + wprintf("
\n"); + wprintf("
\n"); + wprintf(""); + wDumpContent(1); + + if (created_new_vtodo) { + icalcomponent_free(vtodo); + } +} + +/* + * Save an edited task + * + * supplied_vtodo the task to save + * msgnum number of the mesage in our db + */ +void save_individual_task(icalcomponent *supplied_vtodo, long msgnum, char* from, int unread, + calview *calv) +{ + char buf[SIZ]; + int delete_existing = 0; + icalproperty *prop; + icalcomponent *vtodo, *encaps; + int created_new_vtodo = 0; + int i; + int sequence = 0; + struct icaltimetype t; + + if (supplied_vtodo != NULL) { + vtodo = supplied_vtodo; + /** + * If we're looking at a fully encapsulated VCALENDAR + * rather than a VTODO component, attempt to use the first + * relevant VTODO 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(vtodo) == ICAL_VCALENDAR_COMPONENT) { + save_individual_task( + icalcomponent_get_first_component( + vtodo, ICAL_VTODO_COMPONENT), + msgnum, from, unread, calv + ); + return; + } + } + else { + vtodo = icalcomponent_new(ICAL_VTODO_COMPONENT); + created_new_vtodo = 1; + } + + if (havebstr("save_button")) { + + /** Replace values in the component with ones from the form */ + + while (prop = icalcomponent_get_first_property(vtodo, + ICAL_SUMMARY_PROPERTY), prop != NULL) { + icalcomponent_remove_property(vtodo, prop); + icalproperty_free(prop); + } + if (havebstr("summary")) { + + icalcomponent_add_property(vtodo, + icalproperty_new_summary(bstr("summary"))); + } else { + icalcomponent_add_property(vtodo, + icalproperty_new_summary(_("Untitled Task"))); + } + + while (prop = icalcomponent_get_first_property(vtodo, + ICAL_DESCRIPTION_PROPERTY), prop != NULL) { + icalcomponent_remove_property(vtodo, prop); + icalproperty_free(prop); + } + if (havebstr("description")) { + icalcomponent_add_property(vtodo, + icalproperty_new_description(bstr("description"))); + } + + while (prop = icalcomponent_get_first_property(vtodo, + ICAL_DTSTART_PROPERTY), prop != NULL) { + icalcomponent_remove_property(vtodo, prop); + icalproperty_free(prop); + } + if (IsEmptyStr(bstr("nodtstart"))) { + if (yesbstr("dtstart_time")) { + icaltime_from_webform(&t, "dtstart"); + } + else { + icaltime_from_webform_dateonly(&t, "dtstart"); + } + icalcomponent_add_property(vtodo, + icalproperty_new_dtstart(t) + ); + } + while(prop = icalcomponent_get_first_property(vtodo, + ICAL_STATUS_PROPERTY), prop != NULL) { + icalcomponent_remove_property(vtodo,prop); + icalproperty_free(prop); + } + while(prop = icalcomponent_get_first_property(vtodo, + ICAL_PERCENTCOMPLETE_PROPERTY), prop != NULL) { + icalcomponent_remove_property(vtodo,prop); + icalproperty_free(prop); + } + + if (havebstr("status")) { + icalproperty_status taskStatus = icalproperty_string_to_status(bstr("status")); + icalcomponent_set_status(vtodo, taskStatus); + icalcomponent_add_property(vtodo, + icalproperty_new_percentcomplete( + (strcasecmp(bstr("status"), "completed") ? 0 : 100) + ) + ); + } + else { + icalcomponent_add_property(vtodo, icalproperty_new_percentcomplete(0)); + } + while (prop = icalcomponent_get_first_property(vtodo, + ICAL_CATEGORIES_PROPERTY), prop != NULL) { + icalcomponent_remove_property(vtodo,prop); + icalproperty_free(prop); + } + if (!IsEmptyStr(bstr("category"))) { + prop = icalproperty_new_categories(bstr("category")); + icalcomponent_add_property(vtodo,prop); + } + while (prop = icalcomponent_get_first_property(vtodo, + ICAL_DUE_PROPERTY), prop != NULL) { + icalcomponent_remove_property(vtodo, prop); + icalproperty_free(prop); + } + if (IsEmptyStr(bstr("nodue"))) { + if (yesbstr("due_time")) { + icaltime_from_webform(&t, "due"); + } + else { + icaltime_from_webform_dateonly(&t, "due"); + } + icalcomponent_add_property(vtodo, + icalproperty_new_due(t) + ); + } + /** Give this task a UID if it doesn't have one. */ + lprintf(9, "Give this task a UID if it doesn't have one.\n"); + if (icalcomponent_get_first_property(vtodo, + ICAL_UID_PROPERTY) == NULL) { + generate_uuid(buf); + icalcomponent_add_property(vtodo, + icalproperty_new_uid(buf) + ); + } + + /* Increment the sequence ID */ + lprintf(9, "Increment the sequence ID\n"); + while (prop = icalcomponent_get_first_property(vtodo, + 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(vtodo, prop); + icalproperty_free(prop); + } + ++sequence; + lprintf(9, "New sequence is %d. Adding...\n", sequence); + icalcomponent_add_property(vtodo, + icalproperty_new_sequence(sequence) + ); + + /* + * Encapsulate event into full VCALENDAR component. Clone it first, + * for two reasons: one, it's easier to just free the whole thing + * when we're done instead of unbundling, but more importantly, we + * can't encapsulate something that may already be encapsulated + * somewhere else. + */ + lprintf(9, "Encapsulating into a full VCALENDAR component\n"); + encaps = ical_encapsulate_subcomponent(icalcomponent_new_clone(vtodo)); + + /* Serialize it and save it to the message base */ + serv_puts("ENT0 1|||4"); + serv_getln(buf, sizeof buf); + if (buf[0] == '4') { + serv_puts("Content-type: text/calendar"); + serv_puts(""); + serv_puts(icalcomponent_as_ical_string(encaps)); + serv_puts("000"); + + /* + * Probably not necessary; the server will see the UID + * of the object and delete the old one anyway, but + * just in case... + */ + delete_existing = 1; + } + icalcomponent_free(encaps); + } + + /** + * If the user clicked 'Delete' then explicitly delete the message. + */ + if (havebstr("delete_button")) { + delete_existing = 1; + } + + if ( (delete_existing) && (msgnum > 0L) ) { + serv_printf("DELE %ld", lbstr("msgnum")); + serv_getln(buf, sizeof buf); + } + + if (created_new_vtodo) { + icalcomponent_free(vtodo); + } + + /* Go back to wherever we came from */ + if (ibstr("return_to_summary") == 1) { + summary(); + } + else { + readloop(readfwd); + } +} + +/* + * Display task view + */ +int tasks_LoadMsgFromServer(SharedMessageStatus *Stat, + void **ViewSpecific, + message_summary* Msg, + int is_new, + int i) +{ + /* Not (yet?) needed here? calview *c = (calview *) *ViewSpecific; */ + + load_ical_object(Msg->msgnum, is_new, ICAL_VTODO_COMPONENT, display_individual_cal, NULL, 0); + return 0; +} + +/* + * Display the editor component for a task + */ +void display_edit_task(void) { + long msgnum = 0L; + + /* Force change the room if we have to */ + if (havebstr("taskrm")) { + gotoroom(sbstr("taskrm")); + } + + msgnum = lbstr("msgnum"); + if (msgnum > 0L) { + /* existing task */ + load_ical_object(msgnum, 0, + ICAL_VTODO_COMPONENT, + display_edit_individual_task, + NULL, 0 + ); + } + else { + /* new task */ + display_edit_individual_task(NULL, 0L, "", 0, NULL); + } +} + +/* + * save an edited task + */ +void save_task(void) { + long msgnum = 0L; + msgnum = lbstr("msgnum"); + if (msgnum > 0L) { + load_ical_object(msgnum, 0, ICAL_VTODO_COMPONENT, save_individual_task, NULL, 0); + } + else { + save_individual_task(NULL, 0L, "", 0, NULL); + } +} + + + +int tasks_GetParamsGetServerCall(SharedMessageStatus *Stat, + void **ViewSpecific, + long oper, + char *cmd, + long len) +{ + strcpy(cmd, "MSGS ALL"); + Stat->maxmsgs = 32767; + return 200; +} + + +int tasks_Cleanup(void **ViewSpecific) +{ + end_burst(); +/* Tasks doesn't need the calview struct... + free (*ViewSpecific); + *ViewSpecific = NULL; + */ + return 0; +} + +void +InitModule_TASKS +(void) +{ + RegisterReadLoopHandlerset( + VIEW_TASKS, + tasks_GetParamsGetServerCall, + NULL, + tasks_LoadMsgFromServer, + tasks_RenderView_or_Tail, + tasks_Cleanup); + WebcitAddUrlHandler(HKEY("save_task"), save_task, 0); +} diff --git a/webcit/useredit.c b/webcit/useredit.c index 1396fdebc..7d0c6cc8f 100644 --- a/webcit/useredit.c +++ b/webcit/useredit.c @@ -419,12 +419,15 @@ long locate_user_vcard_in_this_room(message_summary **VCMsg, wc_mime_attachment int already_tried_creating_one = 0; StrBuf *FoundCharset = NewStrBuf(); StrBuf *Error = NULL; + SharedMessageStatus Stat; + Buf = NewStrBuf(); TRYAGAIN: + memset(&Stat, 0, sizeof(SharedMessageStatus)); Done = 0; /* Search for the user's vCard */ - if (load_msg_ptrs("MSGS ALL||||1", 1, NULL, NULL) > 0) { + if (load_msg_ptrs("MSGS ALL||||1", &Stat) > 0) { at = GetNewHashPos(WCC->summ, 0); while (GetNextHashPos(WCC->summ, at, &HKLen, &HashKey, &vMsg)) { Msg = (message_summary*) vMsg; diff --git a/webcit/vcard_edit.c b/webcit/vcard_edit.c index e05bb11c2..11a059b87 100644 --- a/webcit/vcard_edit.c +++ b/webcit/vcard_edit.c @@ -3,7 +3,7 @@ */ #include "webcit.h" - +#include "calendar.h" /* * Record compare function for sorting address book indices @@ -1241,12 +1241,102 @@ void display_vcard_photo_img(void) free(photosrc); } +typedef struct _vcardview_struct { + long is_singlecard; + addrbookent *addrbook; + long num_ab; + +} vcardview_struct; + +int vcard_GetParamsGetServerCall(SharedMessageStatus *Stat, + void **ViewSpecific, + long oper, + char *cmd, + long len) +{ + vcardview_struct *VS; + + VS = (vcardview_struct*) malloc (sizeof(vcardview_struct)); + memset(VS, 0, sizeof(vcardview_struct)); + *ViewSpecific = (void*)VS; + + VS->is_singlecard = ibstr("is_singlecard"); + if (VS->is_singlecard != 1) { + if (oper == do_search) { + snprintf(cmd, len, "MSGS SEARCH|%s", bstr("query")); + } + else { + strcpy(cmd, "MSGS ALL"); + } + Stat->maxmsgs = 9999999; + } + return 200; +} + +int vcard_LoadMsgFromServer(SharedMessageStatus *Stat, + void **ViewSpecific, + message_summary* Msg, + int is_new, + int i) +{ + vcardview_struct *VS; + char *ab_name; + + VS = (vcardview_struct*) *ViewSpecific; + + ab_name = NULL; + fetch_ab_name(Msg, &ab_name); + if (ab_name == NULL) + return 0; + ++VS->num_ab; + VS->addrbook = realloc(VS->addrbook, + (sizeof(addrbookent) * VS->num_ab) ); + safestrncpy(VS->addrbook[VS->num_ab-1].ab_name, ab_name, + sizeof(VS->addrbook[VS->num_ab-1].ab_name)); + VS->addrbook[VS->num_ab-1].ab_msgnum = Msg->msgnum; + free(ab_name); + return 0; +} + +int vcard_RenderView_or_Tail(SharedMessageStatus *Stat, void **ViewSpecific, long oper) +{ + const StrBuf *Mime; + vcardview_struct *VS; + + VS = (vcardview_struct*) *ViewSpecific; + if (VS->is_singlecard) + read_message(WC->WBuf, HKEY("view_message"), lbstr("startmsg"), NULL, &Mime); + else + do_addrbook_view(VS->addrbook, VS->num_ab); /* Render the address book */ + return 0; +} + +int vcard_Cleanup(void **ViewSpecific) +{ + vcardview_struct *VS; + + VS = (vcardview_struct*) *ViewSpecific; + end_burst(); + if ((VS != NULL) && + (VS->addrbook != NULL)) + free(VS->addrbook); + if (VS != NULL) + free(VS); + return 0; +} void InitModule_VCARD (void) { + RegisterReadLoopHandlerset( + VIEW_ADDRESSBOOK, + vcard_GetParamsGetServerCall, + NULL, + vcard_LoadMsgFromServer, + vcard_RenderView_or_Tail, + vcard_Cleanup); WebcitAddUrlHandler(HKEY("edit_vcard"), edit_vcard, 0); WebcitAddUrlHandler(HKEY("submit_vcard"), submit_vcard, 0); WebcitAddUrlHandler(HKEY("vcardphoto"), display_vcard_photo_img, NEED_URL); diff --git a/webcit/webcit.h b/webcit/webcit.h index 7b3d90c28..da0e0c717 100644 --- a/webcit/webcit.h +++ b/webcit/webcit.h @@ -91,7 +91,6 @@ #include "paramhandling.h" #include "preferences.h" - #ifdef HAVE_OPENSSL /* Work around RedHat's b0rken OpenSSL includes */ #define OPENSSL_NO_KRB5 @@ -581,27 +580,6 @@ enum { }; -/* - * calview contains data passed back and forth between the message fetching loop - * and the calendar view renderer. - */ -enum { - calview_month, - calview_day, - calview_week, - calview_brief, - calview_summary -}; - -struct calview { - int view; - int year; - int month; - int day; - time_t lower_bound; - time_t upper_bound; -}; - #ifndef num_parms #define num_parms(source) num_tokens(source, '|') #endif @@ -785,13 +763,9 @@ void output_html(const char *, int, int, StrBuf *, StrBuf *); void do_listsub(void); ssize_t write(int fd, const void *buf, size_t count); void cal_process_attachment(wc_mime_attachment *Mime); -void load_calendar_item(message_summary *Msg, int unread, struct calview *c); void display_calendar(message_summary *Msg, int unread); -void display_task(message_summary *Msg, int unread); void display_note(message_summary *Msg, int unread); void updatenote(void); -void parse_calendar_view_request(struct calview *c); -void render_calendar_view(struct calview *c); void do_tasks_view(void); int calendar_summary_view(void); void free_march_list(wcsession *wcf); @@ -813,11 +787,6 @@ icaltimezone *get_default_icaltimezone(void); void display_icaltimetype_as_webform(struct icaltimetype *, char *, int); void icaltime_from_webform(struct icaltimetype *result, char *prefix); void icaltime_from_webform_dateonly(struct icaltimetype *result, char *prefix); -void display_edit_individual_event(icalcomponent *supplied_vtodo, long msgnum, char *from, - int unread, struct calview *calv); -void save_individual_event(icalcomponent *supplied_vtodo, long msgnum, char *from, - int unread, struct calview *calv); -void ical_dezonify(icalcomponent *cal); void partstat_as_string(char *buf, icalproperty *attendee); icalcomponent *ical_encapsulate_subcomponent(icalcomponent *subcomp); void check_attendee_availability(icalcomponent *supplied_vevent); @@ -902,7 +871,6 @@ extern char *hourname[]; /* Names of hours (12am, 1am, etc.) */ void http_datestring(char *buf, size_t n, time_t xtime); -typedef void (*IcalCallbackFunc)(icalcomponent *, long, char*, int, struct calview *); /* These should be empty, but we have them for testing */ #define DEFAULT_HTTPAUTH_USER "" diff --git a/webcit/wiki.c b/webcit/wiki.c index ca904d1a4..efc3c8890 100644 --- a/webcit/wiki.c +++ b/webcit/wiki.c @@ -99,10 +99,33 @@ void display_wiki_page(void) wDumpContent(1); } +int wiki_GetParamsGetServerCall(SharedMessageStatus *Stat, + void **ViewSpecific, + long oper, + char *cmd, + long len) +{ + char buf[SIZ]; + sprintf(buf, "wiki?room=%s&page=home", ChrPtr(WC->wc_roomname)); + http_redirect(buf); + return 300; +} + void InitModule_WIKI (void) { + RegisterReadLoopHandlerset( + VIEW_WIKI, + wiki_GetParamsGetServerCall, + NULL, + NULL, + NULL, + NULL + ); + WebcitAddUrlHandler(HKEY("wiki"), display_wiki_page, 0); return ; } + + -- 2.30.2