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