INFRA-132 - Move stray comments into docblocks
[civicrm-core.git] / CRM / Report / Form.php
CommitLineData
6a488035 1<?php
6a488035 2/*
36241b02 3 +--------------------------------------------------------------------+
39de6fd5 4 | CiviCRM version 4.6 |
db4cd986 5 +--------------------------------------------------------------------+
06b69b18 6 | Copyright CiviCRM LLC (c) 2004-2014 |
db4cd986 7 +--------------------------------------------------------------------+
6a488035
TO
8 | This file is a part of CiviCRM. |
9 | |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
13 | |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
18 | |
19 | You should have received a copy of the GNU Affero General Public |
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
36241b02 25 +--------------------------------------------------------------------+
6a488035
TO
26*/
27
28/**
29 *
30 * @package CRM
06b69b18 31 * @copyright CiviCRM LLC (c) 2004-2014
6a488035
TO
32 * $Id$
33 *
34 */
35class CRM_Report_Form extends CRM_Core_Form {
7da04cde 36 const ROW_COUNT_LIMIT = 50;
6a488035
TO
37
38 /**
39 * Operator types - used for displaying filter elements
40 */
7da04cde 41 const
9d72cede 42 OP_INT = 1,
6a488035 43 OP_STRING = 2,
9d72cede 44 OP_DATE = 4,
6a488035 45 OP_DATETIME = 5,
9d72cede 46 OP_FLOAT = 8,
6a488035
TO
47 OP_SELECT = 64,
48 OP_MULTISELECT = 65,
49 OP_MULTISELECT_SEPARATOR = 66,
2107cde9
CW
50 OP_MONTH = 128,
51 OP_ENTITYREF = 256;
6a488035
TO
52
53 /**
54 * The id of the report instance
55 *
56 * @var integer
57 */
58 protected $_id;
59
60 /**
61 * The id of the report template
62 *
63 * @var integer;
64 */
65 protected $_templateID;
66
67 /**
68 * The report title
69 *
70 * @var string
71 */
72 protected $_title;
73 protected $_noFields = FALSE;
74
75 /**
76 * The set of all columns in the report. An associative array
77 * with column name as the key and attribues as the value
78 *
79 * @var array
80 */
81 protected $_columns = array();
82
83 /**
84 * The set of filters in the report
85 *
86 * @var array
87 */
88 protected $_filters = array();
89
90 /**
91 * The set of optional columns in the report
92 *
93 * @var array
94 */
95 protected $_options = array();
96
97 protected $_defaults = array();
98
99 /*
100 * By default most reports hide contact id.
101 * Setting this to true makes it available
102 */
103 protected $_exposeContactID = TRUE;
104
105 /**
106 * Set of statistic fields
107 *
108 * @var array
109 */
110 protected $_statFields = array();
111
112 /**
113 * Set of statistics data
114 *
115 * @var array
116 */
117 protected $_statistics = array();
118
119 /**
120 * List of fields not to be repeated during display
121 *
122 * @var array
123 */
124 protected $_noRepeats = array();
125
126 /**
127 * List of fields not to be displayed
128 *
129 * @var array
130 */
131 protected $_noDisplay = array();
132
133 /**
134 * Object type that a custom group extends
135 *
136 * @var null
137 */
138 protected $_customGroupExtends = NULL;
aa1aa08e 139 protected $_customGroupExtendsJoin = array();
6a488035
TO
140 protected $_customGroupFilters = TRUE;
141 protected $_customGroupGroupBy = FALSE;
9d72cede 142 protected $_customGroupJoin = 'LEFT JOIN';
6a488035
TO
143
144 /**
100fef9d 145 * Build tags filter
6a488035
TO
146 */
147 protected $_tagFilter = FALSE;
148
149 /**
353ffa53 150 * <<<<<<< HEAD
100fef9d 151 * Build groups filter
353ffa53 152 * =======
ed795723
JM
153 * specify entity table for tags filter
154 *
155 */
156 protected $_tagFilterTable = 'civicrm_contact';
157
6a488035
TO
158 /**
159 * build groups filter
160 *
353ffa53 161 * >>>>>>> 098a61330d0fe12153370741ec98cb1172ece849
6a488035
TO
162 */
163 protected $_groupFilter = FALSE;
164
165 /**
166 * Navigation fields
167 *
168 * @var array
169 */
170 public $_navigation = array();
171
172 public $_drilldownReport = array();
173
174 /**
175 * An attribute for checkbox/radio form field layout
176 *
177 * @var array
178 */
179 protected $_fourColumnAttribute = array(
9d72cede
EM
180 '</td><td width="25%">',
181 '</td><td width="25%">',
182 '</td><td width="25%">',
183 '</tr><tr><td>',
6a488035
TO
184 );
185
186 protected $_force = 1;
187
188 protected $_params = NULL;
189 protected $_formValues = NULL;
190 protected $_instanceValues = NULL;
191
192 protected $_instanceForm = FALSE;
193 protected $_criteriaForm = FALSE;
194
195 protected $_instanceButtonName = NULL;
196 protected $_createNewButtonName = NULL;
197 protected $_printButtonName = NULL;
198 protected $_pdfButtonName = NULL;
199 protected $_csvButtonName = NULL;
200 protected $_groupButtonName = NULL;
201 protected $_chartButtonName = NULL;
202 protected $_csvSupported = TRUE;
203 protected $_add2groupSupported = TRUE;
204 protected $_groups = NULL;
9b0380d9 205 protected $_grandFlag = FALSE;
6a488035
TO
206 protected $_rowsFound = NULL;
207 protected $_selectAliases = array();
208 protected $_rollup = NULL;
6f900755
E
209
210 /**
211 * SQL Limit clause
212 * @var string
213 */
6a488035 214 protected $_limit = NULL;
6f900755
E
215
216 /**
217 * This can be set to specify a limit to the number of rows
218 * Since it is currently envisaged as part of the api usage it is only being applied
219 * when $_output mode is not 'html' or 'group' so as not to have to interpret / mess with that part
220 * of the code (see limit() fn
221 * @var integer
222 */
223 protected $_limitValue = NULL;
224
225 /**
226 * This can be set to specify row offset
227 * See notes on _limitValue
228 * @var integer
229 */
230 protected $_offsetValue = NULL;
6a488035
TO
231 protected $_sections = NULL;
232 protected $_autoIncludeIndexedFieldsAsOrderBys = 0;
233 protected $_absoluteUrl = FALSE;
234
ae555e90
DS
235 /**
236 * Flag to indicate if result-set is to be stored in a class variable which could be retrieved using getResultSet() method.
237 *
238 * @var boolean
239 */
240 protected $_storeResultSet = FALSE;
241
242 /**
243 * When _storeResultSet Flag is set use this var to store result set in form of array
244 *
245 * @var boolean
246 */
247 protected $_resultSet = array();
248
6a488035
TO
249 /**
250 * To what frequency group-by a date column
251 *
252 * @var array
253 */
254 protected $_groupByDateFreq = array(
255 'MONTH' => 'Month',
256 'YEARWEEK' => 'Week',
257 'QUARTER' => 'Quarter',
258 'YEAR' => 'Year',
259 );
260
261 /**
262 * Variables to hold the acl inner join and where clause
263 */
264 protected $_aclFrom = NULL;
265 protected $_aclWhere = NULL;
266
267 /**
268 * Array of DAO tables having columns included in SELECT or ORDER BY clause
269 *
270 * @var array
271 */
272 protected $_selectedTables;
273
c58f66e0 274 /**
100fef9d 275 * Outputmode e.g 'print', 'csv', 'pdf'
c58f66e0
E
276 * @var string
277 */
f63fae91 278 protected $_outputMode;
c58f66e0 279
6a488035
TO
280 public $_having = NULL;
281 public $_select = NULL;
1f220d30 282 public $_selectClauses = array();
6a488035
TO
283 public $_columnHeaders = array();
284 public $_orderBy = NULL;
f2947aea 285 public $_orderByFields = array();
9d72cede 286 public $_orderByArray = array();
6a488035 287 public $_groupBy = NULL;
adfe2750 288 public $_whereClauses = array();
289 public $_havingClauses = array();
1c4d8c3e 290
dbb4a0f9 291 /**
100fef9d 292 * DashBoardRowCount Dashboard row count
dbb4a0f9
PN
293 * @var Integer
294 */
295 public $_dashBoardRowCount;
296
c58f66e0
E
297 /**
298 * Is this being called without a form controller (ie. the report is being render outside the normal form
299 * - e.g the api is retrieving the rows
300 * @var boolean
301 */
302 public $noController = FALSE;
303
7a961f19 304 /**
305 * Variable to hold the currency alias
306 */
307 protected $_currencyColumn = NULL;
6a488035
TO
308
309 /**
6a488035 310 */
00be9182 311 public function __construct() {
6a488035
TO
312 parent::__construct();
313
314 // build tag filter
315 if ($this->_tagFilter) {
316 $this->buildTagFilter();
317 }
318 if ($this->_exposeContactID) {
319 if (array_key_exists('civicrm_contact', $this->_columns)) {
320 $this->_columns['civicrm_contact']['fields']['exposed_id'] = array(
321 'name' => 'id',
322 'title' => 'Contact ID',
323 'no_repeat' => TRUE,
324 );
325 }
326 }
327
328 if ($this->_groupFilter) {
329 $this->buildGroupFilter();
330 }
331
332 // Get all custom groups
cd43c5e3 333 $allGroups = CRM_Core_PseudoConstant::get('CRM_Core_DAO_CustomField', 'custom_group_id');
6a488035 334
4f8ec8be
DS
335 // Get the custom groupIds for which the user has VIEW permission
336 // If the user has 'access all custom data' permission, we'll leave $permCustomGroupIds empty
337 // and addCustomDataToColumns() will allow access to all custom groups.
338 $permCustomGroupIds = array();
339 if (!CRM_Core_Permission::check('access all custom data')) {
340 $permCustomGroupIds = CRM_ACL_API::group(CRM_Core_Permission::VIEW, NULL, 'civicrm_custom_group', $allGroups, NULL);
341 // do not allow custom data for reports if user doesn't have
342 // permission to access custom data.
343 if (!empty($this->_customGroupExtends) && empty($permCustomGroupIds)) {
344 $this->_customGroupExtends = array();
345 }
6a488035
TO
346 }
347
348 // merge custom data columns to _columns list, if any
349 $this->addCustomDataToColumns(TRUE, $permCustomGroupIds);
350
351 // add / modify display columns, filters ..etc
352 CRM_Utils_Hook::alterReportVar('columns', $this->_columns, $this);
7a961f19 353
354 //assign currencyColumn variable to tpl
355 $this->assign('currencyColumn', $this->_currencyColumn);
6a488035
TO
356 }
357
00be9182 358 public function preProcessCommon() {
8f1445ea
DL
359 $this->_force =
360 CRM_Utils_Request::retrieve(
361 'force',
362 'Boolean',
363 CRM_Core_DAO::$_nullObject
364 );
77b97be7 365
dbb4a0f9
PN
366 $this->_dashBoardRowCount =
367 CRM_Utils_Request::retrieve(
368 'rowCount',
369 'Integer',
370 CRM_Core_DAO::$_nullObject
371 );
6a488035
TO
372
373 $this->_section = CRM_Utils_Request::retrieve('section', 'Integer', CRM_Core_DAO::$_nullObject);
374
375 $this->assign('section', $this->_section);
376 CRM_Core_Region::instance('page-header')->add(array(
377 'markup' => sprintf('<!-- Report class: [%s] -->', htmlentities(get_class($this))),
378 ));
9d72cede 379 if (!$this->noController) {
c58f66e0 380 $this->setID($this->get('instanceId'));
6a488035 381
6a488035 382 if (!$this->_id) {
c58f66e0
E
383 $this->setID(CRM_Report_Utils_Report::getInstanceID());
384 if (!$this->_id) {
9d72cede 385 $this->setID(CRM_Report_Utils_Report::getInstanceIDForPath());
c58f66e0 386 }
6a488035 387 }
6a488035 388
c58f66e0
E
389 // set qfkey so that pager picks it up and use it in the "Next > Last >>" links.
390 // FIXME: Note setting it in $_GET doesn't work, since pager generates link based on QUERY_STRING
391 $_SERVER['QUERY_STRING'] .= "&qfKey={$this->controller->_key}";
392 }
6a488035
TO
393
394 if ($this->_id) {
395 $this->assign('instanceId', $this->_id);
396 $params = array('id' => $this->_id);
397 $this->_instanceValues = array();
0b25329b 398 CRM_Core_DAO::commonRetrieve('CRM_Report_DAO_ReportInstance',
6a488035
TO
399 $params,
400 $this->_instanceValues
401 );
402 if (empty($this->_instanceValues)) {
403 CRM_Core_Error::fatal("Report could not be loaded.");
404 }
7cab1323 405 $this->_title = $this->_instanceValues['title'];
6a488035
TO
406 if (!empty($this->_instanceValues['permission']) &&
407 (!(CRM_Core_Permission::check($this->_instanceValues['permission']) ||
408 CRM_Core_Permission::check('administer Reports')
409 ))
410 ) {
411 CRM_Utils_System::permissionDenied();
412 CRM_Utils_System::civiExit();
413 }
414
415 $formValues = CRM_Utils_Array::value('form_values', $this->_instanceValues);
416 if ($formValues) {
417 $this->_formValues = unserialize($formValues);
418 }
419 else {
420 $this->_formValues = NULL;
421 }
422
423 // lets always do a force if reset is found in the url.
a7488080 424 if (!empty($_REQUEST['reset'])) {
6a488035
TO
425 $this->_force = 1;
426 }
427
428 // set the mode
429 $this->assign('mode', 'instance');
430 }
c58f66e0 431 elseif (!$this->noController) {
6a488035
TO
432 list($optionValueID, $optionValue) = CRM_Report_Utils_Report::getValueIDFromUrl();
433 $instanceCount = CRM_Report_Utils_Report::getInstanceCount($optionValue);
434 if (($instanceCount > 0) && $optionValueID) {
435 $this->assign('instanceUrl',
436 CRM_Utils_System::url('civicrm/report/list',
437 "reset=1&ovid=$optionValueID"
438 )
439 );
440 }
441 if ($optionValueID) {
442 $this->_description = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_OptionValue', $optionValueID, 'description');
443 }
444
445 // set the mode
446 $this->assign('mode', 'template');
447 }
448
449 // lets display the Report Settings section
450 $this->_instanceForm = $this->_force || $this->_id || (!empty($_POST));
451
452 // Do not display Report Settings section if administer Reports permission is absent OR
453 // if report instance is reserved and administer reserved reports absent
454 if (!CRM_Core_Permission::check('administer Reports') ||
9d72cede
EM
455 ($this->_instanceValues['is_reserved'] &&
456 !CRM_Core_Permission::check('administer reserved reports'))
457 ) {
6a488035
TO
458 $this->_instanceForm = FALSE;
459 }
460
461 $this->assign('criteriaForm', FALSE);
462 // Display Report Criteria section if user has access Report Criteria OR administer Reports AND report instance is not reserved
9d72cede
EM
463 if (CRM_Core_Permission::check('administer Reports') ||
464 CRM_Core_Permission::check('access Report Criteria')
465 ) {
466 if (!$this->_instanceValues['is_reserved'] ||
467 CRM_Core_Permission::check('administer reserved reports')
468 ) {
6a488035
TO
469 $this->assign('criteriaForm', TRUE);
470 $this->_criteriaForm = TRUE;
471 }
472 }
473
474 $this->_instanceButtonName = $this->getButtonName('submit', 'save');
475 $this->_createNewButtonName = $this->getButtonName('submit', 'next');
476 $this->_printButtonName = $this->getButtonName('submit', 'print');
477 $this->_pdfButtonName = $this->getButtonName('submit', 'pdf');
478 $this->_csvButtonName = $this->getButtonName('submit', 'csv');
479 $this->_groupButtonName = $this->getButtonName('submit', 'group');
480 $this->_chartButtonName = $this->getButtonName('submit', 'chart');
481 }
482
00be9182 483 public function addBreadCrumb() {
8f1445ea
DL
484 $breadCrumbs =
485 array(
486 array(
487 'title' => ts('Report Templates'),
488 'url' => CRM_Utils_System::url('civicrm/admin/report/template/list', 'reset=1'),
21dfd5f5 489 ),
8f1445ea 490 );
6a488035
TO
491
492 CRM_Utils_System::appendBreadCrumb($breadCrumbs);
493 }
494
00be9182 495 public function preProcess() {
8f1445ea 496 $this->preProcessCommon();
6a488035 497
6a488035 498 if (!$this->_id) {
29fc2b79 499 $this->addBreadCrumb();
6a488035
TO
500 }
501
502 foreach ($this->_columns as $tableName => $table) {
503 // set alias
504 if (!isset($table['alias'])) {
9d72cede
EM
505 $this->_columns[$tableName]['alias'] = substr($tableName, 8) .
506 '_civireport';
6a488035
TO
507 }
508 else {
509 $this->_columns[$tableName]['alias'] = $table['alias'] . '_civireport';
510 }
511
512 $this->_aliases[$tableName] = $this->_columns[$tableName]['alias'];
513
c75e90fa 514 $daoOrBaoName = NULL;
6a488035
TO
515 // higher preference to bao object
516 if (array_key_exists('bao', $table)) {
c75e90fa 517 $daoOrBaoName = $table['bao'];
9d72cede 518 $expFields = $daoOrBaoName::exportableFields();
6a488035 519 }
9d72cede 520 elseif (array_key_exists('dao', $table)) {
c75e90fa 521 $daoOrBaoName = $table['dao'];
9d72cede 522 $expFields = $daoOrBaoName::export();
6a488035 523 }
9d72cede 524 else {
8edb9849 525 $expFields = array();
526 }
6a488035
TO
527
528 $doNotCopy = array('required');
529
530 $fieldGroups = array('fields', 'filters', 'group_bys', 'order_bys');
531 foreach ($fieldGroups as $fieldGrp) {
a7488080 532 if (!empty($table[$fieldGrp]) && is_array($table[$fieldGrp])) {
6a488035
TO
533 foreach ($table[$fieldGrp] as $fieldName => $field) {
534 // $name is the field name used to reference the BAO/DAO export fields array
535 $name = isset($field['name']) ? $field['name'] : $fieldName;
536
537 // Sometimes the field name key in the BAO/DAO export fields array is
538 // different from the actual database field name.
539 // Unset $field['name'] so that actual database field name can be obtained
540 // from the BAO/DAO export fields array.
541 unset($field['name']);
542
543 if (array_key_exists($name, $expFields)) {
544 foreach ($doNotCopy as $dnc) {
545 // unset the values we don't want to be copied.
546 unset($expFields[$name][$dnc]);
547 }
548 if (empty($field)) {
549 $this->_columns[$tableName][$fieldGrp][$fieldName] = $expFields[$name];
550 }
551 else {
552 foreach ($expFields[$name] as $property => $val) {
553 if (!array_key_exists($property, $field)) {
554 $this->_columns[$tableName][$fieldGrp][$fieldName][$property] = $val;
555 }
556 }
557 }
558 }
559
560 // fill other vars
a7488080 561 if (!empty($field['no_repeat'])) {
6a488035
TO
562 $this->_noRepeats[] = "{$tableName}_{$fieldName}";
563 }
a7488080 564 if (!empty($field['no_display'])) {
6a488035
TO
565 $this->_noDisplay[] = "{$tableName}_{$fieldName}";
566 }
567
568 // set alias = table-name, unless already set
569 $alias = isset($field['alias']) ? $field['alias'] : (isset($this->_columns[$tableName]['alias']) ?
9d72cede 570 $this->_columns[$tableName]['alias'] : $tableName
6a488035
TO
571 );
572 $this->_columns[$tableName][$fieldGrp][$fieldName]['alias'] = $alias;
573
574 // set name = fieldName, unless already set
575 if (!isset($this->_columns[$tableName][$fieldGrp][$fieldName]['name'])) {
576 $this->_columns[$tableName][$fieldGrp][$fieldName]['name'] = $name;
577 }
578
579 // set dbAlias = alias.name, unless already set
580 if (!isset($this->_columns[$tableName][$fieldGrp][$fieldName]['dbAlias'])) {
9d72cede
EM
581 $this->_columns[$tableName][$fieldGrp][$fieldName]['dbAlias'] =
582 $alias . '.' .
583 $this->_columns[$tableName][$fieldGrp][$fieldName]['name'];
6a488035
TO
584 }
585
c75e90fa 586 // a few auto fills for filters
c93f6d83 587 if ($fieldGrp == 'filters') {
c75e90fa
DS
588 // fill operator types
589 if (!array_key_exists('operatorType', $this->_columns[$tableName][$fieldGrp][$fieldName])) {
590 switch (CRM_Utils_Array::value('type', $this->_columns[$tableName][$fieldGrp][$fieldName])) {
591 case CRM_Utils_Type::T_MONEY:
592 case CRM_Utils_Type::T_FLOAT:
593 $this->_columns[$tableName][$fieldGrp][$fieldName]['operatorType'] = CRM_Report_Form::OP_FLOAT;
594 break;
ea100cb5 595
c75e90fa
DS
596 case CRM_Utils_Type::T_INT:
597 $this->_columns[$tableName][$fieldGrp][$fieldName]['operatorType'] = CRM_Report_Form::OP_INT;
598 break;
ea100cb5 599
c75e90fa 600 case CRM_Utils_Type::T_DATE:
c93f6d83 601 $this->_columns[$tableName][$fieldGrp][$fieldName]['operatorType'] = CRM_Report_Form::OP_DATE;
c75e90fa 602 break;
ea100cb5 603
c75e90fa
DS
604 case CRM_Utils_Type::T_BOOLEAN:
605 $this->_columns[$tableName][$fieldGrp][$fieldName]['operatorType'] = CRM_Report_Form::OP_SELECT;
606 if (!array_key_exists('options', $this->_columns[$tableName][$fieldGrp][$fieldName])) {
c93f6d83 607 $this->_columns[$tableName][$fieldGrp][$fieldName]['options'] =
9d72cede
EM
608 array(
609 '' => ts('Any'),
610 '0' => ts('No'),
21dfd5f5 611 '1' => ts('Yes'),
9d72cede 612 );
c75e90fa
DS
613 }
614 break;
ea100cb5 615
c75e90fa 616 default:
c93f6d83 617 if ($daoOrBaoName &&
9d72cede
EM
618 array_key_exists('pseudoconstant', $this->_columns[$tableName][$fieldGrp][$fieldName])
619 ) {
c75e90fa
DS
620 // with multiple options operator-type is generally multi-select
621 $this->_columns[$tableName][$fieldGrp][$fieldName]['operatorType'] = CRM_Report_Form::OP_MULTISELECT;
622 if (!array_key_exists('options', $this->_columns[$tableName][$fieldGrp][$fieldName])) {
623 // fill options
624 $this->_columns[$tableName][$fieldGrp][$fieldName]['options'] = CRM_Core_PseudoConstant::get($daoOrBaoName, $fieldName);
625 }
626 }
627 break;
628 }
6a488035
TO
629 }
630 }
631 }
632 }
633 }
634
635 // copy filters to a separate handy variable
636 if (array_key_exists('filters', $table)) {
637 $this->_filters[$tableName] = $this->_columns[$tableName]['filters'];
638 }
639
640 if (array_key_exists('group_bys', $table)) {
641 $groupBys[$tableName] = $this->_columns[$tableName]['group_bys'];
642 }
643
644 if (array_key_exists('fields', $table)) {
645 $reportFields[$tableName] = $this->_columns[$tableName]['fields'];
646 }
647 }
648
649 if ($this->_force) {
650 $this->setDefaultValues(FALSE);
651 }
652
8f1445ea
DL
653 CRM_Report_Utils_Get::processFilter($this->_filters, $this->_defaults);
654 CRM_Report_Utils_Get::processGroupBy($groupBys, $this->_defaults);
655 CRM_Report_Utils_Get::processFields($reportFields, $this->_defaults);
6a488035
TO
656 CRM_Report_Utils_Get::processChart($this->_defaults);
657
658 if ($this->_force) {
659 $this->_formValues = $this->_defaults;
660 $this->postProcess();
661 }
662 }
663
74cf4551
EM
664 /**
665 * @param bool $freeze
666 *
667 * @return array
668 */
00be9182 669 public function setDefaultValues($freeze = TRUE) {
6a488035
TO
670 $freezeGroup = array();
671
672 // FIXME: generalizing form field naming conventions would reduce
673 // lots of lines below.
674 foreach ($this->_columns as $tableName => $table) {
675 if (array_key_exists('fields', $table)) {
676 foreach ($table['fields'] as $fieldName => $field) {
a7488080 677 if (empty($field['no_display'])) {
6a488035
TO
678 if (isset($field['required'])) {
679 // set default
680 $this->_defaults['fields'][$fieldName] = 1;
681
682 if ($freeze) {
683 // find element object, so that we could use quickform's freeze method
684 // for required elements
8f1445ea 685 $obj = $this->getElementFromGroup("fields", $fieldName);
6a488035
TO
686 if ($obj) {
687 $freezeGroup[] = $obj;
688 }
689 }
690 }
691 elseif (isset($field['default'])) {
692 $this->_defaults['fields'][$fieldName] = $field['default'];
693 }
694 }
695 }
696 }
697
698 if (array_key_exists('group_bys', $table)) {
699 foreach ($table['group_bys'] as $fieldName => $field) {
700 if (isset($field['default'])) {
a7488080 701 if (!empty($field['frequency'])) {
6a488035
TO
702 $this->_defaults['group_bys_freq'][$fieldName] = 'MONTH';
703 }
704 $this->_defaults['group_bys'][$fieldName] = $field['default'];
705 }
706 }
707 }
708 if (array_key_exists('filters', $table)) {
709 foreach ($table['filters'] as $fieldName => $field) {
710 if (isset($field['default'])) {
9d72cede
EM
711 if (CRM_Utils_Array::value('type', $field) & CRM_Utils_Type::T_DATE
712 ) {
713 if (is_array($field['default'])) {
b0e34e7c 714 $this->_defaults["{$fieldName}_from"] = CRM_Utils_Array::value('from', $field['default']);
715 $this->_defaults["{$fieldName}_to"] = CRM_Utils_Array::value('to', $field['default']);
716 $this->_defaults["{$fieldName}_relative"] = 0;
717 }
9d72cede 718 else {
b0e34e7c 719 $this->_defaults["{$fieldName}_relative"] = $field['default'];
720 }
6a488035
TO
721 }
722 else {
723 $this->_defaults["{$fieldName}_value"] = $field['default'];
724 }
725 }
726 //assign default value as "in" for multiselect
727 //operator, To freeze the select element
9d72cede
EM
728 if (CRM_Utils_Array::value('operatorType', $field) ==
729 CRM_Report_Form::OP_MULTISELECT
730 ) {
6a488035
TO
731 $this->_defaults["{$fieldName}_op"] = 'in';
732 }
9d72cede
EM
733 if (CRM_Utils_Array::value('operatorType', $field) ==
734 CRM_Report_Form::OP_ENTITYREF
735 ) {
2107cde9
CW
736 $this->_defaults["{$fieldName}_op"] = 'in';
737 }
9d72cede
EM
738 elseif (CRM_Utils_Array::value('operatorType', $field) ==
739 CRM_Report_Form::OP_MULTISELECT_SEPARATOR
740 ) {
6a488035
TO
741 $this->_defaults["{$fieldName}_op"] = 'mhas';
742 }
743 elseif ($op = CRM_Utils_Array::value('default_op', $field)) {
744 $this->_defaults["{$fieldName}_op"] = $op;
745 }
746 }
747 }
748
8f1445ea
DL
749 if (
750 array_key_exists('order_bys', $table) &&
6a488035
TO
751 is_array($table['order_bys'])
752 ) {
6a488035 753 if (!array_key_exists('order_bys', $this->_defaults)) {
6a488035
TO
754 $this->_defaults['order_bys'] = array();
755 }
756 foreach ($table['order_bys'] as $fieldName => $field) {
8cc574cf 757 if (!empty($field['default']) || !empty($field['default_order']) ||
9d72cede
EM
758 CRM_Utils_Array::value('default_is_section', $field) ||
759 !empty($field['default_weight'])
760 ) {
6a488035
TO
761 $order_by = array(
762 'column' => $fieldName,
763 'order' => CRM_Utils_Array::value('default_order', $field, 'ASC'),
764 'section' => CRM_Utils_Array::value('default_is_section', $field, 0),
765 );
766
a7488080 767 if (!empty($field['default_weight'])) {
6a488035
TO
768 $this->_defaults['order_bys'][(int) $field['default_weight']] = $order_by;
769 }
770 else {
771 array_unshift($this->_defaults['order_bys'], $order_by);
772 }
773 }
774 }
775 }
776
777 foreach ($this->_options as $fieldName => $field) {
778 if (isset($field['default'])) {
779 $this->_defaults['options'][$fieldName] = $field['default'];
780 }
781 }
782 }
783
784 if (!empty($this->_submitValues)) {
785 $this->preProcessOrderBy($this->_submitValues);
786 }
787 else {
788 $this->preProcessOrderBy($this->_defaults);
789 }
790
791 // lets finish freezing task here itself
792 if (!empty($freezeGroup)) {
793 foreach ($freezeGroup as $elem) {
794 $elem->freeze();
795 }
796 }
797
798 if ($this->_formValues) {
799 $this->_defaults = array_merge($this->_defaults, $this->_formValues);
800 }
801
802 if ($this->_instanceValues) {
803 $this->_defaults = array_merge($this->_defaults, $this->_instanceValues);
804 }
805
806 CRM_Report_Form_Instance::setDefaultValues($this, $this->_defaults);
807
808 return $this->_defaults;
809 }
810
74cf4551
EM
811 /**
812 * @param $group
100fef9d 813 * @param string $grpFieldName
74cf4551
EM
814 *
815 * @return bool
816 */
00be9182 817 public function getElementFromGroup($group, $grpFieldName) {
6a488035
TO
818 $eleObj = $this->getElement($group);
819 foreach ($eleObj->_elements as $index => $obj) {
820 if ($grpFieldName == $obj->_attributes['name']) {
821 return $obj;
822 }
823 }
824 return FALSE;
825 }
826
c58f66e0
E
827 /**
828 * Setter for $_params
9d72cede 829 *
c58f66e0
E
830 * @param array $params
831 */
00be9182 832 public function setParams($params) {
c58f66e0
E
833 $this->_params = $params;
834 }
835
836 /**
837 * Setter for $_id
2a6da8d7 838 *
100fef9d 839 * @param int $instanceid
c58f66e0 840 */
00be9182 841 public function setID($instanceid) {
c58f66e0
E
842 $this->_id = $instanceid;
843 }
844
845 /**
846 * Setter for $_force
9d72cede
EM
847 *
848 * @param $isForce
849 */
00be9182 850 public function setForce($isForce) {
c58f66e0
E
851 $this->_force = $isForce;
852 }
6f900755
E
853
854 /**
855 * Setter for $_limitValue
9d72cede 856 *
d3e86119 857 * @param int $_limitValue
6f900755 858 */
00be9182 859 public function setLimitValue($_limitValue) {
6f900755
E
860 $this->_limitValue = $_limitValue;
861 }
862
863 /**
864 * Setter for $_offsetValue
9d72cede 865 *
d3e86119 866 * @param int $_offsetValue
6f900755 867 */
00be9182 868 public function setOffsetValue($_offsetValue) {
6f900755
E
869 $this->_offsetValue = $_offsetValue;
870 }
871
c58f66e0
E
872 /**
873 * Getter for $_defaultValues
a6c01b45 874 * @return array
c58f66e0 875 */
00be9182 876 public function getDefaultValues() {
c58f66e0
E
877 return $this->_defaults;
878 }
879
00be9182 880 public function addColumns() {
6a488035
TO
881 $options = array();
882 $colGroups = NULL;
883 foreach ($this->_columns as $tableName => $table) {
884 if (array_key_exists('fields', $table)) {
885 foreach ($table['fields'] as $fieldName => $field) {
8bdc861c 886 $groupTitle = '';
a7488080 887 if (empty($field['no_display'])) {
9d72cede 888 foreach (array('table', 'field') as $var) {
8bdc861c
DS
889 if (!empty(${$var}['grouping'])) {
890 if (!is_array(${$var}['grouping'])) {
891 $tableName = ${$var}['grouping'];
9d72cede
EM
892 }
893 else {
b80138e0
DS
894 $tableName = array_keys(${$var}['grouping']);
895 $tableName = $tableName[0];
896 $groupTitle = array_values(${$var}['grouping']);
897 $groupTitle = $groupTitle[0];
8bdc861c
DS
898 }
899 }
6a488035 900 }
8bdc861c
DS
901
902 if (!$groupTitle && isset($table['group_title'])) {
903 $groupTitle = $table['group_title'];
6a488035 904 }
6a488035 905
8bdc861c 906 $colGroups[$tableName]['fields'][$fieldName] = CRM_Utils_Array::value('title', $field);
8cc574cf 907 if ($groupTitle && empty($colGroups[$tableName]['group_title'])) {
8bdc861c 908 $colGroups[$tableName]['group_title'] = $groupTitle;
6a488035
TO
909 }
910
911 $options[$fieldName] = CRM_Utils_Array::value('title', $field);
912 }
913 }
914 }
915 }
916
917 $this->addCheckBox("fields", ts('Select Columns'), $options, NULL,
918 NULL, NULL, NULL, $this->_fourColumnAttribute, TRUE
919 );
920 $this->assign('colGroups', $colGroups);
921 }
922
00be9182 923 public function addFilters() {
c927c151 924 $filters = array();
6a488035
TO
925 $count = 1;
926 foreach ($this->_filters as $table => $attributes) {
927 foreach ($attributes as $fieldName => $field) {
928 // get ready with option value pair
1b36206c 929 // @ todo being able to specific options for a field (e.g a date field) in the field spec as an array rather than an override
930 // would be useful
931 $operations = $this->getOperationPair(
8f1445ea 932 CRM_Utils_Array::value('operatorType', $field),
1b36206c 933 $fieldName);
6a488035
TO
934
935 $filters[$table][$fieldName] = $field;
936
937 switch (CRM_Utils_Array::value('operatorType', $field)) {
938 case CRM_Report_Form::OP_MONTH:
9d72cede
EM
939 if (!array_key_exists('options', $field) ||
940 !is_array($field['options']) || empty($field['options'])
941 ) {
6a488035
TO
942 // If there's no option list for this filter, define one.
943 $field['options'] = array(
944 1 => ts('January'),
945 2 => ts('February'),
946 3 => ts('March'),
947 4 => ts('April'),
948 5 => ts('May'),
949 6 => ts('June'),
950 7 => ts('July'),
951 8 => ts('August'),
952 9 => ts('September'),
953 10 => ts('October'),
954 11 => ts('November'),
955 12 => ts('December'),
956 );
957 // Add this option list to this column _columns. This is
958 // required so that filter statistics show properly.
959 $this->_columns[$table]['filters'][$fieldName]['options'] = $field['options'];
960 }
160d32e1
E
961 case CRM_Report_Form::OP_MULTISELECT:
962 case CRM_Report_Form::OP_MULTISELECT_SEPARATOR:
6a488035 963 // assume a multi-select field
9d72cede
EM
964 if (!empty($field['options']) ||
965 $fieldName == 'state_province_id' || $fieldName == 'county_id'
966 ) {
6a488035
TO
967 $element = $this->addElement('select', "{$fieldName}_op", ts('Operator:'), $operations);
968 if (count($operations) <= 1) {
969 $element->freeze();
970 }
9d72cede
EM
971 if ($fieldName == 'state_province_id' ||
972 $fieldName == 'county_id'
973 ) {
974 $this->addChainSelect($fieldName . '_value', array(
86bd39be
TO
975 'multiple' => TRUE,
976 'label' => NULL,
21dfd5f5 977 'class' => 'huge',
86bd39be 978 ));
c927c151
CW
979 }
980 else {
981 $this->addElement('select', "{$fieldName}_value", NULL, $field['options'], array(
982 'style' => 'min-width:250px',
2107cde9 983 'class' => 'crm-select2 huge',
c927c151
CW
984 'multiple' => TRUE,
985 'placeholder' => ts('- select -'),
986 ));
987 }
6a488035
TO
988 }
989 break;
990
160d32e1 991 case CRM_Report_Form::OP_SELECT:
6a488035
TO
992 // assume a select field
993 $this->addElement('select', "{$fieldName}_op", ts('Operator:'), $operations);
9d72cede 994 if (!empty($field['options'])) {
5a9a44d9 995 $this->addElement('select', "{$fieldName}_value", NULL, $field['options']);
9d72cede 996 }
6a488035
TO
997 break;
998
2107cde9
CW
999 case CRM_Report_Form::OP_ENTITYREF:
1000 $this->addElement('select', "{$fieldName}_op", ts('Operator:'), $operations);
1001 $this->setEntityRefDefaults($field, $table);
1002 $this->addEntityRef("{$fieldName}_value", NULL, $field['attributes']);
1003 break;
1004
160d32e1 1005 case CRM_Report_Form::OP_DATE:
6a488035 1006 // build datetime fields
9d72cede 1007 CRM_Core_Form_Date::buildDateRange($this, $fieldName, $count, '_from', '_to', 'From:', FALSE, $operations);
6a488035
TO
1008 $count++;
1009 break;
1010
160d32e1 1011 case CRM_Report_Form::OP_DATETIME:
6a488035 1012 // build datetime fields
9d72cede 1013 CRM_Core_Form_Date::buildDateRange($this, $fieldName, $count, '_from', '_to', 'From:', FALSE, $operations, 'searchDate', TRUE);
6a488035
TO
1014 $count++;
1015 break;
1016
160d32e1
E
1017 case CRM_Report_Form::OP_INT:
1018 case CRM_Report_Form::OP_FLOAT:
6a488035
TO
1019 // and a min value input box
1020 $this->add('text', "{$fieldName}_min", ts('Min'));
1021 // and a max value input box
1022 $this->add('text', "{$fieldName}_max", ts('Max'));
1023 default:
1024 // default type is string
1025 $this->addElement('select', "{$fieldName}_op", ts('Operator:'), $operations,
1026 array('onchange' => "return showHideMaxMinVal( '$fieldName', this.value );")
1027 );
1028 // we need text box for value input
2107cde9 1029 $this->add('text', "{$fieldName}_value", NULL, array('class' => 'huge'));
6a488035
TO
1030 break;
1031 }
1032 }
1033 }
1034 $this->assign('filters', $filters);
1035 }
1036
00be9182 1037 public function addOptions() {
6a488035
TO
1038 if (!empty($this->_options)) {
1039 // FIXME: For now lets build all elements as checkboxes.
1040 // Once we clear with the format we can build elements based on type
1041
1042 $options = array();
1043 foreach ($this->_options as $fieldName => $field) {
1044 if ($field['type'] == 'select') {
1045 $this->addElement('select', "{$fieldName}", $field['title'], $field['options']);
1046 }
4c9b6178 1047 elseif ($field['type'] == 'checkbox') {
6a488035 1048 $options[$field['title']] = $fieldName;
52634dad
DS
1049 $this->addCheckBox($fieldName, NULL,
1050 $options, NULL,
1051 NULL, NULL, NULL, $this->_fourColumnAttribute
1052 );
6a488035
TO
1053 }
1054 }
6a488035 1055 }
52634dad 1056 $this->assign('otherOptions', $this->_options);
6a488035
TO
1057 }
1058
00be9182 1059 public function addChartOptions() {
6a488035
TO
1060 if (!empty($this->_charts)) {
1061 $this->addElement('select', "charts", ts('Chart'), $this->_charts, array('onchange' => 'disablePrintPDFButtons(this.value);'));
1062 $this->assign('charts', $this->_charts);
1063 $this->addElement('submit', $this->_chartButtonName, ts('View'));
1064 }
1065 }
1066
00be9182 1067 public function addGroupBys() {
6a488035
TO
1068 $options = $freqElements = array();
1069
1070 foreach ($this->_columns as $tableName => $table) {
1071 if (array_key_exists('group_bys', $table)) {
1072 foreach ($table['group_bys'] as $fieldName => $field) {
1073 if (!empty($field)) {
1074 $options[$field['title']] = $fieldName;
a7488080 1075 if (!empty($field['frequency'])) {
6a488035
TO
1076 $freqElements[$field['title']] = $fieldName;
1077 }
1078 }
1079 }
1080 }
1081 }
1082 $this->addCheckBox("group_bys", ts('Group by columns'), $options, NULL,
1083 NULL, NULL, NULL, $this->_fourColumnAttribute
1084 );
1085 $this->assign('groupByElements', $options);
1086
1087 foreach ($freqElements as $name) {
1088 $this->addElement('select', "group_bys_freq[$name]",
1089 ts('Frequency'), $this->_groupByDateFreq
1090 );
1091 }
1092 }
1093
00be9182 1094 public function addOrderBys() {
6a488035
TO
1095 $options = array();
1096 foreach ($this->_columns as $tableName => $table) {
1097
1098 // Report developer may define any column to order by; include these as order-by options
1099 if (array_key_exists('order_bys', $table)) {
1100 foreach ($table['order_bys'] as $fieldName => $field) {
1101 if (!empty($field)) {
1102 $options[$fieldName] = $field['title'];
1103 }
1104 }
1105 }
1106
1107 /* Add searchable custom fields as order-by options, if so requested
1108 * (These are already indexed, so allowing to order on them is cheap.)
1109 */
1110
1111
9d72cede
EM
1112 if ($this->_autoIncludeIndexedFieldsAsOrderBys &&
1113 array_key_exists('extends', $table) && !empty($table['extends'])
1114 ) {
6a488035 1115 foreach ($table['fields'] as $fieldName => $field) {
a7488080 1116 if (empty($field['no_display'])) {
6a488035
TO
1117 $options[$fieldName] = $field['title'];
1118 }
1119 }
1120 }
1121 }
1122
1123 asort($options);
1124
1125 $this->assign('orderByOptions', $options);
1126
1127 if (!empty($options)) {
1128 $options = array(
21dfd5f5 1129 '-' => ' - none - ',
9d72cede 1130 ) + $options;
6a488035
TO
1131 for ($i = 1; $i <= 5; $i++) {
1132 $this->addElement('select', "order_bys[{$i}][column]", ts('Order by Column'), $options);
9d72cede 1133 $this->addElement('select', "order_bys[{$i}][order]", ts('Order by Order'), array(
86bd39be 1134 'ASC' => 'Ascending',
21dfd5f5 1135 'DESC' => 'Descending',
86bd39be 1136 ));
6a488035 1137 $this->addElement('checkbox', "order_bys[{$i}][section]", ts('Order by Section'), FALSE, array('id' => "order_by_section_$i"));
5895cba0 1138 $this->addElement('checkbox', "order_bys[{$i}][pageBreak]", ts('Page Break'), FALSE, array('id' => "order_by_pagebreak_$i"));
6a488035
TO
1139 }
1140 }
1141 }
1142
00be9182 1143 public function buildInstanceAndButtons() {
6a488035
TO
1144 CRM_Report_Form_Instance::buildForm($this);
1145
1146 $label = $this->_id ? ts('Update Report') : ts('Create Report');
1147
1148 $this->addElement('submit', $this->_instanceButtonName, $label);
1149 $this->addElement('submit', $this->_printButtonName, ts('Print Report'));
1150 $this->addElement('submit', $this->_pdfButtonName, ts('PDF'));
1151
1152 if ($this->_id) {
9d72cede
EM
1153 $this->addElement('submit', $this->_createNewButtonName,
1154 ts('Save a Copy') . '...');
6a488035 1155 }
c9cf6554 1156 $this->assign('instanceForm', $this->_instanceForm);
6a488035
TO
1157
1158 $label = $this->_id ? ts('Print Report') : ts('Print Preview');
1159 $this->addElement('submit', $this->_printButtonName, $label);
1160
1161 $label = $this->_id ? ts('PDF') : ts('Preview PDF');
1162 $this->addElement('submit', $this->_pdfButtonName, $label);
1163
1164 $label = $this->_id ? ts('Export to CSV') : ts('Preview CSV');
1165
1166 if ($this->_csvSupported) {
1167 $this->addElement('submit', $this->_csvButtonName, $label);
1168 }
1169
9d72cede
EM
1170 if (CRM_Core_Permission::check('administer Reports') &&
1171 $this->_add2groupSupported
1172 ) {
6a488035 1173 $this->addElement('select', 'groups', ts('Group'),
9d72cede
EM
1174 array('' => ts('Add Contacts to Group')) +
1175 CRM_Core_PseudoConstant::nestedGroup(),
24431f7b 1176 array('class' => 'crm-select2 crm-action-menu action-icon-plus huge')
6a488035
TO
1177 );
1178 $this->assign('group', TRUE);
1179 }
1180
24431f7b 1181 $this->addElement('submit', $this->_groupButtonName, '', array('style' => 'display: none;'));
6a488035
TO
1182
1183 $this->addChartOptions();
1184 $this->addButtons(array(
1185 array(
1186 'type' => 'submit',
1187 'name' => ts('Preview Report'),
1188 'isDefault' => TRUE,
1189 ),
1190 )
1191 );
1192 }
1193
00be9182 1194 public function buildQuickForm() {
6a488035
TO
1195 $this->addColumns();
1196
1197 $this->addFilters();
1198
1199 $this->addOptions();
1200
1201 $this->addGroupBys();
1202
1203 $this->addOrderBys();
1204
1205 $this->buildInstanceAndButtons();
1206
1207 //add form rule for report
1208 if (is_callable(array(
9d72cede 1209 $this,
21dfd5f5 1210 'formRule',
9d72cede 1211 ))) {
6a488035
TO
1212 $this->addFormRule(array(get_class($this), 'formRule'), $this);
1213 }
1214 }
1215
74cf4551 1216 /**
4f1f1f2a
CW
1217 * a formrule function to ensure that fields selected in group_by
1218 * (if any) should only be the ones present in display/select fields criteria;
1219 * note: works if and only if any custom field selected in group_by.
74cf4551
EM
1220 * @param $fields
1221 * @param array $ignoreFields
1222 *
1223 * @return array
1224 */
00be9182 1225 public function customDataFormRule($fields, $ignoreFields = array()) {
6a488035 1226 $errors = array();
9d72cede
EM
1227 if (!empty($this->_customGroupExtends) && $this->_customGroupGroupBy &&
1228 !empty($fields['group_bys'])
1229 ) {
6a488035 1230 foreach ($this->_columns as $tableName => $table) {
9d72cede
EM
1231 if ((substr($tableName, 0, 13) == 'civicrm_value' ||
1232 substr($tableName, 0, 12) == 'custom_value') &&
1233 !empty($this->_columns[$tableName]['fields'])
1234 ) {
6a488035
TO
1235 foreach ($this->_columns[$tableName]['fields'] as $fieldName => $field) {
1236 if (array_key_exists($fieldName, $fields['group_bys']) &&
1237 !array_key_exists($fieldName, $fields['fields'])
1238 ) {
1239 $errors['fields'] = "Please make sure fields selected in 'Group by Columns' section are also selected in 'Display Columns' section.";
1240 }
1241 elseif (array_key_exists($fieldName, $fields['group_bys'])) {
1242 foreach ($fields['fields'] as $fld => $val) {
9d72cede
EM
1243 if (!array_key_exists($fld, $fields['group_bys']) &&
1244 !in_array($fld, $ignoreFields)
1245 ) {
6a488035
TO
1246 $errors['fields'] = "Please ensure that fields selected in 'Display Columns' are also selected in 'Group by Columns' section.";
1247 }
1248 }
1249 }
1250 }
1251 }
1252 }
1253 }
1254 return $errors;
1255 }
1256
74cf4551 1257 /**
4f1f1f2a
CW
1258 * Note: $fieldName param allows inheriting class to build operationPairs
1259 * specific to a field.
74cf4551
EM
1260 * @param string $type
1261 * @param null $fieldName
1262 *
1263 * @return array
1264 */
00be9182 1265 public function getOperationPair($type = "string", $fieldName = NULL) {
6a488035
TO
1266 // FIXME: At some point we should move these key-val pairs
1267 // to option_group and option_value table.
6a488035 1268 switch ($type) {
160d32e1
E
1269 case CRM_Report_Form::OP_INT:
1270 case CRM_Report_Form::OP_FLOAT:
bc3f7f04 1271 return array(
1272 'lte' => ts('Is less than or equal to'),
6a488035
TO
1273 'gte' => ts('Is greater than or equal to'),
1274 'bw' => ts('Is between'),
1275 'eq' => ts('Is equal to'),
1276 'lt' => ts('Is less than'),
1277 'gt' => ts('Is greater than'),
1278 'neq' => ts('Is not equal to'),
1279 'nbw' => ts('Is not between'),
1280 'nll' => ts('Is empty (Null)'),
1281 'nnll' => ts('Is not empty (Null)'),
1282 );
6a488035 1283
160d32e1 1284 case CRM_Report_Form::OP_SELECT:
bc3f7f04 1285 return array(
1286 'eq' => ts('Is equal to'),
1287 );
6a488035 1288
160d32e1
E
1289 case CRM_Report_Form::OP_MONTH:
1290 case CRM_Report_Form::OP_MULTISELECT:
2107cde9 1291 case CRM_Report_Form::OP_ENTITYREF:
bc3f7f04 1292 return array(
1293 'in' => ts('Is one of'),
6a488035
TO
1294 'notin' => ts('Is not one of'),
1295 );
6a488035 1296
160d32e1 1297 case CRM_Report_Form::OP_DATE:
bc3f7f04 1298 return array(
1299 'nll' => ts('Is empty (Null)'),
6a488035
TO
1300 'nnll' => ts('Is not empty (Null)'),
1301 );
6a488035 1302
160d32e1 1303 case CRM_Report_Form::OP_MULTISELECT_SEPARATOR:
6a488035
TO
1304 // use this operator for the values, concatenated with separator. For e.g if
1305 // multiple options for a column is stored as ^A{val1}^A{val2}^A
bc3f7f04 1306 return array(
1307 'mhas' => ts('Is one of'),
67788963 1308 'mnot' => ts('Is not one of'),
bc3f7f04 1309 );
6a488035
TO
1310
1311 default:
1312 // type is string
bc3f7f04 1313 return array(
1314 'has' => ts('Contains'),
6a488035
TO
1315 'sw' => ts('Starts with'),
1316 'ew' => ts('Ends with'),
1317 'nhas' => ts('Does not contain'),
1318 'eq' => ts('Is equal to'),
1319 'neq' => ts('Is not equal to'),
1320 'nll' => ts('Is empty (Null)'),
1321 'nnll' => ts('Is not empty (Null)'),
1322 );
1323 }
1324 }
1325
00be9182 1326 public function buildTagFilter() {
ed795723 1327 $contactTags = CRM_Core_BAO_Tag::getTags($this->_tagFilterTable);
6a488035
TO
1328 if (!empty($contactTags)) {
1329 $this->_columns['civicrm_tag'] = array(
1330 'dao' => 'CRM_Core_DAO_Tag',
9d72cede
EM
1331 'filters' => array(
1332 'tagid' => array(
6a488035
TO
1333 'name' => 'tag_id',
1334 'title' => ts('Tag'),
1335 'tag' => TRUE,
1336 'operatorType' => CRM_Report_Form::OP_MULTISELECT,
1337 'options' => $contactTags,
1338 ),
1339 ),
1340 );
1341 }
1342 }
1343
1344 /*
1345 * Adds group filters to _columns (called from _Constuct
1346 */
00be9182 1347 public function buildGroupFilter() {
6a488035 1348 $this->_columns['civicrm_group']['filters'] = array(
9d72cede 1349 'gid' => array(
6a488035
TO
1350 'name' => 'group_id',
1351 'title' => ts('Group'),
1352 'operatorType' => CRM_Report_Form::OP_MULTISELECT,
1353 'group' => TRUE,
16e2e80c 1354 'options' => CRM_Core_PseudoConstant::nestedGroup(),
6a488035
TO
1355 ),
1356 );
1357 if (empty($this->_columns['civicrm_group']['dao'])) {
1358 $this->_columns['civicrm_group']['dao'] = 'CRM_Contact_DAO_GroupContact';
1359 }
1360 if (empty($this->_columns['civicrm_group']['alias'])) {
1361 $this->_columns['civicrm_group']['alias'] = 'cgroup';
1362 }
1363 }
1364
74cf4551
EM
1365 /**
1366 * @param string $operator
1367 *
1368 * @return string
1369 */
00be9182 1370 public function getSQLOperator($operator = "like") {
6a488035
TO
1371 switch ($operator) {
1372 case 'eq':
1373 return '=';
1374
1375 case 'lt':
1376 return '<';
1377
1378 case 'lte':
1379 return '<=';
1380
1381 case 'gt':
1382 return '>';
1383
1384 case 'gte':
1385 return '>=';
1386
1387 case 'ne':
1388 case 'neq':
1389 return '!=';
1390
1391 case 'nhas':
1392 return 'NOT LIKE';
1393
1394 case 'in':
1395 return 'IN';
1396
1397 case 'notin':
1398 return 'NOT IN';
1399
1400 case 'nll':
1401 return 'IS NULL';
1402
1403 case 'nnll':
1404 return 'IS NOT NULL';
1405
1406 default:
1407 // type is string
1408 return 'LIKE';
1409 }
1410 }
1411
74cf4551
EM
1412 /**
1413 * @param $field
1414 * @param $op
1415 * @param $value
1416 * @param $min
1417 * @param $max
1418 *
1419 * @return null|string
1420 */
7d8c1168
TO
1421 function whereClause(
1422 &$field, $op,
1423 $value, $min, $max
6a488035
TO
1424 ) {
1425
1426 $type = CRM_Utils_Type::typeToString(CRM_Utils_Array::value('type', $field));
1427 $clause = NULL;
1428
1429 switch ($op) {
1430 case 'bw':
1431 case 'nbw':
1432 if (($min !== NULL && strlen($min) > 0) ||
1433 ($max !== NULL && strlen($max) > 0)
1434 ) {
9d72cede
EM
1435 $min = CRM_Utils_Type::escape($min, $type);
1436 $max = CRM_Utils_Type::escape($max, $type);
6a488035
TO
1437 $clauses = array();
1438 if ($min) {
1439 if ($op == 'bw') {
1440 $clauses[] = "( {$field['dbAlias']} >= $min )";
1441 }
1442 else {
1443 $clauses[] = "( {$field['dbAlias']} < $min )";
1444 }
1445 }
1446 if ($max) {
1447 if ($op == 'bw') {
1448 $clauses[] = "( {$field['dbAlias']} <= $max )";
1449 }
1450 else {
1451 $clauses[] = "( {$field['dbAlias']} > $max )";
1452 }
1453 }
1454
1455 if (!empty($clauses)) {
1456 if ($op == 'bw') {
1457 $clause = implode(' AND ', $clauses);
1458 }
1459 else {
1460 $clause = implode(' OR ', $clauses);
1461 }
1462 }
1463 }
1464 break;
1465
1466 case 'has':
1467 case 'nhas':
1468 if ($value !== NULL && strlen($value) > 0) {
1469 $value = CRM_Utils_Type::escape($value, $type);
1470 if (strpos($value, '%') === FALSE) {
1471 $value = "'%{$value}%'";
1472 }
1473 else {
1474 $value = "'{$value}'";
1475 }
29fc2b79 1476 $sqlOP = $this->getSQLOperator($op);
6a488035
TO
1477 $clause = "( {$field['dbAlias']} $sqlOP $value )";
1478 }
1479 break;
1480
1481 case 'in':
1482 case 'notin':
2107cde9
CW
1483 if (is_string($value) && strlen($value)) {
1484 $value = explode(',', $value);
1485 }
6a488035 1486 if ($value !== NULL && is_array($value) && count($value) > 0) {
29fc2b79 1487 $sqlOP = $this->getSQLOperator($op);
9d72cede
EM
1488 if (CRM_Utils_Array::value('type', $field) ==
1489 CRM_Utils_Type::T_STRING
1490 ) {
6a488035
TO
1491 //cycle through selections and esacape values
1492 foreach ($value as $key => $selection) {
1493 $value[$key] = CRM_Utils_Type::escape($selection, $type);
1494 }
9d72cede
EM
1495 $clause =
1496 "( {$field['dbAlias']} $sqlOP ( '" . implode("' , '", $value) .
1497 "') )";
6a488035
TO
1498 }
1499 else {
1500 // for numerical values
9d72cede
EM
1501 $clause = "{$field['dbAlias']} $sqlOP (" . implode(', ', $value) .
1502 ")";
6a488035
TO
1503 }
1504 if ($op == 'notin') {
1505 $clause = "( " . $clause . " OR {$field['dbAlias']} IS NULL )";
1506 }
1507 else {
1508 $clause = "( " . $clause . " )";
1509 }
1510 }
1511 break;
1512
1513 case 'mhas':
1514 // mhas == multiple has
1515 if ($value !== NULL && count($value) > 0) {
29fc2b79 1516 $sqlOP = $this->getSQLOperator($op);
9d72cede
EM
1517 $clause =
1518 "{$field['dbAlias']} REGEXP '[[:<:]]" . implode('|', $value) .
1519 "[[:>:]]'";
6a488035
TO
1520 }
1521 break;
77b97be7 1522
67788963
J
1523 case 'mnot':
1524 // mnot == multiple is not one of
1525 if ($value !== NULL && count($value) > 0) {
1526 $sqlOP = $this->getSQLOperator($op);
9d72cede
EM
1527 $clause =
1528 "( {$field['dbAlias']} NOT REGEXP '[[:<:]]" . implode('|', $value) .
1529 "[[:>:]]' OR {$field['dbAlias']} IS NULL )";
67788963
J
1530 }
1531 break;
6a488035
TO
1532
1533 case 'sw':
1534 case 'ew':
1535 if ($value !== NULL && strlen($value) > 0) {
1536 $value = CRM_Utils_Type::escape($value, $type);
1537 if (strpos($value, '%') === FALSE) {
1538 if ($op == 'sw') {
1539 $value = "'{$value}%'";
1540 }
1541 else {
1542 $value = "'%{$value}'";
1543 }
1544 }
1545 else {
1546 $value = "'{$value}'";
1547 }
29fc2b79 1548 $sqlOP = $this->getSQLOperator($op);
6a488035
TO
1549 $clause = "( {$field['dbAlias']} $sqlOP $value )";
1550 }
1551 break;
1552
1553 case 'nll':
1554 case 'nnll':
29fc2b79 1555 $sqlOP = $this->getSQLOperator($op);
6a488035
TO
1556 $clause = "( {$field['dbAlias']} $sqlOP )";
1557 break;
1558
1559 default:
1560 if ($value !== NULL && strlen($value) > 0) {
1561 if (isset($field['clause'])) {
1562 // FIXME: we not doing escape here. Better solution is to use two
1563 // different types - data-type and filter-type
0e6e8724 1564 $clause = $field['clause'];
6a488035
TO
1565 }
1566 else {
1567 $value = CRM_Utils_Type::escape($value, $type);
29fc2b79 1568 $sqlOP = $this->getSQLOperator($op);
6a488035
TO
1569 if ($field['type'] == CRM_Utils_Type::T_STRING) {
1570 $value = "'{$value}'";
1571 }
1572 $clause = "( {$field['dbAlias']} $sqlOP $value )";
1573 }
1574 }
1575 break;
1576 }
1577
a7488080 1578 if (!empty($field['group']) && $clause) {
6a488035
TO
1579 $clause = $this->whereGroupClause($field, $value, $op);
1580 }
a7488080 1581 elseif (!empty($field['tag']) && $clause) {
6a488035
TO
1582 // not using left join in query because if any contact
1583 // belongs to more than one tag, results duplicate
1584 // entries.
1585 $clause = $this->whereTagClause($field, $value, $op);
1586 }
114a2c85
DG
1587 elseif (!empty($field['membership_org']) && $clause) {
1588 $clause = $this->whereMembershipOrgClause($field, $value, $op);
1589 }
1590 elseif (!empty($field['membership_type']) && $clause) {
1591 $clause = $this->whereMembershipTypeClause($field, $value, $op);
1592 }
6a488035
TO
1593 return $clause;
1594 }
1595
74cf4551 1596 /**
100fef9d 1597 * @param string $fieldName
74cf4551
EM
1598 * @param $relative
1599 * @param $from
1600 * @param $to
1601 * @param null $type
1602 * @param null $fromTime
1603 * @param null $toTime
1604 *
1605 * @return null|string
1606 */
7d8c1168
TO
1607 function dateClause(
1608 $fieldName,
1609 $relative, $from, $to, $type = NULL, $fromTime = NULL, $toTime = NULL
6a488035
TO
1610 ) {
1611 $clauses = array();
160d32e1 1612 if (in_array($relative, array_keys($this->getOperationPair(CRM_Report_Form::OP_DATE)))) {
29fc2b79 1613 $sqlOP = $this->getSQLOperator($relative);
6a488035
TO
1614 return "( {$fieldName} {$sqlOP} )";
1615 }
1616
29fc2b79 1617 list($from, $to) = $this->getFromTo($relative, $from, $to, $fromTime, $toTime);
6a488035
TO
1618
1619 if ($from) {
1620 $from = ($type == CRM_Utils_Type::T_DATE) ? substr($from, 0, 8) : $from;
1621 $clauses[] = "( {$fieldName} >= $from )";
1622 }
1623
1624 if ($to) {
1625 $to = ($type == CRM_Utils_Type::T_DATE) ? substr($to, 0, 8) : $to;
1626 $clauses[] = "( {$fieldName} <= {$to} )";
1627 }
1628
1629 if (!empty($clauses)) {
1630 return implode(' AND ', $clauses);
1631 }
1632
1633 return NULL;
1634 }
9d72cede
EM
1635
1636 /**
1637 * @todo - could not find any instances where this is called
1638 *
1639 * @param unknown_type $relative
7e06c9f5
TO
1640 * @param string $from
1641 * @param string_type $to
9d72cede
EM
1642 *
1643 * @return string|NULL
1644 */
00be9182 1645 public function dateDisplay($relative, $from, $to) {
29fc2b79 1646 list($from, $to) = $this->getFromTo($relative, $from, $to);
6a488035
TO
1647
1648 if ($from) {
1649 $clauses[] = CRM_Utils_Date::customFormat($from, NULL, array('m', 'M'));
1650 }
1651 else {
1652 $clauses[] = 'Past';
1653 }
1654
1655 if ($to) {
1656 $clauses[] = CRM_Utils_Date::customFormat($to, NULL, array('m', 'M'));
1657 }
1658 else {
1659 $clauses[] = 'Today';
1660 }
1661
1662 if (!empty($clauses)) {
1663 return implode(' - ', $clauses);
1664 }
1665
1666 return NULL;
1667 }
1668
74cf4551
EM
1669 /**
1670 * @param $relative
1671 * @param $from
1672 * @param $to
1673 * @param null $fromtime
1674 * @param null $totime
1675 *
1676 * @return array
1677 */
00be9182 1678 public function getFromTo($relative, $from, $to, $fromtime = NULL, $totime = NULL) {
6a488035
TO
1679 if (empty($totime)) {
1680 $totime = '235959';
1681 }
1682 //FIX ME not working for relative
1683 if ($relative) {
1684 list($term, $unit) = CRM_Utils_System::explode('.', $relative, 2);
1685 $dateRange = CRM_Utils_Date::relativeToAbsolute($term, $unit);
1686 $from = substr($dateRange['from'], 0, 8);
1687 //Take only Date Part, Sometime Time part is also present in 'to'
1688 $to = substr($dateRange['to'], 0, 8);
1689 }
1690 $from = CRM_Utils_Date::processDate($from, $fromtime);
1691 $to = CRM_Utils_Date::processDate($to, $totime);
1692 return array($from, $to);
1693 }
1694
74cf4551
EM
1695 /**
1696 * @param $rows
1697 */
00be9182 1698 public function alterDisplay(&$rows) {
6a488035
TO
1699 // custom code to alter rows
1700 }
1701
74cf4551
EM
1702 /**
1703 * @param $rows
1704 */
00be9182 1705 public function alterCustomDataDisplay(&$rows) {
6a488035
TO
1706 // custom code to alter rows having custom values
1707 if (empty($this->_customGroupExtends)) {
1708 return;
1709 }
1710
1711 $customFieldIds = array();
1712 foreach ($this->_params['fields'] as $fieldAlias => $value) {
1713 if ($fieldId = CRM_Core_BAO_CustomField::getKeyID($fieldAlias)) {
1714 $customFieldIds[$fieldAlias] = $fieldId;
1715 }
1716 }
1717 if (empty($customFieldIds)) {
1718 return;
1719 }
1720
1721 $customFields = $fieldValueMap = array();
9d72cede
EM
1722 $customFieldCols = array(
1723 'column_name',
1724 'data_type',
1725 'html_type',
1726 'option_group_id',
21dfd5f5 1727 'id',
9d72cede 1728 );
6a488035
TO
1729
1730 // skip for type date and ContactReference since date format is already handled
1731 $query = "
1732SELECT cg.table_name, cf." . implode(", cf.", $customFieldCols) . ", ov.value, ov.label
1733FROM civicrm_custom_field cf
1734INNER JOIN civicrm_custom_group cg ON cg.id = cf.custom_group_id
1735LEFT JOIN civicrm_option_value ov ON cf.option_group_id = ov.option_group_id
1736WHERE cg.extends IN ('" . implode("','", $this->_customGroupExtends) . "') AND
1737 cg.is_active = 1 AND
1738 cf.is_active = 1 AND
1739 cf.is_searchable = 1 AND
1740 cf.data_type NOT IN ('ContactReference', 'Date') AND
1741 cf.id IN (" . implode(",", $customFieldIds) . ")";
1742
1743 $dao = CRM_Core_DAO::executeQuery($query);
1744 while ($dao->fetch()) {
1745 foreach ($customFieldCols as $key) {
9d72cede
EM
1746 $customFields[$dao->table_name . '_custom_' .
1747 $dao->id][$key] = $dao->$key;
6a488035
TO
1748 }
1749 if ($dao->option_group_id) {
1750 $fieldValueMap[$dao->option_group_id][$dao->value] = $dao->label;
1751 }
1752 }
1753 $dao->free();
1754
1755 $entryFound = FALSE;
1756 foreach ($rows as $rowNum => $row) {
1757 foreach ($row as $tableCol => $val) {
1758 if (array_key_exists($tableCol, $customFields)) {
1759 $rows[$rowNum][$tableCol] = $this->formatCustomValues($val, $customFields[$tableCol], $fieldValueMap);
1760 $entryFound = TRUE;
1761 }
1762 }
1763
1764 // skip looking further in rows, if first row itself doesn't
1765 // have the column we need
1766 if (!$entryFound) {
1767 break;
1768 }
1769 }
1770 }
1771
74cf4551
EM
1772 /**
1773 * @param $value
1774 * @param $customField
1775 * @param $fieldValueMap
1776 *
1777 * @return float|string
1778 */
00be9182 1779 public function formatCustomValues($value, $customField, $fieldValueMap) {
6a488035
TO
1780 if (CRM_Utils_System::isNull($value)) {
1781 return;
1782 }
1783
1784 $htmlType = $customField['html_type'];
1785
1786 switch ($customField['data_type']) {
1787 case 'Boolean':
1788 if ($value == '1') {
1789 $retValue = ts('Yes');
1790 }
1791 else {
1792 $retValue = ts('No');
1793 }
1794 break;
1795
1796 case 'Link':
1797 $retValue = CRM_Utils_System::formatWikiURL($value);
1798 break;
1799
1800 case 'File':
1801 $retValue = $value;
1802 break;
1803
1804 case 'Memo':
1805 $retValue = $value;
1806 break;
1807
1808 case 'Float':
1809 if ($htmlType == 'Text') {
9d72cede 1810 $retValue = (float) $value;
6a488035
TO
1811 break;
1812 }
1813 case 'Money':
1814 if ($htmlType == 'Text') {
1815
1816
6a488035
TO
1817 $retValue = CRM_Utils_Money::format($value, NULL, '%a');
1818 break;
1819 }
1820 case 'String':
1821 case 'Int':
1822 if (in_array($htmlType, array(
9d72cede 1823 'Text',
21dfd5f5 1824 'TextArea',
9d72cede 1825 ))) {
6a488035
TO
1826 $retValue = $value;
1827 break;
1828 }
1829 case 'StateProvince':
1830 case 'Country':
1831
1832 switch ($htmlType) {
1833 case 'Multi-Select Country':
1834 $value = explode(CRM_Core_DAO::VALUE_SEPARATOR, $value);
1835 $customData = array();
1836 foreach ($value as $val) {
1837 if ($val) {
1838 $customData[] = CRM_Core_PseudoConstant::country($val, FALSE);
1839 }
1840 }
1841 $retValue = implode(', ', $customData);
1842 break;
1843
1844 case 'Select Country':
1845 $retValue = CRM_Core_PseudoConstant::country($value, FALSE);
1846 break;
1847
1848 case 'Select State/Province':
1849 $retValue = CRM_Core_PseudoConstant::stateProvince($value, FALSE);
1850 break;
1851
1852 case 'Multi-Select State/Province':
1853 $value = explode(CRM_Core_DAO::VALUE_SEPARATOR, $value);
1854 $customData = array();
1855 foreach ($value as $val) {
1856 if ($val) {
1857 $customData[] = CRM_Core_PseudoConstant::stateProvince($val, FALSE);
1858 }
1859 }
1860 $retValue = implode(', ', $customData);
1861 break;
1862
1863 case 'Select':
1864 case 'Radio':
1865 case 'Autocomplete-Select':
1866 $retValue = $fieldValueMap[$customField['option_group_id']][$value];
1867 break;
1868
1869 case 'CheckBox':
1870 case 'AdvMulti-Select':
1871 case 'Multi-Select':
1872 $value = explode(CRM_Core_DAO::VALUE_SEPARATOR, $value);
1873 $customData = array();
1874 foreach ($value as $val) {
1875 if ($val) {
1876 $customData[] = $fieldValueMap[$customField['option_group_id']][$val];
1877 }
1878 }
1879 $retValue = implode(', ', $customData);
1880 break;
1881
1882 default:
1883 $retValue = $value;
1884 }
1885 break;
1886
1887 default:
1888 $retValue = $value;
1889 }
1890
1891 return $retValue;
1892 }
1893
74cf4551
EM
1894 /**
1895 * @param $rows
1896 */
00be9182 1897 public function removeDuplicates(&$rows) {
6a488035
TO
1898 if (empty($this->_noRepeats)) {
1899 return;
1900 }
1901 $checkList = array();
1902
1903 foreach ($rows as $key => $list) {
1904 foreach ($list as $colName => $colVal) {
8f1445ea 1905 if (array_key_exists($colName, $checkList) &&
9d72cede
EM
1906 $checkList[$colName] == $colVal
1907 ) {
6a488035
TO
1908 $rows[$key][$colName] = "";
1909 }
1910 if (in_array($colName, $this->_noRepeats)) {
1911 $checkList[$colName] = $colVal;
1912 }
1913 }
1914 }
1915 }
1916
74cf4551
EM
1917 /**
1918 * @param $row
1919 * @param $fields
1920 * @param bool $subtotal
1921 */
00be9182 1922 public function fixSubTotalDisplay(&$row, $fields, $subtotal = TRUE) {
6a488035
TO
1923 foreach ($row as $colName => $colVal) {
1924 if (in_array($colName, $fields)) {
1925 $row[$colName] = $row[$colName];
1926 }
1927 elseif (isset($this->_columnHeaders[$colName])) {
1928 if ($subtotal) {
1929 $row[$colName] = "Subtotal";
1930 $subtotal = FALSE;
1931 }
1932 else {
1933 unset($row[$colName]);
1934 }
1935 }
1936 }
1937 }
1938
74cf4551
EM
1939 /**
1940 * @param $rows
1941 *
1942 * @return bool
1943 */
00be9182 1944 public function grandTotal(&$rows) {
6a488035
TO
1945 if (!$this->_rollup || ($this->_rollup == '') ||
1946 ($this->_limit && count($rows) >= self::ROW_COUNT_LIMIT)
1947 ) {
1948 return FALSE;
1949 }
1950 $lastRow = array_pop($rows);
1951
6a488035
TO
1952 foreach ($this->_columnHeaders as $fld => $val) {
1953 if (!in_array($fld, $this->_statFields)) {
1954 if (!$this->_grandFlag) {
1955 $lastRow[$fld] = "Grand Total";
1956 $this->_grandFlag = TRUE;
1957 }
1958 else {
1959 $lastRow[$fld] = "";
1960 }
1961 }
1962 }
1963
1964 $this->assign('grandStat', $lastRow);
1965 return TRUE;
1966 }
1967
74cf4551
EM
1968 /**
1969 * @param $rows
1970 * @param bool $pager
1971 */
00be9182 1972 public function formatDisplay(&$rows, $pager = TRUE) {
6a488035
TO
1973 // set pager based on if any limit was applied in the query.
1974 if ($pager) {
1975 $this->setPager();
1976 }
1977
1978 // allow building charts if any
1979 if (!empty($this->_params['charts']) && !empty($rows)) {
1980 $this->buildChart($rows);
1981 $this->assign('chartEnabled', TRUE);
9d72cede
EM
1982 $this->_chartId = "{$this->_params['charts']}_" .
1983 ($this->_id ? $this->_id : substr(get_class($this), 16)) . '_' .
1984 session_id();
6a488035
TO
1985 $this->assign('chartId', $this->_chartId);
1986 }
1987
1988 // unset columns not to be displayed.
1989 foreach ($this->_columnHeaders as $key => $value) {
a7488080 1990 if (!empty($value['no_display'])) {
6a488035
TO
1991 unset($this->_columnHeaders[$key]);
1992 }
1993 }
1994
1995 // unset columns not to be displayed.
1996 if (!empty($rows)) {
1997 foreach ($this->_noDisplay as $noDisplayField) {
1998 foreach ($rows as $rowNum => $row) {
1999 unset($this->_columnHeaders[$noDisplayField]);
2000 }
2001 }
2002 }
2003
2004 // build array of section totals
2005 $this->sectionTotals();
2006
2007 // process grand-total row
2008 $this->grandTotal($rows);
2009
2010 // use this method for formatting rows for display purpose.
2011 $this->alterDisplay($rows);
2012 CRM_Utils_Hook::alterReportVar('rows', $rows, $this);
2013
2014 // use this method for formatting custom rows for display purpose.
2015 $this->alterCustomDataDisplay($rows);
2016 }
2017
74cf4551
EM
2018 /**
2019 * @param $rows
2020 */
00be9182 2021 public function buildChart(&$rows) {
6a488035
TO
2022 // override this method for building charts.
2023 }
2024
2025 // select() method below has been added recently (v3.3), and many of the report templates might
2026 // still be having their own select() method. We should fix them as and when encountered and move
2027 // towards generalizing the select() method below.
00be9182 2028 public function select() {
1f220d30 2029 $select = $this->_selectAliases = array();
6a488035
TO
2030
2031 foreach ($this->_columns as $tableName => $table) {
2032 if (array_key_exists('fields', $table)) {
2033 foreach ($table['fields'] as $fieldName => $field) {
2034 if ($tableName == 'civicrm_address') {
2035 $this->_addressField = TRUE;
2036 }
2037 if ($tableName == 'civicrm_email') {
2038 $this->_emailField = TRUE;
2039 }
2040 if ($tableName == 'civicrm_phone') {
2041 $this->_phoneField = TRUE;
2042 }
2043
9d72cede
EM
2044 if (!empty($field['required']) ||
2045 !empty($this->_params['fields'][$fieldName])
2046 ) {
6a488035
TO
2047
2048 // 1. In many cases we want select clause to be built in slightly different way
2049 // for a particular field of a particular type.
2050 // 2. This method when used should receive params by reference and modify $this->_columnHeaders
2051 // as needed.
2052 $selectClause = $this->selectClause($tableName, 'fields', $fieldName, $field);
2053 if ($selectClause) {
2054 $select[] = $selectClause;
2055 continue;
2056 }
2057
2058 // include statistics columns only if set
a7488080 2059 if (!empty($field['statistics'])) {
6a488035
TO
2060 foreach ($field['statistics'] as $stat => $label) {
2061 $alias = "{$tableName}_{$fieldName}_{$stat}";
2062 switch (strtolower($stat)) {
2063 case 'max':
2064 case 'sum':
2065 $select[] = "$stat({$field['dbAlias']}) as $alias";
2066 $this->_columnHeaders["{$tableName}_{$fieldName}_{$stat}"]['title'] = $label;
2067 $this->_columnHeaders["{$tableName}_{$fieldName}_{$stat}"]['type'] = $field['type'];
9e6d7767 2068 $this->_statFields[$label] = $alias;
6a488035
TO
2069 $this->_selectAliases[] = $alias;
2070 break;
2071
2072 case 'count':
2073 $select[] = "COUNT({$field['dbAlias']}) as $alias";
2074 $this->_columnHeaders["{$tableName}_{$fieldName}_{$stat}"]['title'] = $label;
2075 $this->_columnHeaders["{$tableName}_{$fieldName}_{$stat}"]['type'] = CRM_Utils_Type::T_INT;
9e6d7767 2076 $this->_statFields[$label] = $alias;
6a488035
TO
2077 $this->_selectAliases[] = $alias;
2078 break;
2079
1bfaf6a6
DS
2080 case 'count_distinct':
2081 $select[] = "COUNT(DISTINCT {$field['dbAlias']}) as $alias";
2082 $this->_columnHeaders["{$tableName}_{$fieldName}_{$stat}"]['title'] = $label;
2083 $this->_columnHeaders["{$tableName}_{$fieldName}_{$stat}"]['type'] = CRM_Utils_Type::T_INT;
2084 $this->_statFields[$label] = $alias;
2085 $this->_selectAliases[] = $alias;
2086 break;
2087
6a488035
TO
2088 case 'avg':
2089 $select[] = "ROUND(AVG({$field['dbAlias']}),2) as $alias";
2090 $this->_columnHeaders["{$tableName}_{$fieldName}_{$stat}"]['title'] = $label;
2091 $this->_columnHeaders["{$tableName}_{$fieldName}_{$stat}"]['type'] = $field['type'];
9e6d7767 2092 $this->_statFields[$label] = $alias;
6a488035
TO
2093 $this->_selectAliases[] = $alias;
2094 break;
2095 }
2096 }
2097 }
2098 else {
2099 $alias = "{$tableName}_{$fieldName}";
2100 $select[] = "{$field['dbAlias']} as $alias";
2101 $this->_columnHeaders["{$tableName}_{$fieldName}"]['title'] = CRM_Utils_Array::value('title', $field);
2102 $this->_columnHeaders["{$tableName}_{$fieldName}"]['type'] = CRM_Utils_Array::value('type', $field);
2103 $this->_selectAliases[] = $alias;
2104 }
2105 }
2106 }
2107 }
2108
2109 // select for group bys
2110 if (array_key_exists('group_bys', $table)) {
2111 foreach ($table['group_bys'] as $fieldName => $field) {
2112
2113 if ($tableName == 'civicrm_address') {
2114 $this->_addressField = TRUE;
2115 }
2116 if ($tableName == 'civicrm_email') {
2117 $this->_emailField = TRUE;
2118 }
2119 if ($tableName == 'civicrm_phone') {
2120 $this->_phoneField = TRUE;
2121 }
2122 // 1. In many cases we want select clause to be built in slightly different way
2123 // for a particular field of a particular type.
2124 // 2. This method when used should receive params by reference and modify $this->_columnHeaders
2125 // as needed.
2126 $selectClause = $this->selectClause($tableName, 'group_bys', $fieldName, $field);
2127 if ($selectClause) {
2128 $select[] = $selectClause;
2129 continue;
2130 }
2131
9d72cede
EM
2132 if (!empty($this->_params['group_bys']) &&
2133 !empty($this->_params['group_bys'][$fieldName]) &&
2134 !empty($this->_params['group_bys_freq'])
2135 ) {
6a488035
TO
2136 switch (CRM_Utils_Array::value($fieldName, $this->_params['group_bys_freq'])) {
2137 case 'YEARWEEK':
9d72cede
EM
2138 $select[] = "DATE_SUB({$field['dbAlias']}, INTERVAL WEEKDAY({$field['dbAlias']}) DAY) AS {$tableName}_{$fieldName}_start";
2139 $select[] = "YEARWEEK({$field['dbAlias']}) AS {$tableName}_{$fieldName}_subtotal";
2140 $select[] = "WEEKOFYEAR({$field['dbAlias']}) AS {$tableName}_{$fieldName}_interval";
6a488035
TO
2141 $field['title'] = 'Week';
2142 break;
2143
2144 case 'YEAR':
9d72cede
EM
2145 $select[] = "MAKEDATE(YEAR({$field['dbAlias']}), 1) AS {$tableName}_{$fieldName}_start";
2146 $select[] = "YEAR({$field['dbAlias']}) AS {$tableName}_{$fieldName}_subtotal";
2147 $select[] = "YEAR({$field['dbAlias']}) AS {$tableName}_{$fieldName}_interval";
6a488035
TO
2148 $field['title'] = 'Year';
2149 break;
2150
2151 case 'MONTH':
9d72cede
EM
2152 $select[] = "DATE_SUB({$field['dbAlias']}, INTERVAL (DAYOFMONTH({$field['dbAlias']})-1) DAY) as {$tableName}_{$fieldName}_start";
2153 $select[] = "MONTH({$field['dbAlias']}) AS {$tableName}_{$fieldName}_subtotal";
2154 $select[] = "MONTHNAME({$field['dbAlias']}) AS {$tableName}_{$fieldName}_interval";
6a488035
TO
2155 $field['title'] = 'Month';
2156 break;
2157
2158 case 'QUARTER':
9d72cede
EM
2159 $select[] = "STR_TO_DATE(CONCAT( 3 * QUARTER( {$field['dbAlias']} ) -2 , '/', '1', '/', YEAR( {$field['dbAlias']} ) ), '%m/%d/%Y') AS {$tableName}_{$fieldName}_start";
2160 $select[] = "QUARTER({$field['dbAlias']}) AS {$tableName}_{$fieldName}_subtotal";
2161 $select[] = "QUARTER({$field['dbAlias']}) AS {$tableName}_{$fieldName}_interval";
6a488035
TO
2162 $field['title'] = 'Quarter';
2163 break;
2164 }
2165 // for graphs and charts -
a7488080 2166 if (!empty($this->_params['group_bys_freq'][$fieldName])) {
6a488035 2167 $this->_interval = $field['title'];
9d72cede
EM
2168 $this->_columnHeaders["{$tableName}_{$fieldName}_start"]['title'] =
2169 $field['title'] . ' Beginning';
6a488035
TO
2170 $this->_columnHeaders["{$tableName}_{$fieldName}_start"]['type'] = $field['type'];
2171 $this->_columnHeaders["{$tableName}_{$fieldName}_start"]['group_by'] = $this->_params['group_bys_freq'][$fieldName];
2172
2173 // just to make sure these values are transfered to rows.
2174 // since we 'll need them for calculation purpose,
2175 // e.g making subtotals look nicer or graphs
2176 $this->_columnHeaders["{$tableName}_{$fieldName}_interval"] = array('no_display' => TRUE);
2177 $this->_columnHeaders["{$tableName}_{$fieldName}_subtotal"] = array('no_display' => TRUE);
2178 }
2179 }
2180 }
2181 }
2182 }
2183
1f220d30 2184 $this->_selectClauses = $select;
6a488035
TO
2185 $this->_select = "SELECT " . implode(', ', $select) . " ";
2186 }
2187
74cf4551 2188 /**
100fef9d 2189 * @param string $tableName
74cf4551 2190 * @param $tableKey
100fef9d 2191 * @param string $fieldName
74cf4551
EM
2192 * @param $field
2193 *
2194 * @return bool
2195 */
00be9182 2196 public function selectClause(&$tableName, $tableKey, &$fieldName, &$field) {
6a488035
TO
2197 return FALSE;
2198 }
2199
00be9182 2200 public function where() {
adfe2750 2201 $this->storeWhereHavingClauseArray();
2202
2203 if (empty($this->_whereClauses)) {
2204 $this->_where = "WHERE ( 1 ) ";
2205 $this->_having = "";
2206 }
2207 else {
2208 $this->_where = "WHERE " . implode(' AND ', $this->_whereClauses);
2209 }
2210
2211 if ($this->_aclWhere) {
2212 $this->_where .= " AND {$this->_aclWhere} ";
2213 }
2214
2215 if (!empty($this->_havingClauses)) {
2216 // use this clause to construct group by clause.
2217 $this->_having = "HAVING " . implode(' AND ', $this->_havingClauses);
2218 }
2219 }
2220
2221 /**
2222 * Store Where clauses into an array - breaking out this step makes
2223 * over-riding more flexible as the clauses can be used in constructing a
2224 * temp table that may not be part of the final where clause or added
2225 * in other functions
2226 */
00be9182 2227 public function storeWhereHavingClauseArray() {
6a488035
TO
2228 foreach ($this->_columns as $tableName => $table) {
2229 if (array_key_exists('filters', $table)) {
2230 foreach ($table['filters'] as $fieldName => $field) {
d12de91c 2231 // respect pseudofield to filter spec so fields can be marked as
2232 // not to be handled here
9d72cede 2233 if (!empty($field['pseudofield'])) {
d12de91c 2234 continue;
2235 }
6a488035
TO
2236 $clause = NULL;
2237 if (CRM_Utils_Array::value('type', $field) & CRM_Utils_Type::T_DATE) {
9d72cede
EM
2238 if (CRM_Utils_Array::value('operatorType', $field) ==
2239 CRM_Report_Form::OP_MONTH
2240 ) {
6a488035
TO
2241 $op = CRM_Utils_Array::value("{$fieldName}_op", $this->_params);
2242 $value = CRM_Utils_Array::value("{$fieldName}_value", $this->_params);
2243 if (is_array($value) && !empty($value)) {
9d72cede
EM
2244 $clause =
2245 "(month({$field['dbAlias']}) $op (" . implode(', ', $value) .
2246 '))';
6a488035
TO
2247 }
2248 }
2249 else {
2250 $relative = CRM_Utils_Array::value("{$fieldName}_relative", $this->_params);
9d72cede
EM
2251 $from = CRM_Utils_Array::value("{$fieldName}_from", $this->_params);
2252 $to = CRM_Utils_Array::value("{$fieldName}_to", $this->_params);
6a488035 2253 $fromTime = CRM_Utils_Array::value("{$fieldName}_from_time", $this->_params);
9d72cede
EM
2254 $toTime = CRM_Utils_Array::value("{$fieldName}_to_time", $this->_params);
2255 $clause = $this->dateClause($field['dbAlias'], $relative, $from, $to, $field['type'], $fromTime, $toTime);
6a488035
TO
2256 }
2257 }
2258 else {
2259 $op = CRM_Utils_Array::value("{$fieldName}_op", $this->_params);
2260 if ($op) {
2261 $clause = $this->whereClause($field,
adfe2750 2262 $op,
2263 CRM_Utils_Array::value("{$fieldName}_value", $this->_params),
2264 CRM_Utils_Array::value("{$fieldName}_min", $this->_params),
2265 CRM_Utils_Array::value("{$fieldName}_max", $this->_params)
6a488035
TO
2266 );
2267 }
2268 }
2269
2270 if (!empty($clause)) {
a7488080 2271 if (!empty($field['having'])) {
adfe2750 2272 $this->_havingClauses[] = $clause;
6a488035
TO
2273 }
2274 else {
adfe2750 2275 $this->_whereClauses[] = $clause;
6a488035
TO
2276 }
2277 }
2278 }
2279 }
2280 }
2281
6a488035 2282 }
9d72cede 2283
00be9182 2284 public function processReportMode() {
6a488035
TO
2285 $buttonName = $this->controller->getButtonName();
2286
884605ca
DL
2287 $output = CRM_Utils_Request::retrieve(
2288 'output',
2289 'String',
2290 CRM_Core_DAO::$_nullObject
6a488035
TO
2291 );
2292
2293 $this->_sendmail =
884605ca
DL
2294 CRM_Utils_Request::retrieve(
2295 'sendmail',
2296 'Boolean',
2297 CRM_Core_DAO::$_nullObject
2298 );
6a488035
TO
2299
2300 $this->_absoluteUrl = FALSE;
2301 $printOnly = FALSE;
2302 $this->assign('printOnly', FALSE);
2303
9d72cede
EM
2304 if ($this->_printButtonName == $buttonName || $output == 'print' ||
2305 ($this->_sendmail && !$output)
2306 ) {
6a488035
TO
2307 $this->assign('printOnly', TRUE);
2308 $printOnly = TRUE;
2309 $this->assign('outputMode', 'print');
2310 $this->_outputMode = 'print';
2311 if ($this->_sendmail) {
2312 $this->_absoluteUrl = TRUE;
9d72cede 2313 }
6a488035
TO
2314 }
2315 elseif ($this->_pdfButtonName == $buttonName || $output == 'pdf') {
2316 $this->assign('printOnly', TRUE);
2317 $printOnly = TRUE;
2318 $this->assign('outputMode', 'pdf');
2319 $this->_outputMode = 'pdf';
2320 $this->_absoluteUrl = TRUE;
2321 }
2322 elseif ($this->_csvButtonName == $buttonName || $output == 'csv') {
2323 $this->assign('printOnly', TRUE);
2324 $printOnly = TRUE;
2325 $this->assign('outputMode', 'csv');
2326 $this->_outputMode = 'csv';
2327 $this->_absoluteUrl = TRUE;
2328 }
2329 elseif ($this->_groupButtonName == $buttonName || $output == 'group') {
2330 $this->assign('outputMode', 'group');
2331 $this->_outputMode = 'group';
2332 }
2333 elseif ($output == 'create_report' && $this->_criteriaForm) {
2334 $this->assign('outputMode', 'create_report');
2335 $this->_outputMode = 'create_report';
2336 }
2337 else {
2338 $this->assign('outputMode', 'html');
2339 $this->_outputMode = 'html';
2340 }
2341
2342 // Get today's date to include in printed reports
2343 if ($printOnly) {
2344 $reportDate = CRM_Utils_Date::customFormat(date('Y-m-d H:i'));
2345 $this->assign('reportDate', $reportDate);
2346 }
2347 }
2348
9657ccf2
EM
2349 /**
2350 * Post Processing function for Form (postProcessCommon should be used to set other variables from input as the api accesses that function)
2351 */
00be9182 2352 public function beginPostProcess() {
c58f66e0 2353 $this->setParams($this->controller->exportValues($this->_name));
6a488035
TO
2354
2355 if (empty($this->_params) &&
2356 $this->_force
2357 ) {
c58f66e0 2358 $this->setParams($this->_formValues);
6a488035
TO
2359 }
2360
2361 // hack to fix params when submitted from dashboard, CRM-8532
2362 // fields array is missing because form building etc is skipped
2363 // in dashboard mode for report
c58f66e0 2364 //@todo - this could be done in the dashboard no we have a setter
a7488080 2365 if (empty($this->_params['fields']) && !$this->_noFields) {
c58f66e0 2366 $this->setParams($this->_formValues);
6a488035
TO
2367 }
2368
2369 $this->_formValues = $this->_params;
2370 if (CRM_Core_Permission::check('administer Reports') &&
2371 isset($this->_id) &&
9d72cede
EM
2372 ($this->_instanceButtonName ==
2373 $this->controller->getButtonName() . '_save' ||
6a488035
TO
2374 $this->_chartButtonName == $this->controller->getButtonName()
2375 )
2376 ) {
2377 $this->assign('updateReportButton', TRUE);
2378 }
2379 $this->processReportMode();
c58f66e0
E
2380 $this->beginPostProcessCommon();
2381 }
2382
2383 /**
100fef9d 2384 * BeginPostProcess function run in both report mode and non-report mode (api)
c58f66e0 2385 */
00be9182 2386 public function beginPostProcessCommon() {
c58f66e0 2387
6a488035
TO
2388 }
2389
74cf4551
EM
2390 /**
2391 * @param bool $applyLimit
2392 *
2393 * @return string
2394 */
00be9182 2395 public function buildQuery($applyLimit = TRUE) {
6a488035
TO
2396 $this->select();
2397 $this->from();
2398 $this->customDataFrom();
2399 $this->where();
2400 $this->groupBy();
2401 $this->orderBy();
2402
2403 // order_by columns not selected for display need to be included in SELECT
2404 $unselectedSectionColumns = $this->unselectedSectionColumns();
2405 foreach ($unselectedSectionColumns as $alias => $section) {
2406 $this->_select .= ", {$section['dbAlias']} as {$alias}";
2407 }
2408
8cc574cf 2409 if ($applyLimit && empty($this->_params['charts'])) {
6a488035
TO
2410 $this->limit();
2411 }
2412 CRM_Utils_Hook::alterReportVar('sql', $this, $this);
2413
2414 $sql = "{$this->_select} {$this->_from} {$this->_where} {$this->_groupBy} {$this->_having} {$this->_orderBy} {$this->_limit}";
2415 return $sql;
2416 }
2417
00be9182 2418 public function groupBy() {
6a488035 2419 $groupBys = array();
a7488080 2420 if (!empty($this->_params['group_bys']) &&
6a488035
TO
2421 is_array($this->_params['group_bys']) &&
2422 !empty($this->_params['group_bys'])
2423 ) {
2424 foreach ($this->_columns as $tableName => $table) {
2425 if (array_key_exists('group_bys', $table)) {
2426 foreach ($table['group_bys'] as $fieldName => $field) {
a7488080 2427 if (!empty($this->_params['group_bys'][$fieldName])) {
6a488035
TO
2428 $groupBys[] = $field['dbAlias'];
2429 }
2430 }
2431 }
2432 }
2433 }
2434
2435 if (!empty($groupBys)) {
2436 $this->_groupBy = "GROUP BY " . implode(', ', $groupBys);
2437 }
2438 }
2439
00be9182 2440 public function orderBy() {
9d72cede 2441 $this->_orderBy = "";
6a488035
TO
2442 $this->_sections = array();
2443 $this->storeOrderByArray();
9d72cede 2444 if (!empty($this->_orderByArray) && !$this->_rollup == 'WITH ROLLUP') {
6a488035
TO
2445 $this->_orderBy = "ORDER BY " . implode(', ', $this->_orderByArray);
2446 }
2447 $this->assign('sections', $this->_sections);
2448 }
f2947aea 2449
6a488035
TO
2450 /*
2451 * In some cases other functions want to know which fields are selected for ordering by
2452 * Separating this into a separate function allows it to be called separately from constructing
2453 * the order by clause
2454 */
00be9182 2455 public function storeOrderByArray() {
9d72cede 2456 $orderBys = array();
6a488035 2457
a7488080 2458 if (!empty($this->_params['order_bys']) &&
6a488035
TO
2459 is_array($this->_params['order_bys']) &&
2460 !empty($this->_params['order_bys'])
2461 ) {
2462
2463 // Proces order_bys in user-specified order
2464 foreach ($this->_params['order_bys'] as $orderBy) {
2465 $orderByField = array();
2466 foreach ($this->_columns as $tableName => $table) {
2467 if (array_key_exists('order_bys', $table)) {
2468 // For DAO columns defined in $this->_columns
2469 $fields = $table['order_bys'];
2470 }
2471 elseif (array_key_exists('extends', $table)) {
2472 // For custom fields referenced in $this->_customGroupExtends
1efec7ff 2473 $fields = CRM_Utils_Array::value('fields', $table, array());
6a488035
TO
2474 }
2475 if (!empty($fields) && is_array($fields)) {
2476 foreach ($fields as $fieldName => $field) {
2477 if ($fieldName == $orderBy['column']) {
f2947aea 2478 $orderByField = array_merge($field, $orderBy);
6a488035
TO
2479 $orderByField['tplField'] = "{$tableName}_{$fieldName}";
2480 break 2;
2481 }
2482 }
2483 }
2484 }
2485
2486 if (!empty($orderByField)) {
f2947aea 2487 $this->_orderByFields[] = $orderByField;
6a488035
TO
2488 $orderBys[] = "{$orderByField['dbAlias']} {$orderBy['order']}";
2489
2490 // Record any section headers for assignment to the template
a7488080 2491 if (!empty($orderBy['section'])) {
b5801e1d 2492 $orderByField['pageBreak'] = CRM_Utils_Array::value('pageBreak', $orderBy);
6a488035
TO
2493 $this->_sections[$orderByField['tplField']] = $orderByField;
2494 }
2495 }
2496 }
2497 }
2498
2499 $this->_orderByArray = $orderBys;
2500
2501 $this->assign('sections', $this->_sections);
2502 }
2503
74cf4551
EM
2504 /**
2505 * @return array
2506 */
00be9182 2507 public function unselectedSectionColumns() {
6a488035
TO
2508 $selectColumns = array();
2509 foreach ($this->_columns as $tableName => $table) {
2510 if (array_key_exists('fields', $table)) {
2511 foreach ($table['fields'] as $fieldName => $field) {
9d72cede
EM
2512 if (!empty($field['required']) ||
2513 !empty($this->_params['fields'][$fieldName])
2514 ) {
6a488035
TO
2515
2516 $selectColumns["{$tableName}_{$fieldName}"] = 1;
2517 }
2518 }
2519 }
2520 }
f2947aea 2521
6a488035
TO
2522 if (is_array($this->_sections)) {
2523 return array_diff_key($this->_sections, $selectColumns);
2524 }
2525 else {
2526 return array();
2527 }
2528 }
2529
74cf4551
EM
2530 /**
2531 * @param $sql
2532 * @param $rows
2533 */
00be9182 2534 public function buildRows($sql, &$rows) {
6a488035
TO
2535 $dao = CRM_Core_DAO::executeQuery($sql);
2536 if (!is_array($rows)) {
2537 $rows = array();
2538 }
2539
2540 // use this method to modify $this->_columnHeaders
2541 $this->modifyColumnHeaders();
2542
2543 $unselectedSectionColumns = $this->unselectedSectionColumns();
2544
2545 while ($dao->fetch()) {
2546 $row = array();
2547 foreach ($this->_columnHeaders as $key => $value) {
2548 if (property_exists($dao, $key)) {
2549 $row[$key] = $dao->$key;
2550 }
2551 }
2552
2553 // section headers not selected for display need to be added to row
2554 foreach ($unselectedSectionColumns as $key => $values) {
2555 if (property_exists($dao, $key)) {
2556 $row[$key] = $dao->$key;
2557 }
2558 }
2559
2560 $rows[] = $row;
2561 }
2562 }
2563
2564 /**
2565 * When "order by" fields are marked as sections, this assigns to the template
2566 * an array of total counts for each section. This data is used by the Smarty
2567 * plugin {sectionTotal}
2568 */
00be9182 2569 public function sectionTotals() {
6a488035
TO
2570
2571 // Reports using order_bys with sections must populate $this->_selectAliases in select() method.
2572 if (empty($this->_selectAliases)) {
2573 return;
2574 }
2575
2576 if (!empty($this->_sections)) {
2577 // build the query with no LIMIT clause
2578 $select = str_ireplace('SELECT SQL_CALC_FOUND_ROWS ', 'SELECT ', $this->_select);
2579 $sql = "{$select} {$this->_from} {$this->_where} {$this->_groupBy} {$this->_having} {$this->_orderBy}";
2580
2581 // pull section aliases out of $this->_sections
2582 $sectionAliases = array_keys($this->_sections);
2583
2584 $ifnulls = array();
2585 foreach (array_merge($sectionAliases, $this->_selectAliases) as $alias) {
2586 $ifnulls[] = "ifnull($alias, '') as $alias";
2587 }
2588
2589 /* Group (un-limited) report by all aliases and get counts. This might
2590 * be done more efficiently when the contents of $sql are known, ie. by
2591 * overriding this method in the report class.
2592 */
2593
2594
9d72cede
EM
2595 $query = "select " . implode(", ", $ifnulls) .
2596 ", count(*) as ct from ($sql) as subquery group by " .
2597 implode(", ", $sectionAliases);
6a488035
TO
2598
2599 // initialize array of total counts
2600 $totals = array();
2601 $dao = CRM_Core_DAO::executeQuery($query);
2602 while ($dao->fetch()) {
2603
2604 // let $this->_alterDisplay translate any integer ids to human-readable values.
2605 $rows[0] = $dao->toArray();
2606 $this->alterDisplay($rows);
2607 $row = $rows[0];
2608
2609 // add totals for all permutations of section values
9d72cede
EM
2610 $values = array();
2611 $i = 1;
6a488035
TO
2612 $aliasCount = count($sectionAliases);
2613 foreach ($sectionAliases as $alias) {
2614 $values[] = $row[$alias];
2615 $key = implode(CRM_Core_DAO::VALUE_SEPARATOR, $values);
2616 if ($i == $aliasCount) {
2617 // the last alias is the lowest-level section header; use count as-is
2618 $totals[$key] = $dao->ct;
2619 }
2620 else {
2621 // other aliases are higher level; roll count into their total
2622 $totals[$key] += $dao->ct;
2623 }
2624 }
2625 }
2626 $this->assign('sectionTotals', $totals);
2627 }
2628 }
2629
00be9182 2630 public function modifyColumnHeaders() {
6a488035
TO
2631 // use this method to modify $this->_columnHeaders
2632 }
2633
74cf4551
EM
2634 /**
2635 * @param $rows
2636 */
00be9182 2637 public function doTemplateAssignment(&$rows) {
6a488035
TO
2638 $this->assign_by_ref('columnHeaders', $this->_columnHeaders);
2639 $this->assign_by_ref('rows', $rows);
2640 $this->assign('statistics', $this->statistics($rows));
2641 }
2642
74cf4551 2643 /**
4f1f1f2a 2644 * override this method to build your own statistics
74cf4551
EM
2645 * @param $rows
2646 *
2647 * @return array
2648 */
00be9182 2649 public function statistics(&$rows) {
6a488035
TO
2650 $statistics = array();
2651
2652 $count = count($rows);
2653
2654 if ($this->_rollup && ($this->_rollup != '') && $this->_grandFlag) {
2655 $count++;
2656 }
2657
2658 $this->countStat($statistics, $count);
2659
2660 $this->groupByStat($statistics);
2661
2662 $this->filterStat($statistics);
2663
2664 return $statistics;
2665 }
2666
74cf4551
EM
2667 /**
2668 * @param $statistics
2669 * @param $count
2670 */
00be9182 2671 public function countStat(&$statistics, $count) {
9d72cede
EM
2672 $statistics['counts']['rowCount'] = array(
2673 'title' => ts('Row(s) Listed'),
2674 'value' => $count,
6a488035
TO
2675 );
2676
2677 if ($this->_rowsFound && ($this->_rowsFound > $count)) {
9d72cede
EM
2678 $statistics['counts']['rowsFound'] = array(
2679 'title' => ts('Total Row(s)'),
2680 'value' => $this->_rowsFound,
6a488035
TO
2681 );
2682 }
2683 }
2684
74cf4551
EM
2685 /**
2686 * @param $statistics
2687 */
00be9182 2688 public function groupByStat(&$statistics) {
a7488080 2689 if (!empty($this->_params['group_bys']) &&
6a488035
TO
2690 is_array($this->_params['group_bys']) &&
2691 !empty($this->_params['group_bys'])
2692 ) {
2693 foreach ($this->_columns as $tableName => $table) {
2694 if (array_key_exists('group_bys', $table)) {
2695 foreach ($table['group_bys'] as $fieldName => $field) {
a7488080 2696 if (!empty($this->_params['group_bys'][$fieldName])) {
6a488035
TO
2697 $combinations[] = $field['title'];
2698 }
2699 }
2700 }
2701 }
9d72cede
EM
2702 $statistics['groups'][] = array(
2703 'title' => ts('Grouping(s)'),
2704 'value' => implode(' & ', $combinations),
6a488035
TO
2705 );
2706 }
2707 }
2708
74cf4551
EM
2709 /**
2710 * @param $statistics
2711 */
00be9182 2712 public function filterStat(&$statistics) {
6a488035
TO
2713 foreach ($this->_columns as $tableName => $table) {
2714 if (array_key_exists('filters', $table)) {
2715 foreach ($table['filters'] as $fieldName => $field) {
9d72cede
EM
2716 if (CRM_Utils_Array::value('type', $field) & CRM_Utils_Type::T_DATE &&
2717 CRM_Utils_Array::value('operatorType', $field) !=
2718 CRM_Report_Form::OP_MONTH
2719 ) {
8f1445ea
DL
2720 list($from, $to) =
2721 $this->getFromTo(
2722 CRM_Utils_Array::value("{$fieldName}_relative", $this->_params),
2723 CRM_Utils_Array::value("{$fieldName}_from", $this->_params),
2724 CRM_Utils_Array::value("{$fieldName}_to", $this->_params),
2725 CRM_Utils_Array::value("{$fieldName}_from_time", $this->_params),
2726 CRM_Utils_Array::value("{$fieldName}_to_time", $this->_params)
2727 );
0d8afee2 2728 $from_time_format = !empty($this->_params["{$fieldName}_from_time"]) ? 'h' : 'd';
9d72cede 2729 $from = CRM_Utils_Date::customFormat($from, NULL, array($from_time_format));
6a488035 2730
0d8afee2 2731 $to_time_format = !empty($this->_params["{$fieldName}_to_time"]) ? 'h' : 'd';
9d72cede 2732 $to = CRM_Utils_Date::customFormat($to, NULL, array($to_time_format));
6a488035
TO
2733
2734 if ($from || $to) {
2735 $statistics['filters'][] = array(
2736 'title' => $field['title'],
10a5be27 2737 'value' => ts("Between %1 and %2", array(1 => $from, 2 => $to)),
6a488035
TO
2738 );
2739 }
2740 elseif (in_array($rel = CRM_Utils_Array::value("{$fieldName}_relative", $this->_params),
9d72cede
EM
2741 array_keys($this->getOperationPair(CRM_Report_Form::OP_DATE))
2742 )) {
160d32e1 2743 $pair = $this->getOperationPair(CRM_Report_Form::OP_DATE);
6a488035
TO
2744 $statistics['filters'][] = array(
2745 'title' => $field['title'],
2746 'value' => $pair[$rel],
2747 );
2748 }
2749 }
2750 else {
2751 $op = CRM_Utils_Array::value("{$fieldName}_op", $this->_params);
2752 $value = NULL;
2753 if ($op) {
1b36206c 2754 $pair = $this->getOperationPair(
8f1445ea
DL
2755 CRM_Utils_Array::value('operatorType', $field),
2756 $fieldName
6a488035
TO
2757 );
2758 $min = CRM_Utils_Array::value("{$fieldName}_min", $this->_params);
2759 $max = CRM_Utils_Array::value("{$fieldName}_max", $this->_params);
2760 $val = CRM_Utils_Array::value("{$fieldName}_value", $this->_params);
2107cde9
CW
2761 if (in_array($op, array('bw', 'nbw')) && ($min || $max)) {
2762 $value = "{$pair[$op]} $min " . ts('and') . " $max";
2763 }
d3e86119 2764 elseif ($val && CRM_Utils_Array::value('operatorType', $field) & self::OP_ENTITYREF) {
2107cde9 2765 $this->setEntityRefDefaults($field, $tableName);
9d72cede
EM
2766 $result = civicrm_api3($field['attributes']['entity'], 'getlist',
2767 array('id' => $val) +
2768 CRM_Utils_Array::value('api', $field['attributes'], array()));
2107cde9
CW
2769 $values = array();
2770 foreach ($result['values'] as $v) {
2771 $values[] = $v['label'];
2772 }
2773 $value = "{$pair[$op]} " . implode(', ', $values);
6a488035
TO
2774 }
2775 elseif ($op == 'nll' || $op == 'nnll') {
2776 $value = $pair[$op];
2777 }
2778 elseif (is_array($val) && (!empty($val))) {
f974e915 2779 $options = CRM_Utils_Array::value('options', $field, array());
6a488035
TO
2780 foreach ($val as $key => $valIds) {
2781 if (isset($options[$valIds])) {
2782 $val[$key] = $options[$valIds];
2783 }
2784 }
9d72cede
EM
2785 $pair[$op] = (count($val) == 1) ? (($op == 'notin' || $op ==
2786 'mnot') ? ts('Is Not') : ts('Is')) : CRM_Utils_Array::value($op, $pair);
2787 $val = implode(', ', $val);
2788 $value = "{$pair[$op]} " . $val;
6a488035 2789 }
9d72cede
EM
2790 elseif (!is_array($val) && (!empty($val) || $val == '0') &&
2791 isset($field['options']) &&
6a488035
TO
2792 is_array($field['options']) && !empty($field['options'])
2793 ) {
9d72cede
EM
2794 $value = CRM_Utils_Array::value($op, $pair) . " " .
2795 CRM_Utils_Array::value($val, $field['options'], $val);
6a488035
TO
2796 }
2797 elseif ($val) {
2798 $value = CRM_Utils_Array::value($op, $pair) . " " . $val;
2799 }
2800 }
2801 if ($value) {
9d72cede
EM
2802 $statistics['filters'][] = array(
2803 'title' => CRM_Utils_Array::value('title', $field),
2804 'value' => $value,
6a488035
TO
2805 );
2806 }
2807 }
2808 }
2809 }
2810 }
2811 }
2812
74cf4551
EM
2813 /**
2814 * @param null $rows
2815 */
00be9182 2816 public function endPostProcess(&$rows = NULL) {
9d72cede 2817 if ($this->_storeResultSet) {
ae555e90
DS
2818 $this->_resultSet = $rows;
2819 }
2820
6a488035
TO
2821 if ($this->_outputMode == 'print' ||
2822 $this->_outputMode == 'pdf' ||
2823 $this->_sendmail
2824 ) {
2825
2826 $content = $this->compileContent();
2827 $url = CRM_Utils_System::url("civicrm/report/instance/{$this->_id}",
9d72cede 2828 "reset=1", TRUE
6a488035
TO
2829 );
2830
2831 if ($this->_sendmail) {
2832 $config = CRM_Core_Config::singleton();
2833 $attachments = array();
2834
2835 if ($this->_outputMode == 'csv') {
9d72cede
EM
2836 $content =
2837 $this->_formValues['report_header'] . '<p>' . ts('Report URL') .
2838 ": {$url}</p>" . '<p>' .
2839 ts('The report is attached as a CSV file.') . '</p>' .
2840 $this->_formValues['report_footer'];
2841
2842 $csvFullFilename = $config->templateCompileDir .
2843 CRM_Utils_File::makeFileName('CiviReport.csv');
6a488035
TO
2844 $csvContent = CRM_Report_Utils_Report::makeCsv($this, $rows);
2845 file_put_contents($csvFullFilename, $csvContent);
2846 $attachments[] = array(
2847 'fullPath' => $csvFullFilename,
2848 'mime_type' => 'text/csv',
2849 'cleanName' => 'CiviReport.csv',
2850 );
2851 }
2852 if ($this->_outputMode == 'pdf') {
2853 // generate PDF content
9d72cede
EM
2854 $pdfFullFilename = $config->templateCompileDir .
2855 CRM_Utils_File::makeFileName('CiviReport.pdf');
6a488035
TO
2856 file_put_contents($pdfFullFilename,
2857 CRM_Utils_PDF_Utils::html2pdf($content, "CiviReport.pdf",
2858 TRUE, array('orientation' => 'landscape')
2859 )
2860 );
2861 // generate Email Content
9d72cede
EM
2862 $content =
2863 $this->_formValues['report_header'] . '<p>' . ts('Report URL') .
2864 ": {$url}</p>" . '<p>' .
2865 ts('The report is attached as a PDF file.') . '</p>' .
2866 $this->_formValues['report_footer'];
6a488035
TO
2867
2868 $attachments[] = array(
2869 'fullPath' => $pdfFullFilename,
2870 'mime_type' => 'application/pdf',
2871 'cleanName' => 'CiviReport.pdf',
2872 );
2873 }
2874
2875 if (CRM_Report_Utils_Report::mailReport($content, $this->_id,
9d72cede
EM
2876 $this->_outputMode, $attachments
2877 )
2878 ) {
6a488035
TO
2879 CRM_Core_Session::setStatus(ts("Report mail has been sent."), ts('Sent'), 'success');
2880 }
2881 else {
2882 CRM_Core_Session::setStatus(ts("Report mail could not be sent."), ts('Mail Error'), 'error');
2883 }
824aede6 2884 return TRUE;
6a488035
TO
2885 }
2886 elseif ($this->_outputMode == 'print') {
2887 echo $content;
2888 }
2889 else {
2890 if ($chartType = CRM_Utils_Array::value('charts', $this->_params)) {
2891 $config = CRM_Core_Config::singleton();
2892 //get chart image name
2893 $chartImg = $this->_chartId . '.png';
2894 //get image url path
9d72cede
EM
2895 $uploadUrl =
2896 str_replace('/persist/contribute/', '/persist/', $config->imageUploadURL) .
2897 'openFlashChart/';
6a488035
TO
2898 $uploadUrl .= $chartImg;
2899 //get image doc path to overwrite
9d72cede
EM
2900 $uploadImg =
2901 str_replace('/persist/contribute/', '/persist/', $config->imageUploadDir) .
2902 'openFlashChart/' . $chartImg;
6a488035
TO
2903 //Load the image
2904 $chart = imagecreatefrompng($uploadUrl);
2905 //convert it into formattd png
2906 header('Content-type: image/png');
2907 //overwrite with same image
2908 imagepng($chart, $uploadImg);
2909 //delete the object
2910 imagedestroy($chart);
2911 }
2912 CRM_Utils_PDF_Utils::html2pdf($content, "CiviReport.pdf", FALSE, array('orientation' => 'landscape'));
2913 }
2914 CRM_Utils_System::civiExit();
2915 }
2916 elseif ($this->_outputMode == 'csv') {
2917 CRM_Report_Utils_Report::export2csv($this, $rows);
2918 }
2919 elseif ($this->_outputMode == 'group') {
2920 $group = $this->_params['groups'];
2921 $this->add2group($group);
2922 }
2923 elseif ($this->_instanceButtonName == $this->controller->getButtonName()) {
2924 CRM_Report_Form_Instance::postProcess($this);
2925 }
2926 elseif ($this->_createNewButtonName == $this->controller->getButtonName() ||
9d72cede
EM
2927 $this->_outputMode == 'create_report'
2928 ) {
6a488035
TO
2929 $this->_createNew = TRUE;
2930 CRM_Report_Form_Instance::postProcess($this);
2931 }
2932 }
8f1445ea 2933
00be9182 2934 public function storeResultSet() {
ae555e90
DS
2935 $this->_storeResultSet = TRUE;
2936 }
2937
74cf4551
EM
2938 /**
2939 * @return bool
2940 */
00be9182 2941 public function getResultSet() {
ae555e90
DS
2942 return $this->_resultSet;
2943 }
2944
6a488035
TO
2945 /*
2946 * Get Template file name - use default form template if a specific one has not been set up for this report
6a488035 2947 */
74cf4551
EM
2948 /**
2949 * Use the form name to create the tpl file name
2950 *
2951 * @return string
74cf4551
EM
2952 */
2953 /**
2954 * @return string
2955 */
00be9182 2956 public function getTemplateFileName() {
6a488035 2957 $defaultTpl = parent::getTemplateFileName();
9d72cede 2958 $template = CRM_Core_Smarty::singleton();
6a488035
TO
2959 if (!$template->template_exists($defaultTpl)) {
2960 $defaultTpl = 'CRM/Report/Form.tpl';
2961 }
2962 return $defaultTpl;
2963 }
2964
2965 /*
2966 * Compile the report content
2967 *
2968 * Although this function is super-short it is useful to keep separate so it can be over-ridden by report classes.
2969 */
74cf4551
EM
2970 /**
2971 * @return string
2972 */
00be9182 2973 public function compileContent() {
8aac22c8 2974 $templateFile = $this->getHookedTemplateFileName();
9d72cede
EM
2975 return $this->_formValues['report_header'] .
2976 CRM_Core_Form::$_template->fetch($templateFile) .
2977 $this->_formValues['report_footer'];
6a488035
TO
2978 }
2979
2980
00be9182 2981 public function postProcess() {
6a488035
TO
2982 // get ready with post process params
2983 $this->beginPostProcess();
2984
2985 // build query
2986 $sql = $this->buildQuery();
2987
2988 // build array of result based on column headers. This method also allows
2989 // modifying column headers before using it to build result set i.e $rows.
2990 $rows = array();
2991 $this->buildRows($sql, $rows);
2992
2993 // format result set.
2994 $this->formatDisplay($rows);
2995
2996 // assign variables to templates
2997 $this->doTemplateAssignment($rows);
2998
2999 // do print / pdf / instance stuff if needed
3000 $this->endPostProcess($rows);
3001 }
3002
74cf4551
EM
3003 /**
3004 * @param int $rowCount
3005 */
00be9182 3006 public function limit($rowCount = self::ROW_COUNT_LIMIT) {
6a488035
TO
3007 // lets do the pager if in html mode
3008 $this->_limit = NULL;
77b97be7 3009
dbb4a0f9
PN
3010 // CRM-14115, over-ride row count if rowCount is specified in URL
3011 if ($this->_dashBoardRowCount) {
3012 $rowCount = $this->_dashBoardRowCount;
3013 }
6a488035
TO
3014 if ($this->_outputMode == 'html' || $this->_outputMode == 'group') {
3015 $this->_select = str_ireplace('SELECT ', 'SELECT SQL_CALC_FOUND_ROWS ', $this->_select);
3016
3017 $pageId = CRM_Utils_Request::retrieve('crmPID', 'Integer', CRM_Core_DAO::$_nullObject);
3018
3019 if (!$pageId && !empty($_POST)) {
3020 if (isset($_POST['PagerBottomButton']) && isset($_POST['crmPID_B'])) {
9d72cede 3021 $pageId = max((int) @$_POST['crmPID_B'], 1);
6a488035
TO
3022 }
3023 elseif (isset($_POST['PagerTopButton']) && isset($_POST['crmPID'])) {
9d72cede 3024 $pageId = max((int) @$_POST['crmPID'], 1);
6a488035
TO
3025 }
3026 unset($_POST['crmPID_B'], $_POST['crmPID']);
3027 }
3028
3029 $pageId = $pageId ? $pageId : 1;
3030 $this->set(CRM_Utils_Pager::PAGE_ID, $pageId);
3031 $offset = ($pageId - 1) * $rowCount;
3032
bf00d1b6
DL
3033 $offset = CRM_Utils_Type::escape($offset, 'Int');
3034 $rowCount = CRM_Utils_Type::escape($rowCount, 'Int');
3035
dd3a4117 3036 $this->_limit = " LIMIT $offset, $rowCount";
6a488035
TO
3037 return array($offset, $rowCount);
3038 }
9d72cede
EM
3039 if ($this->_limitValue) {
3040 if ($this->_offsetValue) {
6f900755
E
3041 $this->_limit = " LIMIT {$this->_offsetValue}, {$this->_limitValue} ";
3042 }
3043 else {
3044 $this->_limit = " LIMIT " . $this->_limitValue;
3045 }
3046 }
6a488035
TO
3047 }
3048
74cf4551
EM
3049 /**
3050 * @param int $rowCount
3051 */
00be9182 3052 public function setPager($rowCount = self::ROW_COUNT_LIMIT) {
dbb4a0f9
PN
3053
3054 // CRM-14115, over-ride row count if rowCount is specified in URL
3055 if ($this->_dashBoardRowCount) {
3056 $rowCount = $this->_dashBoardRowCount;
3057 }
3058
6a488035 3059 if ($this->_limit && ($this->_limit != '')) {
9d72cede 3060 $sql = "SELECT FOUND_ROWS();";
6a488035 3061 $this->_rowsFound = CRM_Core_DAO::singleValueQuery($sql);
9d72cede 3062 $params = array(
6a488035
TO
3063 'total' => $this->_rowsFound,
3064 'rowCount' => $rowCount,
3065 'status' => ts('Records') . ' %%StatusMessage%%',
3066 'buttonBottom' => 'PagerBottomButton',
3067 'buttonTop' => 'PagerTopButton',
3068 'pageID' => $this->get(CRM_Utils_Pager::PAGE_ID),
3069 );
3070
3071 $pager = new CRM_Utils_Pager($params);
3072 $this->assign_by_ref('pager', $pager);
ecc20f0e 3073 $this->ajaxResponse['totalRows'] = $this->_rowsFound;
6a488035
TO
3074 }
3075 }
3076
74cf4551
EM
3077 /**
3078 * @param $field
3079 * @param $value
3080 * @param $op
3081 *
3082 * @return string
3083 */
00be9182 3084 public function whereGroupClause($field, $value, $op) {
6a488035
TO
3085
3086 $smartGroupQuery = "";
3087
3088 $group = new CRM_Contact_DAO_Group();
3089 $group->is_active = 1;
3090 $group->find();
3091 $smartGroups = array();
3092 while ($group->fetch()) {
9d72cede
EM
3093 if (in_array($group->id, $this->_params['gid_value']) &&
3094 $group->saved_search_id
3095 ) {
6a488035
TO
3096 $smartGroups[] = $group->id;
3097 }
3098 }
3099
3100 CRM_Contact_BAO_GroupContactCache::check($smartGroups);
3101
3102 $smartGroupQuery = '';
3103 if (!empty($smartGroups)) {
3104 $smartGroups = implode(',', $smartGroups);
3105 $smartGroupQuery = " UNION DISTINCT
3106 SELECT DISTINCT smartgroup_contact.contact_id
3107 FROM civicrm_group_contact_cache smartgroup_contact
3108 WHERE smartgroup_contact.group_id IN ({$smartGroups}) ";
3109 }
3110
29fc2b79 3111 $sqlOp = $this->getSQLOperator($op);
6a488035
TO
3112 if (!is_array($value)) {
3113 $value = array($value);
3114 }
3115 $clause = "{$field['dbAlias']} IN (" . implode(', ', $value) . ")";
3116
3117 return " {$this->_aliases['civicrm_contact']}.id {$sqlOp} (
3118 SELECT DISTINCT {$this->_aliases['civicrm_group']}.contact_id
3119 FROM civicrm_group_contact {$this->_aliases['civicrm_group']}
3120 WHERE {$clause} AND {$this->_aliases['civicrm_group']}.status = 'Added'
3121 {$smartGroupQuery} ) ";
3122 }
3123
74cf4551
EM
3124 /**
3125 * @param $field
3126 * @param $value
3127 * @param $op
3128 *
3129 * @return string
3130 */
00be9182 3131 public function whereTagClause($field, $value, $op) {
6a488035
TO
3132 // not using left join in query because if any contact
3133 // belongs to more than one tag, results duplicate
3134 // entries.
29fc2b79 3135 $sqlOp = $this->getSQLOperator($op);
6a488035
TO
3136 if (!is_array($value)) {
3137 $value = array($value);
3138 }
3139 $clause = "{$field['dbAlias']} IN (" . implode(', ', $value) . ")";
ed795723
JM
3140 $entity_table = $this->_tagFilterTable;
3141 return " {$this->_aliases[$entity_table]}.id {$sqlOp} (
6a488035
TO
3142 SELECT DISTINCT {$this->_aliases['civicrm_tag']}.entity_id
3143 FROM civicrm_entity_tag {$this->_aliases['civicrm_tag']}
ed795723 3144 WHERE entity_table = '$entity_table' AND {$clause} ) ";
6a488035
TO
3145 }
3146
00be9182 3147 public function whereMembershipOrgClause($field, $value, $op) {
114a2c85
DG
3148 $sqlOp = $this->getSQLOperator($op);
3149 if (!is_array($value)) {
3150 $value = array($value);
3151 }
258e2add 3152
114a2c85
DG
3153 $tmp_membership_org_sql_list = implode(', ', $value);
3154 return " {$this->_aliases['civicrm_contact']}.id {$sqlOp} (
97709b72 3155 SELECT DISTINCT mem.contact_id
258e2add 3156 FROM civicrm_membership mem
97709b72 3157 LEFT JOIN civicrm_membership_status mem_status ON mem.status_id = mem_status.id
258e2add 3158 LEFT JOIN civicrm_membership_type mt ON mem.membership_type_id = mt.id
9d72cede
EM
3159 WHERE mt.member_of_contact_id IN (" .
3160 $tmp_membership_org_sql_list . ")
97709b72
DG
3161 AND mt.is_active = '1'
3162 AND mem_status.is_current_member = '1'
3163 AND mem_status.is_active = '1' ) ";
9d72cede 3164 }
114a2c85 3165
00be9182 3166 public function whereMembershipTypeClause($field, $value, $op) {
114a2c85
DG
3167 $sqlOp = $this->getSQLOperator($op);
3168 if (!is_array($value)) {
3169 $value = array($value);
3170 }
258e2add 3171
9d72cede 3172 $tmp_membership_sql_list = implode(', ', $value);
114a2c85 3173 return " {$this->_aliases['civicrm_contact']}.id {$sqlOp} (
97709b72 3174 SELECT DISTINCT mem.contact_id
258e2add 3175 FROM civicrm_membership mem
3176 LEFT JOIN civicrm_membership_status mem_status ON mem.status_id = mem_status.id
3177 LEFT JOIN civicrm_membership_type mt ON mem.membership_type_id = mt.id
9d72cede
EM
3178 WHERE mem.membership_type_id IN (" .
3179 $tmp_membership_sql_list . ")
97709b72
DG
3180 AND mt.is_active = '1'
3181 AND mem_status.is_current_member = '1'
3182 AND mem_status.is_active = '1' ) ";
114a2c85 3183 }
258e2add 3184
74cf4551
EM
3185 /**
3186 * @param string $tableAlias
3187 */
00be9182 3188 public function buildACLClause($tableAlias = 'contact_a') {
6a488035
TO
3189 list($this->_aclFrom, $this->_aclWhere) = CRM_Contact_BAO_Contact_Permission::cacheClause($tableAlias);
3190 }
3191
74cf4551
EM
3192 /**
3193 * @param bool $addFields
3194 * @param array $permCustomGroupIds
3195 */
00be9182 3196 public function addCustomDataToColumns($addFields = TRUE, $permCustomGroupIds = array()) {
6a488035
TO
3197 if (empty($this->_customGroupExtends)) {
3198 return;
3199 }
3200 if (!is_array($this->_customGroupExtends)) {
3201 $this->_customGroupExtends = array($this->_customGroupExtends);
3202 }
3203 $customGroupWhere = '';
3204 if (!empty($permCustomGroupIds)) {
9d72cede
EM
3205 $customGroupWhere = "cg.id IN (" . implode(',', $permCustomGroupIds) .
3206 ") AND";
6a488035
TO
3207 }
3208 $sql = "
3209SELECT cg.table_name, cg.title, cg.extends, cf.id as cf_id, cf.label,
3210 cf.column_name, cf.data_type, cf.html_type, cf.option_group_id, cf.time_format
3211FROM civicrm_custom_group cg
3212INNER JOIN civicrm_custom_field cf ON cg.id = cf.custom_group_id
3213WHERE cg.extends IN ('" . implode("','", $this->_customGroupExtends) . "') AND
3214 {$customGroupWhere}
3215 cg.is_active = 1 AND
3216 cf.is_active = 1 AND
3217 cf.is_searchable = 1
3218ORDER BY cg.weight, cf.weight";
3219 $customDAO = CRM_Core_DAO::executeQuery($sql);
3220
3221 $curTable = NULL;
3222 while ($customDAO->fetch()) {
3223 if ($customDAO->table_name != $curTable) {
3224 $curTable = $customDAO->table_name;
3225 $curFields = $curFilters = array();
3226
3227 // dummy dao object
3228 $this->_columns[$curTable]['dao'] = 'CRM_Contact_DAO_Contact';
3229 $this->_columns[$curTable]['extends'] = $customDAO->extends;
3230 $this->_columns[$curTable]['grouping'] = $customDAO->table_name;
3231 $this->_columns[$curTable]['group_title'] = $customDAO->title;
3232
3233 foreach (array(
9d72cede
EM
3234 'fields',
3235 'filters',
21dfd5f5 3236 'group_bys',
9d72cede 3237 ) as $colKey) {
6a488035
TO
3238 if (!array_key_exists($colKey, $this->_columns[$curTable])) {
3239 $this->_columns[$curTable][$colKey] = array();
3240 }
3241 }
3242 }
3243 $fieldName = 'custom_' . $customDAO->cf_id;
3244
3245 if ($addFields) {
3246 // this makes aliasing work in favor
3247 $curFields[$fieldName] = array(
3248 'name' => $customDAO->column_name,
3249 'title' => $customDAO->label,
3250 'dataType' => $customDAO->data_type,
3251 'htmlType' => $customDAO->html_type,
3252 );
3253 }
3254 if ($this->_customGroupFilters) {
3255 // this makes aliasing work in favor
3256 $curFilters[$fieldName] = array(
3257 'name' => $customDAO->column_name,
3258 'title' => $customDAO->label,
3259 'dataType' => $customDAO->data_type,
3260 'htmlType' => $customDAO->html_type,
3261 );
3262 }
3263
3264 switch ($customDAO->data_type) {
3265 case 'Date':
3266 // filters
3267 $curFilters[$fieldName]['operatorType'] = CRM_Report_Form::OP_DATE;
3268 $curFilters[$fieldName]['type'] = CRM_Utils_Type::T_DATE;
3269 // CRM-6946, show time part for datetime date fields
3270 if ($customDAO->time_format) {
3271 $curFields[$fieldName]['type'] = CRM_Utils_Type::T_TIMESTAMP;
3272 }
3273 break;
3274
3275 case 'Boolean':
3276 $curFilters[$fieldName]['operatorType'] = CRM_Report_Form::OP_SELECT;
9d72cede
EM
3277 $curFilters[$fieldName]['options'] = array(
3278 '' => ts('- select -'),
3279 1 => ts('Yes'),
3280 0 => ts('No'),
6a488035
TO
3281 );
3282 $curFilters[$fieldName]['type'] = CRM_Utils_Type::T_INT;
3283 break;
3284
3285 case 'Int':
3286 $curFilters[$fieldName]['operatorType'] = CRM_Report_Form::OP_INT;
3287 $curFilters[$fieldName]['type'] = CRM_Utils_Type::T_INT;
3288 break;
3289
3290 case 'Money':
3291 $curFilters[$fieldName]['operatorType'] = CRM_Report_Form::OP_FLOAT;
3292 $curFilters[$fieldName]['type'] = CRM_Utils_Type::T_MONEY;
3293 break;
3294
3295 case 'Float':
3296 $curFilters[$fieldName]['operatorType'] = CRM_Report_Form::OP_FLOAT;
3297 $curFilters[$fieldName]['type'] = CRM_Utils_Type::T_FLOAT;
3298 break;
3299
3300 case 'String':
3301 $curFilters[$fieldName]['type'] = CRM_Utils_Type::T_STRING;
3302
3303 if (!empty($customDAO->option_group_id)) {
3304 if (in_array($customDAO->html_type, array(
9d72cede
EM
3305 'Multi-Select',
3306 'AdvMulti-Select',
21dfd5f5 3307 'CheckBox',
9d72cede 3308 ))) {
6a488035
TO
3309 $curFilters[$fieldName]['operatorType'] = CRM_Report_Form::OP_MULTISELECT_SEPARATOR;
3310 }
3311 else {
3312 $curFilters[$fieldName]['operatorType'] = CRM_Report_Form::OP_MULTISELECT;
3313 }
3314 if ($this->_customGroupFilters) {
3315 $curFilters[$fieldName]['options'] = array();
9d72cede 3316 $ogDAO = CRM_Core_DAO::executeQuery("SELECT ov.value, ov.label FROM civicrm_option_value ov WHERE ov.option_group_id = %1 ORDER BY ov.weight", array(
86bd39be
TO
3317 1 => array(
3318 $customDAO->option_group_id,
21dfd5f5
TO
3319 'Integer',
3320 ),
86bd39be 3321 ));
6a488035
TO
3322 while ($ogDAO->fetch()) {
3323 $curFilters[$fieldName]['options'][$ogDAO->value] = $ogDAO->label;
3324 }
3325 }
3326 }
3327 break;
3328
3329 case 'StateProvince':
3330 if (in_array($customDAO->html_type, array(
21dfd5f5 3331 'Multi-Select State/Province',
9d72cede 3332 ))) {
6a488035
TO
3333 $curFilters[$fieldName]['operatorType'] = CRM_Report_Form::OP_MULTISELECT_SEPARATOR;
3334 }
3335 else {
3336 $curFilters[$fieldName]['operatorType'] = CRM_Report_Form::OP_MULTISELECT;
3337 }
3338 $curFilters[$fieldName]['options'] = CRM_Core_PseudoConstant::stateProvince();
3339 break;
3340
3341 case 'Country':
3342 if (in_array($customDAO->html_type, array(
21dfd5f5 3343 'Multi-Select Country',
9d72cede 3344 ))) {
6a488035
TO
3345 $curFilters[$fieldName]['operatorType'] = CRM_Report_Form::OP_MULTISELECT_SEPARATOR;
3346 }
3347 else {
3348 $curFilters[$fieldName]['operatorType'] = CRM_Report_Form::OP_MULTISELECT;
3349 }
3350 $curFilters[$fieldName]['options'] = CRM_Core_PseudoConstant::country();
3351 break;
3352
3353 case 'ContactReference':
3354 $curFilters[$fieldName]['type'] = CRM_Utils_Type::T_STRING;
3355 $curFilters[$fieldName]['name'] = 'display_name';
3356 $curFilters[$fieldName]['alias'] = "contact_{$fieldName}_civireport";
3357
3358 $curFields[$fieldName]['type'] = CRM_Utils_Type::T_STRING;
3359 $curFields[$fieldName]['name'] = 'display_name';
3360 $curFields[$fieldName]['alias'] = "contact_{$fieldName}_civireport";
3361 break;
3362
3363 default:
3364 $curFields[$fieldName]['type'] = CRM_Utils_Type::T_STRING;
3365 $curFilters[$fieldName]['type'] = CRM_Utils_Type::T_STRING;
3366 }
3367
3368 if (!array_key_exists('type', $curFields[$fieldName])) {
fc161185 3369 $curFields[$fieldName]['type'] = CRM_Utils_Array::value('type', $curFilters[$fieldName], array());
6a488035
TO
3370 }
3371
3372 if ($addFields) {
3373 $this->_columns[$curTable]['fields'] = array_merge($this->_columns[$curTable]['fields'], $curFields);
3374 }
3375 if ($this->_customGroupFilters) {
3376 $this->_columns[$curTable]['filters'] = array_merge($this->_columns[$curTable]['filters'], $curFilters);
3377 }
3378 if ($this->_customGroupGroupBy) {
3379 $this->_columns[$curTable]['group_bys'] = array_merge($this->_columns[$curTable]['group_bys'], $curFields);
3380 }
3381 }
3382 }
3383
00be9182 3384 public function customDataFrom() {
6a488035
TO
3385 if (empty($this->_customGroupExtends)) {
3386 return;
3387 }
3388 $mapper = CRM_Core_BAO_CustomQuery::$extendsMap;
3389
3390 foreach ($this->_columns as $table => $prop) {
9d72cede
EM
3391 if (substr($table, 0, 13) == 'civicrm_value' ||
3392 substr($table, 0, 12) == 'custom_value'
3393 ) {
6a488035
TO
3394 $extendsTable = $mapper[$prop['extends']];
3395
3396 // check field is in params
3397 if (!$this->isFieldSelected($prop)) {
3398 continue;
3399 }
4c9d78ea 3400 $baseJoin = CRM_Utils_Array::value($prop['extends'], $this->_customGroupExtendsJoin, "{$this->_aliases[$extendsTable]}.id");
6a488035 3401
9d72cede 3402 $customJoin = is_array($this->_customGroupJoin) ? $this->_customGroupJoin[$table] : $this->_customGroupJoin;
6a488035 3403 $this->_from .= "
aa1aa08e 3404{$customJoin} {$table} {$this->_aliases[$table]} ON {$this->_aliases[$table]}.entity_id = {$baseJoin}";
6a488035
TO
3405 // handle for ContactReference
3406 if (array_key_exists('fields', $prop)) {
3407 foreach ($prop['fields'] as $fieldName => $field) {
9d72cede
EM
3408 if (CRM_Utils_Array::value('dataType', $field) ==
3409 'ContactReference'
3410 ) {
6a488035
TO
3411 $columnName = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomField', CRM_Core_BAO_CustomField::getKeyID($fieldName), 'column_name');
3412 $this->_from .= "
3413LEFT JOIN civicrm_contact {$field['alias']} ON {$field['alias']}.id = {$this->_aliases[$table]}.{$columnName} ";
3414 }
3415 }
3416 }
3417 }
3418 }
3419 }
3420
74cf4551
EM
3421 /**
3422 * @param $prop
3423 *
3424 * @return bool
3425 */
00be9182 3426 public function isFieldSelected($prop) {
6a488035
TO
3427 if (empty($prop)) {
3428 return FALSE;
3429 }
3430
3431 if (!empty($this->_params['fields'])) {
3432 foreach (array_keys($prop['fields']) as $fieldAlias) {
3433 $customFieldId = CRM_Core_BAO_CustomField::getKeyID($fieldAlias);
3434 if ($customFieldId) {
3435 if (array_key_exists($fieldAlias, $this->_params['fields'])) {
3436 return TRUE;
3437 }
3438
3439 //might be survey response field.
9d72cede
EM
3440 if (!empty($this->_params['fields']['survey_response']) &&
3441 !empty($prop['fields'][$fieldAlias]['isSurveyResponseField'])
3442 ) {
6a488035
TO
3443 return TRUE;
3444 }
3445 }
3446 }
3447 }
3448
3449 if (!empty($this->_params['group_bys']) && $this->_customGroupGroupBy) {
3450 foreach (array_keys($prop['group_bys']) as $fieldAlias) {
9d72cede
EM
3451 if (array_key_exists($fieldAlias, $this->_params['group_bys']) &&
3452 CRM_Core_BAO_CustomField::getKeyID($fieldAlias)
3453 ) {
6a488035
TO
3454 return TRUE;
3455 }
3456 }
3457 }
3458
3459 if (!empty($this->_params['order_bys'])) {
3460 foreach (array_keys($prop['fields']) as $fieldAlias) {
3461 foreach ($this->_params['order_bys'] as $orderBy) {
9d72cede
EM
3462 if ($fieldAlias == $orderBy['column'] &&
3463 CRM_Core_BAO_CustomField::getKeyID($fieldAlias)
3464 ) {
6a488035
TO
3465 return TRUE;
3466 }
3467 }
3468 }
3469 }
3470
3471 if (!empty($prop['filters']) && $this->_customGroupFilters) {
3472 foreach ($prop['filters'] as $fieldAlias => $val) {
3473 foreach (array(
9d72cede
EM
3474 'value',
3475 'min',
3476 'max',
3477 'relative',
3478 'from',
21dfd5f5 3479 'to',
9d72cede 3480 ) as $attach) {
6a488035 3481 if (isset($this->_params[$fieldAlias . '_' . $attach]) &&
dfe4b2f5 3482 (!empty($this->_params[$fieldAlias . '_' . $attach])
9d72cede
EM
3483 || ($attach != 'relative' &&
3484 $this->_params[$fieldAlias . '_' . $attach] == '0')
dfe4b2f5 3485 )
9d72cede 3486 ) {
6a488035
TO
3487 return TRUE;
3488 }
3489 }
a7488080 3490 if (!empty($this->_params[$fieldAlias . '_op']) &&
6a488035
TO
3491 in_array($this->_params[$fieldAlias . '_op'], array('nll', 'nnll'))
3492 ) {
3493 return TRUE;
3494 }
3495 }
3496 }
3497
3498 return FALSE;
3499 }
3500
3501 /**
3502 * Check for empty order_by configurations and remove them; also set
3503 * template to hide them.
3504 */
00be9182 3505 public function preProcessOrderBy(&$formValues) {
6a488035 3506 // Object to show/hide form elements
ae555e90 3507 $_showHide = new CRM_Core_ShowHideBlocks('', '');
6a488035
TO
3508
3509 $_showHide->addShow('optionField_1');
3510
3511 // Cycle through order_by options; skip any empty ones, and hide them as well
3512 $n = 1;
3513
3514 if (!empty($formValues['order_bys'])) {
3515 foreach ($formValues['order_bys'] as $order_by) {
3516 if ($order_by['column'] && $order_by['column'] != '-') {
3517 $_showHide->addShow('optionField_' . $n);
3518 $orderBys[$n] = $order_by;
3519 $n++;
3520 }
3521 }
3522 }
3523 for ($i = $n; $i <= 5; $i++) {
3524 if ($i > 1) {
3525 $_showHide->addHide('optionField_' . $i);
3526 }
3527 }
3528
3529 // overwrite order_by options with modified values
3530 if (!empty($orderBys)) {
3531 $formValues['order_bys'] = $orderBys;
3532 }
3533 else {
3534 $formValues['order_bys'] = array(1 => array('column' => '-'));
3535 }
3536
3537 // assign show/hide data to template
3538 $_showHide->addToTemplate();
3539 }
3540
3541 /**
3542 * Does table name have columns in SELECT clause?
3543 *
7e06c9f5
TO
3544 * @param string $tableName
3545 * Name of table (index of $this->_columns array).
6a488035
TO
3546 *
3547 * @return bool
3548 */
00be9182 3549 public function isTableSelected($tableName) {
6a488035
TO
3550 return in_array($tableName, $this->selectedTables());
3551 }
3552
3553 /**
3554 * Fetch array of DAO tables having columns included in SELECT or ORDER BY clause
3555 * (building the array if it's unset)
3556 *
a6c01b45
CW
3557 * @return Array
3558 * >_selectedTables
6a488035 3559 */
00be9182 3560 public function selectedTables() {
6a488035
TO
3561 if (!$this->_selectedTables) {
3562 $orderByColumns = array();
9d72cede
EM
3563 if (array_key_exists('order_bys', $this->_params) &&
3564 is_array($this->_params['order_bys'])
3565 ) {
6a488035
TO
3566 foreach ($this->_params['order_bys'] as $orderBy) {
3567 $orderByColumns[] = $orderBy['column'];
3568 }
3569 }
3570
3571 foreach ($this->_columns as $tableName => $table) {
3572 if (array_key_exists('fields', $table)) {
3573 foreach ($table['fields'] as $fieldName => $field) {
9d72cede
EM
3574 if (!empty($field['required']) ||
3575 !empty($this->_params['fields'][$fieldName])
3576 ) {
6a488035
TO
3577 $this->_selectedTables[] = $tableName;
3578 break;
3579 }
3580 }
3581 }
3582 if (array_key_exists('order_bys', $table)) {
3583 foreach ($table['order_bys'] as $orderByName => $orderBy) {
3584 if (in_array($orderByName, $orderByColumns)) {
3585 $this->_selectedTables[] = $tableName;
3586 break;
3587 }
3588 }
3589 }
3590 if (array_key_exists('filters', $table)) {
3591 foreach ($table['filters'] as $filterName => $filter) {
a7488080 3592 if (!empty($this->_params["{$filterName}_value"]) ||
9d72cede
EM
3593 CRM_Utils_Array::value("{$filterName}_op", $this->_params) ==
3594 'nll' ||
3595 CRM_Utils_Array::value("{$filterName}_op", $this->_params) ==
3596 'nnll'
6a488035
TO
3597 ) {
3598 $this->_selectedTables[] = $tableName;
3599 break;
3600 }
3601 }
3602 }
3603 }
3604 }
3605 return $this->_selectedTables;
3606 }
3607
850e4640
E
3608 /**
3609 * @deprecated - use getAddressColumns which is a more accurate description
3610 * and also accepts an array of options rather than a long list
3611 *
c490a46a 3612 * adding address fields to construct function in reports
77b97be7 3613 *
7e06c9f5
TO
3614 * @param bool $groupBy
3615 * Add GroupBy? Not appropriate for detail report.
3616 * @param bool $orderBy
3617 * Add GroupBy? Not appropriate for detail report.
77b97be7
EM
3618 * @param bool $filters
3619 * @param array $defaults
3620 *
a6c01b45
CW
3621 * @return array
3622 * address fields for construct clause
6a488035 3623 */
00be9182 3624 public function addAddressFields($groupBy = TRUE, $orderBy = FALSE, $filters = TRUE, $defaults = array('country_id' => TRUE)) {
6a488035 3625 $addressFields = array(
9d72cede 3626 'civicrm_address' => array(
6a488035 3627 'dao' => 'CRM_Core_DAO_Address',
9d72cede
EM
3628 'fields' => array(
3629 'name' => array(
3630 'title' => ts('Address Name'),
6a488035
TO
3631 'default' => CRM_Utils_Array::value('name', $defaults, FALSE),
3632 ),
9d72cede
EM
3633 'street_address' => array(
3634 'title' => ts('Street Address'),
6a488035
TO
3635 'default' => CRM_Utils_Array::value('street_address', $defaults, FALSE),
3636 ),
9d72cede
EM
3637 'supplemental_address_1' => array(
3638 'title' => ts('Supplementary Address Field 1'),
6a488035
TO
3639 'default' => CRM_Utils_Array::value('supplemental_address_1', $defaults, FALSE),
3640 ),
9d72cede
EM
3641 'supplemental_address_2' => array(
3642 'title' => ts('Supplementary Address Field 2'),
6a488035
TO
3643 'default' => CRM_Utils_Array::value('supplemental_address_2', $defaults, FALSE),
3644 ),
9d72cede 3645 'street_number' => array(
6a488035
TO
3646 'name' => 'street_number',
3647 'title' => ts('Street Number'),
3648 'type' => 1,
3649 'default' => CRM_Utils_Array::value('street_number', $defaults, FALSE),
3650 ),
9d72cede 3651 'street_name' => array(
6a488035
TO
3652 'name' => 'street_name',
3653 'title' => ts('Street Name'),
3654 'type' => 1,
3655 'default' => CRM_Utils_Array::value('street_name', $defaults, FALSE),
3656 ),
9d72cede 3657 'street_unit' => array(
6a488035
TO
3658 'name' => 'street_unit',
3659 'title' => ts('Street Unit'),
3660 'type' => 1,
3661 'default' => CRM_Utils_Array::value('street_unit', $defaults, FALSE),
3662 ),
9d72cede
EM
3663 'city' => array(
3664 'title' => ts('City'),
6a488035
TO
3665 'default' => CRM_Utils_Array::value('city', $defaults, FALSE),
3666 ),
9d72cede
EM
3667 'postal_code' => array(
3668 'title' => ts('Postal Code'),
6a488035 3669 'default' => CRM_Utils_Array::value('postal_code', $defaults, FALSE),
4cbdd2b1 3670 ),
9d72cede
EM
3671 'postal_code_suffix' => array(
3672 'title' => ts('Postal Code Suffix'),
4cbdd2b1 3673 'default' => CRM_Utils_Array::value('postal_code_suffix', $defaults, FALSE),
6a488035 3674 ),
9d72cede
EM
3675 'country_id' => array(
3676 'title' => ts('Country'),
3677 'default' => CRM_Utils_Array::value('country_id', $defaults, FALSE),
3678 ),
3679 'state_province_id' => array(
3680 'title' => ts('State/Province'),
6a488035
TO
3681 'default' => CRM_Utils_Array::value('state_province_id', $defaults, FALSE),
3682 ),
9d72cede
EM
3683 'county_id' => array(
3684 'title' => ts('County'),
3685 'default' => CRM_Utils_Array::value('county_id', $defaults, FALSE),
3686 ),
6a488035
TO
3687 ),
3688 'grouping' => 'location-fields',
3689 ),
3690 );
3691
3692 if ($filters) {
3693 $addressFields['civicrm_address']['filters'] = array(
9d72cede
EM
3694 'street_number' => array(
3695 'title' => ts('Street Number'),
3696 'type' => 1,
3697 'name' => 'street_number',
6a488035 3698 ),
9d72cede
EM
3699 'street_name' => array(
3700 'title' => ts('Street Name'),
3701 'name' => 'street_name',
3702 'operator' => 'like',
6a488035 3703 ),
9d72cede
EM
3704 'postal_code' => array(
3705 'title' => ts('Postal Code'),
3706 'type' => 1,
3707 'name' => 'postal_code',
6a488035 3708 ),
9d72cede
EM
3709 'city' => array(
3710 'title' => ts('City'),
3711 'operator' => 'like',
3712 'name' => 'city',
6a488035 3713 ),
863581d1
CW
3714 'country_id' => array(
3715 'name' => 'country_id',
3716 'title' => ts('Country'),
6a488035 3717 'type' => CRM_Utils_Type::T_INT,
9d72cede 3718 'operatorType' => CRM_Report_Form::OP_MULTISELECT,
c927c151 3719 'options' => CRM_Core_PseudoConstant::country(),
6a488035
TO
3720 ),
3721 'state_province_id' => array(
3722 'name' => 'state_province_id',
3723 'title' => ts('State/Province'),
3724 'type' => CRM_Utils_Type::T_INT,
c927c151
CW
3725 'operatorType' => CRM_Report_Form::OP_MULTISELECT,
3726 'options' => array(),
6a488035 3727 ),
863581d1
CW
3728 'county_id' => array(
3729 'name' => 'county_id',
3730 'title' => ts('County'),
6a488035 3731 'type' => CRM_Utils_Type::T_INT,
c927c151
CW
3732 'operatorType' => CRM_Report_Form::OP_MULTISELECT,
3733 'options' => array(),
6a488035
TO
3734 ),
3735 );
3736 }
3737
3738 if ($orderBy) {
9d72cede
EM
3739 $addressFields['civicrm_address']['order_bys'] = array(
3740 'street_name' => array('title' => ts('Street Name')),
3741 'street_number' => array('title' => 'Odd / Even Street Number'),
3742 'street_address' => NULL,
3743 'city' => NULL,
3744 'postal_code' => NULL,
6a488035
TO
3745 );
3746 }
3747
3748 if ($groupBy) {
3749 $addressFields['civicrm_address']['group_bys'] = array(
3750 'street_address' => NULL,
3751 'city' => NULL,
3752 'postal_code' => NULL,
9d72cede
EM
3753 'state_province_id' => array(
3754 'title' => ts('State/Province'),
6a488035 3755 ),
9d72cede
EM
3756 'country_id' => array(
3757 'title' => ts('Country'),
6a488035 3758 ),
9d72cede
EM
3759 'county_id' => array(
3760 'title' => ts('County'),
6a488035
TO
3761 ),
3762 );
3763 }
3764 return $addressFields;
3765 }
3766
3767 /*
3768 * Do AlterDisplay processing on Address Fields
3769 */
74cf4551
EM
3770 /**
3771 * @param $row
3772 * @param $rows
3773 * @param $rowNum
3774 * @param $baseUrl
3775 * @param $urltxt
3776 *
3777 * @return bool
3778 */
00be9182 3779 public function alterDisplayAddressFields(&$row, &$rows, &$rowNum, $baseUrl, $urltxt) {
6a488035
TO
3780 $criteriaQueryParams = CRM_Report_Utils_Report::getPreviewCriteriaQueryParams($this->_defaults, $this->_params);
3781 $entryFound = FALSE;
3782 // handle country
3783 if (array_key_exists('civicrm_address_country_id', $row)) {
3784 if ($value = $row['civicrm_address_country_id']) {
3785 $rows[$rowNum]['civicrm_address_country_id'] = CRM_Core_PseudoConstant::country($value, FALSE);
3786 $url = CRM_Report_Utils_Report::getNextUrl($baseUrl,
9d72cede
EM
3787 "reset=1&force=1&{$criteriaQueryParams}&" .
3788 "country_id_op=in&country_id_value={$value}",
3789 $this->_absoluteUrl, $this->_id
6a488035
TO
3790 );
3791 $rows[$rowNum]['civicrm_address_country_id_link'] = $url;
3792 $rows[$rowNum]['civicrm_address_country_id_hover'] = ts("%1 for this country.",
9d72cede 3793 array(1 => $urltxt)
6a488035
TO
3794 );
3795 }
3796
3797 $entryFound = TRUE;
3798 }
3799 if (array_key_exists('civicrm_address_county_id', $row)) {
3800 if ($value = $row['civicrm_address_county_id']) {
3801 $rows[$rowNum]['civicrm_address_county_id'] = CRM_Core_PseudoConstant::county($value, FALSE);
3802 $url = CRM_Report_Utils_Report::getNextUrl($baseUrl,
9d72cede
EM
3803 "reset=1&force=1&{$criteriaQueryParams}&" .
3804 "county_id_op=in&county_id_value={$value}",
3805 $this->_absoluteUrl, $this->_id
6a488035
TO
3806 );
3807 $rows[$rowNum]['civicrm_address_county_id_link'] = $url;
3808 $rows[$rowNum]['civicrm_address_county_id_hover'] = ts("%1 for this county.",
9d72cede 3809 array(1 => $urltxt)
6a488035
TO
3810 );
3811 }
3812 $entryFound = TRUE;
3813 }
3814 // handle state province
3815 if (array_key_exists('civicrm_address_state_province_id', $row)) {
3816 if ($value = $row['civicrm_address_state_province_id']) {
3817 $rows[$rowNum]['civicrm_address_state_province_id'] = CRM_Core_PseudoConstant::stateProvince($value, FALSE);
3818
3819 $url = CRM_Report_Utils_Report::getNextUrl($baseUrl,
9d72cede
EM
3820 "reset=1&force=1&{$criteriaQueryParams}&state_province_id_op=in&state_province_id_value={$value}",
3821 $this->_absoluteUrl, $this->_id
6a488035
TO
3822 );
3823 $rows[$rowNum]['civicrm_address_state_province_id_link'] = $url;
3824 $rows[$rowNum]['civicrm_address_state_province_id_hover'] = ts("%1 for this state.",
9d72cede 3825 array(1 => $urltxt)
6a488035
TO
3826 );
3827 }
3828 $entryFound = TRUE;
3829 }
3830
3831 return $entryFound;
3832 }
3833
3834 /*
3835 * Adjusts dates passed in to YEAR() for fiscal year.
3836 */
74cf4551 3837 /**
100fef9d 3838 * @param string $fieldName
74cf4551
EM
3839 *
3840 * @return string
3841 */
00be9182 3842 public function fiscalYearOffset($fieldName) {
6a488035
TO
3843 $config = CRM_Core_Config::singleton();
3844 $fy = $config->fiscalYearStart;
9d72cede
EM
3845 if (CRM_Utils_Array::value('yid_op', $this->_params) == 'calendar' ||
3846 ($fy['d'] == 1 && $fy['M'] == 1)
3847 ) {
6a488035
TO
3848 return "YEAR( $fieldName )";
3849 }
9d72cede
EM
3850 return "YEAR( $fieldName - INTERVAL " . ($fy['M'] - 1) . " MONTH" .
3851 ($fy['d'] > 1 ? (" - INTERVAL " . ($fy['d'] - 1) . " DAY") : '') . " )";
6a488035
TO
3852 }
3853
3854 /*
3855 * Add Address into From Table if required
3856 */
00be9182 3857 public function addAddressFromClause() {
6a488035
TO
3858 // include address field if address column is to be included
3859 if ((isset($this->_addressField) &&
3860 $this->_addressField
3861 ) ||
3862 $this->isTableSelected('civicrm_address')
3863 ) {
3864 $this->_from .= "
3865 LEFT JOIN civicrm_address {$this->_aliases['civicrm_address']}
3866 ON ({$this->_aliases['civicrm_contact']}.id =
3867 {$this->_aliases['civicrm_address']}.contact_id) AND
3868 {$this->_aliases['civicrm_address']}.is_primary = 1\n";
3869 }
3870 }
3871
850e4640
E
3872 /**
3873 * Add Phone into From Table if required
3874 */
00be9182 3875 public function addPhoneFromClause() {
850e4640
E
3876 // include address field if address column is to be included
3877 if ($this->isTableSelected('civicrm_phone')
3878 ) {
3879 $this->_from .= "
3880 LEFT JOIN civicrm_phone {$this->_aliases['civicrm_phone']}
3881 ON ({$this->_aliases['civicrm_contact']}.id =
3882 {$this->_aliases['civicrm_phone']}.contact_id) AND
3883 {$this->_aliases['civicrm_phone']}.is_primary = 1\n";
3884 }
3885 }
3886
3887 /**
3888 * Get phone columns to add to array
9d72cede 3889 *
850e4640 3890 * @param array $options
16b10e64
CW
3891 * - prefix Prefix to add to table (in case of more than one instance of the table)
3892 * - prefix_label Label to give columns from this phone table instance
9d72cede 3893 *
a6c01b45
CW
3894 * @return array
3895 * phone columns definition
850e4640 3896 */
00be9182 3897 public function getPhoneColumns($options = array()) {
850e4640
E
3898 $defaultOptions = array(
3899 'prefix' => '',
3900 'prefix_label' => '',
3901 );
3902
9d72cede 3903 $options = array_merge($defaultOptions, $options);
850e4640
E
3904
3905 $fields = array(
3906 $options['prefix'] . 'civicrm_phone' => array(
9d72cede 3907 'dao' => 'CRM_Core_DAO_Phone',
850e4640
E
3908 'fields' => array(
3909 $options['prefix'] . 'phone' => array(
3910 'title' => ts($options['prefix_label'] . 'Phone'),
21dfd5f5 3911 'name' => 'phone',
850e4640
E
3912 ),
3913 ),
3914 ),
3915 );
3916 return $fields;
3917 }
3918
3919 /**
3920 * Get address columns to add to array
9d72cede 3921 *
850e4640 3922 * @param array $options
16b10e64
CW
3923 * - prefix Prefix to add to table (in case of more than one instance of the table)
3924 * - prefix_label Label to give columns from this address table instance
9d72cede 3925 *
a6c01b45
CW
3926 * @return array
3927 * address columns definition
850e4640 3928 */
00be9182 3929 public function getAddressColumns($options = array()) {
c927c151 3930 $options += array(
850e4640
E
3931 'prefix' => '',
3932 'prefix_label' => '',
3933 'group_by' => TRUE,
3934 'order_by' => TRUE,
3935 'filters' => TRUE,
c927c151 3936 'defaults' => array(),
850e4640 3937 );
850e4640
E
3938 return $this->addAddressFields(
3939 $options['group_by'],
3940 $options['order_by'],
3941 $options['filters'],
3942 $options['defaults']
3943 );
850e4640
E
3944 }
3945
74cf4551 3946 /**
100fef9d 3947 * @param int $groupID
74cf4551 3948 */
00be9182 3949 public function add2group($groupID) {
6a488035
TO
3950 if (is_numeric($groupID) && isset($this->_aliases['civicrm_contact'])) {
3951 $select = "SELECT DISTINCT {$this->_aliases['civicrm_contact']}.id AS addtogroup_contact_id, ";
3952 $select = str_ireplace('SELECT SQL_CALC_FOUND_ROWS ', $select, $this->_select);
3953
3954 $sql = "{$select} {$this->_from} {$this->_where} {$this->_groupBy} {$this->_having} {$this->_orderBy}";
3955 $dao = CRM_Core_DAO::executeQuery($sql);
3956
3957 $contact_ids = array();
3958 // Add resulting contacts to group
3959 while ($dao->fetch()) {
3960 if ($dao->addtogroup_contact_id) {
3961 $contact_ids[$dao->addtogroup_contact_id] = $dao->addtogroup_contact_id;
3962 }
3963 }
3964
9d72cede 3965 if (!empty($contact_ids)) {
6a488035
TO
3966 CRM_Contact_BAO_GroupContact::addContactsToGroup($contact_ids, $groupID);
3967 CRM_Core_Session::setStatus(ts("Listed contact(s) have been added to the selected group."), ts('Contacts Added'), 'success');
3968 }
3969 else {
3970 CRM_Core_Session::setStatus(ts("The listed records(s) cannot be added to the group."));
3971 }
3972 }
3973 }
46065582
PJ
3974
3975 /* function used for showing charts on print screen */
00be9182 3976 public static function uploadChartImage() {
46065582 3977 // upload strictly for '.png' images
d04d4eef
PJ
3978 $name = trim(basename(CRM_Utils_Request::retrieve('name', 'String', CRM_Core_DAO::$_nullObject, FALSE, NULL, 'GET')));
3979 if (preg_match('/\.png$/', $name)) {
46065582
PJ
3980 //
3981 // POST data is usually string data, but we are passing a RAW .png
3982 // so PHP is a bit confused and $_POST is empty. But it has saved
3983 // the raw bits into $HTTP_RAW_POST_DATA
3984 //
3985 $httpRawPostData = $GLOBALS['HTTP_RAW_POST_DATA'];
3986
3987 // prepare the directory
3988 $config = CRM_Core_Config::singleton();
9d72cede
EM
3989 $defaultPath =
3990 str_replace('/persist/contribute/', '/persist/', $config->imageUploadDir) .
3991 '/openFlashChart/';
46065582
PJ
3992 if (!file_exists($defaultPath)) {
3993 mkdir($defaultPath, 0777, TRUE);
3994 }
3995
3996 // full path to the saved image including filename
d04d4eef 3997 $destination = $defaultPath . $name;
46065582
PJ
3998
3999 //write and save
4000 $jfh = fopen($destination, 'w') or die("can't open file");
4001 fwrite($jfh, $httpRawPostData);
4002 fclose($jfh);
4003 CRM_Utils_System::civiExit();
4004 }
4005 }
2107cde9
CW
4006
4007 /**
4008 * Apply common settings to entityRef fields
9d72cede 4009 *
2107cde9
CW
4010 * @param array $field
4011 * @param string $table
4012 */
4013 private function setEntityRefDefaults(&$field, $table) {
4014 $field['attributes'] = $field['attributes'] ? $field['attributes'] : array();
4015 $field['attributes'] += array(
4016 'entity' => CRM_Core_DAO_AllCoreTables::getBriefName(CRM_Core_DAO_AllCoreTables::getClassForTable($table)),
4017 'multiple' => TRUE,
4018 'placeholder' => ts('- select -'),
4019 );
4020 }
232624b1 4021}