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')) define('DOKU_INC',fullpath(dirname(__FILE__).'/../').'/'); 10if(!defined('NL')) define('NL',"\n"); 11 12require_once(DOKU_INC.'inc/html.php'); 13require_once(DOKU_INC.'inc/search.php'); 14require_once(DOKU_INC.'inc/JpegMeta.php'); 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 */ 24function media_filesinuse($data,$id){ 25 global $lang; 26 echo '<h1>'.$lang['reference'].' <code>'.hsc(noNS($id)).'</code></h1>'; 27 echo '<p>'.hsc($lang['ref_inuse']).'</p>'; 28 29 $hidden=0; //count of hits without read permission 30 foreach($data as $row){ 31 if(auth_quickaclcheck($row) >= AUTH_READ && isVisiblePage($row)){ 32 echo '<div class="search_result">'; 33 echo '<span class="mediaref_ref">'.hsc($row).'</span>'; 34 echo '</div>'; 35 }else 36 $hidden++; 37 } 38 if ($hidden){ 39 print '<div class="mediaref_hidden">'.$lang['ref_hidden'].'</div>'; 40 } 41} 42 43/** 44 * Handles the saving of image meta data 45 * 46 * @author Andreas Gohr <andi@splitbrain.org> 47 */ 48function media_metasave($id,$auth,$data){ 49 if($auth < AUTH_UPLOAD) return false; 50 if(!checkSecurityToken()) return false; 51 global $lang; 52 global $conf; 53 $src = mediaFN($id); 54 55 $meta = new JpegMeta($src); 56 $meta->_parseAll(); 57 58 foreach($data as $key => $val){ 59 $val=trim($val); 60 if(empty($val)){ 61 $meta->deleteField($key); 62 }else{ 63 $meta->setField($key,$val); 64 } 65 } 66 67 if($meta->save()){ 68 if($conf['fperm']) chmod($src, $conf['fperm']); 69 msg($lang['metasaveok'],1); 70 return $id; 71 }else{ 72 msg($lang['metasaveerr'],-1); 73 return false; 74 } 75} 76 77/** 78 * Display the form to edit image meta data 79 * 80 * @author Andreas Gohr <andi@splitbrain.org> 81 */ 82function media_metaform($id,$auth){ 83 if($auth < AUTH_UPLOAD) return false; 84 global $lang; 85 86 // load the field descriptions 87 static $fields = null; 88 if(is_null($fields)){ 89 include(DOKU_CONF.'mediameta.php'); 90 if(@file_exists(DOKU_CONF.'mediameta.local.php')){ 91 include(DOKU_CONF.'mediameta.local.php'); 92 } 93 } 94 95 $src = mediaFN($id); 96 97 // output 98 echo '<h1>'.hsc(noNS($id)).'</h1>'.NL; 99 echo '<form action="'.DOKU_BASE.'lib/exe/mediamanager.php" accept-charset="utf-8" method="post" class="meta">'.NL; 100 formSecurityToken(); 101 foreach($fields as $key => $field){ 102 // get current value 103 $tags = array($field[0]); 104 if(is_array($field[3])) $tags = array_merge($tags,$field[3]); 105 $value = tpl_img_getTag($tags,'',$src); 106 $value = cleanText($value); 107 108 // prepare attributes 109 $p = array(); 110 $p['class'] = 'edit'; 111 $p['id'] = 'meta__'.$key; 112 $p['name'] = 'meta['.$field[0].']'; 113 114 // put label 115 echo '<div class="metafield">'; 116 echo '<label for="meta__'.$key.'">'; 117 echo ($lang[$field[1]]) ? $lang[$field[1]] : $field[1]; 118 echo ':</label>'; 119 120 // put input field 121 if($field[2] == 'text'){ 122 $p['value'] = $value; 123 $p['type'] = 'text'; 124 $att = buildAttributes($p); 125 echo "<input $att/>".NL; 126 }else{ 127 $att = buildAttributes($p); 128 echo "<textarea $att rows=\"6\" cols=\"50\">".formText($value).'</textarea>'.NL; 129 } 130 echo '</div>'.NL; 131 } 132 echo '<div class="buttons">'.NL; 133 echo '<input type="hidden" name="img" value="'.hsc($id).'" />'.NL; 134 echo '<input name="do[save]" type="submit" value="'.$lang['btn_save']. 135 '" title="'.$lang['btn_save'].' [S]" accesskey="s" class="button" />'.NL; 136 echo '<input name="do[cancel]" type="submit" value="'.$lang['btn_cancel']. 137 '" title="'.$lang['btn_cancel'].' [C]" accesskey="c" class="button" />'.NL; 138 echo '</div>'.NL; 139 echo '</form>'.NL; 140} 141 142 143/** 144 * Handles media file deletions 145 * 146 * If configured, checks for media references before deletion 147 * 148 * @author Andreas Gohr <andi@splitbrain.org> 149 * @return mixed false on error, true on delete or array with refs 150 */ 151function media_delete($id,$auth){ 152 if($auth < AUTH_DELETE) return false; 153 if(!checkSecurityToken()) return false; 154 global $conf; 155 global $lang; 156 157 // check for references if needed 158 $mediareferences = array(); 159 if($conf['refcheck']){ 160 require_once(DOKU_INC.'inc/fulltext.php'); 161 $mediareferences = ft_mediause($id,$conf['refshow']); 162 } 163 164 if(!count($mediareferences)){ 165 $file = mediaFN($id); 166 167 // trigger an event - MEDIA_DELETE_FILE 168 $data['name'] = basename($file); 169 $data['path'] = $file; 170 $data['size'] = (@file_exists($file)) ? filesize($file) : 0; 171 $evt = new Doku_Event('MEDIA_DELETE_FILE',$data); 172 if ($evt->advise_before()) { 173 if(@unlink($file)){ 174 msg(str_replace('%s',noNS($id),$lang['deletesucc']),1); 175 $del = io_sweepNS($id,'mediadir'); 176 if($del){ 177 // current namespace was removed. redirecting to root ns passing msg along 178 header('Location: '.DOKU_URL.'lib/exe/mediamanager.php?msg1='. 179 rawurlencode(str_replace('%s',noNS($id),$lang['deletesucc']))); 180 exit; 181 } 182 return true; 183 } 184 } 185 $evt->advise_after(); 186 unset($evt); 187 188 //something went wrong 189 msg(str_replace('%s',$file,$lang['deletefail']),-1); 190 return false; 191 }elseif(!$conf['refshow']){ 192 msg(str_replace('%s',noNS($id),$lang['mediainuse']),0); 193 return false; 194 } 195 196 return $mediareferences; 197} 198 199/** 200 * Handles media file uploads 201 * 202 * This generates an action event and delegates to _media_upload_action(). 203 * Action plugins are allowed to pre/postprocess the uploaded file. 204 * (The triggered event is preventable.) 205 * 206 * Event data: 207 * $data[0] fn_tmp: the temporary file name (read from $_FILES) 208 * $data[1] fn: the file name of the uploaded file 209 * $data[2] id: the future directory id of the uploaded file 210 * $data[3] imime: the mimetype of the uploaded file 211 * 212 * @triggers MEDIA_UPLOAD_FINISH 213 * @author Andreas Gohr <andi@splitbrain.org> 214 * @author Michael Klier <chi@chimeric.de> 215 * @return mixed false on error, id of the new file on success 216 */ 217function media_upload($ns,$auth){ 218 if($auth < AUTH_UPLOAD) return false; 219 if(!checkSecurityToken()) return false; 220 require_once(DOKU_INC.'inc/confutils.php'); 221 global $lang; 222 global $conf; 223 224 // get file and id 225 $id = $_POST['id']; 226 $file = $_FILES['upload']; 227 if(empty($id)) $id = $file['name']; 228 229 // check for data 230 if(!@filesize($file['tmp_name'])){ 231 msg('No data uploaded. Disk full?',-1); 232 return false; 233 } 234 235 // check extensions 236 list($fext,$fmime) = mimetype($file['name']); 237 list($iext,$imime) = mimetype($id); 238 if($fext && !$iext){ 239 // no extension specified in id - read original one 240 $id .= '.'.$fext; 241 $imime = $fmime; 242 }elseif($fext && $fext != $iext){ 243 // extension was changed, print warning 244 msg(sprintf($lang['mediaextchange'],$fext,$iext)); 245 } 246 247 // get filename 248 $id = cleanID($ns.':'.$id,false,true); 249 $fn = mediaFN($id); 250 251 // get filetype regexp 252 $types = array_keys(getMimeTypes()); 253 $types = array_map(create_function('$q','return preg_quote($q,"/");'),$types); 254 $regex = join('|',$types); 255 256 // because a temp file was created already 257 if(preg_match('/\.('.$regex.')$/i',$fn)){ 258 //check for overwrite 259 if(@file_exists($fn) && (!$_REQUEST['ow'] || $auth < AUTH_DELETE)){ 260 msg($lang['uploadexist'],0); 261 return false; 262 } 263 // check for valid content 264 $ok = media_contentcheck($file['tmp_name'],$imime); 265 if($ok == -1){ 266 msg(sprintf($lang['uploadbadcontent'],".$iext"),-1); 267 return false; 268 }elseif($ok == -2){ 269 msg($lang['uploadspam'],-1); 270 return false; 271 }elseif($ok == -3){ 272 msg($lang['uploadxss'],-1); 273 return false; 274 } 275 276 // prepare event data 277 $data[0] = $file['tmp_name']; 278 $data[1] = $fn; 279 $data[2] = $id; 280 $data[3] = $imime; 281 282 // trigger event 283 return trigger_event('MEDIA_UPLOAD_FINISH', $data, '_media_upload_action', true); 284 285 }else{ 286 msg($lang['uploadwrong'],-1); 287 } 288 return false; 289} 290 291/** 292 * Callback adapter for media_upload_finish() 293 * @author Michael Klier <chi@chimeric.de> 294 */ 295function _media_upload_action($data) { 296 // fixme do further sanity tests of given data? 297 if(is_array($data) && count($data)===4) { 298 return media_upload_finish($data[0], $data[1], $data[2], $data[3]); 299 } else { 300 return false; //callback error 301 } 302} 303 304/** 305 * Saves an uploaded media file 306 * 307 * @author Andreas Gohr <andi@splitbrain.org> 308 * @author Michael Klier <chi@chimeric.de> 309 */ 310function media_upload_finish($fn_tmp, $fn, $id, $imime) { 311 global $conf; 312 global $lang; 313 314 // prepare directory 315 io_createNamespace($id, 'media'); 316 317 if(move_uploaded_file($fn_tmp, $fn)) { 318 // Set the correct permission here. 319 // Always chmod media because they may be saved with different permissions than expected from the php umask. 320 // (Should normally chmod to $conf['fperm'] only if $conf['fperm'] is set.) 321 chmod($fn, $conf['fmode']); 322 msg($lang['uploadsucc'],1); 323 media_notify($id,$fn,$imime); 324 return $id; 325 }else{ 326 msg($lang['uploadfail'],-1); 327 } 328} 329 330/** 331 * This function checks if the uploaded content is really what the 332 * mimetype says it is. We also do spam checking for text types here. 333 * 334 * We need to do this stuff because we can not rely on the browser 335 * to do this check correctly. Yes, IE is broken as usual. 336 * 337 * @author Andreas Gohr <andi@splitbrain.org> 338 * @link http://www.splitbrain.org/blog/2007-02/12-internet_explorer_facilitates_cross_site_scripting 339 * @fixme check all 26 magic IE filetypes here? 340 */ 341function media_contentcheck($file,$mime){ 342 global $conf; 343 if($conf['iexssprotect']){ 344 $fh = @fopen($file, 'rb'); 345 if($fh){ 346 $bytes = fread($fh, 256); 347 fclose($fh); 348 if(preg_match('/<(script|a|img|html|body|iframe)[\s>]/i',$bytes)){ 349 return -3; 350 } 351 } 352 } 353 if(substr($mime,0,6) == 'image/'){ 354 $info = @getimagesize($file); 355 if($mime == 'image/gif' && $info[2] != 1){ 356 return -1; 357 }elseif($mime == 'image/jpeg' && $info[2] != 2){ 358 return -1; 359 }elseif($mime == 'image/png' && $info[2] != 3){ 360 return -1; 361 } 362 # fixme maybe check other images types as well 363 }elseif(substr($mime,0,5) == 'text/'){ 364 global $TEXT; 365 $TEXT = io_readFile($file); 366 if(checkwordblock()){ 367 return -2; 368 } 369 } 370 return 0; 371} 372 373/** 374 * Send a notify mail on uploads 375 * 376 * @author Andreas Gohr <andi@splitbrain.org> 377 */ 378function media_notify($id,$file,$mime){ 379 global $lang; 380 global $conf; 381 if(empty($conf['notify'])) return; //notify enabled? 382 383 $text = rawLocale('uploadmail'); 384 $text = str_replace('@DATE@',strftime($conf['dformat']),$text); 385 $text = str_replace('@BROWSER@',$_SERVER['HTTP_USER_AGENT'],$text); 386 $text = str_replace('@IPADDRESS@',$_SERVER['REMOTE_ADDR'],$text); 387 $text = str_replace('@HOSTNAME@',gethostbyaddr($_SERVER['REMOTE_ADDR']),$text); 388 $text = str_replace('@DOKUWIKIURL@',DOKU_URL,$text); 389 $text = str_replace('@USER@',$_SERVER['REMOTE_USER'],$text); 390 $text = str_replace('@MIME@',$mime,$text); 391 $text = str_replace('@MEDIA@',ml($id,'',true,'&',true),$text); 392 $text = str_replace('@SIZE@',filesize_h(filesize($file)),$text); 393 394 $from = $conf['mailfrom']; 395 $from = str_replace('@USER@',$_SERVER['REMOTE_USER'],$from); 396 $from = str_replace('@NAME@',$INFO['userinfo']['name'],$from); 397 $from = str_replace('@MAIL@',$INFO['userinfo']['mail'],$from); 398 399 $subject = '['.$conf['title'].'] '.$lang['mail_upload'].' '.$id; 400 401 mail_send($conf['notify'],$subject,$text,$from); 402} 403 404/** 405 * List all files in a given Media namespace 406 */ 407function media_filelist($ns,$auth=null,$jump=''){ 408 global $conf; 409 global $lang; 410 $ns = cleanID($ns); 411 412 // check auth our self if not given (needed for ajax calls) 413 if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*"); 414 415 echo '<h1 id="media__ns">:'.hsc($ns).'</h1>'.NL; 416 417 if($auth < AUTH_READ){ 418 // FIXME: print permission warning here instead? 419 echo '<div class="nothing">'.$lang['nothingfound'].'</div>'.NL; 420 return; 421 } 422 423 media_uploadform($ns, $auth); 424 425 $dir = utf8_encodeFN(str_replace(':','/',$ns)); 426 $data = array(); 427 search($data,$conf['mediadir'],'search_media',array('showmsg'=>true),$dir); 428 429 if(!count($data)){ 430 echo '<div class="nothing">'.$lang['nothingfound'].'</div>'.NL; 431 return; 432 } 433 434 foreach($data as $item){ 435 media_printfile($item,$auth,$jump); 436 } 437} 438 439/** 440 * Print action links for a file depending on filetype 441 * and available permissions 442 * 443 * @todo contains inline javascript 444 */ 445function media_fileactions($item,$auth){ 446 global $lang; 447 448 // view button 449 $link = ml($item['id'],'',true); 450 echo ' <a href="'.$link.'" target="_blank"><img src="'.DOKU_BASE.'lib/images/magnifier.png" '. 451 'alt="'.$lang['mediaview'].'" title="'.$lang['mediaview'].'" class="btn" /></a>'; 452 453 454 // no further actions if not writable 455 if(!$item['writable']) return; 456 457 // delete button 458 if($auth >= AUTH_DELETE){ 459 echo ' <a href="'.DOKU_BASE.'lib/exe/mediamanager.php?delete='.rawurlencode($item['id']). 460 '&sectok='.getSecurityToken().'" class="btn_media_delete" title="'.$item['id'].'">'. 461 '<img src="'.DOKU_BASE.'lib/images/trash.png" alt="'.$lang['btn_delete'].'" '. 462 'title="'.$lang['btn_delete'].'" class="btn" /></a>'; 463 } 464 465 // edit button 466 if($auth >= AUTH_UPLOAD && $item['isimg'] && $item['meta']->getField('File.Mime') == 'image/jpeg'){ 467 echo ' <a href="'.DOKU_BASE.'lib/exe/mediamanager.php?edit='.rawurlencode($item['id']).'">'. 468 '<img src="'.DOKU_BASE.'lib/images/pencil.png" alt="'.$lang['metaedit'].'" '. 469 'title="'.$lang['metaedit'].'" class="btn" /></a>'; 470 } 471 472} 473 474/** 475 * Formats and prints one file in the list 476 */ 477function media_printfile($item,$auth,$jump){ 478 global $lang; 479 global $conf; 480 481 // Prepare zebra coloring 482 // I always wanted to use this variable name :-D 483 static $twibble = 1; 484 $twibble *= -1; 485 $zebra = ($twibble == -1) ? 'odd' : 'even'; 486 487 // Automatically jump to recent action 488 if($jump == $item['id']) { 489 $jump = ' id="scroll__here" '; 490 }else{ 491 $jump = ''; 492 } 493 494 // Prepare fileicons 495 list($ext,$mime) = mimetype($item['file']); 496 $class = preg_replace('/[^_\-a-z0-9]+/i','_',$ext); 497 $class = 'select mediafile mf_'.$class; 498 499 // Prepare filename 500 $file = utf8_decodeFN($item['file']); 501 502 // Prepare info 503 $info = ''; 504 if($item['isimg']){ 505 $info .= (int) $item['meta']->getField('File.Width'); 506 $info .= '×'; 507 $info .= (int) $item['meta']->getField('File.Height'); 508 $info .= ' '; 509 } 510 $info .= '<i>'.strftime($conf['dformat'],$item['mtime']).'</i>'; 511 $info .= ' '; 512 $info .= filesize_h($item['size']); 513 514 // ouput 515 echo '<div class="'.$zebra.'"'.$jump.'>'.NL; 516 echo '<a name="h_'.$item['id'].'" class="'.$class.'">'.$file.'</a> '; 517 echo '<span class="info">('.$info.')</span>'.NL; 518 media_fileactions($item,$auth); 519 echo '<div class="example" id="ex_'.str_replace(':','_',$item['id']).'">'; 520 echo $lang['mediausage'].' <code>{{:'.$item['id'].'}}</code>'; 521 echo '</div>'; 522 if($item['isimg']) media_printimgdetail($item); 523 echo '<div class="clearer"></div>'.NL; 524 echo '</div>'.NL; 525} 526 527/** 528 * Prints a thumbnail and metainfos 529 */ 530function media_printimgdetail($item){ 531 // prepare thumbnail 532 $w = (int) $item['meta']->getField('File.Width'); 533 $h = (int) $item['meta']->getField('File.Height'); 534 if($w>120 || $h>120){ 535 $ratio = $item['meta']->getResizeRatio(120); 536 $w = floor($w * $ratio); 537 $h = floor($h * $ratio); 538 } 539 $src = ml($item['id'],array('w'=>$w,'h'=>$h)); 540 $p = array(); 541 $p['width'] = $w; 542 $p['height'] = $h; 543 $p['alt'] = $item['id']; 544 $p['class'] = 'thumb'; 545 $att = buildAttributes($p); 546 547 // output 548 echo '<div class="detail">'; 549 echo '<div class="thumb">'; 550 echo '<a name="d_'.$item['id'].'" class="select">'; 551 echo '<img src="'.$src.'" '.$att.' />'; 552 echo '</a>'; 553 echo '</div>'; 554 555 // read EXIF/IPTC data 556 $t = $item['meta']->getField(array('IPTC.Headline','xmp.dc:title')); 557 $d = $item['meta']->getField(array('IPTC.Caption','EXIF.UserComment', 558 'EXIF.TIFFImageDescription', 559 'EXIF.TIFFUserComment')); 560 if(utf8_strlen($d) > 250) $d = utf8_substr($d,0,250).'...'; 561 $k = $item['meta']->getField(array('IPTC.Keywords','IPTC.Category','xmp.dc:subject')); 562 563 // print EXIF/IPTC data 564 if($t || $d || $k ){ 565 echo '<p>'; 566 if($t) echo '<strong>'.htmlspecialchars($t).'</strong><br />'; 567 if($d) echo htmlspecialchars($d).'<br />'; 568 if($t) echo '<em>'.htmlspecialchars($k).'</em>'; 569 echo '</p>'; 570 } 571 echo '</div>'; 572} 573 574/** 575 * Print the media upload form if permissions are correct 576 * 577 * @author Andreas Gohr <andi@splitbrain.org> 578 */ 579function media_uploadform($ns, $auth){ 580 global $lang; 581 582 if($auth < AUTH_UPLOAD) return; //fixme print info on missing permissions? 583 584 // The default HTML upload form 585 $form = new Doku_Form('dw__upload', DOKU_BASE.'lib/exe/mediamanager.php', false, 'multipart/form-data'); 586 $form->addElement('<div class="upload">' . $lang['mediaupload'] . '</div>'); 587 $form->addElement(formSecurityToken()); 588 $form->addHidden('ns', hsc($ns)); 589 $form->addElement(form_makeOpenTag('p')); 590 $form->addElement(form_makeFileField('upload', $lang['txt_upload'].':', 'upload__file')); 591 $form->addElement(form_makeCloseTag('p')); 592 $form->addElement(form_makeOpenTag('p')); 593 $form->addElement(form_makeTextField('id', '', $lang['txt_filename'].':', 'upload__name')); 594 $form->addElement(form_makeButton('submit', '', $lang['btn_upload'])); 595 $form->addElement(form_makeCloseTag('p')); 596 597 if($auth >= AUTH_DELETE){ 598 $form->addElement(form_makeOpenTag('p')); 599 $form->addElement(form_makeCheckboxField('ow', 1, $lang['txt_overwrt'], 'dw__ow', 'check')); 600 $form->addElement(form_makeCloseTag('p')); 601 } 602 html_form('upload', $form); 603 604 // prepare flashvars for multiupload 605 $opt = array( 606 'L_gridname' => $lang['mu_gridname'] , 607 'L_gridsize' => $lang['mu_gridsize'] , 608 'L_gridstat' => $lang['mu_gridstat'] , 609 'L_namespace' => $lang['mu_namespace'] , 610 'L_overwrite' => $lang['txt_overwrt'], 611 'L_browse' => $lang['mu_browse'], 612 'L_upload' => $lang['btn_upload'], 613 'L_toobig' => $lang['mu_toobig'], 614 'L_ready' => $lang['mu_ready'], 615 'L_done' => $lang['mu_done'], 616 'L_fail' => $lang['mu_fail'], 617 'L_authfail' => $lang['mu_authfail'], 618 'L_progress' => $lang['mu_progress'], 619 'L_filetypes' => $lang['mu_filetypes'], 620 621 'O_ns' => ":$ns", 622 'O_backend' => 'mediamanager.php?'.session_name().'='.session_id(), 623 'O_size' => php_to_byte(ini_get('upload_max_filesize')), 624 'O_extensions'=> join('|',array_keys(getMimeTypes())), 625 'O_overwrite' => ($auth >= AUTH_DELETE), 626 'O_sectok' => getSecurityToken(), 627 'O_authtok' => auth_createToken(), 628 ); 629 $var = buildURLparams($opt); 630 // output the flash uploader 631 ?> 632 <div id="dw__flashupload" style="display:none"> 633 <div class="upload"><?php echo $lang['mu_intro']?></div> 634 <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="100%" height="100%" 635 codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab"> 636 <param name="movie" value="multipleUpload.swf?t=<?php echo time()?>" /> 637 <param name="quality" value="high" /> 638 <param name="bgcolor" value="#ffffff" /> 639 <param name="FlashVars" value="<?php echo $var?>" /> 640 <embed src="multipleUpload.swf?t=<?php echo time()?>" quality="high" bgcolor="#ffffff" 641 width="100%" height="100%" name="fileUpload" align="middle" 642 play="true" loop="false" quality="high" FlashVars="<?php echo $var?>" 643 allowScriptAccess="sameDomain" 644 type="application/x-shockwave-flash" 645 pluginspage="http://www.macromedia.com/go/getflashplayer"> 646 </embed> 647 </object> 648 </div> 649 <?php 650} 651 652/** 653 * Build a tree outline of available media namespaces 654 * 655 * @author Andreas Gohr <andi@splitbrain.org> 656 */ 657function media_nstree($ns){ 658 global $conf; 659 global $lang; 660 661 // currently selected namespace 662 $ns = cleanID($ns); 663 if(empty($ns)){ 664 $ns = dirname(str_replace(':','/',$ID)); 665 if($ns == '.') $ns =''; 666 } 667 $ns = utf8_encodeFN(str_replace(':','/',$ns)); 668 669 $data = array(); 670 search($data,$conf['mediadir'],'search_index',array('ns' => $ns, 'nofiles' => true)); 671 672 // wrap a list with the root level around the other namespaces 673 $item = array( 'level' => 0, 'id' => '', 674 'open' =>'true', 'label' => '['.$lang['mediaroot'].']'); 675 676 echo '<ul class="idx">'; 677 echo media_nstree_li($item); 678 echo media_nstree_item($item); 679 echo html_buildlist($data,'idx','media_nstree_item','media_nstree_li'); 680 echo '</li>'; 681 echo '</ul>'; 682} 683 684/** 685 * Userfunction for html_buildlist 686 * 687 * Prints a media namespace tree item 688 * 689 * @author Andreas Gohr <andi@splitbrain.org> 690 */ 691function media_nstree_item($item){ 692 $pos = strrpos($item['id'], ':'); 693 $label = substr($item['id'], $pos > 0 ? $pos + 1 : 0); 694 if(!$item['label']) $item['label'] = $label; 695 696 $ret = ''; 697 $ret .= '<a href="'.DOKU_BASE.'lib/exe/mediamanager.php?ns='.idfilter($item['id']).'" class="idx_dir">'; 698 $ret .= $item['label']; 699 $ret .= '</a>'; 700 return $ret; 701} 702 703/** 704 * Userfunction for html_buildlist 705 * 706 * Prints a media namespace tree item opener 707 * 708 * @author Andreas Gohr <andi@splitbrain.org> 709 */ 710function media_nstree_li($item){ 711 $class='media level'.$item['level']; 712 if($item['open']){ 713 $class .= ' open'; 714 $img = DOKU_BASE.'lib/images/minus.gif'; 715 $alt = '−'; 716 }else{ 717 $class .= ' closed'; 718 $img = DOKU_BASE.'lib/images/plus.gif'; 719 $alt = '+'; 720 } 721 return '<li class="'.$class.'">'. 722 '<img src="'.$img.'" alt="'.$alt.'" />'; 723} 724 725/** 726 * Resizes the given image to the given size 727 * 728 * @author Andreas Gohr <andi@splitbrain.org> 729 */ 730function media_resize_image($file, $ext, $w, $h=0){ 731 global $conf; 732 733 $info = @getimagesize($file); //get original size 734 if($info == false) return $file; // that's no image - it's a spaceship! 735 736 if(!$h) $h = round(($w * $info[1]) / $info[0]); 737 738 // we wont scale up to infinity 739 if($w > 2000 || $h > 2000) return $file; 740 741 //cache 742 $local = getCacheName($file,'.media.'.$w.'x'.$h.'.'.$ext); 743 $mtime = @filemtime($local); // 0 if not exists 744 745 if( $mtime > filemtime($file) || 746 media_resize_imageIM($ext,$file,$info[0],$info[1],$local,$w,$h) || 747 media_resize_imageGD($ext,$file,$info[0],$info[1],$local,$w,$h) ){ 748 if($conf['fperm']) chmod($local, $conf['fperm']); 749 return $local; 750 } 751 //still here? resizing failed 752 return $file; 753} 754 755/** 756 * Crops the given image to the wanted ratio, then calls media_resize_image to scale it 757 * to the wanted size 758 * 759 * Crops are centered horizontally but prefer the upper third of an vertical 760 * image because most pics are more interesting in that area (rule of thirds) 761 * 762 * @author Andreas Gohr <andi@splitbrain.org> 763 */ 764function media_crop_image($file, $ext, $w, $h=0){ 765 global $conf; 766 767 if(!$h) $h = $w; 768 $info = @getimagesize($file); //get original size 769 if($info == false) return $file; // that's no image - it's a spaceship! 770 771 // calculate crop size 772 $fr = $info[0]/$info[1]; 773 $tr = $w/$h; 774 if($tr >= 1){ 775 if($tr > $fr){ 776 $cw = $info[0]; 777 $ch = (int) $info[0]/$tr; 778 }else{ 779 $cw = (int) $info[1]*$tr; 780 $ch = $info[1]; 781 } 782 }else{ 783 if($tr < $fr){ 784 $cw = (int) $info[1]*$tr; 785 $ch = $info[1]; 786 }else{ 787 $cw = $info[0]; 788 $ch = (int) $info[0]/$tr; 789 } 790 } 791 // calculate crop offset 792 $cx = (int) ($info[0]-$cw)/2; 793 $cy = (int) ($info[1]-$ch)/3; 794 795 //cache 796 $local = getCacheName($file,'.media.'.$cw.'x'.$ch.'.crop.'.$ext); 797 $mtime = @filemtime($local); // 0 if not exists 798 799 if( $mtime > filemtime($file) || 800 media_crop_imageIM($ext,$file,$info[0],$info[1],$local,$cw,$ch,$cx,$cy) || 801 media_resize_imageGD($ext,$file,$cw,$ch,$local,$cw,$ch,$cx,$cy) ){ 802 if($conf['fperm']) chmod($local, $conf['fperm']); 803 return media_resize_image($local,$ext, $w, $h); 804 } 805 806 //still here? cropping failed 807 return media_resize_image($file,$ext, $w, $h); 808} 809 810/** 811 * Download a remote file and return local filename 812 * 813 * returns false if download fails. Uses cached file if available and 814 * wanted 815 * 816 * @author Andreas Gohr <andi@splitbrain.org> 817 * @author Pavel Vitis <Pavel.Vitis@seznam.cz> 818 */ 819function media_get_from_URL($url,$ext,$cache){ 820 global $conf; 821 822 // if no cache or fetchsize just redirect 823 if ($cache==0) return false; 824 if (!$conf['fetchsize']) return false; 825 826 $local = getCacheName(strtolower($url),".media.$ext"); 827 $mtime = @filemtime($local); // 0 if not exists 828 829 //decide if download needed: 830 if( ($mtime == 0) || // cache does not exist 831 ($cache != -1 && $mtime < time()-$cache) // 'recache' and cache has expired 832 ){ 833 if(media_image_download($url,$local)){ 834 return $local; 835 }else{ 836 return false; 837 } 838 } 839 840 //if cache exists use it else 841 if($mtime) return $local; 842 843 //else return false 844 return false; 845} 846 847/** 848 * Download image files 849 * 850 * @author Andreas Gohr <andi@splitbrain.org> 851 */ 852function media_image_download($url,$file){ 853 global $conf; 854 $http = new DokuHTTPClient(); 855 $http->max_bodysize = $conf['fetchsize']; 856 $http->timeout = 25; //max. 25 sec 857 $http->header_regexp = '!\r\nContent-Type: image/(jpe?g|gif|png)!i'; 858 859 $data = $http->get($url); 860 if(!$data) return false; 861 862 $fileexists = @file_exists($file); 863 $fp = @fopen($file,"w"); 864 if(!$fp) return false; 865 fwrite($fp,$data); 866 fclose($fp); 867 if(!$fileexists and $conf['fperm']) chmod($file, $conf['fperm']); 868 869 // check if it is really an image 870 $info = @getimagesize($file); 871 if(!$info){ 872 @unlink($file); 873 return false; 874 } 875 876 return true; 877} 878 879/** 880 * resize images using external ImageMagick convert program 881 * 882 * @author Pavel Vitis <Pavel.Vitis@seznam.cz> 883 * @author Andreas Gohr <andi@splitbrain.org> 884 */ 885function media_resize_imageIM($ext,$from,$from_w,$from_h,$to,$to_w,$to_h){ 886 global $conf; 887 888 // check if convert is configured 889 if(!$conf['im_convert']) return false; 890 891 // prepare command 892 $cmd = $conf['im_convert']; 893 $cmd .= ' -resize '.$to_w.'x'.$to_h.'!'; 894 if ($ext == 'jpg' || $ext == 'jpeg') { 895 $cmd .= ' -quality '.$conf['jpg_quality']; 896 } 897 $cmd .= " $from $to"; 898 899 @exec($cmd,$out,$retval); 900 if ($retval == 0) return true; 901 return false; 902} 903 904/** 905 * crop images using external ImageMagick convert program 906 * 907 * @author Andreas Gohr <andi@splitbrain.org> 908 */ 909function media_crop_imageIM($ext,$from,$from_w,$from_h,$to,$to_w,$to_h,$ofs_x,$ofs_y){ 910 global $conf; 911 912 // check if convert is configured 913 if(!$conf['im_convert']) return false; 914 915 // prepare command 916 $cmd = $conf['im_convert']; 917 $cmd .= ' -crop '.$to_w.'x'.$to_h.'+'.$ofs_x.'+'.$ofs_y; 918 if ($ext == 'jpg' || $ext == 'jpeg') { 919 $cmd .= ' -quality '.$conf['jpg_quality']; 920 } 921 $cmd .= " $from $to"; 922 923 @exec($cmd,$out,$retval); 924 if ($retval == 0) return true; 925 return false; 926} 927 928/** 929 * resize or crop images using PHP's libGD support 930 * 931 * @author Andreas Gohr <andi@splitbrain.org> 932 * @author Sebastian Wienecke <s_wienecke@web.de> 933 */ 934function media_resize_imageGD($ext,$from,$from_w,$from_h,$to,$to_w,$to_h,$ofs_x=0,$ofs_y=0){ 935 global $conf; 936 937 if($conf['gdlib'] < 1) return false; //no GDlib available or wanted 938 939 // check available memory 940 if(!is_mem_available(($from_w * $from_h * 4) + ($to_w * $to_h * 4))){ 941 return false; 942 } 943 944 // create an image of the given filetype 945 if ($ext == 'jpg' || $ext == 'jpeg'){ 946 if(!function_exists("imagecreatefromjpeg")) return false; 947 $image = @imagecreatefromjpeg($from); 948 }elseif($ext == 'png') { 949 if(!function_exists("imagecreatefrompng")) return false; 950 $image = @imagecreatefrompng($from); 951 952 }elseif($ext == 'gif') { 953 if(!function_exists("imagecreatefromgif")) return false; 954 $image = @imagecreatefromgif($from); 955 } 956 if(!$image) return false; 957 958 if(($conf['gdlib']>1) && function_exists("imagecreatetruecolor") && $ext != 'gif'){ 959 $newimg = @imagecreatetruecolor ($to_w, $to_h); 960 } 961 if(!$newimg) $newimg = @imagecreate($to_w, $to_h); 962 if(!$newimg){ 963 imagedestroy($image); 964 return false; 965 } 966 967 //keep png alpha channel if possible 968 if($ext == 'png' && $conf['gdlib']>1 && function_exists('imagesavealpha')){ 969 imagealphablending($newimg, false); 970 imagesavealpha($newimg,true); 971 } 972 973 //keep gif transparent color if possible 974 if($ext == 'gif' && function_exists('imagefill') && function_exists('imagecolorallocate')) { 975 if(function_exists('imagecolorsforindex') && function_exists('imagecolortransparent')) { 976 $transcolorindex = @imagecolortransparent($image); 977 if($transcolorindex >= 0 ) { //transparent color exists 978 $transcolor = @imagecolorsforindex($image, $transcolorindex); 979 $transcolorindex = @imagecolorallocate($newimg, $transcolor['red'], $transcolor['green'], $transcolor['blue']); 980 @imagefill($newimg, 0, 0, $transcolorindex); 981 @imagecolortransparent($newimg, $transcolorindex); 982 }else{ //filling with white 983 $whitecolorindex = @imagecolorallocate($newimg, 255, 255, 255); 984 @imagefill($newimg, 0, 0, $whitecolorindex); 985 } 986 }else{ //filling with white 987 $whitecolorindex = @imagecolorallocate($newimg, 255, 255, 255); 988 @imagefill($newimg, 0, 0, $whitecolorindex); 989 } 990 } 991 992 //try resampling first 993 if(function_exists("imagecopyresampled")){ 994 if(!@imagecopyresampled($newimg, $image, 0, 0, $ofs_x, $ofs_y, $to_w, $to_h, $from_w, $from_h)) { 995 imagecopyresized($newimg, $image, 0, 0, $ofs_x, $ofs_y, $to_w, $to_h, $from_w, $from_h); 996 } 997 }else{ 998 imagecopyresized($newimg, $image, 0, 0, $ofs_x, $ofs_y, $to_w, $to_h, $from_w, $from_h); 999 } 1000 1001 $okay = false; 1002 if ($ext == 'jpg' || $ext == 'jpeg'){ 1003 if(!function_exists('imagejpeg')){ 1004 $okay = false; 1005 }else{ 1006 $okay = imagejpeg($newimg, $to, $conf['jpg_quality']); 1007 } 1008 }elseif($ext == 'png') { 1009 if(!function_exists('imagepng')){ 1010 $okay = false; 1011 }else{ 1012 $okay = imagepng($newimg, $to); 1013 } 1014 }elseif($ext == 'gif') { 1015 if(!function_exists('imagegif')){ 1016 $okay = false; 1017 }else{ 1018 $okay = imagegif($newimg, $to); 1019 } 1020 } 1021 1022 // destroy GD image ressources 1023 if($image) imagedestroy($image); 1024 if($newimg) imagedestroy($newimg); 1025 1026 return $okay; 1027} 1028 1029