xref: /dokuwiki/inc/Ui/PageDiff.php (revision defc7576146ce958f2bb8ef51bd5113cfc8e87d1)
1*defc7576SSatoshi Sahara<?php
2*defc7576SSatoshi Sahara
3*defc7576SSatoshi Saharanamespace dokuwiki\Ui;
4*defc7576SSatoshi Sahara
5*defc7576SSatoshi Saharause dokuwiki\ChangeLog\PageChangeLog;
6*defc7576SSatoshi Saharause dokuwiki\ChangeLog\MediaChangeLog;
7*defc7576SSatoshi Saharause dokuwiki\Extension\Event;
8*defc7576SSatoshi Saharause dokuwiki\Form\Form;
9*defc7576SSatoshi Sahara
10*defc7576SSatoshi Sahara/**
11*defc7576SSatoshi Sahara * DokuWiki PageDiff Interface
12*defc7576SSatoshi Sahara *
13*defc7576SSatoshi Sahara * @package dokuwiki\Ui
14*defc7576SSatoshi Sahara */
15*defc7576SSatoshi Saharaclass PageDiff extends Ui
16*defc7576SSatoshi Sahara{
17*defc7576SSatoshi Sahara    protected $text;
18*defc7576SSatoshi Sahara    protected $showIntro;
19*defc7576SSatoshi Sahara    protected $difftype;
20*defc7576SSatoshi Sahara
21*defc7576SSatoshi Sahara    /**
22*defc7576SSatoshi Sahara     * PageDiff Ui constructor
23*defc7576SSatoshi Sahara     *
24*defc7576SSatoshi Sahara     * @param  string $text  when non-empty: compare with this text with most current version
25*defc7576SSatoshi Sahara     * @param  bool   $showIntro display the intro text
26*defc7576SSatoshi Sahara     * @param  string $difftype  diff view type (inline or sidebyside)
27*defc7576SSatoshi Sahara     */
28*defc7576SSatoshi Sahara    public function __construct($text = '', $showIntro = true, $difftype = null)
29*defc7576SSatoshi Sahara    {
30*defc7576SSatoshi Sahara        $this->text      = $text;
31*defc7576SSatoshi Sahara        $this->showIntro = $showIntro;
32*defc7576SSatoshi Sahara
33*defc7576SSatoshi Sahara        // determine diff view type
34*defc7576SSatoshi Sahara        if (isset($difftype)) {
35*defc7576SSatoshi Sahara            $this->difftype  = $difftype;
36*defc7576SSatoshi Sahara        } else {
37*defc7576SSatoshi Sahara            global $INPUT;
38*defc7576SSatoshi Sahara            global $INFO;
39*defc7576SSatoshi Sahara            $this->difftype = $INPUT->str('difftype') ?: get_doku_pref('difftype', $difftype);
40*defc7576SSatoshi Sahara            if (empty($this->difftype) && $INFO['ismobile']) {
41*defc7576SSatoshi Sahara                $this->difftype = 'inline';
42*defc7576SSatoshi Sahara            }
43*defc7576SSatoshi Sahara        }
44*defc7576SSatoshi Sahara        if ($this->difftype !== 'inline') $this->difftype = 'sidebyside';
45*defc7576SSatoshi Sahara    }
46*defc7576SSatoshi Sahara
47*defc7576SSatoshi Sahara    /**
48*defc7576SSatoshi Sahara     * Show diff
49*defc7576SSatoshi Sahara     * between current page version and provided $text
50*defc7576SSatoshi Sahara     * or between the revisions provided via GET or POST
51*defc7576SSatoshi Sahara     *
52*defc7576SSatoshi Sahara     * @author Andreas Gohr <andi@splitbrain.org>
53*defc7576SSatoshi Sahara     *
54*defc7576SSatoshi Sahara     * @return void
55*defc7576SSatoshi Sahara     */
56*defc7576SSatoshi Sahara    public function show()
57*defc7576SSatoshi Sahara    {
58*defc7576SSatoshi Sahara        global $ID;
59*defc7576SSatoshi Sahara        global $REV;
60*defc7576SSatoshi Sahara        global $lang;
61*defc7576SSatoshi Sahara        global $INPUT;
62*defc7576SSatoshi Sahara        global $INFO;
63*defc7576SSatoshi Sahara        $pagelog = new PageChangeLog($ID);
64*defc7576SSatoshi Sahara
65*defc7576SSatoshi Sahara        /*
66*defc7576SSatoshi Sahara         * Determine requested revision(s)
67*defc7576SSatoshi Sahara         */
68*defc7576SSatoshi Sahara        // we're trying to be clever here, revisions to compare can be either
69*defc7576SSatoshi Sahara        // given as rev and rev2 parameters, with rev2 being optional. Or in an
70*defc7576SSatoshi Sahara        // array in rev2.
71*defc7576SSatoshi Sahara        $rev1 = $REV;
72*defc7576SSatoshi Sahara
73*defc7576SSatoshi Sahara        $rev2 = $INPUT->ref('rev2');
74*defc7576SSatoshi Sahara        if (is_array($rev2)) {
75*defc7576SSatoshi Sahara            $rev1 = (int) $rev2[0];
76*defc7576SSatoshi Sahara            $rev2 = (int) $rev2[1];
77*defc7576SSatoshi Sahara
78*defc7576SSatoshi Sahara            if (!$rev1) {
79*defc7576SSatoshi Sahara                $rev1 = $rev2;
80*defc7576SSatoshi Sahara                unset($rev2);
81*defc7576SSatoshi Sahara            }
82*defc7576SSatoshi Sahara        } else {
83*defc7576SSatoshi Sahara            $rev2 = $INPUT->int('rev2');
84*defc7576SSatoshi Sahara        }
85*defc7576SSatoshi Sahara
86*defc7576SSatoshi Sahara        /*
87*defc7576SSatoshi Sahara         * Determine left and right revision, its texts and the header
88*defc7576SSatoshi Sahara         */
89*defc7576SSatoshi Sahara        $r_minor = '';
90*defc7576SSatoshi Sahara        $l_minor = '';
91*defc7576SSatoshi Sahara
92*defc7576SSatoshi Sahara        if ($this->text) { // compare text to the most current revision
93*defc7576SSatoshi Sahara            $l_rev = '';
94*defc7576SSatoshi Sahara            $l_text = rawWiki($ID, '');
95*defc7576SSatoshi Sahara            $l_head = '<a class="wikilink1" href="'. wl($ID) .'">'
96*defc7576SSatoshi Sahara                . $ID .' '. dformat((int) @filemtime(wikiFN($ID))) .'</a> '
97*defc7576SSatoshi Sahara                . $lang['current'];
98*defc7576SSatoshi Sahara
99*defc7576SSatoshi Sahara            $r_rev = '';
100*defc7576SSatoshi Sahara            $r_text = cleanText($this->text);
101*defc7576SSatoshi Sahara            $r_head = $lang['yours'];
102*defc7576SSatoshi Sahara        } else {
103*defc7576SSatoshi Sahara            if ($rev1 && isset($rev2) && $rev2) { // two specific revisions wanted
104*defc7576SSatoshi Sahara                // make sure order is correct (older on the left)
105*defc7576SSatoshi Sahara                if ($rev1 < $rev2) {
106*defc7576SSatoshi Sahara                    $l_rev = $rev1;
107*defc7576SSatoshi Sahara                    $r_rev = $rev2;
108*defc7576SSatoshi Sahara                } else {
109*defc7576SSatoshi Sahara                    $l_rev = $rev2;
110*defc7576SSatoshi Sahara                    $r_rev = $rev1;
111*defc7576SSatoshi Sahara                }
112*defc7576SSatoshi Sahara            } elseif ($rev1) { // single revision given, compare to current
113*defc7576SSatoshi Sahara                $r_rev = '';
114*defc7576SSatoshi Sahara                $l_rev = $rev1;
115*defc7576SSatoshi Sahara            } else { // no revision was given, compare previous to current
116*defc7576SSatoshi Sahara                $r_rev = '';
117*defc7576SSatoshi Sahara                $revs = $pagelog->getRevisions(0, 1);
118*defc7576SSatoshi Sahara                $l_rev = $revs[0];
119*defc7576SSatoshi Sahara                $REV = $l_rev; // store revision back in $REV
120*defc7576SSatoshi Sahara            }
121*defc7576SSatoshi Sahara
122*defc7576SSatoshi Sahara            // when both revisions are empty then the page was created just now
123*defc7576SSatoshi Sahara            if (!$l_rev && !$r_rev) {
124*defc7576SSatoshi Sahara                $l_text = '';
125*defc7576SSatoshi Sahara            } else {
126*defc7576SSatoshi Sahara                $l_text = rawWiki($ID, $l_rev);
127*defc7576SSatoshi Sahara            }
128*defc7576SSatoshi Sahara            $r_text = rawWiki($ID, $r_rev);
129*defc7576SSatoshi Sahara
130*defc7576SSatoshi Sahara            list($l_head, $r_head, $l_minor, $r_minor) = $this->diffHead(
131*defc7576SSatoshi Sahara                $l_rev, $r_rev, null, false, ($this->difftype == 'inline')
132*defc7576SSatoshi Sahara            );
133*defc7576SSatoshi Sahara        }
134*defc7576SSatoshi Sahara
135*defc7576SSatoshi Sahara        /*
136*defc7576SSatoshi Sahara         * Build navigation
137*defc7576SSatoshi Sahara         */
138*defc7576SSatoshi Sahara        $l_nav = '';
139*defc7576SSatoshi Sahara        $r_nav = '';
140*defc7576SSatoshi Sahara        if (!$this->text) {
141*defc7576SSatoshi Sahara            list($l_nav, $r_nav) = $this->diffNavigation($pagelog, $l_rev, $r_rev);
142*defc7576SSatoshi Sahara        }
143*defc7576SSatoshi Sahara        /*
144*defc7576SSatoshi Sahara         * Create diff object and the formatter
145*defc7576SSatoshi Sahara         */
146*defc7576SSatoshi Sahara        $diff = new \Diff(explode("\n", $l_text), explode("\n", $r_text));
147*defc7576SSatoshi Sahara
148*defc7576SSatoshi Sahara        if ($this->difftype == 'inline') {
149*defc7576SSatoshi Sahara            $diffformatter = new \InlineDiffFormatter();
150*defc7576SSatoshi Sahara        } else {
151*defc7576SSatoshi Sahara            $diffformatter = new \TableDiffFormatter();
152*defc7576SSatoshi Sahara        }
153*defc7576SSatoshi Sahara        /*
154*defc7576SSatoshi Sahara         * Display intro
155*defc7576SSatoshi Sahara         */
156*defc7576SSatoshi Sahara        if ($this->showIntro) print p_locale_xhtml('diff');
157*defc7576SSatoshi Sahara
158*defc7576SSatoshi Sahara        /*
159*defc7576SSatoshi Sahara         * Display type and exact reference
160*defc7576SSatoshi Sahara         */
161*defc7576SSatoshi Sahara        if (!$this->text) {
162*defc7576SSatoshi Sahara            print '<div class="diffoptions group">';
163*defc7576SSatoshi Sahara
164*defc7576SSatoshi Sahara            // create the form to select difftype
165*defc7576SSatoshi Sahara            $form = new Form(['action' => wl()]);
166*defc7576SSatoshi Sahara            $form->setHiddenField('id', $ID);
167*defc7576SSatoshi Sahara            $form->setHiddenField('rev2[0]', $l_rev);
168*defc7576SSatoshi Sahara            $form->setHiddenField('rev2[1]', $r_rev);
169*defc7576SSatoshi Sahara            $form->setHiddenField('do', 'diff');
170*defc7576SSatoshi Sahara            $options = array(
171*defc7576SSatoshi Sahara                         'sidebyside' => $lang['diff_side'],
172*defc7576SSatoshi Sahara                         'inline' => $lang['diff_inline']
173*defc7576SSatoshi Sahara            );
174*defc7576SSatoshi Sahara            $input = $form->addDropdown('difftype', $options, $lang['diff_type'])
175*defc7576SSatoshi Sahara                ->val($this->difftype)->addClass('quickselect');
176*defc7576SSatoshi Sahara            $input->useInput(false); // inhibit prefillInput() during toHTML() process
177*defc7576SSatoshi Sahara            $form->addButton('do[diff]', 'Go')->attr('type','submit');
178*defc7576SSatoshi Sahara            print $form->toHTML();
179*defc7576SSatoshi Sahara
180*defc7576SSatoshi Sahara            print '<p>';
181*defc7576SSatoshi Sahara            // link to exactly this view FS#2835
182*defc7576SSatoshi Sahara            print $this->diffViewlink('difflink', $l_rev, ($r_rev ?: $INFO['currentrev']));
183*defc7576SSatoshi Sahara            print '</p>';
184*defc7576SSatoshi Sahara
185*defc7576SSatoshi Sahara            print '</div>'; // .diffoptions
186*defc7576SSatoshi Sahara        }
187*defc7576SSatoshi Sahara
188*defc7576SSatoshi Sahara        /*
189*defc7576SSatoshi Sahara         * Display diff view table
190*defc7576SSatoshi Sahara         */
191*defc7576SSatoshi Sahara        print '<div class="table">';
192*defc7576SSatoshi Sahara        print '<table class="diff diff_'. $this->difftype .'">';
193*defc7576SSatoshi Sahara
194*defc7576SSatoshi Sahara        //navigation and header
195*defc7576SSatoshi Sahara        if ($this->difftype == 'inline') {
196*defc7576SSatoshi Sahara            if (!$this->text) {
197*defc7576SSatoshi Sahara                print '<tr>'
198*defc7576SSatoshi Sahara                    . '<td class="diff-lineheader">-</td>'
199*defc7576SSatoshi Sahara                    . '<td class="diffnav">'. $l_nav .'</td>'
200*defc7576SSatoshi Sahara                    . '</tr>';
201*defc7576SSatoshi Sahara                print '<tr>'
202*defc7576SSatoshi Sahara                    . '<th class="diff-lineheader">-</th>'
203*defc7576SSatoshi Sahara                    . '<th '. $l_minor .'>'. $l_head .'</th>'
204*defc7576SSatoshi Sahara                    .'</tr>';
205*defc7576SSatoshi Sahara            }
206*defc7576SSatoshi Sahara            print '<tr>'
207*defc7576SSatoshi Sahara                . '<td class="diff-lineheader">+</td>'
208*defc7576SSatoshi Sahara                . '<td class="diffnav">'. $r_nav .'</td>'
209*defc7576SSatoshi Sahara                .'</tr>';
210*defc7576SSatoshi Sahara            print '<tr>'
211*defc7576SSatoshi Sahara                . '<th class="diff-lineheader">+</th>'
212*defc7576SSatoshi Sahara                . '<th '. $r_minor .'>'. $r_head .'</th>'
213*defc7576SSatoshi Sahara                . '</tr>';
214*defc7576SSatoshi Sahara        } else {
215*defc7576SSatoshi Sahara            if (!$this->text) {
216*defc7576SSatoshi Sahara                print '<tr>'
217*defc7576SSatoshi Sahara                    . '<td colspan="2" class="diffnav">'. $l_nav .'</td>'
218*defc7576SSatoshi Sahara                    . '<td colspan="2" class="diffnav">'. $r_nav .'</td>'
219*defc7576SSatoshi Sahara                    . '</tr>';
220*defc7576SSatoshi Sahara            }
221*defc7576SSatoshi Sahara            print '<tr>'
222*defc7576SSatoshi Sahara                . '<th colspan="2" '. $l_minor .'>'. $l_head .'</th>'
223*defc7576SSatoshi Sahara                . '<th colspan="2" '. $r_minor .'>'. $r_head .'</th>'
224*defc7576SSatoshi Sahara                . '</tr>';
225*defc7576SSatoshi Sahara        }
226*defc7576SSatoshi Sahara
227*defc7576SSatoshi Sahara        //diff view
228*defc7576SSatoshi Sahara        print $this->insertSoftbreaks($diffformatter->format($diff));
229*defc7576SSatoshi Sahara
230*defc7576SSatoshi Sahara        print '</table>';
231*defc7576SSatoshi Sahara        print '</div>';
232*defc7576SSatoshi Sahara    }
233*defc7576SSatoshi Sahara
234*defc7576SSatoshi Sahara
235*defc7576SSatoshi Sahara    /**
236*defc7576SSatoshi Sahara     * Get header of diff HTML
237*defc7576SSatoshi Sahara     *
238*defc7576SSatoshi Sahara     * @param string $l_rev   Left revisions
239*defc7576SSatoshi Sahara     * @param string $r_rev   Right revision
240*defc7576SSatoshi Sahara     * @param string $id      Page id, if null $ID is used
241*defc7576SSatoshi Sahara     * @param bool   $media   If it is for media files
242*defc7576SSatoshi Sahara     * @param bool   $inline  Return the header on a single line
243*defc7576SSatoshi Sahara     * @return string[] HTML snippets for diff header
244*defc7576SSatoshi Sahara     */
245*defc7576SSatoshi Sahara    public function diffHead($l_rev, $r_rev, $id = null, $media = false, $inline = false)
246*defc7576SSatoshi Sahara    {
247*defc7576SSatoshi Sahara        global $lang;
248*defc7576SSatoshi Sahara        if ($id === null) {
249*defc7576SSatoshi Sahara            global $ID;
250*defc7576SSatoshi Sahara            $id = $ID;
251*defc7576SSatoshi Sahara        }
252*defc7576SSatoshi Sahara        $head_separator = $inline ? ' ' : '<br />';
253*defc7576SSatoshi Sahara        $media_or_wikiFN = $media ? 'mediaFN' : 'wikiFN';
254*defc7576SSatoshi Sahara        $ml_or_wl = $media ? 'ml' : 'wl';
255*defc7576SSatoshi Sahara        $l_minor = $r_minor = '';
256*defc7576SSatoshi Sahara
257*defc7576SSatoshi Sahara        if ($media) {
258*defc7576SSatoshi Sahara            $changelog = new MediaChangeLog($id);
259*defc7576SSatoshi Sahara        } else {
260*defc7576SSatoshi Sahara            $changelog = new PageChangeLog($id);
261*defc7576SSatoshi Sahara        }
262*defc7576SSatoshi Sahara        if (!$l_rev) {
263*defc7576SSatoshi Sahara            $l_head = '&mdash;';
264*defc7576SSatoshi Sahara        } else {
265*defc7576SSatoshi Sahara            $l_info   = $changelog->getRevisionInfo($l_rev);
266*defc7576SSatoshi Sahara            if ($l_info['user']) {
267*defc7576SSatoshi Sahara                $l_user = '<bdi>'.editorinfo($l_info['user']).'</bdi>';
268*defc7576SSatoshi Sahara                if (auth_ismanager()) $l_user .= ' <bdo dir="ltr">('.$l_info['ip'].')</bdo>';
269*defc7576SSatoshi Sahara            } else {
270*defc7576SSatoshi Sahara                $l_user = '<bdo dir="ltr">'.$l_info['ip'].'</bdo>';
271*defc7576SSatoshi Sahara            }
272*defc7576SSatoshi Sahara            $l_user  = '<span class="user">'.$l_user.'</span>';
273*defc7576SSatoshi Sahara            $l_sum   = ($l_info['sum']) ? '<span class="sum"><bdi>'.hsc($l_info['sum']).'</bdi></span>' : '';
274*defc7576SSatoshi Sahara            if ($l_info['type'] === DOKU_CHANGE_TYPE_MINOR_EDIT) $l_minor = 'class="minor"';
275*defc7576SSatoshi Sahara
276*defc7576SSatoshi Sahara            $l_head_title = ($media) ? dformat($l_rev) : $id.' ['.dformat($l_rev).']';
277*defc7576SSatoshi Sahara            $l_head = '<bdi><a class="wikilink1" href="'.$ml_or_wl($id,"rev=$l_rev").'">'
278*defc7576SSatoshi Sahara                . $l_head_title.'</a></bdi>'.$head_separator.$l_user.' '.$l_sum;
279*defc7576SSatoshi Sahara        }
280*defc7576SSatoshi Sahara
281*defc7576SSatoshi Sahara        if ($r_rev) {
282*defc7576SSatoshi Sahara            $r_info   = $changelog->getRevisionInfo($r_rev);
283*defc7576SSatoshi Sahara            if ($r_info['user']) {
284*defc7576SSatoshi Sahara                $r_user = '<bdi>'.editorinfo($r_info['user']).'</bdi>';
285*defc7576SSatoshi Sahara                if (auth_ismanager()) $r_user .= ' <bdo dir="ltr">('.$r_info['ip'].')</bdo>';
286*defc7576SSatoshi Sahara            } else {
287*defc7576SSatoshi Sahara                $r_user = '<bdo dir="ltr">'.$r_info['ip'].'</bdo>';
288*defc7576SSatoshi Sahara            }
289*defc7576SSatoshi Sahara            $r_user = '<span class="user">'.$r_user.'</span>';
290*defc7576SSatoshi Sahara            $r_sum  = ($r_info['sum']) ? '<span class="sum"><bdi>'.hsc($r_info['sum']).'</bdi></span>' : '';
291*defc7576SSatoshi Sahara            if ($r_info['type'] === DOKU_CHANGE_TYPE_MINOR_EDIT) $r_minor = 'class="minor"';
292*defc7576SSatoshi Sahara
293*defc7576SSatoshi Sahara            $r_head_title = ($media) ? dformat($r_rev) : $id.' ['.dformat($r_rev).']';
294*defc7576SSatoshi Sahara            $r_head = '<bdi><a class="wikilink1" href="'.$ml_or_wl($id,"rev=$r_rev").'">'
295*defc7576SSatoshi Sahara                . $r_head_title.'</a></bdi>'.$head_separator.$r_user.' '.$r_sum;
296*defc7576SSatoshi Sahara        } elseif ($_rev = @filemtime($media_or_wikiFN($id))) {
297*defc7576SSatoshi Sahara            $_info   = $changelog->getRevisionInfo($_rev);
298*defc7576SSatoshi Sahara            if ($_info['user']) {
299*defc7576SSatoshi Sahara                $_user = '<bdi>'.editorinfo($_info['user']).'</bdi>';
300*defc7576SSatoshi Sahara                if (auth_ismanager()) $_user .= ' <bdo dir="ltr">('.$_info['ip'].')</bdo>';
301*defc7576SSatoshi Sahara            } else {
302*defc7576SSatoshi Sahara                $_user = '<bdo dir="ltr">'.$_info['ip'].'</bdo>';
303*defc7576SSatoshi Sahara            }
304*defc7576SSatoshi Sahara            $_user = '<span class="user">'.$_user.'</span>';
305*defc7576SSatoshi Sahara            $_sum  = ($_info['sum']) ? '<span class="sum"><bdi>'.hsc($_info['sum']).'</span></bdi>' : '';
306*defc7576SSatoshi Sahara            if ($_info['type'] === DOKU_CHANGE_TYPE_MINOR_EDIT) $r_minor = 'class="minor"';
307*defc7576SSatoshi Sahara
308*defc7576SSatoshi Sahara            $r_head_title = ($media) ? dformat($_rev) : $id.' ['.dformat($_rev).']';
309*defc7576SSatoshi Sahara            $r_head  = '<bdi><a class="wikilink1" href="'.$ml_or_wl($id).'">'
310*defc7576SSatoshi Sahara                . $r_head_title.'</a></bdi> '.'('.$lang['current'].')'.$head_separator.$_user.' '.$_sum;
311*defc7576SSatoshi Sahara        }else{
312*defc7576SSatoshi Sahara            $r_head = '&mdash; ('.$lang['current'].')';
313*defc7576SSatoshi Sahara        }
314*defc7576SSatoshi Sahara
315*defc7576SSatoshi Sahara        return array($l_head, $r_head, $l_minor, $r_minor);
316*defc7576SSatoshi Sahara    }
317*defc7576SSatoshi Sahara
318*defc7576SSatoshi Sahara    /**
319*defc7576SSatoshi Sahara     * Create html for revision navigation
320*defc7576SSatoshi Sahara     *
321*defc7576SSatoshi Sahara     * @param PageChangeLog $pagelog changelog object of current page
322*defc7576SSatoshi Sahara     * @param int           $l_rev   left revision timestamp
323*defc7576SSatoshi Sahara     * @param int           $r_rev   right revision timestamp
324*defc7576SSatoshi Sahara     * @return string[] html of left and right navigation elements
325*defc7576SSatoshi Sahara     */
326*defc7576SSatoshi Sahara    protected function diffNavigation($pagelog, $l_rev, $r_rev)
327*defc7576SSatoshi Sahara    {
328*defc7576SSatoshi Sahara        global $INFO, $ID;
329*defc7576SSatoshi Sahara
330*defc7576SSatoshi Sahara        // last timestamp is not in changelog, retrieve timestamp from metadata
331*defc7576SSatoshi Sahara        // note: when page is removed, the metadata timestamp is zero
332*defc7576SSatoshi Sahara        if (!$r_rev) {
333*defc7576SSatoshi Sahara            if (isset($INFO['meta']['last_change']['date'])) {
334*defc7576SSatoshi Sahara                $r_rev = $INFO['meta']['last_change']['date'];
335*defc7576SSatoshi Sahara            } else {
336*defc7576SSatoshi Sahara                $r_rev = 0;
337*defc7576SSatoshi Sahara            }
338*defc7576SSatoshi Sahara        }
339*defc7576SSatoshi Sahara
340*defc7576SSatoshi Sahara        //retrieve revisions with additional info
341*defc7576SSatoshi Sahara        list($l_revs, $r_revs) = $pagelog->getRevisionsAround($l_rev, $r_rev);
342*defc7576SSatoshi Sahara        $l_revisions = array();
343*defc7576SSatoshi Sahara        if (!$l_rev) {
344*defc7576SSatoshi Sahara            //no left revision given, add dummy
345*defc7576SSatoshi Sahara            $l_revisions[0]= array('label' => '', 'attrs' => []);
346*defc7576SSatoshi Sahara        }
347*defc7576SSatoshi Sahara        foreach ($l_revs as $rev) {
348*defc7576SSatoshi Sahara            $info = $pagelog->getRevisionInfo($rev);
349*defc7576SSatoshi Sahara            $l_revisions[$rev] = array(
350*defc7576SSatoshi Sahara                'label' => dformat($info['date']) .' '. editorinfo($info['user'], true) .' '. $info['sum'],
351*defc7576SSatoshi Sahara                'attrs' => ['title' => $rev],
352*defc7576SSatoshi Sahara            );
353*defc7576SSatoshi Sahara            if ($r_rev ? $rev >= $r_rev : false) $l_revisions[$rev]['attrs']['disabled'] = 'disabled';
354*defc7576SSatoshi Sahara        }
355*defc7576SSatoshi Sahara        $r_revisions = array();
356*defc7576SSatoshi Sahara        if (!$r_rev) {
357*defc7576SSatoshi Sahara            //no right revision given, add dummy
358*defc7576SSatoshi Sahara            $r_revisions[0] = array('label' => '', 'attrs' => []);
359*defc7576SSatoshi Sahara        }
360*defc7576SSatoshi Sahara        foreach ($r_revs as $rev) {
361*defc7576SSatoshi Sahara            $info = $pagelog->getRevisionInfo($rev);
362*defc7576SSatoshi Sahara            $r_revisions[$rev] = array(
363*defc7576SSatoshi Sahara                'label' => dformat($info['date']) .' '. editorinfo($info['user'], true) .' '. $info['sum'],
364*defc7576SSatoshi Sahara                'attrs' => ['title' => $rev],
365*defc7576SSatoshi Sahara            );
366*defc7576SSatoshi Sahara            if ($rev <= $l_rev) $r_revisions[$rev]['attrs']['disabled'] = 'disabled';
367*defc7576SSatoshi Sahara        }
368*defc7576SSatoshi Sahara
369*defc7576SSatoshi Sahara        //determine previous/next revisions
370*defc7576SSatoshi Sahara        $l_index = array_search($l_rev, $l_revs);
371*defc7576SSatoshi Sahara        $l_prev = $l_revs[$l_index + 1];
372*defc7576SSatoshi Sahara        $l_next = $l_revs[$l_index - 1];
373*defc7576SSatoshi Sahara        if ($r_rev) {
374*defc7576SSatoshi Sahara            $r_index = array_search($r_rev, $r_revs);
375*defc7576SSatoshi Sahara            $r_prev = $r_revs[$r_index + 1];
376*defc7576SSatoshi Sahara            $r_next = $r_revs[$r_index - 1];
377*defc7576SSatoshi Sahara        } else {
378*defc7576SSatoshi Sahara            //removed page
379*defc7576SSatoshi Sahara            if ($l_next) {
380*defc7576SSatoshi Sahara                $r_prev = $r_revs[0];
381*defc7576SSatoshi Sahara            } else {
382*defc7576SSatoshi Sahara                $r_prev = null;
383*defc7576SSatoshi Sahara            }
384*defc7576SSatoshi Sahara            $r_next = null;
385*defc7576SSatoshi Sahara        }
386*defc7576SSatoshi Sahara
387*defc7576SSatoshi Sahara        /*
388*defc7576SSatoshi Sahara         * Left side:
389*defc7576SSatoshi Sahara         */
390*defc7576SSatoshi Sahara        $l_nav = '';
391*defc7576SSatoshi Sahara        //move back
392*defc7576SSatoshi Sahara        if ($l_prev) {
393*defc7576SSatoshi Sahara            $l_nav .= $this->diffViewlink('diffbothprevrev', $l_prev, $r_prev);
394*defc7576SSatoshi Sahara            $l_nav .= $this->diffViewlink('diffprevrev', $l_prev, $r_rev);
395*defc7576SSatoshi Sahara        }
396*defc7576SSatoshi Sahara        //dropdown
397*defc7576SSatoshi Sahara        $form = new Form(['action' => wl()]);
398*defc7576SSatoshi Sahara        $form->setHiddenField('id', $ID);
399*defc7576SSatoshi Sahara        $form->setHiddenField('difftype', $this->difftype);
400*defc7576SSatoshi Sahara        $form->setHiddenField('rev2[1]', $r_rev);
401*defc7576SSatoshi Sahara        $form->setHiddenField('do', 'diff');
402*defc7576SSatoshi Sahara        $input = $form->addDropdown('rev2[0]', $l_revisions)->val($l_rev)->addClass('quickselect');
403*defc7576SSatoshi Sahara        $input->useInput(false); // inhibit prefillInput() during toHTML() process
404*defc7576SSatoshi Sahara        $form->addButton('do[diff]', 'Go')->attr('type','submit');
405*defc7576SSatoshi Sahara        $l_nav .= $form->toHTML();
406*defc7576SSatoshi Sahara        //move forward
407*defc7576SSatoshi Sahara        if ($l_next && ($l_next < $r_rev || !$r_rev)) {
408*defc7576SSatoshi Sahara            $l_nav .= $this->diffViewlink('diffnextrev', $l_next, $r_rev);
409*defc7576SSatoshi Sahara        }
410*defc7576SSatoshi Sahara
411*defc7576SSatoshi Sahara        /*
412*defc7576SSatoshi Sahara         * Right side:
413*defc7576SSatoshi Sahara         */
414*defc7576SSatoshi Sahara        $r_nav = '';
415*defc7576SSatoshi Sahara        //move back
416*defc7576SSatoshi Sahara        if ($l_rev < $r_prev) {
417*defc7576SSatoshi Sahara            $r_nav .= $this->diffViewlink('diffprevrev', $l_rev, $r_prev);
418*defc7576SSatoshi Sahara        }
419*defc7576SSatoshi Sahara        //dropdown
420*defc7576SSatoshi Sahara        $form = new Form(['action' => wl()]);
421*defc7576SSatoshi Sahara        $form->setHiddenField('id', $ID);
422*defc7576SSatoshi Sahara        $form->setHiddenField('rev2[0]', $l_rev);
423*defc7576SSatoshi Sahara        $form->setHiddenField('difftype', $this->difftype);
424*defc7576SSatoshi Sahara        $form->setHiddenField('do', 'diff');
425*defc7576SSatoshi Sahara        $input = $form->addDropdown('rev2[1]', $r_revisions)->val($r_rev)->addClass('quickselect');
426*defc7576SSatoshi Sahara        $input->useInput(false); // inhibit prefillInput() during toHTML() process
427*defc7576SSatoshi Sahara        $form->addButton('do[diff]', 'Go')->attr('type','submit');
428*defc7576SSatoshi Sahara        $r_nav .= $form->toHTML();
429*defc7576SSatoshi Sahara        //move forward
430*defc7576SSatoshi Sahara        if ($r_next) {
431*defc7576SSatoshi Sahara            if ($pagelog->isCurrentRevision($r_next)) {
432*defc7576SSatoshi Sahara                //last revision is diff with current page
433*defc7576SSatoshi Sahara                $r_nav .= $this->diffViewlink('difflastrev', $l_rev);
434*defc7576SSatoshi Sahara            } else {
435*defc7576SSatoshi Sahara                $r_nav .= $this->diffViewlink('diffnextrev', $l_rev, $r_next);
436*defc7576SSatoshi Sahara            }
437*defc7576SSatoshi Sahara        } else {
438*defc7576SSatoshi Sahara            $r_nav .= $this->diffViewlink('diffbothnextrev', $l_next, $r_next);
439*defc7576SSatoshi Sahara        }
440*defc7576SSatoshi Sahara        return array($l_nav, $r_nav);
441*defc7576SSatoshi Sahara    }
442*defc7576SSatoshi Sahara
443*defc7576SSatoshi Sahara    /**
444*defc7576SSatoshi Sahara     * Create html link to a diff view defined by two revisions
445*defc7576SSatoshi Sahara     *
446*defc7576SSatoshi Sahara     * @param string $linktype
447*defc7576SSatoshi Sahara     * @param int $lrev oldest revision
448*defc7576SSatoshi Sahara     * @param int $rrev newest revision or null for diff with current revision
449*defc7576SSatoshi Sahara     * @return string html of link to a diff view
450*defc7576SSatoshi Sahara     */
451*defc7576SSatoshi Sahara    protected function diffViewlink($linktype, $lrev, $rrev = null)
452*defc7576SSatoshi Sahara    {
453*defc7576SSatoshi Sahara        global $ID, $lang;
454*defc7576SSatoshi Sahara        if ($rrev === null) {
455*defc7576SSatoshi Sahara            $urlparam = array(
456*defc7576SSatoshi Sahara                'do' => 'diff',
457*defc7576SSatoshi Sahara                'rev' => $lrev,
458*defc7576SSatoshi Sahara                'difftype' => $this->difftype,
459*defc7576SSatoshi Sahara            );
460*defc7576SSatoshi Sahara        } else {
461*defc7576SSatoshi Sahara            $urlparam = array(
462*defc7576SSatoshi Sahara                'do' => 'diff',
463*defc7576SSatoshi Sahara                'rev2[0]' => $lrev,
464*defc7576SSatoshi Sahara                'rev2[1]' => $rrev,
465*defc7576SSatoshi Sahara                'difftype' => $this->difftype,
466*defc7576SSatoshi Sahara            );
467*defc7576SSatoshi Sahara        }
468*defc7576SSatoshi Sahara        return  '<a class="'. $linktype .'" href="'. wl($ID, $urlparam) .'" title="'. $lang[$linktype] .'">'
469*defc7576SSatoshi Sahara              . '<span>'. $lang[$linktype] .'</span>'
470*defc7576SSatoshi Sahara              . '</a>';
471*defc7576SSatoshi Sahara    }
472*defc7576SSatoshi Sahara
473*defc7576SSatoshi Sahara
474*defc7576SSatoshi Sahara    /**
475*defc7576SSatoshi Sahara     * Insert soft breaks in diff html
476*defc7576SSatoshi Sahara     *
477*defc7576SSatoshi Sahara     * @param string $diffhtml
478*defc7576SSatoshi Sahara     * @return string
479*defc7576SSatoshi Sahara     */
480*defc7576SSatoshi Sahara    public function insertSoftbreaks($diffhtml)
481*defc7576SSatoshi Sahara    {
482*defc7576SSatoshi Sahara        // search the diff html string for both:
483*defc7576SSatoshi Sahara        // - html tags, so these can be ignored
484*defc7576SSatoshi Sahara        // - long strings of characters without breaking characters
485*defc7576SSatoshi Sahara        return preg_replace_callback('/<[^>]*>|[^<> ]{12,}/', function ($match) {
486*defc7576SSatoshi Sahara            // if match is an html tag, return it intact
487*defc7576SSatoshi Sahara            if ($match[0][0] == '<') return $match[0];
488*defc7576SSatoshi Sahara            // its a long string without a breaking character,
489*defc7576SSatoshi Sahara            // make certain characters into breaking characters by inserting a
490*defc7576SSatoshi Sahara            // word break opportunity (<wbr> tag) in front of them.
491*defc7576SSatoshi Sahara            $regex = <<< REGEX
492*defc7576SSatoshi Sahara(?(?=              # start a conditional expression with a positive look ahead ...
493*defc7576SSatoshi Sahara&\#?\\w{1,6};)     # ... for html entities - we don't want to split them (ok to catch some invalid combinations)
494*defc7576SSatoshi Sahara&\#?\\w{1,6};      # yes pattern - a quicker match for the html entity, since we know we have one
495*defc7576SSatoshi Sahara|
496*defc7576SSatoshi Sahara[?/,&\#;:]         # no pattern - any other group of 'special' characters to insert a breaking character after
497*defc7576SSatoshi Sahara)+                 # end conditional expression
498*defc7576SSatoshi SaharaREGEX;
499*defc7576SSatoshi Sahara            return preg_replace('<'.$regex.'>xu', '\0<wbr>', $match[0]);
500*defc7576SSatoshi Sahara        }, $diffhtml);
501*defc7576SSatoshi Sahara    }
502*defc7576SSatoshi Sahara
503*defc7576SSatoshi Sahara}
504