Commit | Line | Data |
---|---|---|
ba21c9d4 | 1 | <?php |
117bd748 PS |
2 | // This file is part of Moodle - http://moodle.org/ |
3 | // | |
ba21c9d4 | 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. | |
117bd748 | 13 | // |
ba21c9d4 | 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 | ||
da6f8763 | 17 | /** |
18 | * formslib.php - library of classes for creating forms in Moodle, based on PEAR QuickForms. | |
da1320da | 19 | * |
20 | * To use formslib then you will want to create a new file purpose_form.php eg. edit_form.php | |
21 | * and you want to name your class something like {modulename}_{purpose}_form. Your class will | |
22 | * extend moodleform overriding abstract classes definition and optionally defintion_after_data | |
23 | * and validation. | |
24 | * | |
25 | * See examples of use of this library in course/edit.php and course/edit_form.php | |
26 | * | |
27 | * A few notes : | |
78bfb562 | 28 | * form definition is used for both printing of form and processing and should be the same |
da1320da | 29 | * for both or you may lose some submitted data which won't be let through. |
30 | * you should be using setType for every form element except select, radio or checkbox | |
31 | * elements, these elements clean themselves. | |
32 | * | |
6c1fd304 RT |
33 | * @package core_form |
34 | * @copyright 2006 Jamie Pratt <me@jamiep.org> | |
35 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
da6f8763 | 36 | */ |
37 | ||
78bfb562 PS |
38 | defined('MOODLE_INTERNAL') || die(); |
39 | ||
40 | /** setup.php includes our hacked pear libs first */ | |
da6f8763 | 41 | require_once 'HTML/QuickForm.php'; |
42 | require_once 'HTML/QuickForm/DHTMLRulesTableless.php'; | |
43 | require_once 'HTML/QuickForm/Renderer/Tableless.php'; | |
7b5702b6 | 44 | require_once 'HTML/QuickForm/Rule.php'; |
da6f8763 | 45 | |
a83ad946 | 46 | require_once $CFG->libdir.'/filelib.php'; |
49292f8c | 47 | |
6c1fd304 RT |
48 | /** |
49 | * EDITOR_UNLIMITED_FILES - hard-coded value for the 'maxfiles' option | |
50 | */ | |
832e13f1 | 51 | define('EDITOR_UNLIMITED_FILES', -1); |
52 | ||
a23f0aaf | 53 | /** |
54 | * Callback called when PEAR throws an error | |
55 | * | |
56 | * @param PEAR_Error $error | |
57 | */ | |
58 | function pear_handle_error($error){ | |
59 | echo '<strong>'.$error->GetMessage().'</strong> '.$error->getUserInfo(); | |
60 | echo '<br /> <strong>Backtrace </strong>:'; | |
61 | print_object($error->backtrace); | |
62 | } | |
63 | ||
66b3302d | 64 | if (!empty($CFG->debug) and ($CFG->debug >= DEBUG_ALL or $CFG->debug == -1)){ |
fabbf439 PS |
65 | //TODO: this is a wrong place to init PEAR! |
66 | $GLOBALS['_PEAR_default_error_mode'] = PEAR_ERROR_CALLBACK; | |
67 | $GLOBALS['_PEAR_default_error_options'] = 'pear_handle_error'; | |
864cc1de | 68 | } |
69 | ||
ba21c9d4 | 70 | /** |
6c1fd304 | 71 | * Initalize javascript for date type form element |
117bd748 | 72 | * |
6c1fd304 | 73 | * @staticvar bool $done make sure it gets initalize once. |
e5473854 | 74 | * @global moodle_page $PAGE |
ba21c9d4 | 75 | */ |
8e7cebb0 | 76 | function form_init_date_js() { |
cf615522 | 77 | global $PAGE; |
8e7cebb0 | 78 | static $done = false; |
79 | if (!$done) { | |
e5473854 SH |
80 | $module = 'moodle-form-dateselector'; |
81 | $function = 'M.form.dateselector.init_date_selectors'; | |
a0658f00 | 82 | $config = array(array( |
1bae1fe5 | 83 | 'firstdayofweek' => get_string('firstdayofweek', 'langconfig'), |
15396bba FM |
84 | 'mon' => date_format_string(strtotime("Monday"), '%a', 99), |
85 | 'tue' => date_format_string(strtotime("Tuesday"), '%a', 99), | |
86 | 'wed' => date_format_string(strtotime("Wednesday"), '%a', 99), | |
87 | 'thu' => date_format_string(strtotime("Thursday"), '%a', 99), | |
88 | 'fri' => date_format_string(strtotime("Friday"), '%a', 99), | |
89 | 'sat' => date_format_string(strtotime("Saturday"), '%a', 99), | |
90 | 'sun' => date_format_string(strtotime("Sunday"), '%a', 99), | |
91 | 'january' => date_format_string(strtotime("January 1"), '%B', 99), | |
92 | 'february' => date_format_string(strtotime("February 1"), '%B', 99), | |
93 | 'march' => date_format_string(strtotime("March 1"), '%B', 99), | |
94 | 'april' => date_format_string(strtotime("April 1"), '%B', 99), | |
95 | 'may' => date_format_string(strtotime("May 1"), '%B', 99), | |
96 | 'june' => date_format_string(strtotime("June 1"), '%B', 99), | |
97 | 'july' => date_format_string(strtotime("July 1"), '%B', 99), | |
98 | 'august' => date_format_string(strtotime("August 1"), '%B', 99), | |
99 | 'september' => date_format_string(strtotime("September 1"), '%B', 99), | |
100 | 'october' => date_format_string(strtotime("October 1"), '%B', 99), | |
101 | 'november' => date_format_string(strtotime("November 1"), '%B', 99), | |
102 | 'december' => date_format_string(strtotime("December 1"), '%B', 99) | |
a0658f00 | 103 | )); |
e5473854 | 104 | $PAGE->requires->yui_module($module, $function, $config); |
8e7cebb0 | 105 | $done = true; |
106 | } | |
107 | } | |
f07b9627 | 108 | |
05f5c40c | 109 | /** |
6c1fd304 RT |
110 | * Wrapper that separates quickforms syntax from moodle code |
111 | * | |
da1320da | 112 | * Moodle specific wrapper that separates quickforms syntax from moodle code. You won't directly |
6073a598 | 113 | * use this class you should write a class definition which extends this class or a more specific |
da1320da | 114 | * subclass such a moodleform_mod for each form you want to display and/or process with formslib. |
115 | * | |
116 | * You will write your own definition() method which performs the form set up. | |
ba21c9d4 | 117 | * |
6c1fd304 RT |
118 | * @package core_form |
119 | * @copyright 2006 Jamie Pratt <me@jamiep.org> | |
ba21c9d4 | 120 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later |
6c1fd304 | 121 | * @todo MDL-19380 rethink the file scanning |
05f5c40c | 122 | */ |
afd63fe5 | 123 | abstract class moodleform { |
6c1fd304 | 124 | /** @var string name of the form */ |
172dd12c | 125 | protected $_formname; // form name |
6c1fd304 RT |
126 | |
127 | /** @var MoodleQuickForm quickform object definition */ | |
172dd12c | 128 | protected $_form; |
6c1fd304 RT |
129 | |
130 | /** @var array globals workaround */ | |
172dd12c | 131 | protected $_customdata; |
6c1fd304 RT |
132 | |
133 | /** @var object definition_after_data executed flag */ | |
172dd12c | 134 | protected $_definition_finalized = false; |
ebd3c7ac | 135 | |
da1320da | 136 | /** |
137 | * The constructor function calls the abstract function definition() and it will then | |
138 | * process and clean and attempt to validate incoming data. | |
139 | * | |
140 | * It will call your custom validate method to validate data and will also check any rules | |
141 | * you have specified in definition using addRule | |
142 | * | |
143 | * The name of the form (id attribute of the form) is automatically generated depending on | |
144 | * the name you gave the class extending moodleform. You should call your class something | |
145 | * like | |
146 | * | |
4f51f48f | 147 | * @param mixed $action the action attribute for the form. If empty defaults to auto detect the |
6c1fd304 RT |
148 | * current url. If a moodle_url object then outputs params as hidden variables. |
149 | * @param mixed $customdata if your form defintion method needs access to data such as $course | |
150 | * $cm, etc. to construct the form definition then pass it in this array. You can | |
151 | * use globals for somethings. | |
da1320da | 152 | * @param string $method if you set this to anything other than 'post' then _GET and _POST will |
153 | * be merged and used as incoming data to the form. | |
154 | * @param string $target target frame for form submission. You will rarely use this. Don't use | |
6c1fd304 | 155 | * it if you don't need to as the target attribute is deprecated in xhtml strict. |
da1320da | 156 | * @param mixed $attributes you can pass a string of html attributes here or an array. |
ba21c9d4 | 157 | * @param bool $editable |
da1320da | 158 | */ |
4f51f48f | 159 | function moodleform($action=null, $customdata=null, $method='post', $target='', $attributes=null, $editable=true) { |
a38a17a1 | 160 | global $CFG, $FULLME; |
13725b37 PS |
161 | // no standard mform in moodle should allow autocomplete with the exception of user signup |
162 | if (empty($attributes)) { | |
163 | $attributes = array('autocomplete'=>'off'); | |
164 | } else if (is_array($attributes)) { | |
165 | $attributes['autocomplete'] = 'off'; | |
166 | } else { | |
167 | if (strpos($attributes, 'autocomplete') === false) { | |
168 | $attributes .= ' autocomplete="off" '; | |
6e9989db PS |
169 | } |
170 | } | |
171 | ||
a23f0aaf | 172 | if (empty($action)){ |
a38a17a1 PS |
173 | // do not rely on PAGE->url here because dev often do not setup $actualurl properly in admin_externalpage_setup() |
174 | $action = strip_querystring($FULLME); | |
175 | if (!empty($CFG->sslproxy)) { | |
176 | // return only https links when using SSL proxy | |
177 | $action = preg_replace('/^http:/', 'https:', $action, 1); | |
178 | } | |
179 | //TODO: use following instead of FULLME - see MDL-33015 | |
180 | //$action = strip_querystring(qualified_me()); | |
a23f0aaf | 181 | } |
3a70ccb8 | 182 | // Assign custom data first, so that get_form_identifier can use it. |
7f40a229 | 183 | $this->_customdata = $customdata; |
3a70ccb8 RT |
184 | $this->_formname = $this->get_form_identifier(); |
185 | ||
66491cf1 | 186 | $this->_form = new MoodleQuickForm($this->_formname, $method, $action, $target, $attributes); |
4f51f48f | 187 | if (!$editable){ |
188 | $this->_form->hardFreeze(); | |
189 | } | |
7f40a229 | 190 | |
191 | $this->definition(); | |
192 | ||
193 | $this->_form->addElement('hidden', 'sesskey', null); // automatic sesskey protection | |
d18e0fe6 | 194 | $this->_form->setType('sesskey', PARAM_RAW); |
7f40a229 | 195 | $this->_form->setDefault('sesskey', sesskey()); |
5bc97c98 | 196 | $this->_form->addElement('hidden', '_qf__'.$this->_formname, null); // form submission marker |
d18e0fe6 | 197 | $this->_form->setType('_qf__'.$this->_formname, PARAM_RAW); |
5bc97c98 | 198 | $this->_form->setDefault('_qf__'.$this->_formname, 1); |
199 | $this->_form->_setDefaultRuleMessages(); | |
7f40a229 | 200 | |
201 | // we have to know all input types before processing submission ;-) | |
202 | $this->_process_submission($method); | |
7f40a229 | 203 | } |
05f5c40c | 204 | |
3a70ccb8 RT |
205 | /** |
206 | * It should returns unique identifier for the form. | |
207 | * Currently it will return class name, but in case two same forms have to be | |
208 | * rendered on same page then override function to get unique form identifier. | |
209 | * e.g This is used on multiple self enrollments page. | |
210 | * | |
211 | * @return string form identifier. | |
212 | */ | |
213 | protected function get_form_identifier() { | |
214 | return get_class($this); | |
215 | } | |
216 | ||
2c412890 | 217 | /** |
da1320da | 218 | * To autofocus on first form element or first element with error. |
2c412890 | 219 | * |
8dec2253 | 220 | * @param string $name if this is set then the focus is forced to a field with this name |
6c1fd304 RT |
221 | * @return string javascript to select form element with first error or |
222 | * first element if no errors. Use this as a parameter | |
223 | * when calling print_header | |
2c412890 | 224 | */ |
46f3921e | 225 | function focus($name=NULL) { |
9403060a | 226 | $form =& $this->_form; |
46f3921e | 227 | $elkeys = array_keys($form->_elementIndex); |
228 | $error = false; | |
9403060a | 229 | if (isset($form->_errors) && 0 != count($form->_errors)){ |
230 | $errorkeys = array_keys($form->_errors); | |
231 | $elkeys = array_intersect($elkeys, $errorkeys); | |
46f3921e | 232 | $error = true; |
2c412890 | 233 | } |
46f3921e | 234 | |
235 | if ($error or empty($name)) { | |
236 | $names = array(); | |
237 | while (empty($names) and !empty($elkeys)) { | |
238 | $el = array_shift($elkeys); | |
239 | $names = $form->_getElNamesRecursive($el); | |
240 | } | |
241 | if (!empty($names)) { | |
242 | $name = array_shift($names); | |
243 | } | |
8dec2253 | 244 | } |
46f3921e | 245 | |
246 | $focus = ''; | |
247 | if (!empty($name)) { | |
248 | $focus = 'forms[\''.$form->getAttribute('id').'\'].elements[\''.$name.'\']'; | |
9403060a | 249 | } |
46f3921e | 250 | |
9403060a | 251 | return $focus; |
252 | } | |
7f40a229 | 253 | |
05f5c40c | 254 | /** |
255 | * Internal method. Alters submitted data to be suitable for quickforms processing. | |
256 | * Must be called when the form is fully set up. | |
ba21c9d4 | 257 | * |
6c1fd304 | 258 | * @param string $method name of the method which alters submitted data |
05f5c40c | 259 | */ |
7f40a229 | 260 | function _process_submission($method) { |
261 | $submission = array(); | |
262 | if ($method == 'post') { | |
263 | if (!empty($_POST)) { | |
264 | $submission = $_POST; | |
265 | } | |
266 | } else { | |
267 | $submission = array_merge_recursive($_GET, $_POST); // emulate handling of parameters in xxxx_param() | |
268 | } | |
269 | ||
270 | // following trick is needed to enable proper sesskey checks when using GET forms | |
5bc97c98 | 271 | // the _qf__.$this->_formname serves as a marker that form was actually submitted |
272 | if (array_key_exists('_qf__'.$this->_formname, $submission) and $submission['_qf__'.$this->_formname] == 1) { | |
7f40a229 | 273 | if (!confirm_sesskey()) { |
c3f9ee10 | 274 | print_error('invalidsesskey'); |
7f40a229 | 275 | } |
05f5c40c | 276 | $files = $_FILES; |
7f40a229 | 277 | } else { |
278 | $submission = array(); | |
05f5c40c | 279 | $files = array(); |
7f40a229 | 280 | } |
b45ba7f6 | 281 | $this->detectMissingSetType(); |
7f40a229 | 282 | |
05f5c40c | 283 | $this->_form->updateSubmission($submission, $files); |
7f40a229 | 284 | } |
285 | ||
05f5c40c | 286 | /** |
f728d49b PS |
287 | * Internal method. Validates all old-style deprecated uploaded files. |
288 | * The new way is to upload files via repository api. | |
117bd748 | 289 | * |
6c1fd304 | 290 | * @param array $files list of files to be validated |
ba21c9d4 | 291 | * @return bool|array Success or an array of errors |
05f5c40c | 292 | */ |
89489cfe | 293 | function _validate_files(&$files) { |
172dd12c | 294 | global $CFG, $COURSE; |
295 | ||
89489cfe | 296 | $files = array(); |
297 | ||
49292f8c | 298 | if (empty($_FILES)) { |
299 | // we do not need to do any checks because no files were submitted | |
89489cfe | 300 | // note: server side rules do not work for files - use custom verification in validate() instead |
49292f8c | 301 | return true; |
302 | } | |
49292f8c | 303 | |
172dd12c | 304 | $errors = array(); |
305 | $filenames = array(); | |
49292f8c | 306 | |
307 | // now check that we really want each file | |
308 | foreach ($_FILES as $elname=>$file) { | |
172dd12c | 309 | $required = $this->_form->isElementRequired($elname); |
89489cfe | 310 | |
172dd12c | 311 | if ($file['error'] == 4 and $file['size'] == 0) { |
312 | if ($required) { | |
313 | $errors[$elname] = get_string('required'); | |
49292f8c | 314 | } |
172dd12c | 315 | unset($_FILES[$elname]); |
316 | continue; | |
317 | } | |
318 | ||
a83ad946 | 319 | if (!empty($file['error'])) { |
320 | $errors[$elname] = file_get_upload_error($file['error']); | |
172dd12c | 321 | unset($_FILES[$elname]); |
322 | continue; | |
323 | } | |
324 | ||
325 | if (!is_uploaded_file($file['tmp_name'])) { | |
326 | // TODO: improve error message | |
327 | $errors[$elname] = get_string('error'); | |
328 | unset($_FILES[$elname]); | |
329 | continue; | |
330 | } | |
331 | ||
332 | if (!$this->_form->elementExists($elname) or !$this->_form->getElementType($elname)=='file') { | |
333 | // hmm, this file was not requested | |
334 | unset($_FILES[$elname]); | |
335 | continue; | |
336 | } | |
337 | ||
338 | /* | |
f728d49b | 339 | // TODO: rethink the file scanning MDL-19380 |
172dd12c | 340 | if ($CFG->runclamonupload) { |
341 | if (!clam_scan_moodle_file($_FILES[$elname], $COURSE)) { | |
342 | $errors[$elname] = $_FILES[$elname]['uploadlog']; | |
343 | unset($_FILES[$elname]); | |
344 | continue; | |
345 | } | |
346 | } | |
347 | */ | |
348 | $filename = clean_param($_FILES[$elname]['name'], PARAM_FILE); | |
349 | if ($filename === '') { | |
350 | // TODO: improve error message - wrong chars | |
351 | $errors[$elname] = get_string('error'); | |
352 | unset($_FILES[$elname]); | |
353 | continue; | |
49292f8c | 354 | } |
172dd12c | 355 | if (in_array($filename, $filenames)) { |
356 | // TODO: improve error message - duplicate name | |
357 | $errors[$elname] = get_string('error'); | |
358 | unset($_FILES[$elname]); | |
359 | continue; | |
360 | } | |
361 | $filenames[] = $filename; | |
362 | $_FILES[$elname]['name'] = $filename; | |
363 | ||
364 | $files[$elname] = $_FILES[$elname]['tmp_name']; | |
49292f8c | 365 | } |
366 | ||
367 | // return errors if found | |
172dd12c | 368 | if (count($errors) == 0){ |
49292f8c | 369 | return true; |
89489cfe | 370 | |
49292f8c | 371 | } else { |
89489cfe | 372 | $files = array(); |
49292f8c | 373 | return $errors; |
374 | } | |
375 | } | |
376 | ||
c6aa3670 RT |
377 | /** |
378 | * Internal method. Validates filepicker and filemanager files if they are | |
379 | * set as required fields. Also, sets the error message if encountered one. | |
380 | * | |
6c1fd304 | 381 | * @return bool|array with errors |
c6aa3670 RT |
382 | */ |
383 | protected function validate_draft_files() { | |
384 | global $USER; | |
385 | $mform =& $this->_form; | |
386 | ||
387 | $errors = array(); | |
388 | //Go through all the required elements and make sure you hit filepicker or | |
389 | //filemanager element. | |
390 | foreach ($mform->_rules as $elementname => $rules) { | |
391 | $elementtype = $mform->getElementType($elementname); | |
392 | //If element is of type filepicker then do validation | |
393 | if (($elementtype == 'filepicker') || ($elementtype == 'filemanager')){ | |
394 | //Check if rule defined is required rule | |
395 | foreach ($rules as $rule) { | |
396 | if ($rule['type'] == 'required') { | |
397 | $draftid = (int)$mform->getSubmitValue($elementname); | |
398 | $fs = get_file_storage(); | |
b0c6dc1c | 399 | $context = context_user::instance($USER->id); |
c6aa3670 RT |
400 | if (!$files = $fs->get_area_files($context->id, 'user', 'draft', $draftid, 'id DESC', false)) { |
401 | $errors[$elementname] = $rule['message']; | |
402 | } | |
403 | } | |
404 | } | |
405 | } | |
406 | } | |
a32dabc2 DS |
407 | // Check all the filemanager elements to make sure they do not have too many |
408 | // files in them. | |
409 | foreach ($mform->_elements as $element) { | |
410 | if ($element->_type == 'filemanager') { | |
411 | $maxfiles = $element->getMaxfiles(); | |
412 | if ($maxfiles > 0) { | |
413 | $draftid = (int)$element->getValue(); | |
414 | $fs = get_file_storage(); | |
415 | $context = context_user::instance($USER->id); | |
416 | $files = $fs->get_area_files($context->id, 'user', 'draft', $draftid, '', false); | |
417 | if (count($files) > $maxfiles) { | |
418 | $errors[$element->getName()] = get_string('err_maxfiles', 'form', $maxfiles); | |
419 | } | |
420 | } | |
421 | } | |
422 | } | |
c6aa3670 RT |
423 | if (empty($errors)) { |
424 | return true; | |
425 | } else { | |
426 | return $errors; | |
427 | } | |
428 | } | |
429 | ||
05f5c40c | 430 | /** |
da1320da | 431 | * Load in existing data as form defaults. Usually new entry defaults are stored directly in |
432 | * form definition (new entry form); this function is used to load in data where values | |
433 | * already exist and data is being edited (edit entry form). | |
05f5c40c | 434 | * |
294ce987 | 435 | * note: $slashed param removed |
436 | * | |
6c1fd304 | 437 | * @param stdClass|array $default_values object or array of default values |
05f5c40c | 438 | */ |
294ce987 | 439 | function set_data($default_values) { |
7f40a229 | 440 | if (is_object($default_values)) { |
441 | $default_values = (array)$default_values; | |
442 | } | |
294ce987 | 443 | $this->_form->setDefaults($default_values); |
7f40a229 | 444 | } |
445 | ||
05f5c40c | 446 | /** |
447 | * Check that form was submitted. Does not check validity of submitted data. | |
448 | * | |
449 | * @return bool true if form properly submitted | |
450 | */ | |
7f40a229 | 451 | function is_submitted() { |
452 | return $this->_form->isSubmitted(); | |
453 | } | |
454 | ||
ba21c9d4 | 455 | /** |
6c1fd304 RT |
456 | * Checks if button pressed is not for submitting the form |
457 | * | |
458 | * @staticvar bool $nosubmit keeps track of no submit button | |
459 | * @return bool | |
ba21c9d4 | 460 | */ |
a23f0aaf | 461 | function no_submit_button_pressed(){ |
462 | static $nosubmit = null; // one check is enough | |
463 | if (!is_null($nosubmit)){ | |
464 | return $nosubmit; | |
465 | } | |
466 | $mform =& $this->_form; | |
467 | $nosubmit = false; | |
f07b9627 | 468 | if (!$this->is_submitted()){ |
469 | return false; | |
470 | } | |
a23f0aaf | 471 | foreach ($mform->_noSubmitButtons as $nosubmitbutton){ |
472 | if (optional_param($nosubmitbutton, 0, PARAM_RAW)){ | |
473 | $nosubmit = true; | |
474 | break; | |
475 | } | |
476 | } | |
477 | return $nosubmit; | |
478 | } | |
479 | ||
480 | ||
05f5c40c | 481 | /** |
482 | * Check that form data is valid. | |
58b7d48f | 483 | * You should almost always use this, rather than {@link validate_defined_fields} |
05f5c40c | 484 | * |
485 | * @return bool true if form data valid | |
486 | */ | |
7f40a229 | 487 | function is_validated() { |
4f51f48f | 488 | //finalize the form definition before any processing |
489 | if (!$this->_definition_finalized) { | |
490 | $this->_definition_finalized = true; | |
491 | $this->definition_after_data(); | |
492 | } | |
493 | ||
6bba6dbb | 494 | return $this->validate_defined_fields(); |
495 | } | |
496 | ||
497 | /** | |
498 | * Validate the form. | |
499 | * | |
58b7d48f SH |
500 | * You almost always want to call {@link is_validated} instead of this |
501 | * because it calls {@link definition_after_data} first, before validating the form, | |
6bba6dbb | 502 | * which is what you want in 99% of cases. |
503 | * | |
504 | * This is provided as a separate function for those special cases where | |
505 | * you want the form validated before definition_after_data is called | |
506 | * for example, to selectively add new elements depending on a no_submit_button press, | |
507 | * but only when the form is valid when the no_submit_button is pressed, | |
508 | * | |
6c1fd304 RT |
509 | * @param bool $validateonnosubmit optional, defaults to false. The default behaviour |
510 | * is NOT to validate the form when a no submit button has been pressed. | |
511 | * pass true here to override this behaviour | |
6bba6dbb | 512 | * |
513 | * @return bool true if form data valid | |
514 | */ | |
515 | function validate_defined_fields($validateonnosubmit=false) { | |
516 | static $validated = null; // one validation is enough | |
517 | $mform =& $this->_form; | |
518 | if ($this->no_submit_button_pressed() && empty($validateonnosubmit)){ | |
9aa022fe | 519 | return false; |
520 | } elseif ($validated === null) { | |
3ba2c187 | 521 | $internal_val = $mform->validate(); |
89489cfe | 522 | |
523 | $files = array(); | |
524 | $file_val = $this->_validate_files($files); | |
c6aa3670 RT |
525 | //check draft files for validation and flag them if required files |
526 | //are not in draft area. | |
527 | $draftfilevalue = $this->validate_draft_files(); | |
528 | ||
529 | if ($file_val !== true && $draftfilevalue !== true) { | |
530 | $file_val = array_merge($file_val, $draftfilevalue); | |
531 | } else if ($draftfilevalue !== true) { | |
532 | $file_val = $draftfilevalue; | |
533 | } //default is file_val, so no need to assign. | |
534 | ||
89489cfe | 535 | if ($file_val !== true) { |
536 | if (!empty($file_val)) { | |
537 | foreach ($file_val as $element=>$msg) { | |
538 | $mform->setElementError($element, $msg); | |
539 | } | |
540 | } | |
541 | $file_val = false; | |
542 | } | |
543 | ||
4287fc0d | 544 | $data = $mform->exportValues(); |
89489cfe | 545 | $moodle_val = $this->validation($data, $files); |
a78890d5 | 546 | if ((is_array($moodle_val) && count($moodle_val)!==0)) { |
547 | // non-empty array means errors | |
548 | foreach ($moodle_val as $element=>$msg) { | |
549 | $mform->setElementError($element, $msg); | |
7f40a229 | 550 | } |
a78890d5 | 551 | $moodle_val = false; |
552 | ||
553 | } else { | |
554 | // anything else means validation ok | |
555 | $moodle_val = true; | |
7f40a229 | 556 | } |
89489cfe | 557 | |
49292f8c | 558 | $validated = ($internal_val and $moodle_val and $file_val); |
7f40a229 | 559 | } |
9aa022fe | 560 | return $validated; |
7f40a229 | 561 | } |
562 | ||
19110c57 | 563 | /** |
564 | * Return true if a cancel button has been pressed resulting in the form being submitted. | |
565 | * | |
6c1fd304 | 566 | * @return bool true if a cancel button has been pressed |
19110c57 | 567 | */ |
568 | function is_cancelled(){ | |
569 | $mform =& $this->_form; | |
a23f0aaf | 570 | if ($mform->isSubmitted()){ |
571 | foreach ($mform->_cancelButtons as $cancelbutton){ | |
572 | if (optional_param($cancelbutton, 0, PARAM_RAW)){ | |
573 | return true; | |
574 | } | |
19110c57 | 575 | } |
576 | } | |
577 | return false; | |
578 | } | |
579 | ||
05f5c40c | 580 | /** |
da1320da | 581 | * Return submitted data if properly submitted or returns NULL if validation fails or |
582 | * if there is no submitted data. | |
172dd12c | 583 | * |
294ce987 | 584 | * note: $slashed param removed |
05f5c40c | 585 | * |
d2b322ae | 586 | * @return object submitted data; NULL if not valid or not submitted or cancelled |
05f5c40c | 587 | */ |
294ce987 | 588 | function get_data() { |
19110c57 | 589 | $mform =& $this->_form; |
3ba2c187 | 590 | |
d2b322ae | 591 | if (!$this->is_cancelled() and $this->is_submitted() and $this->is_validated()) { |
294ce987 | 592 | $data = $mform->exportValues(); |
5bc97c98 | 593 | unset($data['sesskey']); // we do not need to return sesskey |
594 | unset($data['_qf__'.$this->_formname]); // we do not need the submission marker too | |
7f40a229 | 595 | if (empty($data)) { |
596 | return NULL; | |
597 | } else { | |
598 | return (object)$data; | |
599 | } | |
600 | } else { | |
601 | return NULL; | |
602 | } | |
603 | } | |
604 | ||
4f51f48f | 605 | /** |
606 | * Return submitted data without validation or NULL if there is no submitted data. | |
294ce987 | 607 | * note: $slashed param removed |
4f51f48f | 608 | * |
4f51f48f | 609 | * @return object submitted data; NULL if not submitted |
610 | */ | |
294ce987 | 611 | function get_submitted_data() { |
4f51f48f | 612 | $mform =& $this->_form; |
613 | ||
614 | if ($this->is_submitted()) { | |
294ce987 | 615 | $data = $mform->exportValues(); |
4f51f48f | 616 | unset($data['sesskey']); // we do not need to return sesskey |
617 | unset($data['_qf__'.$this->_formname]); // we do not need the submission marker too | |
618 | if (empty($data)) { | |
619 | return NULL; | |
620 | } else { | |
621 | return (object)$data; | |
622 | } | |
623 | } else { | |
624 | return NULL; | |
625 | } | |
626 | } | |
627 | ||
05f5c40c | 628 | /** |
629 | * Save verified uploaded files into directory. Upload process can be customised from definition() | |
ba21c9d4 | 630 | * |
6c1fd304 RT |
631 | * @deprecated since Moodle 2.0 |
632 | * @todo MDL-31294 remove this api | |
633 | * @see moodleform::save_stored_file() | |
634 | * @see moodleform::save_file() | |
635 | * @param string $destination path where file should be stored | |
ba21c9d4 | 636 | * @return bool Always false |
05f5c40c | 637 | */ |
49292f8c | 638 | function save_files($destination) { |
172dd12c | 639 | debugging('Not used anymore, please fix code! Use save_stored_file() or save_file() instead'); |
49292f8c | 640 | return false; |
641 | } | |
2b63df96 | 642 | |
feaf5d06 | 643 | /** |
172dd12c | 644 | * Returns name of uploaded file. |
ba21c9d4 | 645 | * |
6c1fd304 RT |
646 | * @param string $elname first element if null |
647 | * @return string|bool false in case of failure, string if ok | |
feaf5d06 | 648 | */ |
172dd12c | 649 | function get_new_filename($elname=null) { |
4287fc0d | 650 | global $USER; |
651 | ||
172dd12c | 652 | if (!$this->is_submitted() or !$this->is_validated()) { |
653 | return false; | |
654 | } | |
655 | ||
656 | if (is_null($elname)) { | |
657 | if (empty($_FILES)) { | |
658 | return false; | |
659 | } | |
660 | reset($_FILES); | |
661 | $elname = key($_FILES); | |
662 | } | |
4287fc0d | 663 | |
664 | if (empty($elname)) { | |
665 | return false; | |
666 | } | |
667 | ||
668 | $element = $this->_form->getElement($elname); | |
669 | ||
0022e2d7 | 670 | if ($element instanceof MoodleQuickForm_filepicker || $element instanceof MoodleQuickForm_filemanager) { |
4287fc0d | 671 | $values = $this->_form->exportValues($elname); |
672 | if (empty($values[$elname])) { | |
673 | return false; | |
674 | } | |
675 | $draftid = $values[$elname]; | |
676 | $fs = get_file_storage(); | |
b0c6dc1c | 677 | $context = context_user::instance($USER->id); |
64f93798 | 678 | if (!$files = $fs->get_area_files($context->id, 'user', 'draft', $draftid, 'id DESC', false)) { |
4287fc0d | 679 | return false; |
680 | } | |
681 | $file = reset($files); | |
682 | return $file->get_filename(); | |
683 | } | |
684 | ||
172dd12c | 685 | if (!isset($_FILES[$elname])) { |
686 | return false; | |
687 | } | |
688 | ||
689 | return $_FILES[$elname]['name']; | |
feaf5d06 | 690 | } |
691 | ||
b6b1d1ca | 692 | /** |
172dd12c | 693 | * Save file to standard filesystem |
ba21c9d4 | 694 | * |
172dd12c | 695 | * @param string $elname name of element |
696 | * @param string $pathname full path name of file | |
697 | * @param bool $override override file if exists | |
698 | * @return bool success | |
b6b1d1ca | 699 | */ |
172dd12c | 700 | function save_file($elname, $pathname, $override=false) { |
4287fc0d | 701 | global $USER; |
b6b1d1ca | 702 | |
4287fc0d | 703 | if (!$this->is_submitted() or !$this->is_validated()) { |
b6b1d1ca | 704 | return false; |
705 | } | |
172dd12c | 706 | if (file_exists($pathname)) { |
707 | if ($override) { | |
708 | if (!@unlink($pathname)) { | |
709 | return false; | |
710 | } | |
711 | } else { | |
712 | return false; | |
713 | } | |
714 | } | |
4287fc0d | 715 | |
716 | $element = $this->_form->getElement($elname); | |
717 | ||
0022e2d7 | 718 | if ($element instanceof MoodleQuickForm_filepicker || $element instanceof MoodleQuickForm_filemanager) { |
4287fc0d | 719 | $values = $this->_form->exportValues($elname); |
720 | if (empty($values[$elname])) { | |
721 | return false; | |
722 | } | |
723 | $draftid = $values[$elname]; | |
724 | $fs = get_file_storage(); | |
b0c6dc1c | 725 | $context = context_user::instance($USER->id); |
64f93798 | 726 | if (!$files = $fs->get_area_files($context->id, 'user', 'draft', $draftid, 'id DESC', false)) { |
4287fc0d | 727 | return false; |
728 | } | |
729 | $file = reset($files); | |
730 | ||
731 | return $file->copy_content_to($pathname); | |
732 | ||
733 | } else if (isset($_FILES[$elname])) { | |
734 | return copy($_FILES[$elname]['tmp_name'], $pathname); | |
172dd12c | 735 | } |
736 | ||
4287fc0d | 737 | return false; |
172dd12c | 738 | } |
739 | ||
edfd6a5e PS |
740 | /** |
741 | * Returns a temporary file, do not forget to delete after not needed any more. | |
742 | * | |
6c1fd304 RT |
743 | * @param string $elname name of the elmenet |
744 | * @return string|bool either string or false | |
edfd6a5e PS |
745 | */ |
746 | function save_temp_file($elname) { | |
747 | if (!$this->get_new_filename($elname)) { | |
748 | return false; | |
749 | } | |
af9b1444 | 750 | if (!$dir = make_temp_directory('forms')) { |
edfd6a5e PS |
751 | return false; |
752 | } | |
753 | if (!$tempfile = tempnam($dir, 'tempup_')) { | |
754 | return false; | |
755 | } | |
756 | if (!$this->save_file($elname, $tempfile, true)) { | |
757 | // something went wrong | |
758 | @unlink($tempfile); | |
759 | return false; | |
760 | } | |
761 | ||
762 | return $tempfile; | |
763 | } | |
764 | ||
0022e2d7 | 765 | /** |
d3d98a3a DC |
766 | * Get draft files of a form element |
767 | * This is a protected method which will be used only inside moodleforms | |
768 | * | |
d3d98a3a | 769 | * @param string $elname name of element |
6c1fd304 | 770 | * @return array|bool|null |
d3d98a3a DC |
771 | */ |
772 | protected function get_draft_files($elname) { | |
773 | global $USER; | |
774 | ||
775 | if (!$this->is_submitted()) { | |
776 | return false; | |
777 | } | |
778 | ||
779 | $element = $this->_form->getElement($elname); | |
780 | ||
781 | if ($element instanceof MoodleQuickForm_filepicker || $element instanceof MoodleQuickForm_filemanager) { | |
782 | $values = $this->_form->exportValues($elname); | |
783 | if (empty($values[$elname])) { | |
784 | return false; | |
785 | } | |
786 | $draftid = $values[$elname]; | |
787 | $fs = get_file_storage(); | |
b0c6dc1c | 788 | $context = context_user::instance($USER->id); |
64f93798 | 789 | if (!$files = $fs->get_area_files($context->id, 'user', 'draft', $draftid, 'id DESC', false)) { |
d3d98a3a DC |
790 | return null; |
791 | } | |
792 | return $files; | |
793 | } | |
794 | return null; | |
795 | } | |
796 | ||
172dd12c | 797 | /** |
798 | * Save file to local filesystem pool | |
ba21c9d4 | 799 | * |
172dd12c | 800 | * @param string $elname name of element |
6c1fd304 RT |
801 | * @param int $newcontextid id of context |
802 | * @param string $newcomponent name of the component | |
803 | * @param string $newfilearea name of file area | |
804 | * @param int $newitemid item id | |
805 | * @param string $newfilepath path of file where it get stored | |
806 | * @param string $newfilename use specified filename, if not specified name of uploaded file used | |
807 | * @param bool $overwrite overwrite file if exists | |
808 | * @param int $newuserid new userid if required | |
172dd12c | 809 | * @return mixed stored_file object or false if error; may throw exception if duplicate found |
810 | */ | |
64f93798 | 811 | function save_stored_file($elname, $newcontextid, $newcomponent, $newfilearea, $newitemid, $newfilepath='/', |
49583e9e | 812 | $newfilename=null, $overwrite=false, $newuserid=null) { |
924ddb15 | 813 | global $USER; |
814 | ||
172dd12c | 815 | if (!$this->is_submitted() or !$this->is_validated()) { |
89489cfe | 816 | return false; |
172dd12c | 817 | } |
89489cfe | 818 | |
924ddb15 | 819 | if (empty($newuserid)) { |
820 | $newuserid = $USER->id; | |
89489cfe | 821 | } |
b6b1d1ca | 822 | |
4287fc0d | 823 | $element = $this->_form->getElement($elname); |
824 | $fs = get_file_storage(); | |
172dd12c | 825 | |
4287fc0d | 826 | if ($element instanceof MoodleQuickForm_filepicker) { |
827 | $values = $this->_form->exportValues($elname); | |
828 | if (empty($values[$elname])) { | |
829 | return false; | |
830 | } | |
831 | $draftid = $values[$elname]; | |
b0c6dc1c | 832 | $context = context_user::instance($USER->id); |
64f93798 | 833 | if (!$files = $fs->get_area_files($context->id, 'user' ,'draft', $draftid, 'id DESC', false)) { |
4287fc0d | 834 | return false; |
835 | } | |
836 | $file = reset($files); | |
837 | if (is_null($newfilename)) { | |
838 | $newfilename = $file->get_filename(); | |
839 | } | |
172dd12c | 840 | |
4287fc0d | 841 | if ($overwrite) { |
64f93798 | 842 | if ($oldfile = $fs->get_file($newcontextid, $newcomponent, $newfilearea, $newitemid, $newfilepath, $newfilename)) { |
4287fc0d | 843 | if (!$oldfile->delete()) { |
844 | return false; | |
845 | } | |
924ddb15 | 846 | } |
847 | } | |
848 | ||
64f93798 | 849 | $file_record = array('contextid'=>$newcontextid, 'component'=>$newcomponent, 'filearea'=>$newfilearea, 'itemid'=>$newitemid, |
4287fc0d | 850 | 'filepath'=>$newfilepath, 'filename'=>$newfilename, 'userid'=>$newuserid); |
851 | return $fs->create_file_from_storedfile($file_record, $file); | |
924ddb15 | 852 | |
4287fc0d | 853 | } else if (isset($_FILES[$elname])) { |
854 | $filename = is_null($newfilename) ? $_FILES[$elname]['name'] : $newfilename; | |
172dd12c | 855 | |
4287fc0d | 856 | if ($overwrite) { |
64f93798 | 857 | if ($oldfile = $fs->get_file($newcontextid, $newcomponent, $newfilearea, $newitemid, $newfilepath, $newfilename)) { |
4287fc0d | 858 | if (!$oldfile->delete()) { |
859 | return false; | |
860 | } | |
861 | } | |
924ddb15 | 862 | } |
4287fc0d | 863 | |
64f93798 | 864 | $file_record = array('contextid'=>$newcontextid, 'component'=>$newcomponent, 'filearea'=>$newfilearea, 'itemid'=>$newitemid, |
4287fc0d | 865 | 'filepath'=>$newfilepath, 'filename'=>$newfilename, 'userid'=>$newuserid); |
866 | return $fs->create_file_from_pathname($file_record, $_FILES[$elname]['tmp_name']); | |
924ddb15 | 867 | } |
868 | ||
869 | return false; | |
172dd12c | 870 | } |
871 | ||
872 | /** | |
873 | * Get content of uploaded file. | |
ba21c9d4 | 874 | * |
6c1fd304 RT |
875 | * @param string $elname name of file upload element |
876 | * @return string|bool false in case of failure, string if ok | |
172dd12c | 877 | */ |
878 | function get_file_content($elname) { | |
4287fc0d | 879 | global $USER; |
880 | ||
172dd12c | 881 | if (!$this->is_submitted() or !$this->is_validated()) { |
882 | return false; | |
883 | } | |
884 | ||
4287fc0d | 885 | $element = $this->_form->getElement($elname); |
886 | ||
0022e2d7 | 887 | if ($element instanceof MoodleQuickForm_filepicker || $element instanceof MoodleQuickForm_filemanager) { |
4287fc0d | 888 | $values = $this->_form->exportValues($elname); |
889 | if (empty($values[$elname])) { | |
890 | return false; | |
891 | } | |
892 | $draftid = $values[$elname]; | |
893 | $fs = get_file_storage(); | |
b0c6dc1c | 894 | $context = context_user::instance($USER->id); |
64f93798 | 895 | if (!$files = $fs->get_area_files($context->id, 'user', 'draft', $draftid, 'id DESC', false)) { |
4287fc0d | 896 | return false; |
897 | } | |
898 | $file = reset($files); | |
899 | ||
900 | return $file->get_content(); | |
901 | ||
902 | } else if (isset($_FILES[$elname])) { | |
903 | return file_get_contents($_FILES[$elname]['tmp_name']); | |
b6b1d1ca | 904 | } |
172dd12c | 905 | |
4287fc0d | 906 | return false; |
b6b1d1ca | 907 | } |
908 | ||
05f5c40c | 909 | /** |
910 | * Print html form. | |
911 | */ | |
7f40a229 | 912 | function display() { |
4f51f48f | 913 | //finalize the form definition if not yet done |
914 | if (!$this->_definition_finalized) { | |
915 | $this->_definition_finalized = true; | |
916 | $this->definition_after_data(); | |
917 | } | |
b45ba7f6 | 918 | |
7f40a229 | 919 | $this->_form->display(); |
920 | } | |
921 | ||
d953b184 | 922 | /** |
923 | * Renders the html form (same as display, but returns the result). | |
924 | * | |
925 | * Note that you can only output this rendered result once per page, as | |
926 | * it contains IDs which must be unique. | |
927 | * | |
928 | * @return string HTML code for the form | |
929 | */ | |
930 | public function render() { | |
931 | ob_start(); | |
932 | $this->display(); | |
933 | $out = ob_get_contents(); | |
934 | ob_end_clean(); | |
935 | return $out; | |
936 | } | |
937 | ||
49292f8c | 938 | /** |
6c1fd304 | 939 | * Form definition. Abstract method - always override! |
49292f8c | 940 | */ |
afd63fe5 | 941 | protected abstract function definition(); |
2c412890 | 942 | |
c08ac016 | 943 | /** |
05f5c40c | 944 | * Dummy stub method - override if you need to setup the form depending on current |
beac4717 | 945 | * values. This method is called after definition(), data submission and set_data(). |
05f5c40c | 946 | * All form setup that is dependent on form values should go in here. |
c08ac016 | 947 | */ |
948 | function definition_after_data(){ | |
c08ac016 | 949 | } |
7f40a229 | 950 | |
05f5c40c | 951 | /** |
952 | * Dummy stub method - override if you needed to perform some extra validation. | |
953 | * If there are errors return array of errors ("fieldname"=>"error message"), | |
954 | * otherwise true if ok. | |
38f394b2 | 955 | * |
89489cfe | 956 | * Server side rules do not work for uploaded files, implement serverside rules here if needed. |
957 | * | |
05f5c40c | 958 | * @param array $data array of ("fieldname"=>value) of submitted data |
89489cfe | 959 | * @param array $files array of uploaded files "element_name"=>tmp_file_path |
a78890d5 | 960 | * @return array of "element_name"=>"error_description" if there are errors, |
6c1fd304 | 961 | * or an empty array if everything is OK (true allowed for backwards compatibility too). |
05f5c40c | 962 | */ |
89489cfe | 963 | function validation($data, $files) { |
13ccb7bd | 964 | return array(); |
7f40a229 | 965 | } |
ebd3c7ac | 966 | |
fa2fa5c6 | 967 | /** |
58b7d48f | 968 | * Helper used by {@link repeat_elements()}. |
6c1fd304 | 969 | * |
fa2fa5c6 TH |
970 | * @param int $i the index of this element. |
971 | * @param HTML_QuickForm_element $elementclone | |
03848c45 | 972 | * @param array $namecloned array of names |
fa2fa5c6 TH |
973 | */ |
974 | function repeat_elements_fix_clone($i, $elementclone, &$namecloned) { | |
975 | $name = $elementclone->getName(); | |
976 | $namecloned[] = $name; | |
977 | ||
978 | if (!empty($name)) { | |
979 | $elementclone->setName($name."[$i]"); | |
980 | } | |
981 | ||
982 | if (is_a($elementclone, 'HTML_QuickForm_header')) { | |
983 | $value = $elementclone->_text; | |
984 | $elementclone->setValue(str_replace('{no}', ($i+1), $value)); | |
985 | ||
ebbc5e7b TH |
986 | } else if (is_a($elementclone, 'HTML_QuickForm_submit') || is_a($elementclone, 'HTML_QuickForm_button')) { |
987 | $elementclone->setValue(str_replace('{no}', ($i+1), $elementclone->getValue())); | |
988 | ||
fa2fa5c6 TH |
989 | } else { |
990 | $value=$elementclone->getLabel(); | |
991 | $elementclone->setLabel(str_replace('{no}', ($i+1), $value)); | |
992 | } | |
993 | } | |
994 | ||
616b549a | 995 | /** |
996 | * Method to add a repeating group of elements to a form. | |
997 | * | |
998 | * @param array $elementobjs Array of elements or groups of elements that are to be repeated | |
6c1fd304 | 999 | * @param int $repeats no of times to repeat elements initially |
616b549a | 1000 | * @param array $options Array of options to apply to elements. Array keys are element names. |
6c1fd304 RT |
1001 | * This is an array of arrays. The second sets of keys are the option types for the elements : |
1002 | * 'default' - default value is value | |
1003 | * 'type' - PARAM_* constant is value | |
1004 | * 'helpbutton' - helpbutton params array is value | |
1005 | * 'disabledif' - last three moodleform::disabledIf() | |
1006 | * params are value as an array | |
616b549a | 1007 | * @param string $repeathiddenname name for hidden element storing no of repeats in this form |
1008 | * @param string $addfieldsname name for button to add more fields | |
1009 | * @param int $addfieldsno how many fields to add at a time | |
271ffe3f | 1010 | * @param string $addstring name of button, {no} is replaced by no of blanks that will be added. |
6c1fd304 | 1011 | * @param bool $addbuttoninside if true, don't call closeHeaderBefore($addfieldsname). Default false. |
a23f0aaf | 1012 | * @return int no of repeats of element in this page |
616b549a | 1013 | */ |
6f3b54c8 | 1014 | function repeat_elements($elementobjs, $repeats, $options, $repeathiddenname, |
1015 | $addfieldsname, $addfieldsno=5, $addstring=null, $addbuttoninside=false){ | |
271ffe3f | 1016 | if ($addstring===null){ |
1017 | $addstring = get_string('addfields', 'form', $addfieldsno); | |
1018 | } else { | |
1019 | $addstring = str_ireplace('{no}', $addfieldsno, $addstring); | |
1020 | } | |
ebd3c7ac | 1021 | $repeats = optional_param($repeathiddenname, $repeats, PARAM_INT); |
1022 | $addfields = optional_param($addfieldsname, '', PARAM_TEXT); | |
1023 | if (!empty($addfields)){ | |
1024 | $repeats += $addfieldsno; | |
1025 | } | |
ebd3c7ac | 1026 | $mform =& $this->_form; |
a23f0aaf | 1027 | $mform->registerNoSubmitButton($addfieldsname); |
ebd3c7ac | 1028 | $mform->addElement('hidden', $repeathiddenname, $repeats); |
d18e0fe6 | 1029 | $mform->setType($repeathiddenname, PARAM_INT); |
ebd3c7ac | 1030 | //value not to be overridden by submitted value |
1031 | $mform->setConstants(array($repeathiddenname=>$repeats)); | |
414f7bee | 1032 | $namecloned = array(); |
1033 | for ($i = 0; $i < $repeats; $i++) { | |
ebd3c7ac | 1034 | foreach ($elementobjs as $elementobj){ |
985f0ddd | 1035 | $elementclone = fullclone($elementobj); |
fa2fa5c6 | 1036 | $this->repeat_elements_fix_clone($i, $elementclone, $namecloned); |
ebd3c7ac | 1037 | |
fa2fa5c6 TH |
1038 | if ($elementclone instanceof HTML_QuickForm_group && !$elementclone->_appendName) { |
1039 | foreach ($elementclone->getElements() as $el) { | |
1040 | $this->repeat_elements_fix_clone($i, $el, $namecloned); | |
1041 | } | |
1042 | $elementclone->setLabel(str_replace('{no}', $i + 1, $elementclone->getLabel())); | |
ebd3c7ac | 1043 | } |
7b41a4a9 | 1044 | |
ebd3c7ac | 1045 | $mform->addElement($elementclone); |
1046 | } | |
1047 | } | |
1048 | for ($i=0; $i<$repeats; $i++) { | |
1049 | foreach ($options as $elementname => $elementoptions){ | |
1050 | $pos=strpos($elementname, '['); | |
1051 | if ($pos!==FALSE){ | |
86b600d6 RT |
1052 | $realelementname = substr($elementname, 0, $pos)."[$i]"; |
1053 | $realelementname .= substr($elementname, $pos); | |
ebd3c7ac | 1054 | }else { |
1055 | $realelementname = $elementname."[$i]"; | |
1056 | } | |
1057 | foreach ($elementoptions as $option => $params){ | |
1058 | ||
1059 | switch ($option){ | |
1060 | case 'default' : | |
1851b00b | 1061 | $mform->setDefault($realelementname, str_replace('{no}', $i + 1, $params)); |
ebd3c7ac | 1062 | break; |
ebd3c7ac | 1063 | case 'helpbutton' : |
cd188086 DM |
1064 | $params = array_merge(array($realelementname), $params); |
1065 | call_user_func_array(array(&$mform, 'addHelpButton'), $params); | |
ebd3c7ac | 1066 | break; |
1067 | case 'disabledif' : | |
414f7bee | 1068 | foreach ($namecloned as $num => $name){ |
1069 | if ($params[0] == $name){ | |
1070 | $params[0] = $params[0]."[$i]"; | |
1071 | break; | |
1072 | } | |
1073 | } | |
9aa022fe | 1074 | $params = array_merge(array($realelementname), $params); |
1075 | call_user_func_array(array(&$mform, 'disabledIf'), $params); | |
1076 | break; | |
1077 | case 'rule' : | |
1078 | if (is_string($params)){ | |
1079 | $params = array(null, $params, null, 'client'); | |
1080 | } | |
1081 | $params = array_merge(array($realelementname), $params); | |
1082 | call_user_func_array(array(&$mform, 'addRule'), $params); | |
ebd3c7ac | 1083 | break; |
51070abc RT |
1084 | case 'type' : |
1085 | //Type should be set only once | |
1086 | if (!isset($mform->_types[$elementname])) { | |
1087 | $mform->setType($elementname, $params); | |
1088 | } | |
1089 | break; | |
d23edcf1 | 1090 | |
f160f97c FM |
1091 | case 'expanded': |
1092 | $mform->setExpanded($realelementname, $params); | |
d23edcf1 TH |
1093 | break; |
1094 | ||
1095 | case 'advanced' : | |
1096 | $mform->setAdvanced($realelementname, $params); | |
a4067bfc | 1097 | break; |
ebd3c7ac | 1098 | } |
1099 | } | |
1100 | } | |
1101 | } | |
271ffe3f | 1102 | $mform->addElement('submit', $addfieldsname, $addstring); |
a23f0aaf | 1103 | |
6f3b54c8 | 1104 | if (!$addbuttoninside) { |
1105 | $mform->closeHeaderBefore($addfieldsname); | |
1106 | } | |
ebd3c7ac | 1107 | |
19194f82 | 1108 | return $repeats; |
ebd3c7ac | 1109 | } |
6073a598 | 1110 | |
1111 | /** | |
1112 | * Adds a link/button that controls the checked state of a group of checkboxes. | |
ba21c9d4 | 1113 | * |
6c1fd304 | 1114 | * @param int $groupid The id of the group of advcheckboxes this element controls |
d8e7d1a0 | 1115 | * @param string $text The text of the link. Defaults to selectallornone ("select all/none") |
6c1fd304 RT |
1116 | * @param array $attributes associative array of HTML attributes |
1117 | * @param int $originalValue The original general state of the checkboxes before the user first clicks this element | |
6073a598 | 1118 | */ |
d8e7d1a0 | 1119 | function add_checkbox_controller($groupid, $text = null, $attributes = null, $originalValue = 0) { |
c14584f2 | 1120 | global $CFG, $PAGE; |
d8e7d1a0 | 1121 | |
7f850b23 RT |
1122 | // Name of the controller button |
1123 | $checkboxcontrollername = 'nosubmit_checkbox_controller' . $groupid; | |
1124 | $checkboxcontrollerparam = 'checkbox_controller'. $groupid; | |
1125 | $checkboxgroupclass = 'checkboxgroup'.$groupid; | |
1126 | ||
d8e7d1a0 | 1127 | // Set the default text if none was specified |
6073a598 | 1128 | if (empty($text)) { |
1129 | $text = get_string('selectallornone', 'form'); | |
1130 | } | |
1131 | ||
1132 | $mform = $this->_form; | |
4dd2af12 | 1133 | $selectvalue = optional_param($checkboxcontrollerparam, null, PARAM_INT); |
7f850b23 | 1134 | $contollerbutton = optional_param($checkboxcontrollername, null, PARAM_ALPHAEXT); |
6073a598 | 1135 | |
4dd2af12 SH |
1136 | $newselectvalue = $selectvalue; |
1137 | if (is_null($selectvalue)) { | |
1138 | $newselectvalue = $originalValue; | |
7f850b23 | 1139 | } else if (!is_null($contollerbutton)) { |
4dd2af12 | 1140 | $newselectvalue = (int) !$selectvalue; |
6073a598 | 1141 | } |
7f850b23 | 1142 | // set checkbox state depending on orignal/submitted value by controoler button |
4dd2af12 | 1143 | if (!is_null($contollerbutton) || is_null($selectvalue)) { |
7f850b23 RT |
1144 | foreach ($mform->_elements as $element) { |
1145 | if (($element instanceof MoodleQuickForm_advcheckbox) && | |
91f7f8c1 RT |
1146 | $element->getAttribute('class') == $checkboxgroupclass && |
1147 | !$element->isFrozen()) { | |
4dd2af12 | 1148 | $mform->setConstants(array($element->getName() => $newselectvalue)); |
7f850b23 RT |
1149 | } |
1150 | } | |
1151 | } | |
6073a598 | 1152 | |
4dd2af12 | 1153 | $mform->addElement('hidden', $checkboxcontrollerparam, $newselectvalue, array('id' => "id_".$checkboxcontrollerparam)); |
7f850b23 | 1154 | $mform->setType($checkboxcontrollerparam, PARAM_INT); |
4dd2af12 | 1155 | $mform->setConstants(array($checkboxcontrollerparam => $newselectvalue)); |
172dd12c | 1156 | |
c14584f2 RT |
1157 | $PAGE->requires->yui_module('moodle-form-checkboxcontroller', 'M.form.checkboxcontroller', |
1158 | array( | |
1159 | array('groupid' => $groupid, | |
7f850b23 RT |
1160 | 'checkboxclass' => $checkboxgroupclass, |
1161 | 'checkboxcontroller' => $checkboxcontrollerparam, | |
1162 | 'controllerbutton' => $checkboxcontrollername) | |
c14584f2 RT |
1163 | ) |
1164 | ); | |
1165 | ||
1166 | require_once("$CFG->libdir/form/submit.php"); | |
7f850b23 | 1167 | $submitlink = new MoodleQuickForm_submit($checkboxcontrollername, $attributes); |
172dd12c | 1168 | $mform->addElement($submitlink); |
7f850b23 RT |
1169 | $mform->registerNoSubmitButton($checkboxcontrollername); |
1170 | $mform->setDefault($checkboxcontrollername, $text); | |
6073a598 | 1171 | } |
1172 | ||
a23f0aaf | 1173 | /** |
1d284fbd | 1174 | * Use this method to a cancel and submit button to the end of your form. Pass a param of false |
a23f0aaf | 1175 | * if you don't want a cancel button in your form. If you have a cancel button make sure you |
1176 | * check for it being pressed using is_cancelled() and redirecting if it is true before trying to | |
beac4717 | 1177 | * get data with get_data(). |
a23f0aaf | 1178 | * |
6c1fd304 | 1179 | * @param bool $cancel whether to show cancel button, default true |
a23f0aaf | 1180 | * @param string $submitlabel label for submit button, defaults to get_string('savechanges') |
1181 | */ | |
1d284fbd | 1182 | function add_action_buttons($cancel = true, $submitlabel=null){ |
a23f0aaf | 1183 | if (is_null($submitlabel)){ |
1184 | $submitlabel = get_string('savechanges'); | |
1185 | } | |
1186 | $mform =& $this->_form; | |
1d284fbd | 1187 | if ($cancel){ |
1188 | //when two elements we need a group | |
a23f0aaf | 1189 | $buttonarray=array(); |
1190 | $buttonarray[] = &$mform->createElement('submit', 'submitbutton', $submitlabel); | |
1d284fbd | 1191 | $buttonarray[] = &$mform->createElement('cancel'); |
a23f0aaf | 1192 | $mform->addGroup($buttonarray, 'buttonar', '', array(' '), false); |
4f51f48f | 1193 | $mform->closeHeaderBefore('buttonar'); |
a23f0aaf | 1194 | } else { |
1195 | //no group needed | |
1196 | $mform->addElement('submit', 'submitbutton', $submitlabel); | |
4f51f48f | 1197 | $mform->closeHeaderBefore('submitbutton'); |
a23f0aaf | 1198 | } |
1199 | } | |
572dd8ec SH |
1200 | |
1201 | /** | |
2836e77d | 1202 | * Adds an initialisation call for a standard JavaScript enhancement. |
572dd8ec SH |
1203 | * |
1204 | * This function is designed to add an initialisation call for a JavaScript | |
2836e77d | 1205 | * enhancement that should exist within javascript-static M.form.init_{enhancementname}. |
572dd8ec SH |
1206 | * |
1207 | * Current options: | |
1208 | * - Selectboxes | |
1209 | * - smartselect: Turns a nbsp indented select box into a custom drop down | |
1210 | * control that supports multilevel and category selection. | |
1211 | * $enhancement = 'smartselect'; | |
1212 | * $options = array('selectablecategories' => true|false) | |
1213 | * | |
6c1fd304 RT |
1214 | * @since Moodle 2.0 |
1215 | * @param string|element $element form element for which Javascript needs to be initalized | |
1216 | * @param string $enhancement which init function should be called | |
1217 | * @param array $options options passed to javascript | |
1218 | * @param array $strings strings for javascript | |
572dd8ec SH |
1219 | */ |
1220 | function init_javascript_enhancement($element, $enhancement, array $options=array(), array $strings=null) { | |
1221 | global $PAGE; | |
1222 | if (is_string($element)) { | |
1223 | $element = $this->_form->getElement($element); | |
c7e3e61c | 1224 | } |
572dd8ec SH |
1225 | if (is_object($element)) { |
1226 | $element->_generateId(); | |
1227 | $elementid = $element->getAttribute('id'); | |
1228 | $PAGE->requires->js_init_call('M.form.init_'.$enhancement, array($elementid, $options)); | |
1229 | if (is_array($strings)) { | |
1230 | foreach ($strings as $string) { | |
1231 | if (is_array($string)) { | |
1232 | call_user_method_array('string_for_js', $PAGE->requires, $string); | |
1233 | } else { | |
1234 | $PAGE->requires->string_for_js($string, 'moodle'); | |
1235 | } | |
1236 | } | |
1237 | } | |
1238 | } | |
1239 | } | |
c7e3e61c SH |
1240 | |
1241 | /** | |
1242 | * Returns a JS module definition for the mforms JS | |
6c1fd304 | 1243 | * |
c7e3e61c SH |
1244 | * @return array |
1245 | */ | |
1246 | public static function get_js_module() { | |
1247 | global $CFG; | |
1248 | return array( | |
1249 | 'name' => 'mform', | |
1250 | 'fullpath' => '/lib/form/form.js', | |
ac5e6ca4 | 1251 | 'requires' => array('base', 'node') |
c7e3e61c SH |
1252 | ); |
1253 | } | |
b45ba7f6 DP |
1254 | |
1255 | /** | |
1256 | * Detects elements with missing setType() declerations. | |
1257 | * | |
1258 | * Finds elements in the form which should a PARAM_ type set and throws a | |
1259 | * developer debug warning for any elements without it. This is to reduce the | |
1260 | * risk of potential security issues by developers mistakenly forgetting to set | |
1261 | * the type. | |
1262 | * | |
1263 | * @return void | |
1264 | */ | |
1265 | private function detectMissingSetType() { | |
1266 | if (!debugging('', DEBUG_DEVELOPER)) { | |
1267 | // Only for devs. | |
1268 | return; | |
1269 | } | |
1270 | ||
1271 | $mform = $this->_form; | |
1272 | foreach ($mform->_elements as $element) { | |
1273 | switch ($element->getType()) { | |
1274 | case 'hidden': | |
1275 | case 'text': | |
1276 | case 'url': | |
1277 | $key = $element->getName(); | |
caa30cd6 EL |
1278 | // For repeated elements we need to look for |
1279 | // the "main" type, not for the one present | |
1280 | // on each repetition. All the stuff in formslib | |
1281 | // (repeat_elements(), updateSubmission()... seems | |
1282 | // to work that way. | |
1283 | $pos = strpos($key, '['); | |
1284 | if ($pos !== false) { | |
1285 | $key = substr($key, 0, $pos); | |
1286 | } | |
b45ba7f6 DP |
1287 | if (!array_key_exists($key, $mform->_types)) { |
1288 | debugging("Did you remember to call setType() for '$key'? ". | |
1289 | 'Defaulting to PARAM_RAW cleaning.', DEBUG_DEVELOPER); | |
1290 | } | |
1291 | break; | |
1292 | } | |
1293 | } | |
1294 | } | |
7f40a229 | 1295 | } |
1296 | ||
da1320da | 1297 | /** |
6c1fd304 RT |
1298 | * MoodleQuickForm implementation |
1299 | * | |
da1320da | 1300 | * You never extend this class directly. The class methods of this class are available from |
6073a598 | 1301 | * the private $this->_form property on moodleform and its children. You generally only |
da1320da | 1302 | * call methods on this class from within abstract methods that you override on moodleform such |
1303 | * as definition and definition_after_data | |
1304 | * | |
6c1fd304 RT |
1305 | * @package core_form |
1306 | * @category form | |
1307 | * @copyright 2006 Jamie Pratt <me@jamiep.org> | |
ba21c9d4 | 1308 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later |
da1320da | 1309 | */ |
7f40a229 | 1310 | class MoodleQuickForm extends HTML_QuickForm_DHTMLRulesTableless { |
6c1fd304 | 1311 | /** @var array type (PARAM_INT, PARAM_TEXT etc) of element value */ |
7f40a229 | 1312 | var $_types = array(); |
6c1fd304 RT |
1313 | |
1314 | /** @var array dependent state for the element/'s */ | |
50ef8eb9 | 1315 | var $_dependencies = array(); |
6c1fd304 RT |
1316 | |
1317 | /** @var array Array of buttons that if pressed do not result in the processing of the form. */ | |
19110c57 | 1318 | var $_noSubmitButtons=array(); |
6c1fd304 RT |
1319 | |
1320 | /** @var array Array of buttons that if pressed do not result in the processing of the form. */ | |
19110c57 | 1321 | var $_cancelButtons=array(); |
7f40a229 | 1322 | |
6c1fd304 | 1323 | /** @var array Array whose keys are element names. If the key exists this is a advanced element */ |
19194f82 | 1324 | var $_advancedElements = array(); |
1325 | ||
a4067bfc | 1326 | /** |
90a7e556 FM |
1327 | * Array whose keys are element names and values are the desired collapsible state. |
1328 | * True for collapsed, False for expanded. If not present, set to default in | |
1329 | * {@link self::accept()}. | |
a4067bfc RK |
1330 | * |
1331 | * @var array | |
1332 | */ | |
1333 | var $_collapsibleElements = array(); | |
1334 | ||
1335 | /** | |
1336 | * Whether to enable shortforms for this form | |
1337 | * | |
1338 | * @var boolean | |
1339 | */ | |
1340 | var $_disableShortforms = false; | |
1341 | ||
6b65e50b TH |
1342 | /** @var bool whether to automatically initialise M.formchangechecker for this form. */ |
1343 | protected $_use_form_change_checker = true; | |
1344 | ||
f07b9627 | 1345 | /** |
2836e77d | 1346 | * The form name is derived from the class name of the wrapper minus the trailing form |
6c1fd304 RT |
1347 | * It is a name with words joined by underscores whereas the id attribute is words joined by underscores. |
1348 | * @var string | |
f07b9627 | 1349 | */ |
1350 | var $_formName = ''; | |
43914931 | 1351 | |
4f51f48f | 1352 | /** |
6c1fd304 RT |
1353 | * String with the html for hidden params passed in as part of a moodle_url |
1354 | * object for the action. Output in the form. | |
4f51f48f | 1355 | * @var string |
1356 | */ | |
1357 | var $_pageparams = ''; | |
1358 | ||
da6f8763 | 1359 | /** |
1360 | * Class constructor - same parameters as HTML_QuickForm_DHTMLRulesTableless | |
ba21c9d4 | 1361 | * |
6c1fd304 RT |
1362 | * @staticvar int $formcounter counts number of forms |
1363 | * @param string $formName Form's name. | |
1364 | * @param string $method Form's method defaults to 'POST' | |
1365 | * @param string|moodle_url $action Form's action | |
1366 | * @param string $target (optional)Form's target defaults to none | |
1367 | * @param mixed $attributes (optional)Extra attributes for <form> tag | |
da6f8763 | 1368 | */ |
7f40a229 | 1369 | function MoodleQuickForm($formName, $method, $action, $target='', $attributes=null){ |
96705c33 | 1370 | global $CFG, $OUTPUT; |
dcf6d93c | 1371 | |
271ffe3f | 1372 | static $formcounter = 1; |
7f40a229 | 1373 | |
da6f8763 | 1374 | HTML_Common::HTML_Common($attributes); |
da6f8763 | 1375 | $target = empty($target) ? array() : array('target' => $target); |
f07b9627 | 1376 | $this->_formName = $formName; |
4f51f48f | 1377 | if (is_a($action, 'moodle_url')){ |
6ea66ff3 | 1378 | $this->_pageparams = html_writer::input_hidden_params($action); |
eb788065 | 1379 | $action = $action->out_omit_querystring(); |
4f51f48f | 1380 | } else { |
1381 | $this->_pageparams = ''; | |
1382 | } | |
28c7749f FM |
1383 | // No 'name' atttribute for form in xhtml strict : |
1384 | $attributes = array('action' => $action, 'method' => $method, 'accept-charset' => 'utf-8') + $target; | |
1385 | if (is_null($this->getAttribute('id'))) { | |
1386 | $attributes['id'] = 'mform' . $formcounter; | |
1387 | } | |
271ffe3f | 1388 | $formcounter++; |
da6f8763 | 1389 | $this->updateAttributes($attributes); |
da6f8763 | 1390 | |
28c7749f | 1391 | // This is custom stuff for Moodle : |
da6f8763 | 1392 | $oldclass= $this->getAttribute('class'); |
1393 | if (!empty($oldclass)){ | |
1394 | $this->updateAttributes(array('class'=>$oldclass.' mform')); | |
1395 | }else { | |
80f962df | 1396 | $this->updateAttributes(array('class'=>'mform')); |
da6f8763 | 1397 | } |
b5d0cafc PS |
1398 | $this->_reqHTML = '<img class="req" title="'.get_string('requiredelement', 'form').'" alt="'.get_string('requiredelement', 'form').'" src="'.$OUTPUT->pix_url('req') .'" />'; |
1399 | $this->_advancedHTML = '<img class="adv" title="'.get_string('advancedelement', 'form').'" alt="'.get_string('advancedelement', 'form').'" src="'.$OUTPUT->pix_url('adv') .'" />'; | |
1400 | $this->setRequiredNote(get_string('somefieldsrequired', 'form', '<img alt="'.get_string('requiredelement', 'form').'" src="'.$OUTPUT->pix_url('req') .'" />')); | |
19194f82 | 1401 | } |
1402 | ||
a23f0aaf | 1403 | /** |
1404 | * Use this method to indicate an element in a form is an advanced field. If items in a form | |
1405 | * are marked as advanced then 'Hide/Show Advanced' buttons will automatically be displayed in the | |
1406 | * form so the user can decide whether to display advanced form controls. | |
1407 | * | |
1408 | * If you set a header element to advanced then all elements it contains will also be set as advanced. | |
1409 | * | |
1410 | * @param string $elementName group or element name (not the element name of something inside a group). | |
6c1fd304 | 1411 | * @param bool $advanced default true sets the element to advanced. False removes advanced mark. |
a23f0aaf | 1412 | */ |
90a7e556 | 1413 | function setAdvanced($elementName, $advanced = true) { |
a23f0aaf | 1414 | if ($advanced){ |
1415 | $this->_advancedElements[$elementName]=''; | |
1416 | } elseif (isset($this->_advancedElements[$elementName])) { | |
1417 | unset($this->_advancedElements[$elementName]); | |
1418 | } | |
a23f0aaf | 1419 | } |
a4067bfc RK |
1420 | |
1421 | /** | |
1422 | * Use this method to indicate that the fieldset should be shown as expanded. | |
1423 | * The method is applicable to header elements only. | |
1424 | * | |
f160f97c | 1425 | * @param string $headername header element name |
a4067bfc | 1426 | * @param boolean $expanded default true sets the element to expanded. False makes the element collapsed. |
90a7e556 FM |
1427 | * @param boolean $ignoreuserstate override the state regardless of the state it was on when |
1428 | * the form was submitted. | |
f160f97c | 1429 | * @return void |
a4067bfc | 1430 | */ |
90a7e556 | 1431 | function setExpanded($headername, $expanded = true, $ignoreuserstate = false) { |
807cf3d0 FM |
1432 | if (empty($headername)) { |
1433 | return; | |
1434 | } | |
f160f97c FM |
1435 | $element = $this->getElement($headername); |
1436 | if ($element->getType() != 'header') { | |
1437 | debugging('Cannot use setExpanded on non-header elements', DEBUG_DEVELOPER); | |
1438 | return; | |
1439 | } | |
1440 | if (!$headerid = $element->getAttribute('id')) { | |
1441 | $element->_generateId(); | |
1442 | $headerid = $element->getAttribute('id'); | |
1443 | } | |
1444 | if ($this->getElementType('mform_isexpanded_' . $headerid) === false) { | |
90a7e556 | 1445 | // See if the form has been submitted already. |
d23edcf1 | 1446 | $formexpanded = optional_param('mform_isexpanded_' . $headerid, -1, PARAM_INT); |
90a7e556 | 1447 | if (!$ignoreuserstate && $formexpanded != -1) { |
f160f97c | 1448 | // Override expanded state with the form variable. |
a4067bfc RK |
1449 | $expanded = $formexpanded; |
1450 | } | |
f160f97c | 1451 | // Create the form element for storing expanded state. |
d23edcf1 TH |
1452 | $this->addElement('hidden', 'mform_isexpanded_' . $headerid); |
1453 | $this->setType('mform_isexpanded_' . $headerid, PARAM_INT); | |
f160f97c | 1454 | $this->setConstant('mform_isexpanded_' . $headerid, (int) $expanded); |
a4067bfc | 1455 | } |
f160f97c | 1456 | $this->_collapsibleElements[$headername] = !$expanded; |
a4067bfc RK |
1457 | } |
1458 | ||
1459 | /** | |
ac5e6ca4 RK |
1460 | * Use this method to add show more/less status element required for passing |
1461 | * over the advanced elements visibility status on the form submission. | |
a23f0aaf | 1462 | * |
8dcca4aa | 1463 | * @param string $headerName header element name. |
ac5e6ca4 | 1464 | * @param boolean $showmore default false sets the advanced elements to be hidden. |
a23f0aaf | 1465 | */ |
d23edcf1 | 1466 | function addAdvancedStatusElement($headerid, $showmore=false){ |
8dcca4aa | 1467 | // Add extra hidden element to store advanced items state for each section. |
d23edcf1 | 1468 | if ($this->getElementType('mform_showmore_' . $headerid) === false) { |
8dcca4aa | 1469 | // See if we the form has been submitted already. |
d23edcf1 | 1470 | $formshowmore = optional_param('mform_showmore_' . $headerid, -1, PARAM_INT); |
ac5e6ca4 | 1471 | if (!$showmore && $formshowmore != -1) { |
8dcca4aa | 1472 | // Override showmore state with the form variable. |
ac5e6ca4 | 1473 | $showmore = $formshowmore; |
a23f0aaf | 1474 | } |
8dcca4aa | 1475 | // Create the form element for storing advanced items state. |
d23edcf1 TH |
1476 | $this->addElement('hidden', 'mform_showmore_' . $headerid); |
1477 | $this->setType('mform_showmore_' . $headerid, PARAM_INT); | |
1478 | $this->setConstant('mform_showmore_' . $headerid, (int)$showmore); | |
a23f0aaf | 1479 | } |
19194f82 | 1480 | } |
6c1fd304 | 1481 | |
8dcca4aa DW |
1482 | /** |
1483 | * This function has been deprecated. Show advanced has been replaced by | |
1484 | * "Show more.../Show less..." in the shortforms javascript module. | |
1485 | * | |
1486 | * @deprecated since Moodle 2.5 | |
1487 | * @param bool $showadvancedNow if true will show advanced elements. | |
1488 | */ | |
1489 | function setShowAdvanced($showadvancedNow = null){ | |
1490 | debugging('Call to deprecated function setShowAdvanced. See "Show more.../Show less..." in shortforms yui module.'); | |
1491 | } | |
1492 | ||
1493 | /** | |
1494 | * This function has been deprecated. Show advanced has been replaced by | |
1495 | * "Show more.../Show less..." in the shortforms javascript module. | |
1496 | * | |
1497 | * @deprecated since Moodle 2.5 | |
1498 | * @return bool (Always false) | |
1499 | */ | |
1500 | function getShowAdvanced(){ | |
1501 | debugging('Call to deprecated function setShowAdvanced. See "Show more.../Show less..." in shortforms yui module.'); | |
1502 | return false; | |
1503 | } | |
1504 | ||
6c1fd304 | 1505 | /** |
ac5e6ca4 | 1506 | * Use this method to indicate that the form will not be using shortforms. |
6c1fd304 | 1507 | * |
ac5e6ca4 | 1508 | * @param boolean $disable default true, controls if the shortforms are disabled. |
6c1fd304 | 1509 | */ |
ac5e6ca4 RK |
1510 | function setDisableShortforms ($disable = true) { |
1511 | $this->_disableShortforms = $disable; | |
19194f82 | 1512 | } |
1513 | ||
6b65e50b TH |
1514 | /** |
1515 | * Call this method if you don't want the formchangechecker JavaScript to be | |
1516 | * automatically initialised for this form. | |
1517 | */ | |
1518 | public function disable_form_change_checker() { | |
1519 | $this->_use_form_change_checker = false; | |
1520 | } | |
19194f82 | 1521 | |
6b65e50b TH |
1522 | /** |
1523 | * If you have called {@link disable_form_change_checker()} then you can use | |
1524 | * this method to re-enable it. It is enabled by default, so normally you don't | |
1525 | * need to call this. | |
1526 | */ | |
1527 | public function enable_form_change_checker() { | |
1528 | $this->_use_form_change_checker = true; | |
1529 | } | |
1530 | ||
1531 | /** | |
1532 | * @return bool whether this form should automatically initialise | |
1533 | * formchangechecker for itself. | |
1534 | */ | |
1535 | public function is_form_change_checker_enabled() { | |
1536 | return $this->_use_form_change_checker; | |
1537 | } | |
1538 | ||
1539 | /** | |
19194f82 | 1540 | * Accepts a renderer |
1541 | * | |
6c1fd304 | 1542 | * @param HTML_QuickForm_Renderer $renderer An HTML_QuickForm_Renderer object |
19194f82 | 1543 | */ |
46f3921e | 1544 | function accept(&$renderer) { |
19194f82 | 1545 | if (method_exists($renderer, 'setAdvancedElements')){ |
a4067bfc | 1546 | //Check for visible fieldsets where all elements are advanced |
19194f82 | 1547 | //and mark these headers as advanced as well. |
a4067bfc | 1548 | //Also mark all elements in a advanced header as advanced. |
19194f82 | 1549 | $stopFields = $renderer->getStopFieldSetElements(); |
1550 | $lastHeader = null; | |
1551 | $lastHeaderAdvanced = false; | |
1552 | $anyAdvanced = false; | |
ac5e6ca4 | 1553 | $anyError = false; |
19194f82 | 1554 | foreach (array_keys($this->_elements) as $elementIndex){ |
1555 | $element =& $this->_elements[$elementIndex]; | |
46f3921e | 1556 | |
1557 | // if closing header and any contained element was advanced then mark it as advanced | |
19194f82 | 1558 | if ($element->getType()=='header' || in_array($element->getName(), $stopFields)){ |
d23edcf1 TH |
1559 | if ($anyAdvanced && !is_null($lastHeader)) { |
1560 | $lastHeader->_generateId(); | |
19194f82 | 1561 | $this->setAdvanced($lastHeader->getName()); |
d23edcf1 | 1562 | $this->addAdvancedStatusElement($lastHeader->getAttribute('id'), $anyError); |
19194f82 | 1563 | } |
1564 | $lastHeaderAdvanced = false; | |
46f3921e | 1565 | unset($lastHeader); |
1566 | $lastHeader = null; | |
19194f82 | 1567 | } elseif ($lastHeaderAdvanced) { |
1568 | $this->setAdvanced($element->getName()); | |
1569 | } | |
46f3921e | 1570 | |
19194f82 | 1571 | if ($element->getType()=='header'){ |
1572 | $lastHeader =& $element; | |
1573 | $anyAdvanced = false; | |
ac5e6ca4 | 1574 | $anyError = false; |
19194f82 | 1575 | $lastHeaderAdvanced = isset($this->_advancedElements[$element->getName()]); |
1576 | } elseif (isset($this->_advancedElements[$element->getName()])){ | |
1577 | $anyAdvanced = true; | |
ac5e6ca4 RK |
1578 | if (isset($this->_errors[$element->getName()])) { |
1579 | $anyError = true; | |
1580 | } | |
19194f82 | 1581 | } |
1582 | } | |
46f3921e | 1583 | // the last header may not be closed yet... |
1584 | if ($anyAdvanced && !is_null($lastHeader)){ | |
1585 | $this->setAdvanced($lastHeader->getName()); | |
d23edcf1 TH |
1586 | $lastHeader->_generateId(); |
1587 | $this->addAdvancedStatusElement($lastHeader->getAttribute('id'), $anyError); | |
46f3921e | 1588 | } |
19194f82 | 1589 | $renderer->setAdvancedElements($this->_advancedElements); |
a4067bfc | 1590 | } |
90a7e556 FM |
1591 | if (method_exists($renderer, 'setCollapsibleElements') && !$this->_disableShortforms) { |
1592 | ||
1593 | // Count the number of sections. | |
1594 | $headerscount = 0; | |
1595 | foreach (array_keys($this->_elements) as $elementIndex){ | |
1596 | $element =& $this->_elements[$elementIndex]; | |
1597 | if ($element->getType() == 'header') { | |
1598 | $headerscount++; | |
1599 | } | |
1600 | } | |
1601 | ||
1602 | $anyrequiredorerror = false; | |
a4067bfc | 1603 | $headercounter = 0; |
90a7e556 | 1604 | $headername = null; |
a4067bfc RK |
1605 | foreach (array_keys($this->_elements) as $elementIndex){ |
1606 | $element =& $this->_elements[$elementIndex]; | |
90a7e556 | 1607 | |
41068d98 | 1608 | if ($element->getType() == 'header') { |
a4067bfc | 1609 | $headercounter++; |
90a7e556 FM |
1610 | $element->_generateId(); |
1611 | $headername = $element->getName(); | |
1612 | $anyrequiredorerror = false; | |
1613 | } else if (in_array($element->getName(), $this->_required) || isset($this->_errors[$element->getName()])) { | |
1614 | $anyrequiredorerror = true; | |
1615 | } else { | |
1616 | // Do not reset $anyrequiredorerror to false because we do not want any other element | |
1617 | // in this header (fieldset) to possibly revert the state given. | |
a4067bfc | 1618 | } |
41068d98 | 1619 | |
90a7e556 FM |
1620 | if ($element->getType() == 'header') { |
1621 | if ($headercounter === 1 && !isset($this->_collapsibleElements[$headername])) { | |
1622 | // By default the first section is always expanded, except if a state has already been set. | |
1623 | $this->setExpanded($headername, true); | |
1624 | } else if (($headercounter === 2 && $headerscount === 2) && !isset($this->_collapsibleElements[$headername])) { | |
1625 | // The second section is always expanded if the form only contains 2 sections), | |
1626 | // except if a state has already been set. | |
41068d98 | 1627 | $this->setExpanded($headername, true); |
a4067bfc | 1628 | } |
90a7e556 FM |
1629 | } else if ($anyrequiredorerror) { |
1630 | // If any error or required field are present within the header, we need to expand it. | |
1631 | $this->setExpanded($headername, true, true); | |
1632 | } else if (!isset($this->_collapsibleElements[$headername])) { | |
1633 | // Define element as collapsed by default. | |
1634 | $this->setExpanded($headername, false); | |
a4067bfc RK |
1635 | } |
1636 | } | |
90a7e556 | 1637 | |
a4067bfc | 1638 | // Pass the array to renderer object. |
f160f97c | 1639 | $renderer->setCollapsibleElements($this->_collapsibleElements); |
19194f82 | 1640 | } |
1641 | parent::accept($renderer); | |
1642 | } | |
1643 | ||
ba21c9d4 | 1644 | /** |
6c1fd304 RT |
1645 | * Adds one or more element names that indicate the end of a fieldset |
1646 | * | |
1647 | * @param string $elementName name of the element | |
ba21c9d4 | 1648 | */ |
19194f82 | 1649 | function closeHeaderBefore($elementName){ |
1650 | $renderer =& $this->defaultRenderer(); | |
1651 | $renderer->addStopFieldsetElements($elementName); | |
da6f8763 | 1652 | } |
bb40325e | 1653 | |
da1320da | 1654 | /** |
1655 | * Should be used for all elements of a form except for select, radio and checkboxes which | |
1656 | * clean their own data. | |
1657 | * | |
1658 | * @param string $elementname | |
6c1fd304 | 1659 | * @param int $paramtype defines type of data contained in element. Use the constants PARAM_*. |
58b7d48f | 1660 | * {@link lib/moodlelib.php} for defined parameter types |
da1320da | 1661 | */ |
7f40a229 | 1662 | function setType($elementname, $paramtype) { |
1663 | $this->_types[$elementname] = $paramtype; | |
1664 | } | |
49292f8c | 1665 | |
da1320da | 1666 | /** |
6c1fd304 | 1667 | * This can be used to set several types at once. |
da1320da | 1668 | * |
6c1fd304 RT |
1669 | * @param array $paramtypes types of parameters. |
1670 | * @see MoodleQuickForm::setType | |
da1320da | 1671 | */ |
c56f1826 | 1672 | function setTypes($paramtypes) { |
1673 | $this->_types = $paramtypes + $this->_types; | |
1674 | } | |
49292f8c | 1675 | |
3c0ba94e FM |
1676 | /** |
1677 | * Return the type(s) to use to clean an element. | |
1678 | * | |
1679 | * In the case where the element has an array as a value, we will try to obtain a | |
1680 | * type defined for that specific key, and recursively until done. | |
1681 | * | |
1682 | * This method does not work reverse, you cannot pass a nested element and hoping to | |
1683 | * fallback on the clean type of a parent. This method intends to be used with the | |
1684 | * main element, which will generate child types if needed, not the other way around. | |
1685 | * | |
1686 | * Example scenario: | |
1687 | * | |
1688 | * You have defined a new repeated element containing a text field called 'foo'. | |
1689 | * By default there will always be 2 occurence of 'foo' in the form. Even though | |
1690 | * you've set the type on 'foo' to be PARAM_INT, for some obscure reason, you want | |
1691 | * the first value of 'foo', to be PARAM_FLOAT, which you set using setType: | |
1692 | * $mform->setType('foo[0]', PARAM_FLOAT). | |
1693 | * | |
1694 | * Now if you call this method passing 'foo', along with the submitted values of 'foo': | |
1695 | * array(0 => '1.23', 1 => '10'), you will get an array telling you that the key 0 is a | |
1696 | * FLOAT and 1 is an INT. If you had passed 'foo[1]', along with its value '10', you would | |
1697 | * get the default value returned. | |
1698 | * | |
1699 | * @param string $elementname name of the element. | |
1700 | * @param mixed $value value that should be cleaned. | |
1701 | * @return string|array constant value or array of constant values (PARAM_...) | |
1702 | */ | |
1703 | public function getCleanType($elementname, $value, $default = PARAM_RAW) { | |
1704 | $type = $default; | |
1705 | if (array_key_exists($elementname, $this->_types)) { | |
1706 | $type = $this->_types[$elementname]; | |
1707 | } | |
1708 | if (is_array($value)) { | |
1709 | $default = $type; | |
1710 | $type = array(); | |
1711 | foreach ($value as $subkey => $subvalue) { | |
1712 | $typekey = "$elementname" . "[$subkey]"; | |
1713 | if (array_key_exists($typekey, $this->_types)) { | |
1714 | $subtype = $this->_types[$typekey]; | |
1715 | } else { | |
1716 | $subtype = $default; | |
1717 | } | |
1718 | if (is_array($subvalue)) { | |
1719 | $type[$subkey] = $this->getCleanType($typekey, $subvalue, $subtype); | |
1720 | } else { | |
1721 | $type[$subkey] = $subtype; | |
1722 | } | |
1723 | } | |
1724 | } | |
1725 | return $type; | |
1726 | } | |
1727 | ||
1728 | /** | |
1729 | * Return the cleaned value using the passed type(s). | |
1730 | * | |
1731 | * @param mixed $value value that has to be cleaned. | |
1732 | * @param int|array $type constant value to use to clean (PARAM_...), typically returned by {@link self::getCleanType()}. | |
1733 | * @return mixed cleaned up value. | |
1734 | */ | |
1735 | public function getCleanedValue($value, $type) { | |
1736 | if (is_array($type) && is_array($value)) { | |
1737 | foreach ($type as $key => $param) { | |
1738 | $value[$key] = $this->getCleanedValue($value[$key], $param); | |
1739 | } | |
1740 | } else if (!is_array($type) && !is_array($value)) { | |
1741 | $value = clean_param($value, $type); | |
1742 | } else if (!is_array($type) && is_array($value)) { | |
1743 | $value = clean_param_array($value, $type, true); | |
1744 | } else { | |
1745 | throw new coding_exception('Unexpected type or value received in MoodleQuickForm::cleanValue()'); | |
1746 | } | |
1747 | return $value; | |
1748 | } | |
1749 | ||
ba21c9d4 | 1750 | /** |
6c1fd304 RT |
1751 | * Updates submitted values |
1752 | * | |
1753 | * @param array $submission submitted values | |
1754 | * @param array $files list of files | |
ba21c9d4 | 1755 | */ |
49292f8c | 1756 | function updateSubmission($submission, $files) { |
1757 | $this->_flagSubmitted = false; | |
1758 | ||
7f40a229 | 1759 | if (empty($submission)) { |
1760 | $this->_submitValues = array(); | |
7f40a229 | 1761 | } else { |
3c0ba94e FM |
1762 | foreach ($submission as $key => $s) { |
1763 | $type = $this->getCleanType($key, $s); | |
1764 | $submission[$key] = $this->getCleanedValue($s, $type); | |
7f40a229 | 1765 | } |
294ce987 | 1766 | $this->_submitValues = $submission; |
7f40a229 | 1767 | $this->_flagSubmitted = true; |
1768 | } | |
1769 | ||
49292f8c | 1770 | if (empty($files)) { |
1771 | $this->_submitFiles = array(); | |
1772 | } else { | |
49292f8c | 1773 | $this->_submitFiles = $files; |
1774 | $this->_flagSubmitted = true; | |
1775 | } | |
1776 | ||
2c412890 | 1777 | // need to tell all elements that they need to update their value attribute. |
1778 | foreach (array_keys($this->_elements) as $key) { | |
1779 | $this->_elements[$key]->onQuickFormEvent('updateValue', null, $this); | |
1780 | } | |
7f40a229 | 1781 | } |
1782 | ||
ba21c9d4 | 1783 | /** |
6c1fd304 RT |
1784 | * Returns HTML for required elements |
1785 | * | |
ba21c9d4 | 1786 | * @return string |
1787 | */ | |
da6f8763 | 1788 | function getReqHTML(){ |
1789 | return $this->_reqHTML; | |
1790 | } | |
117bd748 | 1791 | |
ba21c9d4 | 1792 | /** |
6c1fd304 RT |
1793 | * Returns HTML for advanced elements |
1794 | * | |
ba21c9d4 | 1795 | * @return string |
1796 | */ | |
19194f82 | 1797 | function getAdvancedHTML(){ |
1798 | return $this->_advancedHTML; | |
1799 | } | |
1800 | ||
7f40a229 | 1801 | /** |
da1320da | 1802 | * Initializes a default form value. Used to specify the default for a new entry where |
beac4717 | 1803 | * no data is loaded in using moodleform::set_data() |
7f40a229 | 1804 | * |
294ce987 | 1805 | * note: $slashed param removed |
1806 | * | |
6c1fd304 RT |
1807 | * @param string $elementName element name |
1808 | * @param mixed $defaultValue values for that element name | |
7f40a229 | 1809 | */ |
294ce987 | 1810 | function setDefault($elementName, $defaultValue){ |
1811 | $this->setDefaults(array($elementName=>$defaultValue)); | |
6c1fd304 RT |
1812 | } |
1813 | ||
53a78cef | 1814 | /** |
259c165d PS |
1815 | * Add a help button to element, only one button per element is allowed. |
1816 | * | |
ff95caa8 DM |
1817 | * This is new, simplified and preferable method of setting a help icon on form elements. |
1818 | * It uses the new $OUTPUT->help_icon(). | |
1819 | * | |
1820 | * Typically, you will provide the same identifier and the component as you have used for the | |
1821 | * label of the element. The string identifier with the _help suffix added is then used | |
1822 | * as the help string. | |
1823 | * | |
259c165d PS |
1824 | * There has to be two strings defined: |
1825 | * 1/ get_string($identifier, $component) - the title of the help page | |
5435c9dc | 1826 | * 2/ get_string($identifier.'_help', $component) - the actual help page text |
53a78cef | 1827 | * |
6c1fd304 | 1828 | * @since Moodle 2.0 |
53a78cef | 1829 | * @param string $elementname name of the element to add the item to |
2836e77d | 1830 | * @param string $identifier help string identifier without _help suffix |
ff95caa8 DM |
1831 | * @param string $component component name to look the help string in |
1832 | * @param string $linktext optional text to display next to the icon | |
6c1fd304 | 1833 | * @param bool $suppresscheck set to true if the element may not exist |
53a78cef | 1834 | */ |
259c165d PS |
1835 | function addHelpButton($elementname, $identifier, $component = 'moodle', $linktext = '', $suppresscheck = false) { |
1836 | global $OUTPUT; | |
53a78cef | 1837 | if (array_key_exists($elementname, $this->_elementIndex)) { |
af140288 DC |
1838 | $element = $this->_elements[$this->_elementIndex[$elementname]]; |
1839 | $element->_helpbutton = $OUTPUT->help_icon($identifier, $component, $linktext); | |
53a78cef PS |
1840 | } else if (!$suppresscheck) { |
1841 | debugging(get_string('nonexistentformelements', 'form', $elementname)); | |
1842 | } | |
1843 | } | |
1844 | ||
cc444336 | 1845 | /** |
2836e77d | 1846 | * Set constant value not overridden by _POST or _GET |
cc444336 | 1847 | * note: this does not work for complex names with [] :-( |
ba21c9d4 | 1848 | * |
cc444336 | 1849 | * @param string $elname name of element |
1850 | * @param mixed $value | |
cc444336 | 1851 | */ |
1852 | function setConstant($elname, $value) { | |
1853 | $this->_constantValues = HTML_QuickForm::arrayMerge($this->_constantValues, array($elname=>$value)); | |
1854 | $element =& $this->getElement($elname); | |
1855 | $element->onQuickFormEvent('updateValue', null, $this); | |
1856 | } | |
1857 | ||
ba21c9d4 | 1858 | /** |
6c1fd304 RT |
1859 | * export submitted values |
1860 | * | |
1861 | * @param string $elementList list of elements in form | |
1862 | * @return array | |
ba21c9d4 | 1863 | */ |
294ce987 | 1864 | function exportValues($elementList = null){ |
0ffb4cc7 | 1865 | $unfiltered = array(); |
1866 | if (null === $elementList) { | |
1867 | // iterate over all elements, calling their exportValue() methods | |
1868 | foreach (array_keys($this->_elements) as $key) { | |
8e73fbf5 AG |
1869 | if ($this->_elements[$key]->isFrozen() && !$this->_elements[$key]->_persistantFreeze) { |
1870 | $varname = $this->_elements[$key]->_attributes['name']; | |
1871 | $value = ''; | |
1872 | // If we have a default value then export it. | |
1873 | if (isset($this->_defaultValues[$varname])) { | |
5f137dcb | 1874 | $value = $this->prepare_fixed_value($varname, $this->_defaultValues[$varname]); |
8e73fbf5 | 1875 | } |
0ffb4cc7 | 1876 | } else { |
1877 | $value = $this->_elements[$key]->exportValue($this->_submitValues, true); | |
1878 | } | |
1879 | ||
1880 | if (is_array($value)) { | |
1881 | // This shit throws a bogus warning in PHP 4.3.x | |
1882 | $unfiltered = HTML_QuickForm::arrayMerge($unfiltered, $value); | |
1883 | } | |
1884 | } | |
1885 | } else { | |
1886 | if (!is_array($elementList)) { | |
1887 | $elementList = array_map('trim', explode(',', $elementList)); | |
1888 | } | |
1889 | foreach ($elementList as $elementName) { | |
1890 | $value = $this->exportValue($elementName); | |
fabbf439 | 1891 | if (@PEAR::isError($value)) { |
0ffb4cc7 | 1892 | return $value; |
1893 | } | |
4287fc0d | 1894 | //oh, stock QuickFOrm was returning array of arrays! |
1895 | $unfiltered = HTML_QuickForm::arrayMerge($unfiltered, $value); | |
0ffb4cc7 | 1896 | } |
1897 | } | |
7f40a229 | 1898 | |
f1f70bd4 DC |
1899 | if (is_array($this->_constantValues)) { |
1900 | $unfiltered = HTML_QuickForm::arrayMerge($unfiltered, $this->_constantValues); | |
1901 | } | |
294ce987 | 1902 | return $unfiltered; |
da6f8763 | 1903 | } |
6c1fd304 | 1904 | |
5f137dcb TH |
1905 | /** |
1906 | * This is a bit of a hack, and it duplicates the code in | |
1907 | * HTML_QuickForm_element::_prepareValue, but I could not think of a way or | |
1908 | * reliably calling that code. (Think about date selectors, for example.) | |
1909 | * @param string $name the element name. | |
1910 | * @param mixed $value the fixed value to set. | |
1911 | * @return mixed the appropriate array to add to the $unfiltered array. | |
1912 | */ | |
1913 | protected function prepare_fixed_value($name, $value) { | |
1914 | if (null === $value) { | |
1915 | return null; | |
1916 | } else { | |
1917 | if (!strpos($name, '[')) { | |
1918 | return array($name => $value); | |
1919 | } else { | |
1920 | $valueAry = array(); | |
1921 | $myIndex = "['" . str_replace(array(']', '['), array('', "']['"), $name) . "']"; | |
1922 | eval("\$valueAry$myIndex = \$value;"); | |
1923 | return $valueAry; | |
1924 | } | |
1925 | } | |
1926 | } | |
1927 | ||
f07b9627 | 1928 | /** |
1929 | * Adds a validation rule for the given field | |
1930 | * | |
1931 | * If the element is in fact a group, it will be considered as a whole. | |
1932 | * To validate grouped elements as separated entities, | |
1933 | * use addGroupRule instead of addRule. | |
1934 | * | |
6c1fd304 RT |
1935 | * @param string $element Form element name |
1936 | * @param string $message Message to display for invalid data | |
1937 | * @param string $type Rule type, use getRegisteredRules() to get types | |
1938 | * @param string $format (optional)Required for extra rule data | |
1939 | * @param string $validation (optional)Where to perform validation: "server", "client" | |
1940 | * @param bool $reset Client-side validation: reset the form element to its original value if there is an error? | |
1941 | * @param bool $force Force the rule to be applied, even if the target form element does not exist | |
f07b9627 | 1942 | */ |
1943 | function addRule($element, $message, $type, $format=null, $validation='server', $reset = false, $force = false) | |
1944 | { | |
1945 | parent::addRule($element, $message, $type, $format, $validation, $reset, $force); | |
1946 | if ($validation == 'client') { | |
1947 | $this->updateAttributes(array('onsubmit' => 'try { var myValidator = validate_' . $this->_formName . '; } catch(e) { return true; } return myValidator(this);')); | |
1948 | } | |
1949 | ||
6c1fd304 RT |
1950 | } |
1951 | ||
f07b9627 | 1952 | /** |
1953 | * Adds a validation rule for the given group of elements | |
1954 | * | |
1955 | * Only groups with a name can be assigned a validation rule | |
1956 | * Use addGroupRule when you need to validate elements inside the group. | |
1957 | * Use addRule if you need to validate the group as a whole. In this case, | |
1958 | * the same rule will be applied to all elements in the group. | |
1959 | * Use addRule if you need to validate the group against a function. | |
1960 | * | |
6c1fd304 RT |
1961 | * @param string $group Form group name |
1962 | * @param array|string $arg1 Array for multiple elements or error message string for one element | |
1963 | * @param string $type (optional)Rule type use getRegisteredRules() to get types | |
1964 | * @param string $format (optional)Required for extra rule data | |
1965 | * @param int $howmany (optional)How many valid elements should be in the group | |
1966 | * @param string $validation (optional)Where to perform validation: "server", "client" | |
1967 | * @param bool $reset Client-side: whether to reset the element's value to its original state if validation failed. | |
f07b9627 | 1968 | */ |
1969 | function addGroupRule($group, $arg1, $type='', $format=null, $howmany=0, $validation = 'server', $reset = false) | |
1970 | { | |
1971 | parent::addGroupRule($group, $arg1, $type, $format, $howmany, $validation, $reset); | |
1972 | if (is_array($arg1)) { | |
3a298174 | 1973 | foreach ($arg1 as $rules) { |
f07b9627 | 1974 | foreach ($rules as $rule) { |
1975 | $validation = (isset($rule[3]) && 'client' == $rule[3])? 'client': 'server'; | |
1976 | ||
1977 | if ('client' == $validation) { | |
1978 | $this->updateAttributes(array('onsubmit' => 'try { var myValidator = validate_' . $this->_formName . '; } catch(e) { return true; } return myValidator(this);')); | |
1979 | } | |
1980 | } | |
1981 | } | |
1982 | } elseif (is_string($arg1)) { | |
1983 | ||
1984 | if ($validation == 'client') { | |
3a298174 | 1985 | $this->updateAttributes(array('onsubmit' => 'try { var myValidator = validate_' . $this->_formName . '; } catch(e) { return true; } return myValidator(this);')); |
f07b9627 | 1986 | } |
1987 | } | |
6c1fd304 | 1988 | } |
f07b9627 | 1989 | |
5bc97c98 | 1990 | /** |
1991 | * Returns the client side validation script | |
1992 | * | |
1993 | * The code here was copied from HTML_QuickForm_DHTMLRulesTableless who copied it from HTML_QuickForm | |
1994 | * and slightly modified to run rules per-element | |
1995 | * Needed to override this because of an error with client side validation of grouped elements. | |
1996 | * | |
6c1fd304 | 1997 | * @return string Javascript to perform validation, empty string if no 'client' rules were added |
5bc97c98 | 1998 | */ |
1999 | function getValidationScript() | |
2000 | { | |
2001 | if (empty($this->_rules) || empty($this->_attributes['onsubmit'])) { | |
2002 | return ''; | |
2003 | } | |
2004 | ||
2005 | include_once('HTML/QuickForm/RuleRegistry.php'); | |
2006 | $registry =& HTML_QuickForm_RuleRegistry::singleton(); | |
2007 | $test = array(); | |
2008 | $js_escape = array( | |
2009 | "\r" => '\r', | |
2010 | "\n" => '\n', | |
2011 | "\t" => '\t', | |
2012 | "'" => "\\'", | |
2013 | '"' => '\"', | |
2014 | '\\' => '\\\\' | |
2015 | ); | |
2016 | ||
2017 | foreach ($this->_rules as $elementName => $rules) { | |
2018 | foreach ($rules as $rule) { | |
2019 | if ('client' == $rule['validation']) { | |
da1320da | 2020 | unset($element); //TODO: find out how to properly initialize it |
5bc97c98 | 2021 | |
2022 | $dependent = isset($rule['dependent']) && is_array($rule['dependent']); | |
2023 | $rule['message'] = strtr($rule['message'], $js_escape); | |
2024 | ||
2025 | if (isset($rule['group'])) { | |
2026 | $group =& $this->getElement($rule['group']); | |
2027 | // No JavaScript validation for frozen elements | |
2028 | if ($group->isFrozen()) { | |
2029 | continue 2; | |
2030 | } | |
2031 | $elements =& $group->getElements(); | |
2032 | foreach (array_keys($elements) as $key) { | |
2033 | if ($elementName == $group->getElementName($key)) { | |
2034 | $element =& $elements[$key]; | |
2035 | break; | |
2036 | } | |
2037 | } | |
2038 | } elseif ($dependent) { | |
2039 | $element = array(); | |
2040 | $element[] =& $this->getElement($elementName); | |
3a298174 | 2041 | foreach ($rule['dependent'] as $elName) { |
5bc97c98 | 2042 | $element[] =& $this->getElement($elName); |
2043 | } | |
2044 | } else { | |
2045 | $element =& $this->getElement($elementName); | |
2046 | } | |
2047 | // No JavaScript validation for frozen elements | |
2048 | if (is_object($element) && $element->isFrozen()) { | |
2049 | continue 2; | |
2050 | } elseif (is_array($element)) { | |
2051 | foreach (array_keys($element) as $key) { | |
2052 | if ($element[$key]->isFrozen()) { | |
2053 | continue 3; | |
2054 | } | |
2055 | } | |
2056 | } | |
ab978b38 RT |
2057 | //for editor element, [text] is appended to the name. |
2058 | if ($element->getType() == 'editor') { | |
56728202 | 2059 | $elementName .= '[text]'; |
ab978b38 RT |
2060 | //Add format to rule as moodleform check which format is supported by browser |
2061 | //it is not set anywhere... So small hack to make sure we pass it down to quickform | |
2062 | if (is_null($rule['format'])) { | |
2063 | $rule['format'] = $element->getFormat(); | |
2064 | } | |
56728202 | 2065 | } |
ab978b38 | 2066 | // Fix for bug displaying errors for elements in a group |
5bc97c98 | 2067 | $test[$elementName][0][] = $registry->getValidationScript($element, $elementName, $rule); |
2068 | $test[$elementName][1]=$element; | |
2069 | //end of fix | |
2070 | } | |
2071 | } | |
2072 | } | |
7c77033f | 2073 | |
2074 | // Fix for MDL-9524. If you don't do this, then $element may be left as a reference to one of the fields in | |
2075 | // the form, and then that form field gets corrupted by the code that follows. | |
2076 | unset($element); | |
2077 | ||
5bc97c98 | 2078 | $js = ' |
2079 | <script type="text/javascript"> | |
2080 | //<![CDATA[ | |
1cbb09f1 | 2081 | |
2082 | var skipClientValidation = false; | |
2083 | ||
5bc97c98 | 2084 | function qf_errorHandler(element, _qfMsg) { |
2085 | div = element.parentNode; | |
b29c9b05 RT |
2086 | |
2087 | if ((div == undefined) || (element.name == undefined)) { | |
2088 | //no checking can be done for undefined elements so let server handle it. | |
2089 | return true; | |
2090 | } | |
2091 | ||
5bc97c98 | 2092 | if (_qfMsg != \'\') { |
e35c9eeb | 2093 | var errorSpan = document.getElementById(\'id_error_\'+element.name); |
e7004d05 | 2094 | if (!errorSpan) { |
2095 | errorSpan = document.createElement("span"); | |
e35c9eeb | 2096 | errorSpan.id = \'id_error_\'+element.name; |
2097 | errorSpan.className = "error"; | |
fed13a5e | 2098 | element.parentNode.insertBefore(errorSpan, element.parentNode.firstChild); |
5bc97c98 | 2099 | } |
fed13a5e | 2100 | |
e7004d05 | 2101 | while (errorSpan.firstChild) { |
2102 | errorSpan.removeChild(errorSpan.firstChild); | |
5bc97c98 | 2103 | } |
2c412890 | 2104 | |
e7004d05 | 2105 | errorSpan.appendChild(document.createTextNode(_qfMsg.substring(3))); |
e35c9eeb | 2106 | errorSpan.appendChild(document.createElement("br")); |
5bc97c98 | 2107 | |
2108 | if (div.className.substr(div.className.length - 6, 6) != " error" | |
2109 | && div.className != "error") { | |
2110 | div.className += " error"; | |
2111 | } | |
2112 | ||
2113 | return false; | |
2114 | } else { | |
e35c9eeb | 2115 | var errorSpan = document.getElementById(\'id_error_\'+element.name); |
e7004d05 | 2116 | if (errorSpan) { |
2117 | errorSpan.parentNode.removeChild(errorSpan); | |
5bc97c98 | 2118 | } |
2119 | ||
2120 | if (div.className.substr(div.className.length - 6, 6) == " error") { | |
2121 | div.className = div.className.substr(0, div.className.length - 6); | |
2122 | } else if (div.className == "error") { | |
2123 | div.className = ""; | |
2124 | } | |
2125 | ||
2126 | return true; | |
2127 | } | |
2128 | }'; | |
2129 | $validateJS = ''; | |
2130 | foreach ($test as $elementName => $jsandelement) { | |
2131 | // Fix for bug displaying errors for elements in a group | |
2132 | //unset($element); | |
2133 | list($jsArr,$element)=$jsandelement; | |
2134 | //end of fix | |
8af229fe | 2135 | $escapedElementName = preg_replace_callback( |
ed608e7a | 2136 | '/[_\[\]-]/', |
8af229fe DM |
2137 | create_function('$matches', 'return sprintf("_%2x",ord($matches[0]));'), |
2138 | $elementName); | |
5bc97c98 | 2139 | $js .= ' |
8af229fe | 2140 | function validate_' . $this->_formName . '_' . $escapedElementName . '(element) { |
b29c9b05 RT |
2141 | if (undefined == element) { |
2142 | //required element was not found, then let form be submitted without client side validation | |
2143 | return true; | |
2144 | } | |
5bc97c98 | 2145 | var value = \'\'; |
2146 | var errFlag = new Array(); | |
2147 | var _qfGroups = {}; | |
2148 | var _qfMsg = \'\'; | |
2149 | var frm = element.parentNode; | |
b29c9b05 RT |
2150 | if ((undefined != element.name) && (frm != undefined)) { |
2151 | while (frm && frm.nodeName.toUpperCase() != "FORM") { | |
2152 | frm = frm.parentNode; | |
2153 | } | |
2154 | ' . join("\n", $jsArr) . ' | |
2155 | return qf_errorHandler(element, _qfMsg); | |
2156 | } else { | |
2157 | //element name should be defined else error msg will not be displayed. | |
2158 | return true; | |
5bc97c98 | 2159 | } |
5bc97c98 | 2160 | } |
2161 | '; | |
2162 | $validateJS .= ' | |
8af229fe | 2163 | ret = validate_' . $this->_formName . '_' . $escapedElementName.'(frm.elements[\''.$elementName.'\']) && ret; |
2ef7c374 | 2164 | if (!ret && !first_focus) { |
2165 | first_focus = true; | |
2166 | frm.elements[\''.$elementName.'\'].focus(); | |
2167 | } | |
2168 | '; | |
4f51f48f | 2169 | |
5bc97c98 | 2170 | // Fix for bug displaying errors for elements in a group |
2171 | //unset($element); | |
2172 | //$element =& $this->getElement($elementName); | |
2173 | //end of fix | |
8af229fe | 2174 | $valFunc = 'validate_' . $this->_formName . '_' . $escapedElementName . '(this)'; |
5bc97c98 | 2175 | $onBlur = $element->getAttribute('onBlur'); |
2176 | $onChange = $element->getAttribute('onChange'); | |
2177 | $element->updateAttributes(array('onBlur' => $onBlur . $valFunc, | |
2178 | 'onChange' => $onChange . $valFunc)); | |
2179 | } | |
e7004d05 | 2180 | // do not rely on frm function parameter, because htmlarea breaks it when overloading the onsubmit method |
5bc97c98 | 2181 | $js .= ' |
f07b9627 | 2182 | function validate_' . $this->_formName . '(frm) { |
1cbb09f1 | 2183 | if (skipClientValidation) { |
2184 | return true; | |
2185 | } | |
5bc97c98 | 2186 | var ret = true; |
4f51f48f | 2187 | |
0befbdfd | 2188 | var frm = document.getElementById(\''. $this->_attributes['id'] .'\') |
2ef7c374 | 2189 | var first_focus = false; |
5bc97c98 | 2190 | ' . $validateJS . '; |
2191 | return ret; | |
2192 | } | |
2193 | //]]> | |
2194 | </script>'; | |
2195 | return $js; | |
2196 | } // end func getValidationScript | |
6c1fd304 RT |
2197 | |
2198 | /** | |
2199 | * Sets default error message | |
2200 | */ | |
5bc97c98 | 2201 | function _setDefaultRuleMessages(){ |
2202 | foreach ($this->_rules as $field => $rulesarr){ | |
2203 | foreach ($rulesarr as $key => $rule){ | |
2204 | if ($rule['message']===null){ | |
365a5941 | 2205 | $a=new stdClass(); |
5bc97c98 | 2206 | $a->format=$rule['format']; |
2207 | $str=get_string('err_'.$rule['type'], 'form', $a); | |
2208 | if (strpos($str, '[[')!==0){ | |
2209 | $this->_rules[$field][$key]['message']=$str; | |
2c412890 | 2210 | } |
5bc97c98 | 2211 | } |
2212 | } | |
2213 | } | |
2214 | } | |
bb40325e | 2215 | |
6c1fd304 RT |
2216 | /** |
2217 | * Get list of attributes which have dependencies | |
2218 | * | |
2219 | * @return array | |
2220 | */ | |
c7e3e61c SH |
2221 | function getLockOptionObject(){ |
2222 | $result = array(); | |
dd07bbac | 2223 | foreach ($this->_dependencies as $dependentOn => $conditions){ |
c7e3e61c | 2224 | $result[$dependentOn] = array(); |
dd07bbac | 2225 | foreach ($conditions as $condition=>$values) { |
c7e3e61c | 2226 | $result[$dependentOn][$condition] = array(); |
dd07bbac | 2227 | foreach ($values as $value=>$dependents) { |
c7e3e61c | 2228 | $result[$dependentOn][$condition][$value] = array(); |
dd07bbac | 2229 | $i = 0; |
2230 | foreach ($dependents as $dependent) { | |
2231 | $elements = $this->_getElNamesRecursive($dependent); | |
46f3921e | 2232 | if (empty($elements)) { |
2233 | // probably element inside of some group | |
2234 | $elements = array($dependent); | |
2235 | } | |
dd07bbac | 2236 | foreach($elements as $element) { |
2237 | if ($element == $dependentOn) { | |
2238 | continue; | |
2239 | } | |
c7e3e61c | 2240 | $result[$dependentOn][$condition][$value][] = $element; |
dd07bbac | 2241 | } |
11f260f4 | 2242 | } |
2243 | } | |
50ef8eb9 | 2244 | } |
dd07bbac | 2245 | } |
c7e3e61c | 2246 | return array($this->getAttribute('id'), $result); |
bb40325e | 2247 | } |
d01a38cb | 2248 | |
ba21c9d4 | 2249 | /** |
6c1fd304 RT |
2250 | * Get names of element or elements in a group. |
2251 | * | |
2252 | * @param HTML_QuickForm_group|element $element element group or element object | |
ba21c9d4 | 2253 | * @return array |
2254 | */ | |
46f3921e | 2255 | function _getElNamesRecursive($element) { |
2256 | if (is_string($element)) { | |
4f51f48f | 2257 | if (!$this->elementExists($element)) { |
2258 | return array(); | |
2259 | } | |
46f3921e | 2260 | $element = $this->getElement($element); |
d01a38cb | 2261 | } |
46f3921e | 2262 | |
2263 | if (is_a($element, 'HTML_QuickForm_group')) { | |
2264 | $elsInGroup = $element->getElements(); | |
9403060a | 2265 | $elNames = array(); |
d01a38cb | 2266 | foreach ($elsInGroup as $elInGroup){ |
e850ec48 | 2267 | if (is_a($elInGroup, 'HTML_QuickForm_group')) { |
2268 | // not sure if this would work - groups nested in groups | |
2269 | $elNames = array_merge($elNames, $this->_getElNamesRecursive($elInGroup)); | |
2270 | } else { | |
2271 | $elNames[] = $element->getElementName($elInGroup->getName()); | |
2272 | } | |
d01a38cb | 2273 | } |
46f3921e | 2274 | |
2275 | } else if (is_a($element, 'HTML_QuickForm_header')) { | |
2276 | return array(); | |
2277 | ||
2278 | } else if (is_a($element, 'HTML_QuickForm_hidden')) { | |
2279 | return array(); | |
2280 | ||
8f6384a9 TH |
2281 | } else if (method_exists($element, 'getPrivateName') && |
2282 | !($element instanceof HTML_QuickForm_advcheckbox)) { | |
2283 | // The advcheckbox element implements a method called getPrivateName, | |
2284 | // but in a way that is not compatible with the generic API, so we | |
2285 | // have to explicitly exclude it. | |
46f3921e | 2286 | return array($element->getPrivateName()); |
2287 | ||
2288 | } else { | |
2289 | $elNames = array($element->getName()); | |
d01a38cb | 2290 | } |
d01a38cb | 2291 | |
46f3921e | 2292 | return $elNames; |
50ef8eb9 | 2293 | } |
46f3921e | 2294 | |
6e372b25 | 2295 | /** |
2296 | * Adds a dependency for $elementName which will be disabled if $condition is met. | |
9403060a | 2297 | * If $condition = 'notchecked' (default) then the condition is that the $dependentOn element |
2298 | * is not checked. If $condition = 'checked' then the condition is that the $dependentOn element | |
31a6c06c | 2299 | * is checked. If $condition is something else (like "eq" for equals) then it is checked to see if the value |
2300 | * of the $dependentOn element is $condition (such as equal) to $value. | |
6e372b25 | 2301 | * |
2302 | * @param string $elementName the name of the element which will be disabled | |
6c1fd304 | 2303 | * @param string $dependentOn the name of the element whose state will be checked for condition |
6e372b25 | 2304 | * @param string $condition the condition to check |
19110c57 | 2305 | * @param mixed $value used in conjunction with condition. |
6e372b25 | 2306 | */ |
dd07bbac | 2307 | function disabledIf($elementName, $dependentOn, $condition = 'notchecked', $value='1'){ |
2308 | if (!array_key_exists($dependentOn, $this->_dependencies)) { | |
2309 | $this->_dependencies[$dependentOn] = array(); | |
2310 | } | |
2311 | if (!array_key_exists($condition, $this->_dependencies[$dependentOn])) { | |
2312 | $this->_dependencies[$dependentOn][$condition] = array(); | |
2313 | } | |
2314 | if (!array_key_exists($value, $this->_dependencies[$dependentOn][$condition])) { | |
2315 | $this->_dependencies[$dependentOn][$condition][$value] = array(); | |
2316 | } | |
2317 | $this->_dependencies[$dependentOn][$condition][$value][] = $elementName; | |
bb40325e | 2318 | } |
dd07bbac | 2319 | |
6c1fd304 RT |
2320 | /** |
2321 | * Registers button as no submit button | |
2322 | * | |
2323 | * @param string $buttonname name of the button | |
2324 | */ | |
a23f0aaf | 2325 | function registerNoSubmitButton($buttonname){ |
2326 | $this->_noSubmitButtons[]=$buttonname; | |
2327 | } | |
dd07bbac | 2328 | |
ba21c9d4 | 2329 | /** |
6c1fd304 RT |
2330 | * Checks if button is a no submit button, i.e it doesn't submit form |
2331 | * | |
2332 | * @param string $buttonname name of the button to check | |
2333 | * @return bool | |
ba21c9d4 | 2334 | */ |
a23f0aaf | 2335 | function isNoSubmitButton($buttonname){ |
2336 | return (array_search($buttonname, $this->_noSubmitButtons)!==FALSE); | |
19110c57 | 2337 | } |
dd07bbac | 2338 | |
ba21c9d4 | 2339 | /** |
6c1fd304 RT |
2340 | * Registers a button as cancel button |
2341 | * | |
2342 | * @param string $addfieldsname name of the button | |
ba21c9d4 | 2343 | */ |
19110c57 | 2344 | function _registerCancelButton($addfieldsname){ |
2345 | $this->_cancelButtons[]=$addfieldsname; | |
2346 | } | |
6c1fd304 | 2347 | |
acc9c3e0 | 2348 | /** |
2349 | * Displays elements without HTML input tags. | |
2350 | * This method is different to freeze() in that it makes sure no hidden | |
cc444336 | 2351 | * elements are included in the form. |
2352 | * Note: If you want to make sure the submitted value is ignored, please use setDefaults(). | |
acc9c3e0 | 2353 | * |
4af06dda | 2354 | * This function also removes all previously defined rules. |
2355 | * | |
6c1fd304 RT |
2356 | * @param string|array $elementList array or string of element(s) to be frozen |
2357 | * @return object|bool if element list is not empty then return error object, else true | |
acc9c3e0 | 2358 | */ |
2359 | function hardFreeze($elementList=null) | |
2360 | { | |
2361 | if (!isset($elementList)) { | |
2362 | $this->_freezeAll = true; | |
2363 | $elementList = array(); | |
2364 | } else { | |
2365 | if (!is_array($elementList)) { | |
2366 | $elementList = preg_split('/[ ]*,[ ]*/', $elementList); | |
2367 | } | |
2368 | $elementList = array_flip($elementList); | |
2369 | } | |
2370 | ||
2371 | foreach (array_keys($this->_elements) as $key) { | |
2372 | $name = $this->_elements[$key]->getName(); | |
2373 | if ($this->_freezeAll || isset($elementList[$name])) { | |
2374 | $this->_elements[$key]->freeze(); | |
2375 | $this->_elements[$key]->setPersistantFreeze(false); | |
2376 | unset($elementList[$name]); | |
4af06dda | 2377 | |
2378 | // remove all rules | |
2379 | $this->_rules[$name] = array(); | |
2380 | // if field is required, remove the rule | |
2381 | $unset = array_search($name, $this->_required); | |
2382 | if ($unset !== false) { | |
2383 | unset($this->_required[$unset]); | |
2384 | } | |
acc9c3e0 | 2385 | } |
2386 | } | |
2387 | ||
2388 | if (!empty($elementList)) { | |
66b3302d | 2389 | return self::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Nonexistant element(s): '" . implode("', '", array_keys($elementList)) . "' in HTML_QuickForm::freeze()", 'HTML_QuickForm_Error', true); |
acc9c3e0 | 2390 | } |
2391 | return true; | |
4f51f48f | 2392 | } |
6c1fd304 | 2393 | |
4f51f48f | 2394 | /** |
2395 | * Hard freeze all elements in a form except those whose names are in $elementList or hidden elements in a form. | |
2396 | * | |
2397 | * This function also removes all previously defined rules of elements it freezes. | |
2398 | * | |
6c1fd304 RT |
2399 | * @throws HTML_QuickForm_Error |
2400 | * @param array $elementList array or string of element(s) not to be frozen | |
2401 | * @return bool returns true | |
4f51f48f | 2402 | */ |
2403 | function hardFreezeAllVisibleExcept($elementList) | |
2404 | { | |
2405 | $elementList = array_flip($elementList); | |
2406 | foreach (array_keys($this->_elements) as $key) { | |
2407 | $name = $this->_elements[$key]->getName(); | |
2408 | $type = $this->_elements[$key]->getType(); | |
56015454 | 2409 | |
4f51f48f | 2410 | if ($type == 'hidden'){ |
2411 | // leave hidden types as they are | |
2412 | } elseif (!isset($elementList[$name])) { | |
2413 | $this->_elements[$key]->freeze(); | |
2414 | $this->_elements[$key]->setPersistantFreeze(false); | |
2415 | ||
2416 | // remove all rules | |
2417 | $this->_rules[$name] = array(); | |
2418 | // if field is required, remove the rule | |
2419 | $unset = array_search($name, $this->_required); | |
2420 | if ($unset !== false) { | |
2421 | unset($this->_required[$unset]); | |
2422 | } | |
2423 | } | |
2424 | } | |
2425 | return true; | |
2426 | } | |
6c1fd304 | 2427 | |
4f51f48f | 2428 | /** |
2429 | * Tells whether the form was already submitted | |
2430 | * | |
2431 | * This is useful since the _submitFiles and _submitValues arrays | |
2432 | * may be completely empty after the trackSubmit value is removed. | |
2433 | * | |
4f51f48f | 2434 | * @return bool |
2435 | */ | |
2436 | function isSubmitted() | |
2437 | { | |
2438 | return parent::isSubmitted() && (!$this->isFrozen()); | |
2439 | } | |
da6f8763 | 2440 | } |
2441 | ||
2442 | /** | |
6c1fd304 RT |
2443 | * MoodleQuickForm renderer |
2444 | * | |
7f40a229 | 2445 | * A renderer for MoodleQuickForm that only uses XHTML and CSS and no |
da6f8763 | 2446 | * table tags, extends PEAR class HTML_QuickForm_Renderer_Tableless |
7f40a229 | 2447 | * |
da6f8763 | 2448 | * Stylesheet is part of standard theme and should be automatically included. |
2449 | * | |
6c1fd304 RT |
2450 | * @package core_form |
2451 | * @copyright 2007 Jamie Pratt <me@jamiep.org> | |
ba21c9d4 | 2452 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later |
da6f8763 | 2453 | */ |
7f40a229 | 2454 | class MoodleQuickForm_Renderer extends HTML_QuickForm_Renderer_Tableless{ |
da6f8763 | 2455 | |
6c1fd304 | 2456 | /** @var array Element template array */ |
da6f8763 | 2457 | var $_elementTemplates; |
6c1fd304 | 2458 | |
49c53687 | 2459 | /** |
6c1fd304 RT |
2460 | * Template used when opening a hidden fieldset |
2461 | * (i.e. a fieldset that is opened when there is no header element) | |
2462 | * @var string | |
2463 | */ | |
c02345e3 | 2464 | var $_openHiddenFieldsetTemplate = "\n\t<fieldset class=\"hidden\"><div>"; |
6c1fd304 RT |
2465 | |
2466 | /** @var string Header Template string */ | |
19194f82 | 2467 | var $_headerTemplate = |
ac5e6ca4 | 2468 | "\n\t\t<legend class=\"ftoggler\">{header}</legend>\n\t\t<div class=\"fcontainer clearfix\">\n\t\t"; |
7f40a229 | 2469 | |
6c1fd304 | 2470 | /** @var string Template used when opening a fieldset */ |
9ec532be | 2471 | var $_openFieldsetTemplate = "\n\t<fieldset class=\"{classes}\" {id} {aria-live}>"; |
bc9ec4a6 | 2472 | |
6c1fd304 | 2473 | /** @var string Template used when closing a fieldset */ |
c02345e3 | 2474 | var $_closeFieldsetTemplate = "\n\t\t</div></fieldset>"; |
42f248e6 | 2475 | |
6c1fd304 | 2476 | /** @var string Required Note template string */ |
7f40a229 | 2477 | var $_requiredNoteTemplate = |
6ba2c73d | 2478 | "\n\t\t<div class=\"fdescription required\">{requiredNote}</div>"; |
f2c0aba8 | 2479 | |
a4b076d5 FM |
2480 | /** |
2481 | * Collapsible buttons string template. | |
2482 | * | |
2483 | * Note that the <span> will be converted as a link. This is done so that the link is not yet clickable | |
2484 | * until the Javascript has been fully loaded. | |
2485 | * | |
2486 | * @var string | |
2487 | */ | |
f2c0aba8 | 2488 | var $_collapseButtonsTemplate = |
a4b076d5 | 2489 | "\n\t<div class=\"collapsible-actions\"><span class=\"collapseexpand\">{strexpandall}</span></div>"; |
f2c0aba8 | 2490 | |
a4067bfc RK |
2491 | /** |
2492 | * Array whose keys are element names. If the key exists this is a advanced element | |
2493 | * | |
2494 | * @var array | |
2495 | */ | |
19194f82 | 2496 | var $_advancedElements = array(); |
2497 | ||
a4067bfc RK |
2498 | /** |
2499 | * Array whose keys are element names and the the boolean values reflect the current state. If the key exists this is a collapsible element. | |
2500 | * | |
2501 | * @var array | |
2502 | */ | |
2503 | var $_collapsibleElements = array(); | |
2504 | ||
f2c0aba8 FM |
2505 | /** |
2506 | * @var string Contains the collapsible buttons to add to the form. | |
2507 | */ | |
2508 | var $_collapseButtons = ''; | |
2509 | ||
6c1fd304 RT |
2510 | /** |
2511 | * Constructor | |
2512 | */ | |
7f40a229 | 2513 | function MoodleQuickForm_Renderer(){ |
42f248e6 | 2514 | // switch next two lines for ol li containers for form items. |
49c7f3a8 | 2515 | // $this->_elementTemplates=array('default'=>"\n\t\t".'<li class="fitem"><label>{label}{help}<!-- BEGIN required -->{req}<!-- END required --></label><div class="qfelement<!-- BEGIN error --> error<!-- END error --> {type}"><!-- BEGIN error --><span class="error">{error}</span><br /><!-- END error -->{element}</div></li>'); |
9aa022fe | 2516 | $this->_elementTemplates = array( |
87c1c822 | 2517 | 'default'=>"\n\t\t".'<div id="{id}" class="fitem {advanced}<!-- BEGIN required --> required<!-- END required --> fitem_{type}" {aria-live}><div class="fitemtitle"><label>{label}<!-- BEGIN required -->{req}<!-- END required -->{advancedimg}{help} </label></div><div class="felement {type}<!-- BEGIN error --> error<!-- END error -->"><!-- BEGIN error --><span class="error">{error}</span><br /><!-- END error -->{element}</div></div>', |
49c7f3a8 | 2518 | |
95114b18 FM |
2519 | 'actionbuttons'=>"\n\t\t".'<div id="{id}" class="fitem fitem_actionbuttons fitem_{type}"><div class="felement {type}">{element}</div></div>', |
2520 | ||
510ccc55 | 2521 | 'fieldset'=>"\n\t\t".'<div id="{id}" class="fitem {advanced}<!-- BEGIN required --> required<!-- END required --> fitem_{type}"><div class="fitemtitle"><div class="fgrouplabel"><label>{label}<!-- BEGIN required -->{req}<!-- END required -->{advancedimg}{help} </label></div></div><fieldset class="felement {type}<!-- BEGIN error --> error<!-- END error -->"><!-- BEGIN error --><span class="error">{error}</span><br /><!-- END error -->{element}</fieldset></div>', |
4f51f48f | 2522 | |
510ccc55 | 2523 | 'static'=>"\n\t\t".'<div class="fitem {advanced}"><div class="fitemtitle"><div class="fstaticlabel"><label>{label}<!-- BEGIN required -->{req}<!-- END required -->{advancedimg}{help} </label></div></div><div class="felement fstatic <!-- BEGIN error --> error<!-- END error -->"><!-- BEGIN error --><span class="error">{error}</span><br /><!-- END error -->{element} </div></div>', |
43914931 | 2524 | |
510ccc55 | 2525 | 'warning'=>"\n\t\t".'<div class="fitem {advanced}">{element}</div>', |
1ae1941e | 2526 | |
4f51f48f | 2527 | 'nodisplay'=>''); |
da6f8763 | 2528 | |
2529 | parent::HTML_QuickForm_Renderer_Tableless(); | |
2530 | } | |
7f40a229 | 2531 | |
ba21c9d4 | 2532 | /** |
6c1fd304 RT |
2533 | * Set element's as adavance element |
2534 | * | |
2535 | * @param array $elements form elements which needs to be grouped as advance elements. | |
ba21c9d4 | 2536 | */ |
19194f82 | 2537 | function setAdvancedElements($elements){ |
2538 | $this->_advancedElements = $elements; | |
2539 | } | |
2540 | ||
a4067bfc RK |
2541 | /** |
2542 | * Setting collapsible elements | |
2543 | * | |
2544 | * @param array $elements | |
2545 | */ | |
2546 | function setCollapsibleElements($elements) { | |
2547 | $this->_collapsibleElements = $elements; | |
2548 | } | |
8dcca4aa | 2549 | |
19194f82 | 2550 | /** |
2551 | * What to do when starting the form | |
2552 | * | |
6c1fd304 | 2553 | * @param MoodleQuickForm $form reference of the form |
19194f82 | 2554 | */ |
da6f8763 | 2555 | function startForm(&$form){ |
00e8d08d | 2556 | global $PAGE; |
9403060a | 2557 | $this->_reqHTML = $form->getReqHTML(); |
2558 | $this->_elementTemplates = str_replace('{req}', $this->_reqHTML, $this->_elementTemplates); | |
19194f82 | 2559 | $this->_advancedHTML = $form->getAdvancedHTML(); |
f2c0aba8 | 2560 | $this->_collapseButtons = ''; |
a4067bfc | 2561 | $formid = $form->getAttribute('id'); |
da6f8763 | 2562 | parent::startForm($form); |
4f51f48f | 2563 | if ($form->isFrozen()){ |
2564 | $this->_formTemplate = "\n<div class=\"mform frozen\">\n{content}\n</div>"; | |
2565 | } else { | |
f2c0aba8 | 2566 | $this->_formTemplate = "\n<form{attributes}>\n\t<div style=\"display: none;\">{hidden}</div>\n{collapsebtns}\n{content}\n</form>"; |
4f51f48f | 2567 | $this->_hiddenHtml .= $form->_pageparams; |
2568 | } | |
2569 | ||
6b65e50b TH |
2570 | if ($form->is_form_change_checker_enabled()) { |
2571 | $PAGE->requires->yui_module('moodle-core-formchangechecker', | |
2572 | 'M.core_formchangechecker.init', | |
2573 | array(array( | |
a4067bfc | 2574 | 'formid' => $formid |
6b65e50b TH |
2575 | )) |
2576 | ); | |
2577 | $PAGE->requires->string_for_js('changesmadereallygoaway', 'moodle'); | |
2578 | } | |
d23edcf1 | 2579 | if (!empty($this->_collapsibleElements)) { |
f2c0aba8 FM |
2580 | if (count($this->_collapsibleElements) > 1) { |
2581 | $this->_collapseButtons = $this->_collapseButtonsTemplate; | |
f2c0aba8 | 2582 | $this->_collapseButtons = str_replace('{strexpandall}', get_string('expandall'), $this->_collapseButtons); |
a4b076d5 | 2583 | $PAGE->requires->strings_for_js(array('collapseall', 'expandall'), 'moodle'); |
f2c0aba8 | 2584 | } |
a4067bfc RK |
2585 | $PAGE->requires->yui_module('moodle-form-shortforms', 'M.form.shortforms', array(array('formid' => $formid))); |
2586 | } | |
8dcca4aa | 2587 | if (!empty($this->_advancedElements)){ |
ac5e6ca4 RK |
2588 | $PAGE->requires->strings_for_js(array('showmore', 'showless'), 'form'); |
2589 | $PAGE->requires->yui_module('moodle-form-showadvanced', 'M.form.showadvanced', array(array('formid' => $formid))); | |
2590 | } | |
da6f8763 | 2591 | } |
117bd748 | 2592 | |
ba21c9d4 | 2593 | /** |
6c1fd304 RT |
2594 | * Create advance group of elements |
2595 | * | |
ba21c9d4 | 2596 | * @param object $group Passed by reference |
6c1fd304 RT |
2597 | * @param bool $required if input is required field |
2598 | * @param string $error error message to display | |
ba21c9d4 | 2599 | */ |
da6f8763 | 2600 | function startGroup(&$group, $required, $error){ |
0f3f6fed AF |
2601 | // Make sure the element has an id. |
2602 | $group->_generateId(); | |
2603 | ||
da6f8763 | 2604 | if (method_exists($group, 'getElementTemplateType')){ |
e249661f | 2605 | $html = $this->_elementTemplates[$group->getElementTemplateType()]; |
da6f8763 | 2606 | }else{ |
2607 | $html = $this->_elementTemplates['default']; | |
7f40a229 | 2608 | |
da6f8763 | 2609 | } |
ac5e6ca4 | 2610 | |
19194f82 | 2611 | if (isset($this->_advancedElements[$group->getName()])){ |
ac5e6ca4 | 2612 | $html =str_replace(' {advanced}', ' advanced', $html); |
19194f82 | 2613 | $html =str_replace('{advancedimg}', $this->_advancedHTML, $html); |
2614 | } else { | |
2615 | $html =str_replace(' {advanced}', '', $html); | |
2616 | $html =str_replace('{advancedimg}', '', $html); | |
2617 | } | |
da6f8763 | 2618 | if (method_exists($group, 'getHelpButton')){ |
2619 | $html =str_replace('{help}', $group->getHelpButton(), $html); | |
2620 | }else{ | |
2621 | $html =str_replace('{help}', '', $html); | |
da6f8763 | 2622 | } |
0f3f6fed | 2623 | $html =str_replace('{id}', 'fgroup_' . $group->getAttribute('id'), $html); |
e7004d05 | 2624 | $html =str_replace('{name}', $group->getName(), $html); |
49c53687 | 2625 | $html =str_replace('{type}', 'fgroup', $html); |
7f40a229 | 2626 | |
da6f8763 | 2627 | $this->_templates[$group->getName()]=$html; |
2628 | // Fix for bug in tableless quickforms that didn't allow you to stop a | |
2629 | // fieldset before a group of elements. | |
2630 | // if the element name indicates the end of a fieldset, close the fieldset | |
2631 | if ( in_array($group->getName(), $this->_stopFieldsetElements) | |
2632 | && $this->_fieldsetsOpen > 0 | |
2633 | ) { | |
2634 | $this->_html .= $this->_closeFieldsetTemplate; | |
2635 | $this->_fieldsetsOpen--; | |
2636 | } | |
2637 | parent::startGroup($group, $required, $error); | |
2638 | } | |
8dcca4aa | 2639 | |
ba21c9d4 | 2640 | /** |
6c1fd304 RT |
2641 | * Renders element |
2642 | * | |
2643 | * @param HTML_QuickForm_element $element element | |
2644 | * @param bool $required if input is required field | |
2645 | * @param string $error error message to display | |
ba21c9d4 | 2646 | */ |
da6f8763 | 2647 | function renderElement(&$element, $required, $error){ |
820b41e3 TH |
2648 | // Make sure the element has an id. |
2649 | $element->_generateId(); | |
86aab05c | 2650 | |
2651 | //adding stuff to place holders in template | |
172dd12c | 2652 | //check if this is a group element first |
906ebc4b | 2653 | if (($this->_inGroup) and !empty($this->_groupElementTemplate)) { |
3493eb15 | 2654 | // so it gets substitutions for *each* element |
c07b5ad4 | 2655 | $html = $this->_groupElementTemplate; |
906ebc4b | 2656 | } |
2657 | elseif (method_exists($element, 'getElementTemplateType')){ | |
da6f8763 | 2658 | $html = $this->_elementTemplates[$element->getElementTemplateType()]; |
2659 | }else{ | |
2660 | $html = $this->_elementTemplates['default']; | |
19194f82 | 2661 | } |
19194f82 | 2662 | if (isset($this->_advancedElements[$element->getName()])){ |
87c1c822 FM |
2663 | $html = str_replace(' {advanced}', ' advanced', $html); |
2664 | $html = str_replace(' {aria-live}', ' aria-live="polite"', $html); | |
19194f82 | 2665 | } else { |
87c1c822 FM |
2666 | $html = str_replace(' {advanced}', '', $html); |
2667 | $html = str_replace(' {aria-live}', '', $html); | |
19194f82 | 2668 | } |
2669 | if (isset($this->_advancedElements[$element->getName()])||$element->getName() == 'mform_showadvanced'){ | |
2670 | $html =str_replace('{advancedimg}', $this->_advancedHTML, $html); | |
2671 | } else { | |
2672 | $html =str_replace('{advancedimg}', '', $html); | |
da6f8763 | 2673 | } |
0f3f6fed | 2674 | $html =str_replace('{id}', 'fitem_' . $element->getAttribute('id'), $html); |
49c53687 | 2675 | $html =str_replace('{type}', 'f'.$element->getType(), $html); |
e7004d05 | 2676 | $html =str_replace('{name}', $element->getName(), $html); |
da6f8763 | 2677 | if (method_exists($element, 'getHelpButton')){ |
9403060a | 2678 | $html = str_replace('{help}', $element->getHelpButton(), $html); |
da6f8763 | 2679 | }else{ |
9403060a | 2680 | $html = str_replace('{help}', '', $html); |
7f40a229 | 2681 | |
da6f8763 | 2682 | } |
906ebc4b | 2683 | if (($this->_inGroup) and !empty($this->_groupElementTemplate)) { |
2684 | $this->_groupElementTemplate = $html; | |
41b6d001 | 2685 | } |
906ebc4b | 2686 | elseif (!isset($this->_templates[$element->getName()])) { |
2687 | $this->_templates[$element->getName()] = $html; | |
172dd12c | 2688 | } |
2689 | ||
da6f8763 | 2690 | parent::renderElement($element, $required, $error); |
2691 | } | |
19194f82 | 2692 | |
ba21c9d4 | 2693 | /** |
6c1fd304 RT |
2694 | * Called when visiting a form, after processing all form elements |
2695 | * Adds required note, form attributes, validation javascript and form content. | |
2696 | * | |
c7e3e61c | 2697 | * @global moodle_page $PAGE |
6c1fd304 | 2698 | * @param moodleform $form Passed by reference |
ba21c9d4 | 2699 | */ |
bb40325e | 2700 | function finishForm(&$form){ |
c7e3e61c | 2701 | global $PAGE; |
4f51f48f | 2702 | if ($form->isFrozen()){ |
2703 | $this->_hiddenHtml = ''; | |
2704 | } | |
bb40325e | 2705 | parent::finishForm($form); |
f2c0aba8 | 2706 | $this->_html = str_replace('{collapsebtns}', $this->_collapseButtons, $this->_html); |
c7e3e61c SH |
2707 | if (!$form->isFrozen()) { |
2708 | $args = $form->getLockOptionObject(); | |
2709 | if (count($args[1]) > 0) { | |
63d5c4ac | 2710 | $PAGE->requires->js_init_call('M.form.initFormDependencies', $args, true, moodleform::get_js_module()); |
c7e3e61c | 2711 | } |
bb40325e | 2712 | } |
2713 | } | |
19194f82 | 2714 | /** |
2715 | * Called when visiting a header element | |
2716 | * | |
6c1fd304 | 2717 | * @param HTML_QuickForm_header $header An HTML_QuickForm_header element being visited |
c7e3e61c | 2718 | * @global moodle_page $PAGE |
19194f82 | 2719 | */ |
c28bf5c9 | 2720 | function renderHeader(&$header) { |
2721 | global $PAGE; | |
9262d2d3 | 2722 | |
d23edcf1 | 2723 | $header->_generateId(); |
19194f82 | 2724 | $name = $header->getName(); |
2725 | ||
d23edcf1 | 2726 | $id = empty($name) ? '' : ' id="' . $header->getAttribute('id') . '"'; |
19194f82 | 2727 | if (is_null($header->_text)) { |
2728 | $header_html = ''; | |
2729 | } elseif (!empty($name) && isset($this->_templates[$name])) { | |
2730 | $header_html = str_replace('{header}', $header->toHtml(), $this->_templates[$name]); | |
2731 | } else { | |
2732 | $header_html = str_replace('{header}', $header->toHtml(), $this->_headerTemplate); | |
2733 | } | |
2734 | ||
19194f82 | 2735 | if ($this->_fieldsetsOpen > 0) { |
2736 | $this->_html .= $this->_closeFieldsetTemplate; | |
2737 | $this->_fieldsetsOpen--; | |
2738 | } | |
2739 | ||
8dcca4aa | 2740 | // Define collapsible classes for fieldsets. |
9ec532be | 2741 | $arialive = ''; |
a4067bfc | 2742 | $fieldsetclasses = array('clearfix'); |
f160f97c | 2743 | if (isset($this->_collapsibleElements[$header->getName()])) { |
a4067bfc | 2744 | $fieldsetclasses[] = 'collapsible'; |
9ec532be | 2745 | $arialive = 'aria-live="polite"'; |
f160f97c | 2746 | if ($this->_collapsibleElements[$header->getName()]) { |
a4067bfc RK |
2747 | $fieldsetclasses[] = 'collapsed'; |
2748 | } | |
19194f82 | 2749 | } |
a4067bfc | 2750 | |
ac5e6ca4 RK |
2751 | if (isset($this->_advancedElements[$name])){ |
2752 | $fieldsetclasses[] = 'containsadvancedelements'; | |
2753 | } | |
2754 | ||
a4067bfc RK |
2755 | $openFieldsetTemplate = str_replace('{id}', $id, $this->_openFieldsetTemplate); |
2756 | $openFieldsetTemplate = str_replace('{classes}', join(' ', $fieldsetclasses), $openFieldsetTemplate); | |
9ec532be | 2757 | $openFieldsetTemplate = str_replace('{aria-live}', $arialive, $openFieldsetTemplate); |
a4067bfc | 2758 | |
19194f82 | 2759 | $this->_html .= $openFieldsetTemplate . $header_html; |
2760 | $this->_fieldsetsOpen++; | |
6c1fd304 | 2761 | } |
19194f82 | 2762 | |
6c1fd304 RT |
2763 | /** |
2764 | * Return Array of element names that indicate the end of a fieldset | |
2765 | * | |
2766 | * @return array | |
2767 | */ | |
19194f82 | 2768 | function getStopFieldsetElements(){ |
2769 | return $this->_stopFieldsetElements; | |
2770 | } | |
da6f8763 | 2771 | } |
2772 | ||
7b5702b6 MG |
2773 | /** |
2774 | * Required elements validation | |
6c1fd304 | 2775 | * |
7b5702b6 | 2776 | * This class overrides QuickForm validation since it allowed space or empty tag as a value |
6c1fd304 RT |
2777 | * |
2778 | * @package core_form | |
2779 | * @category form | |
2780 | * @copyright 2006 Jamie Pratt <me@jamiep.org> | |
2781 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
7b5702b6 MG |
2782 | */ |
2783 | class MoodleQuickForm_Rule_Required extends HTML_QuickForm_Rule { | |
2784 | /** | |
2785 | * Checks if an element is not empty. | |
2786 | * This is a server-side validation, it works for both text fields and editor fields | |
2787 | * | |
6c1fd304 RT |
2788 | * @param string $value Value to check |
2789 | * @param int|string|array $options Not used yet | |
2790 | * @return bool true if value is not empty | |
7b5702b6 MG |
2791 | */ |
2792 | function validate($value, $options = null) { | |
2793 | global $CFG; | |
2794 | if (is_array($value) && array_key_exists('text', $value)) { | |
2795 | $value = $value['text']; | |
2796 | } | |
b85b25eb PS |
2797 | if (is_array($value)) { |
2798 | // nasty guess - there has to be something in the array, hopefully nobody invents arrays in arrays | |
2799 | $value = implode('', $value); | |
2800 | } | |
7b5702b6 MG |
2801 | $stripvalues = array( |
2802 | '#</?(?!img|canvas|hr).*?>#im', // all tags except img, canvas and hr | |
2803 | '#(\xc2|\xa0|\s| )#', //any whitespaces actually | |
2804 | ); | |
2805 | if (!empty($CFG->strictformsrequired)) { | |
2806 | $value = preg_replace($stripvalues, '', (string)$value); | |
2807 | } | |
2808 | if ((string)$value == '') { | |
2809 | return false; | |
2810 | } | |
2811 | return true; | |
2812 | } | |
2813 | ||
2814 | /** | |
2815 | * This function returns Javascript code used to build client-side validation. | |
2816 | * It checks if an element is not empty. | |
7b5702b6 | 2817 | * |
6c1fd304 | 2818 | * @param int $format format of data which needs to be validated. |
7b5702b6 MG |
2819 | * @return array |
2820 | */ | |
ab978b38 | 2821 | function getValidationScript($format = null) { |
7b5702b6 MG |
2822 | global $CFG; |
2823 | if (!empty($CFG->strictformsrequired)) { | |
ab978b38 RT |
2824 | if (!empty($format) && $format == FORMAT_HTML) { |
2825 | return array('', "{jsVar}.replace(/(<[^img|hr|canvas]+>)| |\s+/ig, '') == ''"); | |
2826 | } else { | |
2827 | return array('', "{jsVar}.replace(/^\s+$/g, '') == ''"); | |
2828 | } | |
7b5702b6 MG |
2829 | } else { |
2830 | return array('', "{jsVar} == ''"); | |
2831 | } | |
2832 | } | |
2833 | } | |
2834 | ||
ba21c9d4 | 2835 | /** |
2836 | * @global object $GLOBALS['_HTML_QuickForm_default_renderer'] | |
2837 | * @name $_HTML_QuickForm_default_renderer | |
2838 | */ | |
66491cf1 | 2839 | $GLOBALS['_HTML_QuickForm_default_renderer'] = new MoodleQuickForm_Renderer(); |
da6f8763 | 2840 | |
ba21c9d4 | 2841 | /** Please keep this list in alphabetical order. */ |
c583482c | 2842 | MoodleQuickForm::registerElementType('advcheckbox', "$CFG->libdir/form/advcheckbox.php", 'MoodleQuickForm_advcheckbox'); |
2843 | MoodleQuickForm::registerElementType('button', "$CFG->libdir/form/button.php", 'MoodleQuickForm_button'); | |
2844 | MoodleQuickForm::registerElementType('cancel', "$CFG->libdir/form/cancel.php", 'MoodleQuickForm_cancel'); | |
09179b78 | 2845 | MoodleQuickForm::registerElementType('searchableselector', "$CFG->libdir/form/searchableselector.php", 'MoodleQuickForm_searchableselector'); |
7f40a229 | 2846 | MoodleQuickForm::registerElementType('checkbox', "$CFG->libdir/form/checkbox.php", 'MoodleQuickForm_checkbox'); |
c583482c | 2847 | MoodleQuickForm::registerElementType('date_selector', "$CFG->libdir/form/dateselector.php", 'MoodleQuickForm_date_selector'); |
2848 | MoodleQuickForm::registerElementType('date_time_selector', "$CFG->libdir/form/datetimeselector.php", 'MoodleQuickForm_date_time_selector'); | |
32fa2272 | 2849 | MoodleQuickForm::registerElementType('duration', "$CFG->libdir/form/duration.php", 'MoodleQuickForm_duration'); |
c583482c | 2850 | MoodleQuickForm::registerElementType('editor', "$CFG->libdir/form/editor.php", 'MoodleQuickForm_editor'); |
241431cd | 2851 | MoodleQuickForm::registerElementType('filemanager', "$CFG->libdir/form/filemanager.php", 'MoodleQuickForm_filemanager'); |
c5704ec6 | 2852 | MoodleQuickForm::registerElementType('filepicker', "$CFG->libdir/form/filepicker.php", 'MoodleQuickForm_filepicker'); |
6798c63e | 2853 | MoodleQuickForm::registerElementType('grading', "$CFG->libdir/form/grading.php", 'MoodleQuickForm_grading'); |
7f40a229 | 2854 | MoodleQuickForm::registerElementType('group', "$CFG->libdir/form/group.php", 'MoodleQuickForm_group'); |
c583482c | 2855 | MoodleQuickForm::registerElementType('header', "$CFG->libdir/form/header.php", 'MoodleQuickForm_header'); |
2856 | MoodleQuickForm::registerElementType('hidden', "$CFG->libdir/form/hidden.php", 'MoodleQuickForm_hidden'); | |
2857 | MoodleQuickForm::registerElementType('htmleditor', "$CFG->libdir/form/htmleditor.php", 'MoodleQuickForm_htmleditor'); | |
46b229f9 | 2858 | MoodleQuickForm::registerElementType('listing', "$CFG->libdir/form/listing.php", 'MoodleQuickForm_listing'); |
c583482c | 2859 | MoodleQuickForm::registerElementType('modgrade', "$CFG->libdir/form/modgrade.php", 'MoodleQuickForm_modgrade'); |
2860 | MoodleQuickForm::registerElementType('modvisible', "$CFG->libdir/form/modvisible.php", 'MoodleQuickForm_modvisible'); | |
7f40a229 | 2861 | MoodleQuickForm::registerElementType('password', "$CFG->libdir/form/password.php", 'MoodleQuickForm_password'); |
4f51f48f | 2862 | MoodleQuickForm::registerElementType('passwordunmask', "$CFG->libdir/form/passwordunmask.php", 'MoodleQuickForm_passwordunmask'); |
c583482c | 2863 | MoodleQuickForm::registerElementType('questioncategory', "$CFG->libdir/form/questioncategory.php", 'MoodleQuickForm_questioncategory'); |
7f40a229 | 2864 | MoodleQuickForm::registerElementType('radio', "$CFG->libdir/form/radio.php", 'MoodleQuickForm_radio'); |
c583482c | 2865 | MoodleQuickForm::registerElementType('recaptcha', "$CFG->libdir/form/recaptcha.php", 'MoodleQuickForm_recaptcha'); |
7f40a229 | 2866 | MoodleQuickForm::registerElementType('select', "$CFG->libdir/form/select.php", 'MoodleQuickForm_select'); |
4f51f48f | 2867 | MoodleQuickForm::registerElementType('selectgroups', "$CFG->libdir/form/selectgroups.php", 'MoodleQuickForm_selectgroups'); |
c583482c | 2868 | MoodleQuickForm::registerElementType('selectwithlink', "$CFG->libdir/form/selectwithlink.php", 'MoodleQuickForm_selectwithlink'); |
2869 | MoodleQuickForm::registerElementType('selectyesno', "$CFG->libdir/form/selectyesno.php", 'MoodleQuickForm_selectyesno'); | |
2870 | MoodleQuickForm::registerElementType('static', "$CFG->libdir/form/static.php", 'MoodleQuickForm_static'); | |
2871 | MoodleQuickForm::registerElementType('submit', "$CFG->libdir/form/submit.php", 'MoodleQuickForm_submit'); | |
6073a598 | 2872 | MoodleQuickForm::registerElementType('submitlink', "$CFG->libdir/form/submitlink.php", 'MoodleQuickForm_submitlink'); |
9f1c9dfc | 2873 | MoodleQuickForm::registerElementType('tags', "$CFG->libdir/form/tags.php", 'MoodleQuickForm_tags'); |
7f40a229 | 2874 | MoodleQuickForm::registerElementType('text', "$CFG->libdir/form/text.php", 'MoodleQuickForm_text'); |
2875 | MoodleQuickForm::registerElementType('textarea', "$CFG->libdir/form/textarea.php", 'MoodleQuickForm_textarea'); | |
1a03384f | 2876 | MoodleQuickForm::registerElementType('url', "$CFG->libdir/form/url.php", 'MoodleQuickForm_url'); |
1ae1941e | 2877 | MoodleQuickForm::registerElementType('warning', "$CFG->libdir/form/warning.php", 'MoodleQuickForm_warning'); |
7b5702b6 MG |
2878 | |
2879 | MoodleQuickForm::registerRule('required', null, 'MoodleQuickForm_Rule_Required', "$CFG->libdir/formslib.php"); |