1: <?php
2: /**
3: * CDetailView 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: * CDetailView displays the detail of a single data model.
13: *
14: * CDetailView is best used for displaying a model in a regular format (e.g. each model attribute
15: * is displayed as a row in a table.) The model can be either an instance of {@link CModel}
16: * or an associative array.
17: *
18: * CDetailView uses the {@link attributes} property to determines which model attributes
19: * should be displayed and how they should be formatted.
20: *
21: * A typical usage of CDetailView is as follows:
22: * <pre>
23: * $this->widget('zii.widgets.CDetailView', array(
24: * 'data'=>$model,
25: * 'attributes'=>array(
26: * 'title', // title attribute (in plain text)
27: * 'owner.name', // an attribute of the related object "owner"
28: * 'description:html', // description attribute in HTML
29: * array( // related city displayed as a link
30: * 'label'=>'City',
31: * 'type'=>'raw',
32: * 'value'=>CHtml::link(CHtml::encode($model->city->name),
33: * array('city/view','id'=>$model->city->id)),
34: * ),
35: * ),
36: * ));
37: * </pre>
38: *
39: * @property CFormatter $formatter The formatter instance. Defaults to the 'format' application component.
40: *
41: * @author Qiang Xue <qiang.xue@gmail.com>
42: * @package zii.widgets
43: * @since 1.1
44: */
45: class CDetailView extends CWidget
46: {
47: private $_formatter;
48:
49: /**
50: * @var mixed the data model whose details are to be displayed. This can be either a {@link CModel} instance
51: * (e.g. a {@link CActiveRecord} object or a {@link CFormModel} object) or an associative array.
52: */
53: public $data;
54: /**
55: * @var array a list of attributes to be displayed in the detail view. Each array element
56: * represents the specification for displaying one particular attribute.
57: *
58: * An attribute can be specified as a string in the format of "Name:Type:Label".
59: * Both "Type" and "Label" are optional.
60: *
61: * "Name" refers to the attribute name. It can be either a property (e.g. "title") or a sub-property (e.g. "owner.username").
62: *
63: * "Label" represents the label for the attribute display. If it is not given, "Name" will be used to generate the appropriate label.
64: *
65: * "Type" represents the type of the attribute. It determines how the attribute value should be formatted and displayed.
66: * It is defaulted to be 'text'.
67: * "Type" should be recognizable by the {@link formatter}. In particular, if "Type" is "xyz", then the "formatXyz" method
68: * of {@link formatter} will be invoked to format the attribute value for display. By default when {@link CFormatter} is used,
69: * these "Type" values are valid: raw, text, ntext, html, date, time, datetime, boolean, number, email, image, url.
70: * For more details about these types, please refer to {@link CFormatter}.
71: *
72: * An attribute can also be specified in terms of an array with the following elements:
73: * <ul>
74: * <li>label: the label associated with the attribute. If this is not specified, the following "name" element
75: * will be used to generate an appropriate label.</li>
76: * <li>name: the name of the attribute. This can be either a property or a sub-property of the model.
77: * If the below "value" element is specified, this will be ignored.</li>
78: * <li>value: the value to be displayed. If this is not specified, the above "name" element will be used
79: * to retrieve the corresponding attribute value for display. Note that this value will be formatted according
80: * to the "type" option as described below. This can also be an anonymous function whose return value will be
81: * used as a value. The signature of the function should be <code>function($data)</code> where data refers to
82: * the {@link data} property of the detail view widget.</li>
83: * <li>type: the type of the attribute that determines how the attribute value would be formatted.
84: * Please see above for possible values.
85: * <li>cssClass: the CSS class to be used for this item. This option is available since version 1.1.3.</li>
86: * <li>template: the template used to render the attribute. If this is not specified, {@link itemTemplate}
87: * will be used instead. For more details on how to set this option, please refer to {@link itemTemplate}.
88: * This option is available since version 1.1.1.</li>
89: * <li>visible: whether the attribute is visible. If set to <code>false</code>, the table row for the attribute will not be rendered.
90: * This option is available since version 1.1.5.</li>
91: * </ul>
92: */
93: public $attributes;
94: /**
95: * @var string the text to be displayed when an attribute value is null. Defaults to "Not set".
96: */
97: public $nullDisplay;
98: /**
99: * @var string the name of the tag for rendering the detail view. Defaults to 'table'.
100: * If set to null, no tag will be rendered.
101: * @see itemTemplate
102: */
103: public $tagName='table';
104: /**
105: * @var string the template used to render a single attribute. Defaults to a table row.
106: * These tokens are recognized: "{class}", "{label}" and "{value}". They will be replaced
107: * with the CSS class name for the item, the label and the attribute value, respectively.
108: * @see itemCssClass
109: */
110: public $itemTemplate="<tr class=\"{class}\"><th>{label}</th><td>{value}</td></tr>\n";
111: /**
112: * @var array the CSS class names for the items displaying attribute values. If multiple CSS class names are given,
113: * they will be assigned to the items sequentially and repeatedly.
114: * Defaults to <code>array('odd', 'even')</code>.
115: */
116: public $itemCssClass=array('odd','even');
117: /**
118: * @var array the HTML options used for {@link tagName}
119: */
120: public $htmlOptions=array('class'=>'detail-view');
121: /**
122: * @var string the base script URL for all detail view resources (e.g. javascript, CSS file, images).
123: * Defaults to null, meaning using the integrated detail view resources (which are published as assets).
124: */
125: public $baseScriptUrl;
126: /**
127: * @var string the URL of the CSS file used by this detail view. Defaults to null, meaning using the integrated
128: * CSS file. If this is set false, you are responsible to explicitly include the necessary CSS file in your page.
129: */
130: public $cssFile;
131:
132: /**
133: * Initializes the detail view.
134: * This method will initialize required property values.
135: */
136: public function init()
137: {
138: if($this->data===null)
139: throw new CException(Yii::t('zii','Please specify the "data" property.'));
140: if($this->attributes===null)
141: {
142: if($this->data instanceof CModel)
143: $this->attributes=$this->data->attributeNames();
144: elseif(is_array($this->data))
145: $this->attributes=array_keys($this->data);
146: else
147: throw new CException(Yii::t('zii','Please specify the "attributes" property.'));
148: }
149: if($this->nullDisplay===null)
150: $this->nullDisplay='<span class="null">'.Yii::t('zii','Not set').'</span>';
151: if(isset($this->htmlOptions['id']))
152: $this->id=$this->htmlOptions['id'];
153: else
154: $this->htmlOptions['id']=$this->id;
155:
156: if($this->baseScriptUrl===null)
157: $this->baseScriptUrl=Yii::app()->getAssetManager()->publish(Yii::getPathOfAlias('zii.widgets.assets')).'/detailview';
158:
159: if($this->cssFile!==false)
160: {
161: if($this->cssFile===null)
162: $this->cssFile=$this->baseScriptUrl.'/styles.css';
163: Yii::app()->getClientScript()->registerCssFile($this->cssFile);
164: }
165: }
166:
167: /**
168: * Renders the detail view.
169: * This is the main entry of the whole detail view rendering.
170: */
171: public function run()
172: {
173: $formatter=$this->getFormatter();
174: if ($this->tagName!==null)
175: echo CHtml::openTag($this->tagName,$this->htmlOptions);
176:
177: $i=0;
178: $n=is_array($this->itemCssClass) ? count($this->itemCssClass) : 0;
179:
180: foreach($this->attributes as $attribute)
181: {
182: if(is_string($attribute))
183: {
184: if(!preg_match('/^([\w\.]+)(:(\w*))?(:(.*))?$/',$attribute,$matches))
185: throw new CException(Yii::t('zii','The attribute must be specified in the format of "Name:Type:Label", where "Type" and "Label" are optional.'));
186: $attribute=array(
187: 'name'=>$matches[1],
188: 'type'=>isset($matches[3]) ? $matches[3] : 'text',
189: );
190: if(isset($matches[5]))
191: $attribute['label']=$matches[5];
192: }
193:
194: if(isset($attribute['visible']) && !$attribute['visible'])
195: continue;
196:
197: $tr=array('{label}'=>'', '{class}'=>$n ? $this->itemCssClass[$i%$n] : '');
198: if(isset($attribute['cssClass']))
199: $tr['{class}']=$attribute['cssClass'].' '.($n ? $tr['{class}'] : '');
200:
201: if(isset($attribute['label']))
202: $tr['{label}']=$attribute['label'];
203: elseif(isset($attribute['name']))
204: {
205: if($this->data instanceof CModel)
206: $tr['{label}']=$this->data->getAttributeLabel($attribute['name']);
207: else
208: $tr['{label}']=ucwords(trim(strtolower(str_replace(array('-','_','.'),' ',preg_replace('/(?<![A-Z])[A-Z]/', ' \0', $attribute['name'])))));
209: }
210:
211: if(!isset($attribute['type']))
212: $attribute['type']='text';
213: if(isset($attribute['value']))
214: $value=is_object($attribute['value']) && get_class($attribute['value']) === 'Closure' ? call_user_func($attribute['value'],$this->data) : $attribute['value'];
215: elseif(isset($attribute['name']))
216: $value=CHtml::value($this->data,$attribute['name']);
217: else
218: $value=null;
219:
220: $tr['{value}']=$value===null ? $this->nullDisplay : $formatter->format($value,$attribute['type']);
221:
222: $this->renderItem($attribute, $tr);
223:
224: $i++;
225: }
226:
227: if ($this->tagName!==null)
228: echo CHtml::closeTag($this->tagName);
229: }
230:
231: /**
232: * This method is used by run() to render item row
233: *
234: * @param array $options config options for this item/attribute from {@link attributes}
235: * @param string $templateData data that will be inserted into {@link itemTemplate}
236: * @since 1.1.11
237: */
238: protected function renderItem($options,$templateData)
239: {
240: echo strtr(isset($options['template']) ? $options['template'] : $this->itemTemplate,$templateData);
241: }
242:
243: /**
244: * @return CFormatter the formatter instance. Defaults to the 'format' application component.
245: */
246: public function getFormatter()
247: {
248: if($this->_formatter===null)
249: $this->_formatter=Yii::app()->format;
250: return $this->_formatter;
251: }
252:
253: /**
254: * @param CFormatter $value the formatter instance
255: */
256: public function setFormatter($value)
257: {
258: $this->_formatter=$value;
259: }
260: }
261: