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