10 Yii::import(
'booster.widgets.TbGridView');
46 public $template =
"{summary}\n{items}\n{pager}\n{extendedSummary}";
197 'TbCountOfTypeOperation',
198 'TbPercentOfTypeOperation',
199 'TbPercentOfTypeEasyPieOperation',
200 'TbPercentOfTypeGooglePieOperation'
211 '/extendedsummary/i',
213 ) && !empty($this->extendedSummary) && isset($this->extendedSummary[
'columns'])
215 $this->
template .=
"\n{extendedSummaryContent}";
216 $this->displayExtendedSummary =
true;
218 if (!empty($this->chartOptions) && @$this->chartOptions[
'data'] && $this->dataProvider->getItemCount()) {
219 $this->displayChart =
true;
221 if ($this->bulkActions !== array() && isset($this->bulkActions[
'actionButtons'])) {
222 if (!isset($this->bulkActions[
'class'])) {
223 $this->bulkActions[
'class'] =
'booster.widgets.TbBulkActions';
226 $this->bulk = Yii::createComponent($this->bulkActions, $this);
230 $this->selectionChanged =
'js:function(id) {
231 $("#"+id+" input[type=checkbox]").change();
245 $this->registerCustomClientScript();
255 $data = $this->dataProvider->getData();
257 if (!$this->sortableRows || (isset(
$data[0]) && !isset(
$data[0]->attributes[(
string)$this->sortableAttribute]))) {
265 'style' =>
'display:none',
266 'title' => Yii::app()->getRequest()->getUrl(),
269 foreach (
$data as $d) {
272 array(
'data-order' => $this->
getAttribute($d, $this->sortableAttribute)),
292 if ($this->dataProvider instanceof CActiveDataProvider &&
$data->hasAttribute($attribute)) {
293 return $data->{$attribute};
296 if ($this->dataProvider instanceof CArrayDataProvider || $this->dataProvider instanceof CSqlDataProvider) {
297 if (is_object(
$data) && isset(
$data->{$attribute})) {
298 return $data->{$attribute};
300 if (isset(
$data[$attribute])) {
301 return $data[$attribute];
317 protected function getPrimaryKey(
$data)
319 if ($this->dataProvider instanceof CActiveDataProvider) {
320 $key = $this->dataProvider->keyAttribute === null ?
$data->getPrimaryKey() :
$data->{$this->dataProvider->keyAttribute};
321 return is_array($key) ? implode(
',', $key) : $key;
323 if (($this->dataProvider instanceof CArrayDataProvider || $this->dataProvider instanceof CSqlDataProvider) && !empty($this->dataProvider->keyField)) {
324 return is_object(
$data) ?
$data->{$this->dataProvider->keyField}
325 :
$data[$this->dataProvider->keyField];
338 $this->renderChart();
339 parent::renderTableHeader();
347 public function renderTableFooter()
349 $hasFilter = $this->filter !== null && $this->filterPosition === self::FILTER_POS_FOOTER;
351 $hasFooter = $this->getHasFooter();
352 if ($this->bulk !== null || $hasFilter || $hasFooter) {
357 foreach ($this->columns as $column) {
358 $column->renderFooterCell();
363 $this->renderFilter();
366 if ($this->bulk !== null) {
367 $this->renderBulkActions();
379 $this->componentsAfterAjaxUpdate[] =
"$.fn.yiiGridView.afterUpdateGrid('".$this->id.
"');";
380 echo
'<tr><td colspan="' . count($this->columns) .
'">';
381 $this->bulk->renderButtons();
394 if (!$this->displayChart || $this->dataProvider->getItemCount() <= 0) {
398 if (!isset($this->chartOptions[
'data'][
'series'])) {
399 throw new CException(Yii::t(
401 'You need to set the "series" attribute in order to render a chart'
405 $configSeries = $this->chartOptions[
'data'][
'series'];
406 if (!is_array($configSeries)) {
407 throw new CException(Yii::t(
'zii',
'"chartOptions.series" is expected to be an array.'));
411 if (!isset($this->chartOptions[
'config'])) {
412 $this->chartOptions[
'config'] = array();
417 $buttons = Yii::createComponent(
419 'class' =>
'booster.widgets.TbButtonGroup',
423 'label' => Yii::t(
'zii',
'Grid'),
425 'htmlOptions' => array(
'class' =>
'active ' . $this->getId() .
'-grid-control grid')
428 'label' => Yii::t(
'zii',
'Chart'),
430 'htmlOptions' => array(
'class' => $this->getId() .
'-grid-control chart')
433 'htmlOptions' => array(
'style' =>
'margin-bottom:5px')
441 $chartId = preg_replace(
'[-\\ ?]',
'_',
'exgvwChart' . $this->getId());
443 $this->componentsReadyScripts[] =
'$(document).on("click",".' . $this->getId() .
'-grid-control", function() {
444 $(this).parent().find("input[type=\"radio\"]").parent().toggleClass("active");
445 if ($(this).hasClass("grid") && $("#' . $this->getId() .
' #' . $chartId .
'").is(":visible"))
447 $("#' . $this->getId() .
' #' . $chartId .
'").hide();
448 $("#' . $this->getId() .
' table.items").show();
450 if ($(this).hasClass("chart") && $("#' . $this->getId() .
' table.items").is(":visible"))
452 $("#' . $this->getId() .
' table.items").hide();
453 $("#' . $this->getId() .
' #' . $chartId .
'").show();
458 $this->componentsAfterAjaxUpdate[] =
'
459 if($("label.grid.'.$this->getId().
'-grid-control").hasClass("active")) {
460 $("#' . $this->getId() .
' #' . $chartId .
'").hide();
461 $("#' . $this->getId() .
' table.items").show();
463 $("#' . $this->getId() .
' table.items").hide();
464 $("#' . $this->getId() .
' #' . $chartId .
'").show();
472 $data = $this->dataProvider->getData();
474 $seriesData = array();
476 foreach ($configSeries as $set) {
477 $seriesData[$cnt] = array(
'name' => isset($set[
'name']) ? $set[
'name'] : null,
'data' => array());
479 for ($row = 0; $row <
$count; ++$row) {
480 $column = $this->getColumnByName($set[
'attribute']);
481 if (!is_null($column) && $column->value !== null) {
482 $seriesData[$cnt][
'data'][] = $this->evaluateExpression(
484 array(
'data' =>
$data[$row],
'row' => $row)
488 $seriesData[$cnt][
'data'][] = is_numeric($value) ? (float)$value : $value;
497 $xAxisData[] = array(
'categories'=>array());
498 if(!empty($this->chartOptions[
'data'][
'xAxis'])){
499 $xAxis = $this->chartOptions[
'data'][
'xAxis'];
500 $categories = $xAxis[
'categories'];
501 if(is_array($categories)) {
502 $xAxisData[
'categories'] = $categories;
504 for ($row = 0; $row <
$count; ++$row) {
505 $column = $this->getColumnByName($categories);
506 if (!is_null($column) && $column->value !== null) {
507 $xAxisData[
'categories'][] = $this->evaluateExpression(
509 array(
'data' =>
$data[$row],
'row' => $row)
513 $xAxisData[
'categories'][] = $value;
521 $options = CMap::mergeArray(
522 $this->chartOptions[
'config'],
523 array(
'series' => $seriesData,
'xAxis' => $xAxisData)
525 $this->chartOptions[
'htmlOptions'] = isset($this->chartOptions[
'htmlOptions'])
526 ? $this->chartOptions[
'htmlOptions'] : array();
529 if(empty($this->chartOptions[
'htmlOptions'][
'style']))
530 $this->chartOptions[
'htmlOptions'][
'style'] =
'width: 100%; height: 100%;';
532 $this->chartOptions[
'htmlOptions'][
'style'] = $this->chartOptions[
'htmlOptions'][
'style'].
'; width: 100%; height: 100%;';
537 if ($this->ajaxUpdate !==
false) {
538 if (isset($options[
'chart']) && is_array($options[
'chart'])) {
539 $options[
'chart'][
'renderTo'] = $chartId;
541 $options[
'chart'] = array(
'renderTo' => $chartId);
543 $jsOptions = CJSON::encode($options);
545 if (isset($this->chartOptions[
'htmlOptions'][
'data-config'])) {
546 unset($this->chartOptions[
'htmlOptions'][
'data-config']);
549 echo
"<div id='{$chartId}' " . CHtml::renderAttributes(
550 $this->chartOptions[
'htmlOptions']
551 ) .
" data-config='{$jsOptions}'></div>";
554 $this->componentsAfterAjaxUpdate[] =
"
555 $('#".$chartId.
"').width($('#".$this->
id.
" table').width());
556 $('#".$chartId.
"').height($('#".$this->
id.
" table').height() + 150);
557 highchart{$chartId} = new Highcharts.Chart($('#{$chartId}').data('config'));
560 $configChart = array(
561 'class' =>
'booster.widgets.TbHighCharts',
563 'options' => $options,
564 'htmlOptions' => $this->chartOptions[
'htmlOptions']
566 $chart = Yii::createComponent($configChart);
574 if(isset($this->chartOptions[
'defaultView']) && $this->chartOptions[
'defaultView'] ===
true) {
575 $this->componentsReadyScripts[] =
'
576 $(".' . $this->getId() .
'-grid-control.grid").removeClass("active");
577 $(".' . $this->getId() .
'-grid-control.chart").addClass("active");
578 $("#' . $this->getId() .
' table.items").hide();
579 $("#' . $this->getId() .
' #' . $chartId .
'").show();
582 $this->componentsReadyScripts[] =
'
583 $(".' . $this->getId() .
'-grid-control.grid").addClass("active");
584 $(".' . $this->getId() .
'-grid-control.chart").removeClass("active");
585 $("#' . $this->getId() .
' table.items").show();
586 $("#' . $this->getId() .
' #' . $chartId .
'").hide();
598 public function renderTableRow($row)
600 $htmlOptions = array();
601 if ($this->rowHtmlOptionsExpression !== null) {
602 $data = $this->dataProvider->data[$row];
603 $options = $this->evaluateExpression(
604 $this->rowHtmlOptionsExpression,
605 array(
'row' => $row,
'data' =>
$data)
607 if (is_array($options)) {
608 $htmlOptions = $options;
612 if ($this->rowCssClassExpression !== null) {
613 $data = $this->dataProvider->data[$row];
614 $class = $this->evaluateExpression($this->rowCssClassExpression, array(
'row' => $row,
'data' =>
$data));
615 } elseif (is_array($this->rowCssClass) && ($n = count($this->rowCssClass)) > 0) {
616 $class = $this->rowCssClass[$row % $n];
620 if (isset($htmlOptions[
'class'])) {
621 $htmlOptions[
'class'] .=
' ' .
$class;
623 $htmlOptions[
'class'] =
$class;
627 echo CHtml::openTag(
'tr', $htmlOptions);
628 foreach ($this->columns as $column) {
629 echo $this->displayExtendedSummary && !empty($this->extendedSummary[
'columns']) ? $this->parseColumnValue(
632 ) : $column->renderDataCell($row);
635 echo CHtml::closeTag(
'tr');
643 public function renderExtendedSummary()
645 if (!isset($this->extendedSummaryOptions[
'class'])) {
646 $this->extendedSummaryOptions[
'class'] = $this->extendedSummaryCssClass;
648 $this->extendedSummaryOptions[
'class'] .=
' ' . $this->extendedSummaryCssClass;
651 echo
'<div ' . CHtml::renderAttributes($this->extendedSummaryOptions) .
'></div>';
659 public function renderExtendedSummaryContent()
661 if ((
$count = $this->dataProvider->getItemCount()) <= 0) {
665 if (!empty($this->extendedSummaryTypes)) {
666 echo
'<div id="' . $this->
id .
'-extended-summary" style="display:none">';
667 if (isset($this->extendedSummary[
'title'])) {
668 echo
'<h3>' . $this->extendedSummary[
'title'] .
'</h3>';
670 foreach ($this->extendedSummaryTypes as $summaryType) {
684 public function registerCustomClientScript()
687 $cs = Yii::app()->getClientScript();
690 if ($this->fixedHeader) {
691 Booster::getBooster()->registerAssetJs(
'jquery.stickytableheaders' . (!YII_DEBUG ?
'.min' :
'') .
'.js');
692 $fixedHeaderJs =
"$('#{$this->id} table.items').stickyTableHeaders({fixedOffset:{$this->headerOffset}});";
693 $this->componentsAfterAjaxUpdate[] = $fixedHeaderJs;
696 if ($this->sortableRows) {
697 $afterSortableUpdate =
'';
698 if ($this->afterSortableUpdate !== null) {
699 if (!($this->afterSortableUpdate instanceof CJavaScriptExpression) && strpos(
700 $this->afterSortableUpdate,
704 $afterSortableUpdate =
new CJavaScriptExpression($this->afterSortableUpdate);
706 $afterSortableUpdate = $this->afterSortableUpdate;
710 $this->selectableRows = 1;
711 $cs->registerCoreScript(
'jquery.ui');
714 if ($this->sortableAjaxSave && $this->sortableAction !== null) {
715 $sortableAction = Yii::app()->createUrl(
716 $this->sortableAction,
717 array(
'sortableAttribute' => $this->sortableAttribute)
720 $sortableAction =
'';
723 $afterSortableUpdate = CJavaScript::encode($afterSortableUpdate);
724 if (Yii::app()->request->enableCsrfValidation)
726 $csrfTokenName = Yii::app()->request->csrfTokenName;
727 $csrfToken = Yii::app()->request->csrfToken;
728 $csrf =
"{'$csrfTokenName':'$csrfToken' }";
732 $this->componentsReadyScripts[] =
"$.fn.yiiGridView.sortable('{$this->id}', '{$sortableAction}', {$afterSortableUpdate}, $csrf);";
733 $this->componentsAfterAjaxUpdate[] =
"$.fn.yiiGridView.sortable('{$this->id}', '{$sortableAction}', {$afterSortableUpdate}, $csrf);";
736 if ($this->selectableCells) {
737 $afterSelectableCells =
'';
738 if ($this->afterSelectableCells !== null) {
739 if (!($this->afterSelectableCells instanceof CJavaScriptExpression) && strpos($this->afterSelectableCells,
'js:') !== 0) {
740 $afterSelectableCells =
new CJavaScriptExpression($this->afterSelectableCells);
742 $afterSelectableCells = $this->afterSelectableCells;
745 $cs->registerCoreScript(
'jquery.ui');
747 $afterSelectableCells = CJavaScript::encode($afterSelectableCells);
748 $this->componentsReadyScripts[] =
"$.fn.yiiGridView.selectable('{$this->id}','{$this->selectableCellsFilter}',{$afterSelectableCells});";
749 $this->componentsAfterAjaxUpdate[] =
"$.fn.yiiGridView.selectable('{$this->id}','{$this->selectableCellsFilter}', {$afterSelectableCells});";
753 __CLASS__ .
'#' . $this->
id .
'Ex',
755 var $grid = $("#' . $this->
id .
'");
756 ' . $fixedHeaderJs .
'
757 if ($(".' . $this->extendedSummaryCssClass .
'", $grid).length)
759 $(".' . $this->extendedSummaryCssClass .
'", $grid).html($("#' . $this->
id .
'-extended-summary", $grid).html());
761 ' . (count($this->componentsReadyScripts) ? implode(PHP_EOL, $this->componentsReadyScripts) :
'') .
'
762 $.ajaxPrefilter(function (options, originalOptions, jqXHR) {
763 var qs = $.deparam.querystring(options.url);
764 if (qs.hasOwnProperty("ajax") && qs.ajax == "' . $this->
id .
'")
766 if (typeof (options.realsuccess) == "undefined" || options.realsuccess !== options.success)
768 options.realsuccess = options.success;
769 options.success = function(data)
771 if (options.realsuccess) {
772 options.realsuccess(data);
773 var $data = $("<div>" + data + "</div>");
774 // we need to get the grid again... as it has been updated
775 if ($(".' . $this->extendedSummaryCssClass .
'", $("#' . $this->
id .
'")))
777 $(".' . $this->extendedSummaryCssClass .
'", $("#' . $this->
id .
'")).html($("#' . $this->
id .
'-extended-summary", $data).html());
779 ' . (count($this->componentsAfterAjaxUpdate) ? implode(
781 $this->componentsAfterAjaxUpdate
799 protected function parseColumnValue($column, $row)
802 $column->renderDataCell($row);
803 $value = ob_get_clean();
805 if ($column instanceof CDataColumn && array_key_exists($column->name, $this->extendedSummary[
'columns'])) {
807 $config = $this->extendedSummary[
'columns'][$column->name];
811 $op = $this->getSummaryOperationInstance($column->name,
$config);
813 $op->processValue($value);
829 protected function getSummaryOperationInstance($name,
$config)
831 if (!isset(
$config[
'class'])) {
832 throw new CException(Yii::t(
834 'Column summary configuration must be an array containing a "type" element.'
838 if (!in_array(
$config[
'class'], $this->extendedSummaryOperations)) {
839 throw new CException(Yii::t(
841 '"{operation}" is an unsupported class operation.',
842 array(
'{operation}' =>
$config[
'class'])
847 if (!isset($this->extendedSummaryTypes[$name])) {
848 $this->extendedSummaryTypes[$name] = Yii::createComponent(
$config);
849 $this->extendedSummaryTypes[$name]->init();
851 return $this->extendedSummaryTypes[$name];
863 protected function getColumnByName($name)
865 foreach ($this->columns as $column) {
866 if (strcmp($column->name, $name) === 0) {
887 public $template =
'{label}: {value}';
908 public function init()
910 if (null == $this->column) {
911 throw new CException(Yii::t(
913 '"{attribute}" attribute must be defined',
914 array(
'{attribute}' =>
'column')
922 public function run()
924 $this->displaySummary();
934 abstract public function processValue($value);
940 abstract public function displaySummary();
961 protected $supportedTypes = array(
'raw',
'text',
'ntext',
'number');
967 public function init()
971 if (!in_array($this->column->type, $this->supportedTypes)) {
972 throw new CException(Yii::t(
974 'Unsupported column type. Supported column types are: "{types}"',
976 '{types}' => implode(
', ', $this->supportedTypes)
989 protected function extractNumber($value)
991 preg_match_all(
'/([+-]?[0-9]+[,\.]?)+/', $value, $matches);
992 return !empty($matches[0]) && @$matches[0][0] ? $matches[0][0] : 0;
1002 public function processValue($value)
1005 $clean = strip_tags($value);
1006 $this->total += ((float)$this->extractNumber($clean));
1013 public function displaySummary()
1018 '{label}' => $this->label,
1019 '{value}' => $this->total === null ?
'' : Yii::app()->format->format($this->total, $this->column->type)
1039 public $template =
'{label}: {types}';
1044 public $typeTemplate =
'{label}({value})';
1057 public $types = array();
1063 public function init()
1065 if (empty($this->types)) {
1066 throw new CException(Yii::t(
1068 '"{attribute}" attribute must be defined',
1069 array(
'{attribute}' =>
'types')
1072 foreach ($this->types as $type) {
1073 if (!isset($type[
'label'])) {
1074 throw new CException(Yii::t(
'zii',
'The "label" of a type must be defined.'));
1088 public function processValue($value)
1090 $clean = strip_tags($value);
1092 if (array_key_exists($clean, $this->types)) {
1093 if (!isset($this->types[$clean][
'value'])) {
1094 $this->types[$clean][
'value'] = 0;
1096 $this->types[$clean][
'value'] += 1;
1105 public function displaySummary()
1107 $typesResults = array();
1108 foreach ($this->types as $type) {
1109 if (!isset($type[
'value'])) {
1113 $typesResults[] = strtr(
1114 $this->typeTemplate,
1115 array(
'{label}' => $type[
'label'],
'{value}' => $type[
'value'])
1118 echo strtr($this->
template, array(
'{label}' => $this->label,
'{types}' => implode(
' ', $typesResults)));
1136 public $typeTemplate =
'{label}({value}%)';
1147 public function displaySummary()
1149 $typesResults = array();
1151 foreach ($this->types as $type) {
1152 if (!isset($type[
'value'])) {
1156 $type[
'value'] = $this->getTotal() ? number_format((
float)($type[
'value'] / $this->getTotal()) * 100, 1)
1158 $typesResults[] = strtr(
1159 $this->typeTemplate,
1160 array(
'{label}' => $type[
'label'],
'{value}' => $type[
'value'])
1164 echo strtr($this->
template, array(
'{label}' => $this->label,
'{types}' => implode(
' ', $typesResults)));
1171 protected function getTotal()
1173 if (null == $this->_total) {
1175 foreach ($this->types as $type) {
1176 if (isset($type[
'value'])) {
1177 $this->_total += $type[
'value'];
1181 return $this->_total;
1197 public $chartCssClass =
'bootstrap-operation-google-pie-chart';
1204 public $chartOptions = array(
1205 'title' =>
'Google Pie Chart'
1219 $this->data[] = array(
'Label',
'Percent');
1221 foreach ($this->types as $type) {
1222 if (!isset($type[
'value'])) {
1226 $this->data[] = $this->getTotal() ? array(
1228 (
float)number_format(($type[
'value'] / $this->getTotal()) * 100, 1)
1231 $data = CJavaScript::jsonEncode($this->data);
1232 $options = CJavaScript::jsonEncode($this->chartOptions);
1233 echo
"<div id='{$this->id}' class='{$this->chartCssClass}' data-data='{$data}' data-options='{$options}'></div>";
1235 $this->registerClientScript();
1241 public function registerClientScript()
1243 $chart = Yii::createComponent(
1245 'class' =>
'booster.widgets.TbGoogleVisualizationChart',
1246 'visualization' =>
'PieChart',
1247 'containerId' => $this->getId(),
1248 'data' => $this->data,
1249 'options' => $this->chartOptions
1259 $this->column->grid->componentsAfterAjaxUpdate[__CLASS__] =
1260 'var $el = $("#' . $this->getId() .
'");var data = $el.data("data");var opts = $el.data("options");
1261 data = google.visualization.arrayToDataTable(data);
1262 ' . $chart->getId() .
'=new google.visualization.PieChart(document.getElementById("' . $this->getId() .
'"));
1263 ' . $chart->getId() .
'.draw(data,opts);';
1280 public $chartCssClass =
'bootstrap-operation-easy-pie-chart';
1286 public $template =
'<div style="clear:both">{label}: </div>{types}';
1292 public $typeTemplate =
'<div style="float:left;text-align:center;margin:2px"><div class="{class}" data-percent="{value}">{value}%</div><div>{label}</div></div>';
1296 public $chartOptions = array(
1297 'barColor' =>
'#ef1e25',
1301 'trackColor' =>
'#f2f2f2',
1303 'scaleColor' =>
'#dfe0e0',
1305 'lineCap' =>
'round',
1313 'onStart' =>
'js:$.noop',
1315 'onStop' =>
'js:$.noop'
1323 public function displaySummary()
1325 $this->typeTemplate = strtr($this->typeTemplate, array(
'{class}' => $this->chartCssClass));
1327 parent::displaySummary();
1328 $this->registerClientScripts();
1334 protected function registerClientScripts()
1337 $booster->registerAssetCss(
'easy-pie-chart.css');
1338 $booster->registerAssetJs(
'jquery.easy.pie.chart.js');
1340 $options = CJavaScript::encode($this->chartOptions);
1341 Yii::app()->getClientScript()->registerScript(
1342 __CLASS__ .
'#percent-of-type-operation-simple-pie',
1344 $("#' . $this->column->grid->id .
' .' . $this->column->grid->extendedSummaryCssClass .
' .' . $this->chartCssClass .
'")
1345 .easyPieChart(' . $options .
');
1348 $this->column->grid->componentsReadyScripts[__CLASS__] =
1349 $this->column->grid->componentsAfterAjaxUpdate[__CLASS__] =
1350 '$("#' . $this->column->grid->id .
' .' . $this->column->grid->extendedSummaryCssClass .
' .' . $this->chartCssClass .
'")
1351 .easyPieChart(' . $options .
');';