xref: /dokuwiki/inc/Ui/MediaDiff.php (revision eeda7ada8c965be1a48afa72ccc473ba9e1db91b)
1<?php
2
3namespace dokuwiki\Ui;
4
5use dokuwiki\ChangeLog\MediaChangeLog;
6use dokuwiki\ChangeLog\RevisionInfo;
7use dokuwiki\Form\Form;
8use InvalidArgumentException;
9use JpegMeta;
10
11/**
12 * DokuWiki MediaDiff Interface
13 *
14 * @package dokuwiki\Ui
15 */
16class MediaDiff extends Diff
17{
18    /* @var MediaChangeLog */
19    protected $changelog;
20
21    /* @var RevisionInfo older revision */
22    protected $Rev1;
23    /* @var RevisionInfo newer revision */
24    protected $Rev2;
25
26    /* @var bool */
27    protected $is_img;
28
29    /**
30     * MediaDiff Ui constructor
31     *
32     * @param string $id  media id
33     */
34    public function __construct($id)
35    {
36        if (!isset($id)) {
37            throw new InvalidArgumentException('media id should not be empty!');
38        }
39
40        // init preference
41        $this->preference['fromAjax'] = false;  // see dokuwiki\Ajax::callMediadiff()
42        $this->preference['showIntro'] = false;
43        $this->preference['difftype'] = 'both'; // diff view type: both, opacity or portions
44
45        parent::__construct($id);
46    }
47
48    /** @inheritdoc */
49    protected function setChangeLog()
50    {
51        $this->changelog = new MediaChangeLog($this->id);
52    }
53
54    /**
55     * Handle requested revision(s) and diff view preferences
56     *
57     * @return void
58     */
59    protected function handle()
60    {
61        global $INPUT;
62
63        // retrieve requested rev or rev2
64        parent::handle();
65
66        // requested diff view type
67        if ($INPUT->has('difftype')) {
68            $this->preference['difftype'] = $INPUT->str('difftype');
69        }
70    }
71
72    /**
73     * Prepare revision info of comparison pair
74     */
75    protected function preProcess()
76    {
77        $changelog =& $this->changelog;
78
79        // create revision info object for older and newer sides
80        // Rev1 : older, left side
81        // Rev2 : newer, right side
82        $this->Rev1 = new RevisionInfo($changelog->getRevisionInfo($this->rev1));
83        $this->Rev2 = new RevisionInfo($changelog->getRevisionInfo($this->rev2));
84        [$Rev1, $Rev2] = [$this->Rev1, $this->Rev2];
85
86        $this->is_img = preg_match('/\.(jpe?g|gif|png)$/', $this->id);
87
88        foreach ([$Rev1, $Rev2] as $Revision) {
89            $isCurrent = $changelog->isCurrentRevision($Revision->val('date'));
90            $Revision->isCurrent($isCurrent);
91
92            if ($this->is_img) {
93                $rev = $isCurrent ? '' : $Revision->val('date');
94                $meta = new JpegMeta(mediaFN($this->id, $rev));
95                // get image width and height for the media manager preview panel
96                $Revision->append([
97                    'previewSize' => media_image_preview_size($this->id, $rev, $meta)
98                ]);
99            }
100        }
101
102        // re-check image, ensure minimum image width for showImageDiff()
103        $this->is_img = ($this->is_img
104            && ($Rev1->val('previewSize')[0] ?? 0) >= 30
105            && ($Rev2->val('previewSize')[0] ?? 0) >= 30
106        );
107        // adjust requested diff view type
108        if (!$this->is_img) {
109            $this->preference['difftype'] = 'both';
110        }
111    }
112
113
114    /**
115     * Shows difference between two revisions of media
116     *
117     * @author Kate Arzamastseva <pshns@ukr.net>
118     */
119    public function show()
120    {
121        global $conf;
122
123        $ns = getNS($this->id);
124        $auth = auth_quickaclcheck("$ns:*");
125
126        if ($auth < AUTH_READ || !$this->id || !$conf['mediarevisions']) return;
127
128        // retrieve form parameters: rev, rev2, difftype
129        $this->handle();
130        // prepare revision info of comparison pair
131        $this->preProcess();
132
133        // display intro
134        if ($this->preference['showIntro']) echo p_locale_xhtml('diff');
135
136        // print form to choose diff view type
137        if ($this->is_img && !$this->preference['fromAjax']) {
138            $this->showDiffViewSelector();
139            echo '<div id="mediamanager__diff" >';
140        }
141
142        switch ($this->preference['difftype']) {
143            case 'opacity':
144            case 'portions':
145                $this->showImageDiff();
146                break;
147            case 'both':
148            default:
149                $this->showFileDiff();
150                break;
151        }
152
153        if ($this->is_img && !$this->preference['fromAjax']) {
154            echo '</div>';
155        }
156    }
157
158    /**
159     * Print form to choose diff view type
160     * the dropdown is to be added through JavaScript, see lib/scripts/media.js
161     */
162    protected function showDiffViewSelector()
163    {
164        // revision information object
165        [$Rev1, $Rev2] = [$this->Rev1, $this->Rev2];
166
167        echo '<div class="diffoptions group">';
168
169        $form = new Form([
170            'id' => 'mediamanager__form_diffview',
171            'action' => media_managerURL([], '&'),
172            'method' => 'get',
173            'class' => 'diffView',
174        ]);
175        $form->addTagOpen('div')->addClass('no');
176        $form->setHiddenField('sectok', null);
177        $form->setHiddenField('mediado', 'diff');
178        $form->setHiddenField('rev2[0]', (int)$Rev1->val('date'));
179        $form->setHiddenField('rev2[1]', (int)$Rev2->val('date'));
180        $form->addTagClose('div');
181        echo $form->toHTML();
182
183        echo '</div>'; // .diffoptions
184    }
185
186    /**
187     * Prints two images side by side
188     * and slider
189     *
190     * @author Kate Arzamastseva <pshns@ukr.net>
191     */
192    protected function showImageDiff()
193    {
194        // revision information object
195        [$Rev1, $Rev2] = [$this->Rev1, $this->Rev2];
196
197        $rev1 = $Rev1->isCurrent() ? '' : $Rev1->val('date');
198        $rev2 = $Rev2->isCurrent() ? '' : $Rev2->val('date');
199
200        // diff view type: opacity or portions
201        $type = $this->preference['difftype'];
202
203        // adjust image width, right side (newer) has priority
204        $rev1Size = $Rev1->val('previewSize');
205        $rev2Size = $Rev2->val('previewSize');
206        if ($rev1Size != $rev2Size) {
207            if ($rev2Size[0] > $rev1Size[0]) {
208                $rev1Size = $rev2Size;
209            }
210        }
211
212        $rev1Src = ml($this->id, ['rev' => $rev1, 'h' => $rev1Size[1], 'w' => $rev1Size[0]]);
213        $rev2Src = ml($this->id, ['rev' => $rev2, 'h' => $rev1Size[1], 'w' => $rev1Size[0]]);
214
215        // slider
216        echo '<div class="slider" style="max-width: '.($rev1Size[0]-20).'px;" ></div>';
217
218        // two images in divs
219        echo '<div class="imageDiff '.$type.'">';
220        echo '<div class="image1" style="max-width: '.$rev1Size[0].'px;">';
221        echo '<img src="'.$rev1Src.'" alt="" />';
222        echo '</div>';
223        echo '<div class="image2" style="max-width: '.$rev1Size[0].'px;">';
224        echo '<img src="'.$rev2Src.'" alt="" />';
225        echo '</div>';
226        echo '</div>';
227    }
228
229    /**
230     * Shows difference between two revisions of media file
231     *
232     * @author Kate Arzamastseva <pshns@ukr.net>
233     */
234    protected function showFileDiff()
235    {
236        global $lang;
237
238        $ns = getNS($this->id);
239        $auth = auth_quickaclcheck("$ns:*");
240
241        // revision information object
242        [$Rev1, $Rev2] = [$this->Rev1, $this->Rev2];
243
244        $rev1 = $Rev1->isCurrent() ? '' : (int)$Rev1->val('date');
245        $rev2 = $Rev2->isCurrent() ? '' : (int)$Rev2->val('date');
246
247        // revision title
248        $rev1Title = trim($Rev1->showRevisionTitle() .' '. $Rev1->showCurrentIndicator());
249        $rev1Supple = ($Rev1->val('date'))
250            ? $Rev1->showEditSummary() .' '. $Rev1->showEditor()
251            : '';
252        $rev2Title = trim($Rev2->showRevisionTitle() .' '. $Rev2->showCurrentIndicator());
253        $rev2Supple = ($Rev2->val('date'))
254            ? $Rev2->showEditSummary() .' '. $Rev2->showEditor()
255            : '';
256
257        $rev1Meta = new JpegMeta(mediaFN($this->id, $rev1));
258        $rev2Meta = new JpegMeta(mediaFN($this->id, $rev2));
259
260        // display diff view table
261        echo '<div class="table">';
262        echo '<table>';
263        echo '<tr>';
264        echo '<th>'. $rev1Title .' '. $rev1Supple .'</th>';
265        echo '<th>'. $rev2Title .' '. $rev2Supple .'</th>';
266        echo '</tr>';
267
268        echo '<tr class="image">';
269        echo '<td>';
270        media_preview($this->id, $auth, $rev1, $rev1Meta); // $auth not used in media_preview()?
271        echo '</td>';
272
273        echo '<td>';
274        media_preview($this->id, $auth, $rev2, $rev2Meta);
275        echo '</td>';
276        echo '</tr>';
277
278        echo '<tr class="actions">';
279        echo '<td>';
280        media_preview_buttons($this->id, $auth, $rev1); // $auth used in media_preview_buttons()
281        echo '</td>';
282
283        echo '<td>';
284        media_preview_buttons($this->id, $auth, $rev2);
285        echo '</td>';
286        echo '</tr>';
287
288        $rev1Tags = media_file_tags($rev1Meta);
289        $rev2Tags = media_file_tags($rev2Meta);
290        // FIXME rev2Tags-only stuff ignored
291        foreach ($rev1Tags as $key => $tag) {
292            if ($tag['value'] != $rev2Tags[$key]['value']) {
293                $rev2Tags[$key]['highlighted'] = true;
294                $rev1Tags[$key]['highlighted'] = true;
295            } elseif (!$tag['value'] || !$rev2Tags[$key]['value']) {
296                unset($rev2Tags[$key]);
297                unset($rev1Tags[$key]);
298            }
299        }
300
301        echo '<tr>';
302        foreach ([$rev1Tags, $rev2Tags] as $tags) {
303            echo '<td>';
304
305            echo '<dl class="img_tags">';
306            foreach ($tags as $tag) {
307                $value = cleanText($tag['value']);
308                if (!$value) $value = '-';
309                echo '<dt>'.$lang[$tag['tag'][1]].'</dt>';
310                echo '<dd>';
311                if (!empty($tag['highlighted'])) echo '<strong>';
312                if ($tag['tag'][2] == 'date') {
313                    echo dformat($value);
314                } else {
315                    echo hsc($value);
316                }
317                if (!empty($tag['highlighted'])) echo '</strong>';
318                echo '</dd>';
319            }
320            echo '</dl>';
321
322            echo '</td>';
323        }
324        echo '</tr>';
325
326        echo '</table>';
327        echo '</div>';
328    }
329
330}
331