diff --git a/system/CBaseListView.php b/system/CBaseListView.php new file mode 100644 index 0000000..2c1a834 --- /dev/null +++ b/system/CBaseListView.php @@ -0,0 +1,307 @@ + + * @link http://www.yiiframework.com/ + * @copyright 2008-2013 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CBaseListView is the base class for {@link CListView} and {@link CGridView}. + * + * CBaseListView implements the common features needed by a view wiget for rendering multiple models. + * + * @author Qiang Xue + * @package zii.widgets + * @since 1.1 + */ +abstract class CBaseListView extends CWidget +{ + /** + * @var IDataProvider the data provider for the view. + */ + public $dataProvider; + /** + * @var string the tag name for the view container. Defaults to 'div'. + */ + public $tagName='div'; + /** + * @var array the HTML options for the view container tag. + */ + public $htmlOptions=array(); + /** + * @var boolean whether to enable sorting. Note that if the {@link IDataProvider::sort} property + * of {@link dataProvider} is false, this will be treated as false as well. When sorting is enabled, + * sortable columns will have their headers clickable to trigger sorting along that column. + * Defaults to true. + * @see sortableAttributes + */ + public $enableSorting=true; + /** + * @var boolean whether to enable pagination. Note that if the {@link IDataProvider::pagination} property + * of {@link dataProvider} is false, this will be treated as false as well. When pagination is enabled, + * a pager will be displayed in the view so that it can trigger pagination of the data display. + * Defaults to true. + */ + public $enablePagination=true; + /** + * @var array|string the configuration for the pager. Defaults to array('class'=>'OLinkPager'). + * String value will be treated as the class name of the pager ('ClassName' value is similar + * to the array('class'=>'ClassName') value). See {@link CBasePager} and {@link OLinkPager} + * for more details about pager configuration array values. + * @see enablePagination + */ + public $pager=array('class'=>'OLinkPager'); + /** + * @var string the template to be used to control the layout of various sections in the view. + * These tokens are recognized: {summary}, {items} and {pager}. They will be replaced with the + * summary text, the items, and the pager. + */ + public $template="{summary}\n{items}\n{pager}"; + /** + * @var string the summary text template for the view. These tokens are recognized and will be replaced + * with the corresponding values: + * + */ + public $summaryText; + /** + * @var string the HTML tag name for the container of the {@link summaryText} property. + * @since 1.1.16 + */ + public $summaryTagName='div'; + /** + * @var string the message to be displayed when {@link dataProvider} does not have any data. + */ + public $emptyText; + /** + * @var string the HTML tag name for the container of the {@link emptyText} property. + */ + public $emptyTagName='span'; + /** + * @var string the CSS class name for the container of the {@link emptyText} property. Defaults to 'empty'. + * @since 1.1.16 + */ + public $emptyCssClass='empty'; + /** + * @var string the CSS class name for the container of all data item display. Defaults to 'items'. + * Note, this property must not contain false, null or empty string values. Otherwise such values may + * cause undefined behavior. + */ + public $itemsCssClass='items'; + /** + * @var string the CSS class name for the summary text container. Defaults to 'summary'. + */ + public $summaryCssClass='summary'; + /** + * @var string the CSS class name for the pager container. Defaults to 'pager'. + * Note, this property must not contain false, null or empty string values. Otherwise such values may + * cause undefined behavior. + */ + public $pagerCssClass='pager'; + /** + * @var string the CSS class name that will be assigned to the widget container element + * when the widget is updating its content via AJAX. Defaults to 'loading'. + * @since 1.1.1 + */ + public $loadingCssClass='loading'; + + /** + * Initializes the view. + * This method will initialize required property values and instantiate {@link columns} objects. + */ + public function init() + { + if($this->dataProvider===null) + throw new CException(Yii::t('zii','The "dataProvider" property cannot be empty.')); + + $this->dataProvider->getData(); + + if(isset($this->htmlOptions['id'])) + $this->id=$this->htmlOptions['id']; + else + $this->htmlOptions['id']=$this->id; + + if($this->enableSorting && $this->dataProvider->getSort()===false) + $this->enableSorting=false; + if($this->enablePagination && $this->dataProvider->getPagination()===false) + $this->enablePagination=false; + } + + /** + * Renders the view. + * This is the main entry of the whole view rendering. + * Child classes should mainly override {@link renderContent} method. + */ + public function run() + { + $this->registerClientScript(); + + echo CHtml::openTag($this->tagName,$this->htmlOptions)."\n"; + + $this->renderContent(); + $this->renderKeys(); + + echo CHtml::closeTag($this->tagName); + } + + /** + * Renders the main content of the view. + * The content is divided into sections, such as summary, items, pager. + * Each section is rendered by a method named as "renderXyz", where "Xyz" is the section name. + * The rendering results will replace the corresponding placeholders in {@link template}. + */ + public function renderContent() + { + ob_start(); + echo preg_replace_callback("/{(\w+)}/",array($this,'renderSection'),$this->template); + ob_end_flush(); + } + + /** + * Renders a section. + * This method is invoked by {@link renderContent} for every placeholder found in {@link template}. + * It should return the rendering result that would replace the placeholder. + * @param array $matches the matches, where $matches[0] represents the whole placeholder, + * while $matches[1] contains the name of the matched placeholder. + * @return string the rendering result of the section + */ + protected function renderSection($matches) + { + $method='render'.$matches[1]; + if(method_exists($this,$method)) + { + $this->$method(); + $html=ob_get_contents(); + ob_clean(); + return $html; + } + else + return $matches[0]; + } + + /** + * Renders the empty message when there is no data. + */ + public function renderEmptyText() + { + $emptyText=$this->emptyText===null ? Yii::t('zii','No results found.') : $this->emptyText; + echo CHtml::tag($this->emptyTagName, array('class'=>$this->emptyCssClass), $emptyText); + } + + /** + * Renders the key values of the data in a hidden tag. + */ + public function renderKeys() + { + echo CHtml::openTag('div',array( + 'class'=>'keys', + 'style'=>'display:none', + 'title'=>Yii::app()->getRequest()->getUrl(), + )); + foreach($this->dataProvider->getKeys() as $key) + echo "".CHtml::encode($key).""; + echo "\n"; + } + + /** + * Renders the summary text. + */ + public function renderSummary() + { + if(($count=$this->dataProvider->getItemCount())<=0) + return; + + echo CHtml::openTag($this->summaryTagName, array('class'=>$this->summaryCssClass)); + if($this->enablePagination) + { + $pagination=$this->dataProvider->getPagination(); + $total=$this->dataProvider->getTotalItemCount(); + $start=$pagination->currentPage*$pagination->pageSize+1; + $end=$start+$count-1; + if($end>$total) + { + $end=$total; + $start=$end-$count+1; + } + if(($summaryText=$this->summaryText)===null) + $summaryText=Yii::t('zii','Displaying {start}-{end} of 1 result.|Displaying {start}-{end} of {count} results.',$total); + echo strtr($summaryText,array( + '{start}'=>$start, + '{end}'=>$end, + '{count}'=>$total, + '{page}'=>$pagination->currentPage+1, + '{pages}'=>$pagination->pageCount, + )); + } + else + { + if(($summaryText=$this->summaryText)===null) + $summaryText=Yii::t('zii','Total 1 result.|Total {count} results.',$count); + echo strtr($summaryText,array( + '{count}'=>$count, + '{start}'=>1, + '{end}'=>$count, + '{page}'=>1, + '{pages}'=>1, + )); + } + echo CHtml::closeTag($this->summaryTagName); + } + + /** + * Renders the pager. + */ + public function renderPager() + { + if(!$this->enablePagination) + return; + + $pager=array(); + $class='OLinkPager'; + if(is_string($this->pager)) + $class=$this->pager; + elseif(is_array($this->pager)) + { + $pager=$this->pager; + if(isset($pager['class'])) + { + $class=$pager['class']; + unset($pager['class']); + } + } + $pager['pages']=$this->dataProvider->getPagination(); + + if($pager['pages']->getPageCount()>1) + { + echo '
'; + $this->widget($class,$pager); + echo '
'; + } + else + $this->widget($class,$pager); + } + + /** + * Registers necessary client scripts. + * This method is invoked by {@link run}. + * Child classes may override this method to register customized client scripts. + */ + public function registerClientScript() + { + } + + /** + * Renders the data items for the view. + * Each item is corresponding to a single data model instance. + * Child classes should override this method to provide the actual item rendering logic. + */ + abstract public function renderItems(); +} diff --git a/system/OGridView.php b/system/OGridView.php index 2616bc5..29ef09c 100644 --- a/system/OGridView.php +++ b/system/OGridView.php @@ -12,7 +12,7 @@ * */ -Yii::import('zii.widgets.CBaseListView'); +Yii::import('application.libraries.yii-traits.system.CBaseListView'); Yii::import('application.libraries.yii-traits.system.CDataColumn'); Yii::import('zii.widgets.grid.CLinkColumn'); Yii::import('application.libraries.yii-traits.system.CButtonColumn'); @@ -68,7 +68,7 @@ * 'value'=>'$data->author->username', * ), * array( // display a column with "view", "update" and "delete" buttons - * 'class'=>'CButtonColumn', + * 'class' => 'CButtonColumn', * ), * ), * )); diff --git a/system/OLinkPager.php b/system/OLinkPager.php new file mode 100644 index 0000000..06f52e0 --- /dev/null +++ b/system/OLinkPager.php @@ -0,0 +1,239 @@ + + * @link http://www.yiiframework.com/ + * @copyright 2008-2013 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * OLinkPager displays a list of hyperlinks that lead to different pages of target. + * + * @author Qiang Xue + * @package system.web.widgets.pagers + * @since 1.0 + */ +class OLinkPager extends CBasePager +{ + const CSS_FIRST_PAGE='first'; + const CSS_LAST_PAGE='last'; + const CSS_PREVIOUS_PAGE='previous'; + const CSS_NEXT_PAGE='next'; + const CSS_INTERNAL_PAGE='page'; + const CSS_HIDDEN_PAGE='hidden'; + const CSS_SELECTED_PAGE='selected'; + + /** + * @var string the CSS class for the first page button. Defaults to 'first'. + * @since 1.1.11 + */ + public $firstPageCssClass=self::CSS_FIRST_PAGE; + /** + * @var string the CSS class for the last page button. Defaults to 'last'. + * @since 1.1.11 + */ + public $lastPageCssClass=self::CSS_LAST_PAGE; + /** + * @var string the CSS class for the previous page button. Defaults to 'previous'. + * @since 1.1.11 + */ + public $previousPageCssClass=self::CSS_PREVIOUS_PAGE; + /** + * @var string the CSS class for the next page button. Defaults to 'next'. + * @since 1.1.11 + */ + public $nextPageCssClass=self::CSS_NEXT_PAGE; + /** + * @var string the CSS class for the internal page buttons. Defaults to 'page'. + * @since 1.1.11 + */ + public $internalPageCssClass=self::CSS_INTERNAL_PAGE; + /** + * @var string the CSS class for the hidden page buttons. Defaults to 'hidden'. + * @since 1.1.11 + */ + public $hiddenPageCssClass=self::CSS_HIDDEN_PAGE; + /** + * @var string the CSS class for the selected page buttons. Defaults to 'selected'. + * @since 1.1.11 + */ + public $selectedPageCssClass=self::CSS_SELECTED_PAGE; + /** + * @var integer maximum number of page buttons that can be displayed. Defaults to 10. + */ + public $maxButtonCount=10; + /** + * @var string the text label for the next page button. Defaults to 'Next >'. + * Setting this to false will disable this button. + */ + public $nextPageLabel; + /** + * @var string the text label for the previous page button. Defaults to '< Previous'. + * Setting this to false will disable this button. + */ + public $prevPageLabel; + /** + * @var string the text label for the first page button. Defaults to '<< First'. + * Setting this to false will disable this button. + */ + public $firstPageLabel; + /** + * @var string the text label for the last page button. Defaults to 'Last >>'. + * Setting this to false will disable this button. + */ + public $lastPageLabel; + /** + * @var string the text shown before page buttons. Defaults to 'Go to page: '. + */ + public $header; + /** + * @var string the text shown after page buttons. + */ + public $footer=''; + /** + * @var mixed the CSS file used for the widget. Defaults to null, meaning + * using the default CSS file included together with the widget. + * If false, no CSS file will be used. Otherwise, the specified CSS file + * will be included when using this widget. + */ + public $cssFile; + /** + * @var array HTML attributes for the pager container tag. + */ + public $htmlOptions=array(); + + /** + * Initializes the pager by setting some default property values. + */ + public function init() + { + if($this->nextPageLabel===null) + $this->nextPageLabel=Yii::t('yii','Next >'); + if($this->prevPageLabel===null) + $this->prevPageLabel=Yii::t('yii','< Previous'); + if($this->firstPageLabel===null) + $this->firstPageLabel=Yii::t('yii','<< First'); + if($this->lastPageLabel===null) + $this->lastPageLabel=Yii::t('yii','Last >>'); + if($this->header===null) + $this->header=Yii::t('yii','Go to page: '); + + if(!isset($this->htmlOptions['id'])) + $this->htmlOptions['id']=$this->getId(); + if(!isset($this->htmlOptions['class'])) + $this->htmlOptions['class']='yiiPager'; + } + + /** + * Executes the widget. + * This overrides the parent implementation by displaying the generated page buttons. + */ + public function run() + { + $this->registerClientScript(); + $buttons=$this->createPageButtons(); + if(empty($buttons)) + return; + echo $this->header; + echo CHtml::tag('ul',$this->htmlOptions,implode("\n",$buttons)); + echo $this->footer; + } + + /** + * Creates the page buttons. + * @return array a list of page buttons (in HTML code). + */ + protected function createPageButtons() + { + if(($pageCount=$this->getPageCount())<=1) + return array(); + + list($beginPage,$endPage)=$this->getPageRange(); + $currentPage=$this->getCurrentPage(false); // currentPage is calculated in getPageRange() + $buttons=array(); + + // first page + if ($this->firstPageLabel !== false) { + $buttons[]=$this->createPageButton($this->firstPageLabel,0,$this->firstPageCssClass,$currentPage<=0,false); + } + // prev page + if ($this->prevPageLabel !== false) { + if(($page=$currentPage-1)<0) + $page=0; + $buttons[]=$this->createPageButton($this->prevPageLabel,$page,$this->previousPageCssClass,$currentPage<=0,false); + } + + // internal pages + for($i=$beginPage;$i<=$endPage;++$i) + $buttons[]=$this->createPageButton($i+1,$i,$this->internalPageCssClass,false,$i==$currentPage); + + // next page + if ($this->nextPageLabel !== false) { + if(($page=$currentPage+1)>=$pageCount-1) + $page=$pageCount-1; + $buttons[]=$this->createPageButton($this->nextPageLabel,$page,$this->nextPageCssClass,$currentPage>=$pageCount-1,false); + } + // last page + if ($this->lastPageLabel !== false) { + $buttons[]=$this->createPageButton($this->lastPageLabel,$pageCount-1,$this->lastPageCssClass,$currentPage>=$pageCount-1,false); + } + + return $buttons; + } + + /** + * Creates a page button. + * You may override this method to customize the page buttons. + * @param string $label the text label for the button + * @param integer $page the page number + * @param string $class the CSS class for the page button. + * @param boolean $hidden whether this page button is visible + * @param boolean $selected whether this page button is selected + * @return string the generated button + */ + protected function createPageButton($label,$page,$class,$hidden,$selected) + { + if($hidden || $selected) + $class.=' '.($hidden ? $this->hiddenPageCssClass : $this->selectedPageCssClass); + return '
  • '.CHtml::link($label,$this->createPageUrl($page), array('class'=>'page-link')).'
  • '; + } + + /** + * @return array the begin and end pages that need to be displayed. + */ + protected function getPageRange() + { + $currentPage=$this->getCurrentPage(); + $pageCount=$this->getPageCount(); + + $beginPage=max(0, $currentPage-(int)($this->maxButtonCount/2)); + if(($endPage=$beginPage+$this->maxButtonCount-1)>=$pageCount) + { + $endPage=$pageCount-1; + $beginPage=max(0,$endPage-$this->maxButtonCount+1); + } + return array($beginPage,$endPage); + } + + /** + * Registers the needed client scripts (mainly CSS file). + */ + public function registerClientScript() + { + if($this->cssFile!==false) + self::registerCssFile($this->cssFile); + } + + /** + * Registers the needed CSS file. + * @param string $url the CSS URL. If null, a default CSS URL will be used. + */ + public static function registerCssFile($url=null) + { + if($url===null) + $url=CHtml::asset(Yii::getPathOfAlias('system.web.widgets.pagers.pager').'.css'); + Yii::app()->getClientScript()->registerCssFile($url); + } +}