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 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   * External files API
  20   *
  21   * @package    core_files
  22   * @category   external
  23   * @copyright  2010 Dongsheng Cai
  24   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  25   */
  26  
  27  defined('MOODLE_INTERNAL') || die();
  28  
  29  require_once("$CFG->libdir/externallib.php");
  30  require_once("$CFG->libdir/filelib.php");
  31  
  32  /**
  33   * Files external functions
  34   *
  35   * @package    core_files
  36   * @category   external
  37   * @copyright  2011 Jerome Mouneyrac
  38   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  39   * @since Moodle 2.2
  40   */
  41  class core_files_external extends external_api {
  42  
  43      /**
  44       * Returns description of get_files parameters
  45       *
  46       * @return external_function_parameters
  47       * @since Moodle 2.2
  48       */
  49      public static function get_files_parameters() {
  50          return new external_function_parameters(
  51              array(
  52                  'contextid'    => new external_value(PARAM_INT, 'context id Set to -1 to use contextlevel and instanceid.'),
  53                  'component'    => new external_value(PARAM_TEXT, 'component'),
  54                  'filearea'     => new external_value(PARAM_TEXT, 'file area'),
  55                  'itemid'       => new external_value(PARAM_INT, 'associated id'),
  56                  'filepath'     => new external_value(PARAM_PATH, 'file path'),
  57                  'filename'     => new external_value(PARAM_TEXT, 'file name'),
  58                  'modified'     => new external_value(PARAM_INT, 'timestamp to return files changed after this time.', VALUE_DEFAULT, null),
  59                  'contextlevel' => new external_value(PARAM_ALPHA, 'The context level for the file location.', VALUE_DEFAULT, null),
  60                  'instanceid'   => new external_value(PARAM_INT, 'The instance id for where the file is located.', VALUE_DEFAULT, null)
  61  
  62              )
  63          );
  64      }
  65  
  66      /**
  67       * Return moodle files listing
  68       *
  69       * @param int $contextid context id
  70       * @param int $component component
  71       * @param int $filearea file area
  72       * @param int $itemid item id
  73       * @param string $filepath file path
  74       * @param string $filename file name
  75       * @param int $modified timestamp to return files changed after this time.
  76       * @param string $contextlevel The context level for the file location.
  77       * @param int $instanceid The instance id for where the file is located.
  78       * @return array
  79       * @since Moodle 2.9 Returns additional fields (timecreated, filesize, author, license)
  80       * @since Moodle 2.2
  81       */
  82      public static function get_files($contextid, $component, $filearea, $itemid, $filepath, $filename, $modified = null,
  83                                       $contextlevel = null, $instanceid = null) {
  84  
  85          $parameters = array(
  86              'contextid'    => $contextid,
  87              'component'    => $component,
  88              'filearea'     => $filearea,
  89              'itemid'       => $itemid,
  90              'filepath'     => $filepath,
  91              'filename'     => $filename,
  92              'modified'     => $modified,
  93              'contextlevel' => $contextlevel,
  94              'instanceid'   => $instanceid);
  95          $fileinfo = self::validate_parameters(self::get_files_parameters(), $parameters);
  96  
  97          $browser = get_file_browser();
  98  
  99          // We need to preserve backwards compatibility. Zero will use the system context and minus one will
 100          // use the addtional parameters to determine the context.
 101          // TODO MDL-40489 get_context_from_params should handle this logic.
 102          if ($fileinfo['contextid'] == 0) {
 103              $context = context_system::instance();
 104          } else {
 105              if ($fileinfo['contextid'] == -1) {
 106                  $fileinfo['contextid'] = null;
 107              }
 108              $context = self::get_context_from_params($fileinfo);
 109          }
 110          self::validate_context($context);
 111  
 112          if (empty($fileinfo['component'])) {
 113              $fileinfo['component'] = null;
 114          }
 115          if (empty($fileinfo['filearea'])) {
 116              $fileinfo['filearea'] = null;
 117          }
 118          if (empty($fileinfo['filename'])) {
 119              $fileinfo['filename'] = null;
 120          }
 121          if (empty($fileinfo['filepath'])) {
 122              $fileinfo['filepath'] = null;
 123          }
 124  
 125          $return = array();
 126          $return['parents'] = array();
 127          $return['files'] = array();
 128          $list = array();
 129  
 130          if ($file = $browser->get_file_info(
 131              $context, $fileinfo['component'], $fileinfo['filearea'], $fileinfo['itemid'],
 132                  $fileinfo['filepath'], $fileinfo['filename'])) {
 133              $level = $file->get_parent();
 134              while ($level) {
 135                  $params = $level->get_params();
 136                  $params['filename'] = $level->get_visible_name();
 137                  array_unshift($return['parents'], $params);
 138                  $level = $level->get_parent();
 139              }
 140              $children = $file->get_children();
 141              foreach ($children as $child) {
 142  
 143                  $params = $child->get_params();
 144                  $timemodified = $child->get_timemodified();
 145                  $timecreated = $child->get_timecreated();
 146  
 147                  if ($child->is_directory()) {
 148                      if ((is_null($modified)) or ($modified < $timemodified)) {
 149                          $node = array(
 150                              'contextid' => $params['contextid'],
 151                              'component' => $params['component'],
 152                              'filearea'  => $params['filearea'],
 153                              'itemid'    => $params['itemid'],
 154                              'filepath'  => $params['filepath'],
 155                              'filename'  => $child->get_visible_name(),
 156                              'url'       => null,
 157                              'isdir'     => true,
 158                              'timemodified' => $timemodified,
 159                              'timecreated' => $timecreated,
 160                              'filesize' => 0,
 161                              'author' => null,
 162                              'license' => null
 163                             );
 164                             $list[] = $node;
 165                      }
 166                  } else {
 167                      if ((is_null($modified)) or ($modified < $timemodified)) {
 168                          $node = array(
 169                              'contextid' => $params['contextid'],
 170                              'component' => $params['component'],
 171                              'filearea'  => $params['filearea'],
 172                              'itemid'    => $params['itemid'],
 173                              'filepath'  => $params['filepath'],
 174                              'filename'  => $child->get_visible_name(),
 175                              'url'       => $child->get_url(),
 176                              'isdir'     => false,
 177                              'timemodified' => $timemodified,
 178                              'timecreated' => $timecreated,
 179                              'filesize' => $child->get_filesize(),
 180                              'author' => $child->get_author(),
 181                              'license' => $child->get_license()
 182                          );
 183                             $list[] = $node;
 184                      }
 185                  }
 186              }
 187          }
 188          $return['files'] = $list;
 189          return $return;
 190      }
 191  
 192      /**
 193       * Returns description of get_files returns
 194       *
 195       * @return external_single_structure
 196       * @since Moodle 2.9 Returns additional fields for files (timecreated, filesize, author, license)
 197       * @since Moodle 2.2
 198       */
 199      public static function get_files_returns() {
 200          return new external_single_structure(
 201              array(
 202                  'parents' => new external_multiple_structure(
 203                      new external_single_structure(
 204                          array(
 205                              'contextid' => new external_value(PARAM_INT, ''),
 206                              'component' => new external_value(PARAM_COMPONENT, ''),
 207                              'filearea'  => new external_value(PARAM_AREA, ''),
 208                              'itemid'    => new external_value(PARAM_INT, ''),
 209                              'filepath'  => new external_value(PARAM_TEXT, ''),
 210                              'filename'  => new external_value(PARAM_TEXT, ''),
 211                          )
 212                      )
 213                  ),
 214                  'files' => new external_multiple_structure(
 215                      new external_single_structure(
 216                          array(
 217                              'contextid' => new external_value(PARAM_INT, ''),
 218                              'component' => new external_value(PARAM_COMPONENT, ''),
 219                              'filearea'  => new external_value(PARAM_AREA, ''),
 220                              'itemid'   => new external_value(PARAM_INT, ''),
 221                              'filepath' => new external_value(PARAM_TEXT, ''),
 222                              'filename' => new external_value(PARAM_TEXT, ''),
 223                              'isdir'    => new external_value(PARAM_BOOL, ''),
 224                              'url'      => new external_value(PARAM_TEXT, ''),
 225                              'timemodified' => new external_value(PARAM_INT, ''),
 226                              'timecreated' => new external_value(PARAM_INT, 'Time created', VALUE_OPTIONAL),
 227                              'filesize' => new external_value(PARAM_INT, 'File size', VALUE_OPTIONAL),
 228                              'author' => new external_value(PARAM_TEXT, 'File owner', VALUE_OPTIONAL),
 229                              'license' => new external_value(PARAM_TEXT, 'File license', VALUE_OPTIONAL),
 230                          )
 231                      )
 232                  )
 233              )
 234          );
 235      }
 236  
 237      /**
 238       * Returns description of upload parameters
 239       *
 240       * @return external_function_parameters
 241       * @since Moodle 2.2
 242       */
 243      public static function upload_parameters() {
 244          return new external_function_parameters(
 245              array(
 246                  'contextid' => new external_value(PARAM_INT, 'context id', VALUE_DEFAULT, null),
 247                  'component' => new external_value(PARAM_COMPONENT, 'component'),
 248                  'filearea'  => new external_value(PARAM_AREA, 'file area'),
 249                  'itemid'    => new external_value(PARAM_INT, 'associated id'),
 250                  'filepath'  => new external_value(PARAM_PATH, 'file path'),
 251                  'filename'  => new external_value(PARAM_FILE, 'file name'),
 252                  'filecontent' => new external_value(PARAM_TEXT, 'file content'),
 253                  'contextlevel' => new external_value(PARAM_ALPHA, 'The context level to put the file in,
 254                          (block, course, coursecat, system, user, module)', VALUE_DEFAULT, null),
 255                  'instanceid' => new external_value(PARAM_INT, 'The Instance id of item associated
 256                           with the context level', VALUE_DEFAULT, null)
 257              )
 258          );
 259      }
 260  
 261      /**
 262       * Uploading a file to moodle
 263       *
 264       * @param int    $contextid    context id
 265       * @param string $component    component
 266       * @param string $filearea     file area
 267       * @param int    $itemid       item id
 268       * @param string $filepath     file path
 269       * @param string $filename     file name
 270       * @param string $filecontent  file content
 271       * @param string $contextlevel Context level (block, course, coursecat, system, user or module)
 272       * @param int    $instanceid   Instance id of the item associated with the context level
 273       * @return array
 274       * @since Moodle 2.2
 275       */
 276      public static function upload($contextid, $component, $filearea, $itemid, $filepath, $filename, $filecontent, $contextlevel, $instanceid) {
 277          global $USER, $CFG;
 278  
 279          $fileinfo = self::validate_parameters(self::upload_parameters(), array(
 280                  'contextid' => $contextid, 'component' => $component, 'filearea' => $filearea, 'itemid' => $itemid,
 281                  'filepath' => $filepath, 'filename' => $filename, 'filecontent' => $filecontent, 'contextlevel' => $contextlevel,
 282                  'instanceid' => $instanceid));
 283  
 284          if (!isset($fileinfo['filecontent'])) {
 285              throw new moodle_exception('nofile');
 286          }
 287          // Saving file.
 288          $dir = make_temp_directory('wsupload');
 289  
 290          if (empty($fileinfo['filename'])) {
 291              $filename = uniqid('wsupload', true).'_'.time().'.tmp';
 292          } else {
 293              $filename = $fileinfo['filename'];
 294          }
 295  
 296          if (file_exists($dir.$filename)) {
 297              $savedfilepath = $dir.uniqid('m').$filename;
 298          } else {
 299              $savedfilepath = $dir.$filename;
 300          }
 301  
 302          file_put_contents($savedfilepath, base64_decode($fileinfo['filecontent']));
 303          @chmod($savedfilepath, $CFG->filepermissions);
 304          unset($fileinfo['filecontent']);
 305  
 306          if (!empty($fileinfo['filepath'])) {
 307              $filepath = $fileinfo['filepath'];
 308          } else {
 309              $filepath = '/';
 310          }
 311  
 312          // Only allow uploads to draft area
 313          if (!($fileinfo['component'] == 'user' and $fileinfo['filearea'] == 'draft')) {
 314              throw new coding_exception('File can be uploaded to user draft area only');
 315          } else {
 316              $component = 'user';
 317              $filearea = $fileinfo['filearea'];
 318          }
 319  
 320          $itemid = 0;
 321          if (isset($fileinfo['itemid'])) {
 322              $itemid = $fileinfo['itemid'];
 323          }
 324          if ($filearea == 'draft' && $itemid <= 0) {
 325              // Generate a draft area for the files.
 326              $itemid = file_get_unused_draft_itemid();
 327          } else if ($filearea == 'private') {
 328              // TODO MDL-31116 in user private area, itemid is always 0.
 329              $itemid = 0;
 330          }
 331  
 332          // We need to preserve backword compatibility. Context id is no more a required.
 333          if (empty($fileinfo['contextid'])) {
 334              unset($fileinfo['contextid']);
 335          }
 336  
 337          // Get and validate context.
 338          $context = self::get_context_from_params($fileinfo);
 339          self::validate_context($context);
 340          if (($fileinfo['component'] == 'user' and $fileinfo['filearea'] == 'private')) {
 341              throw new moodle_exception('privatefilesupload');
 342          }
 343  
 344          $browser = get_file_browser();
 345  
 346          // Check existing file.
 347          if ($file = $browser->get_file_info($context, $component, $filearea, $itemid, $filepath, $filename)) {
 348              throw new moodle_exception('fileexist');
 349          }
 350  
 351          // Move file to filepool.
 352          if ($dir = $browser->get_file_info($context, $component, $filearea, $itemid, $filepath, '.')) {
 353              $info = $dir->create_file_from_pathname($filename, $savedfilepath);
 354              $params = $info->get_params();
 355              unlink($savedfilepath);
 356              return array(
 357                  'contextid'=>$params['contextid'],
 358                  'component'=>$params['component'],
 359                  'filearea'=>$params['filearea'],
 360                  'itemid'=>$params['itemid'],
 361                  'filepath'=>$params['filepath'],
 362                  'filename'=>$params['filename'],
 363                  'url'=>$info->get_url()
 364                  );
 365          } else {
 366              throw new moodle_exception('nofile');
 367          }
 368      }
 369  
 370      /**
 371       * Returns description of upload returns
 372       *
 373       * @return external_single_structure
 374       * @since Moodle 2.2
 375       */
 376      public static function upload_returns() {
 377          return new external_single_structure(
 378               array(
 379                   'contextid' => new external_value(PARAM_INT, ''),
 380                   'component' => new external_value(PARAM_COMPONENT, ''),
 381                   'filearea'  => new external_value(PARAM_AREA, ''),
 382                   'itemid'   => new external_value(PARAM_INT, ''),
 383                   'filepath' => new external_value(PARAM_TEXT, ''),
 384                   'filename' => new external_value(PARAM_FILE, ''),
 385                   'url'      => new external_value(PARAM_TEXT, ''),
 386               )
 387          );
 388      }
 389  }