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 32 // init preference 33 $this->preference['fromAjax'] = false; // see doluwiki\Ajax::callMediadiff() 34 $this->preference['showIntro'] = false; 35 $this->preference['difftype'] = 'both'; // media diff view type: both, opacity or portions 36 37 parent::__construct($id); 38 } 39 40 /** @inheritdoc */ 41 protected function setChangeLog() 42 { 43 $this->changelog = new MediaChangeLog($this->id); 44 } 45 46 /** @inheritdoc */ 47 protected function preProcess() 48 { 49 parent::preProcess(); 50 if (!isset($this->oldRev, $this->newRev)) { 51 // no revision was given, compare previous to current 52 $changelog =& $this->changelog; 53 $this->oldRev = $changelog->getRevisions(0, 1)[0]; 54 $this->newRev = $changelog->currentRevision(); 55 } 56 } 57 58 /** 59 * Shows difference between two revisions of media 60 * 61 * @author Kate Arzamastseva <pshns@ukr.net> 62 */ 63 public function show() 64 { 65 global $conf; 66 $changelog =& $this->changelog; 67 68 $ns = getNS($this->id); 69 $auth = auth_quickaclcheck("$ns:*"); 70 71 if ($auth < AUTH_READ || !$this->id || !$conf['mediarevisions']) return ''; 72 73 // determine left and right revision 74 if (!isset($this->oldRev, $this->newRev)) $this->preProcess(); 75 76 // use timestamp and '' properly as $rev for the current file 77 if ($changelog->isCurrentRevision($this->newRev)) { 78 [$oldRev, $newRev] = [$this->oldRev, '']; 79 } else { 80 [$oldRev, $newRev] = [$this->oldRev, $this->newRev]; 81 } 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); 143 $form->setHiddenField('rev2[1]', $this->newRev); 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 $changelog =& $this->changelog; 207 208 // revison info of older file (left side) 209 $oldRevInfo = $changelog->getRevisionInfo($this->oldRev); 210 // revison info of newer file (right side) 211 $newRevInfo = $changelog->getRevisionInfo($this->newRev); 212 213 // display diff view table 214 echo '<div class="table">'; 215 echo '<table>'; 216 echo '<tr>'; 217 echo '<th>'. $this->revisionTitle($oldRevInfo) .'</th>'; 218 echo '<th>'. $this->revisionTitle($newRevInfo) .'</th>'; 219 echo '</tr>'; 220 221 echo '<tr class="image">'; 222 echo '<td>'; 223 media_preview($this->id, $auth, $oldRev, $oldRevMeta); // $auth not used in media_preview()? 224 echo '</td>'; 225 226 echo '<td>'; 227 media_preview($this->id, $auth, $newRev, $newRevMeta); 228 echo '</td>'; 229 echo '</tr>'; 230 231 echo '<tr class="actions">'; 232 echo '<td>'; 233 media_preview_buttons($this->id, $auth, $oldRev); // $auth used in media_preview_buttons() 234 echo '</td>'; 235 236 echo '<td>'; 237 media_preview_buttons($this->id, $auth, $newRev); 238 echo '</td>'; 239 echo '</tr>'; 240 241 $l_tags = media_file_tags($oldRevMeta); 242 $r_tags = media_file_tags($newRevMeta); 243 // FIXME r_tags-only stuff 244 foreach ($l_tags as $key => $l_tag) { 245 if ($l_tag['value'] != $r_tags[$key]['value']) { 246 $r_tags[$key]['highlighted'] = true; 247 $l_tags[$key]['highlighted'] = true; 248 } elseif (!$l_tag['value'] || !$r_tags[$key]['value']) { 249 unset($r_tags[$key]); 250 unset($l_tags[$key]); 251 } 252 } 253 254 echo '<tr>'; 255 foreach (array($l_tags, $r_tags) as $tags) { 256 echo '<td>'; 257 258 echo '<dl class="img_tags">'; 259 foreach ($tags as $tag) { 260 $value = cleanText($tag['value']); 261 if (!$value) $value = '-'; 262 echo '<dt>'.$lang[$tag['tag'][1]].'</dt>'; 263 echo '<dd>'; 264 if ($tag['highlighted']) echo '<strong>'; 265 if ($tag['tag'][2] == 'date') { 266 echo dformat($value); 267 } else { 268 echo hsc($value); 269 } 270 if ($tag['highlighted']) echo '</strong>'; 271 echo '</dd>'; 272 } 273 echo '</dl>'; 274 275 echo '</td>'; 276 } 277 echo '</tr>'; 278 279 echo '</table>'; 280 echo '</div>'; 281 } 282 283 /** 284 * Revision Title for MediaDiff table headline 285 * 286 * @param array $info Revision info structure of a media file 287 * @return string 288 */ 289 protected function revisionTitle(array $info) 290 { 291 global $lang, $INFO; 292 293 if (isset($info['date'])) { 294 $rev = $info['date']; 295 $title = '<bdi><a class="wikilink1" href="'.ml($this->id, ['rev' => $rev]).'">' 296 . dformat($rev).'</a></bdi>'; 297 } else { 298 $rev = false; 299 $title = '—'; 300 } 301 if (isset($info['current']) || ($rev && $rev == $INFO['currentrev'])) { 302 $title .= ' ('.$lang['current'].')'; 303 } 304 305 // append separator 306 $title .= ($this->preference['difftype'] === 'inline') ? ' ' : '<br />'; 307 308 // supplement 309 if (isset($info['date'])) { 310 $objRevInfo = (new MediaRevisions($this->id))->getObjRevInfo($info); 311 $title .= $objRevInfo->editSummary().' '.$objRevInfo->editor(); 312 } 313 return $title; 314 } 315 316} 317