Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 3.9.x will end* 10 May 2021 (12 months).
  • Bug fixes for security issues in 3.9.x will end* 8 May 2023 (36 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

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

   1  <?php
   2  
   3  /**
   4   * Class
   5   */
   6  class H5peditorFile {
   7    private $result, $field, $interface;
   8    public $type, $name, $path, $mime, $size;
   9  
  10    /**
  11     * Constructor. Process data for file uploaded through the editor.
  12     */
  13    function __construct($interface) {
  14      $field = filter_input(INPUT_POST, 'field', FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES);
  15  
  16      // Check for file upload.
  17      if ($field === NULL || empty($_FILES) || !isset($_FILES['file'])) {
  18        return;
  19      }
  20  
  21      $this->interface = $interface;
  22  
  23      // Create a new result object.
  24      $this->result = new stdClass();
  25  
  26      // Get the field.
  27      $this->field = json_decode($field);
  28  
  29      // Handle temporarily uploaded form file
  30      if (function_exists('finfo_file')) {
  31        $finfo = finfo_open(FILEINFO_MIME_TYPE);
  32        $this->type = finfo_file($finfo, $_FILES['file']['tmp_name']);
  33        finfo_close($finfo);
  34      }
  35      elseif (function_exists('mime_content_type')) {
  36        // Deprecated, only when finfo isn't available.
  37        $this->type = mime_content_type($_FILES['file']['tmp_name']);
  38      }
  39      else {
  40        $this->type = $_FILES['file']['type'];
  41      }
  42  
  43      $this->extension = pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION);
  44      $this->size = $_FILES['file']['size'];
  45    }
  46  
  47    /**
  48     * Indicates if an uploaded file was found or not.
  49     *
  50     * @return boolean
  51     */
  52    public function isLoaded() {
  53      return is_object($this->result);
  54    }
  55  
  56    /**
  57     * Check current file up agains mime types and extensions in the given list.
  58     *
  59     * @param array $mimes List to check against.
  60     * @return boolean
  61     */
  62    public function check($mimes) {
  63      $ext = strtolower($this->extension);
  64      foreach ($mimes as $mime => $extension) {
  65        if (is_array($extension)) {
  66          // Multiple extensions
  67          if (in_array($ext, $extension)) {
  68            $this->type = $mime;
  69            return TRUE;
  70          }
  71        }
  72        elseif (/*$this->type === $mime && */$ext === $extension) {
  73          // TODO: Either remove everything that has to do with mime types, or make it work
  74          // Currently we're experiencing trouble with mime types on different servers...
  75          $this->type = $mime;
  76          return TRUE;
  77        }
  78      }
  79      return FALSE;
  80    }
  81  
  82    /**
  83     * Validate the file.
  84     *
  85     * @return boolean
  86     */
  87    public function validate() {
  88      if (isset($this->result->error)) {
  89        return FALSE;
  90      }
  91  
  92      // Check for field type.
  93      if (!isset($this->field->type)) {
  94        $this->result->error = $this->interface->t('Unable to get field type.');
  95        return FALSE;
  96      }
  97  
  98      $whitelist = explode(' ', $this->interface->getWhitelist(
  99        FALSE,
 100        H5PCore::$defaultContentWhitelist,
 101        H5PCore::$defaultLibraryWhitelistExtras
 102      ));
 103  
 104      // Check if mime type is allowed.
 105      $isValidMime = !isset($this->field->mimes) || in_array($this->type, $this->field->mimes);
 106      $isPhp = substr($this->extension, 0, 3) === 'php';
 107      $isWhitelisted = in_array(strtolower($this->extension), $whitelist);
 108      if (!$isValidMime || !$isWhitelisted || $isPhp) {
 109        $this->result->error = $this->interface->t("File type isn't allowed.");
 110        return FALSE;
 111      }
 112  
 113      // Type specific validations.
 114      switch ($this->field->type) {
 115        default:
 116          $this->result->error = $this->interface->t('Invalid field type.');
 117          return FALSE;
 118  
 119        case 'image':
 120          $allowed = array(
 121            'image/png' => 'png',
 122            'image/jpeg' => array('jpg', 'jpeg'),
 123            'image/gif' => 'gif',
 124          );
 125          if (!$this->check($allowed)) {
 126            $this->result->error = $this->interface->t('Invalid image file format. Use jpg, png or gif.');
 127            return FALSE;
 128          }
 129  
 130          // Image size from temp file
 131          $image = @getimagesize($_FILES['file']['tmp_name']);
 132  
 133          if (!$image) {
 134            $this->result->error = $this->interface->t('File is not an image.');
 135            return FALSE;
 136          }
 137  
 138          $this->result->width = $image[0];
 139          $this->result->height = $image[1];
 140          $this->result->mime = $this->type;
 141          break;
 142  
 143        case 'audio':
 144          $allowed = array(
 145            'audio/mpeg' => 'mp3',
 146            'audio/mp3' => 'mp3',
 147            'audio/mp4' => 'm4a',
 148            'audio/x-wav' => 'wav',
 149            'audio/wav' => 'wav',
 150            //'application/ogg' => 'ogg',
 151            'audio/ogg' => 'ogg',
 152            //'video/ogg' => 'ogg',
 153          );
 154          if (!$this->check($allowed)) {
 155            $this->result->error = $this->interface->t('Invalid audio file format. Use mp3 or wav.');
 156            return FALSE;
 157  
 158          }
 159  
 160          $this->result->mime = $this->type;
 161          break;
 162  
 163        case 'video':
 164          $allowed = array(
 165            'video/mp4' => 'mp4',
 166            'video/webm' => 'webm',
 167           // 'application/ogg' => 'ogv',
 168            'video/ogg' => 'ogv',
 169          );
 170          if (!$this->check($allowed)) {
 171            $this->result->error = $this->interface->t('Invalid video file format. Use mp4 or webm.');
 172            return FALSE;
 173          }
 174  
 175          $this->result->mime = $this->type;
 176          break;
 177  
 178        case 'file':
 179          // TODO: Try to get file extension for type and check that it matches the current extension.
 180          $this->result->mime = $this->type;
 181      }
 182  
 183      return TRUE;
 184    }
 185  
 186    /**
 187     * Get the type of the current file.
 188     *
 189     * @return string
 190     */
 191    public function getType() {
 192      return $this->field->type;
 193    }
 194  
 195    /**
 196     * Get the name of the current file.
 197     *
 198     * @return string
 199     */
 200    public function getName() {
 201      static $name;
 202  
 203      if (empty($name)) {
 204        $name = uniqid($this->field->name . '-');
 205  
 206        $matches = array();
 207        preg_match('/([a-z0-9]{1,})$/i', $_FILES['file']['name'], $matches);
 208        if (isset($matches[0])) {
 209          $name .= '.' . $matches[0];
 210        }
 211      }
 212  
 213      return $name;
 214    }
 215  
 216    /**
 217     * Get result from file processing.
 218     */
 219    public function getResult() {
 220      return json_encode($this->result);
 221    }
 222  
 223    /**
 224     * Print result from file processing.
 225     */
 226    public function printResult() {
 227      $this->result->path = $this->getType() . 's/' . $this->getName() . '#tmp';
 228  
 229      // text/plain is used to support IE
 230      header('Cache-Control: no-cache');
 231      header('Content-Type: text/plain; charset=utf-8');
 232  
 233      print $this->getResult();
 234    }
 235  }