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