xref: /dokuwiki/inc/Ui/Diff.php (revision 179b4660137f502a186abc8ff59180efd8707d10)
1<?php
2
3namespace dokuwiki\Ui;
4
5/**
6 * DokuWiki Diff Interface
7 * parent class of PageDiff and MediaDiff
8 *
9 * @package dokuwiki\Ui
10 */
11abstract class Diff extends Ui
12{
13    /* @var string */
14    protected $id;   // page id or media id
15    protected $item; // page or media
16
17    /* @var int */
18    protected $old_rev;  // older revision, timestamp of left side
19    protected $new_rev;  // newer revision, timestamp of right side
20    protected $last_rev; // current revision, or last revision when it had removed
21
22    /* @var array */
23    protected $preference = [];
24
25    /* @var ChangeLog */
26    protected $changelog; // PageChangeLog or MediaChangeLog object
27
28    /**
29     * Diff Ui constructor
30     *
31     * @param string $id  page id or media id
32     */
33    public function __construct($id)
34    {
35        $this->id = $id;
36        $this->setChangeLog();
37    }
38
39    /**
40     * set class property changelog
41     */
42    abstract protected function setChangeLog();
43
44    /**
45     * Set a pair of revisions to be compared
46     *
47     * @param int $old_rev
48     * @param int $new_rev
49     * @return $this
50     */
51    public function compare($old_rev, $new_rev)
52    {
53        $this->old_rev = $old_rev;
54        $this->new_rev = $new_rev;
55        return $this;
56    }
57
58    /**
59     * Gets or Sets preference of the Ui\Diff object
60     *
61     * @param string|array $prefs  a key name or key-value pair(s)
62     * @param mixed $value         value used when the first args is string
63     * @return array|$this
64     */
65    public function preference($prefs = null, $value = null)
66    {
67        // set
68        if (is_string($prefs) && isset($value)) {
69            $this->preference[$prefs] = $value;
70            return $this;
71        } elseif (is_array($prefs)) {
72            foreach ($prefs as $name => $value) {
73                $this->preference[$name] = $value;
74            }
75            return $this;
76        }
77        // get
78        return $this->preference;
79    }
80
81    /**
82     * Retrieve requested revision(s) and difftype from Ui\Revisions
83     *
84     * @return void
85     */
86    protected function preProcess()
87    {
88        global $INPUT;
89
90        // difflink icon click, eg. ?rev=123456789&do=diff
91        if ($INPUT->has('rev')) {
92            $this->old_rev = $INPUT->int('rev');
93            $this->new_rev = ''; // current revision
94        }
95
96        // submit button with two checked boxes
97        $rev2 = $INPUT->arr('rev2', []);
98        if (count($rev2) > 1) {
99            if ($rev2[0] == 'current') {
100                [$this->old_rev, $this->new_rev] = [$rev2[1], ''];
101            } elseif ($rev2[1] == 'current') {
102                [$this->old_rev, $this->new_rev] = [$rev2[0], ''];
103            } elseif ($rev2[0] < $rev2[1]) {
104                [$this->old_rev, $this->new_rev] = [$rev2[0], $rev2[1]];
105            } else {
106                [$this->old_rev, $this->new_rev] = [$rev2[1], $rev2[0]];
107            }
108        }
109
110        // diff view type
111        if ($INPUT->has('difftype')) {
112            // retrieve requested $difftype
113            $this->preference['difftype'] = $INPUT->str('difftype');
114        } else {
115            // read preference from DokuWiki cookie. PageDiff only
116            get_doku_pref('difftype', $mode);
117            if (isset($mode)) $this->preference['difftype'] = $mode;
118        }
119    }
120
121    /**
122     * get extended revision info
123     *
124     * @param int|string $rev  revision identifier, '' means current one
125     * @return array  revision info structure of a page or media file
126     */
127    protected function getExtendedRevisionInfo($rev)
128    {
129        $changelog =& $this->changelog;
130
131        if ($rev) {
132            $info = $changelog->getRevisionInfo($rev);
133        } elseif (file_exists($filename = $this->itemFN($this->id))) {
134            $rev = filemtime(fullpath($filename));
135            $info = $changelog->getRevisionInfo($rev) + array(
136                'current' => true,
137            );
138        } else { // once exists, but now removed
139            $info = array(
140                'current' => true,
141            );
142        }
143        return array('item' => $this->item) + $info;
144    }
145
146
147
148    /**
149     * Build header of diff HTML
150     *
151     * @param string $l_rev   Left revisions
152     * @param string $r_rev   Right revision
153     * @return string[] HTML snippets for diff header
154     */
155    public function buildDiffHead($l_rev, $r_rev)
156    {
157        global $lang;
158
159        $changelog =& $this->changelog;
160
161        switch ($this->item) {
162            case 'page':
163                $isMedia = false;
164                $ui = new PageRevisions($this->id);
165                break;
166            case 'media':
167                $isMedia = true;
168                $ui = new MediaRevisions($this->id);
169                break;
170        }
171
172        $head_separator = ($this->preference['difftype'] === 'inline') ? ' ' : '<br />';
173
174        // assign minor edit checker to the variable
175        $isMinorEdit = function ($info) {
176            return ($info['type'] === DOKU_CHANGE_TYPE_MINOR_EDIT);
177        };
178
179        // assign title builder to the variable
180        $itemTitle = function ($id, $rev = '') use ($isMedia) {
181            return ($isMedia) ? dformat($rev) : $id.' ['.dformat($rev).']';
182        };
183
184        // left side
185        if (!$l_rev) {
186            $l_minor = '';
187            $l_head = '&mdash;';
188        } else {
189            $info = $changelog->getRevisionInfo($l_rev);
190            $objRevInfo = $ui->getObjRevInfo($info);
191            $l_minor = $isMinorEdit($info) ? ' class="minor"' : '';
192            $l_head = '<bdi>'
193                    .'<a class="wikilink1" href="'.$this->itemUrl($this->id, "rev=$l_rev").'">'
194                    .$itemTitle($this->id, $l_rev).'</a></bdi>'.$head_separator
195                    .$objRevInfo->editor().' '.$objRevInfo->editSummary();
196        }
197
198        // right side
199        if ($r_rev) {
200            $info  = $changelog->getRevisionInfo($r_rev);
201            $objRevInfo = $ui->getObjRevInfo($info);
202            $r_minor = $isMinorEdit($info) ? ' class="minor"' : '';
203            $r_head = '<bdi>'
204                    .'<a class="wikilink1" href="'.$this->itemUrl($this->id, "rev=$r_rev").'">'
205                    .$itemTitle($this->id, $r_rev).'</a></bdi>'.$head_separator
206                    .$objRevInfo->editor().' '.$objRevInfo->editSummary();
207        } elseif ($this->last_rev) {
208            $_rev = $this->last_rev;
209            $info = $changelog->getRevisionInfo($_rev);
210            $objRevInfo = $ui->getObjRevInfo($info);
211            $r_minor = $isMinorEdit($info) ? ' class="minor"' : '';
212            $r_head  = '<bdi>'
213                     .'<a class="wikilink1" href="'.$this->itemUrl($this->id).'">'
214                     .$itemTitle($this->id, $_rev).'</a></bdi> '.'('.$lang['current'].')'.$head_separator
215                     .$objRevInfo->editor().' '.$objRevInfo->editSummary();
216        } else {
217            $r_minor = '';
218            $r_head = '&mdash; ('.$lang['current'].')';
219        }
220
221        return array($l_head, $r_head, $l_minor, $r_minor);
222    }
223
224    /**
225     * item url generator
226     *
227     * @param string $id  page id or media id
228     * @param string|array $urlParameters  URL parameters, associative array recommended
229     * @return string
230     */
231    protected function itemUrl($id, $urlParameters = '')
232    {
233        switch ($this->item) {
234            case 'page':  return wl($id, $urlParameters, $absolute = false, '&');
235            case 'media': return ml($id, $urlParameters, $direct = true, '&', $absolute = false);
236        }
237    }
238
239    /**
240     * item filename resolver
241     *
242     * @param string $id  page id or media id
243     * @param string|int $rev empty string or revision timestamp
244     * @return string
245     */
246    protected function itemFN($id, $rev = '')
247    {
248        switch ($this->item) {
249            case 'page':  return wikiFN($id, $rev);
250            case 'media': return mediaFN($id, $rev);
251        }
252    }
253
254}
255