xref: /dokuwiki/inc/Ui/MediaDiff.php (revision 648769227534c0a8fa99ff0d08fc5eb614600681)
1<?php
2
3namespace dokuwiki\Ui;
4
5use dokuwiki\ChangeLog\MediaChangeLog;
6use dokuwiki\Ui\MediaRevisions;
7use dokuwiki\Extension\Event;
8use dokuwiki\Form\Form;
9use JpegMeta;
10
11/**
12 * DokuWiki MediaDiff Interface
13 *
14 * @package dokuwiki\Ui
15 */
16class MediaDiff extends Diff
17{
18    /* @var MediaChangeLog */
19    protected $changelog;
20
21    /**
22     * MediaDiff Ui constructor
23     *
24     * @param string $id  media id
25     */
26    public function __construct($id)
27    {
28        if (!isset($id)) {
29            throw new \InvalidArgumentException('media id should not be empty!');
30        }
31
32        // init preference
33        $this->preference['fromAjax'] = false; // see doluwiki\Ajax::callMediadiff()
34        $this->preference['showIntro'] = false;
35        $this->preference['difftype'] = 'both';  // media diff view type: both, opacity or portions
36
37        parent::__construct($id);
38    }
39
40    /** @inheritdoc */
41    protected function setChangeLog()
42    {
43        $this->changelog = new MediaChangeLog($this->id);
44    }
45
46    /** @inheritdoc */
47    protected function preProcess()
48    {
49        parent::preProcess();
50        if (!isset($this->oldRev, $this->newRev)) {
51            // no revision was given, compare previous to current
52            $changelog =& $this->changelog;
53            $this->oldRev = $changelog->getRevisions(0, 1)[0];
54            $this->newRev = $changelog->currentRevision();
55        }
56    }
57
58    /**
59     * Shows difference between two revisions of media
60     *
61     * @author Kate Arzamastseva <pshns@ukr.net>
62     */
63    public function show()
64    {
65        global $conf;
66        $changelog =& $this->changelog;
67
68        $ns = getNS($this->id);
69        $auth = auth_quickaclcheck("$ns:*");
70
71        if ($auth < AUTH_READ || !$this->id || !$conf['mediarevisions']) return '';
72
73       // determine left and right revision
74        if (!isset($this->oldRev, $this->newRev)) $this->preProcess();
75
76        // use timestamp and '' properly as $rev for the current file
77        if ($changelog->isCurrentRevision($this->newRev)) {
78            [$oldRev, $newRev] = [$this->oldRev, ''];
79        } else {
80            [$oldRev, $newRev] = [$this->oldRev, $this->newRev];
81        }
82
83        $oldRevMeta = new JpegMeta(mediaFN($this->id, $oldRev));
84        $newRevMeta = new JpegMeta(mediaFN($this->id, $newRev));
85
86        $is_img = preg_match('/\.(jpe?g|gif|png)$/', $this->id);
87        if ($is_img) {
88            // get image width and height for the mediamanager preview panel
89            $oldRevSize = media_image_preview_size($this->id, $oldRev, $oldRevMeta);
90            $newRevSize = media_image_preview_size($this->id, $newRev, $newRevMeta);
91            // re-check image, ensure minimum image width for showImageDiff()
92            $is_img = ($oldRevSize && $newRevSize && ($oldRevSize[0] >= 30 || $newRevSize[0] >= 30));
93        }
94
95        // determine requested diff view type
96        if (!$is_img) {
97            $this->preference['difftype'] = 'both';
98        }
99
100        // display intro
101        if ($this->preference['showIntro']) echo p_locale_xhtml('diff');
102
103        // print form to choose diff view type
104        if ($is_img && !$this->preference['fromAjax']) {
105            $this->showDiffViewSelector();
106            echo '<div id="mediamanager__diff" >';
107        }
108
109        switch ($this->preference['difftype']) {
110            case 'opacity':
111            case 'portions':
112                $this->showImageDiff($oldRev, $newRev, $oldRevSize, $newRevSize);
113                break;
114            case 'both':
115            default:
116                $this->showFileDiff($oldRev, $newRev, $oldRevMeta, $newRevMeta, $auth);
117                break;
118        }
119
120        if ($is_img && !$this->preference['fromAjax']) {
121            echo '</div>';
122        }
123    }
124
125    /**
126     * Print form to choose diff view type
127     * the dropdown is to be added through JavaScript, see lib/scripts/media.js
128     */
129    protected function showDiffViewSelector()
130    {
131        echo '<div class="diffoptions group">';
132
133        $form = new Form([
134            'id' => 'mediamanager__form_diffview',
135            'action' => media_managerURL([], '&'),
136            'method' => 'get',
137            'class' => 'diffView',
138        ]);
139        $form->addTagOpen('div')->addClass('no');
140        $form->setHiddenField('sectok', null);
141        $form->setHiddenField('mediado', 'diff');
142        $form->setHiddenField('rev2[0]', $this->oldRev);
143        $form->setHiddenField('rev2[1]', $this->newRev);
144        $form->addTagClose('div');
145        echo $form->toHTML();
146
147        echo '</div>'; // .diffoptions
148    }
149
150    /**
151     * Prints two images side by side
152     * and slider
153     *
154     * @author Kate Arzamastseva <pshns@ukr.net>
155     *
156     * @param string|int $oldRev revision timestamp, or empty string
157     * @param string|int $newRev revision timestamp, or empty string
158     * @param array  $oldRevSize  array with width and height
159     * @param array  $newRevSize  array with width and height
160     * @param string $type    diff view type: opacity or portions
161     */
162    protected function showImageDiff($oldRev, $newRev, $oldRevSize, $newRevSize, $type = null)
163    {
164        if (!isset($type)) {
165            $type = $this->preference['difftype'];
166        }
167
168        // adjust image width, right side (newer) has priority
169        if ($oldRevSize != $newRevSize) {
170            if ($newRevSize[0] > $oldRevSize[0]) {
171                $oldRevSize = $newRevSize;
172            }
173        }
174
175        $oldRevSrc = ml($this->id, ['rev' => $oldRev, 'h' => $oldRevSize[1], 'w' => $oldRevSize[0]]);
176        $newRevSrc = ml($this->id, ['rev' => $newRev, 'h' => $oldRevSize[1], 'w' => $oldRevSize[0]]);
177
178        // slider
179        echo '<div class="slider" style="max-width: '.($oldRevSize[0]-20).'px;" ></div>';
180
181        // two images in divs
182        echo '<div class="imageDiff '.$type.'">';
183        echo '<div class="image1" style="max-width: '.$oldRevSize[0].'px;">';
184        echo '<img src="'.$oldRevSrc.'" alt="" />';
185        echo '</div>';
186        echo '<div class="image2" style="max-width: '.$oldRevSize[0].'px;">';
187        echo '<img src="'.$newRevSrc.'" alt="" />';
188        echo '</div>';
189        echo '</div>';
190    }
191
192    /**
193     * Shows difference between two revisions of media file
194     *
195     * @author Kate Arzamastseva <pshns@ukr.net>
196     *
197     * @param string|int $oldRev revision timestamp, or empty string
198     * @param string|int $newRev revision timestamp, or empty string
199     * @param JpegMeta $oldRevMeta
200     * @param JpegMeta $newRevMeta
201     * @param int $auth permission level
202     */
203    protected function showFileDiff($oldRev, $newRev, $oldRevMeta, $newRevMeta, $auth)
204    {
205        global $lang;
206        $changelog =& $this->changelog;
207
208        // revison info of older file (left side)
209        $oldRevInfo = $changelog->getRevisionInfo($this->oldRev);
210        // revison info of newer file (right side)
211        $newRevInfo = $changelog->getRevisionInfo($this->newRev);
212
213        // display diff view table
214        echo '<div class="table">';
215        echo '<table>';
216        echo '<tr>';
217        echo '<th>'. $this->revisionTitle($oldRevInfo) .'</th>';
218        echo '<th>'. $this->revisionTitle($newRevInfo) .'</th>';
219        echo '</tr>';
220
221        echo '<tr class="image">';
222        echo '<td>';
223        media_preview($this->id, $auth, $oldRev, $oldRevMeta); // $auth not used in media_preview()?
224        echo '</td>';
225
226        echo '<td>';
227        media_preview($this->id, $auth, $newRev, $newRevMeta);
228        echo '</td>';
229        echo '</tr>';
230
231        echo '<tr class="actions">';
232        echo '<td>';
233        media_preview_buttons($this->id, $auth, $oldRev); // $auth used in media_preview_buttons()
234        echo '</td>';
235
236        echo '<td>';
237        media_preview_buttons($this->id, $auth, $newRev);
238        echo '</td>';
239        echo '</tr>';
240
241        $l_tags = media_file_tags($oldRevMeta);
242        $r_tags = media_file_tags($newRevMeta);
243        // FIXME r_tags-only stuff
244        foreach ($l_tags as $key => $l_tag) {
245            if ($l_tag['value'] != $r_tags[$key]['value']) {
246                $r_tags[$key]['highlighted'] = true;
247                $l_tags[$key]['highlighted'] = true;
248            } elseif (!$l_tag['value'] || !$r_tags[$key]['value']) {
249                unset($r_tags[$key]);
250                unset($l_tags[$key]);
251            }
252        }
253
254        echo '<tr>';
255        foreach (array($l_tags, $r_tags) as $tags) {
256            echo '<td>';
257
258            echo '<dl class="img_tags">';
259            foreach ($tags as $tag) {
260                $value = cleanText($tag['value']);
261                if (!$value) $value = '-';
262                echo '<dt>'.$lang[$tag['tag'][1]].'</dt>';
263                echo '<dd>';
264                if ($tag['highlighted']) echo '<strong>';
265                if ($tag['tag'][2] == 'date') {
266                    echo dformat($value);
267                } else {
268                    echo hsc($value);
269                }
270                if ($tag['highlighted']) echo '</strong>';
271                echo '</dd>';
272            }
273            echo '</dl>';
274
275            echo '</td>';
276        }
277        echo '</tr>';
278
279        echo '</table>';
280        echo '</div>';
281    }
282
283    /**
284     * Revision Title for MediaDiff table headline
285     *
286     * @param array $info  Revision info structure of a media file
287     * @return string
288     */
289    protected function revisionTitle(array $info)
290    {
291        global $lang, $INFO;
292
293        if (isset($info['date'])) {
294            $rev = $info['date'];
295            $title = '<bdi><a class="wikilink1" href="'.ml($this->id, ['rev' => $rev]).'">'
296                   . dformat($rev).'</a></bdi>';
297        } else {
298            $rev = false;
299            $title = '&mdash;';
300        }
301        if (isset($info['current']) || ($rev && $rev == $INFO['currentrev'])) {
302            $title .= '&nbsp;('.$lang['current'].')';
303        }
304
305        // append separator
306        $title .= ($this->preference['difftype'] === 'inline') ? ' ' : '<br />';
307
308        // supplement
309        if (isset($info['date'])) {
310            $objRevInfo = (new MediaRevisions($this->id))->getObjRevInfo($info);
311            $title .= $objRevInfo->editSummary().' '.$objRevInfo->editor();
312        }
313        return $title;
314    }
315
316}
317