2 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
 
   5  * Create, validate and process HTML forms
 
   9  * LICENSE: This source file is subject to version 3.01 of the PHP license
 
  10  * that is available through the world-wide-web at the following URI:
 
  11  * http://www.php.net/license/3_01.txt If you did not receive a copy of
 
  12  * the PHP License and are unable to obtain it through the web, please
 
  13  * send a note to license@php.net so we can mail you a copy immediately.
 
  16  * @package     HTML_QuickForm
 
  17  * @author      Adam Daniel <adaniel1@eesus.jnj.com>
 
  18  * @author      Bertrand Mansion <bmansion@mamasam.com>
 
  19  * @author      Alexey Borzov <avb@php.net>
 
  20  * @copyright   2001-2011 The PHP Group
 
  21  * @license     http://www.php.net/license/3_01.txt PHP License 3.01
 
  23  * @link        http://pear.php.net/package/HTML_QuickForm
 
  27  * PEAR and PEAR_Error classes, for error handling
 
  29 require_once 'PEAR.php';
 
  31  * Base class for all HTML classes
 
  33 require_once 'HTML/Common.php';
 
  35  * Static utility methods
 
  37 require_once 'HTML/QuickForm/utils.php';
 
  40  * Element types known to HTML_QuickForm
 
  41  * @see HTML_QuickForm::registerElementType(), HTML_QuickForm::getRegisteredTypes(),
 
  42  *      HTML_QuickForm::isTypeRegistered()
 
  43  * @global array $GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES']
 
  45 $GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES'] =
 
  47             'group'         =>array('HTML/QuickForm/group.php','HTML_QuickForm_group'),
 
  48             'hidden'        =>array('HTML/QuickForm/hidden.php','HTML_QuickForm_hidden'),
 
  49             'reset'         =>array('HTML/QuickForm/reset.php','HTML_QuickForm_reset'),
 
  50             'checkbox'      =>array('HTML/QuickForm/checkbox.php','HTML_QuickForm_checkbox'),
 
  51             'file'          =>array('HTML/QuickForm/file.php','HTML_QuickForm_file'),
 
  52             'image'         =>array('HTML/QuickForm/image.php','HTML_QuickForm_image'),
 
  53             'password'      =>array('HTML/QuickForm/password.php','HTML_QuickForm_password'),
 
  54             'radio'         =>array('HTML/QuickForm/radio.php','HTML_QuickForm_radio'),
 
  55             'button'        =>array('HTML/QuickForm/button.php','HTML_QuickForm_button'),
 
  56             'submit'        =>array('HTML/QuickForm/submit.php','HTML_QuickForm_submit'),
 
  57             'select'        =>array('HTML/QuickForm/select.php','HTML_QuickForm_select'),
 
  58             'hiddenselect'  =>array('HTML/QuickForm/hiddenselect.php','HTML_QuickForm_hiddenselect'),
 
  59             'text'          =>array('HTML/QuickForm/text.php','HTML_QuickForm_text'),
 
  60             'textarea'      =>array('HTML/QuickForm/textarea.php','HTML_QuickForm_textarea'),
 
  61             'link'          =>array('HTML/QuickForm/link.php','HTML_QuickForm_link'),
 
  62             'advcheckbox'   =>array('HTML/QuickForm/advcheckbox.php','HTML_QuickForm_advcheckbox'),
 
  63             'date'          =>array('HTML/QuickForm/date.php','HTML_QuickForm_date'),
 
  64             'static'        =>array('HTML/QuickForm/static.php','HTML_QuickForm_static'),
 
  65             'header'        =>array('HTML/QuickForm/header.php', 'HTML_QuickForm_header'),
 
  66             'html'          =>array('HTML/QuickForm/html.php', 'HTML_QuickForm_html'),
 
  67             'hierselect'    =>array('HTML/QuickForm/hierselect.php', 'HTML_QuickForm_hierselect'),
 
  68             'autocomplete'  =>array('HTML/QuickForm/autocomplete.php', 'HTML_QuickForm_autocomplete'),
 
  69             'xbutton'       =>array('HTML/QuickForm/xbutton.php','HTML_QuickForm_xbutton')
 
  73  * Validation rules known to HTML_QuickForm
 
  74  * @see HTML_QuickForm::registerRule(), HTML_QuickForm::getRegisteredRules(),
 
  75  *      HTML_QuickForm::isRuleRegistered()
 
  76  * @global array $GLOBALS['_HTML_QuickForm_registered_rules']
 
  78 $GLOBALS['_HTML_QuickForm_registered_rules'] = array(
 
  79     'required'      => array('html_quickform_rule_required', 'HTML/QuickForm/Rule/Required.php'),
 
  80     'maxlength'     => array('html_quickform_rule_range',    'HTML/QuickForm/Rule/Range.php'),
 
  81     'minlength'     => array('html_quickform_rule_range',    'HTML/QuickForm/Rule/Range.php'),
 
  82     'rangelength'   => array('html_quickform_rule_range',    'HTML/QuickForm/Rule/Range.php'),
 
  83     'email'         => array('html_quickform_rule_email',    'HTML/QuickForm/Rule/Email.php'),
 
  84     'regex'         => array('html_quickform_rule_regex',    'HTML/QuickForm/Rule/Regex.php'),
 
  85     'lettersonly'   => array('html_quickform_rule_regex',    'HTML/QuickForm/Rule/Regex.php'),
 
  86     'alphanumeric'  => array('html_quickform_rule_regex',    'HTML/QuickForm/Rule/Regex.php'),
 
  87     'numeric'       => array('html_quickform_rule_regex',    'HTML/QuickForm/Rule/Regex.php'),
 
  88     'nopunctuation' => array('html_quickform_rule_regex',    'HTML/QuickForm/Rule/Regex.php'),
 
  89     'nonzero'       => array('html_quickform_rule_regex',    'HTML/QuickForm/Rule/Regex.php'),
 
  90     'callback'      => array('html_quickform_rule_callback', 'HTML/QuickForm/Rule/Callback.php'),
 
  91     'compare'       => array('html_quickform_rule_compare',  'HTML/QuickForm/Rule/Compare.php')
 
  97  * Error codes for HTML_QuickForm
 
  99  * Codes are mapped to textual messages by errorMessage() method, if you add a
 
 100  * new code be sure to add a new message for it to errorMessage()
 
 102  * @see HTML_QuickForm::errorMessage()
 
 104 define('QUICKFORM_OK',                      1);
 
 105 define('QUICKFORM_ERROR',                  -1);
 
 106 define('QUICKFORM_INVALID_RULE',           -2);
 
 107 define('QUICKFORM_NONEXIST_ELEMENT',       -3);
 
 108 define('QUICKFORM_INVALID_FILTER',         -4);
 
 109 define('QUICKFORM_UNREGISTERED_ELEMENT',   -5);
 
 110 define('QUICKFORM_INVALID_ELEMENT_NAME',   -6);
 
 111 define('QUICKFORM_INVALID_PROCESS',        -7);
 
 112 define('QUICKFORM_DEPRECATED',             -8);
 
 113 define('QUICKFORM_INVALID_DATASOURCE',     -9);
 
 119  * Create, validate and process HTML forms
 
 122  * @package     HTML_QuickForm
 
 123  * @author      Adam Daniel <adaniel1@eesus.jnj.com>
 
 124  * @author      Bertrand Mansion <bmansion@mamasam.com>
 
 125  * @author      Alexey Borzov <avb@php.net>
 
 126  * @version     Release: 3.2.16
 
 128 class HTML_QuickForm extends HTML_Common
 
 133      * Array containing the form fields
 
 138     var $_elements = array();
 
 141      * Array containing element name to index map
 
 146     var $_elementIndex = array();
 
 149      * Array containing indexes of duplicate elements
 
 154     var $_duplicateIndex = array();
 
 157      * Array containing required field IDs
 
 162     var $_required = array();
 
 165      * Prefix message in javascript alert if error
 
 170     var $_jsPrefix = 'Invalid information entered.';
 
 173      * Postfix message in javascript alert if error
 
 178     var $_jsPostfix = 'Please correct these fields.';
 
 181      * Datasource object implementing the informal
 
 182      * datasource protocol
 
 190      * Array of default form values
 
 195     var $_defaultValues = array();
 
 198      * Array of constant form values
 
 203     var $_constantValues = array();
 
 206      * Array of submitted form values
 
 211     var $_submitValues = array();
 
 214      * Array of submitted form files
 
 219     var $_submitFiles = array();
 
 222      * Value for maxfilesize hidden element if form contains file input
 
 227     var $_maxFileSize = 1048576; // 1 Mb = 1048576
 
 230      * Flag to know if all fields are frozen
 
 235     var $_freezeAll = false;
 
 238      * Array containing the form rules
 
 243     var $_rules = array();
 
 246      * Form rules, global variety
 
 250     var $_formRules = array();
 
 253      * Array containing the validation errors
 
 258     var $_errors = array();
 
 261      * Note for required fields in the form
 
 266     var $_requiredNote = '<span style="font-size:80%; color:#ff0000;">*</span><span style="font-size:80%;"> denotes required field</span>';
 
 269      * Whether the form was submitted
 
 273     var $_flagSubmitted = false;
 
 280      * @param    string      $formName          Form's name.
 
 281      * @param    string      $method            (optional)Form's method defaults to 'POST'
 
 282      * @param    string      $action            (optional)Form's action
 
 283      * @param    string      $target            (optional)Form's target defaults to '_self'
 
 284      * @param    mixed       $attributes        (optional)Extra attributes for <form> tag
 
 285      * @param    bool        $trackSubmit       (optional)Whether to track if the form was submitted by adding a special hidden field
 
 288     function HTML_QuickForm($formName='', $method='post', $action='', $target='', $attributes=null, $trackSubmit = false)
 
 290         HTML_Common::HTML_Common($attributes);
 
 291         $method = (strtoupper($method) == 'GET') ? 'get' : 'post';
 
 292         $action = ($action == '') ? $_SERVER['PHP_SELF'] : $action;
 
 293         $target = empty($target) ? array() : array('target' => $target);
 
 294         $attributes = array('action'=>$action, 'method'=>$method, 'name'=>$formName, 'id'=>$formName) + $target;
 
 295         $this->updateAttributes($attributes);
 
 296         if (!$trackSubmit || isset($_REQUEST['_qf__' . $formName])) {
 
 297             if (1 == get_magic_quotes_gpc()) {
 
 298                 $this->_submitValues = $this->_recursiveFilter('stripslashes', 'get' == $method? $_GET: $_POST);
 
 299                 foreach ($_FILES as $keyFirst => $valFirst) {
 
 300                     foreach ($valFirst as $keySecond => $valSecond) {
 
 301                         if ('name' == $keySecond) {
 
 302                             $this->_submitFiles[$keyFirst][$keySecond] = $this->_recursiveFilter('stripslashes', $valSecond);
 
 304                             $this->_submitFiles[$keyFirst][$keySecond] = $valSecond;
 
 309                 $this->_submitValues = 'get' == $method? $_GET: $_POST;
 
 310                 $this->_submitFiles  = $_FILES;
 
 312             $this->_flagSubmitted = count($this->_submitValues) > 0 || count($this->_submitFiles) > 0;
 
 315             unset($this->_submitValues['_qf__' . $formName]);
 
 316             $this->addElement('hidden', '_qf__' . $formName, null);
 
 318         if (preg_match('/^([0-9]+)([a-zA-Z]*)$/', ini_get('upload_max_filesize'), $matches)) {
 
 319             // see http://www.php.net/manual/en/faq.using.php#faq.using.shorthandbytes
 
 320             switch (strtoupper($matches['2'])) {
 
 322                     $this->_maxFileSize = $matches['1'] * 1073741824;
 
 325                     $this->_maxFileSize = $matches['1'] * 1048576;
 
 328                     $this->_maxFileSize = $matches['1'] * 1024;
 
 331                     $this->_maxFileSize = $matches['1'];
 
 340      * Returns the current API version
 
 346     function apiVersion()
 
 349     } // end func apiVersion
 
 352     // {{{ registerElementType()
 
 355      * Registers a new element type
 
 357      * @param     string    $typeName   Name of element type
 
 358      * @param     string    $include    Include path for element type
 
 359      * @param     string    $className  Element class name
 
 364     function registerElementType($typeName, $include, $className)
 
 366         $GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES'][strtolower($typeName)] = array($include, $className);
 
 367     } // end func registerElementType
 
 370     // {{{ registerRule()
 
 373      * Registers a new validation rule
 
 375      * @param     string    $ruleName   Name of validation rule
 
 376      * @param     string    $type       Either: 'regex', 'function' or 'rule' for an HTML_QuickForm_Rule object
 
 377      * @param     string    $data1      Name of function, regular expression or HTML_QuickForm_Rule classname
 
 378      * @param     string    $data2      Object parent of above function or HTML_QuickForm_Rule file path
 
 383     function registerRule($ruleName, $type, $data1, $data2 = null)
 
 385         include_once('HTML/QuickForm/RuleRegistry.php');
 
 386         $registry =& HTML_QuickForm_RuleRegistry::singleton();
 
 387         $registry->registerRule($ruleName, $type, $data1, $data2);
 
 388     } // end func registerRule
 
 391     // {{{ elementExists()
 
 394      * Returns true if element is in the form
 
 396      * @param     string   $element         form name of element to check
 
 401     function elementExists($element=null)
 
 403         return isset($this->_elementIndex[$element]);
 
 404     } // end func elementExists
 
 407     // {{{ setDatasource()
 
 410      * Sets a datasource object for this form object
 
 412      * Datasource default and constant values will feed the QuickForm object if
 
 413      * the datasource implements defaultValues() and constantValues() methods.
 
 415      * @param     object   $datasource          datasource object implementing the informal datasource protocol
 
 416      * @param     mixed    $defaultsFilter      string or array of filter(s) to apply to default values
 
 417      * @param     mixed    $constantsFilter     string or array of filter(s) to apply to constants values
 
 421      * @throws    HTML_QuickForm_Error
 
 423     function setDatasource(&$datasource, $defaultsFilter = null, $constantsFilter = null)
 
 425         if (is_object($datasource)) {
 
 426             $this->_datasource =& $datasource;
 
 427             if (is_callable(array($datasource, 'defaultValues'))) {
 
 428                 $this->setDefaults($datasource->defaultValues($this), $defaultsFilter);
 
 430             if (is_callable(array($datasource, 'constantValues'))) {
 
 431                 $this->setConstants($datasource->constantValues($this), $constantsFilter);
 
 434             return PEAR::raiseError(null, QUICKFORM_INVALID_DATASOURCE, null, E_USER_WARNING, "Datasource is not an object in QuickForm::setDatasource()", 'HTML_QuickForm_Error', true);
 
 436     } // end func setDatasource
 
 442      * Initializes default form values
 
 444      * @param     array    $defaultValues       values used to fill the form
 
 445      * @param     mixed    $filter              (optional) filter(s) to apply to all default values
 
 449      * @throws    HTML_QuickForm_Error
 
 451     function setDefaults($defaultValues = null, $filter = null)
 
 453         if (is_array($defaultValues)) {
 
 454             if (isset($filter)) {
 
 455                 if (is_array($filter) && (2 != count($filter) || !is_callable($filter))) {
 
 456                     foreach ($filter as $val) {
 
 457                         if (!is_callable($val)) {
 
 458                             return PEAR::raiseError(null, QUICKFORM_INVALID_FILTER, null, E_USER_WARNING, "Callback function does not exist in QuickForm::setDefaults()", 'HTML_QuickForm_Error', true);
 
 460                             $defaultValues = $this->_recursiveFilter($val, $defaultValues);
 
 463                 } elseif (!is_callable($filter)) {
 
 464                     return PEAR::raiseError(null, QUICKFORM_INVALID_FILTER, null, E_USER_WARNING, "Callback function does not exist in QuickForm::setDefaults()", 'HTML_QuickForm_Error', true);
 
 466                     $defaultValues = $this->_recursiveFilter($filter, $defaultValues);
 
 469             $this->_defaultValues = HTML_QuickForm::arrayMerge($this->_defaultValues, $defaultValues);
 
 470             foreach (array_keys($this->_elements) as $key) {
 
 471                 $this->_elements[$key]->onQuickFormEvent('updateValue', null, $this);
 
 474     } // end func setDefaults
 
 477     // {{{ setConstants()
 
 480      * Initializes constant form values.
 
 481      * These values won't get overridden by POST or GET vars
 
 483      * @param     array   $constantValues        values used to fill the form
 
 484      * @param     mixed    $filter              (optional) filter(s) to apply to all default values
 
 489      * @throws    HTML_QuickForm_Error
 
 491     function setConstants($constantValues = null, $filter = null)
 
 493         if (is_array($constantValues)) {
 
 494             if (isset($filter)) {
 
 495                 if (is_array($filter) && (2 != count($filter) || !is_callable($filter))) {
 
 496                     foreach ($filter as $val) {
 
 497                         if (!is_callable($val)) {
 
 498                             return PEAR::raiseError(null, QUICKFORM_INVALID_FILTER, null, E_USER_WARNING, "Callback function does not exist in QuickForm::setConstants()", 'HTML_QuickForm_Error', true);
 
 500                             $constantValues = $this->_recursiveFilter($val, $constantValues);
 
 503                 } elseif (!is_callable($filter)) {
 
 504                     return PEAR::raiseError(null, QUICKFORM_INVALID_FILTER, null, E_USER_WARNING, "Callback function does not exist in QuickForm::setConstants()", 'HTML_QuickForm_Error', true);
 
 506                     $constantValues = $this->_recursiveFilter($filter, $constantValues);
 
 509             $this->_constantValues = HTML_QuickForm::arrayMerge($this->_constantValues, $constantValues);
 
 510             foreach (array_keys($this->_elements) as $key) {
 
 511                 $this->_elements[$key]->onQuickFormEvent('updateValue', null, $this);
 
 514     } // end func setConstants
 
 517     // {{{ setMaxFileSize()
 
 520      * Sets the value of MAX_FILE_SIZE hidden element
 
 522      * @param     int    $bytes    Size in bytes
 
 527     function setMaxFileSize($bytes = 0)
 
 530             $this->_maxFileSize = $bytes;
 
 532         if (!$this->elementExists('MAX_FILE_SIZE')) {
 
 533             $this->addElement('hidden', 'MAX_FILE_SIZE', $this->_maxFileSize);
 
 535             $el =& $this->getElement('MAX_FILE_SIZE');
 
 536             $el->updateAttributes(array('value' => $this->_maxFileSize));
 
 538     } // end func setMaxFileSize
 
 541     // {{{ getMaxFileSize()
 
 544      * Returns the value of MAX_FILE_SIZE hidden element
 
 548      * @return    int   max file size in bytes
 
 550     function getMaxFileSize()
 
 552         return $this->_maxFileSize;
 
 553     } // end func getMaxFileSize
 
 556     // {{{ &createElement()
 
 559      * Creates a new form element of the given type.
 
 561      * This method accepts variable number of parameters, their
 
 562      * meaning and count depending on $elementType
 
 564      * @param     string     $elementType    type of element to add (text, textarea, file...)
 
 567      * @return    HTML_QuickForm_Element
 
 568      * @throws    HTML_QuickForm_Error
 
 570     function &createElement($elementType)
 
 572         $args    =  func_get_args();
 
 573         $element =& HTML_QuickForm::_loadElement('createElement', $elementType, array_slice($args, 1), null);
 
 575     } // end func createElement
 
 578     // {{{ _loadElement()
 
 581      * Returns a form element of the given type
 
 583      * @param     string   $event   event to send to newly created element ('createElement' or 'addElement')
 
 584      * @param     string   $type    element type
 
 585      * @param     array    $args    arguments for event
 
 588      * @return    HTML_QuickForm_Element
 
 589      * @throws    HTML_QuickForm_Error
 
 591     function &_loadElement($event, $type, $args, $form)
 
 593         $type = strtolower($type);
 
 594         if (!HTML_QuickForm::isTypeRegistered($type)) {
 
 595             $error = PEAR::raiseError(null, QUICKFORM_UNREGISTERED_ELEMENT, null, E_USER_WARNING, "Element '$type' does not exist in HTML_QuickForm::_loadElement()", 'HTML_QuickForm_Error', true);
 
 598         $className = $GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES'][$type][1];
 
 599         $includeFile = $GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES'][$type][0];
 
 600         include_once($includeFile);
 
 601         $elementObject = new $className();
 
 602         for ($i = 0; $i < 5; $i++) {
 
 603             if (!isset($args[$i])) {
 
 607         $err = $elementObject->onQuickFormEvent($event, $args, $form);
 
 611         return $elementObject;
 
 612     } // end func _loadElement
 
 618      * Adds an element into the form
 
 620      * If $element is a string representing element type, then this
 
 621      * method accepts variable number of parameters, their meaning
 
 622      * and count depending on $element
 
 624      * @param    mixed      $element        element object or type of element to add (text, textarea, file...)
 
 626      * @return   HTML_QuickForm_Element     a reference to newly added element
 
 628      * @throws   HTML_QuickForm_Error
 
 630     function &addElement($element)
 
 632         if (is_object($element) && is_subclass_of($element, 'html_quickform_element')) {
 
 633            $elementObject = &$element;
 
 634            $elementObject->onQuickFormEvent('updateValue', null, $this);
 
 636             $args = func_get_args();
 
 637             $elementObject =& $this->_loadElement('addElement', $element, array_slice($args, 1), $this);
 
 638             if (PEAR::isError($elementObject)) {
 
 639                 return $elementObject;
 
 642         $elementName = $elementObject->getName();
 
 644         // Add the element if it is not an incompatible duplicate
 
 645         if (!empty($elementName) && isset($this->_elementIndex[$elementName])) {
 
 646             if ($this->_elements[$this->_elementIndex[$elementName]]->getType() ==
 
 647                 $elementObject->getType()) {
 
 648                 $this->_elements[] =& $elementObject;
 
 649                 $elKeys = array_keys($this->_elements);
 
 650                 $this->_duplicateIndex[$elementName][] = end($elKeys);
 
 652                 $error = PEAR::raiseError(null, QUICKFORM_INVALID_ELEMENT_NAME, null, E_USER_WARNING, "Element '$elementName' already exists in HTML_QuickForm::addElement()", 'HTML_QuickForm_Error', true);
 
 656             $this->_elements[] =& $elementObject;
 
 657             $elKeys = array_keys($this->_elements);
 
 658             $this->_elementIndex[$elementName] = end($elKeys);
 
 660         if ($this->_freezeAll) {
 
 661             $elementObject->freeze();
 
 664         return $elementObject;
 
 665     } // end func addElement
 
 668     // {{{ insertElementBefore()
 
 671     * Inserts a new element right before the other element
 
 673     * Warning: it is not possible to check whether the $element is already
 
 674     * added to the form, therefore if you want to move the existing form
 
 675     * element to a new position, you'll have to use removeElement():
 
 676     * $form->insertElementBefore($form->removeElement('foo', false), 'bar');
 
 680     * @param    HTML_QuickForm_element  Element to insert
 
 681     * @param    string                  Name of the element before which the new
 
 683     * @return   HTML_QuickForm_element  reference to inserted element
 
 684     * @throws   HTML_QuickForm_Error
 
 686     function &insertElementBefore(&$element, $nameAfter)
 
 688         if (!empty($this->_duplicateIndex[$nameAfter])) {
 
 689             $error = PEAR::raiseError(null, QUICKFORM_INVALID_ELEMENT_NAME, null, E_USER_WARNING, 'Several elements named "' . $nameAfter . '" exist in HTML_QuickForm::insertElementBefore().', 'HTML_QuickForm_Error', true);
 
 691         } elseif (!$this->elementExists($nameAfter)) {
 
 692             $error = PEAR::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$nameAfter' does not exist in HTML_QuickForm::insertElementBefore()", 'HTML_QuickForm_Error', true);
 
 695         $elementName = $element->getName();
 
 696         $targetIdx   = $this->_elementIndex[$nameAfter];
 
 698         // Like in addElement(), check that it's not an incompatible duplicate
 
 699         if (!empty($elementName) && isset($this->_elementIndex[$elementName])) {
 
 700             if ($this->_elements[$this->_elementIndex[$elementName]]->getType() != $element->getType()) {
 
 701                 $error = PEAR::raiseError(null, QUICKFORM_INVALID_ELEMENT_NAME, null, E_USER_WARNING, "Element '$elementName' already exists in HTML_QuickForm::insertElementBefore()", 'HTML_QuickForm_Error', true);
 
 706         // Move all the elements after added back one place, reindex _elementIndex and/or _duplicateIndex
 
 707         $elKeys = array_keys($this->_elements);
 
 708         for ($i = end($elKeys); $i >= $targetIdx; $i--) {
 
 709             if (isset($this->_elements[$i])) {
 
 710                 $currentName = $this->_elements[$i]->getName();
 
 711                 $this->_elements[$i + 1] =& $this->_elements[$i];
 
 712                 if ($this->_elementIndex[$currentName] == $i) {
 
 713                     $this->_elementIndex[$currentName] = $i + 1;
 
 715                     $dupIdx = array_search($i, $this->_duplicateIndex[$currentName]);
 
 716                     $this->_duplicateIndex[$currentName][$dupIdx] = $i + 1;
 
 718                 unset($this->_elements[$i]);
 
 721         // Put the element in place finally
 
 722         $this->_elements[$targetIdx] =& $element;
 
 724             $this->_elementIndex[$elementName] = $targetIdx;
 
 726             $this->_duplicateIndex[$elementName][] = $targetIdx;
 
 728         $element->onQuickFormEvent('updateValue', null, $this);
 
 729         if ($this->_freezeAll) {
 
 732         // If not done, the elements will appear in reverse order
 
 733         ksort($this->_elements);
 
 741      * Adds an element group
 
 742      * @param    array      $elements       array of elements composing the group
 
 743      * @param    string     $name           (optional)group name
 
 744      * @param    string     $groupLabel     (optional)group label
 
 745      * @param    string     $separator      (optional)string to separate elements
 
 746      * @param    string     $appendName     (optional)specify whether the group name should be
 
 747      *                                      used in the form element name ex: group[element]
 
 748      * @return   HTML_QuickForm_group       reference to a newly added group
 
 751      * @throws   HTML_QuickForm_Error
 
 753     function &addGroup($elements, $name=null, $groupLabel='', $separator=null, $appendName = true)
 
 755         static $anonGroups = 1;
 
 757         if (0 == strlen($name)) {
 
 758             $name       = 'qf_group_' . $anonGroups++;
 
 761         $group =& $this->addElement('group', $name, $groupLabel, $elements, $separator, $appendName);
 
 763     } // end func addGroup
 
 769      * Returns a reference to the element
 
 771      * @param     string     $element    Element name
 
 774      * @return    HTML_QuickForm_element    reference to element
 
 775      * @throws    HTML_QuickForm_Error
 
 777     function &getElement($element)
 
 779         if (isset($this->_elementIndex[$element])) {
 
 780             return $this->_elements[$this->_elementIndex[$element]];
 
 782             $error = PEAR::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$element' does not exist in HTML_QuickForm::getElement()", 'HTML_QuickForm_Error', true);
 
 785     } // end func getElement
 
 788     // {{{ &getElementValue()
 
 791      * Returns the element's raw value
 
 793      * This returns the value as submitted by the form (not filtered)
 
 794      * or set via setDefaults() or setConstants()
 
 796      * @param     string     $element    Element name
 
 799      * @return    mixed     element value
 
 800      * @throws    HTML_QuickForm_Error
 
 802     function &getElementValue($element)
 
 804         if (!isset($this->_elementIndex[$element])) {
 
 805             $error = PEAR::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$element' does not exist in HTML_QuickForm::getElementValue()", 'HTML_QuickForm_Error', true);
 
 808         $value = $this->_elements[$this->_elementIndex[$element]]->getValue();
 
 809         if (isset($this->_duplicateIndex[$element])) {
 
 810             foreach ($this->_duplicateIndex[$element] as $index) {
 
 811                 if (null !== ($v = $this->_elements[$index]->getValue())) {
 
 812                     if (is_array($value)) {
 
 815                         $value = (null === $value)? $v: array($value, $v);
 
 821     } // end func getElementValue
 
 824     // {{{ getSubmitValue()
 
 827      * Returns the elements value after submit and filter
 
 829      * @param     string     Element name
 
 832      * @return    mixed     submitted element value or null if not set
 
 834     function getSubmitValue($elementName)
 
 837         if (isset($this->_submitValues[$elementName]) || isset($this->_submitFiles[$elementName])) {
 
 838             $value = isset($this->_submitValues[$elementName])? $this->_submitValues[$elementName]: array();
 
 839             if (is_array($value) && isset($this->_submitFiles[$elementName])) {
 
 840                 foreach ($this->_submitFiles[$elementName] as $k => $v) {
 
 841                     $value = HTML_QuickForm::arrayMerge($value, $this->_reindexFiles($this->_submitFiles[$elementName][$k], $k));
 
 845         } elseif ('file' == $this->getElementType($elementName)) {
 
 846             return $this->getElementValue($elementName);
 
 848         } elseif (false !== ($pos = strpos($elementName, '['))) {
 
 850                         array('\\', '\''), array('\\\\', '\\\''),
 
 851                         substr($elementName, 0, $pos)
 
 855                 array('\\', '\'', ']', '['), array('\\\\', '\\\'', '', "']['"),
 
 856                 substr($elementName, $pos + 1, -1)
 
 858             $idx  = "['" . $keys . "']";
 
 859             $keyArray = explode("']['", $keys);
 
 861             if (isset($this->_submitValues[$base])) {
 
 862                 $value = HTML_QuickForm_utils::recursiveValue($this->_submitValues[$base], $keyArray, NULL);
 
 865             if ((is_array($value) || null === $value) && isset($this->_submitFiles[$base])) {
 
 866                 $props = array('name', 'type', 'size', 'tmp_name', 'error');
 
 867                 $code  = "if (!isset(\$this->_submitFiles['{$base}']['name']{$idx})) {\n" .
 
 871                 foreach ($props as $prop) {
 
 872                     $code .= "    \$v = HTML_QuickForm::arrayMerge(\$v, \$this->_reindexFiles(\$this->_submitFiles['{$base}']['{$prop}']{$idx}, '{$prop}'));\n";
 
 874                 $fileValue = eval($code . "    return \$v;\n}\n");
 
 875                 if (null !== $fileValue) {
 
 876                     $value = null === $value? $fileValue: HTML_QuickForm::arrayMerge($value, $fileValue);
 
 881         // This is only supposed to work for groups with appendName = false
 
 882         if (null === $value && 'group' == $this->getElementType($elementName)) {
 
 883             $group    =& $this->getElement($elementName);
 
 884             $elements =& $group->getElements();
 
 885             foreach (array_keys($elements) as $key) {
 
 886                 $name = $group->getElementName($key);
 
 887                 // prevent endless recursion in case of radios and such
 
 888                 if ($name != $elementName) {
 
 889                     if (null !== ($v = $this->getSubmitValue($name))) {
 
 896     } // end func getSubmitValue
 
 899     // {{{ _reindexFiles()
 
 902     * A helper function to change the indexes in $_FILES array
 
 904     * @param  mixed   Some value from the $_FILES array
 
 905     * @param  string  The key from the $_FILES array that should be appended
 
 908     function _reindexFiles($value, $key)
 
 910         if (!is_array($value)) {
 
 911             return array($key => $value);
 
 914             foreach ($value as $k => $v) {
 
 915                 $ret[$k] = $this->_reindexFiles($v, $key);
 
 922     // {{{ getElementError()
 
 925      * Returns error corresponding to validated element
 
 927      * @param     string    $element        Name of form element to check
 
 930      * @return    string    error message corresponding to checked element
 
 932     function getElementError($element)
 
 934         if (isset($this->_errors[$element])) {
 
 935             return $this->_errors[$element];
 
 937     } // end func getElementError
 
 940     // {{{ setElementError()
 
 943      * Set error message for a form element
 
 945      * @param     string    $element    Name of form element to set error for
 
 946      * @param     string    $message    Error message, if empty then removes the current error message
 
 951     function setElementError($element, $message = null)
 
 953         if (!empty($message)) {
 
 954             $this->_errors[$element] = $message;
 
 956             unset($this->_errors[$element]);
 
 958     } // end func setElementError
 
 961      // {{{ getElementType()
 
 964       * Returns the type of the given element
 
 966       * @param      string    $element    Name of form element
 
 969       * @return     string    Type of the element, false if the element is not found
 
 971      function getElementType($element)
 
 973          if (isset($this->_elementIndex[$element])) {
 
 974              return $this->_elements[$this->_elementIndex[$element]]->getType();
 
 977      } // end func getElementType
 
 980      // {{{ updateElementAttr()
 
 983      * Updates Attributes for one or more elements
 
 985      * @param      mixed    $elements   Array of element names/objects or string of elements to be updated
 
 986      * @param      mixed    $attrs      Array or sting of html attributes
 
 991     function updateElementAttr($elements, $attrs)
 
 993         if (is_string($elements)) {
 
 994             $elements = preg_split('/[ ]?,[ ]?/', $elements);
 
 996         foreach (array_keys($elements) as $key) {
 
 997             if (is_object($elements[$key]) && is_a($elements[$key], 'HTML_QuickForm_element')) {
 
 998                 $elements[$key]->updateAttributes($attrs);
 
 999             } elseif (isset($this->_elementIndex[$elements[$key]])) {
 
1000                 $this->_elements[$this->_elementIndex[$elements[$key]]]->updateAttributes($attrs);
 
1001                 if (isset($this->_duplicateIndex[$elements[$key]])) {
 
1002                     foreach ($this->_duplicateIndex[$elements[$key]] as $index) {
 
1003                         $this->_elements[$index]->updateAttributes($attrs);
 
1008     } // end func updateElementAttr
 
1011     // {{{ removeElement()
 
1014      * Removes an element
 
1016      * The method "unlinks" an element from the form, returning the reference
 
1017      * to the element object. If several elements named $elementName exist,
 
1018      * it removes the first one, leaving the others intact.
 
1020      * @param string    $elementName The element name
 
1021      * @param boolean   $removeRules True if rules for this element are to be removed too
 
1024      * @return HTML_QuickForm_element    a reference to the removed element
 
1025      * @throws HTML_QuickForm_Error
 
1027     function &removeElement($elementName, $removeRules = true)
 
1029         if (!isset($this->_elementIndex[$elementName])) {
 
1030             $error = PEAR::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$elementName' does not exist in HTML_QuickForm::removeElement()", 'HTML_QuickForm_Error', true);
 
1033         $el =& $this->_elements[$this->_elementIndex[$elementName]];
 
1034         unset($this->_elements[$this->_elementIndex[$elementName]]);
 
1035         if (empty($this->_duplicateIndex[$elementName])) {
 
1036             unset($this->_elementIndex[$elementName]);
 
1038             $this->_elementIndex[$elementName] = array_shift($this->_duplicateIndex[$elementName]);
 
1041             $this->_required = array_diff($this->_required, array($elementName));
 
1042             unset($this->_rules[$elementName], $this->_errors[$elementName]);
 
1043             if ('group' == $el->getType()) {
 
1044                 foreach (array_keys($el->getElements()) as $key) {
 
1045                     unset($this->_rules[$el->getElementName($key)]);
 
1050     } // end func removeElement
 
1056      * Adds a validation rule for the given field
 
1058      * If the element is in fact a group, it will be considered as a whole.
 
1059      * To validate grouped elements as separated entities,
 
1060      * use addGroupRule instead of addRule.
 
1062      * @param    string     $element       Form element name
 
1063      * @param    string     $message       Message to display for invalid data
 
1064      * @param    string     $type          Rule type, use getRegisteredRules() to get types
 
1065      * @param    string     $format        (optional)Required for extra rule data
 
1066      * @param    string     $validation    (optional)Where to perform validation: "server", "client"
 
1067      * @param    boolean    $reset         Client-side validation: reset the form element to its original value if there is an error?
 
1068      * @param    boolean    $force         Force the rule to be applied, even if the target form element does not exist
 
1071      * @throws   HTML_QuickForm_Error
 
1073     function addRule($element, $message, $type, $format=null, $validation='server', $reset = false, $force = false)
 
1076             if (!is_array($element) && !$this->elementExists($element)) {
 
1077                 return PEAR::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$element' does not exist in HTML_QuickForm::addRule()", 'HTML_QuickForm_Error', true);
 
1078             } elseif (is_array($element)) {
 
1079                 foreach ($element as $el) {
 
1080                     if (!$this->elementExists($el)) {
 
1081                         return PEAR::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$el' does not exist in HTML_QuickForm::addRule()", 'HTML_QuickForm_Error', true);
 
1086         if (false === ($newName = $this->isRuleRegistered($type, true))) {
 
1087             return PEAR::raiseError(null, QUICKFORM_INVALID_RULE, null, E_USER_WARNING, "Rule '$type' is not registered in HTML_QuickForm::addRule()", 'HTML_QuickForm_Error', true);
 
1088         } elseif (is_string($newName)) {
 
1091         if (is_array($element)) {
 
1092             $dependent = $element;
 
1093             $element   = array_shift($dependent);
 
1097         if ($type == 'required' || $type == 'uploadedfile') {
 
1098             $this->_required[] = $element;
 
1100         if (!isset($this->_rules[$element])) {
 
1101             $this->_rules[$element] = array();
 
1103         if ($validation == 'client') {
 
1104             $this->updateAttributes(array('onsubmit' => 'try { var myValidator = validate_' . $this->_attributes['id'] . '; } catch(e) { return true; } return myValidator(this);'));
 
1106         $this->_rules[$element][] = array(
 
1108             'format'      => $format,
 
1109             'message'     => $message,
 
1110             'validation'  => $validation,
 
1112             'dependent'   => $dependent
 
1114     } // end func addRule
 
1117     // {{{ addGroupRule()
 
1120      * Adds a validation rule for the given group of elements
 
1122      * Only groups with a name can be assigned a validation rule
 
1123      * Use addGroupRule when you need to validate elements inside the group.
 
1124      * Use addRule if you need to validate the group as a whole. In this case,
 
1125      * the same rule will be applied to all elements in the group.
 
1126      * Use addRule if you need to validate the group against a function.
 
1128      * @param    string     $group         Form group name
 
1129      * @param    mixed      $arg1          Array for multiple elements or error message string for one element
 
1130      * @param    string     $type          (optional)Rule type use getRegisteredRules() to get types
 
1131      * @param    string     $format        (optional)Required for extra rule data
 
1132      * @param    int        $howmany       (optional)How many valid elements should be in the group
 
1133      * @param    string     $validation    (optional)Where to perform validation: "server", "client"
 
1134      * @param    bool       $reset         Client-side: whether to reset the element's value to its original state if validation failed.
 
1137      * @throws   HTML_QuickForm_Error
 
1139     function addGroupRule($group, $arg1, $type='', $format=null, $howmany=0, $validation = 'server', $reset = false)
 
1141         if (!$this->elementExists($group)) {
 
1142             return PEAR::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Group '$group' does not exist in HTML_QuickForm::addGroupRule()", 'HTML_QuickForm_Error', true);
 
1145         $groupObj =& $this->getElement($group);
 
1146         if (is_array($arg1)) {
 
1148             foreach ($arg1 as $elementIndex => $rules) {
 
1149                 $elementName = $groupObj->getElementName($elementIndex);
 
1150                 foreach ($rules as $rule) {
 
1151                     $format = (isset($rule[2])) ? $rule[2] : null;
 
1152                     $validation = (isset($rule[3]) && 'client' == $rule[3])? 'client': 'server';
 
1153                     $reset = isset($rule[4]) && $rule[4];
 
1155                     if (false === ($newName = $this->isRuleRegistered($type, true))) {
 
1156                         return PEAR::raiseError(null, QUICKFORM_INVALID_RULE, null, E_USER_WARNING, "Rule '$type' is not registered in HTML_QuickForm::addGroupRule()", 'HTML_QuickForm_Error', true);
 
1157                     } elseif (is_string($newName)) {
 
1161                     $this->_rules[$elementName][] = array(
 
1163                                                         'format'      => $format,
 
1164                                                         'message'     => $rule[0],
 
1165                                                         'validation'  => $validation,
 
1169                     if ('required' == $type || 'uploadedfile' == $type) {
 
1170                         $groupObj->_required[] = $elementName;
 
1171                         $this->_required[] = $elementName;
 
1174                     if ('client' == $validation) {
 
1175                         $this->updateAttributes(array('onsubmit' => 'try { var myValidator = validate_' . $this->_attributes['id'] . '; } catch(e) { return true; } return myValidator(this);'));
 
1179             if ($required > 0 && count($groupObj->getElements()) == $required) {
 
1180                 $this->_required[] = $group;
 
1182         } elseif (is_string($arg1)) {
 
1183             if (false === ($newName = $this->isRuleRegistered($type, true))) {
 
1184                 return PEAR::raiseError(null, QUICKFORM_INVALID_RULE, null, E_USER_WARNING, "Rule '$type' is not registered in HTML_QuickForm::addGroupRule()", 'HTML_QuickForm_Error', true);
 
1185             } elseif (is_string($newName)) {
 
1189             // addGroupRule() should also handle <select multiple>
 
1190             if (is_a($groupObj, 'html_quickform_group')) {
 
1191                 // Radios need to be handled differently when required
 
1192                 if ($type == 'required' && $groupObj->getGroupType() == 'radio') {
 
1193                     $howmany = ($howmany == 0) ? 1 : $howmany;
 
1195                     $howmany = ($howmany == 0) ? count($groupObj->getElements()) : $howmany;
 
1199             $this->_rules[$group][] = array('type'       => $type,
 
1200                                             'format'     => $format,
 
1202                                             'validation' => $validation,
 
1203                                             'howmany'    => $howmany,
 
1205             if ($type == 'required') {
 
1206                 $this->_required[] = $group;
 
1208             if ($validation == 'client') {
 
1209                 $this->updateAttributes(array('onsubmit' => 'try { var myValidator = validate_' . $this->_attributes['id'] . '; } catch(e) { return true; } return myValidator(this);'));
 
1212     } // end func addGroupRule
 
1215     // {{{ addFormRule()
 
1218     * Adds a global validation rule
 
1220     * This should be used when for a rule involving several fields or if
 
1221     * you want to use some completely custom validation for your form.
 
1222     * The rule function/method should return true in case of successful
 
1223     * validation and array('element name' => 'error') when there were errors.
 
1226     * @param    mixed   Callback, either function name or array(&$object, 'method')
 
1227     * @throws   HTML_QuickForm_Error
 
1229     function addFormRule($rule)
 
1231         if (!is_callable($rule)) {
 
1232             return PEAR::raiseError(null, QUICKFORM_INVALID_RULE, null, E_USER_WARNING, 'Callback function does not exist in HTML_QuickForm::addFormRule()', 'HTML_QuickForm_Error', true);
 
1234         $this->_formRules[] = $rule;
 
1238     // {{{ applyFilter()
 
1241      * Applies a data filter for the given field(s)
 
1243      * @param    mixed     $element       Form element name or array of such names
 
1244      * @param    mixed     $filter        Callback, either function name or array(&$object, 'method')
 
1247      * @throws   HTML_QuickForm_Error
 
1249     function applyFilter($element, $filter)
 
1251         if (!is_callable($filter)) {
 
1252             return PEAR::raiseError(null, QUICKFORM_INVALID_FILTER, null, E_USER_WARNING, "Callback function does not exist in QuickForm::applyFilter()", 'HTML_QuickForm_Error', true);
 
1254         if ($element == '__ALL__') {
 
1255             $this->_submitValues = $this->_recursiveFilter($filter, $this->_submitValues);
 
1257             if (!is_array($element)) {
 
1258                 $element = array($element);
 
1260             foreach ($element as $elName) {
 
1261                 $value = $this->getSubmitValue($elName);
 
1262                 if (null !== $value) {
 
1263                     if (false === strpos($elName, '[')) {
 
1264                         $this->_submitValues[$elName] = $this->_recursiveFilter($filter, $value);
 
1266                         $idx  = "['" . str_replace(
 
1267                                     array('\\', '\'', ']', '['), array('\\\\', '\\\'', '', "']['"),
 
1270                         eval("\$this->_submitValues{$idx} = \$this->_recursiveFilter(\$filter, \$value);");
 
1275     } // end func applyFilter
 
1278     // {{{ _recursiveFilter()
 
1281      * Recursively apply a filter function
 
1283      * @param     string   $filter    filter to apply
 
1284      * @param     mixed    $value     submitted values
 
1287      * @return    cleaned values
 
1289     function _recursiveFilter($filter, $value)
 
1291         if (is_array($value)) {
 
1292             $cleanValues = array();
 
1293             foreach ($value as $k => $v) {
 
1294                 $cleanValues[$k] = $this->_recursiveFilter($filter, $v);
 
1296             return $cleanValues;
 
1298             return call_user_func($filter, $value);
 
1300     } // end func _recursiveFilter
 
1308     * Merges two array like the PHP function array_merge but recursively.
 
1309     * The main difference is that existing keys will not be renumbered
 
1310     * if they are integers.
 
1313     * @param    array   $a  original array
 
1314     * @param    array   $b  array which will be merged into first one
 
1315     * @return   array   merged array
 
1317     function arrayMerge($a, $b)
 
1319         foreach ($b as $k => $v) {
 
1321                 if (isset($a[$k]) && !is_array($a[$k])) {
 
1324                     if (!isset($a[$k])) {
 
1327                     $a[$k] = HTML_QuickForm::arrayMerge($a[$k], $v);
 
1334     } // end func arrayMerge
 
1337     // {{{ isTypeRegistered()
 
1340      * Returns whether or not the form element type is supported
 
1342      * @param     string   $type     Form element type
 
1347     function isTypeRegistered($type)
 
1349         return isset($GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES'][strtolower($type)]);
 
1350     } // end func isTypeRegistered
 
1353     // {{{ getRegisteredTypes()
 
1356      * Returns an array of registered element types
 
1362     function getRegisteredTypes()
 
1364         return array_keys($GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES']);
 
1365     } // end func getRegisteredTypes
 
1368     // {{{ isRuleRegistered()
 
1371      * Returns whether or not the given rule is supported
 
1373      * @param     string   $name    Validation rule name
 
1374      * @param     bool     Whether to automatically register subclasses of HTML_QuickForm_Rule
 
1377      * @return    mixed    true if previously registered, false if not, new rule name if auto-registering worked
 
1379     function isRuleRegistered($name, $autoRegister = false)
 
1381         if (is_scalar($name) && isset($GLOBALS['_HTML_QuickForm_registered_rules'][$name])) {
 
1383         } elseif (!$autoRegister) {
 
1386         // automatically register the rule if requested
 
1387         include_once 'HTML/QuickForm/RuleRegistry.php';
 
1389         if (is_object($name) && is_a($name, 'html_quickform_rule')) {
 
1390             $ruleName = !empty($name->name)? $name->name: strtolower(get_class($name));
 
1391         } elseif (is_string($name) && class_exists($name)) {
 
1392             $parent = strtolower($name);
 
1394                 if ('html_quickform_rule' == strtolower($parent)) {
 
1395                     $ruleName = strtolower($name);
 
1398             } while ($parent = get_parent_class($parent));
 
1401             $registry =& HTML_QuickForm_RuleRegistry::singleton();
 
1402             $registry->registerRule($ruleName, null, $name);
 
1405     } // end func isRuleRegistered
 
1408     // {{{ getRegisteredRules()
 
1411      * Returns an array of registered validation rules
 
1417     function getRegisteredRules()
 
1419         return array_keys($GLOBALS['_HTML_QuickForm_registered_rules']);
 
1420     } // end func getRegisteredRules
 
1423     // {{{ isElementRequired()
 
1426      * Returns whether or not the form element is required
 
1428      * @param     string   $element     Form element name
 
1433     function isElementRequired($element)
 
1435         return in_array($element, $this->_required, true);
 
1436     } // end func isElementRequired
 
1439     // {{{ isElementFrozen()
 
1442      * Returns whether or not the form element is frozen
 
1444      * @param     string   $element     Form element name
 
1449     function isElementFrozen($element)
 
1451          if (isset($this->_elementIndex[$element])) {
 
1452              return $this->_elements[$this->_elementIndex[$element]]->isFrozen();
 
1455     } // end func isElementFrozen
 
1458     // {{{ setJsWarnings()
 
1461      * Sets JavaScript warning messages
 
1463      * @param     string   $pref        Prefix warning
 
1464      * @param     string   $post        Postfix warning
 
1469     function setJsWarnings($pref, $post)
 
1471         $this->_jsPrefix = $pref;
 
1472         $this->_jsPostfix = $post;
 
1473     } // end func setJsWarnings
 
1476     // {{{ setRequiredNote()
 
1479      * Sets required-note
 
1481      * @param     string   $note        Message indicating some elements are required
 
1486     function setRequiredNote($note)
 
1488         $this->_requiredNote = $note;
 
1489     } // end func setRequiredNote
 
1492     // {{{ getRequiredNote()
 
1495      * Returns the required note
 
1501     function getRequiredNote()
 
1503         return $this->_requiredNote;
 
1504     } // end func getRequiredNote
 
1510      * Performs the server side validation
 
1513      * @return    boolean   true if no error found
 
1514      * @throws    HTML_QuickForm_Error
 
1518         if (count($this->_rules) == 0 && count($this->_formRules) == 0 &&
 
1519             $this->isSubmitted()) {
 
1520             return (0 == count($this->_errors));
 
1521         } elseif (!$this->isSubmitted()) {
 
1525         include_once('HTML/QuickForm/RuleRegistry.php');
 
1526         $registry =& HTML_QuickForm_RuleRegistry::singleton();
 
1528         foreach ($this->_rules as $target => $rules) {
 
1529             $submitValue = $this->getSubmitValue($target);
 
1531             foreach ($rules as $rule) {
 
1532                 if ((isset($rule['group']) && isset($this->_errors[$rule['group']])) ||
 
1533                      isset($this->_errors[$target])) {
 
1536                 // If element is not required and is empty, we shouldn't validate it
 
1537                 if (!$this->isElementRequired($target)) {
 
1538                     if (!isset($submitValue) || '' == $submitValue) {
 
1540                     // Fix for bug #3501: we shouldn't validate not uploaded files, either.
 
1541                     // Unfortunately, we can't just use $element->isUploadedFile() since
 
1542                     // the element in question can be buried in group. Thus this hack.
 
1543                     // See also bug #12014, we should only consider a file that has
 
1544                     // status UPLOAD_ERR_NO_FILE as not uploaded, in all other cases
 
1545                     // validation should be performed, so that e.g. 'maxfilesize' rule
 
1546                     // will display an error if status is UPLOAD_ERR_INI_SIZE
 
1547                     // or UPLOAD_ERR_FORM_SIZE
 
1548                     } elseif (is_array($submitValue)) {
 
1549                         if (false === ($pos = strpos($target, '['))) {
 
1550                             $isUpload = !empty($this->_submitFiles[$target]);
 
1552                             $base = str_replace(
 
1553                                         array('\\', '\''), array('\\\\', '\\\''),
 
1554                                         substr($target, 0, $pos)
 
1556                             $idx  = "['" . str_replace(
 
1557                                         array('\\', '\'', ']', '['), array('\\\\', '\\\'', '', "']['"),
 
1558                                         substr($target, $pos + 1, -1)
 
1560                             eval("\$isUpload = isset(\$this->_submitFiles['{$base}']['name']{$idx});");
 
1562                         if ($isUpload && (!isset($submitValue['error']) || UPLOAD_ERR_NO_FILE == $submitValue['error'])) {
 
1567                 if (isset($rule['dependent']) && is_array($rule['dependent'])) {
 
1568                     $values = array($submitValue);
 
1569                     foreach ($rule['dependent'] as $elName) {
 
1570                         $values[] = $this->getSubmitValue($elName);
 
1572                     $result = $registry->validate($rule['type'], $values, $rule['format'], true);
 
1573                 } elseif (is_array($submitValue) && !isset($rule['howmany'])) {
 
1574                     $result = $registry->validate($rule['type'], $submitValue, $rule['format'], true);
 
1576                     $result = $registry->validate($rule['type'], $submitValue, $rule['format'], false);
 
1579                 if (!$result || (!empty($rule['howmany']) && $rule['howmany'] > (int)$result)) {
 
1580                     if (isset($rule['group'])) {
 
1581                         $this->_errors[$rule['group']] = $rule['message'];
 
1583                         $this->_errors[$target] = $rule['message'];
 
1589         // process the global rules now
 
1590         foreach ($this->_formRules as $rule) {
 
1591             if (true !== ($res = call_user_func($rule, $this->_submitValues, $this->_submitFiles))) {
 
1592                 if (is_array($res)) {
 
1593                     $this->_errors += $res;
 
1595                     return PEAR::raiseError(null, QUICKFORM_ERROR, null, E_USER_WARNING, 'Form rule callback returned invalid value in HTML_QuickForm::validate()', 'HTML_QuickForm_Error', true);
 
1600         return (0 == count($this->_errors));
 
1601     } // end func validate
 
1607      * Displays elements without HTML input tags
 
1609      * @param    mixed   $elementList       array or string of element(s) to be frozen
 
1612      * @throws   HTML_QuickForm_Error
 
1614     function freeze($elementList=null)
 
1616         if (!isset($elementList)) {
 
1617             $this->_freezeAll = true;
 
1618             $elementList = array();
 
1620             if (!is_array($elementList)) {
 
1621                 $elementList = preg_split('/[ ]*,[ ]*/', $elementList);
 
1623             $elementList = array_flip($elementList);
 
1626         foreach (array_keys($this->_elements) as $key) {
 
1627             $name = $this->_elements[$key]->getName();
 
1628             if ($this->_freezeAll || isset($elementList[$name])) {
 
1629                 $this->_elements[$key]->freeze();
 
1630                 unset($elementList[$name]);
 
1634         if (!empty($elementList)) {
 
1635             return PEAR::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Nonexistant element(s): '" . implode("', '", array_keys($elementList)) . "' in HTML_QuickForm::freeze()", 'HTML_QuickForm_Error', true);
 
1638     } // end func freeze
 
1644      * Returns whether or not the whole form is frozen
 
1652          return $this->_freezeAll;
 
1653     } // end func isFrozen
 
1659      * Performs the form data processing
 
1661      * @param    mixed     $callback        Callback, either function name or array(&$object, 'method')
 
1662      * @param    bool      $mergeFiles      Whether uploaded files should be processed too
 
1665      * @throws   HTML_QuickForm_Error
 
1666      * @return   mixed     Whatever value the $callback function returns
 
1668     function process($callback, $mergeFiles = true)
 
1670         if (!is_callable($callback)) {
 
1671             return PEAR::raiseError(null, QUICKFORM_INVALID_PROCESS, null, E_USER_WARNING, "Callback function does not exist in QuickForm::process()", 'HTML_QuickForm_Error', true);
 
1673         $values = ($mergeFiles === true) ? HTML_QuickForm::arrayMerge($this->_submitValues, $this->_submitFiles) : $this->_submitValues;
 
1674         return call_user_func($callback, $values);
 
1675     } // end func process
 
1681     * Accepts a renderer
 
1683     * @param object     An HTML_QuickForm_Renderer object
 
1688     function accept(&$renderer)
 
1690         $renderer->startForm($this);
 
1691         foreach (array_keys($this->_elements) as $key) {
 
1692             $element =& $this->_elements[$key];
 
1693             $elementName = $element->getName();
 
1694             $required    = ($this->isElementRequired($elementName) && !$element->isFrozen());
 
1695             $error       = $this->getElementError($elementName);
 
1696             $element->accept($renderer, $required, $error);
 
1698         $renderer->finishForm($this);
 
1699     } // end func accept
 
1702     // {{{ defaultRenderer()
 
1705     * Returns a reference to default renderer object
 
1709     * @return object a default renderer object
 
1711     function &defaultRenderer()
 
1713         if (!isset($GLOBALS['_HTML_QuickForm_default_renderer'])) {
 
1714             include_once('HTML/QuickForm/Renderer/Default.php');
 
1715             $GLOBALS['_HTML_QuickForm_default_renderer'] = new HTML_QuickForm_Renderer_Default();
 
1717         return $GLOBALS['_HTML_QuickForm_default_renderer'];
 
1718     } // end func defaultRenderer
 
1724      * Returns an HTML version of the form
 
1726      * @param string $in_data (optional) Any extra data to insert right
 
1727      *               before form is rendered.  Useful when using templates.
 
1729      * @return   string     Html version of the form
 
1733     function toHtml ($in_data = null)
 
1735         if (!is_null($in_data)) {
 
1736             $this->addElement('html', $in_data);
 
1738         $renderer =& $this->defaultRenderer();
 
1739         $this->accept($renderer);
 
1740         return $renderer->toHtml();
 
1741     } // end func toHtml
 
1744     // {{{ getValidationScript()
 
1747      * Returns the client side validation script
 
1751      * @return    string    Javascript to perform validation, empty string if no 'client' rules were added
 
1753     function getValidationScript()
 
1755         if (empty($this->_rules) || empty($this->_attributes['onsubmit'])) {
 
1759         include_once('HTML/QuickForm/RuleRegistry.php');
 
1760         $registry =& HTML_QuickForm_RuleRegistry::singleton();
 
1771         foreach ($this->_rules as $elementName => $rules) {
 
1772             foreach ($rules as $rule) {
 
1773                 if ('client' == $rule['validation']) {
 
1776                     $dependent  = isset($rule['dependent']) && is_array($rule['dependent']);
 
1777                     $rule['message'] = strtr($rule['message'], $js_escape);
 
1779                     if (isset($rule['group'])) {
 
1780                         $group    =& $this->getElement($rule['group']);
 
1781                         // No JavaScript validation for frozen elements
 
1782                         if ($group->isFrozen()) {
 
1785                         $elements =& $group->getElements();
 
1786                         foreach (array_keys($elements) as $key) {
 
1787                             if ($elementName == $group->getElementName($key)) {
 
1788                                 $element =& $elements[$key];
 
1792                     } elseif ($dependent) {
 
1794                         $element[] =& $this->getElement($elementName);
 
1795                         foreach ($rule['dependent'] as $elName) {
 
1796                             $element[] =& $this->getElement($elName);
 
1799                         $element =& $this->getElement($elementName);
 
1801                     // No JavaScript validation for frozen elements
 
1802                     if (is_object($element) && $element->isFrozen()) {
 
1804                     } elseif (is_array($element)) {
 
1805                         foreach (array_keys($element) as $key) {
 
1806                             if ($element[$key]->isFrozen()) {
 
1812                     $test[] = $registry->getValidationScript($element, $elementName, $rule);
 
1816         if (count($test) > 0) {
 
1818                 "\n<script type=\"text/javascript\">\n" .
 
1820                 "function validate_" . $this->_attributes['id'] . "(frm) {\n" .
 
1821                 "  var value = '';\n" .
 
1822                 "  var errFlag = new Array();\n" .
 
1823                 "  var _qfGroups = {};\n" .
 
1824                 "  _qfMsg = '';\n\n" .
 
1826                 "\n  if (_qfMsg != '') {\n" .
 
1827                 "    _qfMsg = '" . strtr($this->_jsPrefix, $js_escape) . "' + _qfMsg;\n" .
 
1828                 "    _qfMsg = _qfMsg + '\\n" . strtr($this->_jsPostfix, $js_escape) . "';\n" .
 
1829                 "    alert(_qfMsg);\n" .
 
1830                 "    return false;\n" .
 
1838     } // end func getValidationScript
 
1841     // {{{ getSubmitValues()
 
1844      * Returns the values submitted by the form
 
1848      * @param     bool      Whether uploaded files should be returned too
 
1851     function getSubmitValues($mergeFiles = false)
 
1853         return $mergeFiles? HTML_QuickForm::arrayMerge($this->_submitValues, $this->_submitFiles): $this->_submitValues;
 
1854     } // end func getSubmitValues
 
1860      * Returns the form's contents in an array.
 
1862      * The description of the array structure is in HTML_QuickForm_Renderer_Array docs
 
1866      * @param     bool      Whether to collect hidden elements (passed to the Renderer's constructor)
 
1867      * @return    array of form contents
 
1869     function toArray($collectHidden = false)
 
1871         include_once 'HTML/QuickForm/Renderer/Array.php';
 
1872         $renderer = new HTML_QuickForm_Renderer_Array($collectHidden);
 
1873         $this->accept($renderer);
 
1874         return $renderer->toArray();
 
1875      } // end func toArray
 
1878     // {{{ exportValue()
 
1881      * Returns a 'safe' element's value
 
1883      * This method first tries to find a cleaned-up submitted value,
 
1884      * it will return a value set by setValue()/setDefaults()/setConstants()
 
1885      * if submitted value does not exist for the given element.
 
1887      * @param  string   Name of an element
 
1890      * @throws HTML_QuickForm_Error
 
1892     function exportValue($element)
 
1894         if (!isset($this->_elementIndex[$element])) {
 
1895             return PEAR::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$element' does not exist in HTML_QuickForm::getElementValue()", 'HTML_QuickForm_Error', true);
 
1897         $value = $this->_elements[$this->_elementIndex[$element]]->exportValue($this->_submitValues, false);
 
1898         if (isset($this->_duplicateIndex[$element])) {
 
1899             foreach ($this->_duplicateIndex[$element] as $index) {
 
1900                 if (null !== ($v = $this->_elements[$index]->exportValue($this->_submitValues, false))) {
 
1901                     if (is_array($value)) {
 
1904                         $value = (null === $value)? $v: array($value, $v);
 
1913     // {{{ exportValues()
 
1916      * Returns 'safe' elements' values
 
1918      * Unlike getSubmitValues(), this will return only the values
 
1919      * corresponding to the elements present in the form.
 
1921      * @param   mixed   Array/string of element names, whose values we want. If not set then return all elements.
 
1923      * @return  array   An assoc array of elements' values
 
1924      * @throws  HTML_QuickForm_Error
 
1926     function exportValues($elementList = null)
 
1929         if (null === $elementList) {
 
1930             // iterate over all elements, calling their exportValue() methods
 
1931             foreach (array_keys($this->_elements) as $key) {
 
1932                 $value = $this->_elements[$key]->exportValue($this->_submitValues, true);
 
1933                 if (is_array($value)) {
 
1934                     // This shit throws a bogus warning in PHP 4.3.x
 
1935                     $values = HTML_QuickForm::arrayMerge($values, $value);
 
1939             if (!is_array($elementList)) {
 
1940                 $elementList = array_map('trim', explode(',', $elementList));
 
1942             foreach ($elementList as $elementName) {
 
1943                 $value = $this->exportValue($elementName);
 
1944                 if (PEAR::isError($value)) {
 
1947                 $values[$elementName] = $value;
 
1954     // {{{ isSubmitted()
 
1957     * Tells whether the form was already submitted
 
1959     * This is useful since the _submitFiles and _submitValues arrays
 
1960     * may be completely empty after the trackSubmit value is removed.
 
1965     function isSubmitted()
 
1967         return $this->_flagSubmitted;
 
1975      * Tell whether a result from a QuickForm method is an error (an instance of HTML_QuickForm_Error)
 
1978      * @param mixed     result code
 
1979      * @return bool     whether $value is an error
 
1982     function isError($value)
 
1984         return (is_object($value) && is_a($value, 'html_quickform_error'));
 
1985     } // end func isError
 
1988     // {{{ errorMessage()
 
1991      * Return a textual error message for an QuickForm error code
 
1994      * @param   int     error code
 
1995      * @return  string  error message
 
1998     function errorMessage($value)
 
2000         // make the variable static so that it only has to do the defining on the first call
 
2001         static $errorMessages;
 
2003         // define the varies error messages
 
2004         if (!isset($errorMessages)) {
 
2005             $errorMessages = array(
 
2006                 QUICKFORM_OK                    => 'no error',
 
2007                 QUICKFORM_ERROR                 => 'unknown error',
 
2008                 QUICKFORM_INVALID_RULE          => 'the rule does not exist as a registered rule',
 
2009                 QUICKFORM_NONEXIST_ELEMENT      => 'nonexistent html element',
 
2010                 QUICKFORM_INVALID_FILTER        => 'invalid filter',
 
2011                 QUICKFORM_UNREGISTERED_ELEMENT  => 'unregistered element',
 
2012                 QUICKFORM_INVALID_ELEMENT_NAME  => 'element already exists',
 
2013                 QUICKFORM_INVALID_PROCESS       => 'process callback does not exist',
 
2014                 QUICKFORM_DEPRECATED            => 'method is deprecated',
 
2015                 QUICKFORM_INVALID_DATASOURCE    => 'datasource is not an object'
 
2019         // If this is an error object, then grab the corresponding error code
 
2020         if (HTML_QuickForm::isError($value)) {
 
2021             $value = $value->getCode();
 
2024         // return the textual error message corresponding to the code
 
2025         return isset($errorMessages[$value]) ? $errorMessages[$value] : $errorMessages[QUICKFORM_ERROR];
 
2026     } // end func errorMessage
 
2029 } // end class HTML_QuickForm
 
2032  * Class for errors thrown by HTML_QuickForm package
 
2035  * @package     HTML_QuickForm
 
2036  * @author      Adam Daniel <adaniel1@eesus.jnj.com>
 
2037  * @author      Bertrand Mansion <bmansion@mamasam.com>
 
2038  * @version     Release: 3.2.16
 
2040 class HTML_QuickForm_Error extends PEAR_Error {
 
2045     * Prefix for all error messages
 
2048     var $error_message_prefix = 'QuickForm Error: ';
 
2054     * Creates a quickform error object, extending the PEAR_Error class
 
2056     * @param int   $code the error code
 
2057     * @param int   $mode the reaction to the error, either return, die or trigger/callback
 
2058     * @param int   $level intensity of the error (PHP error code)
 
2059     * @param mixed $debuginfo any information that can inform user as to nature of the error
 
2061     function HTML_QuickForm_Error($code = QUICKFORM_ERROR, $mode = PEAR_ERROR_RETURN,
 
2062                          $level = E_USER_NOTICE, $debuginfo = null)
 
2064         if (is_int($code)) {
 
2065             $this->PEAR_Error(HTML_QuickForm::errorMessage($code), $code, $mode, $level, $debuginfo);
 
2067             $this->PEAR_Error("Invalid error code: $code", QUICKFORM_ERROR, $mode, $level, $debuginfo);
 
2072 } // end class HTML_QuickForm_Error