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