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