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 $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 != ''){
539 * Bug 44696 - Ivan D.
540 * We have to iterate users in order since they are in the super_set for every group.
541 * This is required to display the correct links for each user in a drill down 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);
595 $data .= $this->nullGroup($superSetValue, $url);
600 $data .= $this->tab('</subgroups>',3);
601 $data .= $this->tab('</group>',2);
609 * This function sets a null group by clause
611 * @param $sugarSetValue Mixed value
612 * @param $url String value of URL for the link
614 private function nullGroup($superSetValue, $url) {
615 return $this->processDataGroup(4, $superSetValue, 'NULL', '', $url);
620 * returns a name for the XML File
622 * @param string $file_id - unique id to make part of the file name
624 public static function getXMLFileName(
628 global $sugar_config, $current_user;
630 $filename = sugar_cached("xml/"). $current_user->id . '_' . $file_id . '.xml';
632 if ( !is_dir(dirname($filename)) ) {
633 create_cache_directory("xml");
639 public function processXmlData(){
642 if ($this->chart_properties['type'] == 'group by chart'){
643 $data .= $this->xmlDataForGroupByChart();
645 else if ($this->chart_properties['type'] == 'bar chart' || $this->chart_properties['type'] == 'horizontal bar chart'){
646 $data .= $this->xmlDataBarChart();
649 $data .= $this->xmlDataGenericChart();
656 $data = $this->tab('<data>',1);
657 $data .= $this->processXmlData();
658 $data .= $this->tab('</data>',1);
664 * function to generate XML and return it
667 * @return string $xmlContents with xml information
669 function generateXML($xmlDataName = false){
670 $xmlContents = $this->xmlHeader();
671 $xmlContents .= $this->xmlProperties();
672 $xmlContents .= $this->xmlData();
673 $xmlContents .= $this->xmlYAxis();
674 $xmlContents .= $this->xmlFooter();
680 * function to save XML contents into a file
682 * @param string $xmlFilename location of the xml file
683 * string $xmlContents contents of the xml file
684 * @return string boolean denoting whether save has failed
686 function saveXMLFile($xmlFilename,$xmlContents) {
690 $xmlContents = chr(255).chr(254).$GLOBALS['locale']->translateCharset($xmlContents, 'UTF-8', 'UTF-16LE');
693 if (!$fh = sugar_fopen($xmlFilename, 'w')) {
694 $GLOBALS['log']->debug("Cannot open file ($xmlFilename)");
698 // write the contents to the file
699 if (fwrite($fh,$xmlContents) === FALSE) {
700 $GLOBALS['log']->debug("Cannot write to file ($xmlFilename)");
704 $GLOBALS['log']->debug("Success, wrote ($xmlContents) to file ($xmlFilename)");
711 * generates xml file for Flash charts to use for internationalized instances
713 * @param string $xmlFile location of the XML file to write to
716 function generateChartStrings($xmlFile){
717 global $current_language, $app_list_strings;
719 $chartStringsXML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
720 $chartStringsXML .= "<sugarlanguage version=\"1.0\">\n";
721 $chartStringsXML .= $this->tab("<charts>",1);
723 if (empty($app_list_strings)) {
724 //set module and application string arrays based upon selected language
725 $app_list_strings = return_app_list_strings_language($current_language);
728 // retrieve the strings defined at include/language/en_us.lang.php
729 foreach ($app_list_strings['chart_strings'] as $tag => $chart_string){
730 $chartStringsXML .= $this->tab("<$tag>$chart_string</$tag>",2);
733 $chartStringsXML .= $this->tab("</charts>",1);
734 $chartStringsXML .= "</sugarlanguage>\n";
736 $this->saveXMLFile($xmlFile, $chartStringsXML);
740 * wrapper function to return the html code containing the chart in a div
742 * @param string $name name of the div
743 * string $xmlFile location of the XML file
744 * string $style optional additional styles for the div
745 * @return string returns the html code through smarty
747 function display($name, $xmlFile, $width='320', $height='480', $resize=false){
750 // generate strings for chart if it does not exist
751 global $current_language, $theme, $sugar_config,$app_strings;
753 $this->app_strings = $app_strings;
754 $this->chartStringsXML = sugar_cached("xml/").'chart_strings.' . $current_language .'.lang.xml';
755 if (!file_exists($this->chartStringsXML)){
756 $this->generateChartStrings($this->chartStringsXML);
760 return $templateFile;
764 function getDashletScript($id,$xmlFile="") {
766 $xmlFile = (!$xmlFile) ? $sugar_config['tmp_dir']. $current_user->id . '_' . $this->id . '.xml' : $xmlFile;
767 $chartStringsXML = $GLOBALS['sugar_config']['tmp_dir'].'chart_strings.' . $current_language .'.lang.xml';
769 $this->ss->assign('chartName', $id);
770 $this->ss->assign('chartXMLFile', $xmlFile);
771 $this->ss->assign('chartStyleCSS', SugarThemeRegistry::current()->getCSSURL('chart.css'));
772 $this->ss->assign('chartColorsXML', SugarThemeRegistry::current()->getImageURL('sugarColors.xml'));
773 $this->ss->assign('chartLangFile', $GLOBALS['sugar_config']['tmp_dir'].'chart_strings.' . $GLOBALS['current_language'] .'.lang.xml');
776 return $templateFile;
781 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.
782 If the data quantity is large, it maybe a little slow.
783 * @param array $data_set The data get from database
784 string $keycolname1 We will sort by this key first
785 bool $translate1 Whether to trabslate the first column
786 string $keycolname1 We will sort by this key secondly, and it can be null, then it will only sort by the first column.
787 bool $translate1 Whether to trabslate the second column
788 bool $ifsort2 Whether to sort by the second column or just translate the second column.
789 * @return The sorted and translated data.
791 function sortData($data_set, $keycolname1=null, $translate1=false, $keycolname2=null, $translate2=false, $ifsort2=false) {
792 //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.
793 global $app_list_strings;
794 $sortby1[] = array();
795 foreach ($data_set as $row) {
796 $sortby1[] = $row[$keycolname1];
798 $sortby1 = array_unique($sortby1);
799 //The data is from the database, the sorting should be done in the sql. So I will not do the sort here.
801 $temp_sortby1 = array();
802 foreach(array_keys($app_list_strings[$keycolname1.'_dom']) as $sortby1_value) {
803 if(in_array($sortby1_value, $sortby1)) {
804 $temp_sortby1[] = $sortby1_value;
807 $sortby1 = $temp_sortby1;
810 //if(isset($sortby1[0]) && $sortby1[0]=='') unset($sortby1[0]);//the beginning of lead_source_dom is blank.
811 if(isset($sortby1[0]) && $sortby1[0]==array()) unset($sortby1[0]);//the beginning of month after search is blank.
813 if($ifsort2==false) $sortby2=array(0);
815 if($keycolname2!=null) {
817 foreach ($data_set as $row) {
818 $sortby2[] = $row[$keycolname2];
820 //The data is from the database, the sorting should be done in the sql. So I will not do the sort here.
821 $sortby2 = array_unique($sortby2);
823 $temp_sortby2 = array();
824 foreach(array_keys($app_list_strings[$keycolname2.'_dom']) as $sortby2_value) {
825 if(in_array($sortby2_value, $sortby2)) {
826 $temp_sortby2[] = $sortby2_value;
829 $sortby2 = $temp_sortby2;
835 foreach($sortby1 as $sort1) {
836 foreach($sortby2 as $sort2) {
838 foreach($data_set as $key => $value){
839 if($value[$keycolname1] == $sort1 && (!$ifsort2 || $value[$keycolname2]== $sort2)) {
841 $value[$keycolname1.'_dom_option'] = $value[$keycolname1];
842 $value[$keycolname1] = $app_list_strings[$keycolname1.'_dom'][$value[$keycolname1]];
845 $value[$keycolname2.'_dom_option'] = $value[$keycolname2];
846 $value[$keycolname2] = $app_list_strings[$keycolname2.'_dom'][$value[$keycolname2]];
848 array_push($data, $value);
849 unset($data_set[$key]);
853 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.
858 $val[$keycolname1] = $app_list_strings[$keycolname1.'_dom'][$sort1];
859 $val[$keycolname1.'_dom_option'] = $sort1;
862 $val[$keycolname1] = $sort1;
865 $val[$keycolname2] = $app_list_strings[$keycolname2.'_dom'][$sort2];
866 $val[$keycolname2.'_dom_option'] = $sort2;
868 elseif($keycolname2!=null) {
869 $val[$keycolname2] = $sort2;
871 array_push($data, $val);
878 function getChartResources() {
884 function getMySugarChartResources() {
885 $mySugarResources = "";
886 return $mySugarResources;
890 * wrapper function to return chart array after any additional processing
892 * @param array $chartsArray array of chart config items that need processing
893 * @return array $chartArray after it has been process
895 function chartArray($chartsArray) {