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 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   * The Web service script that is called from the filepicker front end
  20   *
  21   * @since Moodle 2.0
  22   * @package    repository
  23   * @copyright  2009 Dongsheng Cai {@link http://dongsheng.org}
  24   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  25   */
  26  
  27  define('AJAX_SCRIPT', true);
  28  
  29  require_once(__DIR__ . '/../config.php');
  30  require_once (__DIR__ . '/../lib/filelib.php');
  31  require_once (__DIR__.'/lib.php');
  32  
  33  $err = new stdClass();
  34  
  35  // Parameters
  36  $action    = optional_param('action', '', PARAM_ALPHA);
  37  $repo_id   = optional_param('repo_id', 0, PARAM_INT);           // Repository ID
  38  $contextid = optional_param('ctx_id', SYSCONTEXTID, PARAM_INT); // Context ID
  39  $env       = optional_param('env', 'filepicker', PARAM_ALPHA);  // Opened in editor or moodleform
  40  $license   = optional_param('license', $CFG->sitedefaultlicense, PARAM_TEXT);
  41  $author    = optional_param('author', '', PARAM_TEXT);          // File author
  42  $source    = optional_param('source', '', PARAM_RAW);           // File to download
  43  $sourcekey = optional_param('sourcekey', '', PARAM_RAW);        // Used to verify the source.
  44  $itemid    = optional_param('itemid', 0, PARAM_INT);            // Itemid
  45  $page      = optional_param('page', '', PARAM_RAW);             // Page
  46  $maxbytes  = optional_param('maxbytes', 0, PARAM_INT);          // Maxbytes
  47  $req_path  = optional_param('p', '', PARAM_RAW);                // Path
  48  $accepted_types  = optional_param_array('accepted_types', '*', PARAM_RAW);
  49  $saveas_filename = optional_param('title', '', PARAM_FILE);     // save as file name
  50  $areamaxbytes  = optional_param('areamaxbytes', FILE_AREA_MAX_BYTES_UNLIMITED, PARAM_INT); // Area max bytes.
  51  $saveas_path   = optional_param('savepath', '/', PARAM_PATH);   // save as file path
  52  $search_text   = optional_param('s', '', PARAM_CLEANHTML);
  53  $linkexternal  = optional_param('linkexternal', '', PARAM_ALPHA);
  54  $usefilereference  = optional_param('usefilereference', false, PARAM_BOOL);
  55  $usecontrolledlink  = optional_param('usecontrolledlink', false, PARAM_BOOL);
  56  
  57  list($context, $course, $cm) = get_context_info_array($contextid);
  58  require_login($course, false, $cm, false, true);
  59  $PAGE->set_context($context);
  60  
  61  echo $OUTPUT->header(); // send headers
  62  
  63  // If uploaded file is larger than post_max_size (php.ini) setting, $_POST content will be empty.
  64  if (empty($_POST) && !empty($action)) {
  65      $err->error = get_string('errorpostmaxsize', 'repository');
  66      die(json_encode($err));
  67  }
  68  
  69  if (!confirm_sesskey()) {
  70      $err->error = get_string('invalidsesskey', 'error');
  71      die(json_encode($err));
  72  }
  73  
  74  // Get repository instance information
  75  $repooptions = array(
  76      'ajax' => true,
  77      'mimetypes' => $accepted_types
  78  );
  79  
  80  ajax_capture_output();
  81  $repo = repository::get_repository_by_id($repo_id, $contextid, $repooptions);
  82  
  83  // Check permissions
  84  $repo->check_capability();
  85  
  86  $coursemaxbytes = 0;
  87  if (!empty($course)) {
  88      $coursemaxbytes = $course->maxbytes;
  89  }
  90  // Make sure maxbytes passed is within site filesize limits.
  91  $maxbytes = get_user_max_upload_file_size($context, $CFG->maxbytes, $coursemaxbytes, $maxbytes);
  92  
  93  // Wait as long as it takes for this script to finish
  94  core_php_time_limit::raise();
  95  
  96  // These actions all occur on the currently active repository instance
  97  switch ($action) {
  98      case 'sign':
  99      case 'signin':
 100      case 'list':
 101          if ($repo->check_login()) {
 102              $listing = repository::prepare_listing($repo->get_listing($req_path, $page));
 103              $listing['repo_id'] = $repo_id;
 104              ajax_check_captured_output();
 105              echo json_encode($listing);
 106              break;
 107          } else {
 108              $action = 'login';
 109          }
 110      case 'login':
 111          $listing = $repo->print_login();
 112          $listing['repo_id'] = $repo_id;
 113          ajax_check_captured_output();
 114          echo json_encode($listing);
 115          break;
 116      case 'logout':
 117          $logout = $repo->logout();
 118          $logout['repo_id'] = $repo_id;
 119          ajax_check_captured_output();
 120          echo json_encode($logout);
 121          break;
 122      case 'searchform':
 123          $search_form['repo_id'] = $repo_id;
 124          $search_form['form'] = $repo->print_search();
 125          $search_form['allowcaching'] = true;
 126          ajax_check_captured_output();
 127          echo json_encode($search_form);
 128          break;
 129      case 'search':
 130          $search_result = repository::prepare_listing($repo->search($search_text, (int)$page));
 131          $search_result['repo_id'] = $repo_id;
 132          $search_result['issearchresult'] = true;
 133          ajax_check_captured_output();
 134          echo json_encode($search_result);
 135          break;
 136      case 'download':
 137          // validate mimetype
 138          $mimetypes = array();
 139          if ((is_array($accepted_types) and in_array('*', $accepted_types)) or $accepted_types == '*') {
 140              $mimetypes = '*';
 141          } else {
 142              foreach ($accepted_types as $type) {
 143                  $mimetypes[] = mimeinfo('type', $type);
 144              }
 145              if (!in_array(mimeinfo('type', $saveas_filename), $mimetypes)) {
 146                  throw new moodle_exception('invalidfiletype', 'repository', '', get_mimetype_description(array('filename' => $saveas_filename)));
 147              }
 148          }
 149  
 150          // We have two special repository type need to deal with
 151          // local and recent plugins don't added new files to moodle, just add new records to database
 152          // so we don't check user quota and maxbytes here
 153          $allowexternallink = (int)get_config(null, 'repositoryallowexternallinks');
 154          if (!empty($allowexternallink)) {
 155              $allowexternallink = true;
 156          } else {
 157              $allowexternallink = false;
 158          }
 159          // allow external links in url element all the time
 160          $allowexternallink = ($allowexternallink || ($env == 'url'));
 161  
 162          // Validate the sourcekey.
 163          if (empty($sourcekey)) {
 164              throw new moodle_exception('missingsourcekey', 'repository');
 165          }
 166  
 167          // Check that the sourcekey matches.
 168          if (sha1($source . repository::get_secret_key() . sesskey()) !== $sourcekey) {
 169              throw new moodle_exception('sourcekeymismatch', 'repository');
 170          }
 171  
 172          $reference = $repo->get_file_reference($source);
 173  
 174          // Use link of the files
 175          if ($allowexternallink and $linkexternal === 'yes' and ($repo->supported_returntypes() & FILE_EXTERNAL)) {
 176              // use external link
 177              $link = $repo->get_link($reference);
 178              $info = array();
 179              $info['file'] = $saveas_filename;
 180              $info['type'] = 'link';
 181              $info['url'] = $link;
 182              ajax_check_captured_output();
 183              echo json_encode($info);
 184              die;
 185          } else {
 186              $fs = get_file_storage();
 187  
 188              // Prepare file record.
 189              $record = new stdClass();
 190              $record->filepath = $saveas_path;
 191              $record->filename = $saveas_filename;
 192              $record->component = 'user';
 193              $record->filearea = 'draft';
 194              $record->itemid = $itemid;
 195              $record->license = $license;
 196              $record->author = $author;
 197  
 198              if ($record->filepath !== '/') {
 199                  $record->filepath = trim($record->filepath, '/');
 200                  $record->filepath = '/'.$record->filepath.'/';
 201              }
 202              $usercontext = context_user::instance($USER->id);
 203              $now = time();
 204              $record->contextid = $usercontext->id;
 205              $record->timecreated = $now;
 206              $record->timemodified = $now;
 207              $record->userid = $USER->id;
 208              $record->sortorder = 0;
 209  
 210              // Check that user has permission to access this file
 211              if (!$repo->file_is_accessible($source)) {
 212                  throw new file_exception('storedfilecannotread');
 213              }
 214  
 215              // {@link repository::build_source_field()}
 216              $sourcefield = $repo->get_file_source_info($source);
 217              $record->source = $repo::build_source_field($sourcefield);
 218  
 219              // If file is already a reference, set $source = file source, $repo = file repository
 220              // note that in this case user may not have permission to access the source file directly
 221              // so no file_browser/file_info can be used below
 222              if ($repo->has_moodle_files()) {
 223                  $file = repository::get_moodle_file($reference);
 224                  if ($file && $file->is_external_file()) {
 225                      $sourcefield = $file->get_source(); // remember the original source
 226                      $record->source = $repo::build_source_field($sourcefield);
 227                      $record->contenthash = $file->get_contenthash();
 228                      $record->filesize = $file->get_filesize();
 229                      $reference = $file->get_reference();
 230                      $repo_id = $file->get_repository_id();
 231                      $repo = repository::get_repository_by_id($repo_id, $contextid, $repooptions);
 232                  }
 233              }
 234  
 235              if ($usefilereference || $usecontrolledlink) {
 236                  if ($repo->has_moodle_files()) {
 237                      $sourcefile = repository::get_moodle_file($reference);
 238                      $record->contenthash = $sourcefile->get_contenthash();
 239                      $record->filesize = $sourcefile->get_filesize();
 240                  }
 241  
 242                  // Check if file exists.
 243                  if (repository::draftfile_exists($itemid, $saveas_path, $saveas_filename)) {
 244                      // File name being used, rename it.
 245                      $unused_filename = repository::get_unused_filename($itemid, $saveas_path, $saveas_filename);
 246                      $record->filename = $unused_filename;
 247                      // Create a file copy using unused filename.
 248                      $storedfile = $fs->create_file_from_reference($record, $repo_id, $reference);
 249  
 250                      $event = array();
 251                      $event['event'] = 'fileexists';
 252                      $event['newfile'] = new stdClass;
 253                      $event['newfile']->filepath = $saveas_path;
 254                      $event['newfile']->filename = $unused_filename;
 255                      $event['newfile']->url = moodle_url::make_draftfile_url($itemid, $saveas_path, $unused_filename)->out();
 256  
 257                      $event['existingfile'] = new stdClass;
 258                      $event['existingfile']->filepath = $saveas_path;
 259                      $event['existingfile']->filename = $saveas_filename;
 260                      $event['existingfile']->url      = moodle_url::make_draftfile_url($itemid, $saveas_path, $saveas_filename)->out();
 261                  } else {
 262  
 263                      $storedfile = $fs->create_file_from_reference($record, $repo_id, $reference);
 264                      $event = array(
 265                          'url'=>moodle_url::make_draftfile_url($storedfile->get_itemid(), $storedfile->get_filepath(), $storedfile->get_filename())->out(),
 266                          'id'=>$storedfile->get_itemid(),
 267                          'file'=>$storedfile->get_filename(),
 268                          'icon' => $OUTPUT->image_url(file_file_icon($storedfile, 32))->out(),
 269                      );
 270                  }
 271                  // Repository plugin callback
 272                  // You can cache reository file in this callback
 273                  // or complete other tasks.
 274                  $repo->cache_file_by_reference($reference, $storedfile);
 275                  ajax_check_captured_output();
 276                  echo json_encode($event);
 277                  die;
 278              } else if ($repo->has_moodle_files()) {
 279                  // Some repository plugins (local, user, coursefiles, recent) are hosting moodle
 280                  // internal files, we cannot use get_file method, so we use copy_to_area method
 281  
 282                  // If the moodle file is an alias we copy this alias, otherwise we copy the file
 283                  // {@link repository::copy_to_area()}.
 284                  $fileinfo = $repo->copy_to_area($reference, $record, $maxbytes, $areamaxbytes);
 285  
 286                  ajax_check_captured_output();
 287                  echo json_encode($fileinfo);
 288                  die;
 289              } else {
 290                  // Download file to moodle.
 291                  $downloadedfile = $repo->get_file($reference, $saveas_filename);
 292  
 293                  if (!empty($downloadedfile['newfilename'])) {
 294                      $record->filename = $downloadedfile['newfilename'];
 295                  }
 296                  if (empty($downloadedfile['path'])) {
 297                      $err->error = get_string('cannotdownload', 'repository');
 298                      die(json_encode($err));
 299                  }
 300  
 301                  // Check if exceed maxbytes.
 302                  if ($maxbytes != -1 && filesize($downloadedfile['path']) > $maxbytes) {
 303                      $maxbytesdisplay = display_size($maxbytes, 0);
 304                      throw new file_exception('maxbytesfile', (object) array('file' => $record->filename,
 305                                                                              'size' => $maxbytesdisplay));
 306                  }
 307  
 308                  // Check if we exceed the max bytes of the area.
 309                  if (file_is_draft_area_limit_reached($itemid, $areamaxbytes, filesize($downloadedfile['path']))) {
 310                      throw new file_exception('maxareabytes');
 311                  }
 312                  // Ensure the user does not upload too many draft files in a short period.
 313                  if (file_is_draft_areas_limit_reached($USER->id)) {
 314                      throw new file_exception('maxdraftitemids');
 315                  }
 316  
 317                  $info = repository::move_to_filepool($downloadedfile['path'], $record);
 318                  if (empty($info)) {
 319                      $info['e'] = get_string('error', 'moodle');
 320                  }
 321              }
 322              ajax_check_captured_output();
 323              echo json_encode($info);
 324              die;
 325          }
 326          break;
 327      case 'upload':
 328          $result = $repo->upload($saveas_filename, $maxbytes);
 329          ajax_check_captured_output();
 330          echo json_encode($result);
 331          break;
 332  
 333      case 'overwrite':
 334          // existing file
 335          $filepath    = required_param('existingfilepath', PARAM_PATH);
 336          $filename    = required_param('existingfilename', PARAM_FILE);
 337          // user added file which needs to replace the existing file
 338          $newfilepath = required_param('newfilepath', PARAM_PATH);
 339          $newfilename = required_param('newfilename', PARAM_FILE);
 340  
 341          $info = repository::overwrite_existing_draftfile($itemid, $filepath, $filename, $newfilepath, $newfilename);
 342          ajax_check_captured_output();
 343          echo json_encode($info);
 344          break;
 345  
 346      case 'deletetmpfile':
 347          // delete tmp file
 348          $newfilepath = required_param('newfilepath', PARAM_PATH);
 349          $newfilename = required_param('newfilename', PARAM_FILE);
 350          ajax_check_captured_output();
 351          echo json_encode(repository::delete_tempfile_from_draft($itemid, $newfilepath, $newfilename));
 352  
 353          break;
 354  }