b2evolution PHP Cross Reference Blogging Systems

Source: /inc/files/model/_filelist.class.php - 1266 lines - 29361 bytes - Summary - Text - Print

Description: This file implements the Filelist class. This file is part of the evoCore framework - {@link http://evocore.net/} See also {@link http://sourceforge.net/projects/evocms/}.

   1  <?php
   2  /**
   3   * This file implements the Filelist class.
   4   *
   5   * This file is part of the evoCore framework - {@link http://evocore.net/}
   6   * See also {@link http://sourceforge.net/projects/evocms/}.
   7   *
   8   * @copyright (c)2003-2014 by Francois Planque - {@link http://fplanque.com/}
   9   * Parts of this file are copyright (c)2004-2006 by Daniel HAHLER - {@link http://thequod.de/contact}.
  10   *
  11   * {@internal License choice
  12   * - If you have received this file as part of a package, please find the license.txt file in
  13   *   the same folder or the closest folder above for complete license terms.
  14   * - If you have received this file individually (e-g: from http://evocms.cvs.sourceforge.net/)
  15   *   then you must choose one of the following licenses before using the file:
  16   *   - GNU General Public License 2 (GPL) - http://www.opensource.org/licenses/gpl-license.php
  17   *   - Mozilla Public License 1.1 (MPL) - http://www.opensource.org/licenses/mozilla1.1.php
  18   * }}
  19   *
  20   * {@internal Open Source relicensing agreement:
  21   * Daniel HAHLER grants Francois PLANQUE the right to license
  22   * Daniel HAHLER's contributions to this file and the b2evolution project
  23   * under any OSI approved OSS license (http://www.opensource.org/licenses/).
  24   * }}
  25   *
  26   * @package evocore
  27   *
  28   * {@internal Below is a list of authors who have contributed to design/coding of this file: }}
  29   * @author blueyed: Daniel HAHLER
  30   * @author fplanque: Francois PLANQUE
  31   *
  32   * @version $Id: _filelist.class.php 6136 2014-03-08 07:59:48Z manuel $
  33   */
  34  if( !defined('EVO_MAIN_INIT') ) die( 'Please, do not access this page directly.' );
  35  
  36  
  37  load_class( 'files/model/_file.class.php', 'File' );
  38  
  39  
  40  /**
  41   * Holds a list of File objects.
  42   *
  43   * Can hold an arbitrary list of Files.
  44   * Can list files in a directory by itself.
  45   * Can walk recursively down a directory tree to list in "flat mode".
  46   * Can sort file list.
  47   * Can iterate through list.
  48   * Cannot hold files with different root type/ID.
  49   *
  50   * @see File
  51   * @package evocore
  52   */
  53  class Filelist
  54  {
  55      /**
  56       * Flat mode? (all files recursive without dirs)
  57       * @param boolean
  58       */
  59      var $flatmode;
  60  
  61      /**
  62       * Do we want to include directories? (This gets used by {@link load()}).
  63       * @var boolean
  64       */
  65      var $include_dirs = true;
  66  
  67      /**
  68       * Do we want to include files? (This gets used by {@link load()}).
  69       * @var boolean
  70       */
  71      var $include_files = true;
  72  
  73      /**
  74       * The root of the file list.
  75       *
  76       * All files in this list MUST have that same FileRoot. Adding will fail otherwise.
  77       *
  78       * @var FileRoot
  79       */
  80      var $_FileRoot;
  81  
  82      /**
  83       * Path to list with trailing slash.
  84       *
  85       * false if we are constructing an arbitrary list (i-e not tied to a single directory)
  86       * fp> should use NULL instead of false
  87       *
  88       * @var string
  89       * @access protected
  90       */
  91      var $_ads_list_path = false;
  92  
  93      /**
  94       * Path to list reltive to root, with trailing slash
  95       *
  96       * false if we are constructing an arbitrary list (i-e not tied to a single directory)
  97       * fp> should use NULL instead of false
  98       *
  99       * @param string
 100       * @access protected
 101       */
 102      var $_rds_list_path = false;
 103  
 104      /**
 105       * Filename filter pattern
 106       *
 107       * Will be matched against the filename part (not the path)
 108       * NULL if disabled
 109       *
 110       * Can be a regular expression (see {@link Filelist::$_filter_is_regexp}), internally with delimiters/modifiers!
 111       *
 112       * Use {@link set_filter()} to set it.
 113       *
 114       * @var string
 115       * @access protected
 116       */
 117      var $_filter = NULL;
 118  
 119      /**
 120       * Is the filter a regular expression? NULL if unknown
 121       *
 122       * Use {@link set_filter()} to set it.
 123       *
 124       * @see Filelist::$_filter
 125       * @var boolean
 126       * @access protected
 127       */
 128      var $_filter_is_regexp = NULL;
 129  
 130      /**
 131       * The list of Files.
 132       * @var array
 133       * @access protected
 134       */
 135      var $_entries = array();
 136  
 137      /**
 138       * Index on File IDs (id => {@link $_entries} key).
 139       *
 140       * Note: fplanque>> what's the purpose of the md5 IDs??
 141       *
 142       * @todo make these direct links to &File objects
 143       * @var array
 144       * @access protected
 145       */
 146      var $_md5_ID_index = array();
 147  
 148      /**
 149       * Index on full paths (path => {@link $_entries} key).
 150       * @todo make these direct links to &File objects
 151       * @var array
 152       * @access protected
 153       */
 154      var $_full_path_index = array();
 155  
 156      /**
 157       * Index on (r)elative (s)lash terminated (f)ile/(d)irectory paths (rdfs_path => key into {@link $_entries}).
 158       *
 159       * @todo make these direct links to &File objects
 160       *
 161       * @var array
 162       * @access protected
 163       */
 164      var $_rdfs_rel_path_index = array();
 165  
 166      /**
 167       * Index on sort order (order # => {@link $_entries} key).
 168       * @todo make these direct links to &File objects
 169       * @var array
 170       * @access protected
 171       */
 172      var $_order_index = array();
 173  
 174      /**
 175       * Number of entries in the {@link $_entries} array
 176       *
 177       * Note: $_total_entries = $_total_dirs + $_total_files
 178       *
 179       * @var integer
 180       * @access protected
 181       */
 182      var $_total_entries = 0;
 183  
 184      /**
 185       * Number of directories
 186       * @var integer
 187       * @access protected
 188       */
 189      var $_total_dirs = 0;
 190  
 191      /**
 192       * Number of files
 193       * @var integer
 194       * @access protected
 195       */
 196      var $_total_files = 0;
 197  
 198      /**
 199       * Number of bytes
 200       * @var integer
 201       * @access protected
 202       */
 203      var $_total_bytes = 0;
 204  
 205      /**
 206       * Index of the current iterator position.
 207       *
 208       * This is the key of {@link $_order_index}
 209       *
 210       * @var integer
 211       * @access protected
 212       */
 213      var $_current_idx = -1;
 214  
 215      /**
 216       * What column is the list ordered on?
 217       *
 218       * Possible values are: 'name', 'path', 'type', 'size', 'lastmod', 'perms'
 219       *
 220       * @var string
 221       * @access protected
 222       */
 223      var $_order = NULL;
 224  
 225      /**
 226       * Are we sorting ascending (or descending)?
 227       *
 228       * NULL is default and means ascending for 'name', descending for the rest
 229       *
 230       * @var boolean
 231       * @access protected
 232       */
 233      var $_order_asc = NULL;
 234  
 235      /**
 236       * User preference: Sort dirs not at top
 237       *
 238       * @var boolean
 239       */
 240      var $_dirs_not_at_top = false;
 241  
 242      /**
 243       * User preference: Load and show hidden files?
 244       *
 245       * "Hidden files" are prefixed with a dot .
 246       *
 247       * @var boolean
 248       */
 249      var $_show_hidden_files = false;
 250  
 251      /**
 252       * User preference: Load and show _evocache folder?
 253       *
 254       * @var boolean
 255       */
 256      var $_show_evocache = false;
 257  
 258      /**
 259       * User preference: recursive size of dirs?
 260       *
 261       * The load() & sort() methods use this.
 262       *
 263       * @var boolean
 264       * @access protected
 265       */
 266      var $_use_recursive_dirsize = false;
 267  
 268  
 269      /**
 270       * Constructor
 271       *
 272       * @param FileRoot See FileRootCache::get_by_type_and_ID()
 273       * @param boolean|string Default path for the files, false if you want to create an arbitrary list; NULL for the Fileroot's ads_path.
 274       * @param integer ID of the user, the group or the collection the file belongs to...
 275       */
 276  	function Filelist( $FileRoot, $path = NULL )
 277      {
 278          global $AdminUI;
 279  
 280          if( ! is_object($FileRoot) )
 281          {
 282              debug_die( 'Fatal: $FileRoot is no object!' );
 283          }
 284  
 285          if( is_null($path) )
 286          {
 287              $path = $FileRoot->ads_path;
 288          }
 289          $this->_ads_list_path = $path;
 290          $this->_FileRoot = & $FileRoot;
 291  
 292          if( ! empty($this->_ads_list_path) )
 293          {
 294              // Get the subpath relative to root
 295              $this->_rds_list_path = $this->rdfs_relto_root_from_adfs( $this->_ads_list_path );
 296          }
 297      }
 298  
 299  
 300      /**
 301       * Loads or reloads the filelist entries.
 302       *
 303       * NOTE: this does not work for arbitrary lists!
 304       *
 305       * @uses $flatmode
 306       * @return boolean True on sucess, false on failure (not accessible)
 307       */
 308  	function load()
 309      {
 310          global $Messages;
 311  
 312          if( !$this->_ads_list_path )
 313          {    // We have no path to load from: (happens when FM finds no available root OR we have an arbitrary)
 314              // echo 'Cannot load a filelist with no list path' ;
 315              return false;
 316          }
 317  
 318          // Clears the list (for RE-loads):
 319          $this->_total_entries = 0;
 320          $this->_total_bytes = 0;
 321          $this->_total_files = 0;
 322          $this->_total_dirs = 0;
 323          $this->_entries = array();
 324          $this->_md5_ID_index = array();
 325          $this->_full_path_index = array();
 326          $this->_rdfs_rel_path_index = array();
 327          $this->_order_index = array();
 328  
 329          // Attempt to list files for requested directory: (recursively if flat mode):
 330          $filename_params = array(
 331                  'inc_files'        => $this->include_files,
 332                  'inc_dirs'        => $this->include_dirs,
 333                  'recurse'        => $this->flatmode,
 334                  'inc_hidden'    => $this->_show_hidden_files,
 335                  'inc_evocache'    => $this->_show_evocache,
 336              );
 337          if( ($filepath_array = get_filenames( $this->_ads_list_path, $filename_params )) === false )
 338          {
 339              $Messages->add( sprintf( T_('Cannot open directory &laquo;%s&raquo;!'), $this->_ads_list_path ), 'error' );
 340              return false;
 341          }
 342  
 343          // Loop through file list:
 344          foreach( $filepath_array as $adfp_path )
 345          {
 346              // Extract the filename from the full path
 347              $name = basename( $adfp_path );
 348  
 349              // Check for hidden status...
 350              if( ( ! $this->_show_hidden_files) && (substr($name, 0, 1) == '.') )
 351              { // Do not load & show hidden files (prefixed with .)
 352                  continue;
 353              }
 354  
 355              // Check for _evocache...
 356              if( ( ! $this->_show_evocache ) && ( $name == '_evocache') )
 357              { // Do not load & show _evocache folder
 358                  continue;
 359              }
 360  
 361              // Check filter...
 362              if( $this->_filter !== NULL )
 363              { // Filter: must match filename
 364                  if( $this->_filter_is_regexp )
 365                  { // Filter is a reg exp:
 366                      if( ! preg_match( $this->_filter, $name ) )
 367                      { // does not match the regexp filter
 368                          continue;
 369                      }
 370                  }
 371                  else
 372                  { // Filter is NOT a regexp:
 373                      if( ! fnmatch( $this->_filter, $name ) )
 374                      {
 375                          continue;
 376                      }
 377                  }
 378              }
 379  
 380              // Extract the file's relative path to the root
 381              $rdfp_path_relto_root = $this->rdfs_relto_root_from_adfs( $adfp_path );
 382              // echo '<br>'.$rdfp_rel_path;
 383  
 384              // Add the file into current list:
 385              $this->add_by_subpath( $rdfp_path_relto_root, true );
 386          }
 387  
 388          return true;
 389      }
 390  
 391  
 392      /**
 393       * Add a File object to the list (by reference).
 394       *
 395       * @param File File object (by reference)
 396       * @param boolean Has the file to exist to get added?
 397       * @return boolean true on success, false on failure
 398       */
 399  	function add( & $File, $mustExist = false )
 400      {
 401          if( !is_a( $File, 'file' ) )
 402          {    // Passed object is not a File!! :(
 403              return false;
 404          }
 405  
 406          // Integrity check:
 407          if( $File->_FileRoot->ID != $this->_FileRoot->ID )
 408          {
 409              debug_die( 'Adding file '.$File->_FileRoot->ID.':'.$File->get_rdfs_rel_path().' to filelist '.$this->_FileRoot->ID.' : root mismatch!' );
 410          }
 411  
 412          if( $mustExist && ! $File->exists() )
 413          {    // File does not exist..
 414              return false;
 415          }
 416  
 417  
 418          $this->_entries[$this->_total_entries] = & $File;
 419          $this->_md5_ID_index[$File->get_md5_ID()] = $this->_total_entries;
 420          $this->_full_path_index[$File->get_full_path()] = $this->_total_entries;
 421          $this->_rdfs_rel_path_index[$File->get_rdfs_rel_path()] = $this->_total_entries;
 422          // add file to the end of current list:
 423          $this->_order_index[$this->_total_entries] = $this->_total_entries;
 424  
 425          // Count 1 more entry (file or dir)
 426          $this->_total_entries++;
 427  
 428          if( $File->is_dir() )
 429          {    // Count 1 more directory
 430              $this->_total_dirs++;
 431          }
 432          else
 433          {    // Count 1 more file
 434              $this->_total_files++;
 435          }
 436  
 437          // Count total bytes in this dir
 438          $this->_total_bytes += $this->get_File_size($File);
 439  
 440          return true;
 441      }
 442  
 443  
 444      /**
 445       * Get the size of a given File, according to $_use_recursive_dirsize.
 446       * @param File
 447       * @return int bytes
 448       */
 449  	function get_File_size($File)
 450      {
 451          if( $this->_use_recursive_dirsize )
 452          {
 453              return $File->get_recursive_size();
 454          }
 455          else
 456          {
 457              return $File->get_size();
 458          }
 459      }
 460  
 461  
 462      /**
 463       * Get size of the file/dir, formatted to nearest unit (kb, mb, etc.)
 464       *
 465       * @uses bytesreadable()
 466       * @param File
 467       * @return string size as b/kb/mb/gd; or '&lt;dir&gt;'
 468       */
 469  	function get_File_size_formatted($File)
 470      {
 471          if( $this->_use_recursive_dirsize || ! $File->is_dir() )
 472          {
 473              return bytesreadable($File->get_recursive_size());
 474          }
 475  
 476          return /* TRANS: short for '<directory>' */ T_('&lt;dir&gt;');
 477      }
 478  
 479  
 480      /**
 481       * Update the name dependent caches
 482       *
 483       * This is especially useful after a name change of one of the files in the list
 484       */
 485  	function update_caches()
 486      {
 487          $this->_md5_ID_index = array();
 488          $this->_full_path_index = array();
 489          $this->_rdfs_rel_path_index = array();
 490  
 491          $count = 0;
 492          foreach( $this->_entries as $loop_File )
 493          {
 494              $this->_md5_ID_index[$loop_File->get_md5_ID()] = $count;
 495              $this->_full_path_index[$loop_File->get_full_path()] = $count;
 496              $this->_rdfs_rel_path_index[$loop_File->get_rdfs_rel_path()] = $count;
 497              $count++;
 498          }
 499      }
 500  
 501  
 502      /**
 503       * Add a file to the list, by filename.
 504       *
 505       * This is a stub for {@link Filelist::add()}.
 506       *
 507       * @param string Subpath for this file/folder, relative the associated root, including trailing slash (if directory)
 508       * @param boolean Has the file to exist to get added?
 509       * @return boolean true on success, false on failure (path not allowed,
 510       *                 file does not exist)
 511       */
 512  	function add_by_subpath( $rel_path, $mustExist = false )
 513      {
 514          $FileCache = & get_FileCache();
 515          $NewFile = & $FileCache->get_by_root_and_path( $this->_FileRoot->type, $this->_FileRoot->in_type_ID, $rel_path );
 516  
 517          return $this->add( $NewFile, $mustExist );
 518      }
 519  
 520  
 521      /**
 522       * Sort the entries by sorting the internal {@link $_order_index} array.
 523       *
 524       * @param string The order to use ('name', 'type', 'lastmod', .. )
 525       * @param boolean Ascending (true) or descending
 526       * @param boolean Sort directories at top?
 527       */
 528  	function sort( $order = NULL, $orderasc = NULL, $dirsattop = NULL )
 529      {
 530          if( ! $this->_total_entries )
 531          {
 532              return false;
 533          }
 534  
 535          if( $order !== NULL )
 536          { // New order
 537              $this->_order = $order;
 538          }
 539          elseif( $this->_order === NULL )
 540          { // Init
 541              $this->_order = 'name';
 542          }
 543  
 544          if( $orderasc !== NULL )
 545          { // New ascending/descending setting
 546              $this->_order_asc = $orderasc;
 547          }
 548          elseif( $this->_order_asc === NULL )
 549          { // Init: ascending for 'name' and 'path', else descending
 550              $this->_order_asc = ( ( $this->_order == 'name' || $this->_order == 'path' ) ? 1 : 0 );
 551          }
 552  
 553          if( $dirsattop !== NULL )
 554          {
 555              $this->_dirs_not_at_top = ! $dirsattop;
 556          }
 557  
 558          usort( $this->_order_index, array( $this, '_sort_callback' ) );
 559  
 560          // Reset the iterator:
 561          $this->restart();
 562      }
 563  
 564  
 565      /**
 566       * usort callback function for {@link Filelist::sort()}
 567       *
 568       * @access protected
 569       * @return integer
 570       */
 571  	function _sort_callback( $a, $b )
 572      {
 573          $FileA = & $this->_entries[$a];
 574          $FileB = & $this->_entries[$b];
 575  
 576          // What column are we sorting on?
 577          // TODO: dh> this should probably fallback to sorting by name always if $r==0
 578          switch( $this->_order )
 579          {
 580              case 'size':
 581                  $r = $this->get_File_size($FileA) - $this->get_File_size($FileB);
 582                  break;
 583  
 584              case 'path': // group by dir
 585                  $r = strcasecmp( $FileA->get_dir(), $FileB->get_dir() );
 586                  if( $r == 0 )
 587                  {
 588                      $r = strcasecmp( $FileA->get_name(), $FileB->get_name() );
 589                  }
 590                  break;
 591  
 592              case 'lastmod':
 593                  $r = $FileB->get_lastmod_ts() - $FileA->get_lastmod_ts();
 594                  break;
 595  
 596              case 'perms':
 597                  // This will use literal representation ( 'r', 'r+w' / octal )
 598                  $r = strcasecmp( $FileA->get_perms(), $FileB->get_perms() );
 599                  break;
 600  
 601              case 'fsowner':
 602                  $r = strcasecmp( $FileA->get_fsowner_name(), $FileB->get_fsowner_name() );
 603                  break;
 604  
 605              case 'fsgroup':
 606                  $r = strcasecmp( $FileA->get_fsgroup_name(), $FileB->get_fsgroup_name() );
 607                  break;
 608  
 609              case 'type':
 610                  if( $r = strcasecmp( $FileA->get_type(), $FileB->get_type() ) )
 611                  {
 612                      break;
 613                  }
 614                  // same type: continue to name:
 615              default:
 616              case 'name':
 617                  $r = strcasecmp( $FileA->get_name(), $FileB->get_name() );
 618                  if( $r == 0 )
 619                  { // same name: look at path
 620                      $r = strcasecmp( $FileA->get_dir(), $FileB->get_dir() );
 621                  }
 622                  break;
 623          }
 624  
 625          if( ! $this->_order_asc )
 626          { // We want descending order: switch order
 627              $r = - $r;
 628          }
 629  
 630          if( ! $this->_dirs_not_at_top )
 631          {    // We want dirs to be on top, always:
 632              if( $FileA->is_dir() && !$FileB->is_dir() )
 633              {
 634                  $r = -1;
 635              }
 636              elseif( $FileB->is_dir() && !$FileA->is_dir() )
 637              {
 638                  $r = 1;
 639              }
 640          }
 641  
 642          return $r;
 643      }
 644  
 645  
 646      /**
 647       * Get the used order.
 648       *
 649       * @return string
 650       */
 651  	function get_sort_order()
 652      {
 653          return $this->translate_order( $this->_order );
 654      }
 655  
 656  
 657      /**
 658       * Get the link to sort by a column. Handle current order and appends an
 659       * icon to reflect the current state (ascending/descending), if the column
 660       * is the same we're sorting by.
 661       *
 662       * @todo get this outta here. This is NOT a displayable object.
 663       * We might want to have a "FileListResults" object that derives from Widget/Results/FilteredResults (the more the better)
 664       * This object is what the SQL or the ItemQuery object is to Results or to ItemList2. The model and the display should not be mixed.
 665       * IF NOT doing the clean objects, move this at least to file.funcs.
 666       *
 667       * @param string The type (name, path, size, ..)
 668       * @param string The text for the anchor.
 669       * @return string
 670       */
 671  	function get_sort_link( $type, $atext )
 672      {
 673          global $AdminUI;
 674  
 675          $newAsc = $this->_order == $type ? (1 - $this->is_sorting_asc()) :  1;
 676  
 677          $r = '<a href="'.regenerate_url( 'fm_order,fm_orderasc', 'fm_order='.$type.'&amp;fm_orderasc='.$newAsc ).'" title="'.T_('Change Order').'"';
 678  
 679          /**
 680           * @todo get this outta here. This is NOT a displayable object.
 681           * We might want to have a "FileListResults" object that derives from Widget/Results/FilteredResults (the more the better)
 682           * This object is what the SQL or the ItemQuery object is to Results or to ItemList2. The model and the display should not be mixed.
 683           * IF NOT doing the clean objects, move this at least to file.funcs.
 684           */
 685          $result_params = $AdminUI->get_template('Results');
 686  
 687  
 688          // Sorting icon:
 689          if( $this->_order != $type )
 690          { // Not sorted on this column:
 691              $r .= ' class="basic_sort_link">'.$result_params['basic_sort_off'];
 692          }
 693          elseif( $this->is_sorting_asc($type) )
 694          { // We are sorting on this column , in ascneding order:
 695              $r .=    ' class="basic_current">'.$result_params['basic_sort_asc'];
 696          }
 697          else
 698          { // Descending order:
 699              $r .=    ' class="basic_current">'.$result_params['basic_sort_desc'];
 700          }
 701  
 702          $r .= ' '.$atext;
 703  
 704  
 705          return $r.'</a>';
 706      }
 707  
 708  
 709      /**
 710       * Reset the iterator
 711       */
 712  	function restart()
 713      {
 714          $this->_current_idx = -1;
 715      }
 716  
 717  
 718      /**
 719       * Are we sorting ascending?
 720       *
 721       * @param string The type (empty for current order type)
 722       * @return integer 1 for ascending sorting, 0 for descending
 723       */
 724  	function is_sorting_asc( $col = '' )
 725      {
 726          if( $this->_order_asc === NULL )
 727          { // We have not specified a sort order by now, use default:
 728              if( empty($col) )
 729              {
 730                  $col = $this->_order;
 731              }
 732              return ( $col == 'name' || $col == 'path' ) ? 1 : 0;
 733          }
 734          else
 735          {    // Use previsously specified sort order:
 736              return ( $this->_order_asc ) ? 1 : 0;
 737          }
 738      }
 739  
 740  
 741      /**
 742       * Set the filter.
 743       *
 744       * @param string Filter string (for regular expressions, if no delimiter/modifiers are included, we try magically adding them)
 745       * @param boolean Is the filter a regular expression? (it's a glob pattern otherwise)
 746       */
 747  	function set_filter( $filter_string, $filter_is_regexp )
 748      {
 749          global $Messages;
 750  
 751          $this->_filter_is_regexp = $filter_is_regexp;
 752  
 753          if( $this->_filter_is_regexp && ! empty($filter_string) )
 754          {
 755              if( ! is_regexp( $filter_string, true ) )
 756              {
 757                  // Try with adding delimiters:
 758                  $filter_string_delim = '~'.str_replace( '~', '\~', $filter_string ).'~';
 759                  if( is_regexp( $filter_string_delim, true ) )
 760                  {
 761                      $filter_string = $filter_string_delim;
 762                  }
 763                  else
 764                  {
 765                      $Messages->add( sprintf( T_('The filter &laquo;%s&raquo; is not a regular expression.'), $filter_string ), 'error' );
 766                      $filter_string = '~.*~';
 767                  }
 768              }
 769          }
 770  
 771          $this->_filter = empty($filter_string) ? NULL : $filter_string;
 772      }
 773  
 774  
 775      /**
 776       * Is a filter active?
 777       *
 778       * @return boolean
 779       */
 780  	function is_filtering()
 781      {
 782          return ($this->_filter !== NULL);
 783      }
 784  
 785  
 786      /**
 787       * Does the list contain a specific File?
 788       *
 789       * @param File the File object to look for
 790       * @return boolean
 791       */
 792  	function contains( & $File )
 793      {
 794          return isset( $this->_md5_ID_index[ $File->get_md5_ID() ] );
 795      }
 796  
 797  
 798      /**
 799       * Return the current filter
 800       *
 801       * @param boolean add a note when it's a regexp or no filter?
 802       * @return string the filter
 803       */
 804  	function get_filter( $verbose = true )
 805      {
 806          if( $this->_filter === NULL )
 807          {    // Filtering is not active
 808              return $verbose ? T_('No filter') : '';
 809          }
 810          else
 811          {    // Filtering is active
 812              return $this->_filter
 813                              .( $verbose && $this->_filter_is_regexp ? ' ('.T_('regular expression').')' : '' );
 814          }
 815      }
 816  
 817  
 818      /**
 819       * Is the current Filter a regexp?
 820       *
 821       * @return NULL|boolean true if regexp, NULL if no filter set
 822       */
 823  	function is_filter_regexp()
 824      {
 825          return $this->_filter_is_regexp;
 826      }
 827  
 828  
 829      /**
 830       * Get the total number of entries in the list.
 831       *
 832       * @return integer
 833       */
 834  	function count()
 835      {
 836          return $this->_total_entries;
 837      }
 838  
 839  
 840      /**
 841       * Get the total number of directories in the list
 842       *
 843       * @return integer
 844       */
 845  	function count_dirs()
 846      {
 847          return $this->_total_dirs;
 848      }
 849  
 850  
 851      /**
 852       * Get the total number of files in the list
 853       *
 854       * @return integer
 855       */
 856  	function count_files()
 857      {
 858          return $this->_total_files;
 859      }
 860  
 861  
 862      /**
 863       * Get the total number of bytes of all files in the list
 864       *
 865       * @return integer
 866       */
 867  	function count_bytes()
 868      {
 869          return $this->_total_bytes;
 870      }
 871  
 872  
 873      /**
 874       * Get the next entry and increment internal counter.
 875       *
 876       * @param string can be used to query only 'file's or 'dir's.
 877       * @return File object (by reference) on success, false on end of list
 878       */
 879      function & get_next( $type = '' )
 880      {
 881          for(;;)
 882          {
 883              if( !isset($this->_order_index[$this->_current_idx + 1]) )
 884              {    // End of list:
 885                  $r = false;
 886                  return $r;
 887              }
 888              $this->_current_idx++;
 889  
 890              $index = $this->_order_index[$this->_current_idx];
 891              if( $type != '' )
 892              {
 893                  $is_dir = $this->_entries[$index]->is_dir();
 894                  if( ($type == 'dir' && ! $is_dir )
 895                      || ($type == 'file' && $is_dir) )
 896                  { // we want another type
 897                      continue;
 898                  }
 899              }
 900              break;
 901          }
 902  
 903          return $this->_entries[ $index ];
 904      }
 905  
 906  
 907      /**
 908       * Get a file by its relative (to root) path.
 909       *
 910       * @param string the RELATIVE path (with ending slash for directories)
 911       * @return mixed File object (by reference) on success, false on failure.
 912       */
 913      function & get_by_rdfs_path( $rdfs_path )
 914      {
 915          $path = str_replace( '\\', '/', $rdfs_path );
 916  
 917          if( isset( $this->_rdfs_rel_path_index[ $rdfs_path ] ) )
 918          {
 919              return $this->_entries[ $this->_rdfs_rel_path_index[ $rdfs_path ] ];
 920          }
 921          else
 922          {
 923              $r = false;
 924              return $r;
 925          }
 926      }
 927  
 928  
 929      /**
 930       * Get a file by its full path.
 931       *
 932       * @param string the full/absolute path (with ending slash for directories)
 933       * @return mixed File object (by reference) on success, false on failure.
 934       */
 935      function & get_by_full_path( $adfs_path )
 936      {
 937          $path = str_replace( '\\', '/', $adfs_path );
 938  
 939          if( isset( $this->_full_path_index[ $adfs_path ] ) )
 940          {
 941              return $this->_entries[ $this->_full_path_index[ $adfs_path ] ];
 942          }
 943          else
 944          {
 945              $r = false;
 946              return $r;
 947          }
 948      }
 949  
 950  
 951      /**
 952       * Get a file by it's ID.
 953       *
 954       * @param string the ID (MD5 of path and name)
 955       * @return mixed File object (by reference) on success, false on failure.
 956       */
 957      function & get_by_md5_ID( $md5id )
 958      {
 959          if( isset( $this->_md5_ID_index[ $md5id ] ) )
 960          {
 961              return $this->_entries[ $this->_md5_ID_index[ $md5id ] ];
 962          }
 963          else
 964          {
 965              $r = false;
 966              return $r;
 967          }
 968      }
 969  
 970  
 971      /**
 972       * Get a file by index.
 973       *
 974       * @param integer Index of the entries (starting with 0)
 975       * @param boolean added by fp (set to false when it's a problem)
 976       * @return File
 977       */
 978      function & get_by_idx( $index, $halt_on_error = true )
 979      {
 980          if( isset( $this->_order_index[ $index ] ) )
 981          {
 982              return $this->_entries[ $this->_order_index[ $index ] ];
 983          }
 984          elseif( !$halt_on_error )
 985          {
 986              $r = false;
 987              return $r;
 988          }
 989  
 990          debug_die( 'Requested file does not exist!' );
 991      }
 992  
 993  
 994      /**
 995       * Get the FileLists FileRoot
 996       *
 997       * @return FileRoot
 998       */
 999      function & get_FileRoot()
1000      {
1001          return $this->_FileRoot;
1002      }
1003  
1004  
1005      /**
1006       * Get the FileLists root type.
1007       *
1008       * @return string
1009       */
1010  	function get_root_type()
1011      {
1012          return $this->_FileRoot->type;
1013      }
1014  
1015  
1016      /**
1017       * Get the FileLists root ID (in_type_ID).
1018       *
1019       * @return FileRoot
1020       */
1021  	function get_root_ID()
1022      {
1023          return $this->_FileRoot->in_type_ID;
1024      }
1025  
1026  
1027      /**
1028       * Get absolute path to list.
1029       */
1030  	function get_ads_list_path()
1031      {
1032          return $this->_ads_list_path;
1033      }
1034  
1035      /**
1036       * Get path to list relative to root.
1037       */
1038  	function get_rds_list_path()
1039      {
1040          return $this->_rds_list_path;
1041      }
1042  
1043  
1044      /**
1045       * Get the path (and name) of a {@link File} relative to the {@link Filelist::$_FileRoot::$ads_path}.
1046       *
1047       * @param string
1048       * @return string
1049       */
1050  	function rdfs_relto_root_from_adfs( $adfs_path )
1051      {
1052          // Check that the file is inside root:
1053          if( substr( $adfs_path, 0, strlen($this->_FileRoot->ads_path) ) != $this->_FileRoot->ads_path )
1054          {
1055              debug_die( 'rdfs_relto_root_from_adfs: Path is NOT inside of root!' );
1056          }
1057  
1058          // Return only the relative part:
1059          return substr( $adfs_path, strlen($this->_FileRoot->ads_path) );
1060      }
1061  
1062  
1063      /**
1064       * Removes a {@link File} from the entries list.
1065       *
1066       * This handles indexes and number of total entries, bytes, files/dirs.
1067       *
1068       * @return boolean true on success, false if not found in list.
1069       */
1070  	function remove( & $File )
1071      {
1072          if( isset( $this->_md5_ID_index[ $File->get_md5_ID() ] ) )
1073          {
1074              $this->_total_entries--;
1075              $this->_total_bytes -= $this->get_File_size($File);
1076  
1077              if( $File->is_dir() )
1078              {
1079                  $this->_total_dirs--;
1080              }
1081              else
1082              {
1083                  $this->_total_files--;
1084              }
1085  
1086              // unset from indexes
1087              $index = $this->_full_path_index[ $File->get_full_path() ]; // current index
1088              unset( $this->_entries[ $this->_md5_ID_index[ $File->get_md5_ID() ] ] );
1089              unset( $this->_md5_ID_index[ $File->get_md5_ID() ] );
1090              unset( $this->_full_path_index[ $File->get_full_path() ] );
1091              unset( $this->_rdfs_rel_path_index[ $File->get_rdfs_rel_path() ] );
1092  
1093              // get the ordered index right: move all next files downwards
1094              $order_key = array_search( $index, $this->_order_index );
1095              unset( $this->_order_index[$order_key] );
1096              $this->_order_index = array_values( $this->_order_index );
1097  
1098              if( $this->_current_idx > -1 && $this->_current_idx >= $order_key )
1099              { // We have removed a file before or at the $order_key'th position
1100                  $this->_current_idx--;
1101              }
1102              return true;
1103          }
1104          return false;
1105      }
1106  
1107  
1108      /**
1109       * Get the list of File entries.
1110       *
1111       * You can use a method on each object to get this as result instead of the object
1112       * itself.
1113       *
1114       * @param string Use this method on every File and put the result into the list.
1115       * @return array The array with the File objects or method results
1116       */
1117  	function get_array( $method = NULL )
1118      {
1119          $r = array();
1120  
1121          if( is_string($method) )
1122          {
1123              foreach( $this->_order_index as $index )
1124              {
1125                  $r[] = $this->_entries[ $index ]->$method();
1126              }
1127          }
1128          else
1129          {
1130              foreach( $this->_order_index as $index )
1131              {
1132                  $r[] = & $this->_entries[ $index ];
1133              }
1134          }
1135  
1136          return $r;
1137      }
1138  
1139  
1140      /**
1141       * Get a MD5 checksum over the entries.
1142       * Used to identify a unique filelist.
1143       *
1144       * @return string md5 hash
1145       */
1146  	function md5_checksum()
1147      {
1148          return md5( serialize( $this->_entries ) );
1149      }
1150  
1151  
1152      /**
1153       * Attempt to load meta data for all files in the list.
1154       *
1155       * Will attempt only once per file and cache the result.
1156       */
1157  	function load_meta()
1158      {
1159          global $DB, $Debuglog;
1160  
1161          $to_load = array();
1162  
1163          for( $i=0; $i<count($this->_entries); $i++ )
1164          {    // For each file:
1165              $loop_File = & $this->_entries[$i];
1166              // echo '<br>'.$loop_File->get_full_path();
1167  
1168              if( $loop_File->meta != 'unknown' )
1169              { // We have already loaded meta data:
1170                  continue;
1171              }
1172  
1173              $to_load[] = $DB->quote( $loop_File->get_rdfp_rel_path() );
1174          }
1175  
1176          if( count( $to_load ) )
1177          {    // We have something to load...
1178              /**
1179               * @var FileCache
1180               */
1181              $FileCache = & get_FileCache();
1182  
1183              $rows = $DB->get_results( "
1184                  SELECT *
1185                    FROM T_files
1186                   WHERE file_root_type = '".$this->_FileRoot->type."'
1187                     AND file_root_ID = ".$this->_FileRoot->in_type_ID."
1188                     AND file_path IN (".implode( ',', $to_load ).")",
1189                  OBJECT, 'Load FileList meta data' );
1190  
1191              if( count($rows) )
1192              { // Go through rows of loaded meta data...
1193                  foreach( $rows as $row )
1194                  {
1195                      // Retrieve matching File object:
1196                      /**
1197                       * @var File
1198                       */
1199                      $loop_File = & $FileCache->get_by_root_and_path( $row->file_root_type, $row->file_root_ID, $row->file_path );
1200  
1201                      // Associate meta data to File object:
1202                      $loop_File->load_meta( false, $row );
1203                  }
1204              }
1205          }
1206  
1207          // For all Files that still have no meta data, memorize that we could not find any meta data
1208          for( $i=0; $i<count($this->_entries); $i++ )
1209          {    // For each file:
1210              $loop_File = & $this->_entries[$i];
1211  
1212              if( $loop_File->meta == 'unknown' )
1213              {
1214                  $loop_File->meta = 'notfound';
1215              }
1216          }
1217  
1218          // Has sth been loaded?
1219          return count( $to_load ) && count($rows);
1220      }
1221  
1222  
1223      /**
1224       * Returns cwd, where the accessible directories (below root) are clickable
1225       *
1226       * @return string cwd as clickable html
1227       */
1228  	function get_cwd_clickable( $clickableOnly = true )
1229      {
1230          if( empty($this->_ads_list_path) )
1231          {
1232              return ' -- '.T_('No directory.').' -- ';
1233          }
1234  
1235          // Get the part of the path which is not clickable:
1236          $r = substr( $this->_FileRoot->ads_path, 0, strrpos( substr($this->_FileRoot->ads_path, 0, -1), '/' )+1 );
1237  
1238          // get the part that is clickable
1239          $clickabledirs = explode( '/', substr( $this->_ads_list_path, strlen($r) ) );
1240  
1241          if( $clickableOnly )
1242          {
1243              $r = '';
1244          }
1245  
1246          $cd = '';
1247          foreach( $clickabledirs as $nr => $dir )
1248          {
1249              if( empty($dir) )
1250              {
1251                  break;
1252              }
1253              if( $nr )
1254              {
1255                  $cd .= $dir.'/';
1256              }
1257              $r .= '<a href="'.regenerate_url( 'path', 'path='.$cd )
1258                      .'" title="'.T_('Change to this directory').'">'.$dir.'</a>/';
1259          }
1260  
1261          return $r;
1262      }
1263  
1264  }
1265  
1266  ?>

title

Description

title

Description

title

Description

title

title

Body