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