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