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