1: <?php
2: /**
3: * CBaseListView 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: * CBaseListView is the base class for {@link CListView} and {@link CGridView}.
13: *
14: * CBaseListView implements the common features needed by a view wiget for rendering multiple models.
15: *
16: * @author Qiang Xue <qiang.xue@gmail.com>
17: * @package zii.widgets
18: * @since 1.1
19: */
20: abstract class CBaseListView extends CWidget
21: {
22: /**
23: * @var IDataProvider the data provider for the view.
24: */
25: public $dataProvider;
26: /**
27: * @var string the tag name for the view container. Defaults to 'div'.
28: */
29: public $tagName='div';
30: /**
31: * @var array the HTML options for the view container tag.
32: */
33: public $htmlOptions=array();
34: /**
35: * @var boolean whether to enable sorting. Note that if the {@link IDataProvider::sort} property
36: * of {@link dataProvider} is false, this will be treated as false as well. When sorting is enabled,
37: * sortable columns will have their headers clickable to trigger sorting along that column.
38: * Defaults to true.
39: * @see sortableAttributes
40: */
41: public $enableSorting=true;
42: /**
43: * @var boolean whether to enable pagination. Note that if the {@link IDataProvider::pagination} property
44: * of {@link dataProvider} is false, this will be treated as false as well. When pagination is enabled,
45: * a pager will be displayed in the view so that it can trigger pagination of the data display.
46: * Defaults to true.
47: */
48: public $enablePagination=true;
49: /**
50: * @var array|string the configuration for the pager. Defaults to <code>array('class'=>'CLinkPager')</code>.
51: * String value will be treated as the class name of the pager (<code>'ClassName'</code> value is similar
52: * to the <code>array('class'=>'ClassName')</code> value). See {@link CBasePager} and {@link CLinkPager}
53: * for more details about pager configuration array values.
54: * @see enablePagination
55: */
56: public $pager=array('class'=>'CLinkPager');
57: /**
58: * @var string the template to be used to control the layout of various sections in the view.
59: * These tokens are recognized: {summary}, {items} and {pager}. They will be replaced with the
60: * summary text, the items, and the pager.
61: */
62: public $template="{summary}\n{items}\n{pager}";
63: /**
64: * @var string the summary text template for the view. These tokens are recognized and will be replaced
65: * with the corresponding values:
66: * <ul>
67: * <li>{start}: the starting row number (1-based) currently being displayed</li>
68: * <li>{end}: the ending row number (1-based) currently being displayed</li>
69: * <li>{count}: the total number of rows</li>
70: * <li>{page}: the page number (1-based) current being displayed, available since version 1.1.3</li>
71: * <li>{pages}: the total number of pages, available since version 1.1.3</li>
72: * </ul>
73: */
74: public $summaryText;
75: /**
76: * @var string the HTML tag name for the container of the {@link summaryText} property.
77: * @since 1.1.16
78: */
79: public $summaryTagName='div';
80: /**
81: * @var string the message to be displayed when {@link dataProvider} does not have any data.
82: */
83: public $emptyText;
84: /**
85: * @var string the HTML tag name for the container of the {@link emptyText} property.
86: */
87: public $emptyTagName='span';
88: /**
89: * @var string the CSS class name for the container of the {@link emptyText} property. Defaults to 'empty'.
90: * @since 1.1.16
91: */
92: public $emptyCssClass='empty';
93: /**
94: * @var string the CSS class name for the container of all data item display. Defaults to 'items'.
95: * Note, this property must not contain false, null or empty string values. Otherwise such values may
96: * cause undefined behavior.
97: */
98: public $itemsCssClass='items';
99: /**
100: * @var string the CSS class name for the summary text container. Defaults to 'summary'.
101: */
102: public $summaryCssClass='summary';
103: /**
104: * @var string the CSS class name for the pager container. Defaults to 'pager'.
105: * Note, this property must not contain false, null or empty string values. Otherwise such values may
106: * cause undefined behavior.
107: */
108: public $pagerCssClass='pager';
109: /**
110: * @var string the CSS class name that will be assigned to the widget container element
111: * when the widget is updating its content via AJAX. Defaults to 'loading'.
112: * @since 1.1.1
113: */
114: public $loadingCssClass='loading';
115:
116: /**
117: * Initializes the view.
118: * This method will initialize required property values and instantiate {@link columns} objects.
119: */
120: public function init()
121: {
122: if($this->dataProvider===null)
123: throw new CException(Yii::t('zii','The "dataProvider" property cannot be empty.'));
124:
125: $this->dataProvider->getData();
126:
127: if(isset($this->htmlOptions['id']))
128: $this->id=$this->htmlOptions['id'];
129: else
130: $this->htmlOptions['id']=$this->id;
131:
132: if($this->enableSorting && $this->dataProvider->getSort()===false)
133: $this->enableSorting=false;
134: if($this->enablePagination && $this->dataProvider->getPagination()===false)
135: $this->enablePagination=false;
136: }
137:
138: /**
139: * Renders the view.
140: * This is the main entry of the whole view rendering.
141: * Child classes should mainly override {@link renderContent} method.
142: */
143: public function run()
144: {
145: $this->registerClientScript();
146:
147: echo CHtml::openTag($this->tagName,$this->htmlOptions)."\n";
148:
149: $this->renderContent();
150: $this->renderKeys();
151:
152: echo CHtml::closeTag($this->tagName);
153: }
154:
155: /**
156: * Renders the main content of the view.
157: * The content is divided into sections, such as summary, items, pager.
158: * Each section is rendered by a method named as "renderXyz", where "Xyz" is the section name.
159: * The rendering results will replace the corresponding placeholders in {@link template}.
160: */
161: public function renderContent()
162: {
163: ob_start();
164: echo preg_replace_callback("/{(\w+)}/",array($this,'renderSection'),$this->template);
165: ob_end_flush();
166: }
167:
168: /**
169: * Renders a section.
170: * This method is invoked by {@link renderContent} for every placeholder found in {@link template}.
171: * It should return the rendering result that would replace the placeholder.
172: * @param array $matches the matches, where $matches[0] represents the whole placeholder,
173: * while $matches[1] contains the name of the matched placeholder.
174: * @return string the rendering result of the section
175: */
176: protected function renderSection($matches)
177: {
178: $method='render'.$matches[1];
179: if(method_exists($this,$method))
180: {
181: $this->$method();
182: $html=ob_get_contents();
183: ob_clean();
184: return $html;
185: }
186: else
187: return $matches[0];
188: }
189:
190: /**
191: * Renders the empty message when there is no data.
192: */
193: public function renderEmptyText()
194: {
195: $emptyText=$this->emptyText===null ? Yii::t('zii','No results found.') : $this->emptyText;
196: echo CHtml::tag($this->emptyTagName, array('class'=>$this->emptyCssClass), $emptyText);
197: }
198:
199: /**
200: * Renders the key values of the data in a hidden tag.
201: */
202: public function renderKeys()
203: {
204: echo CHtml::openTag('div',array(
205: 'class'=>'keys',
206: 'style'=>'display:none',
207: 'title'=>Yii::app()->getRequest()->getUrl(),
208: ));
209: foreach($this->dataProvider->getKeys() as $key)
210: echo "<span>".CHtml::encode($key)."</span>";
211: echo "</div>\n";
212: }
213:
214: /**
215: * Renders the summary text.
216: */
217: public function renderSummary()
218: {
219: if(($count=$this->dataProvider->getItemCount())<=0)
220: return;
221:
222: echo CHtml::openTag($this->summaryTagName, array('class'=>$this->summaryCssClass));
223: if($this->enablePagination)
224: {
225: $pagination=$this->dataProvider->getPagination();
226: $total=$this->dataProvider->getTotalItemCount();
227: $start=$pagination->currentPage*$pagination->pageSize+1;
228: $end=$start+$count-1;
229: if($end>$total)
230: {
231: $end=$total;
232: $start=$end-$count+1;
233: }
234: if(($summaryText=$this->summaryText)===null)
235: $summaryText=Yii::t('zii','Displaying {start}-{end} of 1 result.|Displaying {start}-{end} of {count} results.',$total);
236: echo strtr($summaryText,array(
237: '{start}'=>$start,
238: '{end}'=>$end,
239: '{count}'=>$total,
240: '{page}'=>$pagination->currentPage+1,
241: '{pages}'=>$pagination->pageCount,
242: ));
243: }
244: else
245: {
246: if(($summaryText=$this->summaryText)===null)
247: $summaryText=Yii::t('zii','Total 1 result.|Total {count} results.',$count);
248: echo strtr($summaryText,array(
249: '{count}'=>$count,
250: '{start}'=>1,
251: '{end}'=>$count,
252: '{page}'=>1,
253: '{pages}'=>1,
254: ));
255: }
256: echo CHtml::closeTag($this->summaryTagName);
257: }
258:
259: /**
260: * Renders the pager.
261: */
262: public function renderPager()
263: {
264: if(!$this->enablePagination)
265: return;
266:
267: $pager=array();
268: $class='CLinkPager';
269: if(is_string($this->pager))
270: $class=$this->pager;
271: elseif(is_array($this->pager))
272: {
273: $pager=$this->pager;
274: if(isset($pager['class']))
275: {
276: $class=$pager['class'];
277: unset($pager['class']);
278: }
279: }
280: $pager['pages']=$this->dataProvider->getPagination();
281:
282: if($pager['pages']->getPageCount()>1)
283: {
284: echo '<div class="'.$this->pagerCssClass.'">';
285: $this->widget($class,$pager);
286: echo '</div>';
287: }
288: else
289: $this->widget($class,$pager);
290: }
291:
292: /**
293: * Registers necessary client scripts.
294: * This method is invoked by {@link run}.
295: * Child classes may override this method to register customized client scripts.
296: */
297: public function registerClientScript()
298: {
299: }
300:
301: /**
302: * Renders the data items for the view.
303: * Each item is corresponding to a single data model instance.
304: * Child classes should override this method to provide the actual item rendering logic.
305: */
306: abstract public function renderItems();
307: }
308: