xref: /dokuwiki/inc/ChangeLog/RevisionInfo.php (revision b428a50a44f32af0f11595d221760d20901b27d1)
1<?php
2
3namespace dokuwiki\ChangeLog;
4
5/**
6 * Class RevisionInfo
7 *
8 * Provides methods to show Revision Information in DokuWiki Ui compoments:
9 *  - Ui\Recent
10 *  - Ui\PageRevisions
11 *  - Ui\MediaRevisions
12 */
13class RevisionInfo
14{
15    /* @var array */
16    protected $info;
17
18    /**
19     * Constructor
20     *
21     * @param array $info Revision Infomation structure with entries:
22     *      - date:  unix timestamp
23     *      - ip:    IPv4 or IPv6 address
24     *      - type:  change type (log line type)
25     *      - id:    page id
26     *      - user:  user name
27     *      - sum:   edit summary (or action reason)
28     *      - extra: extra data (varies by line type)
29     *      - sizechange: change of filesize
30     *      additionally,
31     *      - current:   (optional) whether current revision or not
32     *      - timestamp: (optional) set only when external edits occurred
33     *      - mode:  (internal use) ether "media" or "page"
34     */
35    public function __construct($info = null)
36    {
37        if (is_array($info) && isset($info['id'])) {
38            // define strategy context
39            $info['mode'] = strrpos($info['id'], '.') ? 'media' : 'page';
40        } else {
41            $info = [
42                'mode' => 'page',
43                'date' => false,
44            ];
45        }
46        $this->info = $info;
47    }
48
49    /**
50     * Set or return whether this revison is current page or media file
51     *
52     * This method does not check exactly whether the revision is current or not. Instead,
53     * set value of associated "current" key for internal use. Some UI element like diff
54     * link button depend on relation to current page or media file. A changelog line does
55     * not indicate whether it coresponds to current page or media file.
56     *
57     * @param bool $value true if the revision is current, otherwise false
58     * @return bool
59     */
60    public function isCurrent($value = null)
61    {
62        return $this->val('current', $value);
63    }
64
65    /**
66     * Return or set a value of assosiated key of revision information
67     * but does not allow to change values of existing keys
68     *
69     * @param string $key
70     * @param mixed $value
71     * @return string|null
72     */
73    public function val($key, $value = null)
74    {
75        if (isset($value) && !array_key_exists($key, $this->info)) {
76            // setter, only for new keys
77            $this->info[$key] = $value;
78        }
79        if (array_key_exists($key, $this->info)) {
80            // getter
81            return $this->info[$key];
82        }
83        return null;
84    }
85
86    /**
87     * Set extra key-value to the revision information
88     * but does not allow to change values of existing keys
89     * @param array $info
90     * @return void
91     */
92    public function append(array $info)
93    {
94        foreach ($info as $key => $value) {
95            $this->val($key, $value);
96        }
97    }
98
99
100    /**
101     * fileicon of the page or media file
102     * used in [Ui\recent]
103     *
104     * @return string
105     */
106    public function showFileIcon()
107    {
108        $id = $this->val('id');
109        switch ($this->val('mode')) {
110            case 'media': // media file revision
111                return media_printicon($id);
112            case 'page': // page revision
113                return '<img class="icon" src="'.DOKU_BASE.'lib/images/fileicons/file.png" alt="'.$id.'" />';
114        }
115    }
116
117    /**
118     * edit date and time of the page or media file
119     * used in [Ui\recent, Ui\Revisions]
120     *
121     * @param bool $checkTimestamp  enable timestamp check, alter formatted string when timestamp is false
122     * @return string
123     */
124    public function showEditDate($checkTimestamp = false)
125    {
126        $formatted = dformat($this->val('date'));
127        if ($checkTimestamp && $this->val('timestamp') === false) {
128            // exact date is unknown for externally deleted file
129            // when unknown, alter formatted string "YYYY-mm-DD HH:MM" to "____-__-__ __:__"
130            $formatted = preg_replace('/[0-9a-zA-Z]/','_', $formatted);
131        }
132        return '<span class="date">'. $formatted .'</span>';
133    }
134
135    /**
136     * edit summary
137     * used in [Ui\recent, Ui\Revisions]
138     *
139     * @return string
140     */
141    public function showEditSummary()
142    {
143        return '<span class="sum">'.' – '. hsc($this->val('sum')).'</span>';
144    }
145
146    /**
147     * editor of the page or media file
148     * used in [Ui\recent, Ui\Revisions]
149     *
150     * @return string
151     */
152    public function showEditor()
153    {
154        if ($this->val('user')) {
155            $html = '<bdi>'. editorinfo($this->val('user')) .'</bdi>';
156            if (auth_ismanager()) $html .= ' <bdo dir="ltr">('. $this->val('ip') .')</bdo>';
157        } else {
158            $html = '<bdo dir="ltr">'. $this->val('ip') .'</bdo>';
159        }
160        return '<span class="user">'. $html. '</span>';
161    }
162
163    /**
164     * name of the page or media file
165     * used in [Ui\recent, Ui\Revisions]
166     *
167     * @return string
168     */
169    public function showFileName()
170    {
171        $id = $this->val('id');
172        $rev = $this->isCurrent() ? '' : $this->val('date');
173
174        switch ($this->val('mode')) {
175            case 'media': // media file revision
176                $params = ['tab_details'=> 'view', 'ns'=> getNS($id), 'image'=> $id];
177                if ($rev) $params += ['rev'=> $rev];
178                $href = media_managerURL($params, '&');
179                $display_name = $id;
180                $class = file_exists(mediaFN($id, $rev)) ? 'wikilink1' : 'wikilink2';
181                break;
182            case 'page': // page revision
183                $params = $rev ? ['rev'=> $rev] : [];
184                $href = wl($id, $params, false, '&');
185                $display_name = useHeading('navigation') ? hsc(p_get_first_heading($id)) : $id;
186                if (!$display_name) $display_name = $id;
187                $class = page_exists($id, $rev) ? 'wikilink1' : 'wikilink2';
188        }
189        if ($this->val('type') == DOKU_CHANGE_TYPE_DELETE) {
190            $class = 'wikilink2';
191        }
192        return '<a href="'.$href.'" class="'.$class.'">'.$display_name.'</a>';
193    }
194
195    /**
196     * Revision Title for PageDiff table headline
197     *
198     * @return string
199     */
200    public function showRevisionTitle()
201    {
202        global $lang;
203
204        if (!$this->val('date')) return '&mdash;';
205
206        $id = $this->val('id');
207        $rev = $this->isCurrent() ? '' : $this->val('date');
208        $params = ($rev) ? ['rev'=> $rev] : [];
209
210        switch ($this->val('mode')) {
211            case 'media': // media file revision
212                $href = ml($id, $params, false, '&');
213                $class = file_exists(mediaFN($id, $rev)) ? 'wikilink1' : 'wikilink2';
214                break;
215            case 'page': // page revision
216                $href = wl($id, $params, false, '&');
217                $class = page_exists($id, $rev) ? 'wikilink1' : 'wikilink2';
218        }
219        if ($this->val('type') == DOKU_CHANGE_TYPE_DELETE) {
220            $class = 'wikilink2';
221        }
222        // revision info may have timestamp key when external edits occurred
223        $date = ($this->val('timestamp') === false)
224            ? $lang['unknowndate']
225            : dformat($this->val('date'));
226
227        return '<bdi><a class="'.$class.'" href="'.$href.'">'.$id.' ['.$date.']'.'</a></bdi>';
228    }
229
230    /**
231     * difflink icon in recents list, to compare (this) current revision with previous one
232     * all items in "recent changes" are current revision of the page or media
233     *
234     * @return string
235     */
236    public function showIconCompareWithPrevious()
237    {
238        global $lang;
239        $id = $this->val('id');
240
241        $href = '';
242        switch ($this->val('mode')) {
243            case 'media': // media file revision
244                // unlike page, media file does not copyed to media_attic when uploaded.
245                // diff icon will not be shown when external edit occurred
246                // because no attic file to be compared with current.
247                $revs = (new MediaChangeLog($id))->getRevisions(0, 1);
248                $showLink = (count($revs) && file_exists(mediaFN($id,$revs[0])) && file_exists(mediaFN($id)));
249                if ($showLink) {
250                    $param = ['tab_details'=>'history', 'mediado'=>'diff', 'ns'=> getNS($id), 'image'=> $id];
251                    $href = media_managerURL($param, '&');
252                }
253                break;
254            case 'page': // page revision
255                // when a page just created anyway, it is natural to expect no older revisions
256                // even if it had once existed but deleted before. Simply ignore to check changelog.
257                if ($this->val('type') !== DOKU_CHANGE_TYPE_CREATE) {
258                    $href = wl($id, ['do'=>'diff'], false, '&');
259                }
260        }
261
262        if ($href) {
263            return '<a href="'.$href.'" class="diff_link">'
264                  .'<img src="'.DOKU_BASE.'lib/images/diff.png" width="15" height="11"'
265                  .' title="'. $lang['diff'] .'" alt="'.$lang['diff'] .'" />'
266                  .'</a>';
267        } else {
268            return '<img src="'.DOKU_BASE.'lib/images/blank.gif" width="15" height="11" alt="" />';
269        }
270    }
271
272    /**
273     * difflink icon in revsions list, compare this revision with current one
274     * the icon does not displayed for the current revision
275     *
276     * @return string
277     */
278    public function showIconCompareWithCurrent()
279    {
280        global $lang;
281        $id = $this->val('id');
282        $rev = $this->isCurrent() ? '' : $this->val('date');
283
284        $href = '';
285        switch ($this->val('mode')) {
286            case 'media': // media file revision
287                if (!$this->isCurrent() && file_exists(mediaFN($id, $rev))) {
288                    $param = ['mediado'=>'diff', 'image'=> $id, 'rev'=> $rev];
289                    $href = media_managerURL($param, '&');
290                }
291                break;
292            case 'page': // page revision
293                if (!$this->isCurrent()) {
294                    $href = wl($id, ['rev'=> $rev, 'do'=>'diff'], false, '&');
295                }
296        }
297
298        if ($href) {
299            return '<a href="'.$href.'" class="diff_link">'
300                  .'<img src="'.DOKU_BASE.'lib/images/diff.png" width="15" height="11"'
301                  .' title="'. $lang['diff'] .'" alt="'.$lang['diff'] .'" />'
302                  .'</a>';
303        } else {
304            return '<img src="'.DOKU_BASE.'lib/images/blank.gif" width="15" height="11" alt="" />';
305        }
306    }
307
308    /**
309     * icon for revision action
310     * used in [Ui\recent]
311     *
312     * @return string
313     */
314    public function showIconRevisions()
315    {
316        global $lang;
317
318        if (!actionOK('revisions')) {
319            return '';
320        }
321
322        $id = $this->val('id');
323        switch ($this->val('mode')) {
324            case 'media': // media file revision
325                $param  = ['tab_details'=>'history', 'ns'=> getNS($id), 'image'=> $id];
326                $href = media_managerURL($param, '&');
327                break;
328            case 'page': // page revision
329                $href = wl($id, ['do'=>'revisions'], false, '&');
330        }
331        return '<a href="'.$href.'" class="revisions_link">'
332              . '<img src="'.DOKU_BASE.'lib/images/history.png" width="12" height="14"'
333              . ' title="'.$lang['btn_revs'].'" alt="'.$lang['btn_revs'].'" />'
334              . '</a>';
335    }
336
337    /**
338     * size change
339     * used in [Ui\recent, Ui\Revisions]
340     *
341     * @return string
342     */
343    public function showSizeChange()
344    {
345        $class = 'sizechange';
346        $value = filesize_h(abs($this->val('sizechange')));
347        if ($this->val('sizechange') > 0) {
348            $class .= ' positive';
349            $value = '+' . $value;
350        } elseif ($this->val('sizechange') < 0) {
351            $class .= ' negative';
352            $value = '-' . $value;
353        } else {
354            $value = '±' . $value;
355        }
356        return '<span class="'.$class.'">'.$value.'</span>';
357    }
358
359    /**
360     * current indicator, used in revison list
361     * not used in Ui\Recents because recent filess are always current one
362     *
363     * @return string
364     */
365    public function showCurrentIndicator()
366    {
367        global $lang;
368        return $this->isCurrent() ? '('.$lang['current'].')' : '';
369    }
370
371
372}
373