1<?php 2/** 3 * DokuWiki Plugin photogallery (Syntax Component) 4 * Embed an image gallery 5 * 6 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 7 * @author Marco Nolletti 8 * @contributors Michael Große 9 */ 10 11 // must be run within Dokuwiki 12if(!defined('DOKU_INC')) die(); 13 14require_once('inc/pgdefines.php'); 15require_once(DOKU_PLUGIN.'syntax.php'); 16require_once(DOKU_INC.'inc/search.php'); 17require_once(DOKU_INC.'inc/JpegMeta.php'); 18require_once('lib/array_column.php'); 19 20class syntax_plugin_photogallery extends DokuWiki_Syntax_Plugin { 21 22 protected $metaAliases = null; 23 24 /** 25 * What kind of syntax are we? 26 */ 27 function getType(){ 28 return 'substition'; 29 } 30 31 /** 32 * What about paragraphs? 33 */ 34 function getPType(){ 35 return 'block'; 36 } 37 38 /** 39 * Where to sort in? 40 */ 41 function getSort(){ 42 return 155; 43 } 44 45 /** 46 * Connect pattern to lexer 47 */ 48 function connectTo($mode) { 49 $this->Lexer->addSpecialPattern('----+ *photogallery(?: [ a-zA-Z0-9_]*)?-+\n.*?\n?----+', $mode, 'plugin_photogallery'); 50 } 51 52 /** 53 * Handle the match - parse the data 54 * 55 * @param string $match The text matched by the patterns 56 * @param int $state The lexer state for the match 57 * @param int $pos The character position of the matched text 58 * @param Doku_Handler $handler The Doku_Handler object 59 * @return bool|array Return an array with all data you want to use in render, false don't add an instruction 60 */ 61 function handle($match, $state, $pos, Doku_Handler $handler){ 62 global $ID; 63 64 $data = array(); 65 66 // get lines 67 $lines = explode("\n", $match); 68 array_pop($lines); 69 70 // get command 71 $cmd = array_shift($lines); 72 $cmd = str_replace('photogallery', '', $cmd); 73 $cmd = trim($cmd, '- '); 74 if (!strpos('show|link',$cmd)) { 75 $cmd = 'show'; 76 } 77 $data['command'] = $cmd; 78 79 // set the defaults 80 $data['phpthumb'] = $this->getConf('use_phpThumb'); 81 $data['autoplay'] = $this->getConf('autoplay'); 82 $data['pw'] = $this->getConf('poster_width'); 83 $data['ph'] = $this->getConf('poster_height'); 84 $data['tw'] = $this->getConf('thumbnail_width'); 85 $data['th'] = $this->getConf('thumbnail_height'); 86 $data['iw'] = $this->getConf('viewport_width'); 87 $data['ih'] = $this->getConf('viewport_height'); 88 $data['vprot'] = $this->getConf('viewport_rotate'); 89 $data['panar'] = $this->getConf('panorama_ratio'); 90 $data['panw'] = $this->getConf('panorama_width'); 91 $data['panh'] = $this->getConf('panorama_height'); 92 $data['posteralign'] = $this->getConf('posteralign'); 93 $data['filter'] = ''; 94 $data['fullsize'] = $this->getConf('fullsize'); 95 $data['sort'] = $this->getConf('sort'); 96 $data['limit'] = 0; 97 $data['offset'] = 0; 98 $data['ns'] = getNS($ID); 99 $this->_setConfOptions($data,$this->getConf('options')); 100 101 // parse additional options 102 $params = $this->getConf('options'); 103 $params = preg_replace('/[,&\?]+/',' ',$params); 104 $params = explode(' ',$params); 105 foreach($params as $param){ 106 if($param === '') continue; 107 if($param == 'titlesort'){ 108 $data['sort'] = 'title'; 109 }elseif($param == 'datesort'){ 110 $data['sort'] = 'date'; 111 }elseif($param == 'modsort'){ 112 $data['sort'] = 'mod'; 113 }elseif(preg_match('/^=(\d+)$/',$param,$match)){ 114 $data['limit'] = $match[1]; 115 }elseif(preg_match('/^\+(\d+)$/',$param,$match)){ 116 $data['offset'] = $match[1]; 117 }elseif(is_numeric($param)){ 118 $data['cols'] = (int) $param; 119 }elseif(preg_match('/^(\d+)([xX])(\d+)$/',$param,$match)){ 120 if($match[2] == 'X'){ 121 $data['iw'] = $match[1]; 122 $data['ih'] = $match[3]; 123 }else{ 124 $data['tw'] = $match[1]; 125 $data['th'] = $match[3]; 126 } 127 }elseif(strpos($param,'*') !== false){ 128 $param = preg_quote($param,'/'); 129 $param = '/^'.str_replace('\\*','.*?',$param).'$/'; 130 $data['filter'] = $param; 131 }else{ 132 if(substr($param,0,2) == 'no'){ 133 $data[substr($param,2)] = false; 134 }else{ 135 $data[$param] = true; 136 } 137 } 138 } 139 140 // Check phpThumb requirements 141 if ($data['phpthumb'] == true){ 142 if (!$this->_phpThumbCheck()){ 143 msg($this->getLang('phpthumbdisabled'),2); 144 $data['phpthumb'] = false; 145 } 146 } 147 148 // parse info 149 foreach ($lines as $line) { 150 // ignore comments 151 preg_match('/^(.*?(?<![&\\\\]))(?:#(.*))?$/', $line, $matches); 152 $line = $matches[1]; 153 $line = str_replace('\\#', '#', $line); 154 $line = trim($line); 155 if (empty($line)) continue; 156 $line = preg_split('/\s*:\s*/', $line, 2); 157 if ($line[0] == 'namespace') $line[0] = 'ns'; 158 if ($line[0] == 'page') $line[0] = 'pg'; 159 160 if ($line[0] == 'ns'){ 161 if (preg_match('/^https?:\/\//i',$line[1])) 162 $data['rss'] = true; 163 else 164 $line[1] = resolve_id(getNS($ID),$line[1]); 165 } 166 if ($line[0] == 'pg') { 167 $line[1] = resolve_id(getNS($ID),$line[1]); 168 } 169 170 // decode height x width values [pti]size strings 171 if (preg_match('/^([pti])(size)$/',$line[0],$type)) { 172 if (preg_match('/^(\d+)([xX])(\d+)$/',$line[1],$size)) { 173 $data[$type[1].'w'] = $size[1]; 174 $data[$type[1].'h'] = $size[3]; 175 } 176 } 177 178 // handle negated options, converts "!crop" to "crop = false" 179 if (!$line[1]) { 180 if (preg_match('/^\!.{1,}/', $line[0], $matches)) 181 $line[0] = substr($matches[0],1); 182 else 183 $line[1] = true; 184 } 185 $data[$line[0]] = $line[1]; 186 } 187 188 // If in link mode, read instructions from linked page 189 if ($cmd == 'link'){ 190 $page = $data['pg']; 191 if (page_exists($page)){ 192 $instr = p_cached_instructions(wikiFN($page),false,$page); 193 } 194 if (isset($instr)){ 195 foreach($instr as $sec){ //NOM forse si può usare array search 196 if ($sec[0] == 'plugin'){ 197 if ($sec[1][0] == 'photogallery'){ 198 $rdata = $sec[1][1]; 199 } 200 } 201 if (isset($rdata)){ 202 $data['ns'] = $rdata['ns']; 203 foreach ($rdata as $key => $value){ 204 if ((!isset($data[$key])) and (isset($rdata[$key]))){ 205 $data[$key] = $value; 206 } 207 } 208 break; 209 } 210 } 211 } 212 } 213 return $data; 214 } 215 216 /** 217 * Create output or save the data 218 * 219 * @param $format string output format being rendered 220 * @param $renderer Doku_Renderer the current renderer object 221 * @param $data array data created by handler() 222 * @return boolean rendered correctly? 223 */ 224 function render($mode, Doku_Renderer $R, $data){ 225 global $ID; 226 global $conf; 227 228 $this->metaAliases = $this->getMetaTagAliases(); 229 230 $cmd = $data['command']; 231 if($mode == 'xhtml'){ 232 233 if($this->_auth_check($data)){ 234 $R->info['cache'] = false; // Disable global render cache 235 $this->_photo_gallery($data, $R); // Start gallery 236 } 237 elseif($cmd == 'show') 238 msg(sprintf($this->getLang('notauthorized'),$data['ns']),-1); 239 return true; 240 }elseif($mode == 'metadata'){ // NOM da rivedere 241 $rel = p_get_metadata($ID,'relation',METADATA_RENDER_USING_CACHE); 242 $img = $rel['firstimage']; 243 if(empty($img)){ 244 $files = $this->_findimages($data); 245 } 246 return true; 247 } 248 return false; 249 } 250 251 function _phpThumbCheck(){ 252 $fperm = fileperms(PHOTOGALLERY_PGFETCH_FILE); 253 if (($fperm & PHOTOGALLERY_PGFETCH_EXE_PERM) != PHOTOGALLERY_PGFETCH_EXE_PERM){ 254 msg($this->getLang('phpthumbexecerror'),-1); 255 if (@chmod(PHOTOGALLERY_PGFETCH_FILE, $fperm | PHOTOGALLERY_PGFETCH_EXE_PERM)){ 256 msg($this->getLang('phpthumbexecpermset'),1); 257 return true; 258 } 259 else{ 260 msg($this->getLang('phpthumbpermseterror'),-1); 261 return false; 262 } 263 } 264 return true; 265 } 266 267 /** 268 * Does the gallery formatting 269 */ 270 function _photo_gallery($data, $R){ 271 global $conf; 272 global $lang; 273 global $ID; 274 global $auth; 275 global $USERINFO; 276 277 $cmd = $data['command']; 278 if (empty($data['rss'])) { 279 if(($cmd == 'show') and (!$this->_media_folder_exists($data['ns']))){ 280 $R->doc .= '<div class="nothing">'.sprintf($this->getLang('nsnotexists'),$data['ns']).'</div>'; 281 return true; 282 }elseif (($cmd == 'link') and (!page_exists($data['pg']))){ 283 $R->doc .= '<div class="nothing">'.sprintf($this->getLang('pgnotexists'),$data['pg']).'</div>'; 284 return true; 285 } 286 } 287 288 $files = $this->_findimages($data); 289 290 // anything found? 291 if(!count($files)){ 292 $R->doc .= '<div class="nothing">'.$lang['nothingfound'].'</div>'; 293 return; 294 } 295 296 // in not exists create in the media folder a zip file containing all the images and link it 297 if (isset($data['zipfile'])) 298 if (class_exists('ZipArchive')){ 299 $zip = $data['ns'].":".$data['zipfile']; 300 $this->_createzipfile($files, mediaFN($zip)); 301 if($this->_zip_auth_check($data)){ 302 $data['ziplink'] = $R->internalmedia($zip,$this->getLang('lnk_download'),null,null,null,null,'linkonly',true); 303 } 304 } 305 else 306 msg($this->getLang('zipdisabled'),2); 307 308 // output pg-container 309 $R->doc .= '<div class="pg-container">'.DOKU_LF; 310 311 // output pg-poster and pg-description 312 if ($data['posteralign'] == 'right'){ 313 $this->_description($files,$data,$R); 314 $this->_poster($files,$data,$R); 315 } 316 else{ 317 $this->_poster($files,$data,$R); 318 $this->_description($files,$data,$R); 319 } 320 321 // Close container 322 $R->doc .= '</div>'.DOKU_LF; 323 return; 324 } 325 326 /** 327 * Gather all photos matching the given criteria 328 */ 329 function _findimages(&$data){ 330 global $conf; 331 $files = array(); 332 333 // is a media RSS feed ? 334 if (!empty($data['rss'])) { 335 $files = $this->_loadRSS($data['ns']); 336 } else { 337 $dir = utf8_encodeFN(str_replace(':','/',$data['ns'])); 338 // just current level or deep recursion into namespace? 339 $depth = !empty($data['recursive']) ? 0 : 1; 340 search($files, 341 $conf['mediadir'], 342 'search_media', 343 array('depth'=>$depth), 344 $dir); 345 } 346 347 // done, yet? 348 $len = count($files); 349 if (!$len) return $files; 350 if ($len == 1) return $files; 351 // filter images 352 for ($i=0; $i<$len; $i++) { 353 if ($data['fullsize'] == true) 354 $files[$i]['fullsize'] = true; 355 $fname = $files[$i]['file']; 356 if (preg_match('/\_([a-z]+?)\_\.(jpe?g|gif|png)$/',$fname,$matches)){ 357 $modifier = $matches[1]; 358 if(($modifier == 'fullsize') || ($data['fullsize'] == 1)) // Show in full size 359 $files[$i]['fullsize'] = true; 360 elseif($modifier == 'poster') // Is a video poster image, remove from list 361 $files[$i]['isimg'] = false; 362 } 363 if (!$files[$i]['isimg']) { 364 if (preg_match('/(.*?)\.(avi|mov|mp4)$/',$fname,$matches)) { // Is a video 365 $files[$i]['isvid'] = true; 366 $poster = getNS($files[$i]['id']).':'.$matches[1].'_poster_.jpg'; // NOM: così i poster possono solo essere jpeg 367 if(media_exists($poster)) $files[$i]['poster'] = $poster; 368 } 369 else { 370 array_splice($files, $i, 1); // unset will not reindex the array, so putting the poster on first position fails 371 $len--; 372 $i--; 373 } 374 } else { 375 if ($data['filter']) { 376 if(!preg_match($data['filter'],noNS($files[$i]['id']))) 377 unset($files[$i]); // NOM da verificare unset come sopra se si decide di usare filter 378 } 379 } 380 } 381 if ($len<1) return $files; 382 383 // sort? 384 if ($data['sort'] == 'random') { 385 shuffle($files); 386 } else { 387 if( $data['sort'] == 'date') { 388 usort($files,array($this,'_datesort')); 389 } elseif ($data['sort'] == 'mod') { 390 usort($files,array($this,'_modsort')); 391 } elseif($data['sort'] == 'title') { 392 usort($files,array($this,'_titlesort')); 393 } 394 // reverse? 395 if (!empty($data['reverse'])) $files = array_reverse($files); 396 } 397 398 // offset? 399 if( $data['offset']) { 400 $offset = $data['offset']; 401 $files = array_slice($files, $offset); 402 } else { 403 $offset = 0; 404 } 405 406 // puts poster element in first array position 407 $i = array_search($data['posterimg'] ?? [], array_column($files, 'file')); 408 if ($i != $offset){ 409 $tmp = $files[$offset]; 410 $files[$offset] = $files[$i]; 411 $files[$i] = $tmp; 412 } 413 414 // limit? 415 if ($data['limit']) 416 $files = array_slice($files,0,$data['limit']); 417 418 return $files; 419 } 420 421 /** 422 * Loads images from a MediaRSS or ATOM feed 423 */ 424 function _loadRSS($url){ 425 require_once(DOKU_INC.'inc/FeedParser.php'); 426 $feed = new FeedParser(); 427 $feed->set_feed_url($url); 428 $feed->init(); 429 $files = array(); 430 431 // base url to use for broken feeds with non-absolute links 432 $main = parse_url($url); 433 $host = $main['scheme'].'://'. 434 $main['host']. 435 (($main['port'])?':'.$main['port']:''); 436 $path = dirname($main['path']).'/'; 437 438 foreach($feed->get_items() as $item){ 439 if ($enclosure = $item->get_enclosure()){ 440 // skip non-image enclosures 441 if($enclosure->get_type()){ 442 if(substr($enclosure->get_type(),0,5) != 'image') continue; 443 }else{ 444 if(!preg_match('/\.(jpe?g|png|gif)(\?|$)/i', 445 $enclosure->get_link())) continue; 446 } 447 448 // non absolute links 449 $ilink = $enclosure->get_link(); 450 if(!preg_match('/^https?:\/\//i',$ilink)){ 451 if($ilink[0] == '/'){ 452 $ilink = $host.$ilink; 453 }else{ 454 $ilink = $host.$path.$ilink; 455 } 456 } 457 $link = $item->link; 458 if(!preg_match('/^https?:\/\//i',$link)){ 459 if($link[0] == '/'){ 460 $link = $host.$link; 461 }else{ 462 $link = $host.$path.$link; 463 } 464 } 465 466 $files[] = array( 467 'id' => $ilink, 468 'isimg' => true, 469 'file' => basename($ilink), 470 // decode to avoid later double encoding 471 'title' => htmlspecialchars_decode($enclosure->get_title(),ENT_COMPAT), 472 'desc' => strip_tags(htmlspecialchars_decode($enclosure->get_description(),ENT_COMPAT)), 473 'width' => $enclosure->get_width(), 474 'height' => $enclosure->get_height(), 475 'mtime' => $item->get_date('U'), 476 'ctime' => $item->get_date('U'), 477 'detail' => $link, 478 ); 479 } 480 } 481 return $files; 482 } 483 484 /** 485 * usort callback to sort by file lastmodified time 486 */ 487 function _modsort($a,$b){ 488 if($a['mtime'] < $b['mtime']) return -1; 489 if($a['mtime'] > $b['mtime']) return 1; 490 return strcmp($a['file'],$b['file']); 491 } 492 493 /** 494 * usort callback to sort by EXIF date 495 */ 496 function _datesort($a,$b){ 497 $da = $this->_meta($a,'cdate'); 498 $db = $this->_meta($b,'cdate'); 499 if($da < $db) return -1; 500 if($da > $db) return 1; 501 return strcmp($a['file'],$b['file']); 502 } 503 504 /** 505 * usort callback to sort by EXIF title 506 */ 507 function _titlesort($a,$b){ 508 $ta = $this->_meta($a,'title'); 509 $tb = $this->_meta($b,'title'); 510 return strcmp($ta,$tb); 511 } 512 513 /** 514 * Does the lightgallery gallery formatting 515 */ 516 function _lightgallery($files,$data,$pgid){ 517 $ret = ''; 518 $ret .= '<ul id="'.$pgid.'" class="pg-show">'.DOKU_LF; 519 520 $page = 0; 521 522 // build gallery 523 $close_pg = false; 524 525 $i = 0; 526 foreach($files as $img){ 527 $ret .= $this->_image($img,$data,$i); 528 $i++; 529 } 530 531 // Close containers 532 $ret .= '</ul>'.DOKU_LF; 533 return $ret; 534 } 535 536 /** 537 * Defines how a poster should look like 538 */ 539 function _poster($files,$data,$R){ 540 $pgid = 'pg-'.substr(md5($data['ns']),4); 541 if ($data['posteralign'] == 'right') 542 $R->doc .= '<div class="pg-poster pg-right">'.DOKU_LF; 543 else 544 $R->doc .= '<div class="pg-poster pg-left">'.DOKU_LF; 545 546 $img = $files[0]; 547 $cmd = $data['command']; 548 549 // calculate poster size 550 $w = $data['pw']; 551 $h = $data['ph']; 552 553 $dim = array('w'=>$w,'h'=>$h); 554 555 //prepare link attributes 556 $a = array(); 557 if ($cmd == 'show'){ 558 $href = ''; 559 $a['data-pg-id'] = $pgid; 560 $a['class'] = 'pg-start'; 561 } 562 else{ 563 $href = wl($data['pg'], 'gallery0#lg=1&slide=0'); 564 } 565 $aatt = buildAttributes($a); 566 567 //prepare img attributes 568 $i = array(); 569 $src = ml($img['id'],$dim); 570 571 $i['width'] = $w; 572 $i['height'] = $h; 573 $i['alt'] = $this->_meta($img,'title'); 574 $iatt = buildAttributes($i); 575 576 // Generate output 577 $R->doc .= '<a href="'.$href.'" '.$aatt.'>'.DOKU_LF; 578 $R->doc .= '<img src="'.$src.'" '.$iatt.'/>'.DOKU_LF; 579 $R->doc .= '<div class="pg-zoom">'; 580 $R->doc .= '<img src="'.PHOTOGALLERY_IMAGES_REL.'zoom.png" alt=""/>'; 581 $R->doc .= '</div>'.DOKU_LF; 582 $R->doc .= '</a>'.DOKU_LF; 583 584 if ($cmd == 'show'){ 585 $R->doc .= $this->_lightgallery($files,$data,$pgid); 586 587 // Call lightGallery init function 588 $ch = strval(intval($data['th'])+20); 589 $auto = $data['autoplay'] ? 'true' : 'false'; 590 $R->doc .= '<script type="text/javascript">/*<![CDATA[*/'.DOKU_LF; 591 $R->doc .= 'jQuery(function(){'; 592 $R->doc .= 'InitPgGallery('.$data['tw'].','.$ch.','.$auto.');'; 593 $R->doc .= '});'.DOKU_LF; 594 $R->doc .= '/*!]]>*/</script>'.DOKU_LF; 595 596 // Override styles to match thumb size 597 $R->doc .= '<style>.lg-outer.lg-pull-caption-up.lg-thumb-open .lg-sub-html {bottom:'.$ch.'px;}</style>'; 598 } 599 $R->doc .= '</div>'.DOKU_LF; 600 } 601 602 /** 603 * Defines how a description should look like 604 */ 605 protected function _description($files,$data,$R){ 606 $imgcnt = 0; 607 $vidcnt = 0; 608 foreach ($files as $file){ 609 if ($file['isimg']) 610 $imgcnt++; 611 elseif ($file['isvid']) 612 $vidcnt++; 613 } 614 if ($data['posteralign'] == 'right') 615 $R->doc .= '<div class="pg-description pg-left">'.DOKU_LF; 616 else 617 $R->doc .= '<div class="pg-description pg-right">'.DOKU_LF; 618 619 $R->header($data['title'],2,0); 620 $R->doc .= '<p>' . ($data['description'] ?? '') . '</p>'; 621 $R->doc .= '<p>'; 622 $info = ''; 623 $this->_addString($info,$imgcnt,sprintf($this->getLang('imagescnt'),$imgcnt)); 624 $this->_addString($info,$vidcnt,sprintf($this->getLang('videoscnt'),$vidcnt),', '); 625 $R->doc .= $info; 626 if (isset($data['ziplink'])){ 627 $R->doc .= ' - '.$data['ziplink']; 628 } 629 $R->doc .= '</p>'; 630 $R->doc .= '<p style="text-align:left"><i>' . ($data['copyright'] ?? '') . '</i></p>'.DOKU_LF; 631 $R->doc .= '</div>'.DOKU_LF; 632 } 633 634 /** 635 * Defines the lightGallery images markup 636 */ 637 protected function _image(&$img,$data,$idx){ 638 639 // unsupported file 640 if (empty($img['isimg']) && empty($img['isvid'])) return ''; 641 642 $tpar = array(); 643 $ipar = array(); 644 $ID = $img['id']; 645 $tw = $data['tw']; 646 $th = $data['th']; 647 // NOM Sistemare le dimensioni dei poster dei video 648 if (!empty($img['isvid'])){ 649 $vsrc = ml($ID); 650 //$vsrc = ml($ID,$tdim); 651 $topt = 'zc=C'; // Crop to given size 652 if ( !empty($img['poster'])){ 653 $ID = $img['poster']; 654 $topt .= '!fltr=over|../images/video_frame.png'; 655 $img['meta'] = new JpegMeta(mediaFN($ID)); 656 $mw = (int) $this->_meta($img,'width'); 657 $mh = (int) $this->_meta($img,'height'); 658 $iw = $data['iw']; 659 $ih = $data['ih']; 660 } else { 661 $tpar['src'] = 'video_thumb.png'; 662 $ipar['src'] = 'video_poster.jpg'; 663 $iw = ''; 664 $ih = ''; 665 } 666 } else { 667 $mw = (int) $this->_meta($img,'width'); 668 $mh = (int) $this->_meta($img,'height'); 669 // Test for panorama aspect ratio 670 $img_ar = ($mw > $mh ? $mw/$mh : $mh/$mw); 671 if (preg_match('/([0-9]+):([0-9]+)/',$data['panar'],$matches)) 672 $max_ar = $matches[1]/$matches[2]; 673 $ispan = ($img_ar > $max_ar); 674 if ($ispan){ 675 $vpw = $data['panw']; 676 $vph = $data['panh']; 677 } else{ 678 $vpw = $data['iw']; 679 $vph = $data['ih']; 680 } 681 if (($mh > $mw) and ($data['vprot'])) // Invert viewport for portrait images 682 list($vpw,$vph) = array($vph,$vpw); 683 if ($ispan) { // Panorama aspect ratio 684 if ($data['phpthumb'] == true) { 685 $topt = 'far=1'; // Force aspect ratio 686 if ($mw > $mh){ // Landscape 687 $tw = floor($data['th'] * 0.6 * $img_ar); 688 $cropw = floor(($tw - $data['tw']) / 2); 689 $topt .= "!fltr=crop|$cropw|$cropw"; 690 $topt .= '!fltr=over|../images/pano_landscape.png'; 691 } else{ // Portrait or square 692 $th = floor($data['tw'] * 0.6 * $img_ar); 693 $croph = floor(($th - $data['th']) / 2); 694 $topt .= "!fltr=crop|0|0|$croph|$croph"; 695 $topt .= '!fltr=over|../images/pano_portrate.png'; 696 } 697 } 698 } else { // Normal image 699 $topt = 'zc=C'; // Crop to given size 700 } 701 // Calculates new image sizes fitting into viewport 702 if (!empty($img['fullsize'])) { // Override image size for fullsize 703 $iw = $mw; 704 $ih = $mh; 705 } else { 706 if (!empty($data['crop'])) { 707 $ratio = $this->_fill_ratio($mw,$mh,$vpw,$vph); 708 $iw = floor($mw * $ratio); 709 $ih = floor($mh * $ratio); 710 if ($iw > $vpw) 711 $iw = $vpw; 712 if ($ih > $vph) 713 $ih = $vph; 714 $iopt = 'zc=C'; // Crop to given size 715 } else { 716 $ratio = $this->_fit_ratio($mw,$mh,$vpw,$vph); 717 $iw = floor($mw * $ratio); 718 $ih = floor($mh * $ratio); 719 $iopt = 'iar=1'; // Simple resize 720 } 721 } 722 // Shows HR overlay 723 if ($iw * $ih > 12000000){ 724 $topt .= '!fltr=over|../images/image_hr.png'; 725 } 726 } 727 728 $tpar['w'] = $tw; 729 $tpar['h'] = $th; 730 $ipar['w'] = $iw; 731 $ipar['h'] = $ih; 732 if (!empty($data['rss'])) 733 $tpar['media'] = $ID; 734 else { 735 $tpar['media'] = idfilter($ID); 736 $ipar['media'] = $tpar['media']; 737 } 738 if ($data['phpthumb'] == true) { 739 $tpar['opt'] = $topt; 740 $ipar['opt'] = $iopt; 741 } 742 $ipar['tok'] = media_get_token($ID,$iw,$ih); 743 $tpar['tok'] = media_get_token($ID,$tw,$th); 744 $isrc = PHOTOGALLERY_PGFETCH_REL.'?'. buildURLparams($ipar,'&'); 745 $tsrc = PHOTOGALLERY_PGFETCH_REL.'?'. buildURLparams($tpar,'&'); 746 // prepare attributes 747 $ta = array(); 748 $ta['alt'] = $this->_caption($img,$data); 749 $tatt = buildAttributes($ta); 750 // HTML rendering 751 $ret =''; 752 $video = ''; 753 if (!empty($img['isvid'])) { 754 $video .= '<div id="video'.$idx.'" style="display:none;">'.DOKU_LF; 755 $video .= '<video class="lg-video-object lg-html5" controls preload="metadata">'; 756 $video .= '<source src="'.$vsrc.'" type="video/mp4">'; 757 $video .= 'HTML5 video not supported.'; 758 $video .= '</video>'.DOKU_LF; 759 $video .= '</div>'.DOKU_LF; 760 $ret .= '<li data-poster="'.$isrc.'" data-html="#video'.$idx.'">'.DOKU_LF; 761 } else { 762 $ret .= '<li data-src="'.$isrc.'">'.DOKU_LF; 763 } 764 $ret .= '<img src="'.$tsrc.'" '.$tatt.'/>'.DOKU_LF; 765 $ret .= $video; 766 $ret .= '</li>'.DOKU_LF; 767 return $ret; 768 } 769 770 /** 771 * Return the metadata of an item 772 * 773 * Automatically checks if a JPEGMeta object is available or if all data is 774 * supplied in array 775 */ 776 function _meta($img,$opt){ 777 if (!empty($img['meta'])) { 778 // map JPEGMeta calls to opt names 779 switch($opt){ 780 case 'title': 781 return $img['meta']->getField($this->metaAliases['img_title']); 782 case 'desc': 783 return $img['meta']->getField($this->metaAliases['img_caption']); 784 case 'keywords': 785 return $img['meta']->getField($this->metaAliases['img_keywords']); 786 case 'cdate': 787 return $img['meta']->getField($this->metaAliases['img_date']); 788 case 'width': 789 return $img['meta']->getField($this->metaAliases['img_width']); 790 case 'height': 791 return $img['meta']->getField($this->metaAliases['img_height']); 792 default: 793 return ''; 794 } 795 }else{ 796 // just return an empty field 797 return $img[$opt] ?? ''; 798 } 799 } 800 801 /** 802 * Use the existing DokuWiki-configuration to find all aliases for a given image meta-information 803 * 804 * This is based on @see tpl_get_img_meta() 805 * 806 * @return array 807 */ 808 protected function getMetaTagAliases() { 809 $config_files = getConfigFiles('mediameta'); 810 foreach ($config_files as $config_file) { 811 if(file_exists($config_file)) { 812 include($config_file); 813 } 814 } 815 /** @var array $fields the included array with metadata */ 816 817 $tagAliases = array(); 818 foreach($fields as $tag){ 819 $t = array(); 820 if (!empty($tag[0])) { 821 $t = array($tag[0]); 822 } 823 if (isset($tag[3]) && is_array($tag[3])) { 824 $t = array_merge($t,$tag[3]); 825 } 826 $tagAliases[$tag[1]] = $t; 827 } 828 return $tagAliases; 829 } 830 831 /** 832 * Calculates the multiplier needed to resize the image to the given 833 * dimensions 834 * 835 */ 836 function _fit_ratio($w, $h, $maxw, $maxh){ 837 $ratio = 1; 838 if($w > $maxw){ 839 $ratio = $maxw/$w; 840 if($h * $ratio > $maxh){ 841 $ratio = $maxh/$h; 842 } 843 } 844 elseif($h > $maxh){ 845 $ratio = $maxh/$h; 846 if($w * $ratio > $maxw){ 847 $ratio = $maxw/$w; 848 } 849 } 850 return $ratio; 851 } 852 853 function _fill_ratio($w, $h, $maxw, $maxh){ 854 $ratio = 1; 855 if($w > $maxw){ 856 $ratio = $maxw/$w; 857 if($h * $ratio < $maxh){ 858 $ratio = $maxh/$h; 859 } 860 } 861 elseif($h > $maxh){ 862 $ratio = $maxh/$h; 863 if($w * $ratio < $maxw){ 864 $ratio = $maxw/$w; 865 } 866 } 867 return $ratio; 868 } 869 870 /** 871 * Return the caption for the image 872 */ 873 function _caption($img,$data){ 874 $ret = ''; 875 if (!empty($data['showtitle'])) { 876 $title = $this->_meta($img,'title'); 877 if(isset($title)){ 878 $ret .= '<h4>'.hsc($title).'</h4>'; 879 } 880 } 881 if (!empty($data['showdescription'])) { 882 $desc = $this->_meta($img,'desc'); 883 if(!empty($desc)){ 884 $ret .= '<p>'.nl2br(hsc($desc)).'</p>'; 885 } 886 } 887 if (!empty($data['showkeywords'])) { 888 $keywords = $this->_meta($img,'keywords'); 889 if(!empty($keywords)){ 890 $ret .= '<p>'.hsc($keywords).'</p>'; 891 } 892 } 893 if (!empty($data['showinfo'])) { 894 $ret .= $this->_exif($img); 895 } 896 if (!empty($data['showfname'])) { 897 $ret .= '<p>'.hsc($img['file']).'</p>'; 898 } 899 if (!empty($data['showlink'])) { 900 $ret .= '<p><a href="'.ml($img['id'], '', false).'">' . 901 '<img title="Details" src="' . DOKU_BASE . 902 'lib/plugins/photogallery/images/details_page.png" width="30" /></a></p>'; 903 } 904 return $ret; 905 } 906 907 /** 908 * Return the EXIF data for the image 909 */ 910 function _exif($img){ 911 // Read EXIF data 912 $jpeg = $img['meta'] ?? null; 913 $ret = ''; 914 if($jpeg){ 915 $make = $jpeg->getField(array('Exif.Make','Exif.TIFFMake')); 916 $model = $jpeg->getField(array('Exif.Model','Exif.TIFFModel')); 917 $model = preg_replace('/^'.$make.' /','',$model); 918 $shutter = $jpeg->getShutterSpeed(); 919 $fnumber = $jpeg->getField(array('Exif.FNumber')); 920 $iso = $jpeg->getField(array('Exif.ISOSpeedRatings')); 921 $date = $jpeg->getDateField('EarliestTimeStr'); 922 $yy = substr($date ,0,4); 923 $mm = substr($date ,5,2); 924 $dd = substr($date ,8,2); 925 $date = $dd.'/'.$mm.'/'.$yy; 926 $ret .= $date; 927 $this->_addString($ret,$make.$model,$make.' '.$model, ' - '); 928 $this->_addString($ret,$shutter,$shutter.'s',', '); 929 $this->_addString($ret,$fnumber,'f/'.$fnumber,', '); 930 $this->_addString($ret,$iso,'ISO '.$iso,', '); 931 $this->_addString($ret,$ret,null,null,'<p>','</p>'); 932 } 933 return $ret; 934 } 935 936 /** 937 * Creates a compressed zip file 938 */ 939 function _createzipfile($files,$zipfile,$overwrite = false) { 940 //if the zip file already exists and overwrite is false, return false 941 if(file_exists($zipfile) && !$overwrite) return false; 942 if(count($files)) { 943 //create the archive 944 $zip = new ZipArchive(); 945 if($zip->open($zipfile,$overwrite ? ZIPARCHIVE::OVERWRITE : ZIPARCHIVE::CREATE) !== true) { 946 return false; 947 } 948 //add the files 949 foreach($files as $img) { 950 $file = mediaFN($img['id']); 951 $zip->addFile($file,basename(dirname($file)).'/'.basename($file)); 952 } 953 954 //close the zip -- done! 955 $zip->close(); 956 957 //check to make sure the file exists 958 return file_exists($zipfile); 959 } 960 else { 961 return false; 962 } 963 } 964 965 /** 966 * Check ACLs on Gallery 967 */ 968 protected function _auth_check($data){ 969 global $USERINFO; 970 global $INPUT; 971 global $auth; 972 global $conf; 973 974 if (!$auth) return false; 975 976 $user = $INPUT->server->str('REMOTE_USER'); 977 if (is_null($user)) return false; 978 979 $groups = $USERINFO['grps'] ?? []; 980 981 if (!empty($data['authlist'])) { 982 $authlist = $data['authlist'] . ','. $conf['superuser']; 983 return auth_isMember($authlist, $user, $groups); 984 } 985 return true; 986 } 987 988 /** 989 * Check ACLs on Zip link 990 */ 991 function _zip_auth_check($data){ 992 global $INPUT; 993 global $USERINFO; 994 global $auth; 995 global $conf; 996 997 if(!$auth) return false; 998 $user = $INPUT->server->str('REMOTE_USER'); 999 1000 if(is_null($user)) return false; 1001 $groups = $USERINFO['grps'] ?? []; 1002 $authlist = $data['zipauthlist']; 1003 if (isset($authlist)){ 1004 $authlist .= ','.$conf['superuser']; 1005 return auth_isMember($authlist, $user, $groups); 1006 } 1007 else 1008 return true; 1009 } 1010 1011 /** 1012 * Return if a namespace has exists as media folder 1013 */ 1014 function _media_folder_exists($ns){ 1015 global $conf; 1016 return is_dir($conf['mediadir'].'/'.utf8_encodeFN(str_replace(':','/',$ns))); 1017 } 1018 1019 /** 1020 * Sets additional comma separated options 1021 */ 1022 function _setConfOptions(&$data, $optstr){ 1023 $opts = explode(',', $optstr); 1024 foreach ($opts as $opt) 1025 $data[trim($opt)] = true; 1026 } 1027 1028 /** 1029 * Adds a string to $source only if $check is true. 1030 */ 1031 function _addString(&$source, $check, $value = '', $separator = '', $prefix = '', $suffix = ''){ 1032 if($check){ 1033 if($source) 1034 $source .= $separator; 1035 $source .= $value; 1036 $source = $prefix.$source.$suffix; 1037 } 1038 } 1039} 1040// $jpeg = new JpegMeta(mediaFN($img['id'])); 1041 // if($ext == 'jpg' || $ext == 'jpeg') { 1042 // //try to use the caption from IPTC/EXIF 1043 // require_once(DOKU_INC.'inc/JpegMeta.php'); 1044 // $jpeg = new JpegMeta(mediaFN($src)); 1045 // if($jpeg !== false) $cap = $jpeg->getTitle(); 1046 1047 // $exif_data = exif_read_data($path,'IFD0',0); 1048 // $emake = $exif_data['Make']; 1049 // $emodel = $exif_data['Model']; 1050 // $emodel = str_replace($emake,"",$emodel); 1051 // $eexposuretime = $exif_data['ExposureTime']; 1052 // // $efnumber = $exif_data['FNumber']; 1053 // $efnumber = $exif_data['COMPUTED']['ApertureFNumber']; 1054 // $eiso = $exif_data['ISOSpeedRatings']; 1055 // $edate = $exif_data['DateTimeOriginal']; 1056 // $yy = substr($edate ,0,4); 1057 // $mm = substr($edate ,5,2); 1058 // $dd = substr($edate ,8,2); 1059 // $h = substr($edate ,11,2); 1060 // $m = substr($edate ,14,2); 1061 // $s = substr($edate ,17,2); 1062 // $date = $dd.'/'.$mm.'/'.$yy; 1063 // $time = $h.':'.$m.':'.$s; 1064 // return $date." - ".$emake." ".$emodel.", ".$eexposuretime."s, ".$efnumber.", ISO ".$eiso; 1065 // $page = $this->_apply_macro($page, $parent_id); 1066 // resolve_pageid(getNS($parent_id), $page, $exists); // resolve shortcuts and clean ID 1067 // if (auth_quickaclcheck($page) >= AUTH_READ) 1068 // $pages[] = $page; 1069 1070 // function _showname($img,$data){ 1071 1072 // //prepare link 1073 // $lnk = ml($img['id'],array('id'=>$ID),false); 1074 1075 // // prepare output 1076 // $ret .= hsc($img['file']); 1077