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-2012 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 if(is_array($value)) continue;
190 $properties .= $this->tab("<$key>$value</$key>",2);
193 if (!empty($this->colors_list)){
194 // open the colors tag
195 $properties .= $this->tab("<colors>",2);
196 foreach ($this->colors_list as $color){
197 $properties .= $this->tab("<color>$color</color>",3);
200 // close the colors tag
201 $properties .= $this->tab("</colors>",2);
204 // close the properties tag
205 $properties .= $this->tab("</properties>",1);
211 * returns the y-axis values for the chart
214 * @return string $yAxis XML yAxis tag
217 $this->chart_yAxis['yStep'] = '100';
218 $this->chart_yAxis['yLog'] = '1';
219 $this->chart_yAxis['yMax'] = $this->is_currency ? $this->convertCurrency($this->chart_yAxis['yMax']) : $this->chart_yAxis['yMax'];
220 $max = $this->chart_yAxis['yMax'];
221 $exp = ($max == 0) ? 1 : floor(log10($max));
222 $baseval = $max / pow(10, $exp);
224 // steps will be 10^n, 2*10^n, 5*10^n (where n >= 0)
225 if ($baseval > 0 && $baseval <= 1){
226 $step = 2 * pow(10, $exp-1);
228 else if ($baseval > 1 && $baseval <= 3){
229 $step = 5 * pow(10, $exp-1);
231 else if ($baseval > 3 && $baseval <= 6){
232 $step = 10 * pow(10, $exp-1);
234 else if ($baseval > 6 && $baseval <= 10){
235 $step = 20 * pow(10, $exp-1);
238 // edge cases for values less than 10
239 if ($max == 0 || $step < 1){
243 $this->chart_yAxis['yStep'] = $step;
245 // to compensate, the yMax should be at least one step above the max value
246 $this->chart_yAxis['yMax'] += $this->chart_yAxis['yStep'];
248 $yAxis = $this->tab("<yAxis>" ,1);
250 foreach ($this->chart_yAxis as $key => $value){
251 $yAxis .= $this->tabValue("{$key}",$value, 2);
254 $yAxis .= $this->tab("</yAxis>" ,1);
260 * returns the total amount value for the group by field
262 * @param group by field
263 * @return int $total total value
265 function calculateTotal($group_by){
268 for($i =0; $i < count($this->data_set); $i++){
269 if ($this->data_set[$i][$this->group_by[0]] == $group_by){
270 $total += $this->data_set[$i]['total'];
277 * returns text with tabs appended before it
279 * @param string $str input string
280 * int $depth number of times to tab
281 * @return string with tabs appended before it
283 function tab($str, $depth){
284 return str_repeat("\t", $depth) . $str . "\n";
287 * returns text with tabs appended before it
289 * @param string $str xml tag
290 int $tagFormat 2 = open and close tag, 1 = close, 0 = open
291 sting $value input string
292 * int $depth number of times to tab
293 * @return string with tabs appended before it
296 function tabValue($tag,$value,$depth) {
298 return $this->tab("<{$tag}>".htmlspecialchars($value,ENT_QUOTES)."</{$tag}>",$depth);
302 * returns xml data format
305 * @return string with xml data format
307 function processData(){
310 $group_by = $this->group_by[0];
311 if (isset($this->group_by[1])){
312 $drill_down = $this->group_by[1];
317 for($i =0; $i < count($this->data_set); $i++){
318 if ($this->data_set[$i][$group_by] != $prev_group_by){
319 $prev_group_by = $this->data_set[$i][$group_by];
320 $data[$this->data_set[$i][$group_by]] = array();
323 $data[$this->data_set[$i][$group_by]][] = $this->data_set[$i];
325 // push new item onto legend items list
326 if (isset($drill_down)){
327 if (!in_array($this->data_set[$i][$drill_down], $this->super_set)){
328 $this->super_set[] = $this->data_set[$i][$drill_down];
336 function processDataGroup($tablevel, $title, $value, $label, $link){
337 $link = $this->forceHideDataGroupLink ? '' : $link;
338 $data = $this->tab('<group>',$tablevel);
339 $data .= $this->tabValue('title',$title,$tablevel+1);
340 $data .= $this->tabValue('value',$value,$tablevel+1);
341 $data .= $this->tabValue('label',$label,$tablevel+1);
342 $data .= $this->tab('<link>' . $link . '</link>',$tablevel+1);
343 $data .= $this->tab('</group>',$tablevel);
347 function calculateGroupByTotal($dataset){
350 foreach ($dataset as $key => $value){
357 function calculateSingleBarMax($dataset){
359 foreach ($dataset as $value){
369 * returns correct yAxis min/max
371 * @param value to check
372 * @return yAxis min and max
374 function checkYAxis($value){
375 if ($value < $this->chart_yAxis['yMin']){
376 $this->chart_yAxis['yMin'] = $value;
378 else if ($value > $this->chart_yAxis['yMax']){
379 $this->chart_yAxis['yMax'] = $value;
384 function convertCurrency($to_convert){
387 $decimals = $locale->getPrecision();
388 $amount = ($this->div == 1) ? $to_convert : round($to_convert * $this->div,$decimals);
393 function formatNumber($number, $decimals= null, $decimal_point= null, $thousands_sep= null){
395 if(is_null($decimals)) {
396 $decimals = $locale->getPrecision();
398 $seps = get_number_seperators();
399 $thousands_sep = $seps[0];
400 $decimal_point = $seps[1];
401 return number_format($number, $decimals, $decimal_point, $thousands_sep);
405 $new_data = $this->processData();
407 foreach ($new_data as $groupByKey => $value){
408 $total += $this->calculateTotal($groupByKey);
414 function xmlDataForGroupByChart(){
416 foreach ($this->data_set as $key => $value){
417 $amount = $this->is_currency ? $this->convertCurrency($this->calculateGroupByTotal($value)) : $this->calculateGroupByTotal($value);
418 $label = $this->is_currency ? ($this->currency_symbol . $this->formatNumber($amount)) : $amount;
420 $data .= $this->tab('<group>',2);
421 $data .= $this->tabValue('title',$key,3);
422 $data .= $this->tabValue('value',$amount,3);
423 $data .= $this->tabValue('label',$label,3);
424 $data .= $this->tab('<link></link>',3);
425 $data .= $this->tab('<subgroups>',3);
427 foreach ($value as $k => $v){
428 $amount = $this->is_currency ? $this->convertCurrency($v) : $v;
429 $label = $this->is_currency ? ($this->currency_symbol . $this->formatNumber($amount)) : $amount;
431 $data .= $this->tab('<group>',4);
432 $data .= $this->tabValue('title',$k,5);
433 $data .= $this->tabValue('value',$amount,5);
434 $data .= $this->tabValue('label',$label,5);
435 $data .= $this->tab('<link></link>',5);
436 $data .= $this->tab('</group>',4);
437 $this->checkYAxis($v);
439 $data .= $this->tab('</subgroups>',3);
440 $data .= $this->tab('</group>',2);
446 function xmlDataForGaugeChart(){
448 $gaugePosition = $this->data_set[0]['num'];
449 $this->chart_yAxis['yMax'] = $this->chart_properties['gaugeTarget'];
450 $this->chart_yAxis['yStep'] = 1;
451 $data .= $this->processDataGroup(2, 'GaugePosition', $gaugePosition, $gaugePosition, '');
452 $data .= $this->processGauge($gaugePosition, $this->chart_properties['gaugeTarget']);
457 function xmlDataBarChart(){
459 $max = $this->calculateSingleBarMax($this->data_set);
460 $this->checkYAxis($max);
462 if (isset($this->group_by[0])){
463 $group_by = $this->group_by[0];
464 if (isset($this->group_by[1])){
465 $drill_down = $this->group_by[1];
469 foreach ($this->data_set as $key => $value){
470 if ($this->is_currency){
471 $value = $this->convertCurrency($value);
472 $label = $this->currency_symbol;
473 $label .= $this->formatNumber($value);
474 $label .= $this->thousands_symbol;
480 $data .= $this->tab('<group>', 2);
481 $data .= $this->tabValue('title',$key, 3);
482 $data .= $this->tabValue('value',$value, 3);
483 $data .= $this->tabValue('label',$label, 3);
484 if (isset($drill_down) && $drill_down){
485 if ($this->group_by[0] == 'm') {
486 $additional_param = '&date_closed_advanced=' . urlencode($key);
487 } else if ( $this->group_by[0] == 'sales_stage' ) {
488 $additional_param = '&sales_stage_advanced[]='.urlencode(array_search($key,$GLOBALS['app_list_strings']['sales_stage_dom']));
490 $additional_param = "&" . $this->group_by[0] . "=" . urlencode($key);
492 $url = $this->constructURL() . $additional_param;
494 $data .= $this->tab('<link>' . $url . '</link>', 3);
496 $data .= $this->tab('<subgroups>', 3);
497 $data .= $this->tab('</subgroups>', 3);
498 $data .= $this->tab('</group>', 2);
503 function xmlDataGenericChart(){
505 $group_by = $this->group_by[0];
506 if (isset($this->group_by[1])){
507 $drill_down = $this->group_by[1];
509 $new_data = $this->processData();
511 foreach ($new_data as $groupByKey => $value){
512 $total = $this->calculateTotal($groupByKey);
513 $this->checkYAxis($total);
515 if ($this->group_by[0] == 'm'){
516 $additional_param = '&date_closed_advanced=' . urlencode($groupByKey);
519 $paramValue = (isset($value[0]['key']) && $value[0]['key'] != '') ? $value[0]['key'] : $groupByKey;
520 $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;
521 $additional_param = "&" . $this->group_by[0] . "=" . urlencode($paramValue);
524 $url = $this->constructURL() . $additional_param;
526 $amount = $this->is_currency ? $this->convertCurrency($total) : $total;
527 $label = $this->is_currency ? ($this->currency_symbol . $this->formatNumber($amount) . 'K') : $amount;
529 $data .= $this->tab('<group>',2);
530 $data .= $this->tabValue('title',$groupByKey,3);
531 $data .= $this->tabValue('value',$amount,3);
532 $data .= $this->tabValue('label',$label,3);
533 $data .= $this->tab('<link>' . $url . '</link>',3);
535 $data .= $this->tab('<subgroups>',3);
536 $processed = array();
538 if (isset($drill_down) && $drill_down != ''){
540 * Bug 44696 - Ivan D.
541 * We have to iterate users in order since they are in the super_set for every group.
542 * This is required to display the correct links for each user in a drill down chart.
544 foreach ($this->super_set as $superSetKey => $superSetValue)
546 $objectInSaleStage = false;
547 foreach ($value as $internalKey => $internalValue)
549 if ($internalValue[$drill_down] == $superSetValue)
551 $objectInSaleStage = $value[$internalKey];
555 if ($objectInSaleStage)
557 if (isset($objectInSaleStage[$group_by]) && $objectInSaleStage[$group_by] == $groupByKey)
559 if ($drill_down == 'user_name')
561 $drill_down_param = '&assigned_user_id[]=' . urlencode($objectInSaleStage['assigned_user_id']);
563 else if ($drill_down == 'm')
565 $drill_down_param = '&date_closed_advanced=' . urlencode($objectInSaleStage[$drill_down]);
569 $paramValue = (isset($objectInSaleStage[$drill_down . "_dom_option"]) && $objectInSaleStage[$drill_down . "_dom_option"] != '') ? $objectInSaleStage[$drill_down . "_dom_option"] : $objectInSaleStage[$drill_down];
570 $drill_down_param = '&' . $drill_down . '=' . urlencode($paramValue);
573 if ($this->is_currency)
575 $sub_amount = $this->formatNumber($this->convertCurrency($objectInSaleStage['total']));
576 $sub_amount_formatted = $this->currency_symbol . $sub_amount . 'K';
577 //bug: 38877 - do not format the amount for the value as it breaks the chart
578 $sub_amount = $this->convertCurrency($objectInSaleStage['total']);
582 $sub_amount = $objectInSaleStage['total'];
583 $sub_amount_formatted = $sub_amount;
586 $data .= $this->processDataGroup(4, $objectInSaleStage[$drill_down], $sub_amount, $sub_amount_formatted, $url . $drill_down_param);
590 $data .= $this->nullGroup($superSetValue, $url);
596 $data .= $this->nullGroup($superSetValue, $url);
601 $data .= $this->tab('</subgroups>',3);
602 $data .= $this->tab('</group>',2);
610 * This function sets a null group by clause
612 * @param $sugarSetValue Mixed value
613 * @param $url String value of URL for the link
615 private function nullGroup($superSetValue, $url) {
616 return $this->processDataGroup(4, $superSetValue, 'NULL', '', $url);
621 * returns a name for the XML File
623 * @param string $file_id - unique id to make part of the file name
625 public static function getXMLFileName(
629 global $sugar_config, $current_user;
631 $filename = sugar_cached("xml/"). $current_user->id . '_' . $file_id . '.xml';
633 if ( !is_dir(dirname($filename)) ) {
634 create_cache_directory("xml");
640 public function processXmlData(){
643 if ($this->chart_properties['type'] == 'group by chart'){
644 $data .= $this->xmlDataForGroupByChart();
646 else if ($this->chart_properties['type'] == 'bar chart' || $this->chart_properties['type'] == 'horizontal bar chart'){
647 $data .= $this->xmlDataBarChart();
650 $data .= $this->xmlDataGenericChart();
657 $data = $this->tab('<data>',1);
658 $data .= $this->processXmlData();
659 $data .= $this->tab('</data>',1);
665 * function to generate XML and return it
668 * @return string $xmlContents with xml information
670 function generateXML($xmlDataName = false){
671 $xmlContents = $this->xmlHeader();
672 $xmlContents .= $this->xmlProperties();
673 $xmlContents .= $this->xmlData();
674 $xmlContents .= $this->xmlYAxis();
675 $xmlContents .= $this->xmlFooter();
681 * function to save XML contents into a file
683 * @param string $xmlFilename location of the xml file
684 * string $xmlContents contents of the xml file
685 * @return string boolean denoting whether save has failed
687 function saveXMLFile($xmlFilename,$xmlContents) {
691 $xmlContents = chr(255).chr(254).$GLOBALS['locale']->translateCharset($xmlContents, 'UTF-8', 'UTF-16LE');
694 if (!$fh = sugar_fopen($xmlFilename, 'w')) {
695 $GLOBALS['log']->debug("Cannot open file ($xmlFilename)");
699 // write the contents to the file
700 if (fwrite($fh,$xmlContents) === FALSE) {
701 $GLOBALS['log']->debug("Cannot write to file ($xmlFilename)");
705 $GLOBALS['log']->debug("Success, wrote ($xmlContents) to file ($xmlFilename)");
712 * generates xml file for Flash charts to use for internationalized instances
714 * @param string $xmlFile location of the XML file to write to
717 function generateChartStrings($xmlFile){
718 global $current_language, $app_list_strings;
720 $chartStringsXML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
721 $chartStringsXML .= "<sugarlanguage version=\"1.0\">\n";
722 $chartStringsXML .= $this->tab("<charts>",1);
724 if (empty($app_list_strings)) {
725 //set module and application string arrays based upon selected language
726 $app_list_strings = return_app_list_strings_language($current_language);
729 // retrieve the strings defined at include/language/en_us.lang.php
730 foreach ($app_list_strings['chart_strings'] as $tag => $chart_string){
731 $chartStringsXML .= $this->tab("<$tag>$chart_string</$tag>",2);
734 $chartStringsXML .= $this->tab("</charts>",1);
735 $chartStringsXML .= "</sugarlanguage>\n";
737 $this->saveXMLFile($xmlFile, $chartStringsXML);
741 * wrapper function to return the html code containing the chart in a div
743 * @param string $name name of the div
744 * string $xmlFile location of the XML file
745 * string $style optional additional styles for the div
746 * @return string returns the html code through smarty
748 function display($name, $xmlFile, $width='320', $height='480', $resize=false){
751 // generate strings for chart if it does not exist
752 global $current_language, $theme, $sugar_config,$app_strings;
754 $this->app_strings = $app_strings;
755 $this->chartStringsXML = sugar_cached("xml/").'chart_strings.' . $current_language .'.lang.xml';
756 if (!file_exists($this->chartStringsXML)){
757 $this->generateChartStrings($this->chartStringsXML);
761 return $templateFile;
765 function getDashletScript($id,$xmlFile="") {
767 $xmlFile = (!$xmlFile) ? $sugar_config['tmp_dir']. $current_user->id . '_' . $this->id . '.xml' : $xmlFile;
768 $chartStringsXML = $GLOBALS['sugar_config']['tmp_dir'].'chart_strings.' . $current_language .'.lang.xml';
770 $this->ss->assign('chartName', $id);
771 $this->ss->assign('chartXMLFile', $xmlFile);
772 $this->ss->assign('chartStyleCSS', SugarThemeRegistry::current()->getCSSURL('chart.css'));
773 $this->ss->assign('chartColorsXML', SugarThemeRegistry::current()->getImageURL('sugarColors.xml'));
774 $this->ss->assign('chartLangFile', $GLOBALS['sugar_config']['tmp_dir'].'chart_strings.' . $GLOBALS['current_language'] .'.lang.xml');
777 return $templateFile;
782 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.
783 If the data quantity is large, it maybe a little slow.
784 * @param array $data_set The data get from database
785 string $keycolname1 We will sort by this key first
786 bool $translate1 Whether to trabslate the first column
787 string $keycolname1 We will sort by this key secondly, and it can be null, then it will only sort by the first column.
788 bool $translate1 Whether to trabslate the second column
789 bool $ifsort2 Whether to sort by the second column or just translate the second column.
790 * @return The sorted and translated data.
792 function sortData($data_set, $keycolname1=null, $translate1=false, $keycolname2=null, $translate2=false, $ifsort2=false) {
793 //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.
794 global $app_list_strings;
795 $sortby1[] = array();
796 foreach ($data_set as $row) {
797 $sortby1[] = $row[$keycolname1];
799 $sortby1 = array_unique($sortby1);
800 //The data is from the database, the sorting should be done in the sql. So I will not do the sort here.
802 $temp_sortby1 = array();
803 foreach(array_keys($app_list_strings[$keycolname1.'_dom']) as $sortby1_value) {
804 if(in_array($sortby1_value, $sortby1)) {
805 $temp_sortby1[] = $sortby1_value;
808 $sortby1 = $temp_sortby1;
811 //if(isset($sortby1[0]) && $sortby1[0]=='') unset($sortby1[0]);//the beginning of lead_source_dom is blank.
812 if(isset($sortby1[0]) && $sortby1[0]==array()) unset($sortby1[0]);//the beginning of month after search is blank.
814 if($ifsort2==false) $sortby2=array(0);
816 if($keycolname2!=null) {
818 foreach ($data_set as $row) {
819 $sortby2[] = $row[$keycolname2];
821 //The data is from the database, the sorting should be done in the sql. So I will not do the sort here.
822 $sortby2 = array_unique($sortby2);
824 $temp_sortby2 = array();
825 foreach(array_keys($app_list_strings[$keycolname2.'_dom']) as $sortby2_value) {
826 if(in_array($sortby2_value, $sortby2)) {
827 $temp_sortby2[] = $sortby2_value;
830 $sortby2 = $temp_sortby2;
836 foreach($sortby1 as $sort1) {
837 foreach($sortby2 as $sort2) {
839 foreach($data_set as $key => $value){
840 if($value[$keycolname1] == $sort1 && (!$ifsort2 || $value[$keycolname2]== $sort2)) {
842 $value[$keycolname1.'_dom_option'] = $value[$keycolname1];
843 $value[$keycolname1] = $app_list_strings[$keycolname1.'_dom'][$value[$keycolname1]];
846 $value[$keycolname2.'_dom_option'] = $value[$keycolname2];
847 $value[$keycolname2] = $app_list_strings[$keycolname2.'_dom'][$value[$keycolname2]];
849 array_push($data, $value);
850 unset($data_set[$key]);
854 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.
859 $val[$keycolname1] = $app_list_strings[$keycolname1.'_dom'][$sort1];
860 $val[$keycolname1.'_dom_option'] = $sort1;
863 $val[$keycolname1] = $sort1;
866 $val[$keycolname2] = $app_list_strings[$keycolname2.'_dom'][$sort2];
867 $val[$keycolname2.'_dom_option'] = $sort2;
869 elseif($keycolname2!=null) {
870 $val[$keycolname2] = $sort2;
872 array_push($data, $val);
879 function getChartResources() {
885 function getMySugarChartResources() {
886 $mySugarResources = "";
887 return $mySugarResources;
891 * wrapper function to return chart array after any additional processing
893 * @param array $chartsArray array of chart config items that need processing
894 * @return array $chartArray after it has been process
896 function chartArray($chartsArray) {