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