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