]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - include/SugarCharts/SugarChart.php
Release 6.2.4
[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                                 /*
535                                         * We have to iterate users in order they are in super_set for every group.  
536                                         * That is required for correct pileup user links to user colors in a chart.
537                                         */ 
538                                         foreach ($this->super_set as $superSetKey => $superSetValue)
539                                         {
540                                             $objectInSaleStage = false;
541                                             foreach ($value as $internalKey => $internalValue)
542                                             {
543                                                 if ($internalValue[$drill_down] == $superSetValue)
544                                                 {
545                                                     $objectInSaleStage = $value[$internalKey];
546                                                 }
547                                             }
548
549                                             if ($objectInSaleStage)
550                                             {
551                                                 if (isset($objectInSaleStage[$group_by]) && $objectInSaleStage[$group_by] == $groupByKey)
552                                                 {
553                                                     if ($drill_down == 'user_name')
554                                                     {
555                                                         $drill_down_param = '&assigned_user_id[]=' . urlencode($objectInSaleStage['assigned_user_id']);
556                                                     } 
557                                                     else if ($drill_down == 'm')
558                                                     {
559                                                         $drill_down_param = '&date_closed_advanced=' . urlencode($objectInSaleStage[$drill_down]);
560                                                     } 
561                                                     else
562                                                     {
563                                                         $paramValue = (isset($objectInSaleStage[$drill_down . "_dom_option"]) && $objectInSaleStage[$drill_down . "_dom_option"] != '') ? $objectInSaleStage[$drill_down . "_dom_option"] : $objectInSaleStage[$drill_down];
564                                                         $drill_down_param = '&' . $drill_down . '=' . urlencode($paramValue);
565                                                     }
566
567                                                     if ($this->is_currency)
568                                                     {
569                                                         $sub_amount = $this->formatNumber($this->convertCurrency($objectInSaleStage['total']));
570                                                         $sub_amount_formatted = $this->currency_symbol . $sub_amount . 'K';
571                                                         //bug: 38877 - do not format the amount for the value as it breaks the chart
572                                                         $sub_amount = $this->convertCurrency($objectInSaleStage['total']);
573                                                     } 
574                                                     else
575                                                     {
576                                                         $sub_amount = $objectInSaleStage['total'];
577                                                         $sub_amount_formatted = $sub_amount;
578                                                     }
579
580                                                     $data .= $this->processDataGroup(4, $objectInSaleStage[$drill_down], $sub_amount, $sub_amount_formatted, $url . $drill_down_param);
581                                                 } 
582                                                 else
583                                                 {
584                                                     $data .= $this->nullGroup($superSetValue, $url);
585                                                 }
586                                             } 
587                                             else
588                                             {
589                                                 $data .= $this->nullGroup($superSetValue, $url);
590                                             }
591                                         }
592                         }
593
594                         $data .= $this->tab('</subgroups>',3);
595                         $data .= $this->tab('</group>',2);
596                 }
597                 return $data;
598         }
599     
600         public function nullGroup($superSetValue, $url) {
601             return $this->processDataGroup(4, $superSetValue, 'NULL', '', $url);
602         }
603     
604     /**
605      * returns a name for the XML File
606      *
607      * @param string $file_id - unique id to make part of the file name
608      */
609     public static function getXMLFileName(
610          $file_id
611          )
612     {
613         global $sugar_config, $current_user;
614
615         $filename = $sugar_config['tmp_dir']. $current_user->id . '_' . $file_id . '.xml';
616
617         if ( !is_dir(dirname($filename)) )
618             create_cache_directory(str_ireplace($GLOBALS['sugar_config']['cache_dir'],"",$filename));
619
620         return $filename;
621     }
622     
623     public function processXmlData(){
624         $data = '';
625         
626                 if ($this->chart_properties['type'] == 'group by chart'){
627                         $data .= $this->xmlDataForGroupByChart();
628                 }
629                 else if ($this->chart_properties['type'] == 'bar chart' || $this->chart_properties['type'] == 'horizontal bar chart'){
630                         $data .= $this->xmlDataBarChart();                      
631                 }
632                 else{
633                         $data .= $this->xmlDataGenericChart();
634                 }               
635
636                 return $data;
637     }
638      
639         function xmlData(){
640                 $data = $this->tab('<data>',1);
641                 $data .= $this->processXmlData();       
642                 $data .= $this->tab('</data>',1);
643                 
644                 return $data;
645         }
646         
647         /**
648      * function to generate XML and return it
649          * 
650      * @param   none
651      * @return  string $xmlContents with xml information
652      */         
653         function generateXML($xmlDataName = false){
654                 $xmlContents = $this->xmlHeader();              
655                 $xmlContents .= $this->xmlProperties();         
656                 $xmlContents .= $this->xmlData();
657                 $xmlContents .= $this->xmlYAxis();      
658                 $xmlContents .= $this->xmlFooter();
659                 
660                 return $xmlContents;
661         }
662
663         /**
664      * function to save XML contents into a file
665          * 
666      * @param   string $xmlFilename location of the xml file
667          *                      string $xmlContents contents of the xml file
668      * @return  string boolean denoting whether save has failed
669      */                 
670         function saveXMLFile($xmlFilename,$xmlContents) {
671                 global $app_strings;
672                 global $locale;
673                 
674                 $xmlContents = chr(255).chr(254).mb_convert_encoding($xmlContents, 'UTF-16LE', 'UTF-8'); 
675                 
676                 // open file
677                 if (!$fh = sugar_fopen($xmlFilename, 'w')) {
678                         $GLOBALS['log']->debug("Cannot open file ($xmlFilename)");
679                         return;
680                 }
681                 
682                 // write the contents to the file
683                 if (fwrite($fh,$xmlContents) === FALSE) {
684                         $GLOBALS['log']->debug("Cannot write to file ($xmlFilename)");
685                         return false;
686                 }
687         
688                 $GLOBALS['log']->debug("Success, wrote ($xmlContents) to file ($xmlFilename)");
689         
690                 fclose($fh);
691                 return true;
692         }
693
694         /**
695      * generates xml file for Flash charts to use for internationalized instances
696          * 
697      * @param   string $xmlFile location of the XML file to write to
698      * @return  none
699      */ 
700         function generateChartStrings($xmlFile){
701                 global $current_language, $app_list_strings;
702                 
703                 $chartStringsXML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
704                 $chartStringsXML .= "<sugarlanguage version=\"1.0\">\n";
705                 $chartStringsXML .= $this->tab("<charts>",1);
706                 
707                 if (empty($app_list_strings)) {
708                     //set module and application string arrays based upon selected language
709                         $app_list_strings = return_app_list_strings_language($current_language);
710                 }
711                 
712                 // retrieve the strings defined at include/language/en_us.lang.php
713                 foreach ($app_list_strings['chart_strings'] as $tag => $chart_string){
714                         $chartStringsXML .= $this->tab("<$tag>$chart_string</$tag>",2);
715                 }               
716                 
717                 $chartStringsXML .= $this->tab("</charts>",1);
718                 $chartStringsXML .= "</sugarlanguage>\n";
719                 
720                 $this->saveXMLFile($xmlFile, $chartStringsXML);
721         }
722         
723         /**
724      * wrapper function to return the html code containing the chart in a div
725          * 
726      * @param   string $name    name of the div
727          *                      string $xmlFile location of the XML file
728          *                      string $style   optional additional styles for the div
729      * @return  string returns the html code through smarty
730      */                                 
731         function display($name, $xmlFile, $width='320', $height='480', $resize=false){
732                 
733                 
734                 // generate strings for chart if it does not exist
735                 global $current_language, $theme, $sugar_config,$app_strings;
736                 
737                 $this->app_strings = $app_strings;
738                 $this->chartStringsXML = $GLOBALS['sugar_config']['tmp_dir'].'chart_strings.' . $current_language .'.lang.xml';
739                 if (!file_exists($this->chartStringsXML)){
740                         $this->generateChartStrings($this->chartStringsXML);
741                 }
742                                 
743                 $templateFile = "";                     
744                 return $templateFile;
745         }
746
747         function getDashletScript($id,$xmlFile="") {
748                 
749         $xmlFile = (!$xmlFile) ? $sugar_config['tmp_dir']. $current_user->id . '_' . $this->id . '.xml' : $xmlFile;
750         $chartStringsXML = $GLOBALS['sugar_config']['tmp_dir'].'chart_strings.' . $current_language .'.lang.xml'; 
751         
752         $this->ss->assign('chartName', $id);
753     $this->ss->assign('chartXMLFile', $xmlFile);
754     $this->ss->assign('chartStyleCSS', SugarThemeRegistry::current()->getCSSURL('chart.css'));
755     $this->ss->assign('chartColorsXML', SugarThemeRegistry::current()->getImageURL('sugarColors.xml'));
756     $this->ss->assign('chartLangFile', $GLOBALS['sugar_config']['tmp_dir'].'chart_strings.' . $GLOBALS['current_language'] .'.lang.xml');
757                 
758                 $templateFile = "";
759                 return $templateFile;
760         }
761         
762         
763   /**
764          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.
765          If the data quantity is large, it maybe a little slow.
766     * @param         array $data_set           The data get from database
767                            string $keycolname1      We will sort by this key first
768                            bool $translate1            Whether to trabslate the first column
769                            string $keycolname1      We will sort by this key secondly, and  it can be null, then it will only sort by the first column.
770                            bool $translate1            Whether to trabslate the second column
771                            bool $ifsort2                 Whether to sort by the second column or just translate the second column.
772     * @return        The sorted and translated data.
773    */
774     function sortData($data_set, $keycolname1=null, $translate1=false, $keycolname2=null, $translate2=false, $ifsort2=false) {
775         //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.
776         global $app_list_strings;
777         $sortby1[] = array();
778         foreach ($data_set as $row) {
779             $sortby1[]  = $row[$keycolname1];
780         }
781         $sortby1 = array_unique($sortby1);
782         //The data is from the database, the sorting should be done in the sql. So I will not do the sort here.
783         if($translate1) {
784             $temp_sortby1 = array();
785             foreach(array_keys($app_list_strings[$keycolname1.'_dom']) as $sortby1_value) {
786                 if(in_array($sortby1_value, $sortby1)) {
787                     $temp_sortby1[] = $sortby1_value;
788                 }
789             }
790             $sortby1 = $temp_sortby1;
791         }
792         
793         //if(isset($sortby1[0]) && $sortby1[0]=='') unset($sortby1[0]);//the beginning of lead_source_dom is blank.
794         if(isset($sortby1[0]) && $sortby1[0]==array()) unset($sortby1[0]);//the beginning of month after search is blank.
795         
796         if($ifsort2==false) $sortby2=array(0);
797         
798         if($keycolname2!=null) {
799             $sortby2 = array();
800             foreach ($data_set as $row) {
801                 $sortby2[]  = $row[$keycolname2];
802             }
803             //The data is from the database, the sorting should be done in the sql. So I will not do the sort here.
804             $sortby2 = array_unique($sortby2);
805             if($translate2) {
806                 $temp_sortby2 = array();
807                 foreach(array_keys($app_list_strings[$keycolname2.'_dom']) as $sortby2_value) {
808                     if(in_array($sortby2_value, $sortby2)) {
809                         $temp_sortby2[] = $sortby2_value;
810                     }
811                 }
812                 $sortby2 = $temp_sortby2;
813             }
814         }
815         
816         $data=array();
817
818         foreach($sortby1 as $sort1) {
819             foreach($sortby2 as $sort2) {
820                 if($ifsort2) $a=0;
821                 foreach($data_set as $key => $value){
822                     if($value[$keycolname1] == $sort1 && (!$ifsort2 || $value[$keycolname2]== $sort2)) {
823                         if($translate1) {
824                             $value[$keycolname1.'_dom_option'] = $value[$keycolname1];
825                             $value[$keycolname1] = $app_list_strings[$keycolname1.'_dom'][$value[$keycolname1]];
826                         }
827                         if($translate2) {
828                             $value[$keycolname2.'_dom_option'] = $value[$keycolname2];
829                             $value[$keycolname2] = $app_list_strings[$keycolname2.'_dom'][$value[$keycolname2]];
830                         }
831                         array_push($data, $value);
832                         unset($data_set[$key]);
833                         $a=1;
834                         }
835                 }
836                 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.
837                     $val=array();
838                     $val['total'] = 0;
839                     $val['count'] = 0;
840                     if($translate1) {
841                         $val[$keycolname1] = $app_list_strings[$keycolname1.'_dom'][$sort1];
842                         $val[$keycolname1.'_dom_option'] = $sort1;
843                     }
844                     else {
845                         $val[$keycolname1] = $sort1;                    
846                     }
847                     if($translate2) {
848                         $val[$keycolname2] = $app_list_strings[$keycolname2.'_dom'][$sort2];
849                         $val[$keycolname2.'_dom_option'] = $sort2;                    
850                     }
851                     elseif($keycolname2!=null) {
852                         $val[$keycolname2] = $sort2;
853                     }
854                     array_push($data, $val);
855                 }
856             }  
857         }
858         return $data;
859     }
860     
861     function getChartResources() {
862                 
863                 $resources = "";
864                 return $resources;
865         }
866         
867         function getMySugarChartResources() {
868                 
869                 $mySugarRources = "";
870                 return $mySugarResources;
871         }
872         
873         /**
874      * wrapper function to return chart array after any additional processing
875          * 
876      * @param   array $chartsArray      array of chart config items that need processing
877      * @return  array $chartArray after it has been process
878      */
879         function chartArray($chartsArray) {
880
881                 return $chartsArray;
882         }
883
884 } // end class def