Completed
Push — console-installer ( e2b50d...6ce748 )
by Adam
22:30
created

AOR_Report   D

Complexity

Total Complexity 299

Size/Duplication

Total Lines 1439
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 8

Importance

Changes 1
Bugs 1 Features 0
Metric Value
dl 0
loc 1439
rs 4.4102
c 1
b 1
f 0
wmc 299
lcom 1
cbo 8

25 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A AOR_Report() 0 10 2
A bean_implements() 0 6 2
B save() 0 24 2
B load_report_beans() 0 15 5
A getReportFields() 0 10 2
F build_report_chart() 0 113 20
C buildMultiGroupReport() 0 56 10
B getGroupDisplayFieldByReportId() 0 24 3
A dbSelect() 0 10 2
A getMultiGroupFrameHTML() 0 7 1
A addDataIdValueToInnertext() 0 5 1
F build_group_report() 0 142 40
F build_report_html() 0 229 27
B getModuleFieldByGroupValue() 0 34 6
C getTotalHTML() 0 67 14
A calculateTotal() 0 12 4
A encloseForCSV() 0 3 1
C build_report_csv() 0 77 10
C build_report_query() 0 59 18
A queryWhereRepair() 0 18 3
D build_report_query_select() 0 92 27
C build_report_query_join() 0 45 8
C build_report_access_query() 0 31 7
D build_report_query_where() 0 272 83

How to fix   Complexity   

Complex Class

Complex classes like AOR_Report often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use AOR_Report, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 *
5
 * SugarCRM Community Edition is a customer relationship management program developed by
6
 * SugarCRM, Inc. Copyright (C) 2004-2013 SugarCRM Inc.
7
 *
8
 * SuiteCRM is an extension to SugarCRM Community Edition developed by SalesAgility Ltd.
9
 * Copyright (C) 2011 - 2016 SalesAgility Ltd.
10
 *
11
 * This program is free software; you can redistribute it and/or modify it under
12
 * the terms of the GNU Affero General Public License version 3 as published by the
13
 * Free Software Foundation with the addition of the following permission added
14
 * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
15
 * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
16
 * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
17
 *
18
 * This program is distributed in the hope that it will be useful, but WITHOUT
19
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
20
 * FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
21
 * details.
22
 *
23
 * You should have received a copy of the GNU Affero General Public License along with
24
 * this program; if not, see http://www.gnu.org/licenses or write to the Free
25
 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26
 * 02110-1301 USA.
27
 *
28
 * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
29
 * SW2-130, Cupertino, CA 95014, USA. or at email address [email protected].
30
 *
31
 * The interactive user interfaces in modified source and object code versions
32
 * of this program must display Appropriate Legal Notices, as required under
33
 * Section 5 of the GNU Affero General Public License version 3.
34
 *
35
 * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
36
 * these Appropriate Legal Notices must retain the display of the "Powered by
37
 * SugarCRM" logo and "Supercharged by SuiteCRM" logo. If the display of the logos is not
38
 * reasonably feasible for  technical reasons, the Appropriate Legal Notices must
39
 * display the words  "Powered by SugarCRM" and "Supercharged by SuiteCRM".
40
 */
