Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 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
}