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