X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=webcit%2Fbbsview_renderer.c;h=67f42b12fb025aa4f9d7079bbcefcb2d5247c5bf;hb=HEAD;hp=958e28a92312fed9dc3a5cf91800e52c2582053b;hpb=c5b6fbb9fa01035c16b1a62af9314a19307558fe;p=citadel.git diff --git a/webcit/bbsview_renderer.c b/webcit/bbsview_renderer.c index 958e28a92..d06045d0b 100644 --- a/webcit/bbsview_renderer.c +++ b/webcit/bbsview_renderer.c @@ -1,266 +1,410 @@ +/* + * BBS View renderer module for WebCit + * + * Note: we briefly had a dynamic UI for this. I thought it was cool, but + * it was not received well by the user community. If you want to play + * with it, go get commit dcf99fe61379b78436c387ea3f89ebfd4ffaf635 of + * bbsview_renderer.c and have fun. + * + * Copyright (c) 1996-2012 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. + */ + +#define RANGE 5 + #include "webcit.h" -#include "webserver.h" -#include "groupdav.h" +#include "dav.h" -typedef struct _bbsview_stuct { - StrBuf *BBViewToolBar; - StrBuf *MessageDropdown; - long *displayed_msgs; - int a; -} bbsview_struct; +/* + * Data which gets passed around between the various functions in this module + * + */ +struct bbsview { + long *msgs; /* Array of msgnums for messages we are displaying */ + int num_msgs; /* Number of msgnums stored in 'msgs' */ + long lastseen; /* The number of the last seen message in this room */ + int alloc_msgs; /* Currently allocated size of array */ + int requested_page; /* Which page number did the user request? */ + int num_pages; /* Total number of pages in this room */ + long start_reading_at; /* Start reading at the page containing this message */ +}; -int bbsview_GetParamsGetServerCall(SharedMessageStatus *Stat, - void **ViewSpecific, - long oper, - char *cmd, - long len) + +/* + * Attempt to determine the closest thing to the "last seen message number" using the + * results of the GTSN command + */ +long bbsview_get_last_seen(void) { - 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; + char buf[SIZ] = "0"; + + serv_puts("GTSN"); + serv_getln(buf, sizeof buf); + if (buf[0] == '2') { + char *colon_pos; + char *comma_pos; + + comma_pos = strchr(buf, ','); /* kill first comma and everything to its right */ + if (comma_pos) { + *comma_pos = 0; + } + + colon_pos = strchr(buf, ':'); /* kill first colon and everything to its left */ + if (colon_pos) { + strcpy(buf, ++colon_pos); + } } - return 200; + return(atol(buf)); } -/* startmsg is an index within the message list. - * starting_from is the Citadel message number to be supplied to a "MSGS GT" operation + + +/* + * Entry point for message read operations. */ -long DrawMessageDropdown(StrBuf *Selector, long maxmsgs, long startmsg, int nMessages, long starting_from) +int bbsview_GetParamsGetServerCall(SharedMessageStatus *Stat, + void **ViewSpecific, + long oper, + char *cmd, + long len, + char *filter, + long flen) { - StrBuf *TmpBuf; - wcsession *WCC = WC; - void *vMsg; - int lo, hi; - long ret; - long hklen; - const char *key; - int nItems; - HashPos *At; - long vector[16]; - WCTemplputParams SubTP; - int wantmore = 1; - - 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 (wantmore) - { - - vector[3] = abs(nMessages); - lo = GetHashPosCounter(WCC->summ, At); - wantmore = GetNextHashPos(WCC->summ, At, &hklen, &key, &vMsg); - if (!wantmore) - break; - 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; - } - } - - /* - * 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; + struct bbsview *BBS = malloc(sizeof(struct bbsview)); + memset(BBS, 0, sizeof(struct bbsview)); + *ViewSpecific = BBS; + + Stat->startmsg = (-1); /* not used here */ + Stat->sortit = 1; /* not used here */ + Stat->num_displayed = DEFAULT_MAXMSGS; /* not used here */ + BBS->requested_page = 0; + BBS->lastseen = bbsview_get_last_seen(); + BBS->start_reading_at = 0; + + /* By default, the requested page is the first one. */ + if (havebstr("start_reading_at")) { + BBS->start_reading_at = lbstr("start_reading_at"); + BBS->requested_page = (-4); } - 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; -} + /* However, if we are asked to start with a specific message number, make sure + * we start on the page containing that message + */ -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); + /* Or, if a specific page was requested, make sure we go there */ + else if (havebstr("page")) { + BBS->requested_page = ibstr("page"); + } + /* Otherwise, if this is a "read new" operation, make sure we start on the page + * containing the first new message + */ + else if (oper == 3) { + BBS->requested_page = (-3); } - if (Stat->nummsgs > 0) { - 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); + + if (havebstr("maxmsgs")) { + Stat->maxmsgs = ibstr("maxmsgs"); } + if (Stat->maxmsgs == 0) Stat->maxmsgs = DEFAULT_MAXMSGS; + + /* perform a "read all" call to fetch the message list -- we'll cut it down later */ + rlid[2].cmd(cmd, len); + return 200; } + +/* + * This function is called for every message in the list. + */ 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)); + struct bbsview *BBS = (struct bbsview *) *ViewSpecific; + + if (BBS->alloc_msgs == 0) { + BBS->alloc_msgs = 1000; + BBS->msgs = malloc(BBS->alloc_msgs * sizeof(long)); + memset(BBS->msgs, 0, (BBS->alloc_msgs * sizeof(long)) ); } - if ((i >= Stat->startmsg) && (i < Stat->startmsg + Stat->maxmsgs)) { - VS->displayed_msgs[Stat->num_displayed] = Msg->msgnum; - Stat->num_displayed++; + + /* Check our buffer size */ + if (BBS->num_msgs >= BBS->alloc_msgs) { + BBS->alloc_msgs *= 2; + BBS->msgs = realloc(BBS->msgs, (BBS->alloc_msgs * sizeof(long))); + memset(&BBS->msgs[BBS->num_msgs], 0, ((BBS->alloc_msgs - BBS->num_msgs) * sizeof(long)) ); } + + BBS->msgs[BBS->num_msgs++] = Msg->msgnum; + return 200; } +int bbsview_sortfunc(const void *s1, const void *s2) { + long l1; + long l2; + + l1 = *(long *)(s1); + l2 = *(long *)(s2); + + if (l1 > l2) return(+1); + if (l1 < l2) return(-1); + return(0); +} + + int bbsview_RenderView_or_Tail(SharedMessageStatus *Stat, void **ViewSpecific, long oper) { - wcsession *WCC = WC; - bbsview_struct *VS; - WCTemplputParams SubTP; + struct bbsview *BBS = (struct bbsview *) *ViewSpecific; + int i; + int seq; const StrBuf *Mime; + int start_index = 0; + int end_index = 0; + int go_to_the_very_end = 0; + + if (Stat->nummsgs > 0) { + syslog(LOG_DEBUG, "sorting %d messages\n", BBS->num_msgs); + qsort(BBS->msgs, (size_t)(BBS->num_msgs), sizeof(long), bbsview_sortfunc); + } + + if ((BBS->num_msgs % Stat->maxmsgs) == 0) { + BBS->num_pages = BBS->num_msgs / Stat->maxmsgs; + } + else { + BBS->num_pages = (BBS->num_msgs / Stat->maxmsgs) + 1; + } - VS = (bbsview_struct*)*ViewSpecific; - if (Stat->nummsgs == 0) { - wc_printf("

