xref: /dokuwiki/inc/Ui/MediaDiff.php (revision 8068440f5f0623dc6322b168a36e3958883e9ba1)
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    /**
17     * MediaDiff Ui constructor
18     *
19     * @param string $id  media id
20     */
21    public function __construct($id)
22    {
23        $this->id = $id;
24
25        $this->preference['fromAjax'] = false; // see doluwiki\Ajax::callMediadiff()
26        $this->preference['showIntro'] = false;
27        $this->preference['difftype'] = null;  // both, opacity or portions.
28
29        $this->setChangeLog();
30    }
31
32    /** @inheritdoc */
33    protected function setChangeLog()
34    {
35        $this->changelog = new MediaChangeLog($this->id);
36    }
37
38    /** @inheritdoc */
39    protected function preProcess()
40    {
41        parent::preProcess();
42        if (!isset($this->old_rev, $this->new_rev)) {
43            // no revision was given, compare previous to current
44            $revs = $this->changelog->getRevisions(0, 1);
45            $this->old_rev = file_exists(mediaFN($this->id, $revs[0])) ? $revs[0] : '';
46            $this->new_rev = '';
47        }
48    }
49
50    /**
51     * Shows difference between two revisions of media
52     *
53     * @author Kate Arzamastseva <pshns@ukr.net>
54     * @param string $difftype diff view type for media (both, opacity or portions)
55     */
56    public function show($difftype = null)
57    {
58        global $conf;
59
60        $ns = getNS($this->id);
61        $auth = auth_quickaclcheck("$ns:*");
62
63        if ($auth < AUTH_READ || !$this->id || !$conf['mediarevisions']) return '';
64
65       // determine left and right revision
66        $this->preProcess();
67        [$l_rev, $r_rev] = [$this->old_rev, $this->new_rev];
68
69        // prepare event data
70        // NOTE: MEDIA_DIFF event does not found in DokuWiki Event List?
71        $data = array();
72        $data[0] = $this->id;
73        $data[1] = $l_rev;
74        $data[2] = $r_rev;
75        $data[3] = $ns;
76        $data[4] = $auth; // permission level
77        $data[5] = $this->preference['fromAjax'];
78
79        // trigger event
80        Event::createAndTrigger('MEDIA_DIFF', $data, null, false);
81
82        if (is_array($data) && count($data) === 6) {
83            $this->id = $data[0];
84            $l_rev = $data[1];
85            $r_rev = $data[2];
86            $ns    = $data[3];
87            $auth  = $data[4];
88            $this->preference['fromAjax'] = $data[5];
89        } else {
90            return '';
91        }
92
93        $l_meta = new \JpegMeta(mediaFN($this->id, $l_rev));
94        $r_meta = new \JpegMeta(mediaFN($this->id, $r_rev));
95
96        $is_img = preg_match('/\.(jpe?g|gif|png)$/', $this->id);
97        if ($is_img) {
98            // get image width and height for the mediamanager preview panel
99            $l_size = media_image_preview_size($this->id, $l_rev, $l_meta);
100            $r_size = media_image_preview_size($this->id, $r_rev, $r_meta);
101            // re-check image, ensure minimum image width for showImageDiff()
102            $is_img = ($l_size && $r_size && ($l_size[0] >= 30 || $r_size[0] >= 30));
103        }
104
105        // determine requested diff view type
106        $difftype = $this->getDiffType($difftype);
107
108        // display intro
109        if ($this->preference['showIntro']) echo p_locale_xhtml('diff');
110
111        // print form to choose diff view type
112        if ($is_img && !$this->preference['fromAjax']) {
113            $this->showDiffViewSelector($l_rev, $r_rev);
114            echo '<div id="mediamanager__diff" >';
115        }
116
117        if ($is_img) {
118            switch ($difftype) {
119                case 'opacity':
120                case 'portions':
121                    $this->showImageDiff($l_rev, $r_rev, $l_size, $r_size, $difftype);
122                    break;
123                case 'both':
124                default:
125                    $this->showFileDiff($l_rev, $r_rev, $l_meta, $r_meta, $auth);
126                    break;
127            }
128        } else {
129            $this->showFileDiff($l_rev, $r_rev, $l_meta, $r_meta, $auth);
130        }
131
132        if ($is_img && !$this->preference['fromAjax']) {
133            echo '</div>';
134        }
135    }
136
137    /**
138     * Determine requested diff view type for media
139     *
140     * @param string $mode  diff view type (both, opacity or portions)
141     * @return string
142     */
143    protected function getDiffType($mode = null)
144    {
145        global $INPUT;
146        $difftype =& $this->preference['difftype'];
147
148        if (!isset($mode)) {
149            $difftype = $INPUT->str('difftype');
150        } elseif (in_array($mode, ['both', 'opacity', 'portions'])) {
151            $difftype = $mode;
152        }
153        return $this->preference['difftype'];
154    }
155
156    /**
157     * Print form to choose diff view type
158     * the dropdown is to be added through JavaScript, see lib/scripts/media.js
159     *
160     * @param int $l_rev  revision timestamp of left side
161     * @param int $r_rev  revision timestamp of right side
162     */
163    protected function showDiffViewSelector($l_rev, $r_rev)
164    {
165        $form = new Form([
166            'id' => 'mediamanager__form_diffview',
167            'action' => media_managerURL([], '&'),
168            'method' => 'get',
169            'class' => 'diffView',
170        ]);
171        $form->addTagOpen('div')->addClass('no');
172        $form->setHiddenField('sectok', null);
173        $form->setHiddenField('mediado', 'diff');
174        $form->setHiddenField('rev2[0]', $l_rev ?: 'current');
175        $form->setHiddenField('rev2[1]', $r_rev ?: 'current');
176        $form->addTagClose('div');
177        echo $form->toHTML();
178    }
179
180    /**
181     * Prints two images side by side
182     * and slider
183     *
184     * @author Kate Arzamastseva <pshns@ukr.net>
185     *
186     * @param int    $l_rev   revision timestamp, or empty string
187     * @param int    $r_rev   revision timestamp, or empty string
188     * @param array  $l_size  array with width and height
189     * @param array  $r_size  array with width and height
190     * @param string $type    diff type: opacity or portions
191     */
192    protected function showImageDiff($l_rev, $r_rev, $l_size, $r_size, $type = null)
193    {
194        if (!isset($type)) {
195            $type = $this->preference['difftype'];
196        }
197
198        // adjust image width, right side (newer) has priority
199        if ($l_size != $r_size) {
200            if ($r_size[0] > $l_size[0]) {
201                $l_size = $r_size;
202            }
203        }
204
205        $l_src = ml($this->id, ['rev' => $l_rev, 'h' => $l_size[1], 'w' => $l_size[0]]);
206        $r_src = ml($this->id, ['rev' => $r_rev, 'h' => $l_size[1], 'w' => $l_size[0]]);
207
208        // slider
209        echo '<div class="slider" style="max-width: '.($l_size[0]-20).'px;" ></div>';
210
211        // two images in divs
212        echo '<div class="imageDiff '.$type.'">';
213        echo '<div class="image1" style="max-width: '.$l_size[0].'px;">';
214        echo '<img src="'.$l_src.'" alt="" />';
215        echo '</div>';
216        echo '<div class="image2" style="max-width: '.$l_size[0].'px;">';
217        echo '<img src="'.$r_src.'" alt="" />';
218        echo '</div>';
219        echo '</div>';
220    }
221
222    /**
223     * Shows difference between two revisions of media file
224     *
225     * @author Kate Arzamastseva <pshns@ukr.net>
226     *
227     * @param string|int $l_rev revision timestamp, or empty string
228     * @param string|int $r_rev revision timestamp, or empty string
229     * @param JpegMeta $l_meta
230     * @param JpegMeta $r_meta
231     * @param int $auth permission level
232     */
233    protected function showFileDiff($l_rev, $r_rev, $l_meta, $r_meta, $auth)
234    {
235        list($l_head, $r_head) = $this->buildDiffHead($l_rev, $r_rev);
236
237        echo '<div class="table">';
238        echo '<table>';
239        echo '<tr>';
240        echo '<th>'. $l_head .'</th>';
241        echo '<th>'. $r_head .'</th>';
242        echo '</tr>';
243
244        echo '<tr class="image">';
245        echo '<td>';
246        media_preview($this->id, $auth, $l_rev, $l_meta); // $auth not used in media_preview()?
247        echo '</td>';
248
249        echo '<td>';
250        media_preview($this->id, $auth, $r_rev, $r_meta);
251        echo '</td>';
252        echo '</tr>';
253
254        echo '<tr class="actions">';
255        echo '<td>';
256        media_preview_buttons($this->id, $auth, $l_rev); // $auth used in media_preview_buttons()
257        echo '</td>';
258
259        echo '<td>';
260        media_preview_buttons($this->id, $auth, $r_rev);
261        echo '</td>';
262        echo '</tr>';
263
264        $l_tags = media_file_tags($l_meta);
265        $r_tags = media_file_tags($r_meta);
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