xref: /dokuwiki/inc/Ui/Revisions.php (revision a3984ddfd9b50ad1131d8a303301b6f72706b82d)
1<?php
2
3namespace dokuwiki\Ui;
4
5/**
6 * DokuWiki Revisions Interface
7 * parent class of PageRevisions and MediaRevisions
8 *
9 * @package dokuwiki\Ui
10 */
11abstract class Revisions extends Ui
12{
13    /* @var string */
14    protected $id;   // page id or media id
15    protected $item; // page or media
16
17    /* @var ChangeLog */
18    protected $changelog; // PageChangeLog or MediaChangeLog object
19
20    /**
21     * Revisions Ui constructor
22     *
23     * @param string $id  page id or media id
24     */
25    public function __construct($id)
26    {
27        $this->id = $id;
28        $this->setChangeLog();
29    }
30
31    /**
32     * set class property changelog
33     */
34    abstract protected function setChangeLog();
35
36    /**
37     * item filename resolver
38     *
39     * @param string $id  page id or media id
40     * @param int|string $rev revision timestamp, or empty string for current one
41     * @return string full path
42     */
43    abstract protected function itemFN($id, $rev = '');
44
45    /**
46     * Get revisions, and set correct pagenation parameters (first, hasNext)
47     *
48     * @param int  $first
49     * @param bool $hasNext
50     * @return array  revisions to be shown in a pagenated list
51     * @see also https://www.dokuwiki.org/devel:changelog
52     */
53    protected function getRevisions(&$first, &$hasNext)
54    {
55        global $conf;
56
57        $changelog =& $this->changelog;
58
59        $revisions = array();
60
61        /* we need to get one additional log entry to be able to
62         * decide if this is the last page or is there another one.
63         * see also Ui\Recent::getRecents()
64         */
65        $revlist = $changelog->getRevisions($first, $conf['recent'] +1);
66        if (count($revlist) == 0 && $first != 0) {
67            $first = 0;
68            $revlist = $changelog->getRevisions($first, $conf['recent'] +1);
69        }
70
71        // add current page or media as revision[0] when necessary
72        if ($first === 0 && file_exists($this->itemFN($this->id))) {
73            $rev = filemtime(fullpath($this->itemFN($this->id)));
74            $changelog->setChunkSize(1024); //FIXME why does chunksize change wanted?
75            $revinfo = $changelog->getRevisionInfo($rev) ?: array(
76                    'date' => $rev,
77                    'ip'   => null,
78                    'type' => null,
79                    'id'   => $this->id,
80                    'user' => null,
81                    'sum'  => null,
82                    'extra' => null,
83                    'sizechange' => null,
84            );
85            $revisions[] = $revinfo + array(
86                    'item' => $this->item,
87                    'current' => true,
88            );
89        }
90
91        // decide if this is the last page or is there another one
92        $hasNext = false;
93        if (count($revlist) > $conf['recent']) {
94            $hasNext = true;
95            array_pop($revlist); // remove one additional log entry
96        }
97
98        // append each revison info array to the revisions
99        foreach ($revlist as $rev) {
100            $revisions[] = $changelog->getRevisionInfo($rev) + array('item' => $this->item);
101        }
102        return $revisions;
103    }
104
105    /**
106     * Navigation buttons for Pagenation (prev/next)
107     *
108     * @param int  $first
109     * @param bool $hasNext
110     * @param callable $callback returns array of hidden fields for the form button
111     * @return array  html
112     */
113    protected function navigation($first, $hasNext, $callback)
114    {
115        global $conf;
116
117        $html = '<div class="pagenav">';
118        $last = $first + $conf['recent'];
119        if ($first > 0) {
120            $first = max($first - $conf['recent'], 0);
121            $html.= '<div class="pagenav-prev">';
122            $html.= html_btn('newer', $this->id, "p", $callback($first));
123            $html.= '</div>';
124        }
125        if ($hasNext) {
126            $html.= '<div class="pagenav-next">';
127            $html.= html_btn('older', $this->id, "n", $callback($last));
128            $html.= '</div>';
129        }
130        $html.= '</div>';
131        return $html;
132    }
133
134    /**
135     * Returns instance of objRevInfo
136     *
137     * @param array $info  Revision info structure of a page or media file
138     * @return objRevInfo object (anonymous class)
139     */
140    public function getObjRevInfo(array $info)
141    {
142        return new class ($info) // anonymous class (objRevInfo)
143        {
144            protected $info;
145
146            public function __construct(array $info)
147            {
148                if (!isset($info['current'])) {
149                    $info['current'] = false;
150                }
151                $this->info = $info;
152            }
153
154            // current indicator
155            public function currentIndicator()
156            {
157                global $lang;
158                return ($this->info['current']) ? '('.$lang['current'].')' : '';
159            }
160
161            // edit date and time of the page or media file
162            public function editDate()
163            {
164                return '<span class="date">'. dformat($this->info['date']) .'</span>';
165            }
166
167            // edit summary
168            public function editSummary()
169            {
170                return '<span class="sum">'.' – '. hsc($this->info['sum']).'</span>';
171            }
172
173            // editor of the page or media file
174            public function editor()
175            {
176                // slightly different with display of Ui\Recent, i.e. external edit
177                global $lang;
178                $html = '<span class="user">';
179                if (!$this->info['user'] && !$this->info['ip']) {
180                    $html.= '('.$lang['external_edit'].')';
181                } elseif ($this->info['user']) {
182                    $html.= '<bdi>'. editorinfo($this->info['user']) .'</bdi>';
183                    if (auth_ismanager()) $html.= ' <bdo dir="ltr">('. $this->info['ip'] .')</bdo>';
184                } else {
185                    $html.= '<bdo dir="ltr">'. $this->info['ip'] .'</bdo>';
186                }
187                $html.= '</span>';
188                return $html;
189            }
190
191            // name of the page or media file
192            public function itemName()
193            {
194                // slightly different with display of Ui\Recent, i.e. revison may not exists
195                $id = $this->info['id'];
196                $rev = $this->info['date'];
197
198                switch ($this->info['item']) {
199                    case 'media': // media file revision
200                        if (isset($this->info['current'])) {
201                            $href = media_managerURL(['image'=> $id, 'tab_details'=> 'view'], '&');
202                            $html = '<a href="'.$href.'" class="wikilink1">'.$id.'</a>';
203                        } elseif (file_exists(mediaFN($id, $rev))) {
204                            $href = media_managerURL(['image'=> $id, 'tab_details'=> 'view', 'rev'=> $rev], '&');
205                            $html = '<a href="'.$href.'" class="wikilink1">'.$id.'</a>';
206                        } else {
207                            $html = $id;
208                        }
209                        return $html;
210                    case 'page': // page revision
211                        $display_name = useHeading('navigation') ? hsc(p_get_first_heading($id)) : $id;
212                        if (!$display_name) $display_name = $id;
213                        if ($this->info['current'] || page_exists($id, $rev)) {
214                            $href = wl($id, "rev=$rev", false, '&');
215                            $html = '<a href="'.$href.'" class="wikilink1">'.$display_name.'</a>';
216                        } else {
217                            $html = $display_name;
218                        }
219                        return $html;
220                }
221                return '';
222            }
223
224            // icon difflink
225            public function difflink()
226            {
227                global $lang;
228                $id = $this->info['id'];
229                $rev = $this->info['date'];
230
231                switch ($this->info['item']) {
232                    case 'media': // media file revision
233                        if (isset($this->info['current']) || !file_exists(mediaFN($id, $rev))) {
234                            $html = '<img src="'.DOKU_BASE.'lib/images/blank.gif" width="15" height="11" alt="" />';
235                        } else {
236                            $href = media_managerURL(['image'=> $id, 'rev'=> $rev, 'mediado'=>'diff'], '&');
237                            $html = '<a href="'.$href.'" class="diff_link">'
238                                  . '<img src="'.DOKU_BASE.'lib/images/diff.png" width="15" height="11"'
239                                  . ' title="'. $lang['diff'] .'" alt="'.$lang['diff'] .'" />'
240                                  . '</a> ';
241                        }
242                        return $html;
243                    case 'page': // page revision
244                        if ($this->info['current'] || !page_exists($id, $rev)) {
245                            $html = '<img src="'.DOKU_BASE.'lib/images/blank.gif" width="15" height="11" alt="" />';
246                        } else {
247                            $href = wl($id, "rev=$rev,do=diff", false, '&');
248                            $html = '<a href="'.$href.'" class="diff_link">'
249                                  . '<img src="'.DOKU_BASE.'lib/images/diff.png" width="15" height="11"'
250                                  . ' title="'.$lang['diff'].'" alt="'.$lang['diff'].'" />'
251                                  . '</a>';
252                        }
253                        return $html;
254                }
255                return '';
256            }
257
258            // size change
259            public function sizeChange()
260            {
261                $class = 'sizechange';
262                $value = filesize_h(abs($this->info['sizechange']));
263                if ($this->info['sizechange'] > 0) {
264                    $class .= ' positive';
265                    $value = '+' . $value;
266                } elseif ($this->info['sizechange'] < 0) {
267                    $class .= ' negative';
268                    $value = '-' . $value;
269                } else {
270                    $value = '±' . $value;
271                }
272                return '<span class="'.$class.'">'.$value.'</span>';
273            }
274        }; // end of anonymous class (objRevInfo)
275    }
276
277}
278