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