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