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 (!isset($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 $oldRevMeta = new JpegMeta(mediaFN($this->id, $oldRev)); 84 $newRevMeta = new JpegMeta(mediaFN($this->id, $newRev)); 85 86 $is_img = preg_match('/\.(jpe?g|gif|png)$/', $this->id); 87 if ($is_img) { 88 // get image width and height for the mediamanager preview panel 89 $oldRevSize = media_image_preview_size($this->id, $oldRev, $oldRevMeta); 90 $newRevSize = media_image_preview_size($this->id, $newRev, $newRevMeta); 91 // re-check image, ensure minimum image width for showImageDiff() 92 $is_img = ($oldRevSize && $newRevSize && ($oldRevSize[0] >= 30 || $newRevSize[0] >= 30)); 93 } 94 95 // determine requested diff view type 96 if (!$is_img) { 97 $this->preference['difftype'] = 'both'; 98 } 99 100 // display intro 101 if ($this->preference['showIntro']) echo p_locale_xhtml('diff'); 102 103 // print form to choose diff view type 104 if ($is_img && !$this->preference['fromAjax']) { 105 $this->showDiffViewSelector(); 106 echo '<div id="mediamanager__diff" >'; 107 } 108 109 switch ($this->preference['difftype']) { 110 case 'opacity': 111 case 'portions': 112 $this->showImageDiff($oldRev, $newRev, $oldRevSize, $newRevSize); 113 break; 114 case 'both': 115 default: 116 $this->showFileDiff($oldRev, $newRev, $oldRevMeta, $newRevMeta, $auth); 117 break; 118 } 119 120 if ($is_img && !$this->preference['fromAjax']) { 121 echo '</div>'; 122 } 123 } 124 125 /** 126 * Print form to choose diff view type 127 * the dropdown is to be added through JavaScript, see lib/scripts/media.js 128 */ 129 protected function showDiffViewSelector() 130 { 131 echo '<div class="diffoptions group">'; 132 133 $form = new Form([ 134 'id' => 'mediamanager__form_diffview', 135 'action' => media_managerURL([], '&'), 136 'method' => 'get', 137 'class' => 'diffView', 138 ]); 139 $form->addTagOpen('div')->addClass('no'); 140 $form->setHiddenField('sectok', null); 141 $form->setHiddenField('mediado', 'diff'); 142 $form->setHiddenField('rev2[0]', $this->oldRev ?: 'current'); 143 $form->setHiddenField('rev2[1]', $this->newRev ?: 'current'); 144 $form->addTagClose('div'); 145 echo $form->toHTML(); 146 147 echo '</div>'; // .diffoptions 148 } 149 150 /** 151 * Prints two images side by side 152 * and slider 153 * 154 * @author Kate Arzamastseva <pshns@ukr.net> 155 * 156 * @param string|int $oldRev revision timestamp, or empty string 157 * @param string|int $newRev revision timestamp, or empty string 158 * @param array $oldRevSize array with width and height 159 * @param array $newRevSize array with width and height 160 * @param string $type diff view type: opacity or portions 161 */ 162 protected function showImageDiff($oldRev, $newRev, $oldRevSize, $newRevSize, $type = null) 163 { 164 if (!isset($type)) { 165 $type = $this->preference['difftype']; 166 } 167 168 // adjust image width, right side (newer) has priority 169 if ($oldRevSize != $newRevSize) { 170 if ($newRevSize[0] > $oldRevSize[0]) { 171 $oldRevSize = $newRevSize; 172 } 173 } 174 175 $oldRevSrc = ml($this->id, ['rev' => $oldRev, 'h' => $oldRevSize[1], 'w' => $oldRevSize[0]]); 176 $newRevSrc = ml($this->id, ['rev' => $newRev, 'h' => $oldRevSize[1], 'w' => $oldRevSize[0]]); 177 178 // slider 179 echo '<div class="slider" style="max-width: '.($oldRevSize[0]-20).'px;" ></div>'; 180 181 // two images in divs 182 echo '<div class="imageDiff '.$type.'">'; 183 echo '<div class="image1" style="max-width: '.$oldRevSize[0].'px;">'; 184 echo '<img src="'.$oldRevSrc.'" alt="" />'; 185 echo '</div>'; 186 echo '<div class="image2" style="max-width: '.$oldRevSize[0].'px;">'; 187 echo '<img src="'.$newRevSrc.'" alt="" />'; 188 echo '</div>'; 189 echo '</div>'; 190 } 191 192 /** 193 * Shows difference between two revisions of media file 194 * 195 * @author Kate Arzamastseva <pshns@ukr.net> 196 * 197 * @param string|int $oldRev revision timestamp, or empty string 198 * @param string|int $newRev revision timestamp, or empty string 199 * @param JpegMeta $oldRevMeta 200 * @param JpegMeta $newRevMeta 201 * @param int $auth permission level 202 */ 203 protected function showFileDiff($oldRev, $newRev, $oldRevMeta, $newRevMeta, $auth) 204 { 205 global $lang; 206 207 // revison info of older file (left side) 208 $oldRevInfo = $this->getExtendedRevisionInfo($oldRev); 209 // revison info of newer file (right side) 210 $newRevInfo = $this->getExtendedRevisionInfo($newRev); 211 212 // display diff view table 213 echo '<div class="table">'; 214 echo '<table>'; 215 echo '<tr>'; 216 echo '<th>'. $this->revisionTitle($oldRevInfo) .'</th>'; 217 echo '<th>'. $this->revisionTitle($newRevInfo) .'</th>'; 218 echo '</tr>'; 219 220 echo '<tr class="image">'; 221 echo '<td>'; 222 media_preview($this->id, $auth, $oldRev, $oldRevMeta); // $auth not used in media_preview()? 223 echo '</td>'; 224 225 echo '<td>'; 226 media_preview($this->id, $auth, $newRev, $newRevMeta); 227 echo '</td>'; 228 echo '</tr>'; 229 230 echo '<tr class="actions">'; 231 echo '<td>'; 232 media_preview_buttons($this->id, $auth, $oldRev); // $auth used in media_preview_buttons() 233 echo '</td>'; 234 235 echo '<td>'; 236 media_preview_buttons($this->id, $auth, $newRev); 237 echo '</td>'; 238 echo '</tr>'; 239 240 $l_tags = media_file_tags($oldRevMeta); 241 $r_tags = media_file_tags($newRevMeta); 242 // FIXME r_tags-only stuff 243 foreach ($l_tags as $key => $l_tag) { 244 if ($l_tag['value'] != $r_tags[$key]['value']) { 245 $r_tags[$key]['highlighted'] = true; 246 $l_tags[$key]['highlighted'] = true; 247 } elseif (!$l_tag['value'] || !$r_tags[$key]['value']) { 248 unset($r_tags[$key]); 249 unset($l_tags[$key]); 250 } 251 } 252 253 echo '<tr>'; 254 foreach (array($l_tags, $r_tags) as $tags) { 255 echo '<td>'; 256 257 echo '<dl class="img_tags">'; 258 foreach ($tags as $tag) { 259 $value = cleanText($tag['value']); 260 if (!$value) $value = '-'; 261 echo '<dt>'.$lang[$tag['tag'][1]].'</dt>'; 262 echo '<dd>'; 263 if ($tag['highlighted']) echo '<strong>'; 264 if ($tag['tag'][2] == 'date') { 265 echo dformat($value); 266 } else { 267 echo hsc($value); 268 } 269 if ($tag['highlighted']) echo '</strong>'; 270 echo '</dd>'; 271 } 272 echo '</dl>'; 273 274 echo '</td>'; 275 } 276 echo '</tr>'; 277 278 echo '</table>'; 279 echo '</div>'; 280 } 281 282 /** 283 * Revision Title for MediaDiff table headline 284 * 285 * @param array $info Revision info structure of a media file 286 * @return string 287 */ 288 protected function revisionTitle(array $info) 289 { 290 global $lang, $INFO; 291 292 if (isset($info['date'])) { 293 $rev = $info['date']; 294 $title = '<bdi><a class="wikilink1" href="'.ml($this->id, ['rev' => $rev]).'">' 295 . dformat($rev).'</a></bdi>'; 296 } else { 297 $rev = false; 298 $title = '—'; 299 } 300 if (isset($info['current']) || ($rev && $rev == $INFO['currentrev'])) { 301 $title .= ' ('.$lang['current'].')'; 302 } 303 304 // append separator 305 $title .= ($this->preference['difftype'] === 'inline') ? ' ' : '<br />'; 306 307 // supplement 308 if (isset($info['date'])) { 309 $objRevInfo = (new MediaRevisions($this->id))->getObjRevInfo($info); 310 $title .= $objRevInfo->editSummary().' '.$objRevInfo->editor(); 311 } 312 return $title; 313 } 314 315} 316