1<?php 2/** 3 * All output and handler function needed for the media management popup 4 * 5 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 6 * @author Andreas Gohr <andi@splitbrain.org> 7 */ 8 9use dokuwiki\ChangeLog\MediaChangeLog; 10use dokuwiki\HTTP\DokuHTTPClient; 11use dokuwiki\Subscriptions\MediaSubscriptionSender; 12use dokuwiki\Extension\Event; 13use dokuwiki\Form\Form; 14use dokuwiki\Utf8\Sort; 15 16/** 17 * Lists pages which currently use a media file selected for deletion 18 * 19 * References uses the same visual as search results and share 20 * their CSS tags except pagenames won't be links. 21 * 22 * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net> 23 * 24 * @param array $data 25 * @param string $id 26 */ 27function media_filesinuse($data,$id){ 28 global $lang; 29 echo '<h1>'.$lang['reference'].' <code>'.hsc(noNS($id)).'</code></h1>'; 30 echo '<p>'.hsc($lang['ref_inuse']).'</p>'; 31 32 $hidden=0; //count of hits without read permission 33 foreach($data as $row){ 34 if(auth_quickaclcheck($row) >= AUTH_READ && isVisiblePage($row)){ 35 echo '<div class="search_result">'; 36 echo '<span class="mediaref_ref">'.hsc($row).'</span>'; 37 echo '</div>'; 38 }else 39 $hidden++; 40 } 41 if ($hidden){ 42 print '<div class="mediaref_hidden">'.$lang['ref_hidden'].'</div>'; 43 } 44} 45 46/** 47 * Handles the saving of image meta data 48 * 49 * @author Andreas Gohr <andi@splitbrain.org> 50 * @author Kate Arzamastseva <pshns@ukr.net> 51 * 52 * @param string $id media id 53 * @param int $auth permission level 54 * @param array $data 55 * @return false|string 56 */ 57function media_metasave($id,$auth,$data){ 58 if($auth < AUTH_UPLOAD) return false; 59 if(!checkSecurityToken()) return false; 60 global $lang; 61 global $conf; 62 $src = mediaFN($id); 63 64 $meta = new JpegMeta($src); 65 $meta->_parseAll(); 66 67 foreach($data as $key => $val){ 68 $val=trim($val); 69 if(empty($val)){ 70 $meta->deleteField($key); 71 }else{ 72 $meta->setField($key,$val); 73 } 74 } 75 76 $old = @filemtime($src); 77 if(!file_exists(mediaFN($id, $old)) && file_exists($src)) { 78 // add old revision to the attic 79 media_saveOldRevision($id); 80 } 81 $filesize_old = filesize($src); 82 if($meta->save()){ 83 if($conf['fperm']) chmod($src, $conf['fperm']); 84 @clearstatcache(true, $src); 85 $new = @filemtime($src); 86 $filesize_new = filesize($src); 87 $sizechange = $filesize_new - $filesize_old; 88 89 // add a log entry to the media changelog 90 addMediaLogEntry($new, $id, DOKU_CHANGE_TYPE_EDIT, $lang['media_meta_edited'], '', null, $sizechange); 91 92 msg($lang['metasaveok'],1); 93 return $id; 94 }else{ 95 msg($lang['metasaveerr'],-1); 96 return false; 97 } 98} 99 100/** 101 * check if a media is external source 102 * 103 * @author Gerrit Uitslag <klapinklapin@gmail.com> 104 * 105 * @param string $id the media ID or URL 106 * @return bool 107 */ 108function media_isexternal($id){ 109 if (preg_match('#^(?:https?|ftp)://#i', $id)) return true; 110 return false; 111} 112 113/** 114 * Check if a media item is public (eg, external URL or readable by @ALL) 115 * 116 * @author Andreas Gohr <andi@splitbrain.org> 117 * 118 * @param string $id the media ID or URL 119 * @return bool 120 */ 121function media_ispublic($id){ 122 if(media_isexternal($id)) return true; 123 $id = cleanID($id); 124 if(auth_aclcheck(getNS($id).':*', '', array()) >= AUTH_READ) return true; 125 return false; 126} 127 128/** 129 * Display the form to edit image meta data 130 * 131 * @author Andreas Gohr <andi@splitbrain.org> 132 * @author Kate Arzamastseva <pshns@ukr.net> 133 * 134 * @param string $id media id 135 * @param int $auth permission level 136 * @return bool 137 */ 138function media_metaform($id, $auth) { 139 global $lang; 140 141 if ($auth < AUTH_UPLOAD) { 142 echo '<div class="nothing">'.$lang['media_perm_upload'].'</div>'.DOKU_LF; 143 return false; 144 } 145 146 // load the field descriptions 147 static $fields = null; 148 if ($fields === null) { 149 $config_files = getConfigFiles('mediameta'); 150 foreach ($config_files as $config_file) { 151 if (file_exists($config_file)) include($config_file); 152 } 153 } 154 155 $src = mediaFN($id); 156 157 // output 158 $form = new Form([ 159 'action' => media_managerURL(['tab_details' => 'view'], '&'), 160 'class' => 'meta' 161 ]); 162 $form->addTagOpen('div')->addClass('no'); 163 $form->setHiddenField('img', $id); 164 $form->setHiddenField('mediado', 'save'); 165 foreach ($fields as $key => $field) { 166 // get current value 167 if (empty($field[0])) continue; 168 $tags = array($field[0]); 169 if (is_array($field[3])) $tags = array_merge($tags, $field[3]); 170 $value = tpl_img_getTag($tags, '', $src); 171 $value = cleanText($value); 172 173 // prepare attributes 174 $p = array( 175 'class' => 'edit', 176 'id' => 'meta__'.$key, 177 'name' => 'meta['.$field[0].']', 178 ); 179 180 $form->addTagOpen('div')->addClass('row'); 181 if ($field[2] == 'text') { 182 $form->addTextInput( 183 $p['name'], 184 ($lang[$field[1]] ? $lang[$field[1]] : $field[1] . ':') 185 )->id($p['id'])->addClass($p['class'])->val($value); 186 } else { 187 $form->addTextarea($p['name'], $lang[$field[1]])->id($p['id']) 188 ->val(formText($value)) 189 ->addClass($p['class']) 190 ->attr('rows', '6')->attr('cols', '50'); 191 } 192 $form->addTagClose('div'); 193 } 194 $form->addTagOpen('div')->addClass('buttons'); 195 $form->addButton('mediado[save]', $lang['btn_save'])->attr('type', 'submit') 196 ->attrs(['accesskey' => 's']); 197 $form->addTagClose('div'); 198 199 $form->addTagClose('div'); 200 echo $form->toHTML(); 201 return true; 202} 203 204/** 205 * Convenience function to check if a media file is still in use 206 * 207 * @author Michael Klier <chi@chimeric.de> 208 * 209 * @param string $id media id 210 * @return array|bool 211 */ 212function media_inuse($id) { 213 global $conf; 214 215 if($conf['refcheck']){ 216 $mediareferences = ft_mediause($id,true); 217 if(!count($mediareferences)) { 218 return false; 219 } else { 220 return $mediareferences; 221 } 222 } else { 223 return false; 224 } 225} 226 227/** 228 * Handles media file deletions 229 * 230 * If configured, checks for media references before deletion 231 * 232 * @author Andreas Gohr <andi@splitbrain.org> 233 * 234 * @param string $id media id 235 * @param int $auth no longer used 236 * @return int One of: 0, 237 * DOKU_MEDIA_DELETED, 238 * DOKU_MEDIA_DELETED | DOKU_MEDIA_EMPTY_NS, 239 * DOKU_MEDIA_NOT_AUTH, 240 * DOKU_MEDIA_INUSE 241 */ 242function media_delete($id,$auth){ 243 global $lang; 244 $auth = auth_quickaclcheck(ltrim(getNS($id).':*', ':')); 245 if($auth < AUTH_DELETE) return DOKU_MEDIA_NOT_AUTH; 246 if(media_inuse($id)) return DOKU_MEDIA_INUSE; 247 248 $file = mediaFN($id); 249 250 // trigger an event - MEDIA_DELETE_FILE 251 $data = array(); 252 $data['id'] = $id; 253 $data['name'] = \dokuwiki\Utf8\PhpString::basename($file); 254 $data['path'] = $file; 255 $data['size'] = (file_exists($file)) ? filesize($file) : 0; 256 257 $data['unl'] = false; 258 $data['del'] = false; 259 $evt = new Event('MEDIA_DELETE_FILE',$data); 260 if ($evt->advise_before()) { 261 $old = @filemtime($file); 262 if(!file_exists(mediaFN($id, $old)) && file_exists($file)) { 263 // add old revision to the attic 264 media_saveOldRevision($id); 265 } 266 267 $data['unl'] = @unlink($file); 268 if($data['unl']) { 269 $sizechange = 0 - $data['size']; 270 addMediaLogEntry(time(), $id, DOKU_CHANGE_TYPE_DELETE, $lang['deleted'], '', null, $sizechange); 271 272 $data['del'] = io_sweepNS($id, 'mediadir'); 273 } 274 } 275 $evt->advise_after(); 276 unset($evt); 277 278 if($data['unl'] && $data['del']){ 279 return DOKU_MEDIA_DELETED | DOKU_MEDIA_EMPTY_NS; 280 } 281 282 return $data['unl'] ? DOKU_MEDIA_DELETED : 0; 283} 284 285/** 286 * Handle file uploads via XMLHttpRequest 287 * 288 * @param string $ns target namespace 289 * @param int $auth current auth check result 290 * @return false|string false on error, id of the new file on success 291 */ 292function media_upload_xhr($ns,$auth){ 293 if(!checkSecurityToken()) return false; 294 global $INPUT; 295 296 $id = $INPUT->get->str('qqfile'); 297 list($ext,$mime) = mimetype($id); 298 $input = fopen("php://input", "r"); 299 if (!($tmp = io_mktmpdir())) return false; 300 $path = $tmp.'/'.md5($id); 301 $target = fopen($path, "w"); 302 $realSize = stream_copy_to_stream($input, $target); 303 fclose($target); 304 fclose($input); 305 if (isset($_SERVER["CONTENT_LENGTH"]) && ($realSize != (int)$_SERVER["CONTENT_LENGTH"])){ 306 unlink($path); 307 return false; 308 } 309 310 $res = media_save( 311 array('name' => $path, 312 'mime' => $mime, 313 'ext' => $ext), 314 $ns.':'.$id, 315 (($INPUT->get->str('ow') == 'true') ? true : false), 316 $auth, 317 'copy' 318 ); 319 unlink($path); 320 if ($tmp) io_rmdir($tmp, true); 321 if (is_array($res)) { 322 msg($res[0], $res[1]); 323 return false; 324 } 325 return $res; 326} 327 328/** 329 * Handles media file uploads 330 * 331 * @author Andreas Gohr <andi@splitbrain.org> 332 * @author Michael Klier <chi@chimeric.de> 333 * 334 * @param string $ns target namespace 335 * @param int $auth current auth check result 336 * @param bool|array $file $_FILES member, $_FILES['upload'] if false 337 * @return false|string false on error, id of the new file on success 338 */ 339function media_upload($ns,$auth,$file=false){ 340 if(!checkSecurityToken()) return false; 341 global $lang; 342 global $INPUT; 343 344 // get file and id 345 $id = $INPUT->post->str('mediaid'); 346 if (!$file) $file = $_FILES['upload']; 347 if(empty($id)) $id = $file['name']; 348 349 // check for errors (messages are done in lib/exe/mediamanager.php) 350 if($file['error']) return false; 351 352 // check extensions 353 list($fext,$fmime) = mimetype($file['name']); 354 list($iext,$imime) = mimetype($id); 355 if($fext && !$iext){ 356 // no extension specified in id - read original one 357 $id .= '.'.$fext; 358 $imime = $fmime; 359 }elseif($fext && $fext != $iext){ 360 // extension was changed, print warning 361 msg(sprintf($lang['mediaextchange'],$fext,$iext)); 362 } 363 364 $res = media_save(array('name' => $file['tmp_name'], 365 'mime' => $imime, 366 'ext' => $iext), $ns.':'.$id, 367 $INPUT->post->bool('ow'), $auth, 'copy_uploaded_file'); 368 if (is_array($res)) { 369 msg($res[0], $res[1]); 370 return false; 371 } 372 return $res; 373} 374 375/** 376 * An alternative to move_uploaded_file that copies 377 * 378 * Using copy, makes sure any setgid bits on the media directory are honored 379 * 380 * @see move_uploaded_file() 381 * 382 * @param string $from 383 * @param string $to 384 * @return bool 385 */ 386function copy_uploaded_file($from, $to){ 387 if(!is_uploaded_file($from)) return false; 388 $ok = copy($from, $to); 389 @unlink($from); 390 return $ok; 391} 392 393/** 394 * This generates an action event and delegates to _media_upload_action(). 395 * Action plugins are allowed to pre/postprocess the uploaded file. 396 * (The triggered event is preventable.) 397 * 398 * Event data: 399 * $data[0] fn_tmp: the temporary file name (read from $_FILES) 400 * $data[1] fn: the file name of the uploaded file 401 * $data[2] id: the future directory id of the uploaded file 402 * $data[3] imime: the mimetype of the uploaded file 403 * $data[4] overwrite: if an existing file is going to be overwritten 404 * $data[5] move: name of function that performs move/copy/.. 405 * 406 * @triggers MEDIA_UPLOAD_FINISH 407 * 408 * @param array $file 409 * @param string $id media id 410 * @param bool $ow overwrite? 411 * @param int $auth permission level 412 * @param string $move name of functions that performs move/copy/.. 413 * @return false|array|string 414 */ 415function media_save($file, $id, $ow, $auth, $move) { 416 if($auth < AUTH_UPLOAD) { 417 return array("You don't have permissions to upload files.", -1); 418 } 419 420 if (!isset($file['mime']) || !isset($file['ext'])) { 421 list($ext, $mime) = mimetype($id); 422 if (!isset($file['mime'])) { 423 $file['mime'] = $mime; 424 } 425 if (!isset($file['ext'])) { 426 $file['ext'] = $ext; 427 } 428 } 429 430 global $lang, $conf; 431 432 // get filename 433 $id = cleanID($id); 434 $fn = mediaFN($id); 435 436 // get filetype regexp 437 $types = array_keys(getMimeTypes()); 438 $types = array_map( 439 function ($q) { 440 return preg_quote($q, "/"); 441 }, 442 $types 443 ); 444 $regex = join('|',$types); 445 446 // because a temp file was created already 447 if(!preg_match('/\.('.$regex.')$/i',$fn)) { 448 return array($lang['uploadwrong'],-1); 449 } 450 451 //check for overwrite 452 $overwrite = file_exists($fn); 453 $auth_ow = (($conf['mediarevisions']) ? AUTH_UPLOAD : AUTH_DELETE); 454 if($overwrite && (!$ow || $auth < $auth_ow)) { 455 return array($lang['uploadexist'], 0); 456 } 457 // check for valid content 458 $ok = media_contentcheck($file['name'], $file['mime']); 459 if($ok == -1){ 460 return array(sprintf($lang['uploadbadcontent'],'.' . $file['ext']),-1); 461 }elseif($ok == -2){ 462 return array($lang['uploadspam'],-1); 463 }elseif($ok == -3){ 464 return array($lang['uploadxss'],-1); 465 } 466 467 // prepare event data 468 $data = array(); 469 $data[0] = $file['name']; 470 $data[1] = $fn; 471 $data[2] = $id; 472 $data[3] = $file['mime']; 473 $data[4] = $overwrite; 474 $data[5] = $move; 475 476 // trigger event 477 return Event::createAndTrigger('MEDIA_UPLOAD_FINISH', $data, '_media_upload_action', true); 478} 479 480/** 481 * Callback adapter for media_upload_finish() triggered by MEDIA_UPLOAD_FINISH 482 * 483 * @author Michael Klier <chi@chimeric.de> 484 * 485 * @param array $data event data 486 * @return false|array|string 487 */ 488function _media_upload_action($data) { 489 // fixme do further sanity tests of given data? 490 if(is_array($data) && count($data)===6) { 491 return media_upload_finish($data[0], $data[1], $data[2], $data[3], $data[4], $data[5]); 492 } else { 493 return false; //callback error 494 } 495} 496 497/** 498 * Saves an uploaded media file 499 * 500 * @author Andreas Gohr <andi@splitbrain.org> 501 * @author Michael Klier <chi@chimeric.de> 502 * @author Kate Arzamastseva <pshns@ukr.net> 503 * 504 * @param string $fn_tmp 505 * @param string $fn 506 * @param string $id media id 507 * @param string $imime mime type 508 * @param bool $overwrite overwrite existing? 509 * @param string $move function name 510 * @return array|string 511 */ 512function media_upload_finish($fn_tmp, $fn, $id, $imime, $overwrite, $move = 'move_uploaded_file') { 513 global $conf; 514 global $lang; 515 global $REV; 516 517 $old = @filemtime($fn); 518 if(!file_exists(mediaFN($id, $old)) && file_exists($fn)) { 519 // add old revision to the attic if missing 520 media_saveOldRevision($id); 521 } 522 523 // prepare directory 524 io_createNamespace($id, 'media'); 525 526 $filesize_old = file_exists($fn) ? filesize($fn) : 0; 527 528 if($move($fn_tmp, $fn)) { 529 @clearstatcache(true,$fn); 530 $new = @filemtime($fn); 531 // Set the correct permission here. 532 // Always chmod media because they may be saved with different permissions than expected from the php umask. 533 // (Should normally chmod to $conf['fperm'] only if $conf['fperm'] is set.) 534 chmod($fn, $conf['fmode']); 535 msg($lang['uploadsucc'],1); 536 media_notify($id,$fn,$imime,$old,$new); 537 // add a log entry to the media changelog 538 $filesize_new = filesize($fn); 539 $sizechange = $filesize_new - $filesize_old; 540 if($REV) { 541 addMediaLogEntry( 542 $new, 543 $id, 544 DOKU_CHANGE_TYPE_REVERT, 545 sprintf($lang['restored'], dformat($REV)), 546 $REV, 547 null, 548 $sizechange 549 ); 550 } elseif($overwrite) { 551 addMediaLogEntry($new, $id, DOKU_CHANGE_TYPE_EDIT, '', '', null, $sizechange); 552 } else { 553 addMediaLogEntry($new, $id, DOKU_CHANGE_TYPE_CREATE, $lang['created'], '', null, $sizechange); 554 } 555 return $id; 556 }else{ 557 return array($lang['uploadfail'],-1); 558 } 559} 560 561/** 562 * Moves the current version of media file to the media_attic 563 * directory 564 * 565 * @author Kate Arzamastseva <pshns@ukr.net> 566 * 567 * @param string $id 568 * @return int - revision date 569 */ 570function media_saveOldRevision($id) { 571 global $conf, $lang; 572 573 $oldf = mediaFN($id); 574 if (!file_exists($oldf)) return ''; 575 $date = filemtime($oldf); 576 if (!$conf['mediarevisions']) return $date; 577 578 $medialog = new MediaChangeLog($id); 579 if (!$medialog->getRevisionInfo($date)) { 580 // there was an external edit, 581 // there is no log entry for current version of file 582 $sizechange = filesize($oldf); 583 if(!file_exists(mediaMetaFN($id, '.changes'))) { 584 addMediaLogEntry($date, $id, DOKU_CHANGE_TYPE_CREATE, $lang['created'], '', null, $sizechange); 585 } else { 586 $oldRev = $medialog->getRevisions(-1, 1); // from changelog 587 $oldRev = (int) (empty($oldRev) ? 0 : $oldRev[0]); 588 $filesize_old = filesize(mediaFN($id, $oldRev)); 589 $sizechange = $sizechange - $filesize_old; 590 591 addMediaLogEntry($date, $id, DOKU_CHANGE_TYPE_EDIT, '', '', null, $sizechange); 592 } 593 } 594 595 $newf = mediaFN($id, $date); 596 io_makeFileDir($newf); 597 if (copy($oldf, $newf)) { 598 // Set the correct permission here. 599 // Always chmod media because they may be saved with different permissions than expected from the php umask. 600 // (Should normally chmod to $conf['fperm'] only if $conf['fperm'] is set.) 601 chmod($newf, $conf['fmode']); 602 } 603 return $date; 604} 605 606/** 607 * This function checks if the uploaded content is really what the 608 * mimetype says it is. We also do spam checking for text types here. 609 * 610 * We need to do this stuff because we can not rely on the browser 611 * to do this check correctly. Yes, IE is broken as usual. 612 * 613 * @author Andreas Gohr <andi@splitbrain.org> 614 * @link http://www.splitbrain.org/blog/2007-02/12-internet_explorer_facilitates_cross_site_scripting 615 * @fixme check all 26 magic IE filetypes here? 616 * 617 * @param string $file path to file 618 * @param string $mime mimetype 619 * @return int 620 */ 621function media_contentcheck($file,$mime){ 622 global $conf; 623 if($conf['iexssprotect']){ 624 $fh = @fopen($file, 'rb'); 625 if($fh){ 626 $bytes = fread($fh, 256); 627 fclose($fh); 628 if(preg_match('/<(script|a|img|html|body|iframe)[\s>]/i',$bytes)){ 629 return -3; //XSS: possibly malicious content 630 } 631 } 632 } 633 if(substr($mime,0,6) == 'image/'){ 634 $info = @getimagesize($file); 635 if($mime == 'image/gif' && $info[2] != 1){ 636 return -1; // uploaded content did not match the file extension 637 }elseif($mime == 'image/jpeg' && $info[2] != 2){ 638 return -1; 639 }elseif($mime == 'image/png' && $info[2] != 3){ 640 return -1; 641 } 642 # fixme maybe check other images types as well 643 }elseif(substr($mime,0,5) == 'text/'){ 644 global $TEXT; 645 $TEXT = io_readFile($file); 646 if(checkwordblock()){ 647 return -2; //blocked by the spam blacklist 648 } 649 } 650 return 0; 651} 652 653/** 654 * Send a notify mail on uploads 655 * 656 * @author Andreas Gohr <andi@splitbrain.org> 657 * 658 * @param string $id media id 659 * @param string $file path to file 660 * @param string $mime mime type 661 * @param bool|int $old_rev revision timestamp or false 662 * @return bool 663 */ 664function media_notify($id,$file,$mime,$old_rev=false,$current_rev=false){ 665 global $conf; 666 if(empty($conf['notify'])) return false; //notify enabled? 667 668 $subscription = new MediaSubscriptionSender(); 669 return $subscription->sendMediaDiff($conf['notify'], 'uploadmail', $id, $old_rev, $current_rev); 670} 671 672/** 673 * List all files in a given Media namespace 674 * 675 * @param string $ns namespace 676 * @param null|int $auth permission level 677 * @param string $jump id 678 * @param bool $fullscreenview 679 * @param bool|string $sort sorting order, false skips sorting 680 */ 681function media_filelist($ns,$auth=null,$jump='',$fullscreenview=false,$sort=false){ 682 global $conf; 683 global $lang; 684 $ns = cleanID($ns); 685 686 // check auth our self if not given (needed for ajax calls) 687 if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*"); 688 689 if (!$fullscreenview) echo '<h1 id="media__ns">:'.hsc($ns).'</h1>'.NL; 690 691 if($auth < AUTH_READ){ 692 // FIXME: print permission warning here instead? 693 echo '<div class="nothing">'.$lang['nothingfound'].'</div>'.NL; 694 }else{ 695 if (!$fullscreenview) { 696 media_uploadform($ns, $auth); 697 media_searchform($ns); 698 } 699 700 $dir = utf8_encodeFN(str_replace(':','/',$ns)); 701 $data = array(); 702 search($data,$conf['mediadir'],'search_mediafiles', 703 array('showmsg'=>true,'depth'=>1),$dir,1,$sort); 704 705 if(!count($data)){ 706 echo '<div class="nothing">'.$lang['nothingfound'].'</div>'.NL; 707 }else { 708 if ($fullscreenview) { 709 echo '<ul class="' . _media_get_list_type() . '">'; 710 } 711 foreach($data as $item){ 712 if (!$fullscreenview) { 713 //FIXME old call: media_printfile($item,$auth,$jump); 714 $display = new \dokuwiki\Ui\Media\DisplayRow($item); 715 $display->scrollIntoView($jump == $item->getID()); 716 $display->show(); 717 } else { 718 //FIXME old call: media_printfile_thumbs($item,$auth,$jump); 719 echo '<li>'; 720 $display = new \dokuwiki\Ui\Media\DisplayTile($item); 721 $display->scrollIntoView($jump == $item->getID()); 722 $display->show(); 723 echo '</li>'; 724 } 725 } 726 if ($fullscreenview) echo '</ul>'.NL; 727 } 728 } 729} 730 731/** 732 * Prints tabs for files list actions 733 * 734 * @author Kate Arzamastseva <pshns@ukr.net> 735 * @author Adrian Lang <mail@adrianlang.de> 736 * 737 * @param string $selected_tab - opened tab 738 */ 739 740function media_tabs_files($selected_tab = ''){ 741 global $lang; 742 $tabs = array(); 743 foreach(array('files' => 'mediaselect', 744 'upload' => 'media_uploadtab', 745 'search' => 'media_searchtab') as $tab => $caption) { 746 $tabs[$tab] = array('href' => media_managerURL(['tab_files' => $tab], '&'), 747 'caption' => $lang[$caption]); 748 } 749 750 html_tabs($tabs, $selected_tab); 751} 752 753/** 754 * Prints tabs for files details actions 755 * 756 * @author Kate Arzamastseva <pshns@ukr.net> 757 * @param string $image filename of the current image 758 * @param string $selected_tab opened tab 759 */ 760function media_tabs_details($image, $selected_tab = '') { 761 global $lang, $conf; 762 763 $tabs = array(); 764 $tabs['view'] = array('href' => media_managerURL(['tab_details' => 'view'], '&'), 765 'caption' => $lang['media_viewtab']); 766 767 list(, $mime) = mimetype($image); 768 if ($mime == 'image/jpeg' && file_exists(mediaFN($image))) { 769 $tabs['edit'] = array('href' => media_managerURL(['tab_details' => 'edit'], '&'), 770 'caption' => $lang['media_edittab']); 771 } 772 if ($conf['mediarevisions']) { 773 $tabs['history'] = array('href' => media_managerURL(['tab_details' => 'history'], '&'), 774 'caption' => $lang['media_historytab']); 775 } 776 777 html_tabs($tabs, $selected_tab); 778} 779 780/** 781 * Prints options for the tab that displays a list of all files 782 * 783 * @author Kate Arzamastseva <pshns@ukr.net> 784 */ 785function media_tab_files_options() { 786 global $lang; 787 global $INPUT; 788 global $ID; 789 790 $form = new Form([ 791 'method' => 'get', 792 'action' => wl($ID), 793 'class' => 'options' 794 ]); 795 $form->addTagOpen('div')->addClass('no'); 796 $form->setHiddenField('sectok', null); 797 $media_manager_params = media_managerURL([], '', false, true); 798 foreach ($media_manager_params as $pKey => $pVal) { 799 $form->setHiddenField($pKey, $pVal); 800 } 801 if ($INPUT->has('q')) { 802 $form->setHiddenField('q', $INPUT->str('q')); 803 } 804 $form->addHTML('<ul>'.NL); 805 foreach (array('list' => array('listType', array('thumbs', 'rows')), 806 'sort' => array('sortBy', array('name', 'date'))) 807 as $group => $content) { 808 $checked = "_media_get_${group}_type"; 809 $checked = $checked(); 810 811 $form->addHTML('<li class="'. $content[0] .'">'); 812 foreach ($content[1] as $option) { 813 $attrs = array(); 814 if ($checked == $option) { 815 $attrs['checked'] = 'checked'; 816 } 817 $radio = $form->addRadioButton( 818 $group.'_dwmedia', 819 $lang['media_'.$group.'_'.$option] 820 )->val($option)->id($content[0].'__'.$option)->addClass($option); 821 $radio->attrs($attrs); 822 } 823 $form->addHTML('</li>'.NL); 824 } 825 $form->addHTML('<li>'); 826 $form->addButton('', $lang['btn_apply'])->attr('type', 'submit'); 827 $form->addHTML('</li>'.NL); 828 $form->addHTML('</ul>'.NL); 829 $form->addTagClose('div'); 830 print $form->toHTML(); 831} 832 833/** 834 * Returns type of sorting for the list of files in media manager 835 * 836 * @author Kate Arzamastseva <pshns@ukr.net> 837 * 838 * @return string - sort type 839 */ 840function _media_get_sort_type() { 841 return _media_get_display_param('sort', array('default' => 'name', 'date')); 842} 843 844/** 845 * Returns type of listing for the list of files in media manager 846 * 847 * @author Kate Arzamastseva <pshns@ukr.net> 848 * 849 * @return string - list type 850 */ 851function _media_get_list_type() { 852 return _media_get_display_param('list', array('default' => 'thumbs', 'rows')); 853} 854 855/** 856 * Get display parameters 857 * 858 * @param string $param name of parameter 859 * @param array $values allowed values, where default value has index key 'default' 860 * @return string the parameter value 861 */ 862function _media_get_display_param($param, $values) { 863 global $INPUT; 864 if (in_array($INPUT->str($param), $values)) { 865 // FIXME: Set cookie 866 return $INPUT->str($param); 867 } else { 868 $val = get_doku_pref($param, $values['default']); 869 if (!in_array($val, $values)) { 870 $val = $values['default']; 871 } 872 return $val; 873 } 874} 875 876/** 877 * Prints tab that displays a list of all files 878 * 879 * @author Kate Arzamastseva <pshns@ukr.net> 880 * 881 * @param string $ns 882 * @param null|int $auth permission level 883 * @param string $jump item id 884 */ 885function media_tab_files($ns,$auth=null,$jump='') { 886 global $lang; 887 if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*"); 888 889 if($auth < AUTH_READ){ 890 echo '<div class="nothing">'.$lang['media_perm_read'].'</div>'.NL; 891 }else{ 892 media_filelist($ns,$auth,$jump,true,_media_get_sort_type()); 893 } 894} 895 896/** 897 * Prints tab that displays uploading form 898 * 899 * @author Kate Arzamastseva <pshns@ukr.net> 900 * 901 * @param string $ns 902 * @param null|int $auth permission level 903 * @param string $jump item id 904 */ 905function media_tab_upload($ns,$auth=null,$jump='') { 906 global $lang; 907 if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*"); 908 909 echo '<div class="upload">'.NL; 910 if ($auth >= AUTH_UPLOAD) { 911 echo '<p>' . $lang['mediaupload'] . '</p>'; 912 } 913 media_uploadform($ns, $auth, true); 914 echo '</div>'.NL; 915} 916 917/** 918 * Prints tab that displays search form 919 * 920 * @author Kate Arzamastseva <pshns@ukr.net> 921 * 922 * @param string $ns 923 * @param null|int $auth permission level 924 */ 925function media_tab_search($ns,$auth=null) { 926 global $INPUT; 927 928 $do = $INPUT->str('mediado'); 929 $query = $INPUT->str('q'); 930 echo '<div class="search">'.NL; 931 932 media_searchform($ns, $query, true); 933 if ($do == 'searchlist' || $query) { 934 media_searchlist($query,$ns,$auth,true,_media_get_sort_type()); 935 } 936 echo '</div>'.NL; 937} 938 939/** 940 * Prints tab that displays mediafile details 941 * 942 * @author Kate Arzamastseva <pshns@ukr.net> 943 * 944 * @param string $image media id 945 * @param string $ns 946 * @param null|int $auth permission level 947 * @param string|int $rev revision timestamp or empty string 948 */ 949function media_tab_view($image, $ns, $auth=null, $rev='') { 950 global $lang; 951 if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*"); 952 953 if ($image && $auth >= AUTH_READ) { 954 $meta = new JpegMeta(mediaFN($image, $rev)); 955 media_preview($image, $auth, $rev, $meta); 956 media_preview_buttons($image, $auth, $rev); 957 media_details($image, $auth, $rev, $meta); 958 959 } else { 960 echo '<div class="nothing">'.$lang['media_perm_read'].'</div>'.NL; 961 } 962} 963 964/** 965 * Prints tab that displays form for editing mediafile metadata 966 * 967 * @author Kate Arzamastseva <pshns@ukr.net> 968 * 969 * @param string $image media id 970 * @param string $ns 971 * @param null|int $auth permission level 972 */ 973function media_tab_edit($image, $ns, $auth=null) { 974 if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*"); 975 976 if ($image) { 977 list(, $mime) = mimetype($image); 978 if ($mime == 'image/jpeg') media_metaform($image,$auth); 979 } 980} 981 982/** 983 * Prints tab that displays mediafile revisions 984 * 985 * @author Kate Arzamastseva <pshns@ukr.net> 986 * 987 * @param string $image media id 988 * @param string $ns 989 * @param null|int $auth permission level 990 */ 991function media_tab_history($image, $ns, $auth=null) { 992 global $lang; 993 global $INPUT; 994 995 if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*"); 996 $do = $INPUT->str('mediado'); 997 998 if ($auth >= AUTH_READ && $image) { 999 if ($do == 'diff'){ 1000 (new dokuwiki\Ui\MediaDiff($image))->show(); //media_diff($image, $ns, $auth); 1001 } else { 1002 $first = $INPUT->int('first'); 1003 (new dokuwiki\Ui\MediaRevisions($image))->show($first); 1004 } 1005 } else { 1006 echo '<div class="nothing">'.$lang['media_perm_read'].'</div>'.NL; 1007 } 1008} 1009 1010/** 1011 * Prints mediafile details 1012 * 1013 * @param string $image media id 1014 * @param int $auth permission level 1015 * @param int|string $rev revision timestamp or empty string 1016 * @param JpegMeta|bool $meta 1017 * 1018 * @author Kate Arzamastseva <pshns@ukr.net> 1019 */ 1020function media_preview($image, $auth, $rev = '', $meta = false) { 1021 1022 $size = media_image_preview_size($image, $rev, $meta); 1023 1024 if ($size) { 1025 global $lang; 1026 echo '<div class="image">'; 1027 1028 $more = array(); 1029 if ($rev) { 1030 $more['rev'] = $rev; 1031 } else { 1032 $t = @filemtime(mediaFN($image)); 1033 $more['t'] = $t; 1034 } 1035 1036 $more['w'] = $size[0]; 1037 $more['h'] = $size[1]; 1038 $src = ml($image, $more); 1039 1040 echo '<a href="'.$src.'" target="_blank" title="'.$lang['mediaview'].'">'; 1041 echo '<img src="'.$src.'" alt="" style="max-width: '.$size[0].'px;" />'; 1042 echo '</a>'; 1043 1044 echo '</div>'; 1045 } 1046} 1047 1048/** 1049 * Prints mediafile action buttons 1050 * 1051 * @author Kate Arzamastseva <pshns@ukr.net> 1052 * 1053 * @param string $image media id 1054 * @param int $auth permission level 1055 * @param int|string $rev revision timestamp, or empty string 1056 */ 1057function media_preview_buttons($image, $auth, $rev = '') { 1058 global $lang, $conf; 1059 1060 echo '<ul class="actions">'; 1061 1062 if ($auth >= AUTH_DELETE && !$rev && file_exists(mediaFN($image))) { 1063 1064 // delete button 1065 $form = new Form([ 1066 'id' => 'mediamanager__btn_delete', 1067 'action' => media_managerURL(['delete' => $image], '&'), 1068 ]); 1069 $form->addTagOpen('div')->addClass('no'); 1070 $form->addButton('', $lang['btn_delete'])->attr('type', 'submit'); 1071 $form->addTagClose('div'); 1072 echo '<li>'; 1073 echo $form->toHTML(); 1074 echo '</li>'; 1075 } 1076 1077 $auth_ow = (($conf['mediarevisions']) ? AUTH_UPLOAD : AUTH_DELETE); 1078 if ($auth >= $auth_ow && !$rev) { 1079 1080 // upload new version button 1081 $form = new Form([ 1082 'id' => 'mediamanager__btn_update', 1083 'action' => media_managerURL(['image' => $image, 'mediado' => 'update'], '&'), 1084 ]); 1085 $form->addTagOpen('div')->addClass('no'); 1086 $form->addButton('', $lang['media_update'])->attr('type', 'submit'); 1087 $form->addTagClose('div'); 1088 echo '<li>'; 1089 echo $form->toHTML(); 1090 echo '</li>'; 1091 } 1092 1093 if ($auth >= AUTH_UPLOAD && $rev && $conf['mediarevisions'] && file_exists(mediaFN($image, $rev))) { 1094 1095 // restore button 1096 $form = new Form([ 1097 'id' => 'mediamanager__btn_restore', 1098 'action'=>media_managerURL(['image' => $image], '&'), 1099 ]); 1100 $form->addTagOpen('div')->addClass('no'); 1101 $form->setHiddenField('mediado', 'restore'); 1102 $form->setHiddenField('rev', $rev); 1103 $form->addButton('', $lang['media_restore'])->attr('type', 'submit'); 1104 $form->addTagClose('div'); 1105 echo '<li>'; 1106 echo $form->toHTML(); 1107 echo '</li>'; 1108 } 1109 1110 echo '</ul>'; 1111} 1112 1113/** 1114 * Returns image width and height for mediamanager preview panel 1115 * 1116 * @author Kate Arzamastseva <pshns@ukr.net> 1117 * @param string $image 1118 * @param int|string $rev 1119 * @param JpegMeta|bool $meta 1120 * @param int $size 1121 * @return array 1122 */ 1123function media_image_preview_size($image, $rev, $meta = false, $size = 500) { 1124 if (!preg_match("/\.(jpe?g|gif|png)$/", $image) 1125 || !file_exists($filename = mediaFN($image, $rev)) 1126 ) return array(); 1127 1128 $info = getimagesize($filename); 1129 $w = (int) $info[0]; 1130 $h = (int) $info[1]; 1131 1132 if ($meta && ($w > $size || $h > $size)) { 1133 $ratio = $meta->getResizeRatio($size, $size); 1134 $w = floor($w * $ratio); 1135 $h = floor($h * $ratio); 1136 } 1137 return array($w, $h); 1138} 1139 1140/** 1141 * Returns the requested EXIF/IPTC tag from the image meta 1142 * 1143 * @author Kate Arzamastseva <pshns@ukr.net> 1144 * 1145 * @param array $tags array with tags, first existing is returned 1146 * @param JpegMeta $meta 1147 * @param string $alt alternative value 1148 * @return string 1149 */ 1150function media_getTag($tags, $meta = false, $alt = '') { 1151 if (!$meta) return $alt; 1152 $info = $meta->getField($tags); 1153 if (!$info) return $alt; 1154 return $info; 1155} 1156 1157/** 1158 * Returns mediafile tags 1159 * 1160 * @author Kate Arzamastseva <pshns@ukr.net> 1161 * 1162 * @param JpegMeta $meta 1163 * @return array list of tags of the mediafile 1164 */ 1165function media_file_tags($meta) { 1166 // load the field descriptions 1167 static $fields = null; 1168 if (is_null($fields)) { 1169 $config_files = getConfigFiles('mediameta'); 1170 foreach ($config_files as $config_file) { 1171 if (file_exists($config_file)) include($config_file); 1172 } 1173 } 1174 1175 $tags = array(); 1176 1177 foreach ($fields as $key => $tag) { 1178 $t = array(); 1179 if (!empty($tag[0])) $t = array($tag[0]); 1180 if (isset($tag[3]) && is_array($tag[3])) $t = array_merge($t,$tag[3]); 1181 $value = media_getTag($t, $meta); 1182 $tags[] = array('tag' => $tag, 'value' => $value); 1183 } 1184 1185 return $tags; 1186} 1187 1188/** 1189 * Prints mediafile tags 1190 * 1191 * @author Kate Arzamastseva <pshns@ukr.net> 1192 * 1193 * @param string $image image id 1194 * @param int $auth permission level 1195 * @param string|int $rev revision timestamp, or empty string 1196 * @param bool|JpegMeta $meta image object, or create one if false 1197 */ 1198function media_details($image, $auth, $rev='', $meta=false) { 1199 global $lang; 1200 1201 if (!$meta) $meta = new JpegMeta(mediaFN($image, $rev)); 1202 $tags = media_file_tags($meta); 1203 1204 echo '<dl>'.NL; 1205 foreach($tags as $tag){ 1206 if ($tag['value']) { 1207 $value = cleanText($tag['value']); 1208 echo '<dt>'.$lang[$tag['tag'][1]].'</dt><dd>'; 1209 if ($tag['tag'][2] == 'date') echo dformat($value); 1210 else echo hsc($value); 1211 echo '</dd>'.NL; 1212 } 1213 } 1214 echo '</dl>'.NL; 1215 echo '<dl>'.NL; 1216 echo '<dt>'.$lang['reference'].':</dt>'; 1217 $media_usage = ft_mediause($image,true); 1218 if(count($media_usage) > 0){ 1219 foreach($media_usage as $path){ 1220 echo '<dd>'.html_wikilink($path).'</dd>'; 1221 } 1222 }else{ 1223 echo '<dd>'.$lang['nothingfound'].'</dd>'; 1224 } 1225 echo '</dl>'.NL; 1226 1227} 1228 1229/** 1230 * Shows difference between two revisions of file 1231 * 1232 * @author Kate Arzamastseva <pshns@ukr.net> 1233 * 1234 * @param string $image image id 1235 * @param string $ns 1236 * @param int $auth permission level 1237 * @param bool $fromajax 1238 * @return false|null|string 1239 * @deprecated 2020-12-31 1240 */ 1241function media_diff($image, $ns, $auth, $fromajax = false) { 1242 dbg_deprecated('see '. \dokuwiki\Ui\MediaDiff::class .'::show()'); 1243} 1244 1245/** 1246 * Callback for media file diff 1247 * 1248 * @param array $data event data 1249 * @return false|null 1250 * @deprecated 2020-12-31 1251 */ 1252function _media_file_diff($data) { 1253 dbg_deprecated('see '. \dokuwiki\Ui\MediaDiff::class .'::show()'); 1254} 1255 1256/** 1257 * Shows difference between two revisions of image 1258 * 1259 * @author Kate Arzamastseva <pshns@ukr.net> 1260 * 1261 * @param string $image 1262 * @param string|int $l_rev revision timestamp, or empty string 1263 * @param string|int $r_rev revision timestamp, or empty string 1264 * @param string $ns 1265 * @param int $auth permission level 1266 * @param bool $fromajax 1267 * @deprecated 2020-12-31 1268 */ 1269function media_file_diff($image, $l_rev, $r_rev, $ns, $auth, $fromajax) { 1270 dbg_deprecated('see '. \dokuwiki\Ui\MediaDiff::class .'::showFileDiff()'); 1271} 1272 1273/** 1274 * Prints two images side by side 1275 * and slider 1276 * 1277 * @author Kate Arzamastseva <pshns@ukr.net> 1278 * 1279 * @param string $image image id 1280 * @param int $l_rev revision timestamp, or empty string 1281 * @param int $r_rev revision timestamp, or empty string 1282 * @param array $l_size array with width and height 1283 * @param array $r_size array with width and height 1284 * @param string $type 1285 * @deprecated 2020-12-31 1286 */ 1287function media_image_diff($image, $l_rev, $r_rev, $l_size, $r_size, $type) { 1288 dbg_deprecated('see '. \dokuwiki\Ui\MediaDiff::class .'::showImageDiff()'); 1289} 1290 1291/** 1292 * Restores an old revision of a media file 1293 * 1294 * @param string $image media id 1295 * @param int $rev revision timestamp or empty string 1296 * @param int $auth 1297 * @return string - file's id 1298 * 1299 * @author Kate Arzamastseva <pshns@ukr.net> 1300 */ 1301function media_restore($image, $rev, $auth){ 1302 global $conf; 1303 if ($auth < AUTH_UPLOAD || !$conf['mediarevisions']) return false; 1304 $removed = (!file_exists(mediaFN($image)) && file_exists(mediaMetaFN($image, '.changes'))); 1305 if (!$image || (!file_exists(mediaFN($image)) && !$removed)) return false; 1306 if (!$rev || !file_exists(mediaFN($image, $rev))) return false; 1307 list(,$imime,) = mimetype($image); 1308 $res = media_upload_finish(mediaFN($image, $rev), 1309 mediaFN($image), 1310 $image, 1311 $imime, 1312 true, 1313 'copy'); 1314 if (is_array($res)) { 1315 msg($res[0], $res[1]); 1316 return false; 1317 } 1318 return $res; 1319} 1320 1321/** 1322 * List all files found by the search request 1323 * 1324 * @author Tobias Sarnowski <sarnowski@cosmocode.de> 1325 * @author Andreas Gohr <gohr@cosmocode.de> 1326 * @author Kate Arzamastseva <pshns@ukr.net> 1327 * @triggers MEDIA_SEARCH 1328 * 1329 * @param string $query 1330 * @param string $ns 1331 * @param null|int $auth 1332 * @param bool $fullscreen 1333 * @param string $sort 1334 */ 1335function media_searchlist($query,$ns,$auth=null,$fullscreen=false,$sort='natural'){ 1336 global $conf; 1337 global $lang; 1338 1339 $ns = cleanID($ns); 1340 $evdata = array( 1341 'ns' => $ns, 1342 'data' => array(), 1343 'query' => $query 1344 ); 1345 if (!blank($query)) { 1346 $evt = new Event('MEDIA_SEARCH', $evdata); 1347 if ($evt->advise_before()) { 1348 $dir = utf8_encodeFN(str_replace(':','/',$evdata['ns'])); 1349 $quoted = preg_quote($evdata['query'],'/'); 1350 //apply globbing 1351 $quoted = str_replace(array('\*', '\?'), array('.*', '.'), $quoted, $count); 1352 1353 //if we use globbing file name must match entirely but may be preceded by arbitrary namespace 1354 if ($count > 0) $quoted = '^([^:]*:)*'.$quoted.'$'; 1355 1356 $pattern = '/'.$quoted.'/i'; 1357 search($evdata['data'], 1358 $conf['mediadir'], 1359 'search_mediafiles', 1360 array('showmsg'=>false,'pattern'=>$pattern), 1361 $dir, 1362 1, 1363 $sort); 1364 } 1365 $evt->advise_after(); 1366 unset($evt); 1367 } 1368 1369 if (!$fullscreen) { 1370 echo '<h1 id="media__ns">'.sprintf($lang['searchmedia_in'],hsc($ns).':*').'</h1>'.NL; 1371 media_searchform($ns,$query); 1372 } 1373 1374 if(!count($evdata['data'])){ 1375 echo '<div class="nothing">'.$lang['nothingfound'].'</div>'.NL; 1376 }else { 1377 if ($fullscreen) { 1378 echo '<ul class="' . _media_get_list_type() . '">'; 1379 } 1380 foreach($evdata['data'] as $item){ 1381 if (!$fullscreen) { 1382 // FIXME old call: media_printfile($item,$item['perm'],'',true); 1383 $display = new \dokuwiki\Ui\Media\DisplayRow($item); 1384 $display->relativeDisplay($ns); 1385 $display->show(); 1386 } else { 1387 // FIXME old call: media_printfile_thumbs($item,$item['perm'],false,true); 1388 $display = new \dokuwiki\Ui\Media\DisplayTile($item); 1389 $display->relativeDisplay($ns); 1390 echo '<li>'; 1391 $display->show(); 1392 echo '</li>'; 1393 } 1394 } 1395 if ($fullscreen) echo '</ul>'.NL; 1396 } 1397} 1398 1399/** 1400 * Display a media icon 1401 * 1402 * @param string $filename media id 1403 * @param string $size the size subfolder, if not specified 16x16 is used 1404 * @return string html 1405 */ 1406function media_printicon($filename, $size=''){ 1407 list($ext) = mimetype(mediaFN($filename),false); 1408 1409 if (file_exists(DOKU_INC.'lib/images/fileicons/'.$size.'/'.$ext.'.png')) { 1410 $icon = DOKU_BASE.'lib/images/fileicons/'.$size.'/'.$ext.'.png'; 1411 } else { 1412 $icon = DOKU_BASE.'lib/images/fileicons/'.$size.'/file.png'; 1413 } 1414 1415 return '<img src="'.$icon.'" alt="'.$filename.'" class="icon" />'; 1416} 1417 1418/** 1419 * Build link based on the current, adding/rewriting parameters 1420 * 1421 * @author Kate Arzamastseva <pshns@ukr.net> 1422 * 1423 * @param array|bool $params 1424 * @param string $amp separator 1425 * @param bool $abs absolute url? 1426 * @param bool $params_array return the parmeters array? 1427 * @return string|array - link or link parameters 1428 */ 1429function media_managerURL($params = false, $amp = '&', $abs = false, $params_array = false) { 1430 global $ID; 1431 global $INPUT; 1432 1433 $gets = array('do' => 'media'); 1434 $media_manager_params = array('tab_files', 'tab_details', 'image', 'ns', 'list', 'sort'); 1435 foreach ($media_manager_params as $x) { 1436 if ($INPUT->has($x)) $gets[$x] = $INPUT->str($x); 1437 } 1438 1439 if ($params) { 1440 $gets = $params + $gets; 1441 } 1442 unset($gets['id']); 1443 if (isset($gets['delete'])) { 1444 unset($gets['image']); 1445 unset($gets['tab_details']); 1446 } 1447 1448 if ($params_array) return $gets; 1449 1450 return wl($ID,$gets,$abs,$amp); 1451} 1452 1453/** 1454 * Print the media upload form if permissions are correct 1455 * 1456 * @author Andreas Gohr <andi@splitbrain.org> 1457 * @author Kate Arzamastseva <pshns@ukr.net> 1458 * 1459 * @param string $ns 1460 * @param int $auth permission level 1461 * @param bool $fullscreen 1462 */ 1463function media_uploadform($ns, $auth, $fullscreen = false) { 1464 global $lang; 1465 global $conf; 1466 global $INPUT; 1467 1468 if ($auth < AUTH_UPLOAD) { 1469 echo '<div class="nothing">'.$lang['media_perm_upload'].'</div>'.NL; 1470 return; 1471 } 1472 $auth_ow = (($conf['mediarevisions']) ? AUTH_UPLOAD : AUTH_DELETE); 1473 1474 $update = false; 1475 $id = ''; 1476 if ($auth >= $auth_ow && $fullscreen && $INPUT->str('mediado') == 'update') { 1477 $update = true; 1478 $id = cleanID($INPUT->str('image')); 1479 } 1480 1481 // The default HTML upload form 1482 $form = new Form([ 1483 'id' => 'dw__upload', 1484 'enctype' => 'multipart/form-data', 1485 'action' => ($fullscreen) 1486 ? media_managerURL(['tab_files' => 'files', 'tab_details' => 'view'], '&') 1487 : DOKU_BASE.'lib/exe/mediamanager.php', 1488 ]); 1489 $form->addTagOpen('div')->addClass('no'); 1490 $form->setHiddenField('ns', hsc($ns)); // FIXME hsc required? 1491 $form->addTagOpen('p'); 1492 $form->addTextInput('upload', $lang['txt_upload'])->id('upload__file') 1493 ->attrs(['type' => 'file']); 1494 $form->addTagClose('p'); 1495 $form->addTagOpen('p'); 1496 $form->addTextInput('mediaid', $lang['txt_filename'])->id('upload__name') 1497 ->val(noNS($id)); 1498 $form->addButton('', $lang['btn_upload'])->attr('type', 'submit'); 1499 $form->addTagClose('p'); 1500 if ($auth >= $auth_ow){ 1501 $form->addTagOpen('p'); 1502 $attrs = array(); 1503 if ($update) $attrs['checked'] = 'checked'; 1504 $form->addCheckbox('ow', $lang['txt_overwrt'])->id('dw__ow')->val('1') 1505 ->addClass('check')->attrs($attrs); 1506 $form->addTagClose('p'); 1507 } 1508 $form->addTagClose('div'); 1509 1510 if (!$fullscreen) { 1511 echo '<div class="upload">'. $lang['mediaupload'] .'</div>'.DOKU_LF; 1512 } else { 1513 echo DOKU_LF; 1514 } 1515 1516 echo '<div id="mediamanager__uploader">'.DOKU_LF; 1517 echo $form->toHTML('Upload'); 1518 echo '</div>'.DOKU_LF; 1519 1520 echo '<p class="maxsize">'; 1521 printf($lang['maxuploadsize'], filesize_h(media_getuploadsize())); 1522 echo ' <a class="allowedmime" href="#">'. $lang['allowedmime'] .'</a>'; 1523 echo ' <span>'. implode(', ', array_keys(getMimeTypes())) .'</span>'; 1524 echo '</p>'.DOKU_LF; 1525} 1526 1527/** 1528 * Returns the size uploaded files may have 1529 * 1530 * This uses a conservative approach using the lowest number found 1531 * in any of the limiting ini settings 1532 * 1533 * @returns int size in bytes 1534 */ 1535function media_getuploadsize(){ 1536 $okay = 0; 1537 1538 $post = (int) php_to_byte(@ini_get('post_max_size')); 1539 $suho = (int) php_to_byte(@ini_get('suhosin.post.max_value_length')); 1540 $upld = (int) php_to_byte(@ini_get('upload_max_filesize')); 1541 1542 if($post && ($post < $okay || $okay == 0)) $okay = $post; 1543 if($suho && ($suho < $okay || $okay == 0)) $okay = $suho; 1544 if($upld && ($upld < $okay || $okay == 0)) $okay = $upld; 1545 1546 return $okay; 1547} 1548 1549/** 1550 * Print the search field form 1551 * 1552 * @author Tobias Sarnowski <sarnowski@cosmocode.de> 1553 * @author Kate Arzamastseva <pshns@ukr.net> 1554 * 1555 * @param string $ns 1556 * @param string $query 1557 * @param bool $fullscreen 1558 */ 1559function media_searchform($ns, $query = '', $fullscreen = false) { 1560 global $lang; 1561 1562 // The default HTML search form 1563 $form = new Form([ 1564 'id' => 'dw__mediasearch', 1565 'action' => ($fullscreen) 1566 ? media_managerURL([], '&') 1567 : DOKU_BASE.'lib/exe/mediamanager.php', 1568 ]); 1569 $form->addTagOpen('div')->addClass('no'); 1570 $form->setHiddenField('ns', $ns); 1571 $form->setHiddenField($fullscreen ? 'mediado' : 'do', 'searchlist'); 1572 1573 $form->addTagOpen('p'); 1574 $form->addTextInput('q', $lang['searchmedia']) 1575 ->attr('title', sprintf($lang['searchmedia_in'], hsc($ns) .':*')) 1576 ->val($query); 1577 $form->addHTML(' '); 1578 $form->addButton('', $lang['btn_search'])->attr('type', 'submit'); 1579 $form->addTagClose('p'); 1580 $form->addTagClose('div'); 1581 print $form->toHTML('SearchMedia'); 1582} 1583 1584/** 1585 * Build a tree outline of available media namespaces 1586 * 1587 * @author Andreas Gohr <andi@splitbrain.org> 1588 * 1589 * @param string $ns 1590 */ 1591function media_nstree($ns){ 1592 global $conf; 1593 global $lang; 1594 1595 // currently selected namespace 1596 $ns = cleanID($ns); 1597 if(empty($ns)){ 1598 global $ID; 1599 $ns = (string)getNS($ID); 1600 } 1601 1602 $ns_dir = utf8_encodeFN(str_replace(':','/',$ns)); 1603 1604 $data = array(); 1605 search($data,$conf['mediadir'],'search_index',array('ns' => $ns_dir, 'nofiles' => true)); 1606 1607 // wrap a list with the root level around the other namespaces 1608 array_unshift($data, array('level' => 0, 'id' => '', 'open' =>'true', 1609 'label' => '['.$lang['mediaroot'].']')); 1610 1611 // insert the current ns into the hierarchy if it isn't already part of it 1612 $ns_parts = explode(':', $ns); 1613 $tmp_ns = ''; 1614 $pos = 0; 1615 foreach ($ns_parts as $level => $part) { 1616 if ($tmp_ns) $tmp_ns .= ':'.$part; 1617 else $tmp_ns = $part; 1618 1619 // find the namespace parts or insert them 1620 while ($data[$pos]['id'] != $tmp_ns) { 1621 if ( 1622 $pos >= count($data) || 1623 ($data[$pos]['level'] <= $level+1 && Sort::strcmp($data[$pos]['id'], $tmp_ns) > 0) 1624 ) { 1625 array_splice($data, $pos, 0, array(array('level' => $level+1, 'id' => $tmp_ns, 'open' => 'true'))); 1626 break; 1627 } 1628 ++$pos; 1629 } 1630 } 1631 1632 echo html_buildlist($data,'idx','media_nstree_item','media_nstree_li'); 1633} 1634 1635/** 1636 * Userfunction for html_buildlist 1637 * 1638 * Prints a media namespace tree item 1639 * 1640 * @author Andreas Gohr <andi@splitbrain.org> 1641 * 1642 * @param array $item 1643 * @return string html 1644 */ 1645function media_nstree_item($item){ 1646 global $INPUT; 1647 $pos = strrpos($item['id'], ':'); 1648 $label = substr($item['id'], $pos > 0 ? $pos + 1 : 0); 1649 if(empty($item['label'])) $item['label'] = $label; 1650 1651 $ret = ''; 1652 if (!($INPUT->str('do') == 'media')) 1653 $ret .= '<a href="'.DOKU_BASE.'lib/exe/mediamanager.php?ns='.idfilter($item['id']).'" class="idx_dir">'; 1654 else $ret .= '<a href="'.media_managerURL(['ns' => idfilter($item['id'], false), 'tab_files' => 'files']) 1655 .'" class="idx_dir">'; 1656 $ret .= $item['label']; 1657 $ret .= '</a>'; 1658 return $ret; 1659} 1660 1661/** 1662 * Userfunction for html_buildlist 1663 * 1664 * Prints a media namespace tree item opener 1665 * 1666 * @author Andreas Gohr <andi@splitbrain.org> 1667 * 1668 * @param array $item 1669 * @return string html 1670 */ 1671function media_nstree_li($item){ 1672 $class='media level'.$item['level']; 1673 if($item['open']){ 1674 $class .= ' open'; 1675 $img = DOKU_BASE.'lib/images/minus.gif'; 1676 $alt = '−'; 1677 }else{ 1678 $class .= ' closed'; 1679 $img = DOKU_BASE.'lib/images/plus.gif'; 1680 $alt = '+'; 1681 } 1682 // TODO: only deliver an image if it actually has a subtree... 1683 return '<li class="'.$class.'">'. 1684 '<img src="'.$img.'" alt="'.$alt.'" />'; 1685} 1686 1687/** 1688 * Resizes the given image to the given size 1689 * 1690 * @author Andreas Gohr <andi@splitbrain.org> 1691 * 1692 * @param string $file filename, path to file 1693 * @param string $ext extension 1694 * @param int $w desired width 1695 * @param int $h desired height 1696 * @return string path to resized or original size if failed 1697 */ 1698function media_resize_image($file, $ext, $w, $h=0){ 1699 global $conf; 1700 if(!$h) $h = $w; 1701 // we wont scale up to infinity 1702 if($w > 2000 || $h > 2000) return $file; 1703 1704 //cache 1705 $local = getCacheName($file,'.media.'.$w.'x'.$h.'.'.$ext); 1706 $mtime = (int) @filemtime($local); // 0 if not exists 1707 1708 $options = [ 1709 'quality' => $conf['jpg_quality'], 1710 'imconvert' => $conf['im_convert'], 1711 ]; 1712 1713 if( $mtime <= (int) @filemtime($file) ) { 1714 try { 1715 \splitbrain\slika\Slika::run($file, $options) 1716 ->autorotate() 1717 ->resize($w, $h) 1718 ->save($local, $ext); 1719 if($conf['fperm']) @chmod($local, $conf['fperm']); 1720 } catch (\splitbrain\slika\Exception $e) { 1721 dbglog($e->getMessage()); 1722 return $file; 1723 } 1724 } 1725 1726 return $local; 1727} 1728 1729/** 1730 * Center crops the given image to the wanted size 1731 * 1732 * @author Andreas Gohr <andi@splitbrain.org> 1733 * 1734 * @param string $file filename, path to file 1735 * @param string $ext extension 1736 * @param int $w desired width 1737 * @param int $h desired height 1738 * @return string path to resized or original size if failed 1739 */ 1740function media_crop_image($file, $ext, $w, $h=0){ 1741 global $conf; 1742 if(!$h) $h = $w; 1743 // we wont scale up to infinity 1744 if($w > 2000 || $h > 2000) return $file; 1745 1746 //cache 1747 $local = getCacheName($file,'.media.'.$w.'x'.$h.'.crop.'.$ext); 1748 $mtime = (int) @filemtime($local); // 0 if not exists 1749 1750 $options = [ 1751 'quality' => $conf['jpg_quality'], 1752 'imconvert' => $conf['im_convert'], 1753 ]; 1754 1755 if( $mtime <= (int) @filemtime($file) ) { 1756 try { 1757 \splitbrain\slika\Slika::run($file, $options) 1758 ->autorotate() 1759 ->crop($w, $h) 1760 ->save($local, $ext); 1761 if($conf['fperm']) @chmod($local, $conf['fperm']); 1762 } catch (\splitbrain\slika\Exception $e) { 1763 dbglog($e->getMessage()); 1764 return $file; 1765 } 1766 } 1767 1768 return $local; 1769} 1770 1771/** 1772 * Calculate a token to be used to verify fetch requests for resized or 1773 * cropped images have been internally generated - and prevent external 1774 * DDOS attacks via fetch 1775 * 1776 * @author Christopher Smith <chris@jalakai.co.uk> 1777 * 1778 * @param string $id id of the image 1779 * @param int $w resize/crop width 1780 * @param int $h resize/crop height 1781 * @return string token or empty string if no token required 1782 */ 1783function media_get_token($id,$w,$h){ 1784 // token is only required for modified images 1785 if ($w || $h || media_isexternal($id)) { 1786 $token = $id; 1787 if ($w) $token .= '.'.$w; 1788 if ($h) $token .= '.'.$h; 1789 1790 return substr(\dokuwiki\PassHash::hmac('md5', $token, auth_cookiesalt()),0,6); 1791 } 1792 1793 return ''; 1794} 1795 1796/** 1797 * Download a remote file and return local filename 1798 * 1799 * returns false if download fails. Uses cached file if available and 1800 * wanted 1801 * 1802 * @author Andreas Gohr <andi@splitbrain.org> 1803 * @author Pavel Vitis <Pavel.Vitis@seznam.cz> 1804 * 1805 * @param string $url 1806 * @param string $ext extension 1807 * @param int $cache cachetime in seconds 1808 * @return false|string path to cached file 1809 */ 1810function media_get_from_URL($url,$ext,$cache){ 1811 global $conf; 1812 1813 // if no cache or fetchsize just redirect 1814 if ($cache==0) return false; 1815 if (!$conf['fetchsize']) return false; 1816 1817 $local = getCacheName(strtolower($url),".media.$ext"); 1818 $mtime = @filemtime($local); // 0 if not exists 1819 1820 //decide if download needed: 1821 if(($mtime == 0) || // cache does not exist 1822 ($cache != -1 && $mtime < time() - $cache) // 'recache' and cache has expired 1823 ) { 1824 if(media_image_download($url, $local)) { 1825 return $local; 1826 } else { 1827 return false; 1828 } 1829 } 1830 1831 //if cache exists use it else 1832 if($mtime) return $local; 1833 1834 //else return false 1835 return false; 1836} 1837 1838/** 1839 * Download image files 1840 * 1841 * @author Andreas Gohr <andi@splitbrain.org> 1842 * 1843 * @param string $url 1844 * @param string $file path to file in which to put the downloaded content 1845 * @return bool 1846 */ 1847function media_image_download($url,$file){ 1848 global $conf; 1849 $http = new DokuHTTPClient(); 1850 $http->keep_alive = false; // we do single ops here, no need for keep-alive 1851 1852 $http->max_bodysize = $conf['fetchsize']; 1853 $http->timeout = 25; //max. 25 sec 1854 $http->header_regexp = '!\r\nContent-Type: image/(jpe?g|gif|png)!i'; 1855 1856 $data = $http->get($url); 1857 if(!$data) return false; 1858 1859 $fileexists = file_exists($file); 1860 $fp = @fopen($file,"w"); 1861 if(!$fp) return false; 1862 fwrite($fp,$data); 1863 fclose($fp); 1864 if(!$fileexists and $conf['fperm']) chmod($file, $conf['fperm']); 1865 1866 // check if it is really an image 1867 $info = @getimagesize($file); 1868 if(!$info){ 1869 @unlink($file); 1870 return false; 1871 } 1872 1873 return true; 1874} 1875 1876/** 1877 * resize images using external ImageMagick convert program 1878 * 1879 * @author Pavel Vitis <Pavel.Vitis@seznam.cz> 1880 * @author Andreas Gohr <andi@splitbrain.org> 1881 * 1882 * @param string $ext extension 1883 * @param string $from filename path to file 1884 * @param int $from_w original width 1885 * @param int $from_h original height 1886 * @param string $to path to resized file 1887 * @param int $to_w desired width 1888 * @param int $to_h desired height 1889 * @return bool 1890 */ 1891function media_resize_imageIM($ext,$from,$from_w,$from_h,$to,$to_w,$to_h){ 1892 global $conf; 1893 1894 // check if convert is configured 1895 if(!$conf['im_convert']) return false; 1896 1897 // prepare command 1898 $cmd = $conf['im_convert']; 1899 $cmd .= ' -resize '.$to_w.'x'.$to_h.'!'; 1900 if ($ext == 'jpg' || $ext == 'jpeg') { 1901 $cmd .= ' -quality '.$conf['jpg_quality']; 1902 } 1903 $cmd .= " $from $to"; 1904 1905 @exec($cmd,$out,$retval); 1906 if ($retval == 0) return true; 1907 return false; 1908} 1909 1910/** 1911 * crop images using external ImageMagick convert program 1912 * 1913 * @author Andreas Gohr <andi@splitbrain.org> 1914 * 1915 * @param string $ext extension 1916 * @param string $from filename path to file 1917 * @param int $from_w original width 1918 * @param int $from_h original height 1919 * @param string $to path to resized file 1920 * @param int $to_w desired width 1921 * @param int $to_h desired height 1922 * @param int $ofs_x offset of crop centre 1923 * @param int $ofs_y offset of crop centre 1924 * @return bool 1925 * @deprecated 2020-09-01 1926 */ 1927function media_crop_imageIM($ext,$from,$from_w,$from_h,$to,$to_w,$to_h,$ofs_x,$ofs_y){ 1928 global $conf; 1929 dbg_deprecated('splitbrain\\Slika'); 1930 1931 // check if convert is configured 1932 if(!$conf['im_convert']) return false; 1933 1934 // prepare command 1935 $cmd = $conf['im_convert']; 1936 $cmd .= ' -crop '.$to_w.'x'.$to_h.'+'.$ofs_x.'+'.$ofs_y; 1937 if ($ext == 'jpg' || $ext == 'jpeg') { 1938 $cmd .= ' -quality '.$conf['jpg_quality']; 1939 } 1940 $cmd .= " $from $to"; 1941 1942 @exec($cmd,$out,$retval); 1943 if ($retval == 0) return true; 1944 return false; 1945} 1946 1947/** 1948 * resize or crop images using PHP's libGD support 1949 * 1950 * @author Andreas Gohr <andi@splitbrain.org> 1951 * @author Sebastian Wienecke <s_wienecke@web.de> 1952 * 1953 * @param string $ext extension 1954 * @param string $from filename path to file 1955 * @param int $from_w original width 1956 * @param int $from_h original height 1957 * @param string $to path to resized file 1958 * @param int $to_w desired width 1959 * @param int $to_h desired height 1960 * @param int $ofs_x offset of crop centre 1961 * @param int $ofs_y offset of crop centre 1962 * @return bool 1963 * @deprecated 2020-09-01 1964 */ 1965function media_resize_imageGD($ext,$from,$from_w,$from_h,$to,$to_w,$to_h,$ofs_x=0,$ofs_y=0){ 1966 global $conf; 1967 dbg_deprecated('splitbrain\\Slika'); 1968 1969 if($conf['gdlib'] < 1) return false; //no GDlib available or wanted 1970 1971 // check available memory 1972 if(!is_mem_available(($from_w * $from_h * 4) + ($to_w * $to_h * 4))){ 1973 return false; 1974 } 1975 1976 // create an image of the given filetype 1977 $image = false; 1978 if ($ext == 'jpg' || $ext == 'jpeg'){ 1979 if(!function_exists("imagecreatefromjpeg")) return false; 1980 $image = @imagecreatefromjpeg($from); 1981 }elseif($ext == 'png') { 1982 if(!function_exists("imagecreatefrompng")) return false; 1983 $image = @imagecreatefrompng($from); 1984 1985 }elseif($ext == 'gif') { 1986 if(!function_exists("imagecreatefromgif")) return false; 1987 $image = @imagecreatefromgif($from); 1988 } 1989 if(!$image) return false; 1990 1991 $newimg = false; 1992 if(($conf['gdlib']>1) && function_exists("imagecreatetruecolor") && $ext != 'gif'){ 1993 $newimg = @imagecreatetruecolor ($to_w, $to_h); 1994 } 1995 if(!$newimg) $newimg = @imagecreate($to_w, $to_h); 1996 if(!$newimg){ 1997 imagedestroy($image); 1998 return false; 1999 } 2000 2001 //keep png alpha channel if possible 2002 if($ext == 'png' && $conf['gdlib']>1 && function_exists('imagesavealpha')){ 2003 imagealphablending($newimg, false); 2004 imagesavealpha($newimg,true); 2005 } 2006 2007 //keep gif transparent color if possible 2008 if($ext == 'gif' && function_exists('imagefill') && function_exists('imagecolorallocate')) { 2009 if(function_exists('imagecolorsforindex') && function_exists('imagecolortransparent')) { 2010 $transcolorindex = @imagecolortransparent($image); 2011 if($transcolorindex >= 0 ) { //transparent color exists 2012 $transcolor = @imagecolorsforindex($image, $transcolorindex); 2013 $transcolorindex = @imagecolorallocate( 2014 $newimg, 2015 $transcolor['red'], 2016 $transcolor['green'], 2017 $transcolor['blue'] 2018 ); 2019 @imagefill($newimg, 0, 0, $transcolorindex); 2020 @imagecolortransparent($newimg, $transcolorindex); 2021 }else{ //filling with white 2022 $whitecolorindex = @imagecolorallocate($newimg, 255, 255, 255); 2023 @imagefill($newimg, 0, 0, $whitecolorindex); 2024 } 2025 }else{ //filling with white 2026 $whitecolorindex = @imagecolorallocate($newimg, 255, 255, 255); 2027 @imagefill($newimg, 0, 0, $whitecolorindex); 2028 } 2029 } 2030 2031 //try resampling first 2032 if(function_exists("imagecopyresampled")){ 2033 if(!@imagecopyresampled($newimg, $image, 0, 0, $ofs_x, $ofs_y, $to_w, $to_h, $from_w, $from_h)) { 2034 imagecopyresized($newimg, $image, 0, 0, $ofs_x, $ofs_y, $to_w, $to_h, $from_w, $from_h); 2035 } 2036 }else{ 2037 imagecopyresized($newimg, $image, 0, 0, $ofs_x, $ofs_y, $to_w, $to_h, $from_w, $from_h); 2038 } 2039 2040 $okay = false; 2041 if ($ext == 'jpg' || $ext == 'jpeg'){ 2042 if(!function_exists('imagejpeg')){ 2043 $okay = false; 2044 }else{ 2045 $okay = imagejpeg($newimg, $to, $conf['jpg_quality']); 2046 } 2047 }elseif($ext == 'png') { 2048 if(!function_exists('imagepng')){ 2049 $okay = false; 2050 }else{ 2051 $okay = imagepng($newimg, $to); 2052 } 2053 }elseif($ext == 'gif') { 2054 if(!function_exists('imagegif')){ 2055 $okay = false; 2056 }else{ 2057 $okay = imagegif($newimg, $to); 2058 } 2059 } 2060 2061 // destroy GD image ressources 2062 if($image) imagedestroy($image); 2063 if($newimg) imagedestroy($newimg); 2064 2065 return $okay; 2066} 2067 2068/** 2069 * Return other media files with the same base name 2070 * but different extensions. 2071 * 2072 * @param string $src - ID of media file 2073 * @param string[] $exts - alternative extensions to find other files for 2074 * @return array - array(mime type => file ID) 2075 * 2076 * @author Anika Henke <anika@selfthinker.org> 2077 */ 2078function media_alternativefiles($src, $exts){ 2079 2080 $files = array(); 2081 list($srcExt, /* $srcMime */) = mimetype($src); 2082 $filebase = substr($src, 0, -1 * (strlen($srcExt)+1)); 2083 2084 foreach($exts as $ext) { 2085 $fileid = $filebase.'.'.$ext; 2086 $file = mediaFN($fileid); 2087 if(file_exists($file)) { 2088 list(/* $fileExt */, $fileMime) = mimetype($file); 2089 $files[$fileMime] = $fileid; 2090 } 2091 } 2092 return $files; 2093} 2094 2095/** 2096 * Check if video/audio is supported to be embedded. 2097 * 2098 * @param string $mime - mimetype of media file 2099 * @param string $type - type of media files to check ('video', 'audio', or null for all) 2100 * @return boolean 2101 * 2102 * @author Anika Henke <anika@selfthinker.org> 2103 */ 2104function media_supportedav($mime, $type=NULL){ 2105 $supportedAudio = array( 2106 'ogg' => 'audio/ogg', 2107 'mp3' => 'audio/mpeg', 2108 'wav' => 'audio/wav', 2109 ); 2110 $supportedVideo = array( 2111 'webm' => 'video/webm', 2112 'ogv' => 'video/ogg', 2113 'mp4' => 'video/mp4', 2114 ); 2115 if ($type == 'audio') { 2116 $supportedAv = $supportedAudio; 2117 } elseif ($type == 'video') { 2118 $supportedAv = $supportedVideo; 2119 } else { 2120 $supportedAv = array_merge($supportedAudio, $supportedVideo); 2121 } 2122 return in_array($mime, $supportedAv); 2123} 2124 2125/** 2126 * Return track media files with the same base name 2127 * but extensions that indicate kind and lang. 2128 * ie for foo.webm search foo.sub.lang.vtt, foo.cap.lang.vtt... 2129 * 2130 * @param string $src - ID of media file 2131 * @return array - array(mediaID => array( kind, srclang )) 2132 * 2133 * @author Schplurtz le Déboulonné <Schplurtz@laposte.net> 2134 */ 2135function media_trackfiles($src){ 2136 $kinds=array( 2137 'sub' => 'subtitles', 2138 'cap' => 'captions', 2139 'des' => 'descriptions', 2140 'cha' => 'chapters', 2141 'met' => 'metadata' 2142 ); 2143 2144 $files = array(); 2145 $re='/\\.(sub|cap|des|cha|met)\\.([^.]+)\\.vtt$/'; 2146 $baseid=pathinfo($src, PATHINFO_FILENAME); 2147 $pattern=mediaFN($baseid).'.*.*.vtt'; 2148 $list=glob($pattern); 2149 foreach($list as $track) { 2150 if(preg_match($re, $track, $matches)){ 2151 $files[$baseid.'.'.$matches[1].'.'.$matches[2].'.vtt']=array( 2152 $kinds[$matches[1]], 2153 $matches[2], 2154 ); 2155 } 2156 } 2157 return $files; 2158} 2159 2160/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ 2161