b2evolution PHP Cross Reference Blogging Systems

Source: /inc/files/model/_file.class.php - 2446 lines - 62954 bytes - Summary - Text - Print

Description: This file implements the File 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 File 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: _file.class.php 6136 2014-03-08 07:59:48Z manuel $
  33   *
  34   */
  35  if( !defined('EVO_MAIN_INIT') ) die( 'Please, do not access this page directly.' );
  36  
  37  
  38  // DEBUG: (Turn switch on or off to log debug info for specified category)
  39  $GLOBALS['debug_files'] = false;
  40  
  41  
  42  load_class( '_core/model/dataobjects/_dataobject.class.php', 'DataObject' );
  43  
  44  /**
  45   * Represents a file or folder on disk. Optionnaly stores meta data from DB.
  46   *
  47   * Use {@link FileCache::get_by_root_and_path()} to create an instance.
  48   * This is based on {@link DataObject} for the meta data.
  49   *
  50   * @package evocore
  51   */
  52  class File extends DataObject
  53  {
  54      /**
  55       * Have we checked for meta data in the DB yet?
  56       * @var string
  57       */
  58      var $meta = 'unknown';
  59  
  60      /**
  61       * Meta data: Long title
  62       * @var string
  63       */
  64      var $title;
  65  
  66      /**
  67       * Meta data: ALT text for images
  68       * @var string
  69       */
  70      var $alt;
  71  
  72      /**
  73       * Meta data: Description
  74       * @var string
  75       */
  76      var $desc;
  77  
  78      /**
  79       * Meta data: Hash value of file path
  80       * @var string
  81       */
  82      var $hash;
  83  
  84      /**
  85       * FileRoot of this file
  86       * @var Fileroot
  87       * @access protected
  88       */
  89      var $_FileRoot;
  90  
  91      /**
  92       * Posix subpath for this file/folder, relative the associated root (No trailing slash)
  93       * @var string
  94       * @access protected
  95       */
  96      var $_rdfp_rel_path;
  97  
  98      /**
  99       * Full path for this file/folder, WITHOUT trailing slash.
 100       * @var string
 101       * @access protected
 102       */
 103      var $_adfp_full_path;
 104  
 105      /**
 106       * Directory path for this file/folder, including trailing slash.
 107       * @var string
 108       * @see get_dir()
 109       * @access protected
 110       */
 111      var $_dir;
 112  
 113      /**
 114       * Name of this file/folder, without path.
 115       * @var string
 116       * @access protected
 117       */
 118      var $_name;
 119  
 120      /**
 121       * MD5 hash of full pathname.
 122       *
 123       * This is useful to refer to files in hidden form fields, but might be replaced by the root_ID+relpath.
 124       *
 125       * @todo fplanque>> get rid of it
 126       *
 127       * @var string
 128       * @see get_md5_ID()
 129       * @access protected
 130       */
 131      var $_md5ID;
 132  
 133      /**
 134       * Does the File/folder exist on disk?
 135       * @var boolean
 136       * @see exists()
 137       * @access protected
 138       */
 139      var $_exists;
 140  
 141      /**
 142       * Is the File a directory?
 143       * @var boolean
 144       * @see is_dir()
 145       * @access protected
 146       */
 147      var $_is_dir;
 148  
 149      /**
 150       * File size in bytes.
 151       * @var integer
 152       * @see get_size()
 153       * @access protected
 154       */
 155      var $_size;
 156  
 157      /**
 158       * Recursive directory size in bytes.
 159       * @var integer
 160       * @see get_recursive_size()
 161       * @access protected
 162       */
 163      var $_recursive_size;
 164  
 165      /**
 166       * UNIX timestamp of last modification on disk.
 167       * @var integer
 168       * @see get_lastmod_ts()
 169       * @see get_lastmod_formatted()
 170       * @access protected
 171       */
 172      var $_lastmod_ts;
 173  
 174      /**
 175       * Filesystem file permissions.
 176       * @var integer
 177       * @see get_perms()
 178       * @access protected
 179       */
 180      var $_perms;
 181  
 182      /**
 183       * File owner. NULL if unknown
 184       * @var string
 185       * @see get_fsowner_name()
 186       * @access protected
 187       */
 188      var $_fsowner_name;
 189  
 190      /**
 191       * File group. NULL if unknown
 192       * @var string
 193       * @see get_fsgroup_name()
 194       * @access protected
 195       */
 196      var $_fsgroup_name;
 197  
 198      /**
 199       * Is the File an image? NULL if unknown
 200       * @var boolean
 201       * @see is_image()
 202       * @access protected
 203       */
 204      var $_is_image;
 205  
 206      /**
 207       * Is the File an audio file? NULL if unknown
 208       * @var boolean
 209       * @see is_audio()
 210       * @access protected
 211       */
 212      var $_is_audio;
 213  
 214      /**
 215       * Extension, Mime type, icon, viewtype and 'allowed extension' of the file
 216       * @access protected
 217       * @see File::get_Filetype
 218       * @var Filetype
 219       */
 220      var $Filetype;
 221  
 222  
 223      /**
 224       * Constructor, not meant to be called directly. Use {@link FileCache::get_by_root_and_path()}
 225       * instead, which provides caching and checks that only one object for
 226       * a unique file exists (references).
 227       *
 228       * @param string Root type: 'user', 'group', 'collection' or 'absolute'
 229       * @param integer ID of the user, the group or the collection the file belongs to...
 230       * @param string Posix subpath for this file/folder, relative to the associated root (no trailing slash)
 231       * @param boolean check for meta data?
 232       * @return mixed false on failure, File object on success
 233       */
 234  	function File( $root_type, $root_ID, $rdfp_rel_path, $load_meta = false )
 235      {
 236          global $Debuglog;
 237  
 238          $Debuglog->add( "new File( $root_type, $root_ID, $rdfp_rel_path, load_meta=$load_meta)", 'files' );
 239  
 240          // Call parent constructor
 241          parent::DataObject( 'T_files', 'file_', 'file_ID', '', '', '', '' );
 242  
 243          $this->delete_restrictions = array(
 244                  array( 'table'=>'T_links', 'fk'=>'link_file_ID', 'field' => 'link_itm_ID', 'msg'=>T_('%d linked items') ),
 245                  array( 'table'=>'T_links', 'fk'=>'link_file_ID', 'field' => 'link_cmt_ID', 'msg'=>T_('%d linked comments') ),
 246                  array( 'table'=>'T_users', 'fk'=>'user_avatar_file_ID', 'msg'=>T_('%d linked users (profile pictures)') ),
 247              );
 248  
 249          $this->delete_cascades = array(
 250                  array( 'table'=>'T_files__vote', 'fk'=>'fvot_file_ID', 'msg'=>T_('%d votes') ),
 251              );
 252  
 253          // Memorize filepath:
 254          $FileRootCache = & get_FileRootCache();
 255          $this->_FileRoot = & $FileRootCache->get_by_type_and_ID( $root_type, $root_ID );
 256  
 257          // If there's a valid file root, handle extra stuff. This should not get done when the FileRoot is invalid.
 258          if( $this->_FileRoot )
 259          {
 260              $this->_rdfp_rel_path = trim( str_replace( '\\', '/', $rdfp_rel_path ), '/' );
 261              $this->_adfp_full_path = $this->_FileRoot->ads_path.$this->_rdfp_rel_path;
 262              $this->_name = basename( $this->_adfp_full_path );
 263              $this->_dir = dirname( $this->_adfp_full_path ).'/';
 264              $this->_md5ID = md5( $this->_adfp_full_path );
 265  
 266              // Initializes file properties (type, size, perms...)
 267              $this->load_properties();
 268  
 269              if( $load_meta )
 270              { // Try to load DB meta info:
 271                  $this->load_meta();
 272              }
 273          }
 274      }
 275  
 276  
 277      /**
 278       * Attempt to load meta data.
 279       *
 280       * Will attempt only once and cache the result.
 281       *
 282       * @param boolean create meta data in DB if it doesn't exist yet? (generates a $File->ID)
 283       * @param object database row containing all fields needed to initialize meta data
 284       * @return boolean true if meta data has been loaded/initialized.
 285       */
 286  	function load_meta( $force_creation = false, $row = NULL )
 287      {
 288          global $DB, $Debuglog;
 289  
 290          if( $this->meta == 'unknown' )
 291          { // We haven't tried loading yet:
 292              if( is_null( $row )    )
 293              {    // No DB data has been provided:
 294                  $row = $DB->get_row( "
 295                      SELECT * FROM T_files
 296                       WHERE file_root_type = '".$this->_FileRoot->type."'
 297                         AND file_root_ID = ".$this->_FileRoot->in_type_ID."
 298                         AND file_path = ".$DB->quote($this->_rdfp_rel_path),
 299                      OBJECT, 0, 'Load file meta data' );
 300              }
 301  
 302              // We check that we got something AND that the CASE matches (because of case insensitive collations on MySQL)
 303              if( $row && $row->file_path == $this->_rdfp_rel_path )
 304              { // We found meta data
 305                  $Debuglog->add( "Loaded metadata for {$this->_FileRoot->ID}:{$this->_rdfp_rel_path}", 'files' );
 306                  $this->meta  = 'loaded';
 307                  $this->ID    = $row->file_ID;
 308                  $this->title = $row->file_title;
 309                  $this->alt   = $row->file_alt;
 310                  $this->desc  = $row->file_desc;
 311                  if( isset( $row->file_hash ) )
 312                  {
 313                      $this->hash  = $row->file_hash;
 314                  }
 315  
 316                  // Store this in the FileCache:
 317                  $FileCache = & get_FileCache();
 318                  $FileCache->add( $this );
 319              }
 320              else
 321              { // No meta data...
 322                  $Debuglog->add( sprintf('No metadata could be loaded for %d:%s', $this->_FileRoot ? $this->_FileRoot->ID : 'FALSE', $this->_rdfp_rel_path), 'files' );
 323                  $this->meta = 'notfound';
 324  
 325                  if( $force_creation )
 326                  {    // No meta data, we have to create it now!
 327                      $this->dbinsert();
 328                  }
 329              }
 330          }
 331  
 332          return ($this->meta == 'loaded');
 333      }
 334  
 335  
 336      /**
 337       * Create the file/folder on disk, if it does not exist yet.
 338       *
 339       * Also sets file permissions.
 340       * Also inserts meta data into DB (if file/folder was successfully created).
 341       *
 342       * @param string type ('dir'|'file')
 343       * @param string optional permissions (octal format), otherwise the default from {@link $Settings} gets used
 344       * @return boolean true if file/folder was created, false on failure
 345       */
 346  	function create( $type = 'file', $chmod = NULL )
 347      {
 348          if( $type == 'dir' )
 349          { // Create an empty directory:
 350              $success = @mkdir( $this->_adfp_full_path );
 351              $this->_is_dir = true; // used by chmod
 352          }
 353          else
 354          { // Create an empty file:
 355              $success = @touch( $this->_adfp_full_path );
 356              $this->_is_dir = false; // used by chmod
 357          }
 358          $this->chmod( $chmod ); // uses $Settings for NULL
 359  
 360          if( $success )
 361          {    // The file/folder has been successfully created:
 362  
 363              // Initializes file properties (type, size, perms...)
 364              $this->load_properties();
 365  
 366              // If there was meta data for this file in the DB:
 367              // (maybe the file had existed before?)
 368              // Let's recycle it! :
 369              if( ! $this->load_meta() )
 370              { // No meta data could be loaded, let's make sure localization info gets recorded:
 371                  $this->set( 'root_type', $this->_FileRoot->type );
 372                  $this->set( 'root_ID', $this->_FileRoot->in_type_ID );
 373                  $this->set( 'path', $this->_rdfp_rel_path );
 374              }
 375  
 376              // Record to DB:
 377              $this->dbsave();
 378          }
 379  
 380          return $success;
 381      }
 382  
 383  
 384      /**
 385       * Initializes or refreshes file properties (type, size, perms...)
 386       */
 387  	function load_properties()
 388      {
 389          // Unset values that will be determined (and cached) upon request
 390          $this->_is_image = NULL;
 391          $this->_is_audio = NULL;
 392          $this->_lastmod_ts = NULL;
 393          $this->_exists = NULL;
 394          $this->_perms = NULL;
 395          $this->_size = NULL;
 396          $this->_recursive_size = NULL;
 397  
 398          if( is_dir( $this->_adfp_full_path ) )
 399          {    // The File is a directory:
 400              $this->_is_dir = true;
 401          }
 402          else
 403          {    // The File is a regular file:
 404              $this->_is_dir = false;
 405          }
 406      }
 407  
 408  
 409      /**
 410       * Does the File/folder exist on disk?
 411       *
 412       * @return boolean true, if the file or dir exists; false if not
 413       */
 414  	function exists()
 415      {
 416          if( ! isset($this->_exists) )
 417          {
 418              $this->_exists = file_exists( $this->_adfp_full_path );
 419          }
 420          return $this->_exists;
 421      }
 422  
 423  
 424      /**
 425       * Is the File a directory?
 426       *
 427       * @return boolean true if the object is a directory, false if not
 428       */
 429  	function is_dir()
 430      {
 431          return $this->_is_dir;
 432      }
 433  
 434  
 435      /**
 436       * Is the File an image?
 437       *
 438       * Tries to determine if it is and caches the info.
 439       *
 440       * @return boolean true if the object is an image, false if not
 441       */
 442  	function is_image()
 443      {
 444          if( is_null( $this->_is_image ) )
 445          {    // We don't know yet
 446              $this->_is_image = ( $this->get_image_size() !== false );
 447          }
 448  
 449          return $this->_is_image;
 450      }
 451  
 452      /**
 453       * Is the File an audio file?
 454       *
 455       * Tries to determine if it is and caches the info.
 456       *
 457       * @return boolean true if the object is an audio file, false if not
 458       */
 459  	function is_audio()
 460      {
 461          if ( is_null( $this->_is_audio ) )
 462          {
 463              $this->_is_audio = in_array($this->get_ext(), array('mp3', 'oga'));
 464          }
 465          return $this->_is_audio;
 466      }
 467  
 468  
 469      /**
 470       * Is the file editable?
 471       *
 472       * @param mixed true/false allow locked file types? NULL value means that FileType will decide
 473       */
 474  	function is_editable( $allow_locked = NULL )
 475      {
 476          if( $this->is_dir() )
 477          { // we cannot edit dirs
 478              return false;
 479          }
 480  
 481          $Filetype = & $this->get_Filetype();
 482          if( empty($Filetype) || $this->Filetype->viewtype != 'text' )    // we can only edit text files
 483          {
 484              return false;
 485          }
 486  
 487          // user can edit only allowed file types
 488          return $Filetype->is_allowed( $allow_locked );
 489      }
 490  
 491  
 492      /**
 493       * Get the File's Filetype object (or NULL).
 494       *
 495       * @return Filetype The Filetype object or NULL
 496       */
 497      function & get_Filetype()
 498      {
 499          if( ! isset($this->Filetype) )
 500          {
 501              // Create the filetype with the extension of the file if the extension exist in database
 502              if( $ext = $this->get_ext() )
 503              { // The file has an extension, load filetype object
 504                  $FiletypeCache = & get_FiletypeCache();
 505                  $this->Filetype = & $FiletypeCache->get_by_extension( strtolower( $ext ), false );
 506              }
 507  
 508              if( ! $this->Filetype )
 509              { // remember as being retrieved.
 510                  $this->Filetype = false;
 511              }
 512          }
 513          $r = $this->Filetype ? $this->Filetype : NULL;
 514          return $r;
 515      }
 516  
 517  
 518      /**
 519       * Get the File's ID (MD5 of path and name)
 520       *
 521       * @return string
 522       */
 523  	function get_md5_ID()
 524      {
 525          return $this->_md5ID;
 526      }
 527  
 528  
 529      /**
 530       * Get a member param by its name
 531       *
 532       * @param mixed Name of parameter
 533       * @return mixed Value of parameter
 534       */
 535  	function get( $parname )
 536      {
 537          switch( $parname )
 538          {
 539              case 'name':
 540                  return $this->_name;
 541  
 542              default:
 543                  return parent::get( $parname );
 544          }
 545      }
 546  
 547  
 548      /**
 549       * Get the File's name.
 550       *
 551       * @return string
 552       */
 553  	function get_name()
 554      {
 555          return $this->_name;
 556      }
 557  
 558  
 559      /**
 560       * Get the name prefixed either with "Directory" or "File".
 561       *
 562       * Returned string is localized.
 563       *
 564       * @return string
 565       */
 566  	function get_prefixed_name()
 567      {
 568          if( $this->is_dir() )
 569          {
 570              return sprintf( T_('Directory &laquo;%s&raquo;'), $this->_name );
 571          }
 572          else
 573          {
 574              return sprintf( T_('File &laquo;%s&raquo;'), $this->_name );
 575          }
 576      }
 577  
 578  
 579      /**
 580       * Get the File's directory.
 581       *
 582       * @return string
 583       */
 584  	function get_dir()
 585      {
 586          return $this->_dir;
 587      }
 588  
 589  
 590      /**
 591       * Get the file posix path relative to it's root (no trailing /)
 592       *
 593       * @return string full path
 594       */
 595  	function get_rdfp_rel_path()
 596      {
 597          return $this->_rdfp_rel_path;
 598      }
 599  
 600  
 601      /**
 602       * Get the file path relative to it's root, WITH trailing slash.
 603       *
 604       * @return string full path
 605       */
 606  	function get_rdfs_rel_path()
 607      {
 608          return $this->_rdfp_rel_path.( $this->_is_dir ? '/' : '' );
 609      }
 610  
 611  
 612      /**
 613       * Get the full path (directory and name) to the file.
 614       *
 615       * If the File is a directory, the Path ends with a /
 616       *
 617       * @return string full path
 618       */
 619  	function get_full_path()
 620      {
 621          return $this->_adfp_full_path.( $this->_is_dir ? '/' : '' );
 622      }
 623  
 624  
 625      /**
 626       * Get the absolute file url if the file is public
 627       * Get the getfile.php url if we need to check permission before delivering the file
 628       */
 629  	function get_url()
 630      {
 631          global $public_access_to_media, $htsrv_url;
 632  
 633          if( $this->is_dir() )
 634          { // Directory
 635              if( $public_access_to_media )
 636              { // Public access: full path
 637                  $url = $this->_FileRoot->ads_url.$this->get_rdfs_rel_path().'?mtime='.$this->get_lastmod_ts();
 638              }
 639              else
 640              { // No Access
 641                  // TODO: dh> why can't this go through the FM, preferably opening in a popup, if the user has access?!
 642                  //           (see get_view_url)
 643                  // fp> the FM can do anything as long as this function does not send back an URL to something that is actually private.
 644                  debug_die( 'Private directory! ');
 645              }
 646          }
 647          else
 648          { // File
 649              if( $public_access_to_media )
 650              { // Public Access : full path
 651                  $url = $this->_FileRoot->ads_url.no_leading_slash($this->_rdfp_rel_path).'?mtime='.$this->get_lastmod_ts();
 652              }
 653              else
 654              { // Private Access: doesn't show the full path
 655                  $url = $this->get_getfile_url();
 656              }
 657          }
 658          return $url;
 659      }
 660  
 661  
 662      /**
 663       * Get location of file with its root (for display)
 664       */
 665  	function get_root_and_rel_path()
 666      {
 667          return $this->_FileRoot->name.':'.$this->get_rdfs_rel_path();
 668      }
 669  
 670  
 671      /**
 672       * Get the File's FileRoot.
 673       *
 674       * @return FileRoot
 675       */
 676      function & get_FileRoot()
 677      {
 678          return $this->_FileRoot;
 679      }
 680  
 681  
 682      /**
 683       * Get the file's extension.
 684       *
 685       * @return string the extension
 686       */
 687  	function get_ext()
 688      {
 689          if( preg_match('/\.([^.]+)$/', $this->_name, $match) )
 690          {
 691              return $match[1];
 692          }
 693          else
 694          {
 695              return '';
 696          }
 697      }
 698  
 699  
 700      /**
 701       * Get the file type as a descriptive localized string.
 702       *
 703       * @return string localized type name or 'Directory' or 'Unknown'
 704       */
 705  	function get_type()
 706      {
 707          if( isset( $this->_type ) )
 708          { // The type is already cached for this object:
 709              return $this->_type;
 710          }
 711  
 712          if( $this->is_dir() )
 713          {
 714              $this->_type = T_('Directory');
 715              return $this->_type;
 716          }
 717  
 718          $Filetype = & $this->get_Filetype();
 719          if( isset( $Filetype->mimetype ) )
 720          {
 721              $this->_type = $Filetype->name;
 722              return $this->_type;
 723          }
 724  
 725          $this->_type = T_('Unknown');
 726          return $this->_type;
 727      }
 728  
 729  
 730      /**
 731       * Get file/dir size in bytes.
 732       *
 733       * For the recursive size of a directory see {@link get_recursive_size()}.
 734       *
 735       * @return integer bytes
 736       */
 737  	function get_size()
 738      {
 739          if( ! isset($this->_size) )
 740          {
 741              $this->_size = @filesize( $this->_adfp_full_path );
 742          }
 743          return $this->_size;
 744      }
 745  
 746  
 747      /**
 748       * Get timestamp of last modification.
 749       *
 750       * @return integer Timestamp
 751       */
 752  	function get_lastmod_ts()
 753      {
 754          if( ! isset($this->_lastmod_ts) )
 755          {
 756              $this->_lastmod_ts = @filemtime( $this->_adfp_full_path );
 757          }
 758          return $this->_lastmod_ts;
 759      }
 760  
 761  
 762      /**
 763       * Get date/time of last modification, formatted.
 764       *
 765       * @param string date format or 'date' or 'time' for default locales.
 766       * @return string locale formatted date/time
 767       */
 768  	function get_lastmod_formatted( $format = '#' )
 769      {
 770          global $localtimenow;
 771  
 772          $lastmod_ts = $this->get_lastmod_ts();
 773  
 774          switch( $format )
 775          {
 776              case 'date':
 777                  return date_i18n( locale_datefmt(), $lastmod_ts );
 778  
 779              case 'time':
 780                  return date_i18n( locale_timefmt(), $lastmod_ts );
 781  
 782              case 'compact':
 783                  $age = $localtimenow - $lastmod_ts;
 784                  if( $age < 3600 )
 785                  {    // Less than 1 hour: return full time
 786                      return date_i18n( 'H:i:s', $lastmod_ts );
 787                  }
 788                  if( $age < 86400 )
 789                  {    // Less than 24 hours: return compact time
 790                      return date_i18n( 'H:i', $lastmod_ts );
 791                  }
 792                  if( $age < 31536000 )
 793                  {    // Less than 365 days: Month and day
 794                      return date_i18n( 'M, d', $lastmod_ts );
 795                  }
 796                  // Older: return yeat
 797                  return date_i18n( 'Y', $lastmod_ts );
 798                  break;
 799  
 800              case '#':
 801                  default:
 802                  $format = locale_datefmt().' '.locale_timefmt();
 803                  return date_i18n( $format, $lastmod_ts );
 804          }
 805      }
 806  
 807  
 808      /**
 809       * Get permissions
 810       *
 811       * Possible return formats are:
 812       *   - 'raw'=integer
 813       *   - 'lsl'=string like 'ls -l'
 814       *   - 'octal'=3 digits
 815       *
 816       * Default value:
 817       *   - 'r'/'r+w' for windows
 818       *   - 'octal' for other OS
 819       *
 820       * @param string type, see desc above.
 821       * @return mixed permissions
 822       */
 823  	function get_perms( $type = NULL )
 824      {
 825          if( ! isset($this->_perms) )
 826          {
 827              $this->_perms = @fileperms( $this->_adfp_full_path );
 828          }
 829          switch( $type )
 830          {
 831              case 'raw':
 832                  return $this->_perms;
 833  
 834              case 'lsl':
 835                  $sP = '';
 836  
 837                  if(($this->_perms & 0xC000) == 0xC000)     // Socket
 838                      $sP = 's';
 839                  elseif(($this->_perms & 0xA000) == 0xA000) // Symbolic Link
 840                      $sP = 'l';
 841                  elseif(($this->_perms & 0x8000) == 0x8000) // Regular
 842                      $sP = '&minus;';
 843                  elseif(($this->_perms & 0x6000) == 0x6000) // Block special
 844                      $sP = 'b';
 845                  elseif(($this->_perms & 0x4000) == 0x4000) // Directory
 846                      $sP = 'd';
 847                  elseif(($this->_perms & 0x2000) == 0x2000) // Character special
 848                      $sP = 'c';
 849                  elseif(($this->_perms & 0x1000) == 0x1000) // FIFO pipe
 850                      $sP = 'p';
 851                  else                                   // UNKNOWN
 852                      $sP = 'u';
 853  
 854                  // owner
 855                  $sP .= (($this->_perms & 0x0100) ? 'r' : '&minus;') .
 856                         (($this->_perms & 0x0080) ? 'w' : '&minus;') .
 857                         (($this->_perms & 0x0040) ? (($this->_perms & 0x0800) ? 's' : 'x' )
 858                                                   : (($this->_perms & 0x0800) ? 'S' : '&minus;'));
 859  
 860                  // group
 861                  $sP .= (($this->_perms & 0x0020) ? 'r' : '&minus;') .
 862                         (($this->_perms & 0x0010) ? 'w' : '&minus;') .
 863                         (($this->_perms & 0x0008) ? (($this->_perms & 0x0400) ? 's' : 'x' )
 864                                                   : (($this->_perms & 0x0400) ? 'S' : '&minus;'));
 865  
 866                  // world
 867                  $sP .= (($this->_perms & 0x0004) ? 'r' : '&minus;') .
 868                         (($this->_perms & 0x0002) ? 'w' : '&minus;') .
 869                         (($this->_perms & 0x0001) ? (($this->_perms & 0x0200) ? 't' : 'x' )
 870                                                   : (($this->_perms & 0x0200) ? 'T' : '&minus;'));
 871                  return $sP;
 872  
 873              case NULL:
 874                  if( is_windows() )
 875                  {
 876                      if( $this->_perms & 0x0080 )
 877                      {
 878                          return 'r+w';
 879                      }
 880                      else return 'r';
 881                  }
 882  
 883              case 'octal':
 884                  return substr( sprintf('%o', $this->_perms), -3 );
 885          }
 886  
 887          return false;
 888      }
 889  
 890  
 891      /**
 892       * Get the owner name of the file.
 893       *
 894       * @todo Can this be fixed for windows? filegroup() might only return 0 or 1 nad posix_getgrgid() is not available..
 895       * @return NULL|string
 896       */
 897  	function get_fsgroup_name()
 898      {
 899          if( ! isset( $this->_fsgroup_name ) )
 900          {
 901              $gid = @filegroup( $this->_adfp_full_path ); // might spit a warning for a dangling symlink
 902  
 903              if( $gid !== false
 904                      && function_exists( 'posix_getgrgid' ) ) // func does not exist on windows
 905              {
 906                  $posix_group = posix_getgrgid( $gid );
 907                  if( is_array($posix_group) )
 908                  {
 909                      $this->_fsgroup_name = $posix_group['name'];
 910                  }
 911                  else
 912                  { // fallback to gid:
 913                      $this->_fsgroup_name = $gid;
 914                  }
 915              }
 916          }
 917  
 918          return $this->_fsgroup_name;
 919      }
 920  
 921  
 922      /**
 923       * Get the owner name of the file.
 924       *
 925       * @todo Can this be fixed for windows? fileowner() might only return 0 or 1 nad posix_getpwuid() is not available..
 926       * @return NULL|string
 927       */
 928  	function get_fsowner_name()
 929      {
 930          if( ! isset( $this->_fsowner_name ) )
 931          {
 932              $uid = @fileowner( $this->_adfp_full_path ); // might spit a warning for a dangling symlink
 933              if( $uid !== false
 934                      && function_exists( 'posix_getpwuid' ) ) // func does not exist on windows
 935              {
 936                  $posix_user = posix_getpwuid( $uid );
 937                  if( is_array($posix_user) )
 938                  {
 939                      $this->_fsowner_name = $posix_user['name'];
 940                  }
 941                  else
 942                  { // fallback to uid:
 943                      $this->_fsowner_name = $uid;
 944                  }
 945              }
 946          }
 947  
 948          return $this->_fsowner_name;
 949      }
 950  
 951  
 952      /**
 953       * Get icon for this file.
 954       *
 955       * Looks at the file's extension.
 956       *
 957       * @uses get_icon()
 958       * @return string img tag
 959       */
 960  	function get_icon()
 961      {
 962          if( $this->is_dir() )
 963          { // Directory icon:
 964              $icon = 'folder';
 965          }
 966          else
 967          {
 968              $Filetype = & $this->get_Filetype();
 969              if( isset( $Filetype->icon ) && $Filetype->icon )
 970              { // Return icon for known type of the file
 971                      return $Filetype->get_icon();
 972              }
 973              else
 974              { // Icon for unknown file type:
 975                  $icon = 'file_unknown';
 976              }
 977          }
 978          // Return Icon for a directory or unknown type file:
 979          return get_icon( $icon, 'imgtag', array( 'alt'=>$this->get_ext(), 'title'=>$this->get_type() ) );
 980      }
 981  
 982  
 983      /**
 984       * Get size of an image or false if not an image
 985       *
 986       * @todo cache this data (NOTE: we have different params here! - imgsize() does caching already!)
 987       *
 988       * @uses imgsize()
 989       * @param string {@link imgsize()}
 990       * @return false|mixed false if the File is not an image, the requested data otherwise
 991       */
 992  	function get_image_size( $param = 'widthxheight' )
 993      {
 994          return imgsize( $this->_adfp_full_path, $param );
 995      }
 996  
 997  
 998      /**
 999       * Get size of the file, formatted to nearest unit (kb, mb, etc.)
1000       *
1001       * @uses bytesreadable()
1002       * @return string size as b/kb/mb/gd; or '&lt;dir&gt;'
1003       */
1004  	function get_size_formatted()
1005      {
1006          if( $this->is_dir() )
1007          {
1008              return /* TRANS: short for '<directory>' */ T_('&lt;dir&gt;');
1009          }
1010          else
1011          {
1012              return bytesreadable( $this->get_size() );
1013          }
1014      }
1015  
1016  
1017      /**
1018       * Get a complete tag (IMG or A HREF) pointing to this file.
1019       *
1020       * @param string
1021       * @param string NULL for no legend
1022       * @param string
1023       * @param string
1024       * @param string
1025       * @param string
1026       * @param string
1027       * @param string rel attribute of link, usefull for jQuery libraries selecting on rel='...', e-g: lighbox
1028       * @param string image class
1029       * @param string image align
1030       * @param string image rel
1031       * @param string image caption/description
1032       */
1033  	function get_tag( $before_image = '<div class="image_block">',
1034                        $before_image_legend = '<div class="image_legend">', // can be NULL
1035                        $after_image_legend = '</div>',
1036                        $after_image = '</div>',
1037                        $size_name = 'original',
1038                        $image_link_to = 'original',
1039                        $image_link_title = '',    // can be text or #title# or #desc#
1040                        $image_link_rel = '',
1041                        $image_class = '',
1042                        $image_align = '',
1043                        $image_alt = '',
1044                        $image_desc = '#' )
1045      {
1046          if( $this->is_dir() )
1047          {    // We can't reference a directory
1048              return '';
1049          }
1050  
1051          $this->load_meta();
1052  
1053          if( $this->is_image() )
1054          { // Make an IMG link:
1055              $r = $before_image;
1056  
1057              if( $image_class != '' )
1058              {
1059                  $image_class = ' class="'.$image_class.'"';
1060              }
1061  
1062              if( $image_align != '' )
1063              {
1064                  $image_align =' align="'.$image_align.'"';
1065              }
1066  
1067              $img_attribs = $this->get_img_attribs($size_name);
1068  
1069              if( $img_attribs['alt'] == '' )
1070              {
1071                  $img_attribs['alt'] = $image_alt;
1072              }
1073  
1074              $img = '<img'.get_field_attribs_as_string($img_attribs).$image_class.$image_align.' />';
1075  
1076              if( $image_link_to == 'original' )
1077              {    // special case
1078                  $image_link_to = $this->get_url();
1079              }
1080              if( !empty( $image_link_to ) )
1081              {
1082                  $a = '<a href="'.$image_link_to.'"';
1083  
1084                  if( $image_link_title == '#title#' )
1085                      $image_link_title = $this->title;
1086                  elseif( $image_link_title == '#desc#' )
1087                      $image_link_title = $this->desc;
1088  
1089                  if( !empty($image_link_title) )
1090                  {
1091                      $a .= ' title="'.htmlspecialchars($image_link_title).'"';
1092                  }
1093                  if( !empty($image_link_rel) )
1094                  {
1095                      $a .= ' rel="'.htmlspecialchars($image_link_rel).'"';
1096                  }
1097                  $a .= ' id="f'.$this->ID.'"';
1098                  $img = $a.'>'.$img.'</a>';
1099              }
1100              $r .= $img;
1101  
1102              if( $image_desc == '#' )
1103              {
1104                  $image_desc = $this->dget('desc');
1105              }
1106              if( !empty( $image_desc ) && !is_null( $before_image_legend ) )
1107              {
1108                  $r .= $before_image_legend
1109                              .$image_desc        // If this needs to be changed, please document.
1110                              .$after_image_legend;
1111              }
1112              $r .= $after_image;
1113          }
1114          else
1115          {    // Make an A HREF link:
1116              $r = '<a href="'.$this->get_url().'"'
1117                          // title
1118                          .( $this->get('desc') ? ' title="'.$this->dget('desc', 'htmlattr').'"' : '' ).'>'
1119                          // link text
1120                          .( $this->get('title') ? $this->dget('title') : $this->dget('name') ).'</a>';
1121          }
1122  
1123          return $r;
1124      }
1125  
1126  
1127      /*
1128       * Get gallery for code for a directory
1129       *
1130       * @param array of params
1131       * @return string gallery HTML code
1132       */
1133  	function get_gallery( $params )
1134      {
1135          $params = array_merge( array(
1136                  'before_gallery'        => '<div class="bGallery">',
1137                  'after_gallery'         => '</div>',
1138                  'gallery_image_size'    => 'crop-80x80',
1139                  'gallery_image_limit'   => 1000,
1140                  'gallery_colls'         => 5,
1141                  'gallery_order'            => '', // 'ASC', 'DESC', 'RAND'
1142              ), $params );
1143  
1144          if( ! $this->is_dir() )
1145          {    // Not a directory
1146              return '';
1147          }
1148          if( ! $FileList = $this->get_gallery_images( $params['gallery_image_limit'], $params['gallery_order'] ) )
1149          {    // No images in this directory
1150              return '';
1151          }
1152  
1153          $r = $params['before_gallery'];
1154          $r .= '<table cellpadding="0" cellspacing="3" border="0" class="image_index">';
1155  
1156          $count = 0;
1157          foreach( $FileList as $l_File )
1158          {
1159              // We're linking to the original image, let lighbox (or clone) quick in:
1160              $link_title = '#title#'; // This title will be used by lightbox (colorbox for instance)
1161              $link_rel = 'lightbox[g'.$this->ID.']'; // Make one "gallery" per directory.
1162  
1163              $img_tag = $l_File->get_tag( '', NULL, '', '', $params['gallery_image_size'], 'original', $link_title, $link_rel );
1164  
1165              if( $count % $params['gallery_colls'] == 0 ) $r .= "\n<tr>";
1166              $count++;
1167              $r .= "\n\t".'<td valign="top">';
1168              // ======================================
1169              // Individual table cell
1170  
1171              $r .= '<div class="bGallery-thumbnail">'.$img_tag.'</div>';
1172  
1173              // ======================================
1174              $r .= '</td>';
1175              if( $count % $params['gallery_colls'] == 0 ) $r .= "\n</tr>";
1176          }
1177          if( $count && ( $count % $params['gallery_colls'] != 0 ) ) $r .= "\n</tr>";
1178  
1179          $r .= '</table>';
1180          $r .= $params['after_gallery'];
1181  
1182          return $r;
1183      }
1184  
1185  
1186      /*
1187       * Get all images in a directory (no recursion)
1188       *
1189       * @param integer how many images to return
1190       * @param string filenames order ASC DESC RAND or empty string
1191       * @return array of instantiated File objects or false
1192       */
1193  	function get_gallery_images( $limit = 1000, $order = '' )
1194      {
1195          if( $filenames = $this->get_directory_files('relative') )
1196          {
1197              $FileCache = & get_FileCache();
1198  
1199              switch( strtoupper($order) )
1200              {
1201                  case 'ASC':
1202                      sort($filenames);
1203                      break;
1204  
1205                  case 'DESC':
1206                      rsort($filenames);
1207                      break;
1208  
1209                  case 'RAND':
1210                      shuffle($filenames);
1211                      break;
1212              }
1213  
1214              $i = 1;
1215              foreach( $filenames as $filename )
1216              {
1217                  if( $i > $limit )
1218                  {    // We've got enough images
1219                      break;
1220                  }
1221  
1222                  /*
1223                  sam2kb> TODO: we may need to filter files by extension first, it doesn't make sence
1224                          to query the database for every single .txt or .zip file.
1225                          The best solution would be to have file MIME type field in DB
1226                  */
1227                  $l_File = & $FileCache->get_by_root_and_path( $this->_FileRoot->type, $this->_FileRoot->in_type_ID, $filename );
1228                  $l_File->load_meta();
1229  
1230                  if( ! $l_File->is_image() )
1231                  {    // Not an image
1232                      continue;
1233                  }
1234                  $Files[] = $l_File;
1235  
1236                  $i++;
1237              }
1238              if( !empty($Files) )
1239              {
1240                  return $Files;
1241              }
1242          }
1243          return false;
1244      }
1245  
1246  
1247      /*
1248       * Get all files in a directory (no recursion)
1249       *
1250       * @param string what part of file name to return
1251       *        'basename' return file name only e.g. 'bus-stop-ahead.jpg'
1252       *         'ralative' file path relative to '_adfp_full_path' e.g. 'monument-valley/bus-stop-ahead.jpg'
1253       *        'absolute' full file path e.g. '/home/user/html/media/shared/global/monument-valley/bus-stop-ahead.jpg'
1254       * @return array of files
1255       */
1256  	function get_directory_files( $path_type = 'relative' )
1257      {
1258          global $Settings;
1259  
1260          $path = trailing_slash( $this->_adfp_full_path );
1261  
1262          if( $dir = @opendir($path) )
1263          {    // Scan directory and list all files
1264              $filenames = array();
1265              while( ($file = readdir($dir)) !== false )
1266              {
1267                  if( $file == '.' || $file == '..' || $file == $Settings->get('evocache_foldername') )
1268                  {    // Invalid file
1269                      continue;
1270                  }
1271  
1272                  // sam2kb> TODO: Do we need to process directories recursively?
1273                  if( ! is_dir($path.$file) )
1274                  {
1275                      switch( $path_type )
1276                      {
1277                          case 'basename':
1278                              $filenames[] = $file;
1279                              break;
1280  
1281                          case 'relative':
1282                              $filenames[] = trailing_slash($this->_rdfp_rel_path).$file;
1283                              break;
1284  
1285                          case 'absolute':
1286                              $filenames[] = $path.$file;
1287                              break;
1288                      }
1289                  }
1290              }
1291              closedir($dir);
1292  
1293              if( !empty($filenames) )
1294              {
1295                  return $filenames;
1296              }
1297          }
1298          return false;
1299      }
1300  
1301  
1302      /**
1303       * Get the "full" size of a file/dir (recursive for directories).
1304       * This is used by the FileList.
1305       * @return integer Recursive size of the dir or the size alone for a file.
1306       */
1307  	function get_recursive_size()
1308      {
1309          if( ! isset($this->_recursive_size) )
1310          {
1311              if( $this->is_dir() )
1312                  $this->_recursive_size = get_dirsize_recursive( $this->get_full_path() );
1313              else
1314                  $this->_recursive_size = $this->get_size();
1315          }
1316          return $this->_recursive_size;
1317      }
1318  
1319  
1320      /**
1321       * Rewrite the file paths, because one the parent folder name was changed - recursive function
1322       *
1323       * This function should be used just after a folder rename
1324       *
1325       * @access should be private
1326       * @param string relative path for this file's parent directory
1327       * @param string full path for this file's parent directory
1328       */
1329  	function modify_path ( $rel_dir, $full_dir )
1330      {
1331          if( $this->is_dir() )
1332          {
1333              $new_rel_dir = $rel_dir.$this->_name.'/';
1334              $new_full_dir = $full_dir.$this->_name.'/';
1335  
1336              $temp_Filelist = new Filelist( $this->_FileRoot, $this->_adfp_full_path );
1337              $temp_Filelist->load();
1338  
1339              while ( $temp_File = $temp_Filelist->get_next() )
1340              {
1341                  $temp_File->modify_path( $new_rel_dir, $new_full_dir );
1342              }
1343          }
1344  
1345          $this->load_meta();
1346          $this->_rdfp_rel_path = $rel_dir.$this->_name;
1347          $this->_dir = $full_dir;
1348          $this->_adfp_full_path = $this->_dir.$this->_name;
1349          $this->_md5ID = md5( $this->_adfp_full_path );
1350  
1351          if( $this->meta == 'loaded' )
1352          {    // We have meta data, we need to deal with it:
1353              // unchanged : $this->set( 'root_type', $this->_FileRoot->type );
1354              // unchanged : $this->set( 'root_ID', $this->_FileRoot->in_type_ID );
1355              $this->set( 'path', $this->_rdfp_rel_path );
1356              // Record to DB:
1357              $this->dbupdate();
1358          }
1359          else
1360          {    // There might be some old meta data to *recycle* in the DB...
1361              $this->load_meta();
1362          }
1363      }
1364  
1365  
1366      /**
1367       * Rename the file in its current directory on disk.
1368       *
1369       * Also update meta data in DB.
1370       *
1371       * @access public
1372       * @param string new name (without path!)
1373       * @return boolean true on success, false on failure
1374       */
1375  	function rename_to( $newname )
1376      {
1377          // rename() will fail if newname already exists on windows
1378          // if it doesn't work that way on linux we need the extra check below
1379          // but then we have an integrity issue!! :(
1380          if( file_exists($this->_dir.$newname) )
1381          {
1382              return false;
1383          }
1384  
1385          global $DB;
1386          $DB->begin();
1387  
1388          $oldname = $this->get_name();
1389  
1390          if( $this->is_dir() )
1391          { // modify folder content file paths in db
1392              $rel_dir = dirname( $this->_rdfp_rel_path ).'/';
1393              if( $rel_dir == './' )
1394              {
1395                  $rel_dir = '';
1396              }
1397              $rel_dir = $rel_dir.$newname.'/';
1398              $full_dir = $this->_dir.$newname.'/';
1399  
1400              $temp_Filelist = new Filelist( $this->_FileRoot, $this->_adfp_full_path );
1401              $temp_Filelist->load();
1402  
1403              while ( $temp_File = $temp_Filelist->get_next() )
1404              {
1405                  $temp_File->modify_path ( $rel_dir, $full_dir );
1406              }
1407          }
1408  
1409          if( ! @rename( $this->_adfp_full_path, $this->_dir.$newname ) )
1410          { // Rename will fail if $newname already exists (at least on windows)
1411              $DB->rollback();
1412              return false;
1413          }
1414  
1415          // Delete thumb caches for old name:
1416          // Note: new name = new usage : there is a fair chance we won't need the same cache sizes in the new loc.
1417          $this->rm_cache();
1418  
1419          // Get Meta data (before we change name) (we may need to update it later):
1420          $this->load_meta();
1421  
1422          $this->_name = $newname;
1423          $this->Filetype = NULL; // depends on name
1424  
1425          $rel_dir = dirname( $this->_rdfp_rel_path ).'/';
1426          if( $rel_dir == './' )
1427          {
1428              $rel_dir = '';
1429          }
1430          $this->_rdfp_rel_path = $rel_dir.$this->_name;
1431  
1432          $this->_adfp_full_path = $this->_dir.$this->_name;
1433          $this->_md5ID = md5( $this->_adfp_full_path );
1434  
1435          if( $this->meta == 'loaded' )
1436          {    // We have meta data, we need to deal with it:
1437              // unchanged : $this->set( 'root_type', $this->_FileRoot->type );
1438              // unchanged : $this->set( 'root_ID', $this->_FileRoot->in_type_ID );
1439              $this->set( 'path', $this->_rdfp_rel_path );
1440              // Record to DB:
1441              if ( ! $this->dbupdate() )
1442              {    // Update failed, try to rollback the rename on disk:
1443                  if( ! @rename( $this->_adfp_full_path, $this->_dir.$oldname ) )
1444                  { // rename failed
1445                      $DB->rollback();
1446                      return false;
1447                  }
1448                  // Maybe needs a specific error message here, the db and the disk is out of sync
1449                  return false;
1450              }
1451          }
1452          else
1453          {    // There might be some old meta data to *recycle* in the DB...
1454              // This can happen if there has been a file in the same location in the past and if that file
1455              // has been manually deleted or moved since then. When the new file arrives here, we'll recover
1456              // the zombie meta data and we don't reset it on purpose. Actually, we consider that the meta data
1457              // has been *accidentaly* lost and that the user is attempting to recover it by putting back the
1458              // file where it was before. Of course the logical way would be to put back the file manually, but
1459              // experience proves that users are inconsistent!
1460              $this->load_meta();
1461          }
1462  
1463          $DB->commit();
1464  
1465          return true;
1466      }
1467  
1468  
1469      /**
1470       * Move the file to another location
1471       *
1472       * Also updates meta data in DB
1473       *
1474       * @param string Root type: 'user', 'group', 'collection' or 'absolute'
1475       * @param integer ID of the user, the group or the collection the file belongs to...
1476       * @param string Subpath for this file/folder, relative the associated root (no trailing slash)
1477       * @return boolean true on success, false on failure
1478       */
1479  	function move_to( $root_type, $root_ID, $rdfp_rel_path )
1480      {
1481          // echo "relpath= $rel_path ";
1482  
1483          $rdfp_rel_path = str_replace( '\\', '/', $rdfp_rel_path );
1484          $FileRootCache = & get_FileRootCache();
1485  
1486          $new_FileRoot = & $FileRootCache->get_by_type_and_ID( $root_type, $root_ID, true );
1487          $adfp_posix_path = $new_FileRoot->ads_path.$rdfp_rel_path;
1488  
1489          if( ! @rename( $this->_adfp_full_path, $adfp_posix_path ) )
1490          {
1491              return false;
1492          }
1493  
1494          // Delete thumb caches from old location:
1495          // Note: new location = new usage : there is a fair chance we won't need the same cache sizes in the new loc.
1496          $this->rm_cache();
1497  
1498          // Get Meta data (before we change name) (we may need to update it later):
1499          $this->load_meta();
1500  
1501          // Memorize new filepath:
1502          $this->_FileRoot = & $new_FileRoot;
1503          $this->_rdfp_rel_path = $rdfp_rel_path;
1504          $this->_adfp_full_path = $adfp_posix_path;
1505          $this->_name = basename( $this->_adfp_full_path );
1506          $this->Filetype = NULL; // depends on name
1507          $this->_dir = dirname( $this->_adfp_full_path ).'/';
1508          $this->_md5ID = md5( $this->_adfp_full_path );
1509  
1510          if( $this->meta == 'loaded' )
1511          {    // We have meta data, we need to deal with it:
1512              $this->set( 'root_type', $this->_FileRoot->type );
1513              $this->set( 'root_ID', $this->_FileRoot->in_type_ID );
1514              $this->set( 'path', $this->_rdfp_rel_path );
1515              // Record to DB:
1516              $this->dbupdate();
1517          }
1518          else
1519          {    // There might be some old meta data to *recycle* in the DB...
1520              // This can happen if there has been a file in the same location in the past and if that file
1521              // has been manually deleted or moved since then. When the new file arrives here, we'll recover
1522              // the zombie meta data and we don't reset it on purpose. Actually, we consider that the meta data
1523              // has been *accidentaly* lost and that the user is attempting to recover it by putting back the
1524              // file where it was before. Of course the logical way would be to put back the file manually, but
1525              // experience proves that users are inconsistent!
1526              $this->load_meta();
1527          }
1528  
1529          return true;
1530      }
1531  
1532  
1533       /**
1534       * Copy this file to a new location
1535       *
1536       * Also copy meta data in Object
1537       *
1538       * @param File the target file (expected to not exist)
1539       * @return boolean true on success, false on failure
1540       */
1541  	function copy_to( & $dest_File )
1542      {
1543          if( ! $this->exists() || $dest_File->exists() )
1544          {
1545              return false;
1546          }
1547  
1548          // TODO: fp> what happens if someone else creates the destination file right at this moment here?
1549          //       dh> use a locking mechanism.
1550  
1551          if( ! @copy( $this->get_full_path(), $dest_File->get_full_path() ) )
1552          {    // Note: unlike rename() (at least on Windows), copy() will not fail if destination already exists
1553              // this is probably a permission problem
1554              return false;
1555          }
1556  
1557          // Initializes file properties (type, size, perms...)
1558          $dest_File->load_properties();
1559  
1560          // Meta data...:
1561          if( $this->load_meta() )
1562          {    // We have source meta data, we need to copy it:
1563              // Try to load DB meta info for destination file:
1564              $dest_File->load_meta();
1565  
1566              // Copy meta data:
1567              $dest_File->set( 'title', $this->title );
1568              $dest_File->set( 'alt'  , $this->alt );
1569              $dest_File->set( 'desc' , $this->desc );
1570  
1571              // Save meta data:
1572              $dest_File->dbsave();
1573          }
1574  
1575          return true;
1576      }
1577  
1578  
1579      /**
1580       * Unlink/Delete the file or folder from disk.
1581       *
1582       * Also removes meta data from DB.
1583       *
1584       * @access public
1585       * @return boolean true on success, false on failure
1586       */
1587  	function unlink()
1588      {
1589          global $DB;
1590  
1591          $DB->begin();
1592  
1593          // Check if there is meta data to be removed:
1594          if( $this->load_meta() )
1595          { // remove meta data from DB:
1596              $this->dbdelete();
1597          }
1598  
1599          // Remove thumb cache:
1600          $this->rm_cache();
1601  
1602          // Physically remove file from disk:
1603          if( $this->is_dir() )
1604          {
1605              $unlinked =    @rmdir( $this->_adfp_full_path );
1606          }
1607          else
1608          {
1609              $unlinked =    @unlink( $this->_adfp_full_path );
1610          }
1611  
1612          if( !$unlinked )
1613          {
1614              $DB->rollback();
1615  
1616              return false;
1617          }
1618  
1619          $this->_exists = false;
1620  
1621          $DB->commit();
1622  
1623          return true;
1624      }
1625  
1626  
1627      /**
1628       * Change file permissions on disk.
1629       *
1630       * @access public
1631       * @param string chmod (octal three-digit-format, eg '777'), uses {@link $Settings} for NULL
1632       *                    (fm_default_chmod_dir, fm_default_chmod_file)
1633       * @return mixed new permissions on success (octal format), false on failure
1634       */
1635  	function chmod( $chmod = NULL )
1636      {
1637          if( $chmod === NULL )
1638          {
1639              global $Settings;
1640  
1641              $chmod = $this->is_dir()
1642                  ? $Settings->get( 'fm_default_chmod_dir' )
1643                  : $Settings->get( 'fm_default_chmod_file' );
1644          }
1645  
1646          if( @chmod( $this->_adfp_full_path, octdec( $chmod ) ) )
1647          {
1648              clearstatcache();
1649              // update current entry
1650              $this->_perms = fileperms( $this->_adfp_full_path );
1651  
1652              return $this->_perms;
1653          }
1654          else
1655          {
1656              return false;
1657          }
1658      }
1659  
1660  
1661      /**
1662       * Insert object into DB based on previously recorded changes
1663       *
1664       * @return boolean true on success, false on failure
1665       */
1666  	function dbinsert( )
1667      {
1668          global $Debuglog;
1669  
1670          if( $this->meta == 'unknown' )
1671          {
1672              debug_die( 'cannot insert File if meta data has not been checked before' );
1673          }
1674  
1675          if( ($this->ID != 0) || ($this->meta != 'notfound') )
1676          {
1677              debug_die( 'Existing file object cannot be inserted!' );
1678          }
1679  
1680          $Debuglog->add( 'Inserting meta data for new file into db', 'files' );
1681  
1682          // Let's make sure the bare minimum gets saved to DB:
1683          $this->set_param( 'root_type', 'string', $this->_FileRoot->type );
1684          $this->set_param( 'root_ID', 'number', $this->_FileRoot->in_type_ID );
1685          $this->set_param( 'path', 'string', $this->_rdfp_rel_path );
1686          if( ! $this->is_dir() )
1687          { // create hash value only for files but not for folders
1688              $this->set_param( 'hash', 'string', md5_file( $this->get_full_path() ) );
1689          }
1690  
1691          // Let parent do the insert:
1692          $r = parent::dbinsert();
1693  
1694          // We can now consider the meta data has been loaded:
1695          $this->meta  = 'loaded';
1696  
1697          return $r;
1698      }
1699  
1700  
1701      /**
1702       * Update the DB based on previously recorded changes
1703       *
1704       * @return boolean true on success, false on failure / no changes
1705       */
1706  	function dbupdate( )
1707      {
1708          if( $this->meta == 'unknown' )
1709          {
1710              debug_die( 'cannot update File if meta data has not been checked before' );
1711          }
1712  
1713          global $DB;
1714  
1715          $DB->begin();
1716  
1717          // Let parent do the update:
1718          if( ( $r = parent::dbupdate() ) !== false )
1719          {
1720              // Update field 'last_touched_ts' of each item that has a link with this edited file
1721              $LinkCache = & get_LinkCache();
1722              $links = $LinkCache->get_by_file_ID( $this->ID );
1723              foreach( $links as $Link )
1724              {
1725                  $LinkOwner = & $Link->get_LinkOwner();
1726                  if( $LinkOwner != NULL )
1727                  {
1728                      $LinkOwner->item_update_last_touched_date();
1729                  }
1730              }
1731  
1732              $DB->commit();
1733          }
1734          else
1735          {
1736              $DB->rollback();
1737          }
1738  
1739          return $r;
1740      }
1741  
1742  
1743      /**
1744       * Get URL to view the file (either with viewer of with browser, etc...)
1745       */
1746  	function get_view_url( $always_open_dirs_in_fm = true )
1747      {
1748          global $htsrv_url, $public_access_to_media;
1749  
1750          // Get root code
1751          $root_ID = $this->_FileRoot->ID;
1752  
1753          if( $this->is_dir() )
1754          { // Directory
1755              if( $always_open_dirs_in_fm || ! $public_access_to_media )
1756              { // open the dir in the filemanager:
1757                  // fp>> Note: we MUST NOT clear mode, especially when mode=upload, or else the IMG button disappears when entering a subdir
1758                  return regenerate_url( 'root,path', 'root='.$root_ID.'&amp;path='.$this->get_rdfs_rel_path() );
1759              }
1760              else
1761              { // Public access: direct link to folder:
1762                  return $this->get_url();
1763              }
1764          }
1765          else
1766          { // File
1767              $Filetype = & $this->get_Filetype();
1768              if( !isset( $Filetype->viewtype ) )
1769              {
1770                  return NULL;
1771              }
1772              switch( $Filetype->viewtype )
1773              {
1774                  case 'image':
1775                      return  $htsrv_url.'viewfile.php?root='.$root_ID.'&amp;path='.$this->_rdfp_rel_path.'&amp;viewtype=image';
1776  
1777                  case 'text':
1778                      return $htsrv_url.'viewfile.php?root='.$root_ID.'&amp;path='.$this->_rdfp_rel_path.'&amp;viewtype=text';
1779  
1780                  case 'download':     // will NOT open a popup and will insert a Content-disposition: attachment; header
1781                      return $this->get_getfile_url();
1782  
1783                  case 'browser':        // will open a popup
1784                  case 'external':  // will NOT open a popup
1785                  default:
1786                      return $this->get_url();
1787              }
1788          }
1789      }
1790  
1791  
1792      /**
1793       * Get Link to view the file (either with viewer of with browser, etc...)
1794       *
1795       * @param string|NULL Text of the link
1796       * @param string|NULL Title of the link
1797       * @param string|NULL Text when user has no access for this file
1798       * @param string Format for text of the link: $text$
1799       * @param string Class name of the link
1800       * @return string Link tag
1801       */
1802  	function get_view_link( $text = NULL, $title = NULL, $no_access_text = NULL, $format = '$text$', $class = '' )
1803      {
1804          if( is_null( $text ) )
1805          { // Use file root+relpath+name by default
1806              $text = $this->get_root_and_rel_path();
1807          }
1808  
1809          if( is_null( $title ) )
1810          { // Default link title
1811              $this->load_meta();
1812              $title = $this->title;
1813          }
1814  
1815          if( is_null( $no_access_text ) )
1816          { // Default text when no access:
1817              $no_access_text = $text;
1818          }
1819  
1820          // Get the URL for viewing the file/dir:
1821          $url = $this->get_view_url( false );
1822  
1823          if( empty( $url ) )
1824          { // Display this text when current user has no access
1825              return $no_access_text;
1826          }
1827  
1828          // Replace a mask in the link text
1829          $text = str_replace( '$text$', $text, $format );
1830  
1831          // Init an attribute for class
1832          $class_attr = empty( $class ) ? '' : ' class="'.$class.'"';
1833  
1834          $Filetype = & $this->get_Filetype();
1835          if( $Filetype && in_array( $Filetype->viewtype, array( 'external', 'download' ) ) )
1836          { // Link to open in the curent window
1837              return '<a href="'.$url.'" title="'.$title.'"'.$class_attr.'>'.$text.'</a>';
1838          }
1839          else
1840          { // Link to open in a new window
1841              $target = 'evo_fm_'.$this->get_md5_ID();
1842  
1843              // onclick: we unset target attrib and return the return value of pop_up_window() to make the browser not follow the regular href link (at least FF 1.5 needs the target reset)
1844              return '<a href="'.$url.'" target="'.$target.'"
1845                  title="'.T_('Open in a new window').'" onclick="'
1846                  ."this.target = ''; return pop_up_window( '$url', '$target', "
1847                  .(( $width = $this->get_image_size( 'width' ) ) ? ( $width + 100 ) : 750 ).', '
1848                  .(( $height = $this->get_image_size( 'height' ) ) ? ( $height + 150 ) : 550 ).' )"'
1849                  .$class_attr.'>'
1850                  .$text.'</a>';
1851          }
1852      }
1853  
1854  
1855      /**
1856       * Get link to edit linked file.
1857       *
1858       * @param string link type ( item, comment )
1859       * @param integer ID of the object to link to => will open the FM in link mode
1860       * @param string link text
1861       * @param string link title
1862       * @param string text to display if access denied
1863       * @param string page url for the edit action
1864       */
1865  	function get_linkedit_link( $link_type = NULL, $link_obj_ID = NULL, $text = NULL, $title = NULL, $no_access_text = NULL,
1866                                              $actionurl = '#', $target = '' )
1867      {
1868          global $dispatcher;
1869  
1870          if( $actionurl == '#' )
1871          {
1872              $actionurl = $dispatcher.'?ctrl=files';
1873          }
1874  
1875          if( is_null( $text ) )
1876          {    // Use file root+relpath+name by default
1877              $text = $this->get_root_and_rel_path();
1878          }
1879  
1880          if( is_null( $title ) )
1881          {    // Default link title
1882              $this->load_meta();
1883              $title = $this->title;
1884          }
1885  
1886          if( is_null( $no_access_text ) )
1887          {    // Default text when no access:
1888              $no_access_text = $text;
1889          }
1890  
1891          $url = $this->get_linkedit_url( $link_type, $link_obj_ID, $actionurl );
1892  
1893          if( !empty($target) )
1894          {
1895              $target = ' target="'.$target.'"';
1896          }
1897  
1898          return '<a href="'.$url.'" title="'.$title.'"'.$target.'>'.$text.'</a>';
1899      }
1900  
1901  
1902      /**
1903       * Get link edit url for a link object
1904       *
1905       * @param string link type ( item, comment )
1906       * @param integer ID of link object to link to => will open the FM in link mode
1907       * @return string
1908       */
1909  	function get_linkedit_url( $link_type = NULL, $link_obj_ID = NULL, $actionurl = '#' )
1910      {
1911          global $dispatcher;
1912  
1913          if( $actionurl == '#' )
1914          {
1915              $actionurl = $dispatcher.'?ctrl=files';
1916          }
1917  
1918          if( $this->is_dir() )
1919          {
1920              $rdfp_path = $this->_rdfp_rel_path;
1921          }
1922          else
1923          {
1924              $rdfp_path = dirname( $this->_rdfp_rel_path );
1925          }
1926  
1927          $url_params = 'root='.$this->_FileRoot->ID.'&amp;path='.$rdfp_path.'/';
1928  
1929          if( ! is_null($link_obj_ID) )
1930          {    // We want to open the filemanager in link mode:
1931              $url_params .= '&amp;fm_mode=link_object&amp;link_type='.$link_type.'&amp;link_object_ID='.$link_obj_ID;
1932          }
1933  
1934          // Add param to make the file list highlight this (via JS).
1935          $url_params .= '&amp;fm_highlight='.rawurlencode($this->_name);
1936  
1937          $url = url_add_param( $actionurl, $url_params );
1938  
1939          return $url;
1940      }
1941  
1942  
1943      /**
1944       * Get the thumbnail URL for this file
1945       *
1946       * @param string
1947       */
1948  	function get_thumb_url( $size_name = 'fit-80x80', $glue = '&amp;' )
1949      {
1950          global $public_access_to_media, $htsrv_url;
1951  
1952          if( ! $this->is_image() )
1953          { // Not an image
1954              debug_die( 'Can only thumb images');
1955          }
1956  
1957          if( $public_access_to_media )
1958          {
1959              $af_thumb_path = $this->get_af_thumb_path( $size_name, NULL, false );
1960              if( $af_thumb_path[0] != '!' )
1961              { // If the thumbnail was already cached, we could publicly access it:
1962                  if( @is_file( $af_thumb_path ) )
1963                  {    // The thumb IS already in cache! :)
1964                      // Let's point directly into the cache:
1965                      global $Settings;
1966                      $url = $this->_FileRoot->ads_url.dirname($this->_rdfp_rel_path).'/'.$Settings->get( 'evocache_foldername' ).'/'.$this->_name.'/'.$size_name.'.'.$this->get_ext().'?mtime='.$this->get_lastmod_ts();
1967                      $url = str_replace( '\/', '', $url ); // Fix incorrect path
1968                      return $url;
1969                  }
1970              }
1971          }
1972  
1973          // No thumbnail available (at least publicly), we need to go through getfile.php!
1974          $url = $this->get_getfile_url($glue).$glue.'size='.$size_name;
1975  
1976          return $url;
1977      }
1978  
1979  
1980      /**
1981       * Get the URL to access a file through getfile.php.
1982       * @return string
1983       */
1984  	function get_getfile_url( $glue = '&amp;' )
1985      {
1986          global $htsrv_url;
1987          return $htsrv_url.'getfile.php/'
1988              // This is for clean 'save as':
1989              .rawurlencode( $this->_name )
1990              // This is for locating the file:
1991              .'?root='.$this->_FileRoot->ID.$glue.'path='.$this->_rdfp_rel_path
1992              .$glue.'mtime='.$this->get_lastmod_ts(); // TODO: dh> use salt here?!
1993      }
1994  
1995  
1996      /**
1997       * Generate the IMG THUMBNAIL tag with all the alt & title if available.
1998       * @return string
1999       */
2000  	function get_thumb_imgtag( $size_name = 'fit-80x80', $class = '', $align = '', $title = '' )
2001      {
2002          global $use_strict;
2003  
2004          if( ! $this->is_image() )
2005          { // Not an image
2006              return '';
2007          }
2008  
2009          $img_attribs = $this->get_img_attribs($size_name, $title);
2010          // pre_dump( $img_attribs );
2011  
2012          if( $class )
2013          { // add class
2014              $img_attribs['class'] = $class;
2015          }
2016  
2017          if( !$use_strict && $align )
2018          { // add align
2019              $img_attribs['align'] = $align;
2020          }
2021  
2022          return '<img'.get_field_attribs_as_string($img_attribs).' />';
2023      }
2024  
2025  
2026      /**
2027       * Returns an array of things like:
2028       * - src
2029       * - title
2030       * - alt
2031       * - width
2032       * - height
2033       *
2034       * @param string what size do we want src to link to, can be "original" or a thumnbail size
2035       * @param string
2036       * @param string
2037       * @return array List of HTML attributes for the image.
2038       */
2039  	function get_img_attribs( $size_name = 'fit-80x80', $title = NULL, $alt = NULL )
2040      {
2041          $img_attribs = array(
2042                  'title' => isset($title) ? $title : $this->get('title'),
2043                  'alt'   => isset($alt) ? $alt : $this->get('alt'),
2044              );
2045  
2046          if( ! isset($img_attribs['alt']) )
2047          { // use title for alt, too
2048              $img_attribs['alt'] = $img_attribs['title'];
2049          }
2050          if( ! isset($img_attribs['alt']) )
2051          { // always use empty alt
2052              $img_attribs['alt'] = '';
2053          }
2054  
2055          if( $size_name == 'original' )
2056          {    // We want src to link to the original file
2057              $img_attribs['src'] = $this->get_url();
2058              if( ( $size_arr = $this->get_image_size('widthheight_assoc') ) )
2059              {
2060                  $img_attribs += $size_arr;
2061              }
2062          }
2063          else
2064          { // We want src to link to a thumbnail
2065              $img_attribs['src'] = $this->get_thumb_url( $size_name, '&' );
2066              $thumb_path = $this->get_af_thumb_path($size_name, NULL, true);
2067              if( substr($thumb_path, 0, 1) != '!'
2068                  && ( $size_arr = imgsize($thumb_path, 'widthheight_assoc') ) )
2069              { // no error, add width and height attribs
2070                  $img_attribs += $size_arr;
2071              }
2072          }
2073  
2074          return $img_attribs;
2075      }
2076  
2077  
2078      /**
2079       * Displays a preview thumbnail which is clickable and opens a view popup
2080       *
2081       * @param string what do do with files that are not images? 'fulltype'
2082       * @param boolean TRUE - to init colorbox plugin for images
2083       * @return string HTML to display
2084       */
2085  	function get_preview_thumb( $format_for_non_images = '', $preview_image = false )
2086      {
2087          if( $this->is_image() )
2088          {    // Ok, it's an image:
2089              $type = $this->get_type();
2090              $img_attribs = $this->get_img_attribs( 'fit-80x80', $type, $type );
2091              $img = '<img'.get_field_attribs_as_string( $img_attribs ).' />';
2092  
2093              if( $preview_image )
2094              {    // Create link to preview image by colorbox plugin
2095                  $link = '<a href="'.$this->get_url().'" rel="lightbox" id="f'.$this->ID.'">'.$img.'</a>';
2096              }
2097              else
2098              {    // Get link to view the file (fallback to no view link - just the img):
2099                  $link = $this->get_view_link( $img );
2100              }
2101  
2102              if( ! $link )
2103              {    // no view link available:
2104                  $link = $img;
2105              }
2106  
2107              return $link;
2108          }
2109  
2110          // Not an image...
2111          switch( $format_for_non_images )
2112          {
2113              case 'fulltype':
2114                  // Full: Icon + File type:
2115                  return $this->get_view_link( $this->get_icon() ).' '.$this->get_type();
2116                  break;
2117          }
2118  
2119          return '';
2120      }
2121  
2122  
2123      /**
2124       * Get the full path to the thumbnail cache for this file.
2125       *
2126       * ads = Absolute Directory Slash
2127       *
2128       * @param boolean shall we create the dir if it doesn't exist?
2129       * @return string absolute path or !error
2130       */
2131  	function get_ads_evocache( $create_if_needed = false )
2132      {
2133          global $Settings;
2134          if( strpos( $this->_dir, '/'.$Settings->get( 'evocache_foldername' ).'/' ) !== false )
2135          {    // We are already in an evocache folder: refuse to go further!
2136              return '!Recursive caching not allowed';
2137          }
2138  
2139          $adp_evocache = $this->_dir.$Settings->get( 'evocache_foldername' ).'/'.$this->_name;
2140  
2141          if( $create_if_needed && !is_dir( $adp_evocache ) )
2142          {    // Create the directory:
2143              if( ! mkdir_r( $adp_evocache ) )
2144              {    // Could not create
2145                  return '!'.$Settings->get( 'evocache_foldername' ).' folder read/write error! Check filesystem permissions.';
2146              }
2147          }
2148  
2149          return $adp_evocache.'/';
2150      }
2151  
2152  
2153      /**
2154       * Delete cache for a file
2155       */
2156  	function rm_cache()
2157      {
2158          global $Messages, $Settings;
2159  
2160          // Remove cached elts for teh current file:
2161          $ads_filecache = $this->get_ads_evocache( false );
2162          if( $ads_filecache[0] == '!' )
2163          {
2164              // This creates unwanted noise
2165              // $Messages->add( 'Cannot remove '.$Settings->get( 'evocache_foldername' ).' for file. - '.$ads_filecache, 'error' );
2166          }
2167          else
2168          {
2169              rmdir_r( $ads_filecache );
2170  
2171              // In case cache is now empty, delete the folder:
2172              $adp_evocache = $this->_dir.$Settings->get( 'evocache_foldername' );
2173              @rmdir( $adp_evocache );
2174          }
2175      }
2176  
2177  
2178      /**
2179       * Get the full path to the thumbnail for this file.
2180       *
2181       * af = Absolute File
2182       *
2183       * @param string size name (e.g. "fit-80x80")
2184       * @param string mimetype of thumbnail (NULL if we're ready to take whatever is available)
2185       * @param boolean shall we create the dir if it doesn't exist?
2186       * @return string absolute filename or !error
2187       */
2188  	function get_af_thumb_path( $size_name, $thumb_mimetype = NULL, $create_evocache_if_needed = false )
2189      {
2190          $Filetype = & $this->get_Filetype();
2191          if( isset($Filetype) )
2192          {
2193              if( empty($thumb_mimetype) )
2194              {
2195                  $thumb_mimetype = $Filetype->mimetype;
2196              }
2197              elseif( $thumb_mimetype != $Filetype->mimetype )
2198              {
2199                  debug_die( 'Not supported. For now, thumbnails have to have same mime type as their parent file.' );
2200                  // TODO: extract prefered extension of filetypes config
2201              }
2202          }
2203          elseif( !empty($thumb_mimetype) )
2204          {
2205              debug_die( 'Not supported. Can\'t generate thumbnail for unknow parent file.' );
2206          }
2207  
2208          // Get the filename of the thumbnail
2209          $ads_evocache = $this->get_ads_evocache( $create_evocache_if_needed );
2210          if( $ads_evocache[0] != '!' )
2211          {    // Not an error
2212              return $ads_evocache.$size_name.'.'.$this->get_ext();
2213          }
2214  
2215          // error
2216          return $ads_evocache;
2217      }
2218  
2219  
2220      /**
2221       * Save thumbnail for file
2222       *
2223       * @param resource
2224       * @param string size name
2225       * @param string mimetype of thumbnail
2226       * @param integer JPEG image quality
2227       */
2228  	function save_thumb_to_cache( $thumb_imh, $size_name, $thumb_mimetype, $thumb_quality = 90 )
2229      {
2230          global $Plugins;
2231  
2232          $Plugins->trigger_event( 'BeforeThumbCreate', array(
2233                'imh' => & $thumb_imh,
2234                'size' => & $size_name,
2235                'mimetype' => & $thumb_mimetype,
2236                'quality' => & $thumb_quality,
2237                'File' => & $this,
2238                'root_type' => $this->_FileRoot->type,
2239                'root_type_ID' => $this->_FileRoot->in_type_ID,
2240            ) );
2241  
2242          $af_thumb_path = $this->get_af_thumb_path( $size_name, $thumb_mimetype, true );
2243          if( $af_thumb_path[0] != '!' )
2244          {    // We obtained a path for the thumbnail to be saved:
2245              return save_image( $thumb_imh, $af_thumb_path, $thumb_mimetype, $thumb_quality );
2246          }
2247  
2248          return $af_thumb_path;    // !Error code
2249      }
2250  
2251  
2252      /**
2253       * Output previously saved thumbnail for file
2254       *
2255       * @param string size name
2256       * @param string mimetype of thumbnail
2257       * @param int Modified time of the file (should have been provided as GET param)
2258       * @return mixed NULL on success, otherwise string ("!Error code")
2259       */
2260  	function output_cached_thumb( $size_name, $thumb_mimetype, $mtime = NULL )
2261      {
2262          global $servertimenow;
2263  
2264          $af_thumb_path = $this->get_af_thumb_path( $size_name, $thumb_mimetype, false );
2265          //pre_dump($af_thumb_path);
2266          if( $af_thumb_path[0] != '!' )
2267          {    // We obtained a path for the thumbnail to be saved:
2268              if( ! file_exists( $af_thumb_path ) )
2269              {    // The thumbnail was not found...
2270                  global $Settings;
2271                  return '!Thumbnail not found in'.$Settings->get( 'evocache_foldername' ); // WARNING: exact wording match on return
2272              }
2273  
2274              if( ! is_readable( $af_thumb_path ) )
2275              {
2276                  return '!Thumbnail read error! Check filesystem permissions.';
2277              }
2278  
2279              header('Content-Type: '.$thumb_mimetype );
2280              header('Content-Length: '.filesize( $af_thumb_path ) );
2281              header('Last-Modified: ' . date("r",$this->get_lastmod_ts()));
2282  
2283              // dh> if( $mtime && $mtime == $this->get_lastmod_ts() )
2284              // fp> I don't think mtime changes anything to the cacheability of the data
2285              //header_noexpire(); // Static image
2286              // attila> set expires on 30 days
2287              header('Expires: ' . date("r", $servertimenow + 2592000/* 60*60*24*30 = 30 days */ ));
2288  
2289              // Output the content of the file
2290              readfile( $af_thumb_path );
2291              return NULL;
2292          }
2293  
2294          return $af_thumb_path;    // !Error code
2295      }
2296  
2297  
2298      /**
2299       * @param LinkOwner
2300       * @param integer Order
2301       * @param string Position
2302       */
2303  	function link_to_Object( & $LinkOwner, $set_order = 1, $set_position = NULL )
2304      {
2305          global $DB;
2306  
2307          // Automatically determine default position.
2308          // First image becomes "teaser", otherwise "aftermore".
2309  
2310          $order = $set_order;
2311          $position = is_null( $set_position ) ? $LinkOwner->get_default_position( $this->is_image() ) : $set_position;
2312          $existing_Links = & $LinkOwner->get_Links();
2313  
2314          // Find highest order
2315          foreach( $existing_Links as $loop_Link )
2316          {
2317              if( $loop_Link->file_ID == $this->ID )
2318              { // The file is already linked to this owner
2319                  return;
2320              }
2321              $existing_order = $loop_Link->get('order');
2322              if( $existing_order >= $order )
2323                  $order = $existing_order+1;
2324          }
2325  
2326          $DB->begin();
2327  
2328          // Load meta data AND MAKE SURE IT IS CREATED IN DB:
2329          $this->load_meta( true );
2330  
2331          $DB->commit();
2332  
2333          // Let's make the link!
2334          $LinkOwner->add_link( $this->ID, $position, $order );
2335      }
2336  
2337  
2338      /**
2339       * Get link to restricted object
2340       *
2341       * Used when try to delete a file, which is attached to a post, or to a user
2342       *
2343       * @param array restriction
2344       * @return string message with links to objects
2345       */
2346  	function get_restriction_link( $restriction )
2347      {
2348          global $DB, $admin_url;
2349  
2350          switch( $restriction['table'] )
2351          { // can be restricted to different tables
2352              case 'T_links':
2353                  switch( $restriction['field'] )
2354                  {
2355                      case 'link_itm_ID': // Items
2356                          $object_ID = 'post_ID';            // related table object ID
2357                          $object_name = 'post_title';    // related table object name
2358  
2359                          // link to object
2360                          $link = '<a href="'.$admin_url.'?ctrl=items&action=edit&p=%d">%s</a>';
2361                          $object_query = 'SELECT post_ID, post_title FROM T_items__item'
2362                                              .' WHERE post_ID IN'
2363                                              .' (SELECT '.$restriction['field']
2364                                              .' FROM '.$restriction['table']
2365                                              .' WHERE '.$restriction['fk'].' = '.$this->ID.')';
2366                          break;
2367  
2368                      case 'link_cmt_ID': // Comments
2369                          $object_ID = 'comment_ID';            // related table object ID
2370                          $object_name = 'comment_ID';    // related table object name
2371  
2372                          // link to object
2373                          $link = '<a href="'.$admin_url.'?ctrl=comments&action=edit&comment_ID=%d">'.T_('Comment ').'#%s</a>';
2374                          $object_query = 'SELECT comment_ID, comment_ID FROM T_comments'
2375                                              .' WHERE comment_ID IN'
2376                                              .' (SELECT '.$restriction['field']
2377                                              .' FROM '.$restriction['table']
2378                                              .' WHERE '.$restriction['fk'].' = '.$this->ID.')';
2379                          break;
2380  
2381                      default:
2382                          // not defined restriction
2383                          debug_die ( 'unhandled restriction field:' . htmlspecialchars ( $restriction['table'].' - '.$restriction['field'] ) );
2384                  }
2385              break;
2386  
2387              case 'T_users': // Users
2388                  $object_ID = 'user_ID';            // related table object ID
2389                  $object_name = 'user_login';    // related table object name
2390  
2391                  // link to object
2392                  $link = '<a href="'.$admin_url.'?ctrl=user&user_tab=avatar&user_ID=%d">%s</a>';
2393                  $object_query = 'SELECT user_ID, user_login FROM T_users'
2394                                      .' WHERE '.$restriction['fk'].' = '.$this->ID;
2395                  break;
2396  
2397              default:
2398                  // not defined restriction
2399                  debug_die ( 'unhandled restriction:' . htmlspecialchars ( $restriction['table'] ) );
2400          }
2401  
2402          $result_link = '';
2403          $query_result = $DB->get_results( $object_query );
2404          foreach( $query_result as $row )
2405          { // create links for each related object
2406              $result_link .= '<br/>'.sprintf( $link, $row->$object_ID, $row->$object_name );
2407          }
2408  
2409          if( ( $count = count($query_result) ) > 0 )
2410          { // there are restrictions
2411              return sprintf( $restriction['msg'].$result_link, $count );
2412          }
2413          // no restriction
2414          return '';
2415      }
2416  
2417  
2418      /**
2419       * Get icon with link to go to file browser where this file is highlighted
2420       *
2421       * @return string Link
2422       */
2423  	function get_target_icon()
2424      {
2425          global $current_User;
2426  
2427          $r = '';
2428          if( $current_User->check_perm( 'files', 'view', false, $this->get_FileRoot() ) )
2429          {    // Check permission
2430              if( $this->is_dir() )
2431              {    // Dir
2432                  $title = T_('Locate this directory!');
2433              }
2434              else
2435              {    // File
2436                  $title = T_('Locate this file!');
2437              }
2438              $url = $this->get_linkedit_url();
2439              $r .= '<a href="'.$url.'" title="'.$title.'">'.get_icon( 'locate', 'imgtag', array( 'title' => $title ) ).'</a> ';
2440          }
2441  
2442          return $r;
2443      }
2444  }
2445  
2446  ?>

title

Description

title

Description

title

Description

title

title

Body