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-2013 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();
57 var $display_labels = array();
60 var $thousands_symbol;
62 var $supports_image_export = false;
63 var $print_html_legend_pdf = false;
64 var $image_export_type = "";
66 public function __construct() {
67 $this->db = &DBManagerFactory::getInstance();
68 $this->ss = new Sugar_Smarty();
70 $this->chart_yAxis['yMin'] = 0;
71 $this->chart_yAxis['yMax'] = 0;
74 if ($GLOBALS['current_user']->getPreference('currency')){
76 $currency = new Currency();
77 $currency->retrieve($GLOBALS['current_user']->getPreference('currency'));
78 $this->div = $currency->conversion_rate;
79 $this->currency_symbol = $currency->symbol;
82 $this->currency_symbol = $GLOBALS['sugar_config']['default_currency_symbol'];
84 $this->is_currency = false;
86 $this->image_export_type = (extension_loaded('gd') && function_exists('gd_info')) ? "png" : "jpg";
89 function getData($query){
90 $result = $this->db->query($query);
92 $row = $this->db->fetchByAssoc($result);
95 $this->data_set[] = $row;
96 $row = $this->db->fetchByAssoc($result);
100 function constructBaseURL(){
104 foreach ($this->base_url as $param => $value){
105 if ($numParams == 0){
106 $url .= $param . '=' . $value;
109 $url .= '&' .$param . '=' .$value;
117 function constructURL(){
118 $url = $this->constructBaseURL();
119 foreach ($this->url_params as $param => $value){
120 if ($param == 'assigned_user_id') $param = 'assigned_user_id[]';
121 if (is_array($value)){
122 foreach($value as $multiple){
123 $url .= '&' . $param . '=' . urlencode($multiple);
127 $url .= '&' . $param . '=' . urlencode($value);
133 function setData($dataSet){
134 $this->data_set = $dataSet;
137 function setProperties($title, $subtitle, $type, $legend='on', $labels='value', $print='on', $thousands = false)
139 $this->chart_properties['title'] = $title;
140 $this->chart_properties['subtitle'] = $subtitle;
141 $this->chart_properties['type'] = $type;
142 $this->chart_properties['legend'] = $legend;
143 $this->chart_properties['labels'] = $labels;
144 $this->chart_properties['thousands'] = $thousands;
147 function setDisplayProperty($property, $value){
148 $this->chart_properties[$property] = $value;
151 function setColors($colors = array()){
152 $this->colors_list = $colors;
156 * returns the header for the constructed xml file for sugarcharts
159 * @return string $header XML header
161 function xmlHeader(){
162 $header = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
163 $header .= "<sugarcharts version=\"1.0\">\n";
169 * returns the footer for the constructed xml file for sugarcharts
172 * @return string $footer XML footer
174 function xmlFooter(){
175 $footer = "</sugarcharts>";
181 * returns the properties tag for the constructed xml file for sugarcharts
184 * @return string $properties XML properties tag
186 function xmlProperties(){
187 // open the properties tag
188 $properties = $this->tab("<properties>",1);
190 // grab the property and value from the chart_properties variable
191 foreach ($this->chart_properties as $key => $value){
192 if(is_array($value)) continue;
193 $properties .= $this->tab("<$key>$value</$key>",2);
196 if (!empty($this->colors_list)){
197 // open the colors tag
198 $properties .= $this->tab("<colors>",2);
199 foreach ($this->colors_list as $color){
200 $properties .= $this->tab("<color>$color</color>",3);
203 // close the colors tag
204 $properties .= $this->tab("</colors>",2);
207 // close the properties tag
208 $properties .= $this->tab("</properties>",1);
214 * returns the y-axis values for the chart
217 * @return string $yAxis XML yAxis tag
220 $this->chart_yAxis['yStep'] = '100';
221 $this->chart_yAxis['yLog'] = '1';
222 $this->chart_yAxis['yMax'] = $this->is_currency ? $this->convertCurrency($this->chart_yAxis['yMax']) : $this->chart_yAxis['yMax'];
223 $max = $this->chart_yAxis['yMax'];
224 $exp = ($max == 0) ? 1 : floor(log10($max));
225 $baseval = $max / pow(10, $exp);
227 // steps will be 10^n, 2*10^n, 5*10^n (where n >= 0)
228 if ($baseval > 0 && $baseval <= 1){
229 $step = 2 * pow(10, $exp-1);
231 else if ($baseval > 1 && $baseval <= 3){
232 $step = 5 * pow(10, $exp-1);
234 else if ($baseval > 3 && $baseval <= 6){
235 $step = 10 * pow(10, $exp-1);
237 else if ($baseval > 6 && $baseval <= 10){
238 $step = 20 * pow(10, $exp-1);
241 // edge cases for values less than 10
242 if ($max == 0 || $step < 1){
246 $this->chart_yAxis['yStep'] = $step;
248 // to compensate, the yMax should be at least one step above the max value
249 $this->chart_yAxis['yMax'] += $this->chart_yAxis['yStep'];
251 $yAxis = $this->tab("<yAxis>" ,1);
253 foreach ($this->chart_yAxis as $key => $value){
254 $yAxis .= $this->tabValue("{$key}",$value, 2);
257 $yAxis .= $this->tab("</yAxis>" ,1);
263 * returns the total amount value for the group by field
265 * @param group by field
266 * @return int $total total value
268 function calculateTotal($group_by){
271 for($i =0; $i < count($this->data_set); $i++){
272 if ($this->data_set[$i][$this->group_by[0]] == $group_by){
273 $total += $this->data_set[$i]['total'];
280 * returns text with tabs appended before it
282 * @param string $str input string
283 * int $depth number of times to tab
284 * @return string with tabs appended before it
286 function tab($str, $depth){
287 return str_repeat("\t", $depth) . $str . "\n";
290 * returns text with tabs appended before it
292 * @param string $str xml tag
293 int $tagFormat 2 = open and close tag, 1 = close, 0 = open
294 sting $value input string
295 * int $depth number of times to tab
296 * @return string with tabs appended before it
299 function tabValue($tag,$value,$depth) {
301 return $this->tab("<{$tag}>".htmlspecialchars($value,ENT_QUOTES)."</{$tag}>",$depth);
305 * returns xml data format
308 * @return string with xml data format
310 function processData(){
313 $group_by = $this->group_by[0];
314 if (isset($this->group_by[1])){
315 $drill_down = $this->group_by[1];
320 for($i =0; $i < count($this->data_set); $i++){
321 if ($this->data_set[$i][$group_by] != $prev_group_by){
322 $prev_group_by = $this->data_set[$i][$group_by];
323 $data[$this->data_set[$i][$group_by]] = array();
326 $data[$this->data_set[$i][$group_by]][] = $this->data_set[$i];
328 // push new item onto legend items list
329 if (isset($drill_down)){
330 if (!in_array($this->data_set[$i][$drill_down], $this->super_set)){
331 $this->super_set[] = $this->data_set[$i][$drill_down];
339 function processDataGroup($tablevel, $title, $value, $label, $link){
340 $link = $this->forceHideDataGroupLink ? '' : $link;
341 $data = $this->tab('<group>',$tablevel);
342 $data .= $this->tabValue('title',$title,$tablevel+1);
343 $data .= $this->tabValue('value',$value,$tablevel+1);
344 $data .= $this->tabValue('label',$label,$tablevel+1);
345 $data .= $this->tab('<link>' . $link . '</link>',$tablevel+1);
346 $data .= $this->tab('</group>',$tablevel);
350 function calculateGroupByTotal($dataset){
353 foreach ($dataset as $key => $value){
360 function calculateSingleBarMax($dataset){
362 foreach ($dataset as $value){
372 * returns correct yAxis min/max
374 * @param value to check
375 * @return yAxis min and max
377 function checkYAxis($value){
378 if ($value < $this->chart_yAxis['yMin']){
379 $this->chart_yAxis['yMin'] = $value;
381 else if ($value > $this->chart_yAxis['yMax']){
382 $this->chart_yAxis['yMax'] = $value;
388 * Convert the amount given to the User's currency.
390 * TODO make this use the Currency module to convert from dollars and make
393 * @param float $to_convert
394 * The amount to be converted.
397 * The amount converted in the User's current currency.
399 * @see Currency::convertFromDollar()
400 * @see SugarChart::__construct()
402 function convertCurrency($to_convert)
406 $decimals = $locale->getPrecision();
407 $amount = round($to_convert * $this->div, $decimals);
412 function formatNumber($number, $decimals= null, $decimal_point= null, $thousands_sep= null){
414 if(is_null($decimals)) {
415 $decimals = $locale->getPrecision();
417 $seps = get_number_seperators();
418 $thousands_sep = $seps[0];
419 $decimal_point = $seps[1];
420 return number_format($number, $decimals, $decimal_point, $thousands_sep);
424 $new_data = $this->processData();
426 foreach ($new_data as $groupByKey => $value){
427 $total += $this->calculateTotal($groupByKey);
433 function xmlDataForGroupByChart(){
436 foreach ($this->data_set as $key => $value){
437 $amount = $this->is_currency ? $this->convertCurrency($this->calculateGroupByTotal($value)) : $this->calculateGroupByTotal($value);
438 $label = $this->is_currency ? ($this->currency_symbol . $this->formatNumber($amount)) : $amount;
440 $data .= $this->tab('<group>',2);
441 if (!empty($this->display_labels[$key]))
443 $data .= $this->tabValue('title', $this->display_labels[$key],3);
444 $data .= $this->tabValue('id', ++$idcounter,3);
448 $data .= $this->tabValue('title', $key,3);
450 $data .= $this->tabValue('value',$amount,3);
451 $data .= $this->tabValue('label',$label,3);
452 $data .= $this->tab('<link></link>',3);
453 $data .= $this->tab('<subgroups>',3);
455 foreach ($value as $k => $v){
456 $amount = $this->is_currency ? $this->convertCurrency($v) : $v;
457 $label = $this->is_currency ? ($this->currency_symbol . $this->formatNumber($amount)) : $amount;
459 $data .= $this->tab('<group>',4);
460 $data .= $this->tabValue('title',$k,5);
461 $data .= $this->tabValue('value',$amount,5);
462 $data .= $this->tabValue('label',$label,5);
463 $data .= $this->tab('<link></link>',5);
464 $data .= $this->tab('</group>',4);
465 $this->checkYAxis($v);
467 $data .= $this->tab('</subgroups>',3);
468 $data .= $this->tab('</group>',2);
474 function xmlDataForGaugeChart(){
476 $gaugePosition = $this->data_set[0]['num'];
477 $this->chart_yAxis['yMax'] = $this->chart_properties['gaugeTarget'];
478 $this->chart_yAxis['yStep'] = 1;
479 $data .= $this->processDataGroup(2, 'GaugePosition', $gaugePosition, $gaugePosition, '');
480 if (isset($this->chart_properties['gaugePhases']) && is_array($this->chart_properties['gaugePhases'])) {
481 $data .= $this->processGauge($gaugePosition, $this->chart_properties['gaugeTarget'], $this->chart_properties['gaugePhases']);
483 $data .= $this->processGauge($gaugePosition, $this->chart_properties['gaugeTarget']);
489 function xmlDataBarChart(){
491 $max = $this->calculateSingleBarMax($this->data_set);
492 $this->checkYAxis($max);
494 if (isset($this->group_by[0])){
495 $group_by = $this->group_by[0];
496 if (isset($this->group_by[1])){
497 $drill_down = $this->group_by[1];
501 foreach ($this->data_set as $key => $value){
502 if ($this->is_currency){
503 $value = $this->convertCurrency($value);
504 $label = $this->currency_symbol;
505 $label .= $this->formatNumber($value);
506 $label .= $this->thousands_symbol;
512 $data .= $this->tab('<group>', 2);
513 $data .= $this->tabValue('title',$key, 3);
514 $data .= $this->tabValue('value',$value, 3);
515 $data .= $this->tabValue('label',$label, 3);
516 if (isset($drill_down) && $drill_down){
517 if ($this->group_by[0] == 'm') {
518 $additional_param = '&date_closed_advanced=' . urlencode($key);
519 } else if ( $this->group_by[0] == 'sales_stage' ) {
520 $additional_param = '&sales_stage_advanced[]='.urlencode(array_search($key,$GLOBALS['app_list_strings']['sales_stage_dom']));
522 $additional_param = "&" . $this->group_by[0] . "=" . urlencode($key);
524 $url = $this->constructURL() . $additional_param;
526 $data .= $this->tab('<link>' . $url . '</link>', 3);
528 $data .= $this->tab('<subgroups>', 3);
529 $data .= $this->tab('</subgroups>', 3);
530 $data .= $this->tab('</group>', 2);
535 function xmlDataGenericChart(){
537 $group_by = $this->group_by[0];
538 if (isset($this->group_by[1])){
539 $drill_down = $this->group_by[1];
541 $new_data = $this->processData();
543 foreach ($new_data as $groupByKey => $value){
544 $total = $this->calculateTotal($groupByKey);
545 $this->checkYAxis($total);
547 if ($this->group_by[0] == 'm'){
548 $additional_param = '&date_closed_advanced=' . urlencode($groupByKey);
551 $paramValue = (isset($value[0]['key']) && $value[0]['key'] != '') ? $value[0]['key'] : $groupByKey;
552 $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;
553 $additional_param = "&" . $this->group_by[0] . "=" . urlencode($paramValue);
556 $url = $this->constructURL() . $additional_param;
558 $amount = $this->is_currency ? $this->convertCurrency($total) : $total;
559 $label = $this->is_currency ? ($this->currency_symbol . $this->formatNumber($amount) . 'K') : $amount;
561 $data .= $this->tab('<group>',2);
562 $data .= $this->tabValue('title',$groupByKey,3);
563 $data .= $this->tabValue('value',$amount,3);
564 $data .= $this->tabValue('label',$label,3);
565 $data .= $this->tab('<link>' . $url . '</link>',3);
567 $data .= $this->tab('<subgroups>',3);
568 $processed = array();
570 if (isset($drill_down) && $drill_down != ''){
572 * Bug 44696 - Ivan D.
573 * We have to iterate users in order since they are in the super_set for every group.
574 * This is required to display the correct links for each user in a drill down chart.
576 foreach ($this->super_set as $superSetKey => $superSetValue)
578 $objectInSaleStage = false;
579 foreach ($value as $internalKey => $internalValue)
581 if ($internalValue[$drill_down] == $superSetValue)
583 $objectInSaleStage = $value[$internalKey];
587 if ($objectInSaleStage)
589 if (isset($objectInSaleStage[$group_by]) && $objectInSaleStage[$group_by] == $groupByKey)
591 if ($drill_down == 'user_name')
593 $drill_down_param = '&assigned_user_id[]=' . urlencode($objectInSaleStage['assigned_user_id']);
595 else if ($drill_down == 'm')
597 $drill_down_param = '&date_closed_advanced=' . urlencode($objectInSaleStage[$drill_down]);
601 $paramValue = (isset($objectInSaleStage[$drill_down . "_dom_option"]) && $objectInSaleStage[$drill_down . "_dom_option"] != '') ? $objectInSaleStage[$drill_down . "_dom_option"] : $objectInSaleStage[$drill_down];
602 $drill_down_param = '&' . $drill_down . '=' . urlencode($paramValue);
605 if ($this->is_currency)
607 $sub_amount = $this->formatNumber($this->convertCurrency($objectInSaleStage['total']));
608 $sub_amount_formatted = $this->currency_symbol . $sub_amount . 'K';
609 //bug: 38877 - do not format the amount for the value as it breaks the chart
610 $sub_amount = $this->convertCurrency($objectInSaleStage['total']);
614 $sub_amount = $objectInSaleStage['total'];
615 $sub_amount_formatted = $sub_amount;
618 $data .= $this->processDataGroup(4, $objectInSaleStage[$drill_down], $sub_amount, $sub_amount_formatted, $url . $drill_down_param);
622 $data .= $this->nullGroup($superSetValue, $url);
628 $data .= $this->nullGroup($superSetValue, $url);
633 $data .= $this->tab('</subgroups>',3);
634 $data .= $this->tab('</group>',2);
642 * This function sets a null group by clause
644 * @param $sugarSetValue Mixed value
645 * @param $url String value of URL for the link
647 private function nullGroup($superSetValue, $url) {
648 return $this->processDataGroup(4, $superSetValue, 'NULL', '', $url);
653 * returns a name for the XML File
655 * @param string $file_id - unique id to make part of the file name
657 public static function getXMLFileName($file_id)
659 create_cache_directory("xml/".$GLOBALS['current_user']->getUserPrivGuid() . "_{$file_id}.xml");
661 return sugar_cached("xml/"). $GLOBALS['current_user']->getUserPrivGuid() . "_" . $file_id . ".xml";
664 public function processXmlData(){
667 if ($this->chart_properties['type'] == 'group by chart'){
668 $data .= $this->xmlDataForGroupByChart();
670 else if ($this->chart_properties['type'] == 'bar chart' || $this->chart_properties['type'] == 'horizontal bar chart'){
671 $data .= $this->xmlDataBarChart();
674 $data .= $this->xmlDataGenericChart();
681 $data = $this->tab('<data>',1);
682 $data .= $this->processXmlData();
683 $data .= $this->tab('</data>',1);
689 * function to generate XML and return it
692 * @return string $xmlContents with xml information
694 function generateXML($xmlDataName = false){
695 $xmlContents = $this->xmlHeader();
696 $xmlContents .= $this->xmlProperties();
697 $xmlContents .= $this->xmlData();
698 $xmlContents .= $this->xmlYAxis();
699 $xmlContents .= $this->xmlFooter();
705 * function to save XML contents into a file
707 * @param string $xmlFilename location of the xml file
708 * string $xmlContents contents of the xml file
709 * @return string boolean denoting whether save has failed
711 function saveXMLFile($xmlFilename,$xmlContents) {
715 $xmlContents = chr(255).chr(254).$GLOBALS['locale']->translateCharset($xmlContents, 'UTF-8', 'UTF-16LE');
717 // Create dir if it doesn't exist
718 $dir = dirname($xmlFilename);
719 if (!sugar_is_dir($dir))
721 sugar_mkdir($dir, null, true);
725 if (!$fh = sugar_fopen($xmlFilename, 'w')) {
726 $GLOBALS['log']->debug("Cannot open file ($xmlFilename)");
730 // write the contents to the file
731 if (fwrite($fh,$xmlContents) === FALSE) {
732 $GLOBALS['log']->debug("Cannot write to file ($xmlFilename)");
736 $GLOBALS['log']->debug("Success, wrote ($xmlContents) to file ($xmlFilename)");
743 * generates xml file for Flash charts to use for internationalized instances
745 * @param string $xmlFile location of the XML file to write to
748 function generateChartStrings($xmlFile){
749 global $current_language, $app_list_strings;
751 $chartStringsXML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
752 $chartStringsXML .= "<sugarlanguage version=\"1.0\">\n";
753 $chartStringsXML .= $this->tab("<charts>",1);
755 if (empty($app_list_strings)) {
756 //set module and application string arrays based upon selected language
757 $app_list_strings = return_app_list_strings_language($current_language);
760 // retrieve the strings defined at include/language/en_us.lang.php
761 foreach ($app_list_strings['chart_strings'] as $tag => $chart_string){
762 $chartStringsXML .= $this->tab("<$tag>$chart_string</$tag>",2);
765 $chartStringsXML .= $this->tab("</charts>",1);
766 $chartStringsXML .= "</sugarlanguage>\n";
768 $this->saveXMLFile($xmlFile, $chartStringsXML);
772 * wrapper function to return the html code containing the chart in a div
774 * @param string $name name of the div
775 * string $xmlFile location of the XML file
776 * string $style optional additional styles for the div
777 * @return string returns the html code through smarty
779 function display($name, $xmlFile, $width='320', $height='480', $resize=false){
782 // generate strings for chart if it does not exist
783 global $current_language, $theme, $sugar_config,$app_strings;
785 $this->app_strings = $app_strings;
786 $this->chartStringsXML = sugar_cached("xml/").'chart_strings.' . $current_language .'.lang.xml';
787 if (!file_exists($this->chartStringsXML)){
788 $this->generateChartStrings($this->chartStringsXML);
792 return $templateFile;
796 function getDashletScript($id,$xmlFile="") {
798 $xmlFile = (!$xmlFile) ? $sugar_config['tmp_dir']. $current_user->id . '_' . $this->id . '.xml' : $xmlFile;
799 $chartStringsXML = $GLOBALS['sugar_config']['tmp_dir'].'chart_strings.' . $current_language .'.lang.xml';
801 $this->ss->assign('chartName', $id);
802 $this->ss->assign('chartXMLFile', $xmlFile);
803 $this->ss->assign('chartStyleCSS', SugarThemeRegistry::current()->getCSSURL('chart.css'));
804 $this->ss->assign('chartColorsXML', SugarThemeRegistry::current()->getImageURL('sugarColors.xml'));
805 $this->ss->assign('chartLangFile', $GLOBALS['sugar_config']['tmp_dir'].'chart_strings.' . $GLOBALS['current_language'] .'.lang.xml');
808 return $templateFile;
813 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.
814 If the data quantity is large, it maybe a little slow.
815 * @param array $data_set The data get from database
816 string $keycolname1 We will sort by this key first
817 bool $translate1 Whether to trabslate the first column
818 string $keycolname1 We will sort by this key secondly, and it can be null, then it will only sort by the first column.
819 bool $translate1 Whether to trabslate the second column
820 bool $ifsort2 Whether to sort by the second column or just translate the second column.
821 * @return The sorted and translated data.
823 function sortData($data_set, $keycolname1=null, $translate1=false, $keycolname2=null, $translate2=false, $ifsort2=false) {
824 //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.
825 global $app_list_strings;
826 $sortby1[] = array();
827 foreach ($data_set as $row) {
828 $sortby1[] = $row[$keycolname1];
830 $sortby1 = array_unique($sortby1);
831 //The data is from the database, the sorting should be done in the sql. So I will not do the sort here.
833 $temp_sortby1 = array();
834 foreach(array_keys($app_list_strings[$keycolname1.'_dom']) as $sortby1_value) {
835 if(in_array($sortby1_value, $sortby1)) {
836 $temp_sortby1[] = $sortby1_value;
839 $sortby1 = $temp_sortby1;
842 //if(isset($sortby1[0]) && $sortby1[0]=='') unset($sortby1[0]);//the beginning of lead_source_dom is blank.
843 if(isset($sortby1[0]) && $sortby1[0]==array()) unset($sortby1[0]);//the beginning of month after search is blank.
845 if($ifsort2==false) $sortby2=array(0);
847 if($keycolname2!=null) {
849 foreach ($data_set as $row) {
850 $sortby2[] = $row[$keycolname2];
852 //The data is from the database, the sorting should be done in the sql. So I will not do the sort here.
853 $sortby2 = array_unique($sortby2);
855 $temp_sortby2 = array();
856 foreach(array_keys($app_list_strings[$keycolname2.'_dom']) as $sortby2_value) {
857 if(in_array($sortby2_value, $sortby2)) {
858 $temp_sortby2[] = $sortby2_value;
861 $sortby2 = $temp_sortby2;
867 foreach($sortby1 as $sort1) {
868 foreach($sortby2 as $sort2) {
870 foreach($data_set as $key => $value){
871 if($value[$keycolname1] == $sort1 && (!$ifsort2 || $value[$keycolname2]== $sort2)) {
873 $value[$keycolname1.'_dom_option'] = $value[$keycolname1];
874 $value[$keycolname1] = $app_list_strings[$keycolname1.'_dom'][$value[$keycolname1]];
877 $value[$keycolname2.'_dom_option'] = $value[$keycolname2];
878 $value[$keycolname2] = $app_list_strings[$keycolname2.'_dom'][$value[$keycolname2]];
880 array_push($data, $value);
881 unset($data_set[$key]);
885 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.
890 $val[$keycolname1] = $app_list_strings[$keycolname1.'_dom'][$sort1];
891 $val[$keycolname1.'_dom_option'] = $sort1;
894 $val[$keycolname1] = $sort1;
897 $val[$keycolname2] = $app_list_strings[$keycolname2.'_dom'][$sort2];
898 $val[$keycolname2.'_dom_option'] = $sort2;
900 elseif($keycolname2!=null) {
901 $val[$keycolname2] = $sort2;
903 array_push($data, $val);
910 function getChartResources() {
916 function getMySugarChartResources() {
917 $mySugarResources = "";
918 return $mySugarResources;
922 * wrapper function to return chart array after any additional processing
924 * @param array $chartsArray array of chart config items that need processing
925 * @return array $chartArray after it has been process
927 function chartArray($chartsArray) {