3 * TBaseValidator class file
5 * @author Qiang Xue <qiang.xue@gmail.com>
6 * @link http://www.pradosoft.com/
7 * @copyright Copyright © 2005-2013 PradoSoft
8 * @license http://www.pradosoft.com/license/
9 * @version $Id: TBaseValidator.php 3319 2013-09-08 20:59:44Z ctrlaltca $
10 * @package System.Web.UI.WebControls
16 Prado::using('System.Web.UI.WebControls.TLabel');
19 * TBaseValidator class
21 * TBaseValidator serves as the base class for validator controls.
23 * Validation is performed when a postback control, such as a TButton, a TLinkButton
24 * or a TTextBox (under AutoPostBack mode) is submitting the page and
25 * its <b>CausesValidation</b> property is true.
26 * You can also manually perform validation by calling {@link TPage::validate()}.
27 * The input control to be validated is specified by {@link setControlToValidate ControlToValidate}.
29 * Validator controls always validate the associated input control on the serve side.
30 * In addition, if {@link getEnableClientScript EnableClientScript} is true,
31 * validation will also be performed on the client-side using javascript.
32 * Client-side validation will validate user input before it is sent to the server.
33 * The form data will not be submitted if any error is detected. This avoids
34 * the round-trip of information necessary for server-side validation.
36 * You can use multiple validator controls to validate a single input control,
37 * each responsible for validating against a different criteria.
38 * For example, on a user registration form, you may want to make sure the user
39 * enters a value in the username text box, and the input must consist of only word
40 * characters. You can use a {@link TRequiredFieldValidator} to ensure the input
41 * of username and a {@link TRegularExpressionValidator} to ensure the proper input.
43 * If an input control fails validation, the text specified by the {@link setErrorMessage ErrorMessage}
44 * property is displayed in the validation control. However, if the {@link setText Text}
45 * property is set, it will be displayed instead. If both {@link setErrorMessage ErrorMessage}
46 * and {@link setText Text} are empty, the body content of the validator will
47 * be displayed. Error display is controlled by {@link setDisplay Display} property.
49 * You can also customized the client-side behaviour by adding javascript
50 * code to the subproperties of the {@link getClientSide ClientSide}
51 * property. See quickstart documentation for further details.
53 * You can also place a {@link TValidationSummary} control on a page to display error messages
54 * from the validators together. In this case, only the {@link setErrorMessage ErrorMessage}
55 * property of the validators will be displayed in the {@link TValidationSummary} control.
57 * Validators can be partitioned into validation groups by setting their
58 * {@link setValidationGroup ValidationGroup} property. If the control causing the
59 * validation also sets its ValidationGroup property, only those validators having
60 * the same ValidationGroup value will do input validation.
62 * Note, the {@link TPage::getIsValid IsValid} property of the current {@link TPage}
63 * instance will be automatically updated by the validation process which occurs
64 * after {@link TPage::onLoad onLoad} of {@link TPage} and before the postback events.
65 * Therefore, if you use the {@link TPage::getIsValid()} property in
66 * the {@link TPage::onLoad()} method, you must first explicitly call
67 * the {@link TPage::validate()} method.
69 * <b>Notes to Inheritors</b> When you inherit from TBaseValidator, you must
70 * override the method {@link evaluateIsValid}.
72 * @author Qiang Xue <qiang.xue@gmail.com>
73 * @version $Id: TBaseValidator.php 3319 2013-09-08 20:59:44Z ctrlaltca $
74 * @package System.Web.UI.WebControls
77 abstract class TBaseValidator extends TLabel implements IValidator
80 * @var boolean whether the validation succeeds
82 private $_isValid=true;
84 * @var boolean whether the validator has been registered with the page
86 private $_registered=false;
88 * @var TValidatorClientSide validator client-script options.
92 * Controls for which the client-side validation3.js file needs to handle
94 * @var array list of control class names
96 private static $_clientClass = array('THtmlArea', 'THtmlArea4', 'TDatePicker', 'TListBox', 'TCheckBoxList');
100 * This method sets the foreground color to red.
102 public function __construct()
104 parent::__construct();
105 $this->setForeColor('red');
109 * Registers the validator with page.
110 * @param mixed event parameter
112 public function onInit($param)
114 parent::onInit($param);
115 $this->getPage()->getValidators()->add($this);
116 $this->_registered=true;
120 * Unregisters the validator from page.
121 * @param mixed event parameter
123 public function onUnload($param)
125 if($this->_registered && ($page=$this->getPage())!==null)
126 $page->getValidators()->remove($this);
127 $this->_registered=false;
128 parent::onUnload($param);
132 * Adds attributes to renderer. Calls parent implementation and renders the
133 * client control scripts.
134 * @param THtmlWriter the renderer
136 protected function addAttributesToRender($writer)
138 $display=$this->getDisplay();
139 $visible=$this->getEnabled(true) && !$this->getIsValid();
140 if($display===TValidatorDisplayStyle::None || (!$visible && $display===TValidatorDisplayStyle::Dynamic))
141 $writer->addStyleAttribute('display','none');
143 $writer->addStyleAttribute('visibility','hidden');
144 $writer->addAttribute('id',$this->getClientID());
145 parent::addAttributesToRender($writer);
146 $this->renderClientControlScript($writer);
150 * Returns an array of javascript validator options.
151 * @return array javascript validator options.
153 protected function getClientScriptOptions()
155 $control = $this->getValidationTarget();
156 $options['ID'] = $this->getClientID();
157 $options['FormID'] = $this->getPage()->getForm()->getClientID();
158 $options['Display'] = $this->getDisplay();
159 $options['ErrorMessage'] = $this->getErrorMessage();
160 if($this->getFocusOnError())
162 $options['FocusOnError'] = $this->getFocusOnError();
163 $options['FocusElementID'] = $this->getFocusElementID();
165 $options['ValidationGroup'] = $this->getValidationGroup();
167 $options['ControlToValidate'] = $control->getClientID();
168 $options['ControlCssClass'] = $this->getControlCssClass();
170 $options['ControlType'] = $this->getClientControlClass($control);
171 $options['Enabled'] = $this->getEnabled(true);
173 //get date format from date picker target control
174 if($control instanceof TDatePicker)
175 $options['DateFormat'] = $control->getDateFormat();
177 $options = array_merge($options,$this->getClientSide()->getOptions()->toArray());
183 * Gets the Control type for client-side validation. If new cases exists in
184 * TBaseValidator::$_clientClass, be sure to update the corresponding
185 * "Javascript/validation3.js" file as well.
186 * @param TControl control to validate.
187 * @return string control type for client-side validation.
189 private function getClientControlClass($control)
191 foreach(self::$_clientClass as $type)
192 if($control instanceof $type)
194 return get_class($control);
198 * Gets the TValidatorClientSide that allows modification of the client-
199 * side validator events.
201 * The client-side validator supports the following events.
202 * # <tt>OnValidate</tt> -- raised before client-side validation is
204 * # <tt>OnValidationSuccess</tt> -- raised after client-side validation is completed
205 * and is successfull, overrides default validator error messages updates.
206 * # <tt>OnValidationError</tt> -- raised after client-side validation is completed
207 * and failed, overrides default validator error message updates.
209 * You can attach custom javascript code to each of these events
211 * @return TValidatorClientSide javascript validator event options.
213 public function getClientSide()
215 if($this->_clientSide===null)
216 $this->_clientSide = $this->createClientSide();
217 return $this->_clientSide;
221 * @return TValidatorClientSide javascript validator event options.
223 protected function createClientSide()
225 return new TValidatorClientSide;
229 * Renders the javascript code to the end script.
230 * If you override this method, be sure to call the parent implementation
231 * so that the event handlers can be invoked.
232 * @param THtmlWriter the renderer
234 public function renderClientControlScript($writer)
236 $scripts = $this->getPage()->getClientScript();
237 if ($this->getEnableClientScript())
238 $scripts->registerPradoScript('validator');
239 $formID=$this->getPage()->getForm()->getClientID();
240 $scriptKey = "TBaseValidator:$formID";
241 if($this->getEnableClientScript() && !$scripts->isEndScriptRegistered($scriptKey))
243 $manager['FormID'] = $formID;
244 $options = TJavaScript::encode($manager);
245 $scripts->registerEndScript($scriptKey, "new Prado.ValidationManager({$options});");
247 if($this->getEnableClientScript())
248 $this->registerClientScriptValidator();
252 * Override parent implementation to update the control CSS Class before
253 * the validated control is rendered
255 public function onPreRender ($param)
257 parent::onPreRender($param);
258 $this->updateControlCssClass();
262 * Update the ControlToValidate component's css class depending
263 * if the ControlCssClass property is set, and whether this is valid.
264 * @return boolean true if change, false otherwise.
266 protected function updateControlCssClass()
268 if(($cssClass=$this->getControlCssClass())!=='')
270 $control=$this->getValidationTarget();
271 if($control instanceof TWebControl)
273 $class = preg_replace ('/ '.preg_quote($cssClass).'/', '',$control->getCssClass());
274 if(!$this->getIsValid())
276 $class .= ' '.$cssClass;
277 $control->setCssClass($class);
278 } elseif ($control->getIsValid())
279 $control->setCssClass($class);
285 * Registers the individual validator client-side javascript code.
287 protected function registerClientScriptValidator()
289 $key = 'prado:'.$this->getClientID();
290 if(!$this->getPage()->getClientScript()->isEndScriptRegistered($key))
292 $options = TJavaScript::encode($this->getClientScriptOptions());
293 $script = 'new '.$this->getClientClassName().'('.$options.');';
294 $this->getPage()->getClientScript()->registerEndScript($key, $script);
299 * Gets the name of the javascript class responsible for performing validation for this control.
300 * This method overrides the parent implementation.
301 * @return string the javascript class name
303 abstract protected function getClientClassName();
306 * This method overrides the parent implementation to forbid setting ForControl.
307 * @param string the associated control ID
308 * @throws TNotSupportedException whenever this method is called
310 public function setForControl($value)
312 throw new TNotSupportedException('basevalidator_forcontrol_unsupported',get_class($this));
316 * This method overrides parent's implementation by setting {@link setIsValid IsValid} to true if disabled.
317 * @param boolean whether the validator is enabled.
319 public function setEnabled($value)
321 $value=TPropertyValue::ensureBoolean($value);
322 parent::setEnabled($value);
324 $this->_isValid=true;
328 * @return TValidatorDisplayStyle the style of displaying the error message. Defaults to TValidatorDisplayStyle::Fixed.
330 public function getDisplay()
332 return $this->getViewState('Display',TValidatorDisplayStyle::Fixed);
336 * @param TValidatorDisplayStyle the style of displaying the error message
338 public function setDisplay($value)
340 $this->setViewState('Display',TPropertyValue::ensureEnum($value,'TValidatorDisplayStyle'),TValidatorDisplayStyle::Fixed);
344 * @return boolean whether client-side validation is enabled.
346 public function getEnableClientScript()
348 return $this->getViewState('EnableClientScript',true);
352 * @param boolean whether client-side validation is enabled.
354 public function setEnableClientScript($value)
356 $this->setViewState('EnableClientScript',TPropertyValue::ensureBoolean($value),true);
360 * @return string the text for the error message.
362 public function getErrorMessage()
364 return $this->getViewState('ErrorMessage','');
368 * Sets the text for the error message.
369 * @param string the error message
371 public function setErrorMessage($value)
373 $this->setViewState('ErrorMessage',$value,'');
377 * @return string the ID path of the input control to validate
379 public function getControlToValidate()
381 return $this->getViewState('ControlToValidate','');
385 * Sets the ID path of the input control to validate.
386 * The ID path is the dot-connected IDs of the controls reaching from
387 * the validator's naming container to the target control.
388 * @param string the ID path
390 public function setControlToValidate($value)
392 $this->setViewState('ControlToValidate',$value,'');
396 * @return boolean whether to set focus at the validating place if the validation fails. Defaults to false.
398 public function getFocusOnError()
400 return $this->getViewState('FocusOnError',false);
404 * @param boolean whether to set focus at the validating place if the validation fails
406 public function setFocusOnError($value)
408 $this->setViewState('FocusOnError',TPropertyValue::ensureBoolean($value),false);
412 * Gets the ID of the HTML element that will receive focus if validation fails and {@link getFocusOnError FocusOnError} is true.
413 * Defaults to the client ID of the {@link getControlToValidate ControlToValidate}.
414 * @return string the ID of the HTML element to receive focus
416 public function getFocusElementID()
418 if(($id=$this->getViewState('FocusElementID',''))==='')
420 $target=$this->getValidationTarget();
421 /* Workaround: TCheckBoxList and TRadioButtonList nests the actual
422 * inputs inside a table; we ensure the first input gets focused
424 if($target instanceof TCheckBoxList && $target->getItemCount()>0)
426 $id=$target->getClientID().'_c0';
428 $id=$target->getClientID();
435 * Sets the ID of the HTML element that will receive focus if validation fails and {@link getFocusOnError FocusOnError} is true.
436 * @param string the ID of the HTML element to receive focus
438 public function setFocusElementID($value)
440 $this->setViewState('FocusElementID', $value, '');
444 * @return string the group which this validator belongs to
446 public function getValidationGroup()
448 return $this->getViewState('ValidationGroup','');
452 * @param string the group which this validator belongs to
454 public function setValidationGroup($value)
456 $this->setViewState('ValidationGroup',$value,'');
460 * @return boolean whether the validation succeeds
462 public function getIsValid()
464 return $this->_isValid;
468 * Sets the value indicating whether the validation succeeds
469 * @param boolean whether the validation succeeds
471 public function setIsValid($value)
473 $this->_isValid=TPropertyValue::ensureBoolean($value);
477 * @return TControl control to be validated. Null if no control is found.
478 * @throws TConfigurationException if {@link getControlToValidate
479 * ControlToValidate} is empty or does not point to a valid control
481 public function getValidationTarget()
483 if(($id=$this->getControlToValidate())!=='' && ($control=$this->findControl($id))!==null)
486 throw new TConfigurationException('basevalidator_controltovalidate_invalid',get_class($this));
490 * Retrieves the property value of the control being validated.
491 * @param TControl control being validated
492 * @return string property value to be validated
493 * @throws TInvalidDataTypeException if the control to be validated does not implement {@link IValidatable}.
495 protected function getValidationValue($control)
497 if($control instanceof IValidatable)
498 return $control->getValidationPropertyValue();
500 throw new TInvalidDataTypeException('basevalidator_validatable_required',get_class($this));
504 * Validates the specified control.
505 * Do not override this method. Override {@link evaluateIsValid} instead.
506 * @return boolean whether the validation succeeds
508 public function validate()
511 if($this->getVisible(true) && $this->getEnabled(true))
513 $target=$this->getValidationTarget();
514 // if the target is not a disabled web control
517 !($target instanceof TWebControl && !$target->getEnabled(true))))
519 if($this->evaluateIsValid())
521 $this->setIsValid(true);
522 $this->onValidationSuccess();
527 $target->setIsValid(false);
528 $this->setIsValid(false);
529 $this->onValidationError();
534 $this->evaluateIsValid();
535 $this->setIsValid(true);
536 $this->onValidationSuccess();
539 $this->setIsValid(true);
541 return $this->getIsValid();
545 * @return string the css class that is applied to the control being validated in case the validation fails
547 public function getControlCssClass()
549 return $this->getViewState('ControlCssClass','');
553 * @param string the css class that is applied to the control being validated in case the validation fails
555 public function setControlCssClass($value)
557 $this->setViewState('ControlCssClass',$value,'');
561 * This is the major method for validation.
562 * Derived classes should implement this method to provide customized validation.
563 * @return boolean whether the validation succeeds
565 abstract protected function evaluateIsValid();
568 * This event is raised when the validator succeeds in validation.
570 public function onValidationSuccess()
572 $this->raiseEvent('OnValidationSuccess',$this,null);
576 * This event is raised when the validator fails in validation.
578 public function onValidationError()
580 $this->raiseEvent('OnValidationError',$this,null);
584 * This event is raised right before the validator starts to perform validation.
585 * You may use this event to change the behavior of validation.
586 * For example, you may disable the validator if certain condition is satisfied.
587 * Note, the event will NOT be raised if the validator is invisible.
589 public function onValidate()
591 $this->raiseEvent('OnValidate',$this,null);
595 * Renders the validator control.
596 * @param THtmlWriter writer for the rendering purpose
598 public function renderContents($writer)
600 if(($text=$this->getText())!=='')
601 $writer->write($text);
602 else if(($text=$this->getErrorMessage())!=='')
603 $writer->write($text);
605 parent::renderContents($writer);
610 * TValidatorClientSide class.
612 * Client-side validator events can be modified through the {@link
613 * TBaseValidator::getClientSide ClientSide} property of a validator. The
614 * subproperties of ClientSide are those of the TValidatorClientSide
615 * properties. The client-side validator supports the following events.
617 * The <tt>OnValidate</tt> event is raise before the validator validation
618 * functions are called.
620 * The <tt>OnValidationSuccess</tt> event is raised after the validator has successfully
621 * validate the control.
623 * The <tt>OnValidationError</tt> event is raised after the validator fails validation.
625 * See the quickstart documentation for further details.
627 * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
628 * @version $Id: TBaseValidator.php 3319 2013-09-08 20:59:44Z ctrlaltca $
629 * @package System.Web.UI.WebControls
632 class TValidatorClientSide extends TClientSideOptions
635 * @return string javascript code for client-side OnValidate event.
637 public function getOnValidate()
639 return $this->getOption('OnValidate');
643 * Client-side OnValidate validator event is raise before the validators
644 * validation functions are called.
645 * @param string javascript code for client-side OnValidate event.
647 public function setOnValidate($javascript)
649 $this->setFunction('OnValidate', $javascript);
653 * Client-side OnSuccess event is raise after validation is successfull.
654 * This will override the default client-side validator behaviour.
655 * @param string javascript code for client-side OnSuccess event.
657 public function setOnValidationSuccess($javascript)
659 $this->setFunction('OnValidationSuccess', $javascript);
663 * @return string javascript code for client-side OnSuccess event.
665 public function getOnValidationSuccess()
667 return $this->getOption('OnValidationSuccess');
671 * Client-side OnError event is raised after validation failure.
672 * This will override the default client-side validator behaviour.
673 * @param string javascript code for client-side OnError event.
675 public function setOnValidationError($javascript)
677 $this->setFunction('OnValidationError', $javascript);
681 * @return string javascript code for client-side OnError event.
683 public function getOnValidationError()
685 return $this->getOption('OnValidationError');
689 * @param boolean true to revalidate when the control to validate changes value.
691 public function setObserveChanges($value)
693 $this->setOption('ObserveChanges', TPropertyValue::ensureBoolean($value));
697 * @return boolean true to observe changes.
699 public function getObserveChanges()
701 $changes = $this->getOption('ObserveChanges');
702 return ($changes===null) ? true : $changes;
708 * TValidatorDisplayStyle class.
709 * TValidatorDisplayStyle defines the enumerable type for the possible styles
710 * that a validator control can display the error message.
712 * The following enumerable values are defined:
713 * - None: the error message is not displayed
714 * - Dynamic: the error message dynamically appears when the validator fails validation
715 * - Fixed: Similar to Dynamic except that the error message physically occupies the page layout (even though it may not be visible)
717 * @author Qiang Xue <qiang.xue@gmail.com>
718 * @version $Id: TBaseValidator.php 3319 2013-09-08 20:59:44Z ctrlaltca $
719 * @package System.Web.UI.WebControls
722 class TValidatorDisplayStyle extends TEnumerable
725 const Dynamic='Dynamic';
730 * TValidationDataType class.
731 * TValidationDataType defines the enumerable type for the possible data types that
732 * a comparison validator can validate upon.
734 * The following enumerable values are defined:
740 * @author Qiang Xue <qiang.xue@gmail.com>
741 * @version $Id: TBaseValidator.php 3319 2013-09-08 20:59:44Z ctrlaltca $
742 * @package System.Web.UI.WebControls
745 class TValidationDataType extends TEnumerable
747 const Integer='Integer';
750 const String='String';