Differences Between: [Versions 310 and 402] [Versions 311 and 402] [Versions 39 and 402] [Versions 400 and 402]
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 * autocomplete type form element 20 * 21 * Contains HTML class for a autocomplete type element 22 * 23 * @package core_form 24 * @copyright 2015 Damyon Wiese <damyon@moodle.com> 25 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 26 */ 27 28 global $CFG; 29 30 require_once($CFG->libdir . '/form/select.php'); 31 32 /** 33 * Autocomplete as you type form element 34 * 35 * HTML class for a autocomplete type element 36 * 37 * @package core_form 38 * @copyright 2015 Damyon Wiese <damyon@moodle.com> 39 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 40 */ 41 class MoodleQuickForm_autocomplete extends MoodleQuickForm_select { 42 43 /** @var boolean $tags Should we allow typing new entries to the field? */ 44 protected $tags = false; 45 /** @var string $ajax Name of an AMD module to send/process ajax requests. */ 46 protected $ajax = ''; 47 /** @var string $placeholder Placeholder text for an empty list. */ 48 protected $placeholder = ''; 49 /** @var bool $casesensitive Whether the search has to be case-sensitive. */ 50 protected $casesensitive = false; 51 /** @var bool $showsuggestions Show suggestions by default - but this can be turned off. */ 52 protected $showsuggestions = true; 53 /** @var string $noselectionstring String that is shown when there are no selections. */ 54 protected $noselectionstring = ''; 55 /** @var callable|null Function to call (with existing value) to render it to HTML */ 56 protected $valuehtmlcallback = null; 57 58 /** 59 * constructor 60 * 61 * @param string $elementName Select name attribute 62 * @param mixed $elementLabel Label(s) for the select 63 * @param mixed $options Data to be used to populate options 64 * @param mixed $attributes Either a typical HTML attribute string or an associative array. Special options 65 * "tags", "placeholder", "ajax", "multiple", "casesensitive" are supported. 66 */ 67 public function __construct($elementName=null, $elementLabel=null, $options=null, $attributes=null) { 68 // Even if the constructor gets called twice we do not really want 2x options (crazy forms!). 69 $this->_options = array(); 70 if ($attributes === null) { 71 $attributes = array(); 72 } 73 if (isset($attributes['tags'])) { 74 $this->tags = $attributes['tags']; 75 unset($attributes['tags']); 76 } 77 if (isset($attributes['showsuggestions'])) { 78 $this->showsuggestions = $attributes['showsuggestions']; 79 unset($attributes['showsuggestions']); 80 } 81 $this->placeholder = get_string('search'); 82 if (isset($attributes['placeholder'])) { 83 $this->placeholder = $attributes['placeholder']; 84 unset($attributes['placeholder']); 85 } 86 $this->noselectionstring = get_string('noselection', 'form'); 87 if (isset($attributes['noselectionstring'])) { 88 $this->noselectionstring = $attributes['noselectionstring']; 89 unset($attributes['noselectionstring']); 90 } 91 92 if (isset($attributes['ajax'])) { 93 $this->ajax = $attributes['ajax']; 94 unset($attributes['ajax']); 95 } 96 if (isset($attributes['casesensitive'])) { 97 $this->casesensitive = $attributes['casesensitive'] ? true : false; 98 unset($attributes['casesensitive']); 99 } 100 if (isset($attributes['valuehtmlcallback'])) { 101 $this->valuehtmlcallback = $attributes['valuehtmlcallback']; 102 unset($attributes['valuehtmlcallback']); 103 } 104 parent::__construct($elementName, $elementLabel, $options, $attributes); 105 106 $this->_type = 'autocomplete'; 107 } 108 109 /** 110 * Old syntax of class constructor. Deprecated in PHP7. 111 * 112 * @deprecated since Moodle 3.1 113 */ 114 public function MoodleQuickForm_autocomplete($elementName=null, $elementLabel=null, $options=null, $attributes=null) { 115 debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER); 116 self::__construct($elementName, $elementLabel, $options, $attributes); 117 } 118 119 /** 120 * Returns HTML for select form element. 121 * 122 * @return string 123 */ 124 function toHtml(){ 125 global $PAGE; 126 127 // Enhance the select with javascript. 128 $this->_generateId(); 129 $id = $this->getAttribute('id'); 130 131 if (!$this->isFrozen()) { 132 $PAGE->requires->js_call_amd('core/form-autocomplete', 'enhance', $params = array('#' . $id, $this->tags, $this->ajax, 133 $this->placeholder, $this->casesensitive, $this->showsuggestions, $this->noselectionstring)); 134 } 135 136 $html = parent::toHTML(); 137 138 // Hacky bodge to add in the HTML code to the option tag. There is a nicer 139 // version of this code in the new template version (see export_for_template). 140 if ($this->valuehtmlcallback) { 141 $html = preg_replace_callback('~value="([^"]+)"~', function($matches) { 142 $value = html_entity_decode($matches[1], ENT_COMPAT); 143 $htmlvalue = call_user_func($this->valuehtmlcallback, $value); 144 if ($htmlvalue !== false) { 145 return $matches[0] . ' data-html="' . s($htmlvalue) . '"'; 146 } else { 147 return $matches[0]; 148 } 149 }, $html); 150 } 151 152 return $html; 153 } 154 155 /** 156 * Search the current list of options to see if there are any options with this value. 157 * @param string $value to search 158 * @return boolean 159 */ 160 function optionExists($value) { 161 foreach ($this->_options as $option) { 162 if (isset($option['attr']['value']) && ($option['attr']['value'] == $value)) { 163 return true; 164 } 165 } 166 return false; 167 } 168 169 /** 170 * Set the value of this element. If values can be added or are unknown, we will 171 * make sure they exist in the options array. 172 * @param mixed string|array $value The value to set. 173 * @return boolean 174 */ 175 function setValue($value) { 176 $values = (array) $value; 177 foreach ($values as $onevalue) { 178 if (($this->tags || $this->ajax) && 179 (!$this->optionExists($onevalue)) && 180 ($onevalue !== '_qf__force_multiselect_submission')) { 181 $this->addOption($onevalue, $onevalue); 182 } 183 } 184 return parent::setValue($value); 185 } 186 187 /** 188 * Returns a 'safe' element's value 189 * 190 * @param array array of submitted values to search 191 * @param bool whether to return the value as associative array 192 * @access public 193 * @return mixed 194 */ 195 function exportValue(&$submitValues, $assoc = false) { 196 if ($this->ajax || $this->tags) { 197 // When this was an ajax request, we do not know the allowed list of values. 198 $value = $this->_findValue($submitValues); 199 if (null === $value) { 200 $value = $this->getValue(); 201 } 202 // Quickforms inserts a duplicate element in the form with 203 // this value so that a value is always submitted for this form element. 204 // Normally this is cleaned as a side effect of it not being a valid option, 205 // but in this case we need to detect and skip it manually. 206 if ($value === '_qf__force_multiselect_submission' || $value === null) { 207 $value = $this->getMultiple() ? [] : ''; 208 } 209 return $this->_prepareValue($value, $assoc); 210 } else { 211 return parent::exportValue($submitValues, $assoc); 212 } 213 } 214 215 /** 216 * Called by HTML_QuickForm whenever form event is made on this element 217 * 218 * @param string $event Name of event 219 * @param mixed $arg event arguments 220 * @param object $caller calling object 221 * @return bool 222 */ 223 function onQuickFormEvent($event, $arg, &$caller) 224 { 225 switch ($event) { 226 case 'createElement': 227 $caller->setType($arg[0], PARAM_TAGLIST); 228 break; 229 } 230 return parent::onQuickFormEvent($event, $arg, $caller); 231 } 232 233 public function export_for_template(renderer_base $output) { 234 $this->_generateId(); 235 $context = parent::export_for_template($output); 236 $context['tags'] = !empty($this->tags); 237 $context['ajax'] = $this->ajax; 238 $context['placeholder'] = $this->placeholder; 239 $context['casesensitive'] = !empty($this->casesensitive); 240 $context['showsuggestions'] = !empty($this->showsuggestions); 241 $context['noselectionstring'] = $this->noselectionstring; 242 if ($this->valuehtmlcallback) { 243 foreach ($context['options'] as &$option) { 244 $value = $option['value']; 245 $html = call_user_func($this->valuehtmlcallback, $value); 246 if ($html !== false) { 247 $option['html'] = $html; 248 if ($this->isFrozen()) { 249 $option['text'] = $html; 250 } 251 } 252 } 253 } 254 255 return $context; 256 } 257 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body