41
class AOR_Report extends Basic {
42
	var $new_schema = true;
43
	var $module_dir = 'AOR_Reports';
44
	var $object_name = 'AOR_Report';
45
	var $table_name = 'aor_reports';
46
	var $importable = true;
47
	var $disable_row_level_security = true ;
48
49
	var $id;
50
	var $name;
51
	var $date_entered;
52
	var $date_modified;
53
	var $modified_user_id;
54
	var $modified_by_name;
55
	var $created_by;
56
	var $created_by_name;
57
	var $description;
58
	var $deleted;
59
	var $created_by_link;
60
	var $modified_user_link;
61
	var $assigned_user_id;
62
	var $assigned_user_name;
63
	var $assigned_user_link;
64
	var $report_module;
65
66
	function __construct(){
67
		parent::__construct();
68
        $this->load_report_beans();
69
        require_once('modules/AOW_WorkFlow/aow_utils.php');
70
        require_once('modules/AOR_Reports/aor_utils.php');
71
	}
72
73
    /**
74
     * @deprecated deprecated since version 7.6, PHP4 Style Constructors are deprecated and will be remove in 7.8, please update your code, use __construct instead
75
     */
76
    function AOR_Report(){
77
        $deprecatedMessage = 'PHP4 Style Constructors are deprecated and will be remove in 7.8, please update your code';
78
        if(isset($GLOBALS['log'])) {
79
            $GLOBALS['log']->deprecated($deprecatedMessage);
80
        }
81
        else {
82
            trigger_error($deprecatedMessage, E_USER_DEPRECATED);
83
        }
84
        self::__construct();
85
    }
86
87
88
	function bean_implements($interface){
89
		switch($interface){
90
			case 'ACL': return true;
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
Coding Style introduced by
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
91
		}
92
		return false;
93
	}
94
95
    function save($check_notify = FALSE){
96
97
        // TODO: process of saveing the fields and conditions is too long so we will have to make some optimization on save_lines functions
98
        set_time_limit(3600);
99
100
        if (empty($this->id)){
101
            unset($_POST['aor_conditions_id']);
102
            unset($_POST['aor_fields_id']);
103
        }
104
105
        parent::save($check_notify);
106
107
        require_once('modules/AOR_Fields/AOR_Field.php');
108
        $field = new AOR_Field();
109
        $field->save_lines($_POST, $this, 'aor_fields_');
110
111
        require_once('modules/AOR_Conditions/AOR_Condition.php');
112
        $condition = new AOR_Condition();
113
        $condition->save_lines($_POST, $this, 'aor_conditions_');
114
115
        require_once('modules/AOR_Charts/AOR_Chart.php');
116
        $chart = new AOR_Chart();
117
        $chart->save_lines($_POST, $this, 'aor_chart_');
118
    }
119
120
    function load_report_beans(){
121
        global $beanList, $app_list_strings;
122
123
        $app_list_strings['aor_moduleList'] = $app_list_strings['moduleList'];
124
125
        foreach($app_list_strings['aor_moduleList'] as $mkey => $mvalue){
126
            if(!isset($beanList[$mkey]) || str_begin($mkey, 'AOR_') || str_begin($mkey, 'AOW_')){
127
                unset($app_list_strings['aor_moduleList'][$mkey]);
128
            }
129
        }
130
131
        $app_list_strings['aor_moduleList'] = array_merge((array)array(''=>''), (array)$app_list_strings['aor_moduleList']);
132
133
        asort($app_list_strings['aor_moduleList']);
134
    }
135
136
137
    function getReportFields(){
138
        $fields = array();
139
        foreach($this->get_linked_beans('aor_fields','AOR_Fields') as $field){
140
            $fields[] = $field;
141
        }
142
        usort($fields,function($a,$b){
143
            return $a->field_order - $b->field_order;
144
        });
145
        return $fields;
146
    }
147
148
    const CHART_TYPE_PCHART = 'pchart';
149
    const CHART_TYPE_CHARTJS = 'chartjs';
150
    const CHART_TYPE_RGRAPH = 'rgraph';
151
152
153
    function build_report_chart($chartIds = null, $chartType = self::CHART_TYPE_PCHART){
154
        global $beanList;
155
        $linkedCharts = $this->get_linked_beans('aor_charts','AOR_Charts');
156
	if(!$linkedCharts){
0 ignored issues
show
Bug Best Practice introduced by
The expression $linkedCharts of type SugarBean[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
157
            //No charts to display
158
            return '';
159
        }
160
161
        $sql = "SELECT id FROM aor_fields WHERE aor_report_id = '".$this->id."' AND deleted = 0 ORDER BY field_order ASC";
162
        $result = $this->db->query($sql);
163
164
        $fields = array();
165
        $i = 0;
166
167
        $mainGroupField = null;
168
169
        while ($row = $this->db->fetchByAssoc($result)) {
170
171
            $field = new AOR_Field();
172
            $field->retrieve($row['id']);
173
174
            $path = unserialize(base64_decode($field->module_path));
175
176
            $field_bean = new $beanList[$this->report_module]();
177
178
            $field_module = $this->report_module;
179
            $field_alias = $field_bean->table_name;
180
            if($path[0] != $this->report_module){
181
                foreach($path as $rel){
182
                    if(empty($rel)){
183
                        continue;
184
                    }
185
                    $field_module = getRelatedModule($field_module,$rel);
186
                    $field_alias = $field_alias . ':'.$rel;
187
                }
188
            }
189
            $label = str_replace(' ','_',$field->label).$i;
190
            $fields[$label]['field'] = $field->field;
191
            $fields[$label]['label'] = $field->label;
192
            $fields[$label]['display'] = $field->display;
193
            $fields[$label]['function'] = $field->field_function;
194
            $fields[$label]['module'] = $field_module;
195
            $fields[$label]['alias'] = $field_alias;
196
            $fields[$label]['link'] = $field->link;
197
            $fields[$label]['total'] = $field->total;
198
199
200
            $fields[$label]['params'] = array("date_format" => $field->format);
201
202
            // get the main group
203
204
            if($field->group_display) {
205
206
                // if we have a main group already thats wrong cause only one main grouping field possible
207
                if(!is_null($mainGroupField)) {
208
                    $GLOBALS['log']->fatal('main group already found');
209
                }
210
211
                $mainGroupField = $field;
212
            }
213
214
            ++$i;
215
        }
216
217
218
219
        $query = $this->build_report_query();
220
        $result = $this->db->query($query);
221
        $data = array();
222
        while($row = $this->db->fetchByAssoc($result, false))
223
        {
224
            foreach($fields as $name => $att){
225
226
                $currency_id = isset($row[$att['alias'].'_currency_id']) ? $row[$att['alias'].'_currency_id'] : '';
227
228
                switch ($att['function']){
229
                    case 'COUNT':
230
                        break;
231
                    default:
232
                        if(!is_numeric($row[$name])) {
233
                            $row[$name] = trim(strip_tags(getModuleField($att['module'], $att['field'], $att['field'], 'DetailView', $row[$name], '', $currency_id,$att['params'])));
234
235
                        }
236
                        break;
237
                }
238
            }
239
            $data[] = $row;
240
        }
241
        $fields = $this->getReportFields();
242
243
        switch($chartType) {
244
            case self::CHART_TYPE_PCHART:
245
                $html = '<script src="modules/AOR_Charts/lib/pChart/imagemap.js"></script>';
246
                break;
247
            case self::CHART_TYPE_CHARTJS:
248
                $html = '<script src="modules/AOR_Reports/js/Chart.js"></script>';
249
                break;
250
            case self::CHART_TYPE_RGRAPH:
251
                if($_REQUEST['module']!= 'Home')//Need the require_once for the rgraphincludes as they are only loaded when the home page is hit
252
                    require_once('include/SuiteGraphs/RGraphIncludes.php');
253
254
                break;
255
        }
256
        $x = 0;
257
        foreach($linkedCharts as $chart){
258
            if($chartIds !== null && !in_array($chart->id,$chartIds)){
259
                continue;
260
            }
261
            $html .= $chart->buildChartHTML($data,$fields,$x, $chartType, $mainGroupField);
262
            $x++;
263
        }
264
        return $html;
265
    }
266
267
268
    public function buildMultiGroupReport($offset = -1, $links = true, $level = 2, $path = array()) {
269
        global $beanList;
270
271
        $rows = $this->getGroupDisplayFieldByReportId($this->id, $level);
272
273
        if(count($rows) > 1) {
274
            $GLOBALS['log']->fatal('ambiguous group display for report ' . $this->id);
275
        }
276
        else if(count($rows) == 1){
277
            $rows[0]['module_path'] = unserialize(base64_decode($rows[0]['module_path']));
278
            if(!$rows[0]['module_path'][0]) {
279
                $module = new $beanList[$this->report_module]();
280
                $rows[0]['field_id_name'] = $module->field_defs[$rows[0]['field']]['id_name'] ? $module->field_defs[$rows[0]['field']]['id_name'] : $module->field_defs[$rows[0]['field']]['name'];
281
                $rows[0]['module_path'][0] = $module->table_name;
282
            }
283
            else {
284
                $rows[0]['field_id_name'] = $rows[0]['field'];
285
            }
286
            $path[] = $rows[0];
287
288
            if($level>10) {
289
                $msg = 'Too many nested groups';
290
                $GLOBALS['log']->fatal($msg);
291
                return null;
292
            }
293
294
            return $this->buildMultiGroupReport($offset, $links, $level+1, $path);
295
        }
296
        else if(!$rows) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $rows of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
297
            if($path) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $path of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
298
                $html = '';
299
                foreach ($path as $pth) {
300
                    $_fieldIdName = $this->db->quoteIdentifier($pth['field_id_name']);
301
                    $query = "SELECT $_fieldIdName FROM " . $this->db->quoteIdentifier($pth['module_path'][0]) . " GROUP BY $_fieldIdName;";
302
                    $values = $this->dbSelect($query);
303
304
                    foreach($values as $value) {
305
306
                        //$where = [ $this->db->quote($pth['module_path'][0]) . '.' . $_fieldIdName . ' = \'' . $this->db->quote($value[$pth['field_id_name']]) . '\'' ];
307
308
                        $moduleFieldByGroupValue = $this->getModuleFieldByGroupValue($beanList, $value[$pth['field_id_name']]);
309
                        $moduleFieldByGroupValue = $this->addDataIdValueToInnertext($moduleFieldByGroupValue);
310
                        $html .= $this->getMultiGroupFrameHTML($moduleFieldByGroupValue, $this->build_group_report($offset, $links/*, ['where' => $where]*/));
311
                    }
312
                }
313
                return $html;
314
            }
315
            else {
316
                return $this->build_group_report($offset, $links);
317
            }
318
        }
319
        else {
320
            throw new Exception('incorrect results');
321
        }
322
        throw new Exception('incorrect state');
323
    }
324
325
    private function getGroupDisplayFieldByReportId($reportId = null, $level = 1) {
326
327
        // set the default values
328
329
        if (is_null($reportId)) {
330
            $reportId = $this->id;
331
        }
332
333
        if (!$level) {
334
            $level = 1;
335
        }
336
337
        // escape values for query
338
339
        $_id = $this->db->quote($reportId);
340
        $_level = (int) $level;
341
342
        // get results array
343
344
        $query = "SELECT id, field, module_path FROM aor_fields WHERE aor_report_id = '$_id' AND group_display = $_level AND deleted = 0;";
345
        $rows = $this->dbSelect($query);
346
347
        return $rows;
348
    }
349
350
351
    private function dbSelect($query) {
352
        $results = $this->db->query($query);
353
354
        $rows = array();
355
        while($row = $this->db->fetchByAssoc($results)) {
356
            $rows[] = $row;
357
        }
358
359
        return $rows;
360
    }
361
362
    private function getMultiGroupFrameHTML($header, $body) {
363
        $html = '<div class="multi-group-list" style="border: 1px solid black; padding: 10px;">
364
                    <h3>' . $header . '</h3>
365
                    <div class="multi-group-list-inner">' . $body . '</div>
366
                </div>';
367
        return $html;
368
    }
369
370
    private function addDataIdValueToInnertext($html) {
371
        preg_match('/\sdata-id-value\s*=\s*"([^"]*)"/', $html, $match);
372
        $html = preg_replace('/(>)([^<]*)(<\/\w+>$)/', '$1$2' . $match[1] . '$3', $html);
373
        return $html;
374
    }
375
376
377
    function build_group_report($offset = -1, $links = true, $extra = array()){
378
        global $beanList;
379
380
        $html = '';
381
        $query = '';
382
        $query_array = array();
383
        $module = new $beanList[$this->report_module]();
384
385
        $sql = "SELECT id FROM aor_fields WHERE aor_report_id = '".$this->id."' AND group_display = 1 AND deleted = 0 ORDER BY field_order ASC";
386
        $field_id = $this->db->getOne($sql);
387
388
        if(!$field_id) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $field_id of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
389
            $query_array['select'][] = $module->table_name . ".id AS '" . $module->table_name . "_id'";
390
        }
