]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - include/SugarCharts/SugarChart.php
Release 6.5.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-2012 SugarCRM Inc.
6  * 
7  * This program is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU Affero General Public License version 3 as published by the
9  * Free Software Foundation with the addition of the following permission added
10  * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
11  * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
12  * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
13  * 
14  * This program is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16  * FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
17  * details.
18  * 
19  * You should have received a copy of the GNU Affero General Public License along with
20  * this program; if not, see http://www.gnu.org/licenses or write to the Free
21  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22  * 02110-1301 USA.
23  * 
24  * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
25  * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
26  * 
27  * The interactive user interfaces in modified source and object code versions
28  * of this program must display Appropriate Legal Notices, as required under
29  * Section 5 of the GNU Affero General Public License version 3.
30  * 
31  * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
32  * these Appropriate Legal Notices must retain the display of the "Powered by
33  * SugarCRM" logo. If the display of the logo is not reasonably feasible for
34  * technical reasons, the Appropriate Legal Notices must display the words
35  * "Powered by SugarCRM".
36  ********************************************************************************/
37
38
39 /**
40  * Generic chart
41  * @api
42  */
43 class SugarChart {
44
45         private $db;
46         protected $ss;
47         var $forceHideDataGroupLink = false;
48         var $data_set = array();
49         var $display_data = array();
50         var $chart_properties = array();
51         var $chart_yAxis = array();
52         var $group_by = array();
53         var $super_set = array();
54         var $colors_list = array();
55         var $base_url = array();
56         var $url_params = array();
57
58         var $currency_symbol;
59         var $thousands_symbol;
60         var $is_currency;
61         var $supports_image_export = false;
62         var $print_html_legend_pdf = false;
63         var $image_export_type = "";
64
65         public function __construct() {
66                 $this->db = &DBManagerFactory::getInstance();
67                 $this->ss = new Sugar_Smarty();
68
69                 $this->chart_yAxis['yMin'] = 0;
70                 $this->chart_yAxis['yMax'] = 0;
71
72
73                 if ($GLOBALS['current_user']->getPreference('currency')){
74
75             $currency = new Currency();
76             $currency->retrieve($GLOBALS['current_user']->getPreference('currency'));
77             $this->div = $currency->conversion_rate;
78             $this->currency_symbol = $currency->symbol;
79         }
80         else{
81                 $this->currency_symbol = $GLOBALS['sugar_config']['default_currency_symbol'];
82                         $this->div = 1;
83                         $this->is_currency = false;
84         }
85         $this->image_export_type = (extension_loaded('gd') && function_exists('gd_info')) ? "png" : "jpg";
86         }
87
88         function getData($query){
89                 $result = $this->db->query($query);
90
91                 $row = $this->db->fetchByAssoc($result);
92
93                 while ($row != null){
94                         $this->data_set[] = $row;
95                         $row = $this->db->fetchByAssoc($result);
96                 }
97         }
98
99         function constructBaseURL(){
100                 $numParams = 0;
101                 $url = 'index.php?';
102
103                 foreach ($this->base_url as $param => $value){
104                         if ($numParams == 0){
105                                 $url .= $param . '=' . $value;
106                         }
107                         else{
108                                 $url .= '&' .$param . '=' .$value;
109                         }
110                         $numParams++;
111                 }
112
113                 return $url;
114         }
115
116         function constructURL(){
117                 $url = $this->constructBaseURL();
118                 foreach ($this->url_params as $param => $value){
119                         if ($param == 'assigned_user_id') $param = 'assigned_user_id[]';
120                         if (is_array($value)){
121                                 foreach($value as $multiple){
122                                         $url .= '&' . $param . '=' . urlencode($multiple);
123                                 }
124                         }
125                         else{
126                                 $url .= '&' . $param . '=' . urlencode($value);
127                         }
128                 }
129                 return $url;
130         }
131
132         function setData($dataSet){
133                 $this->data_set = $dataSet;
134         }
135
136     function setProperties($title, $subtitle, $type, $legend='on', $labels='value', $print='on', $thousands = false)
137     {
138         $this->chart_properties['title'] = $title;
139         $this->chart_properties['subtitle'] = $subtitle;
140         $this->chart_properties['type'] = $type;
141         $this->chart_properties['legend'] = $legend;
142         $this->chart_properties['labels'] = $labels;
143         $this->chart_properties['thousands'] = $thousands;
144     }
145
146         function setDisplayProperty($property, $value){
147                 $this->chart_properties[$property] = $value;
148         }
149
150         function setColors($colors = array()){
151                 $this->colors_list = $colors;
152         }
153
154     /**
155      * returns the header for the constructed xml file for sugarcharts
156          *
157      * @param   nothing
158      * @return  string $header XML header
159      */
160         function xmlHeader(){
161                 $header = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
162                 $header .= "<sugarcharts version=\"1.0\">\n";
163
164                 return $header;
165         }
166
167         /**
168      * returns the footer for the constructed xml file for sugarcharts
169          *
170      * @param   nothing
171      * @return  string $footer XML footer
172      */
173         function xmlFooter(){
174                 $footer = "</sugarcharts>";
175
176                 return $footer;
177         }
178
179         /**
180      * returns the properties tag for the constructed xml file for sugarcharts
181          *
182      * @param   nothing
183      * @return  string $properties XML properties tag
184      */
185         function xmlProperties(){
186                 // open the properties tag
187                 $properties = $this->tab("<properties>",1);
188
189                 // grab the property and value from the chart_properties variable
190                 foreach ($this->chart_properties as $key => $value){
191                         $properties .= $this->tab("<$key>$value</$key>",2);
192                 }
193
194                 if (!empty($this->colors_list)){
195                         // open the colors tag
196                         $properties .= $this->tab("<colors>",2);
197                         foreach ($this->colors_list as $color){
198                                 $properties .= $this->tab("<color>$color</color>",3);
199                         }
200
201                         // close the colors tag
202                         $properties .= $this->tab("</colors>",2);
203                 }
204
205                 // close the properties tag
206                 $properties .= $this->tab("</properties>",1);
207
208                 return $properties;
209         }
210
211         /**
212      * returns the y-axis values for the chart
213          *
214      * @param   nothing
215      * @return  string $yAxis XML yAxis tag
216      */
217         function xmlYAxis(){
218                 $this->chart_yAxis['yStep'] = '100';
219                 $this->chart_yAxis['yLog'] = '1';
220                 $this->chart_yAxis['yMax'] = $this->is_currency ? $this->convertCurrency($this->chart_yAxis['yMax']) : $this->chart_yAxis['yMax'];
221                 $max = $this->chart_yAxis['yMax'];
222                 $exp = ($max == 0) ? 1 : floor(log10($max));
223                 $baseval = $max / pow(10, $exp);
224
225                 // steps will be 10^n, 2*10^n, 5*10^n (where n >= 0)
226                 if ($baseval > 0 && $baseval <= 1){
227                         $step = 2 * pow(10, $exp-1);
228                 }
229                 else if ($baseval > 1 && $baseval <= 3){
230                         $step = 5 * pow(10, $exp-1);
231                 }
232                 else if ($baseval > 3 && $baseval <= 6){
233                         $step = 10 * pow(10, $exp-1);
234                 }
235                 else if ($baseval > 6 && $baseval <= 10){
236                         $step = 20 * pow(10, $exp-1);
237                 }
238
239                 // edge cases for values less than 10
240                 if ($max == 0 || $step < 1){
241                         $step = 1;
242                 }
243
244                 $this->chart_yAxis['yStep'] = $step;
245
246                 // to compensate, the yMax should be at least one step above the max value
247                         $this->chart_yAxis['yMax'] += $this->chart_yAxis['yStep'];
248
249                 $yAxis = $this->tab("<yAxis>" ,1);
250
251                 foreach ($this->chart_yAxis as $key => $value){
252                         $yAxis .= $this->tabValue("{$key}",$value, 2);
253                 }
254
255                 $yAxis .= $this->tab("</yAxis>" ,1);
256
257                 return $yAxis;
258         }
259
260         /**
261      * returns the total amount value for the group by field
262          *
263      * @param   group by field
264      * @return  int $total total value
265      */
266         function calculateTotal($group_by){
267                 $total = 0;
268
269                 for($i =0; $i < count($this->data_set); $i++){
270                         if ($this->data_set[$i][$this->group_by[0]] == $group_by){
271                                 $total += $this->data_set[$i]['total'];
272                         }
273                 }
274                 return $total;
275         }
276
277         /**
278      * returns text with tabs appended before it
279          *
280      * @param   string $str input string
281          *                      int $depth number of times to tab
282      * @return  string with tabs appended before it
283      */
284         function tab($str, $depth){
285                 return str_repeat("\t", $depth) . $str . "\n";
286         }
287         /**
288      * returns text with tabs appended before it
289          *
290      * @param   string $str xml tag
291                         int $tagFormat 2 = open and close tag, 1 = close, 0 = open
292                         sting $value input string
293          *                      int $depth number of times to tab
294      * @return  string with tabs appended before it
295      */
296
297         function tabValue($tag,$value,$depth) {
298
299                         return $this->tab("<{$tag}>".htmlspecialchars($value,ENT_QUOTES)."</{$tag}>",$depth);
300
301         }
302         /**
303      * returns xml data format
304          *
305      * @param   none
306      * @return  string with xml data format
307      */
308         function processData(){
309                 $data = array();
310
311                 $group_by = $this->group_by[0];
312                 if (isset($this->group_by[1])){
313                         $drill_down = $this->group_by[1];
314                 }
315
316                 $prev_group_by = '';
317
318                 for($i =0; $i < count($this->data_set); $i++){
319                         if ($this->data_set[$i][$group_by] != $prev_group_by){
320                                 $prev_group_by = $this->data_set[$i][$group_by];
321                                 $data[$this->data_set[$i][$group_by]] = array();
322                         }
323
324             $data[$this->data_set[$i][$group_by]][] = $this->data_set[$i];
325
326                         // push new item onto legend items list
327                         if (isset($drill_down)){
328                                 if (!in_array($this->data_set[$i][$drill_down], $this->super_set)){
329                                         $this->super_set[] = $this->data_set[$i][$drill_down];
330                                 }
331                         }
332                 }
333
334                 return $data;
335         }
336
337         function processDataGroup($tablevel, $title, $value, $label, $link){
338                 $link = $this->forceHideDataGroupLink ? '' : $link;
339                 $data = $this->tab('<group>',$tablevel);
340                 $data .= $this->tabValue('title',$title,$tablevel+1);
341                 $data .= $this->tabValue('value',$value,$tablevel+1);
342                 $data .= $this->tabValue('label',$label,$tablevel+1);
343                 $data .= $this->tab('<link>' . $link . '</link>',$tablevel+1);
344                 $data .= $this->tab('</group>',$tablevel);
345                 return $data;
346         }
347
348         function calculateGroupByTotal($dataset){
349                 $total = 0;
350
351                 foreach ($dataset as $key => $value){
352                         $total += $value;
353                 }
354
355                 return $total;
356         }
357
358         function calculateSingleBarMax($dataset){
359                 $max = 0;
360                 foreach ($dataset as $value){
361                         if ($value > $max){
362                                 $max = $value;
363                         }
364                 }
365
366                 return $max;
367         }
368
369         /**
370      * returns correct yAxis min/max
371          *
372      * @param   value to check
373      * @return  yAxis min and max
374      */
375         function checkYAxis($value){
376                 if ($value < $this->chart_yAxis['yMin']){
377                         $this->chart_yAxis['yMin'] = $value;
378                 }
379                 else if ($value > $this->chart_yAxis['yMax']){
380                         $this->chart_yAxis['yMax'] = $value;
381                 }
382         }
383
384
385         function convertCurrency($to_convert){
386                 global $locale;
387                 $decimals = '2';
388                 $decimals = $locale->getPrecision();
389                 $amount = ($this->div == 1) ? $to_convert : round($to_convert * $this->div,$decimals);
390
391                 return $amount;
392         }
393
394         function formatNumber($number, $decimals= null, $decimal_point= null, $thousands_sep= null){
395                 global $locale;
396                 if(is_null($decimals)) {
397                         $decimals = $locale->getPrecision();
398                 }
399                 $seps = get_number_seperators();
400                 $thousands_sep = $seps[0];
401                 $decimal_point = $seps[1];
402                 return number_format($number, $decimals, $decimal_point, $thousands_sep);
403         }
404
405         function getTotal(){
406                 $new_data = $this->processData();
407                 $total = 0;
408                 foreach ($new_data as $groupByKey => $value){
409                         $total += $this->calculateTotal($groupByKey);
410                 }
411
412                 return $total;
413         }
414
415         function xmlDataForGroupByChart(){
416                 $data = '';
417                 foreach ($this->data_set as $key => $value){
418                         $amount = $this->is_currency ? $this->convertCurrency($this->calculateGroupByTotal($value)) : $this->calculateGroupByTotal($value);
419             $label = $this->is_currency ? ($this->currency_symbol . $this->formatNumber($amount)) : $amount;
420
421                         $data .= $this->tab('<group>',2);
422                         $data .= $this->tabValue('title',$key,3);
423                         $data .= $this->tabValue('value',$amount,3);
424                         $data .= $this->tabValue('label',$label,3);
425                         $data .= $this->tab('<link></link>',3);
426                         $data .= $this->tab('<subgroups>',3);
427
428                         foreach ($value as $k => $v){
429                 $amount = $this->is_currency ? $this->convertCurrency($v) : $v;
430                 $label = $this->is_currency ? ($this->currency_symbol . $this->formatNumber($amount)) : $amount;
431
432                                 $data .= $this->tab('<group>',4);
433                                 $data .= $this->tabValue('title',$k,5);
434                                 $data .= $this->tabValue('value',$amount,5);
435                                 $data .= $this->tabValue('label',$label,5);
436                                 $data .= $this->tab('<link></link>',5);
437                                 $data .= $this->tab('</group>',4);
438                                 $this->checkYAxis($v);
439                         }
440                         $data .= $this->tab('</subgroups>',3);
441                         $data .= $this->tab('</group>',2);
442                 }
443
444                 return $data;
445         }
446
447         function xmlDataForGaugeChart(){
448                 $data = '';
449                 $gaugePosition = $this->data_set[0]['num'];
450                 $this->chart_yAxis['yMax'] = $this->chart_properties['gaugeTarget'];
451                 $this->chart_yAxis['yStep'] = 1;
452                 $data .= $this->processDataGroup(2, 'GaugePosition', $gaugePosition, $gaugePosition, '');
453                 $data .= $this->processGauge($gaugePosition, $this->chart_properties['gaugeTarget']);
454
455                 return $data;
456         }
457
458         function xmlDataBarChart(){
459                 $data = '';
460                 $max = $this->calculateSingleBarMax($this->data_set);
461                 $this->checkYAxis($max);
462
463                 if (isset($this->group_by[0])){
464                         $group_by = $this->group_by[0];
465                         if (isset($this->group_by[1])){
466                                 $drill_down = $this->group_by[1];
467                         }
468                 }
469
470                 foreach ($this->data_set as $key => $value){
471                         if ($this->is_currency){
472                                 $value = $this->convertCurrency($value);
473                                 $label = $this->currency_symbol;
474                                 $label .= $this->formatNumber($value);
475                                 $label .= $this->thousands_symbol;
476                         }
477                         else{
478                                 $label = $value;
479                         }
480
481                         $data .= $this->tab('<group>', 2);
482                         $data .= $this->tabValue('title',$key, 3);
483                         $data .= $this->tabValue('value',$value, 3);
484                         $data .= $this->tabValue('label',$label, 3);
485                         if (isset($drill_down) && $drill_down){
486                                 if ($this->group_by[0] == 'm') {
487                     $additional_param = '&date_closed_advanced=' . urlencode($key);
488                                 } else if ( $this->group_by[0] == 'sales_stage' ) {
489                     $additional_param = '&sales_stage_advanced[]='.urlencode(array_search($key,$GLOBALS['app_list_strings']['sales_stage_dom']));
490                 } else{
491                                         $additional_param = "&" . $this->group_by[0] . "=" . urlencode($key);
492                                 }
493                                 $url = $this->constructURL() . $additional_param;
494
495                                 $data .= $this->tab('<link>' . $url . '</link>', 3);
496                         }
497                         $data .= $this->tab('<subgroups>', 3);
498                         $data .= $this->tab('</subgroups>', 3);
499                         $data .= $this->tab('</group>', 2);
500                 }
501                 return $data;
502         }
503
504         function xmlDataGenericChart(){
505                 $data = '';
506                 $group_by = $this->group_by[0];
507                 if (isset($this->group_by[1])){
508                         $drill_down = $this->group_by[1];
509                 }
510                 $new_data = $this->processData();
511
512                 foreach ($new_data as $groupByKey => $value){
513                         $total = $this->calculateTotal($groupByKey);
514                         $this->checkYAxis($total);
515
516                         if ($this->group_by[0] == 'm'){
517                                 $additional_param = '&date_closed_advanced=' . urlencode($groupByKey);
518                         }
519                         else{
520                                 $paramValue = (isset($value[0]['key']) && $value[0]['key'] != '') ? $value[0]['key'] : $groupByKey;
521                                 $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;
522                                 $additional_param = "&" . $this->group_by[0] . "=" . urlencode($paramValue);
523                         }
524
525                         $url = $this->constructURL() . $additional_param;
526
527                         $amount = $this->is_currency ? $this->convertCurrency($total) : $total;
528                         $label = $this->is_currency ? ($this->currency_symbol . $this->formatNumber($amount) . 'K') : $amount;
529
530                         $data .= $this->tab('<group>',2);
531                         $data .= $this->tabValue('title',$groupByKey,3);
532                         $data .= $this->tabValue('value',$amount,3);
533                         $data .= $this->tabValue('label',$label,3);
534                         $data .= $this->tab('<link>' . $url . '</link>',3);
535
536                         $data .= $this->tab('<subgroups>',3);
537                         $processed = array();
538
539                         if (isset($drill_down) && $drill_down != ''){
540                 /*
541                 * Bug 44696 - Ivan D.
542                 * We have to iterate users in order since they are in the super_set for every group.
543                 * This is required to display the correct links for each user in a drill down chart.
544                 */
545                 foreach ($this->super_set as $superSetKey => $superSetValue)
546                 {
547                     $objectInSaleStage = false;
548                     foreach ($value as $internalKey => $internalValue)
549                     {
550                         if ($internalValue[$drill_down] == $superSetValue)
551                         {
552                             $objectInSaleStage = $value[$internalKey];
553                         }
554                     }
555
556                     if ($objectInSaleStage)
557                     {
558                         if (isset($objectInSaleStage[$group_by]) && $objectInSaleStage[$group_by] == $groupByKey)
559                         {
560                             if ($drill_down == 'user_name')
561                             {
562                                 $drill_down_param = '&assigned_user_id[]=' . urlencode($objectInSaleStage['assigned_user_id']);
563                             }
564                             else if ($drill_down == 'm')
565                             {
566                                 $drill_down_param = '&date_closed_advanced=' . urlencode($objectInSaleStage[$drill_down]);
567                             }
568                             else
569                             {
570                                 $paramValue = (isset($objectInSaleStage[$drill_down . "_dom_option"]) && $objectInSaleStage[$drill_down . "_dom_option"] != '') ? $objectInSaleStage[$drill_down . "_dom_option"] : $objectInSaleStage[$drill_down];
571                                 $drill_down_param = '&' . $drill_down . '=' . urlencode($paramValue);
572                             }
573
574                             if ($this->is_currency)
575                             {
576                                 $sub_amount = $this->formatNumber($this->convertCurrency($objectInSaleStage['total']));
577                                 $sub_amount_formatted = $this->currency_symbol . $sub_amount . 'K';
578                                 //bug: 38877 - do not format the amount for the value as it breaks the chart
579                                 $sub_amount = $this->convertCurrency($objectInSaleStage['total']);
580                             }
581                             else
582                             {
583                                 $sub_amount = $objectInSaleStage['total'];
584                                 $sub_amount_formatted = $sub_amount;
585                             }
586
587                             $data .= $this->processDataGroup(4, $objectInSaleStage[$drill_down], $sub_amount, $sub_amount_formatted, $url . $drill_down_param);
588                         }
589                         else
590                         {
591                             $data .= $this->nullGroup($superSetValue, $url);
592                         }
593
594                     }
595                     else
596                     {
597                         $data .= $this->nullGroup($superSetValue, $url);
598                     }
599                 }
600                         }
601
602                         $data .= $this->tab('</subgroups>',3);
603                         $data .= $this->tab('</group>',2);
604                 }
605                 return $data;
606         }
607
608
609     /**
610      * nullGroup
611      * This function sets a null group by clause
612      *
613      * @param $sugarSetValue Mixed value
614      * @param $url String value of URL for the link
615      */
616     private function nullGroup($superSetValue, $url) {
617         return $this->processDataGroup(4, $superSetValue, 'NULL', '', $url);
618     }
619
620
621     /**
622      * returns a name for the XML File
623      *
624      * @param string $file_id - unique id to make part of the file name
625      */
626     public static function getXMLFileName(
627          $file_id
628          )
629     {
630         global $sugar_config, $current_user;
631
632         $filename = sugar_cached("xml/"). $current_user->id . '_' . $file_id . '.xml';
633
634         if ( !is_dir(dirname($filename)) ) {
635             create_cache_directory("xml");
636         }
637
638         return $filename;
639     }
640
641     public function processXmlData(){
642         $data = '';
643
644                 if ($this->chart_properties['type'] == 'group by chart'){
645                         $data .= $this->xmlDataForGroupByChart();
646                 }
647                 else if ($this->chart_properties['type'] == 'bar chart' || $this->chart_properties['type'] == 'horizontal bar chart'){
648                         $data .= $this->xmlDataBarChart();
649                 }
650                 else{
651                         $data .= $this->xmlDataGenericChart();
652                 }
653
654                 return $data;
655     }
656
657         function xmlData(){
658                 $data = $this->tab('<data>',1);
659                 $data .= $this->processXmlData();
660                 $data .= $this->tab('</data>',1);
661
662                 return $data;
663         }
664
665         /**
666      * function to generate XML and return it
667          *
668      * @param   none
669      * @return  string $xmlContents with xml information
670      */
671         function generateXML($xmlDataName = false){
672                 $xmlContents = $this->xmlHeader();
673                 $xmlContents .= $this->xmlProperties();
674                 $xmlContents .= $this->xmlData();
675                 $xmlContents .= $this->xmlYAxis();
676                 $xmlContents .= $this->xmlFooter();
677
678                 return $xmlContents;
679         }
680
681         /**
682      * function to save XML contents into a file
683          *
684      * @param   string $xmlFilename location of the xml file
685          *                      string $xmlContents contents of the xml file
686      * @return  string boolean denoting whether save has failed
687      */
688         function saveXMLFile($xmlFilename,$xmlContents) {
689                 global $app_strings;
690                 global $locale;
691
692                 $xmlContents = chr(255).chr(254).$GLOBALS['locale']->translateCharset($xmlContents, 'UTF-8', 'UTF-16LE');
693
694                 // open file
695                 if (!$fh = sugar_fopen($xmlFilename, 'w')) {
696                         $GLOBALS['log']->debug("Cannot open file ($xmlFilename)");
697                         return;
698                 }
699
700                 // write the contents to the file
701                 if (fwrite($fh,$xmlContents) === FALSE) {
702                         $GLOBALS['log']->debug("Cannot write to file ($xmlFilename)");
703                         return false;
704                 }
705
706                 $GLOBALS['log']->debug("Success, wrote ($xmlContents) to file ($xmlFilename)");
707
708                 fclose($fh);
709                 return true;
710         }
711
712         /**
713      * generates xml file for Flash charts to use for internationalized instances
714          *
715      * @param   string $xmlFile location of the XML file to write to
716      * @return  none
717      */
718         function generateChartStrings($xmlFile){
719                 global $current_language, $app_list_strings;
720
721                 $chartStringsXML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
722                 $chartStringsXML .= "<sugarlanguage version=\"1.0\">\n";
723                 $chartStringsXML .= $this->tab("<charts>",1);
724
725                 if (empty($app_list_strings)) {
726                     //set module and application string arrays based upon selected language
727                         $app_list_strings = return_app_list_strings_language($current_language);
728                 }
729
730                 // retrieve the strings defined at include/language/en_us.lang.php
731                 foreach ($app_list_strings['chart_strings'] as $tag => $chart_string){
732                         $chartStringsXML .= $this->tab("<$tag>$chart_string</$tag>",2);
733                 }
734
735                 $chartStringsXML .= $this->tab("</charts>",1);
736                 $chartStringsXML .= "</sugarlanguage>\n";
737
738                 $this->saveXMLFile($xmlFile, $chartStringsXML);
739         }
740
741         /**
742      * wrapper function to return the html code containing the chart in a div
743          *
744      * @param   string $name    name of the div
745          *                      string $xmlFile location of the XML file
746          *                      string $style   optional additional styles for the div
747      * @return  string returns the html code through smarty
748      */
749         function display($name, $xmlFile, $width='320', $height='480', $resize=false){
750
751
752                 // generate strings for chart if it does not exist
753                 global $current_language, $theme, $sugar_config,$app_strings;
754
755                 $this->app_strings = $app_strings;
756                 $this->chartStringsXML = sugar_cached("xml/").'chart_strings.' . $current_language .'.lang.xml';
757                 if (!file_exists($this->chartStringsXML)){
758                         $this->generateChartStrings($this->chartStringsXML);
759                 }
760
761                 $templateFile = "";
762                 return $templateFile;
763         }
764
765                 
766         function getDashletScript($id,$xmlFile="") {
767
768         $xmlFile = (!$xmlFile) ? $sugar_config['tmp_dir']. $current_user->id . '_' . $this->id . '.xml' : $xmlFile;
769         $chartStringsXML = $GLOBALS['sugar_config']['tmp_dir'].'chart_strings.' . $current_language .'.lang.xml';
770
771         $this->ss->assign('chartName', $id);
772     $this->ss->assign('chartXMLFile', $xmlFile);
773     $this->ss->assign('chartStyleCSS', SugarThemeRegistry::current()->getCSSURL('chart.css'));
774     $this->ss->assign('chartColorsXML', SugarThemeRegistry::current()->getImageURL('sugarColors.xml'));
775     $this->ss->assign('chartLangFile', $GLOBALS['sugar_config']['tmp_dir'].'chart_strings.' . $GLOBALS['current_language'] .'.lang.xml');
776
777                 $templateFile = "";
778                 return $templateFile;
779         }
780
781
782   /**
783          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.
784          If the data quantity is large, it maybe a little slow.
785     * @param         array $data_set           The data get from database
786                            string $keycolname1      We will sort by this key first
787                            bool $translate1            Whether to trabslate the first column
788                            string $keycolname1      We will sort by this key secondly, and  it can be null, then it will only sort by the first column.
789                            bool $translate1            Whether to trabslate the second column
790                            bool $ifsort2                 Whether to sort by the second column or just translate the second column.
791     * @return        The sorted and translated data.
792    */
793     function sortData($data_set, $keycolname1=null, $translate1=false, $keycolname2=null, $translate2=false, $ifsort2=false) {
794         //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.
795         global $app_list_strings;
796         $sortby1[] = array();
797         foreach ($data_set as $row) {
798             $sortby1[]  = $row[$keycolname1];
799         }
800         $sortby1 = array_unique($sortby1);
801         //The data is from the database, the sorting should be done in the sql. So I will not do the sort here.
802         if($translate1) {
803             $temp_sortby1 = array();
804             foreach(array_keys($app_list_strings[$keycolname1.'_dom']) as $sortby1_value) {
805                 if(in_array($sortby1_value, $sortby1)) {
806                     $temp_sortby1[] = $sortby1_value;
807                 }
808             }
809             $sortby1 = $temp_sortby1;
810         }
811
812         //if(isset($sortby1[0]) && $sortby1[0]=='') unset($sortby1[0]);//the beginning of lead_source_dom is blank.
813         if(isset($sortby1[0]) && $sortby1[0]==array()) unset($sortby1[0]);//the beginning of month after search is blank.
814
815         if($ifsort2==false) $sortby2=array(0);
816
817         if($keycolname2!=null) {
818             $sortby2 = array();
819             foreach ($data_set as $row) {
820                 $sortby2[]  = $row[$keycolname2];
821             }
822             //The data is from the database, the sorting should be done in the sql. So I will not do the sort here.
823             $sortby2 = array_unique($sortby2);
824             if($translate2) {
825                 $temp_sortby2 = array();
826                 foreach(array_keys($app_list_strings[$keycolname2.'_dom']) as $sortby2_value) {
827                     if(in_array($sortby2_value, $sortby2)) {
828                         $temp_sortby2[] = $sortby2_value;
829                     }
830                 }
831                 $sortby2 = $temp_sortby2;
832             }
833         }
834
835         $data=array();
836
837         foreach($sortby1 as $sort1) {
838             foreach($sortby2 as $sort2) {
839                 if($ifsort2) $a=0;
840                 foreach($data_set as $key => $value){
841                     if($value[$keycolname1] == $sort1 && (!$ifsort2 || $value[$keycolname2]== $sort2)) {
842                         if($translate1) {
843                             $value[$keycolname1.'_dom_option'] = $value[$keycolname1];
844                             $value[$keycolname1] = $app_list_strings[$keycolname1.'_dom'][$value[$keycolname1]];
845                         }
846                         if($translate2) {
847                             $value[$keycolname2.'_dom_option'] = $value[$keycolname2];
848                             $value[$keycolname2] = $app_list_strings[$keycolname2.'_dom'][$value[$keycolname2]];
849                         }
850                         array_push($data, $value);
851                         unset($data_set[$key]);
852                         $a=1;
853                         }
854                 }
855                 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.
856                     $val=array();
857                     $val['total'] = 0;
858                     $val['count'] = 0;
859                     if($translate1) {
860                         $val[$keycolname1] = $app_list_strings[$keycolname1.'_dom'][$sort1];
861                         $val[$keycolname1.'_dom_option'] = $sort1;
862                     }
863                     else {
864                         $val[$keycolname1] = $sort1;
865                     }
866                     if($translate2) {
867                         $val[$keycolname2] = $app_list_strings[$keycolname2.'_dom'][$sort2];
868                         $val[$keycolname2.'_dom_option'] = $sort2;
869                     }
870                     elseif($keycolname2!=null) {
871                         $val[$keycolname2] = $sort2;
872                     }
873                     array_push($data, $val);
874                 }
875             }
876         }
877         return $data;
878     }
879
880     function getChartResources() {
881
882                 $resources = "";
883                 return $resources;
884         }
885
886         function getMySugarChartResources() {
887                 $mySugarResources = "";
888                 return $mySugarResources;
889         }
890
891         /**
892      * wrapper function to return chart array after any additional processing
893          *
894      * @param   array $chartsArray      array of chart config items that need processing
895      * @return  array $chartArray after it has been process
896      */
897         function chartArray($chartsArray) {
898
899                 return $chartsArray;
900         }
901
902 } // end class def