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