]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - include/SugarCharts/SugarChart.php
Release 6.1.4
[Github/sugarcrm.git] / include / SugarCharts / SugarChart.php
1 <?php
2 if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
3 /*********************************************************************************
4  * SugarCRM is a customer relationship management program developed by
5  * SugarCRM, Inc. Copyright (C) 2004-2011 SugarCRM Inc.
6  * 
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.
13  * 
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
17  * details.
18  * 
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
22  * 02110-1301 USA.
23  * 
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.
26  * 
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.
30  * 
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  ********************************************************************************/
37
38 /*********************************************************************************
39
40  * Description:
41  * Portions created by SugarCRM are Copyright (C) SugarCRM, Inc. All Rights
42  * Reserved. Contributor(s): ______________________________________..
43  *********************************************************************************/
44
45 class SugarChart {
46
47         private $db;
48         protected $ss;
49         var $forceHideDataGroupLink = false;
50         var $data_set = array();
51         var $display_data = array();
52         var $chart_properties = array();
53         var $chart_yAxis = array();
54         var $group_by = array();
55         var $super_set = array();
56         var $colors_list = array();
57         var $base_url = array();
58         var $url_params = array();
59         
60         var $currency_symbol;
61         var $thousands_symbol;
62         var $is_currency;
63         
64         public function __construct() {
65                 $this->db = &DBManagerFactory::getInstance();
66                 $this->ss = new Sugar_Smarty();
67                 
68                 $this->chart_yAxis['yMin'] = 0;         
69                 $this->chart_yAxis['yMax'] = 0;
70                 
71                 
72                 if ($GLOBALS['current_user']->getPreference('currency')){
73                     
74             $currency = new Currency();
75             $currency->retrieve($GLOBALS['current_user']->getPreference('currency'));
76             $this->div = $currency->conversion_rate;
77             $this->currency_symbol = $currency->symbol;
78         }
79         else{
80                 $this->currency_symbol = $GLOBALS['sugar_config']['default_currency_symbol'];
81                         $this->div = 1;
82                         $this->is_currency = false;
83         }
84         }
85         
86         function getData($query){
87                 $result = $this->db->query($query);
88                 
89                 $row = $this->db->fetchByAssoc($result);
90                 
91                 while ($row != null){
92                         $this->data_set[] = $row;
93                         $row = $this->db->fetchByAssoc($result);
94                 }
95         }
96         
97         function constructBaseURL(){
98                 $numParams = 0;
99                 $url = 'index.php?';
100                 
101                 foreach ($this->base_url as $param => $value){
102                         if ($numParams == 0){
103                                 $url .= $param . '=' . $value;
104                         }
105                         else{
106                                 $url .= '&' .$param . '=' .$value;
107                         }
108                         $numParams++;
109                 }
110
111                 return $url;
112         }
113         
114         function constructURL(){
115                 $url = $this->constructBaseURL();
116                 foreach ($this->url_params as $param => $value){
117                         if ($param == 'assigned_user_id') $param = 'assigned_user_id[]';
118                         if (is_array($value)){
119                                 foreach($value as $multiple){
120                                         $url .= '&' . $param . '=' . urlencode($multiple);
121                                 }
122                         }
123                         else{
124                                 $url .= '&' . $param . '=' . urlencode($value);
125                         }
126                 }
127                 return $url;
128         }
129         
130         function setData($dataSet){
131                 $this->data_set = $dataSet;
132         }
133         
134         function setProperties($title, $subtitle, $type, $legend='on', $labels='value', $print='on'){
135                 $this->chart_properties['title'] = $title;
136                 $this->chart_properties['subtitle'] = $subtitle;
137                 $this->chart_properties['type'] = $type;
138                 $this->chart_properties['legend'] = $legend;
139                 $this->chart_properties['labels'] = $labels;
140         }
141         
142         function setDisplayProperty($property, $value){
143                 $this->chart_properties[$property] = $value;
144         }
145         
146         function setColors($colors = array()){
147                 $this->colors_list = $colors;
148         }
149         
150     /**
151      * returns the header for the constructed xml file for sugarcharts
152          * 
153      * @param   nothing
154      * @return  string $header XML header
155      */
156         function xmlHeader(){
157                 $header = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
158                 $header .= "<sugarcharts version=\"1.0\">\n";
159                 
160                 return $header;
161         }
162
163         /**
164      * returns the footer for the constructed xml file for sugarcharts
165          * 
166      * @param   nothing
167      * @return  string $footer XML footer
168      */
169         function xmlFooter(){
170                 $footer = "</sugarcharts>";
171                 
172                 return $footer;
173         }
174
175         /**
176      * returns the properties tag for the constructed xml file for sugarcharts
177          * 
178      * @param   nothing
179      * @return  string $properties XML properties tag
180      */ 
181         function xmlProperties(){       
182                 // open the properties tag
183                 $properties = $this->tab("<properties>",1);
184                 
185                 // grab the property and value from the chart_properties variable
186                 foreach ($this->chart_properties as $key => $value){
187                         $properties .= $this->tab("<$key>$value</$key>",2);
188                 }
189                 
190                 if (!empty($this->colors_list)){
191                         // open the colors tag
192                         $properties .= $this->tab("<colors>",2);
193                         foreach ($this->colors_list as $color){
194                                 $properties .= $this->tab("<color>$color</color>",3);
195                         }
196                         
197                         // close the colors tag
198                         $properties .= $this->tab("</colors>",2);
199                 }
200                 
201                 // close the properties tag
202                 $properties .= $this->tab("</properties>",1);
203                 
204                 return $properties;
205         }
206         
207         /**
208      * returns the y-axis values for the chart
209          * 
210      * @param   nothing
211      * @return  string $yAxis XML yAxis tag
212      */ 
213         function xmlYAxis(){
214                 $this->chart_yAxis['yStep'] = '100';
215                 $this->chart_yAxis['yLog'] = '1';
216                 $this->chart_yAxis['yMax'] = $this->is_currency ? $this->convertCurrency($this->chart_yAxis['yMax']) : $this->chart_yAxis['yMax'];
217                 $max = $this->chart_yAxis['yMax'];
218                 $exp = ($max == 0) ? 1 : floor(log10($max));
219                 $baseval = $max / pow(10, $exp);
220                 
221                 // steps will be 10^n, 2*10^n, 5*10^n (where n >= 0)
222                 if ($baseval > 0 && $baseval <= 1){
223                         $step = 2 * pow(10, $exp-1);
224                 }
225                 else if ($baseval > 1 && $baseval <= 3){
226                         $step = 5 * pow(10, $exp-1);
227                 }               
228                 else if ($baseval > 3 && $baseval <= 6){
229                         $step = 10 * pow(10, $exp-1);
230                 }       
231                 else if ($baseval > 6 && $baseval <= 10){
232                         $step = 20 * pow(10, $exp-1);
233                 }       
234
235                 // edge cases for values less than 10
236                 if ($max == 0 || $step < 1){
237                         $step = 1;
238                 }
239         
240                 $this->chart_yAxis['yStep'] = $step;
241                 
242                 // to compensate, the yMax should be at least one step above the max value
243                         $this->chart_yAxis['yMax'] += $this->chart_yAxis['yStep'];
244                 
245                 $yAxis = $this->tab("<yAxis>" ,1);
246                 
247                 foreach ($this->chart_yAxis as $key => $value){
248                         $yAxis .= $this->tab("<$key>$value</$key>", 2);
249                 }
250                 
251                 $yAxis .= $this->tab("</yAxis>" ,1);
252                 
253                 return $yAxis;
254         }
255
256         /**
257      * returns the total amount value for the group by field
258          * 
259      * @param   group by field
260      * @return  int $total total value
261      */ 
262         function calculateTotal($group_by){
263                 $total = 0;     
264                 
265                 for($i =0; $i < count($this->data_set); $i++){
266                         if ($this->data_set[$i][$this->group_by[0]] == $group_by){
267                                 $total += $this->data_set[$i]['total'];
268                         }
269                 }               
270                 return $total;
271         }
272
273         /**
274      * returns text with tabs appended before it
275          * 
276      * @param   string $str input string
277          *                      int $depth number of times to tab
278      * @return  string with tabs appended before it
279      */ 
280         function tab($str, $depth){
281                 return str_repeat("\t", $depth) . $str . "\n";  
282         }
283         
284         /**
285      * returns xml data format
286          * 
287      * @param   none
288      * @return  string with xml data format
289      */         
290         function processData(){
291                 $data = array();
292
293                 $group_by = $this->group_by[0];
294                 if (isset($this->group_by[1])){
295                         $drill_down = $this->group_by[1];
296                 }
297                 
298                 $prev_group_by = '';
299
300                 for($i =0; $i < count($this->data_set); $i++){
301                         if ($this->data_set[$i][$group_by] != $prev_group_by){
302                                 $prev_group_by = $this->data_set[$i][$group_by];
303                                 $data[$this->data_set[$i][$group_by]] = array();
304                         }
305             
306             $data[$this->data_set[$i][$group_by]][] = $this->data_set[$i];
307                         
308                         // push new item onto legend items list
309                         if (isset($drill_down)){
310                                 if (!in_array($this->data_set[$i][$drill_down], $this->super_set)){
311                                         $this->super_set[] = $this->data_set[$i][$drill_down];
312                                 }
313                         }
314                 }       
315
316                 return $data;
317         }
318         
319         function processDataGroup($tablevel, $title, $value, $label, $link){
320                 $link = $this->forceHideDataGroupLink ? '' : $link;
321                 $data = $this->tab('<group>',$tablevel);
322                 $data .= $this->tab('<title>' . $title . '</title>',$tablevel+1);
323                 $data .= $this->tab('<value>' . $value . '</value>',$tablevel+1);
324                 $data .= $this->tab('<label>' . $label . '</label>',$tablevel+1);
325                 $data .= $this->tab('<link>' . $link . '</link>',$tablevel+1);
326                 $data .= $this->tab('</group>',$tablevel);
327                 return $data;
328         }
329         
330         function calculateGroupByTotal($dataset){
331                 $total = 0;
332                 
333                 foreach ($dataset as $key => $value){
334                         $total += $value;
335                 }
336                 
337                 return $total;
338         }
339         
340         function calculateSingleBarMax($dataset){
341                 $max = 0;               
342                 foreach ($dataset as $value){
343                         if ($value > $max){
344                                 $max = $value;
345                         }
346                 }
347                 
348                 return $max;
349         }
350
351         /**
352      * returns correct yAxis min/max
353          * 
354      * @param   value to check 
355      * @return  yAxis min and max
356      */                 
357         function checkYAxis($value){
358                 if ($value < $this->chart_yAxis['yMin']){
359                         $this->chart_yAxis['yMin'] = $value;
360                 }
361                 else if ($value > $this->chart_yAxis['yMax']){
362                         $this->chart_yAxis['yMax'] = $value;
363                 }
364         }
365         
366         
367         function convertCurrency($to_convert){
368                 global $locale;
369                 $decimals = '2';
370                 $decimals = $locale->getPrecision();
371                 $amount = ($this->div == 1) ? $to_convert : round($to_convert * $this->div,$decimals);
372                 
373                 return $amount;
374         }
375         
376         function formatNumber($number, $decimals= null, $decimal_point= null, $thousands_sep= null){
377                 global $locale;
378                 if(is_null($decimals)) {
379                         $decimals = $locale->getPrecision();
380                 }
381                 $seps = get_number_seperators();
382                 $thousands_sep = $seps[0];
383                 $decimal_point = $seps[1];
384                 return number_format($number, $decimals, $decimal_point, $thousands_sep);
385         }
386         
387         function getTotal(){
388                 $new_data = $this->processData();
389                 $total = 0;             
390                 foreach ($new_data as $groupByKey => $value){
391                         $total += $this->calculateTotal($groupByKey);
392                 }               
393                 
394                 return $total;
395         }
396
397         function xmlDataForGroupByChart(){
398                 $data = '';             
399                 foreach ($this->data_set as $key => $value){
400                         $amount = $this->is_currency ? $this->convertCurrency($this->calculateGroupByTotal($value)) : $this->calculateGroupByTotal($value);
401             $label = $this->is_currency ? ($this->currency_symbol . $this->formatNumber($amount)) : $amount;
402             
403                         $data .= $this->tab('<group>',2);
404                         $data .= $this->tab('<title>' . $key . '</title>',3);
405                         $data .= $this->tab('<value>' . $amount . '</value>',3);
406                         $data .= $this->tab('<label>' . $label . '</label>',3);
407                         $data .= $this->tab('<link></link>',3);
408                         $data .= $this->tab('<subgroups>',3);
409                         
410                         foreach ($value as $k => $v){
411                 $amount = $this->is_currency ? $this->convertCurrency($v) : $v;
412                 $label = $this->is_currency ? ($this->currency_symbol . $this->formatNumber($amount)) : $amount;
413                 
414                                 $data .= $this->tab('<group>',4);
415                                 $data .= $this->tab('<title>' . $k . '</title>',5);
416                                 $data .= $this->tab('<value>' . $amount . '</value>',5);
417                                 $data .= $this->tab('<label>' . $label . '</label>',5);
418                                 $data .= $this->tab('<link></link>',5);
419                                 $data .= $this->tab('</group>',4);
420                                 $this->checkYAxis($v);
421                         }
422                         $data .= $this->tab('</subgroups>',3);
423                         $data .= $this->tab('</group>',2);
424                 }
425                 
426                 return $data;           
427         }
428         
429         function xmlDataForGaugeChart(){
430                 $data = '';             
431                 $gaugePosition = $this->data_set[0]['num'];
432                 $this->chart_yAxis['yMax'] = $this->chart_properties['gaugeTarget'];
433                 $this->chart_yAxis['yStep'] = 1;
434                 $data .= $this->processDataGroup(2, 'GaugePosition', $gaugePosition, $gaugePosition, '');
435                 $data .= $this->processGauge($gaugePosition, $this->chart_properties['gaugeTarget']);
436                 
437                 return $data;           
438         }
439         
440         function xmlDataBarChart(){
441                 $data = '';
442                 $max = $this->calculateSingleBarMax($this->data_set);
443                 $this->checkYAxis($max);
444
445                 if (isset($this->group_by[0])){
446                         $group_by = $this->group_by[0];
447                         if (isset($this->group_by[1])){
448                                 $drill_down = $this->group_by[1];
449                         }       
450                 }
451
452                 foreach ($this->data_set as $key => $value){
453                         if ($this->is_currency){
454                                 $value = $this->convertCurrency($value);
455                                 $label = $this->currency_symbol;
456                                 $label .= $this->formatNumber($value);
457                                 $label .= $this->thousands_symbol;
458                         }                               
459                         else{
460                                 $label = $value;
461                         }
462
463                         $data .= $this->tab('<group>', 2);
464                         $data .= $this->tab('<title>' . $key . '</title>', 3);
465                         $data .= $this->tab('<value>' . $value . '</value>', 3);
466                         $data .= $this->tab('<label>' . $label . '</label>', 3);
467                         if (isset($drill_down) && $drill_down){
468                                 if ($this->group_by[0] == 'm'){
469                                         $additional_param = '&date_closed_advanced=' . urlencode($key);                                 
470                                 }
471                                 else{
472                                         $additional_param = "&" . $this->group_by[0] . "=" . urlencode($key);   
473                                 }
474                                 $url = $this->constructURL() . $additional_param;                                       
475                                 
476                                 $data .= $this->tab('<link>' . $url . '</link>', 3);
477                         }                               
478                         $data .= $this->tab('<subgroups>', 3);
479                         $data .= $this->tab('</subgroups>', 3);
480                         $data .= $this->tab('</group>', 2);
481                 }
482                 return $data;   
483         }
484         
485         function xmlDataGenericChart(){
486                 $data = '';
487                 $group_by = $this->group_by[0];
488                 if (isset($this->group_by[1])){
489                         $drill_down = $this->group_by[1];
490                 }               
491                 $new_data = $this->processData();
492
493                 foreach ($new_data as $groupByKey => $value){
494                         $total = $this->calculateTotal($groupByKey);
495                         $this->checkYAxis($total);
496                         
497                         if ($this->group_by[0] == 'm'){
498                                 $additional_param = '&date_closed_advanced=' . urlencode($groupByKey);                                  
499                         }
500                         else{
501                                 $paramValue = (isset($value[0]['key']) && $value[0]['key'] != '') ? $value[0]['key'] : $groupByKey;
502                                 $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;
503                                 $additional_param = "&" . $this->group_by[0] . "=" . urlencode($paramValue);    
504                         }
505                         
506                         $url = $this->constructURL() . $additional_param;
507                         
508                         $amount = $this->is_currency ? $this->convertCurrency($total) : $total;
509                         $label = $this->is_currency ? ($this->currency_symbol . $this->formatNumber($amount) . 'K') : $amount;
510
511                         $data .= $this->tab('<group>',2);
512                         $data .= $this->tab('<title>' . $groupByKey . '</title>',3);
513                         $data .= $this->tab('<value>' . $amount . '</value>',3);
514                         $data .= $this->tab('<label>' . $label . '</label>',3);
515                         $data .= $this->tab('<link>' . $url . '</link>',3);
516                         
517                         $data .= $this->tab('<subgroups>',3);                                           
518                         $processed = array();
519                                 
520                         if (isset($drill_down) && $drill_down != ''){
521                                 for($i =0; $i < count($new_data[$groupByKey]); $i++){
522                                         if ($new_data[$groupByKey][$i][$group_by] == $groupByKey){
523                                                 if ($drill_down == 'user_name'){
524                                                         $drill_down_param = '&assigned_user_id[]=' . urlencode($new_data[$groupByKey][$i]['assigned_user_id']);
525                                                 }
526                                                 else if ($drill_down == 'm'){
527                                                         $drill_down_param = '&date_closed_advanced=' . urlencode($new_data[$groupByKey][$i][$drill_down]);
528                                                 }
529                                                 else{
530                                                         $paramValue = (isset($new_data[$groupByKey][$i][$drill_down."_dom_option"]) && $new_data[$groupByKey][$i][$drill_down."_dom_option"] != '') ? $new_data[$groupByKey][$i][$drill_down."_dom_option"] : $new_data[$groupByKey][$i][$drill_down];
531                                                         $drill_down_param = '&' . $drill_down . '=' . urlencode($paramValue);
532                                                 }
533                                                 
534                                                 if($this->is_currency) {
535                                                   $sub_amount = $this->formatNumber($this->convertCurrency($new_data[$groupByKey][$i]['total']));
536                                                   $sub_amount_formatted = $this->currency_symbol . $sub_amount . 'K';
537                                                 } else {
538                                                   $sub_amount = $new_data[$groupByKey][$i]['total'];
539                                                   $sub_amount_formatted = $sub_amount;
540                                                 }
541                                                 
542                                                 $data .= $this->processDataGroup(4, $new_data[$groupByKey][$i][$drill_down],
543                                                                                                                     $sub_amount,
544                                                                                                                     $sub_amount_formatted,
545                                                                                                                     $url . $drill_down_param );
546                                                 array_push($processed, $new_data[$groupByKey][$i][$drill_down]);
547                                         }
548                                 }
549                                 $not_processed = array_diff($this->super_set, $processed);
550                                 foreach ($not_processed as $title){
551                                         $data .= $this->processDataGroup(4, $title, 'NULL', '', $url);
552                                 }                       
553                         }
554
555                         $data .= $this->tab('</subgroups>',3);
556                         $data .= $this->tab('</group>',2);
557                 }
558                 return $data;
559         }
560     
561     /**
562      * returns a name for the XML File
563      *
564      * @param string $file_id - unique id to make part of the file name
565      */
566     public static function getXMLFileName(
567          $file_id
568          )
569     {
570         global $sugar_config, $current_user;
571
572         $filename = $sugar_config['tmp_dir']. $current_user->id . '_' . $file_id . '.xml';
573
574         if ( !is_dir(dirname($filename)) )
575             create_cache_directory(str_ireplace($GLOBALS['sugar_config']['cache_dir'],"",$filename));
576
577         return $filename;
578     }
579     
580     public function processXmlData(){
581         $data = '';
582         
583                 if ($this->chart_properties['type'] == 'group by chart'){
584                         $data .= $this->xmlDataForGroupByChart();
585                 }
586                 else if ($this->chart_properties['type'] == 'bar chart' || $this->chart_properties['type'] == 'horizontal bar chart'){
587                         $data .= $this->xmlDataBarChart();                      
588                 }
589                 else{
590                         $data .= $this->xmlDataGenericChart();
591                 }               
592
593                 return $data;
594     }
595      
596         function xmlData(){
597                 $data = $this->tab('<data>',1);
598                 $data .= $this->processXmlData();       
599                 $data .= $this->tab('</data>',1);
600                 
601                 return $data;
602         }
603         
604         /**
605      * function to generate XML and return it
606          * 
607      * @param   none
608      * @return  string $xmlContents with xml information
609      */         
610         function generateXML($xmlDataName = false){
611                 $xmlContents = $this->xmlHeader();              
612                 $xmlContents .= $this->xmlProperties();         
613                 $xmlContents .= $this->xmlData();
614                 $xmlContents .= $this->xmlYAxis();      
615                 $xmlContents .= $this->xmlFooter();
616                 
617                 return $xmlContents;
618         }
619
620         /**
621      * function to save XML contents into a file
622          * 
623      * @param   string $xmlFilename location of the xml file
624          *                      string $xmlContents contents of the xml file
625      * @return  string boolean denoting whether save has failed
626      */                 
627         function saveXMLFile($xmlFilename,$xmlContents) {
628                 global $app_strings;
629                 global $locale;
630                 
631                 $xmlContents = chr(255).chr(254).mb_convert_encoding($xmlContents, 'UTF-16LE', 'UTF-8'); 
632                 
633                 // open file
634                 if (!$fh = sugar_fopen($xmlFilename, 'w')) {
635                         $GLOBALS['log']->debug("Cannot open file ($xmlFilename)");
636                         return;
637                 }
638                 
639                 // write the contents to the file
640                 if (fwrite($fh,$xmlContents) === FALSE) {
641                         $GLOBALS['log']->debug("Cannot write to file ($xmlFilename)");
642                         return false;
643                 }
644         
645                 $GLOBALS['log']->debug("Success, wrote ($xmlContents) to file ($xmlFilename)");
646         
647                 fclose($fh);
648                 return true;
649         }
650
651         /**
652      * generates xml file for Flash charts to use for internationalized instances
653          * 
654      * @param   string $xmlFile location of the XML file to write to
655      * @return  none
656      */ 
657         function generateChartStrings($xmlFile){
658                 global $current_language, $app_list_strings;
659                 
660                 $chartStringsXML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
661                 $chartStringsXML .= "<sugarlanguage version=\"1.0\">\n";
662                 $chartStringsXML .= $this->tab("<charts>",1);
663                 
664                 if (empty($app_list_strings)) {
665                     //set module and application string arrays based upon selected language
666                         $app_list_strings = return_app_list_strings_language($current_language);
667                 }
668                 
669                 // retrieve the strings defined at include/language/en_us.lang.php
670                 foreach ($app_list_strings['chart_strings'] as $tag => $chart_string){
671                         $chartStringsXML .= $this->tab("<$tag>$chart_string</$tag>",2);
672                 }               
673                 
674                 $chartStringsXML .= $this->tab("</charts>",1);
675                 $chartStringsXML .= "</sugarlanguage>\n";
676                 
677                 $this->saveXMLFile($xmlFile, $chartStringsXML);
678         }
679         
680         /**
681      * wrapper function to return the html code containing the chart in a div
682          * 
683      * @param   string $name    name of the div
684          *                      string $xmlFile location of the XML file
685          *                      string $style   optional additional styles for the div
686      * @return  string returns the html code through smarty
687      */                                 
688         function display($name, $xmlFile, $width='320', $height='480', $resize=false){
689                 
690                 
691                 // generate strings for chart if it does not exist
692                 global $current_language, $theme, $sugar_config,$app_strings;
693                 
694                 $chartStringsXML = $GLOBALS['sugar_config']['tmp_dir'].'chart_strings.' . $current_language .'.lang.xml';
695                 if (!file_exists($chartStringsXML)){
696                         $this->generateChartStrings($chartStringsXML);
697                 }
698                                                         
699                 $this->ss->assign("chartName", $name);
700                 $this->ss->assign("chartXMLFile", $xmlFile);
701                 $this->ss->assign("chartStringsXML", $chartStringsXML);
702                 
703                 // chart styles and color definitions
704                 $this->ss->assign("chartStyleCSS", SugarThemeRegistry::current()->getCSSURL('chart.css'));
705                 $this->ss->assign("chartColorsXML", SugarThemeRegistry::current()->getImageURL('sugarColors.xml'));
706                 
707                 $this->ss->assign("width", $width);
708                 $this->ss->assign("height", $height);
709                 
710                 $this->ss->assign("resize", $resize);
711                 $this->ss->assign("app_strings", $app_strings);                         
712                 return $this->ss->fetch('include/SugarCharts/tpls/chart.tpl');
713         }
714
715
716   /**
717          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.
718          If the data quantity is large, it maybe a little slow.
719     * @param         array $data_set           The data get from database
720                            string $keycolname1      We will sort by this key first
721                            bool $translate1            Whether to trabslate the first column
722                            string $keycolname1      We will sort by this key secondly, and  it can be null, then it will only sort by the first column.
723                            bool $translate1            Whether to trabslate the second column
724                            bool $ifsort2                 Whether to sort by the second column or just translate the second column.
725     * @return        The sorted and translated data.
726    */
727     function sortData($data_set, $keycolname1=null, $translate1=false, $keycolname2=null, $translate2=false, $ifsort2=false) {
728         //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.
729         global $app_list_strings;
730         $sortby1[] = array();
731         foreach ($data_set as $row) {
732             $sortby1[]  = $row[$keycolname1];
733         }
734         $sortby1 = array_unique($sortby1);
735         //The data is from the database, the sorting should be done in the sql. So I will not do the sort here.
736         if($translate1) {
737             $temp_sortby1 = array();
738             foreach(array_keys($app_list_strings[$keycolname1.'_dom']) as $sortby1_value) {
739                 if(in_array($sortby1_value, $sortby1)) {
740                     $temp_sortby1[] = $sortby1_value;
741                 }
742             }
743             $sortby1 = $temp_sortby1;
744         }
745         
746         //if(isset($sortby1[0]) && $sortby1[0]=='') unset($sortby1[0]);//the beginning of lead_source_dom is blank.
747         if(isset($sortby1[0]) && $sortby1[0]==array()) unset($sortby1[0]);//the beginning of month after search is blank.
748         
749         if($ifsort2==false) $sortby2=array(0);
750         
751         if($keycolname2!=null) {
752             $sortby2 = array();
753             foreach ($data_set as $row) {
754                 $sortby2[]  = $row[$keycolname2];
755             }
756             //The data is from the database, the sorting should be done in the sql. So I will not do the sort here.
757             $sortby2 = array_unique($sortby2);
758             if($translate2) {
759                 $temp_sortby2 = array();
760                 foreach(array_keys($app_list_strings[$keycolname2.'_dom']) as $sortby2_value) {
761                     if(in_array($sortby2_value, $sortby2)) {
762                         $temp_sortby2[] = $sortby2_value;
763                     }
764                 }
765                 $sortby2 = $temp_sortby2;
766             }
767         }
768         
769         $data=array();
770
771         foreach($sortby1 as $sort1) {
772             foreach($sortby2 as $sort2) {
773                 if($ifsort2) $a=0;
774                 foreach($data_set as $key => $value){
775                     if($value[$keycolname1] == $sort1 && (!$ifsort2 || $value[$keycolname2]== $sort2)) {
776                         if($translate1) {
777                             $value[$keycolname1.'_dom_option'] = $value[$keycolname1];
778                             $value[$keycolname1] = $app_list_strings[$keycolname1.'_dom'][$value[$keycolname1]];
779                         }
780                         if($translate2) {
781                             $value[$keycolname2.'_dom_option'] = $value[$keycolname2];
782                             $value[$keycolname2] = $app_list_strings[$keycolname2.'_dom'][$value[$keycolname2]];
783                         }
784                         array_push($data, $value);
785                         unset($data_set[$key]);
786                         $a=1;
787                         }
788                 }
789                 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.
790                     $val=array();
791                     $val['total'] = 0;
792                     $val['count'] = 0;
793                     if($translate1) {
794                         $val[$keycolname1] = $app_list_strings[$keycolname1.'_dom'][$sort1];
795                         $val[$keycolname1.'_dom_option'] = $sort1;
796                     }
797                     else {
798                         $val[$keycolname1] = $sort1;                    
799                     }
800                     if($translate2) {
801                         $val[$keycolname2] = $app_list_strings[$keycolname2.'_dom'][$sort2];
802                         $val[$keycolname2.'_dom_option'] = $sort2;                    
803                     }
804                     elseif($keycolname2!=null) {
805                         $val[$keycolname2] = $sort2;
806                     }
807                     array_push($data, $val);
808                 }
809             }  
810         }
811         return $data;
812     }
813
814 } // end class def