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