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