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