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.
/lib/form/ -> tags.php (source)

Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 400 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  /**
  19   * Tag autocomplete field.
  20   *
  21   * Contains HTML class for editing tags, both standard and not.
  22   *
  23   * @package   core_form
  24   * @copyright 2009 Tim Hunt
  25   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  26   */
  27  
  28  global $CFG;
  29  require_once($CFG->libdir . '/form/autocomplete.php');
  30  
  31  /**
  32   * Form field type for editing tags.
  33   *
  34   * HTML class for editing tags, both standard and not.
  35   *
  36   * @package   core_form
  37   * @copyright 2009 Tim Hunt
  38   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  39   */
  40  class MoodleQuickForm_tags extends MoodleQuickForm_autocomplete {
  41      /**
  42       * Inidcates that the user should be the usual interface, with the official
  43       * tags listed seprately, and a text box where they can type anything.
  44       * @deprecated since 3.1
  45       * @var int
  46       */
  47      const DEFAULTUI = 'defaultui';
  48  
  49      /**
  50       * Indicates that the user should only be allowed to select official tags.
  51       * @deprecated since 3.1
  52       * @var int
  53       */
  54      const ONLYOFFICIAL = 'onlyofficial';
  55  
  56      /**
  57       * Indicates that the user should just be given a text box to type in (they
  58       * can still type official tags though.
  59       * @deprecated since 3.1
  60       * @var int
  61       */
  62      const NOOFFICIAL = 'noofficial';
  63  
  64      /**
  65       * @var boolean $showstandard Standard tags suggested? (if not, then don't show link to manage standard tags).
  66       */
  67      protected $showstandard = false;
  68  
  69      /**
  70       * Options passed when creating an element.
  71       * @var array
  72       */
  73      protected $tagsoptions = array();
  74  
  75      /**
  76       * Constructor
  77       *
  78       * @param string $elementName Element name
  79       * @param mixed $elementLabel Label(s) for an element
  80       * @param array $options Options to control the element's display
  81       * @param mixed $attributes Either a typical HTML attribute string or an associative array.
  82       */
  83      public function __construct($elementName = null, $elementLabel = null, $options = array(), $attributes = null) {
  84          $validoptions = array();
  85  
  86          if (!empty($options)) {
  87              // Only execute it when the element was created and $options has values set by user.
  88              // In onQuickFormEvent() we make sure that $options is not empty even if developer left it empty.
  89              $showstandard = core_tag_tag::BOTH_STANDARD_AND_NOT;
  90              if (isset($options['showstandard'])) {
  91                  $showstandard = $options['showstandard'];
  92              } else if (isset($options['display'])) {
  93                  debugging('Option "display" is deprecated, each tag area can be configured to show standard tags or not ' .
  94                      'by admin or manager. If it is necessary for the developer to override it, please use "showstandard" option',
  95                      DEBUG_DEVELOPER);
  96                  if ($options['display'] === self::NOOFFICIAL) {
  97                      $showstandard = core_tag_tag::HIDE_STANDARD;
  98                  } else if ($options['display'] === self::ONLYOFFICIAL) {
  99                      $showstandard = core_tag_tag::STANDARD_ONLY;
 100                  }
 101              } else if (!empty($options['component']) && !empty($options['itemtype'])) {
 102                  $showstandard = core_tag_area::get_showstandard($options['component'], $options['itemtype']);
 103              }
 104  
 105              $this->tagsoptions = $options;
 106  
 107              $this->showstandard = ($showstandard != core_tag_tag::HIDE_STANDARD);
 108              if ($this->showstandard) {
 109                  $validoptions = $this->load_standard_tags();
 110              }
 111              // Option 'tags' allows us to type new tags.
 112              $attributes['tags'] = ($showstandard != core_tag_tag::STANDARD_ONLY);
 113              $attributes['multiple'] = 'multiple';
 114              $attributes['placeholder'] = get_string('entertags', 'tag');
 115              $attributes['showsuggestions'] = $this->showstandard;
 116          }
 117  
 118  
 119          parent::__construct($elementName, $elementLabel, $validoptions, $attributes);
 120          $this->_type = 'tags';
 121      }
 122  
 123      /**
 124       * Called by HTML_QuickForm whenever form event is made on this element
 125       *
 126       * @param string $event Name of event
 127       * @param mixed $arg event arguments
 128       * @param object $caller calling object
 129       * @return bool
 130       */
 131      public function onQuickFormEvent($event, $arg, &$caller) {
 132          if ($event === 'createElement') {
 133              if (!is_array($arg[2])) {
 134                  $arg[2] = [];
 135              }
 136              $arg[2] += array('itemtype' => '', 'component' => '');
 137          }
 138          return parent::onQuickFormEvent($event, $arg, $caller);
 139      }
 140  
 141      /**
 142       * Checks if tagging is enabled for this itemtype
 143       *
 144       * @return boolean
 145       */
 146      protected function is_tagging_enabled() {
 147          if (!empty($this->tagsoptions['itemtype']) && !empty($this->tagsoptions['component'])) {
 148              $enabled = core_tag_tag::is_enabled($this->tagsoptions['component'], $this->tagsoptions['itemtype']);
 149              if ($enabled === false) {
 150                  return false;
 151              }
 152          }
 153          // Backward compatibility with code developed before Moodle 3.0 where itemtype/component were not specified.
 154          return true;
 155      }
 156  
 157      /**
 158       * Old syntax of class constructor. Deprecated in PHP7.
 159       *
 160       * @deprecated since Moodle 3.1
 161       */
 162      public function MoodleQuickForm_tags($elementName = null, $elementLabel = null, $options = array(), $attributes = null) {
 163          debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER);
 164          self::__construct($elementName, $elementLabel, $options, $attributes);
 165      }
 166  
 167      /**
 168       * Finds the tag collection to use for standard tag selector
 169       *
 170       * @return int
 171       */
 172      protected function get_tag_collection() {
 173          if (empty($this->tagsoptions['tagcollid']) && (empty($this->tagsoptions['itemtype']) ||
 174                  empty($this->tagsoptions['component']))) {
 175              debugging('You need to specify \'itemtype\' and \'component\' of the tagged '
 176                      . 'area in the tags form element options',
 177                      DEBUG_DEVELOPER);
 178          }
 179          if (!empty($this->tagsoptions['tagcollid'])) {
 180              return $this->tagsoptions['tagcollid'];
 181          }
 182          if ($this->tagsoptions['itemtype']) {
 183              $this->tagsoptions['tagcollid'] = core_tag_area::get_collection($this->tagsoptions['component'],
 184                      $this->tagsoptions['itemtype']);
 185          } else {
 186              $this->tagsoptions['tagcollid'] = core_tag_collection::get_default();
 187          }
 188          return $this->tagsoptions['tagcollid'];
 189      }
 190  
 191      /**
 192       * Returns HTML for select form element.
 193       *
 194       * @return string
 195       */
 196      function toHtml(){
 197          global $OUTPUT;
 198  
 199          $managelink = '';
 200          if (has_capability('moodle/tag:manage', context_system::instance()) && $this->showstandard) {
 201              $url = new moodle_url('/tag/manage.php', array('tc' => $this->get_tag_collection()));
 202              $managelink = ' ' . $OUTPUT->action_link($url, get_string('managestandardtags', 'tag'));
 203          }
 204  
 205          return parent::toHTML() . $managelink;
 206      }
 207  
 208      /**
 209       * Accepts a renderer
 210       *
 211       * @param HTML_QuickForm_Renderer $renderer An HTML_QuickForm_Renderer object
 212       * @param bool $required Whether a group is required
 213       * @param string $error An error message associated with a group
 214       */
 215      public function accept(&$renderer, $required = false, $error = null) {
 216          if ($this->is_tagging_enabled()) {
 217              $renderer->renderElement($this, $required, $error);
 218          } else {
 219              $renderer->renderHidden($this);
 220          }
 221      }
 222  
 223      /**
 224       * Internal function to load standard tags
 225       */
 226      protected function load_standard_tags() {
 227          global $CFG, $DB;
 228          if (!$this->is_tagging_enabled()) {
 229              return array();
 230          }
 231          $namefield = empty($CFG->keeptagnamecase) ? 'name' : 'rawname';
 232          $tags = $DB->get_records_menu('tag',
 233              array('isstandard' => 1, 'tagcollid' => $this->get_tag_collection()),
 234              $namefield, 'id,' . $namefield);
 235          return array_combine($tags, $tags);
 236      }
 237  
 238      /**
 239       * Returns a 'safe' element's value
 240       *
 241       * @param  array  $submitValues array of submitted values to search
 242       * @param  bool   $assoc        whether to return the value as associative array
 243       * @return mixed
 244       */
 245      public function exportValue(&$submitValues, $assoc = false) {
 246          if (!$this->is_tagging_enabled()) {
 247              return $this->_prepareValue([], $assoc);
 248          }
 249          if ($this->_findValue($submitValues) === '_qf__force_multiselect_submission') {
 250              // Nothing was selected.
 251              return $this->_prepareValue([], $assoc);
 252          }
 253  
 254          // Submitted tag data will be encoded, we want original text.
 255          if (array_key_exists($this->getName(), $submitValues)) {
 256              array_walk($submitValues[$this->getName()], static function(string &$tag): void {
 257                  $tag = html_entity_decode($tag, ENT_COMPAT);
 258              });
 259          }
 260  
 261          return parent::exportValue($submitValues, $assoc);
 262      }
 263  
 264      public function export_for_template(renderer_base $output) {
 265  
 266          $context = parent::export_for_template($output);
 267          if (has_capability('moodle/tag:manage', context_system::instance()) && $this->showstandard) {
 268              $url = new moodle_url('/tag/manage.php', array('tc' => $this->get_tag_collection()));
 269              $context['managestandardtagsurl'] = $url->out(false);
 270          }
 271  
 272          return $context;
 273      }
 274  }