xref: /dokuwiki/inc/Ui/MediaDiff.php (revision cf3c75d69f60ac546e745559f7767c5f84b1a18c)
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 (!$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        // prepare event data
84        // NOTE: MEDIA_DIFF event does not found in DokuWiki Event List?
85        $data = array();
86        $data[0] = $this->id;
87        $data[1] = $oldRev;
88        $data[2] = $newRev;
89        $data[3] = $ns;
90        $data[4] = $auth; // permission level
91        $data[5] = $this->preference['fromAjax'];
92
93        // trigger event
94        Event::createAndTrigger('MEDIA_DIFF', $data, null, false);
95
96        if (is_array($data) && count($data) === 6) {
97            $this->id = $data[0];
98            $oldRev = $data[1];
99            $newRev = $data[2];
100            $ns     = $data[3];
101            $auth   = $data[4];
102            $this->preference['fromAjax'] = $data[5];
103        } else {
104            return '';
105        }
106
107        $oldRevMeta = new JpegMeta(mediaFN($this->id, $oldRev));
108        $newRevMeta = new JpegMeta(mediaFN($this->id, $newRev));
109
110        $is_img = preg_match('/\.(jpe?g|gif|png)$/', $this->id);
111        if ($is_img) {
112            // get image width and height for the mediamanager preview panel
113            $oldRevSize = media_image_preview_size($this->id, $oldRev, $oldRevMeta);
114            $newRevSize = media_image_preview_size($this->id, $newRev, $newRevMeta);
115            // re-check image, ensure minimum image width for showImageDiff()
116            $is_img = ($oldRevSize && $newRevSize && ($oldRevSize[0] >= 30 || $newRevSize[0] >= 30));
117        }
118
119        // determine requested diff view type
120        if (!$is_img) {
121            $this->preference['difftype'] = 'both';
122        }
123
124        // display intro
125        if ($this->preference['showIntro']) echo p_locale_xhtml('diff');
126
127        // print form to choose diff view type
128        if ($is_img && !$this->preference['fromAjax']) {
129            $this->showDiffViewSelector();
130            echo '<div id="mediamanager__diff" >';
131        }
132
133        switch ($this->preference['difftype']) {
134            case 'opacity':
135            case 'portions':
136                $this->showImageDiff($oldRev, $newRev, $oldRevSize, $newRevSize);
137                break;
138            case 'both':
139            default:
140                $this->showFileDiff($oldRev, $newRev, $oldRevMeta, $newRevMeta, $auth);
141                break;
142        }
143
144        if ($is_img && !$this->preference['fromAjax']) {
145            echo '</div>';
146        }
147    }
148
149    /**
150     * Print form to choose diff view type
151     * the dropdown is to be added through JavaScript, see lib/scripts/media.js
152     */
153    protected function showDiffViewSelector()
154    {
155        echo '<div class="diffoptions group">';
156
157        $form = new Form([
158            'id' => 'mediamanager__form_diffview',
159            'action' => media_managerURL([], '&'),
160            'method' => 'get',
161            'class' => 'diffView',
162        ]);
163        $form->addTagOpen('div')->addClass('no');
164        $form->setHiddenField('sectok', null);
165        $form->setHiddenField('mediado', 'diff');
166        $form->setHiddenField('rev2[0]', $this->oldRev ?: 'current');
167        $form->setHiddenField('rev2[1]', $this->newRev ?: 'current');
168        $form->addTagClose('div');
169        echo $form->toHTML();
170
171        echo '</div>'; // .diffoptions
172    }
173
174    /**
175     * Prints two images side by side
176     * and slider
177     *
178     * @author Kate Arzamastseva <pshns@ukr.net>
179     *
180     * @param string|int $oldRev revision timestamp, or empty string
181     * @param string|int $newRev revision timestamp, or empty string
182     * @param array  $oldRevSize  array with width and height
183     * @param array  $newRevSize  array with width and height
184     * @param string $type    diff view type: opacity or portions
185     */
186    protected function showImageDiff($oldRev, $newRev, $oldRevSize, $newRevSize, $type = null)
187    {
188        if (!isset($type)) {
189            $type = $this->preference['difftype'];
190        }
191
192        // adjust image width, right side (newer) has priority
193        if ($oldRevSize != $newRevSize) {
194            if ($newRevSize[0] > $oldRevSize[0]) {
195                $oldRevSize = $newRevSize;
196            }
197        }
198
199        $oldRevSrc = ml($this->id, ['rev' => $oldRev, 'h' => $oldRevSize[1], 'w' => $oldRevSize[0]]);
200        $newRevSrc = ml($this->id, ['rev' => $newRev, 'h' => $oldRevSize[1], 'w' => $oldRevSize[0]]);
201
202        // slider
203        echo '<div class="slider" style="max-width: '.($oldRevSize[0]-20).'px;" ></div>';
204
205        // two images in divs
206        echo '<div class="imageDiff '.$type.'">';
207        echo '<div class="image1" style="max-width: '.$oldRevSize[0].'px;">';
208        echo '<img src="'.$oldRevSrc.'" alt="" />';
209        echo '</div>';
210        echo '<div class="image2" style="max-width: '.$oldRevSize[0].'px;">';
211        echo '<img src="'.$newRevSrc.'" alt="" />';
212        echo '</div>';
213        echo '</div>';
214    }
215
216    /**
217     * Shows difference between two revisions of media file
218     *
219     * @author Kate Arzamastseva <pshns@ukr.net>
220     *
221     * @param string|int $oldRev revision timestamp, or empty string
222     * @param string|int $newRev revision timestamp, or empty string
223     * @param JpegMeta $oldRevMeta
224     * @param JpegMeta $newRevMeta
225     * @param int $auth permission level
226     */
227    protected function showFileDiff($oldRev, $newRev, $oldRevMeta, $newRevMeta, $auth)
228    {
229        global $lang;
230
231        // revison info of older file (left side)
232        $oldRevInfo = $this->getExtendedRevisionInfo($oldRev);
233        // revison info of newer file (right side)
234        $newRevInfo = $this->getExtendedRevisionInfo($newRev);
235
236        // display diff view table
237        echo '<div class="table">';
238        echo '<table>';
239        echo '<tr>';
240        echo '<th>'. $this->revisionTitle($oldRevInfo) .'</th>';
241        echo '<th>'. $this->revisionTitle($newRevInfo) .'</th>';
242        echo '</tr>';
243
244        echo '<tr class="image">';
245        echo '<td>';
246        media_preview($this->id, $auth, $oldRev, $oldRevMeta); // $auth not used in media_preview()?
247        echo '</td>';
248
249        echo '<td>';
250        media_preview($this->id, $auth, $newRev, $newRevMeta);
251        echo '</td>';
252        echo '</tr>';
253
254        echo '<tr class="actions">';
255        echo '<td>';
256        media_preview_buttons($this->id, $auth, $oldRev); // $auth used in media_preview_buttons()
257        echo '</td>';
258
259        echo '<td>';
260        media_preview_buttons($this->id, $auth, $newRev);
261        echo '</td>';
262        echo '</tr>';
263
264        $l_tags = media_file_tags($oldRevMeta);
265        $r_tags = media_file_tags($newRevMeta);
266        // FIXME r_tags-only stuff
267        foreach ($l_tags as $key => $l_tag) {
268            if ($l_tag['value'] != $r_tags[$key]['value']) {
269                $r_tags[$key]['highlighted'] = true;
270                $l_tags[$key]['highlighted'] = true;
271            } elseif (!$l_tag['value'] || !$r_tags[$key]['value']) {
272                unset($r_tags[$key]);
273                unset($l_tags[$key]);
274            }
275        }
276
277        echo '<tr>';
278        foreach (array($l_tags, $r_tags) as $tags) {
279            echo '<td>';
280
281            echo '<dl class="img_tags">';
282            foreach ($tags as $tag) {
283                $value = cleanText($tag['value']);
284                if (!$value) $value = '-';
285                echo '<dt>'.$lang[$tag['tag'][1]].'</dt>';
286                echo '<dd>';
287                if ($tag['highlighted']) echo '<strong>';
288                if ($tag['tag'][2] == 'date') {
289                    echo dformat($value);
290                } else {
291                    echo hsc($value);
292                }
293                if ($tag['highlighted']) echo '</strong>';
294                echo '</dd>';
295            }
296            echo '</dl>';
297
298            echo '</td>';
299        }
300        echo '</tr>';
301
302        echo '</table>';
303        echo '</div>';
304    }
305
306    /**
307     * Revision Title for MediaDiff table headline
308     *
309     * @param array $info  Revision info structure of a media file
310     * @return string
311     */
312    protected function revisionTitle(array $info)
313    {
314        global $lang, $INFO;
315
316        if (isset($info['date'])) {
317            $rev = $info['date'];
318            $title = '<bdi><a class="wikilink1" href="'.ml($this->id, ['rev' => $rev]).'">'
319                   . dformat($rev).'</a></bdi>';
320        } else {
321            $rev = false;
322            $title = '&mdash;';
323        }
324        if (isset($info['current']) || ($rev && $rev == $INFO['currentrev'])) {
325            $title .= '&nbsp;('.$lang['current'].')';
326        }
327
328        // append separator
329        $title .= ($this->preference['difftype'] === 'inline') ? ' ' : '<br />';
330
331        // supplement
332        if (isset($info['date'])) {
333            $objRevInfo = (new MediaRevisions($this->id))->getObjRevInfo($info);
334            $title .= $objRevInfo->editSummary().' '.$objRevInfo->editor();
335        }
336        return $title;
337    }
338
339}
340