PATH:
home
/
letacommog
/
letaweb
/
protected
/
extensions
/
yiibooster
/
widgets
<?php /** * ## TbExtendedGridView class file * * @author Antonio Ramirez <antonio@clevertech.biz> * @copyright Copyright © Clevertech 2012- * @license [New BSD License](http://www.opensource.org/licenses/bsd-license.php) */ Yii::import('booster.widgets.TbGridView'); /** *## TbExtendedGridView is an extended version of TbGridView. * * Features are: * - Display an extended summary of the records shown. The extended summary can be configured to any of the * {@link TbOperation} type of widgets. * - Automatic chart display (using TbHighCharts widget), where user can 'switch' between views. * - Selectable cells * - Sortable rows * * @property CActiveDataProvider $dataProvider the data provider for the view. * @property TbDataColumn[] $columns * * @package booster.widgets.grids */ class TbExtendedGridView extends TbGridView { /** * @var bool $fixedHeader if set to true will keep the header fixed position */ public $fixedHeader = false; /** * @var integer $headerOffset, when $fixedHeader is set to true, headerOffset will position table header top position * at $headerOffset. If you are using bootstrap and has navigation top fixed, its height is 40px, so it is recommended * to use $headerOffset=40; */ public $headerOffset = 0; /** * @var string the template to be used to control the layout of various sections in the view. * These tokens are recognized: {extendedSummary}, {summary}, {items} and {pager}. They will be replaced with the * extended summary, summary text, the items, and the pager. */ public $template = "{summary}\n{items}\n{pager}\n{extendedSummary}"; /** * @var array $extendedSummary displays an extended summary version. * There are different types of summary types, * please, see {@link TbSumOperation}, {@link TbSumOfTypeOperation},{@link TbPercentOfTypeGooglePieOperation} * {@link TbPercentOfTypeOperation} and {@link TbPercentOfTypeEasyPieOperation}. * * The following is an example, please review the different types of TbOperation classes to find out more about * its configuration parameters. * * <pre> * 'extendedSummary' => array( * 'title' => '', // the extended summary title * 'columns' => array( // the 'columns' that will be displayed at the extended summary * 'id' => array( // column name "id" * 'class' => 'TbSumOperation', // what is the type of TbOperation we are going to display * 'label' => 'Sum of Ids' // label is name of label of the resulted value (ie Sum of Ids:) * ), * 'results' => array( // column name "results" * 'class' => 'TbPercentOfTypeGooglePieOperation', // the type of TbOperation * 'label' => 'How Many Of Each? ', // the label of the operation * 'types' => array( // TbPercentOfTypeGooglePieOperation "types" attributes * '0' => array('label' => 'zeros'), // a value of "0" will be labelled "zeros" * '1' => array('label' => 'ones'), // a value of "1" will be labelled "ones" * '2' => array('label' => 'twos')) // a value of "2" will be labelled "twos" * ) * ) * ), * </pre> */ public $extendedSummary = array(); /** * @var string $extendedSummaryCssClass is the class name of the layer containing the extended summary */ public $extendedSummaryCssClass = 'extended-summary'; /** * @var array $extendedSummaryOptions the HTML attributes of the layer containing the extended summary */ public $extendedSummaryOptions = array(); /** * @var array $componentsAfterAjaxUpdate has scripts that will be executed after components have updated. * It is used internally to render scripts required for components to work correctly. You may use it for your own * scripts, just make sure it is of type array. */ public $componentsAfterAjaxUpdate = array(); /** * @var array $componentsReadyScripts hold scripts that will be executed on document ready. * It is used internally to render scripts required for components to work correctly. You may use it for your own * scripts, just make sure it is of type array. */ public $componentsReadyScripts = array(); /** * @var array $chartOptions if configured, the extended view will display a highcharts chart. */ public $chartOptions = array(); /** * @var bool $sortableRows. If true the rows at the table will be sortable. */ public $sortableRows = false; /** * @var string Database field name for row sorting */ public $sortableAttribute = 'sort_order'; /** * @var boolean Save sort order by ajax defaults to false * @see bootstrap.action.TbSortableAction for an easy way to use with your controller */ public $sortableAjaxSave = false; /** * @var string Name of the action to call and sort values * @see bootstrap.action.TbSortableAction for an easy way to use with your controller * * <pre> * 'sortableAction' => 'module/controller/sortable' | 'controller/sortable' * </pre> * * The widget will make use of the string to create the URL and then append $sortableAttribute * @see $sortableAttribute */ public $sortableAction; /** * @var string a javascript function that will be invoked after a successful sorting is done. * The function signature is <code>function(id, position)</code> where 'id' refers to the ID of the model id key, * 'position' the new position in the list. */ public $afterSortableUpdate; /** * @var bool whether to allow selecting of cells */ public $selectableCells = false; /** * @var string the filter to use to allow selection. For example, if you set the "htmlOptions" property of a column to have a * "class" of "tobeselected", you could set this property as: "td.tobeselected" in order to allow selection to * those columns with that class only. */ public $selectableCellsFilter = 'td'; /** * @var string a javascript function that will be invoked after a selection is done. * The function signature is <code>function(selected)</code> where 'selected' refers to the selected columns. */ public $afterSelectableCells; /** * @var array the configuration options to display a TbBulkActions widget * @see TbBulkActions widget for its configuration */ public $bulkActions = array(); /** * @var string the aligment of the bulk actions. It can be 'left' or 'right'. */ public $bulkActionAlign = 'right'; /** * @var TbBulkActions component that will display the bulk actions to the grid */ protected $bulk; /** * @var boolean $displayExtendedSummary a helper property that is set to true if we have to render the * extended summary */ protected $displayExtendedSummary; /** * @var boolean $displayChart a helper property that is set to true if we have to render a chart. */ protected $displayChart; /** * @var TbOperation[] $extendedSummaryTypes hold the current configured TbOperation that will process column values. */ protected $extendedSummaryTypes = array(); /** * @var array $extendedSummaryOperations hold the supported operation types */ protected $extendedSummaryOperations = array( 'TbSumOperation', 'TbCountOfTypeOperation', 'TbPercentOfTypeOperation', 'TbPercentOfTypeEasyPieOperation', 'TbPercentOfTypeGooglePieOperation' ); /** *### .init() * * Widget initialization */ public function init(){ if (preg_match( '/extendedsummary/i', $this->template ) && !empty($this->extendedSummary) && isset($this->extendedSummary['columns']) ) { $this->template .= "\n{extendedSummaryContent}"; $this->displayExtendedSummary = true; } if (!empty($this->chartOptions) && @$this->chartOptions['data'] && $this->dataProvider->getItemCount()) { $this->displayChart = true; } if ($this->bulkActions !== array() && isset($this->bulkActions['actionButtons'])) { if (!isset($this->bulkActions['class'])) { $this->bulkActions['class'] = 'booster.widgets.TbBulkActions'; } $this->bulk = Yii::createComponent($this->bulkActions, $this); $this->bulk->init(); } // if(isset($this->bulkActions['selectableEqualsChecked']) && $this->bulkActions['selectableEqualsChecked'] === true) { $this->selectionChanged = 'js:function(id) { $("#"+id+" input[type=checkbox]").change(); }'; // } parent::init(); } /** *### .renderContent() * * Renders grid content */ public function renderContent() { parent::renderContent(); $this->registerCustomClientScript(); } /** *### .renderKeys() * * Renders the key values of the data in a hidden tag. */ public function renderKeys() { $data = $this->dataProvider->getData(); if (!$this->sortableRows || (isset($data[0]) && !isset($data[0]->attributes[(string)$this->sortableAttribute]))) { parent::renderKeys(); } echo CHtml::openTag( 'div', array( 'class' => 'keys', 'style' => 'display:none', 'title' => Yii::app()->getRequest()->getUrl(), ) ); foreach ($data as $d) { echo CHtml::tag( 'span', array('data-order' => $this->getAttribute($d, $this->sortableAttribute)), CHtml::encode($this->getPrimaryKey($d)) ); } echo "</div>\n"; return true; } /** *### .getAttribute() * * Helper function to get an attribute from the data * * @param CActiveRecord $data * @param string $attribute the attribute to get * * @return mixed the attribute value null if none found */ protected function getAttribute($data, $attribute) { if ($this->dataProvider instanceof CActiveDataProvider && $data->hasAttribute($attribute)) { return $data->{$attribute}; } if ($this->dataProvider instanceof CArrayDataProvider || $this->dataProvider instanceof CSqlDataProvider) { if (is_object($data) && isset($data->{$attribute})) { return $data->{$attribute}; } if (isset($data[$attribute])) { return $data[$attribute]; } } return null; } /** *### .getPrimaryKey() * * Helper function to return the primary key of the $data * IMPORTANT: composite keys on CActiveDataProviders will return the keys joined by comma * * @param CActiveRecord $data * * @return null|string */ protected function getPrimaryKey($data) { if ($this->dataProvider instanceof CActiveDataProvider) { $key = $this->dataProvider->keyAttribute === null ? $data->getPrimaryKey() : $data->{$this->dataProvider->keyAttribute}; return is_array($key) ? implode(',', $key) : $key; } if (($this->dataProvider instanceof CArrayDataProvider || $this->dataProvider instanceof CSqlDataProvider) && !empty($this->dataProvider->keyField)) { return is_object($data) ? $data->{$this->dataProvider->keyField} : $data[$this->dataProvider->keyField]; } return null; } /** *### .renderTableHeader() * * Renders grid header */ public function renderTableHeader() { $this->renderChart(); parent::renderTableHeader(); } /** *### .renderTableFooter() * * Renders the table footer. */ public function renderTableFooter() { $hasFilter = $this->filter !== null && $this->filterPosition === self::FILTER_POS_FOOTER; $hasFooter = $this->getHasFooter(); if ($this->bulk !== null || $hasFilter || $hasFooter) { echo "<tfoot>\n"; if ($hasFooter) { echo "<tr>\n"; /** @var $column CDataColumn */ foreach ($this->columns as $column) { $column->renderFooterCell(); } echo "</tr>\n"; } if ($hasFilter) { $this->renderFilter(); } if ($this->bulk !== null) { $this->renderBulkActions(); } echo "</tfoot>\n"; } } /** *### .renderBulkActions() */ public function renderBulkActions() { Booster::getBooster()->registerAssetJs('jquery.saveselection.gridview.js'); $this->componentsAfterAjaxUpdate[] = "$.fn.yiiGridView.afterUpdateGrid('".$this->id."');"; echo '<tr><td colspan="' . count($this->columns) . '">'; $this->bulk->renderButtons(); echo '</td></tr>'; } /** *### .renderChart() * * Renders chart * @throws CException */ public function renderChart() { if (!$this->displayChart || $this->dataProvider->getItemCount() <= 0) { return; } if (!isset($this->chartOptions['data']['series'])) { throw new CException(Yii::t( 'zii', 'You need to set the "series" attribute in order to render a chart' )); } $configSeries = $this->chartOptions['data']['series']; if (!is_array($configSeries)) { throw new CException(Yii::t('zii', '"chartOptions.series" is expected to be an array.')); } if (!isset($this->chartOptions['config'])) { $this->chartOptions['config'] = array(); } // **************************************** // render switch buttons $buttons = Yii::createComponent( array( 'class' => 'booster.widgets.TbButtonGroup', 'toggle' => 'radio', 'buttons' => array( array( 'label' => Yii::t('zii', 'Grid'), 'url' => '#', 'htmlOptions' => array('class' => 'active ' . $this->getId() . '-grid-control grid') ), array( 'label' => Yii::t('zii', 'Chart'), 'url' => '#', 'htmlOptions' => array('class' => $this->getId() . '-grid-control chart') ), ), 'htmlOptions' => array('style' => 'margin-bottom:5px') ) ); echo '<div>'; $buttons->init(); $buttons->run(); echo '</div>'; $chartId = preg_replace('[-\\ ?]', '_', 'exgvwChart' . $this->getId()); // cleaning out most possible characters invalid as javascript variable identifiers. $this->componentsReadyScripts[] = '$(document).on("click",".' . $this->getId() . '-grid-control", function() { $(this).parent().find("input[type=\"radio\"]").parent().toggleClass("active"); if ($(this).hasClass("grid") && $("#' . $this->getId() . ' #' . $chartId . '").is(":visible")) { $("#' . $this->getId() . ' #' . $chartId . '").hide(); $("#' . $this->getId() . ' table.items").show(); } if ($(this).hasClass("chart") && $("#' . $this->getId() . ' table.items").is(":visible")) { $("#' . $this->getId() . ' table.items").hide(); $("#' . $this->getId() . ' #' . $chartId . '").show(); } return false; });'; $this->componentsAfterAjaxUpdate[] = ' if($("label.grid.'.$this->getId().'-grid-control").hasClass("active")) { $("#' . $this->getId() . ' #' . $chartId . '").hide(); $("#' . $this->getId() . ' table.items").show(); } else { $("#' . $this->getId() . ' table.items").hide(); $("#' . $this->getId() . ' #' . $chartId . '").show(); } '; // end switch buttons // **************************************** // render Chart // chart options $data = $this->dataProvider->getData(); $count = count($data); $seriesData = array(); $cnt = 0; foreach ($configSeries as $set) { $seriesData[$cnt] = array('name' => isset($set['name']) ? $set['name'] : null, 'data' => array()); for ($row = 0; $row < $count; ++$row) { $column = $this->getColumnByName($set['attribute']); if (!is_null($column) && $column->value !== null) { $seriesData[$cnt]['data'][] = $this->evaluateExpression( $column->value, array('data' => $data[$row], 'row' => $row) ); } else { $value = CHtml::value($data[$row], $set['attribute']); $seriesData[$cnt]['data'][] = is_numeric($value) ? (float)$value : $value; } } ++$cnt; } $xAxisData = array(); $xAxisData[] = array('categories'=>array()); if(!empty($this->chartOptions['data']['xAxis'])){ $xAxis = $this->chartOptions['data']['xAxis']; $categories = $xAxis['categories']; if(is_array($categories)) { $xAxisData['categories'] = $categories; } else { // field name for ($row = 0; $row < $count; ++$row) { $column = $this->getColumnByName($categories); if (!is_null($column) && $column->value !== null) { $xAxisData['categories'][] = $this->evaluateExpression( $column->value, array('data' => $data[$row], 'row' => $row) ); } else { $value = CHtml::value($data[$row], $categories); $xAxisData['categories'][] = $value; } } } } // **************************************** // render chart $options = CMap::mergeArray( $this->chartOptions['config'], array('series' => $seriesData, 'xAxis' => $xAxisData) ); $this->chartOptions['htmlOptions'] = isset($this->chartOptions['htmlOptions']) ? $this->chartOptions['htmlOptions'] : array(); // sorry but use a class to provide styles, we need this if(empty($this->chartOptions['htmlOptions']['style'])) $this->chartOptions['htmlOptions']['style'] = 'width: 100%; height: 100%;'; else $this->chartOptions['htmlOptions']['style'] = $this->chartOptions['htmlOptions']['style'].'; width: 100%; height: 100%;'; // build unique ID // important! echo '<div>'; if ($this->ajaxUpdate !== false) { if (isset($options['chart']) && is_array($options['chart'])) { $options['chart']['renderTo'] = $chartId; } else { $options['chart'] = array('renderTo' => $chartId); } $jsOptions = CJSON::encode($options); if (isset($this->chartOptions['htmlOptions']['data-config'])) { unset($this->chartOptions['htmlOptions']['data-config']); } echo "<div id='{$chartId}' " . CHtml::renderAttributes( $this->chartOptions['htmlOptions'] ) . " data-config='{$jsOptions}'></div>"; /* fix for chart dimensions changing after ajax */ $this->componentsAfterAjaxUpdate[] = " $('#".$chartId."').width($('#".$this->id." table').width()); $('#".$chartId."').height($('#".$this->id." table').height() + 150); highchart{$chartId} = new Highcharts.Chart($('#{$chartId}').data('config')); "; } $configChart = array( 'class' => 'booster.widgets.TbHighCharts', 'id' => $chartId, 'options' => $options, 'htmlOptions' => $this->chartOptions['htmlOptions'] ); $chart = Yii::createComponent($configChart); $chart->init(); $chart->run(); echo '</div>'; // end chart display // **************************************** // check if the chart should appear by default if(isset($this->chartOptions['defaultView']) && $this->chartOptions['defaultView'] === true) { $this->componentsReadyScripts[] = ' $(".' . $this->getId() . '-grid-control.grid").removeClass("active"); $(".' . $this->getId() . '-grid-control.chart").addClass("active"); $("#' . $this->getId() . ' table.items").hide(); $("#' . $this->getId() . ' #' . $chartId . '").show(); '; } else { $this->componentsReadyScripts[] = ' $(".' . $this->getId() . '-grid-control.grid").addClass("active"); $(".' . $this->getId() . '-grid-control.chart").removeClass("active"); $("#' . $this->getId() . ' table.items").show(); $("#' . $this->getId() . ' #' . $chartId . '").hide(); '; } } /** *### .renderTableRow() * * Renders a table body row. * * @param integer $row the row number (zero-based). */ public function renderTableRow($row) { $htmlOptions = array(); if ($this->rowHtmlOptionsExpression !== null) { $data = $this->dataProvider->data[$row]; $options = $this->evaluateExpression( $this->rowHtmlOptionsExpression, array('row' => $row, 'data' => $data) ); if (is_array($options)) { $htmlOptions = $options; } } if ($this->rowCssClassExpression !== null) { $data = $this->dataProvider->data[$row]; $class = $this->evaluateExpression($this->rowCssClassExpression, array('row' => $row, 'data' => $data)); } elseif (is_array($this->rowCssClass) && ($n = count($this->rowCssClass)) > 0) { $class = $this->rowCssClass[$row % $n]; } if (!empty($class)) { if (isset($htmlOptions['class'])) { $htmlOptions['class'] .= ' ' . $class; } else { $htmlOptions['class'] = $class; } } echo CHtml::openTag('tr', $htmlOptions); foreach ($this->columns as $column) { echo $this->displayExtendedSummary && !empty($this->extendedSummary['columns']) ? $this->parseColumnValue( $column, $row ) : $column->renderDataCell($row); } echo CHtml::closeTag('tr'); } /** *### .renderExtendedSummary() * * Renders summary */ public function renderExtendedSummary() { if (!isset($this->extendedSummaryOptions['class'])) { $this->extendedSummaryOptions['class'] = $this->extendedSummaryCssClass; } else { $this->extendedSummaryOptions['class'] .= ' ' . $this->extendedSummaryCssClass; } echo '<div ' . CHtml::renderAttributes($this->extendedSummaryOptions) . '></div>'; } /** *### .renderExtendedSummaryContent() * * Renders summary content. Will be appended to */ public function renderExtendedSummaryContent() { if (($count = $this->dataProvider->getItemCount()) <= 0) { return; } if (!empty($this->extendedSummaryTypes)) { echo '<div id="' . $this->id . '-extended-summary" style="display:none">'; if (isset($this->extendedSummary['title'])) { echo '<h3>' . $this->extendedSummary['title'] . '</h3>'; } foreach ($this->extendedSummaryTypes as $summaryType) { /** @var $summaryType TbOperation */ $summaryType->run(); echo '<br/>'; } echo '</div>'; } } /** *### .registerCustomClientScript() * * This script must be run at the end of content rendering not at the beginning as it is common with normal CGridViews */ public function registerCustomClientScript() { /** @var $cs CClientScript */ $cs = Yii::app()->getClientScript(); $fixedHeaderJs = ''; if ($this->fixedHeader) { Booster::getBooster()->registerAssetJs('jquery.stickytableheaders' . (!YII_DEBUG ? '.min' : '') . '.js'); $fixedHeaderJs = "$('#{$this->id} table.items').stickyTableHeaders({fixedOffset:{$this->headerOffset}});"; $this->componentsAfterAjaxUpdate[] = $fixedHeaderJs; } if ($this->sortableRows) { $afterSortableUpdate = ''; if ($this->afterSortableUpdate !== null) { if (!($this->afterSortableUpdate instanceof CJavaScriptExpression) && strpos( $this->afterSortableUpdate, 'js:' ) !== 0 ) { $afterSortableUpdate = new CJavaScriptExpression($this->afterSortableUpdate); } else { $afterSortableUpdate = $this->afterSortableUpdate; } } $this->selectableRows = 1; $cs->registerCoreScript('jquery.ui'); Booster::getBooster()->registerAssetJs('jquery.sortable.gridview.js'); if ($this->sortableAjaxSave && $this->sortableAction !== null) { $sortableAction = Yii::app()->createUrl( $this->sortableAction, array('sortableAttribute' => $this->sortableAttribute) ); } else { $sortableAction = ''; } $afterSortableUpdate = CJavaScript::encode($afterSortableUpdate); if (Yii::app()->request->enableCsrfValidation) { $csrfTokenName = Yii::app()->request->csrfTokenName; $csrfToken = Yii::app()->request->csrfToken; $csrf = "{'$csrfTokenName':'$csrfToken' }"; } else $csrf = '{}'; $this->componentsReadyScripts[] = "$.fn.yiiGridView.sortable('{$this->id}', '{$sortableAction}', {$afterSortableUpdate}, $csrf);"; $this->componentsAfterAjaxUpdate[] = "$.fn.yiiGridView.sortable('{$this->id}', '{$sortableAction}', {$afterSortableUpdate}, $csrf);"; } if ($this->selectableCells) { $afterSelectableCells = ''; if ($this->afterSelectableCells !== null) { if (!($this->afterSelectableCells instanceof CJavaScriptExpression) && strpos($this->afterSelectableCells,'js:') !== 0) { $afterSelectableCells = new CJavaScriptExpression($this->afterSelectableCells); } else { $afterSelectableCells = $this->afterSelectableCells; } } $cs->registerCoreScript('jquery.ui'); Booster::getBooster()->registerAssetJs('jquery.selectable.gridview.js'); $afterSelectableCells = CJavaScript::encode($afterSelectableCells); $this->componentsReadyScripts[] = "$.fn.yiiGridView.selectable('{$this->id}','{$this->selectableCellsFilter}',{$afterSelectableCells});"; $this->componentsAfterAjaxUpdate[] = "$.fn.yiiGridView.selectable('{$this->id}','{$this->selectableCellsFilter}', {$afterSelectableCells});"; } $cs->registerScript( __CLASS__ . '#' . $this->id . 'Ex', ' var $grid = $("#' . $this->id . '"); ' . $fixedHeaderJs . ' if ($(".' . $this->extendedSummaryCssClass . '", $grid).length) { $(".' . $this->extendedSummaryCssClass . '", $grid).html($("#' . $this->id . '-extended-summary", $grid).html()); } ' . (count($this->componentsReadyScripts) ? implode(PHP_EOL, $this->componentsReadyScripts) : '') . ' $.ajaxPrefilter(function (options, originalOptions, jqXHR) { var qs = $.deparam.querystring(options.url); if (qs.hasOwnProperty("ajax") && qs.ajax == "' . $this->id . '") { if (typeof (options.realsuccess) == "undefined" || options.realsuccess !== options.success) { options.realsuccess = options.success; options.success = function(data) { if (options.realsuccess) { options.realsuccess(data); var $data = $("<div>" + data + "</div>"); // we need to get the grid again... as it has been updated if ($(".' . $this->extendedSummaryCssClass . '", $("#' . $this->id . '"))) { $(".' . $this->extendedSummaryCssClass . '", $("#' . $this->id . '")).html($("#' . $this->id . '-extended-summary", $data).html()); } ' . (count($this->componentsAfterAjaxUpdate) ? implode( PHP_EOL, $this->componentsAfterAjaxUpdate ) : '') . ' } } } } });' ); } /** *### .parseColumnValue() * * @param CDataColumn $column * @param integer $row the current row number * * @return string */ protected function parseColumnValue($column, $row) { ob_start(); $column->renderDataCell($row); $value = ob_get_clean(); if ($column instanceof CDataColumn && array_key_exists($column->name, $this->extendedSummary['columns'])) { // lets get the configuration $config = $this->extendedSummary['columns'][$column->name]; // add the required column object in $config['column'] = $column; // build the summary operation object $op = $this->getSummaryOperationInstance($column->name, $config); // process the value $op->processValue($value); } return $value; } /** *### .getSummaryOperationInstance() * * Each type of 'extended' summary * * @param string $name the name of the column * @param array $config the configuration of the column at the extendedSummary * * @return mixed * @throws CException */ protected function getSummaryOperationInstance($name, $config) { if (!isset($config['class'])) { throw new CException(Yii::t( 'zii', 'Column summary configuration must be an array containing a "type" element.' )); } if (!in_array($config['class'], $this->extendedSummaryOperations)) { throw new CException(Yii::t( 'zii', '"{operation}" is an unsupported class operation.', array('{operation}' => $config['class']) )); } // name of the column should be unique if (!isset($this->extendedSummaryTypes[$name])) { $this->extendedSummaryTypes[$name] = Yii::createComponent($config); $this->extendedSummaryTypes[$name]->init(); } return $this->extendedSummaryTypes[$name]; } /** *### .getColumnByName() * * Helper function to get a column by its name * * @param string $name * * @return CDataColumn|null */ protected function getColumnByName($name) { foreach ($this->columns as $column) { if (strcmp($column->name, $name) === 0) { return $column; } } return null; } } /** *## TbOperation class * * Abstract class where all types of operations extend from * * @package booster.widgets.grids.operations */ abstract class TbOperation extends CWidget { /** * @var string $template the template to display label and value of the operation at the summary */ public $template = '{label}: {value}'; /** * @var int $value the resulted value of operation */ public $value = 0; /** * @var string $label the label of the calculated value */ public $label; /** * @var TbDataColumn $column */ public $column; /** * Widget initialization * @throws CException */ public function init() { if (null == $this->column) { throw new CException(Yii::t( 'zii', '"{attribute}" attribute must be defined', array('{attribute}' => 'column') )); } } /** * Widget's run method */ public function run() { $this->displaySummary(); } /** * Process the row data value * * @param $value * * @return mixed */ abstract public function processValue($value); /** * Displays the resulting summary * @return mixed */ abstract public function displaySummary(); } /** * TbSumOperation class * * Displays a total of specified column name. * * @package booster.widgets.grids.operations */ class TbSumOperation extends TbOperation { /** * @var float $total the total sum */ protected $total; /** * @var array $supportedTypes the supported type of values */ protected $supportedTypes = array('raw', 'text', 'ntext', 'number'); /** * Widget's initialization method * @throws CException */ public function init() { parent::init(); if (!in_array($this->column->type, $this->supportedTypes)) { throw new CException(Yii::t( 'zii', 'Unsupported column type. Supported column types are: "{types}"', array( '{types}' => implode(', ', $this->supportedTypes) ) )); } } /** * Extracts the digital part of the calculated value. * * @param int $value * * @return bool */ protected function extractNumber($value) { preg_match_all('/([+-]?[0-9]+[,\.]?)+/', $value, $matches); return !empty($matches[0]) && @$matches[0][0] ? $matches[0][0] : 0; } /** * Process the value to calculate * * @param $value * * @return mixed|void */ public function processValue($value) { // remove html tags as we cannot access renderDataCellContent from the column $clean = strip_tags($value); $this->total += ((float)$this->extractNumber($clean)); } /** * Displays the summary * @return mixed|void */ public function displaySummary() { echo strtr( $this->template, array( '{label}' => $this->label, '{value}' => $this->total === null ? '' : Yii::app()->format->format($this->total, $this->column->type) ) ); } } /** * TbCountOfTypeOperation class * * Renders a summary based on the count of specified types. For example, if a value has a type 'blue', this class will * count the number of times the value 'blue' has on that column. * * @package booster.widgets.grids.operations */ class TbCountOfTypeOperation extends TbOperation { /** * @var string $template * @see parent class */ public $template = '{label}: {types}'; /** * @var string $typeTemplate holds the template of each calculated type */ public $typeTemplate = '{label}({value})'; /** * @var array $types hold the configuration of types to calculate. The configuration is set by an array which keys * are the value types to count. You can set their 'label' independently. * * <pre> * 'types' => array( * '0' => array('label' => 'zeros'), * '1' => array('label' => 'ones'), * '2' => array('label' => 'twos') * </pre> */ public $types = array(); /** * Widget's initialization * @throws CException */ public function init() { if (empty($this->types)) { throw new CException(Yii::t( 'zii', '"{attribute}" attribute must be defined', array('{attribute}' => 'types') )); } foreach ($this->types as $type) { if (!isset($type['label'])) { throw new CException(Yii::t('zii', 'The "label" of a type must be defined.')); } } parent::init(); } /** * (no phpDoc) * @see TbOperation * * @param $value * * @return mixed|void */ public function processValue($value) { $clean = strip_tags($value); if (array_key_exists($clean, $this->types)) { if (!isset($this->types[$clean]['value'])) { $this->types[$clean]['value'] = 0; } $this->types[$clean]['value'] += 1; } } /** * (no phpDoc) * @see TbOperation * @return mixed|void */ public function displaySummary() { $typesResults = array(); foreach ($this->types as $type) { if (!isset($type['value'])) { $type['value'] = 0; } $typesResults[] = strtr( $this->typeTemplate, array('{label}' => $type['label'], '{value}' => $type['value']) ); } echo strtr($this->template, array('{label}' => $this->label, '{types}' => implode(' ', $typesResults))); } } /** *## TbPercentOfTypeOperation class * * Renders a summary based on the percent count of specified types. For example, if a value has a type 'blue', this class will * count the percentage number of times the value 'blue' has on that column. * * @package booster.widgets.grids.operations */ class TbPercentOfTypeOperation extends TbCountOfTypeOperation { /** * @var string $typeTemplate * @see TbCountOfTypeOperation */ public $typeTemplate = '{label}({value}%)'; /** * @var integer $_total holds the total sum of the values. Required to get the percentage. */ protected $_total; /** * @return mixed|void * @see TbOperation */ public function displaySummary() { $typesResults = array(); foreach ($this->types as $type) { if (!isset($type['value'])) { $type['value'] = 0; } $type['value'] = $this->getTotal() ? number_format((float)($type['value'] / $this->getTotal()) * 100, 1) : 0; $typesResults[] = strtr( $this->typeTemplate, array('{label}' => $type['label'], '{value}' => $type['value']) ); } echo strtr($this->template, array('{label}' => $this->label, '{types}' => implode(' ', $typesResults))); } /** * Returns the total of types * @return int holds */ protected function getTotal() { if (null == $this->_total) { $this->_total = 0; foreach ($this->types as $type) { if (isset($type['value'])) { $this->_total += $type['value']; } } } return $this->_total; } } /** *## TbPercentOfTypeGooglePieOperation class * * Displays a Google visualization pie chart based on the percentage count of type. * * @package booster.widgets.grids.operations */ class TbPercentOfTypeGooglePieOperation extends TbPercentOfTypeOperation { /** * @var string $chartCssClass the class name of the layer holding the chart */ public $chartCssClass = 'bootstrap-operation-google-pie-chart'; /** * The options * @var array $chartOptions * @see https://google-developers.appspot.com/chart/interactive/docs/gallery/piechart */ public $chartOptions = array( 'title' => 'Google Pie Chart' ); /** * @var array $data the configuration data of the chart */ protected $data = array(); /** * @see TbOperation * @return mixed|void */ public function displaySummary() { $this->data[] = array('Label', 'Percent'); foreach ($this->types as $type) { if (!isset($type['value'])) { $type['value'] = 0; } $this->data[] = $this->getTotal() ? array( $type['label'], (float)number_format(($type['value'] / $this->getTotal()) * 100, 1) ) : 0; } $data = CJavaScript::jsonEncode($this->data); $options = CJavaScript::jsonEncode($this->chartOptions); echo "<div id='{$this->id}' class='{$this->chartCssClass}' data-data='{$data}' data-options='{$options}'></div>"; $this->registerClientScript(); } /** * Registers required scripts */ public function registerClientScript() { $chart = Yii::createComponent( array( 'class' => 'booster.widgets.TbGoogleVisualizationChart', 'visualization' => 'PieChart', 'containerId' => $this->getId(), 'data' => $this->data, 'options' => $this->chartOptions ) ); $chart->init(); $chart->run(); /** * create custom chart update by using the global chart variable * @see TbGoogleVisualizationChart */ $this->column->grid->componentsAfterAjaxUpdate[__CLASS__] = 'var $el = $("#' . $this->getId() . '");var data = $el.data("data");var opts = $el.data("options"); data = google.visualization.arrayToDataTable(data); ' . $chart->getId() . '=new google.visualization.PieChart(document.getElementById("' . $this->getId() . '")); ' . $chart->getId() . '.draw(data,opts);'; } } /** *## TbPercentOfTypeEasyPieOperation class * * Displays an chart based on jquery.easy.pie plugin * * @package booster.widgets.grids.operations */ class TbPercentOfTypeEasyPieOperation extends TbPercentOfTypeOperation { /** * @var string $chartCssClass the class of the layer containing the class */ public $chartCssClass = 'bootstrap-operation-easy-pie-chart'; /** * @var string $template * @see TbOperation */ public $template = '<div style="clear:both">{label}: </div>{types}'; /** * @var string $typeTemplate * @see parent class */ public $typeTemplate = '<div style="float:left;text-align:center;margin:2px"><div class="{class}" data-percent="{value}">{value}%</div><div>{label}</div></div>'; // easy-pie-chart plugin options // @see https://github.com/rendro/easy-pie-chart#configuration-parameter public $chartOptions = array( 'barColor' => '#ef1e25', // The color of the curcular bar. You can pass either a css valid color string like rgb, // rgba hex or string colors. But you can also pass a function that accepts the current // percentage as a value to return a dynamically generated color. 'trackColor' => '#f2f2f2', // The color of the track for the bar, false to disable rendering. 'scaleColor' => '#dfe0e0', // The color of the scale lines, false to disable rendering. 'lineCap' => 'round', // Defines how the ending of the bar line looks like. Possible values are: butt, round and square. 'lineWidth' => 5, // Width of the bar line in px. 'size' => 80, // Size of the pie chart in px. It will always be a square. 'animate' => false, // Time in milliseconds for a eased animation of the bar growing, or false to deactivate. 'onStart' => 'js:$.noop', // Callback function that is called at the start of any animation (only if animate is not false). 'onStop' => 'js:$.noop' // Callback function that is called at the end of any animation (only if animate is not false). ); /** * @see TbOperation * @return mixed|void */ public function displaySummary() { $this->typeTemplate = strtr($this->typeTemplate, array('{class}' => $this->chartCssClass)); parent::displaySummary(); $this->registerClientScripts(); } /** * Register required scripts */ protected function registerClientScripts() { $booster = Booster::getBooster(); $booster->registerAssetCss('easy-pie-chart.css'); $booster->registerAssetJs('jquery.easy.pie.chart.js'); $options = CJavaScript::encode($this->chartOptions); Yii::app()->getClientScript()->registerScript( __CLASS__ . '#percent-of-type-operation-simple-pie', ' $("#' . $this->column->grid->id . ' .' . $this->column->grid->extendedSummaryCssClass . ' .' . $this->chartCssClass . '") .easyPieChart(' . $options . '); ' ); $this->column->grid->componentsReadyScripts[__CLASS__] = $this->column->grid->componentsAfterAjaxUpdate[__CLASS__] = '$("#' . $this->column->grid->id . ' .' . $this->column->grid->extendedSummaryCssClass . ' .' . $this->chartCssClass . '") .easyPieChart(' . $options . ');'; } }
[+]
..
[-] TbTabView.php
[edit]
[-] TbNavbar.php
[edit]
[-] TbHighCharts.php
[edit]
[-] TbPager.php
[edit]
[-] TbLabel.php
[edit]
[-] TbActiveForm.php
[edit]
[-] TbPanel.php
[edit]
[-] TbJsonButtonColumn.php
[edit]
[-] TbModalManager.php
[edit]
[-] TbToggleColumn.php
[edit]
[-] TbUiLayout.php
[edit]
[-] TbBaseMenu.php
[edit]
[-] widgets.md
[edit]
[-] TbEditableDetailView.php
[edit]
[-] TbButtonGroup.php
[edit]
[-] TbEditable.php
[edit]
[-] TbFileUpload.php
[edit]
[-] TbEditableField.php
[edit]
[-] TbTags.php
[edit]
[-] TbFormButtonElement.php
[edit]
[-] TbFormInputElement.php
[edit]
[-] TbJsonPager.php
[edit]
[-] TbCollapse.php
[edit]
[-] TbBulkActions.php
[edit]
[-] TbRelationalColumn.php
[edit]
[-] TbTabs.php
[edit]
[-] TbModal.php
[edit]
[-] TbExtendedGridView.php
[edit]
[-] TbJumbotron.php
[edit]
[-] TbImageGallery.php
[edit]
[-] TbWidget.php
[edit]
[-] TbMenu.php
[edit]
[-] TbProgress.php
[edit]
[-] TbButtonColumn.php
[edit]
[-] TbJsonToggleColumn.php
[edit]
[-] TbJsonPickerColumn.php
[edit]
[-] TbColorPicker.php
[edit]
[-] TbButtonGroupColumn.php
[edit]
[-] TbSwitch.php
[edit]
[-] TbHeroUnit.php
[edit]
[-] TbDatePicker.php
[edit]
[-] TbJsonCheckBoxColumn.php
[edit]
[-] TbButton.php
[edit]
[-] TbTypeahead.php
[edit]
[-] TbForm.php
[edit]
[-] TbPassfield.php
[edit]
[-] TbEditableColumn.php
[edit]
[-] TbMarkdownEditorJs.php
[edit]
[-] TbPopoverColumn.php
[edit]
[-] TbMarkdownEditor.php
[edit]
[-] TbAlert.php
[edit]
[-] TbGroupGridView.php
[edit]
[-] TbBaseInputWidget.php
[edit]
[-] TbWizard.php
[edit]
[-] TbGroupButtonColumn.php
[edit]
[-] TbExtendedFilter.php
[edit]
[-] TbGoogleVisualizationChart.php
[edit]
[-] TbHtml5Editor.php
[edit]
[-] TbGridView.php
[edit]
[-] TbListView.php
[edit]
[-] TbTimePicker.php
[edit]
[-] TbJsonGridView.php
[edit]
[-] TbCKEditor.php
[edit]
[-] TbTotalSumColumn.php
[edit]
[-] TbDateRangePicker.php
[edit]
[-] TbBadge.php
[edit]
[-] TbDropdown.php
[edit]
[-] TbBreadcrumbs.php
[edit]
[-] TbJsonGridColumn.php
[edit]
[-] TbDateTimePicker.php
[edit]
[-] TbDataColumn.php
[edit]
[-] TbCarousel.php
[edit]
[-] TbJsonDataColumn.php
[edit]
[-] TbRedactorJs.php
[edit]
[-] TbThumbnails.php
[edit]
[-] TbScrollSpy.php
[edit]
[-] TbDetailView.php
[edit]
[+]
input
[-] TbSelect2.php
[edit]
[-] TbImageColumn.php
[edit]