diff options
| author | Patrick Seeger <pseeger@ccwn.org> | 2012-04-13 23:11:05 +0200 |
|---|---|---|
| committer | Patrick Seeger <pseeger@ccwn.org> | 2012-04-13 23:11:05 +0200 |
| commit | 341cc4dd9c53ffbfb863e026dd58549c1082c7a7 (patch) | |
| tree | 1bbbed20313bafb9b063b6b4d894fe580d8b000f /framework/web/form | |
Diffstat (limited to 'framework/web/form')
| -rw-r--r-- | framework/web/form/CForm.php | 615 | ||||
| -rw-r--r-- | framework/web/form/CFormButtonElement.php | 139 | ||||
| -rw-r--r-- | framework/web/form/CFormElement.php | 168 | ||||
| -rw-r--r-- | framework/web/form/CFormElementCollection.php | 112 | ||||
| -rw-r--r-- | framework/web/form/CFormInputElement.php | 255 | ||||
| -rw-r--r-- | framework/web/form/CFormStringElement.php | 71 |
6 files changed, 1360 insertions, 0 deletions
diff --git a/framework/web/form/CForm.php b/framework/web/form/CForm.php new file mode 100644 index 0000000..e974eaf --- /dev/null +++ b/framework/web/form/CForm.php @@ -0,0 +1,615 @@ +<?php +/** + * CForm class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2011 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CForm represents a form object that contains form input specifications. + * + * The main purpose of introducing the abstraction of form objects is to enhance the + * reusability of forms. In particular, we can divide a form in two parts: those + * that specify each individual form inputs, and those that decorate the form inputs. + * A CForm object represents the former part. It relies on the rendering process to + * accomplish form input decoration. Reusability is mainly achieved in the rendering process. + * That is, a rendering process can be reused to render different CForm objects. + * + * A form can be rendered in different ways. One can call the {@link render} method + * to get a quick form rendering without writing any HTML code; one can also override + * {@link render} to render the form in a different layout; and one can use an external + * view template to render each form element explicitly. In these ways, the {@link render} + * method can be applied to all kinds of forms and thus achieves maximum reusability; + * while the external view template keeps maximum flexibility in rendering complex forms. + * + * Form input specifications are organized in terms of a form element hierarchy. + * At the root of the hierarchy, it is the root CForm object. The root form object maintains + * its children in two collections: {@link elements} and {@link buttons}. + * The former contains non-button form elements ({@link CFormStringElement}, + * {@link CFormInputElement} and CForm); while the latter mainly contains + * button elements ({@link CFormButtonElement}). When a CForm object is embedded in the + * {@link elements} collection, it is called a sub-form which can have its own {@link elements} + * and {@link buttons} collections and thus form the whole form hierarchy. + * + * Sub-forms are mainly used to handle multiple models. For example, in a user + * registration form, we can have the root form to collect input for the user + * table while a sub-form to collect input for the profile table. Sub-form is also + * a good way to partition a lengthy form into shorter ones, even though all inputs + * may belong to the same model. + * + * Form input specifications are given in terms of a configuration array which is + * used to initialize the property values of a CForm object. The {@link elements} and + * {@link buttons} properties need special attention as they are the main properties + * to be configured. To configure {@link elements}, we should give it an array like + * the following: + * <pre> + * 'elements'=>array( + * 'username'=>array('type'=>'text', 'maxlength'=>80), + * 'password'=>array('type'=>'password', 'maxlength'=>80), + * ) + * </pre> + * The above code specifies two input elements: 'username' and 'password'. Note the model + * object must have exactly the same attributes 'username' and 'password'. Each element + * has a type which specifies what kind of input should be used. The rest of the array elements + * (e.g. 'maxlength') in an input specification are rendered as HTML element attributes + * when the input field is rendered. The {@link buttons} property is configured similarly. + * + * For more details about configuring form elements, please refer to {@link CFormInputElement} + * and {@link CFormButtonElement}. + * + * @property CForm $root The top-level form object. + * @property CActiveForm $activeFormWidget The active form widget associated with this form. + * This method will return the active form widget as specified by {@link activeForm}. + * @property CBaseController $owner The owner of this form. This refers to either a controller or a widget + * by which the form is created and rendered. + * @property CModel $model The model associated with this form. If this form does not have a model, + * it will look for a model in its ancestors. + * @property array $models The models that are associated with this form or its sub-forms. + * @property CFormElementCollection $elements The form elements. + * @property CFormElementCollection $buttons The form elements. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CForm.php 3426 2011-10-25 00:01:09Z alexander.makarow $ + * @package system.web.form + * @since 1.1 + */ +class CForm extends CFormElement implements ArrayAccess +{ + /** + * @var string the title for this form. By default, if this is set, a fieldset may be rendered + * around the form body using the title as its legend. Defaults to null. + */ + public $title; + /** + * @var string the description of this form. + */ + public $description; + /** + * @var string the submission method of this form. Defaults to 'post'. + * This property is ignored when this form is a sub-form. + */ + public $method='post'; + /** + * @var mixed the form action URL (see {@link CHtml::normalizeUrl} for details about this parameter.) + * Defaults to an empty string, meaning the current request URL. + * This property is ignored when this form is a sub-form. + */ + public $action=''; + /** + * @var string the name of the class for representing a form input element. Defaults to 'CFormInputElement'. + */ + public $inputElementClass='CFormInputElement'; + /** + * @var string the name of the class for representing a form button element. Defaults to 'CFormButtonElement'. + */ + public $buttonElementClass='CFormButtonElement'; + /** + * @var array HTML attribute values for the form tag. When the form is embedded within another form, + * this property will be used to render the HTML attribute values for the fieldset enclosing the child form. + */ + public $attributes=array(); + /** + * @var boolean whether to show error summary. Defaults to false. + */ + public $showErrorSummary=false; + /** + * @var array the configuration used to create the active form widget. + * The widget will be used to render the form tag and the error messages. + * The 'class' option is required, which specifies the class of the widget. + * The rest of the options will be passed to {@link CBaseController::beginWidget()} call. + * Defaults to array('class'=>'CActiveForm'). + * @since 1.1.1 + */ + public $activeForm=array('class'=>'CActiveForm'); + + private $_model; + private $_elements; + private $_buttons; + private $_activeForm; + + /** + * Constructor. + * If you override this method, make sure you do not modify the method + * signature, and also make sure you call the parent implementation. + * @param mixed $config the configuration for this form. It can be a configuration array + * or the path alias of a PHP script file that returns a configuration array. + * The configuration array consists of name-value pairs that are used to initialize + * the properties of this form. + * @param CModel $model the model object associated with this form. If it is null, + * the parent's model will be used instead. + * @param mixed $parent the direct parent of this form. This could be either a {@link CBaseController} + * object (a controller or a widget), or a {@link CForm} object. + * If the former, it means the form is a top-level form; if the latter, it means this form is a sub-form. + */ + public function __construct($config,$model=null,$parent=null) + { + $this->setModel($model); + if($parent===null) + $parent=Yii::app()->getController(); + parent::__construct($config,$parent); + $this->init(); + } + + /** + * Initializes this form. + * This method is invoked at the end of the constructor. + * You may override this method to provide customized initialization (such as + * configuring the form object). + */ + protected function init() + { + } + + /** + * Returns a value indicating whether this form is submitted. + * @param string $buttonName the name of the submit button + * @param boolean $loadData whether to call {@link loadData} if the form is submitted so that + * the submitted data can be populated to the associated models. + * @return boolean whether this form is submitted. + * @see loadData + */ + public function submitted($buttonName='submit',$loadData=true) + { + $ret=$this->clicked($this->getUniqueId()) && $this->clicked($buttonName); + if($ret && $loadData) + $this->loadData(); + return $ret; + } + + /** + * Returns a value indicating whether the specified button is clicked. + * @param string $name the button name + * @return boolean whether the button is clicked. + */ + public function clicked($name) + { + if(strcasecmp($this->getRoot()->method,'get')) + return isset($_POST[$name]); + else + return isset($_GET[$name]); + } + + /** + * Validates the models associated with this form. + * All models, including those associated with sub-forms, will perform + * the validation. You may use {@link CModel::getErrors()} to retrieve the validation + * error messages. + * @return boolean whether all models are valid + */ + public function validate() + { + $ret=true; + foreach($this->getModels() as $model) + $ret=$model->validate() && $ret; + return $ret; + } + + /** + * Loads the submitted data into the associated model(s) to the form. + * This method will go through all models associated with this form and its sub-forms + * and massively assign the submitted data to the models. + * @see submitted + */ + public function loadData() + { + if($this->_model!==null) + { + $class=get_class($this->_model); + if(strcasecmp($this->getRoot()->method,'get')) + { + if(isset($_POST[$class])) + $this->_model->setAttributes($_POST[$class]); + } + else if(isset($_GET[$class])) + $this->_model->setAttributes($_GET[$class]); + } + foreach($this->getElements() as $element) + { + if($element instanceof self) + $element->loadData(); + } + } + + /** + * @return CForm the top-level form object + */ + public function getRoot() + { + $root=$this; + while($root->getParent() instanceof self) + $root=$root->getParent(); + return $root; + } + + /** + * @return CActiveForm the active form widget associated with this form. + * This method will return the active form widget as specified by {@link activeForm}. + * @since 1.1.1 + */ + public function getActiveFormWidget() + { + if($this->_activeForm!==null) + return $this->_activeForm; + else + return $this->getRoot()->_activeForm; + } + + /** + * @return CBaseController the owner of this form. This refers to either a controller or a widget + * by which the form is created and rendered. + */ + public function getOwner() + { + $owner=$this->getParent(); + while($owner instanceof self) + $owner=$owner->getParent(); + return $owner; + } + + /** + * Returns the model that this form is associated with. + * @param boolean $checkParent whether to return parent's model if this form doesn't have model by itself. + * @return CModel the model associated with this form. If this form does not have a model, + * it will look for a model in its ancestors. + */ + public function getModel($checkParent=true) + { + if(!$checkParent) + return $this->_model; + $form=$this; + while($form->_model===null && $form->getParent() instanceof self) + $form=$form->getParent(); + return $form->_model; + } + + /** + * @param CModel $model the model to be associated with this form + */ + public function setModel($model) + { + $this->_model=$model; + } + + /** + * Returns all models that are associated with this form or its sub-forms. + * @return array the models that are associated with this form or its sub-forms. + */ + public function getModels() + { + $models=array(); + if($this->_model!==null) + $models[]=$this->_model; + foreach($this->getElements() as $element) + { + if($element instanceof self) + $models=array_merge($models,$element->getModels()); + } + return $models; + } + + /** + * Returns the input elements of this form. + * This includes text strings, input elements and sub-forms. + * Note that the returned result is a {@link CFormElementCollection} object, which + * means you can use it like an array. For more details, see {@link CMap}. + * @return CFormElementCollection the form elements. + */ + public function getElements() + { + if($this->_elements===null) + $this->_elements=new CFormElementCollection($this,false); + return $this->_elements; + } + + /** + * Configures the input elements of this form. + * The configuration must be an array of input configuration array indexed by input name. + * Each input configuration array consists of name-value pairs that are used to initialize + * a {@link CFormStringElement} object (when 'type' is 'string'), a {@link CFormElement} object + * (when 'type' is a string ending with 'Form'), or a {@link CFormInputElement} object in + * all other cases. + * @param array $elements the button configurations + */ + public function setElements($elements) + { + $collection=$this->getElements(); + foreach($elements as $name=>$config) + $collection->add($name,$config); + } + + /** + * Returns the button elements of this form. + * Note that the returned result is a {@link CFormElementCollection} object, which + * means you can use it like an array. For more details, see {@link CMap}. + * @return CFormElementCollection the form elements. + */ + public function getButtons() + { + if($this->_buttons===null) + $this->_buttons=new CFormElementCollection($this,true); + return $this->_buttons; + } + + /** + * Configures the buttons of this form. + * The configuration must be an array of button configuration array indexed by button name. + * Each button configuration array consists of name-value pairs that are used to initialize + * a {@link CFormButtonElement} object. + * @param array $buttons the button configurations + */ + public function setButtons($buttons) + { + $collection=$this->getButtons(); + foreach($buttons as $name=>$config) + $collection->add($name,$config); + } + + /** + * Renders the form. + * The default implementation simply calls {@link renderBegin}, {@link renderBody} and {@link renderEnd}. + * @return string the rendering result + */ + public function render() + { + return $this->renderBegin() . $this->renderBody() . $this->renderEnd(); + } + + /** + * Renders the open tag of the form. + * The default implementation will render the open form tag. + * @return string the rendering result + */ + public function renderBegin() + { + if($this->getParent() instanceof self) + return ''; + else + { + $options=$this->activeForm; + if(isset($options['class'])) + { + $class=$options['class']; + unset($options['class']); + } + else + $class='CActiveForm'; + $options['action']=$this->action; + $options['method']=$this->method; + if(isset($options['htmlOptions'])) + { + foreach($this->attributes as $name=>$value) + $options['htmlOptions'][$name]=$value; + } + else + $options['htmlOptions']=$this->attributes; + ob_start(); + $this->_activeForm=$this->getOwner()->beginWidget($class, $options); + return ob_get_clean() . "<div style=\"visibility:hidden\">".CHtml::hiddenField($this->getUniqueID(),1)."</div>\n"; + } + } + + /** + * Renders the close tag of the form. + * @return string the rendering result + */ + public function renderEnd() + { + if($this->getParent() instanceof self) + return ''; + else + { + ob_start(); + $this->getOwner()->endWidget(); + return ob_get_clean(); + } + } + + /** + * Renders the body content of this form. + * This method mainly renders {@link elements} and {@link buttons}. + * If {@link title} or {@link description} is specified, they will be rendered as well. + * And if the associated model contains error, the error summary may also be displayed. + * The form tag will not be rendered. Please call {@link renderBegin} and {@link renderEnd} + * to render the open and close tags of the form. + * You may override this method to customize the rendering of the form. + * @return string the rendering result + */ + public function renderBody() + { + $output=''; + if($this->title!==null) + { + if($this->getParent() instanceof self) + { + $attributes=$this->attributes; + unset($attributes['name'],$attributes['type']); + $output=CHtml::openTag('fieldset', $attributes)."<legend>".$this->title."</legend>\n"; + } + else + $output="<fieldset>\n<legend>".$this->title."</legend>\n"; + } + + if($this->description!==null) + $output.="<div class=\"description\">\n".$this->description."</div>\n"; + + if($this->showErrorSummary && ($model=$this->getModel(false))!==null) + $output.=$this->getActiveFormWidget()->errorSummary($model)."\n"; + + $output.=$this->renderElements()."\n".$this->renderButtons()."\n"; + + if($this->title!==null) + $output.="</fieldset>\n"; + + return $output; + } + + /** + * Renders the {@link elements} in this form. + * @return string the rendering result + */ + public function renderElements() + { + $output=''; + foreach($this->getElements() as $element) + $output.=$this->renderElement($element); + return $output; + } + + /** + * Renders the {@link buttons} in this form. + * @return string the rendering result + */ + public function renderButtons() + { + $output=''; + foreach($this->getButtons() as $button) + $output.=$this->renderElement($button); + return $output!=='' ? "<div class=\"row buttons\">".$output."</div>\n" : ''; + } + + /** + * Renders a single element which could be an input element, a sub-form, a string, or a button. + * @param mixed $element the form element to be rendered. This can be either a {@link CFormElement} instance + * or a string representing the name of the form element. + * @return string the rendering result + */ + public function renderElement($element) + { + if(is_string($element)) + { + if(($e=$this[$element])===null && ($e=$this->getButtons()->itemAt($element))===null) + return $element; + else + $element=$e; + } + if($element->getVisible()) + { + if($element instanceof CFormInputElement) + { + if($element->type==='hidden') + return "<div style=\"visibility:hidden\">\n".$element->render()."</div>\n"; + else + return "<div class=\"row field_{$element->name}\">\n".$element->render()."</div>\n"; + } + else if($element instanceof CFormButtonElement) + return $element->render()."\n"; + else + return $element->render(); + } + return ''; + } + + /** + * This method is called after an element is added to the element collection. + * @param string $name the name of the element + * @param CFormElement $element the element that is added + * @param boolean $forButtons whether the element is added to the {@link buttons} collection. + * If false, it means the element is added to the {@link elements} collection. + */ + public function addedElement($name,$element,$forButtons) + { + } + + /** + * This method is called after an element is removed from the element collection. + * @param string $name the name of the element + * @param CFormElement $element the element that is removed + * @param boolean $forButtons whether the element is removed from the {@link buttons} collection + * If false, it means the element is removed from the {@link elements} collection. + */ + public function removedElement($name,$element,$forButtons) + { + } + + /** + * Evaluates the visibility of this form. + * This method will check the visibility of the {@link elements}. + * If any one of them is visible, the form is considered as visible. Otherwise, it is invisible. + * @return boolean whether this form is visible. + */ + protected function evaluateVisible() + { + foreach($this->getElements() as $element) + if($element->getVisible()) + return true; + return false; + } + + /** + * Returns a unique ID that identifies this form in the current page. + * @return string the unique ID identifying this form + */ + protected function getUniqueId() + { + if(isset($this->attributes['id'])) + return 'yform_'.$this->attributes['id']; + else + return 'yform_'.sprintf('%x',crc32(serialize(array_keys($this->getElements()->toArray())))); + } + + /** + * Returns whether there is an element at the specified offset. + * This method is required by the interface ArrayAccess. + * @param mixed $offset the offset to check on + * @return boolean + */ + public function offsetExists($offset) + { + return $this->getElements()->contains($offset); + } + + /** + * Returns the element at the specified offset. + * This method is required by the interface ArrayAccess. + * @param integer $offset the offset to retrieve element. + * @return mixed the element at the offset, null if no element is found at the offset + */ + public function offsetGet($offset) + { + return $this->getElements()->itemAt($offset); + } + + /** + * Sets the element at the specified offset. + * This method is required by the interface ArrayAccess. + * @param integer $offset the offset to set element + * @param mixed $item the element value + */ + public function offsetSet($offset,$item) + { + $this->getElements()->add($offset,$item); + } + + /** + * Unsets the element at the specified offset. + * This method is required by the interface ArrayAccess. + * @param mixed $offset the offset to unset element + */ + public function offsetUnset($offset) + { + $this->getElements()->remove($offset); + } +} diff --git a/framework/web/form/CFormButtonElement.php b/framework/web/form/CFormButtonElement.php new file mode 100644 index 0000000..36a4d70 --- /dev/null +++ b/framework/web/form/CFormButtonElement.php @@ -0,0 +1,139 @@ +<?php +/** + * CFormButtonElement class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2011 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CFormButtonElement represents a form button element. + * + * CFormButtonElement can represent the following types of button based on {@link type} property: + * <ul> + * <li>htmlButton: a normal button generated using {@link CHtml::htmlButton}</li> + * <li>htmlReset a reset button generated using {@link CHtml::htmlButton}</li> + * <li>htmlSubmit: a submit button generated using {@link CHtml::htmlButton}</li> + * <li>submit: a submit button generated using {@link CHtml::submitButton}</li> + * <li>button: a normal button generated using {@link CHtml::button}</li> + * <li>image: an image button generated using {@link CHtml::imageButton}</li> + * <li>reset: a reset button generated using {@link CHtml::resetButton}</li> + * <li>link: a link button generated using {@link CHtml::linkButton}</li> + * </ul> + * The {@link type} property can also be a class name or a path alias to the class. In this case, + * the button is generated using a widget of the specified class. Note, the widget must + * have a property called "name". + * + * Because CFormElement is an ancestor class of CFormButtonElement, a value assigned to a non-existing property will be + * stored in {@link attributes} which will be passed as HTML attribute values to the {@link CHtml} method + * generating the button or initial values of the widget properties. + * + * @property string $on Scenario names separated by commas. Defaults to null. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CFormButtonElement.php 3426 2011-10-25 00:01:09Z alexander.makarow $ + * @package system.web.form + * @since 1.1 + */ +class CFormButtonElement extends CFormElement +{ + /** + * @var array Core button types (alias=>CHtml method name) + */ + public static $coreTypes=array( + 'htmlButton'=>'htmlButton', + 'htmlSubmit'=>'htmlButton', + 'htmlReset'=>'htmlButton', + 'button'=>'button', + 'submit'=>'submitButton', + 'reset'=>'resetButton', + 'image'=>'imageButton', + 'link'=>'linkButton', + ); + + /** + * @var string the type of this button. This can be a class name, a path alias of a class name, + * or a button type alias (submit, button, image, reset, link, htmlButton, htmlSubmit, htmlReset). + */ + public $type; + /** + * @var string name of this button + */ + public $name; + /** + * @var string the label of this button. This property is ignored when a widget is used to generate the button. + */ + public $label; + + private $_on; + + /** + * Returns a value indicating under which scenarios this button is visible. + * If the value is empty, it means the button is visible under all scenarios. + * Otherwise, only when the model is in the scenario whose name can be found in + * this value, will the button be visible. See {@link CModel::scenario} for more + * information about model scenarios. + * @return string scenario names separated by commas. Defaults to null. + */ + public function getOn() + { + return $this->_on; + } + + /** + * @param string $value scenario names separated by commas. + */ + public function setOn($value) + { + $this->_on=preg_split('/[\s,]+/',$value,-1,PREG_SPLIT_NO_EMPTY); + } + + /** + * Returns this button. + * @return string the rendering result + */ + public function render() + { + $attributes=$this->attributes; + if(isset(self::$coreTypes[$this->type])) + { + $method=self::$coreTypes[$this->type]; + if($method==='linkButton') + { + if(!isset($attributes['params'][$this->name])) + $attributes['params'][$this->name]=1; + } + else if($method==='htmlButton') + { + $attributes['type']=$this->type==='htmlSubmit' ? 'submit' : ($this->type==='htmlReset' ? 'reset' : 'button'); + $attributes['name']=$this->name; + } + else + $attributes['name']=$this->name; + if($method==='imageButton') + return CHtml::imageButton(isset($attributes['src']) ? $attributes['src'] : '',$attributes); + else + return CHtml::$method($this->label,$attributes); + } + else + { + $attributes['name']=$this->name; + ob_start(); + $this->getParent()->getOwner()->widget($this->type, $attributes); + return ob_get_clean(); + } + } + + /** + * Evaluates the visibility of this element. + * This method will check the {@link on} property to see if + * the model is in a scenario that should have this string displayed. + * @return boolean whether this element is visible. + */ + protected function evaluateVisible() + { + return empty($this->_on) || in_array($this->getParent()->getModel()->getScenario(),$this->_on); + } +} diff --git a/framework/web/form/CFormElement.php b/framework/web/form/CFormElement.php new file mode 100644 index 0000000..c926e95 --- /dev/null +++ b/framework/web/form/CFormElement.php @@ -0,0 +1,168 @@ +<?php +/** + * CFormElement class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2011 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CFormElement is the base class for presenting all kinds of form element. + * + * CFormElement implements the way to get and set arbitrary attributes. + * + * @property boolean $visible Whether this element is visible and should be rendered. + * @property mixed $parent The direct parent of this element. This could be either a {@link CForm} object or a {@link CBaseController} object + * (a controller or a widget). + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CFormElement.php 3426 2011-10-25 00:01:09Z alexander.makarow $ + * @package system.web.form + * @since 1.1 + */ +abstract class CFormElement extends CComponent +{ + /** + * @var array list of attributes (name=>value) for the HTML element represented by this object. + */ + public $attributes=array(); + + private $_parent; + private $_visible; + + /** + * Renders this element. + * @return string the rendering result + */ + abstract function render(); + + /** + * Constructor. + * @param mixed $config the configuration for this element. + * @param mixed $parent the direct parent of this element. + * @see configure + */ + public function __construct($config,$parent) + { + $this->configure($config); + $this->_parent=$parent; + } + + /** + * Converts the object to a string. + * This is a PHP magic method. + * The default implementation simply calls {@link render} and return + * the rendering result. + * @return string the string representation of this object. + */ + public function __toString() + { + return $this->render(); + } + + /** + * Returns a property value or an attribute value. + * Do not call this method. This is a PHP magic method that we override + * to allow using the following syntax to read a property or attribute: + * <pre> + * $value=$element->propertyName; + * $value=$element->attributeName; + * </pre> + * @param string $name the property or attribute name + * @return mixed the property or attribute value + * @throws CException if the property or attribute is not defined + * @see __set + */ + public function __get($name) + { + $getter='get'.$name; + if(method_exists($this,$getter)) + return $this->$getter(); + else if(isset($this->attributes[$name])) + return $this->attributes[$name]; + else + throw new CException(Yii::t('yii','Property "{class}.{property}" is not defined.', + array('{class}'=>get_class($this), '{property}'=>$name))); + } + + /** + * Sets value of a property or attribute. + * Do not call this method. This is a PHP magic method that we override + * to allow using the following syntax to set a property or attribute. + * <pre> + * $this->propertyName=$value; + * $this->attributeName=$value; + * </pre> + * @param string $name the property or attribute name + * @param mixed $value the property or attribute value + * @see __get + */ + public function __set($name,$value) + { + $setter='set'.$name; + if(method_exists($this,$setter)) + $this->$setter($value); + else + $this->attributes[$name]=$value; + } + + /** + * Configures this object with property initial values. + * @param mixed $config the configuration for this object. This can be an array + * representing the property names and their initial values. + * It can also be a string representing the file name of the PHP script + * that returns a configuration array. + */ + public function configure($config) + { + if(is_string($config)) + $config=require(Yii::getPathOfAlias($config).'.php'); + if(is_array($config)) + { + foreach($config as $name=>$value) + $this->$name=$value; + } + } + + /** + * Returns a value indicating whether this element is visible and should be rendered. + * This method will call {@link evaluateVisible} to determine the visibility of this element. + * @return boolean whether this element is visible and should be rendered. + */ + public function getVisible() + { + if($this->_visible===null) + $this->_visible=$this->evaluateVisible(); + return $this->_visible; + } + + /** + * @param boolean $value whether this element is visible and should be rendered. + */ + public function setVisible($value) + { + $this->_visible=$value; + } + + /** + * @return mixed the direct parent of this element. This could be either a {@link CForm} object or a {@link CBaseController} object + * (a controller or a widget). + */ + public function getParent() + { + return $this->_parent; + } + + /** + * Evaluates the visibility of this element. + * Child classes should override this method to implement the actual algorithm + * for determining the visibility. + * @return boolean whether this element is visible. Defaults to true. + */ + protected function evaluateVisible() + { + return true; + } +} diff --git a/framework/web/form/CFormElementCollection.php b/framework/web/form/CFormElementCollection.php new file mode 100644 index 0000000..2ccc3f1 --- /dev/null +++ b/framework/web/form/CFormElementCollection.php @@ -0,0 +1,112 @@ +<?php +/** + * CFormElementCollection class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2011 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CFormElementCollection implements the collection for storing form elements. + * + * Because CFormElementCollection extends from {@link CMap}, it can be used like an associative array. + * For example, + * <pre> + * $element=$collection['username']; + * $collection['username']=array('type'=>'text', 'maxlength'=>128); + * $collection['password']=new CFormInputElement(array('type'=>'password'),$form); + * $collection[]='some string'; + * </pre> + * + * CFormElementCollection can store three types of value: a configuration array, a {@link CFormElement} + * object, or a string, as shown in the above example. Internally, these values will be converted + * to {@link CFormElement} objects. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CFormElementCollection.php 3054 2011-03-12 21:30:21Z qiang.xue $ + * @package system.web.form + * @since 1.1 + */ +class CFormElementCollection extends CMap +{ + private $_form; + private $_forButtons; + + /** + * Constructor. + * @param CForm $form the form object that owns this collection + * @param boolean $forButtons whether this collection is used to store buttons. + */ + public function __construct($form,$forButtons=false) + { + parent::__construct(); + $this->_form=$form; + $this->_forButtons=$forButtons; + } + + /** + * Adds an item to the collection. + * This method overrides the parent implementation to ensure + * only configuration arrays, strings, or {@link CFormElement} objects + * can be stored in this collection. + * @param mixed $key key + * @param mixed $value value + * @throws CException if the value is invalid. + */ + public function add($key,$value) + { + if(is_array($value)) + { + if(is_string($key)) + $value['name']=$key; + + if($this->_forButtons) + { + $class=$this->_form->buttonElementClass; + $element=new $class($value,$this->_form); + } + else + { + if(!isset($value['type'])) + $value['type']='text'; + if($value['type']==='string') + { + unset($value['type'],$value['name']); + $element=new CFormStringElement($value,$this->_form); + } + else if(!strcasecmp(substr($value['type'],-4),'form')) // a form + { + $class=$value['type']==='form' ? get_class($this->_form) : Yii::import($value['type']); + $element=new $class($value,null,$this->_form); + } + else + { + $class=$this->_form->inputElementClass; + $element=new $class($value,$this->_form); + } + } + } + else if($value instanceof CFormElement) + { + if(property_exists($value,'name') && is_string($key)) + $value->name=$key; + $element=$value; + } + else + $element=new CFormStringElement(array('content'=>$value),$this->_form); + parent::add($key,$element); + $this->_form->addedElement($key,$element,$this->_forButtons); + } + + /** + * Removes the specified element by key. + * @param string $key the name of the element to be removed from the collection + */ + public function remove($key) + { + if(($item=parent::remove($key))!==null) + $this->_form->removedElement($key,$item,$this->_forButtons); + } +} diff --git a/framework/web/form/CFormInputElement.php b/framework/web/form/CFormInputElement.php new file mode 100644 index 0000000..e53804b --- /dev/null +++ b/framework/web/form/CFormInputElement.php @@ -0,0 +1,255 @@ +<?php +/** + * CFormInputElement class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2011 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CFormInputElement represents form input element. + * + * CFormInputElement can represent the following types of form input based on {@link type} property: + * <ul> + * <li>text: a normal text input generated using {@link CHtml::activeTextField}</li> + * <li>hidden: a hidden input generated using {@link CHtml::activeHiddenField}</li> + * <li>password: a password input generated using {@link CHtml::activePasswordField}</li> + * <li>textarea: a text area generated using {@link CHtml::activeTextArea}</li> + * <li>file: a file input generated using {@link CHtml::activeFileField}</li> + * <li>radio: a radio button generated using {@link CHtml::activeRadioButton}</li> + * <li>checkbox: a check box generated using {@link CHtml::activeCheckBox}</li> + * <li>listbox: a list box generated using {@link CHtml::activeListBox}</li> + * <li>dropdownlist: a drop-down list generated using {@link CHtml::activeDropDownList}</li> + * <li>checkboxlist: a list of check boxes generated using {@link CHtml::activeCheckBoxList}</li> + * <li>radiolist: a list of radio buttons generated using {@link CHtml::activeRadioButtonList}</li> + * </ul> + * The {@link type} property can also be a class name or a path alias to the class. In this case, + * the input is generated using a widget of the specified class. Note, the widget must + * have a property called "model" which expects a model object, and a property called "attribute" + * which expects the name of a model attribute. + * + * Because CFormElement is an ancestor class of CFormInputElement, a value assigned to a non-existing property will be + * stored in {@link attributes} which will be passed as HTML attribute values to the {@link CHtml} method + * generating the input or initial values of the widget properties. + * + * @property boolean $required Whether this input is required. + * @property string $label The label for this input. If the label is not manually set, + * this method will call {@link CModel::getAttributeLabel} to determine the label. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CFormInputElement.php 3426 2011-10-25 00:01:09Z alexander.makarow $ + * @package system.web.form + * @since 1.1 + */ +class CFormInputElement extends CFormElement +{ + /** + * @var array Core input types (alias=>CHtml method name) + */ + public static $coreTypes=array( + 'text'=>'activeTextField', + 'hidden'=>'activeHiddenField', + 'password'=>'activePasswordField', + 'textarea'=>'activeTextArea', + 'file'=>'activeFileField', + 'radio'=>'activeRadioButton', + 'checkbox'=>'activeCheckBox', + 'listbox'=>'activeListBox', + 'dropdownlist'=>'activeDropDownList', + 'checkboxlist'=>'activeCheckBoxList', + 'radiolist'=>'activeRadioButtonList', + ); + + /** + * @var string the type of this input. This can be a widget class name, a path alias of a widget class name, + * or a input type alias (text, hidden, password, textarea, file, radio, checkbox, listbox, dropdownlist, checkboxlist, or radiolist). + * If a widget class, it must extend from {@link CInputWidget} or (@link CJuiInputWidget). + */ + public $type; + /** + * @var string name of this input + */ + public $name; + /** + * @var string hint text of this input + */ + public $hint; + /** + * @var array the options for this input when it is a list box, drop-down list, check box list, or radio button list. + * Please see {@link CHtml::listData} for details of generating this property value. + */ + public $items=array(); + /** + * @var array the options used when rendering the error part. This property will be passed + * to the {@link CActiveForm::error} method call as its $htmlOptions parameter. + * @see CActiveForm::error + * @since 1.1.1 + */ + public $errorOptions=array(); + /** + * @var boolean whether to allow AJAX-based validation for this input. Note that in order to use + * AJAX-based validation, {@link CForm::activeForm} must be configured with 'enableAjaxValidation'=>true. + * This property allows turning on or off AJAX-based validation for individual input fields. + * Defaults to true. + * @since 1.1.7 + */ + public $enableAjaxValidation=true; + /** + * @var boolean whether to allow client-side validation for this input. Note that in order to use + * client-side validation, {@link CForm::activeForm} must be configured with 'enableClientValidation'=>true. + * This property allows turning on or off client-side validation for individual input fields. + * Defaults to true. + * @since 1.1.7 + */ + public $enableClientValidation=true; + /** + * @var string the layout used to render label, input, hint and error. They correspond to the placeholders + * "{label}", "{input}", "{hint}" and "{error}". + */ + public $layout="{label}\n{input}\n{hint}\n{error}"; + + private $_label; + private $_required; + + /** + * Gets the value indicating whether this input is required. + * If this property is not set explicitly, it will be determined by calling + * {@link CModel::isAttributeRequired} for the associated model and attribute of this input. + * @return boolean whether this input is required. + */ + public function getRequired() + { + if($this->_required!==null) + return $this->_required; + else + return $this->getParent()->getModel()->isAttributeRequired($this->name); + } + + /** + * @param boolean $value whether this input is required. + */ + public function setRequired($value) + { + $this->_required=$value; + } + + /** + * @return string the label for this input. If the label is not manually set, + * this method will call {@link CModel::getAttributeLabel} to determine the label. + */ + public function getLabel() + { + if($this->_label!==null) + return $this->_label; + else + return $this->getParent()->getModel()->getAttributeLabel($this->name); + } + + /** + * @param string $value the label for this input + */ + public function setLabel($value) + { + $this->_label=$value; + } + + /** + * Renders everything for this input. + * The default implementation simply returns the result of {@link renderLabel}, {@link renderInput}, + * {@link renderHint}. When {@link CForm::showErrorSummary} is false, {@link renderError} is also called + * to show error messages after individual input fields. + * @return string the complete rendering result for this input, including label, input field, hint, and error. + */ + public function render() + { + if($this->type==='hidden') + return $this->renderInput(); + $output=array( + '{label}'=>$this->renderLabel(), + '{input}'=>$this->renderInput(), + '{hint}'=>$this->renderHint(), + '{error}'=>$this->getParent()->showErrorSummary ? '' : $this->renderError(), + ); + return strtr($this->layout,$output); + } + + /** + * Renders the label for this input. + * The default implementation returns the result of {@link CHtml activeLabelEx}. + * @return string the rendering result + */ + public function renderLabel() + { + $options = array( + 'label'=>$this->getLabel(), + 'required'=>$this->getRequired() + ); + + if(!empty($this->attributes['id'])) + { + $options['for'] = $this->attributes['id']; + } + + return CHtml::activeLabel($this->getParent()->getModel(), $this->name, $options); + } + + /** + * Renders the input field. + * The default implementation returns the result of the appropriate CHtml method or the widget. + * @return string the rendering result + */ + public function renderInput() + { + if(isset(self::$coreTypes[$this->type])) + { + $method=self::$coreTypes[$this->type]; + if(strpos($method,'List')!==false) + return CHtml::$method($this->getParent()->getModel(), $this->name, $this->items, $this->attributes); + else + return CHtml::$method($this->getParent()->getModel(), $this->name, $this->attributes); + } + else + { + $attributes=$this->attributes; + $attributes['model']=$this->getParent()->getModel(); + $attributes['attribute']=$this->name; + ob_start(); + $this->getParent()->getOwner()->widget($this->type, $attributes); + return ob_get_clean(); + } + } + + /** + * Renders the error display of this input. + * The default implementation returns the result of {@link CHtml::error} + * @return string the rendering result + */ + public function renderError() + { + $parent=$this->getParent(); + return $parent->getActiveFormWidget()->error($parent->getModel(), $this->name, $this->errorOptions, $this->enableAjaxValidation, $this->enableClientValidation); + } + + /** + * Renders the hint text for this input. + * The default implementation returns the {@link hint} property enclosed in a paragraph HTML tag. + * @return string the rendering result. + */ + public function renderHint() + { + return $this->hint===null ? '' : '<div class="hint">'.$this->hint.'</div>'; + } + + /** + * Evaluates the visibility of this element. + * This method will check if the attribute associated with this input is safe for + * the current model scenario. + * @return boolean whether this element is visible. + */ + protected function evaluateVisible() + { + return $this->getParent()->getModel()->isAttributeSafe($this->name); + } +} diff --git a/framework/web/form/CFormStringElement.php b/framework/web/form/CFormStringElement.php new file mode 100644 index 0000000..7d869ea --- /dev/null +++ b/framework/web/form/CFormStringElement.php @@ -0,0 +1,71 @@ +<?php +/** + * CFormStringElement class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2011 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CFormStringElement represents a string in a form. + * + * @property string $on Scenario names separated by commas. Defaults to null. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CFormStringElement.php 3426 2011-10-25 00:01:09Z alexander.makarow $ + * @package system.web.form + * @since 1.1 + */ +class CFormStringElement extends CFormElement +{ + /** + * @var string the string content + */ + public $content; + + private $_on; + + /** + * Returns a value indicating under which scenarios this string is visible. + * If the value is empty, it means the string is visible under all scenarios. + * Otherwise, only when the model is in the scenario whose name can be found in + * this value, will the string be visible. See {@link CModel::scenario} for more + * information about model scenarios. + * @return string scenario names separated by commas. Defaults to null. + */ + public function getOn() + { + return $this->_on; + } + + /** + * @param string $value scenario names separated by commas. + */ + public function setOn($value) + { + $this->_on=preg_split('/[\s,]+/',$value,-1,PREG_SPLIT_NO_EMPTY); + } + + /** + * Renders this element. + * The default implementation simply returns {@link content}. + * @return string the string content + */ + public function render() + { + return $this->content; + } + + /** + * Evaluates the visibility of this element. + * This method will check the {@link on} property to see if + * the model is in a scenario that should have this string displayed. + * @return boolean whether this element is visible. + */ + protected function evaluateVisible() + { + return empty($this->_on) || in_array($this->getParent()->getModel()->getScenario(),$this->_on); + } +} |
