Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 3.9.x will end* 10 May 2021 (12 months).
  • Bug fixes for security issues in 3.9.x will end* 8 May 2023 (36 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 39 and 310] [Versions 39 and 311] [Versions 39 and 400] [Versions 39 and 401] [Versions 39 and 402] [Versions 39 and 403]

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