xref: /dokuwiki/inc/Ui/Recent.php (revision 8128de6c978d6ff06285558dd83d15dbe297e888)
1<?php
2
3namespace dokuwiki\Ui;
4
5use dokuwiki\ChangeLog\MediaChangeLog;
6use dokuwiki\Form\Form;
7
8/**
9 * DokuWiki Recent Interface
10 *
11 * @package dokuwiki\Ui
12 */
13class Recent extends Ui
14{
15    protected $first;
16    protected $show_changes;
17
18    /**
19     * Recent Ui constructor
20     *
21     * @param int $first  skip the first n changelog lines
22     * @param string $show_changes  type of changes to show; pages, mediafiles, or both
23     */
24    public function __construct($first = 0, $show_changes = 'both')
25    {
26        $this->first        = $first;
27        $this->show_changes = $show_changes;
28    }
29
30    /**
31     * Display recent changes
32     *
33     * @author Andreas Gohr <andi@splitbrain.org>
34     * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
35     * @author Ben Coburn <btcoburn@silicodon.net>
36     * @author Kate Arzamastseva <pshns@ukr.net>
37     * @author Satoshi Sahara <sahara.satoshi@gmail.com>
38     *
39     * @triggers HTMLFORM_RECENT_OUTPUT
40     * @return void
41     */
42    public function show()
43    {
44        global $conf, $lang;
45        global $ID;
46
47        // get recent items, and set correct pagenation parameters (first, hasNext)
48        $first = $this->first;
49        $hasNext = false;
50        $recents = $this->getRecents($first, $hasNext);
51
52        // print intro
53        print p_locale_xhtml('recent');
54
55        if (getNS($ID) != '') {
56            print '<div class="level1"><p>'
57                . sprintf($lang['recent_global'], getNS($ID), wl('', 'do=recent'))
58                .'</p></div>';
59        }
60
61        // create the form
62        $form = new Form(['id'=>'dw__recent', 'method'=>'GET', 'action'=> wl($ID), 'class'=>'changes']);
63        $form->addTagOpen('div')->addClass('no');
64        $form->setHiddenField('sectok', null);
65        $form->setHiddenField('do', 'recent');
66        $form->setHiddenField('id', $ID);
67
68        // show dropdown selector, whether include not only recent pages but also recent media files?
69        if ($conf['mediarevisions']) {
70            $this->addRecentItemSelector($form);
71        }
72
73        // start listing of recent items
74        $form->addTagOpen('ul');
75        foreach ($recents as $recent) {
76            $objRevInfo = $this->getObjRevInfo($recent);
77            $class = ($recent['type'] === DOKU_CHANGE_TYPE_MINOR_EDIT) ? 'minor': '';
78            $form->addTagOpen('li')->addClass($class);
79            $form->addTagOpen('div')->addClass('li');
80            $html = implode(' ', [
81                $objRevInfo->itemIcon(),          // filetype icon
82                $objRevInfo->editDate(),          // edit date and time
83                $objRevInfo->difflink(),          // link to diffview icon
84                $objRevInfo->revisionlink(),      // linkto revisions icon
85                $objRevInfo->itemName(),          // name of page or media
86                $objRevInfo->editSummary(),       // edit summary
87                $objRevInfo->editor(),            // editor info
88                $objRevInfo->sizechange(),        // size change indicator
89            ]);
90            $form->addHTML($html);
91            $form->addTagClose('div');
92            $form->addTagClose('li');
93        }
94        $form->addTagClose('ul');
95
96        $form->addTagClose('div'); // close div class=no
97
98        // provide navigation for pagenated recent list (of pages and/or media files)
99        $form->addHTML($this->htmlNavigation($first, $hasNext));
100
101        // print form that might be modified by HTMLFORM_RECENT_OUTPUT event handlers
102        print $form->toHTML('recent');
103    }
104
105    /**
106     * Get recent items, and set correct pagenation parameters (first, hasNext)
107     *
108     * @param int  $first
109     * @param bool $hasNext
110     * @return array  recent items to be shown in a pagenated list
111     *
112     * @see also dokuwiki\Changelog::getRevisionInfo()
113     */
114    protected function getRecents(&$first, &$hasNext)
115    {
116        global $ID, $conf;
117
118        $flags = 0;
119        if ($this->show_changes == 'mediafiles' && $conf['mediarevisions']) {
120            $flags = RECENTS_MEDIA_CHANGES;
121        } elseif ($this->show_changes == 'pages') {
122            $flags = 0;
123        } elseif ($conf['mediarevisions']) {
124            $flags = RECENTS_MEDIA_PAGES_MIXED;
125        }
126
127        /* we need to get one additionally log entry to be able to
128         * decide if this is the last page or is there another one.
129         * This is the cheapest solution to get this information.
130         */
131        $recents = getRecents($first, $conf['recent'] + 1, getNS($ID), $flags);
132        if (count($recents) == 0 && $first != 0) {
133            $first = 0;
134            $recents = getRecents($first, $conf['recent'] + 1, getNS($ID), $flags);
135        }
136
137        $hasNext = false;
138        if (count($recents) > $conf['recent']) {
139            $hasNext = true;
140            array_pop($recents); // remove extra log entry
141        }
142        return $recents;
143    }
144
145    /**
146     * Navigation buttons for Pagenation (prev/next)
147     *
148     * @param int  $first
149     * @param bool $hasNext
150     * @return array  html
151     */
152    protected function htmlNavigation($first, $hasNext)
153    {
154        global $conf, $lang;
155
156        $last = $first + $conf['recent'];
157        $html = '<div class="pagenav">';
158        if ($first > 0) {
159            $first = max($first - $conf['recent'], 0);
160            $html.= '<div class="pagenav-prev">';
161            $html.= '<button type="submit" name="first['.$first.']" accesskey="n"'
162                  . ' title="'.$lang['btn_newer'].' [N]" class="button show">'
163                  . $lang['btn_newer']
164                  . '</button>';
165            $html.= '</div>';
166        }
167        if ($hasNext) {
168            $html.= '<div class="pagenav-next">';
169            $html.= '<button type="submit" name="first['.$last.']" accesskey="p"'
170                  . ' title="'.$lang['btn_older'].' [P]" class="button show">'
171                  . $lang['btn_older']
172                  . '</button>';
173            $html.= '</div>';
174        }
175        $html.= '</div>';
176        return $html;
177    }
178
179    /**
180     * Add dropdown selector of item types to the form instance
181     *
182     * @param Form $form
183     * @return void
184     */
185    protected function addRecentItemSelector(Form $form)
186    {
187        global $lang;
188
189        $form->addTagOpen('div')->addClass('changeType');
190        $options = array(
191                    'pages'      => $lang['pages_changes'],
192                    'mediafiles' => $lang['media_changes'],
193                    'both'       => $lang['both_changes'],
194        );
195        $form->addDropdown('show_changes', $options, $lang['changes_type'])
196                ->val($this->show_changes)->addClass('quickselect');
197        $form->addButton('do[recent]', $lang['btn_apply'])->attr('type','submit');
198        $form->addTagClose('div');
199    }
200
201    /**
202     * Returns instance of objRevInfo
203     * @param array $info  Revision info structure of page or media
204     * @return objRevInfo object (anonymous class)
205     */
206    protected function getObjRevInfo(array $info)
207    {
208        return new class ($info) // anonymous class (objRevInfo)
209        {
210            protected $info;
211
212            public function __construct(array $info)
213            {
214                $this->info = $info;
215            }
216
217            // fileicon of the page or media file
218            public function itemIcon()
219            {
220                $id = $this->info['id'];
221                if (isset($this->info['media'])) {
222                    $html = media_printicon($id);
223                } else {
224                    $html = '<img class="icon" src="'.DOKU_BASE.'lib/images/fileicons/file.png" alt="'.$id.'" />';
225                }
226                return $html;
227            }
228
229            // edit date and time of the page or media file
230            public function editDate()
231            {
232                return '<span class="date">'. dformat($this->info['date']) .'</span>';
233            }
234
235            // edit summary
236            public function editSummary()
237            {
238                return '<span class="sum">'.' – '. hsc($this->info['sum']).'</span>';
239            }
240
241            // editor of the page or media file
242            public function editor()
243            {
244                $html = '<span class="user">';
245                if ($this->info['user']) {
246                    $html.= '<bdi>'. editorinfo($this->info['user']) .'</bdi>';
247                    if (auth_ismanager()) $html.= ' <bdo dir="ltr">('. $this->info['ip'] .')</bdo>';
248                } else {
249                    $html.= '<bdo dir="ltr">'. $this->info['ip'] .'</bdo>';
250                }
251                $html.= '</span>';
252                return $html;
253            }
254
255            // name of the page or media file
256            public function itemName()
257            {
258                $id = $this->info['id'];
259                if (isset($this->info['media'])) {
260                    $href = media_managerURL(['tab_details'=>'view', 'image'=> $id, 'ns'=> getNS($id)], '&');
261                    $class = file_exists(mediaFN($id)) ? 'wikilink1' : 'wikilink2';
262                    $html = '<a href="'.$href.'" class="'.$class.'">'.$id.'</a>';
263                } else {
264                    $html = html_wikilink(':'.$id, (useHeading('navigation') ? null : $id));
265                }
266                return $html;
267            }
268
269            // icon difflink
270            public function difflink()
271            {
272                global $lang;
273                $id = $this->info['id'];
274
275                if (isset($this->info['media'])) {
276                    $revs = (new MediaChangeLog($id))->getRevisions(0, 1);
277                    $diff = (count($revs) && file_exists(mediaFN($id)));
278                    if ($diff) {
279                        $href = media_managerURL(
280                            ['tab_details'=>'history', 'mediado'=>'diff', 'image'=> $id, 'ns'=> getNS($id)], '&'
281                        );
282                    } else {
283                        $href = '';
284                    }
285                } else {
286                    $href = wl($id, "do=diff", false, '&');
287                }
288
289                if ($href) {
290                    $html = '<a href="'.$href.'" class="diff_link">'
291                          . '<img src="'.DOKU_BASE.'lib/images/diff.png" width="15" height="11"'
292                          . ' title="'.$lang['diff'].'" alt="'.$lang['diff'].'" />'
293                          . '</a>';
294                } else {
295                    $html = '<img src="'.DOKU_BASE.'lib/images/blank.gif" width="15" height="11" alt="" />';
296                }
297                return $html;
298            }
299
300            // icon revision link
301            public function revisionlink()
302            {
303                global $lang;
304                $id = $this->info['id'];
305                if (isset($this->info['media'])) {
306                    $href = media_managerURL(['tab_details'=>'history', 'image'=> $id, 'ns'=> getNS($id)], '&');
307                } else {
308                    $href = wl($id, "do=revisions", false, '&');
309                }
310                $html = '<a href="'.$href.'" class="revisions_link">'
311                      . '<img src="'.DOKU_BASE.'lib/images/history.png" width="12" height="14"'
312                      . ' title="'.$lang['btn_revs'].'" alt="'.$lang['btn_revs'].'" />'
313                      . '</a>';
314                return $html;
315            }
316
317            // size change
318            public function sizeChange()
319            {
320                $class = 'sizechange';
321                $value = filesize_h(abs($this->info['sizechange']));
322                if ($this->info['sizechange'] > 0) {
323                    $class .= ' positive';
324                    $value = '+' . $value;
325                } elseif ($this->info['sizechange'] < 0) {
326                    $class .= ' negative';
327                    $value = '-' . $value;
328                } else {
329                    $value = '±' . $value;
330                }
331                return '<span class="'.$class.'">'.$value.'</span>';
332            }
333        }; // end of anonymous class (objRevInfo)
334    }
335
336}
337