* Enhance the older/newer logic. It's still not quite right.
[citadel.git] / webcit / bbsview_renderer.c
1 #include "webcit.h"
2 #include "webserver.h"
3 #include "groupdav.h"
4
5
6
7 /* We're jamming both of these in here so I can develop the new BBS view in-tree.
8  * Define NEW_BBS_BIEW to get the new, better, but unfinished and untested version.
9  *
10  */
11
12 #ifndef NEW_BBS_VIEW
13
14
15 /*** Code for the OLD bbs view ***/
16
17
18 typedef struct _bbsview_stuct {
19         StrBuf *BBViewToolBar;
20         StrBuf *MessageDropdown;
21         long *displayed_msgs;
22         int a;
23 } bbsview_struct;
24
25 int bbsview_GetParamsGetServerCall(SharedMessageStatus *Stat, 
26                                    void **ViewSpecific, 
27                                    long oper, 
28                                    char *cmd, 
29                                    long len)
30 {
31         bbsview_struct *VS;
32
33         VS = (bbsview_struct*) malloc(sizeof(bbsview_struct));
34         memset(VS, 0, sizeof(bbsview_struct));
35         *ViewSpecific = (void*)VS;
36         Stat->defaultsortorder = 1;
37         Stat->startmsg = -1;
38         Stat->sortit = 1;
39         
40         rlid[oper].cmd(cmd, len);
41         
42         if (havebstr("maxmsgs"))
43                 Stat->maxmsgs = ibstr("maxmsgs");
44         if (Stat->maxmsgs == 0) Stat->maxmsgs = DEFAULT_MAXMSGS;
45         
46         if (havebstr("startmsg")) {
47                 Stat->startmsg = lbstr("startmsg");
48         }
49         if (lbstr("SortOrder") == 2) {
50                 Stat->reverse = 1;
51                 Stat->num_displayed = -DEFAULT_MAXMSGS;
52         }
53         else {
54                 Stat->reverse = 0;
55                 Stat->num_displayed = DEFAULT_MAXMSGS;
56         }
57
58         return 200;
59 }
60
61 /* startmsg is an index within the message list.
62  * starting_from is the Citadel message number to be supplied to a "MSGS GT" operation
63  */
64 long DrawMessageDropdown(StrBuf *Selector, long maxmsgs, long startmsg, int nMessages, long starting_from)
65 {
66         StrBuf *TmpBuf;
67         wcsession *WCC = WC;
68         void *vMsg;
69         int lo, hi;
70         long ret;
71         long hklen;
72         const char *key;
73         int nItems;
74         HashPos *At;
75         long vector[16];
76         WCTemplputParams SubTP;
77         int wantmore = 1;
78
79         memset(&SubTP, 0, sizeof(WCTemplputParams));
80         SubTP.Filter.ContextType = CTX_LONGVECTOR;
81         SubTP.Context = &vector;
82         TmpBuf = NewStrBufPlain(NULL, SIZ);
83         At = GetNewHashPos(WCC->summ, nMessages);
84         nItems = GetCount(WCC->summ);
85         ret = nMessages;
86         vector[0] = 7;
87         vector[2] = 1;
88         vector[1] = startmsg;
89         vector[3] = 0;
90         vector[7] = starting_from;
91
92         while (wantmore)
93         {
94         
95                 vector[3] = abs(nMessages);
96                 lo = GetHashPosCounter(WCC->summ, At);
97                 wantmore = GetNextHashPos(WCC->summ, At, &hklen, &key, &vMsg);
98                 if (!wantmore)
99                         break;
100                 if (nMessages > 0) {
101                         if (lo + nMessages >= nItems) {
102                                 hi = nItems - 1;
103                                 vector[3] = nItems - lo;
104                                 if (startmsg == lo) 
105                                         ret = vector[3];
106                         }
107                         else {
108                                 hi = lo + nMessages - 1;
109                         }
110                 } else {
111                         if (lo + nMessages < -1) {
112                                 hi = 0;
113                         }
114                         else {
115                                 if ((lo % abs(nMessages)) != 0) {
116                                         int offset = (lo % abs(nMessages) *
117                                                       (nMessages / abs(nMessages)));
118                                         hi = lo + offset;
119                                         vector[3] = abs(offset);
120                                         if (startmsg == lo)
121                                                  ret = offset;
122                                 }
123                                 else
124                                         hi = lo + nMessages;
125                         }
126                 }
127                 
128                 /*
129                  * Bump these because although we're thinking in zero base, the user
130                  * is a drooling idiot and is thinking in one base.
131                  */
132                 vector[4] = lo + 1;
133                 vector[5] = hi + 1;
134                 vector[6] = lo;
135                 FlushStrBuf(TmpBuf);
136                 dbg_print_longvector(vector);
137                 DoTemplate(HKEY("select_messageindex"), TmpBuf, &SubTP);
138                 StrBufAppendBuf(Selector, TmpBuf, 0);
139         }
140         vector[6] = 0;
141         FlushStrBuf(TmpBuf);
142         if (maxmsgs == 9999999) {
143                 vector[1] = 1;
144                 ret = maxmsgs;
145         }
146         else
147                 vector[1] = 0;          
148         vector[2] = 0;
149         dbg_print_longvector(vector);
150         DoTemplate(HKEY("select_messageindex_all"), TmpBuf, &SubTP);
151         StrBufAppendBuf(Selector, TmpBuf, 0);
152         FreeStrBuf(&TmpBuf);
153         DeleteHashPos(&At);
154         return ret;
155 }
156
157
158 int bbsview_PrintViewHeader(SharedMessageStatus *Stat, void **ViewSpecific)
159 {
160         bbsview_struct *VS;
161         WCTemplputParams SubTP;
162
163         VS = (bbsview_struct*)*ViewSpecific;
164
165         VS->BBViewToolBar = NewStrBufPlain(NULL, SIZ);
166         VS->MessageDropdown = NewStrBufPlain(NULL, SIZ);
167
168         /*** startmsg->maxmsgs = **/DrawMessageDropdown(VS->MessageDropdown, 
169                                                         Stat->maxmsgs, 
170                                                         Stat->startmsg,
171                                                         Stat->num_displayed, 
172                                                         Stat->lowest_found-1);
173         if (Stat->num_displayed < 0) {
174                 Stat->startmsg += Stat->maxmsgs;
175                 if (Stat->num_displayed != Stat->maxmsgs)                               
176                         Stat->maxmsgs = abs(Stat->maxmsgs) + 1;
177                 else
178                         Stat->maxmsgs = abs(Stat->maxmsgs);
179
180         }
181         if (Stat->nummsgs > 0) {
182                 memset(&SubTP, 0, sizeof(WCTemplputParams));
183                 SubTP.Filter.ContextType = CTX_STRBUF;
184                 SubTP.Context = VS->MessageDropdown;
185                 DoTemplate(HKEY("msg_listselector_top"), VS->BBViewToolBar, &SubTP);
186                 StrBufAppendBuf(WC->WBuf, VS->BBViewToolBar, 0);
187                 FlushStrBuf(VS->BBViewToolBar);
188         }
189         return 200;
190 }
191
192 int bbsview_LoadMsgFromServer(SharedMessageStatus *Stat, 
193                               void **ViewSpecific, 
194                               message_summary* Msg, 
195                               int is_new, 
196                               int i)
197 {
198         bbsview_struct *VS;
199
200         VS = (bbsview_struct*)*ViewSpecific;
201         if (VS->displayed_msgs == NULL) {
202                 VS->displayed_msgs = malloc(sizeof(long) *
203                                             ((Stat->maxmsgs < Stat->nummsgs) ? 
204                                              Stat->maxmsgs + 1 : 
205                                              Stat->nummsgs + 1));
206         }
207         if ((i >= Stat->startmsg) && (i < Stat->startmsg + Stat->maxmsgs)) {
208                 VS->displayed_msgs[Stat->num_displayed] = Msg->msgnum;
209                 Stat->num_displayed++;
210         }
211         return 200;
212 }
213
214
215 int bbsview_RenderView_or_Tail(SharedMessageStatus *Stat, 
216                                void **ViewSpecific, 
217                                long oper)
218 {
219         wcsession *WCC = WC;
220         bbsview_struct *VS;
221         WCTemplputParams SubTP;
222         const StrBuf *Mime;
223
224         VS = (bbsview_struct*)*ViewSpecific;
225         if (Stat->nummsgs == 0) {
226                 wc_printf("<div class=\"nomsgs\"><br><em>");
227                 switch (oper) {
228                 case readnew:
229                         wc_printf(_("No new messages."));
230                         break;
231                 case readold:
232                         wc_printf(_("No old messages."));
233                         break;
234                 default:
235                         wc_printf(_("No messages here."));
236                 }
237                 wc_printf("</em><br></div>\n");
238         }
239         else 
240         {
241                 if (VS->displayed_msgs != NULL) {
242                         /* if we do a split bbview in the future, begin messages div here */
243                         int a;/// todo  
244                         for (a=0; a < Stat->num_displayed; ++a) {
245                                 read_message(WCC->WBuf, HKEY("view_message"), VS->displayed_msgs[a], NULL, &Mime);
246                         }
247                         
248                         /* if we do a split bbview in the future, end messages div here */
249                         
250                         free(VS->displayed_msgs);
251                         VS->displayed_msgs = NULL;
252                 }
253                 memset(&SubTP, 0, sizeof(WCTemplputParams));
254                 SubTP.Filter.ContextType = CTX_STRBUF;
255                 SubTP.Context = VS->MessageDropdown;
256                 DoTemplate(HKEY("msg_listselector_bottom"), VS->BBViewToolBar, &SubTP);
257                 StrBufAppendBuf(WCC->WBuf, VS->BBViewToolBar, 0);
258         }
259         return 0;
260
261 }
262
263
264 int bbsview_Cleanup(void **ViewSpecific)
265 {
266         bbsview_struct *VS;
267
268         VS = (bbsview_struct*)*ViewSpecific;
269         wDumpContent(1);
270         FreeStrBuf(&VS->BBViewToolBar);
271         FreeStrBuf(&VS->MessageDropdown);
272         free(VS);
273         return 0;
274 }
275
276 void 
277 InitModule_BBSVIEWRENDERERS
278 (void)
279 {
280         RegisterReadLoopHandlerset(
281                 VIEW_BBS,
282                 bbsview_GetParamsGetServerCall,
283                 bbsview_PrintViewHeader,
284                 bbsview_LoadMsgFromServer,
285                 bbsview_RenderView_or_Tail,
286                 bbsview_Cleanup);
287 }
288
289
290
291
292
293 #else /* NEW_BBS_VIEW */
294
295
296 /*** Code for the NEW bbs view ***/
297
298
299
300 /*
301  * Data which gets passed around between the various functions in this module
302  */
303 struct bbsview {
304         long *msgs;             /* Array of msgnums for messages we are displaying */
305         int num_msgs;           /* Number of msgnums stored in 'msgs' */
306         int alloc_msgs;         /* Currently allocated size of array */
307 };
308
309
310 /*
311  * Entry point for message read operations.
312  */
313 int bbsview_GetParamsGetServerCall(SharedMessageStatus *Stat, 
314                                    void **ViewSpecific, 
315                                    long oper, 
316                                    char *cmd, 
317                                    long len)
318 {
319         struct bbsview *BBS = malloc(sizeof(struct bbsview));
320         memset(BBS, 0, sizeof(struct bbsview));
321         *ViewSpecific = BBS;
322
323         Stat->defaultsortorder = 1;
324         Stat->startmsg = -1;
325         Stat->sortit = 1;
326         
327         rlid[oper].cmd(cmd, len);               /* this performs the server call to fetch the msg list */
328         
329         if (havebstr("maxmsgs")) {
330                 Stat->maxmsgs = ibstr("maxmsgs");
331         }
332         if (Stat->maxmsgs == 0) Stat->maxmsgs = DEFAULT_MAXMSGS;
333         
334         if (havebstr("startmsg")) {
335                 Stat->startmsg = lbstr("startmsg");
336         }
337         if (lbstr("SortOrder") == 2) {
338                 Stat->reverse = 1;
339                 Stat->num_displayed = -DEFAULT_MAXMSGS;
340         }
341         else {
342                 Stat->reverse = 0;
343                 Stat->num_displayed = DEFAULT_MAXMSGS;
344         }
345
346         return 200;
347 }
348
349
350 /*
351  * begin_ajax_response() was moved from bbsview_LoadMsgFromServer() to here ...
352  */
353 int bbsview_PrintViewHeader(SharedMessageStatus *Stat, void **ViewSpecific)
354 {
355         lprintf(9, "bbsview_PrintViewHeader() has been called.\n");
356
357         if (WC->is_ajax) {
358                 begin_ajax_response();          /* for non-ajax, headers are output in messages.c */
359         }
360
361         return 200;
362 }
363
364
365 /*
366  * This function is called for every message in the list.
367  */
368 int bbsview_LoadMsgFromServer(SharedMessageStatus *Stat, 
369                               void **ViewSpecific, 
370                               message_summary* Msg, 
371                               int is_new, 
372                               int i)
373 {
374         struct bbsview *BBS = (struct bbsview *) *ViewSpecific;
375
376         lprintf(9, "bbsview_LoadMsgFromServer() has been called.\n");
377
378         if (BBS->alloc_msgs == 0) {
379                 BBS->alloc_msgs = Stat->maxmsgs;
380                 BBS->msgs = malloc(BBS->alloc_msgs * sizeof(long));
381         }
382
383         /* Theoretically this never happens because the initial allocation == maxmsgs */
384         if (BBS->num_msgs >= BBS->alloc_msgs) {
385                 BBS->alloc_msgs *= 2;
386                 BBS->msgs = realloc(BBS->msgs, (BBS->alloc_msgs * sizeof(long)));
387         }
388
389         BBS->msgs[BBS->num_msgs++] = Msg->msgnum;
390
391         return 200;
392 }
393
394 int bbsview_sortfunc_reverse(const void *s1, const void *s2) {
395         long l1;
396         long l2;
397
398         l1 = *(long *)(s1);
399         l2 = *(long *)(s2);
400
401         if (l1 > l2) return(-1);
402         if (l1 < l2) return(+1);
403         return(0);
404 }
405
406
407 int bbsview_sortfunc_forward(const void *s1, const void *s2) {
408         long l1;
409         long l2;
410
411         l1 = *(long *)(s1);
412         l2 = *(long *)(s2);
413
414         if (l1 > l2) return(+1);
415         if (l1 < l2) return(-1);
416         return(0);
417 }
418
419
420 int bbsview_RenderView_or_Tail(SharedMessageStatus *Stat, 
421                                void **ViewSpecific, 
422                                long oper)
423 {
424         struct bbsview *BBS = (struct bbsview *) *ViewSpecific;
425         int i;
426         const StrBuf *Mime;
427         char olderdiv[64];
428         char newerdiv[64];
429         int doing_older_messages = 0;
430         int doing_newer_messages = 0;
431
432         snprintf(olderdiv, sizeof olderdiv, "olderdiv%08lx%08x", time(NULL), rand());
433         snprintf(newerdiv, sizeof newerdiv, "newerdiv%08lx%08x", time(NULL), rand());
434
435         lprintf(9, "starting bbsview_RenderView_or_Tail() - there are %d messages.\n", BBS->num_msgs);
436
437         /* If this is the initial page load (and not an update), supply the required JavaScript code */
438         if (!WC->is_ajax) {
439            StrBufAppendPrintf(WC->trailing_javascript,
440                 "       function moremsgs(target_div, gt_or_lt, gt_or_lt_value, maxmsgs, sortorder) {   \n"
441                 "               $(target_div).innerHTML = '<div class=\"moreprompt\">%s ... <img src=\"static/throbber.gif\"><br><br><br></div>';       \n"
442                 "               p = gt_or_lt + '=' + gt_or_lt_value + '&maxmsgs=' + maxmsgs             \n"
443                 "                       + '&is_summary=0&SortOrder=' + sortorder + '&is_ajax=1'         \n"
444                 "                       + '&gt_or_lt=' + gt_or_lt                                       \n"
445                 "                       + '&r=' + CtdlRandomString();                                   \n"
446                 "               new Ajax.Updater(target_div, 'read' + gt_or_lt,                                 \n"
447                 "                       { method: 'get', parameters: p, evalScripts: true } );          \n"
448                 "       }                                                                               \n"
449                 "",
450                 _("Loading")
451            );
452         }
453
454
455         /* Determine whether we are in the middle of a 'click for older messages' or 'click for
456          * newer messages' operation.  If neither, then we are in the initial page load.
457          */
458         if (!strcasecmp(bstr("gt_or_lt"), "lt")) {
459                 doing_older_messages = 1;
460                 doing_newer_messages = 0;
461                 lprintf(9, "\033[31m ** OLDER MESSAGES ** \033[0m\n");
462         }
463         else if (!strcasecmp(bstr("gt_or_lt"), "gt")) {
464                 doing_older_messages = 0;
465                 doing_newer_messages = 1;
466                 lprintf(9, "\033[32m ** NEWER MESSAGES ** \033[0m\n");
467         }
468         else {
469                 doing_older_messages = 0;
470                 doing_newer_messages = 0;
471                 lprintf(9, "\033[33m ** INITIAL PAGE LOAD ** \033[0m\n");
472         }
473
474
475         /* Supply the link to prepend the previous 20 messages */
476
477         if (doing_newer_messages == 0) {
478                 wc_printf("<div id=\"%s\">", olderdiv);
479                 /* if (Stat->nummsgs > 0) { */
480                 if (Stat->nummsgs > 0) {
481                         wc_printf("<a href=\"javascript:moremsgs('%s', 'lt', %ld, %ld, %d );\">",
482                                 olderdiv,
483                                 BBS->msgs[0],
484                                 Stat->maxmsgs,
485                                 (Stat->reverse ? 2 : 1)
486                         );
487                 
488                         wc_printf("<div class=\"moreprompt\">"
489                                 "&uarr; &uarr; &uarr; %s &uarr; &uarr; &uarr;"
490                                 "</div>", _("click here for older messages")
491                         );
492                         wc_printf("</a>");
493                 }
494                 wc_printf("<br></div>");
495         }
496
497
498
499         /* Handle the empty message set gracefully... */
500         if (Stat->nummsgs == 0) {
501                 if (!WC->is_ajax) {
502                         wc_printf("<div class=\"nomsgs\"><br><em>");
503                         wc_printf(_("No messages here."));
504                         wc_printf("</em><br></div>\n");
505                 }
506         }
507
508         /* Non-empty message set... */
509         else {
510                 lprintf(9, "sorting %d messages\n", BBS->num_msgs);
511                 qsort(BBS->msgs, (size_t)(BBS->num_msgs), sizeof(long), (Stat->reverse ? bbsview_sortfunc_reverse : bbsview_sortfunc_forward));
512
513                 /* Cut it down to 20 messages (or whatever value Stat->maxmsgs is set to) */
514
515                 if (BBS->num_msgs > Stat->maxmsgs) {
516
517                         if (doing_older_messages) {
518                                 /* LT ... cut it down to the LAST 20 messages received */
519                                 memcpy(&BBS->msgs[0], &BBS->msgs[BBS->num_msgs - Stat->maxmsgs],
520                                         (Stat->maxmsgs * sizeof(long))
521                                 );
522                                 BBS->num_msgs = Stat->maxmsgs;
523                         }
524                         else {
525                                 /* GT ... cut it down to the FIRST 20 messages received */
526                                 BBS->num_msgs = Stat->maxmsgs;
527                         }
528                 }
529
530                 /* Now render them */
531         
532                 for (i=0; i<BBS->num_msgs; ++i) {
533                         read_message(WC->WBuf, HKEY("view_message"), BBS->msgs[i], NULL, &Mime);
534                 }
535
536         }
537
538
539         /* Supply the link to append the next 20 messages */
540
541         if (doing_older_messages == 0) {
542                 wc_printf("<div id=\"%s\">", newerdiv);
543                 /* if (Stat->nummsgs > 0) { */
544                 if (Stat->nummsgs >= Stat->maxmsgs) {
545                         wc_printf("<a href=\"javascript:moremsgs('%s', 'gt', %ld, %ld, %d );\">",
546                                 newerdiv,
547                                 BBS->msgs[BBS->num_msgs-1],
548                                 Stat->maxmsgs,
549                                 (Stat->reverse ? 2 : 1)
550                         );
551                 
552                         wc_printf("<div class=\"moreprompt\">"
553                                 "&darr; &darr; &darr; %s &darr; &darr; &darr;"
554                                 "</div>", _("click here for newer messages")
555                         );
556                         wc_printf("</a>");
557                 }
558                 else {
559                         long gt = 0;    /* if new messages appear later, where will they begin? */
560                         if (Stat->nummsgs > 0) {
561                                 gt = BBS->msgs[BBS->num_msgs-1];
562                         }
563                         else {
564                                 gt = atol(bstr("gt"));
565                         }
566                         wc_printf("<a href=\"javascript:moremsgs('%s', 'gt', %ld, %ld, %d );\">",
567                                 newerdiv,
568                                 gt,
569                                 Stat->maxmsgs,
570                                 (Stat->reverse ? 2 : 1)
571                         );
572                         wc_printf("<div class=\"moreprompt\">");
573                         wc_printf("no more ... gt would have been %ld ... FIXME", gt);
574                         wc_printf("</div>");
575                         wc_printf("</a>");
576                 }
577                 wc_printf("<br><br><br><br></div>");
578         }
579
580
581         return(0);
582 }
583
584
585 int bbsview_Cleanup(void **ViewSpecific)
586 {
587         struct bbsview *BBS = (struct bbsview *) *ViewSpecific;
588         lprintf(9, "bbsview_Cleanup() has been called.\n");
589         free(BBS);
590
591         if (WC->is_ajax) {
592                 lprintf(9, "end_ajax_response() has been called.\n");
593                 end_ajax_response();
594                 WC->is_ajax = 0;
595         }
596         else {
597                 lprintf(9, "wDumpContent() has been called.\n");
598                 wDumpContent(1);
599         }
600         return 0;
601 }
602
603 void 
604 InitModule_BBSVIEWRENDERERS
605 (void)
606 {
607         RegisterReadLoopHandlerset(
608                 VIEW_BBS,
609                 bbsview_GetParamsGetServerCall,
610                 bbsview_PrintViewHeader,
611                 bbsview_LoadMsgFromServer,
612                 bbsview_RenderView_or_Tail,
613                 bbsview_Cleanup);
614 }
615
616
617 #endif /* NEW_BBS_VIEW */