2 if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
3 /*********************************************************************************
4 * SugarCRM Community Edition is a customer relationship management program developed by
5 * SugarCRM, Inc. Copyright (C) 2004-2011 SugarCRM Inc.
7 * This program is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU Affero General Public License version 3 as published by the
9 * Free Software Foundation with the addition of the following permission added
10 * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
11 * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
12 * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
14 * This program is distributed in the hope that it will be useful, but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
19 * You should have received a copy of the GNU Affero General Public License along with
20 * this program; if not, see http://www.gnu.org/licenses or write to the Free
21 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
24 * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
25 * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
27 * The interactive user interfaces in modified source and object code versions
28 * of this program must display Appropriate Legal Notices, as required under
29 * Section 5 of the GNU Affero General Public License version 3.
31 * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
32 * these Appropriate Legal Notices must retain the display of the "Powered by
33 * SugarCRM" logo. If the display of the logo is not reasonably feasible for
34 * technical reasons, the Appropriate Legal Notices must display the words
35 * "Powered by SugarCRM".
36 ********************************************************************************/
47 var $forceHideDataGroupLink = false;
48 var $data_set = array();
49 var $display_data = array();
50 var $chart_properties = array();
51 var $chart_yAxis = array();
52 var $group_by = array();
53 var $super_set = array();
54 var $colors_list = array();
55 var $base_url = array();
56 var $url_params = array();
59 var $thousands_symbol;
61 var $supports_image_export = false;
62 var $print_html_legend_pdf = false;
63 var $image_export_type = "";
65 public function __construct() {
66 $this->db = &DBManagerFactory::getInstance();
67 $this->ss = new Sugar_Smarty();
69 $this->chart_yAxis['yMin'] = 0;
70 $this->chart_yAxis['yMax'] = 0;
73 if ($GLOBALS['current_user']->getPreference('currency')){
75 $currency = new Currency();
76 $currency->retrieve($GLOBALS['current_user']->getPreference('currency'));
77 $this->div = $currency->conversion_rate;
78 $this->currency_symbol = $currency->symbol;
81 $this->currency_symbol = $GLOBALS['sugar_config']['default_currency_symbol'];
83 $this->is_currency = false;
85 $this->image_export_type = (extension_loaded('gd') && function_exists('gd_info')) ? "png" : "jpg";
88 function getData($query){
89 $result = $this->db->query($query);
91 $row = $this->db->fetchByAssoc($result);
94 $this->data_set[] = $row;
95 $row = $this->db->fetchByAssoc($result);
99 function constructBaseURL(){
103 foreach ($this->base_url as $param => $value){
104 if ($numParams == 0){
105 $url .= $param . '=' . $value;
108 $url .= '&' .$param . '=' .$value;
116 function constructURL(){
117 $url = $this->constructBaseURL();
118 foreach ($this->url_params as $param => $value){
119 if ($param == 'assigned_user_id') $param = 'assigned_user_id[]';
120 if (is_array($value)){
121 foreach($value as $multiple){
122 $url .= '&' . $param . '=' . urlencode($multiple);
126 $url .= '&' . $param . '=' . urlencode($value);
132 function setData($dataSet){
133 $this->data_set = $dataSet;
136 function setProperties($title, $subtitle, $type, $legend='on', $labels='value', $print='on'){
137 $this->chart_properties['title'] = $title;
138 $this->chart_properties['subtitle'] = $subtitle;
139 $this->chart_properties['type'] = $type;
140 $this->chart_properties['legend'] = $legend;
141 $this->chart_properties['labels'] = $labels;
144 function setDisplayProperty($property, $value){
145 $this->chart_properties[$property] = $value;
148 function setColors($colors = array()){
149 $this->colors_list = $colors;
153 * returns the header for the constructed xml file for sugarcharts
156 * @return string $header XML header
158 function xmlHeader(){
159 $header = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
160 $header .= "<sugarcharts version=\"1.0\">\n";
166 * returns the footer for the constructed xml file for sugarcharts
169 * @return string $footer XML footer
171 function xmlFooter(){
172 $footer = "</sugarcharts>";
178 * returns the properties tag for the constructed xml file for sugarcharts
181 * @return string $properties XML properties tag
183 function xmlProperties(){
184 // open the properties tag
185 $properties = $this->tab("<properties>",1);
187 // grab the property and value from the chart_properties variable
188 foreach ($this->chart_properties as $key => $value){
189 $properties .= $this->tab("<$key>$value</$key>",2);
192 if (!empty($this->colors_list)){
193 // open the colors tag
194 $properties .= $this->tab("<colors>",2);
195 foreach ($this->colors_list as $color){
196 $properties .= $this->tab("<color>$color</color>",3);
199 // close the colors tag
200 $properties .= $this->tab("</colors>",2);
203 // close the properties tag
204 $properties .= $this->tab("</properties>",1);
210 * returns the y-axis values for the chart
213 * @return string $yAxis XML yAxis tag
216 $this->chart_yAxis['yStep'] = '100';
217 $this->chart_yAxis['yLog'] = '1';
218 $this->chart_yAxis['yMax'] = $this->is_currency ? $this->convertCurrency($this->chart_yAxis['yMax']) : $this->chart_yAxis['yMax'];
219 $max = $this->chart_yAxis['yMax'];
220 $exp = ($max == 0) ? 1 : floor(log10($max));
221 $baseval = $max / pow(10, $exp);
223 // steps will be 10^n, 2*10^n, 5*10^n (where n >= 0)
224 if ($baseval > 0 && $baseval <= 1){
225 $step = 2 * pow(10, $exp-1);
227 else if ($baseval > 1 && $baseval <= 3){
228 $step = 5 * pow(10, $exp-1);
230 else if ($baseval > 3 && $baseval <= 6){
231 $step = 10 * pow(10, $exp-1);
233 else if ($baseval > 6 && $baseval <= 10){
234 $step = 20 * pow(10, $exp-1);
237 // edge cases for values less than 10
238 if ($max == 0 || $step < 1){
242 $this->chart_yAxis['yStep'] = $step;
244 // to compensate, the yMax should be at least one step above the max value
245 $this->chart_yAxis['yMax'] += $this->chart_yAxis['yStep'];
247 $yAxis = $this->tab("<yAxis>" ,1);
249 foreach ($this->chart_yAxis as $key => $value){
250 $yAxis .= $this->tabValue("{$key}",$value, 2);
253 $yAxis .= $this->tab("</yAxis>" ,1);
259 * returns the total amount value for the group by field
261 * @param group by field
262 * @return int $total total value
264 function calculateTotal($group_by){
267 for($i =0; $i < count($this->data_set); $i++){
268 if ($this->data_set[$i][$this->group_by[0]] == $group_by){
269 $total += $this->data_set[$i]['total'];
276 * returns text with tabs appended before it
278 * @param string $str input string
279 * int $depth number of times to tab
280 * @return string with tabs appended before it
282 function tab($str, $depth){
283 return str_repeat("\t", $depth) . $str . "\n";
286 * returns text with tabs appended before it
288 * @param string $str xml tag
289 int $tagFormat 2 = open and close tag, 1 = close, 0 = open
290 sting $value input string
291 * int $depth number of times to tab
292 * @return string with tabs appended before it
295 function tabValue($tag,$value,$depth) {
297 return $this->tab("<{$tag}>".htmlspecialchars($value,ENT_QUOTES)."</{$tag}>",$depth);
301 * returns xml data format
304 * @return string with xml data format
306 function processData(){
309 $group_by = $this->group_by[0];
310 if (isset($this->group_by[1])){
311 $drill_down = $this->group_by[1];
316 for($i =0; $i < count($this->data_set); $i++){
317 if ($this->data_set[$i][$group_by] != $prev_group_by){
318 $prev_group_by = $this->data_set[$i][$group_by];
319 $data[$this->data_set[$i][$group_by]] = array();
322 $data[$this->data_set[$i][$group_by]][] = $this->data_set[$i];
324 // push new item onto legend items list
325 if (isset($drill_down)){
326 if (!in_array($this->data_set[$i][$drill_down], $this->super_set)){
327 $this->super_set[] = $this->data_set[$i][$drill_down];
335 function processDataGroup($tablevel, $title, $value, $label, $link){
336 $link = $this->forceHideDataGroupLink ? '' : $link;
337 $data = $this->tab('<group>',$tablevel);
338 $data .= $this->tabValue('title',$title,$tablevel+1);
339 $data .= $this->tabValue('value',$value,$tablevel+1);
340 $data .= $this->tabValue('label',$label,$tablevel+1);
341 $data .= $this->tab('<link>' . $link . '</link>',$tablevel+1);
342 $data .= $this->tab('</group>',$tablevel);
346 function calculateGroupByTotal($dataset){
349 foreach ($dataset as $key => $value){
356 function calculateSingleBarMax($dataset){
358 foreach ($dataset as $value){
368 * returns correct yAxis min/max
370 * @param value to check
371 * @return yAxis min and max
373 function checkYAxis($value){
374 if ($value < $this->chart_yAxis['yMin']){
375 $this->chart_yAxis['yMin'] = $value;
377 else if ($value > $this->chart_yAxis['yMax']){
378 $this->chart_yAxis['yMax'] = $value;
383 function convertCurrency($to_convert){
386 $decimals = $locale->getPrecision();
387 $amount = ($this->div == 1) ? $to_convert : round($to_convert * $this->div,$decimals);
392 function formatNumber($number, $decimals= null, $decimal_point= null, $thousands_sep= null){
394 if(is_null($decimals)) {
395 $decimals = $locale->getPrecision();
397 $seps = get_number_seperators();
398 $thousands_sep = $seps[0];
399 $decimal_point = $seps[1];
400 return number_format($number, $decimals, $decimal_point, $thousands_sep);
404 $new_data = $this->processData();
406 foreach ($new_data as $groupByKey => $value){
407 $total += $this->calculateTotal($groupByKey);
413 function xmlDataForGroupByChart(){
415 foreach ($this->data_set as $key => $value){
416 $amount = $this->is_currency ? $this->convertCurrency($this->calculateGroupByTotal($value)) : $this->calculateGroupByTotal($value);
417 $label = $this->is_currency ? ($this->currency_symbol . $this->formatNumber($amount)) : $amount;
419 $data .= $this->tab('<group>',2);
420 $data .= $this->tabValue('title',$key,3);
421 $data .= $this->tabValue('value',$amount,3);
422 $data .= $this->tabValue('label',$label,3);
423 $data .= $this->tab('<link></link>',3);
424 $data .= $this->tab('<subgroups>',3);
426 foreach ($value as $k => $v){
427 $amount = $this->is_currency ? $this->convertCurrency($v) : $v;
428 $label = $this->is_currency ? ($this->currency_symbol . $this->formatNumber($amount)) : $amount;
430 $data .= $this->tab('<group>',4);
431 $data .= $this->tabValue('title',$k,5);
432 $data .= $this->tabValue('value',$amount,5);
433 $data .= $this->tabValue('label',$label,5);
434 $data .= $this->tab('<link></link>',5);
435 $data .= $this->tab('</group>',4);
436 $this->checkYAxis($v);
438 $data .= $this->tab('</subgroups>',3);
439 $data .= $this->tab('</group>',2);
445 function xmlDataForGaugeChart(){
447 $gaugePosition = $this->data_set[0]['num'];
448 $this->chart_yAxis['yMax'] = $this->chart_properties['gaugeTarget'];
449 $this->chart_yAxis['yStep'] = 1;
450 $data .= $this->processDataGroup(2, 'GaugePosition', $gaugePosition, $gaugePosition, '');
451 $data .= $this->processGauge($gaugePosition, $this->chart_properties['gaugeTarget']);
456 function xmlDataBarChart(){
458 $max = $this->calculateSingleBarMax($this->data_set);
459 $this->checkYAxis($max);
461 if (isset($this->group_by[0])){
462 $group_by = $this->group_by[0];
463 if (isset($this->group_by[1])){
464 $drill_down = $this->group_by[1];
468 foreach ($this->data_set as $key => $value){
469 if ($this->is_currency){
470 $value = $this->convertCurrency($value);
471 $label = $this->currency_symbol;
472 $label .= $this->formatNumber($value);
473 $label .= $this->thousands_symbol;
479 $data .= $this->tab('<group>', 2);
480 $data .= $this->tabValue('title',$key, 3);
481 $data .= $this->tabValue('value',$value, 3);
482 $data .= $this->tabValue('label',$label, 3);
483 if (isset($drill_down) && $drill_down){
484 if ($this->group_by[0] == 'm') {
485 $additional_param = '&date_closed_advanced=' . urlencode($key);
486 } else if ( $this->group_by[0] == 'sales_stage' ) {
487 $additional_param = '&sales_stage_advanced[]='.urlencode(array_search($key,$GLOBALS['app_list_strings']['sales_stage_dom']));
489 $additional_param = "&" . $this->group_by[0] . "=" . urlencode($key);
491 $url = $this->constructURL() . $additional_param;
493 $data .= $this->tab('<link>' . $url . '</link>', 3);
495 $data .= $this->tab('<subgroups>', 3);
496 $data .= $this->tab('</subgroups>', 3);
497 $data .= $this->tab('</group>', 2);
502 function xmlDataGenericChart(){
504 $group_by = $this->group_by[0];
505 if (isset($this->group_by[1])){
506 $drill_down = $this->group_by[1];
508 $new_data = $this->processData();
510 foreach ($new_data as $groupByKey => $value){
511 $total = $this->calculateTotal($groupByKey);
512 $this->checkYAxis($total);
514 if ($this->group_by[0] == 'm'){
515 $additional_param = '&date_closed_advanced=' . urlencode($groupByKey);
518 $paramValue = (isset($value[0]['key']) && $value[0]['key'] != '') ? $value[0]['key'] : $groupByKey;
519 $paramValue = (isset($value[0][$this->group_by[0]."_dom_option"]) && $value[0][$this->group_by[0]."_dom_option"] != '') ? $value[0][$this->group_by[0]."_dom_option"] : $paramValue;
520 $additional_param = "&" . $this->group_by[0] . "=" . urlencode($paramValue);
523 $url = $this->constructURL() . $additional_param;
525 $amount = $this->is_currency ? $this->convertCurrency($total) : $total;
526 $label = $this->is_currency ? ($this->currency_symbol . $this->formatNumber($amount) . 'K') : $amount;
528 $data .= $this->tab('<group>',2);
529 $data .= $this->tabValue('title',$groupByKey,3);
530 $data .= $this->tabValue('value',$amount,3);
531 $data .= $this->tabValue('label',$label,3);
532 $data .= $this->tab('<link>' . $url . '</link>',3);
534 $data .= $this->tab('<subgroups>',3);
535 $processed = array();
537 if (isset($drill_down) && $drill_down != ''){
540 * We have to iterate users in order they are in super_set for every group.
541 * That is required for correct pileup user links to user colors in a chart.
543 foreach ($this->super_set as $superSetKey => $superSetValue)
545 $objectInSaleStage = false;
546 foreach ($value as $internalKey => $internalValue)
548 if ($internalValue[$drill_down] == $superSetValue)
550 $objectInSaleStage = $value[$internalKey];
554 if ($objectInSaleStage)
556 if (isset($objectInSaleStage[$group_by]) && $objectInSaleStage[$group_by] == $groupByKey)
558 if ($drill_down == 'user_name')
560 $drill_down_param = '&assigned_user_id[]=' . urlencode($objectInSaleStage['assigned_user_id']);
562 else if ($drill_down == 'm')
564 $drill_down_param = '&date_closed_advanced=' . urlencode($objectInSaleStage[$drill_down]);
568 $paramValue = (isset($objectInSaleStage[$drill_down . "_dom_option"]) && $objectInSaleStage[$drill_down . "_dom_option"] != '') ? $objectInSaleStage[$drill_down . "_dom_option"] : $objectInSaleStage[$drill_down];
569 $drill_down_param = '&' . $drill_down . '=' . urlencode($paramValue);
572 if ($this->is_currency)
574 $sub_amount = $this->formatNumber($this->convertCurrency($objectInSaleStage['total']));
575 $sub_amount_formatted = $this->currency_symbol . $sub_amount . 'K';
576 //bug: 38877 - do not format the amount for the value as it breaks the chart
577 $sub_amount = $this->convertCurrency($objectInSaleStage['total']);
581 $sub_amount = $objectInSaleStage['total'];
582 $sub_amount_formatted = $sub_amount;
585 $data .= $this->processDataGroup(4, $objectInSaleStage[$drill_down], $sub_amount, $sub_amount_formatted, $url . $drill_down_param);
589 $data .= $this->nullGroup($superSetValue, $url);
594 $data .= $this->nullGroup($superSetValue, $url);
600 $data .= $this->tab('</subgroups>',3);
601 $data .= $this->tab('</group>',2);
608 * This function sets a null group by clause
610 * @param $sugarSetValue Mixed value
611 * @param $url String value of URL for the link
613 public function nullGroup($superSetValue, $url) {
614 return $this->processDataGroup(4, $superSetValue, 'NULL', '', $url);
619 * returns a name for the XML File
621 * @param string $file_id - unique id to make part of the file name
623 public static function getXMLFileName(
627 global $sugar_config, $current_user;
629 $filename = sugar_cached("xml/"). $current_user->id . '_' . $file_id . '.xml';
631 if ( !is_dir(dirname($filename)) ) {
632 create_cache_directory("xml");
638 public function processXmlData(){
641 if ($this->chart_properties['type'] == 'group by chart'){
642 $data .= $this->xmlDataForGroupByChart();
644 else if ($this->chart_properties['type'] == 'bar chart' || $this->chart_properties['type'] == 'horizontal bar chart'){
645 $data .= $this->xmlDataBarChart();
648 $data .= $this->xmlDataGenericChart();
655 $data = $this->tab('<data>',1);
656 $data .= $this->processXmlData();
657 $data .= $this->tab('</data>',1);
663 * function to generate XML and return it
666 * @return string $xmlContents with xml information
668 function generateXML($xmlDataName = false){
669 $xmlContents = $this->xmlHeader();
670 $xmlContents .= $this->xmlProperties();
671 $xmlContents .= $this->xmlData();
672 $xmlContents .= $this->xmlYAxis();
673 $xmlContents .= $this->xmlFooter();
679 * function to save XML contents into a file
681 * @param string $xmlFilename location of the xml file
682 * string $xmlContents contents of the xml file
683 * @return string boolean denoting whether save has failed
685 function saveXMLFile($xmlFilename,$xmlContents) {
689 $xmlContents = chr(255).chr(254).$GLOBALS['locale']->translateCharset($xmlContents, 'UTF-8', 'UTF-16LE');
692 if (!$fh = sugar_fopen($xmlFilename, 'w')) {
693 $GLOBALS['log']->debug("Cannot open file ($xmlFilename)");
697 // write the contents to the file
698 if (fwrite($fh,$xmlContents) === FALSE) {
699 $GLOBALS['log']->debug("Cannot write to file ($xmlFilename)");
703 $GLOBALS['log']->debug("Success, wrote ($xmlContents) to file ($xmlFilename)");
710 * generates xml file for Flash charts to use for internationalized instances
712 * @param string $xmlFile location of the XML file to write to
715 function generateChartStrings($xmlFile){
716 global $current_language, $app_list_strings;
718 $chartStringsXML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
719 $chartStringsXML .= "<sugarlanguage version=\"1.0\">\n";
720 $chartStringsXML .= $this->tab("<charts>",1);
722 if (empty($app_list_strings)) {
723 //set module and application string arrays based upon selected language
724 $app_list_strings = return_app_list_strings_language($current_language);
727 // retrieve the strings defined at include/language/en_us.lang.php
728 foreach ($app_list_strings['chart_strings'] as $tag => $chart_string){
729 $chartStringsXML .= $this->tab("<$tag>$chart_string</$tag>",2);
732 $chartStringsXML .= $this->tab("</charts>",1);
733 $chartStringsXML .= "</sugarlanguage>\n";
735 $this->saveXMLFile($xmlFile, $chartStringsXML);
739 * wrapper function to return the html code containing the chart in a div
741 * @param string $name name of the div
742 * string $xmlFile location of the XML file
743 * string $style optional additional styles for the div
744 * @return string returns the html code through smarty
746 function display($name, $xmlFile, $width='320', $height='480', $resize=false){
749 // generate strings for chart if it does not exist
750 global $current_language, $theme, $sugar_config,$app_strings;
752 $this->app_strings = $app_strings;
753 $this->chartStringsXML = sugar_cached("xml/").'chart_strings.' . $current_language .'.lang.xml';
754 if (!file_exists($this->chartStringsXML)){
755 $this->generateChartStrings($this->chartStringsXML);
759 return $templateFile;
762 function getDashletScript($id,$xmlFile="") {
764 $xmlFile = (!$xmlFile) ? sugar_cached("xml/"). $current_user->id . '_' . $this->id . '.xml' : $xmlFile;
765 $chartStringsXML = sugar_cached("xml/").'chart_strings.' . $current_language .'.lang.xml';
767 $this->ss->assign('chartName', $id);
768 $this->ss->assign('chartXMLFile', $xmlFile);
769 $this->ss->assign('chartStyleCSS', SugarThemeRegistry::current()->getCSSURL('chart.css'));
770 $this->ss->assign('chartColorsXML', SugarThemeRegistry::current()->getImageURL('sugarColors.xml'));
771 $this->ss->assign('chartLangFile', sugar_cached("xml/").'chart_strings.' . $GLOBALS['current_language'] .'.lang.xml');
774 return $templateFile;
779 This function is used for localize all the characters in the Chart. And it can also sort all the dom_values by the sequence defined in the dom, but this may produce a lot of extra empty data in the xml file, when the chart is sorted by two key cols.
780 If the data quantity is large, it maybe a little slow.
781 * @param array $data_set The data get from database
782 string $keycolname1 We will sort by this key first
783 bool $translate1 Whether to trabslate the first column
784 string $keycolname1 We will sort by this key secondly, and it can be null, then it will only sort by the first column.
785 bool $translate1 Whether to trabslate the second column
786 bool $ifsort2 Whether to sort by the second column or just translate the second column.
787 * @return The sorted and translated data.
789 function sortData($data_set, $keycolname1=null, $translate1=false, $keycolname2=null, $translate2=false, $ifsort2=false) {
790 //You can set whether the columns need to be translated or sorted. It the column needn't to be translated, the sorting must be done in SQL, this function will not do the sorting.
791 global $app_list_strings;
792 $sortby1[] = array();
793 foreach ($data_set as $row) {
794 $sortby1[] = $row[$keycolname1];
796 $sortby1 = array_unique($sortby1);
797 //The data is from the database, the sorting should be done in the sql. So I will not do the sort here.
799 $temp_sortby1 = array();
800 foreach(array_keys($app_list_strings[$keycolname1.'_dom']) as $sortby1_value) {
801 if(in_array($sortby1_value, $sortby1)) {
802 $temp_sortby1[] = $sortby1_value;
805 $sortby1 = $temp_sortby1;
808 //if(isset($sortby1[0]) && $sortby1[0]=='') unset($sortby1[0]);//the beginning of lead_source_dom is blank.
809 if(isset($sortby1[0]) && $sortby1[0]==array()) unset($sortby1[0]);//the beginning of month after search is blank.
811 if($ifsort2==false) $sortby2=array(0);
813 if($keycolname2!=null) {
815 foreach ($data_set as $row) {
816 $sortby2[] = $row[$keycolname2];
818 //The data is from the database, the sorting should be done in the sql. So I will not do the sort here.
819 $sortby2 = array_unique($sortby2);
821 $temp_sortby2 = array();
822 foreach(array_keys($app_list_strings[$keycolname2.'_dom']) as $sortby2_value) {
823 if(in_array($sortby2_value, $sortby2)) {
824 $temp_sortby2[] = $sortby2_value;
827 $sortby2 = $temp_sortby2;
833 foreach($sortby1 as $sort1) {
834 foreach($sortby2 as $sort2) {
836 foreach($data_set as $key => $value){
837 if($value[$keycolname1] == $sort1 && (!$ifsort2 || $value[$keycolname2]== $sort2)) {
839 $value[$keycolname1.'_dom_option'] = $value[$keycolname1];
840 $value[$keycolname1] = $app_list_strings[$keycolname1.'_dom'][$value[$keycolname1]];
843 $value[$keycolname2.'_dom_option'] = $value[$keycolname2];
844 $value[$keycolname2] = $app_list_strings[$keycolname2.'_dom'][$value[$keycolname2]];
846 array_push($data, $value);
847 unset($data_set[$key]);
851 if($ifsort2 && $a==0) {//Add 0 for sorting by the second column, if the first row doesn't have a certain col, it will fill the column with 0.
856 $val[$keycolname1] = $app_list_strings[$keycolname1.'_dom'][$sort1];
857 $val[$keycolname1.'_dom_option'] = $sort1;
860 $val[$keycolname1] = $sort1;
863 $val[$keycolname2] = $app_list_strings[$keycolname2.'_dom'][$sort2];
864 $val[$keycolname2.'_dom_option'] = $sort2;
866 elseif($keycolname2!=null) {
867 $val[$keycolname2] = $sort2;
869 array_push($data, $val);
876 function getChartResources() {
882 function getMySugarChartResources() {
883 $mySugarResources = "";
884 return $mySugarResources;
888 * wrapper function to return chart array after any additional processing
890 * @param array $chartsArray array of chart config items that need processing
891 * @return array $chartArray after it has been process
893 function chartArray($chartsArray) {