Differences Between: [Versions 311 and 401] [Versions 311 and 402] [Versions 311 and 403] [Versions 39 and 311]
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 if (isset($this->_submitValues[$elementName]) || isset($this->_submitFiles[$elementName])) { 817 $value = isset($this->_submitValues[$elementName])? $this->_submitValues[$elementName]: array(); 818 if (is_array($value) && isset($this->_submitFiles[$elementName])) { 819 foreach ($this->_submitFiles[$elementName] as $k => $v) { 820 $value = HTML_QuickForm::arrayMerge($value, $this->_reindexFiles($this->_submitFiles[$elementName][$k], $k)); 821 } 822 } 823 824 } elseif ('file' == $this->getElementType($elementName)) { 825 return $this->getElementValue($elementName); 826 827 } elseif (false !== ($pos = strpos($elementName, '['))) { 828 $base = substr($elementName, 0, $pos); 829 $keys = str_replace( 830 array('\\', '\'', ']', '['), array('\\\\', '\\\'', '', "']['"), 831 substr($elementName, $pos + 1, -1) 832 ); 833 $idx = "['" . $keys . "']"; 834 $keyArray = explode("']['", $keys); 835 836 if (isset($this->_submitValues[$base])) { 837 $value = HTML_QuickForm_utils::recursiveValue($this->_submitValues[$base], $keyArray, NULL); 838 } 839 840 if ((is_array($value) || null === $value) && isset($this->_submitFiles[$base])) { 841 $props = array('name', 'type', 'size', 'tmp_name', 'error'); 842 $code = "if (!isset(\$this->_submitFiles['{$base}']['name']{$idx})) {\n" . 843 " return null;\n" . 844 "} else {\n" . 845 " \$v = array();\n"; 846 foreach ($props as $prop) { 847 $code .= " \$v = HTML_QuickForm::arrayMerge(\$v, \$this->_reindexFiles(\$this->_submitFiles['{$base}']['{$prop}']{$idx}, '{$prop}'));\n"; 848 } 849 $fileValue = eval($code . " return \$v;\n}\n"); 850 if (null !== $fileValue) { 851 $value = null === $value? $fileValue: HTML_QuickForm::arrayMerge($value, $fileValue); 852 } 853 } 854 } 855 856 // This is only supposed to work for groups with appendName = false 857 if (null === $value && 'group' == $this->getElementType($elementName)) { 858 $group =& $this->getElement($elementName); 859 $elements =& $group->getElements(); 860 foreach (array_keys($elements) as $key) { 861 $name = $group->getElementName($key); 862 // prevent endless recursion in case of radios and such 863 if ($name != $elementName) { 864 if (null !== ($v = $this->getSubmitValue($name))) { 865 $value[$name] = $v; 866 } 867 } 868 } 869 } 870 return $value; 871 } // end func getSubmitValue 872 873 // }}} 874 // {{{ _reindexFiles() 875 876 /** 877 * A helper function to change the indexes in $_FILES array 878 * 879 * @param mixed Some value from the $_FILES array 880 * @param string The key from the $_FILES array that should be appended 881 * @return array 882 */ 883 function _reindexFiles($value, $key) 884 { 885 if (!is_array($value)) { 886 return array($key => $value); 887 } else { 888 $ret = array(); 889 foreach ($value as $k => $v) { 890 $ret[$k] = $this->_reindexFiles($v, $key); 891 } 892 return $ret; 893 } 894 } 895 896 // }}} 897 // {{{ getElementError() 898 899 /** 900 * Returns error corresponding to validated element 901 * 902 * @param string $element Name of form element to check 903 * @since 1.0 904 * @access public 905 * @return string error message corresponding to checked element 906 */ 907 function getElementError($element) 908 { 909 if (isset($this->_errors[$element])) { 910 return $this->_errors[$element]; 911 } 912 } // end func getElementError 913 914 // }}} 915 // {{{ setElementError() 916 917 /** 918 * Set error message for a form element 919 * 920 * @param string $element Name of form element to set error for 921 * @param string $message Error message, if empty then removes the current error message 922 * @since 1.0 923 * @access public 924 * @return void 925 */ 926 function setElementError($element, $message = null) 927 { 928 if (!empty($message)) { 929 $this->_errors[$element] = $message; 930 } else { 931 unset($this->_errors[$element]); 932 } 933 } // end func setElementError 934 935 // }}} 936 // {{{ getElementType() 937 938 /** 939 * Returns the type of the given element 940 * 941 * @param string $element Name of form element 942 * @since 1.1 943 * @access public 944 * @return string Type of the element, false if the element is not found 945 */ 946 function getElementType($element) 947 { 948 if (isset($this->_elementIndex[$element])) { 949 return $this->_elements[$this->_elementIndex[$element]]->getType(); 950 } 951 return false; 952 } // end func getElementType 953 954 // }}} 955 // {{{ updateElementAttr() 956 957 /** 958 * Updates Attributes for one or more elements 959 * 960 * @param mixed $elements Array of element names/objects or string of elements to be updated 961 * @param mixed $attrs Array or sting of html attributes 962 * @since 2.10 963 * @access public 964 * @return void 965 */ 966 function updateElementAttr($elements, $attrs) 967 { 968 if (is_string($elements)) { 969 $elements = preg_split('/[ ]?,[ ]?/', $elements); 970 } 971 foreach (array_keys($elements) as $key) { 972 if (is_object($elements[$key]) && is_a($elements[$key], 'HTML_QuickForm_element')) { 973 $elements[$key]->updateAttributes($attrs); 974 } elseif (isset($this->_elementIndex[$elements[$key]])) { 975 $this->_elements[$this->_elementIndex[$elements[$key]]]->updateAttributes($attrs); 976 if (isset($this->_duplicateIndex[$elements[$key]])) { 977 foreach ($this->_duplicateIndex[$elements[$key]] as $index) { 978 $this->_elements[$index]->updateAttributes($attrs); 979 } 980 } 981 } 982 } 983 } // end func updateElementAttr 984 985 // }}} 986 // {{{ removeElement() 987 988 /** 989 * Removes an element 990 * 991 * The method "unlinks" an element from the form, returning the reference 992 * to the element object. If several elements named $elementName exist, 993 * it removes the first one, leaving the others intact. 994 * 995 * @param string $elementName The element name 996 * @param boolean $removeRules True if rules for this element are to be removed too 997 * @access public 998 * @since 2.0 999 * @return object HTML_QuickForm_element a reference to the removed element 1000 * @throws HTML_QuickForm_Error 1001 */ 1002 function &removeElement($elementName, $removeRules = true) 1003 { 1004 if (!isset($this->_elementIndex[$elementName])) { 1005 $error = self::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$elementName' does not exist in HTML_QuickForm::removeElement()", 'HTML_QuickForm_Error', true); 1006 return $error; 1007 } 1008 $el =& $this->_elements[$this->_elementIndex[$elementName]]; 1009 unset($this->_elements[$this->_elementIndex[$elementName]]); 1010 if (empty($this->_duplicateIndex[$elementName])) { 1011 unset($this->_elementIndex[$elementName]); 1012 } else { 1013 $this->_elementIndex[$elementName] = array_shift($this->_duplicateIndex[$elementName]); 1014 } 1015 if ($removeRules) { 1016 unset($this->_rules[$elementName], $this->_errors[$elementName]); 1017 } 1018 return $el; 1019 } // end func removeElement 1020 1021 // }}} 1022 // {{{ addRule() 1023 1024 /** 1025 * Adds a validation rule for the given field 1026 * 1027 * If the element is in fact a group, it will be considered as a whole. 1028 * To validate grouped elements as separated entities, 1029 * use addGroupRule instead of addRule. 1030 * 1031 * @param string $element Form element name 1032 * @param string $message Message to display for invalid data 1033 * @param string $type Rule type, use getRegisteredRules() to get types 1034 * @param string $format (optional)Required for extra rule data 1035 * @param string $validation (optional)Where to perform validation: "server", "client" 1036 * @param boolean $reset Client-side validation: reset the form element to its original value if there is an error? 1037 * @param boolean $force Force the rule to be applied, even if the target form element does not exist 1038 * @since 1.0 1039 * @access public 1040 * @throws HTML_QuickForm_Error 1041 */ 1042 function addRule($element, $message, $type, $format=null, $validation='server', $reset = false, $force = false) 1043 { 1044 if (!$force) { 1045 if (!is_array($element) && !$this->elementExists($element)) { 1046 return self::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$element' does not exist in HTML_QuickForm::addRule()", 'HTML_QuickForm_Error', true); 1047 } elseif (is_array($element)) { 1048 foreach ($element as $el) { 1049 if (!$this->elementExists($el)) { 1050 return self::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$el' does not exist in HTML_QuickForm::addRule()", 'HTML_QuickForm_Error', true); 1051 } 1052 } 1053 } 1054 } 1055 if (false === ($newName = $this->isRuleRegistered($type, true))) { 1056 return self::raiseError(null, QUICKFORM_INVALID_RULE, null, E_USER_WARNING, "Rule '$type' is not registered in HTML_QuickForm::addRule()", 'HTML_QuickForm_Error', true); 1057 } elseif (is_string($newName)) { 1058 $type = $newName; 1059 } 1060 if (is_array($element)) { 1061 $dependent = $element; 1062 $element = array_shift($dependent); 1063 } else { 1064 $dependent = null; 1065 } 1066 if ($type == 'required' || $type == 'uploadedfile') { 1067 $this->_required[] = $element; 1068 } 1069 if (!isset($this->_rules[$element])) { 1070 $this->_rules[$element] = array(); 1071 } 1072 $this->_rules[$element][] = array( 1073 'type' => $type, 1074 'format' => $format, 1075 'message' => $message, 1076 'validation' => $validation, 1077 'reset' => $reset, 1078 'dependent' => $dependent 1079 ); 1080 } // end func addRule 1081 1082 // }}} 1083 // {{{ addGroupRule() 1084 1085 /** 1086 * Adds a validation rule for the given group of elements 1087 * 1088 * Only groups with a name can be assigned a validation rule 1089 * Use addGroupRule when you need to validate elements inside the group. 1090 * Use addRule if you need to validate the group as a whole. In this case, 1091 * the same rule will be applied to all elements in the group. 1092 * Use addRule if you need to validate the group against a function. 1093 * 1094 * @param string $group Form group name 1095 * @param mixed $arg1 Array for multiple elements or error message string for one element 1096 * @param string $type (optional)Rule type use getRegisteredRules() to get types 1097 * @param string $format (optional)Required for extra rule data 1098 * @param int $howmany (optional)How many valid elements should be in the group 1099 * @param string $validation (optional)Where to perform validation: "server", "client" 1100 * @param bool $reset Client-side: whether to reset the element's value to its original state if validation failed. 1101 * @since 2.5 1102 * @access public 1103 * @throws HTML_QuickForm_Error 1104 */ 1105 function addGroupRule($group, $arg1, $type='', $format=null, $howmany=0, $validation = 'server', $reset = false) 1106 { 1107 if (!$this->elementExists($group)) { 1108 return self::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Group '$group' does not exist in HTML_QuickForm::addGroupRule()", 'HTML_QuickForm_Error', true); 1109 } 1110 1111 $groupObj =& $this->getElement($group); 1112 if (is_array($arg1)) { 1113 $required = 0; 1114 foreach ($arg1 as $elementIndex => $rules) { 1115 $elementName = $groupObj->getElementName($elementIndex); 1116 foreach ($rules as $rule) { 1117 $format = (isset($rule[2])) ? $rule[2] : null; 1118 $validation = (isset($rule[3]) && 'client' == $rule[3])? 'client': 'server'; 1119 $reset = isset($rule[4]) && $rule[4]; 1120 $type = $rule[1]; 1121 if (false === ($newName = $this->isRuleRegistered($type, true))) { 1122 return self::raiseError(null, QUICKFORM_INVALID_RULE, null, E_USER_WARNING, "Rule '$type' is not registered in HTML_QuickForm::addGroupRule()", 'HTML_QuickForm_Error', true); 1123 } elseif (is_string($newName)) { 1124 $type = $newName; 1125 } 1126 1127 $this->_rules[$elementName][] = array( 1128 'type' => $type, 1129 'format' => $format, 1130 'message' => $rule[0], 1131 'validation' => $validation, 1132 'reset' => $reset, 1133 'group' => $group); 1134 1135 if ('required' == $type || 'uploadedfile' == $type) { 1136 $groupObj->_required[] = $elementName; 1137 $this->_required[] = $elementName; 1138 $required++; 1139 } 1140 } 1141 } 1142 if ($required > 0 && count($groupObj->getElements()) == $required) { 1143 $this->_required[] = $group; 1144 } 1145 } elseif (is_string($arg1)) { 1146 if (false === ($newName = $this->isRuleRegistered($type, true))) { 1147 return self::raiseError(null, QUICKFORM_INVALID_RULE, null, E_USER_WARNING, "Rule '$type' is not registered in HTML_QuickForm::addGroupRule()", 'HTML_QuickForm_Error', true); 1148 } elseif (is_string($newName)) { 1149 $type = $newName; 1150 } 1151 1152 // addGroupRule() should also handle <select multiple> 1153 if (is_a($groupObj, 'html_quickform_group')) { 1154 // Radios need to be handled differently when required 1155 if ($type == 'required' && $groupObj->getGroupType() == 'radio') { 1156 $howmany = ($howmany == 0) ? 1 : $howmany; 1157 } else { 1158 $howmany = ($howmany == 0) ? count($groupObj->getElements()) : $howmany; 1159 } 1160 } 1161 1162 $this->_rules[$group][] = array('type' => $type, 1163 'format' => $format, 1164 'message' => $arg1, 1165 'validation' => $validation, 1166 'howmany' => $howmany, 1167 'reset' => $reset); 1168 if ($type == 'required') { 1169 $this->_required[] = $group; 1170 } 1171 } 1172 } // end func addGroupRule 1173 1174 // }}} 1175 // {{{ addFormRule() 1176 1177 /** 1178 * Adds a global validation rule 1179 * 1180 * This should be used when for a rule involving several fields or if 1181 * you want to use some completely custom validation for your form. 1182 * The rule function/method should return true in case of successful 1183 * validation and array('element name' => 'error') when there were errors. 1184 * 1185 * @access public 1186 * @param mixed Callback, either function name or array(&$object, 'method') 1187 * @throws HTML_QuickForm_Error 1188 */ 1189 function addFormRule($rule) 1190 { 1191 if (!is_callable($rule)) { 1192 return self::raiseError(null, QUICKFORM_INVALID_RULE, null, E_USER_WARNING, 'Callback function does not exist in HTML_QuickForm::addFormRule()', 'HTML_QuickForm_Error', true); 1193 } 1194 $this->_formRules[] = $rule; 1195 } 1196 1197 // }}} 1198 // {{{ applyFilter() 1199 1200 /** 1201 * Applies a data filter for the given field(s) 1202 * 1203 * @param mixed $element Form element name or array of such names 1204 * @param mixed $filter Callback, either function name or array(&$object, 'method') 1205 * @since 2.0 1206 * @access public 1207 */ 1208 function applyFilter($element, $filter) 1209 { 1210 if (!is_callable($filter)) { 1211 return self::raiseError(null, QUICKFORM_INVALID_FILTER, null, E_USER_WARNING, "Callback function does not exist in QuickForm::applyFilter()", 'HTML_QuickForm_Error', true); 1212 } 1213 if ($element == '__ALL__') { 1214 $this->_submitValues = $this->_recursiveFilter($filter, $this->_submitValues); 1215 } else { 1216 if (!is_array($element)) { 1217 $element = array($element); 1218 } 1219 foreach ($element as $elName) { 1220 $value = $this->getSubmitValue($elName); 1221 if (null !== $value) { 1222 if (false === strpos($elName, '[')) { 1223 $this->_submitValues[$elName] = $this->_recursiveFilter($filter, $value); 1224 } else { 1225 $idx = "['" . str_replace(array(']', '['), array('', "']['"), $elName) . "']"; 1226 eval("\$this->_submitValues{$idx} = \$this->_recursiveFilter(\$filter, \$value);"); 1227 } 1228 } 1229 } 1230 } 1231 } // end func applyFilter 1232 1233 // }}} 1234 // {{{ _recursiveFilter() 1235 1236 /** 1237 * Recursively apply a filter function 1238 * 1239 * @param string $filter filter to apply 1240 * @param mixed $value submitted values 1241 * @since 2.0 1242 * @access private 1243 * @return cleaned values 1244 */ 1245 function _recursiveFilter($filter, $value) 1246 { 1247 if (is_array($value)) { 1248 $cleanValues = array(); 1249 foreach ($value as $k => $v) { 1250 $cleanValues[$k] = $this->_recursiveFilter($filter, $v); 1251 } 1252 return $cleanValues; 1253 } else { 1254 return call_user_func($filter, $value); 1255 } 1256 } // end func _recursiveFilter 1257 1258 // }}} 1259 // {{{ arrayMerge() 1260 1261 /** 1262 * Merges two arrays 1263 * 1264 * Merges two array like the PHP function array_merge but recursively. 1265 * The main difference is that existing keys will not be renumbered 1266 * if they are integers. 1267 * 1268 * @access puplic 1269 * @param array $a original array 1270 * @param array $b array which will be merged into first one 1271 * @return array merged array 1272 */ 1273 static function arrayMerge($a, $b) 1274 { 1275 if (is_null($a)) {$a = array();} 1276 if (is_null($b)) {$b = array();} 1277 foreach ($b as $k => $v) { 1278 if (is_array($v)) { 1279 if (isset($a[$k]) && !is_array($a[$k])) { 1280 $a[$k] = $v; 1281 } else { 1282 if (!isset($a[$k])) { 1283 $a[$k] = array(); 1284 } 1285 $a[$k] = HTML_QuickForm::arrayMerge($a[$k], $v); 1286 } 1287 } else { 1288 $a[$k] = $v; 1289 } 1290 } 1291 return $a; 1292 } // end func arrayMerge 1293 1294 // }}} 1295 // {{{ isTypeRegistered() 1296 1297 /** 1298 * Returns whether or not the form element type is supported 1299 * 1300 * @param string $type Form element type 1301 * @since 1.0 1302 * @access public 1303 * @return boolean 1304 */ 1305 function isTypeRegistered($type) 1306 { 1307 return isset($GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES'][strtolower($type)]); 1308 } // end func isTypeRegistered 1309 1310 // }}} 1311 // {{{ getRegisteredTypes() 1312 1313 /** 1314 * Returns an array of registered element types 1315 * 1316 * @since 1.0 1317 * @access public 1318 * @return array 1319 */ 1320 function getRegisteredTypes() 1321 { 1322 return array_keys($GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES']); 1323 } // end func getRegisteredTypes 1324 1325 // }}} 1326 // {{{ isRuleRegistered() 1327 1328 /** 1329 * Returns whether or not the given rule is supported 1330 * 1331 * @param string $name Validation rule name 1332 * @param bool Whether to automatically register subclasses of HTML_QuickForm_Rule 1333 * @since 1.0 1334 * @access public 1335 * @return mixed true if previously registered, false if not, new rule name if auto-registering worked 1336 */ 1337 function isRuleRegistered($name, $autoRegister = false) 1338 { 1339 if (is_scalar($name) && isset($GLOBALS['_HTML_QuickForm_registered_rules'][$name])) { 1340 return true; 1341 } elseif (!$autoRegister) { 1342 return false; 1343 } 1344 // automatically register the rule if requested 1345 include_once 'HTML/QuickForm/RuleRegistry.php'; 1346 $ruleName = false; 1347 if (is_object($name) && is_a($name, 'html_quickform_rule')) { 1348 $ruleName = !empty($name->name)? $name->name: strtolower(get_class($name)); 1349 } elseif (is_string($name) && class_exists($name)) { 1350 $parent = strtolower($name); 1351 do { 1352 if ('html_quickform_rule' == strtolower($parent)) { 1353 $ruleName = strtolower($name); 1354 break; 1355 } 1356 } while ($parent = get_parent_class($parent)); 1357 } 1358 if ($ruleName) { 1359 $registry =& HTML_QuickForm_RuleRegistry::singleton(); 1360 $registry->registerRule($ruleName, null, $name); 1361 } 1362 return $ruleName; 1363 } // end func isRuleRegistered 1364 1365 // }}} 1366 // {{{ getRegisteredRules() 1367 1368 /** 1369 * Returns an array of registered validation rules 1370 * 1371 * @since 1.0 1372 * @access public 1373 * @return array 1374 */ 1375 function getRegisteredRules() 1376 { 1377 return array_keys($GLOBALS['_HTML_QuickForm_registered_rules']); 1378 } // end func getRegisteredRules 1379 1380 // }}} 1381 // {{{ isElementRequired() 1382 1383 /** 1384 * Returns whether or not the form element is required 1385 * 1386 * @param string $element Form element name 1387 * @since 1.0 1388 * @access public 1389 * @return boolean 1390 */ 1391 function isElementRequired($element) 1392 { 1393 return in_array($element, $this->_required, true); 1394 } // end func isElementRequired 1395 1396 // }}} 1397 // {{{ isElementFrozen() 1398 1399 /** 1400 * Returns whether or not the form element is frozen 1401 * 1402 * @param string $element Form element name 1403 * @since 1.0 1404 * @access public 1405 * @return boolean 1406 */ 1407 function isElementFrozen($element) 1408 { 1409 if (isset($this->_elementIndex[$element])) { 1410 return $this->_elements[$this->_elementIndex[$element]]->isFrozen(); 1411 } 1412 return false; 1413 } // end func isElementFrozen 1414 1415 // }}} 1416 // {{{ setJsWarnings() 1417 1418 /** 1419 * Sets JavaScript warning messages 1420 * 1421 * @param string $pref Prefix warning 1422 * @param string $post Postfix warning 1423 * @since 1.1 1424 * @access public 1425 * @return void 1426 */ 1427 function setJsWarnings($pref, $post) 1428 { 1429 $this->_jsPrefix = $pref; 1430 $this->_jsPostfix = $post; 1431 } // end func setJsWarnings 1432 1433 // }}} 1434 // {{{ setRequiredNote() 1435 1436 /** 1437 * Sets required-note 1438 * 1439 * @param string $note Message indicating some elements are required 1440 * @since 1.1 1441 * @access public 1442 * @return void 1443 */ 1444 function setRequiredNote($note) 1445 { 1446 $this->_requiredNote = $note; 1447 } // end func setRequiredNote 1448 1449 // }}} 1450 // {{{ getRequiredNote() 1451 1452 /** 1453 * Returns the required note 1454 * 1455 * @since 2.0 1456 * @access public 1457 * @return string 1458 */ 1459 function getRequiredNote() 1460 { 1461 return $this->_requiredNote; 1462 } // end func getRequiredNote 1463 1464 // }}} 1465 // {{{ validate() 1466 1467 /** 1468 * Performs the server side validation 1469 * @access public 1470 * @since 1.0 1471 * @return boolean true if no error found 1472 */ 1473 function validate() 1474 { 1475 if (count($this->_rules) == 0 && count($this->_formRules) == 0 && 1476 $this->isSubmitted()) { 1477 return (0 == count($this->_errors)); 1478 } elseif (!$this->isSubmitted()) { 1479 return false; 1480 } 1481 1482 include_once('HTML/QuickForm/RuleRegistry.php'); 1483 $registry =& HTML_QuickForm_RuleRegistry::singleton(); 1484 1485 foreach ($this->_rules as $target => $rules) { 1486 $submitValue = $this->getSubmitValue($target); 1487 1488 foreach ($rules as $rule) { 1489 if ((isset($rule['group']) && isset($this->_errors[$rule['group']])) || 1490 isset($this->_errors[$target])) { 1491 continue 2; 1492 } 1493 // If element is not required and is empty, we shouldn't validate it 1494 if (!$this->isElementRequired($target)) { 1495 if (!isset($submitValue) || '' == $submitValue) { 1496 continue 2; 1497 // Fix for bug #3501: we shouldn't validate not uploaded files, either. 1498 // Unfortunately, we can't just use $element->isUploadedFile() since 1499 // the element in question can be buried in group. Thus this hack. 1500 } elseif (is_array($submitValue)) { 1501 if (false === ($pos = strpos($target, '['))) { 1502 $isUpload = !empty($this->_submitFiles[$target]); 1503 } else { 1504 $base = substr($target, 0, $pos); 1505 $idx = "['" . str_replace(array(']', '['), array('', "']['"), substr($target, $pos + 1, -1)) . "']"; 1506 eval("\$isUpload = isset(\$this->_submitFiles['{$base}']['name']{$idx});"); 1507 } 1508 if ($isUpload && (!isset($submitValue['error']) || 0 != $submitValue['error'])) { 1509 continue 2; 1510 } 1511 } 1512 } 1513 if (isset($rule['dependent']) && is_array($rule['dependent'])) { 1514 $values = array($submitValue); 1515 foreach ($rule['dependent'] as $elName) { 1516 $values[] = $this->getSubmitValue($elName); 1517 } 1518 $result = $registry->validate($rule['type'], $values, $rule['format'], true); 1519 } elseif (is_array($submitValue) && !isset($rule['howmany'])) { 1520 $result = $registry->validate($rule['type'], $submitValue, $rule['format'], true); 1521 } else { 1522 $result = $registry->validate($rule['type'], $submitValue, $rule['format'], false); 1523 } 1524 1525 if (!$result || (!empty($rule['howmany']) && $rule['howmany'] > (int)$result)) { 1526 if (isset($rule['group'])) { 1527 $this->_errors[$rule['group']] = $rule['message']; 1528 } else { 1529 $this->_errors[$target] = $rule['message']; 1530 } 1531 } 1532 } 1533 } 1534 1535 // process the global rules now 1536 foreach ($this->_formRules as $rule) { 1537 if (true !== ($res = call_user_func($rule, $this->_submitValues, $this->_submitFiles))) { 1538 if (is_array($res)) { 1539 $this->_errors += $res; 1540 } else { 1541 return self::raiseError(null, QUICKFORM_ERROR, null, E_USER_WARNING, 'Form rule callback returned invalid value in HTML_QuickForm::validate()', 'HTML_QuickForm_Error', true); 1542 } 1543 } 1544 } 1545 1546 return (0 == count($this->_errors)); 1547 } // end func validate 1548 1549 // }}} 1550 // {{{ freeze() 1551 1552 /** 1553 * Displays elements without HTML input tags 1554 * 1555 * @param mixed $elementList array or string of element(s) to be frozen 1556 * @since 1.0 1557 * @access public 1558 * @throws HTML_QuickForm_Error 1559 */ 1560 function freeze($elementList=null) 1561 { 1562 if (!isset($elementList)) { 1563 $this->_freezeAll = true; 1564 $elementList = array(); 1565 } else { 1566 if (!is_array($elementList)) { 1567 $elementList = preg_split('/[ ]*,[ ]*/', $elementList); 1568 } 1569 $elementList = array_flip($elementList); 1570 } 1571 1572 foreach (array_keys($this->_elements) as $key) { 1573 $name = $this->_elements[$key]->getName(); 1574 if ($this->_freezeAll || isset($elementList[$name])) { 1575 $this->_elements[$key]->freeze(); 1576 unset($elementList[$name]); 1577 } 1578 } 1579 1580 if (!empty($elementList)) { 1581 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); 1582 } 1583 return true; 1584 } // end func freeze 1585 1586 // }}} 1587 // {{{ isFrozen() 1588 1589 /** 1590 * Returns whether or not the whole form is frozen 1591 * 1592 * @since 3.0 1593 * @access public 1594 * @return boolean 1595 */ 1596 function isFrozen() 1597 { 1598 return $this->_freezeAll; 1599 } // end func isFrozen 1600 1601 // }}} 1602 // {{{ process() 1603 1604 /** 1605 * Performs the form data processing 1606 * 1607 * @param mixed $callback Callback, either function name or array(&$object, 'method') 1608 * @param bool $mergeFiles Whether uploaded files should be processed too 1609 * @since 1.0 1610 * @access public 1611 * @throws HTML_QuickForm_Error 1612 */ 1613 function process($callback, $mergeFiles = true) 1614 { 1615 if (!is_callable($callback)) { 1616 return self::raiseError(null, QUICKFORM_INVALID_PROCESS, null, E_USER_WARNING, "Callback function does not exist in QuickForm::process()", 'HTML_QuickForm_Error', true); 1617 } 1618 $values = ($mergeFiles === true) ? HTML_QuickForm::arrayMerge($this->_submitValues, $this->_submitFiles) : $this->_submitValues; 1619 return call_user_func($callback, $values); 1620 } // end func process 1621 1622 // }}} 1623 // {{{ accept() 1624 1625 /** 1626 * Accepts a renderer 1627 * 1628 * @param object An HTML_QuickForm_Renderer object 1629 * @since 3.0 1630 * @access public 1631 * @return void 1632 */ 1633 function accept(&$renderer) 1634 { 1635 $renderer->startForm($this); 1636 foreach (array_keys($this->_elements) as $key) { 1637 $element =& $this->_elements[$key]; 1638 $elementName = $element->getName(); 1639 $required = ($this->isElementRequired($elementName) && !$element->isFrozen()); 1640 $error = $this->getElementError($elementName); 1641 $element->accept($renderer, $required, $error); 1642 } 1643 $renderer->finishForm($this); 1644 } // end func accept 1645 1646 // }}} 1647 // {{{ defaultRenderer() 1648 1649 /** 1650 * Returns a reference to default renderer object 1651 * 1652 * @access public 1653 * @since 3.0 1654 * @return object a default renderer object 1655 */ 1656 function &defaultRenderer() 1657 { 1658 if (!isset($GLOBALS['_HTML_QuickForm_default_renderer'])) { 1659 include_once('HTML/QuickForm/Renderer/Default.php'); 1660 $GLOBALS['_HTML_QuickForm_default_renderer'] = new HTML_QuickForm_Renderer_Default(); //Moodle: PHP 5.3 compatibility 1661 } 1662 return $GLOBALS['_HTML_QuickForm_default_renderer']; 1663 } // end func defaultRenderer 1664 1665 // }}} 1666 // {{{ toHtml () 1667 1668 /** 1669 * Returns an HTML version of the form 1670 * 1671 * @param string $in_data (optional) Any extra data to insert right 1672 * before form is rendered. Useful when using templates. 1673 * 1674 * @return string Html version of the form 1675 * @since 1.0 1676 * @access public 1677 */ 1678 function toHtml ($in_data = null) 1679 { 1680 if (!is_null($in_data)) { 1681 $this->addElement('html', $in_data); 1682 } 1683 $renderer =& $this->defaultRenderer(); 1684 $this->accept($renderer); 1685 return $renderer->toHtml(); 1686 } // end func toHtml 1687 1688 // }}} 1689 // {{{ getValidationScript() 1690 1691 /** 1692 * Returns the client side validation script 1693 * 1694 * @since 2.0 1695 * @access public 1696 * @return string Javascript to perform validation, empty string if no 'client' rules were added 1697 */ 1698 function getValidationScript() 1699 { 1700 if (empty($this->_rules) || empty($this->_attributes['onsubmit'])) { 1701 return ''; 1702 } 1703 1704 include_once('HTML/QuickForm/RuleRegistry.php'); 1705 $registry =& HTML_QuickForm_RuleRegistry::singleton(); 1706 $test = array(); 1707 $js_escape = array( 1708 "\r" => '\r', 1709 "\n" => '\n', 1710 "\t" => '\t', 1711 "'" => "\\'", 1712 '"' => '\"', 1713 '\\' => '\\\\' 1714 ); 1715 1716 foreach ($this->_rules as $elementName => $rules) { 1717 foreach ($rules as $rule) { 1718 if ('client' == $rule['validation']) { 1719 unset($element); 1720 1721 $dependent = isset($rule['dependent']) && is_array($rule['dependent']); 1722 $rule['message'] = strtr($rule['message'], $js_escape); 1723 1724 if (isset($rule['group'])) { 1725 $group =& $this->getElement($rule['group']); 1726 // No JavaScript validation for frozen elements 1727 if ($group->isFrozen()) { 1728 continue 2; 1729 } 1730 $elements =& $group->getElements(); 1731 foreach (array_keys($elements) as $key) { 1732 if ($elementName == $group->getElementName($key)) { 1733 $element =& $elements[$key]; 1734 break; 1735 } 1736 } 1737 } elseif ($dependent) { 1738 $element = array(); 1739 $element[] =& $this->getElement($elementName); 1740 foreach ($rule['dependent'] as $elName) { 1741 $element[] =& $this->getElement($elName); 1742 } 1743 } else { 1744 $element =& $this->getElement($elementName); 1745 } 1746 // No JavaScript validation for frozen elements 1747 if (is_object($element) && $element->isFrozen()) { 1748 continue 2; 1749 } elseif (is_array($element)) { 1750 foreach (array_keys($element) as $key) { 1751 if ($element[$key]->isFrozen()) { 1752 continue 3; 1753 } 1754 } 1755 } 1756 1757 $test[] = $registry->getValidationScript($element, $elementName, $rule); 1758 } 1759 } 1760 } 1761 if (count($test) > 0) { 1762 return 1763 "\n<script type=\"text/javascript\">\n" . 1764 "//<![CDATA[\n" . 1765 "function validate_" . $this->_attributes['id'] . "(frm) {\n" . 1766 " var value = '';\n" . 1767 " var errFlag = new Array();\n" . 1768 " var _qfGroups = {};\n" . 1769 " _qfMsg = '';\n\n" . 1770 join("\n", $test) . 1771 "\n if (_qfMsg != '') {\n" . 1772 " _qfMsg = '" . strtr($this->_jsPrefix, $js_escape) . "' + _qfMsg;\n" . 1773 " _qfMsg = _qfMsg + '\\n" . strtr($this->_jsPostfix, $js_escape) . "';\n" . 1774 " alert(_qfMsg);\n" . 1775 " return false;\n" . 1776 " }\n" . 1777 " return true;\n" . 1778 "}\n" . 1779 "//]]>\n" . 1780 "</script>"; 1781 } 1782 return ''; 1783 } // end func getValidationScript 1784 1785 // }}} 1786 // {{{ getSubmitValues() 1787 1788 /** 1789 * Returns the values submitted by the form 1790 * 1791 * @since 2.0 1792 * @access public 1793 * @param bool Whether uploaded files should be returned too 1794 * @return array 1795 */ 1796 function getSubmitValues($mergeFiles = false) 1797 { 1798 return $mergeFiles? HTML_QuickForm::arrayMerge($this->_submitValues, $this->_submitFiles): $this->_submitValues; 1799 } // end func getSubmitValues 1800 1801 // }}} 1802 // {{{ toArray() 1803 1804 /** 1805 * Returns the form's contents in an array. 1806 * 1807 * The description of the array structure is in HTML_QuickForm_Renderer_Array docs 1808 * 1809 * @since 2.0 1810 * @access public 1811 * @param bool Whether to collect hidden elements (passed to the Renderer's constructor) 1812 * @return array of form contents 1813 */ 1814 function toArray($collectHidden = false) 1815 { 1816 include_once 'HTML/QuickForm/Renderer/Array.php'; 1817 $renderer = new HTML_QuickForm_Renderer_Array($collectHidden); //Moodle: PHP 5.3 compatibility 1818 $this->accept($renderer); 1819 return $renderer->toArray(); 1820 } // end func toArray 1821 1822 // }}} 1823 // {{{ exportValue() 1824 1825 /** 1826 * Returns a 'safe' element's value 1827 * 1828 * This method first tries to find a cleaned-up submitted value, 1829 * it will return a value set by setValue()/setDefaults()/setConstants() 1830 * if submitted value does not exist for the given element. 1831 * 1832 * @param string Name of an element 1833 * @access public 1834 * @return mixed 1835 */ 1836 function exportValue($element) 1837 { 1838 if (!isset($this->_elementIndex[$element])) { 1839 return self::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$element' does not exist in HTML_QuickForm::getElementValue()", 'HTML_QuickForm_Error', true); 1840 } 1841 $value = $this->_elements[$this->_elementIndex[$element]]->exportValue($this->_submitValues, false); 1842 if (isset($this->_duplicateIndex[$element])) { 1843 foreach ($this->_duplicateIndex[$element] as $index) { 1844 if (null !== ($v = $this->_elements[$index]->exportValue($this->_submitValues, false))) { 1845 if (is_array($value)) { 1846 $value[] = $v; 1847 } else { 1848 $value = (null === $value)? $v: array($value, $v); 1849 } 1850 } 1851 } 1852 } 1853 return $value; 1854 } 1855 1856 // }}} 1857 // {{{ exportValues() 1858 1859 /** 1860 * Returns 'safe' elements' values 1861 * 1862 * Unlike getSubmitValues(), this will return only the values 1863 * corresponding to the elements present in the form. 1864 * 1865 * @param mixed Array/string of element names, whose values we want. If not set then return all elements. 1866 * @access public 1867 * @return array An assoc array of elements' values 1868 * @throws HTML_QuickForm_Error 1869 */ 1870 function exportValues($elementList = null) 1871 { 1872 $values = array(); 1873 if (null === $elementList) { 1874 // iterate over all elements, calling their exportValue() methods 1875 foreach (array_keys($this->_elements) as $key) { 1876 $value = $this->_elements[$key]->exportValue($this->_submitValues, true); 1877 if (is_array($value)) { 1878 // This shit throws a bogus warning in PHP 4.3.x 1879 $values = HTML_QuickForm::arrayMerge($values, $value); 1880 } 1881 } 1882 } else { 1883 if (!is_array($elementList)) { 1884 $elementList = array_map('trim', explode(',', $elementList)); 1885 } 1886 foreach ($elementList as $elementName) { 1887 $value = $this->exportValue($elementName); 1888 $pear = new PEAR(); 1889 if ($pear->isError($value)) { 1890 return $value; 1891 } 1892 $values[$elementName] = $value; 1893 } 1894 } 1895 return $values; 1896 } 1897 1898 // }}} 1899 // {{{ isSubmitted() 1900 1901 /** 1902 * Tells whether the form was already submitted 1903 * 1904 * This is useful since the _submitFiles and _submitValues arrays 1905 * may be completely empty after the trackSubmit value is removed. 1906 * 1907 * @access public 1908 * @return bool 1909 */ 1910 function isSubmitted() 1911 { 1912 return $this->_flagSubmitted; 1913 } 1914 1915 1916 // }}} 1917 // {{{ isError() 1918 1919 /** 1920 * Tell whether a result from a QuickForm method is an error (an instance of HTML_QuickForm_Error) 1921 * 1922 * @access public 1923 * @param mixed result code 1924 * @return bool whether $value is an error 1925 */ 1926 static function isError($value) 1927 { 1928 return (is_object($value) && is_a($value, 'html_quickform_error')); 1929 } // end func isError 1930 1931 // }}} 1932 // {{{ errorMessage() 1933 1934 /** 1935 * Return a textual error message for an QuickForm error code 1936 * 1937 * @access public 1938 * @param int error code 1939 * @return string error message 1940 */ 1941 static function errorMessage($value) 1942 { 1943 // make the variable static so that it only has to do the defining on the first call 1944 static $errorMessages; 1945 1946 // define the varies error messages 1947 if (!isset($errorMessages)) { 1948 $errorMessages = array( 1949 QUICKFORM_OK => 'no error', 1950 QUICKFORM_ERROR => 'unknown error', 1951 QUICKFORM_INVALID_RULE => 'the rule does not exist as a registered rule', 1952 QUICKFORM_NONEXIST_ELEMENT => 'nonexistent html element', 1953 QUICKFORM_INVALID_FILTER => 'invalid filter', 1954 QUICKFORM_UNREGISTERED_ELEMENT => 'unregistered element', 1955 QUICKFORM_INVALID_ELEMENT_NAME => 'element already exists', 1956 QUICKFORM_INVALID_PROCESS => 'process callback does not exist', 1957 QUICKFORM_DEPRECATED => 'method is deprecated', 1958 QUICKFORM_INVALID_DATASOURCE => 'datasource is not an object' 1959 ); 1960 } 1961 1962 // If this is an error object, then grab the corresponding error code 1963 if (HTML_QuickForm::isError($value)) { 1964 $value = $value->getCode(); 1965 } 1966 1967 // return the textual error message corresponding to the code 1968 return isset($errorMessages[$value]) ? $errorMessages[$value] : $errorMessages[QUICKFORM_ERROR]; 1969 } // end func errorMessage 1970 1971 // }}} 1972 } // end class HTML_QuickForm 1973 1974 class HTML_QuickForm_Error extends PEAR_Error { 1975 1976 // {{{ properties 1977 1978 /** 1979 * Prefix for all error messages 1980 * @var string 1981 */ 1982 var $error_message_prefix = 'QuickForm Error: '; 1983 1984 // }}} 1985 // {{{ constructor 1986 1987 /** 1988 * Creates a quickform error object, extending the PEAR_Error class 1989 * 1990 * @param int $code the error code 1991 * @param int $mode the reaction to the error, either return, die or trigger/callback 1992 * @param int $level intensity of the error (PHP error code) 1993 * @param mixed $debuginfo any information that can inform user as to nature of the error 1994 */ 1995 public function __construct($code = QUICKFORM_ERROR, $mode = PEAR_ERROR_RETURN, 1996 $level = E_USER_NOTICE, $debuginfo = null) 1997 { 1998 if (is_int($code)) { 1999 parent::__construct(HTML_QuickForm::errorMessage($code), $code, $mode, $level, $debuginfo); 2000 } else { 2001 parent::__construct("Invalid error code: $code", QUICKFORM_ERROR, $mode, $level, $debuginfo); 2002 } 2003 } 2004 2005 /** 2006 * Old syntax of class constructor. Deprecated in PHP7. 2007 * 2008 * @deprecated since Moodle 3.1 2009 */ 2010 public function HTML_QuickForm_Error($code = QUICKFORM_ERROR, $mode = PEAR_ERROR_RETURN, 2011 $level = E_USER_NOTICE, $debuginfo = null) { 2012 debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER); 2013 self::__construct($code, $mode, $level, $debuginfo); 2014 } 2015 2016 // }}} 2017 } // end class HTML_QuickForm_Error 2018 ?>
title
Description
Body
title
Description
Body
title
Description
Body
title
Body