Gentics Portal.Node PHP API
 All Classes Namespaces Functions Variables Pages
UActiveForm.php
1 <?php
2 /**
3  * CActiveForm class file.
4  *
5  * @author Qiang Xue <qiang.xue@gmail.com>
6  * @link http://www.yiiframework.com/
7  * @copyright Copyright &copy; 2008-2010 Yii Software LLC
8  * @license http://www.yiiframework.com/license/
9  */
10 
11 /**
12  * CActiveForm provides a set of methods that can facilitate creating a form associated with some data models.
13  *
14  * CActiveForm implements a set of wrapper methods that call the corresponding
15  * 'active' methods in {@link CHtml}. For example, the {@link textField} method
16  * is a wrapper of {@link CHtml::activeTextField}.
17  *
18  * The 'beginWidget' and 'endWidget' call of CActiveForm widget will render
19  * the open and close form tags. Anything in between are rendered as form content
20  * (such as input fields, labels). We can call the wrapper methods of CActiveForm
21  * to generate these form contents. For example, calling {@link CActiveForm::textField},
22  * which is a wrapper of {@link CHtml::activeTextField}, would generate an input field
23  * for a specified model attribute.
24  *
25  * Besides the wrapper methods, CActiveForm also implements an important feature
26  * known as AJAX validation. This feature may be turned on setting {@link enableAjaxValidation}
27  * to be true. When the user enters some value in an input field, the AJAX validation
28  * feature would trigger an AJAX request to the server to call for validating the model
29  * with the current user inputs. If there are any validation errors, the corresponding
30  * error messages will show up next to the input fields immediately.
31  *
32  * The AJAX validation feature may greatly improve the user experience at entering
33  * data into a form. Because the validation is done on the server side using the rules
34  * defined in the data model, no extra javascript code needs to be written.
35  * More importantly, and the validation result is consistent with the server-side validation.
36  * And in case when the user turns off javascript in his browser, it automatically
37  * falls back to traditional validation via whole page submission.
38  *
39  * To use CActiveForm with AJAX validation, one needs to write both the view code
40  * and the controller action code.
41  *
42  * The following is a piece of sample view code:
43  * <pre>
44  * &lt;?php $form = $this->beginWidget('CActiveForm', array(
45  * 'id'=>'user-form',
46  * 'enableAjaxValidation'=>true,
47  * )); ?&gt;
48  *
49  * &lt;?php echo $form-&gt;errorSummary($model); ?&gt;
50  *
51  * &lt;div class="row"&gt;
52  * &lt;?php echo $form-&gt;labelEx($model,'firstName'); ?&gt;
53  * &lt;?php echo $form-&gt;textField($model,'firstName'); ?&gt;
54  * &lt;?php echo $form-&gt;error($model,'firstName'); ?&gt;
55  * &lt;/div&gt;
56  * &lt;div class="row"&gt;
57  * &lt;?php echo $form-&gt;labelEx($model,'lastName'); ?&gt;
58  * &lt;?php echo $form-&gt;textField($model,'lastName'); ?&gt;
59  * &lt;?php echo $form-&gt;error($model,'lastName'); ?&gt;
60  * &lt;/div&gt;
61  *
62  * &lt;?php $this->endWidget(); ?&gt;
63  * </pre>
64  *
65  * To respond to the AJAX validation requests, we need the following class code:
66  * <pre>
67  * public function actionCreate()
68  * {
69  * $model=new User;
70  * $this->performAjaxValidation($model);
71  * if(isset($_POST['User']))
72  * {
73  * $model->attributes=$_POST['User'];
74  * if($model->save())
75  * $this->redirect('index');
76  * }
77  * $this->render('create',array('model'=>$model));
78  * }
79  *
80  * protected function performAjaxValidation($model)
81  * {
82  * if(isset($_POST['ajax']) && $_POST['ajax']==='user-form')
83  * {
84  * echo CActiveForm::validate($model);
85  * Yii::app()->end();
86  * }
87  * }
88  * </pre>
89  * The method <code>performAjaxValidation</code> is the main extra code we add to our
90  * traditional model creation action code. In this method, we check if the request
91  * is submitted via AJAX by the 'user-form'. If so, we validate the model and return
92  * the validation results. We may call the same method in model update action.
93  *
94  * On the client side, an input field may be in one of the four states: initial (not validated),
95  * validating, error and success. To differentiate these states, CActiveForm automatically
96  * assigns different CSS classes for the last three states to the HTML element containing the input field.
97  * By default, these CSS classes are named as 'validating', 'error' and 'success', respectively.
98  * They may be changed by configuring the {@link options} property or specifying in the {@link error} method.
99  *
100  * Sometimes, we may want to limit the AJAX validation to certain model attributes only.
101  * This can be achieved by setting the model with a scenario that is specific for AJAX validation.
102  * Then only list those attributes that need AJAX validation in the scenario in {@link CModel::rules()} declaration.
103  *
104  * There are some limitations of CActiveForm regarding to its AJAX validation support.
105  * First, it does not validate with file upload fields.
106  * Second, it should not be used to perform validations that may cause server-side state change.
107  * For example, it is not suitable to perform CAPTCHA validation done by {@link CCaptchAction}
108  * because each validation request will increase the number of tests by one. Third, it is not designed
109  * to work with tabular data input for the moment.
110  *
111  * Because CActiveForm relies on submitting the whole form in AJAX mode to perform the validation,
112  * if the form has a lot of data to submit, the performance may not be good. In this case,
113  * you should design your own lightweight AJAX validation.
114  *
115  * @author Qiang Xue <qiang.xue@gmail.com>
116  * @version $Id: CActiveForm.php 2104 2010-05-06 20:52:29Z qiang.xue $
117  * @package system.web.widgets
118  * @since 1.1.1
119  */
120 class UActiveForm extends CWidget
121 {
122  /**
123  * @var mixed the form action URL (see {@link normalizeUrl} for details about this parameter.)
124  * If not set, the current page URL is used.
125  */
126  public $action='';
127  /**
128  * @var string the form submission method. This should be either 'post' or 'get'.
129  * Defaults to 'post'.
130  */
131  public $method='post';
132  /**
133  * @var boolean whether to generate a stateful form (See {@link CHtml::statefulForm}). Defaults to false.
134  */
135  public $stateful=false;
136  /**
137  * @var string the CSS class name for error messages. Defaults to 'errorMessage'.
138  * Individual {@link error} call may override this value by specifying the 'class' HTML option.
139  */
140  public $errorMessageCssClass='errorMessage';
141  /**
142  * @var array additional HTML attributes that should be rendered for the form tag.
143  */
144  public $htmlOptions=array();
145  /**
146  * @var array the options to be passed to the javascript validation plugin.
147  * The following options are supported:
148  * <ul>
149  * <li>ajaxVar: string, the name of the parameter indicating the request is an AJAX request.
150  * When the AJAX validation is triggered, a parameter named as this property will be sent
151  * together with the other form data to the server. The parameter value is the form ID.
152  * The server side can then detect who triggers the AJAX validation and react accordingly.
153  * Defaults to 'ajax'.</li>
154  * <li>validationUrl: string, the URL that performs the AJAX validations.
155  * If not set, it will take the value of {@link action}.</li>
156  * <li>validationDelay: integer, the number of milliseconds that an AJAX validation should be
157  * delayed after an input is changed. A value 0 means the validation will be triggered immediately
158  * when an input is changed. A value greater than 0 means changing several inputs may only
159  * trigger a single validation if they happen fast enough, which may help reduce the server load.
160  * Defaults to 100 (0.1 second).</li>
161  * <li>validateOnSubmit: boolean, whether to perform AJAX validation when the form is being submitted.
162  * If there are any validation errors, the form submission will be stopped.
163  * Defaults to false.</li>
164  * <li>validateOnChange: boolean, whether to trigger an AJAX validation
165  * each time when an input's value is changed. You may want to turn this off
166  * if it causes too much performance impact, because each AJAX validation request
167  * will submit the data of the whole form. Defaults to true.</li>
168  * <li>validateOnType: boolean, whether to trigger an AJAX validation each time when the user
169  * presses a key. When setting this property to be true, you should tune up the 'validationDelay'
170  * option to avoid triggering too many AJAX validations. Defaults to false.</li>
171  * <li>hideErrorMessage: boolean, whether to hide the error message even if there is an error.
172  * Defaults to false, which means the error message will show up whenever the input has an error.</li>
173  * <li>inputContainer: string, the jQuery selector for the HTML element containing the input field.
174  * During the validation process, CActiveForm will set different CSS class for the container element
175  * to indicate the state change. If not set, it means the closest 'div' element that contains the input field.</li>
176  * <li>errorCssClass: string, the CSS class to be assigned to the container whose associated input
177  * has AJAX validation error. Defaults to 'error'.</li>
178  * <li>successCssClass: string, the CSS class to be assigned to the container whose associated input
179  * passes AJAX validation without any error. Defaults to 'success'.</li>
180  * <li>validatingCssClass: string, the CSS class to be assigned to the container whose associated input
181  * is currently being validated via AJAX. Defaults to 'validating'.</li>
182  * <li>errorMessageCssClass: string, the CSS class assigned to the error messages returned
183  * by AJAX validations. Defaults to 'errorMessage'.</li>
184  * <li>beforeValidate: function, the function that will be invoked before performing ajax-based validation
185  * triggered by form submission action (available only when validateOnSubmit is set true).
186  * The expected function signature should be <code>beforeValidate(form) {...}</code>, where 'form' is
187  * the jquery representation of the form object. If the return value of this function is NOT true, the validation
188  * will be cancelled.
189  *
190  * Note that because this option refers to a js function, you should prefix the value with 'js:' to prevent it
191  * from being encoded as a string. This option has been available since version 1.1.3.</li>
192  * <li>afterValidate: function, the function that will be invoked after performing ajax-based validation
193  * triggered by form submission action (available only when validateOnSubmit is set true).
194  * The expected function signature should be <code>afterValidate(form, data, hasError) {...}</code>, where 'form' is
195  * the jquery representation of the form object; 'data' is the JSON response from the server-side validation; 'hasError'
196  * is a boolean value indicating whether there is any validation error. If the return value of this function is NOT true,
197  * the normal form submission will be cancelled.
198  *
199  * Note that because this option refers to a js function, you should prefix the value with 'js:' to prevent it
200  * from being encoded as a string. This option has been available since version 1.1.3.</li>
201  * <li>beforeValidateAttribute: function, the function that will be invoked before performing ajax-based validation
202  * triggered by a single attribute input change. The expected function signature should be
203  * <code>beforeValidateAttribute(form, attribute) {...}</code>, where 'form' is the jquery representation of the form object
204  * and 'attribute' refers to the js options for the triggering attribute (see {@link error}).
205  * If the return value of this function is NOT true, the validation will be cancelled.
206  *
207  * Note that because this option refers to a js function, you should prefix the value with 'js:' to prevent it
208  * from being encoded as a string. This option has been available since version 1.1.3.</li>
209  * <li>afterValidateAttribute: function, the function that will be invoked after performing ajax-based validation
210  * triggered by a single attribute input change. The expected function signature should be
211  * <code>beforeValidateAttribute(form, attribute, data, hasError) {...}</code>, where 'form' is the jquery
212  * representation of the form object; 'attribute' refers to the js options for the triggering attribute (see {@link error});
213  * 'data' is the JSON response from the server-side validation; 'hasError' is a boolean value indicating whether
214  * there is any validation error.
215  *
216  * Note that because this option refers to a js function, you should prefix the value with 'js:' to prevent it
217  * from being encoded as a string. This option has been available since version 1.1.3.</li>
218  * </ul>
219  *
220  * Some of the above options may be overridden in individual calls of {@link error()}.
221  * They include: validationDelay, validateOnChange, validateOnType, hideErrorMessage,
222  * inputContainer, errorCssClass, successCssClass, validatingCssClass, beforeValidateAttribute, afterValidateAttribute.
223  */
224  public $clientOptions=array();
225  /**
226  * @var boolean whether to enable data validation via AJAX. Defaults to false.
227  * When this property is set true, you should
228  */
229  public $enableAjaxValidation=false;
230  public $disableAjaxValidationAttributes = array();
231 
232  private $_attributes=array();
233  private $_summary;
234 
235  /**
236  * Initializes the widget.
237  * This renders the form open tag.
238  */
239  public function init()
240  {
241  $this->htmlOptions['id']=$this->id;
242  if($this->stateful)
243  echo CHtml::statefulForm($this->action, $this->method, $this->htmlOptions);
244  else
245  echo CHtml::beginForm($this->action, $this->method, $this->htmlOptions);
246  }
247 
248  /**
249  * Runs the widget.
250  * This registers the necessary javascript code and renders the form close tag.
251  */
252  public function run()
253  {
254  echo CHtml::endForm();
255  if(!$this->enableAjaxValidation || empty($this->_attributes))
256  return;
257  $options=$this->clientOptions;
258  if(isset($this->clientOptions['validationUrl']) && is_array($this->clientOptions['validationUrl']))
259  $options['validationUrl']=CHtml::normalizeUrl($this->clientOptions['validationUrl']);
260  $options['attributes']=array();
261  foreach ($this->_attributes as $attr=>$item) {
262  if (in_array($attr,$this->disableAjaxValidationAttributes)===false) {
263 // $item['enableAjaxValidation'] = true;
264  array_push($options['attributes'],$item);
265  }
266  }
267  if($this->_summary!==null)
268  $options['summaryID']=$this->_summary;
269 
270  $options=CJavaScript::encode($options);
271  Yii::app()->clientScript->registerCoreScript('yiiactiveform');
272  $id=$this->id;
273  Yii::app()->clientScript->registerScript(__CLASS__.'#'.$id,"\$('#$id').yiiactiveform($options);");
274  }
275 
276  /**
277  * Displays the first validation error for a model attribute.
278  * This is similar to {@link CHtml::error} except that it registers the model attribute
279  * so that if its value is changed by users, an AJAX validation may be triggered.
280  * @param CModel the data model
281  * @param string the attribute name
282  * @param array additional HTML attributes to be rendered in the container div tag.
283  * Besides all those options available in {@link CHtml::error}, the following options are recognized in addition:
284  * <ul>
285  * <li>validationDelay</li>
286  * <li>validateOnChange</li>
287  * <li>validateOnType</li>
288  * <li>hideErrorMessage</li>
289  * <li>inputContainer</li>
290  * <li>errorCssClass</li>
291  * <li>successCssClass</li>
292  * <li>validatingCssClass</li>
293  * <li>beforeValidateAttribute</li>
294  * <li>afterValidateAttribute</li>
295  * </ul>
296  * These options override the corresponding options as declared in {@link options} for this
297  * particular model attribute. For more details about these options, please refer to {@link clientOptions}.
298  * Note that these options are only used when {@link enableAjaxValidation} is set true.
299  * @param boolean whether to enable AJAX validation for the specified attribute.
300  * Note that in order toe enable AJAX validation, both {@link enableAjaxValidation} and this parameter
301  * must be true.
302  * @return string the validation result (error display or success message).
303  * @see CHtml::error
304  */
305  public function error($model,$attribute,$htmlOptions=array(),$enableAjaxValidation=true)
306  {
307  if(!$this->enableAjaxValidation || !$enableAjaxValidation)
308  return CHtml::error($model,$attribute,$htmlOptions);
309 
310  $inputID=isset($htmlOptions['inputID']) ? $htmlOptions['inputID'] : CHtml::activeId($model,$attribute);
311  unset($htmlOptions['inputID']);
312  if(!isset($htmlOptions['id']))
313  $htmlOptions['id']=$inputID.'_em_';
314 
315  $option=array('inputID'=>$inputID, 'errorID'=>$htmlOptions['id']);
316 
317  $optionNames=array(
318  'validationDelay',
319  'validateOnChange',
320  'validateOnType',
321  'hideErrorMessage',
322  'inputContainer',
323  'errorCssClass',
324  'successCssClass',
325  'validatingCssClass',
326  'beforeValidateAttribute',
327  'afterValidateAttribute',
328  );
329  foreach($optionNames as $name)
330  {
331  if(isset($htmlOptions[$name]))
332  {
333  $option[$name]=$htmlOptions[$name];
334  unset($htmlOptions[$name]);
335  }
336  }
337  if($model instanceof CActiveRecord && !$model->isNewRecord)
338  $option['status']=1;
339 
340  if(!isset($htmlOptions['class']))
341  $htmlOptions['class']=$this->errorMessageCssClass;
342  $html=CHtml::error($model,$attribute,$htmlOptions);
343  if($html==='')
344  {
345  if(isset($htmlOptions['style']))
346  $htmlOptions['style']=rtrim($htmlOptions['style'],';').';display:none';
347  else
348  $htmlOptions['style']='display:none';
349  $html=CHtml::tag('div',$htmlOptions,'');
350  }
351 
352  $this->_attributes[$inputID]=$option;
353  return $html;
354  }
355 
356  /**
357  * Displays a summary of validation errors for one or several models.
358  * This method is very similar to {@link CHtml::errorSummary} except that it also works
359  * when AJAX validation is performed.
360  * @param mixed the models whose input errors are to be displayed. This can be either
361  * a single model or an array of models.
362  * @param string a piece of HTML code that appears in front of the errors
363  * @param string a piece of HTML code that appears at the end of the errors
364  * @param array additional HTML attributes to be rendered in the container div tag.
365  * @return string the error summary. Empty if no errors are found.
366  * @see CHtml::errorSummary
367  */
368  public function errorSummary($models,$header=null,$footer=null,$htmlOptions=array())
369  {
370  if(!$this->enableAjaxValidation)
371  return CHtml::errorSummary($models,$header,$footer,$htmlOptions);
372 
373  if(!isset($htmlOptions['id']))
374  $htmlOptions['id']=$this->id.'_es_';
375  $html=CHtml::errorSummary($models,$header,$footer,$htmlOptions);
376  if($html==='')
377  {
378  if($header===null)
379  $header='<p>'.Yii::t('yii','Please fix the following input errors:').'</p>';
380  if(!isset($htmlOptions['class']))
381  $htmlOptions['class']=CHtml::$errorSummaryCss;
382  $htmlOptions['style']=isset($htmlOptions['style']) ? rtrim($htmlOptions['style'],';').';display:none' : 'display:none';
383  $html=CHtml::tag('div',$htmlOptions,$header."\n<ul><li>dummy</li></ul>".$footer);
384  }
385 
386  $this->_summary=$htmlOptions['id'];
387  return $html;
388  }
389 
390  /**
391  * Renders an HTML label for a model attribute.
392  * This method is a wrapper of {@link CHtml::activeLabel}.
393  * Please check {@link CHtml::activeLabel} for detailed information
394  * about the parameters for this method.
395  */
396  public function label($model,$attribute,$htmlOptions=array())
397  {
398  return CHtml::activeLabel($model,$attribute,$htmlOptions);
399  }
400 
401  /**
402  * Renders an HTML label for a model attribute.
403  * This method is a wrapper of {@link CHtml::activeLabelEx}.
404  * Please check {@link CHtml::activeLabelEx} for detailed information
405  * about the parameters for this method.
406  */
407  public function labelEx($model,$attribute,$htmlOptions=array())
408  {
409  return CHtml::activeLabelEx($model,$attribute,$htmlOptions);
410  }
411 
412  /**
413  * Renders a text field for a model attribute.
414  * This method is a wrapper of {@link CHtml::activeTextField}.
415  * Please check {@link CHtml::activeTextField} for detailed information
416  * about the parameters for this method.
417  */
418  public function textField($model,$attribute,$htmlOptions=array())
419  {
420  return CHtml::activeTextField($model,$attribute,$htmlOptions);
421  }
422 
423  /**
424  * Renders a hidden field for a model attribute.
425  * This method is a wrapper of {@link CHtml::activeHiddenField}.
426  * Please check {@link CHtml::activeHiddenField} for detailed information
427  * about the parameters for this method.
428  */
429  public function hiddenField($model,$attribute,$htmlOptions=array())
430  {
431  return CHtml::activeHiddenField($model,$attribute,$htmlOptions);
432  }
433 
434  /**
435  * Renders a password field for a model attribute.
436  * This method is a wrapper of {@link CHtml::activePasswordField}.
437  * Please check {@link CHtml::activePasswordField} for detailed information
438  * about the parameters for this method.
439  */
440  public function passwordField($model,$attribute,$htmlOptions=array())
441  {
442  return CHtml::activePasswordField($model,$attribute,$htmlOptions);
443  }
444 
445  /**
446  * Renders a text area for a model attribute.
447  * This method is a wrapper of {@link CHtml::activeTextArea}.
448  * Please check {@link CHtml::activeTextArea} for detailed information
449  * about the parameters for this method.
450  */
451  public function textArea($model,$attribute,$htmlOptions=array())
452  {
453  return CHtml::activeTextArea($model,$attribute,$htmlOptions);
454  }
455 
456  /**
457  * Renders a file field for a model attribute.
458  * This method is a wrapper of {@link CHtml::activeFileField}.
459  * Please check {@link CHtml::activeFileField} for detailed information
460  * about the parameters for this method.
461  */
462  public function fileField($model,$attribute,$htmlOptions=array())
463  {
464  return CHtml::activeFileField($model,$attribute,$htmlOptions);
465  }
466 
467  /**
468  * Renders a radio button for a model attribute.
469  * This method is a wrapper of {@link CHtml::activeRadioButton}.
470  * Please check {@link CHtml::activeRadioButton} for detailed information
471  * about the parameters for this method.
472  */
473  public function radioButton($model,$attribute,$htmlOptions=array())
474  {
475  return CHtml::activeRadioButton($model,$attribute,$htmlOptions);
476  }
477 
478  /**
479  * Renders a checkbox for a model attribute.
480  * This method is a wrapper of {@link CHtml::activeCheckBox}.
481  * Please check {@link CHtml::activeCheckBox} for detailed information
482  * about the parameters for this method.
483  */
484  public function checkBox($model,$attribute,$htmlOptions=array())
485  {
486  return CHtml::activeCheckBox($model,$attribute,$htmlOptions);
487  }
488 
489  /**
490  * Renders a dropdown list for a model attribute.
491  * This method is a wrapper of {@link CHtml::activeDropDownList}.
492  * Please check {@link CHtml::activeDropDownList} for detailed information
493  * about the parameters for this method.
494  */
495  public function dropDownList($model,$attribute,$data,$htmlOptions=array())
496  {
497  return CHtml::activeDropDownList($model,$attribute,$data,$htmlOptions);
498  }
499 
500  /**
501  * Renders a list box for a model attribute.
502  * This method is a wrapper of {@link CHtml::activeListBox}.
503  * Please check {@link CHtml::activeListBox} for detailed information
504  * about the parameters for this method.
505  */
506  public function listBox($model,$attribute,$data,$htmlOptions=array())
507  {
508  return CHtml::activeListBox($model,$attribute,$data,$htmlOptions);
509  }
510 
511  /**
512  * Renders a checkbox list for a model attribute.
513  * This method is a wrapper of {@link CHtml::activeCheckBoxList}.
514  * Please check {@link CHtml::activeCheckBoxList} for detailed information
515  * about the parameters for this method.
516  */
517  public function checkBoxList($model,$attribute,$data,$htmlOptions=array())
518  {
519  return CHtml::activeCheckBoxList($model,$attribute,$data,$htmlOptions);
520  }
521 
522  /**
523  * Renders a radio button list for a model attribute.
524  * This method is a wrapper of {@link CHtml::activeRadioButtonList}.
525  * Please check {@link CHtml::activeRadioButtonList} for detailed information
526  * about the parameters for this method.
527  */
528  public function radioButtonList($model,$attribute,$data,$htmlOptions=array())
529  {
530  return CHtml::activeRadioButtonList($model,$attribute,$data,$htmlOptions);
531  }
532 
533  /**
534  * Validates one or several models and returns the results in JSON format.
535  * This is a helper method that simplies the way of writing AJAX validation code.
536  * @param mixed a single model instance or an array of models.
537  * @param array list of attributes that should be validated. Defaults to null,
538  * meaning any attribute listed in the applicable validation rules of the models should be
539  * validated. If this parameter is given as a list of attributes, only
540  * the listed attributes will be validated.
541  * @param boolean whether to load the data from $_POST array in this method.
542  * If this is true, the model will be populated from <code>$_POST[ModelClass]</code>.
543  * @return string the JSON representation of the validation error messages.
544  */
545  public static function validate($models, $attributes=null, $loadInput=true)
546  {
547  $result=array();
548  if(!is_array($models))
549  $models=array($models);
550  foreach($models as $model)
551  {
552  if($loadInput && isset($_POST[get_class($model)]))
553  $model->attributes=$_POST[get_class($model)];
554  $model->validate($attributes);
555  foreach($model->getErrors() as $attribute=>$errors)
556  $result[CHtml::activeId($model,$attribute)]=$errors;
557  }
558  return function_exists('json_encode') ? json_encode($result) : CJSON::encode($result);
559  }
560 }