See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 400 and 401] [Versions 401 and 402] [Versions 401 and 403]
1 <?php 2 /* vim: set expandtab tabstop=4 shiftwidth=4: */ 3 // +----------------------------------------------------------------------+ 4 // | PHP version 4.0 | 5 // +----------------------------------------------------------------------+ 6 // | Copyright (c) 1997-2003 The PHP Group | 7 // +----------------------------------------------------------------------+ 8 // | This source file is subject to version 2.0 of the PHP license, | 9 // | that is bundled with this package in the file LICENSE, and is | 10 // | available at through the world-wide-web at | 11 // | http://www.php.net/license/2_02.txt. | 12 // | If you did not receive a copy of the PHP license and are unable to | 13 // | obtain it through the world-wide-web, please send a note to | 14 // | license@php.net so we can mail you a copy immediately. | 15 // +----------------------------------------------------------------------+ 16 // | Authors: Adam Daniel <adaniel1@eesus.jnj.com> | 17 // | Bertrand Mansion <bmansion@mamasam.com> | 18 // +----------------------------------------------------------------------+ 19 // 20 // $Id$ 21 22 require_once('PEAR.php'); 23 require_once('HTML/Common.php'); 24 /** 25 * Static utility methods. 26 */ 27 require_once('HTML/QuickForm/utils.php'); 28 29 $GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES'] = 30 array( 31 'group' =>array('HTML/QuickForm/group.php','HTML_QuickForm_group'), 32 'hidden' =>array('HTML/QuickForm/hidden.php','HTML_QuickForm_hidden'), 33 'reset' =>array('HTML/QuickForm/reset.php','HTML_QuickForm_reset'), 34 'checkbox' =>array('HTML/QuickForm/checkbox.php','HTML_QuickForm_checkbox'), 35 'file' =>array('HTML/QuickForm/file.php','HTML_QuickForm_file'), 36 'image' =>array('HTML/QuickForm/image.php','HTML_QuickForm_image'), 37 'password' =>array('HTML/QuickForm/password.php','HTML_QuickForm_password'), 38 'radio' =>array('HTML/QuickForm/radio.php','HTML_QuickForm_radio'), 39 'button' =>array('HTML/QuickForm/button.php','HTML_QuickForm_button'), 40 'submit' =>array('HTML/QuickForm/submit.php','HTML_QuickForm_submit'), 41 'select' =>array('HTML/QuickForm/select.php','HTML_QuickForm_select'), 42 'hiddenselect' =>array('HTML/QuickForm/hiddenselect.php','HTML_QuickForm_hiddenselect'), 43 'text' =>array('HTML/QuickForm/text.php','HTML_QuickForm_text'), 44 'textarea' =>array('HTML/QuickForm/textarea.php','HTML_QuickForm_textarea'), 45 'link' =>array('HTML/QuickForm/link.php','HTML_QuickForm_link'), 46 'advcheckbox' =>array('HTML/QuickForm/advcheckbox.php','HTML_QuickForm_advcheckbox'), 47 'date' =>array('HTML/QuickForm/date.php','HTML_QuickForm_date'), 48 'static' =>array('HTML/QuickForm/static.php','HTML_QuickForm_static'), 49 'header' =>array('HTML/QuickForm/header.php', 'HTML_QuickForm_header'), 50 'html' =>array('HTML/QuickForm/html.php', 'HTML_QuickForm_html'), 51 'hierselect' =>array('HTML/QuickForm/hierselect.php', 'HTML_QuickForm_hierselect'), 52 'autocomplete' =>array('HTML/QuickForm/autocomplete.php', 'HTML_QuickForm_autocomplete'), 53 'xbutton' =>array('HTML/QuickForm/xbutton.php','HTML_QuickForm_xbutton') 54 ); 55 56 $GLOBALS['_HTML_QuickForm_registered_rules'] = array( 57 'required' => array('html_quickform_rule_required', 'HTML/QuickForm/Rule/Required.php'), 58 'maxlength' => array('html_quickform_rule_range', 'HTML/QuickForm/Rule/Range.php'), 59 'minlength' => array('html_quickform_rule_range', 'HTML/QuickForm/Rule/Range.php'), 60 'rangelength' => array('html_quickform_rule_range', 'HTML/QuickForm/Rule/Range.php'), 61 'email' => array('html_quickform_rule_email', 'HTML/QuickForm/Rule/Email.php'), 62 'regex' => array('html_quickform_rule_regex', 'HTML/QuickForm/Rule/Regex.php'), 63 'lettersonly' => array('html_quickform_rule_regex', 'HTML/QuickForm/Rule/Regex.php'), 64 'alphanumeric' => array('html_quickform_rule_regex', 'HTML/QuickForm/Rule/Regex.php'), 65 'numeric' => array('html_quickform_rule_regex', 'HTML/QuickForm/Rule/Regex.php'), 66 'nopunctuation' => array('html_quickform_rule_regex', 'HTML/QuickForm/Rule/Regex.php'), 67 'nonzero' => array('html_quickform_rule_regex', 'HTML/QuickForm/Rule/Regex.php'), 68 'positiveint' => array('html_quickform_rule_regex', 'HTML/QuickForm/Rule/Regex.php'), 69 'callback' => array('html_quickform_rule_callback', 'HTML/QuickForm/Rule/Callback.php'), 70 'compare' => array('html_quickform_rule_compare', 'HTML/QuickForm/Rule/Compare.php') 71 ); 72 73 // {{{ error codes 74 75 /* 76 * Error codes for the QuickForm interface, which will be mapped to textual messages 77 * in the QuickForm::errorMessage() function. If you are to add a new error code, be 78 * sure to add the textual messages to the QuickForm::errorMessage() function as well 79 */ 80 81 define('QUICKFORM_OK', 1); 82 define('QUICKFORM_ERROR', -1); 83 define('QUICKFORM_INVALID_RULE', -2); 84 define('QUICKFORM_NONEXIST_ELEMENT', -3); 85 define('QUICKFORM_INVALID_FILTER', -4); 86 define('QUICKFORM_UNREGISTERED_ELEMENT', -5); 87 define('QUICKFORM_INVALID_ELEMENT_NAME', -6); 88 define('QUICKFORM_INVALID_PROCESS', -7); 89 define('QUICKFORM_DEPRECATED', -8); 90 define('QUICKFORM_INVALID_DATASOURCE', -9); 91 92 // }}} 93 94 /** 95 * Create, validate and process HTML forms 96 * 97 * @author Adam Daniel <adaniel1@eesus.jnj.com> 98 * @author Bertrand Mansion <bmansion@mamasam.com> 99 * @version 2.0 100 * @since PHP 4.0.3pl1 101 */ 102 class HTML_QuickForm extends HTML_Common { 103 // {{{ properties 104 105 /** 106 * Array containing the form fields 107 * @since 1.0 108 * @var array 109 * @access private 110 */ 111 var $_elements = array(); 112 113 /** 114 * Array containing element name to index map 115 * @since 1.1 116 * @var array 117 * @access private 118 */ 119 var $_elementIndex = array(); 120 121 /** 122 * Array containing indexes of duplicate elements 123 * @since 2.10 124 * @var array 125 * @access private 126 */ 127 var $_duplicateIndex = array(); 128 129 /** 130 * Array containing required field IDs 131 * @since 1.0 132 * @var array 133 * @access private 134 */ 135 var $_required = array(); 136 137 /** 138 * Prefix message in javascript alert if error 139 * @since 1.0 140 * @var string 141 * @access public 142 */ 143 var $_jsPrefix = 'Invalid information entered.'; 144 145 /** 146 * Postfix message in javascript alert if error 147 * @since 1.0 148 * @var string 149 * @access public 150 */ 151 var $_jsPostfix = 'Please correct these fields.'; 152 153 /** 154 * Datasource object implementing the informal 155 * datasource protocol 156 * @since 3.3 157 * @var object 158 * @access private 159 */ 160 var $_datasource; 161 162 /** 163 * Array of default form values 164 * @since 2.0 165 * @var array 166 * @access private 167 */ 168 var $_defaultValues = array(); 169 170 /** 171 * Array of constant form values 172 * @since 2.0 173 * @var array 174 * @access private 175 */ 176 var $_constantValues = array(); 177 178 /** 179 * Array of submitted form values 180 * @since 1.0 181 * @var array 182 * @access private 183 */ 184 var $_submitValues = array(); 185 186 /** 187 * Array of submitted form files 188 * @since 1.0 189 * @var integer 190 * @access public 191 */ 192 var $_submitFiles = array(); 193 194 /** 195 * Value for maxfilesize hidden element if form contains file input 196 * @since 1.0 197 * @var integer 198 * @access public 199 */ 200 var $_maxFileSize = 1048576; // 1 Mb = 1048576 201 202 /** 203 * Flag to know if all fields are frozen 204 * @since 1.0 205 * @var boolean 206 * @access private 207 */ 208 var $_freezeAll = false; 209 210 /** 211 * Array containing the form rules 212 * @since 1.0 213 * @var array 214 * @access private 215 */ 216 var $_rules = array(); 217 218 /** 219 * Form rules, global variety 220 * @var array 221 * @access private 222 */ 223 var $_formRules = array(); 224 225 /** 226 * Array containing the validation errors 227 * @since 1.0 228 * @var array 229 * @access private 230 */ 231 var $_errors = array(); 232 233 /** 234 * Note for required fields in the form 235 * @var string 236 * @since 1.0 237 * @access private 238 */ 239 var $_requiredNote = '<span style="font-size:80%; color:#ff0000;">*</span><span style="font-size:80%;"> denotes required field</span>'; 240 241 /** 242 * Whether the form was submitted 243 * @var boolean 244 * @access private 245 */ 246 var $_flagSubmitted = false; 247 248 // }}} 249 // {{{ constructor 250 251 /** 252 * Class constructor 253 * @param string $formName Form's name. 254 * @param string $method (optional)Form's method defaults to 'POST' 255 * @param string $action (optional)Form's action 256 * @param string $target (optional)Form's target defaults to '_self' 257 * @param mixed $attributes (optional)Extra attributes for <form> tag 258 * @param bool $trackSubmit (optional)Whether to track if the form was submitted by adding a special hidden field 259 * @access public 260 */ 261 public function __construct($formName='', $method='post', $action='', $target='', $attributes=null, $trackSubmit = false) 262 { 263 parent::__construct($attributes); 264 $method = (strtoupper($method) == 'GET') ? 'get' : 'post'; 265 $action = ($action == '') ? $_SERVER['PHP_SELF'] : $action; 266 $target = empty($target) ? array() : array('target' => $target); 267 $attributes = array('action'=>$action, 'method'=>$method, 'name'=>$formName, 'id'=>$formName) + $target; 268 $this->updateAttributes($attributes); 269 if (!$trackSubmit || isset($_REQUEST['_qf__' . $formName])) { 270 $this->_submitValues = 'get' == $method? $_GET: $_POST; 271 $this->_submitFiles = $_FILES; 272 $this->_flagSubmitted = count($this->_submitValues) > 0 || count($this->_submitFiles) > 0; 273 } 274 if ($trackSubmit) { 275 unset($this->_submitValues['_qf__' . $formName]); 276 $this->addElement('hidden', '_qf__' . $formName, null); 277 } 278 if (preg_match('/^([0-9]+)([a-zA-Z]*)$/', ini_get('upload_max_filesize'), $matches)) { 279 // see http://www.php.net/manual/en/faq.using.php#faq.using.shorthandbytes 280 switch (strtoupper($matches['2'])) { 281 case 'G': 282 $this->_maxFileSize = $matches['1'] * 1073741824; 283 break; 284 case 'M': 285 $this->_maxFileSize = $matches['1'] * 1048576; 286 break; 287 case 'K': 288 $this->_maxFileSize = $matches['1'] * 1024; 289 break; 290 default: 291 $this->_maxFileSize = $matches['1']; 292 } 293 } 294 } // end constructor 295 296 /** 297 * Old syntax of class constructor. Deprecated in PHP7. 298 * 299 * @deprecated since Moodle 3.1 300 */ 301 public function HTML_QuickForm($formName='', $method='post', $action='', $target='', $attributes=null, $trackSubmit = false) { 302 debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER); 303 self::__construct($formName, $method, $action, $target, $attributes, $trackSubmit); 304 } 305 306 // }}} 307 // {{{ apiVersion() 308 309 /** 310 * Returns the current API version 311 * 312 * @since 1.0 313 * @access public 314 * @return float 315 */ 316 function apiVersion() 317 { 318 return 3.2; 319 } // end func apiVersion 320 321 // }}} 322 // {{{ registerElementType() 323 324 /** 325 * Registers a new element type 326 * 327 * @param string $typeName Name of element type 328 * @param string $include Include path for element type 329 * @param string $className Element class name 330 * @since 1.0 331 * @access public 332 * @return void 333 */ 334 static function registerElementType($typeName, $include, $className) 335 { 336 $GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES'][strtolower($typeName)] = array($include, $className); 337 } // end func registerElementType 338 339 // }}} 340 // {{{ registerRule() 341 342 /** 343 * Registers a new validation rule 344 * 345 * @param string $ruleName Name of validation rule 346 * @param string $type Either: 'regex', 'function' or 'rule' for an HTML_QuickForm_Rule object 347 * @param string $data1 Name of function, regular expression or HTML_QuickForm_Rule classname 348 * @param string $data2 Object parent of above function or HTML_QuickForm_Rule file path 349 * @since 1.0 350 * @access public 351 * @return void 352 */ 353 static function registerRule($ruleName, $type, $data1, $data2 = null) 354 { 355 include_once('HTML/QuickForm/RuleRegistry.php'); 356 $registry =& HTML_QuickForm_RuleRegistry::singleton(); 357 $registry->registerRule($ruleName, $type, $data1, $data2); 358 } // end func registerRule 359 360 // }}} 361 // {{{ elementExists() 362 363 /** 364 * Returns true if element is in the form 365 * 366 * @param string $element form name of element to check 367 * @since 1.0 368 * @access public 369 * @return boolean 370 */ 371 function elementExists($element=null) 372 { 373 return isset($this->_elementIndex[$element]); 374 } // end func elementExists 375 376 // }}} 377 // {{{ setDatasource() 378 379 /** 380 * Sets a datasource object for this form object 381 * 382 * Datasource default and constant values will feed the QuickForm object if 383 * the datasource implements defaultValues() and constantValues() methods. 384 * 385 * @param object $datasource datasource object implementing the informal datasource protocol 386 * @param mixed $defaultsFilter string or array of filter(s) to apply to default values 387 * @param mixed $constantsFilter string or array of filter(s) to apply to constants values 388 * @since 3.3 389 * @access public 390 * @return void 391 */ 392 function setDatasource(&$datasource, $defaultsFilter = null, $constantsFilter = null) 393 { 394 if (is_object($datasource)) { 395 $this->_datasource =& $datasource; 396 if (is_callable(array($datasource, 'defaultValues'))) { 397 $this->setDefaults($datasource->defaultValues($this), $defaultsFilter); 398 } 399 if (is_callable(array($datasource, 'constantValues'))) { 400 $this->setConstants($datasource->constantValues($this), $constantsFilter); 401 } 402 } else { 403 return self::raiseError(null, QUICKFORM_INVALID_DATASOURCE, null, E_USER_WARNING, "Datasource is not an object in QuickForm::setDatasource()", 'HTML_QuickForm_Error', true); 404 } 405 } // end func setDatasource 406 407 // }}} 408 // {{{ setDefaults() 409 410 /** 411 * Initializes default form values 412 * 413 * @param array $defaultValues values used to fill the form 414 * @param mixed $filter (optional) filter(s) to apply to all default values 415 * @since 1.0 416 * @access public 417 * @return void 418 */ 419 function setDefaults($defaultValues = null, $filter = null) 420 { 421 if (is_array($defaultValues)) { 422 if (isset($filter)) { 423 if (is_array($filter) && (2 != count($filter) || !is_callable($filter))) { 424 foreach ($filter as $val) { 425 if (!is_callable($val)) { 426 return self::raiseError(null, QUICKFORM_INVALID_FILTER, null, E_USER_WARNING, "Callback function does not exist in QuickForm::setDefaults()", 'HTML_QuickForm_Error', true); 427 } else { 428 $defaultValues = $this->_recursiveFilter($val, $defaultValues); 429 } 430 } 431 } elseif (!is_callable($filter)) { 432 return self::raiseError(null, QUICKFORM_INVALID_FILTER, null, E_USER_WARNING, "Callback function does not exist in QuickForm::setDefaults()", 'HTML_QuickForm_Error', true); 433 } else { 434 $defaultValues = $this->_recursiveFilter($filter, $defaultValues); 435 } 436 } 437 $this->_defaultValues = HTML_QuickForm::arrayMerge($this->_defaultValues, $defaultValues); 438 foreach (array_keys($this->_elements) as $key) { 439 $this->_elements[$key]->onQuickFormEvent('updateValue', null, $this); 440 } 441 } 442 } // end func setDefaults 443 444 // }}} 445 // {{{ setConstants() 446 447 /** 448 * Initializes constant form values. 449 * These values won't get overridden by POST or GET vars 450 * 451 * @param array $constantValues values used to fill the form 452 * @param mixed $filter (optional) filter(s) to apply to all default values 453 * 454 * @since 2.0 455 * @access public 456 * @return void 457 */ 458 function setConstants($constantValues = null, $filter = null) 459 { 460 if (is_array($constantValues)) { 461 if (isset($filter)) { 462 if (is_array($filter) && (2 != count($filter) || !is_callable($filter))) { 463 foreach ($filter as $val) { 464 if (!is_callable($val)) { 465 return self::raiseError(null, QUICKFORM_INVALID_FILTER, null, E_USER_WARNING, "Callback function does not exist in QuickForm::setConstants()", 'HTML_QuickForm_Error', true); 466 } else { 467 $constantValues = $this->_recursiveFilter($val, $constantValues); 468 } 469 } 470 } elseif (!is_callable($filter)) { 471 return self::raiseError(null, QUICKFORM_INVALID_FILTER, null, E_USER_WARNING, "Callback function does not exist in QuickForm::setConstants()", 'HTML_QuickForm_Error', true); 472 } else { 473 $constantValues = $this->_recursiveFilter($filter, $constantValues); 474 } 475 } 476 $this->_constantValues = HTML_QuickForm::arrayMerge($this->_constantValues, $constantValues); 477 foreach (array_keys($this->_elements) as $key) { 478 $this->_elements[$key]->onQuickFormEvent('updateValue', null, $this); 479 } 480 } 481 } // end func setConstants 482 483 // }}} 484 // {{{ setMaxFileSize() 485 486 /** 487 * Sets the value of MAX_FILE_SIZE hidden element 488 * 489 * @param int $bytes Size in bytes 490 * @since 3.0 491 * @access public 492 * @return void 493 */ 494 function setMaxFileSize($bytes = 0) 495 { 496 if ($bytes > 0) { 497 $this->_maxFileSize = $bytes; 498 } 499 if (!$this->elementExists('MAX_FILE_SIZE')) { 500 $this->addElement('hidden', 'MAX_FILE_SIZE', $this->_maxFileSize); 501 } else { 502 $el =& $this->getElement('MAX_FILE_SIZE'); 503 $el->updateAttributes(array('value' => $this->_maxFileSize)); 504 } 505 } // end func setMaxFileSize 506 507 // }}} 508 // {{{ getMaxFileSize() 509 510 /** 511 * Returns the value of MAX_FILE_SIZE hidden element 512 * 513 * @since 3.0 514 * @access public 515 * @return int max file size in bytes 516 */ 517 function getMaxFileSize() 518 { 519 return $this->_maxFileSize; 520 } // end func getMaxFileSize 521 522 // }}} 523 // {{{ &createElement() 524 525 /** 526 * Creates a new form element of the given type. 527 * 528 * This method accepts variable number of parameters, their 529 * meaning and count depending on $elementType 530 * 531 * @param string $elementType type of element to add (text, textarea, file...) 532 * @since 1.0 533 * @access public 534 * @return object extended class of HTML_element 535 * @throws HTML_QuickForm_Error 536 */ 537 function &createElement($elementType) 538 { 539 if (!isset($this) || !($this instanceof HTML_QuickForm)) { 540 // Several form elements in Moodle core before 3.2 were calling this method 541 // statically suppressing PHP notices. This debugging message should notify 542 // developers who copied such code and did not test their plugins on PHP 7.1. 543 // Example of fixing group form elements can be found in commit 544 // https://github.com/moodle/moodle/commit/721e2def56a48fab4f8d3ec7847af5cd03f5ec79 545 debugging('Function createElement() can not be called statically, ' . 546 'this will no longer work in PHP 7.1', 547 DEBUG_DEVELOPER); 548 } 549 $args = func_get_args(); 550 $element = self::_loadElement('createElement', $elementType, array_slice($args, 1)); 551 return $element; 552 } // end func createElement 553 554 // }}} 555 // {{{ _loadElement() 556 557 /** 558 * Returns a form element of the given type 559 * 560 * @param string $event event to send to newly created element ('createElement' or 'addElement') 561 * @param string $type element type 562 * @param array $args arguments for event 563 * @since 2.0 564 * @access private 565 * @return object a new element 566 * @throws HTML_QuickForm_Error 567 */ 568 function &_loadElement($event, $type, $args) 569 { 570 $type = strtolower($type); 571 if (!self::isTypeRegistered($type)) { 572 $error = self::raiseError(null, QUICKFORM_UNREGISTERED_ELEMENT, null, E_USER_WARNING, "Element '$type' does not exist in HTML_QuickForm::_loadElement()", 'HTML_QuickForm_Error', true); 573 return $error; 574 } 575 $className = $GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES'][$type][1]; 576 $includeFile = $GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES'][$type][0]; 577 include_once($includeFile); 578 $elementObject = new $className(); //Moodle: PHP 5.3 compatibility 579 for ($i = 0; $i < 5; $i++) { 580 if (!isset($args[$i])) { 581 $args[$i] = null; 582 } 583 } 584 $err = $elementObject->onQuickFormEvent($event, $args, $this); 585 if ($err !== true) { 586 return $err; 587 } 588 return $elementObject; 589 } // end func _loadElement 590 591 // }}} 592 // {{{ addElement() 593 594 /** 595 * Adds an element into the form 596 * 597 * If $element is a string representing element type, then this 598 * method accepts variable number of parameters, their meaning 599 * and count depending on $element 600 * 601 * @param mixed $element element object or type of element to add (text, textarea, file...) 602 * @since 1.0 603 * @return object reference to element 604 * @access public 605 * @throws HTML_QuickForm_Error 606 */ 607 function &addElement($element) 608 { 609 if (is_object($element) && is_subclass_of($element, 'html_quickform_element')) { 610 $elementObject = &$element; 611 $elementObject->onQuickFormEvent('updateValue', null, $this); 612 } else { 613 $args = func_get_args(); 614 $elementObject =& $this->_loadElement('addElement', $element, array_slice($args, 1)); 615 $pear = new PEAR(); 616 if ($pear->isError($elementObject)) { 617 return $elementObject; 618 } 619 } 620 $elementName = $elementObject->getName(); 621 622 // Add the element if it is not an incompatible duplicate 623 if (!empty($elementName) && isset($this->_elementIndex[$elementName])) { 624 if ($this->_elements[$this->_elementIndex[$elementName]]->getType() == 625 $elementObject->getType()) { 626 $this->_elements[] =& $elementObject; 627 $elKeys = array_keys($this->_elements); 628 $this->_duplicateIndex[$elementName][] = end($elKeys); 629 } else { 630 $error = self::raiseError(null, QUICKFORM_INVALID_ELEMENT_NAME, null, E_USER_WARNING, "Element '$elementName' already exists in HTML_QuickForm::addElement()", 'HTML_QuickForm_Error', true); 631 return $error; 632 } 633 } else { 634 $this->_elements[] =& $elementObject; 635 $elKeys = array_keys($this->_elements); 636 $this->_elementIndex[$elementName] = end($elKeys); 637 } 638 if ($this->_freezeAll) { 639 $elementObject->freeze(); 640 } 641 642 return $elementObject; 643 } // end func addElement 644 645 // }}} 646 // {{{ insertElementBefore() 647 648 /** 649 * Inserts a new element right before the other element 650 * 651 * Warning: it is not possible to check whether the $element is already 652 * added to the form, therefore if you want to move the existing form 653 * element to a new position, you'll have to use removeElement(): 654 * $form->insertElementBefore($form->removeElement('foo', false), 'bar'); 655 * 656 * @access public 657 * @since 3.2.4 658 * @param object HTML_QuickForm_element Element to insert 659 * @param string Name of the element before which the new one is inserted 660 * @return object HTML_QuickForm_element reference to inserted element 661 * @throws HTML_QuickForm_Error 662 */ 663 function &insertElementBefore(&$element, $nameAfter) 664 { 665 if (!empty($this->_duplicateIndex[$nameAfter])) { 666 $error = self::raiseError(null, QUICKFORM_INVALID_ELEMENT_NAME, null, E_USER_WARNING, 'Several elements named "' . $nameAfter . '" exist in HTML_QuickForm::insertElementBefore().', 'HTML_QuickForm_Error', true); 667 return $error; 668 } elseif (!$this->elementExists($nameAfter)) { 669 $error = self::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$nameAfter' does not exist in HTML_QuickForm::insertElementBefore()", 'HTML_QuickForm_Error', true); 670 return $error; 671 } 672 $elementName = $element->getName(); 673 $targetIdx = $this->_elementIndex[$nameAfter]; 674 $duplicate = false; 675 // Like in addElement(), check that it's not an incompatible duplicate 676 if (!empty($elementName) && isset($this->_elementIndex[$elementName])) { 677 if ($this->_elements[$this->_elementIndex[$elementName]]->getType() != $element->getType()) { 678 $error = self::raiseError(null, QUICKFORM_INVALID_ELEMENT_NAME, null, E_USER_WARNING, "Element '$elementName' already exists in HTML_QuickForm::insertElementBefore()", 'HTML_QuickForm_Error', true); 679 return $error; 680 } 681 $duplicate = true; 682 } 683 // Move all the elements after added back one place, reindex _elementIndex and/or _duplicateIndex 684 $elKeys = array_keys($this->_elements); 685 for ($i = end($elKeys); $i >= $targetIdx; $i--) { 686 if (isset($this->_elements[$i])) { 687 $currentName = $this->_elements[$i]->getName(); 688 $this->_elements[$i + 1] =& $this->_elements[$i]; 689 if ($this->_elementIndex[$currentName] == $i) { 690 $this->_elementIndex[$currentName] = $i + 1; 691 } else { 692 if (!empty($currentName)) { 693 $dupIdx = array_search($i, $this->_duplicateIndex[$currentName]); 694 $this->_duplicateIndex[$currentName][$dupIdx] = $i + 1; 695 } 696 } 697 unset($this->_elements[$i]); 698 } 699 } 700 // Put the element in place finally 701 $this->_elements[$targetIdx] =& $element; 702 if (!$duplicate) { 703 $this->_elementIndex[$elementName] = $targetIdx; 704 } else { 705 $this->_duplicateIndex[$elementName][] = $targetIdx; 706 } 707 $element->onQuickFormEvent('updateValue', null, $this); 708 if ($this->_freezeAll) { 709 $element->freeze(); 710 } 711 // If not done, the elements will appear in reverse order 712 ksort($this->_elements); 713 return $element; 714 } 715 716 // }}} 717 // {{{ addGroup() 718 719 /** 720 * Adds an element group 721 * @param array $elements array of elements composing the group 722 * @param string $name (optional)group name 723 * @param string $groupLabel (optional)group label 724 * @param string $separator (optional)string to separate elements 725 * @param string $appendName (optional)specify whether the group name should be 726 * used in the form element name ex: group[element] 727 * @return object reference to added group of elements 728 * @since 2.8 729 * @access public 730 * @throws PEAR_Error 731 */ 732 function &addGroup($elements, $name=null, $groupLabel='', $separator=null, $appendName = true) 733 { 734 static $anonGroups = 1; 735 736 if (0 == strlen($name ?? '')) { 737 $name = 'qf_group_' . $anonGroups++; 738 $appendName = false; 739 } 740 $group =& $this->addElement('group', $name, $groupLabel, $elements, $separator, $appendName); 741 return $group; 742 } // end func addGroup 743 744 // }}} 745 // {{{ &getElement() 746 747 /** 748 * Returns a reference to the element 749 * 750 * @param string $element Element name 751 * @since 2.0 752 * @access public 753 * @return object reference to element 754 * @throws HTML_QuickForm_Error 755 */ 756 function &getElement($element) 757 { 758 if (isset($this->_elementIndex[$element])) { 759 return $this->_elements[$this->_elementIndex[$element]]; 760 } else { 761 $error = self::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$element' does not exist in HTML_QuickForm::getElement()", 'HTML_QuickForm_Error', true); 762 return $error; 763 } 764 } // end func getElement 765 766 // }}} 767 // {{{ &getElementValue() 768 769 /** 770 * Returns the element's raw value 771 * 772 * This returns the value as submitted by the form (not filtered) 773 * or set via setDefaults() or setConstants() 774 * 775 * @param string $element Element name 776 * @since 2.0 777 * @access public 778 * @return mixed element value 779 * @throws HTML_QuickForm_Error 780 */ 781 function &getElementValue($element) 782 { 783 if (!isset($this->_elementIndex[$element])) { 784 $error = self::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$element' does not exist in HTML_QuickForm::getElementValue()", 'HTML_QuickForm_Error', true); 785 return $error; 786 } 787 $value = $this->_elements[$this->_elementIndex[$element]]->getValue(); 788 if (isset($this->_duplicateIndex[$element])) { 789 foreach ($this->_duplicateIndex[$element] as $index) { 790 if (null !== ($v = $this->_elements[$index]->getValue())) { 791 if (is_array($value)) { 792 $value[] = $v; 793 } else { 794 $value = (null === $value)? $v: array($value, $v); 795 } 796 } 797 } 798 } 799 return $value; 800 } // end func getElementValue 801 802 // }}} 803 // {{{ getSubmitValue() 804 805 /** 806 * Returns the elements value after submit and filter 807 * 808 * @param string Element name 809 * @since 2.0 810 * @access public 811 * @return mixed submitted element value or null if not set 812 */ 813 function getSubmitValue($elementName) 814 { 815 $value = null; 816 $elementName = $elementName ?? ''; 817 if (isset($this->_submitValues[$elementName]) || isset($this->_submitFiles[$elementName])) { 818 $value = isset($this->_submitValues[$elementName])? $this->_submitValues[$elementName]: array(); 819 if (is_array($value) && isset($this->_submitFiles[$elementName])) { 820 foreach ($this->_submitFiles[$elementName] as $k => $v) { 821 $value = HTML_QuickForm::arrayMerge($value, $this->_reindexFiles($this->_submitFiles[$elementName][$k], $k)); 822 } 823 } 824 825 } elseif ('file' == $this->getElementType($elementName)) { 826 return $this->getElementValue($elementName); 827 828 } elseif (false !== ($pos = strpos($elementName, '['))) { 829 $base = substr($elementName, 0, $pos); 830 $keys = str_replace( 831 array('\\', '\'', ']', '['), array('\\\\', '\\\'', '', "']['"), 832 substr($elementName, $pos + 1, -1) 833 ); 834 $idx = "['" . $keys . "']"; 835 $keyArray = explode("']['", $keys); 836 837 if (isset($this->_submitValues[$base])) { 838 $value = HTML_QuickForm_utils::recursiveValue($this->_submitValues[$base], $keyArray, NULL); 839 } 840 841 if ((is_array($value) || null === $value) && isset($this->_submitFiles[$base])) { 842 $props = array('name', 'type', 'size', 'tmp_name', 'error'); 843 $code = "if (!isset(\$this->_submitFiles['{$base}']['name']{$idx})) {\n" . 844 " return null;\n" . 845 "} else {\n" . 846 " \$v = array();\n"; 847 foreach ($props as $prop) { 848 $code .= " \$v = HTML_QuickForm::arrayMerge(\$v, \$this->_reindexFiles(\$this->_submitFiles['{$base}']['{$prop}']{$idx}, '{$prop}'));\n"; 849 } 850 $fileValue = eval($code . " return \$v;\n}\n"); 851 if (null !== $fileValue) { 852 $value = null === $value? $fileValue: HTML_QuickForm::arrayMerge($value, $fileValue); 853 } 854 } 855 } 856 857 // This is only supposed to work for groups with appendName = false 858 if (null === $value && 'group' == $this->getElementType($elementName)) { 859 $group =& $this->getElement($elementName); 860 $elements =& $group->getElements(); 861 foreach (array_keys($elements) as $key) { 862 $name = $group->getElementName($key); 863 // prevent endless recursion in case of radios and such 864 if ($name != $elementName) { 865 if (null !== ($v = $this->getSubmitValue($name))) { 866 $value[$name] = $v; 867 } 868 } 869 } 870 } 871 return $value; 872 } // end func getSubmitValue 873 874 // }}} 875 // {{{ _reindexFiles() 876 877 /** 878 * A helper function to change the indexes in $_FILES array 879 * 880 * @param mixed Some value from the $_FILES array 881 * @param string The key from the $_FILES array that should be appended 882 * @return array 883 */ 884 function _reindexFiles($value, $key) 885 { 886 if (!is_array($value)) { 887 return array($key => $value); 888 } else { 889 $ret = array(); 890 foreach ($value as $k => $v) { 891 $ret[$k] = $this->_reindexFiles($v, $key); 892 } 893 return $ret; 894 } 895 } 896 897 // }}} 898 // {{{ getElementError() 899 900 /** 901 * Returns error corresponding to validated element 902 * 903 * @param string $element Name of form element to check 904 * @since 1.0 905 * @access public 906 * @return string error message corresponding to checked element 907 */ 908 function getElementError($element) 909 { 910 if (isset($this->_errors[$element])) { 911 return $this->_errors[$element]; 912 } 913 } // end func getElementError 914 915 // }}} 916 // {{{ setElementError() 917 918 /** 919 * Set error message for a form element 920 * 921 * @param string $element Name of form element to set error for 922 * @param string $message Error message, if empty then removes the current error message 923 * @since 1.0 924 * @access public 925 * @return void 926 */ 927 function setElementError($element, $message = null) 928 { 929 if (!empty($message)) { 930 $this->_errors[$element] = $message; 931 } else { 932 unset($this->_errors[$element]); 933 } 934 } // end func setElementError 935 936 // }}} 937 // {{{ getElementType() 938 939 /** 940 * Returns the type of the given element 941 * 942 * @param string $element Name of form element 943 * @since 1.1 944 * @access public 945 * @return string Type of the element, false if the element is not found 946 */ 947 function getElementType($element) 948 { 949 if (isset($this->_elementIndex[$element])) { 950 return $this->_elements[$this->_elementIndex[$element]]->getType(); 951 } 952 return false; 953 } // end func getElementType 954 955 // }}} 956 // {{{ updateElementAttr() 957 958 /** 959 * Updates Attributes for one or more elements 960 * 961 * @param mixed $elements Array of element names/objects or string of elements to be updated 962 * @param mixed $attrs Array or sting of html attributes 963 * @since 2.10 964 * @access public 965 * @return void 966 */ 967 function updateElementAttr($elements, $attrs) 968 { 969 if (is_string($elements)) { 970 $elements = preg_split('/[ ]?,[ ]?/', $elements); 971 } 972 foreach (array_keys($elements) as $key) { 973 if (is_object($elements[$key]) && is_a($elements[$key], 'HTML_QuickForm_element')) { 974 $elements[$key]->updateAttributes($attrs); 975 } elseif (isset($this->_elementIndex[$elements[$key]])) { 976 $this->_elements[$this->_elementIndex[$elements[$key]]]->updateAttributes($attrs); 977 if (isset($this->_duplicateIndex[$elements[$key]])) { 978 foreach ($this->_duplicateIndex[$elements[$key]] as $index) { 979 $this->_elements[$index]->updateAttributes($attrs); 980 } 981 } 982 } 983 } 984 } // end func updateElementAttr 985 986 // }}} 987 // {{{ removeElement() 988 989 /** 990 * Removes an element 991 * 992 * The method "unlinks" an element from the form, returning the reference 993 * to the element object. If several elements named $elementName exist, 994 * it removes the first one, leaving the others intact. 995 * 996 * @param string $elementName The element name 997 * @param boolean $removeRules True if rules for this element are to be removed too 998 * @access public 999 * @since 2.0 1000 * @return object HTML_QuickForm_element a reference to the removed element 1001 * @throws HTML_QuickForm_Error 1002 */ 1003 function &removeElement($elementName, $removeRules = true) 1004 { 1005 if (!isset($this->_elementIndex[$elementName])) { 1006 $error = self::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$elementName' does not exist in HTML_QuickForm::removeElement()", 'HTML_QuickForm_Error', true); 1007 return $error; 1008 } 1009 $el =& $this->_elements[$this->_elementIndex[$elementName]]; 1010 unset($this->_elements[$this->_elementIndex[$elementName]]); 1011 if (empty($this->_duplicateIndex[$elementName])) { 1012 unset($this->_elementIndex[$elementName]); 1013 } else { 1014 $this->_elementIndex[$elementName] = array_shift($this->_duplicateIndex[$elementName]); 1015 } 1016 if ($removeRules) { 1017 unset($this->_rules[$elementName], $this->_errors[$elementName]); 1018 } 1019 return $el; 1020 } // end func removeElement 1021 1022 // }}} 1023 // {{{ addRule() 1024 1025 /** 1026 * Adds a validation rule for the given field 1027 * 1028 * If the element is in fact a group, it will be considered as a whole. 1029 * To validate grouped elements as separated entities, 1030 * use addGroupRule instead of addRule. 1031 * 1032 * @param string $element Form element name 1033 * @param string $message Message to display for invalid data 1034 * @param string $type Rule type, use getRegisteredRules() to get types 1035 * @param string $format (optional)Required for extra rule data 1036 * @param string $validation (optional)Where to perform validation: "server", "client" 1037 * @param boolean $reset Client-side validation: reset the form element to its original value if there is an error? 1038 * @param boolean $force Force the rule to be applied, even if the target form element does not exist 1039 * @since 1.0 1040 * @access public 1041 * @throws HTML_QuickForm_Error 1042 */ 1043 function addRule($element, $message, $type, $format=null, $validation='server', $reset = false, $force = false) 1044 { 1045 if (!$force) { 1046 if (!is_array($element) && !$this->elementExists($element)) { 1047 return self::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$element' does not exist in HTML_QuickForm::addRule()", 'HTML_QuickForm_Error', true); 1048 } elseif (is_array($element)) { 1049 foreach ($element as $el) { 1050 if (!$this->elementExists($el)) { 1051 return self::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$el' does not exist in HTML_QuickForm::addRule()", 'HTML_QuickForm_Error', true); 1052 } 1053 } 1054 } 1055 } 1056 if (false === ($newName = $this->isRuleRegistered($type, true))) { 1057 return self::raiseError(null, QUICKFORM_INVALID_RULE, null, E_USER_WARNING, "Rule '$type' is not registered in HTML_QuickForm::addRule()", 'HTML_QuickForm_Error', true); 1058 } elseif (is_string($newName)) { 1059 $type = $newName; 1060 } 1061 if (is_array($element)) { 1062 $dependent = $element; 1063 $element = array_shift($dependent); 1064 } else { 1065 $dependent = null; 1066 } 1067 if ($type == 'required' || $type == 'uploadedfile') { 1068 $this->_required[] = $element; 1069 } 1070 if (!isset($this->_rules[$element])) { 1071 $this->_rules[$element] = array(); 1072 } 1073 $this->_rules[$element][] = array( 1074 'type' => $type, 1075 'format' => $format, 1076 'message' => $message, 1077 'validation' => $validation, 1078 'reset' => $reset, 1079 'dependent' => $dependent 1080 ); 1081 } // end func addRule 1082 1083 // }}} 1084 // {{{ addGroupRule() 1085 1086 /** 1087 * Adds a validation rule for the given group of elements 1088 * 1089 * Only groups with a name can be assigned a validation rule 1090 * Use addGroupRule when you need to validate elements inside the group. 1091 * Use addRule if you need to validate the group as a whole. In this case, 1092 * the same rule will be applied to all elements in the group. 1093 * Use addRule if you need to validate the group against a function. 1094 * 1095 * @param string $group Form group name 1096 * @param mixed $arg1 Array for multiple elements or error message string for one element 1097 * @param string $type (optional)Rule type use getRegisteredRules() to get types 1098 * @param string $format (optional)Required for extra rule data 1099 * @param int $howmany (optional)How many valid elements should be in the group 1100 * @param string $validation (optional)Where to perform validation: "server", "client" 1101 * @param bool $reset Client-side: whether to reset the element's value to its original state if validation failed. 1102 * @since 2.5 1103 * @access public 1104 * @throws HTML_QuickForm_Error 1105 */ 1106 function addGroupRule($group, $arg1, $type='', $format=null, $howmany=0, $validation = 'server', $reset = false) 1107 { 1108 if (!$this->elementExists($group)) { 1109 return self::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Group '$group' does not exist in HTML_QuickForm::addGroupRule()", 'HTML_QuickForm_Error', true); 1110 } 1111 1112 $groupObj =& $this->getElement($group); 1113 if (is_array($arg1)) { 1114 $required = 0; 1115 foreach ($arg1 as $elementIndex => $rules) { 1116 $elementName = $groupObj->getElementName($elementIndex); 1117 foreach ($rules as $rule) { 1118 $format = (isset($rule[2])) ? $rule[2] : null; 1119 $validation = (isset($rule[3]) && 'client' == $rule[3])? 'client': 'server'; 1120 $reset = isset($rule[4]) && $rule[4]; 1121 $type = $rule[1]; 1122 if (false === ($newName = $this->isRuleRegistered($type, true))) { 1123 return self::raiseError(null, QUICKFORM_INVALID_RULE, null, E_USER_WARNING, "Rule '$type' is not registered in HTML_QuickForm::addGroupRule()", 'HTML_QuickForm_Error', true); 1124 } elseif (is_string($newName)) { 1125 $type = $newName; 1126 } 1127 1128 $this->_rules[$elementName][] = array( 1129 'type' => $type, 1130 'format' => $format, 1131 'message' => $rule[0], 1132 'validation' => $validation, 1133 'reset' => $reset, 1134 'group' => $group); 1135 1136 if ('required' == $type || 'uploadedfile' == $type) { 1137 $groupObj->_required[] = $elementName; 1138 $this->_required[] = $elementName; 1139 $required++; 1140 } 1141 } 1142 } 1143 if ($required > 0 && count($groupObj->getElements()) == $required) { 1144 $this->_required[] = $group; 1145 } 1146 } elseif (is_string($arg1)) { 1147 if (false === ($newName = $this->isRuleRegistered($type, true))) { 1148 return self::raiseError(null, QUICKFORM_INVALID_RULE, null, E_USER_WARNING, "Rule '$type' is not registered in HTML_QuickForm::addGroupRule()", 'HTML_QuickForm_Error', true); 1149 } elseif (is_string($newName)) { 1150 $type = $newName; 1151 } 1152 1153 // addGroupRule() should also handle <select multiple> 1154 if (is_a($groupObj, 'html_quickform_group')) { 1155 // Radios need to be handled differently when required 1156 if ($type == 'required' && $groupObj->getGroupType() == 'radio') { 1157 $howmany = ($howmany == 0) ? 1 : $howmany; 1158 } else { 1159 $howmany = ($howmany == 0) ? count($groupObj->getElements()) : $howmany; 1160 } 1161 } 1162 1163 $this->_rules[$group][] = array('type' => $type, 1164 'format' => $format, 1165 'message' => $arg1, 1166 'validation' => $validation, 1167 'howmany' => $howmany, 1168 'reset' => $reset); 1169 if ($type == 'required') { 1170 $this->_required[] = $group; 1171 } 1172 } 1173 } // end func addGroupRule 1174 1175 // }}} 1176 // {{{ addFormRule() 1177 1178 /** 1179 * Adds a global validation rule 1180 * 1181 * This should be used when for a rule involving several fields or if 1182 * you want to use some completely custom validation for your form. 1183 * The rule function/method should return true in case of successful 1184 * validation and array('element name' => 'error') when there were errors. 1185 * 1186 * @access public 1187 * @param mixed Callback, either function name or array(&$object, 'method') 1188 * @throws HTML_QuickForm_Error 1189 */ 1190 function addFormRule($rule) 1191 { 1192 if (!is_callable($rule)) { 1193 return self::raiseError(null, QUICKFORM_INVALID_RULE, null, E_USER_WARNING, 'Callback function does not exist in HTML_QuickForm::addFormRule()', 'HTML_QuickForm_Error', true); 1194 } 1195 $this->_formRules[] = $rule; 1196 } 1197 1198 // }}} 1199 // {{{ applyFilter() 1200 1201 /** 1202 * Applies a data filter for the given field(s) 1203 * 1204 * @param mixed $element Form element name or array of such names 1205 * @param mixed $filter Callback, either function name or array(&$object, 'method') 1206 * @since 2.0 1207 * @access public 1208 */ 1209 function applyFilter($element, $filter) 1210 { 1211 if (!is_callable($filter)) { 1212 return self::raiseError(null, QUICKFORM_INVALID_FILTER, null, E_USER_WARNING, "Callback function does not exist in QuickForm::applyFilter()", 'HTML_QuickForm_Error', true); 1213 } 1214 if ($element == '__ALL__') { 1215 $this->_submitValues = $this->_recursiveFilter($filter, $this->_submitValues); 1216 } else { 1217 if (!is_array($element)) { 1218 $element = array($element); 1219 } 1220 foreach ($element as $elName) { 1221 $value = $this->getSubmitValue($elName); 1222 if (null !== $value) { 1223 if (false === strpos($elName, '[')) { 1224 $this->_submitValues[$elName] = $this->_recursiveFilter($filter, $value); 1225 } else { 1226 $idx = "['" . str_replace(array(']', '['), array('', "']['"), $elName) . "']"; 1227 eval("\$this->_submitValues{$idx} = \$this->_recursiveFilter(\$filter, \$value);"); 1228 } 1229 } 1230 } 1231 } 1232 } // end func applyFilter 1233 1234 // }}} 1235 // {{{ _recursiveFilter() 1236 1237 /** 1238 * Recursively apply a filter function 1239 * 1240 * @param string $filter filter to apply 1241 * @param mixed $value submitted values 1242 * @since 2.0 1243 * @access private 1244 * @return cleaned values 1245 */ 1246 function _recursiveFilter($filter, $value) 1247 { 1248 if (is_array($value)) { 1249 $cleanValues = array(); 1250 foreach ($value as $k => $v) { 1251 $cleanValues[$k] = $this->_recursiveFilter($filter, $v); 1252 } 1253 return $cleanValues; 1254 } else { 1255 return call_user_func($filter, $value); 1256 } 1257 } // end func _recursiveFilter 1258 1259 // }}} 1260 // {{{ arrayMerge() 1261 1262 /** 1263 * Merges two arrays 1264 * 1265 * Merges two array like the PHP function array_merge but recursively. 1266 * The main difference is that existing keys will not be renumbered 1267 * if they are integers. 1268 * 1269 * @access puplic 1270 * @param array $a original array 1271 * @param array $b array which will be merged into first one 1272 * @return array merged array 1273 */ 1274 static function arrayMerge($a, $b) 1275 { 1276 if (is_null($a)) {$a = array();} 1277 if (is_null($b)) {$b = array();} 1278 foreach ($b as $k => $v) { 1279 if (is_array($v)) { 1280 if (isset($a[$k]) && !is_array($a[$k])) { 1281 $a[$k] = $v; 1282 } else { 1283 if (!isset($a[$k])) { 1284 $a[$k] = array(); 1285 } 1286 $a[$k] = HTML_QuickForm::arrayMerge($a[$k], $v); 1287 } 1288 } else { 1289 $a[$k] = $v; 1290 } 1291 } 1292 return $a; 1293 } // end func arrayMerge 1294 1295 // }}} 1296 // {{{ isTypeRegistered() 1297 1298 /** 1299 * Returns whether or not the form element type is supported 1300 * 1301 * @param string $type Form element type 1302 * @since 1.0 1303 * @access public 1304 * @return boolean 1305 */ 1306 function isTypeRegistered($type) 1307 { 1308 return isset($GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES'][strtolower($type)]); 1309 } // end func isTypeRegistered 1310 1311 // }}} 1312 // {{{ getRegisteredTypes() 1313 1314 /** 1315 * Returns an array of registered element types 1316 * 1317 * @since 1.0 1318 * @access public 1319 * @return array 1320 */ 1321 function getRegisteredTypes() 1322 { 1323 return array_keys($GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES']); 1324 } // end func getRegisteredTypes 1325 1326 // }}} 1327 // {{{ isRuleRegistered() 1328 1329 /** 1330 * Returns whether or not the given rule is supported 1331 * 1332 * @param string $name Validation rule name 1333 * @param bool Whether to automatically register subclasses of HTML_QuickForm_Rule 1334 * @since 1.0 1335 * @access public 1336 * @return mixed true if previously registered, false if not, new rule name if auto-registering worked 1337 */ 1338 function isRuleRegistered($name, $autoRegister = false) 1339 { 1340 if (is_scalar($name) && isset($GLOBALS['_HTML_QuickForm_registered_rules'][$name])) { 1341 return true; 1342 } elseif (!$autoRegister) { 1343 return false; 1344 } 1345 // automatically register the rule if requested 1346 include_once 'HTML/QuickForm/RuleRegistry.php'; 1347 $ruleName = false; 1348 if (is_object($name) && is_a($name, 'html_quickform_rule')) { 1349 $ruleName = !empty($name->name)? $name->name: strtolower(get_class($name)); 1350 } elseif (is_string($name) && class_exists($name)) { 1351 $parent = strtolower($name); 1352 do { 1353 if ('html_quickform_rule' == strtolower($parent)) { 1354 $ruleName = strtolower($name); 1355 break; 1356 } 1357 } while ($parent = get_parent_class($parent)); 1358 } 1359 if ($ruleName) { 1360 $registry =& HTML_QuickForm_RuleRegistry::singleton(); 1361 $registry->registerRule($ruleName, null, $name); 1362 } 1363 return $ruleName; 1364 } // end func isRuleRegistered 1365 1366 // }}} 1367 // {{{ getRegisteredRules() 1368 1369 /** 1370 * Returns an array of registered validation rules 1371 * 1372 * @since 1.0 1373 * @access public 1374 * @return array 1375 */ 1376 function getRegisteredRules() 1377 { 1378 return array_keys($GLOBALS['_HTML_QuickForm_registered_rules']); 1379 } // end func getRegisteredRules 1380 1381 // }}} 1382 // {{{ isElementRequired() 1383 1384 /** 1385 * Returns whether or not the form element is required 1386 * 1387 * @param string $element Form element name 1388 * @since 1.0 1389 * @access public 1390 * @return boolean 1391 */ 1392 function isElementRequired($element) 1393 { 1394 return in_array($element, $this->_required, true); 1395 } // end func isElementRequired 1396 1397 // }}} 1398 // {{{ isElementFrozen() 1399 1400 /** 1401 * Returns whether or not the form element is frozen 1402 * 1403 * @param string $element Form element name 1404 * @since 1.0 1405 * @access public 1406 * @return boolean 1407 */ 1408 function isElementFrozen($element) 1409 { 1410 if (isset($this->_elementIndex[$element])) { 1411 return $this->_elements[$this->_elementIndex[$element]]->isFrozen(); 1412 } 1413 return false; 1414 } // end func isElementFrozen 1415 1416 // }}} 1417 // {{{ setJsWarnings() 1418 1419 /** 1420 * Sets JavaScript warning messages 1421 * 1422 * @param string $pref Prefix warning 1423 * @param string $post Postfix warning 1424 * @since 1.1 1425 * @access public 1426 * @return void 1427 */ 1428 function setJsWarnings($pref, $post) 1429 { 1430 $this->_jsPrefix = $pref; 1431 $this->_jsPostfix = $post; 1432 } // end func setJsWarnings 1433 1434 // }}} 1435 // {{{ setRequiredNote() 1436 1437 /** 1438 * Sets required-note 1439 * 1440 * @param string $note Message indicating some elements are required 1441 * @since 1.1 1442 * @access public 1443 * @return void 1444 */ 1445 function setRequiredNote($note) 1446 { 1447 $this->_requiredNote = $note; 1448 } // end func setRequiredNote 1449 1450 // }}} 1451 // {{{ getRequiredNote() 1452 1453 /** 1454 * Returns the required note 1455 * 1456 * @since 2.0 1457 * @access public 1458 * @return string 1459 */ 1460 function getRequiredNote() 1461 { 1462 return $this->_requiredNote; 1463 } // end func getRequiredNote 1464 1465 // }}} 1466 // {{{ validate() 1467 1468 /** 1469 * Performs the server side validation 1470 * @access public 1471 * @since 1.0 1472 * @return boolean true if no error found 1473 */ 1474 function validate() 1475 { 1476 if (count($this->_rules) == 0 && count($this->_formRules) == 0 && 1477 $this->isSubmitted()) { 1478 return (0 == count($this->_errors)); 1479 } elseif (!$this->isSubmitted()) { 1480 return false; 1481 } 1482 1483 include_once('HTML/QuickForm/RuleRegistry.php'); 1484 $registry =& HTML_QuickForm_RuleRegistry::singleton(); 1485 1486 foreach ($this->_rules as $target => $rules) { 1487 $submitValue = $this->getSubmitValue($target); 1488 1489 foreach ($rules as $rule) { 1490 if ((isset($rule['group']) && isset($this->_errors[$rule['group']])) || 1491 isset($this->_errors[$target])) { 1492 continue 2; 1493 } 1494 // If element is not required and is empty, we shouldn't validate it 1495 if (!$this->isElementRequired($target)) { 1496 if (!isset($submitValue) || '' == $submitValue) { 1497 continue 2; 1498 // Fix for bug #3501: we shouldn't validate not uploaded files, either. 1499 // Unfortunately, we can't just use $element->isUploadedFile() since 1500 // the element in question can be buried in group. Thus this hack. 1501 } elseif (is_array($submitValue)) { 1502 if (false === ($pos = strpos($target, '['))) { 1503 $isUpload = !empty($this->_submitFiles[$target]); 1504 } else { 1505 $base = substr($target, 0, $pos); 1506 $idx = "['" . str_replace(array(']', '['), array('', "']['"), substr($target, $pos + 1, -1)) . "']"; 1507 eval("\$isUpload = isset(\$this->_submitFiles['{$base}']['name']{$idx});"); 1508 } 1509 if ($isUpload && (!isset($submitValue['error']) || 0 != $submitValue['error'])) { 1510 continue 2; 1511 } 1512 } 1513 } 1514 if (isset($rule['dependent']) && is_array($rule['dependent'])) { 1515 $values = array($submitValue); 1516 foreach ($rule['dependent'] as $elName) { 1517 $values[] = $this->getSubmitValue($elName); 1518 } 1519 $result = $registry->validate($rule['type'], $values, $rule['format'], true); 1520 } elseif (is_array($submitValue) && !isset($rule['howmany'])) { 1521 $result = $registry->validate($rule['type'], $submitValue, $rule['format'], true); 1522 } else { 1523 $result = $registry->validate($rule['type'], $submitValue, $rule['format'], false); 1524 } 1525 1526 if (!$result || (!empty($rule['howmany']) && $rule['howmany'] > (int)$result)) { 1527 if (isset($rule['group'])) { 1528 $this->_errors[$rule['group']] = $rule['message']; 1529 } else { 1530 $this->_errors[$target] = $rule['message']; 1531 } 1532 } 1533 } 1534 } 1535 1536 // process the global rules now 1537 foreach ($this->_formRules as $rule) { 1538 if (true !== ($res = call_user_func($rule, $this->_submitValues, $this->_submitFiles))) { 1539 if (is_array($res)) { 1540 $this->_errors += $res; 1541 } else { 1542 return self::raiseError(null, QUICKFORM_ERROR, null, E_USER_WARNING, 'Form rule callback returned invalid value in HTML_QuickForm::validate()', 'HTML_QuickForm_Error', true); 1543 } 1544 } 1545 } 1546 1547 return (0 == count($this->_errors)); 1548 } // end func validate 1549 1550 // }}} 1551 // {{{ freeze() 1552 1553 /** 1554 * Displays elements without HTML input tags 1555 * 1556 * @param mixed $elementList array or string of element(s) to be frozen 1557 * @since 1.0 1558 * @access public 1559 * @throws HTML_QuickForm_Error 1560 */ 1561 function freeze($elementList=null) 1562 { 1563 if (!isset($elementList)) { 1564 $this->_freezeAll = true; 1565 $elementList = array(); 1566 } else { 1567 if (!is_array($elementList)) { 1568 $elementList = preg_split('/[ ]*,[ ]*/', $elementList); 1569 } 1570 $elementList = array_flip($elementList); 1571 } 1572 1573 foreach (array_keys($this->_elements) as $key) { 1574 $name = $this->_elements[$key]->getName(); 1575 if ($this->_freezeAll || isset($elementList[$name])) { 1576 $this->_elements[$key]->freeze(); 1577 unset($elementList[$name]); 1578 } 1579 } 1580 1581 if (!empty($elementList)) { 1582 return self::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Nonexistant element(s): '" . implode("', '", array_keys($elementList)) . "' in HTML_QuickForm::freeze()", 'HTML_QuickForm_Error', true); 1583 } 1584 return true; 1585 } // end func freeze 1586 1587 // }}} 1588 // {{{ isFrozen() 1589 1590 /** 1591 * Returns whether or not the whole form is frozen 1592 * 1593 * @since 3.0 1594 * @access public 1595 * @return boolean 1596 */ 1597 function isFrozen() 1598 { 1599 return $this->_freezeAll; 1600 } // end func isFrozen 1601 1602 // }}} 1603 // {{{ process() 1604 1605 /** 1606 * Performs the form data processing 1607 * 1608 * @param mixed $callback Callback, either function name or array(&$object, 'method') 1609 * @param bool $mergeFiles Whether uploaded files should be processed too 1610 * @since 1.0 1611 * @access public 1612 * @throws HTML_QuickForm_Error 1613 */ 1614 function process($callback, $mergeFiles = true) 1615 { 1616 if (!is_callable($callback)) { 1617 return self::raiseError(null, QUICKFORM_INVALID_PROCESS, null, E_USER_WARNING, "Callback function does not exist in QuickForm::process()", 'HTML_QuickForm_Error', true); 1618 } 1619 $values = ($mergeFiles === true) ? HTML_QuickForm::arrayMerge($this->_submitValues, $this->_submitFiles) : $this->_submitValues; 1620 return call_user_func($callback, $values); 1621 } // end func process 1622 1623 // }}} 1624 // {{{ accept() 1625 1626 /** 1627 * Accepts a renderer 1628 * 1629 * @param object An HTML_QuickForm_Renderer object 1630 * @since 3.0 1631 * @access public 1632 * @return void 1633 */ 1634 function accept(&$renderer) 1635 { 1636 $renderer->startForm($this); 1637 foreach (array_keys($this->_elements) as $key) { 1638 $element =& $this->_elements[$key]; 1639 $elementName = $element->getName(); 1640 $required = ($this->isElementRequired($elementName) && !$element->isFrozen()); 1641 $error = $this->getElementError($elementName); 1642 $element->accept($renderer, $required, $error); 1643 } 1644 $renderer->finishForm($this); 1645 } // end func accept 1646 1647 // }}} 1648 // {{{ defaultRenderer() 1649 1650 /** 1651 * Returns a reference to default renderer object 1652 * 1653 * @access public 1654 * @since 3.0 1655 * @return object a default renderer object 1656 */ 1657 function &defaultRenderer() 1658 { 1659 if (!isset($GLOBALS['_HTML_QuickForm_default_renderer'])) { 1660 include_once('HTML/QuickForm/Renderer/Default.php'); 1661 $GLOBALS['_HTML_QuickForm_default_renderer'] = new HTML_QuickForm_Renderer_Default(); //Moodle: PHP 5.3 compatibility 1662 } 1663 return $GLOBALS['_HTML_QuickForm_default_renderer']; 1664 } // end func defaultRenderer 1665 1666 // }}} 1667 // {{{ toHtml () 1668 1669 /** 1670 * Returns an HTML version of the form 1671 * 1672 * @param string $in_data (optional) Any extra data to insert right 1673 * before form is rendered. Useful when using templates. 1674 * 1675 * @return string Html version of the form 1676 * @since 1.0 1677 * @access public 1678 */ 1679 function toHtml ($in_data = null) 1680 { 1681 if (!is_null($in_data)) { 1682 $this->addElement('html', $in_data); 1683 } 1684 $renderer =& $this->defaultRenderer(); 1685 $this->accept($renderer); 1686 return $renderer->toHtml(); 1687 } // end func toHtml 1688 1689 // }}} 1690 // {{{ getValidationScript() 1691 1692 /** 1693 * Returns the client side validation script 1694 * 1695 * @since 2.0 1696 * @access public 1697 * @return string Javascript to perform validation, empty string if no 'client' rules were added 1698 */ 1699 function getValidationScript() 1700 { 1701 if (empty($this->_rules) || empty($this->_attributes['onsubmit'])) { 1702 return ''; 1703 } 1704 1705 include_once('HTML/QuickForm/RuleRegistry.php'); 1706 $registry =& HTML_QuickForm_RuleRegistry::singleton(); 1707 $test = array(); 1708 $js_escape = array( 1709 "\r" => '\r', 1710 "\n" => '\n', 1711 "\t" => '\t', 1712 "'" => "\\'", 1713 '"' => '\"', 1714 '\\' => '\\\\' 1715 ); 1716 1717 foreach ($this->_rules as $elementName => $rules) { 1718 foreach ($rules as $rule) { 1719 if ('client' == $rule['validation']) { 1720 unset($element); 1721 1722 $dependent = isset($rule['dependent']) && is_array($rule['dependent']); 1723 $rule['message'] = strtr($rule['message'], $js_escape); 1724 1725 if (isset($rule['group'])) { 1726 $group =& $this->getElement($rule['group']); 1727 // No JavaScript validation for frozen elements 1728 if ($group->isFrozen()) { 1729 continue 2; 1730 } 1731 $elements =& $group->getElements(); 1732 foreach (array_keys($elements) as $key) { 1733 if ($elementName == $group->getElementName($key)) { 1734 $element =& $elements[$key]; 1735 break; 1736 } 1737 } 1738 } elseif ($dependent) { 1739 $element = array(); 1740 $element[] =& $this->getElement($elementName); 1741 foreach ($rule['dependent'] as $elName) { 1742 $element[] =& $this->getElement($elName); 1743 } 1744 } else { 1745 $element =& $this->getElement($elementName); 1746 } 1747 // No JavaScript validation for frozen elements 1748 if (is_object($element) && $element->isFrozen()) { 1749 continue 2; 1750 } elseif (is_array($element)) { 1751 foreach (array_keys($element) as $key) { 1752 if ($element[$key]->isFrozen()) { 1753 continue 3; 1754 } 1755 } 1756 } 1757 1758 $test[] = $registry->getValidationScript($element, $elementName, $rule); 1759 } 1760 } 1761 } 1762 if (count($test) > 0) { 1763 return 1764 "\n<script type=\"text/javascript\">\n" . 1765 "//<![CDATA[\n" . 1766 "function validate_" . $this->_attributes['id'] . "(frm) {\n" . 1767 " var value = '';\n" . 1768 " var errFlag = new Array();\n" . 1769 " var _qfGroups = {};\n" . 1770 " _qfMsg = '';\n\n" . 1771 join("\n", $test) . 1772 "\n if (_qfMsg != '') {\n" . 1773 " _qfMsg = '" . strtr($this->_jsPrefix, $js_escape) . "' + _qfMsg;\n" . 1774 " _qfMsg = _qfMsg + '\\n" . strtr($this->_jsPostfix, $js_escape) . "';\n" . 1775 " alert(_qfMsg);\n" . 1776 " return false;\n" . 1777 " }\n" . 1778 " return true;\n" . 1779 "}\n" . 1780 "//]]>\n" . 1781 "</script>"; 1782 } 1783 return ''; 1784 } // end func getValidationScript 1785 1786 // }}} 1787 // {{{ getSubmitValues() 1788 1789 /** 1790 * Returns the values submitted by the form 1791 * 1792 * @since 2.0 1793 * @access public 1794 * @param bool Whether uploaded files should be returned too 1795 * @return array 1796 */ 1797 function getSubmitValues($mergeFiles = false) 1798 { 1799 return $mergeFiles? HTML_QuickForm::arrayMerge($this->_submitValues, $this->_submitFiles): $this->_submitValues; 1800 } // end func getSubmitValues 1801 1802 // }}} 1803 // {{{ toArray() 1804 1805 /** 1806 * Returns the form's contents in an array. 1807 * 1808 * The description of the array structure is in HTML_QuickForm_Renderer_Array docs 1809 * 1810 * @since 2.0 1811 * @access public 1812 * @param bool Whether to collect hidden elements (passed to the Renderer's constructor) 1813 * @return array of form contents 1814 */ 1815 function toArray($collectHidden = false) 1816 { 1817 include_once 'HTML/QuickForm/Renderer/Array.php'; 1818 $renderer = new HTML_QuickForm_Renderer_Array($collectHidden); //Moodle: PHP 5.3 compatibility 1819 $this->accept($renderer); 1820 return $renderer->toArray(); 1821 } // end func toArray 1822 1823 // }}} 1824 // {{{ exportValue() 1825 1826 /** 1827 * Returns a 'safe' element's value 1828 * 1829 * This method first tries to find a cleaned-up submitted value, 1830 * it will return a value set by setValue()/setDefaults()/setConstants() 1831 * if submitted value does not exist for the given element. 1832 * 1833 * @param string Name of an element 1834 * @access public 1835 * @return mixed 1836 */ 1837 function exportValue($element) 1838 { 1839 if (!isset($this->_elementIndex[$element])) { 1840 return self::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$element' does not exist in HTML_QuickForm::getElementValue()", 'HTML_QuickForm_Error', true); 1841 } 1842 $value = $this->_elements[$this->_elementIndex[$element]]->exportValue($this->_submitValues, false); 1843 if (isset($this->_duplicateIndex[$element])) { 1844 foreach ($this->_duplicateIndex[$element] as $index) { 1845 if (null !== ($v = $this->_elements[$index]->exportValue($this->_submitValues, false))) { 1846 if (is_array($value)) { 1847 $value[] = $v; 1848 } else { 1849 $value = (null === $value)? $v: array($value, $v); 1850 } 1851 } 1852 } 1853 } 1854 return $value; 1855 } 1856 1857 // }}} 1858 // {{{ exportValues() 1859 1860 /** 1861 * Returns 'safe' elements' values 1862 * 1863 * Unlike getSubmitValues(), this will return only the values 1864 * corresponding to the elements present in the form. 1865 * 1866 * @param mixed Array/string of element names, whose values we want. If not set then return all elements. 1867 * @access public 1868 * @return array An assoc array of elements' values 1869 * @throws HTML_QuickForm_Error 1870 */ 1871 function exportValues($elementList = null) 1872 { 1873 $values = array(); 1874 if (null === $elementList) { 1875 // iterate over all elements, calling their exportValue() methods 1876 foreach (array_keys($this->_elements) as $key) { 1877 $value = $this->_elements[$key]->exportValue($this->_submitValues, true); 1878 if (is_array($value)) { 1879 // This shit throws a bogus warning in PHP 4.3.x 1880 $values = HTML_QuickForm::arrayMerge($values, $value); 1881 } 1882 } 1883 } else { 1884 if (!is_array($elementList)) { 1885 $elementList = array_map('trim', explode(',', $elementList)); 1886 } 1887 foreach ($elementList as $elementName) { 1888 $value = $this->exportValue($elementName); 1889 $pear = new PEAR(); 1890 if ($pear->isError($value)) { 1891 return $value; 1892 } 1893 $values[$elementName] = $value; 1894 } 1895 } 1896 return $values; 1897 } 1898 1899 // }}} 1900 // {{{ isSubmitted() 1901 1902 /** 1903 * Tells whether the form was already submitted 1904 * 1905 * This is useful since the _submitFiles and _submitValues arrays 1906 * may be completely empty after the trackSubmit value is removed. 1907 * 1908 * @access public 1909 * @return bool 1910 */ 1911 function isSubmitted() 1912 { 1913 return $this->_flagSubmitted; 1914 } 1915 1916 1917 // }}} 1918 // {{{ isError() 1919 1920 /** 1921 * Tell whether a result from a QuickForm method is an error (an instance of HTML_QuickForm_Error) 1922 * 1923 * @access public 1924 * @param mixed result code 1925 * @return bool whether $value is an error 1926 */ 1927 static function isError($value) 1928 { 1929 return (is_object($value) && is_a($value, 'html_quickform_error')); 1930 } // end func isError 1931 1932 // }}} 1933 // {{{ errorMessage() 1934 1935 /** 1936 * Return a textual error message for an QuickForm error code 1937 * 1938 * @access public 1939 * @param int error code 1940 * @return string error message 1941 */ 1942 static function errorMessage($value) 1943 { 1944 // make the variable static so that it only has to do the defining on the first call 1945 static $errorMessages; 1946 1947 // define the varies error messages 1948 if (!isset($errorMessages)) { 1949 $errorMessages = array( 1950 QUICKFORM_OK => 'no error', 1951 QUICKFORM_ERROR => 'unknown error', 1952 QUICKFORM_INVALID_RULE => 'the rule does not exist as a registered rule', 1953 QUICKFORM_NONEXIST_ELEMENT => 'nonexistent html element', 1954 QUICKFORM_INVALID_FILTER => 'invalid filter', 1955 QUICKFORM_UNREGISTERED_ELEMENT => 'unregistered element', 1956 QUICKFORM_INVALID_ELEMENT_NAME => 'element already exists', 1957 QUICKFORM_INVALID_PROCESS => 'process callback does not exist', 1958 QUICKFORM_DEPRECATED => 'method is deprecated', 1959 QUICKFORM_INVALID_DATASOURCE => 'datasource is not an object' 1960 ); 1961 } 1962 1963 // If this is an error object, then grab the corresponding error code 1964 if (HTML_QuickForm::isError($value)) { 1965 $value = $value->getCode(); 1966 } 1967 1968 // return the textual error message corresponding to the code 1969 return isset($errorMessages[$value]) ? $errorMessages[$value] : $errorMessages[QUICKFORM_ERROR]; 1970 } // end func errorMessage 1971 1972 // }}} 1973 } // end class HTML_QuickForm 1974 1975 class HTML_QuickForm_Error extends PEAR_Error { 1976 1977 // {{{ properties 1978 1979 /** 1980 * Prefix for all error messages 1981 * @var string 1982 */ 1983 var $error_message_prefix = 'QuickForm Error: '; 1984 1985 // }}} 1986 // {{{ constructor 1987 1988 /** 1989 * Creates a quickform error object, extending the PEAR_Error class 1990 * 1991 * @param int $code the error code 1992 * @param int $mode the reaction to the error, either return, die or trigger/callback 1993 * @param int $level intensity of the error (PHP error code) 1994 * @param mixed $debuginfo any information that can inform user as to nature of the error 1995 */ 1996 public function __construct($code = QUICKFORM_ERROR, $mode = PEAR_ERROR_RETURN, 1997 $level = E_USER_NOTICE, $debuginfo = null) 1998 { 1999 if (is_int($code)) { 2000 parent::__construct(HTML_QuickForm::errorMessage($code), $code, $mode, $level, $debuginfo); 2001 } else { 2002 parent::__construct("Invalid error code: $code", QUICKFORM_ERROR, $mode, $level, $debuginfo); 2003 } 2004 } 2005 2006 /** 2007 * Old syntax of class constructor. Deprecated in PHP7. 2008 * 2009 * @deprecated since Moodle 3.1 2010 */ 2011 public function HTML_QuickForm_Error($code = QUICKFORM_ERROR, $mode = PEAR_ERROR_RETURN, 2012 $level = E_USER_NOTICE, $debuginfo = null) { 2013 debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER); 2014 self::__construct($code, $mode, $level, $debuginfo); 2015 } 2016 2017 // }}} 2018 } // end class HTML_QuickForm_Error 2019 ?>
title
Description
Body
title
Description
Body
title
Description
Body
title
Body