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