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