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