]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/plugin/WikiBlog.php
SUBPAGE_SEPARATOR *must* be "/"
[SourceForge/phpwiki.git] / lib / plugin / WikiBlog.php
1 <?php
2
3 /*
4  * Copyright 2002,2003,2007,2009 $ThePhpWikiProgrammingTeam
5  *
6  * This file is part of PhpWiki.
7  *
8  * PhpWiki is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * PhpWiki is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License along
19  * with PhpWiki; if not, write to the Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21  */
22 /**
23  * @author: MichaelVanDam, major refactor by JeffDairiki (as AddComment)
24  * @author: Changed as baseclass to AddComment and WikiForum and EditToolbar integration by ReiniUrban.
25  */
26
27 require_once 'lib/TextSearchQuery.php';
28
29 /**
30  * This plugin shows 'blogs' (comments/news) associated with a
31  * particular page and provides an input form for adding a new blog.
32  *
33  * TODO:
34  *
35  * It also works as an action-page if you create a page called 'WikiBlog'
36  * containing this plugin.  This allows adding comments to any page
37  * by linking "PageName?action=WikiBlog".  Maybe a nice feature in
38  * lib/display.php would be to automatically check if there are
39  * blogs for the given page, then provide a link to them somewhere on
40  * the page.  Or maybe this just creates a huge mess...
41  *
42  * Maybe it would be a good idea to ENABLE blogging of only certain
43  * pages by setting metadata or something...?  If a page is non-bloggable
44  * the plugin is ignored (perhaps with a warning message).
45  *
46  * Should blogs be by default filtered out of RecentChanges et al???
47  *
48  * Think of better name for this module: Blog? WikiLog? WebLog? WikiDot?
49  *
50  * Have other 'styles' for the plugin?... e.g. 'quiet'.  Display only
51  * 'This page has 23 associated comments. Click here to view / add.'
52  *
53  * For admin user, put checkboxes beside comments to allow for bulk removal.
54  *
55  * Permissions for who can add blogs?  Display entry box only if
56  * user meets these requirements...?
57  *
58  * Code cleanup: break into functions, use templates (or at least remove CSS)
59  */
60
61 class WikiPlugin_WikiBlog
62     extends WikiPlugin
63 {
64     function getDescription()
65     {
66         return sprintf(_("Show and add blogs for %s."), '[pagename]');
67     }
68
69     // Arguments:
70     // TODO:
71     //
72     // - arguments to allow selection of time range to display
73     // - arguments to display only XX blogs per page (can this 'paging'
74     //    co-exist with the wiki??  difficult)
75     // - arguments to allow comments outside this range to be
76     //    display as e.g. June 2002 archive, July 2002 archive, etc..
77     // - captions for 'show' and 'add' sections
78
79     function getDefaultArguments()
80     {
81         return array('pagename' => '[pagename]',
82             'order' => 'normal',
83             'mode' => 'show,add',
84             'noheader' => false
85         );
86     }
87
88     /**
89      * @param WikiDB $dbi
90      * @param string $argstr
91      * @param WikiRequest $request
92      * @param string $basepage
93      * @return mixed
94      */
95     function run($dbi, $argstr, &$request, $basepage)
96     {
97         $args = $this->getArgs($argstr, $request);
98         // allow empty pagenames for ADMIN_USER style blogs: "Blog/day"
99         //if (!$args['pagename'])
100         //    return $this->error(_("No pagename specified"));
101
102         // Get our form args.
103         $blog = $request->getArg("edit");
104         $request->setArg("edit", false);
105
106         if ($request->isPost() and !empty($blog['save'])) {
107             $this->add($request, $blog, 'wikiblog', $basepage); // noreturn
108         }
109         //TODO: preview
110
111         // Now we display previous comments and/or provide entry box
112         // for new comments
113         $html = HTML();
114         foreach (explode(',', $args['mode']) as $show) {
115             if (!empty($seen[$show]))
116                 continue;
117             $seen[$show] = 1;
118
119             switch ($show) {
120                 case 'show':
121                     $html->pushContent($this->showAll($request, $args));
122                     break;
123                 case 'add':
124                     $html->pushContent($this->showForm($request, $args));
125                     break;
126                 default:
127                     return $this->error(sprintf("Bad mode (ā€œ%sā€)", $show));
128             }
129         }
130         return $html;
131     }
132
133     /**
134      * posted: required: pagename, content. optional: summary
135      *
136      * @param WikiRequest $request
137      * @param array $posted
138      * @param string $type
139      */
140     function add(&$request, $posted, $type = 'wikiblog')
141     {
142         // This is similar to editpage. Shouldn't we use just this for preview?
143         $parent = $posted['pagename'];
144         if (empty($parent)) {
145             $prefix = ""; // allow empty parent for default "Blog/day"
146             $parent = HOME_PAGE;
147         } elseif (($parent == 'Blog' or $parent == 'WikiBlog') and $type == 'wikiblog') { // avoid Blog/Blog/2003-01-11/14:03:02+00:00
148             $prefix = "";
149             $parent = ''; // 'Blog';
150         } elseif ($parent == 'Comment' and $type == "comment") {
151             $prefix = "";
152             $parent = ''; // 'Comment';
153         } elseif ($parent == 'Forum' and $type == "wikiforum") {
154             $prefix = "";
155             $parent = ''; // 'Forum';
156         } else {
157             $prefix = $parent . '/';
158         }
159         //$request->finish(fmt("No pagename specified for %s",$type));
160
161         $now = time();
162         $dbi = $request->getDbh();
163         $user = $request->getUser();
164
165         /*
166          * Page^H^H^H^H Blog meta-data
167          * This method is reused for all attachable pagetypes: wikiblog, comment and wikiforum
168          *
169          * This is info that won't change for each revision.
170          * Nevertheless, it's now stored in the revision meta-data.
171          * Several reasons:
172          *  o It's more convenient to have all information required
173          *    to render a page revision in the revision meta-data.
174          *  o We can avoid a race condition, since version meta-data
175          *    updates are atomic with the version creation.
176          */
177
178         $blog_meta = array('ctime' => $now,
179             'creator' => $user->getId(),
180             'creator_id' => $user->getAuthenticatedId(),
181         );
182
183         // Version meta-data
184         $summary = trim($posted['summary']);
185         // edit: private only
186         $perm = new PagePermission();
187         $perm->perm['edit'] = $perm->perm['remove'];
188         $version_meta = array('author' => $blog_meta['creator'],
189             'author_id' => $blog_meta['creator_id'],
190             'summary' => $summary ? $summary : _("New comment."),
191             'mtime' => $now,
192             'pagetype' => $type,
193             $type => $blog_meta,
194             'perm' => $perm->perm,
195         );
196         if ($type == 'comment')
197             unset($version_meta['summary']);
198
199         // Comment body.
200         $body = trim($posted['content']);
201
202         $saved = false;
203         if ($type != 'wikiforum')
204             $pagename = $this->blogPrefix($type);
205         else {
206             $pagename = substr($summary, 0, 12);
207             if (empty($pagename)) {
208                 $saved = true;
209                 trigger_error("Empty title", E_USER_WARNING);
210             }
211         }
212         while (!$saved) {
213             // Generate the page name.  For now, we use the format:
214             //   Rootname/Blog/2003-01-11/14:03:02+00:00
215             // Rootname = $prefix, Blog = $pagename,
216             // This gives us natural chronological order when sorted
217             // alphabetically. "Rootname/" is optional.
218             // Esp. if Rootname is named Blog, it is omitted.
219
220             $time = Iso8601DateTime();
221             // Check intermediate pages. If not existing they should RedirectTo the parent page.
222             // Maybe add the BlogArchives plugin instead for the new interim subpage.
223             $redirected = $prefix . $pagename;
224             if (!$dbi->isWikiPage($redirected)) {
225                 if (!$parent) $parent = HOME_PAGE;
226                 require_once 'lib/loadsave.php';
227                 $pageinfo = array('pagename' => $redirected,
228                     'content' => '<<RedirectTo page="' . $parent . '">>',
229                     'pagedata' => array(),
230                     'versiondata' => array('author' => $blog_meta['creator'], 'is_minor_edit' => 1),
231                 );
232                 SavePage($request, $pageinfo, '', '');
233             }
234             $redirected = $prefix . $pagename . '/' . preg_replace("/T.*/", "", "$time");
235             if (!$dbi->isWikiPage($redirected)) {
236                 if (!$parent) $parent = HOME_PAGE;
237                 require_once 'lib/loadsave.php';
238                 $pageinfo = array('pagename' => $redirected,
239                     'content' => '<<RedirectTo page="' . $parent . '">>',
240                     'pagedata' => array(),
241                     'versiondata' => array('author' => $blog_meta['creator'], 'is_minor_edit' => 1),
242                 );
243                 SavePage($request, $pageinfo, '', '');
244             }
245
246             $p = $dbi->getPage($prefix . $pagename . '/'
247                 . str_replace("T", '/', "$time"));
248             $pr = $p->getCurrentRevision();
249
250             // Version should be zero.  If not, page already exists
251             // so increment timestamp and try again.
252             if ($pr->getVersion() > 0) {
253                 $now++;
254                 continue;
255             }
256
257             // FIXME: there's a slight, but currently unimportant
258             // race condition here.  If someone else happens to
259             // have just created a blog with the same name,
260             // we'll have locked it before we discover that the name
261             // is taken.
262             $saved = $p->save($body, 1, $version_meta);
263
264             $now++;
265         }
266
267         $dbi->touch();
268         $request->setArg("mode", "show");
269         $request->redirect($request->getURLtoSelf()); // noreturn
270
271         // FIXME: when submit a comment from preview mode,
272         // adds the comment properly but jumps to browse mode.
273         // Any way to jump back to preview mode???
274     }
275
276     function showAll(&$request, $args, $type = "wikiblog")
277     {
278         // FIXME: currently blogSearch uses WikiDB->titleSearch to
279         // get results, so results are in alphabetical order.
280         // When PageTypes fully implemented, could have smarter
281         // blogSearch implementation / naming scheme.
282
283         $dbi = $request->getDbh();
284         $basepage = $args['pagename'];
285         $blogs = $this->findBlogs($dbi, $basepage, $type);
286         $html = HTML();
287         if ($blogs) {
288             // First reorder
289             usort($blogs, array("WikiPlugin_WikiBlog",
290                 "cmp"));
291             if ($args['order'] == 'reverse')
292                 $blogs = array_reverse($blogs);
293
294             $name = $this->blogPrefix($type);
295             if (!$args['noheader'])
296                 $html->pushContent(HTML::h4(array('class' => "$type-heading"),
297                     fmt("%s on %s:", $name, WikiLink($basepage))));
298             foreach ($blogs as $rev) {
299                 if (!$rev->get($type)) {
300                     // Ack! this is an old-style blog with data ctime in page meta-data.
301                     $content = $this->transformOldFormatBlog($rev, $type);
302                 } else {
303                     $content = $rev->getTransformedContent($type);
304                 }
305                 $html->pushContent($content);
306             }
307
308         }
309         return $html;
310     }
311
312     // Subpage for the basepage. All Blogs/Forum/Comment entries are
313     // Subpages under this pagename, to find them faster.
314     protected function blogPrefix($type = 'wikiblog')
315     {
316         if ($type == 'wikiblog')
317             $basepage = "Blog";
318         elseif ($type == 'comment')
319             $basepage = "Comment";
320         elseif ($type == 'wikiforum')
321             // $basepage = substr($summary, 0, 12);
322             $basepage = _("Message"); // FIXME: we use now the first 12 chars of the summary
323         return $basepage;
324     }
325
326     private function transformOldFormatBlog($rev, $type = 'wikiblog')
327     {
328         $page = $rev->getPage();
329         $metadata = array();
330         foreach (array('ctime', 'creator', 'creator_id') as $key)
331             $metadata[$key] = $page->get($key);
332         if (empty($metadata) and $type != 'wikiblog')
333             $metadata[$key] = $page->get('wikiblog');
334         $meta = $rev->getMetaData();
335         $meta[$type] = $metadata;
336         return new TransformedText($page, $rev->getPackedContent(), $meta, $type);
337     }
338
339     function findBlogs(&$dbi, $basepage = '', $type = 'wikiblog')
340     {
341         $prefix = (empty($basepage)
342             ? ""
343             : $basepage . '/') . $this->blogPrefix($type);
344         $pages = $dbi->titleSearch(new TextSearchQuery('"' . $prefix . '"', true, 'none'));
345
346         $blogs = array();
347         while ($page = $pages->next()) {
348             if (!string_starts_with($page->getName(), $prefix))
349                 continue;
350             $current = $page->getCurrentRevision();
351             if ($current->get('pagetype') == $type) {
352                 $blogs[] = $current;
353             }
354         }
355         return $blogs;
356     }
357
358     function cmp($a, $b)
359     {
360         return (strcmp($a->get('mtime'),
361             $b->get('mtime')));
362     }
363
364     function showForm(&$request, $args, $template = 'blogform')
365     {
366         // Show blog-entry form.
367         $args = array('PAGENAME' => $args['pagename'],
368             'HIDDEN_INPUTS' =>
369             HiddenInputs($request->getArgs()));
370         if (ENABLE_EDIT_TOOLBAR and !ENABLE_WYSIWYG and ($template != 'addcomment')) {
371             include_once 'lib/EditToolbar.php';
372             $toolbar = new EditToolbar();
373             $args = array_merge($args, $toolbar->getTokens());
374         }
375         return new Template($template, $request, $args);
376     }
377
378     // "2004-12" => "December 2004"
379     protected function monthTitle($month)
380     {
381         if (!$month) $month = strftime("%Y-%m");
382         //list($year,$mon) = explode("-",$month);
383         return strftime("%B %Y", strtotime($month . "-01"));
384     }
385
386     // "UserName/Blog/2004-12-13/12:28:50+01:00" => array('month' => "2004-12", ...)
387     protected function _blog($rev_or_page)
388     {
389         $pagename = $rev_or_page->getName();
390         if (preg_match("/^(.*Blog)\/(\d\d\d\d-\d\d)-(\d\d)\/(.*)/", $pagename, $m))
391             list(, $prefix, $month, $day, $time) = $m;
392         return array('pagename' => $pagename,
393             // page (list pages per month) or revision (list months)?
394             //'title' => is_a($rev_or_page,'WikiDB_PageRevision') ? $rev_or_page->get('summary') : '',
395             //'monthtitle' => $this->monthTitle($month),
396             'month' => $month,
397             'day' => $day,
398             'time' => $time,
399             'prefix' => $prefix);
400     }
401
402     protected function nonDefaultArgs($args)
403     {
404         return array_diff_assoc($args, $this->getDefaultArguments());
405     }
406
407 }
408
409 // Local Variables:
410 // mode: php
411 // tab-width: 8
412 // c-basic-offset: 4
413 // c-hanging-comment-ender-p: nil
414 // indent-tabs-mode: nil
415 // End: