HCE Project DC service web UI  0.2
Hierarchical Cluster Engine DC service web UI
 All Classes Namespaces Files Functions Variables Pages
TbExtendedGridView.php
Go to the documentation of this file.
1 <?php
10 Yii::import('booster.widgets.TbGridView');
11 
28 
32  public $fixedHeader = false;
33 
39  public $headerOffset = 0;
40 
46  public $template = "{summary}\n{items}\n{pager}\n{extendedSummary}";
47 
77  public $extendedSummary = array();
78 
82  public $extendedSummaryCssClass = 'extended-summary';
83 
87  public $extendedSummaryOptions = array();
88 
94  public $componentsAfterAjaxUpdate = array();
95 
101  public $componentsReadyScripts = array();
102 
106  public $chartOptions = array();
107 
111  public $sortableRows = false;
112 
116  public $sortableAttribute = 'sort_order';
117 
122  public $sortableAjaxSave = false;
123 
136 
143 
147  public $selectableCells = false;
148 
154  public $selectableCellsFilter = 'td';
155 
165  public $bulkActions = array();
166 
170  public $bulkActionAlign = 'right';
171 
175  protected $bulk;
176 
185  protected $displayChart;
186 
190  protected $extendedSummaryTypes = array();
191 
195  protected $extendedSummaryOperations = array(
196  'TbSumOperation',
197  'TbCountOfTypeOperation',
198  'TbPercentOfTypeOperation',
199  'TbPercentOfTypeEasyPieOperation',
200  'TbPercentOfTypeGooglePieOperation'
201  );
202 
208  public function init(){
209 
210  if (preg_match(
211  '/extendedsummary/i',
212  $this->template
213  ) && !empty($this->extendedSummary) && isset($this->extendedSummary['columns'])
214  ) {
215  $this->template .= "\n{extendedSummaryContent}";
216  $this->displayExtendedSummary = true;
217  }
218  if (!empty($this->chartOptions) && @$this->chartOptions['data'] && $this->dataProvider->getItemCount()) {
219  $this->displayChart = true;
220  }
221  if ($this->bulkActions !== array() && isset($this->bulkActions['actionButtons'])) {
222  if (!isset($this->bulkActions['class'])) {
223  $this->bulkActions['class'] = 'booster.widgets.TbBulkActions';
224  }
225 
226  $this->bulk = Yii::createComponent($this->bulkActions, $this);
227  $this->bulk->init();
228  }
229  // if(isset($this->bulkActions['selectableEqualsChecked']) && $this->bulkActions['selectableEqualsChecked'] === true) {
230  $this->selectionChanged = 'js:function(id) {
231  $("#"+id+" input[type=checkbox]").change();
232  }';
233  // }
234  parent::init();
235  }
236 
242  public function renderContent()
243  {
245  $this->registerCustomClientScript();
246  }
247 
253  public function renderKeys()
254  {
255  $data = $this->dataProvider->getData();
256 
257  if (!$this->sortableRows || (isset($data[0]) && !isset($data[0]->attributes[(string)$this->sortableAttribute]))) {
259  }
260 
261  echo CHtml::openTag(
262  'div',
263  array(
264  'class' => 'keys',
265  'style' => 'display:none',
266  'title' => Yii::app()->getRequest()->getUrl(),
267  )
268  );
269  foreach ($data as $d) {
270  echo CHtml::tag(
271  'span',
272  array('data-order' => $this->getAttribute($d, $this->sortableAttribute)),
273  CHtml::encode($this->getPrimaryKey($d))
274  );
275  }
276  echo "</div>\n";
277  return true;
278  }
279 
290  protected function getAttribute($data, $attribute)
291  {
292  if ($this->dataProvider instanceof CActiveDataProvider && $data->hasAttribute($attribute)) {
293  return $data->{$attribute};
294  }
295 
296  if ($this->dataProvider instanceof CArrayDataProvider || $this->dataProvider instanceof CSqlDataProvider) {
297  if (is_object($data) && isset($data->{$attribute})) {
298  return $data->{$attribute};
299  }
300  if (isset($data[$attribute])) {
301  return $data[$attribute];
302  }
303  }
304  return null;
305  }
306 
317  protected function getPrimaryKey($data)
318  {
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;
322  }
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];
326  }
327 
328  return null;
329  }
330 
336  public function renderTableHeader() {
337 
338  $this->renderChart();
339  parent::renderTableHeader();
340  }
341 
347  public function renderTableFooter()
348  {
349  $hasFilter = $this->filter !== null && $this->filterPosition === self::FILTER_POS_FOOTER;
350 
351  $hasFooter = $this->getHasFooter();
352  if ($this->bulk !== null || $hasFilter || $hasFooter) {
353  echo "<tfoot>\n";
354  if ($hasFooter) {
355  echo "<tr>\n";
357  foreach ($this->columns as $column) {
358  $column->renderFooterCell();
359  }
360  echo "</tr>\n";
361  }
362  if ($hasFilter) {
363  $this->renderFilter();
364  }
365 
366  if ($this->bulk !== null) {
367  $this->renderBulkActions();
368  }
369  echo "</tfoot>\n";
370  }
371  }
372 
376  public function renderBulkActions() {
377 
378  Booster::getBooster()->registerAssetJs('jquery.saveselection.gridview.js');
379  $this->componentsAfterAjaxUpdate[] = "$.fn.yiiGridView.afterUpdateGrid('".$this->id."');";
380  echo '<tr><td colspan="' . count($this->columns) . '">';
381  $this->bulk->renderButtons();
382  echo '</td></tr>';
383  }
384 
385 
392  public function renderChart() {
393 
394  if (!$this->displayChart || $this->dataProvider->getItemCount() <= 0) {
395  return;
396  }
397 
398  if (!isset($this->chartOptions['data']['series'])) {
399  throw new CException(Yii::t(
400  'zii',
401  'You need to set the "series" attribute in order to render a chart'
402  ));
403  }
404 
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.'));
408  }
409 
410 
411  if (!isset($this->chartOptions['config'])) {
412  $this->chartOptions['config'] = array();
413  }
414 
415  // ****************************************
416  // render switch buttons
417  $buttons = Yii::createComponent(
418  array(
419  'class' => 'booster.widgets.TbButtonGroup',
420  'toggle' => 'radio',
421  'buttons' => array(
422  array(
423  'label' => Yii::t('zii', 'Grid'),
424  'url' => '#',
425  'htmlOptions' => array('class' => 'active ' . $this->getId() . '-grid-control grid')
426  ),
427  array(
428  'label' => Yii::t('zii', 'Chart'),
429  'url' => '#',
430  'htmlOptions' => array('class' => $this->getId() . '-grid-control chart')
431  ),
432  ),
433  'htmlOptions' => array('style' => 'margin-bottom:5px')
434  )
435  );
436  echo '<div>';
437  $buttons->init();
438  $buttons->run();
439  echo '</div>';
440 
441  $chartId = preg_replace('[-\\ ?]', '_', 'exgvwChart' . $this->getId()); // cleaning out most possible characters invalid as javascript variable identifiers.
442 
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"))
446  {
447  $("#' . $this->getId() . ' #' . $chartId . '").hide();
448  $("#' . $this->getId() . ' table.items").show();
449  }
450  if ($(this).hasClass("chart") && $("#' . $this->getId() . ' table.items").is(":visible"))
451  {
452  $("#' . $this->getId() . ' table.items").hide();
453  $("#' . $this->getId() . ' #' . $chartId . '").show();
454  }
455  return false;
456  });';
457 
458  $this->componentsAfterAjaxUpdate[] = '
459  if($("label.grid.'.$this->getId().'-grid-control").hasClass("active")) {
460  $("#' . $this->getId() . ' #' . $chartId . '").hide();
461  $("#' . $this->getId() . ' table.items").show();
462  } else {
463  $("#' . $this->getId() . ' table.items").hide();
464  $("#' . $this->getId() . ' #' . $chartId . '").show();
465  }
466  ';
467  // end switch buttons
468  // ****************************************
469 
470  // render Chart
471  // chart options
472  $data = $this->dataProvider->getData();
473  $count = count($data);
474  $seriesData = array();
475  $cnt = 0;
476  foreach ($configSeries as $set) {
477  $seriesData[$cnt] = array('name' => isset($set['name']) ? $set['name'] : null, 'data' => array());
478 
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(
483  $column->value,
484  array('data' => $data[$row], 'row' => $row)
485  );
486  } else {
487  $value = CHtml::value($data[$row], $set['attribute']);
488  $seriesData[$cnt]['data'][] = is_numeric($value) ? (float)$value : $value;
489  }
490 
491  }
492  ++$cnt;
493  }
494 
495  $xAxisData = [];
496 
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;
503  } else { // field name
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(
508  $column->value,
509  array('data' => $data[$row], 'row' => $row)
510  );
511  } else {
512  $value = CHtml::value($data[$row], $categories);
513  $xAxisData['categories'][] = $value;
514  }
515  }
516  }
517  }
518 
519  // ****************************************
520  // render chart
521  $options = CMap::mergeArray(
522  $this->chartOptions['config'],
523  array('series' => $seriesData, 'xAxis' => $xAxisData)
524  );
525  $this->chartOptions['htmlOptions'] = isset($this->chartOptions['htmlOptions'])
526  ? $this->chartOptions['htmlOptions'] : array();
527 
528  // sorry but use a class to provide styles, we need this
529  if(empty($this->chartOptions['htmlOptions']['style']))
530  $this->chartOptions['htmlOptions']['style'] = 'width: 100%; height: 100%;';
531  else
532  $this->chartOptions['htmlOptions']['style'] = $this->chartOptions['htmlOptions']['style'].'; width: 100%; height: 100%;';
533 
534  // build unique ID
535  // important!
536  echo '<div>';
537  if ($this->ajaxUpdate !== false) {
538  if (isset($options['chart']) && is_array($options['chart'])) {
539  $options['chart']['renderTo'] = $chartId;
540  } else {
541  $options['chart'] = array('renderTo' => $chartId);
542  }
543  $jsOptions = CJSON::encode($options);
544 
545  if (isset($this->chartOptions['htmlOptions']['data-config'])) {
546  unset($this->chartOptions['htmlOptions']['data-config']);
547  }
548 
549  echo "<div id='{$chartId}' " . CHtml::renderAttributes(
550  $this->chartOptions['htmlOptions']
551  ) . " data-config='{$jsOptions}'></div>";
552 
553  /* fix for chart dimensions changing after ajax */
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'));
558  ";
559  }
560  $configChart = array(
561  'class' => 'booster.widgets.TbHighCharts',
562  'id' => $chartId,
563  'options' => $options,
564  'htmlOptions' => $this->chartOptions['htmlOptions']
565  );
566  $chart = Yii::createComponent($configChart);
567  $chart->init();
568  $chart->run();
569  echo '</div>';
570  // end chart display
571  // ****************************************
572 
573  // check if the chart should appear by default
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();
580  ';
581  } else {
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();
587  ';
588  }
589  }
590 
598  public function renderTableRow($row)
599  {
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)
606  );
607  if (is_array($options)) {
608  $htmlOptions = $options;
609  }
610  }
611 
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];
617  }
618 
619  if (!empty($class)) {
620  if (isset($htmlOptions['class'])) {
621  $htmlOptions['class'] .= ' ' . $class;
622  } else {
623  $htmlOptions['class'] = $class;
624  }
625  }
626 
627  echo CHtml::openTag('tr', $htmlOptions);
628  foreach ($this->columns as $column) {
629  echo $this->displayExtendedSummary && !empty($this->extendedSummary['columns']) ? $this->parseColumnValue(
630  $column,
631  $row
632  ) : $column->renderDataCell($row);
633  }
634 
635  echo CHtml::closeTag('tr');
636  }
637 
643  public function renderExtendedSummary()
644  {
645  if (!isset($this->extendedSummaryOptions['class'])) {
646  $this->extendedSummaryOptions['class'] = $this->extendedSummaryCssClass;
647  } else {
648  $this->extendedSummaryOptions['class'] .= ' ' . $this->extendedSummaryCssClass;
649  }
650 
651  echo '<div ' . CHtml::renderAttributes($this->extendedSummaryOptions) . '></div>';
652  }
653 
659  public function renderExtendedSummaryContent()
660  {
661  if (($count = $this->dataProvider->getItemCount()) <= 0) {
662  return;
663  }
664 
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>';
669  }
670  foreach ($this->extendedSummaryTypes as $summaryType) {
672  $summaryType->run();
673  echo '<br/>';
674  }
675  echo '</div>';
676  }
677  }
678 
684  public function registerCustomClientScript()
685  {
687  $cs = Yii::app()->getClientScript();
688 
689  $fixedHeaderJs = '';
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;
694  }
695 
696  if ($this->sortableRows) {
697  $afterSortableUpdate = '';
698  if ($this->afterSortableUpdate !== null) {
699  if (!($this->afterSortableUpdate instanceof CJavaScriptExpression) && strpos(
700  $this->afterSortableUpdate,
701  'js:'
702  ) !== 0
703  ) {
704  $afterSortableUpdate = new CJavaScriptExpression($this->afterSortableUpdate);
705  } else {
706  $afterSortableUpdate = $this->afterSortableUpdate;
707  }
708  }
709 
710  $this->selectableRows = 1;
711  $cs->registerCoreScript('jquery.ui');
712  Booster::getBooster()->registerAssetJs('jquery.sortable.gridview.js');
713 
714  if ($this->sortableAjaxSave && $this->sortableAction !== null) {
715  $sortableAction = Yii::app()->createUrl(
716  $this->sortableAction,
717  array('sortableAttribute' => $this->sortableAttribute)
718  );
719  } else {
720  $sortableAction = '';
721  }
722 
723  $afterSortableUpdate = CJavaScript::encode($afterSortableUpdate);
724  if (Yii::app()->request->enableCsrfValidation)
725  {
726  $csrfTokenName = Yii::app()->request->csrfTokenName;
727  $csrfToken = Yii::app()->request->csrfToken;
728  $csrf = "{'$csrfTokenName':'$csrfToken' }";
729  } else
730  $csrf = '{}';
731 
732  $this->componentsReadyScripts[] = "$.fn.yiiGridView.sortable('{$this->id}', '{$sortableAction}', {$afterSortableUpdate}, $csrf);";
733  $this->componentsAfterAjaxUpdate[] = "$.fn.yiiGridView.sortable('{$this->id}', '{$sortableAction}', {$afterSortableUpdate}, $csrf);";
734  }
735 
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);
741  } else {
742  $afterSelectableCells = $this->afterSelectableCells;
743  }
744  }
745  $cs->registerCoreScript('jquery.ui');
746  Booster::getBooster()->registerAssetJs('jquery.selectable.gridview.js');
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});";
750  }
751 
752  $cs->registerScript(
753  __CLASS__ . '#' . $this->id . 'Ex',
754  '
755  var $grid = $("#' . $this->id . '");
756  ' . $fixedHeaderJs . '
757  if ($(".' . $this->extendedSummaryCssClass . '", $grid).length)
758  {
759  $(".' . $this->extendedSummaryCssClass . '", $grid).html($("#' . $this->id . '-extended-summary", $grid).html());
760  }
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 . '")
765  {
766  if (typeof (options.realsuccess) == "undefined" || options.realsuccess !== options.success)
767  {
768  options.realsuccess = options.success;
769  options.success = function(data)
770  {
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 . '")))
776  {
777  $(".' . $this->extendedSummaryCssClass . '", $("#' . $this->id . '")).html($("#' . $this->id . '-extended-summary", $data).html());
778  }
779  ' . (count($this->componentsAfterAjaxUpdate) ? implode(
780  PHP_EOL,
781  $this->componentsAfterAjaxUpdate
782  ) : '') . '
783  }
784  }
785  }
786  }
787  });'
788  );
789  }
790 
799  protected function parseColumnValue($column, $row)
800  {
801  ob_start();
802  $column->renderDataCell($row);
803  $value = ob_get_clean();
804 
805  if ($column instanceof CDataColumn && array_key_exists($column->name, $this->extendedSummary['columns'])) {
806  // lets get the configuration
807  $config = $this->extendedSummary['columns'][$column->name];
808  // add the required column object in
809  $config['column'] = $column;
810  // build the summary operation object
811  $op = $this->getSummaryOperationInstance($column->name, $config);
812  // process the value
813  $op->processValue($value);
814  }
815  return $value;
816  }
817 
829  protected function getSummaryOperationInstance($name, $config)
830  {
831  if (!isset($config['class'])) {
832  throw new CException(Yii::t(
833  'zii',
834  'Column summary configuration must be an array containing a "type" element.'
835  ));
836  }
837 
838  if (!in_array($config['class'], $this->extendedSummaryOperations)) {
839  throw new CException(Yii::t(
840  'zii',
841  '"{operation}" is an unsupported class operation.',
842  array('{operation}' => $config['class'])
843  ));
844  }
845 
846  // name of the column should be unique
847  if (!isset($this->extendedSummaryTypes[$name])) {
848  $this->extendedSummaryTypes[$name] = Yii::createComponent($config);
849  $this->extendedSummaryTypes[$name]->init();
850  }
851  return $this->extendedSummaryTypes[$name];
852  }
853 
863  protected function getColumnByName($name)
864  {
865  foreach ($this->columns as $column) {
866  if (strcmp($column->name, $name) === 0) {
867  return $column;
868  }
869  }
870  return null;
871  }
872 
873 }
874 
882 abstract class TbOperation extends CWidget
883 {
887  public $template = '{label}: {value}';
888 
892  public $value = 0;
893 
897  public $label;
898 
902  public $column;
903 
908  public function init()
909  {
910  if (null == $this->column) {
911  throw new CException(Yii::t(
912  'zii',
913  '"{attribute}" attribute must be defined',
914  array('{attribute}' => 'column')
915  ));
916  }
917  }
918 
922  public function run()
923  {
924  $this->displaySummary();
925  }
926 
934  abstract public function processValue($value);
935 
940  abstract public function displaySummary();
941 
942 }
943 
952 {
956  protected $total;
957 
961  protected $supportedTypes = array('raw', 'text', 'ntext', 'number');
962 
967  public function init()
968  {
969  parent::init();
970 
971  if (!in_array($this->column->type, $this->supportedTypes)) {
972  throw new CException(Yii::t(
973  'zii',
974  'Unsupported column type. Supported column types are: "{types}"',
975  array(
976  '{types}' => implode(', ', $this->supportedTypes)
977  )
978  ));
979  }
980  }
981 
989  protected function extractNumber($value)
990  {
991  preg_match_all('/([+-]?[0-9]+[,\.]?)+/', $value, $matches);
992  return !empty($matches[0]) && @$matches[0][0] ? $matches[0][0] : 0;
993  }
994 
1002  public function processValue($value)
1003  {
1004  // remove html tags as we cannot access renderDataCellContent from the column
1005  $clean = strip_tags($value);
1006  $this->total += ((float)$this->extractNumber($clean));
1007  }
1008 
1013  public function displaySummary()
1014  {
1015  echo strtr(
1016  $this->template,
1017  array(
1018  '{label}' => $this->label,
1019  '{value}' => $this->total === null ? '' : Yii::app()->format->format($this->total, $this->column->type)
1020  )
1021  );
1022  }
1023 }
1024 
1034 {
1039  public $template = '{label}: {types}';
1040 
1044  public $typeTemplate = '{label}({value})';
1045 
1057  public $types = array();
1058 
1063  public function init()
1064  {
1065  if (empty($this->types)) {
1066  throw new CException(Yii::t(
1067  'zii',
1068  '"{attribute}" attribute must be defined',
1069  array('{attribute}' => 'types')
1070  ));
1071  }
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.'));
1075  }
1076  }
1077  parent::init();
1078  }
1079 
1088  public function processValue($value)
1089  {
1090  $clean = strip_tags($value);
1091 
1092  if (array_key_exists($clean, $this->types)) {
1093  if (!isset($this->types[$clean]['value'])) {
1094  $this->types[$clean]['value'] = 0;
1095  }
1096  $this->types[$clean]['value'] += 1;
1097  }
1098  }
1099 
1105  public function displaySummary()
1106  {
1107  $typesResults = array();
1108  foreach ($this->types as $type) {
1109  if (!isset($type['value'])) {
1110  $type['value'] = 0;
1111  }
1112 
1113  $typesResults[] = strtr(
1114  $this->typeTemplate,
1115  array('{label}' => $type['label'], '{value}' => $type['value'])
1116  );
1117  }
1118  echo strtr($this->template, array('{label}' => $this->label, '{types}' => implode(' ', $typesResults)));
1119  }
1120 }
1121 
1131 {
1136  public $typeTemplate = '{label}({value}%)';
1137 
1141  protected $_total;
1142 
1147  public function displaySummary()
1148  {
1149  $typesResults = array();
1150 
1151  foreach ($this->types as $type) {
1152  if (!isset($type['value'])) {
1153  $type['value'] = 0;
1154  }
1155 
1156  $type['value'] = $this->getTotal() ? number_format((float)($type['value'] / $this->getTotal()) * 100, 1)
1157  : 0;
1158  $typesResults[] = strtr(
1159  $this->typeTemplate,
1160  array('{label}' => $type['label'], '{value}' => $type['value'])
1161  );
1162  }
1163 
1164  echo strtr($this->template, array('{label}' => $this->label, '{types}' => implode(' ', $typesResults)));
1165  }
1166 
1171  protected function getTotal()
1172  {
1173  if (null == $this->_total) {
1174  $this->_total = 0;
1175  foreach ($this->types as $type) {
1176  if (isset($type['value'])) {
1177  $this->_total += $type['value'];
1178  }
1179  }
1180  }
1181  return $this->_total;
1182  }
1183 }
1184 
1193 {
1197  public $chartCssClass = 'bootstrap-operation-google-pie-chart';
1198 
1204  public $chartOptions = array(
1205  'title' => 'Google Pie Chart'
1206  );
1207 
1211  protected $data = array();
1212 
1217  public function displaySummary() {
1218 
1219  $this->data[] = array('Label', 'Percent');
1220 
1221  foreach ($this->types as $type) {
1222  if (!isset($type['value'])) {
1223  $type['value'] = 0;
1224  }
1225 
1226  $this->data[] = $this->getTotal() ? array(
1227  $type['label'],
1228  (float)number_format(($type['value'] / $this->getTotal()) * 100, 1)
1229  ) : 0;
1230  }
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>";
1234 
1235  $this->registerClientScript();
1236  }
1237 
1241  public function registerClientScript()
1242  {
1243  $chart = Yii::createComponent(
1244  array(
1245  'class' => 'booster.widgets.TbGoogleVisualizationChart',
1246  'visualization' => 'PieChart',
1247  'containerId' => $this->getId(),
1248  'data' => $this->data,
1249  'options' => $this->chartOptions
1250  )
1251  );
1252  $chart->init();
1253  $chart->run();
1254 
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);';
1264  }
1265 
1266 }
1267 
1276 {
1280  public $chartCssClass = 'bootstrap-operation-easy-pie-chart';
1281 
1286  public $template = '<div style="clear:both">{label}: </div>{types}';
1287 
1292  public $typeTemplate = '<div style="float:left;text-align:center;margin:2px"><div class="{class}" data-percent="{value}">{value}%</div><div>{label}</div></div>';
1293 
1294  // easy-pie-chart plugin options
1295  // @see https://github.com/rendro/easy-pie-chart#configuration-parameter
1296  public $chartOptions = array(
1297  'barColor' => '#ef1e25',
1298  // The color of the curcular bar. You can pass either a css valid color string like rgb,
1299  // rgba hex or string colors. But you can also pass a function that accepts the current
1300  // percentage as a value to return a dynamically generated color.
1301  'trackColor' => '#f2f2f2',
1302  // The color of the track for the bar, false to disable rendering.
1303  'scaleColor' => '#dfe0e0',
1304  // The color of the scale lines, false to disable rendering.
1305  'lineCap' => 'round',
1306  // Defines how the ending of the bar line looks like. Possible values are: butt, round and square.
1307  'lineWidth' => 5,
1308  // Width of the bar line in px.
1309  'size' => 80,
1310  // Size of the pie chart in px. It will always be a square.
1311  'animate' => false,
1312  // Time in milliseconds for a eased animation of the bar growing, or false to deactivate.
1313  'onStart' => 'js:$.noop',
1314  // Callback function that is called at the start of any animation (only if animate is not false).
1315  'onStop' => 'js:$.noop'
1316  // Callback function that is called at the end of any animation (only if animate is not false).
1317  );
1318 
1323  public function displaySummary()
1324  {
1325  $this->typeTemplate = strtr($this->typeTemplate, array('{class}' => $this->chartCssClass));
1326 
1327  parent::displaySummary();
1328  $this->registerClientScripts();
1329  }
1330 
1334  protected function registerClientScripts()
1335  {
1336  $booster = Booster::getBooster();
1337  $booster->registerAssetCss('easy-pie-chart.css');
1338  $booster->registerAssetJs('jquery.easy.pie.chart.js');
1339 
1340  $options = CJavaScript::encode($this->chartOptions);
1341  Yii::app()->getClientScript()->registerScript(
1342  __CLASS__ . '#percent-of-type-operation-simple-pie',
1343  '
1344  $("#' . $this->column->grid->id . ' .' . $this->column->grid->extendedSummaryCssClass . ' .' . $this->chartCssClass . '")
1345  .easyPieChart(' . $options . ');
1346  '
1347  );
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 . ');';
1352  }
1353 }