1: <?php
2: /**
3: * CHtml class file.
4: *
5: * @author Qiang Xue <qiang.xue@gmail.com>
6: * @link http://www.yiiframework.com/
7: * @copyright 2008-2013 Yii Software LLC
8: * @license http://www.yiiframework.com/license/
9: */
10:
11:
12: /**
13: * CHtml is a static class that provides a collection of helper methods for creating HTML views.
14: *
15: * Nearly all of the methods in this class allow setting additional html attributes for the html
16: * tags they generate. You can specify for example. 'class', 'style' or 'id' for an html element.
17: * For example when using <code>array('class' => 'my-class', 'target' => '_blank')</code> as htmlOptions
18: * it will result in the html attributes rendered like this: <code>class="my-class" target="_blank"</code>.
19: *
20: * @author Qiang Xue <qiang.xue@gmail.com>
21: * @package system.web.helpers
22: * @since 1.0
23: */
24: class CHtml
25: {
26: const ID_PREFIX='yt';
27: /**
28: * @var string the CSS class for displaying error summaries (see {@link errorSummary}).
29: */
30: public static $errorSummaryCss='errorSummary';
31: /**
32: * @var string the CSS class for displaying error messages (see {@link error}).
33: */
34: public static $errorMessageCss='errorMessage';
35: /**
36: * @var string the CSS class for highlighting error inputs. Form inputs will be appended
37: * with this CSS class if they have input errors.
38: */
39: public static $errorCss='error';
40: /**
41: * @var string the tag name for the error container tag. Defaults to 'div'.
42: * @since 1.1.13
43: */
44: public static $errorContainerTag='div';
45: /**
46: * @var string the CSS class for required labels. Defaults to 'required'.
47: * @see label
48: */
49: public static $requiredCss='required';
50: /**
51: * @var string the HTML code to be prepended to the required label.
52: * @see label
53: */
54: public static $beforeRequiredLabel='';
55: /**
56: * @var string the HTML code to be appended to the required label.
57: * @see label
58: */
59: public static $afterRequiredLabel=' <span class="required">*</span>';
60: /**
61: * @var integer the counter for generating automatic input field names.
62: */
63: public static $count=0;
64: /**
65: * Sets the default style for attaching jQuery event handlers.
66: *
67: * If set to true (default), event handlers are delegated.
68: * Event handlers are attached to the document body and can process events
69: * from descendant elements that are added to the document at a later time.
70: *
71: * If set to false, event handlers are directly bound.
72: * Event handlers are attached directly to the DOM element, that must already exist
73: * on the page. Elements injected into the page at a later time will not be processed.
74: *
75: * You can override this setting for a particular element by setting the htmlOptions delegate attribute
76: * (see {@link clientChange}).
77: *
78: * For more information about attaching jQuery event handler see {@link http://api.jquery.com/on/}
79: * @since 1.1.9
80: * @see clientChange
81: */
82: public static $liveEvents=true;
83: /**
84: * @var boolean whether to close single tags. Defaults to true. Can be set to false for HTML5.
85: * @since 1.1.13
86: */
87: public static $closeSingleTags=true;
88: /**
89: * @var boolean whether to render special attributes value. Defaults to true. Can be set to false for HTML5.
90: * @since 1.1.13
91: */
92: public static $renderSpecialAttributesValue=true;
93: /**
94: * @var callback the generator used in the {@link CHtml::modelName()} method.
95: * @since 1.1.14
96: */
97: private static $_modelNameConverter;
98:
99: /**
100: * Encodes special characters into HTML entities.
101: * The {@link CApplication::charset application charset} will be used for encoding.
102: * @param string $text data to be encoded
103: * @return string the encoded data
104: * @see http://www.php.net/manual/en/function.htmlspecialchars.php
105: */
106: public static function encode($text)
107: {
108: return htmlspecialchars($text,ENT_QUOTES,Yii::app()->charset);
109: }
110:
111: /**
112: * Decodes special HTML entities back to the corresponding characters.
113: * This is the opposite of {@link encode()}.
114: * @param string $text data to be decoded
115: * @return string the decoded data
116: * @see http://www.php.net/manual/en/function.htmlspecialchars-decode.php
117: * @since 1.1.8
118: */
119: public static function decode($text)
120: {
121: return htmlspecialchars_decode($text,ENT_QUOTES);
122: }
123:
124: /**
125: * Encodes special characters in an array of strings into HTML entities.
126: * Both the array keys and values will be encoded if needed.
127: * If a value is an array, this method will also encode it recursively.
128: * The {@link CApplication::charset application charset} will be used for encoding.
129: * @param array $data data to be encoded
130: * @return array the encoded data
131: * @see http://www.php.net/manual/en/function.htmlspecialchars.php
132: */
133: public static function encodeArray($data)
134: {
135: $d=array();
136: foreach($data as $key=>$value)
137: {
138: if(is_string($key))
139: $key=htmlspecialchars($key,ENT_QUOTES,Yii::app()->charset);
140: if(is_string($value))
141: $value=htmlspecialchars($value,ENT_QUOTES,Yii::app()->charset);
142: elseif(is_array($value))
143: $value=self::encodeArray($value);
144: $d[$key]=$value;
145: }
146: return $d;
147: }
148:
149: /**
150: * Generates an HTML element.
151: * @param string $tag the tag name
152: * @param array $htmlOptions the element attributes. The values will be HTML-encoded using {@link encode()}.
153: * If an 'encode' attribute is given and its value is false,
154: * the rest of the attribute values will NOT be HTML-encoded.
155: * Since version 1.1.5, attributes whose value is null will not be rendered.
156: * @param mixed $content the content to be enclosed between open and close element tags. It will not be HTML-encoded.
157: * If false, it means there is no body content.
158: * @param boolean $closeTag whether to generate the close tag.
159: * @return string the generated HTML element tag
160: */
161: public static function tag($tag,$htmlOptions=array(),$content=false,$closeTag=true)
162: {
163: $html='<' . $tag . self::renderAttributes($htmlOptions);
164: if($content===false)
165: return $closeTag && self::$closeSingleTags ? $html.' />' : $html.'>';
166: else
167: return $closeTag ? $html.'>'.$content.'</'.$tag.'>' : $html.'>'.$content;
168: }
169:
170: /**
171: * Generates an open HTML element.
172: * @param string $tag the tag name
173: * @param array $htmlOptions the element attributes. The values will be HTML-encoded using {@link encode()}.
174: * If an 'encode' attribute is given and its value is false,
175: * the rest of the attribute values will NOT be HTML-encoded.
176: * Since version 1.1.5, attributes whose value is null will not be rendered.
177: * @return string the generated HTML element tag
178: */
179: public static function openTag($tag,$htmlOptions=array())
180: {
181: return '<' . $tag . self::renderAttributes($htmlOptions) . '>';
182: }
183:
184: /**
185: * Generates a close HTML element.
186: * @param string $tag the tag name
187: * @return string the generated HTML element tag
188: */
189: public static function closeTag($tag)
190: {
191: return '</'.$tag.'>';
192: }
193:
194: /**
195: * Encloses the given string within a CDATA tag.
196: * @param string $text the string to be enclosed
197: * @return string the CDATA tag with the enclosed content.
198: */
199: public static function cdata($text)
200: {
201: return '<![CDATA[' . $text . ']]>';
202: }
203:
204: /**
205: * Generates a meta tag that can be inserted in the head section of HTML page.
206: * @param string $content content attribute of the meta tag
207: * @param string $name name attribute of the meta tag. If null, the attribute will not be generated
208: * @param string $httpEquiv http-equiv attribute of the meta tag. If null, the attribute will not be generated
209: * @param array $options other options in name-value pairs (e.g. 'scheme', 'lang')
210: * @return string the generated meta tag
211: */
212: public static function metaTag($content,$name=null,$httpEquiv=null,$options=array())
213: {
214: if($name!==null)
215: $options['name']=$name;
216: if($httpEquiv!==null)
217: $options['http-equiv']=$httpEquiv;
218: $options['content']=$content;
219: return self::tag('meta',$options);
220: }
221:
222: /**
223: * Generates a link tag that can be inserted in the head section of HTML page.
224: * Do not confuse this method with {@link link()}. The latter generates a hyperlink.
225: * @param string $relation rel attribute of the link tag. If null, the attribute will not be generated.
226: * @param string $type type attribute of the link tag. If null, the attribute will not be generated.
227: * @param string $href href attribute of the link tag. If null, the attribute will not be generated.
228: * @param string $media media attribute of the link tag. If null, the attribute will not be generated.
229: * @param array $options other options in name-value pairs
230: * @return string the generated link tag
231: */
232: public static function linkTag($relation=null,$type=null,$href=null,$media=null,$options=array())
233: {
234: if($relation!==null)
235: $options['rel']=$relation;
236: if($type!==null)
237: $options['type']=$type;
238: if($href!==null)
239: $options['href']=$href;
240: if($media!==null)
241: $options['media']=$media;
242: return self::tag('link',$options);
243: }
244:
245: /**
246: * Encloses the given CSS content with a CSS tag.
247: * @param string $text the CSS content
248: * @param string $media the media that this CSS should apply to.
249: * @return string the CSS properly enclosed
250: */
251: public static function css($text,$media='')
252: {
253: if($media!=='')
254: $media=' media="'.$media.'"';
255: return "<style type=\"text/css\"{$media}>\n/*<![CDATA[*/\n{$text}\n/*]]>*/\n</style>";
256: }
257:
258: /**
259: * Registers a 'refresh' meta tag.
260: * This method can be invoked anywhere in a view. It will register a 'refresh'
261: * meta tag with {@link CClientScript} so that the page can be refreshed in
262: * the specified seconds.
263: * @param integer $seconds the number of seconds to wait before refreshing the page
264: * @param string $url the URL to which the page should be redirected to. If empty, it means the current page.
265: * @since 1.1.1
266: */
267: public static function refresh($seconds,$url='')
268: {
269: $content="$seconds";
270: if($url!=='')
271: $content.=';url='.self::normalizeUrl($url);
272: Yii::app()->clientScript->registerMetaTag($content,null,'refresh');
273: }
274:
275: /**
276: * Links to the specified CSS file.
277: * @param string $url the CSS URL
278: * @param string $media the media that this CSS should apply to.
279: * @return string the CSS link.
280: */
281: public static function cssFile($url,$media='')
282: {
283: return CHtml::linkTag('stylesheet','text/css',$url,$media!=='' ? $media : null);
284: }
285:
286: /**
287: * Encloses the given JavaScript within a script tag.
288: * @param string $text the JavaScript to be enclosed
289: * @param array $htmlOptions additional HTML attributes (see {@link tag})
290: * @return string the enclosed JavaScript
291: */
292: public static function script($text,array $htmlOptions=array())
293: {
294: $defaultHtmlOptions=array(
295: 'type'=>'text/javascript',
296: );
297: $htmlOptions=array_merge($defaultHtmlOptions,$htmlOptions);
298: return self::tag('script',$htmlOptions,"\n/*<![CDATA[*/\n{$text}\n/*]]>*/\n");
299: }
300:
301: /**
302: * Includes a JavaScript file.
303: * @param string $url URL for the JavaScript file
304: * @param array $htmlOptions additional HTML attributes (see {@link tag})
305: * @return string the JavaScript file tag
306: */
307: public static function scriptFile($url,array $htmlOptions=array())
308: {
309: $defaultHtmlOptions=array(
310: 'type'=>'text/javascript',
311: 'src'=>$url
312: );
313: $htmlOptions=array_merge($defaultHtmlOptions,$htmlOptions);
314: return self::tag('script',$htmlOptions,'');
315: }
316:
317: /**
318: * Generates an opening form tag.
319: * This is a shortcut to {@link beginForm}.
320: * @param mixed $action the form action URL (see {@link normalizeUrl} for details about this parameter.)
321: * @param string $method form method (e.g. post, get)
322: * @param array $htmlOptions additional HTML attributes (see {@link tag}).
323: * @return string the generated form tag.
324: */
325: public static function form($action='',$method='post',$htmlOptions=array())
326: {
327: return self::beginForm($action,$method,$htmlOptions);
328: }
329:
330: /**
331: * Generates an opening form tag.
332: * Note, only the open tag is generated. A close tag should be placed manually
333: * at the end of the form.
334: * @param mixed $action the form action URL (see {@link normalizeUrl} for details about this parameter.)
335: * @param string $method form method (e.g. post, get)
336: * @param array $htmlOptions additional HTML attributes (see {@link tag}).
337: * @return string the generated form tag.
338: * @see endForm
339: */
340: public static function beginForm($action='',$method='post',$htmlOptions=array())
341: {
342: $htmlOptions['action']=$url=self::normalizeUrl($action);
343: if(strcasecmp($method,'get')!==0 && strcasecmp($method,'post')!==0)
344: {
345: $customMethod=$method;
346: $method='post';
347: }
348: else
349: $customMethod=false;
350:
351: $htmlOptions['method']=$method;
352: $form=self::tag('form',$htmlOptions,false,false);
353: $hiddens=array();
354: if(!strcasecmp($method,'get') && ($pos=strpos($url,'?'))!==false)
355: {
356: foreach(explode('&',substr($url,$pos+1)) as $pair)
357: {
358: if(($pos=strpos($pair,'='))!==false)
359: $hiddens[]=self::hiddenField(urldecode(substr($pair,0,$pos)),urldecode(substr($pair,$pos+1)),array('id'=>false));
360: else
361: $hiddens[]=self::hiddenField(urldecode($pair),'',array('id'=>false));
362: }
363: }
364: $request=Yii::app()->request;
365: if($request->enableCsrfValidation && !strcasecmp($method,'post'))
366: $hiddens[]=self::hiddenField($request->csrfTokenName,$request->getCsrfToken(),array('id'=>false));
367: if($customMethod!==false)
368: $hiddens[]=self::hiddenField('_method',$customMethod);
369: if($hiddens!==array())
370: $form.="\n".implode("\n",$hiddens);
371: return $form;
372: }
373:
374: /**
375: * Generates a closing form tag.
376: * @return string the generated tag
377: * @see beginForm
378: */
379: public static function endForm()
380: {
381: return '</form>';
382: }
383:
384: /**
385: * Generates a stateful form tag.
386: * A stateful form tag is similar to {@link form} except that it renders an additional
387: * hidden field for storing persistent page states. You should use this method to generate
388: * a form tag if you want to access persistent page states when the form is submitted.
389: * @param mixed $action the form action URL (see {@link normalizeUrl} for details about this parameter.)
390: * @param string $method form method (e.g. post, get)
391: * @param array $htmlOptions additional HTML attributes (see {@link tag}).
392: * @return string the generated form tag.
393: */
394: public static function statefulForm($action='',$method='post',$htmlOptions=array())
395: {
396: return self::form($action,$method,$htmlOptions)."\n".
397: self::tag('div',array('style'=>'display:none'),self::pageStateField(''));
398: }
399:
400: /**
401: * Generates a hidden field for storing persistent page states.
402: * This method is internally used by {@link statefulForm}.
403: * @param string $value the persistent page states in serialized format
404: * @return string the generated hidden field
405: */
406: public static function pageStateField($value)
407: {
408: return '<input type="hidden" name="'.CController::STATE_INPUT_NAME.'" value="'.$value.'" />';
409: }
410:
411: /**
412: * Generates a hyperlink tag.
413: * @param string $text link body. It will NOT be HTML-encoded. Therefore you can pass in HTML code such as an image tag.
414: * @param mixed $url a URL or an action route that can be used to create a URL.
415: * See {@link normalizeUrl} for more details about how to specify this parameter.
416: * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
417: * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
418: * @return string the generated hyperlink
419: * @see normalizeUrl
420: * @see clientChange
421: */
422: public static function link($text,$url='#',$htmlOptions=array())
423: {
424: if($url!=='')
425: $htmlOptions['href']=self::normalizeUrl($url);
426: self::clientChange('click',$htmlOptions);
427: return self::tag('a',$htmlOptions,$text);
428: }
429:
430: /**
431: * Generates a mailto link.
432: * @param string $text link body. It will NOT be HTML-encoded. Therefore you can pass in HTML code such as an image tag.
433: * @param string $email email address. If this is empty, the first parameter (link body) will be treated as the email address.
434: * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
435: * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
436: * @return string the generated mailto link
437: * @see clientChange
438: */
439: public static function mailto($text,$email='',$htmlOptions=array())
440: {
441: if($email==='')
442: $email=$text;
443: return self::link($text,'mailto:'.$email,$htmlOptions);
444: }
445:
446: /**
447: * Generates an image tag.
448: * @param string $src the image URL
449: * @param string $alt the alternative text display
450: * @param array $htmlOptions additional HTML attributes (see {@link tag}).
451: * @return string the generated image tag
452: */
453: public static function image($src,$alt='',$htmlOptions=array())
454: {
455: $htmlOptions['src']=$src;
456: $htmlOptions['alt']=$alt;
457: return self::tag('img',$htmlOptions);
458: }
459:
460: /**
461: * Generates a button.
462: * @param string $label the button label
463: * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
464: * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
465: * @return string the generated button tag
466: * @see clientChange
467: */
468: public static function button($label='button',$htmlOptions=array())
469: {
470: if(!isset($htmlOptions['name']))
471: {
472: if(!array_key_exists('name',$htmlOptions))
473: $htmlOptions['name']=self::ID_PREFIX.self::$count++;
474: }
475: if(!isset($htmlOptions['type']))
476: $htmlOptions['type']='button';
477: if(!isset($htmlOptions['value']) && $htmlOptions['type']!='image')
478: $htmlOptions['value']=$label;
479: self::clientChange('click',$htmlOptions);
480: return self::tag('input',$htmlOptions);
481: }
482:
483: /**
484: * Generates a button using HTML button tag.
485: * This method is similar to {@link button} except that it generates a 'button'
486: * tag instead of 'input' tag.
487: * @param string $label the button label. Note that this value will be directly inserted in the button element
488: * without being HTML-encoded.
489: * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
490: * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
491: * @return string the generated button tag
492: * @see clientChange
493: */
494: public static function htmlButton($label='button',$htmlOptions=array())
495: {
496: if(!isset($htmlOptions['name']))
497: $htmlOptions['name']=self::ID_PREFIX.self::$count++;
498: if(!isset($htmlOptions['type']))
499: $htmlOptions['type']='button';
500: self::clientChange('click',$htmlOptions);
501: return self::tag('button',$htmlOptions,$label);
502: }
503:
504: /**
505: * Generates a submit button.
506: * @param string $label the button label
507: * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
508: * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
509: * @return string the generated button tag
510: * @see clientChange
511: */
512: public static function submitButton($label='submit',$htmlOptions=array())
513: {
514: $htmlOptions['type']='submit';
515: return self::button($label,$htmlOptions);
516: }
517:
518: /**
519: * Generates a reset button.
520: * @param string $label the button label
521: * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
522: * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
523: * @return string the generated button tag
524: * @see clientChange
525: */
526: public static function resetButton($label='reset',$htmlOptions=array())
527: {
528: $htmlOptions['type']='reset';
529: return self::button($label,$htmlOptions);
530: }
531:
532: /**
533: * Generates an image submit button.
534: * @param string $src the image URL
535: * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
536: * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
537: * @return string the generated button tag
538: * @see clientChange
539: */
540: public static function imageButton($src,$htmlOptions=array())
541: {
542: $htmlOptions['src']=$src;
543: $htmlOptions['type']='image';
544: return self::button('submit',$htmlOptions);
545: }
546:
547: /**
548: * Generates a link submit button.
549: * @param string $label the button label
550: * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
551: * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
552: * @return string the generated button tag
553: * @see clientChange
554: */
555: public static function linkButton($label='submit',$htmlOptions=array())
556: {
557: if(!isset($htmlOptions['submit']))
558: $htmlOptions['submit']=isset($htmlOptions['href']) ? $htmlOptions['href'] : '';
559: return self::link($label,'#',$htmlOptions);
560: }
561:
562: /**
563: * Generates a label tag.
564: * @param string $label label text. Note, you should HTML-encode the text if needed.
565: * @param string $for the ID of the HTML element that this label is associated with.
566: * If this is false, the 'for' attribute for the label tag will not be rendered.
567: * @param array $htmlOptions additional HTML attributes.
568: * The following HTML option is recognized:
569: * <ul>
570: * <li>required: if this is set and is true, the label will be styled
571: * with CSS class 'required' (customizable with CHtml::$requiredCss),
572: * and be decorated with {@link CHtml::beforeRequiredLabel} and
573: * {@link CHtml::afterRequiredLabel}.</li>
574: * </ul>
575: * @return string the generated label tag
576: */
577: public static function label($label,$for,$htmlOptions=array())
578: {
579: if($for===false)
580: unset($htmlOptions['for']);
581: else
582: $htmlOptions['for']=$for;
583: if(isset($htmlOptions['required']))
584: {
585: if($htmlOptions['required'])
586: {
587: if(isset($htmlOptions['class']))
588: $htmlOptions['class'].=' '.self::$requiredCss;
589: else
590: $htmlOptions['class']=self::$requiredCss;
591: $label=self::$beforeRequiredLabel.$label.self::$afterRequiredLabel;
592: }
593: unset($htmlOptions['required']);
594: }
595: return self::tag('label',$htmlOptions,$label);
596: }
597:
598: /**
599: * Generates a color picker field input.
600: * @param string $name the input name
601: * @param string $value the input value
602: * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
603: * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
604: * @return string the generated input field
605: * @see clientChange
606: * @see inputField
607: * @since 1.1.16
608: */
609: public static function colorField($name,$value='',$htmlOptions=array())
610: {
611: self::clientChange('change',$htmlOptions);
612: return self::inputField('color',$name,$value,$htmlOptions);
613: }
614:
615: /**
616: * Generates a text field input.
617: * @param string $name the input name
618: * @param string $value the input value
619: * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
620: * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
621: * @return string the generated input field
622: * @see clientChange
623: * @see inputField
624: */
625: public static function textField($name,$value='',$htmlOptions=array())
626: {
627: self::clientChange('change',$htmlOptions);
628: return self::inputField('text',$name,$value,$htmlOptions);
629: }
630:
631: /**
632: * Generates a search field input.
633: * @param string $name the input name
634: * @param string $value the input value
635: * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
636: * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
637: * @return string the generated input field
638: * @see clientChange
639: * @see inputField
640: * @since 1.1.16
641: */
642: public static function searchField($name,$value='',$htmlOptions=array())
643: {
644: self::clientChange('change',$htmlOptions);
645: return self::inputField('search',$name,$value,$htmlOptions);
646: }
647: /**
648: * Generates a number field input.
649: * @param string $name the input name
650: * @param string $value the input value
651: * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
652: * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
653: * @return string the generated input field
654: * @see clientChange
655: * @see inputField
656: * @since 1.1.14
657: */
658: public static function numberField($name,$value='',$htmlOptions=array())
659: {
660: self::clientChange('change',$htmlOptions);
661: return self::inputField('number',$name,$value,$htmlOptions);
662: }
663:
664: /**
665: * Generates a range field input.
666: * @param string $name the input name
667: * @param string $value the input value
668: * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
669: * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
670: * @return string the generated input field
671: * @see clientChange
672: * @see inputField
673: * @since 1.1.14
674: */
675: public static function rangeField($name,$value='',$htmlOptions=array())
676: {
677: self::clientChange('change',$htmlOptions);
678: return self::inputField('range',$name,$value,$htmlOptions);
679: }
680:
681: /**
682: * Generates a date field input.
683: * @param string $name the input name
684: * @param string $value the input value
685: * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
686: * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
687: * @return string the generated input field
688: * @see clientChange
689: * @see inputField
690: * @since 1.1.14
691: */
692: public static function dateField($name,$value='',$htmlOptions=array())
693: {
694: self::clientChange('change',$htmlOptions);
695: return self::inputField('date',$name,$value,$htmlOptions);
696: }
697:
698: /**
699: * Generates a time field input.
700: * @param string $name the input name
701: * @param string $value the input value
702: * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
703: * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
704: * @return string the generated input field
705: * @see clientChange
706: * @see inputField
707: * @since 1.1.14
708: */
709: public static function timeField($name,$value='',$htmlOptions=array())
710: {
711: self::clientChange('change',$htmlOptions);
712: return self::inputField('time',$name,$value,$htmlOptions);
713: }
714:
715: /**
716: * Generates a datetime field input.
717: * @param string $name the input name
718: * @param string $value the input value
719: * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
720: * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
721: * @return string the generated input field
722: * @see clientChange
723: * @see inputField
724: * @since 1.1.16
725: */
726: public static function dateTimeField($name,$value='',$htmlOptions=array())
727: {
728: self::clientChange('change',$htmlOptions);
729: return self::inputField('datetime',$name,$value,$htmlOptions);
730: }
731:
732: /**
733: * Generates a local datetime field input.
734: * @param string $name the input name
735: * @param string $value the input value
736: * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
737: * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
738: * @return string the generated input field
739: * @see clientChange
740: * @see inputField
741: * @since 1.1.16
742: */
743: public static function dateTimeLocalField($name,$value='',$htmlOptions=array())
744: {
745: self::clientChange('change',$htmlOptions);
746: return self::inputField('datetime-local',$name,$value,$htmlOptions);
747: }
748:
749: /**
750: * Generates a week field input.
751: * @param string $name the input name
752: * @param string $value the input value
753: * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
754: * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
755: * @return string the generated input field
756: * @see clientChange
757: * @see inputField
758: * @since 1.1.16
759: */
760: public static function weekField($name,$value='',$htmlOptions=array())
761: {
762: self::clientChange('change',$htmlOptions);
763: return self::inputField('week',$name,$value,$htmlOptions);
764: }
765:
766: /**
767: * Generates an email field input.
768: * @param string $name the input name
769: * @param string $value the input value
770: * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
771: * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
772: * @return string the generated input field
773: * @see clientChange
774: * @see inputField
775: * @since 1.1.14
776: */
777: public static function emailField($name,$value='',$htmlOptions=array())
778: {
779: self::clientChange('change',$htmlOptions);
780: return self::inputField('email',$name,$value,$htmlOptions);
781: }
782:
783: /**
784: * Generates a telephone field input.
785: * @param string $name the input name
786: * @param string $value the input value
787: * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
788: * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
789: * @return string the generated input field
790: * @see clientChange
791: * @see inputField
792: * @since 1.1.14
793: */
794: public static function telField($name,$value='',$htmlOptions=array())
795: {
796: self::clientChange('change',$htmlOptions);
797: return self::inputField('tel',$name,$value,$htmlOptions);
798: }
799:
800: /**
801: * Generates a URL field input.
802: * @param string $name the input name
803: * @param string $value the input value
804: * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
805: * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
806: * @return string the generated input field
807: * @see clientChange
808: * @see inputField
809: * @since 1.1.14
810: */
811: public static function urlField($name,$value='',$htmlOptions=array())
812: {
813: self::clientChange('change',$htmlOptions);
814: return self::inputField('url',$name,$value,$htmlOptions);
815: }
816:
817: /**
818: * Generates a hidden input.
819: * @param string $name the input name
820: * @param string $value the input value
821: * @param array $htmlOptions additional HTML attributes (see {@link tag}).
822: * @return string the generated input field
823: * @see inputField
824: */
825: public static function ($name,$value='',$htmlOptions=array())
826: {
827: return self::inputField('hidden',$name,$value,$htmlOptions);
828: }
829:
830: /**
831: * Generates a password field input.
832: * @param string $name the input name
833: * @param string $value the input value
834: * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
835: * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
836: * @return string the generated input field
837: * @see clientChange
838: * @see inputField
839: */
840: public static function passwordField($name,$value='',$htmlOptions=array())
841: {
842: self::clientChange('change',$htmlOptions);
843: return self::inputField('password',$name,$value,$htmlOptions);
844: }
845:
846: /**
847: * Generates a file input.
848: * Note, you have to set the enclosing form's 'enctype' attribute to be 'multipart/form-data'.
849: * After the form is submitted, the uploaded file information can be obtained via $_FILES[$name] (see
850: * PHP documentation).
851: * @param string $name the input name
852: * @param string $value the input value
853: * @param array $htmlOptions additional HTML attributes (see {@link tag}).
854: * @return string the generated input field
855: * @see inputField
856: */
857: public static function fileField($name,$value='',$htmlOptions=array())
858: {
859: return self::inputField('file',$name,$value,$htmlOptions);
860: }
861:
862: /**
863: * Generates a text area input.
864: * @param string $name the input name
865: * @param string $value the input value
866: * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
867: * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
868: * @return string the generated text area
869: * @see clientChange
870: * @see inputField
871: */
872: public static function textArea($name,$value='',$htmlOptions=array())
873: {
874: $htmlOptions['name']=$name;
875: if(!isset($htmlOptions['id']))
876: $htmlOptions['id']=self::getIdByName($name);
877: elseif($htmlOptions['id']===false)
878: unset($htmlOptions['id']);
879: self::clientChange('change',$htmlOptions);
880: return self::tag('textarea',$htmlOptions,isset($htmlOptions['encode']) && !$htmlOptions['encode'] ? $value : self::encode($value));
881: }
882:
883: /**
884: * Generates a radio button.
885: * @param string $name the input name
886: * @param boolean $checked whether the radio button is checked
887: * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
888: * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
889: * Since version 1.1.2, a special option named 'uncheckValue' is available that can be used to specify
890: * the value returned when the radio button is not checked. When set, a hidden field is rendered so that
891: * when the radio button is not checked, we can still obtain the posted uncheck value.
892: * If 'uncheckValue' is not set or set to NULL, the hidden field will not be rendered.
893: * @return string the generated radio button
894: * @see clientChange
895: * @see inputField
896: */
897: public static function radioButton($name,$checked=false,$htmlOptions=array())
898: {
899: if($checked)
900: $htmlOptions['checked']='checked';
901: else
902: unset($htmlOptions['checked']);
903: $value=isset($htmlOptions['value']) ? $htmlOptions['value'] : 1;
904: self::clientChange('click',$htmlOptions);
905:
906: if(array_key_exists('uncheckValue',$htmlOptions))
907: {
908: $uncheck=$htmlOptions['uncheckValue'];
909: unset($htmlOptions['uncheckValue']);
910: }
911: else
912: $uncheck=null;
913:
914: if($uncheck!==null)
915: {
916: // add a hidden field so that if the radio button is not selected, it still submits a value
917: if(isset($htmlOptions['id']) && $htmlOptions['id']!==false)
918: $uncheckOptions=array('id'=>self::ID_PREFIX.$htmlOptions['id']);
919: else
920: $uncheckOptions=array('id'=>false);
921: $hidden=self::hiddenField($name,$uncheck,$uncheckOptions);
922: }
923: else
924: $hidden='';
925:
926: // add a hidden field so that if the radio button is not selected, it still submits a value
927: return $hidden . self::inputField('radio',$name,$value,$htmlOptions);
928: }
929:
930: /**
931: * Generates a check box.
932: * @param string $name the input name
933: * @param boolean $checked whether the check box is checked
934: * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
935: * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
936: * Since version 1.1.2, a special option named 'uncheckValue' is available that can be used to specify
937: * the value returned when the checkbox is not checked. When set, a hidden field is rendered so that
938: * when the checkbox is not checked, we can still obtain the posted uncheck value.
939: * If 'uncheckValue' is not set or set to NULL, the hidden field will not be rendered.
940: * @return string the generated check box
941: * @see clientChange
942: * @see inputField
943: */
944: public static function checkBox($name,$checked=false,$htmlOptions=array())
945: {
946: if($checked)
947: $htmlOptions['checked']='checked';
948: else
949: unset($htmlOptions['checked']);
950: $value=isset($htmlOptions['value']) ? $htmlOptions['value'] : 1;
951: self::clientChange('click',$htmlOptions);
952:
953: if(array_key_exists('uncheckValue',$htmlOptions))
954: {
955: $uncheck=$htmlOptions['uncheckValue'];
956: unset($htmlOptions['uncheckValue']);
957: }
958: else
959: $uncheck=null;
960:
961: if($uncheck!==null)
962: {
963: // add a hidden field so that if the check box is not checked, it still submits a value
964: if(isset($htmlOptions['id']) && $htmlOptions['id']!==false)
965: $uncheckOptions=array('id'=>self::ID_PREFIX.$htmlOptions['id']);
966: else
967: $uncheckOptions=array('id'=>false);
968: $hidden=self::hiddenField($name,$uncheck,$uncheckOptions);
969: }
970: else
971: $hidden='';
972:
973: // add a hidden field so that if the check box is not checked, it still submits a value
974: return $hidden . self::inputField('checkbox',$name,$value,$htmlOptions);
975: }
976:
977: /**
978: * Generates a drop down list.
979: * @param string $name the input name
980: * @param string $select the selected value
981: * @param array $data data for generating the list options (value=>display).
982: * You may use {@link listData} to generate this data.
983: * Please refer to {@link listOptions} on how this data is used to generate the list options.
984: * Note, the values and labels will be automatically HTML-encoded by this method.
985: * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
986: * attributes are recognized. See {@link clientChange} and {@link tag} for more details.
987: * In addition, the following options are also supported specifically for dropdown list:
988: * <ul>
989: * <li>encode: boolean, specifies whether to encode the values. Defaults to true.</li>
990: * <li>prompt: string, specifies the prompt text shown as the first list option. Its value is empty. Note, the prompt text will NOT be HTML-encoded.</li>
991: * <li>empty: string, specifies the text corresponding to empty selection. Its value is empty.
992: * The 'empty' option can also be an array of value-label pairs.
993: * Each pair will be used to render a list option at the beginning. Note, the text label will NOT be HTML-encoded.</li>
994: * <li>options: array, specifies additional attributes for each OPTION tag.
995: * The array keys must be the option values, and the array values are the extra
996: * OPTION tag attributes in the name-value pairs. For example,
997: * <pre>
998: * array(
999: * 'value1'=>array('disabled'=>true,'label'=>'value 1'),
1000: * 'value2'=>array('label'=>'value 2'),
1001: * );
1002: * </pre>
1003: * </li>
1004: * </ul>
1005: * Since 1.1.13, a special option named 'unselectValue' is available. It can be used to set the value
1006: * that will be returned when no option is selected in multiple mode. When set, a hidden field is
1007: * rendered so that if no option is selected in multiple mode, we can still obtain the posted
1008: * unselect value. If 'unselectValue' is not set or set to NULL, the hidden field will not be rendered.
1009: * @return string the generated drop down list
1010: * @see clientChange
1011: * @see inputField
1012: * @see listData
1013: */
1014: public static function dropDownList($name,$select,$data,$htmlOptions=array())
1015: {
1016: $htmlOptions['name']=$name;
1017:
1018: if(!isset($htmlOptions['id']))
1019: $htmlOptions['id']=self::getIdByName($name);
1020: elseif($htmlOptions['id']===false)
1021: unset($htmlOptions['id']);
1022:
1023: self::clientChange('change',$htmlOptions);
1024: $options="\n".self::listOptions($select,$data,$htmlOptions);
1025: $hidden='';
1026:
1027: if(!empty($htmlOptions['multiple']))
1028: {
1029: if(substr($htmlOptions['name'],-2)!=='[]')
1030: $htmlOptions['name'].='[]';
1031:
1032: if(isset($htmlOptions['unselectValue']))
1033: {
1034: $hiddenOptions=isset($htmlOptions['id']) ? array('id'=>self::ID_PREFIX.$htmlOptions['id']) : array('id'=>false);
1035: $hidden=self::hiddenField(substr($htmlOptions['name'],0,-2),$htmlOptions['unselectValue'],$hiddenOptions);
1036: unset($htmlOptions['unselectValue']);
1037: }
1038: }
1039: // add a hidden field so that if the option is not selected, it still submits a value
1040: return $hidden . self::tag('select',$htmlOptions,$options);
1041: }
1042:
1043: /**
1044: * Generates a list box.
1045: * @param string $name the input name
1046: * @param mixed $select the selected value(s). This can be either a string for single selection or an array for multiple selections.
1047: * @param array $data data for generating the list options (value=>display)
1048: * You may use {@link listData} to generate this data.
1049: * Please refer to {@link listOptions} on how this data is used to generate the list options.
1050: * Note, the values and labels will be automatically HTML-encoded by this method.
1051: * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
1052: * attributes are also recognized. See {@link clientChange} and {@link tag} for more details.
1053: * In addition, the following options are also supported specifically for list box:
1054: * <ul>
1055: * <li>encode: boolean, specifies whether to encode the values. Defaults to true.</li>
1056: * <li>prompt: string, specifies the prompt text shown as the first list option. Its value is empty. Note, the prompt text will NOT be HTML-encoded.</li>
1057: * <li>empty: string, specifies the text corresponding to empty selection. Its value is empty.
1058: * The 'empty' option can also be an array of value-label pairs.
1059: * Each pair will be used to render a list option at the beginning. Note, the text label will NOT be HTML-encoded.</li>
1060: * <li>options: array, specifies additional attributes for each OPTION tag.
1061: * The array keys must be the option values, and the array values are the extra
1062: * OPTION tag attributes in the name-value pairs. For example,
1063: * <pre>
1064: * array(
1065: * 'value1'=>array('disabled'=>true,'label'=>'value 1'),
1066: * 'value2'=>array('label'=>'value 2'),
1067: * );
1068: * </pre>
1069: * </li>
1070: * </ul>
1071: * @return string the generated list box
1072: * @see clientChange
1073: * @see inputField
1074: * @see listData
1075: */
1076: public static function listBox($name,$select,$data,$htmlOptions=array())
1077: {
1078: if(!isset($htmlOptions['size']))
1079: $htmlOptions['size']=4;
1080: if(!empty($htmlOptions['multiple']))
1081: {
1082: if(substr($name,-2)!=='[]')
1083: $name.='[]';
1084: }
1085: return self::dropDownList($name,$select,$data,$htmlOptions);
1086: }
1087:
1088: /**
1089: * Generates a check box list.
1090: * A check box list allows multiple selection, like {@link listBox}.
1091: * As a result, the corresponding POST value is an array.
1092: * @param string $name name of the check box list. You can use this name to retrieve
1093: * the selected value(s) once the form is submitted.
1094: * @param mixed $select selection of the check boxes. This can be either a string
1095: * for single selection or an array for multiple selections.
1096: * @param array $data value-label pairs used to generate the check box list.
1097: * Note, the values will be automatically HTML-encoded, while the labels will not.
1098: * @param array $htmlOptions additional HTML options. The options will be applied to
1099: * each checkbox input. The following special options are recognized:
1100: * <ul>
1101: * <li>template: string, specifies how each checkbox is rendered. Defaults
1102: * to "{input} {label}", where "{input}" will be replaced by the generated
1103: * check box input tag while "{label}" be replaced by the corresponding check box label,
1104: * {beginLabel} will be replaced by <label> with labelOptions, {labelTitle} will be replaced
1105: * by the corresponding check box label title and {endLabel} will be replaced by </label></li>
1106: * <li>separator: string, specifies the string that separates the generated check boxes.</li>
1107: * <li>checkAll: string, specifies the label for the "check all" checkbox.
1108: * If this option is specified, a 'check all' checkbox will be displayed. Clicking on
1109: * this checkbox will cause all checkboxes checked or unchecked.</li>
1110: * <li>checkAllLast: boolean, specifies whether the 'check all' checkbox should be
1111: * displayed at the end of the checkbox list. If this option is not set (default)
1112: * or is false, the 'check all' checkbox will be displayed at the beginning of
1113: * the checkbox list.</li>
1114: * <li>labelOptions: array, specifies the additional HTML attributes to be rendered
1115: * for every label tag in the list.</li>
1116: * <li>container: string, specifies the checkboxes enclosing tag. Defaults to 'span'.
1117: * If the value is an empty string, no enclosing tag will be generated</li>
1118: * <li>baseID: string, specifies the base ID prefix to be used for checkboxes in the list.
1119: * This option is available since version 1.1.13.</li>
1120: * </ul>
1121: * @return string the generated check box list
1122: */
1123: public static function checkBoxList($name,$select,$data,$htmlOptions=array())
1124: {
1125: $template=isset($htmlOptions['template'])?$htmlOptions['template']:'{input} {label}';
1126: $separator=isset($htmlOptions['separator'])?$htmlOptions['separator']:self::tag('br');
1127: $container=isset($htmlOptions['container'])?$htmlOptions['container']:'span';
1128: unset($htmlOptions['template'],$htmlOptions['separator'],$htmlOptions['container']);
1129:
1130: if(substr($name,-2)!=='[]')
1131: $name.='[]';
1132:
1133: if(isset($htmlOptions['checkAll']))
1134: {
1135: $checkAllLabel=$htmlOptions['checkAll'];
1136: $checkAllLast=isset($htmlOptions['checkAllLast']) && $htmlOptions['checkAllLast'];
1137: }
1138: unset($htmlOptions['checkAll'],$htmlOptions['checkAllLast']);
1139:
1140: $labelOptions=isset($htmlOptions['labelOptions'])?$htmlOptions['labelOptions']:array();
1141: unset($htmlOptions['labelOptions']);
1142:
1143: $items=array();
1144: $baseID=isset($htmlOptions['baseID']) ? $htmlOptions['baseID'] : self::getIdByName($name);
1145: unset($htmlOptions['baseID']);
1146: $id=0;
1147: $checkAll=true;
1148:
1149: foreach($data as $value=>$labelTitle)
1150: {
1151: $checked=!is_array($select) && !strcmp($value,$select) || is_array($select) && in_array($value,$select);
1152: $checkAll=$checkAll && $checked;
1153: $htmlOptions['value']=$value;
1154: $htmlOptions['id']=$baseID.'_'.$id++;
1155: $option=self::checkBox($name,$checked,$htmlOptions);
1156: $beginLabel=self::openTag('label',$labelOptions);
1157: $label=self::label($labelTitle,$htmlOptions['id'],$labelOptions);
1158: $endLabel=self::closeTag('label');
1159: $items[]=strtr($template,array(
1160: '{input}'=>$option,
1161: '{beginLabel}'=>$beginLabel,
1162: '{label}'=>$label,
1163: '{labelTitle}'=>$labelTitle,
1164: '{endLabel}'=>$endLabel,
1165: ));
1166: }
1167:
1168: if(isset($checkAllLabel))
1169: {
1170: $htmlOptions['value']=1;
1171: $htmlOptions['id']=$id=$baseID.'_all';
1172: $option=self::checkBox($id,$checkAll,$htmlOptions);
1173: $beginLabel=self::openTag('label',$labelOptions);
1174: $label=self::label($checkAllLabel,$id,$labelOptions);
1175: $endLabel=self::closeTag('label');
1176: $item=strtr($template,array(
1177: '{input}'=>$option,
1178: '{beginLabel}'=>$beginLabel,
1179: '{label}'=>$label,
1180: '{labelTitle}'=>$checkAllLabel,
1181: '{endLabel}'=>$endLabel,
1182: ));
1183: if($checkAllLast)
1184: $items[]=$item;
1185: else
1186: array_unshift($items,$item);
1187: $name=strtr($name,array('['=>'\\[',']'=>'\\]'));
1188: $js=<<<EOD
1189: jQuery('#$id').click(function() {
1190: jQuery("input[name='$name']").prop('checked', this.checked);
1191: });
1192: jQuery("input[name='$name']").click(function() {
1193: jQuery('#$id').prop('checked', !jQuery("input[name='$name']:not(:checked)").length);
1194: });
1195: jQuery('#$id').prop('checked', !jQuery("input[name='$name']:not(:checked)").length);
1196: EOD;
1197: $cs=Yii::app()->getClientScript();
1198: $cs->registerCoreScript('jquery');
1199: $cs->registerScript($id,$js);
1200: }
1201:
1202: if(empty($container))
1203: return implode($separator,$items);
1204: else
1205: return self::tag($container,array('id'=>$baseID),implode($separator,$items));
1206: }
1207:
1208: /**
1209: * Generates a radio button list.
1210: * A radio button list is like a {@link checkBoxList check box list}, except that
1211: * it only allows single selection.
1212: * @param string $name name of the radio button list. You can use this name to retrieve
1213: * the selected value(s) once the form is submitted.
1214: * @param string $select selection of the radio buttons.
1215: * @param array $data value-label pairs used to generate the radio button list.
1216: * Note, the values will be automatically HTML-encoded, while the labels will not.
1217: * @param array $htmlOptions additional HTML options. The options will be applied to
1218: * each radio button input. The following special options are recognized:
1219: * <ul>
1220: * <li>template: string, specifies how each radio button is rendered. Defaults
1221: * to "{input} {label}", where "{input}" will be replaced by the generated
1222: * radio button input tag while "{label}" will be replaced by the corresponding radio button label,
1223: * {beginLabel} will be replaced by <label> with labelOptions, {labelTitle} will be replaced
1224: * by the corresponding radio button label title and {endLabel} will be replaced by </label></li>
1225: * <li>separator: string, specifies the string that separates the generated radio buttons. Defaults to new line (<br/>).</li>
1226: * <li>labelOptions: array, specifies the additional HTML attributes to be rendered
1227: * for every label tag in the list.</li>
1228: * <li>container: string, specifies the radio buttons enclosing tag. Defaults to 'span'.
1229: * If the value is an empty string, no enclosing tag will be generated</li>
1230: * <li>baseID: string, specifies the base ID prefix to be used for radio buttons in the list.
1231: * This option is available since version 1.1.13.</li>
1232: * <li>empty: string, specifies the text corresponding to empty selection. Its value is empty.
1233: * The 'empty' option can also be an array of value-label pairs.
1234: * Each pair will be used to render a radio button at the beginning. Note, the text label will NOT be HTML-encoded.
1235: * This option is available since version 1.1.14.</li>
1236: * </ul>
1237: * @return string the generated radio button list
1238: */
1239: public static function radioButtonList($name,$select,$data,$htmlOptions=array())
1240: {
1241: $template=isset($htmlOptions['template'])?$htmlOptions['template']:'{input} {label}';
1242: $separator=isset($htmlOptions['separator'])?$htmlOptions['separator']:self::tag('br');
1243: $container=isset($htmlOptions['container'])?$htmlOptions['container']:'span';
1244: unset($htmlOptions['template'],$htmlOptions['separator'],$htmlOptions['container']);
1245:
1246: $labelOptions=isset($htmlOptions['labelOptions'])?$htmlOptions['labelOptions']:array();
1247: unset($htmlOptions['labelOptions']);
1248:
1249: if(isset($htmlOptions['empty']))
1250: {
1251: if(!is_array($htmlOptions['empty']))
1252: $htmlOptions['empty']=array(''=>$htmlOptions['empty']);
1253: $data=CMap::mergeArray($htmlOptions['empty'],$data);
1254: unset($htmlOptions['empty']);
1255: }
1256:
1257: $items=array();
1258: $baseID=isset($htmlOptions['baseID']) ? $htmlOptions['baseID'] : self::getIdByName($name);
1259: unset($htmlOptions['baseID']);
1260: $id=0;
1261: foreach($data as $value=>$labelTitle)
1262: {
1263: $checked=!strcmp($value,$select);
1264: $htmlOptions['value']=$value;
1265: $htmlOptions['id']=$baseID.'_'.$id++;
1266: $option=self::radioButton($name,$checked,$htmlOptions);
1267: $beginLabel=self::openTag('label',$labelOptions);
1268: $label=self::label($labelTitle,$htmlOptions['id'],$labelOptions);
1269: $endLabel=self::closeTag('label');
1270: $items[]=strtr($template,array(
1271: '{input}'=>$option,
1272: '{beginLabel}'=>$beginLabel,
1273: '{label}'=>$label,
1274: '{labelTitle}'=>$labelTitle,
1275: '{endLabel}'=>$endLabel,
1276: ));
1277: }
1278: if(empty($container))
1279: return implode($separator,$items);
1280: else
1281: return self::tag($container,array('id'=>$baseID),implode($separator,$items));
1282: }
1283:
1284: /**
1285: * Generates a link that can initiate AJAX requests.
1286: * @param string $text the link body (it will NOT be HTML-encoded.)
1287: * @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.
1288: * @param array $ajaxOptions AJAX options (see {@link ajax})
1289: * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
1290: * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
1291: * @return string the generated link
1292: * @see normalizeUrl
1293: * @see ajax
1294: */
1295: public static function ajaxLink($text,$url,$ajaxOptions=array(),$htmlOptions=array())
1296: {
1297: if(!isset($htmlOptions['href']))
1298: $htmlOptions['href']='#';
1299: $ajaxOptions['url']=$url;
1300: $htmlOptions['ajax']=$ajaxOptions;
1301: self::clientChange('click',$htmlOptions);
1302: return self::tag('a',$htmlOptions,$text);
1303: }
1304:
1305: /**
1306: * Generates a push button that can initiate AJAX requests.
1307: * @param string $label the button label
1308: * @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.
1309: * @param array $ajaxOptions AJAX options (see {@link ajax})
1310: * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
1311: * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
1312: * @return string the generated button
1313: */
1314: public static function ajaxButton($label,$url,$ajaxOptions=array(),$htmlOptions=array())
1315: {
1316: $ajaxOptions['url']=$url;
1317: $htmlOptions['ajax']=$ajaxOptions;
1318: return self::button($label,$htmlOptions);
1319: }
1320:
1321: /**
1322: * Generates a push button that can submit the current form in POST method.
1323: * @param string $label the button label
1324: * @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.
1325: * @param array $ajaxOptions AJAX options (see {@link ajax})
1326: * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
1327: * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
1328: * @return string the generated button
1329: */
1330: public static function ajaxSubmitButton($label,$url,$ajaxOptions=array(),$htmlOptions=array())
1331: {
1332: $ajaxOptions['type']='POST';
1333: $htmlOptions['type']='submit';
1334: return self::ajaxButton($label,$url,$ajaxOptions,$htmlOptions);
1335: }
1336:
1337: /**
1338: * Generates the JavaScript that initiates an AJAX request.
1339: * @param array $options AJAX options. The valid options are used in the form of jQuery.ajax([settings])
1340: * as specified in the jQuery AJAX documentation.
1341: * The following special options are added for convenience:
1342: * <ul>
1343: * <li>update: string, specifies the selector whose HTML content should be replaced
1344: * by the AJAX request result.</li>
1345: * <li>replace: string, specifies the selector whose target should be replaced
1346: * by the AJAX request result.</li>
1347: * </ul>
1348: * Note, if you specify the 'success' option, the above options will be ignored.
1349: * @return string the generated JavaScript
1350: * @see http://api.jquery.com/jQuery.ajax/#jQuery-ajax-settings
1351: */
1352: public static function ajax($options)
1353: {
1354: Yii::app()->getClientScript()->registerCoreScript('jquery');
1355: if(!isset($options['url']))
1356: $options['url']=new CJavaScriptExpression('location.href');
1357: else
1358: $options['url']=self::normalizeUrl($options['url']);
1359: if(!isset($options['cache']))
1360: $options['cache']=false;
1361: if(!isset($options['data']) && isset($options['type']))
1362: $options['data']=new CJavaScriptExpression('jQuery(this).parents("form").serialize()');
1363: foreach(array('beforeSend','complete','error','success') as $name)
1364: {
1365: if(isset($options[$name]) && !($options[$name] instanceof CJavaScriptExpression))
1366: $options[$name]=new CJavaScriptExpression($options[$name]);
1367: }
1368: if(isset($options['update']))
1369: {
1370: if(!isset($options['success']))
1371: $options['success']=new CJavaScriptExpression('function(html){jQuery("'.$options['update'].'").html(html)}');
1372: unset($options['update']);
1373: }
1374: if(isset($options['replace']))
1375: {
1376: if(!isset($options['success']))
1377: $options['success']=new CJavaScriptExpression('function(html){jQuery("'.$options['replace'].'").replaceWith(html)}');
1378: unset($options['replace']);
1379: }
1380: return 'jQuery.ajax('.CJavaScript::encode($options).');';
1381: }
1382:
1383: /**
1384: * Generates the URL for the published assets.
1385: * @param string $path the path of the asset to be published
1386: * @param boolean $hashByName whether the published directory should be named as the hashed basename.
1387: * If false, the name will be the hashed dirname of the path being published.
1388: * Defaults to false. Set true if the path being published is shared among
1389: * different extensions.
1390: * @return string the asset URL
1391: */
1392: public static function asset($path,$hashByName=false)
1393: {
1394: return Yii::app()->getAssetManager()->publish($path,$hashByName);
1395: }
1396:
1397: /**
1398: * Normalizes the input parameter to be a valid URL.
1399: *
1400: * If the input parameter is an empty string, the currently requested URL will be returned.
1401: *
1402: * If the input parameter is a non-empty string, it is treated as a valid URL and will
1403: * be returned without any change.
1404: *
1405: * If the input parameter is an array, it is treated as a controller route and a list of
1406: * GET parameters, and the {@link CController::createUrl} method will be invoked to
1407: * create a URL. In this case, the first array element refers to the controller route,
1408: * and the rest key-value pairs refer to the additional GET parameters for the URL.
1409: * For example, <code>array('post/list', 'page'=>3)</code> may be used to generate the URL
1410: * <code>/index.php?r=post/list&page=3</code>.
1411: *
1412: * @param mixed $url the parameter to be used to generate a valid URL
1413: * @return string the normalized URL
1414: */
1415: public static function normalizeUrl($url)
1416: {
1417: if(is_array($url))
1418: {
1419: if(isset($url[0]))
1420: {
1421: if(($c=Yii::app()->getController())!==null)
1422: $url=$c->createUrl($url[0],array_splice($url,1));
1423: else
1424: $url=Yii::app()->createUrl($url[0],array_splice($url,1));
1425: }
1426: else
1427: $url='';
1428: }
1429: return $url==='' ? Yii::app()->getRequest()->getUrl() : $url;
1430: }
1431:
1432: /**
1433: * Generates an input HTML tag.
1434: * This method generates an input HTML tag based on the given input name and value.
1435: * @param string $type the input type (e.g. 'text', 'radio')
1436: * @param string $name the input name
1437: * @param string $value the input value
1438: * @param array $htmlOptions additional HTML attributes for the HTML tag (see {@link tag}).
1439: * @return string the generated input tag
1440: */
1441: protected static function inputField($type,$name,$value,$htmlOptions)
1442: {
1443: $htmlOptions['type']=$type;
1444: $htmlOptions['value']=$value;
1445: $htmlOptions['name']=$name;
1446: if(!isset($htmlOptions['id']))
1447: $htmlOptions['id']=self::getIdByName($name);
1448: elseif($htmlOptions['id']===false)
1449: unset($htmlOptions['id']);
1450: return self::tag('input',$htmlOptions);
1451: }
1452:
1453: /**
1454: * Generates a label tag for a model attribute.
1455: * The label text is the attribute label and the label is associated with
1456: * the input for the attribute (see {@link CModel::getAttributeLabel}.
1457: * If the attribute has input error, the label's CSS class will be appended with {@link errorCss}.
1458: * @param CModel $model the data model
1459: * @param string $attribute the attribute
1460: * @param array $htmlOptions additional HTML attributes. The following special options are recognized:
1461: * <ul>
1462: * <li>required: if this is set and is true, the label will be styled
1463: * with CSS class 'required' (customizable with CHtml::$requiredCss),
1464: * and be decorated with {@link CHtml::beforeRequiredLabel} and
1465: * {@link CHtml::afterRequiredLabel}.</li>
1466: * <li>label: this specifies the label to be displayed. If this is not set,
1467: * {@link CModel::getAttributeLabel} will be called to get the label for display.
1468: * If the label is specified as false, no label will be rendered.</li>
1469: * </ul>
1470: * @return string the generated label tag
1471: */
1472: public static function activeLabel($model,$attribute,$htmlOptions=array())
1473: {
1474: $inputName=self::resolveName($model,$attribute);
1475: if(isset($htmlOptions['for']))
1476: {
1477: $for=$htmlOptions['for'];
1478: unset($htmlOptions['for']);
1479: }
1480: else
1481: $for=self::getIdByName($inputName);
1482: if(isset($htmlOptions['label']))
1483: {
1484: if(($label=$htmlOptions['label'])===false)
1485: return '';
1486: unset($htmlOptions['label']);
1487: }
1488: else
1489: $label=$model->getAttributeLabel($attribute);
1490: if($model->hasErrors($attribute))
1491: self::addErrorCss($htmlOptions);
1492: return self::label($label,$for,$htmlOptions);
1493: }
1494:
1495: /**
1496: * Generates a label tag for a model attribute.
1497: * This is an enhanced version of {@link activeLabel}. It will render additional
1498: * CSS class and mark when the attribute is required.
1499: * In particular, it calls {@link CModel::isAttributeRequired} to determine
1500: * if the attribute is required.
1501: * If so, it will add a CSS class {@link CHtml::requiredCss} to the label,
1502: * and decorate the label with {@link CHtml::beforeRequiredLabel} and
1503: * {@link CHtml::afterRequiredLabel}.
1504: * @param CModel $model the data model
1505: * @param string $attribute the attribute
1506: * @param array $htmlOptions additional HTML attributes.
1507: * @return string the generated label tag
1508: */
1509: public static function activeLabelEx($model,$attribute,$htmlOptions=array())
1510: {
1511: $realAttribute=$attribute;
1512: self::resolveName($model,$attribute); // strip off square brackets if any
1513: $htmlOptions['required']=$model->isAttributeRequired($attribute);
1514: return self::activeLabel($model,$realAttribute,$htmlOptions);
1515: }
1516:
1517: /**
1518: * Generates a text field input for a model attribute.
1519: * If the attribute has input error, the input field's CSS class will
1520: * be appended with {@link errorCss}.
1521: * @param CModel $model the data model
1522: * @param string $attribute the attribute
1523: * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
1524: * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
1525: * @return string the generated input field
1526: * @see clientChange
1527: * @see activeInputField
1528: */
1529: public static function activeTextField($model,$attribute,$htmlOptions=array())
1530: {
1531: self::resolveNameID($model,$attribute,$htmlOptions);
1532: self::clientChange('change',$htmlOptions);
1533: return self::activeInputField('text',$model,$attribute,$htmlOptions);
1534: }
1535:
1536: /**
1537: * Generates a search field input for a model attribute.
1538: * If the attribute has input error, the input field's CSS class will
1539: * be appended with {@link errorCss}.
1540: * @param CModel $model the data model
1541: * @param string $attribute the attribute
1542: * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
1543: * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
1544: * @return string the generated input field
1545: * @see clientChange
1546: * @see activeInputField
1547: * @since 1.1.14
1548: */
1549: public static function activeSearchField($model,$attribute,$htmlOptions=array())
1550: {
1551: self::resolveNameID($model,$attribute,$htmlOptions);
1552: self::clientChange('change',$htmlOptions);
1553: return self::activeInputField('search',$model,$attribute,$htmlOptions);
1554: }
1555:
1556: /**
1557: * Generates a url field input for a model attribute.
1558: * If the attribute has input error, the input field's CSS class will
1559: * be appended with {@link errorCss}.
1560: * @param CModel $model the data model
1561: * @param string $attribute the attribute
1562: * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
1563: * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
1564: * @return string the generated input field
1565: * @see clientChange
1566: * @see activeInputField
1567: * @since 1.1.11
1568: */
1569: public static function activeUrlField($model,$attribute,$htmlOptions=array())
1570: {
1571: self::resolveNameID($model,$attribute,$htmlOptions);
1572: self::clientChange('change',$htmlOptions);
1573: return self::activeInputField('url',$model,$attribute,$htmlOptions);
1574: }
1575:
1576: /**
1577: * Generates an email field input for a model attribute.
1578: * If the attribute has input error, the input field's CSS class will
1579: * be appended with {@link errorCss}.
1580: * @param CModel $model the data model
1581: * @param string $attribute the attribute
1582: * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
1583: * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
1584: * @return string the generated input field
1585: * @see clientChange
1586: * @see activeInputField
1587: * @since 1.1.11
1588: */
1589: public static function activeEmailField($model,$attribute,$htmlOptions=array())
1590: {
1591: self::resolveNameID($model,$attribute,$htmlOptions);
1592: self::clientChange('change',$htmlOptions);
1593: return self::activeInputField('email',$model,$attribute,$htmlOptions);
1594: }
1595:
1596: /**
1597: * Generates a number field input for a model attribute.
1598: * If the attribute has input error, the input field's CSS class will
1599: * be appended with {@link errorCss}.
1600: * @param CModel $model the data model
1601: * @param string $attribute the attribute
1602: * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
1603: * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
1604: * @return string the generated input field
1605: * @see clientChange
1606: * @see activeInputField
1607: * @since 1.1.11
1608: */
1609: public static function activeNumberField($model,$attribute,$htmlOptions=array())
1610: {
1611: self::resolveNameID($model,$attribute,$htmlOptions);
1612: self::clientChange('change',$htmlOptions);
1613: return self::activeInputField('number',$model,$attribute,$htmlOptions);
1614: }
1615:
1616: /**
1617: * Generates a range field input for a model attribute.
1618: * If the attribute has input error, the input field's CSS class will
1619: * be appended with {@link errorCss}.
1620: * @param CModel $model the data model
1621: * @param string $attribute the attribute
1622: * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
1623: * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
1624: * @return string the generated input field
1625: * @see clientChange
1626: * @see activeInputField
1627: * @since 1.1.11
1628: */
1629: public static function activeRangeField($model,$attribute,$htmlOptions=array())
1630: {
1631: self::resolveNameID($model,$attribute,$htmlOptions);
1632: self::clientChange('change',$htmlOptions);
1633: return self::activeInputField('range',$model,$attribute,$htmlOptions);
1634: }
1635:
1636: /**
1637: * Generates a date field input for a model attribute.
1638: * If the attribute has input error, the input field's CSS class will
1639: * be appended with {@link errorCss}.
1640: * @param CModel $model the data model
1641: * @param string $attribute the attribute
1642: * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
1643: * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
1644: * @return string the generated input field
1645: * @see clientChange
1646: * @see activeInputField
1647: * @since 1.1.11
1648: */
1649: public static function activeDateField($model,$attribute,$htmlOptions=array())
1650: {
1651: self::resolveNameID($model,$attribute,$htmlOptions);
1652: self::clientChange('change',$htmlOptions);
1653: return self::activeInputField('date',$model,$attribute,$htmlOptions);
1654: }
1655:
1656: /**
1657: * Generates a time field input for a model attribute.
1658: * If the attribute has input error, the input field's CSS class will
1659: * be appended with {@link errorCss}.
1660: * @param CModel $model the data model
1661: * @param string $attribute the attribute
1662: * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
1663: * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
1664: * @return string the generated input field
1665: * @see clientChange
1666: * @see activeInputField
1667: * @since 1.1.14
1668: */
1669: public static function activeTimeField($model,$attribute,$htmlOptions=array())
1670: {
1671: self::resolveNameID($model,$attribute,$htmlOptions);
1672: self::clientChange('change',$htmlOptions);
1673: return self::activeInputField('time',$model,$attribute,$htmlOptions);
1674: }
1675:
1676: /**
1677: * Generates a datetime field input for a model attribute.
1678: * If the attribute has input error, the input field's CSS class will
1679: * be appended with {@link errorCss}.
1680: * @param CModel $model the data model
1681: * @param string $attribute the attribute
1682: * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
1683: * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
1684: * @return string the generated input field
1685: * @see clientChange
1686: * @see activeInputField
1687: * @since 1.1.16
1688: */
1689: public static function activeDateTimeField($model,$attribute,$htmlOptions=array())
1690: {
1691: self::resolveNameID($model,$attribute,$htmlOptions);
1692: self::clientChange('change',$htmlOptions);
1693: return self::activeInputField('datetime',$model,$attribute,$htmlOptions);
1694: }
1695:
1696: /**
1697: * Generates a datetime-local field input for a model attribute.
1698: * If the attribute has input error, the input field's CSS class will
1699: * be appended with {@link errorCss}.
1700: * @param CModel $model the data model
1701: * @param string $attribute the attribute
1702: * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
1703: * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
1704: * @return string the generated input field
1705: * @see clientChange
1706: * @see activeInputField
1707: * @since 1.1.16
1708: */
1709: public static function activeDateTimeLocalField($model,$attribute,$htmlOptions=array())
1710: {
1711: self::resolveNameID($model,$attribute,$htmlOptions);
1712: self::clientChange('change',$htmlOptions);
1713: return self::activeInputField('datetime-local',$model,$attribute,$htmlOptions);
1714: }
1715:
1716: /**
1717: * Generates a week field input for a model attribute.
1718: * If the attribute has input error, the input field's CSS class will
1719: * be appended with {@link errorCss}.
1720: * @param CModel $model the data model
1721: * @param string $attribute the attribute
1722: * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
1723: * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
1724: * @return string the generated input field
1725: * @see clientChange
1726: * @see activeInputField
1727: * @since 1.1.16
1728: */
1729: public static function activeWeekField($model,$attribute,$htmlOptions=array())
1730: {
1731: self::resolveNameID($model,$attribute,$htmlOptions);
1732: self::clientChange('change',$htmlOptions);
1733: return self::activeInputField('week',$model,$attribute,$htmlOptions);
1734: }
1735:
1736: /**
1737: * Generates a color picker field input for a model attribute.
1738: * If the attribute has input error, the input field's CSS class will
1739: * be appended with {@link errorCss}.
1740: * @param CModel $model the data model
1741: * @param string $attribute the attribute
1742: * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
1743: * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
1744: * @return string the generated input field
1745: * @see clientChange
1746: * @see activeInputField
1747: * @since 1.1.16
1748: */
1749: public static function activeColorField($model,$attribute,$htmlOptions=array())
1750: {
1751: self::resolveNameID($model,$attribute,$htmlOptions);
1752: self::clientChange('change',$htmlOptions);
1753: return self::activeInputField('color',$model,$attribute,$htmlOptions);
1754: }
1755:
1756: /**
1757: * Generates a telephone field input for a model attribute.
1758: * If the attribute has input error, the input field's CSS class will
1759: * be appended with {@link errorCss}.
1760: * @param CModel $model the data model
1761: * @param string $attribute the attribute
1762: * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
1763: * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
1764: * @return string the generated input field
1765: * @see clientChange
1766: * @see activeInputField
1767: * @since 1.1.14
1768: */
1769: public static function activeTelField($model,$attribute,$htmlOptions=array())
1770: {
1771: self::resolveNameID($model,$attribute,$htmlOptions);
1772: self::clientChange('change',$htmlOptions);
1773: return self::activeInputField('tel',$model,$attribute,$htmlOptions);
1774: }
1775:
1776:
1777: /**
1778: * Generates a hidden input for a model attribute.
1779: * @param CModel $model the data model
1780: * @param string $attribute the attribute
1781: * @param array $htmlOptions additional HTML attributes.
1782: * @return string the generated input field
1783: * @see activeInputField
1784: */
1785: public static function activeHiddenField($model,$attribute,$htmlOptions=array())
1786: {
1787: self::resolveNameID($model,$attribute,$htmlOptions);
1788: return self::activeInputField('hidden',$model,$attribute,$htmlOptions);
1789: }
1790:
1791: /**
1792: * Generates a password field input for a model attribute.
1793: * If the attribute has input error, the input field's CSS class will
1794: * be appended with {@link errorCss}.
1795: * @param CModel $model the data model
1796: * @param string $attribute the attribute
1797: * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
1798: * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
1799: * @return string the generated input field
1800: * @see clientChange
1801: * @see activeInputField
1802: */
1803: public static function activePasswordField($model,$attribute,$htmlOptions=array())
1804: {
1805: self::resolveNameID($model,$attribute,$htmlOptions);
1806: self::clientChange('change',$htmlOptions);
1807: return self::activeInputField('password',$model,$attribute,$htmlOptions);
1808: }
1809:
1810: /**
1811: * Generates a text area input for a model attribute.
1812: * If the attribute has input error, the input field's CSS class will
1813: * be appended with {@link errorCss}.
1814: * @param CModel $model the data model
1815: * @param string $attribute the attribute
1816: * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
1817: * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
1818: * @return string the generated text area
1819: * @see clientChange
1820: */
1821: public static function activeTextArea($model,$attribute,$htmlOptions=array())
1822: {
1823: self::resolveNameID($model,$attribute,$htmlOptions);
1824: self::clientChange('change',$htmlOptions);
1825: if($model->hasErrors($attribute))
1826: self::addErrorCss($htmlOptions);
1827: if(isset($htmlOptions['value']))
1828: {
1829: $text=$htmlOptions['value'];
1830: unset($htmlOptions['value']);
1831: }
1832: else
1833: $text=self::resolveValue($model,$attribute);
1834: return self::tag('textarea',$htmlOptions,isset($htmlOptions['encode']) && !$htmlOptions['encode'] ? $text : self::encode($text));
1835: }
1836:
1837: /**
1838: * Generates a file input for a model attribute.
1839: * Note, you have to set the enclosing form's 'enctype' attribute to be 'multipart/form-data'.
1840: * After the form is submitted, the uploaded file information can be obtained via $_FILES (see
1841: * PHP documentation).
1842: * @param CModel $model the data model
1843: * @param string $attribute the attribute
1844: * @param array $htmlOptions additional HTML attributes (see {@link tag}).
1845: * @return string the generated input field
1846: * @see activeInputField
1847: */
1848: public static function activeFileField($model,$attribute,$htmlOptions=array())
1849: {
1850: self::resolveNameID($model,$attribute,$htmlOptions);
1851: // add a hidden field so that if a model only has a file field, we can
1852: // still use isset($_POST[$modelClass]) to detect if the input is submitted
1853: $hiddenOptions=isset($htmlOptions['id']) ? array('id'=>self::ID_PREFIX.$htmlOptions['id']) : array('id'=>false);
1854: return self::hiddenField($htmlOptions['name'],'',$hiddenOptions)
1855: . self::activeInputField('file',$model,$attribute,$htmlOptions);
1856: }
1857:
1858: /**
1859: * Generates a radio button for a model attribute.
1860: * If the attribute has input error, the input field's CSS class will
1861: * be appended with {@link errorCss}.
1862: * @param CModel $model the data model
1863: * @param string $attribute the attribute
1864: * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
1865: * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
1866: * A special option named 'uncheckValue' is available that can be used to specify
1867: * the value returned when the radio button is not checked. By default, this value is '0'.
1868: * Internally, a hidden field is rendered so that when the radio button is not checked,
1869: * we can still obtain the posted uncheck value.
1870: * If 'uncheckValue' is set as NULL, the hidden field will not be rendered.
1871: * @return string the generated radio button
1872: * @see clientChange
1873: * @see activeInputField
1874: */
1875: public static function activeRadioButton($model,$attribute,$htmlOptions=array())
1876: {
1877: self::resolveNameID($model,$attribute,$htmlOptions);
1878: if(!isset($htmlOptions['value']))
1879: $htmlOptions['value']=1;
1880: if(!isset($htmlOptions['checked']) && self::resolveValue($model,$attribute)==$htmlOptions['value'])
1881: $htmlOptions['checked']='checked';
1882: self::clientChange('click',$htmlOptions);
1883:
1884: if(array_key_exists('uncheckValue',$htmlOptions))
1885: {
1886: $uncheck=$htmlOptions['uncheckValue'];
1887: unset($htmlOptions['uncheckValue']);
1888: }
1889: else
1890: $uncheck='0';
1891:
1892: $hiddenOptions=isset($htmlOptions['id']) ? array('id'=>self::ID_PREFIX.$htmlOptions['id']) : array('id'=>false);
1893: $hidden=$uncheck!==null ? self::hiddenField($htmlOptions['name'],$uncheck,$hiddenOptions) : '';
1894:
1895: // add a hidden field so that if the radio button is not selected, it still submits a value
1896: return $hidden . self::activeInputField('radio',$model,$attribute,$htmlOptions);
1897: }
1898:
1899: /**
1900: * Generates a check box for a model attribute.
1901: * The attribute is assumed to take either true or false value.
1902: * If the attribute has input error, the input field's CSS class will
1903: * be appended with {@link errorCss}.
1904: * @param CModel $model the data model
1905: * @param string $attribute the attribute
1906: * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
1907: * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
1908: * A special option named 'uncheckValue' is available that can be used to specify
1909: * the value returned when the checkbox is not checked. By default, this value is '0'.
1910: * Internally, a hidden field is rendered so that when the checkbox is not checked,
1911: * we can still obtain the posted uncheck value.
1912: * If 'uncheckValue' is set as NULL, the hidden field will not be rendered.
1913: * @return string the generated check box
1914: * @see clientChange
1915: * @see activeInputField
1916: */
1917: public static function activeCheckBox($model,$attribute,$htmlOptions=array())
1918: {
1919: self::resolveNameID($model,$attribute,$htmlOptions);
1920: if(!isset($htmlOptions['value']))
1921: $htmlOptions['value']=1;
1922: if(!isset($htmlOptions['checked']) && self::resolveValue($model,$attribute)==$htmlOptions['value'])
1923: $htmlOptions['checked']='checked';
1924: self::clientChange('click',$htmlOptions);
1925:
1926: if(array_key_exists('uncheckValue',$htmlOptions))
1927: {
1928: $uncheck=$htmlOptions['uncheckValue'];
1929: unset($htmlOptions['uncheckValue']);
1930: }
1931: else
1932: $uncheck='0';
1933:
1934: $hiddenOptions=isset($htmlOptions['id']) ? array('id'=>self::ID_PREFIX.$htmlOptions['id']) : array('id'=>false);
1935: $hidden=$uncheck!==null ? self::hiddenField($htmlOptions['name'],$uncheck,$hiddenOptions) : '';
1936:
1937: return $hidden . self::activeInputField('checkbox',$model,$attribute,$htmlOptions);
1938: }
1939:
1940: /**
1941: * Generates a drop down list for a model attribute.
1942: * If the attribute has input error, the input field's CSS class will
1943: * be appended with {@link errorCss}.
1944: * @param CModel $model the data model
1945: * @param string $attribute the attribute
1946: * @param array $data data for generating the list options (value=>display)
1947: * You may use {@link listData} to generate this data.
1948: * Please refer to {@link listOptions} on how this data is used to generate the list options.
1949: * Note, the values and labels will be automatically HTML-encoded by this method.
1950: * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
1951: * attributes are recognized. See {@link clientChange} and {@link tag} for more details.
1952: * In addition, the following options are also supported:
1953: * <ul>
1954: * <li>encode: boolean, specifies whether to encode the values. Defaults to true.</li>
1955: * <li>prompt: string, specifies the prompt text shown as the first list option. Its value is empty. Note, the prompt text will NOT be HTML-encoded.</li>
1956: * <li>empty: string, specifies the text corresponding to empty selection. Its value is empty.
1957: * The 'empty' option can also be an array of value-label pairs.
1958: * Each pair will be used to render a list option at the beginning. Note, the text label will NOT be HTML-encoded.</li>
1959: * <li>options: array, specifies additional attributes for each OPTION tag.
1960: * The array keys must be the option values, and the array values are the extra
1961: * OPTION tag attributes in the name-value pairs. For example,
1962: * <pre>
1963: * array(
1964: * 'value1'=>array('disabled'=>true,'label'=>'value 1'),
1965: * 'value2'=>array('label'=>'value 2'),
1966: * );
1967: * </pre>
1968: * </li>
1969: * </ul>
1970: * Since 1.1.13, a special option named 'unselectValue' is available. It can be used to set the value
1971: * that will be returned when no option is selected in multiple mode. When set, a hidden field is
1972: * rendered so that if no option is selected in multiple mode, we can still obtain the posted
1973: * unselect value. If 'unselectValue' is not set or set to NULL, the hidden field will not be rendered.
1974: * @return string the generated drop down list
1975: * @see clientChange
1976: * @see listData
1977: */
1978: public static function activeDropDownList($model,$attribute,$data,$htmlOptions=array())
1979: {
1980: self::resolveNameID($model,$attribute,$htmlOptions);
1981: $selection=self::resolveValue($model,$attribute);
1982: $options="\n".self::listOptions($selection,$data,$htmlOptions);
1983: self::clientChange('change',$htmlOptions);
1984:
1985: if($model->hasErrors($attribute))
1986: self::addErrorCss($htmlOptions);
1987:
1988: $hidden='';
1989: if(!empty($htmlOptions['multiple']))
1990: {
1991: if(substr($htmlOptions['name'],-2)!=='[]')
1992: $htmlOptions['name'].='[]';
1993:
1994: if(isset($htmlOptions['unselectValue']))
1995: {
1996: $hiddenOptions=isset($htmlOptions['id']) ? array('id'=>self::ID_PREFIX.$htmlOptions['id']) : array('id'=>false);
1997: $hidden=self::hiddenField(substr($htmlOptions['name'],0,-2),$htmlOptions['unselectValue'],$hiddenOptions);
1998: unset($htmlOptions['unselectValue']);
1999: }
2000: }
2001: return $hidden . self::tag('select',$htmlOptions,$options);
2002: }
2003:
2004: /**
2005: * Generates a list box for a model attribute.
2006: * The model attribute value is used as the selection.
2007: * If the attribute has input error, the input field's CSS class will
2008: * be appended with {@link errorCss}.
2009: * @param CModel $model the data model
2010: * @param string $attribute the attribute
2011: * @param array $data data for generating the list options (value=>display)
2012: * You may use {@link listData} to generate this data.
2013: * Please refer to {@link listOptions} on how this data is used to generate the list options.
2014: * Note, the values and labels will be automatically HTML-encoded by this method.
2015: * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
2016: * attributes are recognized. See {@link clientChange} and {@link tag} for more details.
2017: * In addition, the following options are also supported:
2018: * <ul>
2019: * <li>encode: boolean, specifies whether to encode the values. Defaults to true.</li>
2020: * <li>prompt: string, specifies the prompt text shown as the first list option. Its value is empty. Note, the prompt text will NOT be HTML-encoded.</li>
2021: * <li>empty: string, specifies the text corresponding to empty selection. Its value is empty.
2022: * The 'empty' option can also be an array of value-label pairs.
2023: * Each pair will be used to render a list option at the beginning. Note, the text label will NOT be HTML-encoded.</li>
2024: * <li>options: array, specifies additional attributes for each OPTION tag.
2025: * The array keys must be the option values, and the array values are the extra
2026: * OPTION tag attributes in the name-value pairs. For example,
2027: * <pre>
2028: * array(
2029: * 'value1'=>array('disabled'=>true,'label'=>'value 1'),
2030: * 'value2'=>array('label'=>'value 2'),
2031: * );
2032: * </pre>
2033: * </li>
2034: * </ul>
2035: * @return string the generated list box
2036: * @see clientChange
2037: * @see listData
2038: */
2039: public static function activeListBox($model,$attribute,$data,$htmlOptions=array())
2040: {
2041: if(!isset($htmlOptions['size']))
2042: $htmlOptions['size']=4;
2043: return self::activeDropDownList($model,$attribute,$data,$htmlOptions);
2044: }
2045:
2046: /**
2047: * Generates a check box list for a model attribute.
2048: * The model attribute value is used as the selection.
2049: * If the attribute has input error, the input field's CSS class will
2050: * be appended with {@link errorCss}.
2051: * Note that a check box list allows multiple selection, like {@link listBox}.
2052: * As a result, the corresponding POST value is an array. In case no selection
2053: * is made, the corresponding POST value is an empty string.
2054: * @param CModel $model the data model
2055: * @param string $attribute the attribute
2056: * @param array $data value-label pairs used to generate the check box list.
2057: * Note, the values will be automatically HTML-encoded, while the labels will not.
2058: * @param array $htmlOptions additional HTML options. The options will be applied to
2059: * each checkbox input. The following special options are recognized:
2060: * <ul>
2061: * <li>template: string, specifies how each checkbox is rendered. Defaults
2062: * to "{input} {label}", where "{input}" will be replaced by the generated
2063: * check box input tag while "{label}" will be replaced by the corresponding check box label.</li>
2064: * <li>separator: string, specifies the string that separates the generated check boxes.</li>
2065: * <li>checkAll: string, specifies the label for the "check all" checkbox.
2066: * If this option is specified, a 'check all' checkbox will be displayed. Clicking on
2067: * this checkbox will cause all checkboxes checked or unchecked.</li>
2068: * <li>checkAllLast: boolean, specifies whether the 'check all' checkbox should be
2069: * displayed at the end of the checkbox list. If this option is not set (default)
2070: * or is false, the 'check all' checkbox will be displayed at the beginning of
2071: * the checkbox list.</li>
2072: * <li>encode: boolean, specifies whether to encode HTML-encode tag attributes and values. Defaults to true.</li>
2073: * <li>labelOptions: array, specifies the additional HTML attributes to be rendered
2074: * for every label tag in the list.</li>
2075: * <li>container: string, specifies the checkboxes enclosing tag. Defaults to 'span'.
2076: * If the value is an empty string, no enclosing tag will be generated</li>
2077: * <li>baseID: string, specifies the base ID prefix to be used for checkboxes in the list.
2078: * This option is available since version 1.1.13.</li>
2079: * </ul>
2080: * Since 1.1.7, a special option named 'uncheckValue' is available. It can be used to set the value
2081: * that will be returned when the checkbox is not checked. By default, this value is ''.
2082: * Internally, a hidden field is rendered so when the checkbox is not checked, we can still
2083: * obtain the value. If 'uncheckValue' is set to NULL, there will be no hidden field rendered.
2084: * @return string the generated check box list
2085: * @see checkBoxList
2086: */
2087: public static function activeCheckBoxList($model,$attribute,$data,$htmlOptions=array())
2088: {
2089: self::resolveNameID($model,$attribute,$htmlOptions);
2090: $selection=self::resolveValue($model,$attribute);
2091: if($model->hasErrors($attribute))
2092: self::addErrorCss($htmlOptions);
2093: $name=$htmlOptions['name'];
2094: unset($htmlOptions['name']);
2095:
2096: if(array_key_exists('uncheckValue',$htmlOptions))
2097: {
2098: $uncheck=$htmlOptions['uncheckValue'];
2099: unset($htmlOptions['uncheckValue']);
2100: }
2101: else
2102: $uncheck='';
2103:
2104: $hiddenOptions=isset($htmlOptions['id']) ? array('id'=>self::ID_PREFIX.$htmlOptions['id']) : array('id'=>false);
2105: $hidden=$uncheck!==null ? self::hiddenField($name,$uncheck,$hiddenOptions) : '';
2106:
2107: return $hidden . self::checkBoxList($name,$selection,$data,$htmlOptions);
2108: }
2109:
2110: /**
2111: * Generates a radio button list for a model attribute.
2112: * The model attribute value is used as the selection.
2113: * If the attribute has input error, the input field's CSS class will
2114: * be appended with {@link errorCss}.
2115: * @param CModel $model the data model
2116: * @param string $attribute the attribute
2117: * @param array $data value-label pairs used to generate the radio button list.
2118: * Note, the values will be automatically HTML-encoded, while the labels will not.
2119: * @param array $htmlOptions additional HTML options. The options will be applied to
2120: * each radio button input. The following special options are recognized:
2121: * <ul>
2122: * <li>template: string, specifies how each radio button is rendered. Defaults
2123: * to "{input} {label}", where "{input}" will be replaced by the generated
2124: * radio button input tag while "{label}" will be replaced by the corresponding radio button label,
2125: * {beginLabel} will be replaced by <label> with labelOptions, {labelTitle} will be replaced
2126: * by the corresponding radio button label title and {endLabel} will be replaced by </label></li>
2127: * <li>separator: string, specifies the string that separates the generated radio buttons. Defaults to new line (<br/>).</li>
2128: * <li>encode: boolean, specifies whether to encode HTML-encode tag attributes and values. Defaults to true.</li>
2129: * <li>labelOptions: array, specifies the additional HTML attributes to be rendered
2130: * for every label tag in the list.</li>
2131: * <li>container: string, specifies the radio buttons enclosing tag. Defaults to 'span'.
2132: * If the value is an empty string, no enclosing tag will be generated</li>
2133: * <li>baseID: string, specifies the base ID prefix to be used for radio buttons in the list.
2134: * This option is available since version 1.1.13.</li>
2135: * <li>empty: string, specifies the text corresponding to empty selection. Its value is empty.
2136: * The 'empty' option can also be an array of value-label pairs.
2137: * Each pair will be used to render a radio button at the beginning. Note, the text label will NOT be HTML-encoded.
2138: * This option is available since version 1.1.14.</li>
2139: * </ul>
2140: * Since version 1.1.7, a special option named 'uncheckValue' is available that can be used to specify the value
2141: * returned when the radio button is not checked. By default, this value is ''. Internally, a hidden field is
2142: * rendered so that when the radio button is not checked, we can still obtain the posted uncheck value.
2143: * If 'uncheckValue' is set as NULL, the hidden field will not be rendered.
2144: * @return string the generated radio button list
2145: * @see radioButtonList
2146: */
2147: public static function activeRadioButtonList($model,$attribute,$data,$htmlOptions=array())
2148: {
2149: self::resolveNameID($model,$attribute,$htmlOptions);
2150: $selection=self::resolveValue($model,$attribute);
2151: if($model->hasErrors($attribute))
2152: self::addErrorCss($htmlOptions);
2153: $name=$htmlOptions['name'];
2154: unset($htmlOptions['name']);
2155:
2156: if(array_key_exists('uncheckValue',$htmlOptions))
2157: {
2158: $uncheck=$htmlOptions['uncheckValue'];
2159: unset($htmlOptions['uncheckValue']);
2160: }
2161: else
2162: $uncheck='';
2163:
2164: $hiddenOptions=isset($htmlOptions['id']) ? array('id'=>self::ID_PREFIX.$htmlOptions['id']) : array('id'=>false);
2165: $hidden=$uncheck!==null ? self::hiddenField($name,$uncheck,$hiddenOptions) : '';
2166:
2167: return $hidden . self::radioButtonList($name,$selection,$data,$htmlOptions);
2168: }
2169:
2170: /**
2171: * Displays a summary of validation errors for one or several models.
2172: * @param mixed $model the models whose input errors are to be displayed. This can be either
2173: * a single model or an array of models.
2174: * @param string $header a piece of HTML code that appears in front of the errors
2175: * @param string $footer a piece of HTML code that appears at the end of the errors
2176: * @param array $htmlOptions additional HTML attributes to be rendered in the container div tag.
2177: * A special option named 'firstError' is recognized, which when set true, will
2178: * make the error summary to show only the first error message of each attribute.
2179: * If this is not set or is false, all error messages will be displayed.
2180: * This option has been available since version 1.1.3.
2181: * @return string the error summary. Empty if no errors are found.
2182: * @see CModel::getErrors
2183: * @see errorSummaryCss
2184: */
2185: public static function errorSummary($model,$header=null,$footer=null,$htmlOptions=array())
2186: {
2187: $content='';
2188: if(!is_array($model))
2189: $model=array($model);
2190: if(isset($htmlOptions['firstError']))
2191: {
2192: $firstError=$htmlOptions['firstError'];
2193: unset($htmlOptions['firstError']);
2194: }
2195: else
2196: $firstError=false;
2197: foreach($model as $m)
2198: {
2199: foreach($m->getErrors() as $errors)
2200: {
2201: foreach($errors as $error)
2202: {
2203: if($error!='')
2204: $content.="<li>$error</li>\n";
2205: if($firstError)
2206: break;
2207: }
2208: }
2209: }
2210: if($content!=='')
2211: {
2212: if($header===null)
2213: $header='<p>'.Yii::t('yii','Please fix the following input errors:').'</p>';
2214: if(!isset($htmlOptions['class']))
2215: $htmlOptions['class']=self::$errorSummaryCss;
2216: return self::tag('div',$htmlOptions,$header."\n<ul>\n$content</ul>".$footer);
2217: }
2218: else
2219: return '';
2220: }
2221:
2222: /**
2223: * Displays the first validation error for a model attribute.
2224: * @param CModel $model the data model
2225: * @param string $attribute the attribute name
2226: * @param array $htmlOptions additional HTML attributes to be rendered in the container tag.
2227: * @return string the error display. Empty if no errors are found.
2228: * @see CModel::getErrors
2229: * @see errorMessageCss
2230: * @see $errorContainerTag
2231: */
2232: public static function error($model,$attribute,$htmlOptions=array())
2233: {
2234: self::resolveName($model,$attribute); // turn [a][b]attr into attr
2235: $error=$model->getError($attribute);
2236: if($error!='')
2237: {
2238: if(!isset($htmlOptions['class']))
2239: $htmlOptions['class']=self::$errorMessageCss;
2240: return self::tag(self::$errorContainerTag,$htmlOptions,$error);
2241: }
2242: else
2243: return '';
2244: }
2245:
2246: /**
2247: * Generates the data suitable for list-based HTML elements.
2248: * The generated data can be used in {@link dropDownList}, {@link listBox}, {@link checkBoxList},
2249: * {@link radioButtonList}, and their active-versions (such as {@link activeDropDownList}).
2250: * Note, this method does not HTML-encode the generated data. You may call {@link encodeArray} to
2251: * encode it if needed.
2252: * Please refer to the {@link value} method on how to specify value field, text field and group field.
2253: * You can also pass anonymous functions as second, third and fourth arguments which calculates
2254: * text field value (PHP 5.3+ only) since 1.1.13. Your anonymous function should receive one argument,
2255: * which is the model, the current <option> tag is generated from.
2256: *
2257: * <pre>
2258: * CHtml::listData($posts,'id',function($post) {
2259: * return CHtml::encode($post->title);
2260: * });
2261: * </pre>
2262: *
2263: * @param array $models a list of model objects. This parameter
2264: * can also be an array of associative arrays (e.g. results of {@link CDbCommand::queryAll}).
2265: * @param mixed $valueField the attribute name or anonymous function (PHP 5.3+) for list option values
2266: * @param mixed $textField the attribute name or anonymous function (PHP 5.3+) for list option texts
2267: * @param mixed $groupField the attribute name or anonymous function (PHP 5.3+) for list option group names. If empty, no group will be generated.
2268: * @return array the list data that can be used in {@link dropDownList}, {@link listBox}, etc.
2269: */
2270: public static function listData($models,$valueField,$textField,$groupField='')
2271: {
2272: $listData=array();
2273: if($groupField==='')
2274: {
2275: foreach($models as $model)
2276: {
2277: $value=self::value($model,$valueField);
2278: $text=self::value($model,$textField);
2279: $listData[$value]=$text;
2280: }
2281: }
2282: else
2283: {
2284: foreach($models as $model)
2285: {
2286: $group=self::value($model,$groupField);
2287: $value=self::value($model,$valueField);
2288: $text=self::value($model,$textField);
2289: if($group===null)
2290: $listData[$value]=$text;
2291: else
2292: $listData[$group][$value]=$text;
2293: }
2294: }
2295: return $listData;
2296: }
2297:
2298: /**
2299: * Evaluates the value of the specified attribute for the given model.
2300: * The attribute name can be given in a dot syntax. For example, if the attribute
2301: * is "author.firstName", this method will return the value of "$model->author->firstName".
2302: * A default value (passed as the last parameter) will be returned if the attribute does
2303: * not exist or is broken in the middle (e.g. $model->author is null).
2304: * The model can be either an object or an array. If the latter, the attribute is treated
2305: * as a key of the array. For the example of "author.firstName", if would mean the array value
2306: * "$model['author']['firstName']".
2307: *
2308: * Anonymous function could also be used for attribute calculation since 1.1.13
2309: * ($attribute parameter; PHP 5.3+ only) as follows:
2310: * <pre>
2311: * $taskClosedSecondsAgo=CHtml::value($closedTask,function($model) {
2312: * return time()-$model->closed_at;
2313: * });
2314: * </pre>
2315: * Your anonymous function should receive one argument, which is the model, the current
2316: * value is calculated from. This feature could be used together with the {@link listData}.
2317: * Please refer to its documentation for more details.
2318: *
2319: * @param mixed $model the model. This can be either an object or an array.
2320: * @param mixed $attribute the attribute name (use dot to concatenate multiple attributes)
2321: * or anonymous function (PHP 5.3+). Remember that functions created by "create_function"
2322: * are not supported by this method. Also note that numeric value is meaningless when
2323: * first parameter is object typed.
2324: * @param mixed $defaultValue the default value to return when the attribute does not exist.
2325: * @return mixed the attribute value.
2326: */
2327: public static function value($model,$attribute,$defaultValue=null)
2328: {
2329: if(is_scalar($attribute) || $attribute===null)
2330: foreach(explode('.',$attribute) as $name)
2331: {
2332: if(is_object($model) && isset($model->$name))
2333: $model=$model->$name;
2334: elseif(is_array($model) && isset($model[$name]))
2335: $model=$model[$name];
2336: else
2337: return $defaultValue;
2338: }
2339: else
2340: return call_user_func($attribute,$model);
2341:
2342: return $model;
2343: }
2344:
2345: /**
2346: * Generates a valid HTML ID based on name.
2347: * @param string $name name from which to generate HTML ID
2348: * @return string the ID generated based on name.
2349: */
2350: public static function getIdByName($name)
2351: {
2352: return str_replace(array('[]','][','[',']',' '),array('','_','_','','_'),$name);
2353: }
2354:
2355: /**
2356: * Generates input field ID for a model attribute.
2357: * @param CModel $model the data model
2358: * @param string $attribute the attribute
2359: * @return string the generated input field ID
2360: */
2361: public static function activeId($model,$attribute)
2362: {
2363: return self::getIdByName(self::activeName($model,$attribute));
2364: }
2365:
2366: /**
2367: * Generates HTML name for given model.
2368: * @see CHtml::setModelNameConverter()
2369: * @param CModel|string $model the data model or the model class name
2370: * @return string the generated HTML name value
2371: * @since 1.1.14
2372: */
2373: public static function modelName($model)
2374: {
2375: if(is_callable(self::$_modelNameConverter))
2376: return call_user_func(self::$_modelNameConverter,$model);
2377:
2378: $className=is_object($model) ? get_class($model) : (string)$model;
2379: return trim(str_replace('\\','_',$className),'_');
2380: }
2381:
2382: /**
2383: * Set generator used in the {@link CHtml::modelName()} method. You can use the `null` value to restore default
2384: * generator.
2385: *
2386: * @param callback|null $converter the new generator, the model or class name will be passed to the this callback
2387: * and result must be a valid value for HTML name attribute.
2388: * @throws CException if $converter isn't a valid callback
2389: * @since 1.1.14
2390: */
2391: public static function setModelNameConverter($converter)
2392: {
2393: if(is_callable($converter))
2394: self::$_modelNameConverter=$converter;
2395: elseif($converter===null)
2396: self::$_modelNameConverter=null;
2397: else
2398: throw new CException(Yii::t('yii','The $converter argument must be a valid callback or null.'));
2399: }
2400:
2401: /**
2402: * Generates input field name for a model attribute.
2403: * Unlike {@link resolveName}, this method does NOT modify the attribute name.
2404: * @param CModel $model the data model
2405: * @param string $attribute the attribute
2406: * @return string the generated input field name
2407: */
2408: public static function activeName($model,$attribute)
2409: {
2410: $a=$attribute; // because the attribute name may be changed by resolveName
2411: return self::resolveName($model,$a);
2412: }
2413:
2414: /**
2415: * Generates an input HTML tag for a model attribute.
2416: * This method generates an input HTML tag based on the given data model and attribute.
2417: * If the attribute has input error, the input field's CSS class will
2418: * be appended with {@link errorCss}.
2419: * This enables highlighting the incorrect input.
2420: * @param string $type the input type (e.g. 'text', 'radio')
2421: * @param CModel $model the data model
2422: * @param string $attribute the attribute
2423: * @param array $htmlOptions additional HTML attributes for the HTML tag
2424: * @return string the generated input tag
2425: */
2426: protected static function activeInputField($type,$model,$attribute,$htmlOptions)
2427: {
2428: $htmlOptions['type']=$type;
2429: if($type==='text'||$type==='password'||$type==='color'||$type==='date'||$type==='datetime'||
2430: $type==='datetime-local'||$type==='email'||$type==='month'||$type==='number'||$type==='range'||
2431: $type==='search'||$type==='tel'||$type==='time'||$type==='url'||$type==='week')
2432: {
2433: if(!isset($htmlOptions['maxlength']))
2434: {
2435: foreach($model->getValidators($attribute) as $validator)
2436: {
2437: if($validator instanceof CStringValidator && $validator->max!==null)
2438: {
2439: $htmlOptions['maxlength']=$validator->max;
2440: break;
2441: }
2442: }
2443: }
2444: elseif($htmlOptions['maxlength']===false)
2445: unset($htmlOptions['maxlength']);
2446: }
2447:
2448: if($type==='file')
2449: unset($htmlOptions['value']);
2450: elseif(!isset($htmlOptions['value']))
2451: $htmlOptions['value']=self::resolveValue($model,$attribute);
2452: if($model->hasErrors($attribute))
2453: self::addErrorCss($htmlOptions);
2454: return self::tag('input',$htmlOptions);
2455: }
2456:
2457: /**
2458: * Generates the list options.
2459: * @param mixed $selection the selected value(s). This can be either a string for single selection or an array for multiple selections.
2460: * @param array $listData the option data (see {@link listData})
2461: * @param array $htmlOptions additional HTML attributes. The following two special attributes are recognized:
2462: * <ul>
2463: * <li>encode: boolean, specifies whether to encode the values. Defaults to true.</li>
2464: * <li>prompt: string, specifies the prompt text shown as the first list option. Its value is empty. Note, the prompt text will NOT be HTML-encoded.</li>
2465: * <li>empty: string, specifies the text corresponding to empty selection. Its value is empty.
2466: * The 'empty' option can also be an array of value-label pairs.
2467: * Each pair will be used to render a list option at the beginning. Note, the text label will NOT be HTML-encoded.</li>
2468: * <li>options: array, specifies additional attributes for each OPTION tag.
2469: * The array keys must be the option values, and the array values are the extra
2470: * OPTION tag attributes in the name-value pairs. For example,
2471: * <pre>
2472: * array(
2473: * 'value1'=>array('disabled'=>true,'label'=>'value 1'),
2474: * 'value2'=>array('label'=>'value 2'),
2475: * );
2476: * </pre>
2477: * </li>
2478: * <li>key: string, specifies the name of key attribute of the selection object(s).
2479: * This is used when the selection is represented in terms of objects. In this case,
2480: * the property named by the key option of the objects will be treated as the actual selection value.
2481: * This option defaults to 'primaryKey', meaning using the 'primaryKey' property value of the objects in the selection.
2482: * This option has been available since version 1.1.3.</li>
2483: * </ul>
2484: * @return string the generated list options
2485: */
2486: public static function listOptions($selection,$listData,&$htmlOptions)
2487: {
2488: $raw=isset($htmlOptions['encode']) && !$htmlOptions['encode'];
2489: $content='';
2490: if(isset($htmlOptions['prompt']))
2491: {
2492: $content.='<option value="">'.strtr($htmlOptions['prompt'],array('<'=>'<','>'=>'>'))."</option>\n";
2493: unset($htmlOptions['prompt']);
2494: }
2495: if(isset($htmlOptions['empty']))
2496: {
2497: if(!is_array($htmlOptions['empty']))
2498: $htmlOptions['empty']=array(''=>$htmlOptions['empty']);
2499: foreach($htmlOptions['empty'] as $value=>$label)
2500: $content.='<option value="'.self::encode($value).'">'.strtr($label,array('<'=>'<','>'=>'>'))."</option>\n";
2501: unset($htmlOptions['empty']);
2502: }
2503:
2504: if(isset($htmlOptions['options']))
2505: {
2506: $options=$htmlOptions['options'];
2507: unset($htmlOptions['options']);
2508: }
2509: else
2510: $options=array();
2511:
2512: $key=isset($htmlOptions['key']) ? $htmlOptions['key'] : 'primaryKey';
2513: if(is_array($selection))
2514: {
2515: foreach($selection as $i=>$item)
2516: {
2517: if(is_object($item))
2518: $selection[$i]=$item->$key;
2519: }
2520: }
2521: elseif(is_object($selection))
2522: $selection=$selection->$key;
2523:
2524: foreach($listData as $key=>$value)
2525: {
2526: if(is_array($value))
2527: {
2528: $content.='<optgroup label="'.($raw?$key : self::encode($key))."\">\n";
2529: $dummy=array('options'=>$options);
2530: if(isset($htmlOptions['encode']))
2531: $dummy['encode']=$htmlOptions['encode'];
2532: $content.=self::listOptions($selection,$value,$dummy);
2533: $content.='</optgroup>'."\n";
2534: }
2535: else
2536: {
2537: $attributes=array('value'=>(string)$key,'encode'=>!$raw);
2538: if(!is_array($selection) && !strcmp($key,$selection) || is_array($selection) && in_array($key,$selection))
2539: $attributes['selected']='selected';
2540: if(isset($options[$key]))
2541: $attributes=array_merge($attributes,$options[$key]);
2542: $content.=self::tag('option',$attributes,$raw?(string)$value : self::encode((string)$value))."\n";
2543: }
2544: }
2545:
2546: unset($htmlOptions['key']);
2547:
2548: return $content;
2549: }
2550:
2551: /**
2552: * Generates the JavaScript with the specified client changes.
2553: * @param string $event event name (without 'on')
2554: * @param array $htmlOptions HTML attributes which may contain the following special attributes
2555: * specifying the client change behaviors:
2556: * <ul>
2557: * <li>submit: string, specifies the URL to submit to. If the current element has a parent form, that form will be
2558: * submitted, and if 'submit' is non-empty its value will replace the form's URL. If there is no parent form the
2559: * data listed in 'params' will be submitted instead (via POST method), to the URL in 'submit' or the currently
2560: * requested URL if 'submit' is empty. Please note that if the 'csrf' setting is true, the CSRF token will be
2561: * included in the params too.</li>
2562: * <li>params: array, name-value pairs that should be submitted together with the form. This is only used when 'submit' option is specified.</li>
2563: * <li>csrf: boolean, whether a CSRF token should be automatically included in 'params' when {@link CHttpRequest::enableCsrfValidation} is true. Defaults to false.
2564: * You may want to set this to be true if there is no enclosing form around this element.
2565: * This option is meaningful only when 'submit' option is set.</li>
2566: * <li>return: boolean, the return value of the javascript. Defaults to false, meaning that the execution of
2567: * javascript would not cause the default behavior of the event.</li>
2568: * <li>confirm: string, specifies the message that should show in a pop-up confirmation dialog.</li>
2569: * <li>ajax: array, specifies the AJAX options (see {@link ajax}).</li>
2570: * <li>live: boolean, whether the event handler should be delegated or directly bound.
2571: * If not set, {@link liveEvents} will be used. This option has been available since version 1.1.11.</li>
2572: * </ul>
2573: * This parameter has been available since version 1.1.1.
2574: */
2575: protected static function clientChange($event,&$htmlOptions)
2576: {
2577: if(!isset($htmlOptions['submit']) && !isset($htmlOptions['confirm']) && !isset($htmlOptions['ajax']))
2578: return;
2579:
2580: if(isset($htmlOptions['live']))
2581: {
2582: $live=$htmlOptions['live'];
2583: unset($htmlOptions['live']);
2584: }
2585: else
2586: $live = self::$liveEvents;
2587:
2588: if(isset($htmlOptions['return']) && $htmlOptions['return'])
2589: $return='return true';
2590: else
2591: $return='return false';
2592:
2593: if(isset($htmlOptions['on'.$event]))
2594: {
2595: $handler=trim($htmlOptions['on'.$event],';').';';
2596: unset($htmlOptions['on'.$event]);
2597: }
2598: else
2599: $handler='';
2600:
2601: if(isset($htmlOptions['id']))
2602: $id=$htmlOptions['id'];
2603: else
2604: $id=$htmlOptions['id']=isset($htmlOptions['name'])?$htmlOptions['name']:self::ID_PREFIX.self::$count++;
2605:
2606: $cs=Yii::app()->getClientScript();
2607: $cs->registerCoreScript('jquery');
2608:
2609: if(isset($htmlOptions['submit']))
2610: {
2611: $cs->registerCoreScript('yii');
2612: $request=Yii::app()->getRequest();
2613: if($request->enableCsrfValidation && isset($htmlOptions['csrf']) && $htmlOptions['csrf'])
2614: $htmlOptions['params'][$request->csrfTokenName]=$request->getCsrfToken();
2615: if(isset($htmlOptions['params']))
2616: $params=CJavaScript::encode($htmlOptions['params']);
2617: else
2618: $params='{}';
2619: if($htmlOptions['submit']!=='')
2620: $url=CJavaScript::quote(self::normalizeUrl($htmlOptions['submit']));
2621: else
2622: $url='';
2623: $handler.="jQuery.yii.submitForm(this,'$url',$params);{$return};";
2624: }
2625:
2626: if(isset($htmlOptions['ajax']))
2627: $handler.=self::ajax($htmlOptions['ajax'])."{$return};";
2628:
2629: if(isset($htmlOptions['confirm']))
2630: {
2631: $confirm='confirm(\''.CJavaScript::quote($htmlOptions['confirm']).'\')';
2632: if($handler!=='')
2633: $handler="if($confirm) {".$handler."} else return false;";
2634: else
2635: $handler="return $confirm;";
2636: }
2637:
2638: if($live)
2639: $cs->registerScript('Yii.CHtml.#' . $id,"jQuery('body').on('$event','#$id',function(){{$handler}});");
2640: else
2641: $cs->registerScript('Yii.CHtml.#' . $id,"jQuery('#$id').on('$event', function(){{$handler}});");
2642: unset($htmlOptions['params'],$htmlOptions['submit'],$htmlOptions['ajax'],$htmlOptions['confirm'],$htmlOptions['return'],$htmlOptions['csrf']);
2643: }
2644:
2645: /**
2646: * Generates input name and ID for a model attribute.
2647: * This method will update the HTML options by setting appropriate 'name' and 'id' attributes.
2648: * This method may also modify the attribute name if the name
2649: * contains square brackets (mainly used in tabular input).
2650: * @param CModel $model the data model
2651: * @param string $attribute the attribute
2652: * @param array $htmlOptions the HTML options
2653: */
2654: public static function resolveNameID($model,&$attribute,&$htmlOptions)
2655: {
2656: if(!isset($htmlOptions['name']))
2657: $htmlOptions['name']=self::resolveName($model,$attribute);
2658: if(!isset($htmlOptions['id']))
2659: $htmlOptions['id']=self::getIdByName($htmlOptions['name']);
2660: elseif($htmlOptions['id']===false)
2661: unset($htmlOptions['id']);
2662: }
2663:
2664: /**
2665: * Generates input name for a model attribute.
2666: * Note, the attribute name may be modified after calling this method if the name
2667: * contains square brackets (mainly used in tabular input) before the real attribute name.
2668: * @param CModel $model the data model
2669: * @param string $attribute the attribute
2670: * @return string the input name
2671: */
2672: public static function resolveName($model,&$attribute)
2673: {
2674: $modelName=self::modelName($model);
2675:
2676: if(($pos=strpos($attribute,'['))!==false)
2677: {
2678: if($pos!==0) // e.g. name[a][b]
2679: return $modelName.'['.substr($attribute,0,$pos).']'.substr($attribute,$pos);
2680: if(($pos=strrpos($attribute,']'))!==false && $pos!==strlen($attribute)-1) // e.g. [a][b]name
2681: {
2682: $sub=substr($attribute,0,$pos+1);
2683: $attribute=substr($attribute,$pos+1);
2684: return $modelName.$sub.'['.$attribute.']';
2685: }
2686: if(preg_match('/\](\w+\[.*)$/',$attribute,$matches))
2687: {
2688: $name=$modelName.'['.str_replace(']','][',trim(strtr($attribute,array(']['=>']','['=>']')),']')).']';
2689: $attribute=$matches[1];
2690: return $name;
2691: }
2692: }
2693: return $modelName.'['.$attribute.']';
2694: }
2695:
2696: /**
2697: * Evaluates the attribute value of the model.
2698: * This method can recognize the attribute name written in array format.
2699: * For example, if the attribute name is 'name[a][b]', the value "$model->name['a']['b']" will be returned.
2700: * @param CModel $model the data model
2701: * @param string $attribute the attribute name
2702: * @return mixed the attribute value
2703: * @since 1.1.3
2704: */
2705: public static function resolveValue($model,$attribute)
2706: {
2707: if(($pos=strpos($attribute,'['))!==false)
2708: {
2709: if($pos===0) // [a]name[b][c], should ignore [a]
2710: {
2711: if(preg_match('/\](\w+(\[.+)?)/',$attribute,$matches))
2712: $attribute=$matches[1]; // we get: name[b][c]
2713: if(($pos=strpos($attribute,'['))===false)
2714: return $model->$attribute;
2715: }
2716: $name=substr($attribute,0,$pos);
2717: $value=$model->$name;
2718: foreach(explode('][',rtrim(substr($attribute,$pos+1),']')) as $id)
2719: {
2720: if((is_array($value) || $value instanceof ArrayAccess) && isset($value[$id]))
2721: $value=$value[$id];
2722: else
2723: return null;
2724: }
2725: return $value;
2726: }
2727: else
2728: return $model->$attribute;
2729: }
2730:
2731: /**
2732: * Appends {@link errorCss} to the 'class' attribute.
2733: * @param array $htmlOptions HTML options to be modified
2734: */
2735: protected static function addErrorCss(&$htmlOptions)
2736: {
2737: if(empty(self::$errorCss))
2738: return;
2739:
2740: if(isset($htmlOptions['class']))
2741: $htmlOptions['class'].=' '.self::$errorCss;
2742: else
2743: $htmlOptions['class']=self::$errorCss;
2744: }
2745:
2746: /**
2747: * Renders the HTML tag attributes.
2748: * Since version 1.1.5, attributes whose value is null will not be rendered.
2749: * Special attributes, such as 'checked', 'disabled', 'readonly', will be rendered
2750: * properly based on their corresponding boolean value.
2751: * @param array $htmlOptions attributes to be rendered
2752: * @return string the rendering result
2753: */
2754: public static function renderAttributes($htmlOptions)
2755: {
2756: static $specialAttributes=array(
2757: 'autofocus'=>1,
2758: 'autoplay'=>1,
2759: 'async'=>1,
2760: 'checked'=>1,
2761: 'controls'=>1,
2762: 'declare'=>1,
2763: 'default'=>1,
2764: 'defer'=>1,
2765: 'disabled'=>1,
2766: 'formnovalidate'=>1,
2767: 'hidden'=>1,
2768: 'ismap'=>1,
2769: 'itemscope'=>1,
2770: 'loop'=>1,
2771: 'multiple'=>1,
2772: 'muted'=>1,
2773: 'nohref'=>1,
2774: 'noresize'=>1,
2775: 'novalidate'=>1,
2776: 'open'=>1,
2777: 'readonly'=>1,
2778: 'required'=>1,
2779: 'reversed'=>1,
2780: 'scoped'=>1,
2781: 'seamless'=>1,
2782: 'selected'=>1,
2783: 'typemustmatch'=>1,
2784: );
2785:
2786: if($htmlOptions===array())
2787: return '';
2788:
2789: $html='';
2790: if(isset($htmlOptions['encode']))
2791: {
2792: $raw=!$htmlOptions['encode'];
2793: unset($htmlOptions['encode']);
2794: }
2795: else
2796: $raw=false;
2797:
2798: foreach($htmlOptions as $name=>$value)
2799: {
2800: if(isset($specialAttributes[$name]))
2801: {
2802: if($value===false && $name==='async') {
2803: $html .= ' ' . $name.'="false"';
2804: }
2805: elseif($value)
2806: {
2807: $html .= ' ' . $name;
2808: if(self::$renderSpecialAttributesValue)
2809: $html .= '="' . $name . '"';
2810: }
2811: }
2812: elseif($value!==null)
2813: $html .= ' ' . $name . '="' . ($raw ? $value : self::encode($value)) . '"';
2814: }
2815:
2816: return $html;
2817: }
2818: }
2819: