xref: /dokuwiki/inc/Ui/MediaDiff.php (revision 093fe67e98c0cdb4b73fd46938e49b64971483c2)
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 $RevInfo1;
23    /* @var RevisionInfo newer revision */
24    protected $RevInfo2;
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        // RevInfo1 : older, left side
81        // RevInfo2 : newer, right side
82
83        $changelogRev1 = $changelog->getRevisionInfo($this->rev1);
84        $changelogRev2 = $changelog->getRevisionInfo($this->rev2);
85
86        $this->RevInfo1 = new RevisionInfo($changelogRev1);
87        $this->RevInfo2 = new RevisionInfo($changelogRev2);
88
89        $this->is_img = preg_match('/\.(jpe?g|gif|png)$/', $this->id);
90
91        foreach ([$this->RevInfo1, $this->RevInfo2] as $RevInfo) {
92            $isCurrent = $changelog->isCurrentRevision($RevInfo->val('date'));
93            $RevInfo->isCurrent($isCurrent);
94
95            if ($this->is_img) {
96                $rev = $isCurrent ? '' : $RevInfo->val('date');
97                $meta = new JpegMeta(mediaFN($this->id, $rev));
98                // get image width and height for the media manager preview panel
99                $RevInfo->append([
100                    'previewSize' => media_image_preview_size($this->id, $rev, $meta)
101                ]);
102            }
103        }
104
105        // re-check image, ensure minimum image width for showImageDiff()
106        $this->is_img = ($this->is_img
107            && ($this->RevInfo1->val('previewSize')[0] ?? 0) >= 30
108            && ($this->RevInfo2->val('previewSize')[0] ?? 0) >= 30
109        );
110        // adjust requested diff view type
111        if (!$this->is_img) {
112            $this->preference['difftype'] = 'both';
113        }
114    }
115
116
117    /**
118     * Shows difference between two revisions of media
119     *
120     * @author Kate Arzamastseva <pshns@ukr.net>
121     */
122    public function show()
123    {
124        global $conf;
125
126        $ns = getNS($this->id);
127        $auth = auth_quickaclcheck("$ns:*");
128
129        if ($auth < AUTH_READ || !$this->id || !$conf['mediarevisions']) return;
130
131        // retrieve form parameters: rev, rev2, difftype
132        $this->handle();
133        // prepare revision info of comparison pair
134        $this->preProcess();
135
136        // display intro
137        if ($this->preference['showIntro']) echo p_locale_xhtml('diff');
138
139        // print form to choose diff view type
140        if ($this->is_img && !$this->preference['fromAjax']) {
141            $this->showDiffViewSelector();
142            echo '<div id="mediamanager__diff" >';
143        }
144
145        match ($this->preference['difftype']) {
146            'opacity', 'portions' => $this->showImageDiff(),
147            default => $this->showFileDiff(),
148        };
149
150        if ($this->is_img && !$this->preference['fromAjax']) {
151            echo '</div>';
152        }
153    }
154
155    /**
156     * Print form to choose diff view type
157     * the dropdown is to be added through JavaScript, see lib/scripts/media.js
158     */
159    protected function showDiffViewSelector()
160    {
161        // use timestamp for current revision, date may be false when revisions < 2
162        [$rev1, $rev2] = [(int)$this->RevInfo1->val('date'), (int)$this->RevInfo2->val('date')];
163
164        echo '<div class="diffoptions group">';
165
166        $form = new Form([
167            'id' => 'mediamanager__form_diffview',
168            'action' => media_managerURL([], '&'),
169            'method' => 'get',
170            'class' => 'diffView',
171        ]);
172        $form->addTagOpen('div')->addClass('no');
173        $form->setHiddenField('sectok', null);
174        $form->setHiddenField('mediado', 'diff');
175        $form->setHiddenField('rev2[0]', $rev1);
176        $form->setHiddenField('rev2[1]', $rev2);
177        $form->addTagClose('div');
178        echo $form->toHTML();
179
180        echo '</div>'; // .diffoptions
181    }
182
183    /**
184     * Prints two images side by side
185     * and slider
186     *
187     * @author Kate Arzamastseva <pshns@ukr.net>
188     */
189    protected function showImageDiff()
190    {
191        $rev1 = $this->RevInfo1->isCurrent() ? '' : $this->RevInfo1->val('date');
192        $rev2 = $this->RevInfo2->isCurrent() ? '' : $this->RevInfo2->val('date');
193
194        // diff view type: opacity or portions
195        $type = $this->preference['difftype'];
196
197        // adjust image width, right side (newer) has priority
198        $rev1Size = $this->RevInfo1->val('previewSize');
199        $rev2Size = $this->RevInfo2->val('previewSize');
200        if ($rev1Size != $rev2Size) {
201            if ($rev2Size[0] > $rev1Size[0]) {
202                $rev1Size = $rev2Size;
203            }
204        }
205
206        $rev1Src = ml($this->id, ['rev' => $rev1, 'h' => $rev1Size[1], 'w' => $rev1Size[0]]);
207        $rev2Src = ml($this->id, ['rev' => $rev2, 'h' => $rev1Size[1], 'w' => $rev1Size[0]]);
208
209        // slider
210        echo '<div class="slider" style="max-width: ' . ($rev1Size[0] - 20) . 'px;" ></div>';
211
212        // two images in divs
213        echo '<div class="imageDiff ' . $type . '">';
214        echo '<div class="image1" style="max-width: ' . $rev1Size[0] . 'px;">';
215        echo '<img src="' . $rev1Src . '" alt="" />';
216        echo '</div>';
217        echo '<div class="image2" style="max-width: ' . $rev1Size[0] . 'px;">';
218        echo '<img src="' . $rev2Src . '" alt="" />';
219        echo '</div>';
220        echo '</div>';
221    }
222
223    /**
224     * Shows difference between two revisions of media file
225     *
226     * @author Kate Arzamastseva <pshns@ukr.net>
227     */
228    protected function showFileDiff()
229    {
230        global $lang;
231
232        $ns = getNS($this->id);
233        $auth = auth_quickaclcheck("$ns:*");
234
235        $rev1 = $this->RevInfo1->isCurrent() ? '' : (int)$this->RevInfo1->val('date');
236        $rev2 = $this->RevInfo2->isCurrent() ? '' : (int)$this->RevInfo2->val('date');
237
238        // revision title
239        $rev1Title = trim($this->RevInfo1->showRevisionTitle() . ' ' . $this->RevInfo1->showCurrentIndicator());
240        $rev1Summary = ($this->RevInfo1->val('date'))
241            ? $this->RevInfo1->showEditSummary() . ' ' . $this->RevInfo1->showEditor()
242            : '';
243        $rev2Title = trim($this->RevInfo2->showRevisionTitle() . ' ' . $this->RevInfo2->showCurrentIndicator());
244        $rev2Summary = ($this->RevInfo2->val('date'))
245            ? $this->RevInfo2->showEditSummary() . ' ' . $this->RevInfo2->showEditor()
246            : '';
247
248        $rev1Meta = new JpegMeta(mediaFN($this->id, $rev1));
249        $rev2Meta = new JpegMeta(mediaFN($this->id, $rev2));
250
251        // display diff view table
252        echo '<div class="table">';
253        echo '<table>';
254        echo '<tr>';
255        echo '<th>' . $rev1Title . ' ' . $rev1Summary . '</th>';
256        echo '<th>' . $rev2Title . ' ' . $rev2Summary . '</th>';
257        echo '</tr>';
258
259        echo '<tr class="image">';
260        echo '<td>';
261        media_preview($this->id, $auth, $rev1, $rev1Meta); // $auth not used in media_preview()?
262        echo '</td>';
263
264        echo '<td>';
265        media_preview($this->id, $auth, $rev2, $rev2Meta);
266        echo '</td>';
267        echo '</tr>';
268
269        echo '<tr class="actions">';
270        echo '<td>';
271        media_preview_buttons($this->id, $auth, $rev1); // $auth used in media_preview_buttons()
272        echo '</td>';
273
274        echo '<td>';
275        media_preview_buttons($this->id, $auth, $rev2);
276        echo '</td>';
277        echo '</tr>';
278
279        $rev1Tags = media_file_tags($rev1Meta);
280        $rev2Tags = media_file_tags($rev2Meta);
281        // FIXME rev2Tags-only stuff ignored
282        foreach ($rev1Tags as $key => $tag) {
283            if ($tag['value'] != $rev2Tags[$key]['value']) {
284                $rev2Tags[$key]['highlighted'] = true;
285                $rev1Tags[$key]['highlighted'] = true;
286            } elseif (!$tag['value'] || !$rev2Tags[$key]['value']) {
287                unset($rev2Tags[$key]);
288                unset($rev1Tags[$key]);
289            }
290        }
291
292        echo '<tr>';
293        foreach ([$rev1Tags, $rev2Tags] as $tags) {
294            echo '<td>';
295
296            echo '<dl class="img_tags">';
297            foreach ($tags as $tag) {
298                $value = cleanText($tag['value']);
299                if (!$value) $value = '-';
300                echo '<dt>' . $lang[$tag['tag'][1]] . '</dt>';
301                echo '<dd>';
302                if (!empty($tag['highlighted'])) echo '<strong>';
303                if ($tag['tag'][2] == 'date') {
304                    echo dformat($value);
305                } else {
306                    echo hsc($value);
307                }
308                if (!empty($tag['highlighted'])) echo '</strong>';
309                echo '</dd>';
310            }
311            echo '</dl>';
312
313            echo '</td>';
314        }
315        echo '</tr>';
316
317        echo '</table>';
318        echo '</div>';
319    }
320}
321