Merge pull request #15020 from MegaphoneJon/core-1182-517
[civicrm-core.git] / CRM / Utils / OpenFlashChart.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
fee14197 4 | CiviCRM version 5 |
6a488035 5 +--------------------------------------------------------------------+
6b83d5bd 6 | Copyright CiviCRM LLC (c) 2004-2019 |
6a488035
TO
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
9 | |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
13 | |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
18 | |
19 | You should have received a copy of the GNU Affero General Public |
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 +--------------------------------------------------------------------+
d25dd0ee 26 */
6a488035
TO
27
28/**
29 *
30 * @package CRM
6b83d5bd 31 * @copyright CiviCRM LLC (c) 2004-2019
6a488035
TO
32 */
33
34require_once 'packages/OpenFlashChart/php-ofc-library/open-flash-chart.php';
35
36/**
37 * Build various graphs using Open Flash Chart library.
38 */
39class CRM_Utils_OpenFlashChart {
40
41 /**
100fef9d 42 * Colours.
6a488035 43 * @var array
6a488035 44 */
be2fb01f 45 private static $_colours = [
353ffa53
TO
46 "#C3CC38",
47 "#C8B935",
48 "#CEA632",
49 "#D3932F",
50 "#D9802C",
51 "#FA6900",
52 "#DC9B57",
53 "#F78F01",
54 "#5AB56E",
55 "#6F8069",
56 "#C92200",
57 "#EB6C5C",
be2fb01f 58 ];
6a488035
TO
59
60 /**
61 * Build The Bar Gharph.
62 *
77855840
TO
63 * @param array $params
64 * Assoc array of name/value pairs.
6a488035 65 *
a6c01b45
CW
66 * @return object
67 * $chart object of open flash chart.
6a488035 68 */
00be9182 69 public static function &barChart(&$params) {
6a488035
TO
70 $chart = NULL;
71 if (empty($params)) {
72 return $chart;
73 }
a7488080 74 if (empty($params['multiValues'])) {
be2fb01f 75 $params['multiValues'] = [$params['values']];
0c98098b 76 }
6a488035 77
0c98098b 78 $values = CRM_Utils_Array::value('multiValues', $params);
6a488035
TO
79 if (!is_array($values) || empty($values)) {
80 return $chart;
81 }
82
83 // get the required data.
0d8afee2 84 $chartTitle = !empty($params['legend']) ? $params['legend'] : ts('Bar Chart');
6a488035 85
be2fb01f 86 $xValues = $yValues = [];
0c98098b
DS
87 $xValues = array_keys($values[0]);
88 $yValues = array_values($values[0]);
89
50bfb460 90 // set y axis parameters.
6a488035
TO
91 $yMin = 0;
92
93 // calculate max scale for graph.
94 $yMax = ceil(max($yValues));
95 if ($mod = $yMax % (str_pad(5, strlen($yMax) - 1, 0))) {
96 $yMax += str_pad(5, strlen($yMax) - 1, 0) - $mod;
97 }
98 $ySteps = $yMax / 5;
99
be2fb01f 100 $bars = [];
a5dfa653 101 $symbol = CRM_Core_BAO_Country::defaultCurrencySymbol();
0c98098b
DS
102 foreach ($values as $barCount => $barVal) {
103 $bars[$barCount] = new bar_glass();
6a488035 104
0c98098b
DS
105 $yValues = array_values($barVal);
106 foreach ($yValues as &$yVal) {
107 // type casting is required for chart to render values correctly
108 $yVal = (double) $yVal;
109 }
110 $bars[$barCount]->set_values($yValues);
111 if ($barCount > 0) {
112 // FIXME: for bars > 2, we'll need to come out with other colors
481a74f4 113 $bars[$barCount]->colour('#BF3B69');
0c98098b 114 }
d7797bc2 115
0c98098b 116 if ($barKey = CRM_Utils_Array::value($barCount, CRM_Utils_Array::value('barKeys', $params))) {
e7292422 117 $bars[$barCount]->key($barKey, 12);
0c98098b
DS
118 }
119
120 // call user define function to handle on click event.
121 if ($onClickFunName = CRM_Utils_Array::value('on_click_fun_name', $params)) {
122 $bars[$barCount]->set_on_click($onClickFunName);
123 }
124
125 // get the currency to set in tooltip.
126 $tooltip = CRM_Utils_Array::value('tip', $params, "$symbol #val#");
127 $bars[$barCount]->set_tooltip($tooltip);
128 }
6a488035
TO
129
130 // create x axis label obj.
131 $xLabels = new x_axis_labels();
d7797bc2 132 // set_labels function requires xValues array of string or x_axis_label
5a0052f4 133 // so type casting array values to string values
353ffa53 134 array_walk($xValues, function (&$value, $index) {
e7292422
TO
135 $value = (string) $value;
136 });
6a488035
TO
137 $xLabels->set_labels($xValues);
138
139 // set angle for labels.
140 if ($xLabelAngle = CRM_Utils_Array::value('xLabelAngle', $params)) {
141 $xLabels->rotate($xLabelAngle);
142 }
143
144 // create x axis obj.
145 $xAxis = new x_axis();
146 $xAxis->set_labels($xLabels);
147
50bfb460 148 // create y axis and set range.
6a488035
TO
149 $yAxis = new y_axis();
150 $yAxis->set_range($yMin, $yMax, $ySteps);
151
152 // create chart title obj.
153 $title = new title($chartTitle);
154
155 // create chart.
156 $chart = new open_flash_chart();
157
158 // add x axis w/ labels to chart.
159 $chart->set_x_axis($xAxis);
160
161 // add y axis values to chart.
162 $chart->add_y_axis($yAxis);
163
164 // set title to chart.
165 $chart->set_title($title);
166
167 // add bar element to chart.
0c98098b
DS
168 foreach ($bars as $bar) {
169 $chart->add_element($bar);
170 }
6a488035
TO
171
172 // add x axis legend.
173 if ($xName = CRM_Utils_Array::value('xname', $params)) {
174 $xLegend = new x_legend($xName);
175 $xLegend->set_style("{font-size: 13px; color:#000000; font-family: Verdana; text-align: center;}");
176 $chart->set_x_legend($xLegend);
177 }
178
179 // add y axis legend.
180 if ($yName = CRM_Utils_Array::value('yname', $params)) {
181 $yLegend = new y_legend($yName);
182 $yLegend->set_style("{font-size: 13px; color:#000000; font-family: Verdana; text-align: center;}");
183 $chart->set_y_legend($yLegend);
184 }
185
186 return $chart;
187 }
188
189 /**
190 * Build The Pie Gharph.
191 *
77855840
TO
192 * @param array $params
193 * Assoc array of name/value pairs.
6a488035 194 *
a6c01b45
CW
195 * @return object
196 * $chart object of open flash chart.
6a488035 197 */
00be9182 198 public static function &pieChart(&$params) {
6a488035
TO
199 $chart = NULL;
200 if (empty($params)) {
201 return $chart;
202 }
203 $allValues = CRM_Utils_Array::value('values', $params);
204 if (!is_array($allValues) || empty($allValues)) {
205 return $chart;
206 }
207
208 // get the required data.
be2fb01f 209 $values = [];
6a488035 210 foreach ($allValues as $label => $value) {
e7292422 211 $values[] = new pie_value((double) $value, $label);
6a488035 212 }
0d8afee2 213 $graphTitle = !empty($params['legend']) ? $params['legend'] : ts('Pie Chart');
6a488035 214
50bfb460 215 // get the currency.
a5dfa653 216 $symbol = CRM_Core_BAO_Country::defaultCurrencySymbol();
6a488035
TO
217
218 $pie = new pie();
219 $pie->radius(100);
220
221 // call user define function to handle on click event.
222 if ($onClickFunName = CRM_Utils_Array::value('on_click_fun_name', $params)) {
223 $pie->on_click($onClickFunName);
224 }
225
226 $pie->set_start_angle(35);
227 $pie->add_animation(new pie_fade());
228 $pie->add_animation(new pie_bounce(2));
229
230 // set the tooltip.
231 $tooltip = CRM_Utils_Array::value('tip', $params, "Amount is $symbol #val# of $symbol #total# <br>#percent#");
232 $pie->set_tooltip($tooltip);
233
234 // set colours.
235 $pie->set_colours(self::$_colours);
236
237 $pie->set_values($values);
238
50bfb460 239 // create chart.
6a488035
TO
240 $chart = new open_flash_chart();
241
242 // create chart title obj.
243 $title = new title($graphTitle);
244 $chart->set_title($title);
245
246 $chart->add_element($pie);
247 $chart->x_axis = NULL;
248
249 return $chart;
250 }
251
252 /**
253 * Build The 3-D Bar Gharph.
254 *
77855840
TO
255 * @param array $params
256 * Assoc array of name/value pairs.
6a488035 257 *
a6c01b45
CW
258 * @return object
259 * $chart object of open flash chart.
6a488035 260 */
00be9182 261 public static function &bar_3dChart(&$params) {
6a488035
TO
262 $chart = NULL;
263 if (empty($params)) {
264 return $chart;
265 }
266
267 // $params['values'] should contains the values for each
268 // criteria defined in $params['criteria']
269 $values = CRM_Utils_Array::value('values', $params);
b44e3f84
DS
270 $criteria = CRM_Utils_Array::value('criteria', $params);
271 if (!is_array($values) || empty($values) || !is_array($criteria) || empty($criteria)) {
6a488035
TO
272 return $chart;
273 }
274
275 // get the required data.
be2fb01f 276 $xReferences = $xValueLabels = $xValues = $yValues = [];
6a488035
TO
277
278 foreach ($values as $xVal => $yVal) {
279 if (!is_array($yVal) || empty($yVal)) {
280 continue;
281 }
282
e7292422 283 $xValueLabels[] = (string) $xVal;
b44e3f84 284 foreach ($criteria as $criteria) {
e7292422
TO
285 $xReferences[$criteria][$xVal] = (double) CRM_Utils_Array::value($criteria, $yVal, 0);
286 $yValues[] = (double) CRM_Utils_Array::value($criteria, $yVal, 0);
6a488035
TO
287 }
288 }
289
290 if (empty($xReferences)) {
291
292 return $chart;
293
294 }
295
296 // get the currency.
a5dfa653 297 $symbol = CRM_Core_BAO_Country::defaultCurrencySymbol();
6a488035
TO
298
299 // set the tooltip.
300 $tooltip = CRM_Utils_Array::value('tip', $params, "$symbol #val#");
301
302 $count = 0;
303 foreach ($xReferences as $criteria => $values) {
304 $toolTipVal = $tooltip;
b44e3f84 305 // for separate tooltip for each criteria
6a488035
TO
306 if (is_array($tooltip)) {
307 $toolTipVal = CRM_Utils_Array::value($criteria, $tooltip, "$symbol #val#");
308 }
309
310 // create bar_3d object
311 $xValues[$count] = new bar_3d();
312 // set colour pattel
313 $xValues[$count]->set_colour(self::$_colours[$count]);
b44e3f84 314 // define colur pattel with bar criteria
e7292422 315 $xValues[$count]->key((string) $criteria, 12);
6a488035
TO
316 // define bar chart values
317 $xValues[$count]->set_values(array_values($values));
318
319 // set tooltip
320 $xValues[$count]->set_tooltip($toolTipVal);
321 $count++;
322 }
323
0d8afee2 324 $chartTitle = !empty($params['legend']) ? $params['legend'] : ts('Bar Chart');
6a488035 325
50bfb460 326 // set y axis parameters.
6a488035
TO
327 $yMin = 0;
328
329 // calculate max scale for graph.
330 $yMax = ceil(max($yValues));
331 if ($mod = $yMax % (str_pad(5, strlen($yMax) - 1, 0))) {
332 $yMax += str_pad(5, strlen($yMax) - 1, 0) - $mod;
333 }
334
335 // if max value of y-axis <= 0, then set default values
336 if ($yMax <= 0) {
337 $ySteps = 1;
338 $yMax = 5;
339 }
340 else {
341 $ySteps = $yMax / 5;
342 }
343
344 // create x axis label obj.
345 $xLabels = new x_axis_labels();
346 $xLabels->set_labels($xValueLabels);
347
348 // set angle for labels.
349 if ($xLabelAngle = CRM_Utils_Array::value('xLabelAngle', $params)) {
350 $xLabels->rotate($xLabelAngle);
351 }
352
353 // create x axis obj.
354 $xAxis = new x_axis();
355 $xAxis->set_labels($xLabels);
356
50bfb460 357 // create y axis and set range.
6a488035
TO
358 $yAxis = new y_axis();
359 $yAxis->set_range($yMin, $yMax, $ySteps);
360
361 // create chart title obj.
362 $title = new title($chartTitle);
363
364 // create chart.
365 $chart = new open_flash_chart();
366
367 // add x axis w/ labels to chart.
368 $chart->set_x_axis($xAxis);
369
370 // add y axis values to chart.
371 $chart->add_y_axis($yAxis);
372
373 // set title to chart.
374 $chart->set_title($title);
375
376 foreach ($xValues as $bar) {
377 // add bar element to chart.
378 $chart->add_element($bar);
379 }
380
381 // add x axis legend.
382 if ($xName = CRM_Utils_Array::value('xname', $params)) {
383 $xLegend = new x_legend($xName);
384 $xLegend->set_style("{font-size: 13px; color:#000000; font-family: Verdana; text-align: center;}");
385 $chart->set_x_legend($xLegend);
386 }
387
388 // add y axis legend.
389 if ($yName = CRM_Utils_Array::value('yname', $params)) {
390 $yLegend = new y_legend($yName);
391 $yLegend->set_style("{font-size: 13px; color:#000000; font-family: Verdana; text-align: center;}");
392 $chart->set_y_legend($yLegend);
393 }
394
395 return $chart;
396 }
397
5bc392e6
EM
398 /**
399 * @param $rows
400 * @param $chart
401 * @param $interval
402 *
403 * @return array
404 */
00be9182 405 public static function chart($rows, $chart, $interval) {
891a34b4 406 $lcInterval = strtolower($interval);
407 $label = ucfirst($lcInterval);
be2fb01f
CW
408 $chartData = $dateKeys = [];
409 $intervalLabels = [
58f49704 410 'year' => ts('Yearly'),
411 'fiscalyear' => ts('Yearly (Fiscal)'),
891a34b4 412 'month' => ts('Monthly'),
413 'quarter' => ts('Quarterly'),
414 'week' => ts('Weekly'),
58f49704 415 'yearweek' => ts('Weekly'),
be2fb01f 416 ];
891a34b4 417
418 switch ($lcInterval) {
419 case 'month':
420 case 'quarter':
421 case 'week':
58f49704 422 case 'yearweek':
6a488035
TO
423 foreach ($rows['receive_date'] as $key => $val) {
424 list($year, $month) = explode('-', $val);
58f49704 425 $dateKeys[] = substr($rows[$interval][$key], 0, 3) . ' of ' . $year;
6a488035 426 }
891a34b4 427 $legend = $intervalLabels[$lcInterval];
6a488035
TO
428 break;
429
891a34b4 430 default:
6a488035
TO
431 foreach ($rows['receive_date'] as $key => $val) {
432 list($year, $month) = explode('-', $val);
0c98098b 433 $dateKeys[] = $year;
6a488035 434 }
be2fb01f 435 $legend = ts("%1", [1 => $label]);
58f49704 436 if (!empty($intervalLabels[$lcInterval])) {
437 $legend = $intervalLabels[$lcInterval];
438 }
6a488035
TO
439 break;
440 }
441
0c98098b 442 if (!empty($dateKeys)) {
be2fb01f 443 $graph = [];
0c98098b 444 if (!array_key_exists('multiValue', $rows)) {
be2fb01f 445 $rows['multiValue'] = [$rows['value']];
0c98098b
DS
446 }
447 foreach ($rows['multiValue'] as $key => $val) {
448 $graph[$key] = array_combine($dateKeys, $rows['multiValue'][$key]);
449 }
be2fb01f 450 $chartData = [
317fceb4 451 'legend' => "$legend " . CRM_Utils_Array::value('legend', $rows, ts('Contribution')) . ' ' . ts('Summary'),
452 'values' => $graph[0],
453 'multiValues' => $graph,
be2fb01f
CW
454 'barKeys' => CRM_Utils_Array::value('barKeys', $rows, []),
455 ];
0c98098b
DS
456 }
457
6a488035 458 // rotate the x labels.
0c98098b 459 $chartData['xLabelAngle'] = CRM_Utils_Array::value('xLabelAngle', $rows, 0);
a7488080 460 if (!empty($rows['tip'])) {
6a488035
TO
461 $chartData['tip'] = $rows['tip'];
462 }
463
50bfb460 464 // legend
6a488035
TO
465 $chartData['xname'] = CRM_Utils_Array::value('xname', $rows);
466 $chartData['yname'] = CRM_Utils_Array::value('yname', $rows);
467
468 // carry some chart params if pass.
be2fb01f 469 foreach ([
6714d8d2
SL
470 'xSize',
471 'ySize',
472 'divName',
473 ] as $f) {
a7488080 474 if (!empty($rows[$f])) {
6a488035
TO
475 $chartData[$f] = $rows[$f];
476 }
477 }
478
479 return self::buildChart($chartData, $chart);
480 }
481
5bc392e6
EM
482 /**
483 * @param $rows
484 * @param $chart
485 * @param $interval
486 * @param $chartInfo
487 *
488 * @return array
489 */
00be9182 490 public static function reportChart($rows, $chart, $interval, &$chartInfo) {
6a488035
TO
491 foreach ($interval as $key => $val) {
492 $graph[$val] = $rows['value'][$key];
493 }
494
be2fb01f 495 $chartData = [
6a488035
TO
496 'values' => $graph,
497 'legend' => $chartInfo['legend'],
498 'xname' => $chartInfo['xname'],
499 'yname' => $chartInfo['yname'],
be2fb01f 500 ];
6a488035
TO
501
502 // rotate the x labels.
503 $chartData['xLabelAngle'] = CRM_Utils_Array::value('xLabelAngle', $chartInfo, 20);
a7488080 504 if (!empty($chartInfo['tip'])) {
6a488035
TO
505 $chartData['tip'] = $chartInfo['tip'];
506 }
507
508 // carry some chart params if pass.
be2fb01f 509 foreach ([
6714d8d2
SL
510 'xSize',
511 'ySize',
512 'divName',
513 ] as $f) {
a7488080 514 if (!empty($rows[$f])) {
6a488035
TO
515 $chartData[$f] = $rows[$f];
516 }
517 }
518
519 return self::buildChart($chartData, $chart);
520 }
521
5bc392e6 522 /**
c490a46a 523 * @param array $params
5bc392e6
EM
524 * @param $chart
525 *
526 * @return array
527 */
00be9182 528 public static function buildChart(&$params, $chart) {
be2fb01f 529 $openFlashChart = [];
6a488035
TO
530 if ($chart && is_array($params) && !empty($params)) {
531 // build the chart objects.
0e6e8724 532 $chartObj = CRM_Utils_OpenFlashChart::$chart($params);
6a488035 533
be2fb01f 534 $openFlashChart = [];
6a488035
TO
535 if ($chartObj) {
536 // calculate chart size.
537 $xSize = CRM_Utils_Array::value('xSize', $params, 400);
538 $ySize = CRM_Utils_Array::value('ySize', $params, 300);
539 if ($chart == 'barChart') {
540 $ySize = CRM_Utils_Array::value('ySize', $params, 250);
541 $xSize = 60 * count($params['values']);
50bfb460 542 // hack to show tooltip.
6a488035
TO
543 if ($xSize < 200) {
544 $xSize = (count($params['values']) > 1) ? 100 * count($params['values']) : 170;
545 }
546 elseif ($xSize > 600 && count($params['values']) > 1) {
547 $xSize = (count($params['values']) + 400 / count($params['values'])) * count($params['values']);
548 }
549 }
550
551 // generate unique id for this chart instance
552 $uniqueId = md5(uniqid(rand(), TRUE));
553
be2fb01f 554 $openFlashChart["chart_{$uniqueId}"]['size'] = ['xSize' => $xSize, 'ySize' => $ySize];
6a488035
TO
555 $openFlashChart["chart_{$uniqueId}"]['object'] = $chartObj;
556
557 // assign chart data to template
558 $template = CRM_Core_Smarty::singleton();
559 $template->assign('uniqueId', $uniqueId);
560 $template->assign("openFlashChartData", json_encode($openFlashChart));
561 }
562 }
563
564 return $openFlashChart;
565 }
96025800 566
6a488035 567}