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