391
392
        if($field_id != ''){
393
            $field = new AOR_Field();
394
            $field->retrieve($field_id);
395
396
            $field_label = str_replace(' ','_',$field->label);
397
398
            $path = unserialize(base64_decode($field->module_path));
399
400
            $field_module = $module;
401
            $table_alias = $field_module->table_name;
402
            if(!empty($path[0]) && $path[0] != $module->module_dir){
403
                foreach($path as $rel){
404
                    $new_field_module = new $beanList[getRelatedModule($field_module->module_dir,$rel)];
405
                    $oldAlias = $table_alias;
406
                    $table_alias = $table_alias.":".$rel;
407
408
                    $query_array = $this->build_report_query_join($rel, $table_alias, $oldAlias, $field_module, 'relationship', $query_array, $new_field_module);
409
                    $field_module = $new_field_module;
410
411
                    // ?
412
                    //$table_alias = $rel;
413
                }
414
            }
415
416
            $data = $field_module->field_defs[$field->field];
417
418
            if($data['type'] == 'relate' && isset($data['id_name'])) {
419
                $field->field = $data['id_name'];
420
            }
421
422
            if($data['type'] == 'currency' && !stripos($field->field, '_USD') && isset($field_module->field_defs['currency_id'])) {
423
                if((isset($field_module->field_defs['currency_id']['source']) && $field_module->field_defs['currency_id']['source'] == 'custom_fields')) {
424
                    $query['select'][$table_alias.'_currency_id'] = $table_alias.'_cstm'.".currency_id AS '".$table_alias."_currency_id'";
425
                } else {
426
                    $query_array['select'][$table_alias . '_currency_id'] = $table_alias . ".currency_id AS '" . $table_alias . "_currency_id'";
427
                }
428
            }
429
430
            if(  (isset($data['source']) && $data['source'] == 'custom_fields')) {
431
                $select_field = $this->db->quoteIdentifier($table_alias.'_cstm').'.'.$field->field;
432
                // Fix for #1251 - added a missing parameter to the function call
433
                $query_array = $this->build_report_query_join($table_alias.'_cstm', $table_alias.'_cstm', $table_alias, $field_module, 'custom', $query);
434
            } else {
435
                $select_field= $this->db->quoteIdentifier($table_alias).'.'.$field->field;
436
            }
437
438
            if($field->sort_by != ''){
439
                $query_array['sort_by'][] = $field_label.' '.$field->sort_by;
440
            }
441
442
            if($field->group_by == 1){
443
                $query_array['group_by'][] = $select_field;
444
            }
445
446
            if($field->field_function != null){
447
                $select_field = $field->field_function.'('.$select_field.')';
448
            }
449
450
            $query_array['select'][] = $select_field ." AS '".$field_label."'";
451
            if(isset($extra['select']) && $extra['select']) {
452
                foreach($extra['select'] as $selectField => $selectAlias) {
453
                    if($selectAlias) {
454
                        $query_array['select'][] = $selectField . " AS " . $selectAlias;
455
                    }
456
                    else {
457
                        $query_array['select'][] = $selectField;
458
                    }
459
                }
460
            }
461
            $query_array['where'][] = $select_field ." IS NOT NULL AND ";
462
            if(isset($extra['where']) && $extra['where']) {
463
                $query_array['where'][] = implode(' AND ', $extra['where']) . ' AND ';
464
            }
465
466
            $query_array = $this->build_report_query_where($query_array);
467
468
            foreach ($query_array['select'] as $select){
469
                $query .=  ($query == '' ? 'SELECT ' : ', ').$select;
470
            }
471
472
            $query .= ' FROM '.$module->table_name.' ';
473
474
            if(isset($query_array['join'])){
475
                foreach ($query_array['join'] as $join){
476
                    $query .= $join;
477
                }
478
            }
479
            if(isset($query_array['where'])){
480
                $query_where = '';
481
                foreach ($query_array['where'] as $where){
482
                    $query_where .=  ($query_where == '' ? 'WHERE ' : ' ').$where;
483
                }
484
485
                $query_where = $this->queryWhereRepair($query_where);
486
487
                $query .= ' '.$query_where;
488
            }
489
490
            if(isset($query_array['group_by'])){
491
                $query_group_by = '';
492
                foreach ($query_array['group_by'] as $group_by){
493
                    $query_group_by .=  ($query_group_by == '' ? 'GROUP BY ' : ', ').$group_by;
494
                }
495
                $query .= ' '.$query_group_by;
496
            }
497
498
            if(isset($query_array['sort_by'])){
499
                $query_sort_by = '';
500
                foreach ($query_array['sort_by'] as $sort_by){
501
                    $query_sort_by .=  ($query_sort_by == '' ? 'ORDER BY ' : ', ').$sort_by;
502
                }
503
                $query .= ' '.$query_sort_by;
504
            }
505
            $result = $this->db->query($query);
506
507
            while ($row = $this->db->fetchByAssoc($result)) {
508
                if($html != '') $html .= '<br />';
509
510
               $html .= $this->build_report_html($offset, $links, $row[$field_label], '', $extra);
511
512
            }
513
        }
514
515
        if($html == '') $html = $this->build_report_html($offset, $links);
516
        return $html;
517
518
    }
