xref: /dokuwiki/inc/Ui/MediaDiff.php (revision 90c7493e5431e6bb1956245e233ab58734b59b05)
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        $this->item = 'media';
32
33        // init preference
34        $this->preference['fromAjax'] = false; // see doluwiki\Ajax::callMediadiff()
35        $this->preference['showIntro'] = false;
36        $this->preference['difftype'] = 'both';  // media diff view type: both, opacity or portions
37
38        parent::__construct($id);
39    }
40
41    /** @inheritdoc */
42    protected function setChangeLog()
43    {
44        $this->changelog = new MediaChangeLog($this->id);
45    }
46
47    /** @inheritdoc */
48    protected function itemFN($id, $rev = '')
49    {
50        return mediaFN($id, $rev);
51    }
52
53    /** @inheritdoc */
54    protected function preProcess()
55    {
56        parent::preProcess();
57        if (!isset($this->oldRev, $this->newRev)) {
58            // no revision was given, compare previous to current
59            $revs = $this->changelog->getRevisions(0, 1);
60            $this->oldRev = file_exists(mediaFN($this->id, $revs[0])) ? $revs[0] : '';
61            $this->newRev = '';
62        }
63    }
64
65    /**
66     * Shows difference between two revisions of media
67     *
68     * @author Kate Arzamastseva <pshns@ukr.net>
69     */
70    public function show()
71    {
72        global $conf;
73
74        $ns = getNS($this->id);
75        $auth = auth_quickaclcheck("$ns:*");
76
77        if ($auth < AUTH_READ || !$this->id || !$conf['mediarevisions']) return '';
78
79       // determine left and right revision
80        if (!isset($this->oldRev, $this->newRev)) $this->preProcess();
81        [$oldRev, $newRev] = [$this->oldRev, $this->newRev];
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 ?: 'current');
143        $form->setHiddenField('rev2[1]', $this->newRev ?: 'current');
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
207        // revison info of older file (left side)
208        $oldRevInfo = $this->getExtendedRevisionInfo($oldRev);
209        // revison info of newer file (right side)
210        $newRevInfo = $this->getExtendedRevisionInfo($newRev);
211
212        // display diff view table
213        echo '<div class="table">';
214        echo '<table>';
215        echo '<tr>';
216        echo '<th>'. $this->revisionTitle($oldRevInfo) .'</th>';
217        echo '<th>'. $this->revisionTitle($newRevInfo) .'</th>';
218        echo '</tr>';
219
220        echo '<tr class="image">';
221        echo '<td>';
222        media_preview($this->id, $auth, $oldRev, $oldRevMeta); // $auth not used in media_preview()?
223        echo '</td>';
224
225        echo '<td>';
226        media_preview($this->id, $auth, $newRev, $newRevMeta);
227        echo '</td>';
228        echo '</tr>';
229
230        echo '<tr class="actions">';
231        echo '<td>';
232        media_preview_buttons($this->id, $auth, $oldRev); // $auth used in media_preview_buttons()
233        echo '</td>';
234
235        echo '<td>';
236        media_preview_buttons($this->id, $auth, $newRev);
237        echo '</td>';
238        echo '</tr>';
239
240        $l_tags = media_file_tags($oldRevMeta);
241        $r_tags = media_file_tags($newRevMeta);
242        // FIXME r_tags-only stuff
243        foreach ($l_tags as $key => $l_tag) {
244            if ($l_tag['value'] != $r_tags[$key]['value']) {
245                $r_tags[$key]['highlighted'] = true;
246                $l_tags[$key]['highlighted'] = true;
247            } elseif (!$l_tag['value'] || !$r_tags[$key]['value']) {
248                unset($r_tags[$key]);
249                unset($l_tags[$key]);
250            }
251        }
252
253        echo '<tr>';
254        foreach (array($l_tags, $r_tags) as $tags) {
255            echo '<td>';
256
257            echo '<dl class="img_tags">';
258            foreach ($tags as $tag) {
259                $value = cleanText($tag['value']);
260                if (!$value) $value = '-';
261                echo '<dt>'.$lang[$tag['tag'][1]].'</dt>';
262                echo '<dd>';
263                if ($tag['highlighted']) echo '<strong>';
264                if ($tag['tag'][2] == 'date') {
265                    echo dformat($value);
266                } else {
267                    echo hsc($value);
268                }
269                if ($tag['highlighted']) echo '</strong>';
270                echo '</dd>';
271            }
272            echo '</dl>';
273
274            echo '</td>';
275        }
276        echo '</tr>';
277
278        echo '</table>';
279        echo '</div>';
280    }
281
282    /**
283     * Revision Title for MediaDiff table headline
284     *
285     * @param array $info  Revision info structure of a media file
286     * @return string
287     */
288    protected function revisionTitle(array $info)
289    {
290        global $lang, $INFO;
291
292        if (isset($info['date'])) {
293            $rev = $info['date'];
294            $title = '<bdi><a class="wikilink1" href="'.ml($this->id, ['rev' => $rev]).'">'
295                   . dformat($rev).'</a></bdi>';
296        } else {
297            $rev = false;
298            $title = '&mdash;';
299        }
300        if (isset($info['current']) || ($rev && $rev == $INFO['currentrev'])) {
301            $title .= '&nbsp;('.$lang['current'].')';
302        }
303
304        // append separator
305        $title .= ($this->preference['difftype'] === 'inline') ? ' ' : '<br />';
306
307        // supplement
308        if (isset($info['date'])) {
309            $objRevInfo = (new MediaRevisions($this->id))->getObjRevInfo($info);
310            $title .= $objRevInfo->editSummary().' '.$objRevInfo->editor();
311        }
312        return $title;
313    }
314
315}
316