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.
   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   * Utility class for browsing of stored files.
  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   * Represents an actual file or folder - a row in the file table 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  class file_info_stored extends file_info {
  36      /** @var stored_file|virtual_root_file stored_file or virtual_root_file instance */
  37      protected $lf;
  38      /** @var string the serving script */
  39      protected $urlbase;
  40      /** @var string the human readable name of this area */
  41      protected $topvisiblename;
  42      /** @var int|bool it's false if itemid is 0 and not included in URL */
  43      protected $itemidused;
  44      /** @var bool allow file reading */
  45      protected $readaccess;
  46      /** @var bool allow file write, delee */
  47      protected $writeaccess;
  48      /** @var string do not show links to parent context/area */
  49      protected $areaonly;
  50  
  51      /**
  52       * Constructor
  53       *
  54       * @param file_browser $browser file browser instance
  55       * @param stdClass $context context object
  56       * @param stored_file|virtual_root_file $storedfile stored_file instance
  57       * @param string $urlbase the serving script - usually the $CFG->wwwroot/.'pluginfile.php'
  58       * @param string $topvisiblename the human readable name of this area
  59       * @param int|bool $itemidused false if itemid  always 0 and not included in URL
  60       * @param bool $readaccess allow file reading
  61       * @param bool $writeaccess allow file write, delete
  62       * @param string $areaonly do not show links to parent context/area
  63       */
  64      public function __construct(file_browser $browser, $context, $storedfile, $urlbase, $topvisiblename, $itemidused, $readaccess, $writeaccess, $areaonly) {
  65          parent::__construct($browser, $context);
  66  
  67          $this->lf             = $storedfile;
  68          $this->urlbase        = $urlbase;
  69          $this->topvisiblename = $topvisiblename;
  70          $this->itemidused     = $itemidused;
  71          $this->readaccess     = $readaccess;
  72          $this->writeaccess    = $writeaccess;
  73          $this->areaonly       = $areaonly;
  74      }
  75  
  76      /**
  77       * Returns list of standard virtual file/directory identification.
  78       * The difference from stored_file parameters is that null values
  79       * are allowed in all fields
  80       *
  81       * @return array with keys contextid, component, filearea, itemid, filepath and filename
  82       */
  83      public function get_params() {
  84          return array('contextid'=>$this->context->id,
  85                       'component'=>$this->lf->get_component(),
  86                       'filearea' =>$this->lf->get_filearea(),
  87                       'itemid'   =>$this->lf->get_itemid(),
  88                       'filepath' =>$this->lf->get_filepath(),
  89                       'filename' =>$this->lf->get_filename());
  90      }
  91  
  92      /**
  93       * Returns localised visible name.
  94       *
  95       * @return string
  96       */
  97      public function get_visible_name() {
  98          $filename = $this->lf->get_filename();
  99          $filepath = $this->lf->get_filepath();
 100  
 101          if ($filename !== '.') {
 102              return $filename;
 103  
 104          } else {
 105              $dir = trim($filepath, '/');
 106              $dir = explode('/', $dir);
 107              $dir = array_pop($dir);
 108              if ($dir === '') {
 109                  return $this->topvisiblename;
 110              } else {
 111                  return $dir;
 112              }
 113          }
 114      }
 115  
 116      /**
 117       * Returns the localised human-readable name of the file together with virtual path
 118       *
 119       * @return string
 120       */
 121      public function get_readable_fullname() {
 122          global $CFG;
 123          // retrieve the readable path with all parents (excluding the top most 'System')
 124          $fpath = array();
 125          for ($parent = $this; $parent && $parent->get_parent(); $parent = $parent->get_parent()) {
 126              array_unshift($fpath, $parent->get_visible_name());
 127          }
 128  
 129          if ($this->lf->get_component() == 'user' && $this->lf->get_filearea() == 'private') {
 130              // use the special syntax for user private files - 'USERNAME Private files: PATH'
 131              $username = array_shift($fpath);
 132              array_shift($fpath); // get rid of "Private Files/" in the beginning of the path
 133              return get_string('privatefilesof', 'repository', $username). ': '. join('/', $fpath);
 134          } else {
 135              // for all other files (except user private files) return 'Server files: PATH'
 136  
 137              // first, get and cache the name of the repository_local (will be used as prefix for file names):
 138              static $replocalname = null;
 139              if ($replocalname === null) {
 140                  require_once($CFG->dirroot . "/repository/lib.php");
 141                  $instances = repository::get_instances(array('type' => 'local'));
 142                  if (count($instances)) {
 143                      $firstinstance = reset($instances);
 144                      $replocalname = $firstinstance->get_name();
 145                  } else if (get_string_manager()->string_exists('pluginname', 'repository_local')) {
 146                      $replocalname = get_string('pluginname', 'repository_local');
 147                  } else {
 148                      $replocalname = get_string('arearoot', 'repository');
 149                  }
 150              }
 151  
 152              return $replocalname. ': '. join('/', $fpath);
 153          }
 154      }
 155  
 156      /**
 157       * Returns file download url
 158       *
 159       * @param bool $forcedownload Whether or not force download
 160       * @param bool $https whether or not force https
 161       * @return string url
 162       */
 163      public function get_url($forcedownload=false, $https=false) {
 164          if (!$this->is_readable()) {
 165              return null;
 166          }
 167  
 168          if ($this->is_directory()) {
 169              return null;
 170          }
 171  
 172          $this->urlbase;
 173          $contextid = $this->lf->get_contextid();
 174          $component = $this->lf->get_component();
 175          $filearea  = $this->lf->get_filearea();
 176          $itemid    = $this->lf->get_itemid();
 177          $filepath  = $this->lf->get_filepath();
 178          $filename  = $this->lf->get_filename();
 179  
 180          if ($this->itemidused) {
 181              $path = '/'.$contextid.'/'.$component.'/'.$filearea.'/'.$itemid.$filepath.$filename;
 182          } else {
 183              $path = '/'.$contextid.'/'.$component.'/'.$filearea.$filepath.$filename;
 184          }
 185          return file_encode_url($this->urlbase, $path, $forcedownload, $https);
 186      }
 187  
 188      /**
 189       * Whether or not I can read content of this file or enter directory
 190       *
 191       * @return bool
 192       */
 193      public function is_readable() {
 194          return $this->readaccess;
 195      }
 196  
 197      /**
 198       * Whether or not new files or directories can be added
 199       *
 200       * @return bool
 201       */
 202      public function is_writable() {
 203          return $this->writeaccess;
 204      }
 205  
 206      /**
 207       * Whether or not this is an empty area
 208       *
 209       * @return bool
 210       */
 211      public function is_empty_area() {
 212          if ($this->lf->get_filepath() === '/' and $this->lf->get_filename() === '.') {
 213              // test the emptiness only in the top most level, it does not make sense at lower levels
 214              $fs = get_file_storage();
 215              return $fs->is_area_empty($this->lf->get_contextid(), $this->lf->get_component(), $this->lf->get_filearea(), $this->lf->get_itemid());
 216          } else {
 217              return false;
 218          }
 219      }
 220  
 221      /**
 222       * Returns file size in bytes, null for directories
 223       *
 224       * @return int bytes or null if not known
 225       */
 226      public function get_filesize() {
 227          return $this->lf->get_filesize();
 228      }
 229  
 230      /**
 231       * Returns width, height and mimetype of the stored image, or false
 232       *
 233       * @see stored_file::get_imageinfo()
 234       * @return array|false
 235       */
 236      public function get_imageinfo() {
 237          return $this->lf->get_imageinfo();
 238      }
 239  
 240      /**
 241       * Returns mimetype
 242       *
 243       * @return string mimetype or null if not known
 244       */
 245      public function get_mimetype() {
 246          return $this->lf->get_mimetype();
 247      }
 248  
 249      /**
 250       * Returns time created unix timestamp if known
 251       *
 252       * @return int timestamp or null
 253       */
 254      public function get_timecreated() {
 255          return $this->lf->get_timecreated();
 256      }
 257  
 258      /**
 259       * Returns time modified unix timestamp if known
 260       *
 261       * @return int timestamp or null
 262       */
 263      public function get_timemodified() {
 264          return $this->lf->get_timemodified();
 265      }
 266  
 267      /**
 268       * Whether or not this is a directory
 269       *
 270       * @return bool
 271       */
 272      public function is_directory() {
 273          return $this->lf->is_directory();
 274      }
 275  
 276      /**
 277       * Returns the license type of the file
 278       *
 279       * @return string license short name or null
 280       */
 281      public function get_license() {
 282          return $this->lf->get_license();
 283      }
 284  
 285      /**
 286       * Returns the author name of the file
 287       *
 288       * @return string author name or null
 289       */
 290      public function get_author() {
 291          return $this->lf->get_author();
 292      }
 293  
 294      /**
 295       * Returns the source of the file
 296       *
 297       * @return string a source url or null
 298       */
 299      public function get_source() {
 300          return $this->lf->get_source();
 301      }
 302  
 303      /**
 304       * Returns the sort order of the file
 305       *
 306       * @return int
 307       */
 308      public function get_sortorder() {
 309          return $this->lf->get_sortorder();
 310      }
 311  
 312      /**
 313       * Whether or not this is a external resource
 314       *
 315       * @return bool
 316       */
 317      public function is_external_file() {
 318          return $this->lf->is_external_file();
 319      }
 320  
 321      /**
 322       * Returns file status flag.
 323       *
 324       * @return int 0 means file OK, anything else is a problem and file can not be used
 325       */
 326      public function get_status() {
 327          return $this->lf->get_status();
 328      }
 329  
 330      /**
 331       * Returns list of children.
 332       *
 333       * @return array of file_info instances
 334       */
 335      public function get_children() {
 336          if (!$this->lf->is_directory()) {
 337              return array();
 338          }
 339  
 340          $result = array();
 341          $fs = get_file_storage();
 342  
 343          $storedfiles = $fs->get_directory_files($this->context->id, $this->lf->get_component(), $this->lf->get_filearea(), $this->lf->get_itemid(),
 344                                                  $this->lf->get_filepath(), false, true, "filepath, filename");
 345          foreach ($storedfiles as $file) {
 346              $result[] = new file_info_stored($this->browser, $this->context, $file, $this->urlbase, $this->topvisiblename,
 347                                               $this->itemidused, $this->readaccess, $this->writeaccess, false);
 348          }
 349  
 350          return $result;
 351      }
 352  
 353      /**
 354       * Returns list of children which are either files matching the specified extensions
 355       * or folders that contain at least one such file.
 356       *
 357       * @param string|array $extensions, either '*' or array of lowercase extensions, i.e. array('.gif','.jpg')
 358       * @return array of file_info instances
 359       */
 360      public function get_non_empty_children($extensions = '*') {
 361          $result = array();
 362          if (!$this->lf->is_directory()) {
 363              return $result;
 364          }
 365  
 366          $fs = get_file_storage();
 367  
 368          $storedfiles = $fs->get_directory_files($this->context->id, $this->lf->get_component(), $this->lf->get_filearea(), $this->lf->get_itemid(),
 369                                                  $this->lf->get_filepath(), false, true, "filepath, filename");
 370          foreach ($storedfiles as $file) {
 371              $extension = core_text::strtolower(pathinfo($file->get_filename(), PATHINFO_EXTENSION));
 372              if ($file->is_directory() || $extensions === '*' || (!empty($extension) && in_array('.'.$extension, $extensions))) {
 373                  $fileinfo = new file_info_stored($this->browser, $this->context, $file, $this->urlbase, $this->topvisiblename,
 374                                                   $this->itemidused, $this->readaccess, $this->writeaccess, false);
 375                  if (!$file->is_directory() || $fileinfo->count_non_empty_children($extensions)) {
 376                      $result[] = $fileinfo;
 377                  }
 378              }
 379          }
 380  
 381          return $result;
 382      }
 383  
 384      /**
 385       * Returns the number of children which are either files matching the specified extensions
 386       * or folders containing at least one such file.
 387       *
 388       * @param string|array $extensions, for example '*' or array('.gif','.jpg')
 389       * @param int $limit stop counting after at least $limit non-empty children are found
 390       * @return int
 391       */
 392      public function count_non_empty_children($extensions = '*', $limit = 1) {
 393          global $DB;
 394          if (!$this->lf->is_directory()) {
 395              return 0;
 396          }
 397  
 398          $filepath = $this->lf->get_filepath();
 399          $length = core_text::strlen($filepath);
 400          $sql = "SELECT filepath, filename
 401                    FROM {files} f
 402                   WHERE f.contextid = :contextid AND f.component = :component AND f.filearea = :filearea AND f.itemid = :itemid
 403                         AND ".$DB->sql_substr("f.filepath", 1, $length)." = :filepath
 404                         AND filename <> '.' ";
 405          $params = array('contextid' => $this->context->id,
 406              'component' => $this->lf->get_component(),
 407              'filearea' => $this->lf->get_filearea(),
 408              'itemid' => $this->lf->get_itemid(),
 409              'filepath' => $filepath);
 410          list($sql2, $params2) = $this->build_search_files_sql($extensions);
 411          $rs = $DB->get_recordset_sql($sql.' '.$sql2, array_merge($params, $params2));
 412          $children = array();
 413          foreach ($rs as $record) {
 414              // we don't need to check access to individual files here, since the user can access parent
 415              if ($record->filepath === $filepath) {
 416                  $children[] = $record->filename;
 417              } else {
 418                  $path = explode('/', core_text::substr($record->filepath, $length));
 419                  if (!in_array($path[0], $children)) {
 420                      $children[] = $path[0];
 421                  }
 422              }
 423              if (count($children) >= $limit) {
 424                  break;
 425              }
 426          }
 427          $rs->close();
 428          return count($children);
 429      }
 430  
 431      /**
 432       * Returns parent file_info instance
 433       *
 434       * @return file_info|null file_info instance or null for root
 435       */
 436      public function get_parent() {
 437          if ($this->lf->get_filepath() === '/' and $this->lf->is_directory()) {
 438              if ($this->areaonly) {
 439                  return null;
 440              } else if ($this->itemidused) {
 441                  return $this->browser->get_file_info($this->context, $this->lf->get_component(), $this->lf->get_filearea());
 442              } else {
 443                  return $this->browser->get_file_info($this->context);
 444              }
 445          }
 446  
 447          if (!$this->lf->is_directory()) {
 448              return $this->browser->get_file_info($this->context, $this->lf->get_component(), $this->lf->get_filearea(), $this->lf->get_itemid(), $this->lf->get_filepath(), '.');
 449          }
 450  
 451          $filepath = $this->lf->get_filepath();
 452          $filepath = trim($filepath, '/');
 453          $dirs = explode('/', $filepath);
 454          array_pop($dirs);
 455          $filepath = implode('/', $dirs);
 456          $filepath = ($filepath === '') ? '/' : "/$filepath/";
 457  
 458          return $this->browser->get_file_info($this->context, $this->lf->get_component(), $this->lf->get_filearea(), $this->lf->get_itemid(), $filepath, '.');
 459      }
 460  
 461      /**
 462       * Create new directory, may throw exception - make sure
 463       * params are valid.
 464       *
 465       * @param string $newdirname name of new directory
 466       * @param int $userid id of author, default $USER->id
 467       * @return file_info|null new directory's file_info instance or null if failed
 468       */
 469      public function create_directory($newdirname, $userid = NULL) {
 470          if (!$this->is_writable() or !$this->lf->is_directory()) {
 471              return null;
 472          }
 473  
 474          $newdirname = clean_param($newdirname, PARAM_FILE);
 475          if ($newdirname === '') {
 476              return null;
 477          }
 478  
 479          $filepath = $this->lf->get_filepath().'/'.$newdirname.'/';
 480  
 481          $fs = get_file_storage();
 482  
 483          if ($file = $fs->create_directory($this->lf->get_contextid(), $this->lf->get_component(), $this->lf->get_filearea(), $this->lf->get_itemid(), $filepath, $userid)) {
 484              return $this->browser->get_file_info($this->context, $this->lf->get_component(), $file->get_filearea(), $file->get_itemid(), $file->get_filepath(), $file->get_filename());
 485          }
 486          return null;
 487      }
 488  
 489  
 490      /**
 491       * Create new file from string - make sure
 492       * params are valid.
 493       *
 494       * @param string $newfilename name of new file
 495       * @param string $content of file
 496       * @param int $userid id of author, default $USER->id
 497       * @return file_info|null new file's file_info instance or null if failed
 498       */
 499      public function create_file_from_string($newfilename, $content, $userid = NULL) {
 500          if (!$this->is_writable() or !$this->lf->is_directory()) {
 501              return null;
 502          }
 503  
 504          $newfilename = clean_param($newfilename, PARAM_FILE);
 505          if ($newfilename === '') {
 506              return null;
 507          }
 508  
 509          $fs = get_file_storage();
 510  
 511          $now = time();
 512  
 513          $newrecord = new stdClass();
 514          $newrecord->contextid = $this->lf->get_contextid();
 515          $newrecord->component = $this->lf->get_component();
 516          $newrecord->filearea  = $this->lf->get_filearea();
 517          $newrecord->itemid    = $this->lf->get_itemid();
 518          $newrecord->filepath  = $this->lf->get_filepath();
 519          $newrecord->filename  = $newfilename;
 520  
 521          if ($fs->file_exists($newrecord->contextid, $newrecord->component, $newrecord->filearea, $newrecord->itemid, $newrecord->filepath, $newrecord->filename)) {
 522              // file already exists, sorry
 523              return null;
 524          }
 525  
 526          $newrecord->timecreated  = $now;
 527          $newrecord->timemodified = $now;
 528          $newrecord->mimetype     = mimeinfo('type', $newfilename);
 529          $newrecord->userid       = $userid;
 530  
 531          if ($file = $fs->create_file_from_string($newrecord, $content)) {
 532              return $this->browser->get_file_info($this->context, $file->get_component(), $file->get_filearea(), $file->get_itemid(), $file->get_filepath(), $file->get_filename());
 533          }
 534          return null;
 535      }
 536  
 537      /**
 538       * Create new file from pathname - make sure
 539       * params are valid.
 540       *
 541       * @param string $newfilename name of new file
 542       * @param string $pathname location of file
 543       * @param int $userid id of author, default $USER->id
 544       * @return file_info|null new file's file_info instance or null if failed
 545       */
 546      public function create_file_from_pathname($newfilename, $pathname, $userid = NULL) {
 547          if (!$this->is_writable() or !$this->lf->is_directory()) {
 548              return null;
 549          }
 550  
 551          $newfilename = clean_param($newfilename, PARAM_FILE);
 552          if ($newfilename === '') {
 553              return null;
 554          }
 555  
 556          $fs = get_file_storage();
 557  
 558          $now = time();
 559  
 560          $newrecord = new stdClass();
 561          $newrecord->contextid = $this->lf->get_contextid();
 562          $newrecord->component = $this->lf->get_component();
 563          $newrecord->filearea  = $this->lf->get_filearea();
 564          $newrecord->itemid    = $this->lf->get_itemid();
 565          $newrecord->filepath  = $this->lf->get_filepath();
 566          $newrecord->filename  = $newfilename;
 567  
 568          if ($fs->file_exists($newrecord->contextid, $newrecord->component, $newrecord->filearea, $newrecord->itemid, $newrecord->filepath, $newrecord->filename)) {
 569              // file already exists, sorry
 570              return null;
 571          }
 572  
 573          $newrecord->timecreated  = $now;
 574          $newrecord->timemodified = $now;
 575          $newrecord->mimetype     = mimeinfo('type', $newfilename);
 576          $newrecord->userid       = $userid;
 577  
 578          if ($file = $fs->create_file_from_pathname($newrecord, $pathname)) {
 579              return $this->browser->get_file_info($this->context, $file->get_component(), $file->get_filearea(), $file->get_itemid(), $file->get_filepath(), $file->get_filename());
 580          }
 581          return null;
 582      }
 583  
 584      /**
 585       * Create new file from stored file - make sure
 586       * params are valid.
 587       *
 588       * @param string $newfilename name of new file
 589       * @param int|stored_file $fid file id or stored_file of file
 590       * @param int $userid id of author, default $USER->id
 591       * @return file_info|null new file's file_info instance or null if failed
 592       */
 593      public function create_file_from_storedfile($newfilename, $fid, $userid = NULL) {
 594          if (!$this->is_writable() or $this->lf->get_filename() !== '.') {
 595              return null;
 596          }
 597  
 598          $newfilename = clean_param($newfilename, PARAM_FILE);
 599          if ($newfilename === '') {
 600              return null;
 601          }
 602  
 603          $fs = get_file_storage();
 604  
 605          $now = time();
 606  
 607          $newrecord = new stdClass();
 608          $newrecord->contextid = $this->lf->get_contextid();
 609          $newrecord->component = $this->lf->get_component();
 610          $newrecord->filearea  = $this->lf->get_filearea();
 611          $newrecord->itemid    = $this->lf->get_itemid();
 612          $newrecord->filepath  = $this->lf->get_filepath();
 613          $newrecord->filename  = $newfilename;
 614  
 615          if ($fs->file_exists($newrecord->contextid, $newrecord->component, $newrecord->filearea, $newrecord->itemid, $newrecord->filepath, $newrecord->filename)) {
 616              // file already exists, sorry
 617              return null;
 618          }
 619  
 620          $newrecord->timecreated  = $now;
 621          $newrecord->timemodified = $now;
 622          $newrecord->mimetype     = mimeinfo('type', $newfilename);
 623          $newrecord->userid       = $userid;
 624  
 625          if ($file = $fs->create_file_from_storedfile($newrecord, $fid)) {
 626              return $this->browser->get_file_info($this->context, $file->get_component(), $file->get_filearea(), $file->get_itemid(), $file->get_filepath(), $file->get_filename());
 627          }
 628          return null;
 629      }
 630  
 631      /**
 632       * Delete file, make sure file is deletable first.
 633       *
 634       * @return bool success
 635       */
 636      public function delete() {
 637          if (!$this->is_writable()) {
 638              return false;
 639          }
 640  
 641          if ($this->is_directory()) {
 642              $filepath = $this->lf->get_filepath();
 643              $fs = get_file_storage();
 644              $storedfiles = $fs->get_area_files($this->context->id, $this->get_component(), $this->lf->get_filearea(), $this->lf->get_itemid());
 645              foreach ($storedfiles as $file) {
 646                  if (strpos($file->get_filepath(), $filepath) === 0) {
 647                      $file->delete();
 648                  }
 649              }
 650          }
 651  
 652          return $this->lf->delete();
 653      }
 654  
 655      /**
 656       * Copy content of this file to local storage, overriding current file if needed.
 657       *
 658       * @param array|stdClass $filerecord contains contextid, component, filearea,
 659       *    itemid, filepath, filename and optionally other attributes of the new file
 660       * @return bool success
 661       */
 662      public function copy_to_storage($filerecord) {
 663          if (!$this->is_readable() or $this->is_directory()) {
 664              return false;
 665          }
 666          $filerecord = (array)$filerecord;
 667  
 668          $fs = get_file_storage();
 669          if ($existing = $fs->get_file($filerecord['contextid'], $filerecord['component'], $filerecord['filearea'], $filerecord['itemid'], $filerecord['filepath'], $filerecord['filename'])) {
 670              $existing->delete();
 671          }
 672          $fs->create_file_from_storedfile($filerecord, $this->lf);
 673  
 674          return true;
 675      }
 676  
 677      /**
 678       * Copy content of this file to local storage, overriding current file if needed.
 679       *
 680       * @param string $pathname real local full file name
 681       * @return bool success
 682       */
 683      public function copy_to_pathname($pathname) {
 684          if (!$this->is_readable() or $this->is_directory()) {
 685              return false;
 686          }
 687  
 688          if (file_exists($pathname)) {
 689              if (!unlink($pathname)) {
 690                  return false;
 691              }
 692          }
 693  
 694          $this->lf->copy_content_to($pathname);
 695  
 696          return true;
 697      }
 698  }