]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/plugin/RecentChanges.php
Changes to RecentChanges plugin. Now takes $limit < 0 to return oldest $limit changes.
[SourceForge/phpwiki.git] / lib / plugin / RecentChanges.php
1 <?php // -*-php-*-
2 rcs_id('$Id: RecentChanges.php,v 1.60 2002-02-09 23:07:01 lakka 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
96 class _RecentChanges_HtmlFormatter
97 extends _RecentChanges_Formatter
98 {
99     function diffLink ($rev) {
100         global $Theme;
101         return $Theme->makeButton(_("(diff)"), $this->diffURL($rev), 'wiki-rc-action');
102     }
103
104     function historyLink ($rev) {
105         global $Theme;
106         return $Theme->makeButton(_("(hist)"), $this->historyURL($rev), 'wiki-rc-action');
107     }
108
109     function pageLink ($rev) {
110         $page = $rev->getPage();
111         global $Theme;
112         if ($this->include_versions_in_URLs()) {
113             $version = $rev->getVersion();
114             $exists = !$rev->hasDefaultContents();
115         }
116         else {
117             $version = false;
118             $cur = $page->getCurrentRevision();
119             $exists = !$cur->hasDefaultContents();
120         }
121         if ($exists)
122             return $Theme->linkExistingWikiWord($page->getName(), false, $version);
123         else
124             return $Theme->linkUnknownWikiWord($page->getName(), false, $version);
125     }
126     
127     function authorLink ($rev) {
128         $author = $rev->get('author');
129         if ( $this->authorHasPage($author) ) {
130             return WikiLink($author);
131         } else
132             return $author;
133     }
134
135     function summaryAsHTML ($rev) {
136         if ( !($summary = $this->summary($rev)) )
137             return '';
138         return  HTML::strong( array('class' => 'wiki-summary'),
139                               "[",
140                               TransformLinks($summary, $rev->get('markup')),
141                               "]");
142     }
143         
144     function rss_icon () {
145         global $request, $Theme;
146
147         $rss_url = $request->getURLtoSelf(array('format' => 'rss'));
148         return $Theme->makeButton("RSS", $rss_url, 'rssicon');
149     }
150     
151     function description () {
152         extract($this->_args);
153         // FIXME: say something about show_all.
154         if ($show_major && $show_minor)
155             $edits = _("edits");
156         elseif ($show_major)
157             $edits = _("major edits");
158         else
159             $edits = _("minor edits");
160
161         if ($timespan = $days != 0) {
162             if (intval($days) != $days)
163                 $days = sprintf("%.1f", $days);
164         }
165                 $lmt = abs($limit);
166         /**
167          * Depending how this text is split up it can be tricky or
168          * impossible to translate with good grammar. So the seperate
169          * strings for 1 day and %s days are necessary in this case
170          * for translating to multiple languages, due to differing
171          * overlapping ideal word cutting points.
172          *
173          * en: day/days "The %d most recent %s [during (the past] day) are listed below."
174          * de: 1 Tag    "Die %d jüngste %s [innerhalb (von des letzten] Tages) sind unten aufgelistet."
175          * de: %s days  "Die %d jüngste %s [innerhalb (von] %s Tagen) sind unten aufgelistet."
176          *
177          * en: day/days "The %d most recent %s during [the past] (day) are listed below."
178          * fr: 1 jour   "Les %d %s les plus récentes pendant [le dernier (d'une] jour) sont énumérées ci-dessous."
179          * fr: %s jours "Les %d %s les plus récentes pendant [les derniers (%s] jours) sont énumérées ci-dessous."
180          */
181         if ($limit > 0) {
182             if ($timespan) {
183                 if (intval($days) == 1)
184                     $desc = fmt("The %d most recent %s during the past day are listed below.",
185                                 $limit, $edits);
186                 elseif (intval($days) == -1)
187                                     $desc = fmt("The %d oldest %s during the past day are listed below.",
188                                 $limit, $edits);
189                                 elseif ($days > 1)
190                     $desc = fmt("The %d most recent %s during the past %s days are listed below.",
191                                 $limit, $edits, $days);
192                                 elseif ($days < -1)
193                     $desc = fmt("The %d oldest %s from the first %s days are listed below.",
194                                 $limit, $edits, -$days);
195
196             } else
197                 $desc = fmt("The %d most recent %s are listed below.",
198                             $limit, $edits);
199         }
200         elseif ($limit < 0) {  //$limit < 0 means we want oldest pages
201                 if ($timespan) {
202                     if (intval($days) == 1)
203                         $desc = fmt("The %d oldest %s during the past day are listed below.",
204                                     $lmt, $edits);
205                     else
206                         $desc = fmt("The %d oldest %s during the past %s days are listed below.",
207                                     $lmt, $edits, $days);
208                 } else
209                     $desc = fmt("The %d oldest %s are listed below.",
210                                 $lmt, $edits);
211                 }
212
213                 else {
214             if ($timespan) {
215                 if (intval($days) == 1)
216                     $desc = fmt("The most recent %s during the past day are listed below.",
217                                 $edits);
218                 elseif (intval($days) == -1)
219                     $desc = fmt("The oldest %s during the past day are listed below.",
220                                 $edits);
221                 elseif (intval($days) < -1)
222                     $desc = fmt("The oldest %s from the first %s days are listed below.",
223                                 $edits, -$days);
224                 else
225                     $desc = fmt("The most recent %s during the past %s days are listed below.",
226                                 $edits, -$days);
227             } else
228                 $desc = fmt("All %s are listed below.", $edits);
229         }
230                         return $desc;
231     }
232
233         
234     function title () {
235         extract($this->_args);
236         return array($show_minor ? _("RecentEdits") : _("RecentChanges"),
237                      ' ',
238                      $this->rss_icon());
239     }
240
241     function format ($changes) {
242         $html = HTML(HTML::h2(false, $this->title()));
243         if (($desc = $this->description()))
244             $html->pushContent(HTML::p(false, $desc));
245
246         if ($this->_args['daylist'])
247             $html->pushContent(new DayButtonBar($this->_args));
248         
249         $last_date = '';
250         $lines = false;
251         
252         while ($rev = $changes->next()) {
253             if (($date = $this->date($rev)) != $last_date) {
254                 if ($lines)
255                     $html->pushContent($lines);
256                 $html->pushContent(HTML::h3($date));
257                 $lines = HTML::ul();
258                 $last_date = $date;
259             }
260             $lines->pushContent($this->format_revision($rev));
261         }
262         if ($lines)
263             $html->pushContent($lines);
264         return $html;
265     }
266
267     function format_revision ($rev) {
268         $args = &$this->_args;
269         
270         $class = 'rc-' . $this->importance($rev);
271
272         $time = $this->time($rev);
273         if (! $rev->get('is_minor_edit'))
274             $time = HTML::strong($time);
275
276         $line = HTML::li(array('class' => $class));
277
278
279         if ($args['difflinks'])
280             $line->pushContent($this->diffLink($rev), ' ');
281
282         if ($args['historylinks'])
283             $line->pushContent($this->historyLink($rev), ' ');
284
285         $line->pushContent($this->pageLink($rev), ' ',
286                            $time, ' ',
287                            $this->summaryAsHTML($rev),
288                            ' ... ',
289                            $this->authorLink($rev));
290         return $line;
291     }
292 }
293
294
295 class _RecentChanges_RssFormatter
296 extends _RecentChanges_Formatter
297 {
298     var $_absurls = true;
299
300     function time ($rev) {
301         return Iso8601DateTime($rev->get('mtime'));
302     }
303
304     function pageURI ($rev) {
305         return WikiURL($rev, '', 'absurl');
306     }
307     
308     function format ($changes) {
309         include_once('lib/RssWriter.php');
310         $rss = new RssWriter;
311
312         
313         $rss->channel($this->channel_properties());
314
315         if (($props = $this->image_properties()))
316             $rss->image($props);
317         if (($props = $this->textinput_properties()))
318             $rss->textinput($props);
319
320         while ($rev = $changes->next()) {
321             $rss->addItem($this->item_properties($rev),
322                           $this->pageURI($rev));
323         }
324
325         $rss->finish();
326         printf("\n<!-- Generated by PhpWiki:\n%s-->\n", $GLOBALS['RCS_IDS']);
327
328         // Flush errors in comment, otherwise it's invalid XML.
329         global $ErrorManager;
330         if (($errors = $ErrorManager->getPostponedErrorsAsHTML()))
331             printf("\n<!-- PHP Warnings:\n%s-->\n", AsXML($errors));
332             
333         global $request;        // FIXME
334         $request->finish();     // NORETURN!!!!
335     }
336     
337     function image_properties () {
338         global $Theme;
339
340         $img_url = $Theme->getImageURL('logo');
341         if (!$img_url)
342             return false;
343         
344         return array('title' => WIKI_NAME,
345                      'link' => WikiURL(HomePage, false, 'absurl'),
346                      'url' => $img_url);
347     }
348
349     function textinput_properties () {
350         return array('title' => _("Search"),
351                      'description' => _("Title Search"),
352                      'name' => 's',
353                      'link' => WikiURL(_("TitleSearch"), false, 'absurl'));
354     }
355     
356     function channel_properties () {
357         global $request;
358
359         $rc_url = WikiURL($request->getArg('pagename'), false, 'absurl');
360
361         return array('title' => WIKI_NAME,
362                      'dc:description' => _("RecentChanges"),
363                      'link' => $rc_url,
364                      'dc:date' => Iso8601DateTime(time()));
365
366         /* FIXME: other things one might like in <channel>:                   
367          * sy:updateFrequency
368          * sy:updatePeriod
369          * sy:updateBase
370          * dc:subject
371          * dc:publisher
372          * dc:language
373          * dc:rights
374          * rss091:language
375          * rss091:managingEditor
376          * rss091:webmaster
377          * rss091:lastBuildDate
378          * rss091:copyright
379          */
380     }
381     
382
383     
384         
385     function item_properties ($rev) {
386         $page = $rev->getPage();
387         $pagename = $page->getName();
388         
389         return array( 'title'           => split_pagename($pagename),
390                       'description'     => $this->summary($rev),
391                       'link'            => $this->pageURL($rev),
392                       'dc:date'         => $this->time($rev),
393                       'dc:contributor'  => $rev->get('author'),
394                       'wiki:version'    => $rev->getVersion(),
395                       'wiki:importance' => $this->importance($rev),
396                       'wiki:status'     => $this->status($rev),
397                       'wiki:diff'       => $this->diffURL($rev),
398                       'wiki:history'    => $this->historyURL($rev)
399                       );
400     }
401 }
402
403 class NonDeletedRevisionIterator extends WikiDB_PageRevisionIterator
404 {
405     /** Constructor
406      *
407      * @param $revisions object a WikiDB_PageRevisionIterator.
408      */
409     function NonDeletedRevisionIterator ($revisions, $check_current_revision = true) {
410         $this->_revisions = $revisions;
411         $this->_check_current_revision = $check_current_revision;
412     }
413
414     function next () {
415         while (($rev = $this->_revisions->next())) {
416             if ($this->_check_current_revision) {
417                 $page = $rev->getPage();
418                 $check_rev = $page->getCurrentRevision();
419             }
420             else {
421                 $check_rev = $rev;
422             }
423             if (! $check_rev->hasDefaultContents())
424                 return $rev;
425         }
426         $this->free();
427         return false;
428     }
429
430     function free () {
431         $this->_revisions->free();
432     }
433 }
434
435 class WikiPlugin_RecentChanges
436 extends WikiPlugin
437 {
438     function getName () {
439         return _("RecentChanges");
440     }
441
442     function getDefaultArguments() {
443         return array('days'             => 2,
444                      'show_minor'       => false,
445                      'show_major'       => true,
446                      'show_all'         => false,
447                      'show_deleted'     => 'sometimes',
448                      'limit'            => false,
449                      'format'           => false,
450                      'daylist'          => false,
451                      'difflinks'        => true,
452                      'historylinks'     => false,
453                      'caption'          => ''
454                      );
455     }
456
457     function getArgs ($argstr, $request, $defaults = false) {
458         $args = WikiPlugin::getArgs($argstr, $request, $defaults);
459
460         $action = $request->getArg('action');
461         if ($action != 'browse' && ! $request->isActionPage($action))
462             $args['format'] = false; // default -> HTML
463         
464         if ($args['format'] == 'rss' && empty($args['limit']))
465             $args['limit'] = 15; // Fix default value for RSS.
466
467         return $args;
468     }
469         
470     function getMostRecentParams ($args) {
471         extract($args);
472
473         $params = array('include_minor_revisions' => $show_minor,
474                         'exclude_major_revisions' => !$show_major,
475                         'include_all_revisions' => !empty($show_all));
476
477         if ($limit != 0)
478             $params['limit'] = $limit;
479
480         if ($days > 0.0)
481             $params['since'] = time() - 24 * 3600 * $days;
482         elseif ($days < 0.0)
483             $params['since'] = 24 * 3600 * $days - time();
484                                 
485
486         return $params;
487     }
488     
489     function getChanges ($dbi, $args) {
490         $changes = $dbi->mostRecent($this->getMostRecentParams($args));
491
492         $show_deleted = $args['show_deleted'];
493         if ($show_deleted == 'sometimes')
494             $show_deleted = $args['show_minor'];
495
496         if (!$show_deleted)
497             $changes = new NonDeletedRevisionIterator($changes, !$args['show_all']);
498
499         return $changes;
500     }
501
502     function format ($changes, $args) {
503         global $Theme;
504         $format = $args['format'];
505         
506         $fmt_class = $Theme->getFormatter('RecentChanges', $format);
507         if (!$fmt_class) {
508             if ($format == 'rss')
509                 $fmt_class = '_RecentChanges_RssFormatter';
510             elseif ($format == 'rss091') {
511                 include_once "lib/RSSWriter091.php";
512                 $fmt_class = '_RecentChanges_RssFormatter091';
513             }
514             else
515                 $fmt_class = '_RecentChanges_HtmlFormatter';
516         }
517         
518         $fmt = new $fmt_class($args);
519         return $fmt->format($changes);
520     }
521
522     function run ($dbi, $argstr, $request) {
523         $args = $this->getArgs($argstr, $request);
524
525         // Hack alert: format() is a NORETURN for rss formatters.
526         return $this->format($this->getChanges($dbi, $args), $args);
527     }
528 };
529
530
531 class DayButtonBar extends HtmlElement {
532
533     function DayButtonBar ($plugin_args) {
534         $this->HtmlElement('p', array('class' => 'wiki-rc-action'));
535         
536         // Display days selection buttons
537         extract($plugin_args);
538         
539         // Custom caption
540         if (! $caption) {
541             if ($show_minor)
542                 $caption = _("Show minor edits for:");
543             elseif ($show_all)
544                 $caption = _("Show all changes for:");
545             else
546                 $caption = _("Show changes for:");
547         }
548
549         $this->pushContent($caption, ' ');
550
551         global $Theme;
552         $sep = $Theme->getButtonSeparator();
553         
554         $n = 0;
555         foreach (explode(",", $daylist) as $days) {
556             if ($n++)
557                 $this->pushContent($sep);
558             $this->pushContent($this->_makeDayButton($days));
559         }
560     }
561
562     function _makeDayButton ($days) {
563         global $Theme, $request;
564         
565         if (abs($days) == 1)
566             $label = _("1 day");
567         elseif ($days <= 0)
568             $label = "..."; //alldays
569         else
570             $label = sprintf(_("%s days"), abs($days));
571
572         $url = $request->getURLtoSelf(array('action' => 'browse', 'days' => $days));
573
574         return $Theme->makeButton($label, $url, 'wiki-rc-action');
575     }
576 }
577
578
579
580 // (c-file-style: "gnu")
581 // Local Variables:
582 // mode: php
583 // tab-width: 8
584 // c-basic-offset: 4
585 // c-hanging-comment-ender-p: nil
586 // indent-tabs-mode: nil
587 // End:   
588 ?>