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