diff --git a/framework/util/Html.php b/framework/util/Html.php index f736b60..11b4da6 100644 --- a/framework/util/Html.php +++ b/framework/util/Html.php @@ -7,7 +7,1151 @@ namespace yii\util; +use Yii; + +/** + * @author Qiang Xue + * @since 2.0 + */ class Html { + /** + * @var integer the counter for generating automatic input field names. + */ + public static $count = 0; + /** + * @var boolean whether to close single tags. Defaults to true. Can be set to false for HTML5. + */ + public static $closeSingleTags = true; + /** + * @var boolean whether to render special attributes value. Defaults to true. Can be set to false for HTML5. + */ + public static $renderSpecialAttributesValue = true; + + /** + * 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 + */ + 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 = static::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 . static::renderAttributes($htmlOptions); + if ($content === false) { + return $closeTag && static::$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 . static::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 static::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 static::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 .= ';' . static::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 + * @return string the enclosed JavaScript + */ + public static function script($text) + { + return ""; + } + + /** + * Includes a JavaScript file. + * @param string $url URL for the JavaScript file + * @return string the JavaScript file tag + */ + public static function scriptFile($url) + { + return ''; + } + + /** + * 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 static::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 = static::normalizeUrl($action); + $htmlOptions['method'] = $method; + $form = static::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[] = static::hiddenField(urldecode(substr($pair, 0, $pos)), urldecode(substr($pair, $pos + 1)), array('id' => false)); + } else { + $hiddens[] = static::hiddenField(urldecode($pair), '', array('id' => false)); + } + } + } + $request = Yii::app()->request; + if ($request->enableCsrfValidation && !strcasecmp($method, 'post')) { + $hiddens[] = static::hiddenField($request->csrfTokenName, $request->getCsrfToken(), array('id' => false)); + } + if ($hiddens !== array()) { + $form .= "\n" . static::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 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'] = static::normalizeUrl($url); + } + static::clientChange('click', $htmlOptions); + return static::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 static::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 static::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($name, $label = 'button', $htmlOptions = array()) + { + if (!isset($htmlOptions['name'])) { + if (!array_key_exists('name', $htmlOptions)) { + $htmlOptions['name'] = static::ID_PREFIX . static::$count++; + } + } + if (!isset($htmlOptions['type'])) { + $htmlOptions['type'] = 'button'; + } + if (!isset($htmlOptions['value'])) { + $htmlOptions['value'] = $label; + } + static::clientChange('click', $htmlOptions); + return static::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'] = static::ID_PREFIX . static::$count++; + } + if (!isset($htmlOptions['type'])) { + $htmlOptions['type'] = 'button'; + } + static::clientChange('click', $htmlOptions); + return static::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 static::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 static::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 static::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 static::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'] .= ' ' . static::$requiredCss; + } else { + $htmlOptions['class'] = static::$requiredCss; + } + $label = static::$beforeRequiredLabel . $label . static::$afterRequiredLabel; + } + unset($htmlOptions['required']); + } + return static::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()) + { + static::clientChange('change', $htmlOptions); + return static::inputField('text', $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 static::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()) + { + static::clientChange('change', $htmlOptions); + return static::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 static::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'] = static::getIdByName($name); + } elseif ($htmlOptions['id'] === false) { + unset($htmlOptions['id']); + } + static::clientChange('change', $htmlOptions); + return static::tag('textarea', $htmlOptions, isset($htmlOptions['encode']) && !$htmlOptions['encode'] ? $value : static::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; + static::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' => static::ID_PREFIX . $htmlOptions['id']); + } else { + $uncheckOptions = array('id' => false); + } + $hidden = static::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 . static::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; + static::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' => static::ID_PREFIX . $htmlOptions['id']); + } else { + $uncheckOptions = array('id' => false); + } + $hidden = static::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 . static::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'] = static::getIdByName($name); + } elseif ($htmlOptions['id'] === false) { + unset($htmlOptions['id']); + } + + static::clientChange('change', $htmlOptions); + $options = "\n" . static::listOptions($select, $data, $htmlOptions); + $hidden = ''; + + if (isset($htmlOptions['multiple'])) { + if (substr($htmlOptions['name'], -2) !== '[]') { + $htmlOptions['name'] .= '[]'; + } + + if (isset($htmlOptions['unselectValue'])) { + $hiddenOptions = isset($htmlOptions['id']) ? array('id' => static::ID_PREFIX . $htmlOptions['id']) : array('id' => false); + $hidden = static::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 . static::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 (isset($htmlOptions['multiple'])) { + if (substr($name, -2) !== '[]') { + $name .= '[]'; + } + } + return static::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'] : static::getIdByName($name); + unset($htmlOptions['baseID']); + $id = 0; + $checkAll = true; + + foreach ($data as $value => $label) { + $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 = static::checkBox($name, $checked, $htmlOptions); + $label = static::label($label, $htmlOptions['id'], $labelOptions); + $items[] = strtr($template, array('{input}' => $option, '{label}' => $label)); + } + + if (isset($checkAllLabel)) { + $htmlOptions['value'] = 1; + $htmlOptions['id'] = $id = $baseID . '_all'; + $option = static::checkBox($id, $checkAll, $htmlOptions); + $label = static::label($checkAllLabel, $id, $labelOptions); + $item = strtr($template, array('{input}' => $option, '{label}' => $label)); + 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 static::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']); + + $items = array(); + $baseID = isset($htmlOptions['baseID']) ? $htmlOptions['baseID'] : static::getIdByName($name); + unset($htmlOptions['baseID']); + $id = 0; + foreach ($data as $value => $label) { + $checked = !strcmp($value, $select); + $htmlOptions['value'] = $value; + $htmlOptions['id'] = $baseID . '_' . $id++; + $option = static::radioButton($name, $checked, $htmlOptions); + $label = static::label($label, $htmlOptions['id'], $labelOptions); + $items[] = strtr($template, array('{input}' => $option, '{label}' => $label)); + } + if (empty($container)) { + return implode($separator, $items); + } else { + return static::tag($container, array('id' => $baseID), implode($separator, $items)); + } + } + + /** + * 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'] = static::getIdByName($name); + } elseif ($htmlOptions['id'] === false) { + unset($htmlOptions['id']); + } + return static::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 .= static::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 .= static::tag('option', $attributes, $raw ? (string)$value : static::encode((string)$value)) . "\n"; + } + } + + unset($htmlOptions['key']); + + return $content; + } + + /** + * 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 $attributes attributes to be rendered + * @return string the rendering result + */ + public static function renderAttributes($attributes) + { + 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 ($attributes === array()) { + return ''; + } + + $html = ''; + if (isset($attributes['encode'])) { + $raw = !$attributes['encode']; + unset($attributes['encode']); + } else { + $raw = false; + } + + foreach ($attributes as $name => $value) { + if (isset($specialAttributes[$name])) { + if ($value) { + $html .= ' ' . $name; + if (static::$renderSpecialAttributesValue) { + $html .= '="' . $name . '"'; + } + } + } elseif ($value !== null) { + $html .= ' ' . $name . '="' . ($raw ? $value : static::encode($value)) . '"'; + } + } + return $html; + } }