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