Version: current

      1 <?php
      2 /**
      3  * This validation class handles all form and custom form validation through
      4  * the use of Required fields.
      5  *
      6  * Relies on javascript for client-side validation, and marking fields after serverside validation.
      7  *
      8  * Acts as a visitor to individual form fields.
      9  *
     10  * @todo Automatically mark fields after serverside validation and replace the form through
     11  * FormResponse if the request was made by ajax.
     12  *
     13  * @package forms
     14  * @subpackage validators
     15  */
     16 abstract class Validator extends Object {
     17
     18     /**
     19      * @var Form $form
     20      */
     21     protected $form;
     22
     23     /**
     24      * @var array $errors
     25      */
     26     protected $errors;
     27
     28     /**
     29      * Static for default value of $this->javascriptValidationHandler.
     30      * Set with Validator::set_javascript_validation_handler();
     31      * @var string
     32      */
     33     protected static $javascript_validation_handler = "prototype";
     34
     35     /**
     36      * Handler for javascript validation.  Can be "prototype" or "none".
     37      * @var string
     38      */
     39     protected $javascriptValidationHandler = null;
     40
     41     /**
     42      * Call this function to set the javascript validation handler for all valdiation on your site.
     43      * This could be called from _config.php to set site-wide javascript validation, or from ContentController::init()
     44      * to affect only the front-end site.
     45      * Use instance method {@link setJavascriptValidationHandler()} to
     46      * only set handler for a specific form instance.
     47      *
     48      * @param $handler A string representing the handler to use: 'prototype' or 'none'.
     49      * @todo Add 'jquery' as a handler option.
     50      */
     51     public static function set_javascript_validation_handler($handler) {
     52         if($handler == 'prototype' || $handler == 'none') {
     53             self::$javascript_validation_handler = $handler;
     54         } else {
     55             user_error("Validator::setJavascriptValidationHandler() passed bad handler '$handler'", E_USER_WARNING);
     56         }
     57     }
     58
     59     /**
     60      * Returns global validation handler used for all forms by default,
     61      * unless overwritten by {@link setJavascriptValidationHandler()}.
     62      *
     63      * @return string
     64      */
     65     public static function get_javascript_validator_handler() {
     66         return self::$javascript_validation_handler;
     67     }
     68
     69     /**
     70      * Set JavaScript validation for this validator.
     71      * Use static method {@link set_javascript_validation_handler()}
     72      * to set handlers globally.
     73      *
     74      * @param string $handler
     75      */
     76     public function setJavascriptValidationHandler($handler) {
     77         if($handler == 'prototype' || $handler == 'none') {
     78             $this->javascriptValidationHandler = $handler;
     79         } else {
     80             user_error("Validator::setJavascriptValidationHandler() passed bad handler '$handler'", E_USER_WARNING);
     81         }
     82     }
     83
     84     /**
     85      * Gets the current javascript validation handler for this form.
     86      * If not set, falls back to the global static {@link self::$javascript_validation_handler}.
     87      *
     88      * @return string
     89      */
     90     public function getJavascriptValidationHandler() {
     91         return ($this->javascriptValidationHandler) ? $this->javascriptValidationHandler : self::$javascript_validation_handler;
     92     }
     93
     94     /**
     95      * @param Form $form
     96      */
     97     function setForm($form) {
     98         $this->form = $form;
     99     }
    100
    101     /**
    102      * @return array Errors (if any)
    103      */
    104     function validate(){
    105         $this->errors = null;
    106         $this->php($this->form->getData());
    107
    108         return $this->errors;
    109     }
    110
    111     /**
    112      * Callback to register an error on a field (Called from implementations of {@link FormField::validate})
    113      *
    114      * @param $fieldName name of the field
    115      * @param $message error message to display
    116      * @param $messageType optional parameter, gets loaded into the HTML class attribute in the rendered output. See {@link getErrors()} for details.
    117      */
    118     function validationError($fieldName, $message, $messageType='') {
    119         $this->errors[] = array(
    120             'fieldName' => $fieldName,
    121             'message' => $message,
    122             'messageType' => $messageType,
    123         );
    124     }
    125
    126     /**
    127      * @deprecated 2.4 Use Validator->getErrors() and custom code
    128      */
    129     function showError() {
    130         Debug::show($this->errors);
    131     }
    132
    133     /**
    134      * @deprecated 2.4 Use custom code
    135      */
    136     function getCombinedError(){
    137         if($this->errors) {
    138             foreach($this->errors as $error){
    139                 $ret['message'] .= $error['message']."<br />";
    140                 $ret['messageType'] .= $error['messageType']."<br />";
    141             }
    142
    143             return $ret;
    144         }
    145     }
    146
    147     /**
    148      * @deprecated 2.4 Use getErrors()
    149      */
    150     function getError(){
    151         return $this->getErrors();
    152     }
    153
    154     /**
    155      * Returns all errors found by a previous call to {@link validate()}.
    156      * The array contains the following keys for each error:
    157      * - 'fieldName': the name of the FormField instance
    158      * - 'message': Validation message (optionally localized)
    159      * - 'messageType': Arbitrary type of the message which is rendered as a CSS class in the FormField template,
    160      *   e.g. <span class="message (type)">. Usually "bad|message|validation|required", which renders differently
    161      *   if sapphire/css/Form.css is included.
    162      *
    163      * @return array
    164      */
    165     function getErrors() {
    166         return $this->errors;
    167     }
    168
    169     function requireField($fieldName, $data) {
    170         if(!$data[$fieldName]) $this->validationError($fieldName, "$fieldName is required.", "required");
    171     }
    172
    173     function includeJavascriptValidation() {
    174         if($this->getJavascriptValidationHandler() == 'prototype') {
    175             Requirements::javascript(SAPPHIRE_DIR . "/thirdparty/prototype/prototype.js");
    176             Requirements::javascript(SAPPHIRE_DIR . "/thirdparty/behaviour/behaviour.js");
    177             Requirements::javascript(SAPPHIRE_DIR . "/javascript/prototype_improvements.js");
    178             Requirements::add_i18n_javascript(SAPPHIRE_DIR . '/javascript/lang');
    179             Requirements::javascript(SAPPHIRE_DIR . "/javascript/Validator.js");
    180
    181             $code = $this->javascript();
    182             $formID = $this->form->FormName();
    183             $js = <<<JS
    184 Behaviour.register({
    185     '#$formID': {
    186         validate : function(fromAnOnBlur) {
    187             initialiseForm(this, fromAnOnBlur);
    188             $code
    189
    190             var error = hasHadFormError();
    191             if(!error && fromAnOnBlur) clearErrorMessage(fromAnOnBlur);
    192             if(error && !fromAnOnBlur) focusOnFirstErroredField();
    193
    194             return !error;
    195         },
    196         onsubmit : function() {
    197             if(typeof this.bypassValidation == 'undefined' || !this.bypassValidation) return this.validate();
    198         }
    199     },
    200     '#$formID input' : {
    201         initialise: function() {
    202             if (this.type != 'submit') { // no blur for submit inputs
    203                 if(!this.old_onblur) this.old_onblur = function() { return true; }
    204                 if(!this.old_onfocus) this.old_onfocus = function() { return true; }
    205             } else {
    206                 if(!this.old_onblur) this.old_onblur = function() { return false; }
    207                 if(!this.old_onfocus) this.old_onfocus = function() { return false; }
    208             }
    209         },
    210         onblur : function() {
    211             if(this.old_onblur()) {
    212                 // Don't perform instant validation for CalendarDateField fields; it creates usability wierdness.
    213                 if(this.parentNode.className.indexOf('calendardate') == -1 || this.value) {
    214                     return $('$formID').validate(this);
    215                 } else {
    216                     return true;
    217                 }
    218             }
    219         }
    220     },
    221     '#$formID textarea' : {
    222         initialise: function() {
    223             if(!this.old_onblur) this.old_onblur = function() { return true; }
    224             if(!this.old_onfocus) this.old_onfocus = function() { return true; }
    225         },
    226         onblur : function() {
    227             if(this.old_onblur()) {
    228                 return $('$formID').validate(this);
    229             }
    230         }
    231     },
    232     '#$formID select' : {
    233         initialise: function() {
    234             if(!this.old_onblur) this.old_onblur = function() { return true; }
    235         },
    236         onblur : function() {
    237             if(this.old_onblur()) {
    238                 return $('$formID').validate(this);
    239             }
    240         }
    241     }
    242 });
    243 JS;
    244
    245             Requirements::customScript($js);
    246             // HACK Notify the form that the validators client-side validation code has already been included
    247             if($this->form) $this->form->jsValidationIncluded = true;
    248         }
    249     }
    250
    251     /**
    252      * Returns true if the named field is "required".
    253      * Used by FormField to return a value for FormField::Required(), to do things like show *s on the form template.
    254      * By default, it always returns false.
    255      */
    256     function fieldIsRequired($fieldName) {
    257         return false;
    258     }
    259
    260     abstract function javascript();
    261
    262     abstract function php($data);
    263 }
    264 ?>
    265

[Raise a SilverStripe Framework issue/bug](https://github.com/silverstripe/silverstripe-framework/issues/new)
- [Raise a SilverStripe CMS issue/bug](https://github.com/silverstripe/silverstripe-cms/issues/new)
- Please use the Silverstripe Forums to ask development related questions. -