Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 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 310 and 401] [Versions 310 and 402] [Versions 310 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  // |          Alexey Borzov <borz_off@cs.msu.su>                          |
  18  // |          Bertrand Mansion <bmansion@mamasam.com>                     |
  19  // +----------------------------------------------------------------------+
  20  //
  21  // $Id$
  22  
  23  /**
  24  * Registers rule objects and uses them for validation
  25  *
  26  */
  27  class HTML_QuickForm_RuleRegistry
  28  {
  29      /**
  30       * Array containing references to used rules
  31       * @var     array
  32       * @access  private
  33       */
  34      var $_rules = array();
  35  
  36  
  37      /**
  38       * Returns a singleton of HTML_QuickForm_RuleRegistry
  39       *
  40       * Usually, only one RuleRegistry object is needed, this is the reason
  41       * why it is recommended to use this method to get the validation object.
  42       *
  43       * @access    public
  44       * @static
  45       * @return    object    Reference to the HTML_QuickForm_RuleRegistry singleton
  46       */
  47      static function &singleton()
  48      {
  49          static $obj;
  50          if (!isset($obj)) {
  51              $obj = new HTML_QuickForm_RuleRegistry();
  52          }
  53          return $obj;
  54      } // end func singleton
  55  
  56      /**
  57       * Registers a new validation rule
  58       *
  59       * In order to use a custom rule in your form, you need to register it
  60       * first. For regular expressions, one can directly use the 'regex' type
  61       * rule in addRule(), this is faster than registering the rule.
  62       *
  63       * Functions and methods can be registered. Use the 'function' type.
  64       * When registering a method, specify the class name as second parameter.
  65       *
  66       * You can also register an HTML_QuickForm_Rule subclass with its own
  67       * validate() method.
  68       *
  69       * @param     string    $ruleName   Name of validation rule
  70       * @param     string    $type       Either: 'regex', 'function' or null
  71       * @param     string    $data1      Name of function, regular expression or
  72       *                                  HTML_QuickForm_Rule object class name
  73       * @param     string    $data2      Object parent of above function or HTML_QuickForm_Rule file path
  74       * @access    public
  75       * @return    void
  76       */
  77      function registerRule($ruleName, $type, $data1, $data2 = null)
  78      {
  79          $type = strtolower($type);
  80          if ($type == 'regex') {
  81              // Regular expression
  82              $rule =& $this->getRule('regex');
  83              $rule->addData($ruleName, $data1);
  84              $GLOBALS['_HTML_QuickForm_registered_rules'][$ruleName] = $GLOBALS['_HTML_QuickForm_registered_rules']['regex'];
  85  
  86          } elseif ($type == 'function' || $type == 'callback') {
  87              // Callback function
  88              $rule =& $this->getRule('callback');
  89              $rule->addData($ruleName, $data1, $data2, 'function' == $type);
  90              $GLOBALS['_HTML_QuickForm_registered_rules'][$ruleName] = $GLOBALS['_HTML_QuickForm_registered_rules']['callback'];
  91  
  92          } elseif (is_object($data1)) {
  93              // An instance of HTML_QuickForm_Rule
  94              $this->_rules[strtolower(get_class($data1))] = $data1;
  95              $GLOBALS['_HTML_QuickForm_registered_rules'][$ruleName] = array(strtolower(get_class($data1)), null);
  96  
  97          } else {
  98              // Rule class name
  99              $GLOBALS['_HTML_QuickForm_registered_rules'][$ruleName] = array(strtolower($data1), $data2);
 100          }
 101      } // end func registerRule
 102  
 103      /**
 104       * Returns a reference to the requested rule object
 105       *
 106       * @param     string   $ruleName        Name of the requested rule
 107       * @access    public
 108       * @return    object
 109       */
 110      function &getRule($ruleName)
 111      {
 112          list($class, $path) = $GLOBALS['_HTML_QuickForm_registered_rules'][$ruleName];
 113  
 114          if (!isset($this->_rules[$class])) {
 115              if (!empty($path)) {
 116                  include_once($path);
 117              }
 118              $this->_rules[$class] = new $class();
 119          }
 120          $this->_rules[$class]->setName($ruleName);
 121          return $this->_rules[$class];
 122      } // end func getRule
 123  
 124      /**
 125       * Performs validation on the given values
 126       *
 127       * @param     string   $ruleName        Name of the rule to be used
 128       * @param     mixed    $values          Can be a scalar or an array of values
 129       *                                      to be validated
 130       * @param     mixed    $options         Options used by the rule
 131       * @param     mixed    $multiple        Whether to validate an array of values altogether
 132       * @access    public
 133       * @return    mixed    true if no error found, int of valid values (when an array of values is given) or false if error
 134       */
 135      function validate($ruleName, $values, $options = null, $multiple = false)
 136      {
 137          $rule =& $this->getRule($ruleName);
 138  
 139          if (is_array($values) && !$multiple) {
 140              $result = 0;
 141              foreach ($values as $value) {
 142                  if ($rule->validate($value, $options) === true) {
 143                      $result++;
 144                  }
 145              }
 146              return ($result == 0) ? false : $result;
 147          } else {
 148              return $rule->validate($values, $options);
 149          }
 150      } // end func validate
 151  
 152      /**
 153       * Returns the validation test in javascript code
 154       *
 155       * @param     mixed     Element(s) the rule applies to
 156       * @param     string    Element name, in case $element is not array
 157       * @param     array     Rule data
 158       * @access    public
 159       * @return    string    JavaScript for the rule
 160       */
 161      function getValidationScript(&$element, $elementName, $ruleData)
 162      {
 163          $reset =  (isset($ruleData['reset'])) ? $ruleData['reset'] : false;
 164          $rule  =& $this->getRule($ruleData['type']);
 165          if (!is_array($element)) {
 166              list($jsValue, $jsReset) = $this->_getJsValue($element, $elementName, $reset, null);
 167          } else {
 168              $jsValue = "  value = new Array();\n";
 169              $jsReset = '';
 170              for ($i = 0; $i < count($element); $i++) {
 171                  list($tmp_value, $tmp_reset) = $this->_getJsValue($element[$i], $element[$i]->getName(), $reset, $i);
 172                  $jsValue .= "\n" . $tmp_value;
 173                  $jsReset .= $tmp_reset;
 174              }
 175          }
 176          $jsField = isset($ruleData['group'])? $ruleData['group']: $elementName;
 177          list ($jsPrefix, $jsCheck) = $rule->getValidationScript($ruleData['format']);
 178          if (!isset($ruleData['howmany'])) {
 179              $js = $jsValue . "\n" . $jsPrefix .
 180                    "  if (" . str_replace('{jsVar}', 'value', $jsCheck) . " && !errFlag['{$jsField}']) {\n" .
 181                    "    errFlag['{$jsField}'] = true;\n" .
 182                    "    _qfMsg = _qfMsg + '\\n - {$ruleData['message']}';\n" .
 183                    $jsReset .
 184                    "  }\n";
 185          } else {
 186              $js = $jsValue . "\n" . $jsPrefix .
 187                    "  var res = 0;\n" .
 188                    "  for (var i = 0; i < value.length; i++) {\n" .
 189                    "    if (!(" . str_replace('{jsVar}', 'value[i]', $jsCheck) . ")) {\n" .
 190                    "      res++;\n" .
 191                    "    }\n" .
 192                    "  }\n" .
 193                    "  if (res < {$ruleData['howmany']} && !errFlag['{$jsField}']) {\n" .
 194                    "    errFlag['{$jsField}'] = true;\n" .
 195                    "    _qfMsg = _qfMsg + '\\n - {$ruleData['message']}';\n" .
 196                    $jsReset .
 197                    "  }\n";
 198          }
 199          return $js;
 200      } // end func getValidationScript
 201  
 202  
 203     /**
 204      * Returns JavaScript to get and to reset the element's value
 205      *
 206      * @access private
 207      * @param  object HTML_QuickForm_element     element being processed
 208      * @param  string    element's name
 209      * @param  bool      whether to generate JavaScript to reset the value
 210      * @param  integer   value's index in the array (only used for multielement rules)
 211      * @return array     first item is value javascript, second is reset
 212      */
 213      function _getJsValue(&$element, $elementName, $reset = false, $index = null)
 214      {
 215          $jsIndex = isset($index)? '[' . $index . ']': '';
 216          $tmp_reset = $reset? "    var field = frm.elements['$elementName'];\n": '';
 217          if (is_a($element, 'html_quickform_group')) {
 218              $value = "  _qfGroups['{$elementName}'] = {";
 219              $elements =& $element->getElements();
 220              for ($i = 0, $count = count($elements); $i < $count; $i++) {
 221                  $append = (($elements[$i]->getType() == 'select' || $element->getType() == 'autocomplete') && $elements[$i]->getMultiple())? '[]': '';
 222                  $value .= "'" . $element->getElementName($i) . $append . "': true" .
 223                            ($i < $count - 1? ', ': '');
 224              }
 225              $value .=
 226                  "};\n" .
 227                  "  value{$jsIndex} = new Array();\n" .
 228                  "  var valueIdx = 0;\n" .
 229                  "  for (var i = 0; i < frm.elements.length; i++) {\n" .
 230                  "    var _element = frm.elements[i];\n" .
 231                  "    if (_element.name in _qfGroups['{$elementName}']) {\n" .
 232                  "      switch (_element.type) {\n" .
 233                  "        case 'checkbox':\n" .
 234                  "        case 'radio':\n" .
 235                  "          if (_element.checked) {\n" .
 236                  "            value{$jsIndex}[valueIdx++] = _element.value;\n" .
 237                  "          }\n" .
 238                  "          break;\n" .
 239                  "        case 'select-one':\n" .
 240                  "          if (-1 != _element.selectedIndex) {\n" .
 241                  "            value{$jsIndex}[valueIdx++] = _element.options[_element.selectedIndex].value;\n" .
 242                  "          }\n" .
 243                  "          break;\n" .
 244                  "        case 'select-multiple':\n" .
 245                  "          var tmpVal = new Array();\n" .
 246                  "          var tmpIdx = 0;\n" .
 247                  "          for (var j = 0; j < _element.options.length; j++) {\n" .
 248                  "            if (_element.options[j].selected) {\n" .
 249                  "              tmpVal[tmpIdx++] = _element.options[j].value;\n" .
 250                  "            }\n" .
 251                  "          }\n" .
 252                  "          if (tmpIdx > 0) {\n" .
 253                  "            value{$jsIndex}[valueIdx++] = tmpVal;\n" .
 254                  "          }\n" .
 255                  "          break;\n" .
 256                  "        default:\n" .
 257                  "          value{$jsIndex}[valueIdx++] = _element.value;\n" .
 258                  "      }\n" .
 259                  "    }\n" .
 260                  "  }\n";
 261              if ($reset) {
 262                  $tmp_reset =
 263                      "    for (var i = 0; i < frm.elements.length; i++) {\n" .
 264                      "      var _element = frm.elements[i];\n" .
 265                      "      if (_element.name in _qfGroups['{$elementName}']) {\n" .
 266                      "        switch (_element.type) {\n" .
 267                      "          case 'checkbox':\n" .
 268                      "          case 'radio':\n" .
 269                      "            _element.checked = _element.defaultChecked;\n" .
 270                      "            break;\n" .
 271                      "          case 'select-one':\n" .
 272                      "          case 'select-multiple':\n" .
 273                      "            for (var j = 0; j < _element.options.length; j++) {\n" .
 274                      "              _element.options[j].selected = _element.options[j].defaultSelected;\n" .
 275                      "            }\n" .
 276                      "            break;\n" .
 277                      "          default:\n" .
 278                      "            _element.value = _element.defaultValue;\n" .
 279                      "        }\n" .
 280                      "      }\n" .
 281                      "    }\n";
 282              }
 283  
 284          } elseif ($element->getType() == 'select' || $element->getType() == 'autocomplete') {
 285              if ($element->getMultiple()) {
 286                  $elementName .= '[]';
 287                  $value =
 288                      "  value{$jsIndex} = new Array();\n" .
 289                      "  var valueIdx = 0;\n" .
 290                      "  for (var i = 0; i < frm.elements['{$elementName}'].options.length; i++) {\n" .
 291                      "    if (frm.elements['{$elementName}'].options[i].selected) {\n" .
 292                      "      value{$jsIndex}[valueIdx++] = frm.elements['{$elementName}'].options[i].value;\n" .
 293                      "    }\n" .
 294                      "  }\n";
 295              } else {
 296                  $value = "  value{$jsIndex} = frm.elements['{$elementName}'].selectedIndex == -1? '': frm.elements['{$elementName}'].options[frm.elements['{$elementName}'].selectedIndex].value;\n";
 297              }
 298              if ($reset) {
 299                  $tmp_reset .=
 300                      "    for (var i = 0; i < field.options.length; i++) {\n" .
 301                      "      field.options[i].selected = field.options[i].defaultSelected;\n" .
 302                      "    }\n";
 303              }
 304  
 305          } elseif ($element->getType() == 'advcheckbox') {
 306              $value = "  value{$jsIndex} = frm.elements['$elementName'][1].checked ?" .
 307                      " frm.elements['$elementName'][1].value : frm.elements['$elementName'][0].value;\n";
 308              $tmp_reset .= $reset ? "    field[1].checked = field[1].defaultChecked;\n" : '';
 309          } elseif ($element->getType() == 'checkbox') {
 310              $value = "  value{$jsIndex} = frm.elements['$elementName'].checked? '1': '';\n";
 311              $tmp_reset .= $reset ? "    field.checked = field.defaultChecked;\n" : '';
 312          } elseif ($element->getType() == 'radio') {
 313              $value = "  value{$jsIndex} = '';\n" .
 314                       // Fix for bug #5644
 315                       "  var els = 'length' in frm.elements['$elementName']? frm.elements['$elementName']: [ frm.elements['$elementName'] ];\n" .
 316                       "  for (var i = 0; i < els.length; i++) {\n" .
 317                       "    if (els[i].checked) {\n" .
 318                       "      value{$jsIndex} = els[i].value;\n" .
 319                       "    }\n" .
 320                       "  }";
 321              if ($reset) {
 322                  $tmp_reset .= "    for (var i = 0; i < field.length; i++) {\n" .
 323                                "      field[i].checked = field[i].defaultChecked;\n" .
 324                                "    }";
 325              }
 326  
 327          } else {
 328              $value = "  value{$jsIndex} = frm.elements['$elementName'].value;";
 329              $tmp_reset .= ($reset) ? "    field.value = field.defaultValue;\n" : '';
 330          }
 331          return array($value, $tmp_reset);
 332      }
 333  } // end class HTML_QuickForm_RuleRegistry
 334  ?>