From: Art Cancro Date: Tue, 30 Jan 2018 05:20:32 +0000 (-0500) Subject: initial work on flat view X-Git-Tag: v939~425 X-Git-Url: https://code.citadel.org/?a=commitdiff_plain;h=a2e2b0eab5e29c1bd61ec21737d28d3cb70ee364;p=citadel.git initial work on flat view --- diff --git a/webcit-ng/Makefile b/webcit-ng/Makefile index d1519e0a7..4182603c1 100644 --- a/webcit-ng/Makefile +++ b/webcit-ng/Makefile @@ -1,4 +1,4 @@ -OBJS := http.o main.o request.o ssl.o static.o tcp_sockets.o webserver.o ctdlclient.o admin_functions.o room_functions.o util.o caldav_reports.o messages.o ctdlfunctions.o ctdl_commands.o threaded_view.o html2html.o text2html.o +OBJS := http.o main.o request.o ssl.o static.o tcp_sockets.o webserver.o ctdlclient.o admin_functions.o room_functions.o util.o caldav_reports.o messages.o ctdlfunctions.o ctdl_commands.o forum_view.o html2html.o text2html.o CFLAGS := -ggdb LDFLAGS := diff --git a/webcit-ng/forum_view.c b/webcit-ng/forum_view.c new file mode 100644 index 000000000..995a6e187 --- /dev/null +++ b/webcit-ng/forum_view.c @@ -0,0 +1,291 @@ +/* + * Forum view (threaded/flat) + * + * Copyright (c) 1996-2018 by the citadel.org team + * + * This program is open source software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "webcit.h" + +struct mthread { + long msgnum; + time_t datetime; + int threadhash; + int refhashes[10]; + char from[64]; + int parent; +}; + + +// Renderer for one message in the threaded view +// (This will probably work for the flat view too.) +// +void forum_render_one_message(struct ctdlsession *c, StrBuf *sj, long msgnum) +{ + StrBuf *raw_msg = NULL; + StrBuf *sanitized_msg = NULL; + char buf[1024]; + char content_transfer_encoding[1024] = { 0 }; + char content_type[1024] = { 0 }; + char author[128] = { 0 }; + char datetime[128] = { 0 } ; + + ctdl_printf(c, "MSG4 %ld", msgnum); + ctdl_readline(c, buf, sizeof(buf)); + if (buf[0] != '1') { + StrBufAppendPrintf(sj, "
ERROR CONDITION FIXME WRITE A BOX
"); + return; + } + + while ( (ctdl_readline(c, buf, sizeof(buf)) >= 0) && (strcmp(buf, "text")) && (strcmp(buf, "000")) ) { + // citadel header parsing here + if (!strncasecmp(buf, "from=", 5)) { + safestrncpy(author, &buf[5], sizeof author); + } + if (!strncasecmp(buf, "time=", 5)) { + time_t tt; + struct tm tm; + tt = atol(&buf[5]); + localtime_r(&tt, &tm); + strftime(datetime, sizeof datetime, "%c", &tm); + } + } + + if (!strcmp(buf, "text")) { + while ( (ctdl_readline(c, buf, sizeof(buf)) >= 0) && (strcmp(buf, "")) && (strcmp(buf, "000")) ) { + // rfc822 header parsing here + if (!strncasecmp(buf, "Content-transfer-encoding:", 26)) { + strcpy(content_transfer_encoding, &buf[26]); + striplt(content_transfer_encoding); + } + if (!strncasecmp(buf, "Content-type:", 13)) { + strcpy(content_type, &buf[13]); + striplt(content_type); + } + } + raw_msg = ctdl_readtextmsg(c); + } + else { + raw_msg = NULL; + } + + // begin output + + StrBufAppendPrintf(sj, "
"); // begin message wrapper + StrBufAppendPrintf(sj, "
"); // begin avatar FIXME move the style to a stylesheet + StrBufAppendPrintf(sj, " "); // FIXME temporary avatar + StrBufAppendPrintf(sj, "
"); // end avatar + StrBufAppendPrintf(sj, "
"); // begin content + StrBufAppendPrintf(sj, "
"); // begin header + StrBufAppendPrintf(sj, "%s ", author); // FIXME link to user profile or whatever + StrBufAppendPrintf(sj, "%s ", datetime); + StrBufAppendPrintf(sj, "
"); // end header + StrBufAppendPrintf(sj, "
"); // begin body + + if (raw_msg) { + + // These are the encodings we know how to handle. Decode in-place. + + if (!strcasecmp(content_transfer_encoding, "base64")) { + StrBufDecodeBase64(raw_msg); + } + if (!strcasecmp(content_transfer_encoding, "quoted-printable")) { + StrBufDecodeQP(raw_msg); + } + + // At this point, raw_msg contains the decoded message. + // Now run through the renderers we have available. + + if (!strncasecmp(content_type, "text/html", 9)) { + sanitized_msg = html2html("UTF-8", 0, c->room, msgnum, raw_msg); + } + else if (!strncasecmp(content_type, "text/plain", 10)) { + sanitized_msg = text2html("UTF-8", 0, c->room, msgnum, raw_msg); + } + else if (!strncasecmp(content_type, "text/x-citadel-variformat", 25)) { + sanitized_msg = variformat2html(raw_msg); + } + else { + sanitized_msg = NewStrBufPlain(HKEY("No renderer for this content type
")); + } + FreeStrBuf(&raw_msg); + + // If sanitized_msg is not NULL, we have rendered the message and can output it. + + if (sanitized_msg) { + StrBufAppendBuf(sj, sanitized_msg, 0); + FreeStrBuf(&sanitized_msg); + } + } + + StrBufAppendPrintf(sj, "
"); // end body + StrBufAppendPrintf(sj, "
"); // end content + StrBufAppendPrintf(sj, "
"); // end wrapper +} + + +// Commands we need to send to Citadel Server before we begin rendering forum view. +// These are common to flat and threaded views. +// +void setup_for_forum_view(struct ctdlsession *c) +{ + char buf[1024]; + ctdl_printf(c, "MSGP text/html|text/plain"); // Declare the MIME types we know how to render + ctdl_readline(c, buf, sizeof(buf)); // Ignore the response + ctdl_printf(c, "MSGP dont_decode"); // Tell the server we will decode base64/etc client-side + ctdl_readline(c, buf, sizeof(buf)); // Ignore the response +} + + +// Threaded view (recursive section) +// +void thread_o_print(struct ctdlsession *c, StrBuf *sj, struct mthread *m, int num_msgs, int where_parent_is, int nesting_level) +{ + int i = 0; + int j = 0; + int num_printed = 0; + + for (i=0; i"); + } + + StrBufAppendPrintf(sj, "
  • ", m[i].msgnum); + forum_render_one_message(c, sj, m[i].msgnum); + StrBufAppendPrintf(sj, "
  • \r\n"); + if (i != 0) { + thread_o_print(c, sj, m, num_msgs, i, nesting_level+1); + } + } + } + + if (num_printed > 0) { + StrBufAppendPrintf(sj, ""); + } +} + + +// Threaded view (entry point) +// +void threaded_view(struct http_transaction *h, struct ctdlsession *c, char *which) +{ + int num_msgs = 0; + int num_alloc = 0; + struct mthread *m; + char buf[1024]; + char refs[1024]; + int i, j, k; + + ctdl_printf(c, "MSGS ALL|||9"); // 9 == headers + thread references + ctdl_readline(c, buf, sizeof(buf)); + if (buf[0] != '1') { + do_404(h); + return; + } + + StrBuf *sj = NewStrBuf(); + StrBufAppendPrintf(sj, "\r\n"); + + while (ctdl_readline(c, buf, sizeof buf), strcmp(buf,"000")) { + + ++num_msgs; + if (num_msgs > num_alloc) { + if (num_alloc == 0) { + num_alloc = 100; + m = malloc(num_alloc * sizeof(struct mthread)); + } + else { + num_alloc *= 2; + m = realloc(m, (num_alloc * sizeof(struct mthread))); + } + } + + memset(&m[num_msgs-1], 0, sizeof(struct mthread)); + m[num_msgs-1].msgnum = extract_long(buf, 0); + m[num_msgs-1].datetime = extract_long(buf, 1); + extract_token(m[num_msgs-1].from, buf, 2, '|', sizeof m[num_msgs-1].from); + m[num_msgs-1].threadhash = extract_int(buf, 6); + extract_token(refs, buf, 7, '|', sizeof refs); + + char *t; + char *r = refs; + i = 0; + while ((t = strtok_r(r, ",", &r))) { + if (i == 0) { + m[num_msgs-1].refhashes[0] = atoi(t); // always keep the first one + } + else { + memcpy(&m[num_msgs-1].refhashes[1], &m[num_msgs-1].refhashes[2], sizeof(int)*8 ); // shift the rest + m[num_msgs-1].refhashes[9] = atoi(t); + } + ++i; + } + + } + + // Sort by thread. I did read jwz's sorting algorithm and it looks pretty good, but jwz is a self-righteous asshole so we do it our way. + for (i=0; i=0)&&(m[i].parent==0); --j) { + for (k=0; (k 0) { + free(m); + } + + StrBufAppendPrintf(sj, "\r\n"); + + add_response_header(h, strdup("Content-type"), strdup("text/html; charset=utf-8")); + h->response_code = 200; + h->response_string = strdup("OK"); + h->response_body_length = StrLength(sj); + h->response_body = SmashStrBuf(&sj); + return; +} + + +// flat view (entry point) +// +void flat_view(struct http_transaction *h, struct ctdlsession *c, char *which) +{ + StrBuf *sj = NewStrBuf(); + StrBufAppendPrintf(sj, "\r\n"); + + setup_for_forum_view(c); + long *msglist = get_msglist(c, "ALL"); + if (msglist) { + int i; + for (i=0; (msglist[i] > 0); ++i) { + forum_render_one_message(c, sj, msglist[i]); + } + free(msglist); + } + + StrBufAppendPrintf(sj, "\r\n"); + + add_response_header(h, strdup("Content-type"), strdup("text/html; charset=utf-8")); + h->response_code = 200; + h->response_string = strdup("OK"); + h->response_body_length = StrLength(sj); + h->response_body = SmashStrBuf(&sj); + return; +} diff --git a/webcit-ng/room_functions.c b/webcit-ng/room_functions.c index dc4551ae5..f49485d47 100644 --- a/webcit-ng/room_functions.c +++ b/webcit-ng/room_functions.c @@ -136,6 +136,11 @@ void object_in_room(struct http_transaction *h, struct ctdlsession *c) return; } + if (!strncasecmp(buf, "flat", 5)) { // Client is requesting a flat view (still kind of fuzzy here) + flat_view(h, c, &buf[5]); + return; + } + if ( (c->room_default_view == VIEW_CALENDAR) // room types where objects are referenced by EUID || (c->room_default_view == VIEW_TASKS) || (c->room_default_view == VIEW_ADDRESSBOOK) @@ -382,7 +387,7 @@ void propfind_the_room_itself(struct http_transaction *h, struct ctdlsession *c) StrBufAppendPrintf(Buf, ""); free(datestring); } - if (enumerate_by_euid) { // FIXME ajc 2017oct30 should this really be inside the timestamp conditional? + if (enumerate_by_euid) { // FIXME ajc 2017oct30 should this be inside the timestamp conditional? StrBufAppendPrintf(Buf, "\"%ld\"", msglist[i]); } } diff --git a/webcit-ng/static/index.html b/webcit-ng/static/index.html index f7f4839ad..64f61512d 100644 --- a/webcit-ng/static/index.html +++ b/webcit-ng/static/index.html @@ -12,11 +12,11 @@ - + diff --git a/webcit-ng/static/js/views.js b/webcit-ng/static/js/views.js index 33da43667..3df884315 100644 --- a/webcit-ng/static/js/views.js +++ b/webcit-ng/static/js/views.js @@ -37,7 +37,7 @@ function render_room_view() { switch(current_view) { case views.VIEW_MAILBOX: // FIXME view mail rooms as forums for now case views.VIEW_BBS: - threads_readmessages(); + forum_readmessages("flat"); break; default: document.getElementById("main").innerHTML = "The view for " + current_room + " is " + current_view + " but there is no renderer." ; @@ -47,15 +47,16 @@ function render_room_view() { } -// bbs "threads" view +// Forum view -- flat or threaded // The inner div exists so that if the user clicks away early, the main div doesn't get clobbered when the load completes. +// The parameter can be set to "flat" or "threads" which is passed directly to the API // -function threads_readmessages() { +function forum_readmessages(flat_or_threads) { var innerdivname = randomString(5); document.getElementById("main").innerHTML = "
    " + _("Loading messages from server, please wait") + "
    " ; var request = new XMLHttpRequest(); - request.open("GET", "/ctdl/r/" + escapeHTMLURI(current_room) + "/" + "threads", true); + request.open("GET", "/ctdl/r/" + escapeHTMLURI(current_room) + "/" + flat_or_threads, true); request.onreadystatechange = function() { if (this.readyState === 4) { if ((this.status / 100) == 2) { diff --git a/webcit-ng/threaded_view.c b/webcit-ng/threaded_view.c deleted file mode 100644 index d3e001b4e..000000000 --- a/webcit-ng/threaded_view.c +++ /dev/null @@ -1,253 +0,0 @@ -/* - * Threaded message view - * - * Copyright (c) 1996-2018 by the citadel.org team - * - * This program is open source software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include "webcit.h" - -struct mthread { - long msgnum; - time_t datetime; - int threadhash; - int refhashes[10]; - char from[64]; - int parent; -}; - - -// Renderer for one message in the threaded view -// (This will probably work for the flat view too.) -// -void thread_render_one_message(struct ctdlsession *c, StrBuf *sj, long msgnum) -{ - StrBuf *raw_msg = NULL; - StrBuf *sanitized_msg = NULL; - char buf[1024]; - char content_transfer_encoding[1024] = { 0 }; - char content_type[1024] = { 0 }; - char author[128] = { 0 }; - char datetime[128] = { 0 } ; - - ctdl_printf(c, "MSG4 %ld", msgnum); - ctdl_readline(c, buf, sizeof(buf)); - if (buf[0] != '1') { - StrBufAppendPrintf(sj, "
    ERROR CONDITION FIXME WRITE A BOX
    "); - return; - } - - while ( (ctdl_readline(c, buf, sizeof(buf)) >= 0) && (strcmp(buf, "text")) && (strcmp(buf, "000")) ) { - // citadel header parsing here - if (!strncasecmp(buf, "from=", 5)) { - safestrncpy(author, &buf[5], sizeof author); - } - if (!strncasecmp(buf, "time=", 5)) { - time_t tt; - struct tm tm; - tt = atol(&buf[5]); - localtime_r(&tt, &tm); - strftime(datetime, sizeof datetime, "%c", &tm); - } - } - - if (!strcmp(buf, "text")) { - while ( (ctdl_readline(c, buf, sizeof(buf)) >= 0) && (strcmp(buf, "")) && (strcmp(buf, "000")) ) { - // rfc822 header parsing here - if (!strncasecmp(buf, "Content-transfer-encoding:", 26)) { - strcpy(content_transfer_encoding, &buf[26]); - striplt(content_transfer_encoding); - } - if (!strncasecmp(buf, "Content-type:", 13)) { - strcpy(content_type, &buf[13]); - striplt(content_type); - } - } - raw_msg = ctdl_readtextmsg(c); - } - else { - raw_msg = NULL; - } - - // begin output - - StrBufAppendPrintf(sj, "
    "); // begin message wrapper - StrBufAppendPrintf(sj, "
    "); // begin avatar FIXME move the style to a stylesheet - StrBufAppendPrintf(sj, " "); // FIXME temporary avatar - StrBufAppendPrintf(sj, "
    "); // end avatar - StrBufAppendPrintf(sj, "
    "); // begin content - StrBufAppendPrintf(sj, "
    "); // begin header - StrBufAppendPrintf(sj, "%s ", author); // FIXME link to user profile or whatever - StrBufAppendPrintf(sj, "%s ", datetime); - StrBufAppendPrintf(sj, "
    "); // end header - StrBufAppendPrintf(sj, "
    "); // begin body - - if (raw_msg) { - - // These are the encodings we know how to handle. Decode in-place. - - if (!strcasecmp(content_transfer_encoding, "base64")) { - StrBufDecodeBase64(raw_msg); - } - if (!strcasecmp(content_transfer_encoding, "quoted-printable")) { - StrBufDecodeQP(raw_msg); - } - - // At this point, raw_msg contains the decoded message. - // Now run through the renderers we have available. - - if (!strncasecmp(content_type, "text/html", 9)) { - sanitized_msg = html2html("UTF-8", 0, c->room, msgnum, raw_msg); - } - else if (!strncasecmp(content_type, "text/plain", 10)) { - sanitized_msg = text2html("UTF-8", 0, c->room, msgnum, raw_msg); - } - else if (!strncasecmp(content_type, "text/x-citadel-variformat", 25)) { - sanitized_msg = variformat2html(raw_msg); - } - else { - sanitized_msg = NewStrBufPlain(HKEY("No renderer for this content type
    ")); - } - FreeStrBuf(&raw_msg); - - // If sanitized_msg is not NULL, we have rendered the message and can output it. - - if (sanitized_msg) { - StrBufAppendBuf(sj, sanitized_msg, 0); - FreeStrBuf(&sanitized_msg); - } - } - - StrBufAppendPrintf(sj, "
    "); // end body - StrBufAppendPrintf(sj, "
    "); // end content - StrBufAppendPrintf(sj, "
    "); // end wrapper -} - - -// Threaded view (recursive section) -// -void thread_o_print(struct ctdlsession *c, StrBuf *sj, struct mthread *m, int num_msgs, int where_parent_is, int nesting_level) -{ - int i = 0; - int j = 0; - int num_printed = 0; - - for (i=0; i"); - } - - StrBufAppendPrintf(sj, "
  • ", m[i].msgnum); - thread_render_one_message(c, sj, m[i].msgnum); - StrBufAppendPrintf(sj, "
  • \r\n"); - if (i != 0) { - thread_o_print(c, sj, m, num_msgs, i, nesting_level+1); - } - } - } - - if (num_printed > 0) { - StrBufAppendPrintf(sj, ""); - } -} - - -// Threaded view (entry point) -// -void threaded_view(struct http_transaction *h, struct ctdlsession *c, char *which) -{ - int num_msgs = 0; - int num_alloc = 0; - struct mthread *m; - char buf[1024]; - char refs[1024]; - int i, j, k; - - ctdl_printf(c, "MSGS ALL|||9"); // 9 == headers + thread references - ctdl_readline(c, buf, sizeof(buf)); - if (buf[0] != '1') { - do_404(h); - return; - } - - StrBuf *sj = NewStrBuf(); - StrBufAppendPrintf(sj, "\r\n"); - - while (ctdl_readline(c, buf, sizeof buf), strcmp(buf,"000")) { - - ++num_msgs; - if (num_msgs > num_alloc) { - if (num_alloc == 0) { - num_alloc = 100; - m = malloc(num_alloc * sizeof(struct mthread)); - } - else { - num_alloc *= 2; - m = realloc(m, (num_alloc * sizeof(struct mthread))); - } - } - - memset(&m[num_msgs-1], 0, sizeof(struct mthread)); - m[num_msgs-1].msgnum = extract_long(buf, 0); - m[num_msgs-1].datetime = extract_long(buf, 1); - extract_token(m[num_msgs-1].from, buf, 2, '|', sizeof m[num_msgs-1].from); - m[num_msgs-1].threadhash = extract_int(buf, 6); - extract_token(refs, buf, 7, '|', sizeof refs); - - char *t; - char *r = refs; - i = 0; - while ((t = strtok_r(r, ",", &r))) { - if (i == 0) { - m[num_msgs-1].refhashes[0] = atoi(t); // always keep the first one - } - else { - memcpy(&m[num_msgs-1].refhashes[1], &m[num_msgs-1].refhashes[2], sizeof(int)*8 ); // shift the rest - m[num_msgs-1].refhashes[9] = atoi(t); - } - ++i; - } - - } - - // Sort by thread. I did read jwz's sorting algorithm and it looks pretty good, but jwz is a self-righteous asshole so we do it our way. - for (i=0; i=0)&&(m[i].parent==0); --j) { - for (k=0; (k 0) { - free(m); - } - - StrBufAppendPrintf(sj, "\r\n"); - - add_response_header(h, strdup("Content-type"), strdup("text/html; charset=utf-8")); - h->response_code = 200; - h->response_string = strdup("OK"); - h->response_body_length = StrLength(sj); - h->response_body = SmashStrBuf(&sj); - return; -} diff --git a/webcit-ng/webcit.h b/webcit-ng/webcit.h index 8dfcf7116..72e40eed8 100644 --- a/webcit-ng/webcit.h +++ b/webcit-ng/webcit.h @@ -147,6 +147,7 @@ void dav_put_message(struct http_transaction *h, struct ctdlsession *c, char *eu ssize_t ctdl_write(struct ctdlsession *ctdl, const void *buf, size_t count); int login_to_citadel(struct ctdlsession *c, char *auth, char *resultbuf); void threaded_view(struct http_transaction *h, struct ctdlsession *c, char *which); +void flat_view(struct http_transaction *h, struct ctdlsession *c, char *which); StrBuf *ctdl_readtextmsg(struct ctdlsession *ctdl); StrBuf *html2html(const char *supplied_charset, int treat_as_wiki, char *roomname, long msgnum, StrBuf *Source); void download_mime_component(struct http_transaction *h, struct ctdlsession *c, long msgnum, char *partnum);