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 ********************************************************************************/
43 var $forceHideDataGroupLink = false;
44 var $data_set = array();
45 var $display_data = array();
46 var $chart_properties = array();
47 var $chart_yAxis = array();
48 var $group_by = array();
49 var $super_set = array();
50 var $colors_list = array();
51 var $base_url = array();
52 var $url_params = array();
55 var $thousands_symbol;
57 var $supports_image_export = false;
58 var $print_html_legend_pdf = false;
59 var $image_export_type = "";
61 public function __construct() {
62 $this->db = &DBManagerFactory::getInstance();
63 $this->ss = new Sugar_Smarty();
65 $this->chart_yAxis['yMin'] = 0;
66 $this->chart_yAxis['yMax'] = 0;
69 if ($GLOBALS['current_user']->getPreference('currency')){
71 $currency = new Currency();
72 $currency->retrieve($GLOBALS['current_user']->getPreference('currency'));
73 $this->div = $currency->conversion_rate;
74 $this->currency_symbol = $currency->symbol;
77 $this->currency_symbol = $GLOBALS['sugar_config']['default_currency_symbol'];
79 $this->is_currency = false;
81 $this->image_export_type = (extension_loaded('gd') && function_exists('gd_info')) ? "png" : "jpg";
84 function getData($query){
85 $result = $this->db->query($query);
87 $row = $this->db->fetchByAssoc($result);
90 $this->data_set[] = $row;
91 $row = $this->db->fetchByAssoc($result);
95 function constructBaseURL(){
99 foreach ($this->base_url as $param => $value){
100 if ($numParams == 0){
101 $url .= $param . '=' . $value;
104 $url .= '&' .$param . '=' .$value;
112 function constructURL(){
113 $url = $this->constructBaseURL();
114 foreach ($this->url_params as $param => $value){
115 if ($param == 'assigned_user_id') $param = 'assigned_user_id[]';
116 if (is_array($value)){
117 foreach($value as $multiple){
118 $url .= '&' . $param . '=' . urlencode($multiple);
122 $url .= '&' . $param . '=' . urlencode($value);
128 function setData($dataSet){
129 $this->data_set = $dataSet;
132 function setProperties($title, $subtitle, $type, $legend='on', $labels='value', $print='on'){
133 $this->chart_properties['title'] = $title;
134 $this->chart_properties['subtitle'] = $subtitle;
135 $this->chart_properties['type'] = $type;
136 $this->chart_properties['legend'] = $legend;
137 $this->chart_properties['labels'] = $labels;
140 function setDisplayProperty($property, $value){
141 $this->chart_properties[$property] = $value;
144 function setColors($colors = array()){
145 $this->colors_list = $colors;
149 * returns the header for the constructed xml file for sugarcharts
152 * @return string $header XML header
154 function xmlHeader(){
155 $header = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
156 $header .= "<sugarcharts version=\"1.0\">\n";
162 * returns the footer for the constructed xml file for sugarcharts
165 * @return string $footer XML footer
167 function xmlFooter(){
168 $footer = "</sugarcharts>";
174 * returns the properties tag for the constructed xml file for sugarcharts
177 * @return string $properties XML properties tag
179 function xmlProperties(){
180 // open the properties tag
181 $properties = $this->tab("<properties>",1);
183 // grab the property and value from the chart_properties variable
184 foreach ($this->chart_properties as $key => $value){
185 $properties .= $this->tab("<$key>$value</$key>",2);
188 if (!empty($this->colors_list)){
189 // open the colors tag
190 $properties .= $this->tab("<colors>",2);
191 foreach ($this->colors_list as $color){
192 $properties .= $this->tab("<color>$color</color>",3);
195 // close the colors tag
196 $properties .= $this->tab("</colors>",2);
199 // close the properties tag
200 $properties .= $this->tab("</properties>",1);
206 * returns the y-axis values for the chart
209 * @return string $yAxis XML yAxis tag
212 $this->chart_yAxis['yStep'] = '100';
213 $this->chart_yAxis['yLog'] = '1';
214 $this->chart_yAxis['yMax'] = $this->is_currency ? $this->convertCurrency($this->chart_yAxis['yMax']) : $this->chart_yAxis['yMax'];
215 $max = $this->chart_yAxis['yMax'];
216 $exp = ($max == 0) ? 1 : floor(log10($max));
217 $baseval = $max / pow(10, $exp);
219 // steps will be 10^n, 2*10^n, 5*10^n (where n >= 0)
220 if ($baseval > 0 && $baseval <= 1){
221 $step = 2 * pow(10, $exp-1);
223 else if ($baseval > 1 && $baseval <= 3){
224 $step = 5 * pow(10, $exp-1);
226 else if ($baseval > 3 && $baseval <= 6){
227 $step = 10 * pow(10, $exp-1);
229 else if ($baseval > 6 && $baseval <= 10){
230 $step = 20 * pow(10, $exp-1);
233 // edge cases for values less than 10
234 if ($max == 0 || $step < 1){
238 $this->chart_yAxis['yStep'] = $step;
240 // to compensate, the yMax should be at least one step above the max value
241 $this->chart_yAxis['yMax'] += $this->chart_yAxis['yStep'];
243 $yAxis = $this->tab("<yAxis>" ,1);
245 foreach ($this->chart_yAxis as $key => $value){
246 $yAxis .= $this->tabValue("{$key}",$value, 2);
249 $yAxis .= $this->tab("</yAxis>" ,1);
255 * returns the total amount value for the group by field
257 * @param group by field
258 * @return int $total total value
260 function calculateTotal($group_by){
263 for($i =0; $i < count($this->data_set); $i++){
264 if ($this->data_set[$i][$this->group_by[0]] == $group_by){
265 $total += $this->data_set[$i]['total'];
272 * returns text with tabs appended before it
274 * @param string $str input string
275 * int $depth number of times to tab
276 * @return string with tabs appended before it
278 function tab($str, $depth){
279 return str_repeat("\t", $depth) . $str . "\n";
282 * returns text with tabs appended before it
284 * @param string $str xml tag
285 int $tagFormat 2 = open and close tag, 1 = close, 0 = open
286 sting $value input string
287 * int $depth number of times to tab
288 * @return string with tabs appended before it
291 function tabValue($tag,$value,$depth) {
293 return $this->tab("<{$tag}>".htmlspecialchars($value,ENT_QUOTES)."</{$tag}>",$depth);
297 * returns xml data format
300 * @return string with xml data format
302 function processData(){
305 $group_by = $this->group_by[0];
306 if (isset($this->group_by[1])){
307 $drill_down = $this->group_by[1];
312 for($i =0; $i < count($this->data_set); $i++){
313 if ($this->data_set[$i][$group_by] != $prev_group_by){
314 $prev_group_by = $this->data_set[$i][$group_by];
315 $data[$this->data_set[$i][$group_by]] = array();
318 $data[$this->data_set[$i][$group_by]][] = $this->data_set[$i];
320 // push new item onto legend items list
321 if (isset($drill_down)){
322 if (!in_array($this->data_set[$i][$drill_down], $this->super_set)){
323 $this->super_set[] = $this->data_set[$i][$drill_down];
331 function processDataGroup($tablevel, $title, $value, $label, $link){
332 $link = $this->forceHideDataGroupLink ? '' : $link;
333 $data = $this->tab('<group>',$tablevel);
334 $data .= $this->tabValue('title',$title,$tablevel+1);
335 $data .= $this->tabValue('value',$value,$tablevel+1);
336 $data .= $this->tabValue('label',$label,$tablevel+1);
337 $data .= $this->tab('<link>' . $link . '</link>',$tablevel+1);
338 $data .= $this->tab('</group>',$tablevel);
342 function calculateGroupByTotal($dataset){
345 foreach ($dataset as $key => $value){
352 function calculateSingleBarMax($dataset){
354 foreach ($dataset as $value){
364 * returns correct yAxis min/max
366 * @param value to check
367 * @return yAxis min and max
369 function checkYAxis($value){
370 if ($value < $this->chart_yAxis['yMin']){
371 $this->chart_yAxis['yMin'] = $value;
373 else if ($value > $this->chart_yAxis['yMax']){
374 $this->chart_yAxis['yMax'] = $value;
379 function convertCurrency($to_convert){
382 $decimals = $locale->getPrecision();
383 $amount = ($this->div == 1) ? $to_convert : round($to_convert * $this->div,$decimals);
388 function formatNumber($number, $decimals= null, $decimal_point= null, $thousands_sep= null){
390 if(is_null($decimals)) {
391 $decimals = $locale->getPrecision();
393 $seps = get_number_seperators();
394 $thousands_sep = $seps[0];
395 $decimal_point = $seps[1];
396 return number_format($number, $decimals, $decimal_point, $thousands_sep);
400 $new_data = $this->processData();
402 foreach ($new_data as $groupByKey => $value){
403 $total += $this->calculateTotal($groupByKey);
409 function xmlDataForGroupByChart(){
411 foreach ($this->data_set as $key => $value){
412 $amount = $this->is_currency ? $this->convertCurrency($this->calculateGroupByTotal($value)) : $this->calculateGroupByTotal($value);
413 $label = $this->is_currency ? ($this->currency_symbol . $this->formatNumber($amount)) : $amount;
415 $data .= $this->tab('<group>',2);
416 $data .= $this->tabValue('title',$key,3);
417 $data .= $this->tabValue('value',$amount,3);
418 $data .= $this->tabValue('label',$label,3);
419 $data .= $this->tab('<link></link>',3);
420 $data .= $this->tab('<subgroups>',3);
422 foreach ($value as $k => $v){
423 $amount = $this->is_currency ? $this->convertCurrency($v) : $v;
424 $label = $this->is_currency ? ($this->currency_symbol . $this->formatNumber($amount)) : $amount;
426 $data .= $this->tab('<group>',4);
427 $data .= $this->tabValue('title',$k,5);
428 $data .= $this->tabValue('value',$amount,5);
429 $data .= $this->tabValue('label',$label,5);
430 $data .= $this->tab('<link></link>',5);
431 $data .= $this->tab('</group>',4);
432 $this->checkYAxis($v);
434 $data .= $this->tab('</subgroups>',3);
435 $data .= $this->tab('</group>',2);
441 function xmlDataForGaugeChart(){
443 $gaugePosition = $this->data_set[0]['num'];
444 $this->chart_yAxis['yMax'] = $this->chart_properties['gaugeTarget'];
445 $this->chart_yAxis['yStep'] = 1;
446 $data .= $this->processDataGroup(2, 'GaugePosition', $gaugePosition, $gaugePosition, '');
447 $data .= $this->processGauge($gaugePosition, $this->chart_properties['gaugeTarget']);
452 function xmlDataBarChart(){
454 $max = $this->calculateSingleBarMax($this->data_set);
455 $this->checkYAxis($max);
457 if (isset($this->group_by[0])){
458 $group_by = $this->group_by[0];
459 if (isset($this->group_by[1])){
460 $drill_down = $this->group_by[1];
464 foreach ($this->data_set as $key => $value){
465 if ($this->is_currency){
466 $value = $this->convertCurrency($value);
467 $label = $this->currency_symbol;
468 $label .= $this->formatNumber($value);
469 $label .= $this->thousands_symbol;
475 $data .= $this->tab('<group>', 2);
476 $data .= $this->tabValue('title',$key, 3);
477 $data .= $this->tabValue('value',$value, 3);
478 $data .= $this->tabValue('label',$label, 3);
479 if (isset($drill_down) && $drill_down){
480 if ($this->group_by[0] == 'm') {
481 $additional_param = '&date_closed_advanced=' . urlencode($key);
482 } else if ( $this->group_by[0] == 'sales_stage' ) {
483 $additional_param = '&sales_stage_advanced[]='.urlencode(array_search($key,$GLOBALS['app_list_strings']['sales_stage_dom']));
485 $additional_param = "&" . $this->group_by[0] . "=" . urlencode($key);
487 $url = $this->constructURL() . $additional_param;
489 $data .= $this->tab('<link>' . $url . '</link>', 3);
491 $data .= $this->tab('<subgroups>', 3);
492 $data .= $this->tab('</subgroups>', 3);
493 $data .= $this->tab('</group>', 2);
498 function xmlDataGenericChart(){
500 $group_by = $this->group_by[0];
501 if (isset($this->group_by[1])){
502 $drill_down = $this->group_by[1];
504 $new_data = $this->processData();
506 foreach ($new_data as $groupByKey => $value){
507 $total = $this->calculateTotal($groupByKey);
508 $this->checkYAxis($total);
510 if ($this->group_by[0] == 'm'){
511 $additional_param = '&date_closed_advanced=' . urlencode($groupByKey);
514 $paramValue = (isset($value[0]['key']) && $value[0]['key'] != '') ? $value[0]['key'] : $groupByKey;
515 $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;
516 $additional_param = "&" . $this->group_by[0] . "=" . urlencode($paramValue);
519 $url = $this->constructURL() . $additional_param;
521 $amount = $this->is_currency ? $this->convertCurrency($total) : $total;
522 $label = $this->is_currency ? ($this->currency_symbol . $this->formatNumber($amount) . 'K') : $amount;
524 $data .= $this->tab('<group>',2);
525 $data .= $this->tabValue('title',$groupByKey,3);
526 $data .= $this->tabValue('value',$amount,3);
527 $data .= $this->tabValue('label',$label,3);
528 $data .= $this->tab('<link>' . $url . '</link>',3);
530 $data .= $this->tab('<subgroups>',3);
531 $processed = array();
533 if (isset($drill_down) && $drill_down != ''){
536 * We have to iterate users in order they are in super_set for every group.
537 * That is required for correct pileup user links to user colors in a chart.
539 foreach ($this->super_set as $superSetKey => $superSetValue)
541 $objectInSaleStage = false;
542 foreach ($value as $internalKey => $internalValue)
544 if ($internalValue[$drill_down] == $superSetValue)
546 $objectInSaleStage = $value[$internalKey];
550 if ($objectInSaleStage)
552 if (isset($objectInSaleStage[$group_by]) && $objectInSaleStage[$group_by] == $groupByKey)
554 if ($drill_down == 'user_name')
556 $drill_down_param = '&assigned_user_id[]=' . urlencode($objectInSaleStage['assigned_user_id']);
558 else if ($drill_down == 'm')
560 $drill_down_param = '&date_closed_advanced=' . urlencode($objectInSaleStage[$drill_down]);
564 $paramValue = (isset($objectInSaleStage[$drill_down . "_dom_option"]) && $objectInSaleStage[$drill_down . "_dom_option"] != '') ? $objectInSaleStage[$drill_down . "_dom_option"] : $objectInSaleStage[$drill_down];
565 $drill_down_param = '&' . $drill_down . '=' . urlencode($paramValue);
568 if ($this->is_currency)
570 $sub_amount = $this->formatNumber($this->convertCurrency($objectInSaleStage['total']));
571 $sub_amount_formatted = $this->currency_symbol . $sub_amount . 'K';
572 //bug: 38877 - do not format the amount for the value as it breaks the chart
573 $sub_amount = $this->convertCurrency($objectInSaleStage['total']);
577 $sub_amount = $objectInSaleStage['total'];
578 $sub_amount_formatted = $sub_amount;
581 $data .= $this->processDataGroup(4, $objectInSaleStage[$drill_down], $sub_amount, $sub_amount_formatted, $url . $drill_down_param);
585 $data .= $this->nullGroup($superSetValue, $url);
590 $data .= $this->nullGroup($superSetValue, $url);
596 $data .= $this->tab('</subgroups>',3);
597 $data .= $this->tab('</group>',2);
604 * This function sets a null group by clause
606 * @param $sugarSetValue Mixed value
607 * @param $url String value of URL for the link
609 public function nullGroup($superSetValue, $url) {
610 return $this->processDataGroup(4, $superSetValue, 'NULL', '', $url);
615 * returns a name for the XML File
617 * @param string $file_id - unique id to make part of the file name
619 public static function getXMLFileName(
623 global $sugar_config, $current_user;
625 $filename = sugar_cached("xml/"). $current_user->id . '_' . $file_id . '.xml';
627 if ( !is_dir(dirname($filename)) ) {
628 create_cache_directory("xml");
634 public function processXmlData(){
637 if ($this->chart_properties['type'] == 'group by chart'){
638 $data .= $this->xmlDataForGroupByChart();
640 else if ($this->chart_properties['type'] == 'bar chart' || $this->chart_properties['type'] == 'horizontal bar chart'){
641 $data .= $this->xmlDataBarChart();
644 $data .= $this->xmlDataGenericChart();
651 $data = $this->tab('<data>',1);
652 $data .= $this->processXmlData();
653 $data .= $this->tab('</data>',1);
659 * function to generate XML and return it
662 * @return string $xmlContents with xml information
664 function generateXML($xmlDataName = false){
665 $xmlContents = $this->xmlHeader();
666 $xmlContents .= $this->xmlProperties();
667 $xmlContents .= $this->xmlData();
668 $xmlContents .= $this->xmlYAxis();
669 $xmlContents .= $this->xmlFooter();
675 * function to save XML contents into a file
677 * @param string $xmlFilename location of the xml file
678 * string $xmlContents contents of the xml file
679 * @return string boolean denoting whether save has failed
681 function saveXMLFile($xmlFilename,$xmlContents) {
685 $xmlContents = chr(255).chr(254).$GLOBALS['locale']->translateCharset($xmlContents, 'UTF-8', 'UTF-16LE');
688 if (!$fh = sugar_fopen($xmlFilename, 'w')) {
689 $GLOBALS['log']->debug("Cannot open file ($xmlFilename)");
693 // write the contents to the file
694 if (fwrite($fh,$xmlContents) === FALSE) {
695 $GLOBALS['log']->debug("Cannot write to file ($xmlFilename)");
699 $GLOBALS['log']->debug("Success, wrote ($xmlContents) to file ($xmlFilename)");
706 * generates xml file for Flash charts to use for internationalized instances
708 * @param string $xmlFile location of the XML file to write to
711 function generateChartStrings($xmlFile){
712 global $current_language, $app_list_strings;
714 $chartStringsXML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
715 $chartStringsXML .= "<sugarlanguage version=\"1.0\">\n";
716 $chartStringsXML .= $this->tab("<charts>",1);
718 if (empty($app_list_strings)) {
719 //set module and application string arrays based upon selected language
720 $app_list_strings = return_app_list_strings_language($current_language);
723 // retrieve the strings defined at include/language/en_us.lang.php
724 foreach ($app_list_strings['chart_strings'] as $tag => $chart_string){
725 $chartStringsXML .= $this->tab("<$tag>$chart_string</$tag>",2);
728 $chartStringsXML .= $this->tab("</charts>",1);
729 $chartStringsXML .= "</sugarlanguage>\n";
731 $this->saveXMLFile($xmlFile, $chartStringsXML);
735 * wrapper function to return the html code containing the chart in a div
737 * @param string $name name of the div
738 * string $xmlFile location of the XML file
739 * string $style optional additional styles for the div
740 * @return string returns the html code through smarty
742 function display($name, $xmlFile, $width='320', $height='480', $resize=false){
745 // generate strings for chart if it does not exist
746 global $current_language, $theme, $sugar_config,$app_strings;
748 $this->app_strings = $app_strings;
749 $this->chartStringsXML = sugar_cached("xml/").'chart_strings.' . $current_language .'.lang.xml';
750 if (!file_exists($this->chartStringsXML)){
751 $this->generateChartStrings($this->chartStringsXML);
755 return $templateFile;
758 function getDashletScript($id,$xmlFile="") {
760 $xmlFile = (!$xmlFile) ? sugar_cached("xml/"). $current_user->id . '_' . $this->id . '.xml' : $xmlFile;
761 $chartStringsXML = sugar_cached("xml/").'chart_strings.' . $current_language .'.lang.xml';
763 $this->ss->assign('chartName', $id);
764 $this->ss->assign('chartXMLFile', $xmlFile);
765 $this->ss->assign('chartStyleCSS', SugarThemeRegistry::current()->getCSSURL('chart.css'));
766 $this->ss->assign('chartColorsXML', SugarThemeRegistry::current()->getImageURL('sugarColors.xml'));
767 $this->ss->assign('chartLangFile', sugar_cached("xml/").'chart_strings.' . $GLOBALS['current_language'] .'.lang.xml');
770 return $templateFile;
775 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.
776 If the data quantity is large, it maybe a little slow.
777 * @param array $data_set The data get from database
778 string $keycolname1 We will sort by this key first
779 bool $translate1 Whether to trabslate the first column
780 string $keycolname1 We will sort by this key secondly, and it can be null, then it will only sort by the first column.
781 bool $translate1 Whether to trabslate the second column
782 bool $ifsort2 Whether to sort by the second column or just translate the second column.
783 * @return The sorted and translated data.
785 function sortData($data_set, $keycolname1=null, $translate1=false, $keycolname2=null, $translate2=false, $ifsort2=false) {
786 //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.
787 global $app_list_strings;
788 $sortby1[] = array();
789 foreach ($data_set as $row) {
790 $sortby1[] = $row[$keycolname1];
792 $sortby1 = array_unique($sortby1);
793 //The data is from the database, the sorting should be done in the sql. So I will not do the sort here.
795 $temp_sortby1 = array();
796 foreach(array_keys($app_list_strings[$keycolname1.'_dom']) as $sortby1_value) {
797 if(in_array($sortby1_value, $sortby1)) {
798 $temp_sortby1[] = $sortby1_value;
801 $sortby1 = $temp_sortby1;
804 //if(isset($sortby1[0]) && $sortby1[0]=='') unset($sortby1[0]);//the beginning of lead_source_dom is blank.
805 if(isset($sortby1[0]) && $sortby1[0]==array()) unset($sortby1[0]);//the beginning of month after search is blank.
807 if($ifsort2==false) $sortby2=array(0);
809 if($keycolname2!=null) {
811 foreach ($data_set as $row) {
812 $sortby2[] = $row[$keycolname2];
814 //The data is from the database, the sorting should be done in the sql. So I will not do the sort here.
815 $sortby2 = array_unique($sortby2);
817 $temp_sortby2 = array();
818 foreach(array_keys($app_list_strings[$keycolname2.'_dom']) as $sortby2_value) {
819 if(in_array($sortby2_value, $sortby2)) {
820 $temp_sortby2[] = $sortby2_value;
823 $sortby2 = $temp_sortby2;
829 foreach($sortby1 as $sort1) {
830 foreach($sortby2 as $sort2) {
832 foreach($data_set as $key => $value){
833 if($value[$keycolname1] == $sort1 && (!$ifsort2 || $value[$keycolname2]== $sort2)) {
835 $value[$keycolname1.'_dom_option'] = $value[$keycolname1];
836 $value[$keycolname1] = $app_list_strings[$keycolname1.'_dom'][$value[$keycolname1]];
839 $value[$keycolname2.'_dom_option'] = $value[$keycolname2];
840 $value[$keycolname2] = $app_list_strings[$keycolname2.'_dom'][$value[$keycolname2]];
842 array_push($data, $value);
843 unset($data_set[$key]);
847 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.
852 $val[$keycolname1] = $app_list_strings[$keycolname1.'_dom'][$sort1];
853 $val[$keycolname1.'_dom_option'] = $sort1;
856 $val[$keycolname1] = $sort1;
859 $val[$keycolname2] = $app_list_strings[$keycolname2.'_dom'][$sort2];
860 $val[$keycolname2.'_dom_option'] = $sort2;
862 elseif($keycolname2!=null) {
863 $val[$keycolname2] = $sort2;
865 array_push($data, $val);
872 function getChartResources() {
878 function getMySugarChartResources() {
879 $mySugarResources = "";
880 return $mySugarResources;
884 * wrapper function to return chart array after any additional processing
886 * @param array $chartsArray array of chart config items that need processing
887 * @return array $chartArray after it has been process
889 function chartArray($chartsArray) {