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