1<?php 2/** 3 * Filelist Plugin: Lists files matching a given glob pattern. 4 * 5 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 6 * @author Gina Haeussge <osd@foosel.net> 7 */ 8 9define('DOKU_PLUGIN_FILELIST_NOMATCH', -1); 10define('DOKU_PLUGIN_FILELIST_OUTSIDEJAIL', -2); 11 12/** 13 * All DokuWiki plugins to extend the parser/rendering mechanism 14 * need to inherit from this class 15 */ 16class syntax_plugin_filelist extends DokuWiki_Syntax_Plugin { 17 18 var $mediadir; 19 var $is_odt_export = false; 20 21 function __construct() { 22 global $conf; 23 $mediadir = $conf['mediadir']; 24 if (!$this->_path_is_absolute($mediadir)) { 25 $mediadir = DOKU_INC . '/' . $mediadir; 26 } 27 $this->mediadir = $this->_win_path_convert($this->_realpath($mediadir).'/'); 28 } 29 30 function getType(){ return 'substition'; } 31 function getPType(){ return 'block'; } 32 function getSort(){ return 222; } 33 34 function connectTo($mode) { 35 $this->Lexer->addSpecialPattern('\{\{filename>.+?\}\}',$mode,'plugin_filelist'); 36 $this->Lexer->addSpecialPattern('\{\{filelist>.+?\}\}',$mode,'plugin_filelist'); 37 } 38 39 /** 40 * Handle the match 41 */ 42 function handle($match, $state, $pos, Doku_Handler $handler) { 43 44 // do not allow the syntax in comments 45 if (!$this->getConf('allow_in_comments') && isset($_REQUEST['comment'])) 46 return false; 47 48 $match = substr($match, 2, -2); 49 list($type, $match) = explode('>', $match, 2); 50 list($pattern, $flags) = explode('&', $match, 2); 51 52 if ($type == 'filename') { 53 if (strpos($flags, '|') !== FALSE) { 54 list($flags, $title) = explode('\|', $flags); 55 } else { 56 $title = ''; 57 } 58 } 59 60 // load default config options 61 $flags = $this->getConf('defaults').'&'.$flags; 62 63 $flags = explode('&', $flags); 64 $params = array( 65 'sort' => 'name', 66 'order' => 'asc', 67 'index' => 0, 68 'limit' => 0, 69 'offset' => 0, 70 'style' => 'list', 71 'tableheader' => 0, 72 'tableshowsize' => 0, 73 'tableshowdate' => 0, 74 'direct' => 0, 75 'recursive' => 0, 76 'titlefile' => '_title.txt', 77 'cache' => 0, 78 'randlinks' => 0, 79 'preview' => 0, 80 'previewsize' => 32, 81 'link' => 2, 82 'showsize' => 0, 83 'showdate' => 0, 84 'showcreator' => 0, 85 'listsep' => '", "', 86 'onhover' => 0, 87 'ftp' => 0, 88 ); 89 foreach($flags as $flag) { 90 list($name, $value) = explode('=', $flag); 91 $params[trim($name)] = trim($value); 92 } 93 94 // recursive filelistings are not supported for the filename command 95 if ($type == 'filename') { 96 $params['recursive'] = 0; 97 } 98 99 // Trim list separator 100 $params['listsep'] = trim($params['listsep'], '"'); 101 102 return array($type, $pattern, $params, $title, $pos); 103 } 104 105 /** 106 * Create output 107 */ 108 function render($mode, Doku_Renderer $renderer, $data) { 109 global $conf; 110 111 list($type, $pattern, $params, $title, $pos) = $data; 112 113 if ($mode == 'odt') { 114 $this->is_odt_export = true; 115 } 116 117 // disable caching 118 if ($params['cache'] === 0) { 119 $renderer->nocache(); 120 } 121 if ($mode == 'xhtml' || $mode == 'odt') { 122 123 $result = $this->_create_filelist($pattern, $params); 124 if ($type == 'filename') { 125 $result = $this->_filter_out_directories($result); 126 } 127 128 // if we got nothing back, display a message 129 if ($result == DOKU_PLUGIN_FILELIST_NOMATCH) { 130 $renderer->cdata('[n/a: ' . $this->getLang('error_nomatch') . ']'); 131 return true; 132 } else if ($result == DOKU_PLUGIN_FILELIST_OUTSIDEJAIL) { 133 $renderer->cdata('[n/a: ' . $this->getLang('error_outsidejail') . ']'); 134 return true; 135 } 136 137 // if limit is set for a filelist, cut out the relevant slice from the files 138 if (($type == 'filelist') && ($params['limit'] != 0)) { 139 $result['files'] = array_slice($result['files'], $params['offset'], $params['limit']); 140 } 141 142 switch ($type) { 143 144 case 'filename': 145 146 $filename = $result['files'][$params['index']]['name']; 147 $filepath = $result['files'][$params['index']]['path']; 148 149 $this->_render_link($filename, $filepath, $result['basedir'], $result['webdir'], $params, $renderer); 150 return true; 151 152 case 'filelist': 153 if (count($result['files']) == 0) 154 break; 155 156 switch ($params['style']) { 157 case 'list': 158 case 'olist': 159 if (!$this->is_odt_export) { 160 $renderer->doc .= '<div class="filelist-plugin">'.DOKU_LF; 161 } 162 $this->_render_list($result, $params, $renderer); 163 if (!$this->is_odt_export) { 164 $renderer->doc .= '</div>'.DOKU_LF; 165 } 166 break; 167 168 case 'table': 169 if (!$this->is_odt_export) { 170 $renderer->doc .= '<div class="filelist-plugin">'.DOKU_LF; 171 } 172 $this->_render_table($result, $params, $pos, $renderer); 173 if (!$this->is_odt_export) { 174 $renderer->doc .= '</div>'.DOKU_LF; 175 } 176 break; 177 178 case 'page': 179 $this->_render_page($result, $params, $renderer); 180 break; 181 } 182 return true; 183 184 } 185 } 186 return false; 187 } 188 189 //~~ Render functions 190 191 /** 192 * Creates the downloadlink for the given filename, based on the given 193 * parameters, and adds it to the output of the renderer. 194 * 195 * @param $filename the name of the file 196 * @param $filepath the path of the file 197 * @param $basedir the basedir of the file 198 * @param $webdir the base URL of the file 199 * @param $params the parameters of the filelist command 200 * @param $renderer the renderer to use 201 * @return void 202 */ 203 function _render_link($filename, $filepath, $basedir, $webdir, $params, Doku_Renderer $renderer) { 204 global $conf; 205 206 //prepare for formating 207 $link['target'] = $conf['target']['extern']; 208 $link['style'] = ''; 209 $link['pre'] = ''; 210 $link['suf'] = ''; 211 $link['more'] = ''; 212 $link['url'] = $this->_get_link_url ($filepath, $basedir, $webdir, $params['randlinks'], $params['direct'], $params['ftp']); 213 214 $link['name'] = $filename; 215 $link['title'] = $renderer->_xmlEntities($link['url']); 216 if($conf['relnofollow']) $link['more'] .= ' rel="nofollow"'; 217 218 if ($params['link']) { 219 switch ($params['link']) { 220 case 1: 221 // Link without background image 222 $link['class'] = 'media'; 223 break; 224 default: 225 // Link with background image 226 list($ext,$mime) = mimetype(basename($filepath)); 227 $link['class'] .= ' mediafile mf_'.$ext; 228 break; 229 } 230 231 //output formatted 232 if ( !$this->is_odt_export ) { 233 $renderer->doc .= $renderer->_formatLink($link); 234 } else { 235 $this->render_odt_link ($link, $renderer); 236 } 237 } else { 238 // No link, just plain text. 239 $renderer->cdata($filename); 240 } 241 } 242 243 /** 244 * Renders a link for odt mode. 245 * 246 * @param $link the link parameters 247 * @param $renderer the renderer to use 248 * @return void 249 */ 250 protected function render_odt_link ($link, Doku_Renderer $renderer) { 251 if ( method_exists ($renderer, 'getODTProperties') === true ) { 252 $properties = array (); 253 254 // Get CSS properties for ODT export. 255 $renderer->getODTProperties ($properties, 'a', $link['class'], NULL, 'screen'); 256 257 // Insert image if present for that media class. 258 if ( empty($properties ['background-image']) === false ) { 259 $properties ['background-image'] = 260 $renderer->replaceURLPrefix ($properties ['background-image'], DOKU_INC); 261 $renderer->_odtAddImage ($properties ['background-image']); 262 } 263 } 264 265 // Render link. 266 $renderer->externallink($link['url'], $link['name']); 267 } 268 269 /** 270 * Renders a list. 271 * 272 * @param $result the filelist to render 273 * @param $params the parameters of the filelist call 274 * @param $renderer the renderer to use 275 * @return void 276 */ 277 function _render_list($result, $params, Doku_Renderer $renderer) { 278 $this->_render_list_items($result['files'], $result['basedir'], $result['webdir'], $params, $renderer); 279 } 280 281 /** 282 * Recursively renders a tree of files as list items. 283 * 284 * @param $files the files to render 285 * @param $basedir the basedir to use 286 * @param $webdir the webdir to use 287 * @param $params the parameters of the filelist call 288 * @param $renderer the renderer to use 289 * @param $level the level to render 290 * @return void 291 */ 292 function _render_list_items($files, $basedir, $webdir, $params, Doku_Renderer $renderer, $level = 1) { 293 global $conf; 294 295 if ($params['style'] == 'olist') { 296 $renderer->listo_open(); 297 } else { 298 $renderer->listu_open(); 299 } 300 301 foreach ($files as $file) { 302 if ($file['children'] !== false && $file['treesize'] > 0) { 303 // render the directory and its subtree 304 $renderer->listitem_open($level); 305 if ($this->is_odt_export) { 306 $renderer->p_open(); 307 } 308 $renderer->cdata($file['name']); 309 $this->_render_list_items($file['children'], $basedir, $webdir, $params, $renderer, $level+1); 310 if ($this->is_odt_export) { 311 $renderer->p_close(); 312 } 313 $renderer->listitem_close(); 314 } else if ($file['children'] === false) { 315 // open list item 316 $renderer->listitem_open($level); 317 if ($this->is_odt_export) { 318 $renderer->p_open(); 319 } 320 321 // render the preview image 322 if ($params['preview']) { 323 $this->_render_preview_image($file['path'], $basedir, $webdir, $params, $renderer); 324 } 325 326 // render the file link 327 $this->_render_link($file['name'], $file['path'], $basedir, $webdir, $params, $renderer); 328 329 // render filesize 330 if ($params['showsize']) { 331 $renderer->cdata($params['listsep'].$this->_size_readable($file['size'], 'PiB', 'bi', '%01.1f %s')); 332 } 333 334 // render lastmodified 335 if ($params['showdate']) { 336 $renderer->cdata($params['listsep'].strftime($conf['dformat'], $file['mtime'])); 337 } 338 339 // render lastmodified 340 if ($params['showcreator']) { 341 $renderer->cdata($params['listsep'].$file['creator']); 342 } 343 344 // close list item 345 if ($this->is_odt_export) { 346 $renderer->p_close(); 347 } 348 $renderer->listitem_close(); 349 350 } else { 351 // ignore empty directories 352 continue; 353 } 354 } 355 356 if ($params['style'] == 'olist') { 357 $renderer->listo_close(); 358 } else { 359 $renderer->listu_close(); 360 } 361 } 362 363 /** 364 * Renders the files as a table, including details if configured that way. 365 * 366 * @param $result the filelist to render 367 * @param $params the parameters of the filelist call 368 * @param $renderer the renderer to use 369 * @return void 370 */ 371 function _render_table($result, $params, $pos, Doku_Renderer $renderer) { 372 global $conf; 373 374 if (!$this->is_odt_export) { 375 $renderer->table_open(NULL, NULL, $pos); 376 } else { 377 $columns = 1; 378 if ($params['tableshowsize'] || $params['showsize']) { 379 $columns++; 380 } 381 if ($params['tableshowdate'] || $params['showdate']) { 382 $columns++; 383 } 384 if ($params['showcreator']) { 385 $columns++; 386 } 387 if ($params['preview']) { 388 $columns++; 389 } 390 $renderer->table_open($columns, NULL, $pos); 391 } 392 393 if ($params['tableheader']) { 394 if ($this->is_odt_export) { 395 $renderer->tablerow_open(); 396 } 397 398 $renderer->tableheader_open(); 399 $renderer->cdata($this->getLang('filename')); 400 $renderer->tableheader_close(); 401 402 if ($params['tableshowsize'] || $params['showsize']) { 403 $renderer->tableheader_open(); 404 $renderer->cdata($this->getLang('filesize')); 405 $renderer->tableheader_close(); 406 } 407 408 if ($params['tableshowdate'] || $params['showdate']) { 409 $renderer->tableheader_open(); 410 $renderer->cdata($this->getLang('lastmodified')); 411 $renderer->tableheader_close(); 412 } 413 414 if ($params['showcreator']) { 415 $renderer->tableheader_open(); 416 $renderer->cdata($this->getLang('createdby')); 417 $renderer->tableheader_close(); 418 } 419 420 if ($params['preview']) { 421 $renderer->tableheader_open(1, 'center', 1); 422 switch ($params['preview']) { 423 case 1: 424 $renderer->cdata($this->getLang('preview').' / '.$this->getLang('filetype')); 425 break; 426 case 2: 427 $renderer->cdata($this->getLang('preview')); 428 break; 429 case 3: 430 $renderer->cdata($this->getLang('filetype')); 431 break; 432 } 433 $renderer->tableheader_close(); 434 } 435 436 if ($this->is_odt_export) { 437 $renderer->tablerow_close(); 438 } 439 } 440 441 foreach ($result['files'] as $file) { 442 $renderer->tablerow_open(); 443 $renderer->tablecell_open(); 444 $this->_render_link($file['name'], $file['path'], $result['basedir'], $result['webdir'], $params, $renderer); 445 $renderer->tablecell_close(); 446 447 if ($params['tableshowsize'] || $params['showsize']) { 448 $renderer->tablecell_open(1, 'right'); 449 $renderer->cdata($this->_size_readable($file['size'], 'PiB', 'bi', '%01.1f %s')); 450 $renderer->tablecell_close(); 451 } 452 453 if ($params['tableshowdate'] || $params['showdate']) { 454 $renderer->tablecell_open(); 455 $renderer->cdata(strftime($conf['dformat'], $file['mtime'])); 456 $renderer->tablecell_close(); 457 } 458 459 if ($params['showcreator']) { 460 $renderer->tablecell_open(); 461 $renderer->cdata($file['creator']); 462 $renderer->tablecell_close(); 463 } 464 465 if ($params['preview']) { 466 $renderer->tablecell_open(1, 'center', 1); 467 468 $this->_render_preview_image($file['path'], $result['basedir'], $result['webdir'], $params, $renderer); 469 $renderer->tablecell_close(); 470 } 471 472 $renderer->tablerow_close(); 473 } 474 $renderer->table_close($pos); 475 } 476 477 /** 478 * Renders a page. 479 * 480 * @param $result the filelist to render 481 * @param $params the parameters of the filelist call 482 * @param $renderer the renderer to use 483 * @return void 484 */ 485 function _render_page($result, $params, Doku_Renderer $renderer) { 486 if ( method_exists ($renderer, 'getLastlevel') === false ) { 487 $class_vars = get_class_vars (get_class($renderer)); 488 if ($class_vars ['lastlevel'] !== NULL) { 489 // Old releases before "hrun": $lastlevel is accessible 490 $lastlevel = $renderer->lastlevel + 1; 491 } else { 492 // Release "hrun" or newer without method 'getLastlevel()'. 493 // Lastlevel can't be determined. Workaroud: always use level 1. 494 $lastlevel = 1; 495 } 496 } else { 497 $lastlevel = $renderer->getLastlevel() + 1; 498 } 499 $this->_render_page_section($result['files'], $result['basedir'], $result['webdir'], $params, $renderer, $lastlevel); 500 } 501 502 /** 503 * Recursively renders a tree of files as page sections using headlines. 504 * 505 * @param $files the files to render 506 * @param $basedir the basedir to use 507 * @param $webdir the webdir to use 508 * @param $params the parameters of the filelist call 509 * @param $renderer the renderer to use 510 * @param $level the level to render 511 * @return void 512 */ 513 function _render_page_section($files, $basedir, $webdir, $params, Doku_Renderer $renderer, $level) { 514 $trees = array(); 515 $leafs = array(); 516 517 foreach ($files as $file) { 518 if ($file['children'] !== false) { 519 if ($file['treesize'] > 0) { 520 $trees[] = $file; 521 } 522 } else { 523 $leafs[] = $file; 524 } 525 } 526 527 $this->_render_list_items($leafs, $basedir, $webdir, $params, $renderer); 528 529 if ($level < 7) { 530 foreach ($trees as $tree) { 531 $renderer->header($tree['name'], $level, 0); 532 $renderer->section_open($level); 533 $this->_render_page_section($tree['children'], $basedir, $webdir, $params, $renderer, $level + 1); 534 $renderer->section_close(); 535 } 536 } else { 537 $this->_render_list_items($trees, $basedir, $webdir, $params, $renderer); 538 } 539 } 540 541 /** 542 * Render a preview item for file $filepath. 543 * 544 * @param $filepath the file for which a preview image shall be rendered 545 * @param $basedir the basedir to use 546 * @param $webdir the webdir to use 547 * @param $params the parameters of the filelist call 548 * @param $renderer the renderer to use 549 * @return void 550 */ 551 protected function _render_preview_image ($filepath, $basedir, $webdir, $params, Doku_Renderer $renderer) { 552 $imagepath = $this->get_preview_image_path($filepath, $params, $isImage); 553 if (!empty($imagepath)) { 554 if ($isImage == false) { 555 // Generate link to returned filetype icon 556 $imgLink = $this->_get_link_url ($imagepath, $basedir, $webdir, 0, 1); 557 } else { 558 // Generate link to image file 559 $imgLink = $this->_get_link_url ($filepath, $basedir, $webdir, $params['randlinks'], $params['direct'], $params['ftp']); 560 } 561 562 $previewsize = $params['previewsize']; 563 if ($previewsize == 0) { 564 $previewsize = 32; 565 } 566 $imgclass = ''; 567 if ($params['onhover']) { 568 $imgclass = 'class="filelist_preview"'; 569 } 570 571 if (!$this->is_odt_export) { 572 $renderer->doc .= '<img '.$imgclass.' style=" max-height: '.$previewsize.'px; max-width: '.$previewsize.'px;" src="'.$imgLink.'">'; 573 } else { 574 list($width, $height) = $renderer->_odtGetImageSize ($imagepath, $previewsize, $previewsize); 575 $renderer->_odtAddImage ($imagepath, $width.'cm', $height.'cm'); 576 } 577 } 578 } 579 580 //~~ Filelist functions 581 582 /** 583 * Creates the filelist based on the given glob-pattern and 584 * sorting and ordering parameters. 585 * 586 * @param $pattern the pattern 587 * @param $params the parameters of the filelist command 588 * @return a filelist data structure containing the found files and base- 589 * and webdir 590 */ 591 function _create_filelist($pattern, $params) { 592 global $conf; 593 global $ID; 594 595 $allowed_absolute_paths = explode(',', $this->getConf('allowed_absolute_paths')); 596 597 $result = array( 598 'files' => array(), 599 'basedir' => false, 600 'webdir' => false, 601 ); 602 603 // we don't want to use $conf['media'] here as that path has symlinks resolved 604 if (!$params['direct']) { 605 // if media path is not absolute, precede it with the current namespace 606 if ($pattern[0] != ':') { 607 $pattern = ':'.getNS($ID) . ':' . $pattern; 608 } 609 // replace : with / and prepend mediadir 610 $pattern = $this->mediadir . str_replace(':', '/', $pattern); 611 } elseif($params['direct'] == 2){ 612 // treat path as relative to first configured path 613 $pattern = $allowed_absolute_paths[0].'/'.$pattern; 614 } else { 615 // if path is not absolute, precede it with DOKU_INC 616 if (!$this->_path_is_absolute($pattern)) { 617 $pattern = DOKU_INC.$pattern; 618 } 619 } 620 // get the canonicalized basedir (without resolving symlinks) 621 $dir = $this->_realpath($this->_win_path_convert(dirname($pattern))).'/'; 622 623 // if the directory does not exist, we of course have no matches 624 if (!$dir || !file_exists($dir)) { 625 return DOKU_PLUGIN_FILELIST_NOMATCH; 626 } 627 628 // match pattern aginst allowed paths 629 $web_paths = explode(',', $this->getConf('web_paths')); 630 $basedir = false; 631 $webdir = false; 632 if (count($allowed_absolute_paths) == count($web_paths)) { 633 for($i = 0; $i < count($allowed_absolute_paths); $i++) { 634 $abs_path = $this->_win_path_convert(trim($allowed_absolute_paths[$i])); 635 if (strstr($dir, $abs_path) == $dir) { 636 $basedir = $abs_path; 637 $webdir = trim($web_paths[$i]); 638 break; 639 } 640 } 641 } 642 643 // $basedir is false if $dir was not in one of the allowed paths 644 if ($basedir === false) { 645 return DOKU_PLUGIN_FILELIST_OUTSIDEJAIL; 646 } 647 648 // retrieve fileinformation 649 $result['basedir'] = $basedir; 650 $result['webdir'] = $webdir; 651 $result['files'] = $this->_crawl_files($this->_win_path_convert($pattern), $params); 652 if (!$result['files']) { 653 return DOKU_PLUGIN_FILELIST_NOMATCH; 654 } 655 656 // flatten filelist if the displaymode is table 657 if ($params['style'] == 'table') { 658 $result['files'] = $this->_flatten_filelist($result['files']); 659 } 660 661 // sort the list 662 $callback = false; 663 $reverseflag = false; 664 if ($params['sort'] == 'mtime') { 665 $callback = array($this, '_compare_mtimes'); 666 if ($params['order'] == 'asc') $reverseflag = true; 667 } else if ($params['sort'] == 'ctime') { 668 $callback = array($this, '_compare_ctimes'); 669 if ($params['order'] == 'asc') $reverseflag = true; 670 } else if ($params['sort'] == 'size') { 671 $callback = array($this, '_compare_sizes'); 672 if ($params['order'] == 'desc') $reverseflag = true; 673 } else if ($params['sort'] == 'iname') { 674 $callback = array($this, '_compare_inames'); 675 if ($params['order'] == 'desc') $reverseflag = true; 676 } else { 677 $callback = array($this, '_compare_names'); 678 if ($params['order'] == 'desc') $reverseflag = true; 679 } 680 $this->_sort_filelist($result['files'], $callback, $reverseflag); 681 682 // return the list 683 if (count($result['files']) > 0) 684 return $result; 685 else 686 return DOKU_PLUGIN_FILELIST_NOMATCH; 687 } 688 689 /** 690 * Recursively sorts the given tree using the given callback. Optionally 691 * reverses the sorted tree before returning it. 692 * 693 * @param $files the files to sort 694 * @param $callback the callback function to use for comparison 695 * @param $reverse true if the result is to be reversed 696 * @return the sorted tree 697 */ 698 function _sort_filelist(&$files, $callback, $reverse) { 699 // sort subtrees 700 for ($i = 0; $i < count($files); $i++) { 701 if ($files[$i]['children'] !== false) { 702 $children = $files[$i]['children']; 703 $this->_sort_filelist($children, $callback, $reverse); 704 $files[$i]['children'] = $children; 705 } 706 } 707 708 // sort current tree 709 usort($files, $callback); 710 if ($reverse) { 711 $files = array_reverse($files); 712 } 713 } 714 715 /** 716 * Flattens the filelist by recursively walking through all subtrees and 717 * merging them with a prefix attached to the filenames. 718 * 719 * @param $files the tree to flatten 720 * @param $prefix the prefix to attach to all processed nodes 721 * @return a flattened representation of the tree 722 */ 723 function _flatten_filelist($files, $prefix = '') { 724 $result = array(); 725 foreach ($files as $file) { 726 if ($file['children'] !== false) { 727 $result = array_merge($result, $this->_flatten_filelist($file['children'], $prefix . $file['name'] . '/')); 728 } else { 729 $file['name'] = $prefix . $file['name']; 730 $result[] = $file; 731 } 732 } 733 return $result; 734 } 735 736 /** 737 * Filters out directories and their subtrees from the result. 738 * 739 * @param $result the result to filter 740 * @return the result without any directories contained therein, 741 * DOKU_PLUGIN_FILELIST_NOMATCH if there are no files left or 742 * the given result if it was not an array (but an errorcode) 743 */ 744 function _filter_out_directories($result) { 745 if (!is_array($result)) { 746 return $result; 747 } 748 749 $filtered = array(); 750 $files = $result['files']; 751 foreach ($files as $file) { 752 if ($file['children'] === false) { 753 $filtered[] = $file; 754 } 755 } 756 757 if (count($filtered) == 0) { 758 return DOKU_PLUGIN_FILELIST_NOMATCH; 759 } else { 760 $result['files'] = $filtered; 761 return $result; 762 } 763 } 764 765 /** 766 * Does a (recursive) crawl for finding files based on a given pattern. 767 * Based on a safe glob reimplementation using fnmatch and opendir. 768 * 769 * @param $pattern the pattern to match to 770 * @param params the parameters of the filelist call 771 * @return a hierarchical filelist or false if nothing could be found 772 * 773 * @see <http://www.php.net/manual/en/function.glob.php#71083> 774 */ 775 function _crawl_files($pattern, $params) { 776 $split = explode('/', $pattern); 777 $match = array_pop($split); 778 $path = implode('/', $split); 779 if (!is_dir($path)) { 780 return false; 781 } 782 783 $ext = explode(',',$this->getConf('extensions')); 784 $ext = array_map('trim',$ext); 785 $ext = array_map('preg_quote_cb',$ext); 786 $ext = join('|',$ext); 787 788 if (($dir = opendir($path)) !== false) { 789 $result = array(); 790 while (($file = readdir($dir)) !== false) { 791 if ($file == '.' || $file == '..') { 792 // ignore . and .. 793 continue; 794 } 795 if ($file == $params['titlefile']) { 796 // ignore the title file 797 continue; 798 } 799 if ($file[0] == '.') { 800 // ignore hidden files 801 continue; 802 } 803 $filepath = $path . '/' . $file; 804 805 if ($this->_fnmatch($match, $file) || (is_dir($filepath) && $params['recursive'])) { 806 if(!is_dir($filepath) && !preg_match('/('.$ext.')$/i',$file)){ 807 continue; 808 } 809 810 if (!$params['direct']) { 811 // exclude prohibited media files via ACLs 812 $mid = $this->_convert_mediapath($filepath); 813 $perm = auth_quickaclcheck($mid); 814 if ($perm < AUTH_READ) continue; 815 } else { 816 if (!is_readable($filepath)) continue; 817 } 818 819 $filename = $file; 820 if (is_dir($filepath)) { 821 $titlefile = $filepath . '/' . $params['titlefile']; 822 if (!$params['direct']) { 823 $mid = $this->_convert_mediapath($titlefile); 824 $perm = auth_quickaclcheck($mid); 825 if (is_readable($titlefile) && $perm >= AUTH_READ) { 826 $filename = io_readFile($titlefile, false); 827 } 828 } else { 829 if (is_readable($titlefile)) { 830 $filename = io_readFile($titlefile, false); 831 } 832 } 833 } 834 835 // prepare entry 836 $creator = ''; 837 if (!is_dir($filepath) || $params['recursive']) { 838 if (!$params['direct']) { 839 $medialog = new MediaChangeLog($mid); 840 $revinfo = $medialog->getRevisionInfo(@filemtime(fullpath(mediaFN($mid)))); 841 842 if($revinfo['user']) { 843 $creator = $revinfo['user']; 844 } else { 845 $creator = $revinfo['ip']; 846 } 847 } 848 if (empty($creator)) { 849 $creator = $this->getLang('creatorunknown'); 850 } 851 852 $entry = array( 853 'name' => $filename, 854 'path' => $filepath, 855 'mtime' => filemtime($filepath), 856 'ctime' => filectime($filepath), 857 'size' => filesize($filepath), 858 'children' => ((is_dir($filepath) && $params['recursive']) ? $this->_crawl_files($filepath . '/' . $match, $params) : false), 859 'treesize' => 0, 860 'creator' => $creator, 861 ); 862 863 // calculate tree size 864 if ($entry['children'] !== false) { 865 foreach ($entry['children'] as $child) { 866 $entry['treesize'] += $child['treesize']; 867 } 868 } else { 869 $entry['treesize'] = 1; 870 } 871 872 // add entry to result 873 $result[] = $entry; 874 } 875 } 876 } 877 closedir($dir); 878 return $result; 879 } else { 880 return false; 881 } 882 } 883 884 //~~ Comparators 885 886 function _compare_names($a, $b) { 887 return strcmp($a['name'], $b['name']); 888 } 889 890 function _compare_inames($a, $b) { 891 return strcmp(strtolower($a['name']), strtolower($b['name'])); 892 } 893 894 function _compare_ctimes($a, $b) { 895 if ($a['ctime'] == $b['ctime']) { 896 return 0; 897 } 898 return (($a['ctime'] < $b['ctime']) ? -1 : 1); 899 } 900 901 function _compare_mtimes($a, $b) { 902 if ($a['mtime'] == $b['mtime']) { 903 return 0; 904 } 905 return (($a['mtime'] < $b['mtime']) ? -1 : 1); 906 } 907 908 function _compare_sizes($a, $b) { 909 if ($a['size'] == $b['size']) { 910 return 0; 911 } 912 return (($a['size'] < $b['size']) ? -1 : 1); 913 } 914 915 //~~ Utility functions 916 917 /** 918 * Canonicalizes a given path. A bit like realpath, but without the resolving of symlinks. 919 * 920 * @author anonymous 921 * @see <http://www.php.net/manual/en/function.realpath.php#73563> 922 */ 923 function _realpath($path) { 924 $path=explode('/', $path); 925 $output=array(); 926 for ($i=0; $i<sizeof($path); $i++) { 927 if ('.' == $path[$i]) continue; 928 if ('..' == $path[$i] && '..' != $output[sizeof($output) - 1]) { 929 array_pop($output); 930 continue; 931 } 932 array_push($output, $path[$i]); 933 } 934 return implode('/', $output); 935 } 936 937 /** 938 * Replacement for fnmatch() for windows systems. 939 * 940 * @author jk at ricochetsolutions dot com 941 * @see <http://www.php.net/manual/en/function.fnmatch.php#71725> 942 */ 943 function _fnmatch($pattern, $string) { 944 return preg_match("#^".strtr(preg_quote($pattern, '#'), array('\*' => '.*', '\?' => '.', '\[' => '[', '\]' => ']'))."$#i", $string); 945 } 946 947 /** 948 * Converts backslashs in paths to slashs. 949 * 950 * @param $path the path to convert 951 * @return the converted path 952 */ 953 function _win_path_convert($path) { 954 return str_replace('\\', '/', trim($path)); 955 } 956 957 /** 958 * Return human readable sizes 959 * 960 * @author Aidan Lister <aidan@php.net> 961 * @version 1.3.0 962 * @link http://aidanlister.com/repos/v/function.size_readable.php 963 * @param int $size size in bytes 964 * @param string $max maximum unit 965 * @param string $system 'si' for SI, 'bi' for binary prefixes 966 * @param string $retstring return string format 967 */ 968 function _size_readable($size, $max = null, $system = 'si', $retstring = '%01.2f %s') 969 { 970 // Pick units 971 $systems['si']['prefix'] = array('B', 'K', 'MB', 'GB', 'TB', 'PB'); 972 $systems['si']['size'] = 1000; 973 $systems['bi']['prefix'] = array('B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB'); 974 $systems['bi']['size'] = 1024; 975 $sys = isset($systems[$system]) ? $systems[$system] : $systems['si']; 976 977 // Max unit to display 978 $depth = count($sys['prefix']) - 1; 979 if ($max && false !== $d = array_search($max, $sys['prefix'])) { 980 $depth = $d; 981 } 982 983 // Loop 984 $i = 0; 985 while ($size >= $sys['size'] && $i < $depth) { 986 $size /= $sys['size']; 987 $i++; 988 } 989 990 return sprintf($retstring, $size, $sys['prefix'][$i]); 991 } 992 993 /** 994 * Determines whether a given path is absolute or relative. 995 * On windows plattforms, it does so by checking whether the second character 996 * of the path is a :, on all other plattforms it checks for a / as the 997 * first character. 998 * 999 * @param $path the path to check 1000 * @return true if path is absolute, false otherwise 1001 */ 1002 function _path_is_absolute($path) { 1003 if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { 1004 if ($path[1] == ':' || ($path[0] == '/' && $path[1] == '/')) { 1005 return true; 1006 } 1007 return false; 1008 } else { 1009 return ($path[0] == '/'); 1010 } 1011 } 1012 1013 function _convert_mediapath($path) { 1014 $mid = str_replace('/', ':', substr($path, strlen($this->mediadir))); // strip media base dir 1015 return ltrim($mid, ':'); // strip leading : 1016 } 1017 1018 /** 1019 * The function determines the preview image path for the given file 1020 * depending on the file type and the 'preview' config option value: 1021 * 1: Display file as preview image if itself is an image otherwise 1022 * choose DokuWiki image corresponding to the file extension 1023 * 2: Display file as preview image if itself is an image otherwise 1024 * display no image 1025 * 3. Display DokuWiki image corresponding to the file extension 1026 * 1027 * @param $filename the file to check 1028 * @return string Image to use for preview image 1029 */ 1030 protected function get_preview_image_path ($filename, $params, &$isImage) { 1031 list($ext,$mime) = mimetype(basename($filename)); 1032 $imagepath = ''; 1033 $isImage = false; 1034 if (($params['preview'] == 1 || $params['preview'] == 2) && 1035 strncmp($mime, 'image', strlen('image')) == 0) { 1036 // The file is an image. Return itself as the image path. 1037 $imagepath = $filename; 1038 $isImage = true; 1039 } 1040 if (($params['preview'] == 1 && empty($imagepath)) || 1041 $params['preview'] == 3 ) { 1042 // No image. Return DokuWiki image for file extension. 1043 if (!empty($ext)) { 1044 $imagepath = DOKU_INC.'lib/images/fileicons/32x32/'.$ext.'.png'; 1045 } else { 1046 $imagepath = DOKU_INC.'lib/images/fileicons/32x32/file.png'; 1047 } 1048 } 1049 return $imagepath; 1050 } 1051 1052 /** 1053 * Create URL for file $filepath. 1054 * 1055 * @param $filepath the file for which a preview image shall be rendered 1056 * @param $basedir the basedir to use 1057 * @param $webdir the webdir to use 1058 * @param $params the parameters of the filelist call 1059 * @return string the generated URL 1060 */ 1061 protected function _get_link_url ($filepath, $basedir, $webdir, $randlinks, $direct, $ftp=false) { 1062 $urlparams = ''; 1063 if ($randlinks) { 1064 $urlparams = '?'.time(); 1065 } 1066 if (!$direct) { 1067 $url = ml(':'.$this->_convert_mediapath($filepath)).$urlparams; 1068 } else { 1069 $url = $webdir.substr($filepath, strlen($basedir)).$urlparams; 1070 if ($ftp) 1071 { 1072 $url = str_replace('\\','/', $url); 1073 if (strpos($url, 'http') === false) { 1074 $url = 'ftp:'.$url; 1075 } else { 1076 $url = str_replace('http','ftp', $url); 1077 } 1078 } 1079 } 1080 return $url; 1081 } 1082} 1083