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