Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 400 and 401] [Versions 401 and 402] [Versions 401 and 403]

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  
  18  /**
  19   * Base for all file browsing classes.
  20   *
  21   * @package    core_files
  22   * @copyright  2008 Petr Skoda (http://skodak.org)
  23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  
  26  defined('MOODLE_INTERNAL') || die();
  27  
  28  /**
  29   * Base class for things in the tree navigated by {@link file_browser}.
  30   *
  31   * @package    core_files
  32   * @copyright  2008 Petr Skoda (http://skodak.org)
  33   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  34   */
  35  abstract class file_info {
  36  
  37      /** @var stdClass File context */
  38      protected $context;
  39  
  40      /** @var file_browser File browser instance */
  41      protected $browser;
  42  
  43      /**
  44       * Constructor
  45       *
  46       * @param file_browser $browser file_browser instance
  47       * @param stdClass $context
  48       */
  49      public function __construct($browser, $context) {
  50          $this->browser = $browser;
  51          $this->context = $context;
  52      }
  53  
  54      /**
  55       * Returns list of standard virtual file/directory identification.
  56       * The difference from stored_file parameters is that null values
  57       * are allowed in all fields
  58       *
  59       * @return array with keys contextid, component, filearea, itemid, filepath and filename
  60       */
  61      public function get_params() {
  62          return array('contextid' => $this->context->id,
  63                       'component' => null,
  64                       'filearea'  => null,
  65                       'itemid'    => null,
  66                       'filepath'  => null,
  67                       'filename'  => null);
  68      }
  69  
  70      /**
  71       * Returns localised visible name.
  72       *
  73       * @return string
  74       */
  75      public abstract function get_visible_name();
  76  
  77      /**
  78       * Whether or not this is a directory
  79       *
  80       * @return bool
  81       */
  82      public abstract function is_directory();
  83  
  84      /**
  85       * Returns list of children.
  86       *
  87       * @return array of file_info instances
  88       */
  89      public abstract function get_children();
  90  
  91      /**
  92       * Builds SQL sub query (WHERE clause) for selecting files with the specified extensions
  93       *
  94       * If $extensions == '*' (any file), the result is array('', array())
  95       * otherwise the result is something like array('AND filename ...', array(...))
  96       *
  97       * @param string|array $extensions - either '*' or array of lowercase extensions, i.e. array('.gif','.jpg')
  98       * @param string $prefix prefix for DB table files in the query (empty by default)
  99       * @return array of two elements: $sql - sql where clause and $params - array of parameters
 100       */
 101      protected function build_search_files_sql($extensions, $prefix = null) {
 102          global $DB;
 103          if ($prefix && strlen($prefix)) {
 104              $prefix = $prefix . '.';
 105          } else {
 106              $prefix = '';
 107          }
 108          $sql = '';
 109          $params = array();
 110          if (is_array($extensions) && !in_array('*', $extensions)) {
 111              $likes = array();
 112              $cnt = 0;
 113              foreach ($extensions as $ext) {
 114                  $cnt++;
 115                  $likes[] = $DB->sql_like($prefix.'filename', ':filename'.$cnt, false);
 116                  $params['filename'.$cnt] = '%'.$ext;
 117              }
 118              $sql .= ' AND (' . join(' OR ', $likes) . ')';
 119          }
 120          return array($sql, $params);
 121       }
 122  
 123      /**
 124       * Returns list of children which are either files matching the specified extensions
 125       * or folders that contain at least one such file.
 126       *
 127       * It is recommended to overwrite this function so it uses a proper SQL
 128       * query and does not create unnecessary file_info objects (might require a lot of time
 129       * and memory usage on big sites).
 130       *
 131       * @param string|array $extensions, either '*' or array of lowercase extensions, i.e. array('.gif','.jpg')
 132       * @return array of file_info instances
 133       */
 134      public function get_non_empty_children($extensions = '*') {
 135          $list = $this->get_children();
 136          $nonemptylist = array();
 137          foreach ($list as $fileinfo) {
 138              if ($fileinfo->is_directory()) {
 139                  if ($fileinfo->count_non_empty_children($extensions)) {
 140                      $nonemptylist[] = $fileinfo;
 141                  }
 142              } else if ($extensions === '*') {
 143                  $nonemptylist[] = $fileinfo;
 144              } else {
 145                  $filename = $fileinfo->get_visible_name();
 146                  $extension = core_text::strtolower(pathinfo($filename, PATHINFO_EXTENSION));
 147                  if (!empty($extension) && in_array('.' . $extension, $extensions)) {
 148                      $nonemptylist[] = $fileinfo;
 149                  }
 150              }
 151          }
 152          return $nonemptylist;
 153      }
 154  
 155      /**
 156       * Returns the number of children which are either files matching the specified extensions
 157       * or folders containing at least one such file.
 158       *
 159       * We usually don't need the exact number of non empty children if it is >=2 (see param $limit)
 160       * This function is used by repository_local to evaluate if the folder is empty. But
 161       * it also can be used to check if folder has only one subfolder because in some cases
 162       * this subfolder can be skipped.
 163       *
 164       * It is strongly recommended to overwrite this function so it uses a proper SQL
 165       * query and does not create file_info objects (later might require a lot of time
 166       * and memory usage on big sites).
 167       *
 168       * @param string|array $extensions, for example '*' or array('.gif','.jpg')
 169       * @param int $limit stop counting after at least $limit non-empty children are found
 170       * @return int
 171       */
 172      public function count_non_empty_children($extensions = '*', $limit = 1) {
 173          $list = $this->get_children();
 174          $cnt = 0;
 175          // first loop through files
 176          foreach ($list as $fileinfo) {
 177              if (!$fileinfo->is_directory()) {
 178                  if ($extensions !== '*') {
 179                      $filename = $fileinfo->get_visible_name();
 180                      $extension = core_text::strtolower(pathinfo($filename, PATHINFO_EXTENSION));
 181                      if (empty($extension) || !in_array('.' . $extension, $extensions)) {
 182                          continue;
 183                      }
 184                  }
 185                  if ((++$cnt) >= $limit) {
 186                      return $cnt;
 187                  }
 188              }
 189          }
 190          // now loop through directories
 191          foreach ($list as $fileinfo) {
 192              if ($fileinfo->is_directory() && $fileinfo->count_non_empty_children($extensions)) {
 193                  if ((++$cnt) >= $limit) {
 194                      return $cnt;
 195                  }
 196              }
 197          }
 198          return $cnt;
 199      }
 200  
 201      /**
 202       * Returns parent file_info instance
 203       *
 204       * @return file_info or null for root
 205       */
 206      public abstract function get_parent();
 207  
 208      /**
 209       * Returns array of url encoded params.
 210       *
 211       * @return array with numeric keys
 212       */
 213      public function get_params_rawencoded() {
 214          $params = $this->get_params();
 215          $encoded = array();
 216          $encoded[] = 'contextid=' . $params['contextid'];
 217          $encoded[] = 'component=' . $params['component'];
 218          $encoded[] = 'filearea=' . $params['filearea'];
 219          $encoded[] = 'itemid=' . (is_null($params['itemid']) ? -1 : $params['itemid']);
 220          $encoded[] = 'filepath=' . (is_null($params['filepath']) ? '' : rawurlencode($params['filepath']));
 221          $encoded[] = 'filename=' . ((is_null($params['filename']) or $params['filename'] === '.') ? '' : rawurlencode($params['filename']));
 222  
 223          return $encoded;
 224      }
 225  
 226      /**
 227       * Returns file download url
 228       *
 229       * @param bool $forcedownload whether or not force download
 230       * @param bool $https whether or not force https
 231       * @return string url
 232       */
 233      public function get_url($forcedownload=false, $https=false) {
 234          return null;
 235      }
 236  
 237      /**
 238       * Whether or not I can read content of this file or enter directory
 239       *
 240       * @return bool
 241       */
 242      public function is_readable() {
 243          return true;
 244      }
 245  
 246      /**
 247       * Whether or not new files or directories can be added
 248       *
 249       * @return bool
 250       */
 251      public function is_writable() {
 252          return true;
 253      }
 254  
 255      /**
 256       * Is this info area and is it "empty"? Are there any files in subfolders?
 257       *
 258       * This is used mostly in repositories to reduce the
 259       * number of empty folders. This method may be very slow,
 260       * use with care.
 261       *
 262       * @return bool
 263       */
 264      public function is_empty_area() {
 265          return false;
 266      }
 267  
 268      /**
 269       * Returns file size in bytes, null for directories
 270       *
 271       * @return int bytes or null if not known
 272       */
 273      public function get_filesize() {
 274          return null;
 275      }
 276  
 277      /**
 278       * Returns mimetype
 279       *
 280       * @return string mimetype or null if not known
 281       */
 282      public function get_mimetype() {
 283          return null;
 284      }
 285  
 286      /**
 287       * Returns time created unix timestamp if known
 288       *
 289       * @return int timestamp or null
 290       */
 291      public function get_timecreated() {
 292          return null;
 293      }
 294  
 295      /**
 296       * Returns time modified unix timestamp if known
 297       *
 298       * @return int timestamp or null
 299       */
 300      public function get_timemodified() {
 301          return null;
 302      }
 303  
 304      /**
 305       * Returns the license type of the file
 306       * @return string license short name or null
 307       */
 308      public function get_license() {
 309          return null;
 310      }
 311  
 312      /**
 313       * Returns the author name of the file
 314       *
 315       * @return string author name or null
 316       */
 317      public function get_author() {
 318          return null;
 319      }
 320  
 321      /**
 322       * Returns the source of the file
 323       *
 324       * @return string a source url or null
 325       */
 326      public function get_source() {
 327          return null;
 328      }
 329  
 330      /**
 331       * Returns the sort order of the file
 332       *
 333       * @return int
 334       */
 335      public function get_sortorder() {
 336          return 0;
 337      }
 338  
 339      /**
 340       * Whether or not this is a external resource
 341       *
 342       * @return bool
 343       */
 344      public function is_external_file() {
 345          return false;
 346      }
 347  
 348      /**
 349       * Returns file status flag.
 350       *
 351       * @return int 0 means file OK, anything else is a problem and file can not be used
 352       */
 353      public function get_status() {
 354          return 0;
 355      }
 356  
 357      /**
 358       * Returns the localised human-readable name of the file together with virtual path
 359       *
 360       * @see file_info_stored::get_readable_fullname()
 361       * @return string
 362       */
 363      public function get_readable_fullname() {
 364          return null;
 365      }
 366  
 367      /**
 368       * Create new directory, may throw exception - make sure
 369       * params are valid.
 370       *
 371       * @param string $newdirname name of new directory
 372       * @param int $userid id of author, default $USER->id
 373       * @return file_info new directory
 374       */
 375      public function create_directory($newdirname, $userid = NULL) {
 376          return null;
 377      }
 378  
 379      /**
 380       * Create new file from string - make sure
 381       * params are valid.
 382       *
 383       * @param string $newfilename name of new file
 384       * @param string $content of file
 385       * @param int $userid id of author, default $USER->id
 386       * @return file_info new file
 387       */
 388      public function create_file_from_string($newfilename, $content, $userid = NULL) {
 389          return null;
 390      }
 391  
 392      /**
 393       * Create new file from pathname - make sure
 394       * params are valid.
 395       *
 396       * @param string $newfilename name of new file
 397       * @param string $pathname location of file
 398       * @param int $userid id of author, default $USER->id
 399       * @return file_info new file
 400       */
 401      public function create_file_from_pathname($newfilename, $pathname, $userid = NULL) {
 402          return null;
 403      }
 404  
 405      /**
 406       * Create new file from stored file - make sure
 407       * params are valid.
 408       *
 409       * @param string $newfilename name of new file
 410       * @param int|stored_file $fid id or stored_file of file
 411       * @param int $userid id of author, default $USER->id
 412       * @return file_info new file
 413       */
 414      public function create_file_from_storedfile($newfilename, $fid, $userid = NULL) {
 415          return null;
 416      }
 417  
 418      /**
 419       * Delete file, make sure file is deletable first.
 420       *
 421       * @return bool success
 422       */
 423      public function delete() {
 424          return false;
 425      }
 426  
 427      /**
 428       * Copy content of this file to local storage, overriding current file if needed.
 429       *
 430       * @param array|stdClass $filerecord contains contextid, component, filearea,
 431       *    itemid, filepath, filename and optionally other attributes of the new file
 432       * @return bool success
 433       */
 434      public function copy_to_storage($filerecord) {
 435          return false;
 436      }
 437  
 438      /**
 439       * Copy content of this file to local storage, overriding current file if needed.
 440       *
 441       * @todo MDL-31068 implement move() rename() unzip() zip()
 442       * @param string $pathname real local full file name
 443       * @return boolean success
 444       */
 445      public function copy_to_pathname($pathname) {
 446          return false;
 447      }
 448  
 449  
 450  //TODO: following methods are not implemented yet ;-)
 451      //public abstract function move(location params);
 452      //public abstract function rename(new name);
 453      //public abstract function unzip(location params);
 454      //public abstract function zip(zip file, file info);
 455  }