519
520
521
    function build_report_html($offset = -1, $links = true, $group_value = '', $tableIdentifier = '', $extra = array()){
522
523
        global $beanList, $sugar_config;
524
525
        $_group_value = $this->db->quote($group_value);
526
527
        $report_sql = $this->build_report_query($_group_value, $extra);
528
529
        // Fix for issue 1232 - items listed in a single report, should adhere to the same standard as ListView items.
530
        if($sugar_config['list_max_entries_per_page']!='') {
531
            $max_rows = $sugar_config['list_max_entries_per_page'];
532
        } else {
533
            $max_rows = 20;
534
        }
535
        
536
        $total_rows = 0;
537
        $count_sql = explode('ORDER BY', $report_sql);
538
        $count_query = 'SELECT count(*) c FROM ('.$count_sql[0].') as n';
539
540
        // We have a count query.  Run it and get the results.
541
        $result = $this->db->query($count_query);
542
        $assoc = $this->db->fetchByAssoc($result);
543
        if(!empty($assoc['c']))
544
        {
545
            $total_rows = $assoc['c'];
546
        }
547
548
        $html = "<table class='list' id='report_table".$group_value."' width='100%' cellspacing='0' cellpadding='0' border='0' repeat_header='1'>";
549
550
        if($offset >= 0){
551
            $start = 0;
552
            $end = 0;
553
            $previous_offset = 0;
554
            $next_offset = 0;
555
            $last_offset = 0;
556
557
            if($total_rows > 0){
558
                $start = $offset +1;
559
                $end = (($offset + $max_rows) < $total_rows) ? $offset + $max_rows : $total_rows;
560
                $previous_offset = ($offset - $max_rows) < 0 ? 0 : $offset - $max_rows;
561
                $next_offset = $offset + $max_rows;
562
                if(is_int($total_rows / $max_rows)){
563
                    $last_offset = $max_rows * ($total_rows / $max_rows -1);
564
                } else {
565
                    $last_offset = $max_rows * floor($total_rows / $max_rows);
566
                }
567
568
            }
569
570
            $html .= "<thead><tr class='pagination'>";
571
            
572
573
            $moduleFieldByGroupValue = $this->getModuleFieldByGroupValue($beanList, $group_value);
574
575
            $html .="<td colspan='18'>
576
                       <table class='paginationTable' border='0' cellpadding='0' cellspacing='0' width='100%'>
577
                        <td style='text-align:left' ><H3><a href=\"javascript:void(0)\" class=\"collapseLink\" onclick=\"groupedReportToggler.toggleList(this);\"><img border=\"0\" id=\"detailpanel_1_img_hide\" src=\"themes/SuiteR/images/basic_search.gif\"></a>$moduleFieldByGroupValue</H3></td>
578
                        <td class='paginationChangeButtons' align='right' nowrap='nowrap' width='1%'>";
579
580
            if($offset == 0){
581
                $html .="<button type='button' id='listViewStartButton_top' name='listViewStartButton' title='Start' class='button' disabled='disabled'>
582
                    <img src='".SugarThemeRegistry::current()->getImageURL('start_off.gif')."' alt='Start' align='absmiddle' border='0'>
583
                </button>
584
                <button type='button' id='listViewPrevButton_top' name='listViewPrevButton' class='button' title='Previous' disabled='disabled'>
585
                    <img src='".SugarThemeRegistry::current()->getImageURL('previous_off.gif')."' alt='Previous' align='absmiddle' border='0'>
586
                </button>";
587
            } else {
588
                $html .="<button type='button' id='listViewStartButton_top' name='listViewStartButton' title='Start' class='button' onclick='changeReportPage(\"".$this->id."\",0,\"".$group_value."\",\"".$tableIdentifier."\")'>
589
                    <img src='".SugarThemeRegistry::current()->getImageURL('start.gif')."' alt='Start' align='absmiddle' border='0'>
590
                </button>
591
                <button type='button' id='listViewPrevButton_top' name='listViewPrevButton' class='button' title='Previous' onclick='changeReportPage(\"".$this->id."\",".$previous_offset.",\"".$group_value."\",\"".$tableIdentifier."\")'>
592
                    <img src='".SugarThemeRegistry::current()->getImageURL('previous.gif')."' alt='Previous' align='absmiddle' border='0'>
593
                </button>";
594
            }
595
            $html .=" <span class='pageNumbers'>(".$start ." - ".$end ." of ". $total_rows .")</span>";
596
            if($next_offset < $total_rows){
597
                $html .="<button type='button' id='listViewNextButton_top' name='listViewNextButton' title='Next' class='button' onclick='changeReportPage(\"".$this->id."\",".$next_offset.",\"".$group_value."\",\"".$tableIdentifier."\")'>
598
                        <img src='".SugarThemeRegistry::current()->getImageURL('next.gif')."' alt='Next' align='absmiddle' border='0'>
599
                    </button>
600
                     <button type='button' id='listViewEndButton_top' name='listViewEndButton' title='End' class='button' onclick='changeReportPage(\"".$this->id."\",".$last_offset.",\"".$group_value."\",\"".$tableIdentifier."\")'>
601
                        <img src='".SugarThemeRegistry::current()->getImageURL('end.gif')."' alt='End' align='absmiddle' border='0'>
602
                    </button>";
603
            } else {
604
                $html .="<button type='button' id='listViewNextButton_top' name='listViewNextButton' title='Next' class='button'  disabled='disabled'>
605
                        <img src='".SugarThemeRegistry::current()->getImageURL('next_off.gif')."' alt='Next' align='absmiddle' border='0'>
606
                    </button>
607
                     <button type='button' id='listViewEndButton_top' name='listViewEndButton' title='End' class='button'  disabled='disabled'>
608
                        <img src='".SugarThemeRegistry::current()->getImageURL('end_off.gif')."' alt='End' align='absmiddle' border='0'>
609
                    </button>";
610
611
            }
612
613
            $html .="</td>
614
                       </table>
615
                      </td>";
616
617
            $html .="</tr></thead>";
618
        } else{
619
620
            $moduleFieldByGroupValue = $this->getModuleFieldByGroupValue($beanList, $group_value);
621
622
            $html = "<H3>$moduleFieldByGroupValue</H3>".$html;
623
        }
624
625
        $sql = "SELECT id FROM aor_fields WHERE aor_report_id = '".$this->id."' AND deleted = 0 ORDER BY field_order ASC";
626
        $result = $this->db->query($sql);
627
628
        $html .= "<thead>";
629
        $html .= "<tr>";
630
631
        $fields = array();
632
        $i = 0;
633
        while ($row = $this->db->fetchByAssoc($result)) {
634
635
            $field = new AOR_Field();
636
            $field->retrieve($row['id']);
637
638
            $path = unserialize(base64_decode($field->module_path));
639
640
            $field_bean = new $beanList[$this->report_module]();
641
642
            $field_module = $this->report_module;
643
            $field_alias = $field_bean->table_name;
644
            if($path[0] != $this->report_module){
645
                foreach($path as $rel){
646
                    if(empty($rel)){
647
                        continue;
648
                    }
649
                    $field_module = getRelatedModule($field_module,$rel);
650
                    $field_alias = $field_alias . ':'.$rel;
651
                }
652
            }
653
            $label = str_replace(' ','_',$field->label).$i;
654
            $fields[$label]['field'] = $field->field;
655
            $fields[$label]['label'] = $field->label;
656
            $fields[$label]['display'] = $field->display;
657
            $fields[$label]['function'] = $field->field_function;
658
            $fields[$label]['module'] = $field_module;
659
            $fields[$label]['alias'] = $field_alias;
660
            $fields[$label]['link'] = $field->link;
661
            $fields[$label]['total'] = $field->total;
662
663
            $fields[$label]['params'] = array("date_format" => $field->format);
664
665
666
            if($fields[$label]['display']){
667
                $html .= "<th scope='col'>";
668
                $html .= "<div style='white-space: normal;' width='100%' align='left'>";
669
                $html .= $field->label;
670
                $html .= "</div></th>";
671
            }
672
            ++$i;
673
        }
674
675
        $html .= "</tr>";
676
        $html .= "</thead>";
677
        $html .= "<tbody>";
678
679
        if($offset >= 0){
680
            $result = $this->db->limitQuery($report_sql, $offset, $max_rows);
681
        } else {
682
            $result = $this->db->query($report_sql);
683
        }
684
685
        $row_class = 'oddListRowS1';
686
687
688
        $totals = array();
689
        while ($row = $this->db->fetchByAssoc($result)) {
690
            $html .= "<tr class='".$row_class."' height='20'>";
691
692
            foreach($fields as $name => $att){
693
                if($att['display']){
694
                    $html .= "<td class='' valign='top' align='left'>";
695
                    if($att['link'] && $links){
696
                        $html .= "<a href='" . $sugar_config['site_url'] . "/index.php?module=".$att['module']."&action=DetailView&record=".$row[$att['alias'].'_id']."'>";
697
                    }
698
699
                    $currency_id = isset($row[$att['alias'].'_currency_id']) ? $row[$att['alias'].'_currency_id'] : '';
700
701
                    switch ($att['function']){
702
                        case 'COUNT':
703
                        //case 'SUM':
704
                            $html .= $row[$name];
705
                            break;
706
                        default:
0 ignored issues
show
Coding Style introduced by
The default body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a default statement must start on the line immediately following the statement.

switch ($expr) {
    default:
        doSomething(); //right
        break;
}


switch ($expr) {
    default:

        doSomething(); //wrong
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
707
708
                            $html .= getModuleField($att['module'], $att['field'], $att['field'], 'DetailView',$row[$name],'',$currency_id, $att['params']);
709
                            break;
710
                    }
711
                    if($att['total']){
712
                        $totals[$name][] = $row[$name];
713
                    }
714
                    if($att['link'] && $links) $html .= "</a>";
715
                    $html .= "</td>";
716
                }
717
            }
718
            $html .= "</tr>";
719
720
            $row_class = $row_class == 'oddListRowS1' ?  'evenListRowS1':'oddListRowS1';
721
        }
722
        $html .= "</tbody>";
723
724
        $html .= $this->getTotalHtml($fields,$totals);
725
726
        $html .= "</table>";
727
728
        $html .= "    <script type=\"text/javascript\">
729
                            groupedReportToggler = {
730
731
                                toggleList: function(elem) {
732
                                    $(elem).closest('table.list').find('thead, tbody').each(function(i, e){
733
                                        if(i>1) {
734
                                            $(e).toggle();
735
                                        }
736
                                    });
737
                                    if($(elem).find('img').first().attr('src') == 'themes/SuiteR/images/basic_search.gif') {
738
                                        $(elem).find('img').first().attr('src', 'themes/SuiteR/images/advanced_search.gif');
739
                                    }
740
                                    else {
741
                                        $(elem).find('img').first().attr('src', 'themes/SuiteR/images/basic_search.gif');
742
                                    }
743
                                }
744
745
                            };
746
                        </script>";
747
748
        return $html;
749
    }
750
751
    private function getModuleFieldByGroupValue($beanList, $group_value) {
752
        $moduleFieldByGroupValues = array();
753
754
        $sql = "SELECT id FROM aor_fields WHERE aor_report_id = '".$this->id."' AND group_display = 1 AND deleted = 0 ORDER BY field_order ASC";
755
        $result = $this->db->limitQuery($sql, 0, 1);
756
        while ($row = $this->db->fetchByAssoc($result)) {
757
758
            $field = new AOR_Field();
759
            $field->retrieve($row['id']);
760
761
            $path = unserialize(base64_decode($field->module_path));
762
763
            $field_bean = new $beanList[$this->report_module]();
764
765
            $field_module = $this->report_module;
766
            $field_alias = $field_bean->table_name;
767
            if($path[0] != $this->report_module){
768
                foreach($path as $rel){
769
                    if(empty($rel)){
770
                        continue;
771
                    }
772
                    $field_module = getRelatedModule($field_module,$rel);
773
                    $field_alias = $field_alias . ':'.$rel;
774
                }
775
            }
776
777
            $currency_id = isset($row[$field_alias.'_currency_id']) ? $row[$field_alias.'_currency_id'] : '';
778
            $moduleFieldByGroupValues[] = getModuleField($this->report_module, $field->field, $field->field, 'DetailView', $group_value, '', $currency_id, array("date_format" => $field->format));
779
780
        }
781
782
        $moduleFieldByGroupValue = implode(', ', $moduleFieldByGroupValues);
783
        return $moduleFieldByGroupValue;
784
    }
785
786
    function getTotalHTML($fields,$totals){
787
        global $app_list_strings;
788
789
        $currency = new Currency();
790
        $currency->retrieve($GLOBALS['current_user']->getPreference('currency'));
791
792
        $html = '';
793
        $html .= "<tbody>";
794
        $html .= "<tr>";
795
        foreach($fields as $label => $field){
796
            if(!$field['display']){
797
                continue;
798
            }
799
            if($field['total']){
800
                $totalLabel = $field['label'] ." ".$app_list_strings['aor_total_options'][$field['total']];
801
                $html .= "<th>{$totalLabel}</th>";
802
            }else{
803
                $html .= "<th></th>";
804
            }
805
        }
806
        $html .= "</tr>";
807
        $html .= "<tr>";
808
        foreach($fields as $label => $field){
809
            if(!$field['display']){
810
                continue;
811
            }
812
            if($field['total'] && isset($totals[$label])){
813
                $type = $field['total'];
814
                $total = $this->calculateTotal($type, $totals[$label]);
815
                // Customise display based on the field type
816
                $moduleBean = BeanFactory::newBean($field['module']);
817
                $fieldDefinition = $moduleBean->field_defs[$field['field']];
818
                $fieldDefinitionType = $fieldDefinition['type'];
819
                switch($fieldDefinitionType) {
820
                    case "currency":
821
                        // Customise based on type of function
822
                        switch($type){
823
                            case 'SUM':
0 ignored issues
show
Coding Style introduced by
There must be a comment when fall-through is intentional in a non-empty case body
Loading history...
824
                                if($currency->id == -99) {
825
                                    $total = $currency->symbol.format_number($total, null, null);
826
                                } else {
827
                                    $total = $currency->symbol.format_number($total, null, null, array('convert' => true));
828
                                }
829
                            case 'COUNT':
830
                                break;
831
                            case 'AVG':
0 ignored issues
show
Coding Style introduced by
There must be a comment when fall-through is intentional in a non-empty case body
Loading history...
832
                                if($currency->id == -99) {
833
                                    $total = $currency->symbol.format_number($total, null, null);
834
                                } else {
835
                                    $total = $currency->symbol.format_number($total, null, null, array('convert' => true));
836
                                }
837
                            default:
838
                               break;
839
                        }
840
                        break;
841
                    default:
842
                        break;
843
                }
844
                $html .= "<td>".$total."</td>";
845
            }else{
846
                $html .= "<td></td>";
847
            }
848
        }
849
        $html .= "</tr>";
850
        $html .= "</tbody>";
851
        return $html;
852
    }
853
854
    function calculateTotal($type, $totals){
855
        switch($type){
856
            case 'SUM':
857
                return array_sum($totals);
858
            case 'COUNT':
859
                return count($totals);
860
            case 'AVG':
861
                return array_sum($totals)/count($totals);
862
            default:
863
                return '';
864
        }
865
    }
866
867
    private function encloseForCSV($field){
868
        return '"'.$field.'"';
869
    }
870
871
    function build_report_csv(){
872
873
        ini_set('zlib.output_compression', 'Off');
874
875
        ob_start();
876
        require_once('include/export_utils.php');
877
878
        $delimiter = getDelimiter();
879
        $csv = '';
880
        //text/comma-separated-values
881
882
        $sql = "SELECT id FROM aor_fields WHERE aor_report_id = '".$this->id."' AND deleted = 0 ORDER BY field_order ASC";
883
        $result = $this->db->query($sql);
884
885
        $fields = array();
886
        $i = 0;
887
        while ($row = $this->db->fetchByAssoc($result)) {
888
889
            $field = new AOR_Field();
890
            $field->retrieve($row['id']);
891
892
            $path = unserialize(base64_decode($field->module_path));
893
894
            $field_module = $this->report_module;
895
            if($path[0] != $this->report_module){
896
                foreach($path as $rel){
897
                    $field_module = getRelatedModule($field_module,$rel);
898
                }
899
            }
900
            $label = str_replace(' ','_',$field->label).$i;
901
            $fields[$label]['field'] = $field->field;
902
            $fields[$label]['display'] = $field->display;
903
            $fields[$label]['function'] = $field->field_function;
904
            $fields[$label]['module'] = $field_module;
905
906
907
            if($field->display){
908
                $csv.= $this->encloseForCSV($field->label);
909
                $csv .= $delimiter;
910
            }
911
            ++$i;
912
        }
913
914
        $sql = $this->build_report_query();
915
        $result = $this->db->query($sql);
916
917
        while ($row = $this->db->fetchByAssoc($result)) {
918
            $csv .= "\r\n";
919
            foreach($fields as $name => $att){
920
                if($att['display']){
921
                    if($att['function'] != '' )
922
                        $csv .= $this->encloseForCSV($row[$name]);
923
                    else
924
                        $csv .= $this->encloseForCSV(trim(strip_tags(getModuleField($att['module'], $att['field'], $att['field'], 'DetailView',$row[$name]))));
925
                    $csv .= $delimiter;
926
                }
927
            }
928
        }
929
930
        $csv= $GLOBALS['locale']->translateCharset($csv, 'UTF-8', $GLOBALS['locale']->getExportCharset());
931
932
        ob_clean();
933
        header("Pragma: cache");
934
        header("Content-type: text/comma-separated-values; charset=".$GLOBALS['locale']->getExportCharset());
935
        header("Content-Disposition: attachment; filename=\"{$this->name}.csv\"");
936
        header("Content-transfer-encoding: binary");
937
        header("Expires: Mon, 26 Jul 1997 05:00:00 GMT" );
938
        header("Last-Modified: " . TimeDate::httpTime() );
939
        header("Cache-Control: post-check=0, pre-check=0", false );
940
        header("Content-Length: ".mb_strlen($csv, '8bit'));
941
        if (!empty($sugar_config['export_excel_compatible'])) {
0 ignored issues
show
Bug introduced by
The variable $sugar_config seems to never exist, and therefore empty should always return true. Did you maybe rename this variable?

This check looks for calls to isset(...) or empty() on variables that are yet undefined. These calls will always produce the same result and can be removed.

This is most likely caused by the renaming of a variable or the removal of a function/method parameter.

Loading history...
942
            $csv==chr(255) . chr(254) . mb_convert_encoding($csv, 'UTF-16LE', 'UTF-8');
943
        }
944
        print $csv;
945
946
        sugar_cleanup(true);
947
    }
948
949
950
951
    function build_report_query($group_value ='', $extra = array()){
952
        global $beanList;
953
954
        $module = new $beanList[$this->report_module]();
955
956
        $query = '';
957
        $query_array = array();
958
959
        $query_array = $this->build_report_query_select($query_array, $group_value);
960
        if(isset($extra['where']) && $extra['where']) {
961
            $query_array['where'][] = implode(' AND ', $extra['where']) . ' AND ';
962
        }
963
        $query_array = $this->build_report_query_where($query_array);
964
965
        foreach ($query_array['select'] as $select){
966
            $query .=  ($query == '' ? 'SELECT ' : ', ').$select;
967
        }
968
969
        $query .= ' FROM '.$this->db->quoteIdentifier($module->table_name).' ';
970
971
        if(isset($query_array['join'])){
972
            foreach ($query_array['join'] as $join){
973
                $query .= $join;
974
            }
975
        }
976
        if(isset($query_array['where'])){
977
            $query_where = '';
978
            foreach ($query_array['where'] as $where){
979
                $query_where .=  ($query_where == '' ? 'WHERE ' : ' ').$where;
980
            }
981
982
            $query_where = $this->queryWhereRepair($query_where);
983
984
            $query .= ' '.$query_where;
985
        }
986
987
        if(isset($query_array['group_by'])){
988
            $query_group_by = '';
989
            foreach ($query_array['group_by'] as $group_by){
990
                $query_group_by .=  ($query_group_by == '' ? 'GROUP BY ' : ', ').$group_by;
991
            }
992
            if($query_group_by != '') {
993
                foreach ($query_array['second_group_by'] as $group_by) {
994
                    $query_group_by .= ', ' . $group_by;
995
                }
996
            }
997
            $query .= ' '.$query_group_by;
998
        }
999
1000
        if(isset($query_array['sort_by'])){
1001
            $query_sort_by = '';
1002
            foreach ($query_array['sort_by'] as $sort_by){
1003
                $query_sort_by .=  ($query_sort_by == '' ? 'ORDER BY ' : ', ').$sort_by;
1004
            }
1005
            $query .= ' '.$query_sort_by;
1006
        }
1007
        return $query;
1008
1009
    }
1010
1011
    private function queryWhereRepair($query_where) {
1012
1013
        // remove empty parenthesis and fix query syntax
1014
1015
        $safe = 0;
1016
        $query_where_clean = '';
1017
        while($query_where_clean != $query_where) {
1018
            $query_where_clean = $query_where;
1019
            $query_where = preg_replace('/\b(AND|OR)\s*\(\s*\)|[^\w+\s*]\(\s*\)/i', '', $query_where_clean);
1020
            $safe++;
1021
            if($safe>100){
1022
                $GLOBALS['log']->fatal('Invalid report query conditions');
1023
                break;
1024
            }
1025
        }
1026
1027
        return $query_where;
1028
    }
1029
1030
    function build_report_query_select($query = array(), $group_value =''){
1031
        global $beanList;
1032
1033
        if($beanList[$this->report_module]){
1034
            $module = new $beanList[$this->report_module]();
1035
1036
            $sql = "SELECT id FROM aor_fields WHERE aor_report_id = '".$this->id."' AND deleted = 0 ORDER BY field_order ASC";
1037
1038
            $result = $this->db->query($sql);
1039
            $i = 0;
1040
            while ($row = $this->db->fetchByAssoc($result)) {
1041
1042
                $field = new AOR_Field();
1043
                $field->retrieve($row['id']);
1044
1045
                $field->label = str_replace(' ','_',$field->label).$i;
1046
1047
                $path = unserialize(base64_decode($field->module_path));
1048
1049
                $field_module = $module;
1050
                $table_alias = $field_module->table_name;
1051
                $oldAlias = $table_alias;
1052
                if(!empty($path[0]) && $path[0] != $module->module_dir){
1053
                    foreach($path as $rel){
1054
                        $new_field_module = new $beanList[getRelatedModule($field_module->module_dir,$rel)];
1055
                        $oldAlias = $table_alias;
1056
                        $table_alias = $table_alias.":".$rel;
1057
                        $query = $this->build_report_query_join($rel, $table_alias, $oldAlias, $field_module, 'relationship', $query, $new_field_module);
1058
1059
                        $field_module = $new_field_module;
1060
                    }
1061
                }
1062
1063
                $data = $field_module->field_defs[$field->field];
1064
1065
                if($data['type'] == 'relate' && isset($data['id_name'])) {
1066
                    $field->field = $data['id_name'];
1067
                    $data_new = $field_module->field_defs[$field->field];
1068
                    if(isset($data_new['source']) && $data_new['source'] == 'non-db' && $data_new['type'] != 'link' && isset($data['link'])){
1069
                        $data_new['type'] = 'link';
1070
                        $data_new['relationship'] = $data['link'];
1071
                    }
1072
                    $data = $data_new;
1073
                }
1074
1075
                if($data['type'] == 'link' && $data['source'] == 'non-db') {
1076
                    $new_field_module = new $beanList[getRelatedModule($field_module->module_dir,$data['relationship'])];
1077
                    $table_alias = $data['relationship'];
1078
                    $query = $this->build_report_query_join($data['relationship'],$table_alias, $oldAlias, $field_module, 'relationship', $query, $new_field_module);
1079
                    $field_module = $new_field_module;
1080
                    $field->field = 'id';
1081
                }
1082
1083
                if($data['type'] == 'currency' && isset($field_module->field_defs['currency_id'])) {
1084
                    if((isset($field_module->field_defs['currency_id']['source']) && $field_module->field_defs['currency_id']['source'] == 'custom_fields')) {
1085
                        $query['select'][$table_alias.'_currency_id'] = $this->db->quoteIdentifier($table_alias.'_cstm').".currency_id AS '".$table_alias."_currency_id'";
1086
                        $query['second_group_by'][] = $this->db->quoteIdentifier($table_alias.'_cstm').".currency_id";
1087
                    } else {
1088
                        $query['select'][$table_alias.'_currency_id'] = $this->db->quoteIdentifier($table_alias).".currency_id AS '".$table_alias."_currency_id'";
1089
                        $query['second_group_by'][] = $this->db->quoteIdentifier($table_alias).".currency_id";
1090
                    }
1091
                }
1092
1093
                if((isset($data['source']) && $data['source'] == 'custom_fields')) {
1094
                    $select_field = $this->db->quoteIdentifier($table_alias.'_cstm').'.'.$field->field;
1095
                    $query = $this->build_report_query_join($table_alias.'_cstm', $table_alias.'_cstm',$table_alias, $field_module, 'custom', $query);
1096
                } else {
1097
                    $select_field= $this->db->quoteIdentifier($table_alias).'.'.$field->field;
1098
                }
1099
1100
                if($field->group_by == 1){
1101
                    $query['group_by'][] = $field->format ? str_replace('(%1)', '(' . $select_field . ')', preg_replace(array('/\s+/', '/Y/', '/m/', '/d/'), array(', ', 'YEAR(%1)', 'MONTH(%1)', 'DAY(%1)'), trim(preg_replace('/[^Ymd]/', ' ', $field->format)))) : $select_field;
1102
                } else {
1103
                    $query['second_group_by'][] = $field->format ? str_replace('(%1)', '(' . $select_field . ')', preg_replace(array('/\s+/', '/Y/', '/m/', '/d/'), array(', ', 'YEAR(%1)', 'MONTH(%1)', 'DAY(%1)'), trim(preg_replace('/[^Ymd]/', ' ', $field->format)))) : $select_field;
1104
                }
1105
1106
                if($field->field_function != null){
1107
                    $select_field = $field->field_function.'('.$select_field.')';
1108
                }
1109
1110
                if($field->sort_by != ''){
1111
                    $query['sort_by'][] = $select_field." ".$field->sort_by;
1112
                }
1113
1114
                $query['select'][] = $select_field ." AS '".$field->label."'";
1115
1116
                if($field->group_display == 1 && $group_value) $query['where'][] = $select_field." = '".$group_value."' AND ";
1117
                    ++$i;
1118
            }
1119
        }
1120
        return $query;
1121
    }
1122
1123
1124
    function build_report_query_join($name, $alias, $parentAlias, SugarBean $module, $type, $query = array(),SugarBean $rel_module = null ){
1125
1126
        if(!isset($query['join'][$alias])){
1127
1128
            switch ($type){
1129
                case 'custom':
1130
                    $query['join'][$alias] = 'LEFT JOIN '.$this->db->quoteIdentifier($module->get_custom_table_name()).' '.$this->db->quoteIdentifier($name).' ON '.$this->db->quoteIdentifier($parentAlias).'.id = '. $this->db->quoteIdentifier($name).'.id_c ';
1131
                    break;
1132
1133
                case 'relationship':
1134
                    if($module->load_relationship($name)){
1135
                        $params['join_type'] = 'LEFT JOIN';
1136
                        if($module->$name->relationship_type != 'one-to-many'){
1137
                            if($module->$name->getSide() == REL_LHS){
1138
                                $params['right_join_table_alias'] = $this->db->quoteIdentifier($alias);
1139
                                $params['join_table_alias'] = $this->db->quoteIdentifier($alias);
1140
                                $params['left_join_table_alias'] = $this->db->quoteIdentifier($parentAlias);
1141
                            }else{
1142
                                $params['right_join_table_alias'] = $this->db->quoteIdentifier($parentAlias);
1143
                                $params['join_table_alias'] = $this->db->quoteIdentifier($alias);
1144
                                $params['left_join_table_alias'] = $this->db->quoteIdentifier($alias);
1145
                            }
1146
1147
                        }else{
1148
                            $params['right_join_table_alias'] = $this->db->quoteIdentifier($parentAlias);
1149
                            $params['join_table_alias'] = $this->db->quoteIdentifier($alias);
1150
                            $params['left_join_table_alias'] = $this->db->quoteIdentifier($parentAlias);
1151
                        }
1152
                        $linkAlias = $parentAlias."|".$alias;
1153
                        $params['join_table_link_alias'] = $this->db->quoteIdentifier($linkAlias);
1154
                        $join = $module->$name->getJoin($params, true);
1155
                        $query['join'][$alias] = $join['join'];
1156
                        if($rel_module != null) {
1157
                            $query['join'][$alias] .= $this->build_report_access_query($rel_module, $name);
1158
                        }
1159
                    }
1160
                    break;
1161
                default:
1162
                    break;
1163
1164
            }
1165
1166
        }
1167
        return $query;
1168
    }
1169
1170
    function build_report_access_query(SugarBean $module, $alias){
1171
1172
        $module->table_name = $alias;
1173
        $where = '';
1174
        if($module->bean_implements('ACL') && ACLController::requireOwner($module->module_dir, 'list') )
1175
        {
1176
            global $current_user;
1177
            $owner_where = $module->getOwnerWhere($current_user->id);
1178
            $where = ' AND '.$owner_where;
1179
1180
        }
1181
1182
        if(file_exists('modules/SecurityGroups/SecurityGroup.php')){
1183
            /* BEGIN - SECURITY GROUPS */
1184
            if($module->bean_implements('ACL') && ACLController::requireSecurityGroup($module->module_dir, 'list') )
1185
            {
1186
                require_once('modules/SecurityGroups/SecurityGroup.php');
1187
                global $current_user;
1188
                $owner_where = $module->getOwnerWhere($current_user->id);
1189
                $group_where = SecurityGroup::getGroupWhere($alias,$module->module_dir,$current_user->id);
1190
                if(!empty($owner_where)){
1191
                    $where .= " AND (".  $owner_where." or ".$group_where.") ";
1192
                } else {
1193
                    $where .= ' AND '.  $group_where;
1194
                }
1195
            }
1196
            /* END - SECURITY GROUPS */
1197
        }
1198
1199
        return $where;
1200
    }
1201
1202
    /**
1203
     * @param array $query
1204
     * @return array
1205
     */
1206
    function build_report_query_where($query = array()){
1207
        global $beanList, $app_list_strings, $sugar_config;
1208
1209
        $closure = false;
1210
        if(!empty($query['where'])) {
1211
            $query['where'][] = '(';
1212
            $closure = true;
1213
        }
1214
1215
        if($beanList[$this->report_module]){
1216
            $module = new $beanList[$this->report_module]();
1217
1218
            $sql = "SELECT id FROM aor_conditions WHERE aor_report_id = '".$this->id."' AND deleted = 0 ORDER BY condition_order ASC";
1219
            $result = $this->db->query($sql);
1220
1221
            $tiltLogicOp = true;
1222
1223
            while ($row = $this->db->fetchByAssoc($result)) {
1224
                $condition = new AOR_Condition();
1225
                $condition->retrieve($row['id']);
1226
1227
                $path = unserialize(base64_decode($condition->module_path));
1228
1229
                $condition_module = $module;
1230
                $table_alias = $condition_module->table_name;
1231
                $oldAlias = $table_alias;
1232
                if(!empty($path[0]) && $path[0] != $module->module_dir){
1233
                    foreach($path as $rel){
1234
                        if(empty($rel)){
1235
                            continue;
1236
                        }
1237
                        // Bug: Prevents relationships from loading.
1238
                        //$rel = strtolower($rel);
1239
                        $new_condition_module = new $beanList[getRelatedModule($condition_module->module_dir,$rel)];
1240
                        $oldAlias = $table_alias;
1241
                        $table_alias = $table_alias.":".$rel;
1242
                        $query = $this->build_report_query_join($rel, $table_alias, $oldAlias, $condition_module, 'relationship', $query, $new_condition_module);
1243
                        $condition_module = $new_condition_module;
1244
                    }
1245
                }
1246
                if(isset($app_list_strings['aor_sql_operator_list'][$condition->operator])) {
1247
                    $where_set = false;
1248
1249
                    $data = $condition_module->field_defs[$condition->field];
1250
1251
                    if ($data['type'] == 'relate' && isset($data['id_name'])) {
1252
                        $condition->field = $data['id_name'];
1253
                        $data_new = $condition_module->field_defs[$condition->field];
1254
                        if (!empty($data_new['source']) && $data_new['source'] == 'non-db' && $data_new['type'] != 'link' && isset($data['link'])) {
1255
                            $data_new['type'] = 'link';
1256
                            $data_new['relationship'] = $data['link'];
1257
                        }
1258
                        $data = $data_new;
1259
                    }
1260
1261
                    if($data['type'] == 'link' && $data['source'] == 'non-db') {
1262
                        $new_field_module = new $beanList[getRelatedModule($condition_module->module_dir,$data['relationship'])];
1263
                        $table_alias = $data['relationship'];
1264
                        $query = $this->build_report_query_join($data['relationship'],$table_alias, $oldAlias, $condition_module, 'relationship', $query, $new_field_module);
1265
                        $condition_module = $new_field_module;
1266
1267
                        // Debugging: security groups conditions - It's a hack to just get the query working
1268
                        if($condition_module->module_dir = 'SecurityGroups' && count($path) > 1) {
0 ignored issues
show
Comprehensibility introduced by
Consider adding parentheses for clarity. Current Interpretation: $condition_module->modul...s' && count($path) > 1), Probably Intended Meaning: ($condition_module->modu...s') && count($path) > 1
Loading history...
1269
//                            $table_alias = 'opportunities:assigned_user_link:SecurityGroups' ;
1270
                            $table_alias = $oldAlias. ':' .$rel;
0 ignored issues
show
Bug introduced by
The variable $rel seems to be defined by a foreach iteration on line 1233. Are you sure the iterator is never empty, otherwise this variable is not defined?

It seems like you are relying on a variable being defined by an iteration:

foreach ($a as $b) {
}

// $b is defined here only if $a has elements, for example if $a is array()
// then $b would not be defined here. To avoid that, we recommend to set a
// default value for $b.


// Better
$b = 0; // or whatever default makes sense in your context
foreach ($a as $b) {
}

// $b is now guaranteed to be defined here.
Loading history...
1271
                        }
1272
                        $condition->field = 'id';
1273
                    }
1274
                    if ((isset($data['source']) && $data['source'] == 'custom_fields')) {
1275
                        $field = $this->db->quoteIdentifier($table_alias . '_cstm') . '.' . $condition->field;
1276
                        $query = $this->build_report_query_join($table_alias . '_cstm', $table_alias . '_cstm', $oldAlias, $condition_module, 'custom', $query);
1277
                    } else {
1278
                        $field = $this->db->quoteIdentifier($table_alias) . '.' . $condition->field;
1279
                    }
1280
1281
                    if (!empty($this->user_parameters[$condition->id]) && $condition->parameter) {
1282
                        $condParam = $this->user_parameters[$condition->id];
1283
                        $condition->value = $condParam['value'];
1284
                        $condition->operator = $condParam['operator'];
1285
                        $condition->value_type = $condParam['type'];
1286
                    }
1287
1288
                    switch ($condition->value_type) {
1289
                        case 'Field':
1290
                            $data = $condition_module->field_defs[$condition->value];
1291
1292
                            if ($data['type'] == 'relate' && isset($data['id_name'])) {
1293
                                $condition->value = $data['id_name'];
1294
                                $data_new = $condition_module->field_defs[$condition->value];
1295
                                if ($data_new['source'] == 'non-db' && $data_new['type'] != 'link' && isset($data['link'])) {
1296
                                    $data_new['type'] = 'link';
1297
                                    $data_new['relationship'] = $data['link'];
1298
                                }
1299
                                $data = $data_new;
1300
                            }
1301
1302
                            if ($data['type'] == 'link' && $data['source'] == 'non-db') {
1303
                                $new_field_module = new $beanList[getRelatedModule($condition_module->module_dir, $data['relationship'])];
1304
                                $table_alias = $data['relationship'];
1305
                                $query = $this->build_report_query_join($data['relationship'], $table_alias, $oldAlias, $condition_module, 'relationship', $query, $new_field_module);
1306
                                $condition_module = $new_field_module;
1307
                                $condition->field = 'id';
1308
                            }
1309
                            if ((isset($data['source']) && $data['source'] == 'custom_fields')) {
1310
                                $value = $condition_module->table_name . '_cstm.' . $condition->value;
1311
                                $query = $this->build_report_query_join($condition_module->table_name . '_cstm', $table_alias . '_cstm', $table_alias, $condition_module, 'custom', $query);
1312
                            } else {
1313
                                $value = ($table_alias ? "`$table_alias`" : $condition_module->table_name) . '.' . $condition->value;
1314
                            }
1315
                            break;
1316
1317
                        case 'Date':
1318
                            $params = unserialize(base64_decode($condition->value));
1319
1320
                            // Fix for issue #1272 - AOR_Report module cannot update Date type parameter.
1321
                            if($params == false) {
1322
                                $params = $condition->value;
1323
                            }
1324
1325
                            if ($params[0] == 'now') {
1326
                                if ($sugar_config['dbconfig']['db_type'] == 'mssql') {
1327
                                    $value = 'GetDate()';
1328
                                } else {
1329
                                    $value = 'NOW()';
1330
                                }
1331
                            } else if($params[0] == 'today'){
1332
                                if($sugar_config['dbconfig']['db_type'] == 'mssql'){
1333
                                    //$field =
1334
                                    $value  = 'CAST(GETDATE() AS DATE)';
1335
                                } else {
1336
                                    $field = 'DATE('.$field.')';
1337
                                    $value = 'Curdate()';
1338
                                }
1339
                            } else {
1340
                                $data = $condition_module->field_defs[$params[0]];
1341
                                if ((isset($data['source']) && $data['source'] == 'custom_fields')) {
1342
                                    $value = $condition_module->table_name . '_cstm.' . $params[0];
1343
                                    $query = $this->build_report_query_join($condition_module->table_name . '_cstm', $table_alias . '_cstm', $table_alias, $condition_module, 'custom', $query);
1344
                                } else {
1345
                                    $value = $condition_module->table_name . '.' . $params[0];
1346
                                }
1347
                            }
1348
1349
                            if ($params[1] != 'now') {
1350
                                switch ($params[3]) {
1351
                                    case 'business_hours';
0 ignored issues
show
Coding Style introduced by
There must be a comment when fall-through is intentional in a non-empty case body
Loading history...
1352
                                        //business hours not implemented for query, default to hours
1353
                                        $params[3] = 'hours';
1354
                                    default:
1355
                                        if ($sugar_config['dbconfig']['db_type'] == 'mssql') {
1356
                                            $value = "DATEADD(" . $params[3] . ",  " . $app_list_strings['aor_date_operator'][$params[1]] . " $params[2], $value)";
1357
                                        } else {
1358
                                            $value = "DATE_ADD($value, INTERVAL " . $app_list_strings['aor_date_operator'][$params[1]] . " $params[2] " . $params[3] . ")";
1359
                                        }
1360
                                        break;
1361
                                }
1362
                            }
1363
                            break;
1364
1365
                        case 'Multi':
1366
                            $sep = ' AND ';
1367
                            if ($condition->operator == 'Equal_To') $sep = ' OR ';
1368
                            $multi_values = unencodeMultienum($condition->value);
1369
                            if (!empty($multi_values)) {
1370
                                $value = '(';
1371
                                foreach ($multi_values as $multi_value) {
1372
                                    if ($value != '(') $value .= $sep;
1373
                                    $value .= $field . ' ' . $app_list_strings['aor_sql_operator_list'][$condition->operator] . " '" . $multi_value . "'";
1374
                                }
1375
                                $value .= ')';
1376
                            }
1377
                            $query['where'][] = ($tiltLogicOp ? '' : ($condition->logic_op ? $condition->logic_op . ' ': 'AND ')) . $value;
1378
                            $where_set = true;
1379
                            break;
1380
                        case "Period":
1381
                            if (array_key_exists($condition->value, $app_list_strings['date_time_period_list'])) {
1382
                                $params = $condition->value;
1383
                            } else {
1384
                                $params = base64_decode($condition->value);
1385
                            }
1386
                            $value = '"' . getPeriodDate($params)->format('Y-m-d H:i:s') . '"';
1387
                            break;
1388
                        case "CurrentUserID":
1389
                            global $current_user;
1390
                            $value = '"' . $current_user->id . '"';
1391
                            break;
1392
                        case 'Value':
1393
                        default:
1394
                            $value = "'" . $this->db->quote($condition->value) . "'";
1395
                            break;
1396
                    }
1397
1398
                    //handle like conditions
1399
                    Switch($condition->operator) {
1400
                        case 'Contains':
1401
                            $value = "CONCAT('%', ".$value." ,'%')";
1402
                            break;
1403
                        case 'Starts_With':
1404
                            $value = "CONCAT(".$value." ,'%')";
1405
                            break;
1406
                        case 'Ends_With':
1407
                            $value = "CONCAT('%', ".$value.")";
1408
                            break;
1409
                    }
1410
1411
                    if($condition->value_type == 'Value' && !$condition->value && $condition->operator == 'Equal_To') {
1412
                        $value = "{$value} OR {$field} IS NULL";
1413
                    }
1414
1415
                    if(!$where_set) {
1416
                        if ($condition->value_type == "Period") {
1417
                            if (array_key_exists($condition->value, $app_list_strings['date_time_period_list'])) {
1418
                                $params = $condition->value;
1419
                            } else {
1420
                                $params = base64_decode($condition->value);
1421
                            }
1422
                            $date = getPeriodEndDate($params)->format('Y-m-d H:i:s');
1423
                            $value = '"' . getPeriodDate($params)->format('Y-m-d H:i:s') . '"';
1424
1425
                            $query['where'][] = ($tiltLogicOp ? '' : ($condition->logic_op ? $condition->logic_op . ' ': 'AND '));
1426
                            $tiltLogicOp = false;
1427
1428
                            switch ($app_list_strings['aor_sql_operator_list'][$condition->operator]) {
1429
                                case "=":
1430
                                    $query['where'][] = $field . ' BETWEEN ' . $value .  ' AND ' . '"' . $date . '"';
1431
                                    break;
1432
                                case "!=":
1433
                                    $query['where'][] = $field . ' NOT BETWEEN ' . $value .  ' AND ' . '"' . $date . '"';
1434
                                    break;
1435
                                case ">":
1436
                                case "<":
1437
                                case ">=":
1438
                                case "<=":
1439
                                    $query['where'][] = $field . ' ' . $app_list_strings['aor_sql_operator_list'][$condition->operator] . ' ' . $value;
1440
                                    break;
1441
                            }
1442
                        } else {
1443
                            if (!$where_set) $query['where'][] = ($tiltLogicOp ? '' : ($condition->logic_op ? $condition->logic_op . ' ': 'AND ')) . $field . ' ' . $app_list_strings['aor_sql_operator_list'][$condition->operator] . ' ' . $value;
1444
                        }
1445
                    }
1446
                    $tiltLogicOp = false;
1447
                }
1448
                else if($condition->parenthesis) {
1449
                    if($condition->parenthesis == 'START') {
1450
                        $query['where'][] = ($tiltLogicOp ? '' : ($condition->logic_op ? $condition->logic_op . ' ' : 'AND ')) .  '(';
1451
                        $tiltLogicOp = true;
1452
                    }
1453
                    else {
1454
                        $query['where'][] = ')';
1455
                        $tiltLogicOp = false;
1456
                    }
1457
                }
1458
                else {
1459
                    $GLOBALS['log']->debug('illegal condition');
1460
                }
1461
1462
            }
1463
1464
            if(isset($query['where']) && $query['where']) {
1465
                array_unshift($query['where'], '(');
1466
                $query['where'][] = ') AND ';
1467
            }
1468
            $query['where'][] = $module->table_name.".deleted = 0 ".$this->build_report_access_query($module, $module->table_name);
1469
1470
        }
1471
1472
        if($closure) {
1473
            $query['where'][] = ')';
1474
        }
1475
1476
        return $query;
1477
    }
1478
1479
}
1480