xref: /dokuwiki/inc/Ui/Revisions.php (revision d9c75b22c52ca1b35878418dff1a583d3968d8ad)
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                $this->info = $info;
149            }
150
151            // current indicator
152            public function currentIndicator()
153            {
154                global $lang;
155                return ($this->info['current']) ? '('.$lang['current'].')' : '';
156            }
157
158            // edit date and time of the page or media file
159            public function editDate()
160            {
161                return '<span class="date">'. dformat($this->info['date']) .'</span>';
162            }
163
164            // edit summary
165            public function editSummary()
166            {
167                return '<span class="sum">'.' – '. hsc($this->info['sum']).'</span>';
168            }
169
170            // editor of the page or media file
171            public function editor()
172            {
173                // slightly different with display of Ui\Recent, i.e. external edit
174                global $lang;
175                $html = '<span class="user">';
176                if (!$this->info['user'] && !$this->info['ip']) {
177                    $html.= '('.$lang['external_edit'].')';
178                } elseif ($this->info['user']) {
179                    $html.= '<bdi>'. editorinfo($this->info['user']) .'</bdi>';
180                    if (auth_ismanager()) $html.= ' <bdo dir="ltr">('. $this->info['ip'] .')</bdo>';
181                } else {
182                    $html.= '<bdo dir="ltr">'. $this->info['ip'] .'</bdo>';
183                }
184                $html.= '</span>';
185                return $html;
186            }
187
188            // name of the page or media file
189            public function itemName()
190            {
191                // slightly different with display of Ui\Recent, i.e. revison may not exists
192                $id = $this->info['id'];
193                $rev = $this->info['date'];
194
195                switch ($this->info['item']) {
196                    case 'media': // media file revision
197                        if (isset($this->info['current'])) {
198                            $href = media_managerURL(['image'=> $id, 'tab_details'=> 'view'], '&');
199                            $html = '<a href="'.$href.'" class="wikilink1">'.$id.'</a>';
200                        } elseif (file_exists(mediaFN($id, $rev))) {
201                            $href = media_managerURL(['image'=> $id, 'tab_details'=> 'view', 'rev'=> $rev], '&');
202                            $html = '<a href="'.$href.'" class="wikilink1">'.$id.'</a>';
203                        } else {
204                            $html = $id;
205                        }
206                        return $html;
207                    case 'page': // page revision
208                        $display_name = useHeading('navigation') ? hsc(p_get_first_heading($id)) : $id;
209                        if (!$display_name) $display_name = $id;
210                        if ($this->info['current'] || page_exists($id, $rev)) {
211                            $href = wl($id, "rev=$rev", false, '&');
212                            $html = '<a href="'.$href.'" class="wikilink1">'.$display_name.'</a>';
213                        } else {
214                            $html = $display_name;
215                        }
216                        return $html;
217                }
218                return '';
219            }
220
221            // icon difflink
222            public function difflink()
223            {
224                global $lang;
225                $id = $this->info['id'];
226                $rev = $this->info['date'];
227
228                switch ($this->info['item']) {
229                    case 'media': // media file revision
230                        if (isset($this->info['current']) || !file_exists(mediaFN($id, $rev))) {
231                            $html = '<img src="'.DOKU_BASE.'lib/images/blank.gif" width="15" height="11" alt="" />';
232                        } else {
233                            $href = media_managerURL(['image'=> $id, 'rev'=> $rev, 'mediado'=>'diff'], '&');
234                            $html = '<a href="'.$href.'" class="diff_link">'
235                                  . '<img src="'.DOKU_BASE.'lib/images/diff.png" width="15" height="11"'
236                                  . ' title="'. $lang['diff'] .'" alt="'.$lang['diff'] .'" />'
237                                  . '</a> ';
238                        }
239                        return $html;
240                    case 'page': // page revision
241                        if ($this->info['current'] || !page_exists($id, $rev)) {
242                            $html = '<img src="'.DOKU_BASE.'lib/images/blank.gif" width="15" height="11" alt="" />';
243                        } else {
244                            $href = wl($id, "rev=$rev,do=diff", false, '&');
245                            $html = '<a href="'.$href.'" class="diff_link">'
246                                  . '<img src="'.DOKU_BASE.'lib/images/diff.png" width="15" height="11"'
247                                  . ' title="'.$lang['diff'].'" alt="'.$lang['diff'].'" />'
248                                  . '</a>';
249                        }
250                        return $html;
251                }
252                return '';
253            }
254
255            // size change
256            public function sizeChange()
257            {
258                $class = 'sizechange';
259                $value = filesize_h(abs($this->info['sizechange']));
260                if ($this->info['sizechange'] > 0) {
261                    $class .= ' positive';
262                    $value = '+' . $value;
263                } elseif ($this->info['sizechange'] < 0) {
264                    $class .= ' negative';
265                    $value = '-' . $value;
266                } else {
267                    $value = '±' . $value;
268                }
269                return '<span class="'.$class.'">'.$value.'</span>';
270            }
271        }; // end of anonymous class (objRevInfo)
272    }
273
274}
275