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   * Accept uploading files by web service token to the user draft file area.
  20   *
  21   * POST params:
  22   *  token => the web service user token (needed for authentication)
  23   *  filepath => file path (where files will be stored)
  24   *  [_FILES] => for example you can send the files with <input type=file>,
  25   *              or with curl magic: 'file_1' => '@/path/to/file', or ...
  26   *  itemid   => The draftid - this can be used to add a list of files
  27   *              to a draft area in separate requests. If it is 0, a new draftid will be generated.
  28   *
  29   * @package    core_webservice
  30   * @copyright  2011 Dongsheng Cai <dongsheng@moodle.com>
  31   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  32   */
  33  
  34  /**
  35   * AJAX_SCRIPT - exception will be converted into JSON
  36   */
  37  define('AJAX_SCRIPT', true);
  38  
  39  /**
  40   * NO_MOODLE_COOKIES - we don't want any cookie
  41   */
  42  define('NO_MOODLE_COOKIES', true);
  43  
  44  require_once(__DIR__ . '/../config.php');
  45  require_once($CFG->dirroot . '/webservice/lib.php');
  46  
  47  // Allow CORS requests.
  48  header('Access-Control-Allow-Origin: *');
  49  
  50  $filepath = optional_param('filepath', '/', PARAM_PATH);
  51  $itemid = optional_param('itemid', 0, PARAM_INT);
  52  
  53  echo $OUTPUT->header();
  54  
  55  // Authenticate the user.
  56  $token = required_param('token', PARAM_ALPHANUM);
  57  $webservicelib = new webservice();
  58  $authenticationinfo = $webservicelib->authenticate_user($token);
  59  $fileuploaddisabled = empty($authenticationinfo['service']->uploadfiles);
  60  if ($fileuploaddisabled) {
  61      throw new webservice_access_exception('Web service file upload must be enabled in external service settings');
  62  }
  63  
  64  $context = context_user::instance($USER->id);
  65  
  66  $fs = get_file_storage();
  67  
  68  $totalsize = 0;
  69  $files = array();
  70  foreach ($_FILES as $fieldname => $uploadedfile) {
  71      // Check upload errors.
  72      if (!empty($_FILES[$fieldname]['error'])) {
  73          switch ($_FILES[$fieldname]['error']) {
  74              case UPLOAD_ERR_INI_SIZE:
  75                  throw new moodle_exception('upload_error_ini_size', 'repository_upload');
  76                  break;
  77              case UPLOAD_ERR_FORM_SIZE:
  78                  throw new moodle_exception('upload_error_form_size', 'repository_upload');
  79                  break;
  80              case UPLOAD_ERR_PARTIAL:
  81                  throw new moodle_exception('upload_error_partial', 'repository_upload');
  82                  break;
  83              case UPLOAD_ERR_NO_FILE:
  84                  throw new moodle_exception('upload_error_no_file', 'repository_upload');
  85                  break;
  86              case UPLOAD_ERR_NO_TMP_DIR:
  87                  throw new moodle_exception('upload_error_no_tmp_dir', 'repository_upload');
  88                  break;
  89              case UPLOAD_ERR_CANT_WRITE:
  90                  throw new moodle_exception('upload_error_cant_write', 'repository_upload');
  91                  break;
  92              case UPLOAD_ERR_EXTENSION:
  93                  throw new moodle_exception('upload_error_extension', 'repository_upload');
  94                  break;
  95              default:
  96                  throw new moodle_exception('nofile');
  97          }
  98      }
  99  
 100      // Scan for viruses.
 101      \core\antivirus\manager::scan_file($_FILES[$fieldname]['tmp_name'], $_FILES[$fieldname]['name'], true);
 102  
 103      $file = new stdClass();
 104      $file->filename = clean_param($_FILES[$fieldname]['name'], PARAM_FILE);
 105      // Check system maxbytes setting.
 106      if (($_FILES[$fieldname]['size'] > get_max_upload_file_size($CFG->maxbytes))) {
 107          // Oversize file will be ignored, error added to array to notify
 108          // web service client.
 109          $file->errortype = 'fileoversized';
 110          $file->error = get_string('maxbytes', 'error');
 111      } else {
 112          $file->filepath = $_FILES[$fieldname]['tmp_name'];
 113          // Calculate total size of upload.
 114          $totalsize += $_FILES[$fieldname]['size'];
 115          // Size of individual file.
 116          $file->size = $_FILES[$fieldname]['size'];
 117      }
 118      $files[] = $file;
 119  }
 120  
 121  $fs = get_file_storage();
 122  
 123  if ($itemid <= 0) {
 124      $itemid = file_get_unused_draft_itemid();
 125  }
 126  
 127  // Get any existing file size limits.
 128  $maxupload = get_user_max_upload_file_size($context, $CFG->maxbytes);
 129  
 130  // Check the size of this upload.
 131  if ($maxupload !== USER_CAN_IGNORE_FILE_SIZE_LIMITS && $totalsize > $maxupload) {
 132      throw new file_exception('userquotalimit');
 133  }
 134  
 135  $results = array();
 136  foreach ($files as $file) {
 137      if (!empty($file->error)) {
 138          // Including error and filename.
 139          $results[] = $file;
 140          continue;
 141      }
 142      $filerecord = new stdClass;
 143      $filerecord->component = 'user';
 144      $filerecord->contextid = $context->id;
 145      $filerecord->userid = $USER->id;
 146      $filerecord->filearea = 'draft';
 147      $filerecord->filename = $file->filename;
 148      $filerecord->filepath = $filepath;
 149      $filerecord->itemid = $itemid;
 150      $filerecord->license = $CFG->sitedefaultlicense;
 151      $filerecord->author = fullname($authenticationinfo['user']);
 152      $filerecord->source = serialize((object)array('source' => $file->filename));
 153      $filerecord->filesize = $file->size;
 154  
 155      // Check if the file already exist.
 156      $existingfile = $fs->file_exists($filerecord->contextid, $filerecord->component, $filerecord->filearea,
 157                  $filerecord->itemid, $filerecord->filepath, $filerecord->filename);
 158      if ($existingfile) {
 159          $file->errortype = 'filenameexist';
 160          $file->error = get_string('filenameexist', 'webservice', $file->filename);
 161          $results[] = $file;
 162      } else {
 163          $storedfile = $fs->create_file_from_pathname($filerecord, $file->filepath);
 164          $results[] = $filerecord;
 165  
 166          // Log the event when a file is uploaded to the draft area.
 167          $logevent = \core\event\draft_file_added::create([
 168                  'objectid' => $storedfile->get_id(),
 169                  'context' => $context,
 170                  'other' => [
 171                          'itemid' => $filerecord->itemid,
 172                          'filename' => $filerecord->filename,
 173                          'filesize' => $filerecord->filesize,
 174                          'filepath' => $filerecord->filepath,
 175                          'contenthash' => $storedfile->get_contenthash(),
 176                  ],
 177          ]);
 178          $logevent->trigger();
 179      }
 180  }
 181  echo json_encode($results);