]> git.sur5r.net Git - bacula/bacula/blob - gui/baculum/framework/Web/UI/WebControls/TBaseValidator.php
a23e21040f66bcd221a6d712bc500a6226eeecc9
[bacula/bacula] / gui / baculum / framework / Web / UI / WebControls / TBaseValidator.php
1 <?php
2 /**
3  * TBaseValidator class file
4  *
5  * @author Qiang Xue <qiang.xue@gmail.com>
6  * @link http://www.pradosoft.com/
7  * @copyright Copyright &copy; 2005-2014 PradoSoft
8  * @license http://www.pradosoft.com/license/
9  * @package System.Web.UI.WebControls
10  */
11
12 /**
13  * Using TLabel class
14  */
15 Prado::using('System.Web.UI.WebControls.TLabel');
16
17 /**
18  * TBaseValidator class
19  *
20  * TBaseValidator serves as the base class for validator controls.
21  *
22  * Validation is performed when a postback control, such as a TButton, a TLinkButton
23  * or a TTextBox (under AutoPostBack mode) is submitting the page and
24  * its <b>CausesValidation</b> property is true.
25  * You can also manually perform validation by calling {@link TPage::validate()}.
26  * The input control to be validated is specified by {@link setControlToValidate ControlToValidate}.
27  *
28  * Validator controls always validate the associated input control on the serve side.
29  * In addition, if {@link getEnableClientScript EnableClientScript} is true,
30  * validation will also be performed on the client-side using javascript.
31  * Client-side validation will validate user input before it is sent to the server.
32  * The form data will not be submitted if any error is detected. This avoids
33  * the round-trip of information necessary for server-side validation.
34  *
35  * You can use multiple validator controls to validate a single input control,
36  * each responsible for validating against a different criteria.
37  * For example, on a user registration form, you may want to make sure the user
38  * enters a value in the username text box, and the input must consist of only word
39  * characters. You can use a {@link TRequiredFieldValidator} to ensure the input
40  * of username and a {@link TRegularExpressionValidator} to ensure the proper input.
41  *
42  * If an input control fails validation, the text specified by the {@link setErrorMessage ErrorMessage}
43  * property is displayed in the validation control. However, if the {@link setText Text}
44  * property is set, it will be displayed instead. If both {@link setErrorMessage ErrorMessage}
45  * and {@link setText Text} are empty, the body content of the validator will
46  * be displayed. Error display is controlled by {@link setDisplay Display} property.
47  *
48  * You can also customized the client-side behaviour by adding javascript
49  * code to the subproperties of the {@link getClientSide ClientSide}
50  * property. See quickstart documentation for further details.
51  *
52  * You can also place a {@link TValidationSummary} control on a page to display error messages
53  * from the validators together. In this case, only the {@link setErrorMessage ErrorMessage}
54  * property of the validators will be displayed in the {@link TValidationSummary} control.
55  *
56  * Validators can be partitioned into validation groups by setting their
57  * {@link setValidationGroup ValidationGroup} property. If the control causing the
58  * validation also sets its ValidationGroup property, only those validators having
59  * the same ValidationGroup value will do input validation.
60  *
61  * Note, the {@link TPage::getIsValid IsValid} property of the current {@link TPage}
62  * instance will be automatically updated by the validation process which occurs
63  * after {@link TPage::onLoad onLoad} of {@link TPage} and before the postback events.
64  * Therefore, if you use the {@link TPage::getIsValid()} property in
65  * the {@link TPage::onLoad()} method, you must first explicitly call
66  * the {@link TPage::validate()} method.
67  *
68  * <b>Notes to Inheritors</b>  When you inherit from TBaseValidator, you must
69  * override the method {@link evaluateIsValid}.
70  *
71  * @author Qiang Xue <qiang.xue@gmail.com>
72  * @package System.Web.UI.WebControls
73  * @since 3.0
74  */
75 abstract class TBaseValidator extends TLabel implements IValidator
76 {
77         /**
78          * @var boolean whether the validation succeeds
79          */
80         private $_isValid=true;
81         /**
82          * @var boolean whether the validator has been registered with the page
83          */
84         private $_registered=false;
85         /**
86          * @var TValidatorClientSide validator client-script options.
87          */
88         private $_clientSide;
89         /**
90          * Controls for which the client-side validation3.js file needs to handle
91          * them specially.
92          * @var array list of control class names
93          */
94         private static $_clientClass = array('THtmlArea', 'THtmlArea4', 'TDatePicker', 'TListBox', 'TCheckBoxList');
95
96         /**
97          * Constructor.
98          * This method sets the foreground color to red.
99          */
100         public function __construct()
101         {
102                 parent::__construct();
103                 $this->setForeColor('red');
104         }
105
106         /**
107          * Registers the validator with page.
108          * @param mixed event parameter
109          */
110         public function onInit($param)
111         {
112                 parent::onInit($param);
113                 $this->getPage()->getValidators()->add($this);
114                 $this->_registered=true;
115         }
116
117         /**
118          * Unregisters the validator from page.
119          * @param mixed event parameter
120          */
121         public function onUnload($param)
122         {
123                 if($this->_registered && ($page=$this->getPage())!==null)
124                         $page->getValidators()->remove($this);
125                 $this->_registered=false;
126                 parent::onUnload($param);
127         }
128
129         /**
130          * Adds attributes to renderer. Calls parent implementation and renders the
131          * client control scripts.
132          * @param THtmlWriter the renderer
133          */
134         protected function addAttributesToRender($writer)
135         {
136                 $display=$this->getDisplay();
137                 $visible=$this->getEnabled(true) && !$this->getIsValid();
138                 if($display===TValidatorDisplayStyle::None || (!$visible && $display===TValidatorDisplayStyle::Dynamic))
139                         $writer->addStyleAttribute('display','none');
140                 else if(!$visible)
141                         $writer->addStyleAttribute('visibility','hidden');
142                 $writer->addAttribute('id',$this->getClientID());
143                 parent::addAttributesToRender($writer);
144                 $this->renderClientControlScript($writer);
145         }
146
147         /**
148          * Returns an array of javascript validator options.
149          * @return array javascript validator options.
150          */
151         protected function getClientScriptOptions()
152         {
153                 $control = $this->getValidationTarget();
154                 $options['ID'] = $this->getClientID();
155                 $options['FormID'] = $this->getPage()->getForm()->getClientID();
156                 $options['Display'] = $this->getDisplay();
157                 $options['ErrorMessage'] = $this->getErrorMessage();
158                 if($this->getFocusOnError())
159                 {
160                         $options['FocusOnError'] = $this->getFocusOnError();
161                         $options['FocusElementID'] = $this->getFocusElementID();
162                 }
163                 $options['ValidationGroup'] = $this->getValidationGroup();
164                 if($control)
165                         $options['ControlToValidate'] = $control->getClientID();
166                 $options['ControlCssClass'] = $this->getControlCssClass();
167
168                 $options['ControlType'] = $this->getClientControlClass($control);
169                 $options['Enabled'] = $this->getEnabled(true);
170
171                 //get date format from date picker target control
172                 if($control instanceof TDatePicker)
173                         $options['DateFormat'] = $control->getDateFormat();
174
175                 $options = array_merge($options,$this->getClientSide()->getOptions()->toArray());
176
177                 return $options;
178         }
179
180         /**
181          * Gets the Control type for client-side validation. If new cases exists in
182          * TBaseValidator::$_clientClass, be sure to update the corresponding
183          * "Javascript/validation3.js" file as well.
184          * @param TControl control to validate.
185          * @return string control type for client-side validation.
186          */
187         private function getClientControlClass($control)
188         {
189                 foreach(self::$_clientClass as $type)
190                         if($control instanceof $type)
191                                 return $type;
192                 return get_class($control);
193         }
194
195         /**
196          * Gets the TValidatorClientSide that allows modification of the client-
197          * side validator events.
198          *
199          * The client-side validator supports the following events.
200          * # <tt>OnValidate</tt> -- raised before client-side validation is
201          * executed.
202          * # <tt>OnValidationSuccess</tt> -- raised after client-side validation is completed
203          * and is successfull, overrides default validator error messages updates.
204          * # <tt>OnValidationError</tt> -- raised after client-side validation is completed
205          * and failed, overrides default validator error message updates.
206          *
207          * You can attach custom javascript code to each of these events
208          *
209          * @return TValidatorClientSide javascript validator event options.
210          */
211         public function getClientSide()
212         {
213                 if($this->_clientSide===null)
214                         $this->_clientSide = $this->createClientSide();
215                 return $this->_clientSide;
216         }
217
218         /**
219          * @return TValidatorClientSide javascript validator event options.
220          */
221         protected function createClientSide()
222         {
223                 return new TValidatorClientSide;
224         }
225
226         /**
227          * Renders the javascript code to the end script.
228          * If you override this method, be sure to call the parent implementation
229          * so that the event handlers can be invoked.
230          * @param THtmlWriter the renderer
231          */
232         public function renderClientControlScript($writer)
233         {
234                 $scripts = $this->getPage()->getClientScript();
235                 if ($this->getEnableClientScript())
236                         $scripts->registerPradoScript('validator');
237                 $formID=$this->getPage()->getForm()->getClientID();
238                 $scriptKey = "TBaseValidator:$formID";
239                 if($this->getEnableClientScript() && !$scripts->isEndScriptRegistered($scriptKey))
240                 {
241                         $manager['FormID'] = $formID;
242                         $options = TJavaScript::encode($manager);
243                         $scripts->registerEndScript($scriptKey, "new Prado.ValidationManager({$options});");
244                 }
245                 if($this->getEnableClientScript())
246                         $this->registerClientScriptValidator();
247         }
248
249         /**
250          * Override parent implementation to update the control CSS Class before
251          * the validated control is rendered
252          */
253         public function onPreRender ($param)
254         {
255                 parent::onPreRender($param);
256                 $this->updateControlCssClass();
257         }
258
259         /**
260          * Update the ControlToValidate component's css class depending
261          * if the ControlCssClass property is set, and whether this is valid.
262          * @return boolean true if change, false otherwise.
263          */
264         protected function updateControlCssClass()
265         {
266                 if(($cssClass=$this->getControlCssClass())!=='')
267                 {
268                         $control=$this->getValidationTarget();
269                         if($control instanceof TWebControl)
270                         {
271                                 $class = preg_replace ('/ '.preg_quote($cssClass).'/', '',$control->getCssClass());
272                                 if(!$this->getIsValid())
273                                 {
274                                         $class .= ' '.$cssClass;
275                                         $control->setCssClass($class);
276                                 } elseif ($control->getIsValid())
277                                         $control->setCssClass($class);
278                         }
279                 }
280         }
281
282         /**
283          * Registers the individual validator client-side javascript code.
284          */
285         protected function registerClientScriptValidator()
286         {
287                 $key = 'prado:'.$this->getClientID();
288                 if(!$this->getPage()->getClientScript()->isEndScriptRegistered($key))
289                 {
290                         $options = TJavaScript::encode($this->getClientScriptOptions());
291                         $script = 'new '.$this->getClientClassName().'('.$options.');';
292                         $this->getPage()->getClientScript()->registerEndScript($key, $script);
293                 }
294         }
295
296         /**
297          * Gets the name of the javascript class responsible for performing validation for this control.
298          * This method overrides the parent implementation.
299          * @return string the javascript class name
300          */
301         abstract protected function getClientClassName();
302
303         /**
304          * This method overrides the parent implementation to forbid setting ForControl.
305          * @param string the associated control ID
306          * @throws TNotSupportedException whenever this method is called
307          */
308         public function setForControl($value)
309         {
310                 throw new TNotSupportedException('basevalidator_forcontrol_unsupported',get_class($this));
311         }
312
313         /**
314          * This method overrides parent's implementation by setting {@link setIsValid IsValid} to true if disabled.
315          * @param boolean whether the validator is enabled.
316          */
317         public function setEnabled($value)
318         {
319                 $value=TPropertyValue::ensureBoolean($value);
320                 parent::setEnabled($value);
321                 if(!$value)
322                         $this->_isValid=true;
323         }
324
325         /**
326          * @return TValidatorDisplayStyle the style of displaying the error message. Defaults to TValidatorDisplayStyle::Fixed.
327          */
328         public function getDisplay()
329         {
330                 return $this->getViewState('Display',TValidatorDisplayStyle::Fixed);
331         }
332
333         /**
334          * @param TValidatorDisplayStyle the style of displaying the error message
335          */
336         public function setDisplay($value)
337         {
338                 $this->setViewState('Display',TPropertyValue::ensureEnum($value,'TValidatorDisplayStyle'),TValidatorDisplayStyle::Fixed);
339         }
340
341         /**
342          * @return boolean whether client-side validation is enabled.
343          */
344         public function getEnableClientScript()
345         {
346                 return $this->getViewState('EnableClientScript',true);
347         }
348
349         /**
350          * @param boolean whether client-side validation is enabled.
351          */
352         public function setEnableClientScript($value)
353         {
354                 $this->setViewState('EnableClientScript',TPropertyValue::ensureBoolean($value),true);
355         }
356
357         /**
358          * @return string the text for the error message.
359          */
360         public function getErrorMessage()
361         {
362                 return $this->getViewState('ErrorMessage','');
363         }
364
365         /**
366          * Sets the text for the error message.
367          * @param string the error message
368          */
369         public function setErrorMessage($value)
370         {
371                 $this->setViewState('ErrorMessage',$value,'');
372         }
373
374         /**
375          * @return string the ID path of the input control to validate
376          */
377         public function getControlToValidate()
378         {
379                 return $this->getViewState('ControlToValidate','');
380         }
381
382         /**
383          * Sets the ID path of the input control to validate.
384          * The ID path is the dot-connected IDs of the controls reaching from
385          * the validator's naming container to the target control.
386          * @param string the ID path
387          */
388         public function setControlToValidate($value)
389         {
390                 $this->setViewState('ControlToValidate',$value,'');
391         }
392
393         /**
394          * @return boolean whether to set focus at the validating place if the validation fails. Defaults to false.
395          */
396         public function getFocusOnError()
397         {
398                 return $this->getViewState('FocusOnError',false);
399         }
400
401         /**
402          * @param boolean whether to set focus at the validating place if the validation fails
403          */
404         public function setFocusOnError($value)
405         {
406                 $this->setViewState('FocusOnError',TPropertyValue::ensureBoolean($value),false);
407         }
408
409         /**
410          * Gets the ID of the HTML element that will receive focus if validation fails and {@link getFocusOnError FocusOnError} is true.
411          * Defaults to the client ID of the {@link getControlToValidate ControlToValidate}.
412          * @return string the ID of the HTML element to receive focus
413          */
414         public function getFocusElementID()
415         {
416                 if(($id=$this->getViewState('FocusElementID',''))==='')
417                 {
418                         $target=$this->getValidationTarget();
419                         /* Workaround: TCheckBoxList and TRadioButtonList nests the actual
420                          * inputs inside a table; we ensure the first input gets focused
421                          */
422                         if($target instanceof TCheckBoxList && $target->getItemCount()>0)
423                         {
424                                 $id=$target->getClientID().'_c0';
425                         } else {
426                                 $id=$target->getClientID();
427                         }
428                 }
429                 return $id;
430         }
431
432         /**
433          * Sets the ID of the HTML element that will receive focus if validation fails and {@link getFocusOnError FocusOnError} is true.
434          * @param string the ID of the HTML element to receive focus
435          */
436         public function setFocusElementID($value)
437         {
438                 $this->setViewState('FocusElementID', $value, '');
439         }
440
441         /**
442          * @return string the group which this validator belongs to
443          */
444         public function getValidationGroup()
445         {
446                 return $this->getViewState('ValidationGroup','');
447         }
448
449         /**
450          * @param string the group which this validator belongs to
451          */
452         public function setValidationGroup($value)
453         {
454                 $this->setViewState('ValidationGroup',$value,'');
455         }
456
457         /**
458          * @return boolean whether the validation succeeds
459          */
460         public function getIsValid()
461         {
462                 return $this->_isValid;
463         }
464
465         /**
466          * Sets the value indicating whether the validation succeeds
467          * @param boolean whether the validation succeeds
468          */
469         public function setIsValid($value)
470         {
471                 $this->_isValid=TPropertyValue::ensureBoolean($value);
472         }
473
474         /**
475          * @return TControl control to be validated. Null if no control is found.
476          * @throws TConfigurationException if {@link getControlToValidate
477          * ControlToValidate} is empty or does not point to a valid control
478          */
479         public function getValidationTarget()
480         {
481                 if(($id=$this->getControlToValidate())!=='' && ($control=$this->findControl($id))!==null)
482                         return $control;
483                 else
484                         throw new TConfigurationException('basevalidator_controltovalidate_invalid',get_class($this));
485         }
486
487         /**
488          * Retrieves the property value of the control being validated.
489          * @param TControl control being validated
490          * @return string property value to be validated
491          * @throws TInvalidDataTypeException if the control to be validated does not implement {@link IValidatable}.
492          */
493         protected function getValidationValue($control)
494         {
495                 if($control instanceof IValidatable)
496                         return $control->getValidationPropertyValue();
497                 else
498                         throw new TInvalidDataTypeException('basevalidator_validatable_required',get_class($this));
499         }
500
501         /**
502          * Validates the specified control.
503          * Do not override this method. Override {@link evaluateIsValid} instead.
504          * @return boolean whether the validation succeeds
505          */
506         public function validate()
507         {
508                 $this->onValidate();
509                 if($this->getVisible(true) && $this->getEnabled(true))
510                 {
511                         $target=$this->getValidationTarget();
512                         // if the target is not a disabled web control
513                         if($target===null ||
514                                 ($target!==null &&
515                                 !($target instanceof TWebControl && !$target->getEnabled(true))))
516                         {
517                                 if($this->evaluateIsValid())
518                                 {
519                                         $this->setIsValid(true);
520                                         $this->onValidationSuccess();
521                                 }
522                                 else
523                                 {
524                                         if($target)
525                                                 $target->setIsValid(false);
526                                         $this->setIsValid(false);
527                                         $this->onValidationError();
528                                 }
529                         }
530                         else
531                         {
532                                 $this->evaluateIsValid();
533                                 $this->setIsValid(true);
534                                 $this->onValidationSuccess();
535                         }
536                 } else {
537                         $this->setIsValid(true);
538                 }
539                 return $this->getIsValid();
540         }
541
542         /**
543          * @return string the css class that is applied to the control being validated in case the validation fails
544          */
545         public function getControlCssClass()
546         {
547                 return $this->getViewState('ControlCssClass','');
548         }
549
550         /**
551          * @param string the css class that is applied to the control being validated in case the validation fails
552          */
553         public function setControlCssClass($value)
554         {
555                 $this->setViewState('ControlCssClass',$value,'');
556         }
557
558         /**
559          * This is the major method for validation.
560          * Derived classes should implement this method to provide customized validation.
561          * @return boolean whether the validation succeeds
562          */
563         abstract protected function evaluateIsValid();
564
565         /**
566          * This event is raised when the validator succeeds in validation.
567          */
568         public function onValidationSuccess()
569         {
570                 $this->raiseEvent('OnValidationSuccess',$this,null);
571         }
572
573         /**
574          * This event is raised when the validator fails in validation.
575          */
576         public function onValidationError()
577         {
578                 $this->raiseEvent('OnValidationError',$this,null);
579         }
580
581         /**
582          * This event is raised right before the validator starts to perform validation.
583          * You may use this event to change the behavior of validation.
584          * For example, you may disable the validator if certain condition is satisfied.
585          * Note, the event will NOT be raised if the validator is invisible.
586          */
587         public function onValidate()
588         {
589                 $this->raiseEvent('OnValidate',$this,null);
590         }
591
592         /**
593          * Renders the validator control.
594          * @param THtmlWriter writer for the rendering purpose
595          */
596         public function renderContents($writer)
597         {
598                 if(($text=$this->getText())!=='')
599                         $writer->write($text);
600                 else if(($text=$this->getErrorMessage())!=='')
601                         $writer->write($text);
602                 else
603                         parent::renderContents($writer);
604         }
605 }
606
607 /**
608  * TValidatorClientSide class.
609  *
610  * Client-side validator events can be modified through the {@link
611  * TBaseValidator::getClientSide ClientSide} property of a validator. The
612  * subproperties of ClientSide are those of the TValidatorClientSide
613  * properties. The client-side validator supports the following events.
614  *
615  * The <tt>OnValidate</tt> event is raise before the validator validation
616  * functions are called.
617  *
618  * The <tt>OnValidationSuccess</tt> event is raised after the validator has successfully
619  * validate the control.
620  *
621  * The <tt>OnValidationError</tt> event is raised after the validator fails validation.
622  *
623  * See the quickstart documentation for further details.
624  *
625  * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
626  * @package System.Web.UI.WebControls
627  * @since 3.0
628  */
629 class TValidatorClientSide extends TClientSideOptions
630 {
631         /**
632          * @return string javascript code for client-side OnValidate event.
633          */
634         public function getOnValidate()
635         {
636                 return $this->getOption('OnValidate');
637         }
638
639         /**
640          * Client-side OnValidate validator event is raise before the validators
641          * validation functions are called.
642          * @param string javascript code for client-side OnValidate event.
643          */
644         public function setOnValidate($javascript)
645         {
646                 $this->setFunction('OnValidate', $javascript);
647         }
648
649         /**
650          * Client-side OnSuccess event is raise after validation is successfull.
651          * This will override the default client-side validator behaviour.
652          * @param string javascript code for client-side OnSuccess event.
653          */
654         public function setOnValidationSuccess($javascript)
655         {
656                 $this->setFunction('OnValidationSuccess', $javascript);
657         }
658
659         /**
660          * @return string javascript code for client-side OnSuccess event.
661          */
662         public function getOnValidationSuccess()
663         {
664                 return $this->getOption('OnValidationSuccess');
665         }
666
667         /**
668          * Client-side OnError event is raised after validation failure.
669          * This will override the default client-side validator behaviour.
670          * @param string javascript code for client-side OnError event.
671          */
672         public function setOnValidationError($javascript)
673         {
674                 $this->setFunction('OnValidationError', $javascript);
675         }
676
677         /**
678          * @return string javascript code for client-side OnError event.
679          */
680         public function getOnValidationError()
681         {
682                 return $this->getOption('OnValidationError');
683         }
684
685         /**
686          * @param boolean true to revalidate when the control to validate changes value.
687          */
688         public function setObserveChanges($value)
689         {
690                 $this->setOption('ObserveChanges', TPropertyValue::ensureBoolean($value));
691         }
692
693         /**
694          * @return boolean true to observe changes.
695          */
696         public function getObserveChanges()
697         {
698                 $changes = $this->getOption('ObserveChanges');
699                 return ($changes===null) ? true : $changes;
700         }
701 }
702
703
704 /**
705  * TValidatorDisplayStyle class.
706  * TValidatorDisplayStyle defines the enumerable type for the possible styles
707  * that a validator control can display the error message.
708  *
709  * The following enumerable values are defined:
710  * - None: the error message is not displayed
711  * - Dynamic: the error message dynamically appears when the validator fails validation
712  * - Fixed: Similar to Dynamic except that the error message physically occupies the page layout (even though it may not be visible)
713  *
714  * @author Qiang Xue <qiang.xue@gmail.com>
715  * @package System.Web.UI.WebControls
716  * @since 3.0.4
717  */
718 class TValidatorDisplayStyle extends TEnumerable
719 {
720         const None='None';
721         const Dynamic='Dynamic';
722         const Fixed='Fixed';
723 }
724
725 /**
726  * TValidationDataType class.
727  * TValidationDataType defines the enumerable type for the possible data types that
728  * a comparison validator can validate upon.
729  *
730  * The following enumerable values are defined:
731  * - Integer
732  * - Float
733  * - Date
734  * - String
735  *
736  * @author Qiang Xue <qiang.xue@gmail.com>
737  * @package System.Web.UI.WebControls
738  * @since 3.0.4
739  */
740 class TValidationDataType extends TEnumerable
741 {
742         const Integer='Integer';
743         const Float='Float';
744         const Date='Date';
745         const String='String';
746 }
747