]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - include/SugarCharts/SugarChart.php
Release 6.2.3
[Github/sugarcrm.git] / include / SugarCharts / SugarChart.php
1 <?php
2 if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
3 /*********************************************************************************
4  * SugarCRM Community Edition is a customer relationship management program developed by
5  * SugarCRM, Inc. Copyright (C) 2004-2011 SugarCRM Inc.
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->tabValue("{$key}",$value, 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      * returns text with tabs appended before it
283          * 
284      * @param   string $str xml tag
285                         int $tagFormat 2 = open and close tag, 1 = close, 0 = open
286                         sting $value input string
287          *                      int $depth number of times to tab
288      * @return  string with tabs appended before it
289      */         
290         
291         function tabValue($tag,$value,$depth) {
292
293                         return $this->tab("<{$tag}>".htmlspecialchars($value,ENT_QUOTES)."</{$tag}>",$depth);
294
295         }
296         /**
297      * returns xml data format
298          * 
299      * @param   none
300      * @return  string with xml data format
301      */         
302         function processData(){
303                 $data = array();
304
305                 $group_by = $this->group_by[0];
306                 if (isset($this->group_by[1])){
307                         $drill_down = $this->group_by[1];
308                 }
309                 
310                 $prev_group_by = '';
311
312                 for($i =0; $i < count($this->data_set); $i++){
313                         if ($this->data_set[$i][$group_by] != $prev_group_by){
314                                 $prev_group_by = $this->data_set[$i][$group_by];
315                                 $data[$this->data_set[$i][$group_by]] = array();
316                         }
317             
318             $data[$this->data_set[$i][$group_by]][] = $this->data_set[$i];
319                         
320                         // push new item onto legend items list
321                         if (isset($drill_down)){
322                                 if (!in_array($this->data_set[$i][$drill_down], $this->super_set)){
323                                         $this->super_set[] = $this->data_set[$i][$drill_down];
324                                 }
325                         }
326                 }       
327
328                 return $data;
329         }
330         
331         function processDataGroup($tablevel, $title, $value, $label, $link){
332                 $link = $this->forceHideDataGroupLink ? '' : $link;
333                 $data = $this->tab('<group>',$tablevel);
334                 $data .= $this->tabValue('title',$title,$tablevel+1);
335                 $data .= $this->tabValue('value',$value,$tablevel+1);
336                 $data .= $this->tabValue('label',$label,$tablevel+1);
337                 $data .= $this->tab('<link>' . $link . '</link>',$tablevel+1);
338                 $data .= $this->tab('</group>',$tablevel);
339                 return $data;
340         }
341         
342         function calculateGroupByTotal($dataset){
343                 $total = 0;
344                 
345                 foreach ($dataset as $key => $value){
346                         $total += $value;
347                 }
348                 
349                 return $total;
350         }
351         
352         function calculateSingleBarMax($dataset){
353                 $max = 0;               
354                 foreach ($dataset as $value){
355                         if ($value > $max){
356                                 $max = $value;
357                         }
358                 }
359                 
360                 return $max;
361         }
362
363         /**
364      * returns correct yAxis min/max
365          * 
366      * @param   value to check 
367      * @return  yAxis min and max
368      */                 
369         function checkYAxis($value){
370                 if ($value < $this->chart_yAxis['yMin']){
371                         $this->chart_yAxis['yMin'] = $value;
372                 }
373                 else if ($value > $this->chart_yAxis['yMax']){
374                         $this->chart_yAxis['yMax'] = $value;
375                 }
376         }
377         
378         
379         function convertCurrency($to_convert){
380                 global $locale;
381                 $decimals = '2';
382                 $decimals = $locale->getPrecision();
383                 $amount = ($this->div == 1) ? $to_convert : round($to_convert * $this->div,$decimals);
384                 
385                 return $amount;
386         }
387         
388         function formatNumber($number, $decimals= null, $decimal_point= null, $thousands_sep= null){
389                 global $locale;
390                 if(is_null($decimals)) {
391                         $decimals = $locale->getPrecision();
392                 }
393                 $seps = get_number_seperators();
394                 $thousands_sep = $seps[0];
395                 $decimal_point = $seps[1];
396                 return number_format($number, $decimals, $decimal_point, $thousands_sep);
397         }
398         
399         function getTotal(){
400                 $new_data = $this->processData();
401                 $total = 0;             
402                 foreach ($new_data as $groupByKey => $value){
403                         $total += $this->calculateTotal($groupByKey);
404                 }               
405                 
406                 return $total;
407         }
408
409         function xmlDataForGroupByChart(){
410                 $data = '';             
411                 foreach ($this->data_set as $key => $value){
412                         $amount = $this->is_currency ? $this->convertCurrency($this->calculateGroupByTotal($value)) : $this->calculateGroupByTotal($value);
413             $label = $this->is_currency ? ($this->currency_symbol . $this->formatNumber($amount)) : $amount;
414             
415                         $data .= $this->tab('<group>',2);
416                         $data .= $this->tabValue('title',$key,3);
417                         $data .= $this->tabValue('value',$amount,3);
418                         $data .= $this->tabValue('label',$label,3);
419                         $data .= $this->tab('<link></link>',3);
420                         $data .= $this->tab('<subgroups>',3);
421                         
422                         foreach ($value as $k => $v){
423                 $amount = $this->is_currency ? $this->convertCurrency($v) : $v;
424                 $label = $this->is_currency ? ($this->currency_symbol . $this->formatNumber($amount)) : $amount;
425                 
426                                 $data .= $this->tab('<group>',4);
427                                 $data .= $this->tabValue('title',$k,5);
428                                 $data .= $this->tabValue('value',$amount,5);
429                                 $data .= $this->tabValue('label',$label,5);
430                                 $data .= $this->tab('<link></link>',5);
431                                 $data .= $this->tab('</group>',4);
432                                 $this->checkYAxis($v);
433                         }
434                         $data .= $this->tab('</subgroups>',3);
435                         $data .= $this->tab('</group>',2);
436                 }
437                 
438                 return $data;           
439         }
440         
441         function xmlDataForGaugeChart(){
442                 $data = '';             
443                 $gaugePosition = $this->data_set[0]['num'];
444                 $this->chart_yAxis['yMax'] = $this->chart_properties['gaugeTarget'];
445                 $this->chart_yAxis['yStep'] = 1;
446                 $data .= $this->processDataGroup(2, 'GaugePosition', $gaugePosition, $gaugePosition, '');
447                 $data .= $this->processGauge($gaugePosition, $this->chart_properties['gaugeTarget']);
448                 
449                 return $data;           
450         }
451         
452         function xmlDataBarChart(){
453                 $data = '';
454                 $max = $this->calculateSingleBarMax($this->data_set);
455                 $this->checkYAxis($max);
456
457                 if (isset($this->group_by[0])){
458                         $group_by = $this->group_by[0];
459                         if (isset($this->group_by[1])){
460                                 $drill_down = $this->group_by[1];
461                         }       
462                 }
463
464                 foreach ($this->data_set as $key => $value){
465                         if ($this->is_currency){
466                                 $value = $this->convertCurrency($value);
467                                 $label = $this->currency_symbol;
468                                 $label .= $this->formatNumber($value);
469                                 $label .= $this->thousands_symbol;
470                         }                               
471                         else{
472                                 $label = $value;
473                         }
474
475                         $data .= $this->tab('<group>', 2);
476                         $data .= $this->tabValue('title',$key, 3);
477                         $data .= $this->tabValue('value',$value, 3);
478                         $data .= $this->tabValue('label',$label, 3);
479                         if (isset($drill_down) && $drill_down){
480                                 if ($this->group_by[0] == 'm'){
481                                         $additional_param = '&date_closed_advanced=' . urlencode($key);                                 
482                                 } else if ( $this->group_by[0] == 'sales_stage' ) {
483                     $additional_param = '&sales_stage_advanced[]='.urlencode(array_search($key,$GLOBALS['app_list_strings']['sales_stage_dom']));
484                 } else{
485                                         $additional_param = "&" . $this->group_by[0] . "=" . urlencode($key);   
486                                 }
487                                 $url = $this->constructURL() . $additional_param;                                       
488                                 
489                                 $data .= $this->tab('<link>' . $url . '</link>', 3);
490                         }                               
491                         $data .= $this->tab('<subgroups>', 3);
492                         $data .= $this->tab('</subgroups>', 3);
493                         $data .= $this->tab('</group>', 2);
494                 }
495                 return $data;   
496         }
497         
498         function xmlDataGenericChart(){
499                 $data = '';
500                 $group_by = $this->group_by[0];
501                 if (isset($this->group_by[1])){
502                         $drill_down = $this->group_by[1];
503                 }               
504                 $new_data = $this->processData();
505
506                 foreach ($new_data as $groupByKey => $value){
507                         $total = $this->calculateTotal($groupByKey);
508                         $this->checkYAxis($total);
509                         
510                         if ($this->group_by[0] == 'm'){
511                                 $additional_param = '&date_closed_advanced=' . urlencode($groupByKey);                                  
512                         }
513                         else{
514                                 $paramValue = (isset($value[0]['key']) && $value[0]['key'] != '') ? $value[0]['key'] : $groupByKey;
515                                 $paramValue = (isset($value[0][$this->group_by[0]."_dom_option"]) && $value[0][$this->group_by[0]."_dom_option"] != '') ? $value[0][$this->group_by[0]."_dom_option"] : $paramValue;
516                                 $additional_param = "&" . $this->group_by[0] . "=" . urlencode($paramValue);    
517                         }
518                         
519                         $url = $this->constructURL() . $additional_param;
520                         
521                         $amount = $this->is_currency ? $this->convertCurrency($total) : $total;
522                         $label = $this->is_currency ? ($this->currency_symbol . $this->formatNumber($amount) . 'K') : $amount;
523
524                         $data .= $this->tab('<group>',2);
525                         $data .= $this->tabValue('title',$groupByKey,3);
526                         $data .= $this->tabValue('value',$amount,3);
527                         $data .= $this->tabValue('label',$label,3);
528                         $data .= $this->tab('<link>' . $url . '</link>',3);
529                         
530                         $data .= $this->tab('<subgroups>',3);                                           
531                         $processed = array();
532                                 
533                         if (isset($drill_down) && $drill_down != ''){
534                                 for($i =0; $i < count($new_data[$groupByKey]); $i++){
535                                         if ($new_data[$groupByKey][$i][$group_by] == $groupByKey){
536                                                 if ($drill_down == 'user_name'){
537                                                         $drill_down_param = '&assigned_user_id[]=' . urlencode($new_data[$groupByKey][$i]['assigned_user_id']);
538                                                 }
539                                                 else if ($drill_down == 'm'){
540                                                         $drill_down_param = '&date_closed_advanced=' . urlencode($new_data[$groupByKey][$i][$drill_down]);
541                                                 }
542                                                 else{
543                                                         $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];
544                                                         $drill_down_param = '&' . $drill_down . '=' . urlencode($paramValue);
545                                                 }
546                                                 
547                                                 if($this->is_currency) {
548                                                   $sub_amount = $this->formatNumber($this->convertCurrency($new_data[$groupByKey][$i]['total']));
549                                                   $sub_amount_formatted = $this->currency_symbol . $sub_amount . 'K';
550                                                   //bug: 38877 - do not format the amount for the value as it breaks the chart
551                                                   $sub_amount = $this->convertCurrency($new_data[$groupByKey][$i]['total']);
552                                                 } else {
553                                                   $sub_amount = $new_data[$groupByKey][$i]['total'];
554                                                   $sub_amount_formatted = $sub_amount;
555                                                 }
556                                                 
557                                                 $data .= $this->processDataGroup(4, $new_data[$groupByKey][$i][$drill_down],
558                                                                                                                     $sub_amount,
559                                                                                                                     $sub_amount_formatted,
560                                                                                                                     $url . $drill_down_param );
561                                                 array_push($processed, $new_data[$groupByKey][$i][$drill_down]);
562                                         }
563                                 }
564                                 $not_processed = array_diff($this->super_set, $processed);
565                                 foreach ($not_processed as $title){
566                                         $data .= $this->processDataGroup(4, $title, 'NULL', '', $url);
567                                 }                       
568                         }
569
570                         $data .= $this->tab('</subgroups>',3);
571                         $data .= $this->tab('</group>',2);
572                 }
573                 return $data;
574         }
575     
576     /**
577      * returns a name for the XML File
578      *
579      * @param string $file_id - unique id to make part of the file name
580      */
581     public static function getXMLFileName(
582          $file_id
583          )
584     {
585         global $sugar_config, $current_user;
586
587         $filename = $sugar_config['tmp_dir']. $current_user->id . '_' . $file_id . '.xml';
588
589         if ( !is_dir(dirname($filename)) )
590             create_cache_directory(str_ireplace($GLOBALS['sugar_config']['cache_dir'],"",$filename));
591
592         return $filename;
593     }
594     
595     public function processXmlData(){
596         $data = '';
597         
598                 if ($this->chart_properties['type'] == 'group by chart'){
599                         $data .= $this->xmlDataForGroupByChart();
600                 }
601                 else if ($this->chart_properties['type'] == 'bar chart' || $this->chart_properties['type'] == 'horizontal bar chart'){
602                         $data .= $this->xmlDataBarChart();                      
603                 }
604                 else{
605                         $data .= $this->xmlDataGenericChart();
606                 }               
607
608                 return $data;
609     }
610      
611         function xmlData(){
612                 $data = $this->tab('<data>',1);
613                 $data .= $this->processXmlData();       
614                 $data .= $this->tab('</data>',1);
615                 
616                 return $data;
617         }
618         
619         /**
620      * function to generate XML and return it
621          * 
622      * @param   none
623      * @return  string $xmlContents with xml information
624      */         
625         function generateXML($xmlDataName = false){
626                 $xmlContents = $this->xmlHeader();              
627                 $xmlContents .= $this->xmlProperties();         
628                 $xmlContents .= $this->xmlData();
629                 $xmlContents .= $this->xmlYAxis();      
630                 $xmlContents .= $this->xmlFooter();
631                 
632                 return $xmlContents;
633         }
634
635         /**
636      * function to save XML contents into a file
637          * 
638      * @param   string $xmlFilename location of the xml file
639          *                      string $xmlContents contents of the xml file
640      * @return  string boolean denoting whether save has failed
641      */                 
642         function saveXMLFile($xmlFilename,$xmlContents) {
643                 global $app_strings;
644                 global $locale;
645                 
646                 $xmlContents = chr(255).chr(254).mb_convert_encoding($xmlContents, 'UTF-16LE', 'UTF-8'); 
647                 
648                 // open file
649                 if (!$fh = sugar_fopen($xmlFilename, 'w')) {
650                         $GLOBALS['log']->debug("Cannot open file ($xmlFilename)");
651                         return;
652                 }
653                 
654                 // write the contents to the file
655                 if (fwrite($fh,$xmlContents) === FALSE) {
656                         $GLOBALS['log']->debug("Cannot write to file ($xmlFilename)");
657                         return false;
658                 }
659         
660                 $GLOBALS['log']->debug("Success, wrote ($xmlContents) to file ($xmlFilename)");
661         
662                 fclose($fh);
663                 return true;
664         }
665
666         /**
667      * generates xml file for Flash charts to use for internationalized instances
668          * 
669      * @param   string $xmlFile location of the XML file to write to
670      * @return  none
671      */ 
672         function generateChartStrings($xmlFile){
673                 global $current_language, $app_list_strings;
674                 
675                 $chartStringsXML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
676                 $chartStringsXML .= "<sugarlanguage version=\"1.0\">\n";
677                 $chartStringsXML .= $this->tab("<charts>",1);
678                 
679                 if (empty($app_list_strings)) {
680                     //set module and application string arrays based upon selected language
681                         $app_list_strings = return_app_list_strings_language($current_language);
682                 }
683                 
684                 // retrieve the strings defined at include/language/en_us.lang.php
685                 foreach ($app_list_strings['chart_strings'] as $tag => $chart_string){
686                         $chartStringsXML .= $this->tab("<$tag>$chart_string</$tag>",2);
687                 }               
688                 
689                 $chartStringsXML .= $this->tab("</charts>",1);
690                 $chartStringsXML .= "</sugarlanguage>\n";
691                 
692                 $this->saveXMLFile($xmlFile, $chartStringsXML);
693         }
694         
695         /**
696      * wrapper function to return the html code containing the chart in a div
697          * 
698      * @param   string $name    name of the div
699          *                      string $xmlFile location of the XML file
700          *                      string $style   optional additional styles for the div
701      * @return  string returns the html code through smarty
702      */                                 
703         function display($name, $xmlFile, $width='320', $height='480', $resize=false){
704                 
705                 
706                 // generate strings for chart if it does not exist
707                 global $current_language, $theme, $sugar_config,$app_strings;
708                 
709                 $this->app_strings = $app_strings;
710                 $this->chartStringsXML = $GLOBALS['sugar_config']['tmp_dir'].'chart_strings.' . $current_language .'.lang.xml';
711                 if (!file_exists($this->chartStringsXML)){
712                         $this->generateChartStrings($this->chartStringsXML);
713                 }
714                                 
715                 $templateFile = "";                     
716                 return $templateFile;
717         }
718
719         function getDashletScript($id,$xmlFile="") {
720                 
721         $xmlFile = (!$xmlFile) ? $sugar_config['tmp_dir']. $current_user->id . '_' . $this->id . '.xml' : $xmlFile;
722         $chartStringsXML = $GLOBALS['sugar_config']['tmp_dir'].'chart_strings.' . $current_language .'.lang.xml'; 
723         
724         $this->ss->assign('chartName', $id);
725     $this->ss->assign('chartXMLFile', $xmlFile);
726     $this->ss->assign('chartStyleCSS', SugarThemeRegistry::current()->getCSSURL('chart.css'));
727     $this->ss->assign('chartColorsXML', SugarThemeRegistry::current()->getImageURL('sugarColors.xml'));
728     $this->ss->assign('chartLangFile', $GLOBALS['sugar_config']['tmp_dir'].'chart_strings.' . $GLOBALS['current_language'] .'.lang.xml');
729                 
730                 $templateFile = "";
731                 return $templateFile;
732         }
733         
734         
735   /**
736          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.
737          If the data quantity is large, it maybe a little slow.
738     * @param         array $data_set           The data get from database
739                            string $keycolname1      We will sort by this key first
740                            bool $translate1            Whether to trabslate the first column
741                            string $keycolname1      We will sort by this key secondly, and  it can be null, then it will only sort by the first column.
742                            bool $translate1            Whether to trabslate the second column
743                            bool $ifsort2                 Whether to sort by the second column or just translate the second column.
744     * @return        The sorted and translated data.
745    */
746     function sortData($data_set, $keycolname1=null, $translate1=false, $keycolname2=null, $translate2=false, $ifsort2=false) {
747         //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.
748         global $app_list_strings;
749         $sortby1[] = array();
750         foreach ($data_set as $row) {
751             $sortby1[]  = $row[$keycolname1];
752         }
753         $sortby1 = array_unique($sortby1);
754         //The data is from the database, the sorting should be done in the sql. So I will not do the sort here.
755         if($translate1) {
756             $temp_sortby1 = array();
757             foreach(array_keys($app_list_strings[$keycolname1.'_dom']) as $sortby1_value) {
758                 if(in_array($sortby1_value, $sortby1)) {
759                     $temp_sortby1[] = $sortby1_value;
760                 }
761             }
762             $sortby1 = $temp_sortby1;
763         }
764         
765         //if(isset($sortby1[0]) && $sortby1[0]=='') unset($sortby1[0]);//the beginning of lead_source_dom is blank.
766         if(isset($sortby1[0]) && $sortby1[0]==array()) unset($sortby1[0]);//the beginning of month after search is blank.
767         
768         if($ifsort2==false) $sortby2=array(0);
769         
770         if($keycolname2!=null) {
771             $sortby2 = array();
772             foreach ($data_set as $row) {
773                 $sortby2[]  = $row[$keycolname2];
774             }
775             //The data is from the database, the sorting should be done in the sql. So I will not do the sort here.
776             $sortby2 = array_unique($sortby2);
777             if($translate2) {
778                 $temp_sortby2 = array();
779                 foreach(array_keys($app_list_strings[$keycolname2.'_dom']) as $sortby2_value) {
780                     if(in_array($sortby2_value, $sortby2)) {
781                         $temp_sortby2[] = $sortby2_value;
782                     }
783                 }
784                 $sortby2 = $temp_sortby2;
785             }
786         }
787         
788         $data=array();
789
790         foreach($sortby1 as $sort1) {
791             foreach($sortby2 as $sort2) {
792                 if($ifsort2) $a=0;
793                 foreach($data_set as $key => $value){
794                     if($value[$keycolname1] == $sort1 && (!$ifsort2 || $value[$keycolname2]== $sort2)) {
795                         if($translate1) {
796                             $value[$keycolname1.'_dom_option'] = $value[$keycolname1];
797                             $value[$keycolname1] = $app_list_strings[$keycolname1.'_dom'][$value[$keycolname1]];
798                         }
799                         if($translate2) {
800                             $value[$keycolname2.'_dom_option'] = $value[$keycolname2];
801                             $value[$keycolname2] = $app_list_strings[$keycolname2.'_dom'][$value[$keycolname2]];
802                         }
803                         array_push($data, $value);
804                         unset($data_set[$key]);
805                         $a=1;
806                         }
807                 }
808                 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.
809                     $val=array();
810                     $val['total'] = 0;
811                     $val['count'] = 0;
812                     if($translate1) {
813                         $val[$keycolname1] = $app_list_strings[$keycolname1.'_dom'][$sort1];
814                         $val[$keycolname1.'_dom_option'] = $sort1;
815                     }
816                     else {
817                         $val[$keycolname1] = $sort1;                    
818                     }
819                     if($translate2) {
820                         $val[$keycolname2] = $app_list_strings[$keycolname2.'_dom'][$sort2];
821                         $val[$keycolname2.'_dom_option'] = $sort2;                    
822                     }
823                     elseif($keycolname2!=null) {
824                         $val[$keycolname2] = $sort2;
825                     }
826                     array_push($data, $val);
827                 }
828             }  
829         }
830         return $data;
831     }
832     
833     function getChartResources() {
834                 
835                 $resources = "";
836                 return $resources;
837         }
838         
839         function getMySugarChartResources() {
840                 
841                 $mySugarRources = "";
842                 return $mySugarResources;
843         }
844         
845         /**
846      * wrapper function to return chart array after any additional processing
847          * 
848      * @param   array $chartsArray      array of chart config items that need processing
849      * @return  array $chartArray after it has been process
850      */
851         function chartArray($chartsArray) {
852
853                 return $chartsArray;
854         }
855
856 } // end class def