]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - include/SugarCharts/SugarChart.php
Release 6.2.0beta4
[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 class SugarChart {
40
41         private $db;
42         protected $ss;
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();
53         
54         var $currency_symbol;
55         var $thousands_symbol;
56         var $is_currency;
57         var $supports_image_export = false;
58         var $print_html_legend_pdf = false;
59         var $image_export_type = "";
60         
61         public function __construct() {
62                 $this->db = &DBManagerFactory::getInstance();
63                 $this->ss = new Sugar_Smarty();
64                 
65                 $this->chart_yAxis['yMin'] = 0;         
66                 $this->chart_yAxis['yMax'] = 0;
67                 
68                 
69                 if ($GLOBALS['current_user']->getPreference('currency')){
70                     
71             $currency = new Currency();
72             $currency->retrieve($GLOBALS['current_user']->getPreference('currency'));
73             $this->div = $currency->conversion_rate;
74             $this->currency_symbol = $currency->symbol;
75         }
76         else{
77                 $this->currency_symbol = $GLOBALS['sugar_config']['default_currency_symbol'];
78                         $this->div = 1;
79                         $this->is_currency = false;
80         }
81         $this->image_export_type = (extension_loaded('gd') && function_exists('gd_info')) ? "png" : "jpg";
82         }
83         
84         function getData($query){
85                 $result = $this->db->query($query);
86                 
87                 $row = $this->db->fetchByAssoc($result);
88                 
89                 while ($row != null){
90                         $this->data_set[] = $row;
91                         $row = $this->db->fetchByAssoc($result);
92                 }
93         }
94         
95         function constructBaseURL(){
96                 $numParams = 0;
97                 $url = 'index.php?';
98                 
99                 foreach ($this->base_url as $param => $value){
100                         if ($numParams == 0){
101                                 $url .= $param . '=' . $value;
102                         }
103                         else{
104                                 $url .= '&' .$param . '=' .$value;
105                         }
106                         $numParams++;
107                 }
108
109                 return $url;
110         }
111         
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);
119                                 }
120                         }
121                         else{
122                                 $url .= '&' . $param . '=' . urlencode($value);
123                         }
124                 }
125                 return $url;
126         }
127         
128         function setData($dataSet){
129                 $this->data_set = $dataSet;
130         }
131         
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;
138         }
139         
140         function setDisplayProperty($property, $value){
141                 $this->chart_properties[$property] = $value;
142         }
143         
144         function setColors($colors = array()){
145                 $this->colors_list = $colors;
146         }
147         
148     /**
149      * returns the header for the constructed xml file for sugarcharts
150          * 
151      * @param   nothing
152      * @return  string $header XML header
153      */
154         function xmlHeader(){
155                 $header = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
156                 $header .= "<sugarcharts version=\"1.0\">\n";
157                 
158                 return $header;
159         }
160
161         /**
162      * returns the footer for the constructed xml file for sugarcharts
163          * 
164      * @param   nothing
165      * @return  string $footer XML footer
166      */
167         function xmlFooter(){
168                 $footer = "</sugarcharts>";
169                 
170                 return $footer;
171         }
172
173         /**
174      * returns the properties tag for the constructed xml file for sugarcharts
175          * 
176      * @param   nothing
177      * @return  string $properties XML properties tag
178      */ 
179         function xmlProperties(){       
180                 // open the properties tag
181                 $properties = $this->tab("<properties>",1);
182                 
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);
186                 }
187                 
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);
193                         }
194                         
195                         // close the colors tag
196                         $properties .= $this->tab("</colors>",2);
197                 }
198                 
199                 // close the properties tag
200                 $properties .= $this->tab("</properties>",1);
201                 
202                 return $properties;
203         }
204         
205         /**
206      * returns the y-axis values for the chart
207          * 
208      * @param   nothing
209      * @return  string $yAxis XML yAxis tag
210      */ 
211         function xmlYAxis(){
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);
218                 
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);
222                 }
223                 else if ($baseval > 1 && $baseval <= 3){
224                         $step = 5 * pow(10, $exp-1);
225                 }               
226                 else if ($baseval > 3 && $baseval <= 6){
227                         $step = 10 * pow(10, $exp-1);
228                 }       
229                 else if ($baseval > 6 && $baseval <= 10){
230                         $step = 20 * pow(10, $exp-1);
231                 }       
232
233                 // edge cases for values less than 10
234                 if ($max == 0 || $step < 1){
235                         $step = 1;
236                 }
237         
238                 $this->chart_yAxis['yStep'] = $step;
239                 
240                 // to compensate, the yMax should be at least one step above the max value
241                         $this->chart_yAxis['yMax'] += $this->chart_yAxis['yStep'];
242                 
243                 $yAxis = $this->tab("<yAxis>" ,1);
244                 
245                 foreach ($this->chart_yAxis as $key => $value){
246                         $yAxis .= $this->tab("<$key>$value</$key>", 2);
247                 }
248                 
249                 $yAxis .= $this->tab("</yAxis>" ,1);
250                 
251                 return $yAxis;
252         }
253
254         /**
255      * returns the total amount value for the group by field
256          * 
257      * @param   group by field
258      * @return  int $total total value
259      */ 
260         function calculateTotal($group_by){
261                 $total = 0;     
262                 
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'];
266                         }
267                 }               
268                 return $total;
269         }
270
271         /**
272      * returns text with tabs appended before it
273          * 
274      * @param   string $str input string
275          *                      int $depth number of times to tab
276      * @return  string with tabs appended before it
277      */ 
278         function tab($str, $depth){
279                 return str_repeat("\t", $depth) . $str . "\n";  
280         }
281         
282         /**
283      * returns xml data format
284          * 
285      * @param   none
286      * @return  string with xml data format
287      */         
288         function processData(){
289                 $data = array();
290
291                 $group_by = $this->group_by[0];
292                 if (isset($this->group_by[1])){
293                         $drill_down = $this->group_by[1];
294                 }
295                 
296                 $prev_group_by = '';
297
298                 for($i =0; $i < count($this->data_set); $i++){
299                         if ($this->data_set[$i][$group_by] != $prev_group_by){
300                                 $prev_group_by = $this->data_set[$i][$group_by];
301                                 $data[$this->data_set[$i][$group_by]] = array();
302                         }
303             
304             $data[$this->data_set[$i][$group_by]][] = $this->data_set[$i];
305                         
306                         // push new item onto legend items list
307                         if (isset($drill_down)){
308                                 if (!in_array($this->data_set[$i][$drill_down], $this->super_set)){
309                                         $this->super_set[] = $this->data_set[$i][$drill_down];
310                                 }
311                         }
312                 }       
313
314                 return $data;
315         }
316         
317         function processDataGroup($tablevel, $title, $value, $label, $link){
318                 $link = $this->forceHideDataGroupLink ? '' : $link;
319                 $data = $this->tab('<group>',$tablevel);
320                 $data .= $this->tab('<title>' . $title . '</title>',$tablevel+1);
321                 $data .= $this->tab('<value>' . $value . '</value>',$tablevel+1);
322                 $data .= $this->tab('<label>' . $label . '</label>',$tablevel+1);
323                 $data .= $this->tab('<link>' . $link . '</link>',$tablevel+1);
324                 $data .= $this->tab('</group>',$tablevel);
325                 return $data;
326         }
327         
328         function calculateGroupByTotal($dataset){
329                 $total = 0;
330                 
331                 foreach ($dataset as $key => $value){
332                         $total += $value;
333                 }
334                 
335                 return $total;
336         }
337         
338         function calculateSingleBarMax($dataset){
339                 $max = 0;               
340                 foreach ($dataset as $value){
341                         if ($value > $max){
342                                 $max = $value;
343                         }
344                 }
345                 
346                 return $max;
347         }
348
349         /**
350      * returns correct yAxis min/max
351          * 
352      * @param   value to check 
353      * @return  yAxis min and max
354      */                 
355         function checkYAxis($value){
356                 if ($value < $this->chart_yAxis['yMin']){
357                         $this->chart_yAxis['yMin'] = $value;
358                 }
359                 else if ($value > $this->chart_yAxis['yMax']){
360                         $this->chart_yAxis['yMax'] = $value;
361                 }
362         }
363         
364         
365         function convertCurrency($to_convert){
366                 global $locale;
367                 $decimals = '2';
368                 $decimals = $locale->getPrecision();
369                 $amount = ($this->div == 1) ? $to_convert : round($to_convert * $this->div,$decimals);
370                 
371                 return $amount;
372         }
373         
374         function formatNumber($number, $decimals= null, $decimal_point= null, $thousands_sep= null){
375                 global $locale;
376                 if(is_null($decimals)) {
377                         $decimals = $locale->getPrecision();
378                 }
379                 $seps = get_number_seperators();
380                 $thousands_sep = $seps[0];
381                 $decimal_point = $seps[1];
382                 return number_format($number, $decimals, $decimal_point, $thousands_sep);
383         }
384         
385         function getTotal(){
386                 $new_data = $this->processData();
387                 $total = 0;             
388                 foreach ($new_data as $groupByKey => $value){
389                         $total += $this->calculateTotal($groupByKey);
390                 }               
391                 
392                 return $total;
393         }
394
395         function xmlDataForGroupByChart(){
396                 $data = '';             
397                 foreach ($this->data_set as $key => $value){
398                         $amount = $this->is_currency ? $this->convertCurrency($this->calculateGroupByTotal($value)) : $this->calculateGroupByTotal($value);
399             $label = $this->is_currency ? ($this->currency_symbol . $this->formatNumber($amount)) : $amount;
400             
401                         $data .= $this->tab('<group>',2);
402                         $data .= $this->tab('<title>' . $key . '</title>',3);
403                         $data .= $this->tab('<value>' . $amount . '</value>',3);
404                         $data .= $this->tab('<label>' . $label . '</label>',3);
405                         $data .= $this->tab('<link></link>',3);
406                         $data .= $this->tab('<subgroups>',3);
407                         
408                         foreach ($value as $k => $v){
409                 $amount = $this->is_currency ? $this->convertCurrency($v) : $v;
410                 $label = $this->is_currency ? ($this->currency_symbol . $this->formatNumber($amount)) : $amount;
411                 
412                                 $data .= $this->tab('<group>',4);
413                                 $data .= $this->tab('<title>' . $k . '</title>',5);
414                                 $data .= $this->tab('<value>' . $amount . '</value>',5);
415                                 $data .= $this->tab('<label>' . $label . '</label>',5);
416                                 $data .= $this->tab('<link></link>',5);
417                                 $data .= $this->tab('</group>',4);
418                                 $this->checkYAxis($v);
419                         }
420                         $data .= $this->tab('</subgroups>',3);
421                         $data .= $this->tab('</group>',2);
422                 }
423                 
424                 return $data;           
425         }
426         
427         function xmlDataForGaugeChart(){
428                 $data = '';             
429                 $gaugePosition = $this->data_set[0]['num'];
430                 $this->chart_yAxis['yMax'] = $this->chart_properties['gaugeTarget'];
431                 $this->chart_yAxis['yStep'] = 1;
432                 $data .= $this->processDataGroup(2, 'GaugePosition', $gaugePosition, $gaugePosition, '');
433                 $data .= $this->processGauge($gaugePosition, $this->chart_properties['gaugeTarget']);
434                 
435                 return $data;           
436         }
437         
438         function xmlDataBarChart(){
439                 $data = '';
440                 $max = $this->calculateSingleBarMax($this->data_set);
441                 $this->checkYAxis($max);
442
443                 if (isset($this->group_by[0])){
444                         $group_by = $this->group_by[0];
445                         if (isset($this->group_by[1])){
446                                 $drill_down = $this->group_by[1];
447                         }       
448                 }
449
450                 foreach ($this->data_set as $key => $value){
451                         if ($this->is_currency){
452                                 $value = $this->convertCurrency($value);
453                                 $label = $this->currency_symbol;
454                                 $label .= $this->formatNumber($value);
455                                 $label .= $this->thousands_symbol;
456                         }                               
457                         else{
458                                 $label = $value;
459                         }
460
461                         $data .= $this->tab('<group>', 2);
462                         $data .= $this->tab('<title>' . $key . '</title>', 3);
463                         $data .= $this->tab('<value>' . $value . '</value>', 3);
464                         $data .= $this->tab('<label>' . $label . '</label>', 3);
465                         if (isset($drill_down) && $drill_down){
466                                 if ($this->group_by[0] == 'm'){
467                                         $additional_param = '&date_closed_advanced=' . urlencode($key);                                 
468                                 }
469                                 else{
470                                         $additional_param = "&" . $this->group_by[0] . "=" . urlencode($key);   
471                                 }
472                                 $url = $this->constructURL() . $additional_param;                                       
473                                 
474                                 $data .= $this->tab('<link>' . $url . '</link>', 3);
475                         }                               
476                         $data .= $this->tab('<subgroups>', 3);
477                         $data .= $this->tab('</subgroups>', 3);
478                         $data .= $this->tab('</group>', 2);
479                 }
480                 return $data;   
481         }
482         
483         function xmlDataGenericChart(){
484                 $data = '';
485                 $group_by = $this->group_by[0];
486                 if (isset($this->group_by[1])){
487                         $drill_down = $this->group_by[1];
488                 }               
489                 $new_data = $this->processData();
490
491                 foreach ($new_data as $groupByKey => $value){
492                         $total = $this->calculateTotal($groupByKey);
493                         $this->checkYAxis($total);
494                         
495                         if ($this->group_by[0] == 'm'){
496                                 $additional_param = '&date_closed_advanced=' . urlencode($groupByKey);                                  
497                         }
498                         else{
499                                 $paramValue = (isset($value[0]['key']) && $value[0]['key'] != '') ? $value[0]['key'] : $groupByKey;
500                                 $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;
501                                 $additional_param = "&" . $this->group_by[0] . "=" . urlencode($paramValue);    
502                         }
503                         
504                         $url = $this->constructURL() . $additional_param;
505                         
506                         $amount = $this->is_currency ? $this->convertCurrency($total) : $total;
507                         $label = $this->is_currency ? ($this->currency_symbol . $this->formatNumber($amount) . 'K') : $amount;
508
509                         $data .= $this->tab('<group>',2);
510                         $data .= $this->tab('<title>' . $groupByKey . '</title>',3);
511                         $data .= $this->tab('<value>' . $amount . '</value>',3);
512                         $data .= $this->tab('<label>' . $label . '</label>',3);
513                         $data .= $this->tab('<link>' . $url . '</link>',3);
514                         
515                         $data .= $this->tab('<subgroups>',3);                                           
516                         $processed = array();
517                                 
518                         if (isset($drill_down) && $drill_down != ''){
519                                 for($i =0; $i < count($new_data[$groupByKey]); $i++){
520                                         if ($new_data[$groupByKey][$i][$group_by] == $groupByKey){
521                                                 if ($drill_down == 'user_name'){
522                                                         $drill_down_param = '&assigned_user_id[]=' . urlencode($new_data[$groupByKey][$i]['assigned_user_id']);
523                                                 }
524                                                 else if ($drill_down == 'm'){
525                                                         $drill_down_param = '&date_closed_advanced=' . urlencode($new_data[$groupByKey][$i][$drill_down]);
526                                                 }
527                                                 else{
528                                                         $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];
529                                                         $drill_down_param = '&' . $drill_down . '=' . urlencode($paramValue);
530                                                 }
531                                                 
532                                                 if($this->is_currency) {
533                                                   $sub_amount = $this->formatNumber($this->convertCurrency($new_data[$groupByKey][$i]['total']));
534                                                   $sub_amount_formatted = $this->currency_symbol . $sub_amount . 'K';
535                                                   //bug: 38877 - do not format the amount for the value as it breaks the chart
536                                                   $sub_amount = $this->convertCurrency($new_data[$groupByKey][$i]['total']);
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                 $this->app_strings = $app_strings;
695                 $this->chartStringsXML = $GLOBALS['sugar_config']['tmp_dir'].'chart_strings.' . $current_language .'.lang.xml';
696                 if (!file_exists($this->chartStringsXML)){
697                         $this->generateChartStrings($this->chartStringsXML);
698                 }
699                                 
700                 $templateFile = "";                     
701                 return $templateFile;
702         }
703
704         function getDashletScript($id,$xmlFile="") {
705                 
706         $xmlFile = (!$xmlFile) ? $sugar_config['tmp_dir']. $current_user->id . '_' . $this->id . '.xml' : $xmlFile;
707         $chartStringsXML = $GLOBALS['sugar_config']['tmp_dir'].'chart_strings.' . $current_language .'.lang.xml'; 
708         
709         $this->ss->assign('chartName', $id);
710     $this->ss->assign('chartXMLFile', $xmlFile);
711     $this->ss->assign('chartStyleCSS', SugarThemeRegistry::current()->getCSSURL('chart.css'));
712     $this->ss->assign('chartColorsXML', SugarThemeRegistry::current()->getImageURL('sugarColors.xml'));
713     $this->ss->assign('chartLangFile', $GLOBALS['sugar_config']['tmp_dir'].'chart_strings.' . $GLOBALS['current_language'] .'.lang.xml');
714                 
715                 $templateFile = "";
716                 return $templateFile;
717         }
718         
719         
720   /**
721          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.
722          If the data quantity is large, it maybe a little slow.
723     * @param         array $data_set           The data get from database
724                            string $keycolname1      We will sort by this key first
725                            bool $translate1            Whether to trabslate the first column
726                            string $keycolname1      We will sort by this key secondly, and  it can be null, then it will only sort by the first column.
727                            bool $translate1            Whether to trabslate the second column
728                            bool $ifsort2                 Whether to sort by the second column or just translate the second column.
729     * @return        The sorted and translated data.
730    */
731     function sortData($data_set, $keycolname1=null, $translate1=false, $keycolname2=null, $translate2=false, $ifsort2=false) {
732         //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.
733         global $app_list_strings;
734         $sortby1[] = array();
735         foreach ($data_set as $row) {
736             $sortby1[]  = $row[$keycolname1];
737         }
738         $sortby1 = array_unique($sortby1);
739         //The data is from the database, the sorting should be done in the sql. So I will not do the sort here.
740         if($translate1) {
741             $temp_sortby1 = array();
742             foreach(array_keys($app_list_strings[$keycolname1.'_dom']) as $sortby1_value) {
743                 if(in_array($sortby1_value, $sortby1)) {
744                     $temp_sortby1[] = $sortby1_value;
745                 }
746             }
747             $sortby1 = $temp_sortby1;
748         }
749         
750         //if(isset($sortby1[0]) && $sortby1[0]=='') unset($sortby1[0]);//the beginning of lead_source_dom is blank.
751         if(isset($sortby1[0]) && $sortby1[0]==array()) unset($sortby1[0]);//the beginning of month after search is blank.
752         
753         if($ifsort2==false) $sortby2=array(0);
754         
755         if($keycolname2!=null) {
756             $sortby2 = array();
757             foreach ($data_set as $row) {
758                 $sortby2[]  = $row[$keycolname2];
759             }
760             //The data is from the database, the sorting should be done in the sql. So I will not do the sort here.
761             $sortby2 = array_unique($sortby2);
762             if($translate2) {
763                 $temp_sortby2 = array();
764                 foreach(array_keys($app_list_strings[$keycolname2.'_dom']) as $sortby2_value) {
765                     if(in_array($sortby2_value, $sortby2)) {
766                         $temp_sortby2[] = $sortby2_value;
767                     }
768                 }
769                 $sortby2 = $temp_sortby2;
770             }
771         }
772         
773         $data=array();
774
775         foreach($sortby1 as $sort1) {
776             foreach($sortby2 as $sort2) {
777                 if($ifsort2) $a=0;
778                 foreach($data_set as $key => $value){
779                     if($value[$keycolname1] == $sort1 && (!$ifsort2 || $value[$keycolname2]== $sort2)) {
780                         if($translate1) {
781                             $value[$keycolname1.'_dom_option'] = $value[$keycolname1];
782                             $value[$keycolname1] = $app_list_strings[$keycolname1.'_dom'][$value[$keycolname1]];
783                         }
784                         if($translate2) {
785                             $value[$keycolname2.'_dom_option'] = $value[$keycolname2];
786                             $value[$keycolname2] = $app_list_strings[$keycolname2.'_dom'][$value[$keycolname2]];
787                         }
788                         array_push($data, $value);
789                         unset($data_set[$key]);
790                         $a=1;
791                         }
792                 }
793                 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.
794                     $val=array();
795                     $val['total'] = 0;
796                     $val['count'] = 0;
797                     if($translate1) {
798                         $val[$keycolname1] = $app_list_strings[$keycolname1.'_dom'][$sort1];
799                         $val[$keycolname1.'_dom_option'] = $sort1;
800                     }
801                     else {
802                         $val[$keycolname1] = $sort1;                    
803                     }
804                     if($translate2) {
805                         $val[$keycolname2] = $app_list_strings[$keycolname2.'_dom'][$sort2];
806                         $val[$keycolname2.'_dom_option'] = $sort2;                    
807                     }
808                     elseif($keycolname2!=null) {
809                         $val[$keycolname2] = $sort2;
810                     }
811                     array_push($data, $val);
812                 }
813             }  
814         }
815         return $data;
816     }
817     
818     function getChartResources() {
819                 
820                 $resources = "";
821                 return $resources;
822         }
823         
824         function getMySugarChartResources() {
825                 
826                 $mySugarRources = "";
827                 return $mySugarResources;
828         }
829         
830         /**
831      * wrapper function to return chart array after any additional processing
832          * 
833      * @param   array $chartsArray      array of chart config items that need processing
834      * @return  array $chartArray after it has been process
835      */
836         function chartArray($chartsArray) {
837
838                 return $chartsArray;
839         }
840
841 } // end class def