sprintf(divname, "rsvp%04x", ++divcount);
+ /* Convert timezones to something easy to display.
+ * It's safe to do this in memory because we're only changing it on the
+ * display side -- when we tell the server to do something with the object,
+ * the server will be working with its original copy in the database.
+ */
+ if ((cal) && (recursion_level == 0)) {
+ ical_dezonify(cal);
+ }
+
/* Leading HTML for the display of this object */
if (recursion_level == 0) {
StrBufAppendPrintf(Target, "<div class=\"mimepart\">\n");
/* See what we need to do with this */
if (method != NULL) {
- the_method = icalproperty_get_method(method);
char *title;
+ the_method = icalproperty_get_method(method);
StrBufAppendPrintf(Target, "<div id=\"%s_title\">", divname);
StrBufAppendPrintf(Target, "<img src=\"static/calarea_48x.gif\">");
}
else {
tt = icaltime_as_timet(t);
- webcit_fmt_date(buf, tt, 0);
+ webcit_fmt_date(buf, 256, tt, DATEFMT_FULL);
StrBufAppendPrintf(Target, "<dt>");
StrBufAppendPrintf(Target, _("Starting date/time:"));
StrBufAppendPrintf(Target, "</dt><dd>%s</dd>", buf);
if (p != NULL) {
t = icalproperty_get_dtend(p);
tt = icaltime_as_timet(t);
- webcit_fmt_date(buf, tt, 0);
+ webcit_fmt_date(buf, 256, tt, DATEFMT_FULL);
StrBufAppendPrintf(Target, "<dt>");
StrBufAppendPrintf(Target, _("Ending date/time:"));
StrBufAppendPrintf(Target, "</dt><dd>%s</dd>", buf);
StrBufAppendPrintf(Target, "</dd>\n");
}
+ if (icalcomponent_get_first_property(cal, ICAL_RRULE_PROPERTY)) {
+ /* Unusual string syntax used here in order to re-use existing translations */
+ StrBufAppendPrintf(Target, "<dt>%s:</dt><dd>%s.</dd>\n",
+ _("Recurrence"),
+ _("This is a recurring event")
+ );
+ }
+
/* If the component has attendees, iterate through them. */
for (p = icalcomponent_get_first_property(cal, ICAL_ATTENDEE_PROPERTY);
(p != NULL);
/* If this is a REPLY, display update button */
if (the_method == ICAL_METHOD_REPLY) {
- /* In the future, if we want to validate this object before
- * continuing, we can do it this way:
- serv_printf("ICAL whatever|%ld|%s|", msgnum, cal_partnum);
- serv_getln(buf, sizeof buf);
- }
- ***********/
-
/* Display the update buttons */
StrBufAppendPrintf(Target, "<p id=\"%s_question\" >"
"%s "
}
-/**
- * \brief process calendar mail atachment
- * Deserialize a calendar object in a message so it can be processed.
- * (This is the main entry point for these things)
- * \param part_source the part of the message we want to parse
- * \param msgnum number of the mesage in our db
- * \param cal_partnum the number of the calendar item
+/*
+ * Deserialize a calendar object in a message so it can be displayed.
*/
void cal_process_attachment(wc_mime_attachment *Mime)
{
icalcomponent *cal;
-
+
cal = icalcomponent_new_from_string(ChrPtr(Mime->Data));
FlushStrBuf(Mime->Data);
if (cal == NULL) {
-/**
- * \brief accept/decline meeting
- * Respond to a meeting request
+/*
+ * Respond to a meeting request - accept/decline meeting
*/
void respond_to_request(void)
{
-/**
- * \brief Handle an incoming RSVP
+/*
+ * Handle an incoming RSVP
*/
void handle_rsvp(void)
{
icalrecur_iterator *ritr = NULL;
struct icaltimetype next;
int num_recur = 0;
+ int stop_rr = 0;
dtstart = icaltime_null_time();
dtend = icaltime_null_time();
ritr = icalrecur_iterator_new(recur, dtstart);
if (!ritr) return;
- int stop_rr = 0;
while (next = icalrecur_iterator_next(ritr), ((!icaltime_is_null_time(next))&&(!stop_rr)) ) {
++num_recur;
if (num_recur > 1) { /* Skip the first one. We already did it at the root. */
+ icalcomponent *cptr;
/* lprintf(9, "REPEATS: %s\n", icaltime_as_ical_string(next)); */
/* Note: anything we do here, we also have to do above for the root event. */
memcpy(Cal->from, from, len + 1);
Cal->cal_msgnum = msgnum;
- icalcomponent *cptr;
if (icalcomponent_isa(Cal->cal) == ICAL_VEVENT_COMPONENT) {
cptr = Cal->cal;
}
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
created_new_vtodo = 1;
}
- // TODO: Can we take all this and move it into a template?
+ /* TODO: Can we take all this and move it into a template? */
output_headers(1, 1, 1, 0, 0, 0);
wprintf("<!-- start task edit form -->");
p = icalcomponent_get_first_property(vtodo, ICAL_SUMMARY_PROPERTY);
- // Get summary early for title
+ /* Get summary early for title */
wprintf("<div class=\"box\">\n");
wprintf("<div class=\"boxlabel\">");
wprintf(_("Edit task"));
wprintf("<FORM METHOD=\"POST\" action=\"save_task\">\n");
wprintf("<div style=\"display: none;\">\n ");
wprintf("<input type=\"hidden\" name=\"nonce\" value=\"%d\">\n", WC->nonce);
- wprintf("<INPUT TYPE=\"hidden\" NAME=\"msgnum\" VALUE=\"%ld\">\n",
- msgnum);
+ wprintf("<INPUT TYPE=\"hidden\" NAME=\"msgnum\" VALUE=\"%ld\">\n", msgnum);
+ wprintf("<INPUT TYPE=\"hidden\" NAME=\"return_to_summary\" VALUE=\"%d\">\n",
+ ibstr("return_to_summary"));
wprintf("</div>");
wprintf("<table class=\"calendar_background\"><tr><td>");
wprintf("<TABLE STYLE=\"border: none;\">\n");
wprintf(_("No date"));
wprintf(" ");
+ wprintf("<span ID=\"dtstart_date\">");
wprintf(_("or"));
wprintf(" ");
if (p != NULL) {
else
IcalTime = icaltime_current_time_with_zone(get_default_icaltimezone());
display_icaltimetype_as_webform(&IcalTime, "dtstart", 0);
- wprintf("</TD></TR>\n");
+
+ wprintf("<INPUT TYPE=\"CHECKBOX\" NAME=\"dtstart_time_assoc\" ID=\"dtstart_time_assoc\" VALUE=\"yes\"");
+ if (!IcalTime.is_date) {
+ wprintf("CHECKED=\"CHECKED\"");
+ }
+ wprintf(">");
+ wprintf(_("Time associated"));
+ wprintf("</span></TD></TR>\n");
wprintf("<TR><TD>");
wprintf(_("Due date:"));
wprintf(">");
wprintf(_("No date"));
wprintf(" ");
+ wprintf("<span ID=\"due_date\">\n");
wprintf(_("or"));
wprintf(" ");
if (p != NULL) {
else
IcalTime = icaltime_current_time_with_zone(get_default_icaltimezone());
display_icaltimetype_as_webform(&IcalTime, "due", 0);
-
- wprintf("</TD></TR>\n");
+
+ wprintf("<INPUT TYPE=\"CHECKBOX\" NAME=\"due_time_assoc\" ID=\"due_time_assoc\" VALUE=\"yes\"");
+ if (!IcalTime.is_date) {
+ wprintf("CHECKED=\"CHECKED\"");
+ }
+ wprintf(">");
+ wprintf(_("Time associated"));
+ wprintf("</span></TD></TR>\n");
todoStatus = icalcomponent_get_status(vtodo);
wprintf("<TR><TD>\n");
wprintf(_("Completed:"));
}
wprintf(" >");
wprintf("</TD></TR>");
- // start category field
+ /* start category field */
p = icalcomponent_get_first_property(vtodo, ICAL_CATEGORIES_PROPERTY);
wprintf("<TR><TD>");
wprintf(_("Category:"));
}
wprintf("\">");
wprintf("</TD></TR>\n ");
- // end category field
+ /* end category field */
wprintf("<TR><TD>");
wprintf(_("Description:"));
wprintf("</TD><TD>");
}
/*
- * \brief Save an edited task
- * \param supplied_vtodo the task to save
- * \param msgnum number of the mesage in our db
+ * 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)
icalproperty_new_summary(bstr("summary")));
} else {
icalcomponent_add_property(vtodo,
- icalproperty_new_summary("Untitled Task"));
+ icalproperty_new_summary(_("Untitled Task")));
}
while (prop = icalcomponent_get_first_property(vtodo,
icalproperty_free(prop);
}
if (IsEmptyStr(bstr("nodtstart"))) {
- icaltime_from_webform(&t, "dtstart");
+ if (yesbstr("dtstart_time")) {
+ icaltime_from_webform(&t, "dtstart");
+ }
+ else {
+ icaltime_from_webform_dateonly(&t, "dtstart");
+ }
icalcomponent_add_property(vtodo,
icalproperty_new_dtstart(t)
);
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"));
+ 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) {
icalproperty_free(prop);
}
if (IsEmptyStr(bstr("nodue"))) {
- icaltime_from_webform(&t, "due");
+ if (yesbstr("due_time")) {
+ icaltime_from_webform(&t, "due");
+ }
+ else {
+ icaltime_from_webform_dateonly(&t, "due");
+ }
icalcomponent_add_property(vtodo,
icalproperty_new_due(t)
);
);
}
- /** Increment the sequence ID */
+ /* Increment the sequence ID */
lprintf(9, "Increment the sequence ID\n");
while (prop = icalcomponent_get_first_property(vtodo,
ICAL_SEQUENCE_PROPERTY), (prop != NULL) ) {
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
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...
icalcomponent_free(vtodo);
}
- /** Go back to the task list */
- readloop(readfwd);
+ /* Go back to wherever we came from */
+ if (ibstr("return_to_summary") == 1) {
+ summary();
+ }
+ else {
+ readloop(readfwd);
+ }
}
+void process_ical_object(long msgnum, int unread,
+ char *from,
+ char *FlatIcal,
+ icalcomponent_kind which_kind,
+ IcalCallbackFunc CallBack,
+ struct calview *calv
+ )
+{
+ icalcomponent *cal, *c;
+
+ cal = icalcomponent_new_from_string(FlatIcal);
+ if (cal != NULL) {
+
+ /* A which_kind of (-1) means just load the whole thing */
+ if (which_kind == (-1)) {
+ CallBack(cal, msgnum, from, unread, calv);
+ }
+
+ /* Otherwise recurse and hunt */
+ else {
+
+ /* Simple components of desired type */
+ if (icalcomponent_isa(cal) == which_kind) {
+ CallBack(cal, msgnum, from, unread, calv);
+ }
+
+ /* Subcomponents of desired type */
+ for (c = icalcomponent_get_first_component(cal, which_kind);
+ (c != 0);
+ c = icalcomponent_get_next_component(cal, which_kind)) {
+ CallBack(c, msgnum, from, unread, calv);
+ }
+
+ }
+
+ icalcomponent_free(cal);
+ }
+}
+
/*
* Code common to all icalendar display handlers. Given a message number and a MIME
* type, we load the message and hunt for that MIME type. If found, we load
* the requested object type, and feed it to the specified handler.
*/
void load_ical_object(long msgnum, int unread,
- icalcomponent_kind which_kind,
- void (*callback)(icalcomponent *, long, char*, int, struct calview *),
- struct calview *calv
+ icalcomponent_kind which_kind,
+ IcalCallbackFunc CallBack,
+ struct calview *calv,
+ int RenderAsync
)
{
- char buf[1024];
+ StrBuf *Buf;
+ StrBuf *Data;
+ const char *bptr;
+ int Done = 0;
char from[128] = "";
char mime_partnum[256];
char mime_filename[256];
int mime_length;
char relevant_partnum[256];
char *relevant_source = NULL;
- icalcomponent *cal, *c;
+ int phase = 0; /* 0 = citadel headers, 1 = mime headers, 2 = body */
+ char msg4_content_type[256] = "";
+ char msg4_content_encoding[256] = "";
+ int msg4_content_length = 0;
relevant_partnum[0] = '\0';
- sprintf(buf, "MSG4 %ld", msgnum); /* we need the mime headers */
- serv_puts(buf);
- serv_getln(buf, sizeof buf);
- if (buf[0] != '1') return;
-
- while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
- if (!strncasecmp(buf, "part=", 5)) {
- extract_token(mime_filename, &buf[5], 1, '|', sizeof mime_filename);
- extract_token(mime_partnum, &buf[5], 2, '|', sizeof mime_partnum);
- extract_token(mime_disposition, &buf[5], 3, '|', sizeof mime_disposition);
- extract_token(mime_content_type, &buf[5], 4, '|', sizeof mime_content_type);
- mime_length = extract_int(&buf[5], 5);
-
- if ( (!strcasecmp(mime_content_type, "text/calendar"))
- || (!strcasecmp(mime_content_type, "application/ics"))
- || (!strcasecmp(mime_content_type, "text/vtodo"))
- ) {
- strcpy(relevant_partnum, mime_partnum);
- }
- }
- else if (!strncasecmp(buf, "from=", 4)) {
- extract_token(from, buf, 1, '=', sizeof(from));
- }
+ serv_printf("MSG4 %ld", msgnum); /* we need the mime headers */
+ Buf = NewStrBuf();
+ StrBuf_ServGetlnBuffered(Buf);
+ if (GetServerStatus(Buf, NULL) != 1) {
+ FreeStrBuf (&Buf);
+ return;
}
-
- if (!IsEmptyStr(relevant_partnum)) {
- relevant_source = load_mimepart(msgnum, relevant_partnum);
- if (relevant_source != NULL) {
-
- cal = icalcomponent_new_from_string(relevant_source);
- if (cal != NULL) {
-
- /* A which_kind of (-1) means just load the whole thing */
- if (which_kind == (-1)) {
- callback(cal, msgnum, from, unread, calv);
+ while ((StrBuf_ServGetlnBuffered(Buf)>=0) && !Done) {
+ if ( (StrLength(Buf)==3) &&
+ !strcmp(ChrPtr(Buf), "000")) {
+ Done = 1;
+ break;
+ }
+ bptr = ChrPtr(Buf);
+ switch (phase) {
+ case 0:
+ if (!strncasecmp(bptr, "part=", 5)) {
+ extract_token(mime_filename, &bptr[5], 1, '|', sizeof mime_filename);
+ extract_token(mime_partnum, &bptr[5], 2, '|', sizeof mime_partnum);
+ extract_token(mime_disposition, &bptr[5], 3, '|', sizeof mime_disposition);
+ extract_token(mime_content_type, &bptr[5], 4, '|', sizeof mime_content_type);
+ mime_length = extract_int(&bptr[5], 5);
+
+ if ( (!strcasecmp(mime_content_type, "text/calendar"))
+ || (!strcasecmp(mime_content_type, "application/ics"))
+ || (!strcasecmp(mime_content_type, "text/vtodo"))
+ ) {
+ strcpy(relevant_partnum, mime_partnum);
}
-
- /* Otherwise recurse and hunt */
- else {
-
- /* Simple components of desired type */
- if (icalcomponent_isa(cal) == which_kind) {
- callback(cal, msgnum, from, unread, calv);
- }
-
- /* Subcomponents of desired type */
- for (c = icalcomponent_get_first_component(cal, which_kind);
- (c != 0);
- c = icalcomponent_get_next_component(cal, which_kind)) {
- callback(c, msgnum, from, unread, calv);
- }
-
+ }
+ else if (!strncasecmp(bptr, "from=", 4)) {
+ extract_token(from, bptr, 1, '=', sizeof(from));
+ }
+ else if ((phase == 0) && (!strncasecmp(bptr, "text", 4))) {
+ phase = 1;
+ }
+ break;
+ case 1:
+ if (!IsEmptyStr(bptr)) {
+ if (!strncasecmp(bptr, "Content-type: ", 14)) {
+ safestrncpy(msg4_content_type, &bptr[14], sizeof msg4_content_type);
+ striplt(msg4_content_type);
}
-
- icalcomponent_free(cal);
+ else if (!strncasecmp(bptr, "Content-transfer-encoding: ", 27)) {
+ safestrncpy(msg4_content_encoding, &bptr[27], sizeof msg4_content_encoding);
+ striplt(msg4_content_type);
+ }
+ else if ((!strncasecmp(bptr, "Content-length: ", 16))) {
+ msg4_content_length = atoi(&bptr[16]);
+ }
+ break;
}
- free(relevant_source);
+ else {
+ phase++;
+
+ if ((msg4_content_length > 0)
+ && ( !strcasecmp(msg4_content_encoding, "7bit"))
+ && ((!strcasecmp(mime_content_type, "text/calendar"))
+ || (!strcasecmp(mime_content_type, "application/ics"))
+ || (!strcasecmp(mime_content_type, "text/vtodo"))
+ )
+ )
+ {
+ }
+ }
+ case 2:
+ Data = NewStrBufPlain(NULL, msg4_content_length * 2);
+ if (msg4_content_length > 0) {
+ StrBuf_ServGetBLOBBuffered(Data, msg4_content_length);
+ phase ++;
+ }
+ else {
+ StrBufAppendBuf(Data, Buf, 0);
+ StrBufAppendBufPlain(Data, "\r\n", 1, 0);
+ }
+ case 3:
+ StrBufAppendBuf(Data, Buf, 0);
}
}
+ FreeStrBuf(&Buf);
+
+ /* If MSG4 didn't give us the part we wanted, but we know that we can find it
+ * as one of the other MIME parts, attempt to load it now.
+ */
+ if ((Data == NULL) && (!IsEmptyStr(relevant_partnum))) {
+ Data = load_mimepart(msgnum, relevant_partnum);
+ }
+
+ if (Data != NULL) {
+ relevant_source = (char*) ChrPtr(Data);
+ process_ical_object(msgnum, unread,
+ from,
+ relevant_source,
+ which_kind,
+ CallBack,
+ calv);
+ FreeStrBuf (&Data);
+ }
+
icalmemory_free_ring();
}
* Display a calendar item
*/
void load_calendar_item(message_summary *Msg, int unread, struct calview *c) {
- /*load_ical_object(Msg->msgnum, unread, ICAL_VEVENT_COMPONENT, display_individual_cal, c);*/
- load_ical_object(Msg->msgnum, unread, (-1), display_individual_cal, 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);
+ load_ical_object(Msg->msgnum, unread, ICAL_VTODO_COMPONENT, display_individual_cal, NULL, 0);
}
/*
/* Force change the room if we have to */
if (havebstr("taskrm")) {
- gotoroom((char *)bstr("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
+ ICAL_VTODO_COMPONENT,
+ display_edit_individual_task,
+ NULL, 0
);
}
else {
long msgnum = 0L;
msgnum = lbstr("msgnum");
if (msgnum > 0L) {
- load_ical_object(msgnum, 0, ICAL_VTODO_COMPONENT, save_individual_task, NULL);
+ load_ical_object(msgnum, 0, ICAL_VTODO_COMPONENT, save_individual_task, NULL, 0);
}
else {
save_individual_task(NULL, 0L, "", 0, NULL);
msgnum = lbstr("msgnum");
if (msgnum > 0L) {
/* existing event */
- load_ical_object(msgnum, 0, ICAL_VEVENT_COMPONENT, display_edit_individual_event, NULL);
+ load_ical_object(msgnum, 0, ICAL_VEVENT_COMPONENT, display_edit_individual_event, NULL, 0);
}
else {
/* new event */
msgnum = lbstr("msgnum");
if (msgnum > 0L) {
- /* load_ical_object(msgnum, 0, ICAL_VEVENT_COMPONENT, save_individual_event, NULL); */
- load_ical_object(msgnum, 0, (-1), save_individual_event, NULL);
+ load_ical_object(msgnum, 0, (-1), save_individual_event, NULL, 0);
}
else {
save_individual_event(NULL, 0L, "", 0, NULL);
InitModule_CALENDAR
(void)
{
+ 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("display_edit_task"), display_edit_task, 0);
WebcitAddUrlHandler(HKEY("save_task"), save_task, 0);
WebcitAddUrlHandler(HKEY("display_edit_event"), display_edit_event, 0);