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 39 and 401]

   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   * Provides the {@link MoodleQuickForm_filetypes} class.
  19   *
  20   * @package   core_form
  21   * @copyright 2016 Jonathon Fowler <fowlerj@usq.edu.au>
  22   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  use core_form\filetypes_util;
  26  
  27  defined('MOODLE_INTERNAL') || die;
  28  
  29  global $CFG;
  30  require_once($CFG->dirroot.'/lib/form/group.php');
  31  
  32  /**
  33   * File types and type groups selection form element.
  34   *
  35   * @package   core_form
  36   * @category  form
  37   * @copyright 2016 Jonathon Fowler <fowlerj@usq.edu.au>
  38   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  39   */
  40  class MoodleQuickForm_filetypes extends MoodleQuickForm_group {
  41  
  42      /** @var array Allow selection from these file types only. */
  43      protected $onlytypes = [];
  44  
  45      /** @var bool Allow selection of 'All file types' (will be stored as '*'). */
  46      protected $allowall = true;
  47  
  48      /** @var bool Skip implicit validation against known file types. */
  49      protected $allowunknown = false;
  50  
  51      /** @var core_form\filetypes_util instance to use as a helper. */
  52      protected $util = null;
  53  
  54      /**
  55       * Constructor
  56       *
  57       * @param string $elementname Element's name
  58       * @param string $elementlabel Label(s) for an element
  59       * @param array $options element options:
  60       *   'onlytypes': Allow selection from these file types only; for example ['onlytypes' => ['web_image']].
  61       *   'allowall': Allow to select 'All file types', defaults to true. Does not apply with onlytypes are set.
  62       *   'allowunknown': Skip implicit validation against the list of known file types.
  63       * @param array|string $attributes Either a typical HTML attribute string or an associative array
  64       */
  65      public function __construct($elementname = null, $elementlabel = null, $options = null, $attributes = null) {
  66  
  67          parent::__construct($elementname, $elementlabel);
  68          $this->_type = 'filetypes';
  69  
  70          // Hard-frozen elements do not get the name populated automatically,
  71          // which leads to PHP notice. Add it explicitly here.
  72          $this->setAttributes(array('name' => $elementname));
  73          $this->updateAttributes($attributes);
  74  
  75          if (is_array($options) && $options) {
  76              if (array_key_exists('onlytypes', $options) && is_array($options['onlytypes'])) {
  77                  $this->onlytypes = $options['onlytypes'];
  78              }
  79              if (!$this->onlytypes && array_key_exists('allowall', $options)) {
  80                  $this->allowall = (bool)$options['allowall'];
  81              }
  82              if (array_key_exists('allowunknown', $options)) {
  83                  $this->allowunknown = (bool)$options['allowunknown'];
  84              }
  85          }
  86  
  87          $this->util = new filetypes_util();
  88      }
  89  
  90      /**
  91       * Assemble the elements of the form control.
  92       */
  93      public function _createElements() {
  94  
  95          $this->_generateId();
  96  
  97          $this->setElements([
  98              $this->createFormElement('text', 'filetypes', $this->getLabel(), [
  99                  'id' => $this->getAttribute('id'),
 100              ]),
 101  
 102              $this->createFormElement('static', 'browser', null,
 103                  '<span data-filetypesbrowser="'.$this->getAttribute('id').'"></span>'),
 104  
 105              $this->createFormElement('static', 'descriptions', null,
 106                  '<div data-filetypesdescriptions="'.$this->getAttribute('id').'"></div>')
 107          ]);
 108      }
 109  
 110      /**
 111       * Return the selected file types.
 112       *
 113       * @param array $submitted submitted values
 114       * @param bool $assoc if true the retured value is associated array
 115       * @return array
 116       */
 117      public function exportValue(&$submitted, $assoc = false) {
 118  
 119          $value = '';
 120          $filetypeselement = null;
 121  
 122          foreach ($this->_elements as $key => $element) {
 123              if ($element->_attributes['name'] === 'filetypes') {
 124                  $filetypeselement = $this->_elements[$key];
 125              }
 126          }
 127  
 128          if ($filetypeselement) {
 129              $formval = $filetypeselement->exportValue($submitted[$this->getName()], false);
 130              if ($formval) {
 131                  $value = $this->util->normalize_file_types($formval);
 132                  if ($value === ['*'] && !$this->allowall) {
 133                      $value = [];
 134                  }
 135                  $value = implode(',', $value);
 136              }
 137          }
 138  
 139          return $this->_prepareValue($value, $assoc);
 140      }
 141  
 142      /**
 143       * Accepts a renderer (called shortly before the renderer's toHtml() method).
 144       *
 145       * @param HTML_QuickForm_Renderer $renderer An HTML_QuickForm_Renderer object
 146       * @param bool $required Whether a group is required
 147       * @param string $error An error message associated with a group
 148       */
 149      public function accept(&$renderer, $required = false, $error = null) {
 150          global $PAGE;
 151  
 152          $PAGE->requires->js_call_amd('core_form/filetypes', 'init', [
 153              $this->getAttribute('id'),
 154              $this->getLabel(),
 155              $this->onlytypes,
 156              $this->allowall,
 157          ]);
 158  
 159          if ($this->isFrozen()) {
 160              // Don't render the choose button if the control is frozen.
 161              foreach ($this->_elements as $key => $element) {
 162                  if ($element->_attributes['name'] === 'browser') {
 163                      unset($this->_elements[$key]);
 164                  }
 165              }
 166          }
 167  
 168          parent::accept($renderer, $required, $error);
 169      }
 170  
 171      /**
 172       * Called by HTML_QuickForm whenever form event is made on this element
 173       *
 174       * @param string $event Name of event
 175       * @param mixed $arg event arguments
 176       * @param object $caller calling object
 177       * @return bool
 178       */
 179      public function onQuickFormEvent($event, $arg, &$caller) {
 180          global $OUTPUT;
 181  
 182          switch ($event) {
 183              case 'updateValue':
 184                  $value = $this->_findValue($caller->_constantValues);
 185                  if (null === $value) {
 186                      if ($caller->isSubmitted()) {
 187                          $value = $this->_findValue($caller->_submitValues);
 188                      } else {
 189                          $value = (string)$this->_findValue($caller->_defaultValues);
 190                      }
 191                  }
 192                  if (!is_array($value)) {
 193                      $value = array('filetypes' => $value);
 194                  }
 195                  if ($value['filetypes'] !== null) {
 196                      $filetypes = $this->util->normalize_file_types($value['filetypes']);
 197                      if ($filetypes === ['*'] && !$this->allowall) {
 198                          $filetypes = [];
 199                      }
 200                      $value['descriptions'] = '<div data-filetypesdescriptions="'.$this->getAttribute('id').'">' .
 201                          $OUTPUT->render_from_template('core_form/filetypes-descriptions',
 202                              $this->util->describe_file_types($filetypes)).'</div>';
 203                  }
 204                  $this->setValue($value);
 205                  return true;
 206                  break;
 207  
 208          }
 209  
 210          return parent::onQuickFormEvent($event, $arg, $caller);
 211      }
 212  
 213      /**
 214       * Check that the submitted list contains only known and allowed file types.
 215       *
 216       * The validation obeys the element options 'allowall', 'allowunknown' and
 217       * 'onlytypes' passed when creating the element.
 218       *
 219       * @param array $value Submitted value.
 220       * @return string|null Validation error message or null.
 221       */
 222      public function validateSubmitValue($value) {
 223  
 224          $value = $value ?? ['filetypes' => null]; // A null $value can arrive here. Coalesce, creating the default array.
 225  
 226          if (!$this->allowall) {
 227              // Assert that there is an actual list provided.
 228              $normalized = $this->util->normalize_file_types($value['filetypes']);
 229              if (empty($normalized) || $normalized == ['*']) {
 230                  return get_string('filetypesnotall', 'core_form');
 231              }
 232          }
 233  
 234          if (!$this->allowunknown) {
 235              // Assert that all file types are known.
 236              $unknown = $this->util->get_unknown_file_types($value['filetypes']);
 237  
 238              if ($unknown) {
 239                  return get_string('filetypesunknown', 'core_form', implode(', ', $unknown));
 240              }
 241          }
 242  
 243          if ($this->onlytypes) {
 244              // Assert that all file types are allowed here.
 245              $notlisted = $this->util->get_not_listed($value['filetypes'], $this->onlytypes);
 246  
 247              if ($notlisted) {
 248                  return get_string('filetypesnotallowed', 'core_form', implode(', ', $notlisted));
 249              }
 250          }
 251  
 252          return;
 253      }
 254  }