Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.2.x will end 22 April 2024 (12 months).
  • Bug fixes for security issues in 4.2.x will end 7 October 2024 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.1.x is supported too.

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

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