Require logged-in in order to revert a wiki page
[citadel.git] / webcit / wiki.c
1 /*
2  * Functions pertaining to rooms with a wiki view
3  */
4
5 #include "webcit.h"
6 #include "groupdav.h"
7
8 /* 
9  * Convert a string to something suitable as a wiki index
10  */
11 void str_wiki_index(char *s)
12 {
13         int i;
14
15         if (s == NULL) return;
16
17         /* First remove all non-alphanumeric characters */
18         for (i=0; i<strlen(s); ++i) {
19                 if (!isalnum(s[i])) {
20                         strcpy(&s[i], &s[i+1]);
21                 }
22         }
23
24         /* Then make everything lower case */
25         for (i=0; i<strlen(s); ++i) {
26                 s[i] = tolower(s[i]);
27         }
28 }
29
30 /*
31  * Display a specific page from a wiki room
32  *
33  * "rev" may be set to an empty string to display the current version.
34  * "do_revert" may be set to nonzero to perform a reversion to the specified version.
35  */
36 void display_wiki_page_backend(const StrBuf *roomname, char *pagename, char *rev, int do_revert)
37 {
38         const StrBuf *Mime;
39         long msgnum = (-1L);
40         char buf[256];
41
42         str_wiki_index(pagename);
43
44         if (StrLength(roomname) > 0) {
45
46                 /* If we're not in the correct room, try going there. */
47                 if (strcasecmp(ChrPtr(roomname), ChrPtr(WC->CurRoom.name))) {
48                         gotoroom(roomname);
49                 }
50         
51                 /* If we're still not in the correct room, it doesn't exist. */
52                 if (strcasecmp(ChrPtr(roomname), ChrPtr(WC->CurRoom.name))) {
53                         wc_printf(_("There is no room called '%s'."), ChrPtr(roomname));
54                         return;
55                 }
56
57         }
58
59         if (WC->CurRoom.view != VIEW_WIKI) {
60                 wc_printf(_("'%s' is not a Wiki room."), ChrPtr(roomname));
61                 return;
62         }
63
64         if (IsEmptyStr(pagename)) {
65                 strcpy(pagename, "home");
66         }
67
68         /* Found it!  Now read it. */
69
70         if ((rev != NULL) && (strlen(rev) > 0)) {
71                 /* read an older revision */
72                 serv_printf("WIKI rev|%s|%s|%s", pagename, rev, (do_revert ? "revert" : "fetch") );
73                 serv_getln(buf, sizeof buf);
74                 if (buf[0] == '2') {
75                         msgnum = extract_long(&buf[4], 0);
76                 }
77         }
78         else {
79                 /* read the current revision? */
80                 msgnum = locate_message_by_uid(pagename);
81         }
82
83         if (msgnum >= 0L) {
84                 read_message(WC->WBuf, HKEY("view_message"), msgnum, NULL, &Mime);
85                 return;
86         }
87
88         wc_printf("<br /><br />"
89                 "<div align=\"center\">"
90                 "<table border=\"0\" bgcolor=\"#ffffff\" cellpadding=\"10\">"
91                 "<tr><td align=\"center\">"
92         );
93         wc_printf("<br><b>");
94         wc_printf(_("There is no page called '%s' here."), pagename);
95         wc_printf("</b><br><br>");
96         wc_printf(_("Select the 'Edit this page' link in the room banner "
97                 "if you would like to create this page."));
98         wc_printf("<br><br>");
99         wc_printf("</td></tr></table></div>\n");
100 }
101
102
103 /*
104  * Display a specific page from a wiki room
105  */
106 void display_wiki_page(void)
107 {
108         const StrBuf *roomname;
109         char pagename[128];
110         char rev[128];
111         int do_revert = 0;
112
113         output_headers(1, 1, 1, 0, 0, 0);
114         roomname = sbstr("room");
115         safestrncpy(pagename, bstr("page"), sizeof pagename);
116         safestrncpy(rev, bstr("rev"), sizeof rev);
117         do_revert = atoi(bstr("revert"));
118         display_wiki_page_backend(roomname, pagename, rev, do_revert);
119         wDumpContent(1);
120 }
121
122
123 /*
124  * Display the revision history for a wiki page (template callback)
125  */
126 void tmplput_display_wiki_history(StrBuf *Target, WCTemplputParams *TP)
127 {
128         const StrBuf *roomname;
129         char pagename[128];
130         StrBuf *Buf;
131         int row = 0;
132
133         roomname = sbstr("room");
134         safestrncpy(pagename, bstr("page"), sizeof pagename);
135         str_wiki_index(pagename);
136
137         if (StrLength(roomname) > 0) {
138
139                 /* If we're not in the correct room, try going there. */
140                 if (strcasecmp(ChrPtr(roomname), ChrPtr(WC->CurRoom.name))) {
141                         gotoroom(roomname);
142                 }
143         
144                 /* If we're still not in the correct room, it doesn't exist. */
145                 if (strcasecmp(ChrPtr(roomname), ChrPtr(WC->CurRoom.name))) {
146                         wc_printf(_("There is no room called '%s'."), ChrPtr(roomname));
147                         return;
148                 }
149
150         }
151
152         serv_printf("WIKI history|%s", pagename);
153         Buf = NewStrBuf();
154         StrBuf_ServGetln(Buf);
155         if (GetServerStatus(Buf, NULL) == 1) {
156
157                 time_t rev_date;
158                 char rev_date_displayed[64];
159                 StrBuf *rev_uuid = NewStrBuf();
160                 StrBuf *author = NewStrBuf();
161                 StrBuf *node = NewStrBuf();
162
163                 wc_printf("<table class=\"wiki_history_background\">");
164
165                 wc_printf("<th>%s</th>", _("Date"));
166                 wc_printf("<th>%s</th>", _("Author"));
167
168                 while(StrBuf_ServGetln(Buf), strcmp(ChrPtr(Buf), "000")) {
169
170                         rev_date = extract_long(ChrPtr(Buf), 1);
171                         webcit_fmt_date(rev_date_displayed, sizeof rev_date_displayed, rev_date, DATEFMT_FULL);
172                         StrBufExtract_token(author, Buf, 2, '|');
173
174                         wc_printf("<tr bgcolor=\"%s\">", ((row%2) ? "#FFFFFF" : "#DDDDDD"));
175                         wc_printf("<td>%s</td><td>", rev_date_displayed);
176                         if (!strcasecmp(ChrPtr(node), (char *)WC->serv_info->serv_nodename)) {
177                                 escputs(ChrPtr(author));
178                                 wc_printf(" @ ");
179                                 escputs(ChrPtr(node));
180                         }
181                         else {
182                                 wc_printf("<a href=\"showuser?who=");
183                                 urlescputs(ChrPtr(author));
184                                 wc_printf("\">");
185                                 escputs(ChrPtr(author));
186                                 wc_printf("</a>");
187                         }
188                         wc_printf("</td>");
189
190                         if (row == 0) {
191                                 wc_printf("<td><a href=\"wiki?page=%s\">%s</a></td>",
192                                         bstr("page"),
193                                         _("(show)")
194                                 );
195                                 wc_printf("<td>(%s)</td>", _("Current version"));
196                         }
197
198                         else {
199                                 wc_printf("<td><a href=\"wiki?page=%s?rev=%s\">%s</a></td>",
200                                         bstr("page"),
201                                         ChrPtr(rev_uuid),
202                                         _("(show)")
203                                 );
204                                 wc_printf("<td><a href=\"javascript:GetLoggedInFirst(encodeURIComponent('wiki?page=%s?rev=%s?revert=1'))\">%s</a></td>",
205                                         bstr("page"),
206                                         ChrPtr(rev_uuid),
207                                         _("(revert)")
208                                 );
209                         }
210                         wc_printf("</tr>\n");
211
212                         /* Extract all fields except the author and date after displaying the row.  This
213                          * is deliberate, because the timestamp reflects when the diff was written, not
214                          * when the version which it reflects was written.  Similarly, the name associated
215                          * with each diff is the author who created the newer version of the page that
216                          * made the diff happen.
217                          */
218                         StrBufExtract_token(rev_uuid, Buf, 0, '|');
219                         StrBufExtract_token(node, Buf, 3, '|');
220                         ++row;
221                 }
222
223                 wc_printf("</table>\n");
224                 FreeStrBuf(&author);
225                 FreeStrBuf(&node);
226                 FreeStrBuf(&rev_uuid);
227         }
228         else {
229                 wc_printf("%s", ChrPtr(Buf));
230         }
231
232         FreeStrBuf(&Buf);
233 }
234
235
236
237 /*
238  * Display the revision history for a wiki page
239  */
240 void display_wiki_history(void)
241 {
242         output_headers(1, 1, 1, 0, 0, 0);
243         do_template("wiki_history", NULL);
244         wDumpContent(1);
245 }
246
247
248 /*
249  * Display a list of all pages in a Wiki room (template callback)
250  */
251 void tmplput_display_wiki_pagelist(StrBuf *Target, WCTemplputParams *TP)
252 {
253         const StrBuf *roomname;
254         StrBuf *Buf;
255         int row = 0;
256
257         roomname = sbstr("room");
258         if (StrLength(roomname) > 0) {
259                 /* If we're not in the correct room, try going there. */
260                 if (strcasecmp(ChrPtr(roomname), ChrPtr(WC->CurRoom.name))) {
261                         gotoroom(roomname);
262                 }
263         
264                 /* If we're still not in the correct room, it doesn't exist. */
265                 if (strcasecmp(ChrPtr(roomname), ChrPtr(WC->CurRoom.name))) {
266                         wc_printf(_("There is no room called '%s'."), ChrPtr(roomname));
267                         return;
268                 }
269         }
270
271         if (!IsEmptyStr(bstr("query"))) {
272                 serv_printf("MSGS SEARCH|%s||4", bstr("query"));        /* search-reduced list */
273         }
274         else {
275                 serv_printf("MSGS ALL|||4");                            /* full list */
276         }
277
278         Buf = NewStrBuf();
279         StrBuf_ServGetln(Buf);
280         if (GetServerStatus(Buf, NULL) == 1) {
281                 StrBuf *pagetitle = NewStrBuf();
282
283                 wc_printf("<table class=\"wiki_pagelist_background\">");
284                 wc_printf("<th>%s</th>", _("Page title"));
285
286                 while(StrBuf_ServGetln(Buf), strcmp(ChrPtr(Buf), "000")) {
287                         StrBufExtract_token(pagetitle, Buf, 1, '|');
288
289                         if (!bmstrcasestr((char *)ChrPtr(pagetitle), "_HISTORY_")) {    /* no history pages */
290                                 wc_printf("<tr bgcolor=\"%s\">", ((row%2) ? "#FFFFFF" : "#DDDDDD"));
291                                 wc_printf("<td><a href=\"wiki?page=");
292                                 urlescputs(ChrPtr(pagetitle));
293                                 wc_printf("\">");
294                                 escputs(ChrPtr(pagetitle));
295                                 wc_printf("</a></td>");
296                                 wc_printf("</tr>\n");
297                                 ++row;
298                         }
299                 }
300                 wc_printf("</table>\n");
301                 FreeStrBuf(&pagetitle);
302         }
303
304         FreeStrBuf(&Buf);
305 }
306
307
308 /*
309  * Display a list of all pages in a Wiki room.  Search requests in a Wiki room also go here.
310  */
311 void display_wiki_pagelist(void)
312 {
313         output_headers(1, 1, 1, 0, 0, 0);
314         do_template("wiki_pagelist", NULL);
315         wDumpContent(1);
316 }
317
318
319 int wiki_Cleanup(void **ViewSpecific)
320 {
321         char pagename[5];
322         safestrncpy(pagename, "home", sizeof pagename);
323         display_wiki_page_backend(WC->CurRoom.name, pagename, "", 0);
324         wDumpContent(1);
325         return 0;
326 }
327
328
329
330
331
332
333 int ConditionalHaveWikiPage(StrBuf *Target, WCTemplputParams *TP)
334 {
335         const char *page;
336         const char *pch;
337         long len;
338
339         page = BSTR("page");
340         GetTemplateTokenString(Target, TP, 2, &pch, &len);
341         return strcasecmp(page, pch) == 0;
342 }
343 int ConditionalHavewikiType(StrBuf *Target, WCTemplputParams *TP)
344 {
345         wcsession *WCC = WC;
346         const char *pch;
347         long len;
348
349         GetTemplateTokenString(Target, TP, 1, &pch, &len);
350         return bmstrcasestr((char *)ChrPtr(WCC->Hdr->HR.ReqLine), pch) != NULL;
351 }
352 void 
353 InitModule_WIKI
354 (void)
355 {
356         RegisterReadLoopHandlerset(
357                 VIEW_WIKI,
358                 NULL,
359                 NULL,
360                 NULL,
361                 NULL,
362                 NULL,
363                 wiki_Cleanup
364         );
365
366         WebcitAddUrlHandler(HKEY("wiki"), "", 0, display_wiki_page, 0);
367         WebcitAddUrlHandler(HKEY("wiki_history"), "", 0, display_wiki_history, 0);
368         WebcitAddUrlHandler(HKEY("wiki_pagelist"), "", 0, display_wiki_pagelist, 0);
369         RegisterNamespace("WIKI:DISPLAYHISTORY", 0, 0, tmplput_display_wiki_history, NULL, CTX_NONE);
370         RegisterNamespace("WIKI:DISPLAYPAGELIST", 0, 0, tmplput_display_wiki_pagelist, NULL, CTX_NONE);
371         RegisterConditional(HKEY("COND:WIKI:PAGE"), 1, ConditionalHaveWikiPage, CTX_NONE);
372         RegisterConditional(HKEY("COND:WIKI:TYPE"), 1, ConditionalHavewikiType, CTX_NONE);
373 }