]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/plugin/RecentChanges.php
Fix error msg: Use of undefined constant HomePage - assumed 'HomePage'.
[SourceForge/phpwiki.git] / lib / plugin / RecentChanges.php
1 <?php // -*-php-*-
2 rcs_id('$Id: RecentChanges.php,v 1.67 2002-10-12 09:47:05 carstenklapp Exp $');
3 /**
4  */
5
6
7 class _RecentChanges_Formatter
8 {
9     var $_absurls = false;
10
11     function _RecentChanges_Formatter ($rc_args) {
12         $this->_args = $rc_args;
13         $this->_diffargs = array('action' => 'diff');
14
15         if ($rc_args['show_minor'] || !$rc_args['show_major'])
16             $this->_diffargs['previous'] = 'minor';
17
18         // PageHistoryPlugin doesn't have a 'daylist' arg.
19         if (!isset($this->_args['daylist']))
20             $this->_args['daylist'] = false;
21     }
22
23     function include_versions_in_URLs() {
24         return (bool) $this->_args['show_all'];
25     }
26
27     function date ($rev) {
28         global $Theme;
29         return $Theme->getDay($rev->get('mtime'));
30     }
31
32     function time ($rev) {
33         global $Theme;
34         return $Theme->formatTime($rev->get('mtime'));
35     }
36
37     function diffURL ($rev) {
38         $args = $this->_diffargs;
39         if ($this->include_versions_in_URLs())
40             $args['version'] = $rev->getVersion();
41         $page = $rev->getPage();
42         return WikiURL($page->getName(), $args, $this->_absurls);
43     }
44
45     function historyURL ($rev) {
46         $page = $rev->getPage();
47         return WikiURL($page, array('action' => _("PageHistory")),
48                        $this->_absurls);
49     }
50
51     function pageURL ($rev) {
52         return WikiURL($this->include_versions_in_URLs() ? $rev : $rev->getPage(),
53                        '', $this->_absurls);
54     }
55
56     function authorHasPage ($author) {
57         global $WikiNameRegexp, $request;
58         $dbi = $request->getDbh();
59         return preg_match("/^$WikiNameRegexp\$/", $author) && $dbi->isWikiPage($author);
60     }
61
62     function authorURL ($author) {
63         return $this->authorHasPage() ? WikiURL($author) : false;
64     }
65
66
67     function status ($rev) {
68         if ($rev->hasDefaultContents())
69             return 'deleted';
70         $page = $rev->getPage();
71         $prev = $page->getRevisionBefore($rev->getVersion());
72         if ($prev->hasDefaultContents())
73             return 'new';
74         return 'updated';
75     }
76
77     function importance ($rev) {
78         return $rev->get('is_minor_edit') ? 'minor' : 'major';
79     }
80
81     function summary($rev) {
82         if ( ($summary = $rev->get('summary')) )
83             return $summary;
84
85         switch ($this->status($rev)) {
86             case 'deleted':
87                 return _("Deleted.");
88             case 'new':
89                 return _("New page.");
90             default:
91                 return '';
92         }
93     }
94
95     function http_lastmodified_header($mtime) {
96         static $mostrecentlymodified = false;
97         if (! $mostrecentlymodified) {
98             // set http header to date of most recently modified page
99             $mostrecentlymodified = $mtime; //$rev->get('mtime');
100             header("Last-Modified: " . Rfc2822DateTime($mostrecentlymodified));
101         }
102     }
103 }
104
105 class _RecentChanges_HtmlFormatter
106 extends _RecentChanges_Formatter
107 {
108     function diffLink ($rev) {
109         global $Theme;
110         return $Theme->makeButton(_("(diff)"), $this->diffURL($rev), 'wiki-rc-action');
111     }
112
113     function historyLink ($rev) {
114         global $Theme;
115         return $Theme->makeButton(_("(hist)"), $this->historyURL($rev), 'wiki-rc-action');
116     }
117
118     function pageLink ($rev) {
119         $page = $rev->getPage();
120         global $Theme;
121         if ($this->include_versions_in_URLs()) {
122             $version = $rev->getVersion();
123             $exists = !$rev->hasDefaultContents();
124         }
125         else {
126             $version = false;
127             $cur = $page->getCurrentRevision();
128             $exists = !$cur->hasDefaultContents();
129         }
130         if ($exists)
131             return $Theme->linkExistingWikiWord($page->getName(), false, $version);
132         else
133             return $Theme->linkUnknownWikiWord($page->getName(), false, $version);
134     }
135
136     function authorLink ($rev) {
137         $author = $rev->get('author');
138         if ( $this->authorHasPage($author) ) {
139             return WikiLink($author);
140         } else
141             return $author;
142     }
143
144     function summaryAsHTML ($rev) {
145         if ( !($summary = $this->summary($rev)) )
146             return '';
147         return  HTML::strong( array('class' => 'wiki-summary'),
148                               "[",
149                               TransformLinks($summary, $rev->get('markup')),
150                               "]");
151     }
152
153     function rss_icon () {
154         global $request, $Theme;
155
156         $rss_url = $request->getURLtoSelf(array('format' => 'rss'));
157         return $Theme->makeButton("RSS", $rss_url, 'rssicon');
158     }
159
160     function description () {
161         extract($this->_args);
162         // FIXME: say something about show_all.
163         if ($show_major && $show_minor)
164             $edits = _("edits");
165         elseif ($show_major)
166             $edits = _("major edits");
167         else
168             $edits = _("minor edits");
169
170         if ($timespan = $days > 0) {
171             if (intval($days) != $days)
172                 $days = sprintf("%.1f", $days);
173         }
174         $lmt = abs($limit);
175         /**
176          * Depending how this text is split up it can be tricky or
177          * impossible to translate with good grammar. So the seperate
178          * strings for 1 day and %s days are necessary in this case
179          * for translating to multiple languages, due to differing
180          * overlapping ideal word cutting points.
181          *
182          * en: day/days "The %d most recent %s [during (the past] day) are listed below."
183          * de: 1 Tag    "Die %d jüngste %s [innerhalb (von des letzten] Tages) sind unten aufgelistet."
184          * de: %s days  "Die %d jüngste %s [innerhalb (von] %s Tagen) sind unten aufgelistet."
185          *
186          * en: day/days "The %d most recent %s during [the past] (day) are listed below."
187          * fr: 1 jour   "Les %d %s les plus récentes pendant [le dernier (d'une] jour) sont énumérées ci-dessous."
188          * fr: %s jours "Les %d %s les plus récentes pendant [les derniers (%s] jours) sont énumérées ci-dessous."
189          */
190         if ($limit > 0) {
191             if ($timespan) {
192                 if (intval($days) == 1)
193                     $desc = fmt("The %d most recent %s during the past day are listed below.",
194                                 $limit, $edits);
195                 else
196                     $desc = fmt("The %d most recent %s during the past %s days are listed below.",
197                                 $limit, $edits, $days);
198             } else
199                 $desc = fmt("The %d most recent %s are listed below.",
200                             $limit, $edits);
201         }
202         elseif ($limit < 0) {  //$limit < 0 means we want oldest pages
203             if ($timespan) {
204                 if (intval($days) == 1)
205                     $desc = fmt("The %d oldest %s during the past day are listed below.",
206                                 $lmt, $edits);
207                 else
208                     $desc = fmt("The %d oldest %s during the past %s days are listed below.",
209                                 $lmt, $edits, $days);
210             } else
211                 $desc = fmt("The %d oldest %s are listed below.",
212                             $lmt, $edits);
213         }
214
215         else {
216             if ($timespan) {
217                 if (intval($days) == 1)
218                     $desc = fmt("The most recent %s during the past day are listed below.",
219                                 $edits);
220                 else
221                     $desc = fmt("The most recent %s during the past %s days are listed below.",
222                                 $edits, $days);
223             } else
224                 $desc = fmt("All %s are listed below.", $edits);
225         }
226         return $desc;
227     }
228
229
230     function title () {
231         extract($this->_args);
232         return array($show_minor ? _("RecentEdits") : _("RecentChanges"),
233                      ' ',
234                      $this->rss_icon(),
235                      $this->sidebar_link());
236     }
237
238     function sidebar_link() {
239         extract($this->_args);
240         $pagetitle = $show_minor ? _("RecentEdits") : _("RecentChanges");
241
242         global $request;
243         $sidebarurl = WikiURL($pagetitle, false, 'absurl') . "?format=sidebar";
244
245         $addsidebarjsfunc =
246             "function addPanel() {\n"
247             ."    window.sidebar.addPanel (\"" . sprintf("%s - %s", WIKI_NAME, $pagetitle) . "\",\n"
248             ."       \"$sidebarurl\",\"\");\n"
249             ."}\n";
250         $jsf = $this->_javascript($addsidebarjsfunc);
251     
252         $addsidebarjsclick = " " . "<small style=\"font-weight:normal;\"><a href=\"javascript:addPanel();\">sidebar</a></small>";
253         $jsc = $this->_javascript("if ((typeof window.sidebar == 'object') &&\n"
254                                 ."    (typeof window.sidebar.addPanel == 'function'))\n"
255                                 ."   {\n"
256                                 ."       document.write('$addsidebarjsclick');\n"
257                                 ."   }\n"
258                                 );
259         return HTML(new RawXML("\n"), $jsf, new RawXML("\n"), $jsc);
260     }
261
262     function _javascript($script) {
263         return HTML::script(array('language' => 'JavaScript',
264                                   'type'     => 'text/javascript'),
265                             new RawXml("<!-- //\n$script\n// -->"));
266     }
267
268     function format ($changes) {
269         $html = HTML(HTML::h2(false, $this->title()));
270         if (($desc = $this->description()))
271             $html->pushContent(HTML::p(false, $desc));
272
273         if ($this->_args['daylist'])
274             $html->pushContent(new DayButtonBar($this->_args));
275
276         $last_date = '';
277         $lines = false;
278
279         while ($rev = $changes->next()) {
280             if (($date = $this->date($rev)) != $last_date) {
281                 if ($lines)
282                     $html->pushContent($lines);
283                 $html->pushContent(HTML::h3($date));
284                 $lines = HTML::ul();
285                 $last_date = $date;
286             }
287             $this->http_lastmodified_header($rev->get('mtime'));
288             $lines->pushContent($this->format_revision($rev));
289         }
290         if ($lines)
291             $html->pushContent($lines);
292         return $html;
293     }
294
295     function format_revision ($rev) {
296         $args = &$this->_args;
297
298         $class = 'rc-' . $this->importance($rev);
299
300         $time = $this->time($rev);
301         if (! $rev->get('is_minor_edit'))
302             $time = HTML::strong(array('class' => 'pageinfo-majoredit'), $time);
303
304         $line = HTML::li(array('class' => $class));
305
306
307         if ($args['difflinks'])
308             $line->pushContent($this->diffLink($rev), ' ');
309
310         if ($args['historylinks'])
311             $line->pushContent($this->historyLink($rev), ' ');
312
313         $line->pushContent($this->pageLink($rev), ' ',
314                            $time, ' ',
315                            $this->summaryAsHTML($rev),
316                            ' ... ',
317                            $this->authorLink($rev));
318         return $line;
319     }
320 }
321
322
323 class _RecentChanges_SideBarFormatter
324 extends _RecentChanges_HtmlFormatter
325 {
326     function description () {
327         //omit description
328     }
329     function rss_icon () {
330         //omit rssicon
331     }
332     function title () {
333         //title click opens the normal RC or RE page in the main browser frame
334         extract($this->_args);
335         $titlelink = WikiLink($show_minor ? _("RecentEdits") : _("RecentChanges"));
336         $titlelink->setAttr('target', '_content');
337         return HTML($this->logo(), $titlelink);
338     }
339     function logo () {
340         //logo click opens the HomePage in the main browser frame
341         global $Theme;
342         $img = HTML::img(array('src' => $Theme->getImageURL('logo'),
343                                'border' => 0,
344                                'align' => 'right',
345                                'width' => 32
346                                ));
347         $linkurl = WikiLink(HOME_PAGE, false, $img);
348         $linkurl->setAttr('target', '_content');
349         return $linkurl;
350     }
351
352     function authorLink ($rev) {
353         $author = $rev->get('author');
354         if ( $this->authorHasPage($author) ) {
355             $linkurl = WikiLink($author);
356             $linkurl->setAttr('target', '_content'); // way to do this using parent::authorLink ??
357             return $linkurl;
358         } else
359             return $author;
360     }
361     function diffLink ($rev) {
362         $linkurl = parent::diffLink($rev);
363         $linkurl->setAttr('target', '_content');
364         return $linkurl;
365     }
366     function historyLink ($rev) {
367         $linkurl = parent::historyLink($rev);
368         $linkurl->setAttr('target', '_content');
369         return $linkurl;
370     }
371     function pageLink ($rev) {
372         $linkurl = parent::pageLink($rev);
373         $linkurl->setAttr('target', '_content');
374         return $linkurl;
375     }
376     // Overriding summaryAsHTML, because there is no way yet to
377     // return summary as transformed text with
378     // links setAttr('target', '_content') in Mozilla sidebar.
379     // So for now don't create clickable links inside summary
380     // in the sidebar, or else they target the sidebar and not the
381     // main content window.
382     function summaryAsHTML ($rev) {
383         if ( !($summary = $this->summary($rev)) )
384             return '';
385         return HTML::strong(array('class' => 'wiki-summary'),
386                                 "[",
387                                 /*TransformLinks(*/$summary,/* $rev->get('markup')),*/
388                                 "]");
389     }
390
391
392     function format ($changes) {
393         $this->_args['daylist'] = false; //only 1 day for Mozilla sidebar
394         $html = _RecentChanges_HtmlFormatter::format ($changes);
395         $html = HTML::div(array('class' => 'wikitext'), $html);
396
397         printf("<?xml version=\"1.0\" encoding=\"%s\"?>\n", CHARSET);
398         printf('<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"');
399         printf('  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">');
400         printf('<html xmlns="http://www.w3.org/1999/xhtml">');
401         
402         printf("<head>\n");
403         extract($this->_args);
404         $title = WIKI_NAME . $show_minor ? _("RecentEdits") : _("RecentChanges");
405         printf("<title>" . $title . "</title>\n");
406         global $Theme;
407         $css = $Theme->getCSS();
408         $css->PrintXML();
409         printf("</head>\n");
410         
411         printf("<body class=\"sidebar\">\n");
412         $html->PrintXML();
413         printf("\n</body>\n");
414         printf("</html>\n");
415
416         flush();
417
418         global $request;
419         $request->finish(); // cut rest of page processing short
420     }
421 }
422
423
424 class _RecentChanges_RssFormatter
425 extends _RecentChanges_Formatter
426 {
427     var $_absurls = true;
428
429     function time ($rev) {
430         return Iso8601DateTime($rev->get('mtime'));
431     }
432
433     function pageURI ($rev) {
434         return WikiURL($rev, '', 'absurl');
435     }
436
437     function format ($changes) {
438         include_once('lib/RssWriter.php');
439         $rss = new RssWriter;
440
441
442         $rss->channel($this->channel_properties());
443
444         if (($props = $this->image_properties()))
445             $rss->image($props);
446         if (($props = $this->textinput_properties()))
447             $rss->textinput($props);
448
449         while ($rev = $changes->next()) {
450             $rss->addItem($this->item_properties($rev),
451                           $this->pageURI($rev));
452             $this->http_lastmodified_header($rev->get('mtime'));
453         }
454
455         $rss->finish();
456         printf("\n<!-- Generated by PhpWiki:\n%s-->\n", $GLOBALS['RCS_IDS']);
457
458         // Flush errors in comment, otherwise it's invalid XML.
459         global $ErrorManager;
460         if (($errors = $ErrorManager->getPostponedErrorsAsHTML()))
461             printf("\n<!-- PHP Warnings:\n%s-->\n", AsXML($errors));
462
463         global $request;        // FIXME
464         $request->finish();     // NORETURN!!!!
465     }
466
467     function image_properties () {
468         global $Theme;
469
470         $img_url = $Theme->getImageURL('logo');
471         if (!$img_url)
472             return false;
473
474         return array('title' => WIKI_NAME,
475                      'link' => WikiURL(HOME_PAGE, false, 'absurl'),
476                      'url' => $img_url);
477     }
478
479     function textinput_properties () {
480         return array('title' => _("Search"),
481                      'description' => _("Title Search"),
482                      'name' => 's',
483                      'link' => WikiURL(_("TitleSearch"), false, 'absurl'));
484     }
485
486     function channel_properties () {
487         global $request;
488
489         $rc_url = WikiURL($request->getArg('pagename'), false, 'absurl');
490
491         return array('title' => WIKI_NAME,
492                      'dc:description' => _("RecentChanges"),
493                      'link' => $rc_url,
494                      'dc:date' => Iso8601DateTime(time()));
495
496         /* FIXME: other things one might like in <channel>:
497          * sy:updateFrequency
498          * sy:updatePeriod
499          * sy:updateBase
500          * dc:subject
501          * dc:publisher
502          * dc:language
503          * dc:rights
504          * rss091:language
505          * rss091:managingEditor
506          * rss091:webmaster
507          * rss091:lastBuildDate
508          * rss091:copyright
509          */
510     }
511
512
513
514
515     function item_properties ($rev) {
516         $page = $rev->getPage();
517         $pagename = $page->getName();
518
519         return array( 'title'           => split_pagename($pagename),
520                       'description'     => $this->summary($rev),
521                       'link'            => $this->pageURL($rev),
522                       'dc:date'         => $this->time($rev),
523                       'dc:contributor'  => $rev->get('author'),
524                       'wiki:version'    => $rev->getVersion(),
525                       'wiki:importance' => $this->importance($rev),
526                       'wiki:status'     => $this->status($rev),
527                       'wiki:diff'       => $this->diffURL($rev),
528                       'wiki:history'    => $this->historyURL($rev)
529                       );
530     }
531 }
532
533 class NonDeletedRevisionIterator extends WikiDB_PageRevisionIterator
534 {
535     /** Constructor
536      *
537      * @param $revisions object a WikiDB_PageRevisionIterator.
538      */
539     function NonDeletedRevisionIterator ($revisions, $check_current_revision = true) {
540         $this->_revisions = $revisions;
541         $this->_check_current_revision = $check_current_revision;
542     }
543
544     function next () {
545         while (($rev = $this->_revisions->next())) {
546             if ($this->_check_current_revision) {
547                 $page = $rev->getPage();
548                 $check_rev = $page->getCurrentRevision();
549             }
550             else {
551                 $check_rev = $rev;
552             }
553             if (! $check_rev->hasDefaultContents())
554                 return $rev;
555         }
556         $this->free();
557         return false;
558     }
559
560     function free () {
561         $this->_revisions->free();
562     }
563 }
564
565 class WikiPlugin_RecentChanges
566 extends WikiPlugin
567 {
568     function getName () {
569         return _("RecentChanges");
570     }
571
572     function getDefaultArguments() {
573         return array('days'             => 2,
574                      'show_minor'       => false,
575                      'show_major'       => true,
576                      'show_all'         => false,
577                      'show_deleted'     => 'sometimes',
578                      'limit'            => false,
579                      'format'           => false,
580                      'daylist'          => false,
581                      'difflinks'        => true,
582                      'historylinks'     => false,
583                      'caption'          => ''
584                      );
585     }
586
587     function getArgs ($argstr, $request, $defaults = false) {
588         $args = WikiPlugin::getArgs($argstr, $request, $defaults);
589
590         $action = $request->getArg('action');
591         if ($action != 'browse' && ! $request->isActionPage($action))
592             $args['format'] = false; // default -> HTML
593
594         if ($args['format'] == 'rss' && empty($args['limit']))
595             $args['limit'] = 15; // Fix default value for RSS.
596
597         if ($args['format'] == 'sidebar' && empty($args['limit']))
598             $args['limit'] = 1; // Fix default value for sidebar.
599
600         return $args;
601     }
602
603     function getMostRecentParams ($args) {
604         extract($args);
605
606         $params = array('include_minor_revisions' => $show_minor,
607                         'exclude_major_revisions' => !$show_major,
608                         'include_all_revisions' => !empty($show_all));
609
610         if ($limit != 0)
611             $params['limit'] = $limit;
612
613         if ($days > 0.0)
614             $params['since'] = time() - 24 * 3600 * $days;
615         elseif ($days < 0.0)
616             $params['since'] = 24 * 3600 * $days - time();
617
618
619         return $params;
620     }
621
622     function getChanges ($dbi, $args) {
623         $changes = $dbi->mostRecent($this->getMostRecentParams($args));
624
625         $show_deleted = $args['show_deleted'];
626         if ($show_deleted == 'sometimes')
627             $show_deleted = $args['show_minor'];
628
629         if (!$show_deleted)
630             $changes = new NonDeletedRevisionIterator($changes, !$args['show_all']);
631
632         return $changes;
633     }
634
635     function format ($changes, $args) {
636         global $Theme;
637         $format = $args['format'];
638
639         $fmt_class = $Theme->getFormatter('RecentChanges', $format);
640         if (!$fmt_class) {
641             if ($format == 'rss')
642                 $fmt_class = '_RecentChanges_RssFormatter';
643             elseif ($format == 'rss091') {
644                 include_once "lib/RSSWriter091.php";
645                 $fmt_class = '_RecentChanges_RssFormatter091';
646             }
647             elseif ($format == 'sidebar')
648                 $fmt_class = '_RecentChanges_SideBarFormatter';
649             else
650                 $fmt_class = '_RecentChanges_HtmlFormatter';
651         }
652
653         $fmt = new $fmt_class($args);
654         return $fmt->format($changes);
655     }
656
657     function run ($dbi, $argstr, $request) {
658         $args = $this->getArgs($argstr, $request);
659
660         // Hack alert: format() is a NORETURN for rss formatters.
661         return $this->format($this->getChanges($dbi, $args), $args);
662     }
663 };
664
665
666 class DayButtonBar extends HtmlElement {
667
668     function DayButtonBar ($plugin_args) {
669         $this->HtmlElement('p', array('class' => 'wiki-rc-action'));
670
671         // Display days selection buttons
672         extract($plugin_args);
673
674         // Custom caption
675         if (! $caption) {
676             if ($show_minor)
677                 $caption = _("Show minor edits for:");
678             elseif ($show_all)
679                 $caption = _("Show all changes for:");
680             else
681                 $caption = _("Show changes for:");
682         }
683
684         $this->pushContent($caption, ' ');
685
686         global $Theme;
687         $sep = $Theme->getButtonSeparator();
688
689         $n = 0;
690         foreach (explode(",", $daylist) as $days) {
691             if ($n++)
692                 $this->pushContent($sep);
693             $this->pushContent($this->_makeDayButton($days));
694         }
695     }
696
697     function _makeDayButton ($days) {
698         global $Theme, $request;
699
700         if ($days == 1)
701             $label = _("1 day");
702         elseif ($days < 1)
703             $label = "..."; //alldays
704         else
705             $label = sprintf(_("%s days"), abs($days));
706
707         $url = $request->getURLtoSelf(array('action' => 'browse', 'days' => $days));
708
709         return $Theme->makeButton($label, $url, 'wiki-rc-action');
710     }
711 }
712
713
714
715 // (c-file-style: "gnu")
716 // Local Variables:
717 // mode: php
718 // tab-width: 8
719 // c-basic-offset: 4
720 // c-hanging-comment-ender-p: nil
721 // indent-tabs-mode: nil
722 // End:
723 ?>