Configure the dmca form site on ridley
[chef.git] / cookbooks / dmca / files / default / html / HTML / QuickForm.php
1 <?php
2 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
3
4 /**
5  * Create, validate and process HTML forms
6  *
7  * PHP versions 4 and 5
8  *
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.
14  *
15  * @category    HTML
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
22  * @version     CVS: $Id$
23  * @link        http://pear.php.net/package/HTML_QuickForm
24  */
25
26 /**
27  * PEAR and PEAR_Error classes, for error handling
28  */
29 require_once 'PEAR.php';
30 /**
31  * Base class for all HTML classes
32  */
33 require_once 'HTML/Common.php';
34 /**
35  * Static utility methods
36  */
37 require_once 'HTML/QuickForm/utils.php';
38
39 /**
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']
44  */
45 $GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES'] =
46         array(
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')
70         );
71
72 /**
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']
77  */
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')
92 );
93
94 // {{{ error codes
95
96 /**#@+
97  * Error codes for HTML_QuickForm
98  *
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()
101  *
102  * @see HTML_QuickForm::errorMessage()
103  */
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);
114 /**#@-*/
115
116 // }}}
117
118 /**
119  * Create, validate and process HTML forms
120  *
121  * @category    HTML
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
127  */
128 class HTML_QuickForm extends HTML_Common
129 {
130     // {{{ properties
131
132     /**
133      * Array containing the form fields
134      * @since     1.0
135      * @var  array
136      * @access   private
137      */
138     var $_elements = array();
139
140     /**
141      * Array containing element name to index map
142      * @since     1.1
143      * @var  array
144      * @access   private
145      */
146     var $_elementIndex = array();
147
148     /**
149      * Array containing indexes of duplicate elements
150      * @since     2.10
151      * @var  array
152      * @access   private
153      */
154     var $_duplicateIndex = array();
155
156     /**
157      * Array containing required field IDs
158      * @since     1.0
159      * @var  array
160      * @access   private
161      */
162     var $_required = array();
163
164     /**
165      * Prefix message in javascript alert if error
166      * @since     1.0
167      * @var  string
168      * @access   public
169      */
170     var $_jsPrefix = 'Invalid information entered.';
171
172     /**
173      * Postfix message in javascript alert if error
174      * @since     1.0
175      * @var  string
176      * @access   public
177      */
178     var $_jsPostfix = 'Please correct these fields.';
179
180     /**
181      * Datasource object implementing the informal
182      * datasource protocol
183      * @since     3.3
184      * @var  object
185      * @access   private
186      */
187     var $_datasource;
188
189     /**
190      * Array of default form values
191      * @since     2.0
192      * @var  array
193      * @access   private
194      */
195     var $_defaultValues = array();
196
197     /**
198      * Array of constant form values
199      * @since     2.0
200      * @var  array
201      * @access   private
202      */
203     var $_constantValues = array();
204
205     /**
206      * Array of submitted form values
207      * @since     1.0
208      * @var  array
209      * @access   private
210      */
211     var $_submitValues = array();
212
213     /**
214      * Array of submitted form files
215      * @since     1.0
216      * @var  integer
217      * @access   public
218      */
219     var $_submitFiles = array();
220
221     /**
222      * Value for maxfilesize hidden element if form contains file input
223      * @since     1.0
224      * @var  integer
225      * @access   public
226      */
227     var $_maxFileSize = 1048576; // 1 Mb = 1048576
228
229     /**
230      * Flag to know if all fields are frozen
231      * @since     1.0
232      * @var  boolean
233      * @access   private
234      */
235     var $_freezeAll = false;
236
237     /**
238      * Array containing the form rules
239      * @since     1.0
240      * @var  array
241      * @access   private
242      */
243     var $_rules = array();
244
245     /**
246      * Form rules, global variety
247      * @var     array
248      * @access  private
249      */
250     var $_formRules = array();
251
252     /**
253      * Array containing the validation errors
254      * @since     1.0
255      * @var  array
256      * @access   private
257      */
258     var $_errors = array();
259
260     /**
261      * Note for required fields in the form
262      * @var       string
263      * @since     1.0
264      * @access    private
265      */
266     var $_requiredNote = '<span style="font-size:80%; color:#ff0000;">*</span><span style="font-size:80%;"> denotes required field</span>';
267
268     /**
269      * Whether the form was submitted
270      * @var       boolean
271      * @access    private
272      */
273     var $_flagSubmitted = false;
274
275     // }}}
276     // {{{ constructor
277
278     /**
279      * Class constructor
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
286      * @access   public
287      */
288     function HTML_QuickForm($formName='', $method='post', $action='', $target='', $attributes=null, $trackSubmit = false)
289     {
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);
303                         } else {
304                             $this->_submitFiles[$keyFirst][$keySecond] = $valSecond;
305                         }
306                     }
307                 }
308             } else {
309                 $this->_submitValues = 'get' == $method? $_GET: $_POST;
310                 $this->_submitFiles  = $_FILES;
311             }
312             $this->_flagSubmitted = count($this->_submitValues) > 0 || count($this->_submitFiles) > 0;
313         }
314         if ($trackSubmit) {
315             unset($this->_submitValues['_qf__' . $formName]);
316             $this->addElement('hidden', '_qf__' . $formName, null);
317         }
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'])) {
321                 case 'G':
322                     $this->_maxFileSize = $matches['1'] * 1073741824;
323                     break;
324                 case 'M':
325                     $this->_maxFileSize = $matches['1'] * 1048576;
326                     break;
327                 case 'K':
328                     $this->_maxFileSize = $matches['1'] * 1024;
329                     break;
330                 default:
331                     $this->_maxFileSize = $matches['1'];
332             }
333         }
334     } // end constructor
335
336     // }}}
337     // {{{ apiVersion()
338
339     /**
340      * Returns the current API version
341      *
342      * @since     1.0
343      * @access    public
344      * @return    float
345      */
346     function apiVersion()
347     {
348         return 3.2;
349     } // end func apiVersion
350
351     // }}}
352     // {{{ registerElementType()
353
354     /**
355      * Registers a new element type
356      *
357      * @param     string    $typeName   Name of element type
358      * @param     string    $include    Include path for element type
359      * @param     string    $className  Element class name
360      * @since     1.0
361      * @access    public
362      * @return    void
363      */
364     function registerElementType($typeName, $include, $className)
365     {
366         $GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES'][strtolower($typeName)] = array($include, $className);
367     } // end func registerElementType
368
369     // }}}
370     // {{{ registerRule()
371
372     /**
373      * Registers a new validation rule
374      *
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
379      * @since     1.0
380      * @access    public
381      * @return    void
382      */
383     function registerRule($ruleName, $type, $data1, $data2 = null)
384     {
385         include_once('HTML/QuickForm/RuleRegistry.php');
386         $registry =& HTML_QuickForm_RuleRegistry::singleton();
387         $registry->registerRule($ruleName, $type, $data1, $data2);
388     } // end func registerRule
389
390     // }}}
391     // {{{ elementExists()
392
393     /**
394      * Returns true if element is in the form
395      *
396      * @param     string   $element         form name of element to check
397      * @since     1.0
398      * @access    public
399      * @return    boolean
400      */
401     function elementExists($element=null)
402     {
403         return isset($this->_elementIndex[$element]);
404     } // end func elementExists
405
406     // }}}
407     // {{{ setDatasource()
408
409     /**
410      * Sets a datasource object for this form object
411      *
412      * Datasource default and constant values will feed the QuickForm object if
413      * the datasource implements defaultValues() and constantValues() methods.
414      *
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
418      * @since     3.3
419      * @access    public
420      * @return    void
421      * @throws    HTML_QuickForm_Error
422      */
423     function setDatasource(&$datasource, $defaultsFilter = null, $constantsFilter = null)
424     {
425         if (is_object($datasource)) {
426             $this->_datasource =& $datasource;
427             if (is_callable(array($datasource, 'defaultValues'))) {
428                 $this->setDefaults($datasource->defaultValues($this), $defaultsFilter);
429             }
430             if (is_callable(array($datasource, 'constantValues'))) {
431                 $this->setConstants($datasource->constantValues($this), $constantsFilter);
432             }
433         } else {
434             return PEAR::raiseError(null, QUICKFORM_INVALID_DATASOURCE, null, E_USER_WARNING, "Datasource is not an object in QuickForm::setDatasource()", 'HTML_QuickForm_Error', true);
435         }
436     } // end func setDatasource
437
438     // }}}
439     // {{{ setDefaults()
440
441     /**
442      * Initializes default form values
443      *
444      * @param     array    $defaultValues       values used to fill the form
445      * @param     mixed    $filter              (optional) filter(s) to apply to all default values
446      * @since     1.0
447      * @access    public
448      * @return    void
449      * @throws    HTML_QuickForm_Error
450      */
451     function setDefaults($defaultValues = null, $filter = null)
452     {
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);
459                         } else {
460                             $defaultValues = $this->_recursiveFilter($val, $defaultValues);
461                         }
462                     }
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);
465                 } else {
466                     $defaultValues = $this->_recursiveFilter($filter, $defaultValues);
467                 }
468             }
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);
472             }
473         }
474     } // end func setDefaults
475
476     // }}}
477     // {{{ setConstants()
478
479     /**
480      * Initializes constant form values.
481      * These values won't get overridden by POST or GET vars
482      *
483      * @param     array   $constantValues        values used to fill the form
484      * @param     mixed    $filter              (optional) filter(s) to apply to all default values
485      *
486      * @since     2.0
487      * @access    public
488      * @return    void
489      * @throws    HTML_QuickForm_Error
490      */
491     function setConstants($constantValues = null, $filter = null)
492     {
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);
499                         } else {
500                             $constantValues = $this->_recursiveFilter($val, $constantValues);
501                         }
502                     }
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);
505                 } else {
506                     $constantValues = $this->_recursiveFilter($filter, $constantValues);
507                 }
508             }
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);
512             }
513         }
514     } // end func setConstants
515
516     // }}}
517     // {{{ setMaxFileSize()
518
519     /**
520      * Sets the value of MAX_FILE_SIZE hidden element
521      *
522      * @param     int    $bytes    Size in bytes
523      * @since     3.0
524      * @access    public
525      * @return    void
526      */
527     function setMaxFileSize($bytes = 0)
528     {
529         if ($bytes > 0) {
530             $this->_maxFileSize = $bytes;
531         }
532         if (!$this->elementExists('MAX_FILE_SIZE')) {
533             $this->addElement('hidden', 'MAX_FILE_SIZE', $this->_maxFileSize);
534         } else {
535             $el =& $this->getElement('MAX_FILE_SIZE');
536             $el->updateAttributes(array('value' => $this->_maxFileSize));
537         }
538     } // end func setMaxFileSize
539
540     // }}}
541     // {{{ getMaxFileSize()
542
543     /**
544      * Returns the value of MAX_FILE_SIZE hidden element
545      *
546      * @since     3.0
547      * @access    public
548      * @return    int   max file size in bytes
549      */
550     function getMaxFileSize()
551     {
552         return $this->_maxFileSize;
553     } // end func getMaxFileSize
554
555     // }}}
556     // {{{ &createElement()
557
558     /**
559      * Creates a new form element of the given type.
560      *
561      * This method accepts variable number of parameters, their
562      * meaning and count depending on $elementType
563      *
564      * @param     string     $elementType    type of element to add (text, textarea, file...)
565      * @since     1.0
566      * @access    public
567      * @return    HTML_QuickForm_Element
568      * @throws    HTML_QuickForm_Error
569      */
570     function &createElement($elementType)
571     {
572         $args    =  func_get_args();
573         $element =& HTML_QuickForm::_loadElement('createElement', $elementType, array_slice($args, 1), null);
574         return $element;
575     } // end func createElement
576
577     // }}}
578     // {{{ _loadElement()
579
580     /**
581      * Returns a form element of the given type
582      *
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
586      * @since     2.0
587      * @access    private
588      * @return    HTML_QuickForm_Element
589      * @throws    HTML_QuickForm_Error
590      */
591     function &_loadElement($event, $type, $args, $form)
592     {
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);
596             return $error;
597         }
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])) {
604                 $args[$i] = null;
605             }
606         }
607         $err = $elementObject->onQuickFormEvent($event, $args, $form);
608         if ($err !== true) {
609             return $err;
610         }
611         return $elementObject;
612     } // end func _loadElement
613
614     // }}}
615     // {{{ addElement()
616
617     /**
618      * Adds an element into the form
619      *
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
623      *
624      * @param    mixed      $element        element object or type of element to add (text, textarea, file...)
625      * @since    1.0
626      * @return   HTML_QuickForm_Element     a reference to newly added element
627      * @access   public
628      * @throws   HTML_QuickForm_Error
629      */
630     function &addElement($element)
631     {
632         if (is_object($element) && is_subclass_of($element, 'html_quickform_element')) {
633            $elementObject = &$element;
634            $elementObject->onQuickFormEvent('updateValue', null, $this);
635         } else {
636             $args = func_get_args();
637             $elementObject =& $this->_loadElement('addElement', $element, array_slice($args, 1), $this);
638             if (PEAR::isError($elementObject)) {
639                 return $elementObject;
640             }
641         }
642         $elementName = $elementObject->getName();
643
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);
651             } else {
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);
653                 return $error;
654             }
655         } else {
656             $this->_elements[] =& $elementObject;
657             $elKeys = array_keys($this->_elements);
658             $this->_elementIndex[$elementName] = end($elKeys);
659         }
660         if ($this->_freezeAll) {
661             $elementObject->freeze();
662         }
663
664         return $elementObject;
665     } // end func addElement
666
667     // }}}
668     // {{{ insertElementBefore()
669
670    /**
671     * Inserts a new element right before the other element
672     *
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');
677     *
678     * @access   public
679     * @since    3.2.4
680     * @param    HTML_QuickForm_element  Element to insert
681     * @param    string                  Name of the element before which the new
682     *                                   one is inserted
683     * @return   HTML_QuickForm_element  reference to inserted element
684     * @throws   HTML_QuickForm_Error
685     */
686     function &insertElementBefore(&$element, $nameAfter)
687     {
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);
690             return $error;
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);
693             return $error;
694         }
695         $elementName = $element->getName();
696         $targetIdx   = $this->_elementIndex[$nameAfter];
697         $duplicate   = false;
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);
702                 return $error;
703             }
704             $duplicate = true;
705         }
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;
714                 } else {
715                     $dupIdx = array_search($i, $this->_duplicateIndex[$currentName]);
716                     $this->_duplicateIndex[$currentName][$dupIdx] = $i + 1;
717                 }
718                 unset($this->_elements[$i]);
719             }
720         }
721         // Put the element in place finally
722         $this->_elements[$targetIdx] =& $element;
723         if (!$duplicate) {
724             $this->_elementIndex[$elementName] = $targetIdx;
725         } else {
726             $this->_duplicateIndex[$elementName][] = $targetIdx;
727         }
728         $element->onQuickFormEvent('updateValue', null, $this);
729         if ($this->_freezeAll) {
730             $element->freeze();
731         }
732         // If not done, the elements will appear in reverse order
733         ksort($this->_elements);
734         return $element;
735     }
736
737     // }}}
738     // {{{ addGroup()
739
740     /**
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
749      * @since    2.8
750      * @access   public
751      * @throws   HTML_QuickForm_Error
752      */
753     function &addGroup($elements, $name=null, $groupLabel='', $separator=null, $appendName = true)
754     {
755         static $anonGroups = 1;
756
757         if (0 == strlen($name)) {
758             $name       = 'qf_group_' . $anonGroups++;
759             $appendName = false;
760         }
761         $group =& $this->addElement('group', $name, $groupLabel, $elements, $separator, $appendName);
762         return $group;
763     } // end func addGroup
764
765     // }}}
766     // {{{ &getElement()
767
768     /**
769      * Returns a reference to the element
770      *
771      * @param     string     $element    Element name
772      * @since     2.0
773      * @access    public
774      * @return    HTML_QuickForm_element    reference to element
775      * @throws    HTML_QuickForm_Error
776      */
777     function &getElement($element)
778     {
779         if (isset($this->_elementIndex[$element])) {
780             return $this->_elements[$this->_elementIndex[$element]];
781         } else {
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);
783             return $error;
784         }
785     } // end func getElement
786
787     // }}}
788     // {{{ &getElementValue()
789
790     /**
791      * Returns the element's raw value
792      *
793      * This returns the value as submitted by the form (not filtered)
794      * or set via setDefaults() or setConstants()
795      *
796      * @param     string     $element    Element name
797      * @since     2.0
798      * @access    public
799      * @return    mixed     element value
800      * @throws    HTML_QuickForm_Error
801      */
802     function &getElementValue($element)
803     {
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);
806             return $error;
807         }
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)) {
813                         $value[] = $v;
814                     } else {
815                         $value = (null === $value)? $v: array($value, $v);
816                     }
817                 }
818             }
819         }
820         return $value;
821     } // end func getElementValue
822
823     // }}}
824     // {{{ getSubmitValue()
825
826     /**
827      * Returns the elements value after submit and filter
828      *
829      * @param     string     Element name
830      * @since     2.0
831      * @access    public
832      * @return    mixed     submitted element value or null if not set
833      */
834     function getSubmitValue($elementName)
835     {
836         $value = null;
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));
842                 }
843             }
844
845         } elseif ('file' == $this->getElementType($elementName)) {
846             return $this->getElementValue($elementName);
847
848         } elseif (false !== ($pos = strpos($elementName, '['))) {
849             $base = str_replace(
850                         array('\\', '\''), array('\\\\', '\\\''),
851                         substr($elementName, 0, $pos)
852                     );
853
854             $keys = str_replace(
855                 array('\\', '\'', ']', '['), array('\\\\', '\\\'', '', "']['"),
856                 substr($elementName, $pos + 1, -1)
857                 );
858             $idx  = "['" . $keys . "']";
859             $keyArray = explode("']['", $keys);
860
861             if (isset($this->_submitValues[$base])) {
862                 $value = HTML_QuickForm_utils::recursiveValue($this->_submitValues[$base], $keyArray, NULL);
863             }
864
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" .
868                          "    return null;\n" .
869                          "} else {\n" .
870                          "    \$v = array();\n";
871                 foreach ($props as $prop) {
872                     $code .= "    \$v = HTML_QuickForm::arrayMerge(\$v, \$this->_reindexFiles(\$this->_submitFiles['{$base}']['{$prop}']{$idx}, '{$prop}'));\n";
873                 }
874                 $fileValue = eval($code . "    return \$v;\n}\n");
875                 if (null !== $fileValue) {
876                     $value = null === $value? $fileValue: HTML_QuickForm::arrayMerge($value, $fileValue);
877                 }
878             }
879         }
880
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))) {
890                         $value[$name] = $v;
891                     }
892                 }
893             }
894         }
895         return $value;
896     } // end func getSubmitValue
897
898     // }}}
899     // {{{ _reindexFiles()
900
901    /**
902     * A helper function to change the indexes in $_FILES array
903     *
904     * @param  mixed   Some value from the $_FILES array
905     * @param  string  The key from the $_FILES array that should be appended
906     * @return array
907     */
908     function _reindexFiles($value, $key)
909     {
910         if (!is_array($value)) {
911             return array($key => $value);
912         } else {
913             $ret = array();
914             foreach ($value as $k => $v) {
915                 $ret[$k] = $this->_reindexFiles($v, $key);
916             }
917             return $ret;
918         }
919     }
920
921     // }}}
922     // {{{ getElementError()
923
924     /**
925      * Returns error corresponding to validated element
926      *
927      * @param     string    $element        Name of form element to check
928      * @since     1.0
929      * @access    public
930      * @return    string    error message corresponding to checked element
931      */
932     function getElementError($element)
933     {
934         if (isset($this->_errors[$element])) {
935             return $this->_errors[$element];
936         }
937     } // end func getElementError
938
939     // }}}
940     // {{{ setElementError()
941
942     /**
943      * Set error message for a form element
944      *
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
947      * @since     1.0
948      * @access    public
949      * @return    void
950      */
951     function setElementError($element, $message = null)
952     {
953         if (!empty($message)) {
954             $this->_errors[$element] = $message;
955         } else {
956             unset($this->_errors[$element]);
957         }
958     } // end func setElementError
959
960      // }}}
961      // {{{ getElementType()
962
963      /**
964       * Returns the type of the given element
965       *
966       * @param      string    $element    Name of form element
967       * @since      1.1
968       * @access     public
969       * @return     string    Type of the element, false if the element is not found
970       */
971      function getElementType($element)
972      {
973          if (isset($this->_elementIndex[$element])) {
974              return $this->_elements[$this->_elementIndex[$element]]->getType();
975          }
976          return false;
977      } // end func getElementType
978
979      // }}}
980      // {{{ updateElementAttr()
981
982     /**
983      * Updates Attributes for one or more elements
984      *
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
987      * @since      2.10
988      * @access     public
989      * @return     void
990      */
991     function updateElementAttr($elements, $attrs)
992     {
993         if (is_string($elements)) {
994             $elements = preg_split('/[ ]?,[ ]?/', $elements);
995         }
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);
1004                     }
1005                 }
1006             }
1007         }
1008     } // end func updateElementAttr
1009
1010     // }}}
1011     // {{{ removeElement()
1012
1013     /**
1014      * Removes an element
1015      *
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.
1019      *
1020      * @param string    $elementName The element name
1021      * @param boolean   $removeRules True if rules for this element are to be removed too
1022      * @access public
1023      * @since 2.0
1024      * @return HTML_QuickForm_element    a reference to the removed element
1025      * @throws HTML_QuickForm_Error
1026      */
1027     function &removeElement($elementName, $removeRules = true)
1028     {
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);
1031             return $error;
1032         }
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]);
1037         } else {
1038             $this->_elementIndex[$elementName] = array_shift($this->_duplicateIndex[$elementName]);
1039         }
1040         if ($removeRules) {
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)]);
1046                 }
1047             }
1048         }
1049         return $el;
1050     } // end func removeElement
1051
1052     // }}}
1053     // {{{ addRule()
1054
1055     /**
1056      * Adds a validation rule for the given field
1057      *
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.
1061      *
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
1069      * @since    1.0
1070      * @access   public
1071      * @throws   HTML_QuickForm_Error
1072      */
1073     function addRule($element, $message, $type, $format=null, $validation='server', $reset = false, $force = false)
1074     {
1075         if (!$force) {
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);
1082                     }
1083                 }
1084             }
1085         }
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)) {
1089             $type = $newName;
1090         }
1091         if (is_array($element)) {
1092             $dependent = $element;
1093             $element   = array_shift($dependent);
1094         } else {
1095             $dependent = null;
1096         }
1097         if ($type == 'required' || $type == 'uploadedfile') {
1098             $this->_required[] = $element;
1099         }
1100         if (!isset($this->_rules[$element])) {
1101             $this->_rules[$element] = array();
1102         }
1103         if ($validation == 'client') {
1104             $this->updateAttributes(array('onsubmit' => 'try { var myValidator = validate_' . $this->_attributes['id'] . '; } catch(e) { return true; } return myValidator(this);'));
1105         }
1106         $this->_rules[$element][] = array(
1107             'type'        => $type,
1108             'format'      => $format,
1109             'message'     => $message,
1110             'validation'  => $validation,
1111             'reset'       => $reset,
1112             'dependent'   => $dependent
1113         );
1114     } // end func addRule
1115
1116     // }}}
1117     // {{{ addGroupRule()
1118
1119     /**
1120      * Adds a validation rule for the given group of elements
1121      *
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.
1127      *
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.
1135      * @since    2.5
1136      * @access   public
1137      * @throws   HTML_QuickForm_Error
1138      */
1139     function addGroupRule($group, $arg1, $type='', $format=null, $howmany=0, $validation = 'server', $reset = false)
1140     {
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);
1143         }
1144
1145         $groupObj =& $this->getElement($group);
1146         if (is_array($arg1)) {
1147             $required = 0;
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];
1154                     $type = $rule[1];
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)) {
1158                         $type = $newName;
1159                     }
1160
1161                     $this->_rules[$elementName][] = array(
1162                                                         'type'        => $type,
1163                                                         'format'      => $format,
1164                                                         'message'     => $rule[0],
1165                                                         'validation'  => $validation,
1166                                                         'reset'       => $reset,
1167                                                         'group'       => $group);
1168
1169                     if ('required' == $type || 'uploadedfile' == $type) {
1170                         $groupObj->_required[] = $elementName;
1171                         $this->_required[] = $elementName;
1172                         $required++;
1173                     }
1174                     if ('client' == $validation) {
1175                         $this->updateAttributes(array('onsubmit' => 'try { var myValidator = validate_' . $this->_attributes['id'] . '; } catch(e) { return true; } return myValidator(this);'));
1176                     }
1177                 }
1178             }
1179             if ($required > 0 && count($groupObj->getElements()) == $required) {
1180                 $this->_required[] = $group;
1181             }
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)) {
1186                 $type = $newName;
1187             }
1188
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;
1194                 } else {
1195                     $howmany = ($howmany == 0) ? count($groupObj->getElements()) : $howmany;
1196                 }
1197             }
1198
1199             $this->_rules[$group][] = array('type'       => $type,
1200                                             'format'     => $format,
1201                                             'message'    => $arg1,
1202                                             'validation' => $validation,
1203                                             'howmany'    => $howmany,
1204                                             'reset'      => $reset);
1205             if ($type == 'required') {
1206                 $this->_required[] = $group;
1207             }
1208             if ($validation == 'client') {
1209                 $this->updateAttributes(array('onsubmit' => 'try { var myValidator = validate_' . $this->_attributes['id'] . '; } catch(e) { return true; } return myValidator(this);'));
1210             }
1211         }
1212     } // end func addGroupRule
1213
1214     // }}}
1215     // {{{ addFormRule()
1216
1217    /**
1218     * Adds a global validation rule
1219     *
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.
1224     *
1225     * @access   public
1226     * @param    mixed   Callback, either function name or array(&$object, 'method')
1227     * @throws   HTML_QuickForm_Error
1228     */
1229     function addFormRule($rule)
1230     {
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);
1233         }
1234         $this->_formRules[] = $rule;
1235     }
1236
1237     // }}}
1238     // {{{ applyFilter()
1239
1240     /**
1241      * Applies a data filter for the given field(s)
1242      *
1243      * @param    mixed     $element       Form element name or array of such names
1244      * @param    mixed     $filter        Callback, either function name or array(&$object, 'method')
1245      * @since    2.0
1246      * @access   public
1247      * @throws   HTML_QuickForm_Error
1248      */
1249     function applyFilter($element, $filter)
1250     {
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);
1253         }
1254         if ($element == '__ALL__') {
1255             $this->_submitValues = $this->_recursiveFilter($filter, $this->_submitValues);
1256         } else {
1257             if (!is_array($element)) {
1258                 $element = array($element);
1259             }
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);
1265                     } else {
1266                         $idx  = "['" . str_replace(
1267                                     array('\\', '\'', ']', '['), array('\\\\', '\\\'', '', "']['"),
1268                                     $elName
1269                                 ) . "']";
1270                         eval("\$this->_submitValues{$idx} = \$this->_recursiveFilter(\$filter, \$value);");
1271                     }
1272                 }
1273             }
1274         }
1275     } // end func applyFilter
1276
1277     // }}}
1278     // {{{ _recursiveFilter()
1279
1280     /**
1281      * Recursively apply a filter function
1282      *
1283      * @param     string   $filter    filter to apply
1284      * @param     mixed    $value     submitted values
1285      * @since     2.0
1286      * @access    private
1287      * @return    cleaned values
1288      */
1289     function _recursiveFilter($filter, $value)
1290     {
1291         if (is_array($value)) {
1292             $cleanValues = array();
1293             foreach ($value as $k => $v) {
1294                 $cleanValues[$k] = $this->_recursiveFilter($filter, $v);
1295             }
1296             return $cleanValues;
1297         } else {
1298             return call_user_func($filter, $value);
1299         }
1300     } // end func _recursiveFilter
1301
1302     // }}}
1303     // {{{ arrayMerge()
1304
1305    /**
1306     * Merges two arrays
1307     *
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.
1311     *
1312     * @access   public
1313     * @param    array   $a  original array
1314     * @param    array   $b  array which will be merged into first one
1315     * @return   array   merged array
1316     */
1317     function arrayMerge($a, $b)
1318     {
1319         foreach ($b as $k => $v) {
1320             if (is_array($v)) {
1321                 if (isset($a[$k]) && !is_array($a[$k])) {
1322                     $a[$k] = $v;
1323                 } else {
1324                     if (!isset($a[$k])) {
1325                         $a[$k] = array();
1326                     }
1327                     $a[$k] = HTML_QuickForm::arrayMerge($a[$k], $v);
1328                 }
1329             } else {
1330                 $a[$k] = $v;
1331             }
1332         }
1333         return $a;
1334     } // end func arrayMerge
1335
1336     // }}}
1337     // {{{ isTypeRegistered()
1338
1339     /**
1340      * Returns whether or not the form element type is supported
1341      *
1342      * @param     string   $type     Form element type
1343      * @since     1.0
1344      * @access    public
1345      * @return    boolean
1346      */
1347     function isTypeRegistered($type)
1348     {
1349         return isset($GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES'][strtolower($type)]);
1350     } // end func isTypeRegistered
1351
1352     // }}}
1353     // {{{ getRegisteredTypes()
1354
1355     /**
1356      * Returns an array of registered element types
1357      *
1358      * @since     1.0
1359      * @access    public
1360      * @return    array
1361      */
1362     function getRegisteredTypes()
1363     {
1364         return array_keys($GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES']);
1365     } // end func getRegisteredTypes
1366
1367     // }}}
1368     // {{{ isRuleRegistered()
1369
1370     /**
1371      * Returns whether or not the given rule is supported
1372      *
1373      * @param     string   $name    Validation rule name
1374      * @param     bool     Whether to automatically register subclasses of HTML_QuickForm_Rule
1375      * @since     1.0
1376      * @access    public
1377      * @return    mixed    true if previously registered, false if not, new rule name if auto-registering worked
1378      */
1379     function isRuleRegistered($name, $autoRegister = false)
1380     {
1381         if (is_scalar($name) && isset($GLOBALS['_HTML_QuickForm_registered_rules'][$name])) {
1382             return true;
1383         } elseif (!$autoRegister) {
1384             return false;
1385         }
1386         // automatically register the rule if requested
1387         include_once 'HTML/QuickForm/RuleRegistry.php';
1388         $ruleName = false;
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);
1393             do {
1394                 if ('html_quickform_rule' == strtolower($parent)) {
1395                     $ruleName = strtolower($name);
1396                     break;
1397                 }
1398             } while ($parent = get_parent_class($parent));
1399         }
1400         if ($ruleName) {
1401             $registry =& HTML_QuickForm_RuleRegistry::singleton();
1402             $registry->registerRule($ruleName, null, $name);
1403         }
1404         return $ruleName;
1405     } // end func isRuleRegistered
1406
1407     // }}}
1408     // {{{ getRegisteredRules()
1409
1410     /**
1411      * Returns an array of registered validation rules
1412      *
1413      * @since     1.0
1414      * @access    public
1415      * @return    array
1416      */
1417     function getRegisteredRules()
1418     {
1419         return array_keys($GLOBALS['_HTML_QuickForm_registered_rules']);
1420     } // end func getRegisteredRules
1421
1422     // }}}
1423     // {{{ isElementRequired()
1424
1425     /**
1426      * Returns whether or not the form element is required
1427      *
1428      * @param     string   $element     Form element name
1429      * @since     1.0
1430      * @access    public
1431      * @return    boolean
1432      */
1433     function isElementRequired($element)
1434     {
1435         return in_array($element, $this->_required, true);
1436     } // end func isElementRequired
1437
1438     // }}}
1439     // {{{ isElementFrozen()
1440
1441     /**
1442      * Returns whether or not the form element is frozen
1443      *
1444      * @param     string   $element     Form element name
1445      * @since     1.0
1446      * @access    public
1447      * @return    boolean
1448      */
1449     function isElementFrozen($element)
1450     {
1451          if (isset($this->_elementIndex[$element])) {
1452              return $this->_elements[$this->_elementIndex[$element]]->isFrozen();
1453          }
1454          return false;
1455     } // end func isElementFrozen
1456
1457     // }}}
1458     // {{{ setJsWarnings()
1459
1460     /**
1461      * Sets JavaScript warning messages
1462      *
1463      * @param     string   $pref        Prefix warning
1464      * @param     string   $post        Postfix warning
1465      * @since     1.1
1466      * @access    public
1467      * @return    void
1468      */
1469     function setJsWarnings($pref, $post)
1470     {
1471         $this->_jsPrefix = $pref;
1472         $this->_jsPostfix = $post;
1473     } // end func setJsWarnings
1474
1475     // }}}
1476     // {{{ setRequiredNote()
1477
1478     /**
1479      * Sets required-note
1480      *
1481      * @param     string   $note        Message indicating some elements are required
1482      * @since     1.1
1483      * @access    public
1484      * @return    void
1485      */
1486     function setRequiredNote($note)
1487     {
1488         $this->_requiredNote = $note;
1489     } // end func setRequiredNote
1490
1491     // }}}
1492     // {{{ getRequiredNote()
1493
1494     /**
1495      * Returns the required note
1496      *
1497      * @since     2.0
1498      * @access    public
1499      * @return    string
1500      */
1501     function getRequiredNote()
1502     {
1503         return $this->_requiredNote;
1504     } // end func getRequiredNote
1505
1506     // }}}
1507     // {{{ validate()
1508
1509     /**
1510      * Performs the server side validation
1511      * @access    public
1512      * @since     1.0
1513      * @return    boolean   true if no error found
1514      * @throws    HTML_QuickForm_Error
1515      */
1516     function validate()
1517     {
1518         if (count($this->_rules) == 0 && count($this->_formRules) == 0 &&
1519             $this->isSubmitted()) {
1520             return (0 == count($this->_errors));
1521         } elseif (!$this->isSubmitted()) {
1522             return false;
1523         }
1524
1525         include_once('HTML/QuickForm/RuleRegistry.php');
1526         $registry =& HTML_QuickForm_RuleRegistry::singleton();
1527
1528         foreach ($this->_rules as $target => $rules) {
1529             $submitValue = $this->getSubmitValue($target);
1530
1531             foreach ($rules as $rule) {
1532                 if ((isset($rule['group']) && isset($this->_errors[$rule['group']])) ||
1533                      isset($this->_errors[$target])) {
1534                     continue 2;
1535                 }
1536                 // If element is not required and is empty, we shouldn't validate it
1537                 if (!$this->isElementRequired($target)) {
1538                     if (!isset($submitValue) || '' == $submitValue) {
1539                         continue 2;
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]);
1551                         } else {
1552                             $base = str_replace(
1553                                         array('\\', '\''), array('\\\\', '\\\''),
1554                                         substr($target, 0, $pos)
1555                                     );
1556                             $idx  = "['" . str_replace(
1557                                         array('\\', '\'', ']', '['), array('\\\\', '\\\'', '', "']['"),
1558                                         substr($target, $pos + 1, -1)
1559                                     ) . "']";
1560                             eval("\$isUpload = isset(\$this->_submitFiles['{$base}']['name']{$idx});");
1561                         }
1562                         if ($isUpload && (!isset($submitValue['error']) || UPLOAD_ERR_NO_FILE == $submitValue['error'])) {
1563                             continue 2;
1564                         }
1565                     }
1566                 }
1567                 if (isset($rule['dependent']) && is_array($rule['dependent'])) {
1568                     $values = array($submitValue);
1569                     foreach ($rule['dependent'] as $elName) {
1570                         $values[] = $this->getSubmitValue($elName);
1571                     }
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);
1575                 } else {
1576                     $result = $registry->validate($rule['type'], $submitValue, $rule['format'], false);
1577                 }
1578
1579                 if (!$result || (!empty($rule['howmany']) && $rule['howmany'] > (int)$result)) {
1580                     if (isset($rule['group'])) {
1581                         $this->_errors[$rule['group']] = $rule['message'];
1582                     } else {
1583                         $this->_errors[$target] = $rule['message'];
1584                     }
1585                 }
1586             }
1587         }
1588
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;
1594                 } else {
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);
1596                 }
1597             }
1598         }
1599
1600         return (0 == count($this->_errors));
1601     } // end func validate
1602
1603     // }}}
1604     // {{{ freeze()
1605
1606     /**
1607      * Displays elements without HTML input tags
1608      *
1609      * @param    mixed   $elementList       array or string of element(s) to be frozen
1610      * @since     1.0
1611      * @access   public
1612      * @throws   HTML_QuickForm_Error
1613      */
1614     function freeze($elementList=null)
1615     {
1616         if (!isset($elementList)) {
1617             $this->_freezeAll = true;
1618             $elementList = array();
1619         } else {
1620             if (!is_array($elementList)) {
1621                 $elementList = preg_split('/[ ]*,[ ]*/', $elementList);
1622             }
1623             $elementList = array_flip($elementList);
1624         }
1625
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]);
1631             }
1632         }
1633
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);
1636         }
1637         return true;
1638     } // end func freeze
1639
1640     // }}}
1641     // {{{ isFrozen()
1642
1643     /**
1644      * Returns whether or not the whole form is frozen
1645      *
1646      * @since     3.0
1647      * @access    public
1648      * @return    boolean
1649      */
1650     function isFrozen()
1651     {
1652          return $this->_freezeAll;
1653     } // end func isFrozen
1654
1655     // }}}
1656     // {{{ process()
1657
1658     /**
1659      * Performs the form data processing
1660      *
1661      * @param    mixed     $callback        Callback, either function name or array(&$object, 'method')
1662      * @param    bool      $mergeFiles      Whether uploaded files should be processed too
1663      * @since    1.0
1664      * @access   public
1665      * @throws   HTML_QuickForm_Error
1666      * @return   mixed     Whatever value the $callback function returns
1667      */
1668     function process($callback, $mergeFiles = true)
1669     {
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);
1672         }
1673         $values = ($mergeFiles === true) ? HTML_QuickForm::arrayMerge($this->_submitValues, $this->_submitFiles) : $this->_submitValues;
1674         return call_user_func($callback, $values);
1675     } // end func process
1676
1677     // }}}
1678     // {{{ accept()
1679
1680    /**
1681     * Accepts a renderer
1682     *
1683     * @param object     An HTML_QuickForm_Renderer object
1684     * @since 3.0
1685     * @access public
1686     * @return void
1687     */
1688     function accept(&$renderer)
1689     {
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);
1697         }
1698         $renderer->finishForm($this);
1699     } // end func accept
1700
1701     // }}}
1702     // {{{ defaultRenderer()
1703
1704    /**
1705     * Returns a reference to default renderer object
1706     *
1707     * @access public
1708     * @since 3.0
1709     * @return object a default renderer object
1710     */
1711     function &defaultRenderer()
1712     {
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();
1716         }
1717         return $GLOBALS['_HTML_QuickForm_default_renderer'];
1718     } // end func defaultRenderer
1719
1720     // }}}
1721     // {{{ toHtml ()
1722
1723     /**
1724      * Returns an HTML version of the form
1725      *
1726      * @param string $in_data (optional) Any extra data to insert right
1727      *               before form is rendered.  Useful when using templates.
1728      *
1729      * @return   string     Html version of the form
1730      * @since     1.0
1731      * @access   public
1732      */
1733     function toHtml ($in_data = null)
1734     {
1735         if (!is_null($in_data)) {
1736             $this->addElement('html', $in_data);
1737         }
1738         $renderer =& $this->defaultRenderer();
1739         $this->accept($renderer);
1740         return $renderer->toHtml();
1741     } // end func toHtml
1742
1743     // }}}
1744     // {{{ getValidationScript()
1745
1746     /**
1747      * Returns the client side validation script
1748      *
1749      * @since     2.0
1750      * @access    public
1751      * @return    string    Javascript to perform validation, empty string if no 'client' rules were added
1752      */
1753     function getValidationScript()
1754     {
1755         if (empty($this->_rules) || empty($this->_attributes['onsubmit'])) {
1756             return '';
1757         }
1758
1759         include_once('HTML/QuickForm/RuleRegistry.php');
1760         $registry =& HTML_QuickForm_RuleRegistry::singleton();
1761         $test = array();
1762         $js_escape = array(
1763             "\r"    => '\r',
1764             "\n"    => '\n',
1765             "\t"    => '\t',
1766             "'"     => "\\'",
1767             '"'     => '\"',
1768             '\\'    => '\\\\'
1769         );
1770
1771         foreach ($this->_rules as $elementName => $rules) {
1772             foreach ($rules as $rule) {
1773                 if ('client' == $rule['validation']) {
1774                     unset($element);
1775
1776                     $dependent  = isset($rule['dependent']) && is_array($rule['dependent']);
1777                     $rule['message'] = strtr($rule['message'], $js_escape);
1778
1779                     if (isset($rule['group'])) {
1780                         $group    =& $this->getElement($rule['group']);
1781                         // No JavaScript validation for frozen elements
1782                         if ($group->isFrozen()) {
1783                             continue 2;
1784                         }
1785                         $elements =& $group->getElements();
1786                         foreach (array_keys($elements) as $key) {
1787                             if ($elementName == $group->getElementName($key)) {
1788                                 $element =& $elements[$key];
1789                                 break;
1790                             }
1791                         }
1792                     } elseif ($dependent) {
1793                         $element   =  array();
1794                         $element[] =& $this->getElement($elementName);
1795                         foreach ($rule['dependent'] as $elName) {
1796                             $element[] =& $this->getElement($elName);
1797                         }
1798                     } else {
1799                         $element =& $this->getElement($elementName);
1800                     }
1801                     // No JavaScript validation for frozen elements
1802                     if (is_object($element) && $element->isFrozen()) {
1803                         continue 2;
1804                     } elseif (is_array($element)) {
1805                         foreach (array_keys($element) as $key) {
1806                             if ($element[$key]->isFrozen()) {
1807                                 continue 3;
1808                             }
1809                         }
1810                     }
1811
1812                     $test[] = $registry->getValidationScript($element, $elementName, $rule);
1813                 }
1814             }
1815         }
1816         if (count($test) > 0) {
1817             return
1818                 "\n<script type=\"text/javascript\">\n" .
1819                 "//<![CDATA[\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" .
1825                 join("\n", $test) .
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" .
1831                 "  }\n" .
1832                 "  return true;\n" .
1833                 "}\n" .
1834                 "//]]>\n" .
1835                 "</script>";
1836         }
1837         return '';
1838     } // end func getValidationScript
1839
1840     // }}}
1841     // {{{ getSubmitValues()
1842
1843     /**
1844      * Returns the values submitted by the form
1845      *
1846      * @since     2.0
1847      * @access    public
1848      * @param     bool      Whether uploaded files should be returned too
1849      * @return    array
1850      */
1851     function getSubmitValues($mergeFiles = false)
1852     {
1853         return $mergeFiles? HTML_QuickForm::arrayMerge($this->_submitValues, $this->_submitFiles): $this->_submitValues;
1854     } // end func getSubmitValues
1855
1856     // }}}
1857     // {{{ toArray()
1858
1859     /**
1860      * Returns the form's contents in an array.
1861      *
1862      * The description of the array structure is in HTML_QuickForm_Renderer_Array docs
1863      *
1864      * @since     2.0
1865      * @access    public
1866      * @param     bool      Whether to collect hidden elements (passed to the Renderer's constructor)
1867      * @return    array of form contents
1868      */
1869     function toArray($collectHidden = false)
1870     {
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
1876
1877     // }}}
1878     // {{{ exportValue()
1879
1880     /**
1881      * Returns a 'safe' element's value
1882      *
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.
1886      *
1887      * @param  string   Name of an element
1888      * @access public
1889      * @return mixed
1890      * @throws HTML_QuickForm_Error
1891      */
1892     function exportValue($element)
1893     {
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);
1896         }
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)) {
1902                         $value[] = $v;
1903                     } else {
1904                         $value = (null === $value)? $v: array($value, $v);
1905                     }
1906                 }
1907             }
1908         }
1909         return $value;
1910     }
1911
1912     // }}}
1913     // {{{ exportValues()
1914
1915     /**
1916      * Returns 'safe' elements' values
1917      *
1918      * Unlike getSubmitValues(), this will return only the values
1919      * corresponding to the elements present in the form.
1920      *
1921      * @param   mixed   Array/string of element names, whose values we want. If not set then return all elements.
1922      * @access  public
1923      * @return  array   An assoc array of elements' values
1924      * @throws  HTML_QuickForm_Error
1925      */
1926     function exportValues($elementList = null)
1927     {
1928         $values = array();
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);
1936                 }
1937             }
1938         } else {
1939             if (!is_array($elementList)) {
1940                 $elementList = array_map('trim', explode(',', $elementList));
1941             }
1942             foreach ($elementList as $elementName) {
1943                 $value = $this->exportValue($elementName);
1944                 if (PEAR::isError($value)) {
1945                     return $value;
1946                 }
1947                 $values[$elementName] = $value;
1948             }
1949         }
1950         return $values;
1951     }
1952
1953     // }}}
1954     // {{{ isSubmitted()
1955
1956    /**
1957     * Tells whether the form was already submitted
1958     *
1959     * This is useful since the _submitFiles and _submitValues arrays
1960     * may be completely empty after the trackSubmit value is removed.
1961     *
1962     * @access public
1963     * @return bool
1964     */
1965     function isSubmitted()
1966     {
1967         return $this->_flagSubmitted;
1968     }
1969
1970
1971     // }}}
1972     // {{{ isError()
1973
1974     /**
1975      * Tell whether a result from a QuickForm method is an error (an instance of HTML_QuickForm_Error)
1976      *
1977      * @access public
1978      * @param mixed     result code
1979      * @return bool     whether $value is an error
1980      * @static
1981      */
1982     function isError($value)
1983     {
1984         return (is_object($value) && is_a($value, 'html_quickform_error'));
1985     } // end func isError
1986
1987     // }}}
1988     // {{{ errorMessage()
1989
1990     /**
1991      * Return a textual error message for an QuickForm error code
1992      *
1993      * @access  public
1994      * @param   int     error code
1995      * @return  string  error message
1996      * @static
1997      */
1998     function errorMessage($value)
1999     {
2000         // make the variable static so that it only has to do the defining on the first call
2001         static $errorMessages;
2002
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'
2016             );
2017         }
2018
2019         // If this is an error object, then grab the corresponding error code
2020         if (HTML_QuickForm::isError($value)) {
2021             $value = $value->getCode();
2022         }
2023
2024         // return the textual error message corresponding to the code
2025         return isset($errorMessages[$value]) ? $errorMessages[$value] : $errorMessages[QUICKFORM_ERROR];
2026     } // end func errorMessage
2027
2028     // }}}
2029 } // end class HTML_QuickForm
2030
2031 /**
2032  * Class for errors thrown by HTML_QuickForm package
2033  *
2034  * @category    HTML
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
2039  */
2040 class HTML_QuickForm_Error extends PEAR_Error {
2041
2042     // {{{ properties
2043
2044     /**
2045     * Prefix for all error messages
2046     * @var string
2047     */
2048     var $error_message_prefix = 'QuickForm Error: ';
2049
2050     // }}}
2051     // {{{ constructor
2052
2053     /**
2054     * Creates a quickform error object, extending the PEAR_Error class
2055     *
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
2060     */
2061     function HTML_QuickForm_Error($code = QUICKFORM_ERROR, $mode = PEAR_ERROR_RETURN,
2062                          $level = E_USER_NOTICE, $debuginfo = null)
2063     {
2064         if (is_int($code)) {
2065             $this->PEAR_Error(HTML_QuickForm::errorMessage($code), $code, $mode, $level, $debuginfo);
2066         } else {
2067             $this->PEAR_Error("Invalid error code: $code", QUICKFORM_ERROR, $mode, $level, $debuginfo);
2068         }
2069     }
2070
2071     // }}}
2072 } // end class HTML_QuickForm_Error
2073 ?>