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