PluginProbe
wpDataTables – WordPress Data Table, Dynamic Tables & Table Charts Plugin / trunk
wpDataTables – WordPress Data Table, Dynamic Tables & Table Charts Plugin vtrunk
6.5.1.6 6.5.1.5 6.5.1.4 6.5.1.3 6.5.1.2 6.5.1.1 6.5.0.9 6.5.0.8 6.5.0.7 6.5.0.6 trunk 3.4.2.40 3.4.2.41 3.4.2.42 3.4.2.43 3.4.2.44 3.4.2.45 3.4.2.46 3.4.2.47 3.4.2.48 3.4.2.49 3.4.2.50 6.3.2 6.3.3.1 6.3.3.10 All 46 releases
wpdatatables / source / class.wpdatachart.php

class.wpdatachart.php in wpDataTables – WordPress Data Table, Dynamic Tables & Table Charts Plugin trunk, at source/class.wpdatachart.php

1,277 lines 40.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php /** @noinspection PhpMultipleClassDeclarationsInspection */
2
3 defined('ABSPATH') or die('Access denied.');
4
5 /**
6 * Chart engine of wpDataTables plugin
7 */
8 class WPDataChart
9 {
10 protected $_id = NULL;
11 protected $_wpdatatable_id = NULL;
12 protected $_engine = '';
13 protected $_type = '';
14 protected $_range_type = 'all';
15 protected $_selected_columns = array();
16 protected $_row_range = array();
17 protected $_follow_filtering = false;
18 protected $_wpdatatable = NULL;
19 protected $_user_defined_series_data = array();
20 protected $_render_data = NULL;
21 protected $_type_counters;
22 // Chart
23 protected $_width = 400;
24 protected $_responsiveWidth = false;
25 protected $_height = 400;
26 protected $_group_chart = false;
27 protected $_background_color = '#FFFFFF';
28 // Series
29 protected $_series = array();
30 protected $_series_type = '';
31 // Axes
32 protected $_show_grid = true;
33 protected $_vertical_axis_min;
34 protected $_vertical_axis_max;
35 protected $_axes = array(
36 'major' => array(
37 'label' => ''
38 ),
39 'minor' => array(
40 'label' => ''
41 )
42 );
43 // Title
44 protected $_title = '';
45 protected $_show_title = true;
46 // Tooltip
47 protected $_tooltip_enabled = true;
48 // Render data
49 protected $_json_chart_render_data = null;
50
51 public function setId($id)
52 {
53 $this->_id = $id;
54 }
55
56 public function getId()
57 {
58 return $this->_id;
59 }
60
61 // Chart
62
63 public function setWidth($width)
64 {
65 $this->_width = $width;
66 }
67
68 public function getWidth()
69 {
70 return $this->_width;
71 }
72
73 /**
74 * @return bool
75 */
76 public function isResponsiveWidth()
77 {
78 return $this->_responsiveWidth;
79 }
80
81 /**
82 * @param bool $responsiveWidth
83 */
84 public function setResponsiveWidth($responsiveWidth)
85 {
86 $this->_responsiveWidth = $responsiveWidth;
87 }
88
89 public function setHeight($height)
90 {
91 $this->_height = $height;
92 }
93
94 public function getHeight()
95 {
96 return $this->_height;
97 }
98
99 /**
100 * @param bool $group_chart
101 */
102 public function setGroupChart($group_chart)
103 {
104 $this->_group_chart = $group_chart;
105 }
106
107 /**
108 * @return bool
109 */
110 public function isGroupChart()
111 {
112 return $this->_group_chart;
113 }
114
115 /**
116 * @param $background_color
117 */
118 public function setBackgroundColor($background_color)
119 {
120 $this->_background_color = $background_color;
121 }
122
123 /**
124 * @return string
125 */
126 public function getBackgroundColor()
127 {
128 return $this->_background_color;
129 }
130
131 // Axes
132
133 /**
134 * @param $show_grid
135 */
136 public function setShowGrid($show_grid)
137 {
138 $this->_show_grid = (bool)$show_grid;
139 }
140
141 /**
142 * @return bool
143 */
144 public function isShowGrid()
145 {
146 return $this->_show_grid;
147 }
148
149 /**
150 * @param $label
151 */
152 public function setMajorAxisLabel($label)
153 {
154 $this->_axes['major']['label'] = $label;
155 }
156
157 /**
158 * @return mixed
159 */
160 public function getMajorAxisLabel()
161 {
162 return $this->_axes['major']['label'];
163 }
164
165 /**
166 * @param $label
167 */
168 public function setMinorAxisLabel($label)
169 {
170 $this->_axes['minor']['label'] = $label;
171 }
172
173 /**
174 * @return mixed
175 */
176 public function getMinorAxisLabel()
177 {
178 return $this->_axes['minor']['label'];
179 }
180
181 /**
182 * @param mixed $vertical_axis_min
183 */
184 public function setVerticalAxisMin($vertical_axis_min)
185 {
186 $this->_vertical_axis_min = $vertical_axis_min;
187 }
188
189 /**
190 * @return mixed
191 */
192 public function getVerticalAxisMin()
193 {
194 return $this->_vertical_axis_min;
195 }
196
197 /**
198 * @param mixed $vertical_axis_max
199 */
200 public function setVerticalAxisMax($vertical_axis_max)
201 {
202 $this->_vertical_axis_max = $vertical_axis_max;
203 }
204
205 /**
206 * @return mixed
207 */
208 public function getVerticalAxisMax()
209 {
210 return $this->_vertical_axis_max;
211 }
212
213 // Title
214
215 /**
216 * @param string $title
217 */
218 public function setTitle($title)
219 {
220 $this->_title = $title;
221 }
222
223 /**
224 * @return string
225 */
226 public function getTitle()
227 {
228 return $this->_title;
229 }
230
231
232 /**
233 * @param bool $show_title
234 */
235 public function setShowTitle($show_title)
236 {
237 $this->_show_title = $show_title;
238 }
239
240 /**
241 * @return bool
242 */
243 public function isShowTitle()
244 {
245 return $this->_show_title;
246 }
247
248 /**
249 * @return bool
250 */
251 public function isTooltipEnabled()
252 {
253 return $this->_tooltip_enabled;
254 }
255
256 /**
257 * @param bool $tooltip_enabled
258 */
259 public function setTooltipEnabled($tooltip_enabled)
260 {
261 $this->_tooltip_enabled = (bool)$tooltip_enabled;
262 }
263
264
265 /**
266 * @param $series_data
267 */
268 public function setUserDefinedSeriesData($series_data)
269 {
270 if (is_array($series_data)) {
271 $this->_user_defined_series_data = $series_data;
272 }
273 }
274
275 /**
276 * @return array
277 */
278 public function getUserDefinedSeriesData()
279 {
280 return $this->_user_defined_series_data;
281 }
282
283
284 /**
285 * @param $engine
286 */
287 public function setEngine($engine)
288 {
289 $this->_engine = $engine;
290 }
291
292 /**
293 * @return string
294 */
295 public function getEngine()
296 {
297 return $this->_engine;
298 }
299
300 /**
301 * @param $type
302 */
303 public function setType($type)
304 {
305 $this->_type = $type;
306 }
307
308 /**
309 * @return string
310 */
311 public function getType()
312 {
313 return $this->_type;
314 }
315
316 /**
317 * @return string
318 */
319 public function getSeriesType()
320 {
321 return $this->_series_type;
322 }
323
324 /**
325 * @param string $series_type
326 */
327 public function setSeriesType($series_type)
328 {
329 $this->_series_type = $series_type;
330 }
331
332 /**
333 * @param $row_range
334 */
335 public function setRowRange($row_range)
336 {
337 $this->_row_range = $row_range;
338 }
339
340 /**
341 * @return array
342 */
343 public function getRowRange()
344 {
345 return $this->_row_range;
346 }
347
348 /**
349 * @param $selected_columns
350 */
351 public function setSelectedColumns($selected_columns)
352 {
353 $this->_selected_columns = $selected_columns;
354 }
355
356 /**
357 * @return array
358 */
359 public function getSelectedColumns()
360 {
361 return $this->_selected_columns;
362 }
363
364 /**
365 * @param $wdt_id
366 */
367 public function setwpDataTableId($wdt_id)
368 {
369 $this->_wpdatatable_id = $wdt_id;
370 }
371
372 /**
373 * @return null
374 */
375 public function getwpDataTableId()
376 {
377 return $this->_wpdatatable_id;
378 }
379
380 /**
381 * @param $range_type
382 */
383 public function setRangeType($range_type)
384 {
385 $this->_range_type = $range_type;
386 }
387
388 /**
389 * @return string
390 */
391 public function getRangeType()
392 {
393 return $this->_range_type;
394 }
395
396 /**
397 * @return null
398 */
399 public function getJsonChartRenderData()
400 {
401 return $this->_json_chart_render_data;
402 }
403
404 /**
405 * @param null $json_chart_render_data
406 */
407 public function setJsonChartRenderData($json_chart_render_data)
408 {
409 $this->_json_chart_render_data = $json_chart_render_data;
410 }
411
412 /**
413 * @param $constructedChartData
414 * @param bool $loadFromDB
415 * @throws WDTException
416 */
417 public function __construct($constructedChartData, $loadFromDB)
418 {
419 if (isset($constructedChartData['id'])) {
420 $this->setId((int)$constructedChartData['id']);
421 }
422 $this->setwpDataTableId((int)$constructedChartData['wpdatatable_id']);
423 $this->loadChildWPDataTable();
424 $this->setTitle(sanitize_text_field($constructedChartData['title']));
425 $this->setType(sanitize_text_field($constructedChartData['type']));
426 if (isset($constructedChartData['series_type'])) {
427 $this->setSeriesType(sanitize_text_field($constructedChartData['series_type']));
428 }
429 $this->setSelectedColumns(array_map('sanitize_text_field', (array)$constructedChartData['selected_columns']));
430 $this->setRangeType(sanitize_text_field($constructedChartData['range_type']));
431 if (isset($constructedChartData['range_data'])) {
432 $this->setRowRange(array_map('intval', $constructedChartData['range_data']));
433 }
434
435 // Series
436 if (!empty($constructedChartData['series_data'])) {
437 array_walk_recursive(
438 $constructedChartData['series_data'],
439 function ($value) {
440 sanitize_text_field($value);
441 }
442 );
443 $this->setUserDefinedSeriesData($constructedChartData['series_data']);
444 }
445
446 $this->setWidth((int)WDTTools::defineDefaultValue($constructedChartData, 'width', 0));
447 $this->setResponsiveWidth((bool)WDTTools::defineDefaultValue($constructedChartData, 'responsive_width', 0));
448 $this->setHeight((int)WDTTools::defineDefaultValue($constructedChartData, 'height', 400));
449 $this->setGroupChart((bool)(WDTTools::defineDefaultValue($constructedChartData, 'group_chart', false)));
450 $this->setBackgroundColor(sanitize_text_field(WDTTools::defineDefaultValue($constructedChartData, 'background_color', '#FFFFFF')));
451 $this->setShowGrid((bool)(WDTTools::defineDefaultValue($constructedChartData, 'show_grid', true)));
452 $this->setMajorAxisLabel(sanitize_text_field(WDTTools::defineDefaultValue($constructedChartData, 'horizontal_axis_label')));
453 $this->setMinorAxisLabel(sanitize_text_field(WDTTools::defineDefaultValue($constructedChartData, 'vertical_axis_label')));
454 $this->setVerticalAxisMin(sanitize_text_field(WDTTools::defineDefaultValue($constructedChartData, 'vertical_axis_min')));
455 $this->setVerticalAxisMax(sanitize_text_field(WDTTools::defineDefaultValue($constructedChartData, 'vertical_axis_max')));
456 $this->setTooltipEnabled((bool)(WDTTools::defineDefaultValue($constructedChartData, 'tooltip_enabled', true)));
457 }
458
459 /**
460 *
461 * Creates a new WPDataChart based on the chart engine
462 * @param $constructedChartData
463 * @param bool $loadFromDB
464 * @return mixed
465 */
466 public static function build($constructedChartData, $loadFromDB = false)
467 {
468 // Security Fix: Whitelist valid chart engines to prevent LFI
469 $validEngines = array('google', 'chartjs', 'highcharts', 'apexcharts');
470
471 $engine = isset($constructedChartData['engine']) ? strtolower(sanitize_text_field($constructedChartData['engine'])) : 'google';
472
473 if (!in_array($engine, $validEngines, true)) {
474 // Invalid engine, default to google
475 $engine = 'google';
476 }
477
478 $wdtChart = 'Wdt' . ucfirst($engine) . 'Chart' . '\Wdt' . ucfirst($engine) . 'Chart';
479 $chartClassFileName = 'class.' . $engine . '.wpdatachart.php';
480 require_once(WDT_ROOT_PATH . 'source/' . $chartClassFileName);
481 return new $wdtChart($constructedChartData, $loadFromDB);
482 }
483
484 /**
485 *
486 * Sets the corresponding wpDataTable
487 *
488 * @throws WDTException
489 */
490 public function loadChildWPDataTable()
491 {
492 if (empty($this->getwpDataTableId())) {
493 return false;
494 }
495 $this->_wpdatatable = WPDataTable::loadWpDataTable($this->_wpdatatable_id, null, empty($this->_follow_filtering));
496 return true;
497 }
498
499 /**
500 * @return void
501 */
502 public function shiftXAxisColumnUp()
503 {
504 /**
505 * Check if the string column is not in the beginning and move it up
506 */
507 if (count($this->_render_data['columns']) > 1) {
508 $shiftNeeded = false;
509 $shiftIndex = 0;
510 for ($i = 1; $i < count($this->_render_data['columns']); $i++) {
511 if ($this->_render_data['columns'][$i]['type'] == 'string') {
512 $shiftNeeded = true;
513 $shiftIndex = $i;
514 break;
515 }
516 }
517
518 if ($shiftNeeded) {
519 $this->shiftColumns($shiftIndex);
520 }
521 }
522
523 $this->formatShiftedAxes();
524 }
525
526 public function shiftColumns($shiftIndex)
527 {
528 // Shift columns
529 $strColumn = $this->_render_data['columns'][$shiftIndex];
530 unset($this->_render_data['columns'][$shiftIndex]);
531 array_unshift($this->_render_data['columns'], $strColumn);
532 // Shift rows
533 for ($j = 0; $j < count($this->_render_data['rows']); $j++) {
534 $strCell = $this->_render_data['rows'][$j][$shiftIndex];
535 unset($this->_render_data['rows'][$j][$shiftIndex]);
536 array_unshift($this->_render_data['rows'][$j], $strCell);
537 }
538 // Shift column indexes
539 if (isset($this->_render_data['column_indexes'])) {
540 $shiftedIndex = $this->_render_data['column_indexes'][$shiftIndex];
541 unset($this->_render_data['column_indexes'][$shiftIndex]);
542 array_unshift($this->_render_data['column_indexes'], $shiftedIndex);
543 }
544 }
545
546 public function formatShiftedAxes()
547 {
548 // Format axes
549 $this->_render_data['axes']['major'] = array(
550 'type' => $this->_render_data['columns'][0]['type'],
551 'label' => !empty($this->_render_data['hAxis']['title']) ?
552 $this->_render_data['hAxis']['title'] : $this->_render_data['columns'][0]['label']
553 );
554 $this->_render_data['axes']['minor'] = array(
555 'type' => $this->_render_data['columns'][1]['type'],
556 'label' => !empty($this->_render_data['vAxis']['title']) ?
557 $this->_render_data['vAxis']['title'] : ''
558 );
559
560 // Get all series names
561 if (empty($this->_render_data['series'])) {
562 for ($i = 1; $i < count($this->_render_data['columns']); $i++) {
563 $this->_render_data['series'][] = array(
564 'label' => $this->_render_data['columns'][$i]['label'],
565 'color' => '',
566 'orig_header' => $this->_render_data['columns'][$i]['orig_header']
567 );
568 }
569 }
570 }
571
572 /**
573 * @return void
574 */
575 public function prepareSeriesData()
576 {
577 // Init render data if it is empty
578 if (empty($this->_render_data)) {
579 $this->_render_data = array(
580 'columns' => array(),
581 'rows' => array(),
582 'axes' => array(),
583 'options' => array(
584 'title' => $this->_show_title ? $this->_title : '',
585 'series' => array(),
586 'width' => $this->_width,
587 'height' => $this->_height
588 ),
589 'vAxis' => array(),
590 'hAxis' => array(),
591 'errors' => array(),
592 'series' => array()
593 );
594 }
595
596 if ($this->isResponsiveWidth()) {
597 unset($this->_render_data['options']['width']);
598 $this->_render_data['options']['responsive_width'] = 1;
599 }
600
601 $this->_type_counters = array(
602 'date' => 0,
603 'datetime' => 0,
604 'string' => 0,
605 'number' => 0
606 );
607
608 // Define columns
609 foreach ($this->getSelectedColumns() as $columnKey) {
610 $columnType = $this->_wpdatatable->getColumn($columnKey)->getGoogleChartColumnType();
611 $this->_render_data['columns'][] = array(
612 'type' => $columnType,
613 'label' => isset($this->_user_defined_series_data[$columnKey]['label']) ?
614 $this->_user_defined_series_data[$columnKey]['label'] : $this->_wpdatatable->getColumn($columnKey)->getTitle(),
615 'orig_header' => $columnKey
616 );
617 $this->_type_counters[$columnType]++;
618 }
619
620 // Define axes titles
621 if (isset($this->_axes['major']['label'])) {
622 $this->_render_data['options']['hAxis']['title'] = $this->_axes['major']['label'];
623 }
624 if (isset($this->_axes['minor']['label'])) {
625 $this->_render_data['options']['vAxis']['title'] = $this->_axes['minor']['label'];
626 }
627
628 $this->defineSeries();
629
630 // Group chart data
631 if ($this->isGroupChart()) {
632 $this->_render_data['group_chart'] = true;
633 } else {
634 $this->_render_data['group_chart'] = false;
635 }
636
637 // Define grid settings
638 if (!$this->isShowGrid()) {
639 if (!isset($this->_render_data['options']['hAxis'])) {
640 $this->_render_data['options']['hAxis'] = array();
641 }
642 $this->_render_data['options']['hAxis']['gridlines'] = array(
643 'color' => 'transparent'
644 );
645 if (!isset($this->_render_data['options']['vAxis'])) {
646 $this->_render_data['options']['vAxis'] = array();
647 }
648 $this->_render_data['options']['vAxis']['gridlines'] = array(
649 'color' => 'transparent'
650 );
651 $this->_render_data['show_grid'] = false;
652 } else {
653 $this->_render_data['show_grid'] = true;
654 }
655
656 // Detect errors
657 if ($this->_type_counters['string'] > 1) {
658 $this->_render_data['errors'][] = __('Only one column can be of type String', 'wpdatatables');
659 }
660 if (($this->_type_counters['number'] > 1) && ($this->_type_counters['date'] > 1)) {
661 $this->_render_data['errors'][] = __('You are mixing data types (several date axes and several number)', 'wpdatatables');
662 }
663 if (empty($this->_render_data)) {
664 $this->_render_data = array();
665 }
666
667 }
668
669 /**
670 * @return mixed|null
671 * @throws WDTException
672 */
673 public function prepareData()
674 {
675
676 // Prepare series and columns
677 if (empty($this->_render_data['columns'])) {
678 $this->prepareSeriesData();
679 }
680
681 $dateFormat = $this->getDateFormat();
682 $timeFormat = $this->getDateTimeFormat();
683
684 // The data itself
685 if (empty($this->_render_data['rows'])) {
686 if ($this->getRangeType() == 'all_rows') {
687 foreach ($this->_wpdatatable->getDataRows() as $row) {
688 $return_data_row = array();
689 foreach ($this->getSelectedColumns() as $columnKey) {
690 if (!$this->_wpdatatable->getColumn($columnKey))
691 throw new WDTException('In chart, selected column "' . $columnKey . '" does not exist anymore in source table with ID =' . $this->_wpdatatable->getWpId() . '.<br> Please check your source that you use for creating this table or create new chart from that table and replace new chart shortcode with old one on the front page.');
692 $dataType = $this->_wpdatatable->getColumn($columnKey)->getDataType();
693 $decimalPlaces = $this->_wpdatatable->getColumn($columnKey)->getDecimalPlaces();
694 $thousandsSeparator = $this->_wpdatatable->getColumn($columnKey)->isShowThousandsSeparator();
695 switch ($dataType) {
696 case 'date':
697 $timestamp = is_numeric($row[$columnKey]) ? $row[$columnKey] : strtotime(str_replace('/', '-', $row[$columnKey]));
698 $return_data_row[] = date(
699 $dateFormat,
700 $timestamp
701 );
702 break;
703 case 'datetime':
704 $timestamp = is_numeric($row[$columnKey]) ? $row[$columnKey] : strtotime(str_replace('/', '-', $row[$columnKey]));
705 if ($this->getEngine() == 'google') {
706 $return_data_row[] = date(
707 $dateFormat,
708 $timestamp
709 );
710 } else {
711 $return_data_row[] = date(
712 $dateFormat . ' ' . $timeFormat,
713 $timestamp
714 );
715 }
716 break;
717 case 'time':
718 $timestamp = $row[$columnKey];
719 $return_data_row[] = date(
720 $timeFormat,
721 $timestamp
722 );
723 break;
724 case 'int':
725 if (has_filter('wpdatatables_filter_int_cell_data_in_charts')) {
726 $row[$columnKey] = apply_filters('wpdatatables_filter_int_cell_data_in_charts', $row[$columnKey], $columnKey, $this->getId(), $this->_wpdatatable->getWpId());
727 if (!is_null($row[$columnKey])) {
728 $return_data_row[] = (float)$row[$columnKey];
729 } else {
730 $return_data_row[] = null;
731 }
732 } else {
733 $return_data_row[] = (float)$row[$columnKey];
734 }
735 break;
736 case 'float':
737 if (has_filter('wpdatatables_filter_float_cell_data_in_charts')) {
738 $row[$columnKey] = apply_filters('wpdatatables_filter_float_cell_data_in_charts', $row[$columnKey], $columnKey, $this->getId(), $this->_wpdatatable->getWpId());
739 if (!is_null($row[$columnKey])) {
740 if ($decimalPlaces != -1) {
741 $return_data_row[] = (float)number_format(
742 (float)($row[$columnKey]),
743 $decimalPlaces,
744 '.',
745 $thousandsSeparator ? '' : '.');
746 } else {
747 $return_data_row[] = (float)$row[$columnKey];
748 }
749 } else {
750 $return_data_row[] = null;
751 }
752 } else {
753 if ($decimalPlaces != -1) {
754 $return_data_row[] = (float)number_format(
755 (float)($row[$columnKey]),
756 $decimalPlaces,
757 '.',
758 $thousandsSeparator ? '' : '.');
759 } else {
760 $return_data_row[] = (float)$row[$columnKey];
761 }
762 }
763 break;
764 case 'link':
765 if (!in_array($this->getEngine(), ['google', 'chartjs'])) {
766 if (strpos($row[$columnKey], '||') !== false) {
767 list($link, $row[$columnKey]) = explode('||', $row[$columnKey]);
768 $return_data_row[] = '<a href="' . $link . '">' . $row[$columnKey] . '</a>';
769 } else {
770 $return_data_row[] = '<a href="' . $row[$columnKey] . '">' . $row[$columnKey] . '</a>';
771 }
772 } else {
773 $return_data_row[] = $row[$columnKey];
774 }
775 break;
776 case 'string':
777 default:
778 if ($this->getEngine() == 'apexcharts') {
779 $return_data_row[] = $row[$columnKey] === null ? '' : $row[$columnKey];
780 } else {
781 $return_data_row[] = $row[$columnKey];
782 }
783 break;
784 }
785 }
786 $this->_render_data['rows'][] = $return_data_row;
787 }
788 } else {
789 foreach ($this->getRowRange() as $rowIndex) {
790 $return_data_row = array();
791 foreach ($this->getSelectedColumns() as $columnKey) {
792
793 $dataType = $this->_wpdatatable->getColumn($columnKey)->getDataType();
794 $decimalPlaces = $this->_wpdatatable->getColumn($columnKey)->getDecimalPlaces();
795 switch ($dataType) {
796 case 'date':
797 $timestamp = is_int($this->_wpdatatable->getCell($columnKey, $rowIndex)) ?
798 $this->_wpdatatable->getCell($columnKey, $rowIndex)
799 : strtotime(str_replace('/', '-', $this->_wpdatatable->getCell($columnKey, $rowIndex)));
800 $return_data_row[] = date(
801 $dateFormat,
802 $timestamp
803 );
804 break;
805 case 'datetime':
806 $timestamp = is_int($this->_wpdatatable->getCell($columnKey, $rowIndex)) ?
807 $this->_wpdatatable->getCell($columnKey, $rowIndex) : strtotime(str_replace('/', '-', $this->_wpdatatable->getCell($columnKey, $rowIndex)));
808 if ($this->getEngine() == 'google') {
809 $return_data_row[] = date(
810 $dateFormat,
811 $timestamp
812 );
813 } else {
814 $return_data_row[] = date(
815 $dateFormat . ' ' . $timeFormat,
816 $timestamp
817 );
818 }
819 break;
820 case 'time':
821 $timestamp = $this->_wpdatatable->getCell($columnKey, $rowIndex);
822 $return_data_row[] = date(
823 $timeFormat,
824 $timestamp
825 );
826 break;
827 case 'int':
828 if (has_filter('wpdatatables_filter_int_cell_data_in_charts')) {
829 $cellData = apply_filters('wpdatatables_filter_int_cell_data_in_charts', $this->_wpdatatable->getCell($columnKey, $rowIndex), $columnKey, $this->getId(), $this->_wpdatatable->getWpId());
830 if (!is_null($cellData)) {
831 $return_data_row[] = (float)$cellData;
832 } else {
833 $return_data_row[] = null;
834 }
835 } else {
836 $return_data_row[] = (float)$this->_wpdatatable->getCell($columnKey, $rowIndex);
837 }
838 break;
839 case 'float':
840 if (has_filter('wpdatatables_filter_float_cell_data_in_charts')) {
841 $floatNumber = apply_filters('wpdatatables_filter_float_cell_data_in_charts', $this->_wpdatatable->getCell($columnKey, $rowIndex), $columnKey, $this->getId(), $this->_wpdatatable->getWpId());
842 if (!is_null($floatNumber)) {
843 if ($decimalPlaces != -1) {
844 $return_data_row[] = (float)number_format($floatNumber, $decimalPlaces);
845 } else {
846 $return_data_row[] = $floatNumber;
847 }
848 } else {
849 $return_data_row[] = null;
850 }
851 } else {
852 $floatNumber = (float)$this->_wpdatatable->getCell($columnKey, $rowIndex);
853 if ($decimalPlaces != -1) {
854 $return_data_row[] = (float)number_format($floatNumber, $decimalPlaces);
855 } else {
856 $return_data_row[] = $floatNumber;
857 }
858 }
859 break;
860 case 'link':
861 $cellData = $this->_wpdatatable->getCell($columnKey, $rowIndex);
862 if (!in_array($this->getEngine(), ['google', 'chartjs'])) {
863 if (strpos($cellData, '||') !== false) {
864 list($link, $cellData) = explode('||', $cellData);
865 $return_data_row[] = '<a href="' . $link . '">' . $cellData . '</a>';
866 } else {
867 $return_data_row[] = '<a href="' . $cellData . '">' . $cellData . '</a>';
868 }
869 } else {
870 $return_data_row[] = $cellData;
871 }
872 break;
873 case 'string':
874 default:
875 $return_data_row[] = $this->_wpdatatable->getCell($columnKey, $rowIndex);
876 break;
877 }
878
879 }
880 $this->_render_data['rows'][] = $return_data_row;
881 }
882 }
883
884 }
885 $this->_render_data['type'] = $this->_type;
886 return $this->_render_data;
887 }
888
889 /**
890 * @return void
891 */
892 public function groupData()
893 {
894 if (isset($this->_render_data['group_chart'])) {
895 if ($this->isGroupChart() || $this->_render_data['group_chart']) {
896 $output = array();
897 foreach ($this->_render_data['rows'] as $row) {
898 if (!empty($output)) {
899 $value_key = 'none';
900 foreach ($output as $key => $value) {
901 if ($value_key === 'none') {
902 if ($value[0] == $row[0]) {
903 $value_key = $key;
904 }
905 }
906 }
907 if ($value_key === 'none') {
908 $output[] = $row;
909 } else {
910 for ($n = 1; $n <= count($row) - 1; $n++) {
911 $output[$value_key][$n] += $row[$n];
912 }
913 }
914 } else {
915 $output[] = $row;
916 }
917 }
918 $this->_group_chart = 1;
919 $this->_render_data['rows'] = $output;
920 } else {
921 $this->_group_chart = 0;
922 }
923 } else {
924 $this->_group_chart = 0;
925 }
926 }
927
928 /**
929 * @return void
930 */
931 public function prepareRender()
932 {
933
934 }
935
936 /**
937 * @return mixed|null
938 * @throws WDTException
939 */
940 public function returnData()
941 {
942 $this->prepareData();
943 $this->groupData();
944 $this->shiftXAxisColumnUp();
945 $this->prepareRender();
946 return $this->returnRenderData();
947 }
948
949 /**
950 * Saves the chart data to DB
951 * @global WPDB $wpdb
952 */
953 public function save()
954 {
955 global $wpdb;
956
957 $this->prepareSeriesData();
958 $this->shiftXAxisColumnUp();
959 $this->prepareRender();
960
961 $render_data = $this->formRenderDataForDb();
962
963 if (empty($this->getId())) {
964 // This is a new chart
965 $wpdb->insert(
966 $wpdb->prefix . "wpdatacharts",
967 array(
968 'wpdatatable_id' => $this->getwpDataTableId(),
969 'title' => $this->getTitle(),
970 'engine' => $this->getEngine(),
971 'type' => $this->getType(),
972 'json_render_data' => json_encode($render_data)
973 )
974 );
975
976 $this->_id = $wpdb->insert_id;
977
978 } else {
979 // Updating the chart
980 $wpdb->update(
981 $wpdb->prefix . "wpdatacharts",
982 array(
983 'wpdatatable_id' => $this->getwpDataTableId(),
984 'title' => $this->getTitle(),
985 'engine' => $this->getEngine(),
986 'type' => $this->getType(),
987 'json_render_data' => json_encode($render_data)
988 ),
989 array(
990 'id' => $this->_id
991 )
992 );
993
994 }
995
996 }
997
998 /**
999 * @return void
1000 */
1001 public function getColumnIndexes()
1002 {
1003 foreach ($this->getSelectedColumns() as $columnKey) {
1004 $this->_render_data['column_indexes'][] = $this->_wpdatatable->getColumnHeaderOffset($columnKey);
1005 }
1006 }
1007
1008 /**
1009 * Return the shortcode
1010 */
1011 public function getShortCode()
1012 {
1013 if (!empty($this->_id)) {
1014 return '[wpdatachart id=' . $this->_id . ']';
1015 }
1016 return '';
1017 }
1018
1019 /**
1020 * Load chart from the database
1021 * @return bool
1022 * @throws WDTException
1023 */
1024 public function loadFromDB()
1025 {
1026 $chartData = $this->getChartDataById($this->getId());
1027
1028 $renderData = $this->setChartRenderData($chartData);
1029
1030 $this->setGeneralChartProperties($chartData, $renderData);
1031
1032 $this->setSpecificChartProperties($renderData);
1033
1034 $this->loadChildWPDataTable();
1035
1036 return true;
1037 }
1038
1039 /**
1040 * @param $chartId
1041 * @return array|false|object|stdClass
1042 */
1043 public static function getChartDataById($chartId)
1044 {
1045 global $wpdb;
1046
1047 if (empty($chartId)) {
1048 return false;
1049 }
1050
1051 // Load json data from DB
1052 $chartQuery = $wpdb->prepare(
1053 "SELECT *
1054 FROM " . $wpdb->prefix . "wpdatacharts
1055 WHERE id = %d",
1056 $chartId
1057 );
1058 $chartData = $wpdb->get_row($chartQuery);
1059
1060 if ($chartData === null) {
1061 return false;
1062 }
1063
1064 return $chartData;
1065 }
1066
1067 /**
1068 * Render Chart
1069 * @throws WDTException
1070 */
1071 public function render()
1072 {
1073 $minified_js = get_option('wdtMinifiedJs');
1074
1075 $this->prepareData();
1076 if ($this->_follow_filtering) {
1077 $this->getColumnIndexes();
1078 }
1079
1080 $this->groupData();
1081
1082 $this->shiftXAxisColumnUp();
1083
1084 $js_ext = $minified_js ? '.min.js' : '.js';
1085
1086 wp_enqueue_script('wpdatatables-render-chart', WDT_JS_PATH . 'wdtcharts/wdt.chartsRender' . $js_ext, array('jquery'), WDT_CURRENT_VERSION);
1087 $this->setJsonChartRenderData($this->enqueueChartSpecificScripts($js_ext));
1088 return $this->enqueueScriptsAfterChartRender();
1089 }
1090
1091 /**
1092 * @param $js_ext
1093 * @return false|string
1094 */
1095 public function enqueueChartSpecificScripts($js_ext)
1096 {
1097 return json_encode($this->_render_data);
1098 }
1099
1100 /**
1101 * @return false|string
1102 */
1103 public function enqueueScriptsAfterChartRender()
1104 {
1105 do_action_deprecated(
1106 'wdt-enqueue-scripts-after-chart-render',
1107 array(),
1108 WDT_INITIAL_LITE_VERSION,
1109 'wpdatatables_enqueue_scripts_after_chart_render'
1110 );
1111 do_action('wpdatatables_enqueue_scripts_after_chart_render');
1112
1113
1114 $id = $this->_id;
1115 ob_start();
1116 include(WDT_TEMPLATE_PATH . 'wpdatachart.inc.php');
1117 $chart_html = ob_get_contents();
1118 ob_end_clean();
1119 return $chart_html;
1120 }
1121
1122 /**
1123 * Delete chart by ID
1124 * @param $chartId
1125 * @return bool
1126 */
1127 public static function delete($chartId)
1128 {
1129 global $wpdb;
1130
1131 if (!isset($_REQUEST['wdtNonce']) || empty($chartId) || !current_user_can('manage_options')
1132 || !wp_verify_nonce($_REQUEST['wdtNonce'], 'wdtDeleteChartNonce')) {
1133 return false;
1134 }
1135
1136 $wpdb->delete(
1137 $wpdb->prefix . "wpdatacharts",
1138 array(
1139 'id' => (int)$chartId
1140 )
1141 );
1142
1143 do_action('wpdatatables_after_delete_charts', (int)$chartId, 'chart');
1144
1145 return true;
1146
1147 }
1148
1149 /**
1150 * Get all charts non-paged for the MCE editor
1151 * @return array|null|object
1152 */
1153 public static function getAll()
1154 {
1155 global $wpdb;
1156 $query = "SELECT id, title FROM {$wpdb->prefix}wpdatacharts ";
1157 return $wpdb->get_results($query, ARRAY_A);
1158 }
1159
1160 /**
1161 * @param $renderData
1162 * @return void
1163 */
1164 public function setSpecificChartProperties($renderData)
1165 {
1166
1167 }
1168
1169 /**
1170 * @param $chartData
1171 * @param $renderData
1172 * @return void
1173 */
1174 public function setGeneralChartProperties($chartData, $renderData)
1175 {
1176 $this->setEngine($chartData->engine);
1177 $this->setId($chartData->id);
1178 $this->setwpDataTableId($chartData->wpdatatable_id);
1179 $this->setTitle($chartData->title);
1180 $this->setType($chartData->type);
1181 if (!empty($renderData['series_type'])) {
1182 $this->setSeriesType($renderData['series_type']);
1183 }
1184 $this->setSelectedColumns($renderData['selected_columns']);
1185 $this->setRangeType($renderData['range_type']);
1186 $this->setRowRange($renderData['row_range']);
1187 $this->setShowGrid($renderData['show_grid'] ?: false);
1188 $this->setShowTitle($renderData['show_title'] ?: false);
1189 $this->setResponsiveWidth(isset($renderData['render_data']['options']['responsive_width']) ? (bool)$renderData['render_data']['options']['responsive_width'] : false);
1190 if (!empty($renderData['render_data']['options']['width'])) {
1191 $this->setWidth($renderData['render_data']['options']['width']);
1192 }
1193 $this->setHeight($renderData['render_data']['options']['height']);
1194 }
1195
1196 /**
1197 * @return void
1198 */
1199 public function defineSeries()
1200 {
1201 if (!empty($this->getUserDefinedSeriesData())) {
1202 $seriesIndex = 0;
1203 foreach ($this->_user_defined_series_data as $series_data) {
1204 if (!empty($series_data['color']) || !empty($series_data['type']) || !empty($series_data['label'])) {
1205 $this->_render_data['options']['series'][$seriesIndex] = array(
1206 'color' => $series_data['color'],
1207 'label' => $series_data['label']
1208 );
1209 }
1210 $seriesIndex++;
1211 }
1212 }
1213 }
1214
1215 /**
1216 * @return false|mixed|null
1217 */
1218 public function getDateFormat()
1219 {
1220 return get_option('wdtDateFormat');
1221 }
1222
1223
1224 /**
1225 * @return false|mixed|null
1226 */
1227 public function getDateTimeFormat()
1228 {
1229 return get_option('wdtTimeFormat');
1230 }
1231
1232 /**
1233 * @return mixed|null
1234 */
1235 public function returnRenderData()
1236 {
1237 return $this->_render_data;
1238 }
1239
1240 /**
1241 * @return mixed|null
1242 */
1243 public function getRenderData()
1244 {
1245 return $this->_render_data;
1246 }
1247
1248 /**
1249 * @return array
1250 */
1251 public function formRenderDataForDb()
1252 {
1253 return array(
1254 'selected_columns' => $this->getSelectedColumns(),
1255 'range_type' => $this->getRangeType(),
1256 'row_range' => $this->getRowRange(),
1257 'render_data' => $this->_render_data,
1258 'show_grid' => $this->_show_grid,
1259 'show_title' => $this->_show_title,
1260 'series_type' => $this->getSeriesType()
1261 );
1262 }
1263
1264 /**
1265 * @param $chartData
1266 * @return mixed|null
1267 */
1268 public function setChartRenderData($chartData)
1269 {
1270 $renderData = json_decode($chartData->json_render_data, true);
1271 if (!empty($renderData['render_data'])) {
1272 $this->_render_data = $renderData['render_data'];
1273 }
1274 return $renderData;
1275 }
1276
1277 }