Search moodle.org's
Developer Documentation

  • Bug fixes for general core bugs in 3.11.x will end 9 May 2022 (12 months).
  • Bug fixes for security issues in 3.11.x will end 14 November 2022 (18 months).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.
  • Differences Between: [Versions 36 and 311] [Versions 37 and 311]

       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);
     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  }