Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

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