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