"); - switch (oper) { - case readnew: - wc_printf(_("No new messages.")); - break; - case readold: - wc_printf(_("No old messages.")); - break; - default: - wc_printf(_("No messages here.")); + /* If the requested page number is -4, + * it means "whichever page on which msg#xxxxx starts" + * Change to the page number which contains that message. + */ + if (BBS->requested_page == (-4)) { + if (BBS->num_msgs == 0) { + BBS->requested_page = 0; + } + else { + for (i=0; inum_msgs; ++i) { + if ( + (BBS->msgs[i] >= BBS->start_reading_at) + && (BBS->requested_page == (-4)) + ) { + BBS->requested_page = (i / Stat->maxmsgs) ; + } + } } - wc_printf("
\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 the requested page number is -3, + * it means "whichever page on which new messages start" + * Change that to an actual page number now. + */ + if (BBS->requested_page == (-3)) { + if (BBS->num_msgs == 0) { + /* + * The room is empty; just start at the top and leave it there. + */ + BBS->requested_page = 0; + } + else if ( + (BBS->num_msgs > 0) + && (BBS->lastseen <= BBS->msgs[0]) + ) { + /* + * All messages are new; this is probably the user's first visit to the room, + * so start at the last page instead of showing ancient history. + */ + BBS->requested_page = BBS->num_pages - 1; + go_to_the_very_end = 1; + } + else { + /* + * Some messages are old and some are new. Go to the start of new messages. + */ + for (i=0; inum_msgs; ++i) { + if ( + (BBS->msgs[i] > BBS->lastseen) + && ( (i == 0) || (BBS->msgs[i-1] <= BBS->lastseen) ) + ) { + BBS->requested_page = (i / Stat->maxmsgs) ; + } } - - /* 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; + /* Still set to -3 ? If so, that probably means that there are no new messages, + * so we'll go to the *end* of the final page. + */ + if (BBS->requested_page == (-3)) { + if (BBS->num_msgs == 0) { + BBS->requested_page = 0; + } + else { + BBS->requested_page = BBS->num_pages - 1; + } + } + + /* keep the requested page within bounds */ + if (BBS->requested_page < 0) BBS->requested_page = 0; + if (BBS->requested_page >= BBS->num_pages) BBS->requested_page = BBS->num_pages - 1; + + start_index = BBS->requested_page * Stat->maxmsgs; + if (start_index < 0) start_index = 0; + end_index = start_index + Stat->maxmsgs - 1; + + for (seq = 0; seq < 3; ++seq) { /* cheap & sleazy way of rendering the page numbers twice */ + + if ( (seq == 1) && (Stat->nummsgs > 0)) { + /* display the selected range of messages */ + + for (i=start_index; (i<=end_index && inum_msgs); ++i) { + if ( + (BBS->msgs[i] > BBS->lastseen) + && ( (i == 0) || (BBS->msgs[i-1] <= BBS->lastseen) ) + ) { + /* new messages start here */ + do_template("start_of_new_msgs"); + if (!go_to_the_very_end) { + StrBufAppendPrintf(WC->trailing_javascript, "location.href=\"#newmsgs\";\n"); + } + } + if (BBS->msgs[i] > 0L) { + read_message(WC->WBuf, HKEY("view_message"), BBS->msgs[i], NULL, &Mime, NULL); + } + if ( + (i == (BBS->num_msgs - 1)) + && (BBS->msgs[i] <= BBS->lastseen) + ) { + /* no new messages */ + do_template("no_new_msgs"); + if (!go_to_the_very_end) { + StrBufAppendPrintf(WC->trailing_javascript, "location.href=\"#nonewmsgs\";\n"); + } + } + } + } + + else if ( (seq == 0) || (seq == 2) ) { + int first; + int last; + /* Display the selecto-bar with the page numbers */ + + wc_printf("
"); + if (seq == 2) { + wc_printf(""); + } + wc_printf(_("Go to page: ")); + if (seq == 2) { + wc_printf(""); + } + + first = 0; + last = BBS->num_pages - 1; + + for (i=0; i<=last; ++i) { + + if ( + (i == first) + || (i == last) + || (i == BBS->requested_page) + || ( + ((BBS->requested_page - i) < RANGE) + && ((BBS->requested_page - i) > (0 - RANGE)) + ) + ) { + + if ( + (i == last) + && (last - BBS->requested_page > RANGE) + ) { + wc_printf("... "); + } + if (i == BBS->requested_page) { + wc_printf("["); + } + else { + wc_printf("CurRoom.name)); + wc_printf("?start_reading_at=%ld\">", + BBS->msgs[i*Stat->maxmsgs] + ); + /* wc_printf("?page=%d\">", i); */ + wc_printf(""); + } + if ( + (i == first) + && (BBS->requested_page > (RANGE + 1)) + ) { + wc_printf(_("First")); + } + else if ( + (i == last) + && (last - BBS->requested_page > RANGE) + ) { + wc_printf(_("Last")); + } + else { + wc_printf("%d", i + 1); /* change to one-based for display */ + } + if (i == BBS->requested_page) { + wc_printf("]"); + } + else { + wc_printf(""); + wc_printf(""); + } + if ( + (i == first) + && (BBS->requested_page > (RANGE + 1)) + ) { + wc_printf(" ..."); + } + if (i != last) { + wc_printf(" "); + } + } + } + wc_printf("
\n"); + } + } + + if (go_to_the_very_end) { + StrBufAppendPrintf(WC->trailing_javascript, "location.href=\"#end_of_msgs\";\n"); + } + return(0); } int bbsview_Cleanup(void **ViewSpecific) { - bbsview_struct *VS; + struct bbsview *BBS = (struct bbsview *) *ViewSpecific; + + if (BBS->alloc_msgs != 0) { + free(BBS->msgs); + } + free(BBS); - VS = (bbsview_struct*)*ViewSpecific; wDumpContent(1); - FreeStrBuf(&VS->BBViewToolBar); - FreeStrBuf(&VS->MessageDropdown); - free(VS); return 0; } + void InitModule_BBSVIEWRENDERERS (void) @@ -268,8 +412,12 @@ InitModule_BBSVIEWRENDERERS RegisterReadLoopHandlerset( VIEW_BBS, bbsview_GetParamsGetServerCall, - bbsview_PrintViewHeader, + NULL, + NULL, + NULL, bbsview_LoadMsgFromServer, bbsview_RenderView_or_Tail, - bbsview_Cleanup); + bbsview_Cleanup, + NULL + ); }