MDL-14589 improved param name
[moodle.git] / lib / formslib.php
CommitLineData
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 :
28 * form defintion is used for both printing of form and processing and should be the same
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 *
33 *
ba21c9d4 34 * @copyright Jamie Pratt <me@jamiep.org>
35 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
36 * @package moodlecore
da6f8763 37 */
38
ba21c9d4 39/** setup.php icludes our hacked pear libs first */
da6f8763 40require_once 'HTML/QuickForm.php';
41require_once 'HTML/QuickForm/DHTMLRulesTableless.php';
42require_once 'HTML/QuickForm/Renderer/Tableless.php';
43
a83ad946 44require_once $CFG->libdir.'/filelib.php';
f6ac3e0a 45require_once $CFG->libdir.'/uploadlib.php'; // TODO: remove
49292f8c 46
832e13f1 47define('EDITOR_UNLIMITED_FILES', -1);
48
a23f0aaf 49/**
50 * Callback called when PEAR throws an error
51 *
52 * @param PEAR_Error $error
53 */
54function pear_handle_error($error){
55 echo '<strong>'.$error->GetMessage().'</strong> '.$error->getUserInfo();
56 echo '<br /> <strong>Backtrace </strong>:';
57 print_object($error->backtrace);
58}
59
66491cf1 60if (!empty($CFG->debug) and $CFG->debug >= DEBUG_ALL){
a23f0aaf 61 PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, 'pear_handle_error');
864cc1de 62}
63
ba21c9d4 64/**
117bd748 65 *
ba21c9d4 66 * @staticvar bool $done
ba21c9d4 67 */
8e7cebb0 68function form_init_date_js() {
cf615522 69 global $PAGE;
8e7cebb0 70 static $done = false;
71 if (!$done) {
f44b10ed
PS
72 $PAGE->requires->yui2_lib('calendar');
73 $PAGE->requires->yui2_lib('container');
cf615522 74 $PAGE->requires->js_function_call('init_date_selectors',
0a933ae6 75 array(get_string('firstdayofweek', 'langconfig')));
8e7cebb0 76 $done = true;
77 }
78}
f07b9627 79
05f5c40c 80/**
da1320da 81 * Moodle specific wrapper that separates quickforms syntax from moodle code. You won't directly
6073a598 82 * use this class you should write a class definition which extends this class or a more specific
da1320da 83 * subclass such a moodleform_mod for each form you want to display and/or process with formslib.
84 *
85 * You will write your own definition() method which performs the form set up.
ba21c9d4 86 *
87 * @package moodlecore
88 * @copyright Jamie Pratt <me@jamiep.org>
89 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
05f5c40c 90 */
afd63fe5 91abstract class moodleform {
ba21c9d4 92 /** @var string */
172dd12c 93 protected $_formname; // form name
3c7656b4 94 /**
95 * quickform object definition
96 *
572dd8ec 97 * @var MoodleQuickForm MoodleQuickForm
3c7656b4 98 */
172dd12c 99 protected $_form;
3c7656b4 100 /**
101 * globals workaround
102 *
103 * @var array
104 */
172dd12c 105 protected $_customdata;
4f51f48f 106 /**
107 * definition_after_data executed flag
ba21c9d4 108 * @var object definition_finalized
4f51f48f 109 */
172dd12c 110 protected $_definition_finalized = false;
ebd3c7ac 111
da1320da 112 /**
113 * The constructor function calls the abstract function definition() and it will then
114 * process and clean and attempt to validate incoming data.
115 *
116 * It will call your custom validate method to validate data and will also check any rules
117 * you have specified in definition using addRule
118 *
119 * The name of the form (id attribute of the form) is automatically generated depending on
120 * the name you gave the class extending moodleform. You should call your class something
121 * like
122 *
4f51f48f 123 * @param mixed $action the action attribute for the form. If empty defaults to auto detect the
124 * current url. If a moodle_url object then outputs params as hidden variables.
da1320da 125 * @param array $customdata if your form defintion method needs access to data such as $course
126 * $cm, etc. to construct the form definition then pass it in this array. You can
127 * use globals for somethings.
128 * @param string $method if you set this to anything other than 'post' then _GET and _POST will
129 * be merged and used as incoming data to the form.
130 * @param string $target target frame for form submission. You will rarely use this. Don't use
131 * it if you don't need to as the target attribute is deprecated in xhtml
132 * strict.
133 * @param mixed $attributes you can pass a string of html attributes here or an array.
ba21c9d4 134 * @param bool $editable
135 * @return object moodleform
da1320da 136 */
4f51f48f 137 function moodleform($action=null, $customdata=null, $method='post', $target='', $attributes=null, $editable=true) {
a23f0aaf 138 if (empty($action)){
139 $action = strip_querystring(qualified_me());
140 }
f07b9627 141
72f46d11 142 $this->_formname = get_class($this); // '_form' suffix kept in order to prevent collisions of form id and other element
7f40a229 143 $this->_customdata = $customdata;
66491cf1 144 $this->_form = new MoodleQuickForm($this->_formname, $method, $action, $target, $attributes);
4f51f48f 145 if (!$editable){
146 $this->_form->hardFreeze();
147 }
7f40a229 148
149 $this->definition();
150
151 $this->_form->addElement('hidden', 'sesskey', null); // automatic sesskey protection
d18e0fe6 152 $this->_form->setType('sesskey', PARAM_RAW);
7f40a229 153 $this->_form->setDefault('sesskey', sesskey());
5bc97c98 154 $this->_form->addElement('hidden', '_qf__'.$this->_formname, null); // form submission marker
d18e0fe6 155 $this->_form->setType('_qf__'.$this->_formname, PARAM_RAW);
5bc97c98 156 $this->_form->setDefault('_qf__'.$this->_formname, 1);
157 $this->_form->_setDefaultRuleMessages();
7f40a229 158
159 // we have to know all input types before processing submission ;-)
160 $this->_process_submission($method);
7f40a229 161 }
05f5c40c 162
2c412890 163 /**
da1320da 164 * To autofocus on first form element or first element with error.
2c412890 165 *
8dec2253 166 * @param string $name if this is set then the focus is forced to a field with this name
167 *
2c412890 168 * @return string javascript to select form element with first error or
da1320da 169 * first element if no errors. Use this as a parameter
170 * when calling print_header
2c412890 171 */
46f3921e 172 function focus($name=NULL) {
9403060a 173 $form =& $this->_form;
46f3921e 174 $elkeys = array_keys($form->_elementIndex);
175 $error = false;
9403060a 176 if (isset($form->_errors) && 0 != count($form->_errors)){
177 $errorkeys = array_keys($form->_errors);
178 $elkeys = array_intersect($elkeys, $errorkeys);
46f3921e 179 $error = true;
2c412890 180 }
46f3921e 181
182 if ($error or empty($name)) {
183 $names = array();
184 while (empty($names) and !empty($elkeys)) {
185 $el = array_shift($elkeys);
186 $names = $form->_getElNamesRecursive($el);
187 }
188 if (!empty($names)) {
189 $name = array_shift($names);
190 }
8dec2253 191 }
46f3921e 192
193 $focus = '';
194 if (!empty($name)) {
195 $focus = 'forms[\''.$form->getAttribute('id').'\'].elements[\''.$name.'\']';
9403060a 196 }
46f3921e 197
9403060a 198 return $focus;
199 }
7f40a229 200
05f5c40c 201 /**
202 * Internal method. Alters submitted data to be suitable for quickforms processing.
203 * Must be called when the form is fully set up.
ba21c9d4 204 *
205 * @param string $method
05f5c40c 206 */
7f40a229 207 function _process_submission($method) {
208 $submission = array();
209 if ($method == 'post') {
210 if (!empty($_POST)) {
211 $submission = $_POST;
212 }
213 } else {
214 $submission = array_merge_recursive($_GET, $_POST); // emulate handling of parameters in xxxx_param()
215 }
216
217 // following trick is needed to enable proper sesskey checks when using GET forms
5bc97c98 218 // the _qf__.$this->_formname serves as a marker that form was actually submitted
219 if (array_key_exists('_qf__'.$this->_formname, $submission) and $submission['_qf__'.$this->_formname] == 1) {
7f40a229 220 if (!confirm_sesskey()) {
c3f9ee10 221 print_error('invalidsesskey');
7f40a229 222 }
05f5c40c 223 $files = $_FILES;
7f40a229 224 } else {
225 $submission = array();
05f5c40c 226 $files = array();
7f40a229 227 }
228
05f5c40c 229 $this->_form->updateSubmission($submission, $files);
7f40a229 230 }
231
05f5c40c 232 /**
4287fc0d 233 * Internal method. Validates all old-style uploaded files.
117bd748 234 *
ba21c9d4 235 * @global object
236 * @global object
237 * @param array $files
238 * @return bool|array Success or an array of errors
05f5c40c 239 */
89489cfe 240 function _validate_files(&$files) {
172dd12c 241 global $CFG, $COURSE;
242
89489cfe 243 $files = array();
244
49292f8c 245 if (empty($_FILES)) {
246 // we do not need to do any checks because no files were submitted
89489cfe 247 // note: server side rules do not work for files - use custom verification in validate() instead
49292f8c 248 return true;
249 }
49292f8c 250
172dd12c 251 $errors = array();
252 $filenames = array();
49292f8c 253
254 // now check that we really want each file
255 foreach ($_FILES as $elname=>$file) {
172dd12c 256 $required = $this->_form->isElementRequired($elname);
89489cfe 257
172dd12c 258 if ($file['error'] == 4 and $file['size'] == 0) {
259 if ($required) {
260 $errors[$elname] = get_string('required');
49292f8c 261 }
172dd12c 262 unset($_FILES[$elname]);
263 continue;
264 }
265
a83ad946 266 if (!empty($file['error'])) {
267 $errors[$elname] = file_get_upload_error($file['error']);
172dd12c 268 unset($_FILES[$elname]);
269 continue;
270 }
271
272 if (!is_uploaded_file($file['tmp_name'])) {
273 // TODO: improve error message
274 $errors[$elname] = get_string('error');
275 unset($_FILES[$elname]);
276 continue;
277 }
278
279 if (!$this->_form->elementExists($elname) or !$this->_form->getElementType($elname)=='file') {
280 // hmm, this file was not requested
281 unset($_FILES[$elname]);
282 continue;
283 }
284
285/*
286 // TODO: rethink the file scanning
287 if ($CFG->runclamonupload) {
288 if (!clam_scan_moodle_file($_FILES[$elname], $COURSE)) {
289 $errors[$elname] = $_FILES[$elname]['uploadlog'];
290 unset($_FILES[$elname]);
291 continue;
292 }
293 }
294*/
295 $filename = clean_param($_FILES[$elname]['name'], PARAM_FILE);
296 if ($filename === '') {
297 // TODO: improve error message - wrong chars
298 $errors[$elname] = get_string('error');
299 unset($_FILES[$elname]);
300 continue;
49292f8c 301 }
172dd12c 302 if (in_array($filename, $filenames)) {
303 // TODO: improve error message - duplicate name
304 $errors[$elname] = get_string('error');
305 unset($_FILES[$elname]);
306 continue;
307 }
308 $filenames[] = $filename;
309 $_FILES[$elname]['name'] = $filename;
310
311 $files[$elname] = $_FILES[$elname]['tmp_name'];
49292f8c 312 }
313
314 // return errors if found
172dd12c 315 if (count($errors) == 0){
49292f8c 316 return true;
89489cfe 317
49292f8c 318 } else {
89489cfe 319 $files = array();
49292f8c 320 return $errors;
321 }
322 }
323
05f5c40c 324 /**
da1320da 325 * Load in existing data as form defaults. Usually new entry defaults are stored directly in
326 * form definition (new entry form); this function is used to load in data where values
327 * already exist and data is being edited (edit entry form).
05f5c40c 328 *
294ce987 329 * note: $slashed param removed
330 *
05f5c40c 331 * @param mixed $default_values object or array of default values
332 * @param bool $slased true if magic quotes applied to data values
333 */
294ce987 334 function set_data($default_values) {
7f40a229 335 if (is_object($default_values)) {
336 $default_values = (array)$default_values;
337 }
294ce987 338 $this->_form->setDefaults($default_values);
7f40a229 339 }
340
ba21c9d4 341 /**
342 * @param bool $um
343 */
feaf5d06 344 function set_upload_manager($um=false) {
172dd12c 345 debugging('Not used anymore, please fix code!');
c80a13c7 346 }
347
05f5c40c 348 /**
349 * Check that form was submitted. Does not check validity of submitted data.
350 *
351 * @return bool true if form properly submitted
352 */
7f40a229 353 function is_submitted() {
354 return $this->_form->isSubmitted();
355 }
356
ba21c9d4 357 /**
358 * @staticvar bool $nosubmit
359 */
a23f0aaf 360 function no_submit_button_pressed(){
361 static $nosubmit = null; // one check is enough
362 if (!is_null($nosubmit)){
363 return $nosubmit;
364 }
365 $mform =& $this->_form;
366 $nosubmit = false;
f07b9627 367 if (!$this->is_submitted()){
368 return false;
369 }
a23f0aaf 370 foreach ($mform->_noSubmitButtons as $nosubmitbutton){
371 if (optional_param($nosubmitbutton, 0, PARAM_RAW)){
372 $nosubmit = true;
373 break;
374 }
375 }
376 return $nosubmit;
377 }
378
379
05f5c40c 380 /**
381 * Check that form data is valid.
6bba6dbb 382 * You should almost always use this, rather than {@see validate_defined_fields}
05f5c40c 383 *
ba21c9d4 384 * @staticvar bool $validated
05f5c40c 385 * @return bool true if form data valid
386 */
7f40a229 387 function is_validated() {
4f51f48f 388 //finalize the form definition before any processing
389 if (!$this->_definition_finalized) {
390 $this->_definition_finalized = true;
391 $this->definition_after_data();
392 }
393
6bba6dbb 394 return $this->validate_defined_fields();
395 }
396
397 /**
398 * Validate the form.
399 *
400 * You almost always want to call {@see is_validated} instead of this
401 * because it calls {@see definition_after_data} first, before validating the form,
402 * which is what you want in 99% of cases.
403 *
404 * This is provided as a separate function for those special cases where
405 * you want the form validated before definition_after_data is called
406 * for example, to selectively add new elements depending on a no_submit_button press,
407 * but only when the form is valid when the no_submit_button is pressed,
408 *
409 * @param boolean $validateonnosubmit optional, defaults to false. The default behaviour
410 * is NOT to validate the form when a no submit button has been pressed.
411 * pass true here to override this behaviour
412 *
413 * @return bool true if form data valid
414 */
415 function validate_defined_fields($validateonnosubmit=false) {
416 static $validated = null; // one validation is enough
417 $mform =& $this->_form;
418 if ($this->no_submit_button_pressed() && empty($validateonnosubmit)){
9aa022fe 419 return false;
420 } elseif ($validated === null) {
3ba2c187 421 $internal_val = $mform->validate();
89489cfe 422
423 $files = array();
424 $file_val = $this->_validate_files($files);
425 if ($file_val !== true) {
426 if (!empty($file_val)) {
427 foreach ($file_val as $element=>$msg) {
428 $mform->setElementError($element, $msg);
429 }
430 }
431 $file_val = false;
432 }
433
4287fc0d 434 $data = $mform->exportValues();
89489cfe 435 $moodle_val = $this->validation($data, $files);
a78890d5 436 if ((is_array($moodle_val) && count($moodle_val)!==0)) {
437 // non-empty array means errors
438 foreach ($moodle_val as $element=>$msg) {
439 $mform->setElementError($element, $msg);
7f40a229 440 }
a78890d5 441 $moodle_val = false;
442
443 } else {
444 // anything else means validation ok
445 $moodle_val = true;
7f40a229 446 }
89489cfe 447
49292f8c 448 $validated = ($internal_val and $moodle_val and $file_val);
7f40a229 449 }
9aa022fe 450 return $validated;
7f40a229 451 }
452
19110c57 453 /**
454 * Return true if a cancel button has been pressed resulting in the form being submitted.
455 *
456 * @return boolean true if a cancel button has been pressed
457 */
458 function is_cancelled(){
459 $mform =& $this->_form;
a23f0aaf 460 if ($mform->isSubmitted()){
461 foreach ($mform->_cancelButtons as $cancelbutton){
462 if (optional_param($cancelbutton, 0, PARAM_RAW)){
463 return true;
464 }
19110c57 465 }
466 }
467 return false;
468 }
469
05f5c40c 470 /**
da1320da 471 * Return submitted data if properly submitted or returns NULL if validation fails or
472 * if there is no submitted data.
172dd12c 473 *
294ce987 474 * note: $slashed param removed
05f5c40c 475 *
05f5c40c 476 * @return object submitted data; NULL if not valid or not submitted
477 */
294ce987 478 function get_data() {
19110c57 479 $mform =& $this->_form;
3ba2c187 480
7f40a229 481 if ($this->is_submitted() and $this->is_validated()) {
294ce987 482 $data = $mform->exportValues();
5bc97c98 483 unset($data['sesskey']); // we do not need to return sesskey
484 unset($data['_qf__'.$this->_formname]); // we do not need the submission marker too
7f40a229 485 if (empty($data)) {
486 return NULL;
487 } else {
488 return (object)$data;
489 }
490 } else {
491 return NULL;
492 }
493 }
494
4f51f48f 495 /**
496 * Return submitted data without validation or NULL if there is no submitted data.
294ce987 497 * note: $slashed param removed
4f51f48f 498 *
4f51f48f 499 * @return object submitted data; NULL if not submitted
500 */
294ce987 501 function get_submitted_data() {
4f51f48f 502 $mform =& $this->_form;
503
504 if ($this->is_submitted()) {
294ce987 505 $data = $mform->exportValues();
4f51f48f 506 unset($data['sesskey']); // we do not need to return sesskey
507 unset($data['_qf__'.$this->_formname]); // we do not need the submission marker too
508 if (empty($data)) {
509 return NULL;
510 } else {
511 return (object)$data;
512 }
513 } else {
514 return NULL;
515 }
516 }
517
05f5c40c 518 /**
519 * Save verified uploaded files into directory. Upload process can be customised from definition()
172dd12c 520 * NOTE: please use save_stored_file() or save_file()
ba21c9d4 521 *
522 * @return bool Always false
05f5c40c 523 */
49292f8c 524 function save_files($destination) {
172dd12c 525 debugging('Not used anymore, please fix code! Use save_stored_file() or save_file() instead');
49292f8c 526 return false;
527 }
2b63df96 528
feaf5d06 529 /**
172dd12c 530 * Returns name of uploaded file.
ba21c9d4 531 *
532 * @global object
172dd12c 533 * @param string $elname, first element if null
feaf5d06 534 * @return mixed false in case of failure, string if ok
535 */
172dd12c 536 function get_new_filename($elname=null) {
4287fc0d 537 global $USER;
538
172dd12c 539 if (!$this->is_submitted() or !$this->is_validated()) {
540 return false;
541 }
542
543 if (is_null($elname)) {
544 if (empty($_FILES)) {
545 return false;
546 }
547 reset($_FILES);
548 $elname = key($_FILES);
549 }
4287fc0d 550
551 if (empty($elname)) {
552 return false;
553 }
554
555 $element = $this->_form->getElement($elname);
556
0022e2d7 557 if ($element instanceof MoodleQuickForm_filepicker || $element instanceof MoodleQuickForm_filemanager) {
4287fc0d 558 $values = $this->_form->exportValues($elname);
559 if (empty($values[$elname])) {
560 return false;
561 }
562 $draftid = $values[$elname];
563 $fs = get_file_storage();
564 $context = get_context_instance(CONTEXT_USER, $USER->id);
64f93798 565 if (!$files = $fs->get_area_files($context->id, 'user', 'draft', $draftid, 'id DESC', false)) {
4287fc0d 566 return false;
567 }
568 $file = reset($files);
569 return $file->get_filename();
570 }
571
172dd12c 572 if (!isset($_FILES[$elname])) {
573 return false;
574 }
575
576 return $_FILES[$elname]['name'];
feaf5d06 577 }
578
b6b1d1ca 579 /**
172dd12c 580 * Save file to standard filesystem
ba21c9d4 581 *
582 * @global object
172dd12c 583 * @param string $elname name of element
584 * @param string $pathname full path name of file
585 * @param bool $override override file if exists
586 * @return bool success
b6b1d1ca 587 */
172dd12c 588 function save_file($elname, $pathname, $override=false) {
4287fc0d 589 global $USER;
b6b1d1ca 590
4287fc0d 591 if (!$this->is_submitted() or !$this->is_validated()) {
b6b1d1ca 592 return false;
593 }
594
172dd12c 595 if (file_exists($pathname)) {
596 if ($override) {
597 if (!@unlink($pathname)) {
598 return false;
599 }
600 } else {
601 return false;
602 }
603 }
4287fc0d 604
605 $element = $this->_form->getElement($elname);
606
0022e2d7 607 if ($element instanceof MoodleQuickForm_filepicker || $element instanceof MoodleQuickForm_filemanager) {
4287fc0d 608 $values = $this->_form->exportValues($elname);
609 if (empty($values[$elname])) {
610 return false;
611 }
612 $draftid = $values[$elname];
613 $fs = get_file_storage();
614 $context = get_context_instance(CONTEXT_USER, $USER->id);
64f93798 615 if (!$files = $fs->get_area_files($context->id, 'user', 'draft', $draftid, 'id DESC', false)) {
4287fc0d 616 return false;
617 }
618 $file = reset($files);
619
620 return $file->copy_content_to($pathname);
621
622 } else if (isset($_FILES[$elname])) {
623 return copy($_FILES[$elname]['tmp_name'], $pathname);
172dd12c 624 }
625
4287fc0d 626 return false;
172dd12c 627 }
628
0022e2d7 629 /**
d3d98a3a
DC
630 * Get draft files of a form element
631 * This is a protected method which will be used only inside moodleforms
632 *
633 * @global object $USER
634 * @param string $elname name of element
635 * @return array
636 */
637 protected function get_draft_files($elname) {
638 global $USER;
639
640 if (!$this->is_submitted()) {
641 return false;
642 }
643
644 $element = $this->_form->getElement($elname);
645
646 if ($element instanceof MoodleQuickForm_filepicker || $element instanceof MoodleQuickForm_filemanager) {
647 $values = $this->_form->exportValues($elname);
648 if (empty($values[$elname])) {
649 return false;
650 }
651 $draftid = $values[$elname];
652 $fs = get_file_storage();
653 $context = get_context_instance(CONTEXT_USER, $USER->id);
64f93798 654 if (!$files = $fs->get_area_files($context->id, 'user', 'draft', $draftid, 'id DESC', false)) {
d3d98a3a
DC
655 return null;
656 }
657 return $files;
658 }
659 return null;
660 }
661
172dd12c 662 /**
663 * Save file to local filesystem pool
ba21c9d4 664 *
665 * @global object
172dd12c 666 * @param string $elname name of element
924ddb15 667 * @param int $newcontextid
668 * @param string $newfilearea
669 * @param string $newfilepath
670 * @param string $newfilename - use specified filename, if not specified name of uploaded file used
49583e9e 671 * @param bool $overwrite - overwrite file if exists
924ddb15 672 * @param int $newuserid - new userid if required
172dd12c 673 * @return mixed stored_file object or false if error; may throw exception if duplicate found
674 */
64f93798 675 function save_stored_file($elname, $newcontextid, $newcomponent, $newfilearea, $newitemid, $newfilepath='/',
49583e9e 676 $newfilename=null, $overwrite=false, $newuserid=null) {
924ddb15 677 global $USER;
678
172dd12c 679 if (!$this->is_submitted() or !$this->is_validated()) {
89489cfe 680 return false;
172dd12c 681 }
89489cfe 682
924ddb15 683 if (empty($newuserid)) {
684 $newuserid = $USER->id;
89489cfe 685 }
b6b1d1ca 686
4287fc0d 687 $element = $this->_form->getElement($elname);
688 $fs = get_file_storage();
172dd12c 689
4287fc0d 690 if ($element instanceof MoodleQuickForm_filepicker) {
691 $values = $this->_form->exportValues($elname);
692 if (empty($values[$elname])) {
693 return false;
694 }
695 $draftid = $values[$elname];
696 $context = get_context_instance(CONTEXT_USER, $USER->id);
64f93798 697 if (!$files = $fs->get_area_files($context->id, 'user' ,'draft', $draftid, 'id DESC', false)) {
4287fc0d 698 return false;
699 }
700 $file = reset($files);
701 if (is_null($newfilename)) {
702 $newfilename = $file->get_filename();
703 }
172dd12c 704
4287fc0d 705 if ($overwrite) {
64f93798 706 if ($oldfile = $fs->get_file($newcontextid, $newcomponent, $newfilearea, $newitemid, $newfilepath, $newfilename)) {
4287fc0d 707 if (!$oldfile->delete()) {
708 return false;
709 }
924ddb15 710 }
711 }
712
64f93798 713 $file_record = array('contextid'=>$newcontextid, 'component'=>$newcomponent, 'filearea'=>$newfilearea, 'itemid'=>$newitemid,
4287fc0d 714 'filepath'=>$newfilepath, 'filename'=>$newfilename, 'userid'=>$newuserid);
715 return $fs->create_file_from_storedfile($file_record, $file);
924ddb15 716
4287fc0d 717 } else if (isset($_FILES[$elname])) {
718 $filename = is_null($newfilename) ? $_FILES[$elname]['name'] : $newfilename;
172dd12c 719
4287fc0d 720 if ($overwrite) {
64f93798 721 if ($oldfile = $fs->get_file($newcontextid, $newcomponent, $newfilearea, $newitemid, $newfilepath, $newfilename)) {
4287fc0d 722 if (!$oldfile->delete()) {
723 return false;
724 }
725 }
924ddb15 726 }
4287fc0d 727
64f93798 728 $file_record = array('contextid'=>$newcontextid, 'component'=>$newcomponent, 'filearea'=>$newfilearea, 'itemid'=>$newitemid,
4287fc0d 729 'filepath'=>$newfilepath, 'filename'=>$newfilename, 'userid'=>$newuserid);
730 return $fs->create_file_from_pathname($file_record, $_FILES[$elname]['tmp_name']);
924ddb15 731 }
732
733 return false;
172dd12c 734 }
735
736 /**
737 * Get content of uploaded file.
ba21c9d4 738 *
739 * @global object
172dd12c 740 * @param $element name of file upload element
741 * @return mixed false in case of failure, string if ok
742 */
743 function get_file_content($elname) {
4287fc0d 744 global $USER;
745
172dd12c 746 if (!$this->is_submitted() or !$this->is_validated()) {
747 return false;
748 }
749
4287fc0d 750 $element = $this->_form->getElement($elname);
751
0022e2d7 752 if ($element instanceof MoodleQuickForm_filepicker || $element instanceof MoodleQuickForm_filemanager) {
4287fc0d 753 $values = $this->_form->exportValues($elname);
754 if (empty($values[$elname])) {
755 return false;
756 }
757 $draftid = $values[$elname];
758 $fs = get_file_storage();
759 $context = get_context_instance(CONTEXT_USER, $USER->id);
64f93798 760 if (!$files = $fs->get_area_files($context->id, 'user', 'draft', $draftid, 'id DESC', false)) {
4287fc0d 761 return false;
762 }
763 $file = reset($files);
764
765 return $file->get_content();
766
767 } else if (isset($_FILES[$elname])) {
768 return file_get_contents($_FILES[$elname]['tmp_name']);
b6b1d1ca 769 }
172dd12c 770
4287fc0d 771 return false;
b6b1d1ca 772 }
773
05f5c40c 774 /**
775 * Print html form.
776 */
7f40a229 777 function display() {
4f51f48f 778 //finalize the form definition if not yet done
779 if (!$this->_definition_finalized) {
780 $this->_definition_finalized = true;
781 $this->definition_after_data();
782 }
7f40a229 783 $this->_form->display();
784 }
785
49292f8c 786 /**
05f5c40c 787 * Abstract method - always override!
49292f8c 788 *
789 * If you need special handling of uploaded files, create instance of $this->_upload_manager here.
790 */
afd63fe5 791 protected abstract function definition();
2c412890 792
c08ac016 793 /**
05f5c40c 794 * Dummy stub method - override if you need to setup the form depending on current
beac4717 795 * values. This method is called after definition(), data submission and set_data().
05f5c40c 796 * All form setup that is dependent on form values should go in here.
c08ac016 797 */
798 function definition_after_data(){
c08ac016 799 }
7f40a229 800
05f5c40c 801 /**
802 * Dummy stub method - override if you needed to perform some extra validation.
803 * If there are errors return array of errors ("fieldname"=>"error message"),
804 * otherwise true if ok.
38f394b2 805 *
89489cfe 806 * Server side rules do not work for uploaded files, implement serverside rules here if needed.
807 *
05f5c40c 808 * @param array $data array of ("fieldname"=>value) of submitted data
89489cfe 809 * @param array $files array of uploaded files "element_name"=>tmp_file_path
a78890d5 810 * @return array of "element_name"=>"error_description" if there are errors,
811 * or an empty array if everything is OK (true allowed for backwards compatibility too).
05f5c40c 812 */
89489cfe 813 function validation($data, $files) {
13ccb7bd 814 return array();
7f40a229 815 }
ebd3c7ac 816
616b549a 817 /**
818 * Method to add a repeating group of elements to a form.
819 *
820 * @param array $elementobjs Array of elements or groups of elements that are to be repeated
821 * @param integer $repeats no of times to repeat elements initially
822 * @param array $options Array of options to apply to elements. Array keys are element names.
823 * This is an array of arrays. The second sets of keys are the option types
824 * for the elements :
825 * 'default' - default value is value
826 * 'type' - PARAM_* constant is value
827 * 'helpbutton' - helpbutton params array is value
828 * 'disabledif' - last three moodleform::disabledIf()
829 * params are value as an array
830 * @param string $repeathiddenname name for hidden element storing no of repeats in this form
831 * @param string $addfieldsname name for button to add more fields
832 * @param int $addfieldsno how many fields to add at a time
271ffe3f 833 * @param string $addstring name of button, {no} is replaced by no of blanks that will be added.
6f3b54c8 834 * @param boolean $addbuttoninside if true, don't call closeHeaderBefore($addfieldsname). Default false.
a23f0aaf 835 * @return int no of repeats of element in this page
616b549a 836 */
6f3b54c8 837 function repeat_elements($elementobjs, $repeats, $options, $repeathiddenname,
838 $addfieldsname, $addfieldsno=5, $addstring=null, $addbuttoninside=false){
271ffe3f 839 if ($addstring===null){
840 $addstring = get_string('addfields', 'form', $addfieldsno);
841 } else {
842 $addstring = str_ireplace('{no}', $addfieldsno, $addstring);
843 }
ebd3c7ac 844 $repeats = optional_param($repeathiddenname, $repeats, PARAM_INT);
845 $addfields = optional_param($addfieldsname, '', PARAM_TEXT);
846 if (!empty($addfields)){
847 $repeats += $addfieldsno;
848 }
ebd3c7ac 849 $mform =& $this->_form;
a23f0aaf 850 $mform->registerNoSubmitButton($addfieldsname);
ebd3c7ac 851 $mform->addElement('hidden', $repeathiddenname, $repeats);
d18e0fe6 852 $mform->setType($repeathiddenname, PARAM_INT);
ebd3c7ac 853 //value not to be overridden by submitted value
854 $mform->setConstants(array($repeathiddenname=>$repeats));
414f7bee 855 $namecloned = array();
856 for ($i = 0; $i < $repeats; $i++) {
ebd3c7ac 857 foreach ($elementobjs as $elementobj){
985f0ddd 858 $elementclone = fullclone($elementobj);
7b41a4a9 859 $name = $elementclone->getName();
414f7bee 860 $namecloned[] = $name;
861 if (!empty($name)) {
86aab05c 862 $elementclone->setName($name."[$i]");
863 }
414f7bee 864 if (is_a($elementclone, 'HTML_QuickForm_header')) {
865 $value = $elementclone->_text;
271ffe3f 866 $elementclone->setValue(str_replace('{no}', ($i+1), $value));
867
868 } else {
869 $value=$elementclone->getLabel();
870 $elementclone->setLabel(str_replace('{no}', ($i+1), $value));
ebd3c7ac 871
872 }
7b41a4a9 873
ebd3c7ac 874 $mform->addElement($elementclone);
875 }
876 }
877 for ($i=0; $i<$repeats; $i++) {
878 foreach ($options as $elementname => $elementoptions){
879 $pos=strpos($elementname, '[');
880 if ($pos!==FALSE){
881 $realelementname = substr($elementname, 0, $pos+1)."[$i]";
882 $realelementname .= substr($elementname, $pos+1);
883 }else {
884 $realelementname = $elementname."[$i]";
885 }
886 foreach ($elementoptions as $option => $params){
887
888 switch ($option){
889 case 'default' :
890 $mform->setDefault($realelementname, $params);
891 break;
ebd3c7ac 892 case 'helpbutton' :
893 $mform->setHelpButton($realelementname, $params);
894 break;
895 case 'disabledif' :
414f7bee 896 foreach ($namecloned as $num => $name){
897 if ($params[0] == $name){
898 $params[0] = $params[0]."[$i]";
899 break;
900 }
901 }
9aa022fe 902 $params = array_merge(array($realelementname), $params);
903 call_user_func_array(array(&$mform, 'disabledIf'), $params);
904 break;
905 case 'rule' :
906 if (is_string($params)){
907 $params = array(null, $params, null, 'client');
908 }
909 $params = array_merge(array($realelementname), $params);
910 call_user_func_array(array(&$mform, 'addRule'), $params);
ebd3c7ac 911 break;
912
913 }
914 }
915 }
916 }
271ffe3f 917 $mform->addElement('submit', $addfieldsname, $addstring);
a23f0aaf 918
6f3b54c8 919 if (!$addbuttoninside) {
920 $mform->closeHeaderBefore($addfieldsname);
921 }
ebd3c7ac 922
19194f82 923 return $repeats;
ebd3c7ac 924 }
6073a598 925
926 /**
927 * Adds a link/button that controls the checked state of a group of checkboxes.
ba21c9d4 928 *
929 * @global object
6073a598 930 * @param int $groupid The id of the group of advcheckboxes this element controls
ba21c9d4 931 * @param string $buttontext The text of the link. Defaults to "select all/none"
6073a598 932 * @param array $attributes associative array of HTML attributes
933 * @param int $originalValue The original general state of the checkboxes before the user first clicks this element
934 */
172dd12c 935 function add_checkbox_controller($groupid, $buttontext, $attributes, $originalValue = 0) {
6073a598 936 global $CFG;
937 if (empty($text)) {
938 $text = get_string('selectallornone', 'form');
939 }
940
941 $mform = $this->_form;
942 $select_value = optional_param('checkbox_controller'. $groupid, null, PARAM_INT);
943
944 if ($select_value == 0 || is_null($select_value)) {
945 $new_select_value = 1;
946 } else {
947 $new_select_value = 0;
948 }
949
950 $mform->addElement('hidden', "checkbox_controller$groupid");
d18e0fe6 951 $mform->setType("checkbox_controller$groupid", PARAM_INT);
6073a598 952 $mform->setConstants(array("checkbox_controller$groupid" => $new_select_value));
172dd12c 953
6073a598 954 // Locate all checkboxes for this group and set their value, IF the optional param was given
955 if (!is_null($select_value)) {
956 foreach ($this->_form->_elements as $element) {
957 if ($element->getAttribute('class') == "checkboxgroup$groupid") {
958 $mform->setConstants(array($element->getAttribute('name') => $select_value));
959 }
960 }
961 }
962
963 $checkbox_controller_name = 'nosubmit_checkbox_controller' . $groupid;
964 $mform->registerNoSubmitButton($checkbox_controller_name);
172dd12c 965
6073a598 966 // Prepare Javascript for submit element
967 $js = "\n//<![CDATA[\n";
968 if (!defined('HTML_QUICKFORM_CHECKBOXCONTROLLER_EXISTS')) {
969 $js .= <<<EOS
970function html_quickform_toggle_checkboxes(group) {
971 var checkboxes = getElementsByClassName(document, 'input', 'checkboxgroup' + group);
972 var newvalue = false;
973 var global = eval('html_quickform_checkboxgroup' + group + ';');
974 if (global == 1) {
172dd12c 975 eval('html_quickform_checkboxgroup' + group + ' = 0;');
6073a598 976 newvalue = '';
977 } else {
172dd12c 978 eval('html_quickform_checkboxgroup' + group + ' = 1;');
6073a598 979 newvalue = 'checked';
980 }
981
982 for (i = 0; i < checkboxes.length; i++) {
172dd12c 983 checkboxes[i].checked = newvalue;
6073a598 984 }
985}
986EOS;
987 define('HTML_QUICKFORM_CHECKBOXCONTROLLER_EXISTS', true);
988 }
989 $js .= "\nvar html_quickform_checkboxgroup$groupid=$originalValue;\n";
172dd12c 990
6073a598 991 $js .= "//]]>\n";
172dd12c 992
6073a598 993 require_once("$CFG->libdir/form/submitlink.php");
994 $submitlink = new MoodleQuickForm_submitlink($checkbox_controller_name, $attributes);
995 $submitlink->_js = $js;
996 $submitlink->_onclick = "html_quickform_toggle_checkboxes($groupid); return false;";
172dd12c 997 $mform->addElement($submitlink);
6073a598 998 $mform->setDefault($checkbox_controller_name, $text);
999 }
1000
a23f0aaf 1001 /**
1d284fbd 1002 * Use this method to a cancel and submit button to the end of your form. Pass a param of false
a23f0aaf 1003 * if you don't want a cancel button in your form. If you have a cancel button make sure you
1004 * check for it being pressed using is_cancelled() and redirecting if it is true before trying to
beac4717 1005 * get data with get_data().
a23f0aaf 1006 *
1007 * @param boolean $cancel whether to show cancel button, default true
a23f0aaf 1008 * @param string $submitlabel label for submit button, defaults to get_string('savechanges')
1009 */
1d284fbd 1010 function add_action_buttons($cancel = true, $submitlabel=null){
a23f0aaf 1011 if (is_null($submitlabel)){
1012 $submitlabel = get_string('savechanges');
1013 }
1014 $mform =& $this->_form;
1d284fbd 1015 if ($cancel){
1016 //when two elements we need a group
a23f0aaf 1017 $buttonarray=array();
1018 $buttonarray[] = &$mform->createElement('submit', 'submitbutton', $submitlabel);
1d284fbd 1019 $buttonarray[] = &$mform->createElement('cancel');
a23f0aaf 1020 $mform->addGroup($buttonarray, 'buttonar', '', array(' '), false);
4f51f48f 1021 $mform->closeHeaderBefore('buttonar');
a23f0aaf 1022 } else {
1023 //no group needed
1024 $mform->addElement('submit', 'submitbutton', $submitlabel);
4f51f48f 1025 $mform->closeHeaderBefore('submitbutton');
a23f0aaf 1026 }
1027 }
572dd8ec
SH
1028
1029 /**
2836e77d 1030 * Adds an initialisation call for a standard JavaScript enhancement.
572dd8ec
SH
1031 *
1032 * This function is designed to add an initialisation call for a JavaScript
2836e77d 1033 * enhancement that should exist within javascript-static M.form.init_{enhancementname}.
572dd8ec
SH
1034 *
1035 * Current options:
1036 * - Selectboxes
1037 * - smartselect: Turns a nbsp indented select box into a custom drop down
1038 * control that supports multilevel and category selection.
1039 * $enhancement = 'smartselect';
1040 * $options = array('selectablecategories' => true|false)
1041 *
1042 * @since 2.0
1043 * @param string|element $element
1044 * @param string $enhancement
1045 * @param array $options
1046 * @param array $strings
1047 */
1048 function init_javascript_enhancement($element, $enhancement, array $options=array(), array $strings=null) {
1049 global $PAGE;
1050 if (is_string($element)) {
1051 $element = $this->_form->getElement($element);
1052}
1053 if (is_object($element)) {
1054 $element->_generateId();
1055 $elementid = $element->getAttribute('id');
1056 $PAGE->requires->js_init_call('M.form.init_'.$enhancement, array($elementid, $options));
1057 if (is_array($strings)) {
1058 foreach ($strings as $string) {
1059 if (is_array($string)) {
1060 call_user_method_array('string_for_js', $PAGE->requires, $string);
1061 } else {
1062 $PAGE->requires->string_for_js($string, 'moodle');
1063 }
1064 }
1065 }
1066 }
1067 }
7f40a229 1068}
1069
da1320da 1070/**
1071 * You never extend this class directly. The class methods of this class are available from
6073a598 1072 * the private $this->_form property on moodleform and its children. You generally only
da1320da 1073 * call methods on this class from within abstract methods that you override on moodleform such
1074 * as definition and definition_after_data
1075 *
ba21c9d4 1076 * @package moodlecore
1077 * @copyright Jamie Pratt <me@jamiep.org>
1078 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
da1320da 1079 */
7f40a229 1080class MoodleQuickForm extends HTML_QuickForm_DHTMLRulesTableless {
ba21c9d4 1081 /** @var array */
7f40a229 1082 var $_types = array();
50ef8eb9 1083 var $_dependencies = array();
19110c57 1084 /**
1085 * Array of buttons that if pressed do not result in the processing of the form.
1086 *
1087 * @var array
1088 */
1089 var $_noSubmitButtons=array();
1090 /**
1091 * Array of buttons that if pressed do not result in the processing of the form.
1092 *
1093 * @var array
1094 */
1095 var $_cancelButtons=array();
7f40a229 1096
19194f82 1097 /**
1098 * Array whose keys are element names. If the key exists this is a advanced element
1099 *
1100 * @var array
1101 */
1102 var $_advancedElements = array();
1103
1104 /**
1105 * Whether to display advanced elements (on page load)
1106 *
1107 * @var boolean
1108 */
1109 var $_showAdvanced = null;
1110
f07b9627 1111 /**
2836e77d 1112 * The form name is derived from the class name of the wrapper minus the trailing form
f07b9627 1113 * It is a name with words joined by underscores whereas the id attribute is words joined by
1114 * underscores.
1115 *
1116 * @var unknown_type
1117 */
1118 var $_formName = '';
43914931 1119
4f51f48f 1120 /**
1121 * String with the html for hidden params passed in as part of a moodle_url object for the action. Output in the form.
1122 *
1123 * @var string
1124 */
1125 var $_pageparams = '';
1126
da6f8763 1127 /**
1128 * Class constructor - same parameters as HTML_QuickForm_DHTMLRulesTableless
ba21c9d4 1129 *
1130 * @global object
1131 * @staticvar int $formcounter
da6f8763 1132 * @param string $formName Form's name.
1133 * @param string $method (optional)Form's method defaults to 'POST'
4f51f48f 1134 * @param mixed $action (optional)Form's action - string or moodle_url
da6f8763 1135 * @param string $target (optional)Form's target defaults to none
1136 * @param mixed $attributes (optional)Extra attributes for <form> tag
da6f8763 1137 * @access public
1138 */
7f40a229 1139 function MoodleQuickForm($formName, $method, $action, $target='', $attributes=null){
96705c33 1140 global $CFG, $OUTPUT;
dcf6d93c 1141
271ffe3f 1142 static $formcounter = 1;
7f40a229 1143
da6f8763 1144 HTML_Common::HTML_Common($attributes);
da6f8763 1145 $target = empty($target) ? array() : array('target' => $target);
f07b9627 1146 $this->_formName = $formName;
4f51f48f 1147 if (is_a($action, 'moodle_url')){
6ea66ff3 1148 $this->_pageparams = html_writer::input_hidden_params($action);
eb788065 1149 $action = $action->out_omit_querystring();
4f51f48f 1150 } else {
1151 $this->_pageparams = '';
1152 }
da6f8763 1153 //no 'name' atttribute for form in xhtml strict :
1327f08e 1154 $attributes = array('action'=>$action, 'method'=>$method,
1155 'accept-charset'=>'utf-8', 'id'=>'mform'.$formcounter) + $target;
271ffe3f 1156 $formcounter++;
da6f8763 1157 $this->updateAttributes($attributes);
da6f8763 1158
7f40a229 1159 //this is custom stuff for Moodle :
da6f8763 1160 $oldclass= $this->getAttribute('class');
1161 if (!empty($oldclass)){
1162 $this->updateAttributes(array('class'=>$oldclass.' mform'));
1163 }else {
80f962df 1164 $this->updateAttributes(array('class'=>'mform'));
da6f8763 1165 }
b5d0cafc
PS
1166 $this->_reqHTML = '<img class="req" title="'.get_string('requiredelement', 'form').'" alt="'.get_string('requiredelement', 'form').'" src="'.$OUTPUT->pix_url('req') .'" />';
1167 $this->_advancedHTML = '<img class="adv" title="'.get_string('advancedelement', 'form').'" alt="'.get_string('advancedelement', 'form').'" src="'.$OUTPUT->pix_url('adv') .'" />';
1168 $this->setRequiredNote(get_string('somefieldsrequired', 'form', '<img alt="'.get_string('requiredelement', 'form').'" src="'.$OUTPUT->pix_url('req') .'" />'));
19194f82 1169 }
1170
a23f0aaf 1171 /**
1172 * Use this method to indicate an element in a form is an advanced field. If items in a form
1173 * are marked as advanced then 'Hide/Show Advanced' buttons will automatically be displayed in the
1174 * form so the user can decide whether to display advanced form controls.
1175 *
1176 * If you set a header element to advanced then all elements it contains will also be set as advanced.
1177 *
1178 * @param string $elementName group or element name (not the element name of something inside a group).
1179 * @param boolean $advanced default true sets the element to advanced. False removes advanced mark.
1180 */
1181 function setAdvanced($elementName, $advanced=true){
1182 if ($advanced){
1183 $this->_advancedElements[$elementName]='';
1184 } elseif (isset($this->_advancedElements[$elementName])) {
1185 unset($this->_advancedElements[$elementName]);
1186 }
1187 if ($advanced && $this->getElementType('mform_showadvanced_last')===false){
1188 $this->setShowAdvanced();
1189 $this->registerNoSubmitButton('mform_showadvanced');
1190
1191 $this->addElement('hidden', 'mform_showadvanced_last');
d18e0fe6 1192 $this->setType('mform_showadvanced_last', PARAM_INT);
a23f0aaf 1193 }
1194 }
1195 /**
1196 * Set whether to show advanced elements in the form on first displaying form. Default is not to
1197 * display advanced elements in the form until 'Show Advanced' is pressed.
1198 *
1199 * You can get the last state of the form and possibly save it for this user by using
1200 * value 'mform_showadvanced_last' in submitted data.
1201 *
1202 * @param boolean $showadvancedNow
1203 */
1204 function setShowAdvanced($showadvancedNow = null){
1205 if ($showadvancedNow === null){
1206 if ($this->_showAdvanced !== null){
1207 return;
1208 } else { //if setShowAdvanced is called without any preference
1209 //make the default to not show advanced elements.
f07b9627 1210 $showadvancedNow = get_user_preferences(
1211 moodle_strtolower($this->_formName.'_showadvanced', 0));
a23f0aaf 1212 }
a23f0aaf 1213 }
1214 //value of hidden element
1215 $hiddenLast = optional_param('mform_showadvanced_last', -1, PARAM_INT);
1216 //value of button
1217 $buttonPressed = optional_param('mform_showadvanced', 0, PARAM_RAW);
1218 //toggle if button pressed or else stay the same
1219 if ($hiddenLast == -1) {
1220 $next = $showadvancedNow;
1221 } elseif ($buttonPressed) { //toggle on button press
1222 $next = !$hiddenLast;
1223 } else {
1224 $next = $hiddenLast;
1225 }
1226 $this->_showAdvanced = $next;
f07b9627 1227 if ($showadvancedNow != $next){
1228 set_user_preference($this->_formName.'_showadvanced', $next);
1229 }
a23f0aaf 1230 $this->setConstants(array('mform_showadvanced_last'=>$next));
19194f82 1231 }
1232 function getShowAdvanced(){
1233 return $this->_showAdvanced;
1234 }
1235
19194f82 1236
1237 /**
1238 * Accepts a renderer
1239 *
ba21c9d4 1240 * @param object $renderer HTML_QuickForm_Renderer An HTML_QuickForm_Renderer object
19194f82 1241 * @access public
1242 * @return void
1243 */
46f3921e 1244 function accept(&$renderer) {
19194f82 1245 if (method_exists($renderer, 'setAdvancedElements')){
1246 //check for visible fieldsets where all elements are advanced
1247 //and mark these headers as advanced as well.
1248 //And mark all elements in a advanced header as advanced
1249 $stopFields = $renderer->getStopFieldSetElements();
1250 $lastHeader = null;
1251 $lastHeaderAdvanced = false;
1252 $anyAdvanced = false;
1253 foreach (array_keys($this->_elements) as $elementIndex){
1254 $element =& $this->_elements[$elementIndex];
46f3921e 1255
1256 // if closing header and any contained element was advanced then mark it as advanced
19194f82 1257 if ($element->getType()=='header' || in_array($element->getName(), $stopFields)){
46f3921e 1258 if ($anyAdvanced && !is_null($lastHeader)){
19194f82 1259 $this->setAdvanced($lastHeader->getName());
1260 }
1261 $lastHeaderAdvanced = false;
46f3921e 1262 unset($lastHeader);
1263 $lastHeader = null;
19194f82 1264 } elseif ($lastHeaderAdvanced) {
1265 $this->setAdvanced($element->getName());
1266 }
46f3921e 1267
19194f82 1268 if ($element->getType()=='header'){
1269 $lastHeader =& $element;
1270 $anyAdvanced = false;
1271 $lastHeaderAdvanced = isset($this->_advancedElements[$element->getName()]);
1272 } elseif (isset($this->_advancedElements[$element->getName()])){
1273 $anyAdvanced = true;
1274 }
1275 }
46f3921e 1276 // the last header may not be closed yet...
1277 if ($anyAdvanced && !is_null($lastHeader)){
1278 $this->setAdvanced($lastHeader->getName());
1279 }
19194f82 1280 $renderer->setAdvancedElements($this->_advancedElements);
19194f82 1281
19194f82 1282 }
1283 parent::accept($renderer);
1284 }
1285
ba21c9d4 1286 /**
1287 * @param string $elementName
1288 */
19194f82 1289 function closeHeaderBefore($elementName){
1290 $renderer =& $this->defaultRenderer();
1291 $renderer->addStopFieldsetElements($elementName);
da6f8763 1292 }
bb40325e 1293
da1320da 1294 /**
1295 * Should be used for all elements of a form except for select, radio and checkboxes which
1296 * clean their own data.
1297 *
1298 * @param string $elementname
1299 * @param integer $paramtype use the constants PARAM_*.
1300 * * PARAM_CLEAN is deprecated and you should try to use a more specific type.
1301 * * PARAM_TEXT should be used for cleaning data that is expected to be plain text.
1302 * It will strip all html tags. But will still let tags for multilang support
1303 * through.
1304 * * PARAM_RAW means no cleaning whatsoever, it is used mostly for data from the
1305 * html editor. Data from the editor is later cleaned before display using
1306 * format_text() function. PARAM_RAW can also be used for data that is validated
1307 * by some other way or printed by p() or s().
1308 * * PARAM_INT should be used for integers.
1309 * * PARAM_ACTION is an alias of PARAM_ALPHA and is used for hidden fields specifying
1310 * form actions.
1311 */
7f40a229 1312 function setType($elementname, $paramtype) {
1313 $this->_types[$elementname] = $paramtype;
1314 }
49292f8c 1315
da1320da 1316 /**
1317 * See description of setType above. This can be used to set several types at once.
1318 *
1319 * @param array $paramtypes
1320 */
c56f1826 1321 function setTypes($paramtypes) {
1322 $this->_types = $paramtypes + $this->_types;
1323 }
49292f8c 1324
ba21c9d4 1325 /**
1326 * @param array $submission
1327 * @param array $files
1328 */
49292f8c 1329 function updateSubmission($submission, $files) {
1330 $this->_flagSubmitted = false;
1331
7f40a229 1332 if (empty($submission)) {
1333 $this->_submitValues = array();
7f40a229 1334 } else {
1335 foreach ($submission as $key=>$s) {
1336 if (array_key_exists($key, $this->_types)) {
1337 $submission[$key] = clean_param($s, $this->_types[$key]);
1338 }
1339 }
294ce987 1340 $this->_submitValues = $submission;
7f40a229 1341 $this->_flagSubmitted = true;
1342 }
1343
49292f8c 1344 if (empty($files)) {
1345 $this->_submitFiles = array();
1346 } else {
49292f8c 1347 $this->_submitFiles = $files;
1348 $this->_flagSubmitted = true;
1349 }
1350
2c412890 1351 // need to tell all elements that they need to update their value attribute.
1352 foreach (array_keys($this->_elements) as $key) {
1353 $this->_elements[$key]->onQuickFormEvent('updateValue', null, $this);
1354 }
7f40a229 1355 }
1356
ba21c9d4 1357 /**
1358 * @return string
1359 */
da6f8763 1360 function getReqHTML(){
1361 return $this->_reqHTML;
1362 }
117bd748 1363
ba21c9d4 1364 /**
1365 * @return string
1366 */
19194f82 1367 function getAdvancedHTML(){
1368 return $this->_advancedHTML;
1369 }
1370
7f40a229 1371 /**
da1320da 1372 * Initializes a default form value. Used to specify the default for a new entry where
beac4717 1373 * no data is loaded in using moodleform::set_data()
7f40a229 1374 *
294ce987 1375 * note: $slashed param removed
1376 *
7f40a229 1377 * @param string $elementname element name
1378 * @param mixed $values values for that element name
7f40a229 1379 * @access public
1380 * @return void
1381 */
294ce987 1382 function setDefault($elementName, $defaultValue){
1383 $this->setDefaults(array($elementName=>$defaultValue));
7f40a229 1384 } // end func setDefault
da6f8763 1385 /**
c56f1826 1386 * Add an array of buttons to the form
7f40a229 1387 * @param array $buttons An associative array representing help button to attach to
da6f8763 1388 * to the form. keys of array correspond to names of elements in form.
ba21c9d4 1389 * @param bool $suppresscheck
1390 * @param string $function
da6f8763 1391 * @access public
1392 */
d4fe14d3 1393 function setHelpButtons($buttons, $suppresscheck=false, $function='helpbutton'){
7f40a229 1394
c56f1826 1395 foreach ($buttons as $elementname => $button){
d4fe14d3 1396 $this->setHelpButton($elementname, $button, $suppresscheck, $function);
da6f8763 1397 }
1398 }
c56f1826 1399 /**
da1320da 1400 * Add a single button.
c56f1826 1401 *
ff95caa8 1402 * @deprecated use addHelpButton() instead
c56f1826 1403 * @param string $elementname name of the element to add the item to
ba21c9d4 1404 * @param array $button arguments to pass to function $function
1405 * @param boolean $suppresscheck whether to throw an error if the element
c56f1826 1406 * doesn't exist.
d4fe14d3 1407 * @param string $function - function to generate html from the arguments in $button
ba21c9d4 1408 * @param string $function
c56f1826 1409 */
4bcc5118 1410 function setHelpButton($elementname, $buttonargs, $suppresscheck=false, $function='helpbutton'){
642816a6 1411 global $OUTPUT;
b65d2720 1412 //TODO: obsolete this function completely
4bcc5118 1413 if ($function !== 'helpbutton') {
b65d2720 1414 //debugging('parameter $function in moodle_form::setHelpButton() is not supported any more');
4bcc5118
PS
1415 }
1416
1417 $buttonargs = (array)$buttonargs;
1418
1419 if (array_key_exists($elementname, $this->_elementIndex)) {
c56f1826 1420 //_elements has a numeric index, this code accesses the elements by name
4bcc5118 1421 $element = $this->_elements[$this->_elementIndex[$elementname]];
642816a6 1422
4bcc5118
PS
1423 $page = isset($buttonargs[0]) ? $buttonargs[0] : null;
1424 $text = isset($buttonargs[1]) ? $buttonargs[1] : null;
1425 $module = isset($buttonargs[2]) ? $buttonargs[2] : 'moodle';
1426 $linktext = isset($buttonargs[3]) ? $buttonargs[3] : false;
1427
596509e4 1428 $element->_helpbutton = $OUTPUT->old_help_icon($page, $text, $module, $linktext);
642816a6 1429
4bcc5118 1430 } else if (!$suppresscheck) {
c56f1826 1431 print_error('nonexistentformelements', 'form', '', $elementname);
2c412890 1432 }
c56f1826 1433 }
7f40a229 1434
53a78cef 1435 /**
259c165d
PS
1436 * Add a help button to element, only one button per element is allowed.
1437 *
ff95caa8
DM
1438 * This is new, simplified and preferable method of setting a help icon on form elements.
1439 * It uses the new $OUTPUT->help_icon().
1440 *
1441 * Typically, you will provide the same identifier and the component as you have used for the
1442 * label of the element. The string identifier with the _help suffix added is then used
1443 * as the help string.
1444 *
259c165d
PS
1445 * There has to be two strings defined:
1446 * 1/ get_string($identifier, $component) - the title of the help page
5435c9dc 1447 * 2/ get_string($identifier.'_help', $component) - the actual help page text
53a78cef 1448 *
ff95caa8 1449 * @since 2.0
53a78cef 1450 * @param string $elementname name of the element to add the item to
2836e77d 1451 * @param string $identifier help string identifier without _help suffix
ff95caa8
DM
1452 * @param string $component component name to look the help string in
1453 * @param string $linktext optional text to display next to the icon
1454 * @param boolean $suppresscheck set to true if the element may not exist
53a78cef
PS
1455 * @return void
1456 */
259c165d
PS
1457 function addHelpButton($elementname, $identifier, $component = 'moodle', $linktext = '', $suppresscheck = false) {
1458 global $OUTPUT;
53a78cef 1459 if (array_key_exists($elementname, $this->_elementIndex)) {
af140288
DC
1460 $element = $this->_elements[$this->_elementIndex[$elementname]];
1461 $element->_helpbutton = $OUTPUT->help_icon($identifier, $component, $linktext);
53a78cef
PS
1462 } else if (!$suppresscheck) {
1463 debugging(get_string('nonexistentformelements', 'form', $elementname));
1464 }
1465 }
1466
cc444336 1467 /**
2836e77d 1468 * Set constant value not overridden by _POST or _GET
cc444336 1469 * note: this does not work for complex names with [] :-(
ba21c9d4 1470 *
cc444336 1471 * @param string $elname name of element
1472 * @param mixed $value
1473 * @return void
1474 */
1475 function setConstant($elname, $value) {
1476 $this->_constantValues = HTML_QuickForm::arrayMerge($this->_constantValues, array($elname=>$value));
1477 $element =& $this->getElement($elname);
1478 $element->onQuickFormEvent('updateValue', null, $this);
1479 }
1480
ba21c9d4 1481 /**
1482 * @param string $elementList
1483 */
294ce987 1484 function exportValues($elementList = null){
0ffb4cc7 1485 $unfiltered = array();
1486 if (null === $elementList) {
1487 // iterate over all elements, calling their exportValue() methods
98af2d1d 1488 $emptyarray = array();
0ffb4cc7 1489 foreach (array_keys($this->_elements) as $key) {
1490 if ($this->_elements[$key]->isFrozen() && !$this->_elements[$key]->_persistantFreeze){
98af2d1d 1491 $value = $this->_elements[$key]->exportValue($emptyarray, true);
0ffb4cc7 1492 } else {
1493 $value = $this->_elements[$key]->exportValue($this->_submitValues, true);
1494 }
1495
1496 if (is_array($value)) {
1497 // This shit throws a bogus warning in PHP 4.3.x
1498 $unfiltered = HTML_QuickForm::arrayMerge($unfiltered, $value);
1499 }
1500 }
1501 } else {
1502 if (!is_array($elementList)) {
1503 $elementList = array_map('trim', explode(',', $elementList));
1504 }
1505 foreach ($elementList as $elementName) {
1506 $value = $this->exportValue($elementName);
1507 if (PEAR::isError($value)) {
1508 return $value;
1509 }
4287fc0d 1510 //oh, stock QuickFOrm was returning array of arrays!
1511 $unfiltered = HTML_QuickForm::arrayMerge($unfiltered, $value);
0ffb4cc7 1512 }
1513 }
7f40a229 1514
294ce987 1515 return $unfiltered;
da6f8763 1516 }
f07b9627 1517 /**
1518 * Adds a validation rule for the given field
1519 *
1520 * If the element is in fact a group, it will be considered as a whole.
1521 * To validate grouped elements as separated entities,
1522 * use addGroupRule instead of addRule.
1523 *
1524 * @param string $element Form element name
1525 * @param string $message Message to display for invalid data
1526 * @param string $type Rule type, use getRegisteredRules() to get types
1527 * @param string $format (optional)Required for extra rule data
1528 * @param string $validation (optional)Where to perform validation: "server", "client"
1529 * @param boolean $reset Client-side validation: reset the form element to its original value if there is an error?
1530 * @param boolean $force Force the rule to be applied, even if the target form element does not exist
f07b9627 1531 * @access public
f07b9627 1532 */
1533 function addRule($element, $message, $type, $format=null, $validation='server', $reset = false, $force = false)
1534 {
1535 parent::addRule($element, $message, $type, $format, $validation, $reset, $force);
1536 if ($validation == 'client') {
1537 $this->updateAttributes(array('onsubmit' => 'try { var myValidator = validate_' . $this->_formName . '; } catch(e) { return true; } return myValidator(this);'));
1538 }
1539
1540 } // end func addRule
1541 /**
1542 * Adds a validation rule for the given group of elements
1543 *
1544 * Only groups with a name can be assigned a validation rule
1545 * Use addGroupRule when you need to validate elements inside the group.
1546 * Use addRule if you need to validate the group as a whole. In this case,
1547 * the same rule will be applied to all elements in the group.
1548 * Use addRule if you need to validate the group against a function.
1549 *
1550 * @param string $group Form group name
1551 * @param mixed $arg1 Array for multiple elements or error message string for one element
1552 * @param string $type (optional)Rule type use getRegisteredRules() to get types
1553 * @param string $format (optional)Required for extra rule data
1554 * @param int $howmany (optional)How many valid elements should be in the group
1555 * @param string $validation (optional)Where to perform validation: "server", "client"
1556 * @param bool $reset Client-side: whether to reset the element's value to its original state if validation failed.
f07b9627 1557 * @access public
f07b9627 1558 */
1559 function addGroupRule($group, $arg1, $type='', $format=null, $howmany=0, $validation = 'server', $reset = false)
1560 {
1561 parent::addGroupRule($group, $arg1, $type, $format, $howmany, $validation, $reset);
1562 if (is_array($arg1)) {
3a298174 1563 foreach ($arg1 as $rules) {
f07b9627 1564 foreach ($rules as $rule) {
1565 $validation = (isset($rule[3]) && 'client' == $rule[3])? 'client': 'server';
1566
1567 if ('client' == $validation) {
1568 $this->updateAttributes(array('onsubmit' => 'try { var myValidator = validate_' . $this->_formName . '; } catch(e) { return true; } return myValidator(this);'));
1569 }
1570 }
1571 }
1572 } elseif (is_string($arg1)) {
1573
1574 if ($validation == 'client') {
3a298174 1575 $this->updateAttributes(array('onsubmit' => 'try { var myValidator = validate_' . $this->_formName . '; } catch(e) { return true; } return myValidator(this);'));
f07b9627 1576 }
1577 }
1578 } // end func addGroupRule
1579
1580 // }}}
5bc97c98 1581 /**
1582 * Returns the client side validation script
1583 *
1584 * The code here was copied from HTML_QuickForm_DHTMLRulesTableless who copied it from HTML_QuickForm
1585 * and slightly modified to run rules per-element
1586 * Needed to override this because of an error with client side validation of grouped elements.
1587 *
1588 * @access public
1589 * @return string Javascript to perform validation, empty string if no 'client' rules were added
1590 */
1591 function getValidationScript()
1592 {
1593 if (empty($this->_rules) || empty($this->_attributes['onsubmit'])) {
1594 return '';
1595 }
1596
1597 include_once('HTML/QuickForm/RuleRegistry.php');
1598 $registry =& HTML_QuickForm_RuleRegistry::singleton();
1599 $test = array();
1600 $js_escape = array(
1601 "\r" => '\r',
1602 "\n" => '\n',
1603 "\t" => '\t',
1604 "'" => "\\'",
1605 '"' => '\"',
1606 '\\' => '\\\\'
1607 );
1608
1609 foreach ($this->_rules as $elementName => $rules) {
1610 foreach ($rules as $rule) {
1611 if ('client' == $rule['validation']) {
da1320da 1612 unset($element); //TODO: find out how to properly initialize it
5bc97c98 1613
1614 $dependent = isset($rule['dependent']) && is_array($rule['dependent']);
1615 $rule['message'] = strtr($rule['message'], $js_escape);
1616
1617 if (isset($rule['group'])) {
1618 $group =& $this->getElement($rule['group']);
1619 // No JavaScript validation for frozen elements
1620 if ($group->isFrozen()) {
1621 continue 2;
1622 }
1623 $elements =& $group->getElements();
1624 foreach (array_keys($elements) as $key) {
1625 if ($elementName == $group->getElementName($key)) {
1626 $element =& $elements[$key];
1627 break;
1628 }
1629 }
1630 } elseif ($dependent) {
1631 $element = array();
1632 $element[] =& $this->getElement($elementName);
3a298174 1633 foreach ($rule['dependent'] as $elName) {
5bc97c98 1634 $element[] =& $this->getElement($elName);
1635 }
1636 } else {
1637 $element =& $this->getElement($elementName);
1638 }
1639 // No JavaScript validation for frozen elements
1640 if (is_object($element) && $element->isFrozen()) {
1641 continue 2;
1642 } elseif (is_array($element)) {
1643 foreach (array_keys($element) as $key) {
1644 if ($element[$key]->isFrozen()) {
1645 continue 3;
1646 }
1647 }
1648 }
1649 // Fix for bug displaying errors for elements in a group
1650 //$test[$elementName][] = $registry->getValidationScript($element, $elementName, $rule);
1651 $test[$elementName][0][] = $registry->getValidationScript($element, $elementName, $rule);
1652 $test[$elementName][1]=$element;
1653 //end of fix
1654 }
1655 }
1656 }
7c77033f 1657
1658 // Fix for MDL-9524. If you don't do this, then $element may be left as a reference to one of the fields in
1659 // the form, and then that form field gets corrupted by the code that follows.
1660 unset($element);
1661
5bc97c98 1662 $js = '
1663<script type="text/javascript">
1664//<![CDATA[
1cbb09f1 1665
1666var skipClientValidation = false;
1667
5bc97c98 1668function qf_errorHandler(element, _qfMsg) {
1669 div = element.parentNode;
1670 if (_qfMsg != \'\') {
e35c9eeb 1671 var errorSpan = document.getElementById(\'id_error_\'+element.name);
e7004d05 1672 if (!errorSpan) {
1673 errorSpan = document.createElement("span");
e35c9eeb 1674 errorSpan.id = \'id_error_\'+element.name;
1675 errorSpan.className = "error";
fed13a5e 1676 element.parentNode.insertBefore(errorSpan, element.parentNode.firstChild);
5bc97c98 1677 }
fed13a5e 1678
e7004d05 1679 while (errorSpan.firstChild) {
1680 errorSpan.removeChild(errorSpan.firstChild);
5bc97c98 1681 }
2c412890 1682
e7004d05 1683 errorSpan.appendChild(document.createTextNode(_qfMsg.substring(3)));
e35c9eeb 1684 errorSpan.appendChild(document.createElement("br"));
5bc97c98 1685
1686 if (div.className.substr(div.className.length - 6, 6) != " error"
1687 && div.className != "error") {
1688 div.className += " error";
1689 }
1690
1691 return false;
1692 } else {
e35c9eeb 1693 var errorSpan = document.getElementById(\'id_error_\'+element.name);
e7004d05 1694 if (errorSpan) {
1695 errorSpan.parentNode.removeChild(errorSpan);
5bc97c98 1696 }
1697
1698 if (div.className.substr(div.className.length - 6, 6) == " error") {
1699 div.className = div.className.substr(0, div.className.length - 6);
1700 } else if (div.className == "error") {
1701 div.className = "";
1702 }
1703
1704 return true;
1705 }
1706}';
1707 $validateJS = '';
1708 foreach ($test as $elementName => $jsandelement) {
1709 // Fix for bug displaying errors for elements in a group
1710 //unset($element);
1711 list($jsArr,$element)=$jsandelement;
1712 //end of fix
1713 $js .= '
f07b9627 1714function validate_' . $this->_formName . '_' . $elementName . '(element) {
5bc97c98 1715 var value = \'\';
1716 var errFlag = new Array();
1717 var _qfGroups = {};
1718 var _qfMsg = \'\';
1719 var frm = element.parentNode;
cd350b53 1720 while (frm && frm.nodeName.toUpperCase() != "FORM") {
5bc97c98 1721 frm = frm.parentNode;
1722 }
1723' . join("\n", $jsArr) . '
1724 return qf_errorHandler(element, _qfMsg);
1725}
1726';
1727 $validateJS .= '
573e7e33 1728 ret = validate_' . $this->_formName . '_' . $elementName.'(frm.elements[\''.$elementName.'\']) && ret;
2ef7c374 1729 if (!ret && !first_focus) {
1730 first_focus = true;
1731 frm.elements[\''.$elementName.'\'].focus();
1732 }
1733';
4f51f48f 1734
5bc97c98 1735 // Fix for bug displaying errors for elements in a group
1736 //unset($element);
1737 //$element =& $this->getElement($elementName);
1738 //end of fix
f07b9627 1739 $valFunc = 'validate_' . $this->_formName . '_' . $elementName . '(this)';
5bc97c98 1740 $onBlur = $element->getAttribute('onBlur');
1741 $onChange = $element->getAttribute('onChange');
1742 $element->updateAttributes(array('onBlur' => $onBlur . $valFunc,
1743 'onChange' => $onChange . $valFunc));
1744 }
e7004d05 1745// do not rely on frm function parameter, because htmlarea breaks it when overloading the onsubmit method
5bc97c98 1746 $js .= '
f07b9627 1747function validate_' . $this->_formName . '(frm) {
1cbb09f1 1748 if (skipClientValidation) {
1749 return true;
1750 }
5bc97c98 1751 var ret = true;
4f51f48f 1752
0befbdfd 1753 var frm = document.getElementById(\''. $this->_attributes['id'] .'\')
2ef7c374 1754 var first_focus = false;
5bc97c98 1755' . $validateJS . ';
1756 return ret;
1757}
1758//]]>
1759</script>';
1760 return $js;
1761 } // end func getValidationScript
1762 function _setDefaultRuleMessages(){
1763 foreach ($this->_rules as $field => $rulesarr){
1764 foreach ($rulesarr as $key => $rule){
1765 if ($rule['message']===null){
1766 $a=new object();
1767 $a->format=$rule['format'];
1768 $str=get_string('err_'.$rule['type'], 'form', $a);
1769 if (strpos($str, '[[')!==0){
1770 $this->_rules[$field][$key]['message']=$str;
2c412890 1771 }
5bc97c98 1772 }
1773 }
1774 }
1775 }
bb40325e 1776
ba21c9d4 1777 /**
1778 * @return string
1779 */
d01a38cb 1780 function getLockOptionEndScript(){
dd07bbac 1781
1782 $iname = $this->getAttribute('id').'items';
f4ba7e1a 1783 $js = '<script type="text/javascript">'."\n";
5e87b920 1784 $js .= '//<![CDATA['."\n";
dd07bbac 1785 $js .= "var $iname = Array();\n";
1786
1787 foreach ($this->_dependencies as $dependentOn => $conditions){
1788 $js .= "{$iname}['$dependentOn'] = Array();\n";
1789 foreach ($conditions as $condition=>$values) {
1790 $js .= "{$iname}['$dependentOn']['$condition'] = Array();\n";
1791 foreach ($values as $value=>$dependents) {
1792 $js .= "{$iname}['$dependentOn']['$condition']['$value'] = Array();\n";
1793 $i = 0;
1794 foreach ($dependents as $dependent) {
1795 $elements = $this->_getElNamesRecursive($dependent);
46f3921e 1796 if (empty($elements)) {
1797 // probably element inside of some group
1798 $elements = array($dependent);
1799 }
dd07bbac 1800 foreach($elements as $element) {
1801 if ($element == $dependentOn) {
1802 continue;
1803 }
1804 $js .= "{$iname}['$dependentOn']['$condition']['$value'][$i]='$element';\n";
1805 $i++;
1806 }
11f260f4 1807 }
1808 }
50ef8eb9 1809 }
dd07bbac 1810 }
d01a38cb 1811 $js .="lockoptionsallsetup('".$this->getAttribute('id')."');\n";
5e87b920 1812 $js .='//]]>'."\n";
50ef8eb9 1813 $js .='</script>'."\n";
1814 return $js;
bb40325e 1815 }
d01a38cb 1816
ba21c9d4 1817 /**
1818 * @param mixed $element
1819 * @return array
1820 */
46f3921e 1821 function _getElNamesRecursive($element) {
1822 if (is_string($element)) {
4f51f48f 1823 if (!$this->elementExists($element)) {
1824 return array();
1825 }
46f3921e 1826 $element = $this->getElement($element);
d01a38cb 1827 }
46f3921e 1828
1829 if (is_a($element, 'HTML_QuickForm_group')) {
1830 $elsInGroup = $element->getElements();
9403060a 1831 $elNames = array();
d01a38cb 1832 foreach ($elsInGroup as $elInGroup){
e850ec48 1833 if (is_a($elInGroup, 'HTML_QuickForm_group')) {
1834 // not sure if this would work - groups nested in groups
1835 $elNames = array_merge($elNames, $this->_getElNamesRecursive($elInGroup));
1836 } else {
1837 $elNames[] = $element->getElementName($elInGroup->getName());
1838 }
d01a38cb 1839 }
46f3921e 1840
1841 } else if (is_a($element, 'HTML_QuickForm_header')) {
1842 return array();
1843
1844 } else if (is_a($element, 'HTML_QuickForm_hidden')) {
1845 return array();
1846
1847 } else if (method_exists($element, 'getPrivateName')) {
1848 return array($element->getPrivateName());
1849
1850 } else {
1851 $elNames = array($element->getName());
d01a38cb 1852 }
d01a38cb 1853
46f3921e 1854 return $elNames;
50ef8eb9 1855 }
46f3921e 1856
6e372b25 1857 /**
1858 * Adds a dependency for $elementName which will be disabled if $condition is met.
9403060a 1859 * If $condition = 'notchecked' (default) then the condition is that the $dependentOn element
1860 * is not checked. If $condition = 'checked' then the condition is that the $dependentOn element
31a6c06c 1861 * is checked. If $condition is something else (like "eq" for equals) then it is checked to see if the value
1862 * of the $dependentOn element is $condition (such as equal) to $value.
6e372b25 1863 *
1864 * @param string $elementName the name of the element which will be disabled
1865 * @param string $dependentOn the name of the element whose state will be checked for
1866 * condition
1867 * @param string $condition the condition to check
19110c57 1868 * @param mixed $value used in conjunction with condition.
6e372b25 1869 */
dd07bbac 1870 function disabledIf($elementName, $dependentOn, $condition = 'notchecked', $value='1'){
1871 if (!array_key_exists($dependentOn, $this->_dependencies)) {
1872 $this->_dependencies[$dependentOn] = array();
1873 }
1874 if (!array_key_exists($condition, $this->_dependencies[$dependentOn])) {
1875 $this->_dependencies[$dependentOn][$condition] = array();
1876 }
1877 if (!array_key_exists($value, $this->_dependencies[$dependentOn][$condition])) {
1878 $this->_dependencies[$dependentOn][$condition][$value] = array();
1879 }
1880 $this->_dependencies[$dependentOn][$condition][$value][] = $elementName;
bb40325e 1881 }
dd07bbac 1882
a23f0aaf 1883 function registerNoSubmitButton($buttonname){
1884 $this->_noSubmitButtons[]=$buttonname;
1885 }
dd07bbac 1886
ba21c9d4 1887 /**
1888 * @param string $buttonname
1889 * @return mixed
1890 */
a23f0aaf 1891 function isNoSubmitButton($buttonname){
1892 return (array_search($buttonname, $this->_noSubmitButtons)!==FALSE);
19110c57 1893 }
dd07bbac 1894
ba21c9d4 1895 /**
1896 * @param string $buttonname
1897 */
19110c57 1898 function _registerCancelButton($addfieldsname){
1899 $this->_cancelButtons[]=$addfieldsname;
1900 }
acc9c3e0 1901 /**
1902 * Displays elements without HTML input tags.
1903 * This method is different to freeze() in that it makes sure no hidden
cc444336 1904 * elements are included in the form.
1905 * Note: If you want to make sure the submitted value is ignored, please use setDefaults().
acc9c3e0 1906 *
4af06dda 1907 * This function also removes all previously defined rules.
1908 *
acc9c3e0 1909 * @param mixed $elementList array or string of element(s) to be frozen
acc9c3e0 1910 * @access public
acc9c3e0 1911 */
1912 function hardFreeze($elementList=null)
1913 {
1914 if (!isset($elementList)) {
1915 $this->_freezeAll = true;
1916 $elementList = array();
1917 } else {
1918 if (!is_array($elementList)) {
1919 $elementList = preg_split('/[ ]*,[ ]*/', $elementList);
1920 }
1921 $elementList = array_flip($elementList);
1922 }
1923
1924 foreach (array_keys($this->_elements) as $key) {
1925 $name = $this->_elements[$key]->getName();
1926 if ($this->_freezeAll || isset($elementList[$name])) {
1927 $this->_elements[$key]->freeze();
1928 $this->_elements[$key]->setPersistantFreeze(false);
1929 unset($elementList[$name]);
4af06dda 1930
1931 // remove all rules
1932 $this->_rules[$name] = array();
1933 // if field is required, remove the rule
1934 $unset = array_search($name, $this->_required);
1935 if ($unset !== false) {
1936 unset($this->_required[$unset]);
1937 }
acc9c3e0 1938 }
1939 }
1940
1941 if (!empty($elementList)) {
1942 return PEAR::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Nonexistant element(s): '" . implode("', '", array_keys($elementList)) . "' in HTML_QuickForm::freeze()", 'HTML_QuickForm_Error', true);
1943 }
1944 return true;
4f51f48f 1945 }
1946 /**
1947 * Hard freeze all elements in a form except those whose names are in $elementList or hidden elements in a form.
1948 *
1949 * This function also removes all previously defined rules of elements it freezes.
1950 *
ba21c9d4 1951 * throws HTML_QuickForm_Error
1952 *
4f51f48f 1953 * @param array $elementList array or string of element(s) not to be frozen
4f51f48f 1954 * @access public
4f51f48f 1955 */
1956 function hardFreezeAllVisibleExcept($elementList)
1957 {
1958 $elementList = array_flip($elementList);
1959 foreach (array_keys($this->_elements) as $key) {
1960 $name = $this->_elements[$key]->getName();
1961 $type = $this->_elements[$key]->getType();
56015454 1962
4f51f48f 1963 if ($type == 'hidden'){
1964 // leave hidden types as they are
1965 } elseif (!isset($elementList[$name])) {
1966 $this->_elements[$key]->freeze();
1967 $this->_elements[$key]->setPersistantFreeze(false);
1968
1969 // remove all rules
1970 $this->_rules[$name] = array();
1971 // if field is required, remove the rule
1972 $unset = array_search($name, $this->_required);
1973 if ($unset !== false) {
1974 unset($this->_required[$unset]);
1975 }
1976 }
1977 }
1978 return true;
1979 }
1980 /**
1981 * Tells whether the form was already submitted
1982 *
1983 * This is useful since the _submitFiles and _submitValues arrays
1984 * may be completely empty after the trackSubmit value is removed.
1985 *
1986 * @access public
1987 * @return bool
1988 */
1989 function isSubmitted()
1990 {
1991 return parent::isSubmitted() && (!$this->isFrozen());
1992 }
da6f8763 1993}
1994
e24b7f85 1995
da6f8763 1996/**
7f40a229 1997 * A renderer for MoodleQuickForm that only uses XHTML and CSS and no
da6f8763 1998 * table tags, extends PEAR class HTML_QuickForm_Renderer_Tableless
7f40a229 1999 *
da6f8763 2000 * Stylesheet is part of standard theme and should be automatically included.
2001 *
ba21c9d4 2002 * @package moodlecore
2003 * @copyright Jamie Pratt <me@jamiep.org>
2004 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
da6f8763 2005 */
7f40a229 2006class MoodleQuickForm_Renderer extends HTML_QuickForm_Renderer_Tableless{
da6f8763 2007
2008 /**
2009 * Element template array
2010 * @var array
2011 * @access private
2012 */
2013 var $_elementTemplates;
49c53687 2014 /**
2015 * Template used when opening a hidden fieldset
2016 * (i.e. a fieldset that is opened when there is no header element)
2017 * @var string
2018 * @access private
2019 */
c02345e3 2020 var $_openHiddenFieldsetTemplate = "\n\t<fieldset class=\"hidden\"><div>";
19194f82 2021 /**
2022 * Header Template string
2023 * @var string
2024 * @access private
2025 */
2026 var $_headerTemplate =
c02345e3 2027 "\n\t\t<legend class=\"ftoggler\">{header}</legend>\n\t\t<div class=\"advancedbutton\">{advancedimg}{button}</div><div class=\"fcontainer clearfix\">\n\t\t";
7f40a229 2028
49c53687 2029 /**
bc9ec4a6 2030 * Template used when opening a fieldset
2031 * @var string
2032 * @access private
2033 */
2034 var $_openFieldsetTemplate = "\n\t<fieldset class=\"clearfix\" {id}>";
2035
2036 /**
49c53687 2037 * Template used when closing a fieldset
2038 * @var string
2039 * @access private
2040 */
c02345e3 2041 var $_closeFieldsetTemplate = "\n\t\t</div></fieldset>";
42f248e6 2042
49c53687 2043 /**
2044 * Required Note template string
2045 * @var string
2046 * @access private
2047 */
7f40a229 2048 var $_requiredNoteTemplate =
6ba2c73d 2049 "\n\t\t<div class=\"fdescription required\">{requiredNote}</div>";
7f40a229 2050
19194f82 2051 var $_advancedElements = array();
2052
2053 /**
2054 * Whether to display advanced elements (on page load)
2055 *
2056 * @var integer 1 means show 0 means hide
2057 */
2058 var $_showAdvanced;
2059
7f40a229 2060 function MoodleQuickForm_Renderer(){
42f248e6 2061 // switch next two lines for ol li containers for form items.
49c7f3a8 2062 // $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 2063 $this->_elementTemplates = array(
f8b9ac74 2064 'default'=>"\n\t\t".'<div class="fitem {advanced}<!-- BEGIN required --> required<!-- END required -->"><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 2065
f9f9be73 2066 'fieldset'=>"\n\t\t".'<div class="fitem {advanced}<!-- BEGIN required --> required<!-- END required -->"><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 2067
f9f9be73 2068 '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}&nbsp;</div></div>',
43914931 2069
1ae1941e 2070'warning'=>"\n\t\t".'<div class="fitem {advanced}">{element}</div>',
2071
4f51f48f 2072 'nodisplay'=>'');
da6f8763 2073
2074 parent::HTML_QuickForm_Renderer_Tableless();
2075 }
7f40a229 2076
ba21c9d4 2077 /**
2078 * @param array $elements
2079 */
19194f82 2080 function setAdvancedElements($elements){
2081 $this->_advancedElements = $elements;
2082 }
2083
2084 /**
2085 * What to do when starting the form
2086 *
ba21c9d4 2087 * @param object $form MoodleQuickForm
19194f82 2088 */
da6f8763 2089 function startForm(&$form){
9403060a 2090 $this->_reqHTML = $form->getReqHTML();
2091 $this->_elementTemplates = str_replace('{req}', $this->_reqHTML, $this->_elementTemplates);
19194f82 2092 $this->_advancedHTML = $form->getAdvancedHTML();
2093 $this->_showAdvanced = $form->getShowAdvanced();
da6f8763 2094 parent::startForm($form);
4f51f48f 2095 if ($form->isFrozen()){
2096 $this->_formTemplate = "\n<div class=\"mform frozen\">\n{content}\n</div>";
2097 } else {
0524b1d9 2098 $this->_formTemplate = "\n<form{attributes}>\n\t<div style=\"display: none;\">{hidden}</div>\n{content}\n</form>";
4f51f48f 2099 $this->_hiddenHtml .= $form->_pageparams;
2100 }
2101
2102
da6f8763 2103 }
117bd748 2104
ba21c9d4 2105 /**
2106 * @param object $group Passed by reference
2107 * @param mixed $required
2108 * @param mixed $error
2109 */
da6f8763 2110 function startGroup(&$group, $required, $error){
2111 if (method_exists($group, 'getElementTemplateType')){
e249661f 2112 $html = $this->_elementTemplates[$group->getElementTemplateType()];
da6f8763 2113 }else{
2114 $html = $this->_elementTemplates['default'];
7f40a229 2115
da6f8763 2116 }
19194f82 2117 if ($this->_showAdvanced){
2118 $advclass = ' advanced';
2119 } else {
2120 $advclass = ' advanced hide';
2121 }
2122 if (isset($this->_advancedElements[$group->getName()])){
2123 $html =str_replace(' {advanced}', $advclass, $html);
2124 $html =str_replace('{advancedimg}', $this->_advancedHTML, $html);
2125 } else {
2126 $html =str_replace(' {advanced}', '', $html);
2127 $html =str_replace('{advancedimg}', '', $html);
2128 }
da6f8763 2129 if (method_exists($group, 'getHelpButton')){
2130 $html =str_replace('{help}', $group->getHelpButton(), $html);
2131 }else{
2132 $html =str_replace('{help}', '', $html);
da6f8763 2133 }
e7004d05 2134 $html =str_replace('{name}', $group->getName(), $html);
49c53687 2135 $html =str_replace('{type}', 'fgroup', $html);
7f40a229 2136
da6f8763 2137 $this->_templates[$group->getName()]=$html;
2138 // Fix for bug in tableless quickforms that didn't allow you to stop a
2139 // fieldset before a group of elements.
2140 // if the element name indicates the end of a fieldset, close the fieldset
2141 if ( in_array($group->getName(), $this->_stopFieldsetElements)
2142 && $this->_fieldsetsOpen > 0
2143 ) {
2144 $this->_html .= $this->_closeFieldsetTemplate;
2145 $this->_fieldsetsOpen--;
2146 }
2147 parent::startGroup($group, $required, $error);
2148 }
ba21c9d4 2149 /**
2150 * @param object $element
2151 * @param mixed $required
2152 * @param mixed $error
2153 */
da6f8763 2154 function renderElement(&$element, $required, $error){
172dd12c 2155 //manipulate id of all elements before rendering
86aab05c 2156 if (!is_null($element->getAttribute('id'))) {
2157 $id = $element->getAttribute('id');
2158 } else {
2159 $id = $element->getName();
2160 }
2161 //strip qf_ prefix and replace '[' with '_' and strip ']'
2162 $id = preg_replace(array('/^qf_|\]/', '/\[/'), array('', '_'), $id);
2163 if (strpos($id, 'id_') !== 0){
2164 $element->updateAttributes(array('id'=>'id_'.$id));
2165 }
2166
2167 //adding stuff to place holders in template
172dd12c 2168 //check if this is a group element first
906ebc4b 2169 if (($this->_inGroup) and !empty($this->_groupElementTemplate)) {
3493eb15 2170 // so it gets substitutions for *each* element
c07b5ad4 2171 $html = $this->_groupElementTemplate;
906ebc4b 2172 }
2173 elseif (method_exists($element, 'getElementTemplateType')){
da6f8763 2174 $html = $this->_elementTemplates[$element->getElementTemplateType()];
2175 }else{
2176 $html = $this->_elementTemplates['default'];
19194f82 2177 }
2178 if ($this->_showAdvanced){
2179 $advclass = ' advanced';
2180 } else {
2181 $advclass = ' advanced hide';
2182 }
2183 if (isset($this->_advancedElements[$element->getName()])){
2184 $html =str_replace(' {advanced}', $advclass, $html);
2185 } else {
2186 $html =str_replace(' {advanced}', '', $html);
2187 }
2188 if (isset($this->_advancedElements[$element->getName()])||$element->getName() == 'mform_showadvanced'){
2189 $html =str_replace('{advancedimg}', $this->_advancedHTML, $html);
2190 } else {
2191 $html =str_replace('{advancedimg}', '', $html);
da6f8763 2192 }
49c53687 2193 $html =str_replace('{type}', 'f'.$element->getType(), $html);
e7004d05 2194 $html =str_replace('{name}', $element->getName(), $html);
da6f8763 2195 if (method_exists($element, 'getHelpButton')){
9403060a 2196 $html = str_replace('{help}', $element->getHelpButton(), $html);
da6f8763 2197 }else{
9403060a 2198 $html = str_replace('{help}', '', $html);
7f40a229 2199
da6f8763 2200 }
906ebc4b 2201 if (($this->_inGroup) and !empty($this->_groupElementTemplate)) {
2202 $this->_groupElementTemplate = $html;
41b6d001 2203 }
906ebc4b 2204 elseif (!isset($this->_templates[$element->getName()])) {
2205 $this->_templates[$element->getName()] = $html;
172dd12c 2206 }
2207
da6f8763 2208 parent::renderElement($element, $required, $error);
2209 }
19194f82 2210
ba21c9d4 2211 /**
2212 * @param object $form Passed by reference
2213 */
bb40325e 2214 function finishForm(&$form){
4f51f48f 2215 if ($form->isFrozen()){
2216 $this->_hiddenHtml = '';
2217 }
bb40325e 2218 parent::finishForm($form);
4f51f48f 2219 if ((!$form->isFrozen()) && ('' != ($script = $form->getLockOptionEndScript()))) {
2220 // add a lockoptions script
bb40325e 2221 $this->_html = $this->_html . "\n" . $script;
2222 }
2223 }
19194f82 2224 /**
2225 * Called when visiting a header element
2226 *
ba21c9d4 2227 * @param object $header An HTML_QuickForm_header element being visited
19194f82 2228 * @access public
2229 * @return void
2230 */
c28bf5c9 2231 function renderHeader(&$header) {
2232 global $PAGE;
9262d2d3 2233 static $advformcount;
2234
2235 // This ensures that if 2(+) advanced buttons are used
2236 // that all show/hide buttons appear in the correct place
2237 // Because of now using $PAGE->requires->js_function_call
2238 if ($advformcount==null) {
2239 $advformcount = 1;
2240 }
2241
19194f82 2242 $name = $header->getName();
2243
2244 $id = empty($name) ? '' : ' id="' . $name . '"';
78354cec 2245 $id = preg_replace(array('/\]/', '/\[/'), array('', '_'), $id);
19194f82 2246 if (is_null($header->_text)) {
2247 $header_html = '';
2248 } elseif (!empty($name) && isset($this->_templates[$name])) {
2249 $header_html = str_replace('{header}', $header->toHtml(), $this->_templates[$name]);
2250 } else {
2251 $header_html = str_replace('{header}', $header->toHtml(), $this->_headerTemplate);
2252 }
2253
2254 if (isset($this->_advancedElements[$name])){
2255 $header_html =str_replace('{advancedimg}', $this->_advancedHTML, $header_html);
2256 } else {
2257 $header_html =str_replace('{advancedimg}', '', $header_html);
2258 }
2259 $elementName='mform_showadvanced';
2260 if ($this->_showAdvanced==0){
2261 $buttonlabel = get_string('showadvanced', 'form');
2262 } else {
2263 $buttonlabel = get_string('hideadvanced', 'form');
2264 }
2265
2266 if (isset($this->_advancedElements[$name])){
f44b10ed 2267 $PAGE->requires->yui2_lib('event');
7c9b1d31 2268 // this is tricky - the first submit button on form is "clicked" if user presses enter
2269 // we do not want to "submit" using advanced button if javascript active
9262d2d3 2270 $button_nojs = '<input name="'.$elementName.'" id="'.$elementName.(string)$advformcount.'" class="showadvancedbtn" value="'.$buttonlabel.'" type="submit" />';
cd350b53 2271
2272 $buttonlabel = addslashes_js($buttonlabel);
fc4f5796 2273 $PAGE->requires->string_for_js('showadvanced', 'form');
2274 $PAGE->requires->string_for_js('hideadvanced', 'form');
2275 $PAGE->requires->js_function_call('showAdvancedInit', Array($elementName.(string)$advformcount, $elementName, $buttonlabel));
117bd748 2276
9262d2d3 2277 $advformcount++;
2278 $header_html = str_replace('{button}', $button_nojs, $header_html);
19194f82 2279 } else {
7c9b1d31 2280 $header_html = str_replace('{button}', '', $header_html);
19194f82 2281 }
2282
2283 if ($this->_fieldsetsOpen > 0) {
2284 $this->_html .= $this->_closeFieldsetTemplate;
2285 $this->_fieldsetsOpen--;
2286 }
2287
2288 $openFieldsetTemplate = str_replace('{id}', $id, $this->_openFieldsetTemplate);
2289 if ($this->_showAdvanced){
2290 $advclass = ' class="advanced"';
2291 } else {
2292 $advclass = ' class="advanced hide"';
2293 }
2294 if (isset($this->_advancedElements[$name])){
2295 $openFieldsetTemplate = str_replace('{advancedclass}', $advclass, $openFieldsetTemplate);
2296 } else {
2297 $openFieldsetTemplate = str_replace('{advancedclass}', '', $openFieldsetTemplate);
2298 }
2299 $this->_html .= $openFieldsetTemplate . $header_html;
2300 $this->_fieldsetsOpen++;
2301 } // end func renderHeader
2302
2303 function getStopFieldsetElements(){
2304 return $this->_stopFieldsetElements;
2305 }
da6f8763 2306}
2307
ba21c9d4 2308/**
2309 * @global object $GLOBALS['_HTML_QuickForm_default_renderer']
2310 * @name $_HTML_QuickForm_default_renderer
2311 */
66491cf1 2312$GLOBALS['_HTML_QuickForm_default_renderer'] = new MoodleQuickForm_Renderer();
da6f8763 2313
ba21c9d4 2314/** Please keep this list in alphabetical order. */
c583482c 2315MoodleQuickForm::registerElementType('advcheckbox', "$CFG->libdir/form/advcheckbox.php", 'MoodleQuickForm_advcheckbox');
2316MoodleQuickForm::registerElementType('button', "$CFG->libdir/form/button.php", 'MoodleQuickForm_button');
2317MoodleQuickForm::registerElementType('cancel', "$CFG->libdir/form/cancel.php", 'MoodleQuickForm_cancel');
09179b78 2318MoodleQuickForm::registerElementType('searchableselector', "$CFG->libdir/form/searchableselector.php", 'MoodleQuickForm_searchableselector');
7f40a229 2319MoodleQuickForm::registerElementType('checkbox', "$CFG->libdir/form/checkbox.php", 'MoodleQuickForm_checkbox');
c583482c 2320MoodleQuickForm::registerElementType('date_selector', "$CFG->libdir/form/dateselector.php", 'MoodleQuickForm_date_selector');
2321MoodleQuickForm::registerElementType('date_time_selector', "$CFG->libdir/form/datetimeselector.php", 'MoodleQuickForm_date_time_selector');
32fa2272 2322MoodleQuickForm::registerElementType('duration', "$CFG->libdir/form/duration.php", 'MoodleQuickForm_duration');
c583482c 2323MoodleQuickForm::registerElementType('editor', "$CFG->libdir/form/editor.php", 'MoodleQuickForm_editor');
7f40a229 2324MoodleQuickForm::registerElementType('file', "$CFG->libdir/form/file.php", 'MoodleQuickForm_file');
241431cd 2325MoodleQuickForm::registerElementType('filemanager', "$CFG->libdir/form/filemanager.php", 'MoodleQuickForm_filemanager');
c5704ec6 2326MoodleQuickForm::registerElementType('filepicker', "$CFG->libdir/form/filepicker.php", 'MoodleQuickForm_filepicker');
c583482c 2327MoodleQuickForm::registerElementType('format', "$CFG->libdir/form/format.php", 'MoodleQuickForm_format');
7f40a229 2328MoodleQuickForm::registerElementType('group', "$CFG->libdir/form/group.php", 'MoodleQuickForm_group');
c583482c 2329MoodleQuickForm::registerElementType('header', "$CFG->libdir/form/header.php", 'MoodleQuickForm_header');
2330MoodleQuickForm::registerElementType('hidden', "$CFG->libdir/form/hidden.php", 'MoodleQuickForm_hidden');
2331MoodleQuickForm::registerElementType('htmleditor', "$CFG->libdir/form/htmleditor.php", 'MoodleQuickForm_htmleditor');
2332MoodleQuickForm::registerElementType('modgrade', "$CFG->libdir/form/modgrade.php", 'MoodleQuickForm_modgrade');
2333MoodleQuickForm::registerElementType('modvisible', "$CFG->libdir/form/modvisible.php", 'MoodleQuickForm_modvisible');
7f40a229 2334MoodleQuickForm::registerElementType('password', "$CFG->libdir/form/password.php", 'MoodleQuickForm_password');
4f51f48f 2335MoodleQuickForm::registerElementType('passwordunmask', "$CFG->libdir/form/passwordunmask.php", 'MoodleQuickForm_passwordunmask');
c583482c 2336MoodleQuickForm::registerElementType('questioncategory', "$CFG->libdir/form/questioncategory.php", 'MoodleQuickForm_questioncategory');
7f40a229 2337MoodleQuickForm::registerElementType('radio', "$CFG->libdir/form/radio.php", 'MoodleQuickForm_radio');
c583482c 2338MoodleQuickForm::registerElementType('recaptcha', "$CFG->libdir/form/recaptcha.php", 'MoodleQuickForm_recaptcha');
7f40a229 2339MoodleQuickForm::registerElementType('select', "$CFG->libdir/form/select.php", 'MoodleQuickForm_select');
4f51f48f 2340MoodleQuickForm::registerElementType('selectgroups', "$CFG->libdir/form/selectgroups.php", 'MoodleQuickForm_selectgroups');
c583482c 2341MoodleQuickForm::registerElementType('selectwithlink', "$CFG->libdir/form/selectwithlink.php", 'MoodleQuickForm_selectwithlink');
2342MoodleQuickForm::registerElementType('selectyesno', "$CFG->libdir/form/selectyesno.php", 'MoodleQuickForm_selectyesno');
2343MoodleQuickForm::registerElementType('static', "$CFG->libdir/form/static.php", 'MoodleQuickForm_static');
2344MoodleQuickForm::registerElementType('submit', "$CFG->libdir/form/submit.php", 'MoodleQuickForm_submit');
6073a598 2345MoodleQuickForm::registerElementType('submitlink', "$CFG->libdir/form/submitlink.php", 'MoodleQuickForm_submitlink');
9f1c9dfc 2346MoodleQuickForm::registerElementType('tags', "$CFG->libdir/form/tags.php", 'MoodleQuickForm_tags');
7f40a229 2347MoodleQuickForm::registerElementType('text', "$CFG->libdir/form/text.php", 'MoodleQuickForm_text');
2348MoodleQuickForm::registerElementType('textarea', "$CFG->libdir/form/textarea.php", 'MoodleQuickForm_textarea');
1a03384f 2349MoodleQuickForm::registerElementType('url', "$CFG->libdir/form/url.php", 'MoodleQuickForm_url');
1ae1941e 2350MoodleQuickForm::registerElementType('warning', "$CFG->libdir/form/warning.php", 'MoodleQuickForm_warning');