* @link http://www.yiiframework.com/ * @copyright 2008-2013 Yii Software LLC * @license http://www.yiiframework.com/license/ */ /** * CHtml is a static class that provides a collection of helper methods for creating HTML views. * * @author Qiang Xue * @package system.web.helpers * @since 1.0 */ class CHtml { const ID_PREFIX='yt'; /** * @var string the CSS class for displaying error summaries (see {@link errorSummary}). */ public static $errorSummaryCss='errorSummary'; /** * @var string the CSS class for displaying error messages (see {@link error}). */ public static $errorMessageCss='errorMessage'; /** * @var string the CSS class for highlighting error inputs. Form inputs will be appended * with this CSS class if they have input errors. */ public static $errorCss='error'; /** * @var string the tag name for the error container tag. Defaults to 'div'. * @since 1.1.13 */ public static $errorContainerTag='div'; /** * @var string the CSS class for required labels. Defaults to 'required'. * @see label */ public static $requiredCss='required'; /** * @var string the HTML code to be prepended to the required label. * @see label */ public static $beforeRequiredLabel=''; /** * @var string the HTML code to be appended to the required label. * @see label */ public static $afterRequiredLabel=' *'; /** * @var integer the counter for generating automatic input field names. */ public static $count=0; /** * Sets the default style for attaching jQuery event handlers. * * If set to true (default), event handlers are delegated. * Event handlers are attached to the document body and can process events * from descendant elements that are added to the document at a later time. * * If set to false, event handlers are directly bound. * Event handlers are attached directly to the DOM element, that must already exist * on the page. Elements injected into the page at a later time will not be processed. * * You can override this setting for a particular element by setting the htmlOptions delegate attribute * (see {@link clientChange}). * * For more information about attaching jQuery event handler see {@link http://api.jquery.com/on/} * @since 1.1.9 * @see clientChange */ public static $liveEvents=true; /** * @var boolean whether to close single tags. Defaults to true. Can be set to false for HTML5. * @since 1.1.13 */ public static $closeSingleTags=true; /** * @var boolean whether to render special attributes value. Defaults to true. Can be set to false for HTML5. * @since 1.1.13 */ public static $renderSpecialAttributesValue=true; /** * @var callback the generator used in the {@link CHtml::modelName()} method. * @since 1.1.14 */ private static $_modelNameConverter; /** * Encodes special characters into HTML entities. * The {@link CApplication::charset application charset} will be used for encoding. * @param string $text data to be encoded * @return string the encoded data * @see http://www.php.net/manual/en/function.htmlspecialchars.php */ public static function encode($text) { return htmlspecialchars($text,ENT_QUOTES,Yii::app()->charset); } /** * Decodes special HTML entities back to the corresponding characters. * This is the opposite of {@link encode()}. * @param string $text data to be decoded * @return string the decoded data * @see http://www.php.net/manual/en/function.htmlspecialchars-decode.php * @since 1.1.8 */ public static function decode($text) { return htmlspecialchars_decode($text,ENT_QUOTES); } /** * Encodes special characters in an array of strings into HTML entities. * Both the array keys and values will be encoded if needed. * If a value is an array, this method will also encode it recursively. * The {@link CApplication::charset application charset} will be used for encoding. * @param array $data data to be encoded * @return array the encoded data * @see http://www.php.net/manual/en/function.htmlspecialchars.php */ public static function encodeArray($data) { $d=array(); foreach($data as $key=>$value) { if(is_string($key)) $key=htmlspecialchars($key,ENT_QUOTES,Yii::app()->charset); if(is_string($value)) $value=htmlspecialchars($value,ENT_QUOTES,Yii::app()->charset); elseif(is_array($value)) $value=self::encodeArray($value); $d[$key]=$value; } return $d; } /** * Generates an HTML element. * @param string $tag the tag name * @param array $htmlOptions the element attributes. The values will be HTML-encoded using {@link encode()}. * If an 'encode' attribute is given and its value is false, * the rest of the attribute values will NOT be HTML-encoded. * Since version 1.1.5, attributes whose value is null will not be rendered. * @param mixed $content the content to be enclosed between open and close element tags. It will not be HTML-encoded. * If false, it means there is no body content. * @param boolean $closeTag whether to generate the close tag. * @return string the generated HTML element tag */ public static function tag($tag,$htmlOptions=array(),$content=false,$closeTag=true) { $html='<' . $tag . self::renderAttributes($htmlOptions); if($content===false) return $closeTag && self::$closeSingleTags ? $html.' />' : $html.'>'; else return $closeTag ? $html.'>'.$content.'' : $html.'>'.$content; } /** * Generates an open HTML element. * @param string $tag the tag name * @param array $htmlOptions the element attributes. The values will be HTML-encoded using {@link encode()}. * If an 'encode' attribute is given and its value is false, * the rest of the attribute values will NOT be HTML-encoded. * Since version 1.1.5, attributes whose value is null will not be rendered. * @return string the generated HTML element tag */ public static function openTag($tag,$htmlOptions=array()) { return '<' . $tag . self::renderAttributes($htmlOptions) . '>'; } /** * Generates a close HTML element. * @param string $tag the tag name * @return string the generated HTML element tag */ public static function closeTag($tag) { return ''; } /** * Encloses the given string within a CDATA tag. * @param string $text the string to be enclosed * @return string the CDATA tag with the enclosed content. */ public static function cdata($text) { return ''; } /** * Generates a meta tag that can be inserted in the head section of HTML page. * @param string $content content attribute of the meta tag * @param string $name name attribute of the meta tag. If null, the attribute will not be generated * @param string $httpEquiv http-equiv attribute of the meta tag. If null, the attribute will not be generated * @param array $options other options in name-value pairs (e.g. 'scheme', 'lang') * @return string the generated meta tag */ public static function metaTag($content,$name=null,$httpEquiv=null,$options=array()) { if($name!==null) $options['name']=$name; if($httpEquiv!==null) $options['http-equiv']=$httpEquiv; $options['content']=$content; return self::tag('meta',$options); } /** * Generates a link tag that can be inserted in the head section of HTML page. * Do not confuse this method with {@link link()}. The latter generates a hyperlink. * @param string $relation rel attribute of the link tag. If null, the attribute will not be generated. * @param string $type type attribute of the link tag. If null, the attribute will not be generated. * @param string $href href attribute of the link tag. If null, the attribute will not be generated. * @param string $media media attribute of the link tag. If null, the attribute will not be generated. * @param array $options other options in name-value pairs * @return string the generated link tag */ public static function linkTag($relation=null,$type=null,$href=null,$media=null,$options=array()) { if($relation!==null) $options['rel']=$relation; if($type!==null) $options['type']=$type; if($href!==null) $options['href']=$href; if($media!==null) $options['media']=$media; return self::tag('link',$options); } /** * Encloses the given CSS content with a CSS tag. * @param string $text the CSS content * @param string $media the media that this CSS should apply to. * @return string the CSS properly enclosed */ public static function css($text,$media='') { if($media!=='') $media=' media="'.$media.'"'; return ""; } /** * Registers a 'refresh' meta tag. * This method can be invoked anywhere in a view. It will register a 'refresh' * meta tag with {@link CClientScript} so that the page can be refreshed in * the specified seconds. * @param integer $seconds the number of seconds to wait before refreshing the page * @param string $url the URL to which the page should be redirected to. If empty, it means the current page. * @since 1.1.1 */ public static function refresh($seconds,$url='') { $content="$seconds"; if($url!=='') $content.=';url='.self::normalizeUrl($url); Yii::app()->clientScript->registerMetaTag($content,null,'refresh'); } /** * Links to the specified CSS file. * @param string $url the CSS URL * @param string $media the media that this CSS should apply to. * @return string the CSS link. */ public static function cssFile($url,$media='') { return CHtml::linkTag('stylesheet','text/css',$url,$media!=='' ? $media : null); } /** * Encloses the given JavaScript within a script tag. * @param string $text the JavaScript to be enclosed * @param array $htmlOptions additional HTML attributes (see {@link tag}) * @return string the enclosed JavaScript */ public static function script($text,array $htmlOptions=array()) { $defaultHtmlOptions=array( 'type'=>'text/javascript', ); $htmlOptions=array_merge($defaultHtmlOptions,$htmlOptions); return self::tag('script',$htmlOptions,"\n/**/\n"); } /** * Includes a JavaScript file. * @param string $url URL for the JavaScript file * @param array $htmlOptions additional HTML attributes (see {@link tag}) * @return string the JavaScript file tag */ public static function scriptFile($url,array $htmlOptions=array()) { $defaultHtmlOptions=array( 'type'=>'text/javascript', 'src'=>$url ); $htmlOptions=array_merge($defaultHtmlOptions,$htmlOptions); return self::tag('script',$htmlOptions,''); } /** * Generates an opening form tag. * This is a shortcut to {@link beginForm}. * @param mixed $action the form action URL (see {@link normalizeUrl} for details about this parameter.) * @param string $method form method (e.g. post, get) * @param array $htmlOptions additional HTML attributes (see {@link tag}). * @return string the generated form tag. */ public static function form($action='',$method='post',$htmlOptions=array()) { return self::beginForm($action,$method,$htmlOptions); } /** * Generates an opening form tag. * Note, only the open tag is generated. A close tag should be placed manually * at the end of the form. * @param mixed $action the form action URL (see {@link normalizeUrl} for details about this parameter.) * @param string $method form method (e.g. post, get) * @param array $htmlOptions additional HTML attributes (see {@link tag}). * @return string the generated form tag. * @see endForm */ public static function beginForm($action='',$method='post',$htmlOptions=array()) { $htmlOptions['action']=$url=self::normalizeUrl($action); $htmlOptions['method']=$method; $form=self::tag('form',$htmlOptions,false,false); $hiddens=array(); if(!strcasecmp($method,'get') && ($pos=strpos($url,'?'))!==false) { foreach(explode('&',substr($url,$pos+1)) as $pair) { if(($pos=strpos($pair,'='))!==false) $hiddens[]=self::hiddenField(urldecode(substr($pair,0,$pos)),urldecode(substr($pair,$pos+1)),array('id'=>false)); else $hiddens[]=self::hiddenField(urldecode($pair),'',array('id'=>false)); } } $request=Yii::app()->request; if($request->enableCsrfValidation && !strcasecmp($method,'post')) $hiddens[]=self::hiddenField($request->csrfTokenName,$request->getCsrfToken(),array('id'=>false)); if($hiddens!==array()) $form.="\n".self::tag('div',array('style'=>'display:none'),implode("\n",$hiddens)); return $form; } /** * Generates a closing form tag. * @return string the generated tag * @see beginForm */ public static function endForm() { return ''; } /** * Generates a stateful form tag. * A stateful form tag is similar to {@link form} except that it renders an additional * hidden field for storing persistent page states. You should use this method to generate * a form tag if you want to access persistent page states when the form is submitted. * @param mixed $action the form action URL (see {@link normalizeUrl} for details about this parameter.) * @param string $method form method (e.g. post, get) * @param array $htmlOptions additional HTML attributes (see {@link tag}). * @return string the generated form tag. */ public static function statefulForm($action='',$method='post',$htmlOptions=array()) { return self::form($action,$method,$htmlOptions)."\n". self::tag('div',array('style'=>'display:none'),self::pageStateField('')); } /** * Generates a hidden field for storing persistent page states. * This method is internally used by {@link statefulForm}. * @param string $value the persistent page states in serialized format * @return string the generated hidden field */ public static function pageStateField($value) { return ''; } /** * Generates a hyperlink tag. * @param string $text link body. It will NOT be HTML-encoded. Therefore you can pass in HTML code such as an image tag. * @param mixed $url a URL or an action route that can be used to create a URL. * See {@link normalizeUrl} for more details about how to specify this parameter. * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) * @return string the generated hyperlink * @see normalizeUrl * @see clientChange */ public static function link($text,$url='#',$htmlOptions=array()) { if($url!=='') $htmlOptions['href']=self::normalizeUrl($url); self::clientChange('click',$htmlOptions); return self::tag('a',$htmlOptions,$text); } /** * Generates a mailto link. * @param string $text link body. It will NOT be HTML-encoded. Therefore you can pass in HTML code such as an image tag. * @param string $email email address. If this is empty, the first parameter (link body) will be treated as the email address. * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) * @return string the generated mailto link * @see clientChange */ public static function mailto($text,$email='',$htmlOptions=array()) { if($email==='') $email=$text; return self::link($text,'mailto:'.$email,$htmlOptions); } /** * Generates an image tag. * @param string $src the image URL * @param string $alt the alternative text display * @param array $htmlOptions additional HTML attributes (see {@link tag}). * @return string the generated image tag */ public static function image($src,$alt='',$htmlOptions=array()) { $htmlOptions['src']=$src; $htmlOptions['alt']=$alt; return self::tag('img',$htmlOptions); } /** * Generates a button. * @param string $label the button label * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) * @return string the generated button tag * @see clientChange */ public static function button($label='button',$htmlOptions=array()) { if(!isset($htmlOptions['name'])) { if(!array_key_exists('name',$htmlOptions)) $htmlOptions['name']=self::ID_PREFIX.self::$count++; } if(!isset($htmlOptions['type'])) $htmlOptions['type']='button'; if(!isset($htmlOptions['value']) && $htmlOptions['type']!='image') $htmlOptions['value']=$label; self::clientChange('click',$htmlOptions); return self::tag('input',$htmlOptions); } /** * Generates a button using HTML button tag. * This method is similar to {@link button} except that it generates a 'button' * tag instead of 'input' tag. * @param string $label the button label. Note that this value will be directly inserted in the button element * without being HTML-encoded. * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) * @return string the generated button tag * @see clientChange */ public static function htmlButton($label='button',$htmlOptions=array()) { if(!isset($htmlOptions['name'])) $htmlOptions['name']=self::ID_PREFIX.self::$count++; if(!isset($htmlOptions['type'])) $htmlOptions['type']='button'; self::clientChange('click',$htmlOptions); return self::tag('button',$htmlOptions,$label); } /** * Generates a submit button. * @param string $label the button label * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) * @return string the generated button tag * @see clientChange */ public static function submitButton($label='submit',$htmlOptions=array()) { $htmlOptions['type']='submit'; return self::button($label,$htmlOptions); } /** * Generates a reset button. * @param string $label the button label * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) * @return string the generated button tag * @see clientChange */ public static function resetButton($label='reset',$htmlOptions=array()) { $htmlOptions['type']='reset'; return self::button($label,$htmlOptions); } /** * Generates an image submit button. * @param string $src the image URL * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) * @return string the generated button tag * @see clientChange */ public static function imageButton($src,$htmlOptions=array()) { $htmlOptions['src']=$src; $htmlOptions['type']='image'; return self::button('submit',$htmlOptions); } /** * Generates a link submit button. * @param string $label the button label * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) * @return string the generated button tag * @see clientChange */ public static function linkButton($label='submit',$htmlOptions=array()) { if(!isset($htmlOptions['submit'])) $htmlOptions['submit']=isset($htmlOptions['href']) ? $htmlOptions['href'] : ''; return self::link($label,'#',$htmlOptions); } /** * Generates a label tag. * @param string $label label text. Note, you should HTML-encode the text if needed. * @param string $for the ID of the HTML element that this label is associated with. * If this is false, the 'for' attribute for the label tag will not be rendered. * @param array $htmlOptions additional HTML attributes. * The following HTML option is recognized: * * @return string the generated label tag */ public static function label($label,$for,$htmlOptions=array()) { if($for===false) unset($htmlOptions['for']); else $htmlOptions['for']=$for; if(isset($htmlOptions['required'])) { if($htmlOptions['required']) { if(isset($htmlOptions['class'])) $htmlOptions['class'].=' '.self::$requiredCss; else $htmlOptions['class']=self::$requiredCss; $label=self::$beforeRequiredLabel.$label.self::$afterRequiredLabel; } unset($htmlOptions['required']); } return self::tag('label',$htmlOptions,$label); } /** * Generates a text field input. * @param string $name the input name * @param string $value the input value * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) * @return string the generated input field * @see clientChange * @see inputField */ public static function textField($name,$value='',$htmlOptions=array()) { self::clientChange('change',$htmlOptions); return self::inputField('text',$name,$value,$htmlOptions); } /** * Generates a number field input. * @param string $name the input name * @param string $value the input value * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) * @return string the generated input field * @see clientChange * @see inputField * @since 1.1.14 */ public static function numberField($name,$value='',$htmlOptions=array()) { self::clientChange('change',$htmlOptions); return self::inputField('number',$name,$value,$htmlOptions); } /** * Generates a range field input. * @param string $name the input name * @param string $value the input value * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) * @return string the generated input field * @see clientChange * @see inputField * @since 1.1.14 */ public static function rangeField($name,$value='',$htmlOptions=array()) { self::clientChange('change',$htmlOptions); return self::inputField('range',$name,$value,$htmlOptions); } /** * Generates a date field input. * @param string $name the input name * @param string $value the input value * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) * @return string the generated input field * @see clientChange * @see inputField * @since 1.1.14 */ public static function dateField($name,$value='',$htmlOptions=array()) { self::clientChange('change',$htmlOptions); return self::inputField('date',$name,$value,$htmlOptions); } /** * Generates a time field input. * @param string $name the input name * @param string $value the input value * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) * @return string the generated input field * @see clientChange * @see inputField * @since 1.1.14 */ public static function timeField($name,$value='',$htmlOptions=array()) { self::clientChange('change',$htmlOptions); return self::inputField('time',$name,$value,$htmlOptions); } /** * Generates an email field input. * @param string $name the input name * @param string $value the input value * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) * @return string the generated input field * @see clientChange * @see inputField * @since 1.1.14 */ public static function emailField($name,$value='',$htmlOptions=array()) { self::clientChange('change',$htmlOptions); return self::inputField('email',$name,$value,$htmlOptions); } /** * Generates a telephone field input. * @param string $name the input name * @param string $value the input value * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) * @return string the generated input field * @see clientChange * @see inputField * @since 1.1.14 */ public static function telField($name,$value='',$htmlOptions=array()) { self::clientChange('change',$htmlOptions); return self::inputField('tel',$name,$value,$htmlOptions); } /** * Generates a URL field input. * @param string $name the input name * @param string $value the input value * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) * @return string the generated input field * @see clientChange * @see inputField * @since 1.1.14 */ public static function urlField($name,$value='',$htmlOptions=array()) { self::clientChange('change',$htmlOptions); return self::inputField('url',$name,$value,$htmlOptions); } /** * Generates a hidden input. * @param string $name the input name * @param string $value the input value * @param array $htmlOptions additional HTML attributes (see {@link tag}). * @return string the generated input field * @see inputField */ public static function hiddenField($name,$value='',$htmlOptions=array()) { return self::inputField('hidden',$name,$value,$htmlOptions); } /** * Generates a password field input. * @param string $name the input name * @param string $value the input value * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) * @return string the generated input field * @see clientChange * @see inputField */ public static function passwordField($name,$value='',$htmlOptions=array()) { self::clientChange('change',$htmlOptions); return self::inputField('password',$name,$value,$htmlOptions); } /** * Generates a file input. * Note, you have to set the enclosing form's 'enctype' attribute to be 'multipart/form-data'. * After the form is submitted, the uploaded file information can be obtained via $_FILES[$name] (see * PHP documentation). * @param string $name the input name * @param string $value the input value * @param array $htmlOptions additional HTML attributes (see {@link tag}). * @return string the generated input field * @see inputField */ public static function fileField($name,$value='',$htmlOptions=array()) { return self::inputField('file',$name,$value,$htmlOptions); } /** * Generates a text area input. * @param string $name the input name * @param string $value the input value * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) * @return string the generated text area * @see clientChange * @see inputField */ public static function textArea($name,$value='',$htmlOptions=array()) { $htmlOptions['name']=$name; if(!isset($htmlOptions['id'])) $htmlOptions['id']=self::getIdByName($name); elseif($htmlOptions['id']===false) unset($htmlOptions['id']); self::clientChange('change',$htmlOptions); return self::tag('textarea',$htmlOptions,isset($htmlOptions['encode']) && !$htmlOptions['encode'] ? $value : self::encode($value)); } /** * Generates a radio button. * @param string $name the input name * @param boolean $checked whether the radio button is checked * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) * Since version 1.1.2, a special option named 'uncheckValue' is available that can be used to specify * the value returned when the radio button is not checked. When set, a hidden field is rendered so that * when the radio button is not checked, we can still obtain the posted uncheck value. * If 'uncheckValue' is not set or set to NULL, the hidden field will not be rendered. * @return string the generated radio button * @see clientChange * @see inputField */ public static function radioButton($name,$checked=false,$htmlOptions=array()) { if($checked) $htmlOptions['checked']='checked'; else unset($htmlOptions['checked']); $value=isset($htmlOptions['value']) ? $htmlOptions['value'] : 1; self::clientChange('click',$htmlOptions); if(array_key_exists('uncheckValue',$htmlOptions)) { $uncheck=$htmlOptions['uncheckValue']; unset($htmlOptions['uncheckValue']); } else $uncheck=null; if($uncheck!==null) { // add a hidden field so that if the radio button is not selected, it still submits a value if(isset($htmlOptions['id']) && $htmlOptions['id']!==false) $uncheckOptions=array('id'=>self::ID_PREFIX.$htmlOptions['id']); else $uncheckOptions=array('id'=>false); $hidden=self::hiddenField($name,$uncheck,$uncheckOptions); } else $hidden=''; // add a hidden field so that if the radio button is not selected, it still submits a value return $hidden . self::inputField('radio',$name,$value,$htmlOptions); } /** * Generates a check box. * @param string $name the input name * @param boolean $checked whether the check box is checked * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) * Since version 1.1.2, a special option named 'uncheckValue' is available that can be used to specify * the value returned when the checkbox is not checked. When set, a hidden field is rendered so that * when the checkbox is not checked, we can still obtain the posted uncheck value. * If 'uncheckValue' is not set or set to NULL, the hidden field will not be rendered. * @return string the generated check box * @see clientChange * @see inputField */ public static function checkBox($name,$checked=false,$htmlOptions=array()) { if($checked) $htmlOptions['checked']='checked'; else unset($htmlOptions['checked']); $value=isset($htmlOptions['value']) ? $htmlOptions['value'] : 1; self::clientChange('click',$htmlOptions); if(array_key_exists('uncheckValue',$htmlOptions)) { $uncheck=$htmlOptions['uncheckValue']; unset($htmlOptions['uncheckValue']); } else $uncheck=null; if($uncheck!==null) { // add a hidden field so that if the check box is not checked, it still submits a value if(isset($htmlOptions['id']) && $htmlOptions['id']!==false) $uncheckOptions=array('id'=>self::ID_PREFIX.$htmlOptions['id']); else $uncheckOptions=array('id'=>false); $hidden=self::hiddenField($name,$uncheck,$uncheckOptions); } else $hidden=''; // add a hidden field so that if the check box is not checked, it still submits a value return $hidden . self::inputField('checkbox',$name,$value,$htmlOptions); } /** * Generates a drop down list. * @param string $name the input name * @param string $select the selected value * @param array $data data for generating the list options (value=>display). * You may use {@link listData} to generate this data. * Please refer to {@link listOptions} on how this data is used to generate the list options. * Note, the values and labels will be automatically HTML-encoded by this method. * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special * attributes are recognized. See {@link clientChange} and {@link tag} for more details. * In addition, the following options are also supported specifically for dropdown list: * * Since 1.1.13, a special option named 'unselectValue' is available. It can be used to set the value * that will be returned when no option is selected in multiple mode. When set, a hidden field is * rendered so that if no option is selected in multiple mode, we can still obtain the posted * unselect value. If 'unselectValue' is not set or set to NULL, the hidden field will not be rendered. * @return string the generated drop down list * @see clientChange * @see inputField * @see listData */ public static function dropDownList($name,$select,$data,$htmlOptions=array()) { $htmlOptions['name']=$name; if(!isset($htmlOptions['id'])) $htmlOptions['id']=self::getIdByName($name); elseif($htmlOptions['id']===false) unset($htmlOptions['id']); self::clientChange('change',$htmlOptions); $options="\n".self::listOptions($select,$data,$htmlOptions); $hidden=''; if(!empty($htmlOptions['multiple'])) { if(substr($htmlOptions['name'],-2)!=='[]') $htmlOptions['name'].='[]'; if(isset($htmlOptions['unselectValue'])) { $hiddenOptions=isset($htmlOptions['id']) ? array('id'=>self::ID_PREFIX.$htmlOptions['id']) : array('id'=>false); $hidden=self::hiddenField(substr($htmlOptions['name'],0,-2),$htmlOptions['unselectValue'],$hiddenOptions); unset($htmlOptions['unselectValue']); } } // add a hidden field so that if the option is not selected, it still submits a value return $hidden . self::tag('select',$htmlOptions,$options); } /** * Generates a list box. * @param string $name the input name * @param mixed $select the selected value(s). This can be either a string for single selection or an array for multiple selections. * @param array $data data for generating the list options (value=>display) * You may use {@link listData} to generate this data. * Please refer to {@link listOptions} on how this data is used to generate the list options. * Note, the values and labels will be automatically HTML-encoded by this method. * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special * attributes are also recognized. See {@link clientChange} and {@link tag} for more details. * In addition, the following options are also supported specifically for list box: * * @return string the generated list box * @see clientChange * @see inputField * @see listData */ public static function listBox($name,$select,$data,$htmlOptions=array()) { if(!isset($htmlOptions['size'])) $htmlOptions['size']=4; if(!empty($htmlOptions['multiple'])) { if(substr($name,-2)!=='[]') $name.='[]'; } return self::dropDownList($name,$select,$data,$htmlOptions); } /** * Generates a check box list. * A check box list allows multiple selection, like {@link listBox}. * As a result, the corresponding POST value is an array. * @param string $name name of the check box list. You can use this name to retrieve * the selected value(s) once the form is submitted. * @param mixed $select selection of the check boxes. This can be either a string * for single selection or an array for multiple selections. * @param array $data value-label pairs used to generate the check box list. * Note, the values will be automatically HTML-encoded, while the labels will not. * @param array $htmlOptions additional HTML options. The options will be applied to * each checkbox input. The following special options are recognized: * * @return string the generated check box list */ public static function checkBoxList($name,$select,$data,$htmlOptions=array()) { $template=isset($htmlOptions['template'])?$htmlOptions['template']:'{input} {label}'; $separator=isset($htmlOptions['separator'])?$htmlOptions['separator']:"
\n"; $container=isset($htmlOptions['container'])?$htmlOptions['container']:'span'; unset($htmlOptions['template'],$htmlOptions['separator'],$htmlOptions['container']); if(substr($name,-2)!=='[]') $name.='[]'; if(isset($htmlOptions['checkAll'])) { $checkAllLabel=$htmlOptions['checkAll']; $checkAllLast=isset($htmlOptions['checkAllLast']) && $htmlOptions['checkAllLast']; } unset($htmlOptions['checkAll'],$htmlOptions['checkAllLast']); $labelOptions=isset($htmlOptions['labelOptions'])?$htmlOptions['labelOptions']:array(); unset($htmlOptions['labelOptions']); $items=array(); $baseID=isset($htmlOptions['baseID']) ? $htmlOptions['baseID'] : self::getIdByName($name); unset($htmlOptions['baseID']); $id=0; $checkAll=true; foreach($data as $value=>$labelTitle) { $checked=!is_array($select) && !strcmp($value,$select) || is_array($select) && in_array($value,$select); $checkAll=$checkAll && $checked; $htmlOptions['value']=$value; $htmlOptions['id']=$baseID.'_'.$id++; $option=self::checkBox($name,$checked,$htmlOptions); $beginLabel=self::openTag('label',$labelOptions); $label=self::label($labelTitle,$htmlOptions['id'],$labelOptions); $endLabel=self::closeTag('label'); $items[]=strtr($template,array( '{input}'=>$option, '{beginLabel}'=>$beginLabel, '{label}'=>$label, '{labelTitle}'=>$labelTitle, '{endLabel}'=>$endLabel, )); } if(isset($checkAllLabel)) { $htmlOptions['value']=1; $htmlOptions['id']=$id=$baseID.'_all'; $option=self::checkBox($id,$checkAll,$htmlOptions); $beginLabel=self::openTag('label',$labelOptions); $label=self::label($checkAllLabel,$id,$labelOptions); $endLabel=self::closeTag('label'); $item=strtr($template,array( '{input}'=>$option, '{beginLabel}'=>$beginLabel, '{label}'=>$label, '{labelTitle}'=>$checkAllLabel, '{endLabel}'=>$endLabel, )); if($checkAllLast) $items[]=$item; else array_unshift($items,$item); $name=strtr($name,array('['=>'\\[',']'=>'\\]')); $js=<<getClientScript(); $cs->registerCoreScript('jquery'); $cs->registerScript($id,$js); } if(empty($container)) return implode($separator,$items); else return self::tag($container,array('id'=>$baseID),implode($separator,$items)); } /** * Generates a radio button list. * A radio button list is like a {@link checkBoxList check box list}, except that * it only allows single selection. * @param string $name name of the radio button list. You can use this name to retrieve * the selected value(s) once the form is submitted. * @param string $select selection of the radio buttons. * @param array $data value-label pairs used to generate the radio button list. * Note, the values will be automatically HTML-encoded, while the labels will not. * @param array $htmlOptions additional HTML options. The options will be applied to * each radio button input. The following special options are recognized: * * @return string the generated radio button list */ public static function radioButtonList($name,$select,$data,$htmlOptions=array()) { $template=isset($htmlOptions['template'])?$htmlOptions['template']:'{input} {label}'; $separator=isset($htmlOptions['separator'])?$htmlOptions['separator']:"
\n"; $container=isset($htmlOptions['container'])?$htmlOptions['container']:'span'; unset($htmlOptions['template'],$htmlOptions['separator'],$htmlOptions['container']); $labelOptions=isset($htmlOptions['labelOptions'])?$htmlOptions['labelOptions']:array(); unset($htmlOptions['labelOptions']); if(isset($htmlOptions['empty'])) { if(!is_array($htmlOptions['empty'])) $htmlOptions['empty']=array(''=>$htmlOptions['empty']); $data=array_merge($htmlOptions['empty'],$data); unset($htmlOptions['empty']); } $items=array(); $baseID=isset($htmlOptions['baseID']) ? $htmlOptions['baseID'] : self::getIdByName($name); unset($htmlOptions['baseID']); $id=0; foreach($data as $value=>$labelTitle) { $checked=!strcmp($value,$select); $htmlOptions['value']=$value; $htmlOptions['id']=$baseID.'_'.$id++; $option=self::radioButton($name,$checked,$htmlOptions); $beginLabel=self::openTag('label',$labelOptions); $label=self::label($labelTitle,$htmlOptions['id'],$labelOptions); $endLabel=self::closeTag('label'); $items[]=strtr($template,array( '{input}'=>$option, '{beginLabel}'=>$beginLabel, '{label}'=>$label, '{labelTitle}'=>$labelTitle, '{endLabel}'=>$endLabel, )); } if(empty($container)) return implode($separator,$items); else return self::tag($container,array('id'=>$baseID),implode($separator,$items)); } /** * Generates a link that can initiate AJAX requests. * @param string $text the link body (it will NOT be HTML-encoded.) * @param mixed $url the URL for the AJAX request. If empty, it is assumed to be the current URL. See {@link normalizeUrl} for more details. * @param array $ajaxOptions AJAX options (see {@link ajax}) * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) * @return string the generated link * @see normalizeUrl * @see ajax */ public static function ajaxLink($text,$url,$ajaxOptions=array(),$htmlOptions=array()) { if(!isset($htmlOptions['href'])) $htmlOptions['href']='#'; $ajaxOptions['url']=$url; $htmlOptions['ajax']=$ajaxOptions; self::clientChange('click',$htmlOptions); return self::tag('a',$htmlOptions,$text); } /** * Generates a push button that can initiate AJAX requests. * @param string $label the button label * @param mixed $url the URL for the AJAX request. If empty, it is assumed to be the current URL. See {@link normalizeUrl} for more details. * @param array $ajaxOptions AJAX options (see {@link ajax}) * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) * @return string the generated button */ public static function ajaxButton($label,$url,$ajaxOptions=array(),$htmlOptions=array()) { $ajaxOptions['url']=$url; $htmlOptions['ajax']=$ajaxOptions; return self::button($label,$htmlOptions); } /** * Generates a push button that can submit the current form in POST method. * @param string $label the button label * @param mixed $url the URL for the AJAX request. If empty, it is assumed to be the current URL. See {@link normalizeUrl} for more details. * @param array $ajaxOptions AJAX options (see {@link ajax}) * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) * @return string the generated button */ public static function ajaxSubmitButton($label,$url,$ajaxOptions=array(),$htmlOptions=array()) { $ajaxOptions['type']='POST'; $htmlOptions['type']='submit'; return self::ajaxButton($label,$url,$ajaxOptions,$htmlOptions); } /** * Generates the JavaScript that initiates an AJAX request. * @param array $options AJAX options. The valid options are used in the form of jQuery.ajax([settings]) * as specified in the jQuery AJAX documentation. * The following special options are added for convenience: * * Note, if you specify the 'success' option, the above options will be ignored. * @return string the generated JavaScript * @see http://api.jquery.com/jQuery.ajax/#jQuery-ajax-settings */ public static function ajax($options) { Yii::app()->getClientScript()->registerCoreScript('jquery'); if(!isset($options['url'])) $options['url']=new CJavaScriptExpression('location.href'); else $options['url']=self::normalizeUrl($options['url']); if(!isset($options['cache'])) $options['cache']=false; if(!isset($options['data']) && isset($options['type'])) $options['data']=new CJavaScriptExpression('jQuery(this).parents("form").serialize()'); foreach(array('beforeSend','complete','error','success') as $name) { if(isset($options[$name]) && !($options[$name] instanceof CJavaScriptExpression)) $options[$name]=new CJavaScriptExpression($options[$name]); } if(isset($options['update'])) { if(!isset($options['success'])) $options['success']=new CJavaScriptExpression('function(html){jQuery("'.$options['update'].'").html(html)}'); unset($options['update']); } if(isset($options['replace'])) { if(!isset($options['success'])) $options['success']=new CJavaScriptExpression('function(html){jQuery("'.$options['replace'].'").replaceWith(html)}'); unset($options['replace']); } return 'jQuery.ajax('.CJavaScript::encode($options).');'; } /** * Generates the URL for the published assets. * @param string $path the path of the asset to be published * @param boolean $hashByName whether the published directory should be named as the hashed basename. * If false, the name will be the hashed dirname of the path being published. * Defaults to false. Set true if the path being published is shared among * different extensions. * @return string the asset URL */ public static function asset($path,$hashByName=false) { return Yii::app()->getAssetManager()->publish($path,$hashByName); } /** * Normalizes the input parameter to be a valid URL. * * If the input parameter is an empty string, the currently requested URL will be returned. * * If the input parameter is a non-empty string, it is treated as a valid URL and will * be returned without any change. * * If the input parameter is an array, it is treated as a controller route and a list of * GET parameters, and the {@link CController::createUrl} method will be invoked to * create a URL. In this case, the first array element refers to the controller route, * and the rest key-value pairs refer to the additional GET parameters for the URL. * For example, array('post/list', 'page'=>3) may be used to generate the URL * /index.php?r=post/list&page=3. * * @param mixed $url the parameter to be used to generate a valid URL * @return string the normalized URL */ public static function normalizeUrl($url) { if(is_array($url)) { if(isset($url[0])) { if(($c=Yii::app()->getController())!==null) $url=$c->createUrl($url[0],array_splice($url,1)); else $url=Yii::app()->createUrl($url[0],array_splice($url,1)); } else $url=''; } return $url==='' ? Yii::app()->getRequest()->getUrl() : $url; } /** * Generates an input HTML tag. * This method generates an input HTML tag based on the given input name and value. * @param string $type the input type (e.g. 'text', 'radio') * @param string $name the input name * @param string $value the input value * @param array $htmlOptions additional HTML attributes for the HTML tag (see {@link tag}). * @return string the generated input tag */ protected static function inputField($type,$name,$value,$htmlOptions) { $htmlOptions['type']=$type; $htmlOptions['value']=$value; $htmlOptions['name']=$name; if(!isset($htmlOptions['id'])) $htmlOptions['id']=self::getIdByName($name); elseif($htmlOptions['id']===false) unset($htmlOptions['id']); return self::tag('input',$htmlOptions); } /** * Generates a label tag for a model attribute. * The label text is the attribute label and the label is associated with * the input for the attribute (see {@link CModel::getAttributeLabel}. * If the attribute has input error, the label's CSS class will be appended with {@link errorCss}. * @param CModel $model the data model * @param string $attribute the attribute * @param array $htmlOptions additional HTML attributes. The following special options are recognized: * * @return string the generated label tag */ public static function activeLabel($model,$attribute,$htmlOptions=array()) { $inputName=self::resolveName($model,$attribute); if(isset($htmlOptions['for'])) { $for=$htmlOptions['for']; unset($htmlOptions['for']); } else $for=self::getIdByName($inputName); if(isset($htmlOptions['label'])) { if(($label=$htmlOptions['label'])===false) return ''; unset($htmlOptions['label']); } else $label=$model->getAttributeLabel($attribute); if($model->hasErrors($attribute)) self::addErrorCss($htmlOptions); return self::label($label,$for,$htmlOptions); } /** * Generates a label tag for a model attribute. * This is an enhanced version of {@link activeLabel}. It will render additional * CSS class and mark when the attribute is required. * In particular, it calls {@link CModel::isAttributeRequired} to determine * if the attribute is required. * If so, it will add a CSS class {@link CHtml::requiredCss} to the label, * and decorate the label with {@link CHtml::beforeRequiredLabel} and * {@link CHtml::afterRequiredLabel}. * @param CModel $model the data model * @param string $attribute the attribute * @param array $htmlOptions additional HTML attributes. * @return string the generated label tag */ public static function activeLabelEx($model,$attribute,$htmlOptions=array()) { $realAttribute=$attribute; self::resolveName($model,$attribute); // strip off square brackets if any $htmlOptions['required']=$model->isAttributeRequired($attribute); return self::activeLabel($model,$realAttribute,$htmlOptions); } /** * Generates a text field input for a model attribute. * If the attribute has input error, the input field's CSS class will * be appended with {@link errorCss}. * @param CModel $model the data model * @param string $attribute the attribute * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) * @return string the generated input field * @see clientChange * @see activeInputField */ public static function activeTextField($model,$attribute,$htmlOptions=array()) { self::resolveNameID($model,$attribute,$htmlOptions); self::clientChange('change',$htmlOptions); return self::activeInputField('text',$model,$attribute,$htmlOptions); } /** * Generates a search field input for a model attribute. * If the attribute has input error, the input field's CSS class will * be appended with {@link errorCss}. * @param CModel $model the data model * @param string $attribute the attribute * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) * @return string the generated input field * @see clientChange * @see activeInputField * @since 1.1.14 */ public static function activeSearchField($model,$attribute,$htmlOptions=array()) { self::resolveNameID($model,$attribute,$htmlOptions); self::clientChange('change',$htmlOptions); return self::activeInputField('search',$model,$attribute,$htmlOptions); } /** * Generates a url field input for a model attribute. * If the attribute has input error, the input field's CSS class will * be appended with {@link errorCss}. * @param CModel $model the data model * @param string $attribute the attribute * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) * @return string the generated input field * @see clientChange * @see activeInputField * @since 1.1.11 */ public static function activeUrlField($model,$attribute,$htmlOptions=array()) { self::resolveNameID($model,$attribute,$htmlOptions); self::clientChange('change',$htmlOptions); return self::activeInputField('url',$model,$attribute,$htmlOptions); } /** * Generates an email field input for a model attribute. * If the attribute has input error, the input field's CSS class will * be appended with {@link errorCss}. * @param CModel $model the data model * @param string $attribute the attribute * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) * @return string the generated input field * @see clientChange * @see activeInputField * @since 1.1.11 */ public static function activeEmailField($model,$attribute,$htmlOptions=array()) { self::resolveNameID($model,$attribute,$htmlOptions); self::clientChange('change',$htmlOptions); return self::activeInputField('email',$model,$attribute,$htmlOptions); } /** * Generates a number field input for a model attribute. * If the attribute has input error, the input field's CSS class will * be appended with {@link errorCss}. * @param CModel $model the data model * @param string $attribute the attribute * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) * @return string the generated input field * @see clientChange * @see activeInputField * @since 1.1.11 */ public static function activeNumberField($model,$attribute,$htmlOptions=array()) { self::resolveNameID($model,$attribute,$htmlOptions); self::clientChange('change',$htmlOptions); return self::activeInputField('number',$model,$attribute,$htmlOptions); } /** * Generates a range field input for a model attribute. * If the attribute has input error, the input field's CSS class will * be appended with {@link errorCss}. * @param CModel $model the data model * @param string $attribute the attribute * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) * @return string the generated input field * @see clientChange * @see activeInputField * @since 1.1.11 */ public static function activeRangeField($model,$attribute,$htmlOptions=array()) { self::resolveNameID($model,$attribute,$htmlOptions); self::clientChange('change',$htmlOptions); return self::activeInputField('range',$model,$attribute,$htmlOptions); } /** * Generates a date field input for a model attribute. * If the attribute has input error, the input field's CSS class will * be appended with {@link errorCss}. * @param CModel $model the data model * @param string $attribute the attribute * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) * @return string the generated input field * @see clientChange * @see activeInputField * @since 1.1.11 */ public static function activeDateField($model,$attribute,$htmlOptions=array()) { self::resolveNameID($model,$attribute,$htmlOptions); self::clientChange('change',$htmlOptions); return self::activeInputField('date',$model,$attribute,$htmlOptions); } /** * Generates a time field input for a model attribute. * If the attribute has input error, the input field's CSS class will * be appended with {@link errorCss}. * @param CModel $model the data model * @param string $attribute the attribute * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) * @return string the generated input field * @see clientChange * @see activeInputField * @since 1.1.14 */ public static function activeTimeField($model,$attribute,$htmlOptions=array()) { self::resolveNameID($model,$attribute,$htmlOptions); self::clientChange('change',$htmlOptions); return self::activeInputField('time',$model,$attribute,$htmlOptions); } /** * Generates a telephone field input for a model attribute. * If the attribute has input error, the input field's CSS class will * be appended with {@link errorCss}. * @param CModel $model the data model * @param string $attribute the attribute * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) * @return string the generated input field * @see clientChange * @see activeInputField * @since 1.1.14 */ public static function activeTelField($model,$attribute,$htmlOptions=array()) { self::resolveNameID($model,$attribute,$htmlOptions); self::clientChange('change',$htmlOptions); return self::activeInputField('tel',$model,$attribute,$htmlOptions); } /** * Generates a hidden input for a model attribute. * @param CModel $model the data model * @param string $attribute the attribute * @param array $htmlOptions additional HTML attributes. * @return string the generated input field * @see activeInputField */ public static function activeHiddenField($model,$attribute,$htmlOptions=array()) { self::resolveNameID($model,$attribute,$htmlOptions); return self::activeInputField('hidden',$model,$attribute,$htmlOptions); } /** * Generates a password field input for a model attribute. * If the attribute has input error, the input field's CSS class will * be appended with {@link errorCss}. * @param CModel $model the data model * @param string $attribute the attribute * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) * @return string the generated input field * @see clientChange * @see activeInputField */ public static function activePasswordField($model,$attribute,$htmlOptions=array()) { self::resolveNameID($model,$attribute,$htmlOptions); self::clientChange('change',$htmlOptions); return self::activeInputField('password',$model,$attribute,$htmlOptions); } /** * Generates a text area input for a model attribute. * If the attribute has input error, the input field's CSS class will * be appended with {@link errorCss}. * @param CModel $model the data model * @param string $attribute the attribute * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) * @return string the generated text area * @see clientChange */ public static function activeTextArea($model,$attribute,$htmlOptions=array()) { self::resolveNameID($model,$attribute,$htmlOptions); self::clientChange('change',$htmlOptions); if($model->hasErrors($attribute)) self::addErrorCss($htmlOptions); if(isset($htmlOptions['value'])) { $text=$htmlOptions['value']; unset($htmlOptions['value']); } else $text=self::resolveValue($model,$attribute); return self::tag('textarea',$htmlOptions,isset($htmlOptions['encode']) && !$htmlOptions['encode'] ? $text : self::encode($text)); } /** * Generates a file input for a model attribute. * Note, you have to set the enclosing form's 'enctype' attribute to be 'multipart/form-data'. * After the form is submitted, the uploaded file information can be obtained via $_FILES (see * PHP documentation). * @param CModel $model the data model * @param string $attribute the attribute * @param array $htmlOptions additional HTML attributes (see {@link tag}). * @return string the generated input field * @see activeInputField */ public static function activeFileField($model,$attribute,$htmlOptions=array()) { self::resolveNameID($model,$attribute,$htmlOptions); // add a hidden field so that if a model only has a file field, we can // still use isset($_POST[$modelClass]) to detect if the input is submitted $hiddenOptions=isset($htmlOptions['id']) ? array('id'=>self::ID_PREFIX.$htmlOptions['id']) : array('id'=>false); return self::hiddenField($htmlOptions['name'],'',$hiddenOptions) . self::activeInputField('file',$model,$attribute,$htmlOptions); } /** * Generates a radio button for a model attribute. * If the attribute has input error, the input field's CSS class will * be appended with {@link errorCss}. * @param CModel $model the data model * @param string $attribute the attribute * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) * A special option named 'uncheckValue' is available that can be used to specify * the value returned when the radio button is not checked. By default, this value is '0'. * Internally, a hidden field is rendered so that when the radio button is not checked, * we can still obtain the posted uncheck value. * If 'uncheckValue' is set as NULL, the hidden field will not be rendered. * @return string the generated radio button * @see clientChange * @see activeInputField */ public static function activeRadioButton($model,$attribute,$htmlOptions=array()) { self::resolveNameID($model,$attribute,$htmlOptions); if(!isset($htmlOptions['value'])) $htmlOptions['value']=1; if(!isset($htmlOptions['checked']) && self::resolveValue($model,$attribute)==$htmlOptions['value']) $htmlOptions['checked']='checked'; self::clientChange('click',$htmlOptions); if(array_key_exists('uncheckValue',$htmlOptions)) { $uncheck=$htmlOptions['uncheckValue']; unset($htmlOptions['uncheckValue']); } else $uncheck='0'; $hiddenOptions=isset($htmlOptions['id']) ? array('id'=>self::ID_PREFIX.$htmlOptions['id']) : array('id'=>false); $hidden=$uncheck!==null ? self::hiddenField($htmlOptions['name'],$uncheck,$hiddenOptions) : ''; // add a hidden field so that if the radio button is not selected, it still submits a value return $hidden . self::activeInputField('radio',$model,$attribute,$htmlOptions); } /** * Generates a check box for a model attribute. * The attribute is assumed to take either true or false value. * If the attribute has input error, the input field's CSS class will * be appended with {@link errorCss}. * @param CModel $model the data model * @param string $attribute the attribute * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) * A special option named 'uncheckValue' is available that can be used to specify * the value returned when the checkbox is not checked. By default, this value is '0'. * Internally, a hidden field is rendered so that when the checkbox is not checked, * we can still obtain the posted uncheck value. * If 'uncheckValue' is set as NULL, the hidden field will not be rendered. * @return string the generated check box * @see clientChange * @see activeInputField */ public static function activeCheckBox($model,$attribute,$htmlOptions=array()) { self::resolveNameID($model,$attribute,$htmlOptions); if(!isset($htmlOptions['value'])) $htmlOptions['value']=1; if(!isset($htmlOptions['checked']) && self::resolveValue($model,$attribute)==$htmlOptions['value']) $htmlOptions['checked']='checked'; self::clientChange('click',$htmlOptions); if(array_key_exists('uncheckValue',$htmlOptions)) { $uncheck=$htmlOptions['uncheckValue']; unset($htmlOptions['uncheckValue']); } else $uncheck='0'; $hiddenOptions=isset($htmlOptions['id']) ? array('id'=>self::ID_PREFIX.$htmlOptions['id']) : array('id'=>false); $hidden=$uncheck!==null ? self::hiddenField($htmlOptions['name'],$uncheck,$hiddenOptions) : ''; return $hidden . self::activeInputField('checkbox',$model,$attribute,$htmlOptions); } /** * Generates a drop down list for a model attribute. * If the attribute has input error, the input field's CSS class will * be appended with {@link errorCss}. * @param CModel $model the data model * @param string $attribute the attribute * @param array $data data for generating the list options (value=>display) * You may use {@link listData} to generate this data. * Please refer to {@link listOptions} on how this data is used to generate the list options. * Note, the values and labels will be automatically HTML-encoded by this method. * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special * attributes are recognized. See {@link clientChange} and {@link tag} for more details. * In addition, the following options are also supported: * * Since 1.1.13, a special option named 'unselectValue' is available. It can be used to set the value * that will be returned when no option is selected in multiple mode. When set, a hidden field is * rendered so that if no option is selected in multiple mode, we can still obtain the posted * unselect value. If 'unselectValue' is not set or set to NULL, the hidden field will not be rendered. * @return string the generated drop down list * @see clientChange * @see listData */ public static function activeDropDownList($model,$attribute,$data,$htmlOptions=array()) { self::resolveNameID($model,$attribute,$htmlOptions); $selection=self::resolveValue($model,$attribute); $options="\n".self::listOptions($selection,$data,$htmlOptions); self::clientChange('change',$htmlOptions); if($model->hasErrors($attribute)) self::addErrorCss($htmlOptions); $hidden=''; if(!empty($htmlOptions['multiple'])) { if(substr($htmlOptions['name'],-2)!=='[]') $htmlOptions['name'].='[]'; if(isset($htmlOptions['unselectValue'])) { $hiddenOptions=isset($htmlOptions['id']) ? array('id'=>self::ID_PREFIX.$htmlOptions['id']) : array('id'=>false); $hidden=self::hiddenField(substr($htmlOptions['name'],0,-2),$htmlOptions['unselectValue'],$hiddenOptions); unset($htmlOptions['unselectValue']); } } return $hidden . self::tag('select',$htmlOptions,$options); } /** * Generates a list box for a model attribute. * The model attribute value is used as the selection. * If the attribute has input error, the input field's CSS class will * be appended with {@link errorCss}. * @param CModel $model the data model * @param string $attribute the attribute * @param array $data data for generating the list options (value=>display) * You may use {@link listData} to generate this data. * Please refer to {@link listOptions} on how this data is used to generate the list options. * Note, the values and labels will be automatically HTML-encoded by this method. * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special * attributes are recognized. See {@link clientChange} and {@link tag} for more details. * In addition, the following options are also supported: * * @return string the generated list box * @see clientChange * @see listData */ public static function activeListBox($model,$attribute,$data,$htmlOptions=array()) { if(!isset($htmlOptions['size'])) $htmlOptions['size']=4; return self::activeDropDownList($model,$attribute,$data,$htmlOptions); } /** * Generates a check box list for a model attribute. * The model attribute value is used as the selection. * If the attribute has input error, the input field's CSS class will * be appended with {@link errorCss}. * Note that a check box list allows multiple selection, like {@link listBox}. * As a result, the corresponding POST value is an array. In case no selection * is made, the corresponding POST value is an empty string. * @param CModel $model the data model * @param string $attribute the attribute * @param array $data value-label pairs used to generate the check box list. * Note, the values will be automatically HTML-encoded, while the labels will not. * @param array $htmlOptions additional HTML options. The options will be applied to * each checkbox input. The following special options are recognized: * * Since 1.1.7, a special option named 'uncheckValue' is available. It can be used to set the value * that will be returned when the checkbox is not checked. By default, this value is ''. * Internally, a hidden field is rendered so when the checkbox is not checked, we can still * obtain the value. If 'uncheckValue' is set to NULL, there will be no hidden field rendered. * @return string the generated check box list * @see checkBoxList */ public static function activeCheckBoxList($model,$attribute,$data,$htmlOptions=array()) { self::resolveNameID($model,$attribute,$htmlOptions); $selection=self::resolveValue($model,$attribute); if($model->hasErrors($attribute)) self::addErrorCss($htmlOptions); $name=$htmlOptions['name']; unset($htmlOptions['name']); if(array_key_exists('uncheckValue',$htmlOptions)) { $uncheck=$htmlOptions['uncheckValue']; unset($htmlOptions['uncheckValue']); } else $uncheck=''; $hiddenOptions=isset($htmlOptions['id']) ? array('id'=>self::ID_PREFIX.$htmlOptions['id']) : array('id'=>false); $hidden=$uncheck!==null ? self::hiddenField($name,$uncheck,$hiddenOptions) : ''; return $hidden . self::checkBoxList($name,$selection,$data,$htmlOptions); } /** * Generates a radio button list for a model attribute. * The model attribute value is used as the selection. * If the attribute has input error, the input field's CSS class will * be appended with {@link errorCss}. * @param CModel $model the data model * @param string $attribute the attribute * @param array $data value-label pairs used to generate the radio button list. * Note, the values will be automatically HTML-encoded, while the labels will not. * @param array $htmlOptions additional HTML options. The options will be applied to * each radio button input. The following special options are recognized: * * Since version 1.1.7, a special option named 'uncheckValue' is available that can be used to specify the value * returned when the radio button is not checked. By default, this value is ''. Internally, a hidden field is * rendered so that when the radio button is not checked, we can still obtain the posted uncheck value. * If 'uncheckValue' is set as NULL, the hidden field will not be rendered. * @return string the generated radio button list * @see radioButtonList */ public static function activeRadioButtonList($model,$attribute,$data,$htmlOptions=array()) { self::resolveNameID($model,$attribute,$htmlOptions); $selection=self::resolveValue($model,$attribute); if($model->hasErrors($attribute)) self::addErrorCss($htmlOptions); $name=$htmlOptions['name']; unset($htmlOptions['name']); if(array_key_exists('uncheckValue',$htmlOptions)) { $uncheck=$htmlOptions['uncheckValue']; unset($htmlOptions['uncheckValue']); } else $uncheck=''; $hiddenOptions=isset($htmlOptions['id']) ? array('id'=>self::ID_PREFIX.$htmlOptions['id']) : array('id'=>false); $hidden=$uncheck!==null ? self::hiddenField($name,$uncheck,$hiddenOptions) : ''; return $hidden . self::radioButtonList($name,$selection,$data,$htmlOptions); } /** * Displays a summary of validation errors for one or several models. * @param mixed $model the models whose input errors are to be displayed. This can be either * a single model or an array of models. * @param string $header a piece of HTML code that appears in front of the errors * @param string $footer a piece of HTML code that appears at the end of the errors * @param array $htmlOptions additional HTML attributes to be rendered in the container div tag. * A special option named 'firstError' is recognized, which when set true, will * make the error summary to show only the first error message of each attribute. * If this is not set or is false, all error messages will be displayed. * This option has been available since version 1.1.3. * @return string the error summary. Empty if no errors are found. * @see CModel::getErrors * @see errorSummaryCss */ public static function errorSummary($model,$header=null,$footer=null,$htmlOptions=array()) { $content=''; if(!is_array($model)) $model=array($model); if(isset($htmlOptions['firstError'])) { $firstError=$htmlOptions['firstError']; unset($htmlOptions['firstError']); } else $firstError=false; foreach($model as $m) { foreach($m->getErrors() as $errors) { foreach($errors as $error) { if($error!='') $content.="
  • $error
  • \n"; if($firstError) break; } } } if($content!=='') { if($header===null) $header='

    '.Yii::t('yii','Please fix the following input errors:').'

    '; if(!isset($htmlOptions['class'])) $htmlOptions['class']=self::$errorSummaryCss; return self::tag('div',$htmlOptions,$header."\n".$footer); } else return ''; } /** * Displays the first validation error for a model attribute. * @param CModel $model the data model * @param string $attribute the attribute name * @param array $htmlOptions additional HTML attributes to be rendered in the container tag. * @return string the error display. Empty if no errors are found. * @see CModel::getErrors * @see errorMessageCss * @see $errorContainerTag */ public static function error($model,$attribute,$htmlOptions=array()) { self::resolveName($model,$attribute); // turn [a][b]attr into attr $error=$model->getError($attribute); if($error!='') { if(!isset($htmlOptions['class'])) $htmlOptions['class']=self::$errorMessageCss; return self::tag(self::$errorContainerTag,$htmlOptions,$error); } else return ''; } /** * Generates the data suitable for list-based HTML elements. * The generated data can be used in {@link dropDownList}, {@link listBox}, {@link checkBoxList}, * {@link radioButtonList}, and their active-versions (such as {@link activeDropDownList}). * Note, this method does not HTML-encode the generated data. You may call {@link encodeArray} to * encode it if needed. * Please refer to the {@link value} method on how to specify value field, text field and group field. * You can also pass anonymous functions as second, third and fourth arguments which calculates * text field value (PHP 5.3+ only) since 1.1.13. Your anonymous function should receive one argument, * which is the model, the current <option> tag is generated from. * *
    	 * CHtml::listData($posts,'id',function($post) {
    	 * 	return CHtml::encode($post->title);
    	 * });
    	 * 
    * * @param array $models a list of model objects. This parameter * can also be an array of associative arrays (e.g. results of {@link CDbCommand::queryAll}). * @param mixed $valueField the attribute name or anonymous function (PHP 5.3+) for list option values * @param mixed $textField the attribute name or anonymous function (PHP 5.3+) for list option texts * @param mixed $groupField the attribute name or anonymous function (PHP 5.3+) for list option group names. If empty, no group will be generated. * @return array the list data that can be used in {@link dropDownList}, {@link listBox}, etc. */ public static function listData($models,$valueField,$textField,$groupField='') { $listData=array(); if($groupField==='') { foreach($models as $model) { $value=self::value($model,$valueField); $text=self::value($model,$textField); $listData[$value]=$text; } } else { foreach($models as $model) { $group=self::value($model,$groupField); $value=self::value($model,$valueField); $text=self::value($model,$textField); if($group===null) $listData[$value]=$text; else $listData[$group][$value]=$text; } } return $listData; } /** * Evaluates the value of the specified attribute for the given model. * The attribute name can be given in a dot syntax. For example, if the attribute * is "author.firstName", this method will return the value of "$model->author->firstName". * A default value (passed as the last parameter) will be returned if the attribute does * not exist or is broken in the middle (e.g. $model->author is null). * The model can be either an object or an array. If the latter, the attribute is treated * as a key of the array. For the example of "author.firstName", if would mean the array value * "$model['author']['firstName']". * * Anonymous function could also be used for attribute calculation since 1.1.13 * ($attribute parameter; PHP 5.3+ only) as follows: *
    	 * $taskClosedSecondsAgo=CHtml::value($closedTask,function($model) {
    	 * 	return time()-$model->closed_at;
    	 * });
    	 * 
    * Your anonymous function should receive one argument, which is the model, the current * value is calculated from. This feature could be used together with the {@link listData}. * Please refer to its documentation for more details. * * @param mixed $model the model. This can be either an object or an array. * @param mixed $attribute the attribute name (use dot to concatenate multiple attributes) * or anonymous function (PHP 5.3+). Remember that functions created by "create_function" * are not supported by this method. Also note that numeric value is meaningless when * first parameter is object typed. * @param mixed $defaultValue the default value to return when the attribute does not exist. * @return mixed the attribute value. */ public static function value($model,$attribute,$defaultValue=null) { if(is_scalar($attribute) || $attribute===null) foreach(explode('.',$attribute) as $name) { if(is_object($model) && isset($model->$name)) $model=$model->$name; elseif(is_array($model) && isset($model[$name])) $model=$model[$name]; else return $defaultValue; } else return call_user_func($attribute,$model); return $model; } /** * Generates a valid HTML ID based on name. * @param string $name name from which to generate HTML ID * @return string the ID generated based on name. */ public static function getIdByName($name) { return str_replace(array('[]','][','[',']',' '),array('','_','_','','_'),$name); } /** * Generates input field ID for a model attribute. * @param CModel $model the data model * @param string $attribute the attribute * @return string the generated input field ID */ public static function activeId($model,$attribute) { return self::getIdByName(self::activeName($model,$attribute)); } /** * Generates HTML name for given model. * @see CHtml::setModelNameConverter() * @param CModel|string $model the data model or the model class name * @return string the generated HTML name value * @since 1.1.14 */ public static function modelName($model) { if(is_callable(self::$_modelNameConverter)) return call_user_func(self::$_modelNameConverter,$model); $className=is_object($model) ? get_class($model) : (string)$model; return trim(str_replace('\\','_',$className),'_'); } /** * Set generator used in the {@link CHtml::modelName()} method. You can use the `null` value to restore default * generator. * * @param callback|null $converter the new generator, the model or class name will be passed to the this callback * and result must be a valid value for HTML name attribute. * @throws CException if $converter isn't a valid callback * @since 1.1.14 */ public static function setModelNameConverter($converter) { if(is_callable($converter)) self::$_modelNameConverter=$converter; elseif($converter===null) self::$_modelNameConverter=null; else throw new CException(Yii::t('yii','The $converter argument must be a valid callback or null.')); } /** * Generates input field name for a model attribute. * Unlike {@link resolveName}, this method does NOT modify the attribute name. * @param CModel $model the data model * @param string $attribute the attribute * @return string the generated input field name */ public static function activeName($model,$attribute) { $a=$attribute; // because the attribute name may be changed by resolveName return self::resolveName($model,$a); } /** * Generates an input HTML tag for a model attribute. * This method generates an input HTML tag based on the given data model and attribute. * If the attribute has input error, the input field's CSS class will * be appended with {@link errorCss}. * This enables highlighting the incorrect input. * @param string $type the input type (e.g. 'text', 'radio') * @param CModel $model the data model * @param string $attribute the attribute * @param array $htmlOptions additional HTML attributes for the HTML tag * @return string the generated input tag */ protected static function activeInputField($type,$model,$attribute,$htmlOptions) { $htmlOptions['type']=$type; if($type==='text' || $type==='password') { if(!isset($htmlOptions['maxlength'])) { foreach($model->getValidators($attribute) as $validator) { if($validator instanceof CStringValidator && $validator->max!==null) { $htmlOptions['maxlength']=$validator->max; break; } } } elseif($htmlOptions['maxlength']===false) unset($htmlOptions['maxlength']); } if($type==='file') unset($htmlOptions['value']); elseif(!isset($htmlOptions['value'])) $htmlOptions['value']=self::resolveValue($model,$attribute); if($model->hasErrors($attribute)) self::addErrorCss($htmlOptions); return self::tag('input',$htmlOptions); } /** * Generates the list options. * @param mixed $selection the selected value(s). This can be either a string for single selection or an array for multiple selections. * @param array $listData the option data (see {@link listData}) * @param array $htmlOptions additional HTML attributes. The following two special attributes are recognized: * * @return string the generated list options */ public static function listOptions($selection,$listData,&$htmlOptions) { $raw=isset($htmlOptions['encode']) && !$htmlOptions['encode']; $content=''; if(isset($htmlOptions['prompt'])) { $content.='\n"; unset($htmlOptions['prompt']); } if(isset($htmlOptions['empty'])) { if(!is_array($htmlOptions['empty'])) $htmlOptions['empty']=array(''=>$htmlOptions['empty']); foreach($htmlOptions['empty'] as $value=>$label) $content.='\n"; unset($htmlOptions['empty']); } if(isset($htmlOptions['options'])) { $options=$htmlOptions['options']; unset($htmlOptions['options']); } else $options=array(); $key=isset($htmlOptions['key']) ? $htmlOptions['key'] : 'primaryKey'; if(is_array($selection)) { foreach($selection as $i=>$item) { if(is_object($item)) $selection[$i]=$item->$key; } } elseif(is_object($selection)) $selection=$selection->$key; foreach($listData as $key=>$value) { if(is_array($value)) { $content.='\n"; $dummy=array('options'=>$options); if(isset($htmlOptions['encode'])) $dummy['encode']=$htmlOptions['encode']; $content.=self::listOptions($selection,$value,$dummy); $content.=''."\n"; } else { $attributes=array('value'=>(string)$key,'encode'=>!$raw); if(!is_array($selection) && !strcmp($key,$selection) || is_array($selection) && in_array($key,$selection)) $attributes['selected']='selected'; if(isset($options[$key])) $attributes=array_merge($attributes,$options[$key]); $content.=self::tag('option',$attributes,$raw?(string)$value : self::encode((string)$value))."\n"; } } unset($htmlOptions['key']); return $content; } /** * Generates the JavaScript with the specified client changes. * @param string $event event name (without 'on') * @param array $htmlOptions HTML attributes which may contain the following special attributes * specifying the client change behaviors: * * This parameter has been available since version 1.1.1. */ protected static function clientChange($event,&$htmlOptions) { if(!isset($htmlOptions['submit']) && !isset($htmlOptions['confirm']) && !isset($htmlOptions['ajax'])) return; if(isset($htmlOptions['live'])) { $live=$htmlOptions['live']; unset($htmlOptions['live']); } else $live = self::$liveEvents; if(isset($htmlOptions['return']) && $htmlOptions['return']) $return='return true'; else $return='return false'; if(isset($htmlOptions['on'.$event])) { $handler=trim($htmlOptions['on'.$event],';').';'; unset($htmlOptions['on'.$event]); } else $handler=''; if(isset($htmlOptions['id'])) $id=$htmlOptions['id']; else $id=$htmlOptions['id']=isset($htmlOptions['name'])?$htmlOptions['name']:self::ID_PREFIX.self::$count++; $cs=Yii::app()->getClientScript(); $cs->registerCoreScript('jquery'); if(isset($htmlOptions['submit'])) { $cs->registerCoreScript('yii'); $request=Yii::app()->getRequest(); if($request->enableCsrfValidation && isset($htmlOptions['csrf']) && $htmlOptions['csrf']) $htmlOptions['params'][$request->csrfTokenName]=$request->getCsrfToken(); if(isset($htmlOptions['params'])) $params=CJavaScript::encode($htmlOptions['params']); else $params='{}'; if($htmlOptions['submit']!=='') $url=CJavaScript::quote(self::normalizeUrl($htmlOptions['submit'])); else $url=''; $handler.="jQuery.yii.submitForm(this,'$url',$params);{$return};"; } if(isset($htmlOptions['ajax'])) $handler.=self::ajax($htmlOptions['ajax'])."{$return};"; if(isset($htmlOptions['confirm'])) { $confirm='confirm(\''.CJavaScript::quote($htmlOptions['confirm']).'\')'; if($handler!=='') $handler="if($confirm) {".$handler."} else return false;"; else $handler="return $confirm;"; } if($live) $cs->registerScript('Yii.CHtml.#' . $id,"jQuery('body').on('$event','#$id',function(){{$handler}});"); else $cs->registerScript('Yii.CHtml.#' . $id,"jQuery('#$id').on('$event', function(){{$handler}});"); unset($htmlOptions['params'],$htmlOptions['submit'],$htmlOptions['ajax'],$htmlOptions['confirm'],$htmlOptions['return'],$htmlOptions['csrf']); } /** * Generates input name and ID for a model attribute. * This method will update the HTML options by setting appropriate 'name' and 'id' attributes. * This method may also modify the attribute name if the name * contains square brackets (mainly used in tabular input). * @param CModel $model the data model * @param string $attribute the attribute * @param array $htmlOptions the HTML options */ public static function resolveNameID($model,&$attribute,&$htmlOptions) { if(!isset($htmlOptions['name'])) $htmlOptions['name']=self::resolveName($model,$attribute); if(!isset($htmlOptions['id'])) $htmlOptions['id']=self::getIdByName($htmlOptions['name']); elseif($htmlOptions['id']===false) unset($htmlOptions['id']); } /** * Generates input name for a model attribute. * Note, the attribute name may be modified after calling this method if the name * contains square brackets (mainly used in tabular input) before the real attribute name. * @param CModel $model the data model * @param string $attribute the attribute * @return string the input name */ public static function resolveName($model,&$attribute) { $modelName=self::modelName($model); if(($pos=strpos($attribute,'['))!==false) { if($pos!==0) // e.g. name[a][b] return $modelName.'['.substr($attribute,0,$pos).']'.substr($attribute,$pos); if(($pos=strrpos($attribute,']'))!==false && $pos!==strlen($attribute)-1) // e.g. [a][b]name { $sub=substr($attribute,0,$pos+1); $attribute=substr($attribute,$pos+1); return $modelName.$sub.'['.$attribute.']'; } if(preg_match('/\](\w+\[.*)$/',$attribute,$matches)) { $name=$modelName.'['.str_replace(']','][',trim(strtr($attribute,array(']['=>']','['=>']')),']')).']'; $attribute=$matches[1]; return $name; } } return $modelName.'['.$attribute.']'; } /** * Evaluates the attribute value of the model. * This method can recognize the attribute name written in array format. * For example, if the attribute name is 'name[a][b]', the value "$model->name['a']['b']" will be returned. * @param CModel $model the data model * @param string $attribute the attribute name * @return mixed the attribute value * @since 1.1.3 */ public static function resolveValue($model,$attribute) { if(($pos=strpos($attribute,'['))!==false) { if($pos===0) // [a]name[b][c], should ignore [a] { if(preg_match('/\](\w+(\[.+)?)/',$attribute,$matches)) $attribute=$matches[1]; // we get: name[b][c] if(($pos=strpos($attribute,'['))===false) return $model->$attribute; } $name=substr($attribute,0,$pos); $value=$model->$name; foreach(explode('][',rtrim(substr($attribute,$pos+1),']')) as $id) { if((is_array($value) || $value instanceof ArrayAccess) && isset($value[$id])) $value=$value[$id]; else return null; } return $value; } else return $model->$attribute; } /** * Appends {@link errorCss} to the 'class' attribute. * @param array $htmlOptions HTML options to be modified */ protected static function addErrorCss(&$htmlOptions) { if(empty(self::$errorCss)) return; if(isset($htmlOptions['class'])) $htmlOptions['class'].=' '.self::$errorCss; else $htmlOptions['class']=self::$errorCss; } /** * Renders the HTML tag attributes. * Since version 1.1.5, attributes whose value is null will not be rendered. * Special attributes, such as 'checked', 'disabled', 'readonly', will be rendered * properly based on their corresponding boolean value. * @param array $htmlOptions attributes to be rendered * @return string the rendering result */ public static function renderAttributes($htmlOptions) { static $specialAttributes=array( 'async'=>1, 'autofocus'=>1, 'autoplay'=>1, 'checked'=>1, 'controls'=>1, 'declare'=>1, 'default'=>1, 'defer'=>1, 'disabled'=>1, 'formnovalidate'=>1, 'hidden'=>1, 'ismap'=>1, 'loop'=>1, 'multiple'=>1, 'muted'=>1, 'nohref'=>1, 'noresize'=>1, 'novalidate'=>1, 'open'=>1, 'readonly'=>1, 'required'=>1, 'reversed'=>1, 'scoped'=>1, 'seamless'=>1, 'selected'=>1, 'typemustmatch'=>1, ); if($htmlOptions===array()) return ''; $html=''; if(isset($htmlOptions['encode'])) { $raw=!$htmlOptions['encode']; unset($htmlOptions['encode']); } else $raw=false; foreach($htmlOptions as $name=>$value) { if(isset($specialAttributes[$name])) { if($value) { $html .= ' ' . $name; if(self::$renderSpecialAttributesValue) $html .= '="' . $name . '"'; } } elseif($value!==null) $html .= ' ' . $name . '="' . ($raw ? $value : self::encode($value)) . '"'; } return $html; } }