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