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