Failed Conditions
Pull Request — master (#3198)
by
unknown
03:16
created

Diff::diffViewlink()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 4
dl 0
loc 21
rs 9.584
c 0
b 0
f 0
1
<?php
2
3
namespace dokuwiki\Ui;
4
5
use dokuwiki\ChangeLog\PageChangeLog;
6
use dokuwiki\ChangeLog\MediaChangeLog;
7
use dokuwiki\Extension\Event;
8
use dokuwiki\Form\Form;
9
10
/**
11
 * DokuWiki Diff Insterface
12
 *
13
 * @package dokuwiki\Ui
14
 */
15
class Diff extends Ui
16
{
17
    /**
18
     * Show diff
19
     * between current page version and provided $text
20
     * or between the revisions provided via GET or POST
21
     *
22
     * @author Andreas Gohr <[email protected]>
23
     *
24
     * @param  string $text  when non-empty: compare with this text with most current version
25
     * @param  bool   $intro display the intro text
26
     * @param  string $type  type of the diff (inline or sidebyside)
27
     * @return void
28
     */
29
    public function show($text = '', $intro = true, $type = null)
30
    {
31
        global $ID;
32
        global $REV;
33
        global $lang;
34
        global $INPUT;
35
        global $INFO;
36
        $pagelog = new PageChangeLog($ID);
37
38
        /*
39
         * Determine diff type
40
         */
41
        if (!$type) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $type of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
42
            $type = $INPUT->str('difftype');
43
            if (empty($type)) {
44
                $type = get_doku_pref('difftype', $type);
45
                if (empty($type) && $INFO['ismobile']) {
46
                    $type = 'inline';
47
                }
48
            }
49
        }
50
        if ($type != 'inline') $type = 'sidebyside';
51
52
        /*
53
         * Determine requested revision(s)
54
         */
55
        // we're trying to be clever here, revisions to compare can be either
56
        // given as rev and rev2 parameters, with rev2 being optional. Or in an
57
        // array in rev2.
58
        $rev1 = $REV;
59
60
        $rev2 = $INPUT->ref('rev2');
61
        if (is_array($rev2)) {
62
            $rev1 = (int) $rev2[0];
63
            $rev2 = (int) $rev2[1];
64
65
            if (!$rev1) {
66
                $rev1 = $rev2;
67
                unset($rev2);
68
            }
69
        } else {
70
            $rev2 = $INPUT->int('rev2');
71
        }
72
73
        /*
74
         * Determine left and right revision, its texts and the header
75
         */
76
        $r_minor = '';
77
        $l_minor = '';
78
79
        if ($text) { // compare text to the most current revision
80
            $l_rev = '';
81
            $l_text = rawWiki($ID, '');
82
            $l_head = '<a class="wikilink1" href="'. wl($ID) .'">'
83
                . $ID .' '. dformat((int) @filemtime(wikiFN($ID))) .'</a> '
84
                . $lang['current'];
85
86
            $r_rev = '';
87
            $r_text = cleanText($text);
88
            $r_head = $lang['yours'];
89
        } else {
90
            if ($rev1 && isset($rev2) && $rev2) { // two specific revisions wanted
91
                // make sure order is correct (older on the left)
92
                if ($rev1 < $rev2) {
93
                    $l_rev = $rev1;
94
                    $r_rev = $rev2;
95
                } else {
96
                    $l_rev = $rev2;
97
                    $r_rev = $rev1;
98
                }
99
            } elseif ($rev1) { // single revision given, compare to current
100
                $r_rev = '';
101
                $l_rev = $rev1;
102
            } else { // no revision was given, compare previous to current
103
                $r_rev = '';
104
                $revs = $pagelog->getRevisions(0, 1);
105
                $l_rev = $revs[0];
106
                $REV = $l_rev; // store revision back in $REV
107
            }
108
109
            // when both revisions are empty then the page was created just now
110
            if (!$l_rev && !$r_rev) {
111
                $l_text = '';
112
            } else {
113
                $l_text = rawWiki($ID, $l_rev);
114
            }
115
            $r_text = rawWiki($ID, $r_rev);
116
117
            list($l_head, $r_head, $l_minor, $r_minor) = $this->diffHead(
118
                $l_rev, $r_rev, null, false, $type == 'inline'
119
            );
120
        }
121
122
        /*
123
         * Build navigation
124
         */
125
        $l_nav = '';
126
        $r_nav = '';
127
        if (!$text) {
128
            list($l_nav, $r_nav) = $this->diffNavigation($pagelog, $type, $l_rev, $r_rev);
129
        }
130
        /*
131
         * Create diff object and the formatter
132
         */
133
        $diff = new \Diff(explode("\n", $l_text), explode("\n", $r_text));
134
135
        if ($type == 'inline') {
136
            $diffformatter = new \InlineDiffFormatter();
137
        } else {
138
            $diffformatter = new \TableDiffFormatter();
139
        }
140
        /*
141
         * Display intro
142
         */
143
        if ($intro) print p_locale_xhtml('diff');
144
145
        /*
146
         * Display type and exact reference
147
         */
148
        if (!$text) {
149
            print '<div class="diffoptions group">';
150
151
            // create the form to select diff view type
152
            $form = new Form(['action' => wl()]);
153
            $form->setHiddenField('id', $ID);
154
            $form->setHiddenField('rev2[0]', $l_rev);
155
            $form->setHiddenField('rev2[1]', $r_rev);
156
            $form->setHiddenField('do', 'diff');
157
            $options = array(
158
                         'sidebyside' => $lang['diff_side'],
159
                         'inline' => $lang['diff_inline']
160
            );
161
            $input = $form->addDropdown('difftype', $options, $lang['diff_type'])
162
                ->val($type)->addClass('quickselect');
163
            $input->useInput(false); // inhibit prefillInput() during toHTML() process
164
            $form->addButton('do[diff]', 'Go')->attr('type','submit');
165
            print $form->toHTML();
166
167
            print '<p>';
168
            // link to exactly this view FS#2835
169
            print $this->diffViewlink($type, 'difflink', $l_rev, $r_rev ? $r_rev : $INFO['currentrev']);
170
            print '</p>';
171
172
            print '</div>'; // .diffoptions
173
        }
174
175
        /*
176
         * Display diff view table
177
         */
178
        print '<div class="table">';
179
        print '<table class="diff diff_'. $type .'">';
180
181
        //navigation and header
182
        if ($type == 'inline') {
183
            if (!$text) {
184
                print '<tr>'
185
                    . '<td class="diff-lineheader">-</td>'
186
                    . '<td class="diffnav">'. $l_nav .'</td>'
187
                    . '</tr>';
188
                print '<tr>'
189
                    . '<th class="diff-lineheader">-</th>'
190
                    . '<th '. $l_minor .'>'. $l_head .'</th>'
191
                    .'</tr>';
192
            }
193
            print '<tr>'
194
                . '<td class="diff-lineheader">+</td>'
195
                . '<td class="diffnav">'. $r_nav .'</td>'
196
                .'</tr>';
197
            print '<tr>'
198
                . '<th class="diff-lineheader">+</th>'
199
                . '<th '. $r_minor .'>'. $r_head .'</th>'
200
                . '</tr>';
201
        } else {
202
            if (!$text) {
203
                print '<tr>'
204
                    . '<td colspan="2" class="diffnav">'. $l_nav .'</td>'
205
                    . '<td colspan="2" class="diffnav">'. $r_nav .'</td>'
206
                    . '</tr>';
207
            }
208
            print '<tr>'
209
                . '<th colspan="2" '. $l_minor .'>'. $l_head .'</th>'
210
                . '<th colspan="2" '. $r_minor .'>'. $r_head .'</th>'
211
                . '</tr>';
212
        }
213
214
        //diff view
215
        print html_insert_softbreaks($diffformatter->format($diff));
216
217
        print '</table>';
218
        print '</div>'. DOKU_LF;
219
    }
220
221
222
    /**
223
     * Get header of diff HTML
224
     *
225
     * @param string $l_rev   Left revisions
226
     * @param string $r_rev   Right revision
227
     * @param string $id      Page id, if null $ID is used
228
     * @param bool   $media   If it is for media files
229
     * @param bool   $inline  Return the header on a single line
230
     * @return string[] HTML snippets for diff header
231
     */
232
    protected function diffHead($l_rev, $r_rev, $id = null, $media = false, $inline = false)
233
    {
234
        global $lang;
235
        if ($id === null) {
236
            global $ID;
237
            $id = $ID;
238
        }
239
        $head_separator = $inline ? ' ' : '<br />';
240
        $media_or_wikiFN = $media ? 'mediaFN' : 'wikiFN';
241
        $ml_or_wl = $media ? 'ml' : 'wl';
242
        $l_minor = $r_minor = '';
243
244
        if ($media) {
245
            $changelog = new MediaChangeLog($id);
246
        } else {
247
            $changelog = new PageChangeLog($id);
248
        }
249
        if (!$l_rev) {
250
            $l_head = '&mdash;';
251
        } else {
252
            $l_info   = $changelog->getRevisionInfo($l_rev);
253
            if ($l_info['user']) {
254
                $l_user = '<bdi>'.editorinfo($l_info['user']).'</bdi>';
255
                if (auth_ismanager()) $l_user .= ' <bdo dir="ltr">('.$l_info['ip'].')</bdo>';
256
            } else {
257
                $l_user = '<bdo dir="ltr">'.$l_info['ip'].'</bdo>';
258
            }
259
            $l_user  = '<span class="user">'.$l_user.'</span>';
260
            $l_sum   = ($l_info['sum']) ? '<span class="sum"><bdi>'.hsc($l_info['sum']).'</bdi></span>' : '';
261
            if ($l_info['type'] === DOKU_CHANGE_TYPE_MINOR_EDIT) $l_minor = 'class="minor"';
262
263
            $l_head_title = ($media) ? dformat($l_rev) : $id.' ['.dformat($l_rev).']';
264
            $l_head = '<bdi><a class="wikilink1" href="'.$ml_or_wl($id,"rev=$l_rev").'">'
265
                . $l_head_title.'</a></bdi>'.$head_separator.$l_user.' '.$l_sum;
266
        }
267
268
        if ($r_rev) {
269
            $r_info   = $changelog->getRevisionInfo($r_rev);
270
            if ($r_info['user']) {
271
                $r_user = '<bdi>'.editorinfo($r_info['user']).'</bdi>';
272
                if (auth_ismanager()) $r_user .= ' <bdo dir="ltr">('.$r_info['ip'].')</bdo>';
273
            } else {
274
                $r_user = '<bdo dir="ltr">'.$r_info['ip'].'</bdo>';
275
            }
276
            $r_user = '<span class="user">'.$r_user.'</span>';
277
            $r_sum  = ($r_info['sum']) ? '<span class="sum"><bdi>'.hsc($r_info['sum']).'</bdi></span>' : '';
278
            if ($r_info['type'] === DOKU_CHANGE_TYPE_MINOR_EDIT) $r_minor = 'class="minor"';
279
280
            $r_head_title = ($media) ? dformat($r_rev) : $id.' ['.dformat($r_rev).']';
281
            $r_head = '<bdi><a class="wikilink1" href="'.$ml_or_wl($id,"rev=$r_rev").'">'
282
                . $r_head_title.'</a></bdi>'.$head_separator.$r_user.' '.$r_sum;
283
        } elseif ($_rev = @filemtime($media_or_wikiFN($id))) {
284
            $_info   = $changelog->getRevisionInfo($_rev);
285
            if ($_info['user']) {
286
                $_user = '<bdi>'.editorinfo($_info['user']).'</bdi>';
287
                if (auth_ismanager()) $_user .= ' <bdo dir="ltr">('.$_info['ip'].')</bdo>';
288
            } else {
289
                $_user = '<bdo dir="ltr">'.$_info['ip'].'</bdo>';
290
            }
291
            $_user = '<span class="user">'.$_user.'</span>';
292
            $_sum  = ($_info['sum']) ? '<span class="sum"><bdi>'.hsc($_info['sum']).'</span></bdi>' : '';
293
            if ($_info['type'] === DOKU_CHANGE_TYPE_MINOR_EDIT) $r_minor = 'class="minor"';
294
295
            $r_head_title = ($media) ? dformat($_rev) : $id.' ['.dformat($_rev).']';
296
            $r_head  = '<bdi><a class="wikilink1" href="'.$ml_or_wl($id).'">'
297
                . $r_head_title.'</a></bdi> '.'('.$lang['current'].')'.$head_separator.$_user.' '.$_sum;
298
        }else{
299
            $r_head = '&mdash; ('.$lang['current'].')';
300
        }
301
302
        return array($l_head, $r_head, $l_minor, $r_minor);
303
    }
304
305
    /**
306
     * Create html for revision navigation
307
     *
308
     * @param PageChangeLog $pagelog changelog object of current page
309
     * @param string        $type    inline vs sidebyside
310
     * @param int           $l_rev   left revision timestamp
311
     * @param int           $r_rev   right revision timestamp
312
     * @return string[] html of left and right navigation elements
313
     */
314
    protected function diffNavigation($pagelog, $type, $l_rev, $r_rev)
315
    {
316
        global $INFO, $ID;
317
318
        // last timestamp is not in changelog, retrieve timestamp from metadata
319
        // note: when page is removed, the metadata timestamp is zero
320
        if (!$r_rev) {
321
            if (isset($INFO['meta']['last_change']['date'])) {
322
                $r_rev = $INFO['meta']['last_change']['date'];
323
            } else {
324
                $r_rev = 0;
325
            }
326
        }
327
328
        //retrieve revisions with additional info
329
        list($l_revs, $r_revs) = $pagelog->getRevisionsAround($l_rev, $r_rev);
330
        $l_revisions = array();
331
        if (!$l_rev) {
332
            //no left revision given, add dummy
333
            $l_revisions[0]= array('label' => '', 'attrs' => []);
334
        }
335
        foreach ($l_revs as $rev) {
336
            $info = $pagelog->getRevisionInfo($rev);
337
            $l_revisions[$rev] = array(
338
                'label' => dformat($info['date']) .' '. editorinfo($info['user'], true) .' '. $info['sum'],
339
                'attrs' => ['title' => $rev],
340
            );
341
            if ($r_rev ? $rev >= $r_rev : false) $l_revisions[$rev]['attrs']['disabled'] = 'disabled';
342
        }
343
        $r_revisions = array();
344
        if (!$r_rev) {
345
            //no right revision given, add dummy
346
            $r_revisions[0] = array('label' => '', 'attrs' => []);
347
        }
348
        foreach ($r_revs as $rev) {
349
            $info = $pagelog->getRevisionInfo($rev);
350
            $r_revisions[$rev] = array(
351
                'label' => dformat($info['date']) .' '. editorinfo($info['user'], true) .' '. $info['sum'],
352
                'attrs' => ['title' => $rev],
353
            );
354
            if ($rev <= $l_rev) $r_revisions[$rev]['attrs']['disabled'] = 'disabled';
355
        }
356
357
        //determine previous/next revisions
358
        $l_index = array_search($l_rev, $l_revs);
359
        $l_prev = $l_revs[$l_index + 1];
360
        $l_next = $l_revs[$l_index - 1];
361
        if ($r_rev) {
362
            $r_index = array_search($r_rev, $r_revs);
363
            $r_prev = $r_revs[$r_index + 1];
364
            $r_next = $r_revs[$r_index - 1];
365
        } else {
366
            //removed page
367
            if ($l_next) {
368
                $r_prev = $r_revs[0];
369
            } else {
370
                $r_prev = null;
371
            }
372
            $r_next = null;
373
        }
374
375
        /*
376
         * Left side:
377
         */
378
        $l_nav = '';
379
        //move back
380
        if ($l_prev) {
381
            $l_nav .= $this->diffViewlink($type, 'diffbothprevrev', $l_prev, $r_prev);
382
            $l_nav .= $this->diffViewlink($type, 'diffprevrev', $l_prev, $r_rev);
383
        }
384
        //dropdown
385
        $form = new Form(['action' => wl()]);
386
        $form->setHiddenField('id', $ID);
387
        $form->setHiddenField('difftype', $type);
388
        $form->setHiddenField('rev2[1]', $r_rev);
389
        $form->setHiddenField('do', 'diff');
390
        $input = $form->addDropdown('rev2[0]', $l_revisions)->val($l_rev)->addClass('quickselect');
391
        $input->useInput(false); // inhibit prefillInput() during toHTML() process
392
        $form->addButton('do[diff]', 'Go')->attr('type','submit');
393
        $l_nav .= $form->toHTML();
394
        //move forward
395
        if ($l_next && ($l_next < $r_rev || !$r_rev)) {
396
            $l_nav .= $this->diffViewlink($type, 'diffnextrev', $l_next, $r_rev);
397
        }
398
399
        /*
400
         * Right side:
401
         */
402
        $r_nav = '';
403
        //move back
404
        if ($l_rev < $r_prev) {
405
            $r_nav .= $this->diffViewlink($type, 'diffprevrev', $l_rev, $r_prev);
406
        }
407
        //dropdown
408
        $form = new Form(['action' => wl()]);
409
        $form->setHiddenField('id', $ID);
410
        $form->setHiddenField('rev2[0]', $l_rev);
411
        $form->setHiddenField('difftype', $type);
412
        $form->setHiddenField('do', 'diff');
413
        $input = $form->addDropdown('rev2[1]', $r_revisions)->val($r_rev)->addClass('quickselect');
414
        $input->useInput(false); // inhibit prefillInput() during toHTML() process
415
        $form->addButton('do[diff]', 'Go')->attr('type','submit');
416
        $r_nav .= $form->toHTML();
417
        //move forward
418
        if ($r_next) {
419
            if ($pagelog->isCurrentRevision($r_next)) {
420
                //last revision is diff with current page
421
                $r_nav .= $this->diffViewlink($type, 'difflastrev', $l_rev);
422
            } else {
423
                $r_nav .= $this->diffViewlink($type, 'diffnextrev', $l_rev, $r_next);
424
            }
425
        } else {
426
            $r_nav .= $this->diffViewlink($type, 'diffbothnextrev', $l_next, $r_next);
427
        }
428
        return array($l_nav, $r_nav);
429
    }
430
431
    /**
432
     * Create html link to a diff view defined by two revisions
433
     *
434
     * @param string $difftype display type
435
     * @param string $linktype
436
     * @param int $lrev oldest revision
437
     * @param int $rrev newest revision or null for diff with current revision
438
     * @return string html of link to a diff view
439
     */
440
    protected function diffViewlink($difftype, $linktype, $lrev, $rrev = null)
441
    {
442
        global $ID, $lang;
443
        if (!$rrev) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $rrev of type integer|null is loosely compared to false; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
444
            $urlparam = array(
445
                'do' => 'diff',
446
                'rev' => $lrev,
447
                'difftype' => $difftype,
448
            );
449
        } else {
450
            $urlparam = array(
451
                'do' => 'diff',
452
                'rev2[0]' => $lrev,
453
                'rev2[1]' => $rrev,
454
                'difftype' => $difftype,
455
            );
456
        }
457
        return  '<a class="'. $linktype .'" href="'. wl($ID, $urlparam) .'" title="'. $lang[$linktype] .'">'
458
              . '<span>'. $lang[$linktype] .'</span>'
459
              . '</a>'. DOKU_LF;
460
    }
461
462
}
463