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