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