Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

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

   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 < 5; $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     $separator      (optional)string to separate elements
 725       * @param    string     $appendName     (optional)specify whether the group name should be
 726       *                                      used in the form element name ex: group[element]
 727       * @return   object     reference to added group of elements
 728       * @since    2.8
 729       * @access   public
 730       * @throws   PEAR_Error
 731       */
 732      function &addGroup($elements, $name=null, $groupLabel='', $separator=null, $appendName = true)
 733      {
 734          static $anonGroups = 1;
 735  
 736          if (0 == strlen($name)) {
 737              $name       = 'qf_group_' . $anonGroups++;
 738              $appendName = false;
 739          }
 740          $group =& $this->addElement('group', $name, $groupLabel, $elements, $separator, $appendName);
 741          return $group;
 742      } // end func addGroup
 743  
 744      // }}}
 745      // {{{ &getElement()
 746  
 747      /**
 748       * Returns a reference to the element
 749       *
 750       * @param     string     $element    Element name
 751       * @since     2.0
 752       * @access    public
 753       * @return    object     reference to element
 754       * @throws    HTML_QuickForm_Error
 755       */
 756      function &getElement($element)
 757      {
 758          if (isset($this->_elementIndex[$element])) {
 759              return $this->_elements[$this->_elementIndex[$element]];
 760          } else {
 761              $error = self::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$element' does not exist in HTML_QuickForm::getElement()", 'HTML_QuickForm_Error', true);
 762              return $error;
 763          }
 764      } // end func getElement
 765  
 766      // }}}
 767      // {{{ &getElementValue()
 768  
 769      /**
 770       * Returns the element's raw value
 771       *
 772       * This returns the value as submitted by the form (not filtered)
 773       * or set via setDefaults() or setConstants()
 774       *
 775       * @param     string     $element    Element name
 776       * @since     2.0
 777       * @access    public
 778       * @return    mixed     element value
 779       * @throws    HTML_QuickForm_Error
 780       */
 781      function &getElementValue($element)
 782      {
 783          if (!isset($this->_elementIndex[$element])) {
 784              $error = self::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$element' does not exist in HTML_QuickForm::getElementValue()", 'HTML_QuickForm_Error', true);
 785              return $error;
 786          }
 787          $value = $this->_elements[$this->_elementIndex[$element]]->getValue();
 788          if (isset($this->_duplicateIndex[$element])) {
 789              foreach ($this->_duplicateIndex[$element] as $index) {
 790                  if (null !== ($v = $this->_elements[$index]->getValue())) {
 791                      if (is_array($value)) {
 792                          $value[] = $v;
 793                      } else {
 794                          $value = (null === $value)? $v: array($value, $v);
 795                      }
 796                  }
 797              }
 798          }
 799          return $value;
 800      } // end func getElementValue
 801  
 802      // }}}
 803      // {{{ getSubmitValue()
 804  
 805      /**
 806       * Returns the elements value after submit and filter
 807       *
 808       * @param     string     Element name
 809       * @since     2.0
 810       * @access    public
 811       * @return    mixed     submitted element value or null if not set
 812       */
 813      function getSubmitValue($elementName)
 814      {
 815          $value = null;
 816          if (isset($this->_submitValues[$elementName]) || isset($this->_submitFiles[$elementName])) {
 817              $value = isset($this->_submitValues[$elementName])? $this->_submitValues[$elementName]: array();
 818              if (is_array($value) && isset($this->_submitFiles[$elementName])) {
 819                  foreach ($this->_submitFiles[$elementName] as $k => $v) {
 820                      $value = HTML_QuickForm::arrayMerge($value, $this->_reindexFiles($this->_submitFiles[$elementName][$k], $k));
 821                  }
 822              }
 823  
 824          } elseif ('file' == $this->getElementType($elementName)) {
 825              return $this->getElementValue($elementName);
 826  
 827          } elseif (false !== ($pos = strpos($elementName, '['))) {
 828              $base = substr($elementName, 0, $pos);
 829              $keys = str_replace(
 830                  array('\\', '\'', ']', '['), array('\\\\', '\\\'', '', "']['"),
 831                  substr($elementName, $pos + 1, -1)
 832              );
 833              $idx  = "['" . $keys . "']";
 834              $keyArray = explode("']['", $keys);
 835  
 836              if (isset($this->_submitValues[$base])) {
 837                  $value = HTML_QuickForm_utils::recursiveValue($this->_submitValues[$base], $keyArray, NULL);
 838              }
 839  
 840              if ((is_array($value) || null === $value) && isset($this->_submitFiles[$base])) {
 841                  $props = array('name', 'type', 'size', 'tmp_name', 'error');
 842                  $code  = "if (!isset(\$this->_submitFiles['{$base}']['name']{$idx})) {\n" .
 843                           "    return null;\n" .
 844                           "} else {\n" .
 845                           "    \$v = array();\n";
 846                  foreach ($props as $prop) {
 847                      $code .= "    \$v = HTML_QuickForm::arrayMerge(\$v, \$this->_reindexFiles(\$this->_submitFiles['{$base}']['{$prop}']{$idx}, '{$prop}'));\n";
 848                  }
 849                  $fileValue = eval($code . "    return \$v;\n}\n");
 850                  if (null !== $fileValue) {
 851                      $value = null === $value? $fileValue: HTML_QuickForm::arrayMerge($value, $fileValue);
 852                  }
 853              }
 854          }
 855  
 856          // This is only supposed to work for groups with appendName = false
 857          if (null === $value && 'group' == $this->getElementType($elementName)) {
 858              $group    =& $this->getElement($elementName);
 859              $elements =& $group->getElements();
 860              foreach (array_keys($elements) as $key) {
 861                  $name = $group->getElementName($key);
 862                  // prevent endless recursion in case of radios and such
 863                  if ($name != $elementName) {
 864                      if (null !== ($v = $this->getSubmitValue($name))) {
 865                          $value[$name] = $v;
 866                      }
 867                  }
 868              }
 869          }
 870          return $value;
 871      } // end func getSubmitValue
 872  
 873      // }}}
 874      // {{{ _reindexFiles()
 875  
 876     /**
 877      * A helper function to change the indexes in $_FILES array
 878      *
 879      * @param  mixed   Some value from the $_FILES array
 880      * @param  string  The key from the $_FILES array that should be appended
 881      * @return array
 882      */
 883      function _reindexFiles($value, $key)
 884      {
 885          if (!is_array($value)) {
 886              return array($key => $value);
 887          } else {
 888              $ret = array();
 889              foreach ($value as $k => $v) {
 890                  $ret[$k] = $this->_reindexFiles($v, $key);
 891              }
 892              return $ret;
 893          }
 894      }
 895  
 896      // }}}
 897      // {{{ getElementError()
 898  
 899      /**
 900       * Returns error corresponding to validated element
 901       *
 902       * @param     string    $element        Name of form element to check
 903       * @since     1.0
 904       * @access    public
 905       * @return    string    error message corresponding to checked element
 906       */
 907      function getElementError($element)
 908      {
 909          if (isset($this->_errors[$element])) {
 910              return $this->_errors[$element];
 911          }
 912      } // end func getElementError
 913  
 914      // }}}
 915      // {{{ setElementError()
 916  
 917      /**
 918       * Set error message for a form element
 919       *
 920       * @param     string    $element    Name of form element to set error for
 921       * @param     string    $message    Error message, if empty then removes the current error message
 922       * @since     1.0
 923       * @access    public
 924       * @return    void
 925       */
 926      function setElementError($element, $message = null)
 927      {
 928          if (!empty($message)) {
 929              $this->_errors[$element] = $message;
 930          } else {
 931              unset($this->_errors[$element]);
 932          }
 933      } // end func setElementError
 934  
 935       // }}}
 936       // {{{ getElementType()
 937  
 938       /**
 939        * Returns the type of the given element
 940        *
 941        * @param      string    $element    Name of form element
 942        * @since      1.1
 943        * @access     public
 944        * @return     string    Type of the element, false if the element is not found
 945        */
 946       function getElementType($element)
 947       {
 948           if (isset($this->_elementIndex[$element])) {
 949               return $this->_elements[$this->_elementIndex[$element]]->getType();
 950           }
 951           return false;
 952       } // end func getElementType
 953  
 954       // }}}
 955       // {{{ updateElementAttr()
 956  
 957      /**
 958       * Updates Attributes for one or more elements
 959       *
 960       * @param      mixed    $elements   Array of element names/objects or string of elements to be updated
 961       * @param      mixed    $attrs      Array or sting of html attributes
 962       * @since      2.10
 963       * @access     public
 964       * @return     void
 965       */
 966      function updateElementAttr($elements, $attrs)
 967      {
 968          if (is_string($elements)) {
 969              $elements = preg_split('/[ ]?,[ ]?/', $elements);
 970          }
 971          foreach (array_keys($elements) as $key) {
 972              if (is_object($elements[$key]) && is_a($elements[$key], 'HTML_QuickForm_element')) {
 973                  $elements[$key]->updateAttributes($attrs);
 974              } elseif (isset($this->_elementIndex[$elements[$key]])) {
 975                  $this->_elements[$this->_elementIndex[$elements[$key]]]->updateAttributes($attrs);
 976                  if (isset($this->_duplicateIndex[$elements[$key]])) {
 977                      foreach ($this->_duplicateIndex[$elements[$key]] as $index) {
 978                          $this->_elements[$index]->updateAttributes($attrs);
 979                      }
 980                  }
 981              }
 982          }
 983      } // end func updateElementAttr
 984  
 985      // }}}
 986      // {{{ removeElement()
 987  
 988      /**
 989       * Removes an element
 990       *
 991       * The method "unlinks" an element from the form, returning the reference
 992       * to the element object. If several elements named $elementName exist,
 993       * it removes the first one, leaving the others intact.
 994       *
 995       * @param string    $elementName The element name
 996       * @param boolean   $removeRules True if rules for this element are to be removed too
 997       * @access public
 998       * @since 2.0
 999       * @return object HTML_QuickForm_element    a reference to the removed element
1000       * @throws HTML_QuickForm_Error
1001       */
1002      function &removeElement($elementName, $removeRules = true)
1003      {
1004          if (!isset($this->_elementIndex[$elementName])) {
1005              $error = self::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$elementName' does not exist in HTML_QuickForm::removeElement()", 'HTML_QuickForm_Error', true);
1006              return $error;
1007          }
1008          $el =& $this->_elements[$this->_elementIndex[$elementName]];
1009          unset($this->_elements[$this->_elementIndex[$elementName]]);
1010          if (empty($this->_duplicateIndex[$elementName])) {
1011              unset($this->_elementIndex[$elementName]);
1012          } else {
1013              $this->_elementIndex[$elementName] = array_shift($this->_duplicateIndex[$elementName]);
1014          }
1015          if ($removeRules) {
1016              unset($this->_rules[$elementName], $this->_errors[$elementName]);
1017          }
1018          return $el;
1019      } // end func removeElement
1020  
1021      // }}}
1022      // {{{ addRule()
1023  
1024      /**
1025       * Adds a validation rule for the given field
1026       *
1027       * If the element is in fact a group, it will be considered as a whole.
1028       * To validate grouped elements as separated entities,
1029       * use addGroupRule instead of addRule.
1030       *
1031       * @param    string     $element       Form element name
1032       * @param    string     $message       Message to display for invalid data
1033       * @param    string     $type          Rule type, use getRegisteredRules() to get types
1034       * @param    string     $format        (optional)Required for extra rule data
1035       * @param    string     $validation    (optional)Where to perform validation: "server", "client"
1036       * @param    boolean    $reset         Client-side validation: reset the form element to its original value if there is an error?
1037       * @param    boolean    $force         Force the rule to be applied, even if the target form element does not exist
1038       * @since    1.0
1039       * @access   public
1040       * @throws   HTML_QuickForm_Error
1041       */
1042      function addRule($element, $message, $type, $format=null, $validation='server', $reset = false, $force = false)
1043      {
1044          if (!$force) {
1045              if (!is_array($element) && !$this->elementExists($element)) {
1046                  return self::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$element' does not exist in HTML_QuickForm::addRule()", 'HTML_QuickForm_Error', true);
1047              } elseif (is_array($element)) {
1048                  foreach ($element as $el) {
1049                      if (!$this->elementExists($el)) {
1050                          return self::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$el' does not exist in HTML_QuickForm::addRule()", 'HTML_QuickForm_Error', true);
1051                      }
1052                  }
1053              }
1054          }
1055          if (false === ($newName = $this->isRuleRegistered($type, true))) {
1056              return self::raiseError(null, QUICKFORM_INVALID_RULE, null, E_USER_WARNING, "Rule '$type' is not registered in HTML_QuickForm::addRule()", 'HTML_QuickForm_Error', true);
1057          } elseif (is_string($newName)) {
1058              $type = $newName;
1059          }
1060          if (is_array($element)) {
1061              $dependent = $element;
1062              $element   = array_shift($dependent);
1063          } else {
1064              $dependent = null;
1065          }
1066          if ($type == 'required' || $type == 'uploadedfile') {
1067              $this->_required[] = $element;
1068          }
1069          if (!isset($this->_rules[$element])) {
1070              $this->_rules[$element] = array();
1071          }
1072          $this->_rules[$element][] = array(
1073              'type'        => $type,
1074              'format'      => $format,
1075              'message'     => $message,
1076              'validation'  => $validation,
1077              'reset'       => $reset,
1078              'dependent'   => $dependent
1079          );
1080      } // end func addRule
1081  
1082      // }}}
1083      // {{{ addGroupRule()
1084  
1085      /**
1086       * Adds a validation rule for the given group of elements
1087       *
1088       * Only groups with a name can be assigned a validation rule
1089       * Use addGroupRule when you need to validate elements inside the group.
1090       * Use addRule if you need to validate the group as a whole. In this case,
1091       * the same rule will be applied to all elements in the group.
1092       * Use addRule if you need to validate the group against a function.
1093       *
1094       * @param    string     $group         Form group name
1095       * @param    mixed      $arg1          Array for multiple elements or error message string for one element
1096       * @param    string     $type          (optional)Rule type use getRegisteredRules() to get types
1097       * @param    string     $format        (optional)Required for extra rule data
1098       * @param    int        $howmany       (optional)How many valid elements should be in the group
1099       * @param    string     $validation    (optional)Where to perform validation: "server", "client"
1100       * @param    bool       $reset         Client-side: whether to reset the element's value to its original state if validation failed.
1101       * @since    2.5
1102       * @access   public
1103       * @throws   HTML_QuickForm_Error
1104       */
1105      function addGroupRule($group, $arg1, $type='', $format=null, $howmany=0, $validation = 'server', $reset = false)
1106      {
1107          if (!$this->elementExists($group)) {
1108              return self::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Group '$group' does not exist in HTML_QuickForm::addGroupRule()", 'HTML_QuickForm_Error', true);
1109          }
1110  
1111          $groupObj =& $this->getElement($group);
1112          if (is_array($arg1)) {
1113              $required = 0;
1114              foreach ($arg1 as $elementIndex => $rules) {
1115                  $elementName = $groupObj->getElementName($elementIndex);
1116                  foreach ($rules as $rule) {
1117                      $format = (isset($rule[2])) ? $rule[2] : null;
1118                      $validation = (isset($rule[3]) && 'client' == $rule[3])? 'client': 'server';
1119                      $reset = isset($rule[4]) && $rule[4];
1120                      $type = $rule[1];
1121                      if (false === ($newName = $this->isRuleRegistered($type, true))) {
1122                          return self::raiseError(null, QUICKFORM_INVALID_RULE, null, E_USER_WARNING, "Rule '$type' is not registered in HTML_QuickForm::addGroupRule()", 'HTML_QuickForm_Error', true);
1123                      } elseif (is_string($newName)) {
1124                          $type = $newName;
1125                      }
1126  
1127                      $this->_rules[$elementName][] = array(
1128                                                          'type'        => $type,
1129                                                          'format'      => $format,
1130                                                          'message'     => $rule[0],
1131                                                          'validation'  => $validation,
1132                                                          'reset'       => $reset,
1133                                                          'group'       => $group);
1134  
1135                      if ('required' == $type || 'uploadedfile' == $type) {
1136                          $groupObj->_required[] = $elementName;
1137                          $this->_required[] = $elementName;
1138                          $required++;
1139                      }
1140                  }
1141              }
1142              if ($required > 0 && count($groupObj->getElements()) == $required) {
1143                  $this->_required[] = $group;
1144              }
1145          } elseif (is_string($arg1)) {
1146              if (false === ($newName = $this->isRuleRegistered($type, true))) {
1147                  return self::raiseError(null, QUICKFORM_INVALID_RULE, null, E_USER_WARNING, "Rule '$type' is not registered in HTML_QuickForm::addGroupRule()", 'HTML_QuickForm_Error', true);
1148              } elseif (is_string($newName)) {
1149                  $type = $newName;
1150              }
1151  
1152              // addGroupRule() should also handle <select multiple>
1153              if (is_a($groupObj, 'html_quickform_group')) {
1154                  // Radios need to be handled differently when required
1155                  if ($type == 'required' && $groupObj->getGroupType() == 'radio') {
1156                      $howmany = ($howmany == 0) ? 1 : $howmany;
1157                  } else {
1158                      $howmany = ($howmany == 0) ? count($groupObj->getElements()) : $howmany;
1159                  }
1160              }
1161  
1162              $this->_rules[$group][] = array('type'       => $type,
1163                                              'format'     => $format,
1164                                              'message'    => $arg1,
1165                                              'validation' => $validation,
1166                                              'howmany'    => $howmany,
1167                                              'reset'      => $reset);
1168              if ($type == 'required') {
1169                  $this->_required[] = $group;
1170              }
1171          }
1172      } // end func addGroupRule
1173  
1174      // }}}
1175      // {{{ addFormRule()
1176  
1177     /**
1178      * Adds a global validation rule
1179      *
1180      * This should be used when for a rule involving several fields or if
1181      * you want to use some completely custom validation for your form.
1182      * The rule function/method should return true in case of successful
1183      * validation and array('element name' => 'error') when there were errors.
1184      *
1185      * @access   public
1186      * @param    mixed   Callback, either function name or array(&$object, 'method')
1187      * @throws   HTML_QuickForm_Error
1188      */
1189      function addFormRule($rule)
1190      {
1191          if (!is_callable($rule)) {
1192              return self::raiseError(null, QUICKFORM_INVALID_RULE, null, E_USER_WARNING, 'Callback function does not exist in HTML_QuickForm::addFormRule()', 'HTML_QuickForm_Error', true);
1193          }
1194          $this->_formRules[] = $rule;
1195      }
1196  
1197      // }}}
1198      // {{{ applyFilter()
1199  
1200      /**
1201       * Applies a data filter for the given field(s)
1202       *
1203       * @param    mixed     $element       Form element name or array of such names
1204       * @param    mixed     $filter        Callback, either function name or array(&$object, 'method')
1205       * @since    2.0
1206       * @access   public
1207       */
1208      function applyFilter($element, $filter)
1209      {
1210          if (!is_callable($filter)) {
1211              return self::raiseError(null, QUICKFORM_INVALID_FILTER, null, E_USER_WARNING, "Callback function does not exist in QuickForm::applyFilter()", 'HTML_QuickForm_Error', true);
1212          }
1213          if ($element == '__ALL__') {
1214              $this->_submitValues = $this->_recursiveFilter($filter, $this->_submitValues);
1215          } else {
1216              if (!is_array($element)) {
1217                  $element = array($element);
1218              }
1219              foreach ($element as $elName) {
1220                  $value = $this->getSubmitValue($elName);
1221                  if (null !== $value) {
1222                      if (false === strpos($elName, '[')) {
1223                          $this->_submitValues[$elName] = $this->_recursiveFilter($filter, $value);
1224                      } else {
1225                          $idx  = "['" . str_replace(array(']', '['), array('', "']['"), $elName) . "']";
1226                          eval("\$this->_submitValues{$idx} = \$this->_recursiveFilter(\$filter, \$value);");
1227                      }
1228                  }
1229              }
1230          }
1231      } // end func applyFilter
1232  
1233      // }}}
1234      // {{{ _recursiveFilter()
1235  
1236      /**
1237       * Recursively apply a filter function
1238       *
1239       * @param     string   $filter    filter to apply
1240       * @param     mixed    $value     submitted values
1241       * @since     2.0
1242       * @access    private
1243       * @return    cleaned values
1244       */
1245      function _recursiveFilter($filter, $value)
1246      {
1247          if (is_array($value)) {
1248              $cleanValues = array();
1249              foreach ($value as $k => $v) {
1250                  $cleanValues[$k] = $this->_recursiveFilter($filter, $v);
1251              }
1252              return $cleanValues;
1253          } else {
1254              return call_user_func($filter, $value);
1255          }
1256      } // end func _recursiveFilter
1257  
1258      // }}}
1259      // {{{ arrayMerge()
1260  
1261     /**
1262      * Merges two arrays
1263      *
1264      * Merges two array like the PHP function array_merge but recursively.
1265      * The main difference is that existing keys will not be renumbered
1266      * if they are integers.
1267      *
1268      * @access   puplic
1269      * @param    array   $a  original array
1270      * @param    array   $b  array which will be merged into first one
1271      * @return   array   merged array
1272      */
1273      static function arrayMerge($a, $b)
1274      {
1275          if (is_null($a)) {$a = array();}
1276          if (is_null($b)) {$b = array();}
1277          foreach ($b as $k => $v) {
1278              if (is_array($v)) {
1279                  if (isset($a[$k]) && !is_array($a[$k])) {
1280                      $a[$k] = $v;
1281                  } else {
1282                      if (!isset($a[$k])) {
1283                          $a[$k] = array();
1284                      }
1285                      $a[$k] = HTML_QuickForm::arrayMerge($a[$k], $v);
1286                  }
1287              } else {
1288                  $a[$k] = $v;
1289              }
1290          }
1291          return $a;
1292      } // end func arrayMerge
1293  
1294      // }}}
1295      // {{{ isTypeRegistered()
1296  
1297      /**
1298       * Returns whether or not the form element type is supported
1299       *
1300       * @param     string   $type     Form element type
1301       * @since     1.0
1302       * @access    public
1303       * @return    boolean
1304       */
1305      function isTypeRegistered($type)
1306      {
1307          return isset($GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES'][strtolower($type)]);
1308      } // end func isTypeRegistered
1309  
1310      // }}}
1311      // {{{ getRegisteredTypes()
1312  
1313      /**
1314       * Returns an array of registered element types
1315       *
1316       * @since     1.0
1317       * @access    public
1318       * @return    array
1319       */
1320      function getRegisteredTypes()
1321      {
1322          return array_keys($GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES']);
1323      } // end func getRegisteredTypes
1324  
1325      // }}}
1326      // {{{ isRuleRegistered()
1327  
1328      /**
1329       * Returns whether or not the given rule is supported
1330       *
1331       * @param     string   $name    Validation rule name
1332       * @param     bool     Whether to automatically register subclasses of HTML_QuickForm_Rule
1333       * @since     1.0
1334       * @access    public
1335       * @return    mixed    true if previously registered, false if not, new rule name if auto-registering worked
1336       */
1337      function isRuleRegistered($name, $autoRegister = false)
1338      {
1339          if (is_scalar($name) && isset($GLOBALS['_HTML_QuickForm_registered_rules'][$name])) {
1340              return true;
1341          } elseif (!$autoRegister) {
1342              return false;
1343          }
1344          // automatically register the rule if requested
1345          include_once 'HTML/QuickForm/RuleRegistry.php';
1346          $ruleName = false;
1347          if (is_object($name) && is_a($name, 'html_quickform_rule')) {
1348              $ruleName = !empty($name->name)? $name->name: strtolower(get_class($name));
1349          } elseif (is_string($name) && class_exists($name)) {
1350              $parent = strtolower($name);
1351              do {
1352                  if ('html_quickform_rule' == strtolower($parent)) {
1353                      $ruleName = strtolower($name);
1354                      break;
1355                  }
1356              } while ($parent = get_parent_class($parent));
1357          }
1358          if ($ruleName) {
1359              $registry =& HTML_QuickForm_RuleRegistry::singleton();
1360              $registry->registerRule($ruleName, null, $name);
1361          }
1362          return $ruleName;
1363      } // end func isRuleRegistered
1364  
1365      // }}}
1366      // {{{ getRegisteredRules()
1367  
1368      /**
1369       * Returns an array of registered validation rules
1370       *
1371       * @since     1.0
1372       * @access    public
1373       * @return    array
1374       */
1375      function getRegisteredRules()
1376      {
1377          return array_keys($GLOBALS['_HTML_QuickForm_registered_rules']);
1378      } // end func getRegisteredRules
1379  
1380      // }}}
1381      // {{{ isElementRequired()
1382  
1383      /**
1384       * Returns whether or not the form element is required
1385       *
1386       * @param     string   $element     Form element name
1387       * @since     1.0
1388       * @access    public
1389       * @return    boolean
1390       */
1391      function isElementRequired($element)
1392      {
1393          return in_array($element, $this->_required, true);
1394      } // end func isElementRequired
1395  
1396      // }}}
1397      // {{{ isElementFrozen()
1398  
1399      /**
1400       * Returns whether or not the form element is frozen
1401       *
1402       * @param     string   $element     Form element name
1403       * @since     1.0
1404       * @access    public
1405       * @return    boolean
1406       */
1407      function isElementFrozen($element)
1408      {
1409           if (isset($this->_elementIndex[$element])) {
1410               return $this->_elements[$this->_elementIndex[$element]]->isFrozen();
1411           }
1412           return false;
1413      } // end func isElementFrozen
1414  
1415      // }}}
1416      // {{{ setJsWarnings()
1417  
1418      /**
1419       * Sets JavaScript warning messages
1420       *
1421       * @param     string   $pref        Prefix warning
1422       * @param     string   $post        Postfix warning
1423       * @since     1.1
1424       * @access    public
1425       * @return    void
1426       */
1427      function setJsWarnings($pref, $post)
1428      {
1429          $this->_jsPrefix = $pref;
1430          $this->_jsPostfix = $post;
1431      } // end func setJsWarnings
1432  
1433      // }}}
1434      // {{{ setRequiredNote()
1435  
1436      /**
1437       * Sets required-note
1438       *
1439       * @param     string   $note        Message indicating some elements are required
1440       * @since     1.1
1441       * @access    public
1442       * @return    void
1443       */
1444      function setRequiredNote($note)
1445      {
1446          $this->_requiredNote = $note;
1447      } // end func setRequiredNote
1448  
1449      // }}}
1450      // {{{ getRequiredNote()
1451  
1452      /**
1453       * Returns the required note
1454       *
1455       * @since     2.0
1456       * @access    public
1457       * @return    string
1458       */
1459      function getRequiredNote()
1460      {
1461          return $this->_requiredNote;
1462      } // end func getRequiredNote
1463  
1464      // }}}
1465      // {{{ validate()
1466  
1467      /**
1468       * Performs the server side validation
1469       * @access    public
1470       * @since     1.0
1471       * @return    boolean   true if no error found
1472       */
1473      function validate()
1474      {
1475          if (count($this->_rules) == 0 && count($this->_formRules) == 0 &&
1476              $this->isSubmitted()) {
1477              return (0 == count($this->_errors));
1478          } elseif (!$this->isSubmitted()) {
1479              return false;
1480          }
1481  
1482          include_once('HTML/QuickForm/RuleRegistry.php');
1483          $registry =& HTML_QuickForm_RuleRegistry::singleton();
1484  
1485          foreach ($this->_rules as $target => $rules) {
1486              $submitValue = $this->getSubmitValue($target);
1487  
1488              foreach ($rules as $rule) {
1489                  if ((isset($rule['group']) && isset($this->_errors[$rule['group']])) ||
1490                       isset($this->_errors[$target])) {
1491                      continue 2;
1492                  }
1493                  // If element is not required and is empty, we shouldn't validate it
1494                  if (!$this->isElementRequired($target)) {
1495                      if (!isset($submitValue) || '' == $submitValue) {
1496                          continue 2;
1497                      // Fix for bug #3501: we shouldn't validate not uploaded files, either.
1498                      // Unfortunately, we can't just use $element->isUploadedFile() since
1499                      // the element in question can be buried in group. Thus this hack.
1500                      } elseif (is_array($submitValue)) {
1501                          if (false === ($pos = strpos($target, '['))) {
1502                              $isUpload = !empty($this->_submitFiles[$target]);
1503                          } else {
1504                              $base = substr($target, 0, $pos);
1505                              $idx  = "['" . str_replace(array(']', '['), array('', "']['"), substr($target, $pos + 1, -1)) . "']";
1506                              eval("\$isUpload = isset(\$this->_submitFiles['{$base}']['name']{$idx});");
1507                          }
1508                          if ($isUpload && (!isset($submitValue['error']) || 0 != $submitValue['error'])) {
1509                              continue 2;
1510                          }
1511                      }
1512                  }
1513                  if (isset($rule['dependent']) && is_array($rule['dependent'])) {
1514                      $values = array($submitValue);
1515                      foreach ($rule['dependent'] as $elName) {
1516                          $values[] = $this->getSubmitValue($elName);
1517                      }
1518                      $result = $registry->validate($rule['type'], $values, $rule['format'], true);
1519                  } elseif (is_array($submitValue) && !isset($rule['howmany'])) {
1520                      $result = $registry->validate($rule['type'], $submitValue, $rule['format'], true);
1521                  } else {
1522                      $result = $registry->validate($rule['type'], $submitValue, $rule['format'], false);
1523                  }
1524  
1525                  if (!$result || (!empty($rule['howmany']) && $rule['howmany'] > (int)$result)) {
1526                      if (isset($rule['group'])) {
1527                          $this->_errors[$rule['group']] = $rule['message'];
1528                      } else {
1529                          $this->_errors[$target] = $rule['message'];
1530                      }
1531                  }
1532              }
1533          }
1534  
1535          // process the global rules now
1536          foreach ($this->_formRules as $rule) {
1537              if (true !== ($res = call_user_func($rule, $this->_submitValues, $this->_submitFiles))) {
1538                  if (is_array($res)) {
1539                      $this->_errors += $res;
1540                  } else {
1541                      return self::raiseError(null, QUICKFORM_ERROR, null, E_USER_WARNING, 'Form rule callback returned invalid value in HTML_QuickForm::validate()', 'HTML_QuickForm_Error', true);
1542                  }
1543              }
1544          }
1545  
1546          return (0 == count($this->_errors));
1547      } // end func validate
1548  
1549      // }}}
1550      // {{{ freeze()
1551  
1552      /**
1553       * Displays elements without HTML input tags
1554       *
1555       * @param    mixed   $elementList       array or string of element(s) to be frozen
1556       * @since     1.0
1557       * @access   public
1558       * @throws   HTML_QuickForm_Error
1559       */
1560      function freeze($elementList=null)
1561      {
1562          if (!isset($elementList)) {
1563              $this->_freezeAll = true;
1564              $elementList = array();
1565          } else {
1566              if (!is_array($elementList)) {
1567                  $elementList = preg_split('/[ ]*,[ ]*/', $elementList);
1568              }
1569              $elementList = array_flip($elementList);
1570          }
1571  
1572          foreach (array_keys($this->_elements) as $key) {
1573              $name = $this->_elements[$key]->getName();
1574              if ($this->_freezeAll || isset($elementList[$name])) {
1575                  $this->_elements[$key]->freeze();
1576                  unset($elementList[$name]);
1577              }
1578          }
1579  
1580          if (!empty($elementList)) {
1581              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);
1582          }
1583          return true;
1584      } // end func freeze
1585  
1586      // }}}
1587      // {{{ isFrozen()
1588  
1589      /**
1590       * Returns whether or not the whole form is frozen
1591       *
1592       * @since     3.0
1593       * @access    public
1594       * @return    boolean
1595       */
1596      function isFrozen()
1597      {
1598           return $this->_freezeAll;
1599      } // end func isFrozen
1600  
1601      // }}}
1602      // {{{ process()
1603  
1604      /**
1605       * Performs the form data processing
1606       *
1607       * @param    mixed     $callback        Callback, either function name or array(&$object, 'method')
1608       * @param    bool      $mergeFiles      Whether uploaded files should be processed too
1609       * @since    1.0
1610       * @access   public
1611       * @throws   HTML_QuickForm_Error
1612       */
1613      function process($callback, $mergeFiles = true)
1614      {
1615          if (!is_callable($callback)) {
1616              return self::raiseError(null, QUICKFORM_INVALID_PROCESS, null, E_USER_WARNING, "Callback function does not exist in QuickForm::process()", 'HTML_QuickForm_Error', true);
1617          }
1618          $values = ($mergeFiles === true) ? HTML_QuickForm::arrayMerge($this->_submitValues, $this->_submitFiles) : $this->_submitValues;
1619          return call_user_func($callback, $values);
1620      } // end func process
1621  
1622      // }}}
1623      // {{{ accept()
1624  
1625     /**
1626      * Accepts a renderer
1627      *
1628      * @param object     An HTML_QuickForm_Renderer object
1629      * @since 3.0
1630      * @access public
1631      * @return void
1632      */
1633      function accept(&$renderer)
1634      {
1635          $renderer->startForm($this);
1636          foreach (array_keys($this->_elements) as $key) {
1637              $element =& $this->_elements[$key];
1638              $elementName = $element->getName();
1639              $required    = ($this->isElementRequired($elementName) && !$element->isFrozen());
1640              $error       = $this->getElementError($elementName);
1641              $element->accept($renderer, $required, $error);
1642          }
1643          $renderer->finishForm($this);
1644      } // end func accept
1645  
1646      // }}}
1647      // {{{ defaultRenderer()
1648  
1649     /**
1650      * Returns a reference to default renderer object
1651      *
1652      * @access public
1653      * @since 3.0
1654      * @return object a default renderer object
1655      */
1656      function &defaultRenderer()
1657      {
1658          if (!isset($GLOBALS['_HTML_QuickForm_default_renderer'])) {
1659              include_once('HTML/QuickForm/Renderer/Default.php');
1660              $GLOBALS['_HTML_QuickForm_default_renderer'] = new HTML_QuickForm_Renderer_Default(); //Moodle: PHP 5.3 compatibility
1661          }
1662          return $GLOBALS['_HTML_QuickForm_default_renderer'];
1663      } // end func defaultRenderer
1664  
1665      // }}}
1666      // {{{ toHtml ()
1667  
1668      /**
1669       * Returns an HTML version of the form
1670       *
1671       * @param string $in_data (optional) Any extra data to insert right
1672       *               before form is rendered.  Useful when using templates.
1673       *
1674       * @return   string     Html version of the form
1675       * @since     1.0
1676       * @access   public
1677       */
1678      function toHtml ($in_data = null)
1679      {
1680          if (!is_null($in_data)) {
1681              $this->addElement('html', $in_data);
1682          }
1683          $renderer =& $this->defaultRenderer();
1684          $this->accept($renderer);
1685          return $renderer->toHtml();
1686      } // end func toHtml
1687  
1688      // }}}
1689      // {{{ getValidationScript()
1690  
1691      /**
1692       * Returns the client side validation script
1693       *
1694       * @since     2.0
1695       * @access    public
1696       * @return    string    Javascript to perform validation, empty string if no 'client' rules were added
1697       */
1698      function getValidationScript()
1699      {
1700          if (empty($this->_rules) || empty($this->_attributes['onsubmit'])) {
1701              return '';
1702          }
1703  
1704          include_once('HTML/QuickForm/RuleRegistry.php');
1705          $registry =& HTML_QuickForm_RuleRegistry::singleton();
1706          $test = array();
1707          $js_escape = array(
1708              "\r"    => '\r',
1709              "\n"    => '\n',
1710              "\t"    => '\t',
1711              "'"     => "\\'",
1712              '"'     => '\"',
1713              '\\'    => '\\\\'
1714          );
1715  
1716          foreach ($this->_rules as $elementName => $rules) {
1717              foreach ($rules as $rule) {
1718                  if ('client' == $rule['validation']) {
1719                      unset($element);
1720  
1721                      $dependent  = isset($rule['dependent']) && is_array($rule['dependent']);
1722                      $rule['message'] = strtr($rule['message'], $js_escape);
1723  
1724                      if (isset($rule['group'])) {
1725                          $group    =& $this->getElement($rule['group']);
1726                          // No JavaScript validation for frozen elements
1727                          if ($group->isFrozen()) {
1728                              continue 2;
1729                          }
1730                          $elements =& $group->getElements();
1731                          foreach (array_keys($elements) as $key) {
1732                              if ($elementName == $group->getElementName($key)) {
1733                                  $element =& $elements[$key];
1734                                  break;
1735                              }
1736                          }
1737                      } elseif ($dependent) {
1738                          $element   =  array();
1739                          $element[] =& $this->getElement($elementName);
1740                          foreach ($rule['dependent'] as $elName) {
1741                              $element[] =& $this->getElement($elName);
1742                          }
1743                      } else {
1744                          $element =& $this->getElement($elementName);
1745                      }
1746                      // No JavaScript validation for frozen elements
1747                      if (is_object($element) && $element->isFrozen()) {
1748                          continue 2;
1749                      } elseif (is_array($element)) {
1750                          foreach (array_keys($element) as $key) {
1751                              if ($element[$key]->isFrozen()) {
1752                                  continue 3;
1753                              }
1754                          }
1755                      }
1756  
1757                      $test[] = $registry->getValidationScript($element, $elementName, $rule);
1758                  }
1759              }
1760          }
1761          if (count($test) > 0) {
1762              return
1763                  "\n<script type=\"text/javascript\">\n" .
1764                  "//<![CDATA[\n" .
1765                  "function validate_" . $this->_attributes['id'] . "(frm) {\n" .
1766                  "  var value = '';\n" .
1767                  "  var errFlag = new Array();\n" .
1768                  "  var _qfGroups = {};\n" .
1769                  "  _qfMsg = '';\n\n" .
1770                  join("\n", $test) .
1771                  "\n  if (_qfMsg != '') {\n" .
1772                  "    _qfMsg = '" . strtr($this->_jsPrefix, $js_escape) . "' + _qfMsg;\n" .
1773                  "    _qfMsg = _qfMsg + '\\n" . strtr($this->_jsPostfix, $js_escape) . "';\n" .
1774                  "    alert(_qfMsg);\n" .
1775                  "    return false;\n" .
1776                  "  }\n" .
1777                  "  return true;\n" .
1778                  "}\n" .
1779                  "//]]>\n" .
1780                  "</script>";
1781          }
1782          return '';
1783      } // end func getValidationScript
1784  
1785      // }}}
1786      // {{{ getSubmitValues()
1787  
1788      /**
1789       * Returns the values submitted by the form
1790       *
1791       * @since     2.0
1792       * @access    public
1793       * @param     bool      Whether uploaded files should be returned too
1794       * @return    array
1795       */
1796      function getSubmitValues($mergeFiles = false)
1797      {
1798          return $mergeFiles? HTML_QuickForm::arrayMerge($this->_submitValues, $this->_submitFiles): $this->_submitValues;
1799      } // end func getSubmitValues
1800  
1801      // }}}
1802      // {{{ toArray()
1803  
1804      /**
1805       * Returns the form's contents in an array.
1806       *
1807       * The description of the array structure is in HTML_QuickForm_Renderer_Array docs
1808       *
1809       * @since     2.0
1810       * @access    public
1811       * @param     bool      Whether to collect hidden elements (passed to the Renderer's constructor)
1812       * @return    array of form contents
1813       */
1814      function toArray($collectHidden = false)
1815      {
1816          include_once 'HTML/QuickForm/Renderer/Array.php';
1817          $renderer = new HTML_QuickForm_Renderer_Array($collectHidden); //Moodle: PHP 5.3 compatibility
1818          $this->accept($renderer);
1819          return $renderer->toArray();
1820       } // end func toArray
1821  
1822      // }}}
1823      // {{{ exportValue()
1824  
1825      /**
1826       * Returns a 'safe' element's value
1827       *
1828       * This method first tries to find a cleaned-up submitted value,
1829       * it will return a value set by setValue()/setDefaults()/setConstants()
1830       * if submitted value does not exist for the given element.
1831       *
1832       * @param  string   Name of an element
1833       * @access public
1834       * @return mixed
1835       */
1836      function exportValue($element)
1837      {
1838          if (!isset($this->_elementIndex[$element])) {
1839              return self::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$element' does not exist in HTML_QuickForm::getElementValue()", 'HTML_QuickForm_Error', true);
1840          }
1841          $value = $this->_elements[$this->_elementIndex[$element]]->exportValue($this->_submitValues, false);
1842          if (isset($this->_duplicateIndex[$element])) {
1843              foreach ($this->_duplicateIndex[$element] as $index) {
1844                  if (null !== ($v = $this->_elements[$index]->exportValue($this->_submitValues, false))) {
1845                      if (is_array($value)) {
1846                          $value[] = $v;
1847                      } else {
1848                          $value = (null === $value)? $v: array($value, $v);
1849                      }
1850                  }
1851              }
1852          }
1853          return $value;
1854      }
1855  
1856      // }}}
1857      // {{{ exportValues()
1858  
1859      /**
1860       * Returns 'safe' elements' values
1861       *
1862       * Unlike getSubmitValues(), this will return only the values
1863       * corresponding to the elements present in the form.
1864       *
1865       * @param   mixed   Array/string of element names, whose values we want. If not set then return all elements.
1866       * @access  public
1867       * @return  array   An assoc array of elements' values
1868       * @throws  HTML_QuickForm_Error
1869       */
1870      function exportValues($elementList = null)
1871      {
1872          $values = array();
1873          if (null === $elementList) {
1874              // iterate over all elements, calling their exportValue() methods
1875              foreach (array_keys($this->_elements) as $key) {
1876                  $value = $this->_elements[$key]->exportValue($this->_submitValues, true);
1877                  if (is_array($value)) {
1878                      // This shit throws a bogus warning in PHP 4.3.x
1879                      $values = HTML_QuickForm::arrayMerge($values, $value);
1880                  }
1881              }
1882          } else {
1883              if (!is_array($elementList)) {
1884                  $elementList = array_map('trim', explode(',', $elementList));
1885              }
1886              foreach ($elementList as $elementName) {
1887                  $value = $this->exportValue($elementName);
1888                  $pear = new PEAR();
1889                  if ($pear->isError($value)) {
1890                      return $value;
1891                  }
1892                  $values[$elementName] = $value;
1893              }
1894          }
1895          return $values;
1896      }
1897  
1898      // }}}
1899      // {{{ isSubmitted()
1900  
1901     /**
1902      * Tells whether the form was already submitted
1903      *
1904      * This is useful since the _submitFiles and _submitValues arrays
1905      * may be completely empty after the trackSubmit value is removed.
1906      *
1907      * @access public
1908      * @return bool
1909      */
1910      function isSubmitted()
1911      {
1912          return $this->_flagSubmitted;
1913      }
1914  
1915  
1916      // }}}
1917      // {{{ isError()
1918  
1919      /**
1920       * Tell whether a result from a QuickForm method is an error (an instance of HTML_QuickForm_Error)
1921       *
1922       * @access public
1923       * @param mixed     result code
1924       * @return bool     whether $value is an error
1925       */
1926      static function isError($value)
1927      {
1928          return (is_object($value) && is_a($value, 'html_quickform_error'));
1929      } // end func isError
1930  
1931      // }}}
1932      // {{{ errorMessage()
1933  
1934      /**
1935       * Return a textual error message for an QuickForm error code
1936       *
1937       * @access  public
1938       * @param   int     error code
1939       * @return  string  error message
1940       */
1941      static function errorMessage($value)
1942      {
1943          // make the variable static so that it only has to do the defining on the first call
1944          static $errorMessages;
1945  
1946          // define the varies error messages
1947          if (!isset($errorMessages)) {
1948              $errorMessages = array(
1949                  QUICKFORM_OK                    => 'no error',
1950                  QUICKFORM_ERROR                 => 'unknown error',
1951                  QUICKFORM_INVALID_RULE          => 'the rule does not exist as a registered rule',
1952                  QUICKFORM_NONEXIST_ELEMENT      => 'nonexistent html element',
1953                  QUICKFORM_INVALID_FILTER        => 'invalid filter',
1954                  QUICKFORM_UNREGISTERED_ELEMENT  => 'unregistered element',
1955                  QUICKFORM_INVALID_ELEMENT_NAME  => 'element already exists',
1956                  QUICKFORM_INVALID_PROCESS       => 'process callback does not exist',
1957                  QUICKFORM_DEPRECATED            => 'method is deprecated',
1958                  QUICKFORM_INVALID_DATASOURCE    => 'datasource is not an object'
1959              );
1960          }
1961  
1962          // If this is an error object, then grab the corresponding error code
1963          if (HTML_QuickForm::isError($value)) {
1964              $value = $value->getCode();
1965          }
1966  
1967          // return the textual error message corresponding to the code
1968          return isset($errorMessages[$value]) ? $errorMessages[$value] : $errorMessages[QUICKFORM_ERROR];
1969      } // end func errorMessage
1970  
1971      // }}}
1972  } // end class HTML_QuickForm
1973  
1974  class HTML_QuickForm_Error extends PEAR_Error {
1975  
1976      // {{{ properties
1977  
1978      /**
1979      * Prefix for all error messages
1980      * @var string
1981      */
1982      var $error_message_prefix = 'QuickForm Error: ';
1983  
1984      // }}}
1985      // {{{ constructor
1986  
1987      /**
1988      * Creates a quickform error object, extending the PEAR_Error class
1989      *
1990      * @param int   $code the error code
1991      * @param int   $mode the reaction to the error, either return, die or trigger/callback
1992      * @param int   $level intensity of the error (PHP error code)
1993      * @param mixed $debuginfo any information that can inform user as to nature of the error
1994      */
1995      public function __construct($code = QUICKFORM_ERROR, $mode = PEAR_ERROR_RETURN,
1996                           $level = E_USER_NOTICE, $debuginfo = null)
1997      {
1998          if (is_int($code)) {
1999              parent::__construct(HTML_QuickForm::errorMessage($code), $code, $mode, $level, $debuginfo);
2000          } else {
2001              parent::__construct("Invalid error code: $code", QUICKFORM_ERROR, $mode, $level, $debuginfo);
2002          }
2003      }
2004  
2005      /**
2006       * Old syntax of class constructor. Deprecated in PHP7.
2007       *
2008       * @deprecated since Moodle 3.1
2009       */
2010      public function HTML_QuickForm_Error($code = QUICKFORM_ERROR, $mode = PEAR_ERROR_RETURN,
2011                           $level = E_USER_NOTICE, $debuginfo = null) {
2012          debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER);
2013          self::__construct($code, $mode, $level, $debuginfo);
2014      }
2015  
2016      // }}}
2017  } // end class HTML_QuickForm_Error
2018  ?>