3 +--------------------------------------------------------------------+
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2018 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
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. |
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. |
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 |
25 +--------------------------------------------------------------------+
31 * @copyright CiviCRM LLC (c) 2004-2018
33 class CRM_Report_Form_Activity
extends CRM_Report_Form
{
34 protected $_selectAliasesTotal = array();
36 protected $_customGroupExtends = array(
40 protected $_nonDisplayFields = array();
43 * This report has not been optimised for group filtering.
45 * The functionality for group filtering has been improved but not
46 * all reports have been adjusted to take care of it. This report has not
47 * and will run an inefficient query until fixed.
53 protected $groupFilterNotOptimised = TRUE;
58 public function __construct() {
59 // There could be multiple contacts. We not clear on which contact id to display.
60 // Lets hide it for now.
61 $this->_exposeContactID
= FALSE;
62 // if navigated from count link of activity summary reports.
63 $this->_resetDateFilter
= CRM_Utils_Request
::retrieve('resetDateFilter', 'Boolean');
65 $config = CRM_Core_Config
::singleton();
66 $campaignEnabled = in_array("CiviCampaign", $config->enableComponents
);
67 $caseEnabled = in_array("CiviCase", $config->enableComponents
);
68 if ($campaignEnabled) {
69 $getCampaigns = CRM_Campaign_BAO_Campaign
::getPermissionedCampaigns(NULL, NULL, TRUE, FALSE, TRUE);
70 $this->activeCampaigns
= $getCampaigns['campaigns'];
71 asort($this->activeCampaigns
);
72 $this->engagementLevels
= CRM_Campaign_PseudoConstant
::engagementLevel();
75 $components = CRM_Core_Component
::getEnabledComponents();
76 foreach ($components as $componentName => $componentInfo) {
77 // CRM-19201: Add support for reporting CiviCampaign activities
78 // For CiviCase, "access all cases and activities" is required here
79 // rather than "access my cases and activities" to prevent those with
80 // only the later permission from seeing a list of all cases which might
81 // present a privacy issue.
82 if (CRM_Core_Permission
::access($componentName, TRUE, TRUE)) {
83 $accessAllowed[] = $componentInfo->componentID
;
88 if (!empty($accessAllowed)) {
89 $include = 'OR v.component_id IN (' . implode(', ', $accessAllowed) . ')';
91 $condition = " AND ( v.component_id IS NULL {$include} )";
92 $this->activityTypes
= CRM_Core_OptionGroup
::values('activity_type', FALSE, FALSE, FALSE, $condition);
93 asort($this->activityTypes
);
95 // @todo split the 3 different contact tables into their own array items.
96 // this will massively simplify the needs of this report.
97 $this->_columns
= array(
98 'civicrm_contact' => array(
99 'dao' => 'CRM_Contact_DAO_Contact',
101 'contact_source' => array(
102 'name' => 'sort_name',
103 'title' => ts('Source Name'),
104 'alias' => 'civicrm_contact_source',
107 'contact_assignee' => array(
108 'name' => 'sort_name',
109 'title' => ts('Assignee Name'),
110 'alias' => 'civicrm_contact_assignee',
111 'dbAlias' => "civicrm_contact_assignee.sort_name",
114 'contact_target' => array(
115 'name' => 'sort_name',
116 'title' => ts('Target Name'),
117 'alias' => 'civicrm_contact_target',
118 'dbAlias' => "civicrm_contact_target.sort_name",
121 'contact_source_id' => array(
123 'alias' => 'civicrm_contact_source',
124 'dbAlias' => "civicrm_contact_source.id",
125 'no_display' => TRUE,
129 'contact_assignee_id' => array(
131 'alias' => 'civicrm_contact_assignee',
132 'dbAlias' => "civicrm_contact_assignee.id",
133 'no_display' => TRUE,
137 'contact_target_id' => array(
139 'alias' => 'civicrm_contact_target',
140 'dbAlias' => "civicrm_contact_target.id",
141 'no_display' => TRUE,
147 'contact_source' => array(
148 'name' => 'sort_name',
149 'alias' => 'civicrm_contact_source',
150 'title' => ts('Source Name'),
151 'operator' => 'like',
152 'type' => CRM_Report_Form
::OP_STRING
,
154 'contact_assignee' => array(
155 'name' => 'sort_name',
156 'alias' => 'civicrm_contact_assignee',
157 'title' => ts('Assignee Name'),
158 'operator' => 'like',
159 'type' => CRM_Report_Form
::OP_STRING
,
161 'contact_target' => array(
162 'name' => 'sort_name',
163 'alias' => 'civicrm_contact_target',
164 'title' => ts('Target Name'),
165 'operator' => 'like',
166 'type' => CRM_Report_Form
::OP_STRING
,
168 'current_user' => array(
169 'name' => 'current_user',
170 'title' => ts('Limit To Current User'),
171 'type' => CRM_Utils_Type
::T_INT
,
172 'operatorType' => CRM_Report_Form
::OP_SELECT
,
173 'options' => array('0' => ts('No'), '1' => ts('Yes')),
176 'grouping' => 'contact-fields',
178 'civicrm_email' => array(
179 'dao' => 'CRM_Core_DAO_Email',
181 'contact_source_email' => array(
183 'title' => ts('Source Email'),
184 'alias' => 'civicrm_email_source',
186 'contact_assignee_email' => array(
188 'title' => ts('Assignee Email'),
189 'alias' => 'civicrm_email_assignee',
191 'contact_target_email' => array(
193 'title' => ts('Target Email'),
194 'alias' => 'civicrm_email_target',
197 'order_bys' => array(
198 'source_contact_email' => array(
200 'title' => ts('Source Email'),
201 'dbAlias' => 'civicrm_email_contact_source_email',
205 'civicrm_phone' => array(
206 'dao' => 'CRM_Core_DAO_Phone',
208 'contact_source_phone' => array(
210 'title' => ts('Source Phone'),
211 'alias' => 'civicrm_phone_source',
213 'contact_assignee_phone' => array(
215 'title' => ts('Assignee Phone'),
216 'alias' => 'civicrm_phone_assignee',
218 'contact_target_phone' => array(
220 'title' => ts('Target Phone'),
221 'alias' => 'civicrm_phone_target',
225 'civicrm_activity' => array(
226 'dao' => 'CRM_Activity_DAO_Activity',
229 'no_display' => TRUE,
230 'title' => ts('Activity ID'),
233 'source_record_id' => array(
234 'no_display' => TRUE,
237 'activity_type_id' => array(
238 'title' => ts('Activity Type'),
240 'type' => CRM_Utils_Type
::T_STRING
,
242 'activity_subject' => array(
243 'title' => ts('Subject'),
246 'activity_date_time' => array(
247 'title' => ts('Activity Date'),
250 'status_id' => array(
251 'title' => ts('Activity Status'),
253 'type' => CRM_Utils_Type
::T_STRING
,
256 'title' => ts('Duration'),
257 'type' => CRM_Utils_Type
::T_INT
,
260 'title' => ts('Location'),
261 'type' => CRM_Utils_Type
::T_STRING
,
264 'title' => ts('Activity Details'),
266 'priority_id' => array(
267 'title' => ts('Priority'),
269 'type' => CRM_Utils_Type
::T_STRING
,
273 'activity_date_time' => array(
274 'default' => 'this.month',
275 'operatorType' => CRM_Report_Form
::OP_DATE
,
277 'activity_subject' => array('title' => ts('Activity Subject')),
278 'activity_type_id' => array(
279 'title' => ts('Activity Type'),
280 'operatorType' => CRM_Report_Form
::OP_MULTISELECT
,
281 'options' => $this->activityTypes
,
283 'status_id' => array(
284 'title' => ts('Activity Status'),
285 'type' => CRM_Utils_Type
::T_STRING
,
286 'operatorType' => CRM_Report_Form
::OP_MULTISELECT
,
287 'options' => CRM_Core_PseudoConstant
::activityStatus(),
290 'title' => ts('Location'),
291 'type' => CRM_Utils_Type
::T_TEXT
,
294 'title' => ts('Activity Details'),
295 'type' => CRM_Utils_Type
::T_TEXT
,
297 'priority_id' => array(
298 'title' => ts('Activity Priority'),
299 'type' => CRM_Utils_Type
::T_STRING
,
300 'operatorType' => CRM_Report_Form
::OP_MULTISELECT
,
301 'options' => CRM_Core_PseudoConstant
::get('CRM_Activity_DAO_Activity', 'priority_id'),
304 'order_bys' => array(
305 'activity_date_time' => array(
306 'title' => ts('Activity Date'),
307 'default_weight' => '1',
308 'dbAlias' => 'civicrm_activity_activity_date_time',
310 'activity_type_id' => array(
311 'title' => ts('Activity Type'),
312 'default_weight' => '2',
313 'dbAlias' => 'field(civicrm_activity_activity_type_id, ' . implode(', ', array_keys($this->activityTypes
)) . ')',
316 'grouping' => 'activity-fields',
317 'alias' => 'activity',
319 // Hack to get $this->_alias populated for the table.
320 'civicrm_activity_contact' => array(
321 'dao' => 'CRM_Activity_DAO_ActivityContact',
324 ) +
$this->addressFields(TRUE);
326 if ($caseEnabled && CRM_Core_Permission
::check('access all cases and activities')) {
327 $this->_columns
['civicrm_activity']['filters']['include_case_activities'] = array(
328 'name' => 'include_case_activities',
329 'title' => ts('Include Case Activities'),
330 'type' => CRM_Utils_Type
::T_INT
,
331 'operatorType' => CRM_Report_Form
::OP_SELECT
,
332 'options' => array('0' => ts('No'), '1' => ts('Yes')),
336 if ($campaignEnabled) {
337 // Add display column and filter for Survey Results, Campaign and Engagement Index if CiviCampaign is enabled
339 $this->_columns
['civicrm_activity']['fields']['result'] = array(
340 'title' => ts('Survey Result'),
341 'default' => 'false',
343 $this->_columns
['civicrm_activity']['filters']['result'] = array(
344 'title' => ts('Survey Result'),
345 'operator' => 'like',
346 'type' => CRM_Utils_Type
::T_STRING
,
348 if (!empty($this->activeCampaigns
)) {
349 $this->_columns
['civicrm_activity']['fields']['campaign_id'] = array(
350 'title' => ts('Campaign'),
351 'default' => 'false',
353 $this->_columns
['civicrm_activity']['filters']['campaign_id'] = array(
354 'title' => ts('Campaign'),
355 'type' => CRM_Utils_Type
::T_INT
,
356 'operatorType' => CRM_Report_Form
::OP_MULTISELECT
,
357 'options' => $this->activeCampaigns
,
360 if (!empty($this->engagementLevels
)) {
361 $this->_columns
['civicrm_activity']['fields']['engagement_level'] = array(
362 'title' => ts('Engagement Index'),
363 'default' => 'false',
365 $this->_columns
['civicrm_activity']['filters']['engagement_level'] = array(
366 'title' => ts('Engagement Index'),
367 'type' => CRM_Utils_Type
::T_INT
,
368 'operatorType' => CRM_Report_Form
::OP_MULTISELECT
,
369 'options' => $this->engagementLevels
,
373 $this->_groupFilter
= TRUE;
374 $this->_tagFilter
= TRUE;
375 $this->_tagFilterTable
= 'civicrm_activity';
376 parent
::__construct();
380 * Adding address fields with dbAlias for order clause.
382 * @param bool $orderBy
387 public function addressFields($orderBy = FALSE) {
388 $address = parent
::addAddressFields(FALSE, TRUE);
390 foreach ($address['civicrm_address']['order_bys'] as $fieldName => $field) {
391 $address['civicrm_address']['order_bys'][$fieldName]['dbAlias'] = "civicrm_address_{$fieldName}";
398 * Build select clause.
400 * @todo get rid of $recordType param. It's only because 3 separate contact tables
401 * are mis-declared as one that we need it.
403 * @param null $recordType deprecated
404 * Parameter to hack around the bad decision made in construct to misrepresent
405 * different tables as the same table.
407 public function select($recordType = 'target') {
408 if (!array_key_exists("contact_{$recordType}", $this->_params
['fields']) &&
409 $recordType != 'final'
411 $this->_nonDisplayFields
[] = "civicrm_contact_contact_{$recordType}";
415 if ($recordType == 'final' && !empty($this->_nonDisplayFields
)) {
416 foreach ($this->_nonDisplayFields
as $fieldName) {
417 unset($this->_columnHeaders
[$fieldName]);
421 if (empty($this->_selectAliasesTotal
)) {
422 $this->_selectAliasesTotal
= $this->_selectAliases
;
425 $removeKeys = array();
426 if ($recordType == 'target') {
427 // @todo - fix up the way the tables are declared in construct & remove this.
428 foreach ($this->_selectClauses
as $key => $clause) {
429 if (strstr($clause, 'civicrm_contact_assignee.') ||
430 strstr($clause, 'civicrm_contact_source.') ||
431 strstr($clause, 'civicrm_email_assignee.') ||
432 strstr($clause, 'civicrm_email_source.') ||
433 strstr($clause, 'civicrm_phone_assignee.') ||
434 strstr($clause, 'civicrm_phone_source.')
436 $removeKeys[] = $key;
437 unset($this->_selectClauses
[$key]);
441 elseif ($recordType == 'assignee') {
442 // @todo - fix up the way the tables are declared in construct & remove this.
443 foreach ($this->_selectClauses
as $key => $clause) {
444 if (strstr($clause, 'civicrm_contact_target.') ||
445 strstr($clause, 'civicrm_contact_source.') ||
446 strstr($clause, 'civicrm_email_target.') ||
447 strstr($clause, 'civicrm_email_source.') ||
448 strstr($clause, 'civicrm_phone_target.') ||
449 strstr($clause, 'civicrm_phone_source.') ||
450 strstr($clause, 'civicrm_address_')
452 $removeKeys[] = $key;
453 unset($this->_selectClauses
[$key]);
457 elseif ($recordType == 'source') {
458 // @todo - fix up the way the tables are declared in construct & remove this.
459 foreach ($this->_selectClauses
as $key => $clause) {
460 if (strstr($clause, 'civicrm_contact_target.') ||
461 strstr($clause, 'civicrm_contact_assignee.') ||
462 strstr($clause, 'civicrm_email_target.') ||
463 strstr($clause, 'civicrm_email_assignee.') ||
464 strstr($clause, 'civicrm_phone_target.') ||
465 strstr($clause, 'civicrm_phone_assignee.') ||
466 strstr($clause, 'civicrm_address_')
468 $removeKeys[] = $key;
469 unset($this->_selectClauses
[$key]);
473 elseif ($recordType == 'final') {
474 $this->_selectClauses
= $this->_selectAliasesTotal
;
475 foreach ($this->_selectClauses
as $key => $clause) {
476 // @todo - fix up the way the tables are declared in construct & remove this.
477 if (strstr($clause, 'civicrm_contact_contact_target') ||
478 strstr($clause, 'civicrm_contact_contact_assignee') ||
479 strstr($clause, 'civicrm_contact_contact_source') ||
480 strstr($clause, 'civicrm_phone_contact_source_phone') ||
481 strstr($clause, 'civicrm_phone_contact_assignee_phone') ||
482 strstr($clause, 'civicrm_email_contact_source_email') ||
483 strstr($clause, 'civicrm_email_contact_assignee_email') ||
484 strstr($clause, 'civicrm_email_contact_target_email') ||
485 strstr($clause, 'civicrm_phone_contact_target_phone') ||
486 strstr($clause, 'civicrm_address_')
488 $this->_selectClauses
[$key] = "GROUP_CONCAT(DISTINCT $clause SEPARATOR ';') as $clause";
494 foreach ($removeKeys as $key) {
495 unset($this->_selectAliases
[$key]);
498 if ($recordType == 'target') {
499 foreach ($this->_columns
['civicrm_address']['order_bys'] as $fieldName => $field) {
500 $orderByFld = $this->_columns
['civicrm_address']['order_bys'][$fieldName];
501 $fldInfo = $this->_columns
['civicrm_address']['fields'][$fieldName];
502 $this->_selectAliases
[] = $orderByFld['dbAlias'];
503 $this->_selectClauses
[] = "{$fldInfo['dbAlias']} as {$orderByFld['dbAlias']}";
505 $this->_selectAliases
= array_unique($this->_selectAliases
);
506 $this->_selectClauses
= array_unique($this->_selectClauses
);
508 $this->_select
= "SELECT " . implode(', ', $this->_selectClauses
) . " ";
514 * @todo remove this function & declare the 3 contact tables separately
516 public function from() {
517 $activityContacts = CRM_Activity_BAO_ActivityContact
::buildOptions('record_type_id', 'validate');
518 $targetID = CRM_Utils_Array
::key('Activity Targets', $activityContacts);
521 FROM civicrm_activity {$this->_aliases['civicrm_activity']}
522 INNER JOIN civicrm_activity_contact {$this->_aliases['civicrm_activity_contact']}
523 ON {$this->_aliases['civicrm_activity']}.id = {$this->_aliases['civicrm_activity_contact']}.activity_id AND
524 {$this->_aliases['civicrm_activity_contact']}.record_type_id = {$targetID}
525 INNER JOIN civicrm_contact civicrm_contact_target
526 ON {$this->_aliases['civicrm_activity_contact']}.contact_id = civicrm_contact_target.id
529 if ($this->isTableSelected('civicrm_email')) {
531 LEFT JOIN civicrm_email civicrm_email_target
532 ON {$this->_aliases['civicrm_activity_contact']}.contact_id = civicrm_email_target.contact_id AND
533 civicrm_email_target.is_primary = 1";
536 if ($this->isTableSelected('civicrm_phone')) {
538 LEFT JOIN civicrm_phone civicrm_phone_target
539 ON {$this->_aliases['civicrm_activity_contact']}.contact_id = civicrm_phone_target.contact_id AND
540 civicrm_phone_target.is_primary = 1 ";
542 $this->_aliases
['civicrm_contact'] = 'civicrm_contact_target';
544 $this->joinAddressFromContact();
548 * Build where clause.
550 * @todo get rid of $recordType param. It's only because 3 separate contact tables
551 * are mis-declared as one that we need it.
553 * @param string $recordType
555 public function where($recordType = NULL) {
556 $this->_where
= " WHERE {$this->_aliases['civicrm_activity']}.is_test = 0 AND
557 {$this->_aliases['civicrm_activity']}.is_deleted = 0 AND
558 {$this->_aliases['civicrm_activity']}.is_current_revision = 1";
561 foreach ($this->_columns
as $tableName => $table) {
562 if (array_key_exists('filters', $table)) {
564 foreach ($table['filters'] as $fieldName => $field) {
566 if ($fieldName != 'contact_' . $recordType &&
567 (strstr($fieldName, '_target') ||
568 strstr($fieldName, '_assignee') ||
569 strstr($fieldName, '_source')
574 if (CRM_Utils_Array
::value('type', $field) & CRM_Utils_Type
::T_DATE
) {
575 $relative = CRM_Utils_Array
::value("{$fieldName}_relative", $this->_params
);
576 $from = CRM_Utils_Array
::value("{$fieldName}_from", $this->_params
);
577 $to = CRM_Utils_Array
::value("{$fieldName}_to", $this->_params
);
579 $clause = $this->dateClause($field['name'], $relative, $from, $to, $field['type']);
582 $op = CRM_Utils_Array
::value("{$fieldName}_op", $this->_params
);
583 if ($op && ($op != 'nnll' && $op != 'nll')) {
584 $clause = $this->whereClause($field,
586 CRM_Utils_Array
::value("{$fieldName}_value", $this->_params
),
587 CRM_Utils_Array
::value("{$fieldName}_min", $this->_params
),
588 CRM_Utils_Array
::value("{$fieldName}_max", $this->_params
)
590 if ($field['name'] == 'include_case_activities') {
593 if ($fieldName == 'activity_type_id' &&
594 empty($this->_params
['activity_type_id_value'])
596 if (empty($this->_params
['include_case_activities_value'])) {
597 $this->activityTypes
= CRM_Core_PseudoConstant
::activityType(TRUE, FALSE, FALSE, 'label', TRUE);
599 $actTypes = array_flip($this->activityTypes
);
600 $clause = "( {$this->_aliases['civicrm_activity']}.activity_type_id IN (" .
601 implode(',', $actTypes) . ") )";
606 if ($field['name'] == 'current_user') {
607 if (CRM_Utils_Array
::value("{$fieldName}_value", $this->_params
) ==
611 $session = CRM_Core_Session
::singleton();
612 if ($contactID = $session->get('userID')) {
613 $clause = "{$this->_aliases['civicrm_activity_contact']}.activity_id IN
614 (SELECT activity_id FROM civicrm_activity_contact WHERE contact_id = {$contactID})";
624 if (!empty($clause)) {
625 $clauses[] = $clause;
631 if (empty($clauses)) {
632 $this->_where
.= " ";
635 $this->_where
.= " AND " . implode(' AND ', $clauses);
638 if ($this->_aclWhere
) {
639 $this->_where
.= " AND {$this->_aclWhere} ";
644 * Override group by function.
646 public function groupBy() {
647 $this->_groupBy
= CRM_Contact_BAO_Query
::getGroupByFromSelectColumns($this->_selectClauses
, "{$this->_aliases['civicrm_activity']}.id");
653 * @param string $tableAlias
655 public function buildACLClause($tableAlias = 'contact_a') {
656 //override for ACL( Since Contact may be source
657 //contact/assignee or target also it may be null )
659 if (CRM_Core_Permission
::check('view all contacts')) {
660 $this->_aclFrom
= $this->_aclWhere
= NULL;
664 $session = CRM_Core_Session
::singleton();
665 $contactID = $session->get('userID');
669 $contactID = CRM_Utils_Type
::escape($contactID, 'Integer');
671 CRM_Contact_BAO_Contact_Permission
::cache($contactID);
673 foreach ($tableAlias as $k => $alias) {
674 $clauses[] = " INNER JOIN civicrm_acl_contact_cache aclContactCache_{$k} ON ( {$alias}.id = aclContactCache_{$k}.contact_id OR {$alias}.id IS NULL ) AND aclContactCache_{$k}.user_id = $contactID ";
677 $this->_aclFrom
= implode(" ", $clauses);
678 $this->_aclWhere
= NULL;
682 * @param int $groupID
686 public function add2group($groupID) {
687 if (CRM_Utils_Array
::value("contact_target_op", $this->_params
) == 'nll') {
688 CRM_Core_Error
::fatal(ts('Current filter criteria didn\'t have any target contact to add to group'));
691 $new_select = 'AS addtogroup_contact_id';
692 $select = str_ireplace('AS civicrm_contact_contact_target_id', $new_select, $this->_select
);
693 $new_having = ' addtogroup_contact_id';
694 $having = str_ireplace(' civicrm_contact_contact_target_id', $new_having, $this->_having
);
696 FROM {$this->temporaryTables['activity_temp_table']} tar
697 GROUP BY civicrm_activity_id $having {$this->_orderBy}";
698 $select = 'AS addtogroup_contact_id';
699 $query = str_ireplace('AS civicrm_contact_contact_target_id', $select, $query);
700 $dao = $this->executeReportQuery($query);
702 $contactIDs = array();
703 // Add resulting contacts to group
704 while ($dao->fetch()) {
705 if ($dao->addtogroup_contact_id
) {
706 $contact_id = explode(';', $dao->addtogroup_contact_id
);
707 if ($contact_id[0]) {
708 $contactIDs[$contact_id[0]] = $contact_id[0];
713 if (!empty($contactIDs)) {
714 CRM_Contact_BAO_GroupContact
::addContactsToGroup($contactIDs, $groupID);
715 CRM_Core_Session
::setStatus(ts("Listed contact(s) have been added to the selected group."), ts('Contacts Added'), 'success');
718 CRM_Core_Session
::setStatus(ts("The listed records(s) cannot be added to the group."));
729 public static function formRule($fields, $files, $self) {
731 $config = CRM_Core_Config
::singleton();
732 if (in_array("CiviCase", $config->enableComponents
)) {
733 $componentId = CRM_Core_Component
::getComponentID('CiviCase');
734 $caseActivityTypes = CRM_Core_OptionGroup
::values('activity_type', TRUE, FALSE, FALSE, " AND v.component_id={$componentId}");
735 if (!empty($fields['activity_type_id_value']) && is_array($fields['activity_type_id_value']) && empty($fields['include_case_activities_value'])) {
736 foreach ($fields['activity_type_id_value'] as $activityTypeId) {
737 if (in_array($activityTypeId, $caseActivityTypes)) {
738 $errors['fields'] = ts("Please enable 'Include Case Activities' to filter with Case Activity types.");
751 public function buildQuery($applyLimit = TRUE) {
752 $activityContacts = CRM_Activity_BAO_ActivityContact
::buildOptions('record_type_id', 'validate');
753 $sourceID = CRM_Utils_Array
::key('Activity Source', $activityContacts);
755 //Assign those recordtype to array which have filter operator as 'Is not empty' or 'Is empty'
756 $nullFilters = array();
757 foreach (array('target', 'source', 'assignee') as $type) {
758 if (CRM_Utils_Array
::value("contact_{$type}_op", $this->_params
) ==
759 'nnll' ||
!empty($this->_params
["contact_{$type}_value"])
761 $nullFilters[] = " civicrm_contact_contact_{$type}_id IS NOT NULL ";
763 elseif (CRM_Utils_Array
::value("contact_{$type}_op", $this->_params
) ==
766 $nullFilters[] = " civicrm_contact_contact_{$type}_id IS NULL ";
770 // @todo - all this temp table stuff is here because pre 4.4 the activity contact
771 // form did not exist.
772 // Fixing the way the construct method declares them will make all this redundant.
773 // 1. fill temp table with target results
774 $this->buildACLClause(array('civicrm_contact_target'));
775 $this->select('target');
777 $this->customDataFrom();
778 $this->where('target');
779 $tempTableName = $this->createTemporaryTable('activity_temp_table', "{$this->_select} {$this->_from} {$this->_where}");
781 // 2. add new columns to hold assignee and source results
782 // fixme: add when required
784 ALTER TABLE $tempTableName
785 MODIFY COLUMN civicrm_contact_contact_target_id VARCHAR(128),
786 ADD COLUMN civicrm_contact_contact_assignee VARCHAR(128),
787 ADD COLUMN civicrm_contact_contact_source VARCHAR(128),
788 ADD COLUMN civicrm_contact_contact_assignee_id VARCHAR(128),
789 ADD COLUMN civicrm_contact_contact_source_id VARCHAR(128),
790 ADD COLUMN civicrm_phone_contact_assignee_phone VARCHAR(128),
791 ADD COLUMN civicrm_phone_contact_source_phone VARCHAR(128),
792 ADD COLUMN civicrm_email_contact_assignee_email VARCHAR(128),
793 ADD COLUMN civicrm_email_contact_source_email VARCHAR(128)";
794 $this->executeReportQuery($tempQuery);
796 // 3. fill temp table with assignee results
797 $this->buildACLClause(array('civicrm_contact_assignee'));
798 $this->select('assignee');
799 $this->buildAssigneeFrom();
801 $this->customDataFrom();
802 $this->where('assignee');
803 $insertCols = implode(',', $this->_selectAliases
);
804 $tempQuery = "INSERT INTO $tempTableName ({$insertCols})
806 {$this->_from} {$this->_where}";
807 $this->executeReportQuery($tempQuery);
809 // 4. fill temp table with source results
810 $this->buildACLClause(array('civicrm_contact_source'));
811 $this->select('source');
812 $this->buildSourceFrom();
813 $this->customDataFrom();
814 $this->where('source');
815 $insertCols = implode(',', $this->_selectAliases
);
816 $tempQuery = "INSERT INTO $tempTableName ({$insertCols})
818 {$this->_from} {$this->_where}";
819 $this->executeReportQuery($tempQuery);
821 // 5. show final result set from temp table
823 $this->select('final');
825 if (!empty($nullFilters)) {
826 $this->_having
= "HAVING " . implode(' AND ', $nullFilters);
829 foreach ($this->_sections
as $alias => $section) {
830 if (!empty($section) && $section['name'] == 'activity_date_time') {
831 $this->alterSectionHeaderForDateTime($tempTableName, $section['tplField']);
839 $groupByFromSelect = CRM_Contact_BAO_Query
::getGroupByFromSelectColumns($this->_selectClauses
, 'civicrm_activity_id');
841 $this->_where
= " WHERE (1)";
842 $this->buildPermissionClause();
843 if ($this->_aclWhere
) {
844 $this->_where
.= " AND {$this->_aclWhere} ";
847 $sql = "{$this->_select}
848 FROM $tempTableName tar
849 INNER JOIN civicrm_activity {$this->_aliases['civicrm_activity']} ON {$this->_aliases['civicrm_activity']}.id = tar.civicrm_activity_id
850 INNER JOIN civicrm_activity_contact {$this->_aliases['civicrm_activity_contact']} ON {$this->_aliases['civicrm_activity_contact']}.activity_id = {$this->_aliases['civicrm_activity']}.id
851 AND {$this->_aliases['civicrm_activity_contact']}.record_type_id = {$sourceID}
852 LEFT JOIN civicrm_contact contact_civireport ON contact_civireport.id = {$this->_aliases['civicrm_activity_contact']}.contact_id
853 {$this->_where} {$groupByFromSelect} {$this->_having} {$this->_orderBy} {$this->_limit}";
855 CRM_Utils_Hook
::alterReportVar('sql', $this, $this);
856 $this->addToDeveloperTab($sql);
861 public function postProcess() {
862 //reset value of activity_date
863 if (!empty($this->_resetDateFilter
)) {
864 $this->_formValues
["activity_date_time_relative"] = NULL;
867 $this->beginPostProcess();
868 $sql = $this->buildQuery(TRUE);
869 $this->buildRows($sql, $rows);
871 // format result set.
872 $this->formatDisplay($rows);
874 // assign variables to templates
875 $this->doTemplateAssignment($rows);
877 // do print / pdf / instance stuff if needed
878 $this->endPostProcess($rows);
882 * Alter display of rows.
884 * Iterate through the rows retrieved via SQL and make changes for display purposes,
885 * such as rendering contacts as links.
888 * Rows generated by SQL, with an array for each row.
890 public function alterDisplay(&$rows) {
892 $activityType = CRM_Core_PseudoConstant
::activityType(TRUE, TRUE, FALSE, 'label', TRUE);
893 $activityStatus = CRM_Core_PseudoConstant
::activityStatus();
894 $priority = CRM_Core_PseudoConstant
::get('CRM_Activity_DAO_Activity', 'priority_id');
896 // Would we ever want to retrieve from the form controller??
897 $form = $this->noController ?
NULL : $this;
898 $context = CRM_Utils_Request
::retrieve('context', 'Alphanumeric', $form, FALSE, 'report');
901 if (CRM_Core_Permission
::check('access CiviCRM')) {
903 $onHover = ts('View Contact Summary for this Contact');
904 $onHoverAct = ts('View Activity Record');
906 foreach ($rows as $rowNum => $row) {
907 // if we have an activity type, format the View Activity link for use in various columns
909 array_key_exists('civicrm_activity_activity_type_id', $row)
911 // Check for target contact id(s) and use the first contact id in that list for view activity link if found,
912 // else use source contact id
913 if (!empty($rows[$rowNum]['civicrm_contact_contact_target_id'])) {
914 $targets = explode(';', $rows[$rowNum]['civicrm_contact_contact_target_id']);
918 $cid = $rows[$rowNum]['civicrm_contact_contact_source_id'];
921 $actActionLinks = CRM_Activity_Selector_Activity
::actionLinks($row['civicrm_activity_activity_type_id'],
922 CRM_Utils_Array
::value('civicrm_activity_source_record_id', $rows[$rowNum]),
924 $rows[$rowNum]['civicrm_activity_id']
927 $actLinkValues = array(
928 'id' => $rows[$rowNum]['civicrm_activity_id'],
932 $actUrl = CRM_Utils_System
::url($actActionLinks[CRM_Core_Action
::VIEW
]['url'],
933 CRM_Core_Action
::replace($actActionLinks[CRM_Core_Action
::VIEW
]['qs'], $actLinkValues), TRUE
937 if (array_key_exists('civicrm_contact_contact_source', $row)) {
938 if ($value = $row['civicrm_contact_contact_source_id']) {
940 $url = CRM_Utils_System
::url("civicrm/contact/view",
941 'reset=1&cid=' . $value,
944 $rows[$rowNum]['civicrm_contact_contact_source_link'] = $url;
945 $rows[$rowNum]['civicrm_contact_contact_source_hover'] = $onHover;
951 if (array_key_exists('civicrm_contact_contact_assignee', $row)) {
952 $assigneeNames = explode(';', $row['civicrm_contact_contact_assignee']);
953 if ($value = $row['civicrm_contact_contact_assignee_id']) {
954 $assigneeContactIds = explode(';', $value);
957 foreach ($assigneeContactIds as $id => $value) {
958 if (isset($value) && isset($assigneeNames[$id])) {
959 $url = CRM_Utils_System
::url("civicrm/contact/view",
960 'reset=1&cid=' . $value,
963 $link[] = "<a title='" . $onHover . "' href='" . $url .
964 "'>{$assigneeNames[$id]}</a>";
967 $rows[$rowNum]['civicrm_contact_contact_assignee'] = implode('; ', $link);
973 if (array_key_exists('civicrm_contact_contact_target', $row)) {
974 $targetNames = explode(';', $row['civicrm_contact_contact_target']);
975 if ($value = $row['civicrm_contact_contact_target_id']) {
976 $targetContactIds = explode(';', $value);
979 foreach ($targetContactIds as $id => $value) {
980 if (isset($value) && isset($targetNames[$id])) {
981 $url = CRM_Utils_System
::url("civicrm/contact/view",
982 'reset=1&cid=' . $value,
985 $link[] = "<a title='" . $onHover . "' href='" . $url .
986 "'>{$targetNames[$id]}</a>";
989 $rows[$rowNum]['civicrm_contact_contact_target'] = implode('; ', $link);
995 if (array_key_exists('civicrm_activity_activity_type_id', $row)) {
996 if ($value = $row['civicrm_activity_activity_type_id']) {
997 $rows[$rowNum]['civicrm_activity_activity_type_id'] = $activityType[$value];
999 $rows[$rowNum]['civicrm_activity_activity_type_id_link'] = $actUrl;
1000 $rows[$rowNum]['civicrm_activity_activity_type_id_hover'] = $onHoverAct;
1006 if (array_key_exists('civicrm_activity_status_id', $row)) {
1007 if ($value = $row['civicrm_activity_status_id']) {
1008 $rows[$rowNum]['civicrm_activity_status_id'] = $activityStatus[$value];
1013 if (array_key_exists('civicrm_activity_priority_id', $row)) {
1014 if ($value = $row['civicrm_activity_priority_id']) {
1015 $rows[$rowNum]['civicrm_activity_priority_id'] = $priority[$value];
1020 if (array_key_exists('civicrm_activity_details', $row) && $this->_outputMode
== 'html') {
1021 if ($value = $row['civicrm_activity_details']) {
1022 $fullDetails = $rows[$rowNum]['civicrm_activity_details'];
1023 $rows[$rowNum]['civicrm_activity_details'] = substr($fullDetails, 0, strrpos(substr($fullDetails, 0, 80), ' '));
1025 $rows[$rowNum]['civicrm_activity_details'] .= " <a href='{$actUrl}' title='{$onHoverAct}'>(more)</a>";
1031 if (array_key_exists('civicrm_activity_campaign_id', $row)) {
1032 if ($value = $row['civicrm_activity_campaign_id']) {
1033 $rows[$rowNum]['civicrm_activity_campaign_id'] = $this->activeCampaigns
[$value];
1038 if (array_key_exists('civicrm_activity_engagement_level', $row)) {
1039 if ($value = $row['civicrm_activity_engagement_level']) {
1040 $rows[$rowNum]['civicrm_activity_engagement_level'] = $this->engagementLevels
[$value];
1045 if (array_key_exists('civicrm_activity_activity_date_time', $row) &&
1046 array_key_exists('civicrm_activity_status_id', $row)
1048 if (CRM_Utils_Date
::overdue($rows[$rowNum]['civicrm_activity_activity_date_time']) &&
1049 $activityStatus[$row['civicrm_activity_status_id']] != 'Completed'
1051 $rows[$rowNum]['class'] = "status-overdue";
1056 $entryFound = $this->alterDisplayAddressFields($row, $rows, $rowNum, 'activity', 'List all activities for this', ';') ?
TRUE : $entryFound;
1064 public function sectionTotals() {
1065 // Reports using order_bys with sections must populate $this->_selectAliases in select() method.
1066 if (empty($this->_selectAliases
)) {
1070 if (!empty($this->_sections
)) {
1071 // pull section aliases out of $this->_sections
1072 $sectionAliases = array_keys($this->_sections
);
1075 foreach (array_merge($sectionAliases, $this->_selectAliases
) as $alias) {
1076 $ifnulls[] = "ifnull($alias, '') as $alias";
1078 $this->_select
= "SELECT " . implode(", ", $ifnulls);
1079 $this->_select
= CRM_Contact_BAO_Query
::appendAnyValueToSelect($ifnulls, $sectionAliases);
1081 $query = $this->_select
.
1082 ", count(DISTINCT civicrm_activity_id) as ct from {$this->temporaryTables['activity_temp_table']} group by " .
1083 implode(", ", $sectionAliases);
1085 // initialize array of total counts
1087 $dao = $this->executeReportQuery($query);
1088 while ($dao->fetch()) {
1089 // let $this->_alterDisplay translate any integer ids to human-readable values.
1090 $rows[0] = $dao->toArray();
1091 $this->alterDisplay($rows);
1094 // add totals for all permutations of section values
1097 $aliasCount = count($sectionAliases);
1098 foreach ($sectionAliases as $alias) {
1099 $values[] = $row[$alias];
1100 $key = implode(CRM_Core_DAO
::VALUE_SEPARATOR
, $values);
1101 if ($i == $aliasCount) {
1102 // the last alias is the lowest-level section header; use count as-is
1103 $totals[$key] = $dao->ct
;
1106 // other aliases are higher level; roll count into their total
1107 $totals[$key] +
= $dao->ct
;
1111 $this->assign('sectionTotals', $totals);
1116 * @todo remove this function & declare the 3 contact tables separately
1118 * (Currently the construct method incorrectly melds them - this is an interim
1119 * refactor in order to get this under ReportTemplateTests)
1121 protected function buildAssigneeFrom() {
1122 $activityContacts = CRM_Activity_BAO_ActivityContact
::buildOptions('record_type_id', 'validate');
1123 $assigneeID = CRM_Utils_Array
::key('Activity Assignees', $activityContacts);
1125 FROM civicrm_activity {$this->_aliases['civicrm_activity']}
1126 INNER JOIN civicrm_activity_contact {$this->_aliases['civicrm_activity_contact']}
1127 ON {$this->_aliases['civicrm_activity']}.id = {$this->_aliases['civicrm_activity_contact']}.activity_id AND
1128 {$this->_aliases['civicrm_activity_contact']}.record_type_id = {$assigneeID}
1129 INNER JOIN civicrm_contact civicrm_contact_assignee
1130 ON {$this->_aliases['civicrm_activity_contact']}.contact_id = civicrm_contact_assignee.id
1133 if ($this->isTableSelected('civicrm_email')) {
1135 LEFT JOIN civicrm_email civicrm_email_assignee
1136 ON {$this->_aliases['civicrm_activity_contact']}.contact_id = civicrm_email_assignee.contact_id AND
1137 civicrm_email_assignee.is_primary = 1";
1139 if ($this->isTableSelected('civicrm_phone')) {
1141 LEFT JOIN civicrm_phone civicrm_phone_assignee
1142 ON {$this->_aliases['civicrm_activity_contact']}.contact_id = civicrm_phone_assignee.contact_id AND
1143 civicrm_phone_assignee.is_primary = 1 ";
1145 $this->_aliases
['civicrm_contact'] = 'civicrm_contact_assignee';
1146 $this->joinAddressFromContact();
1150 * @todo remove this function & declare the 3 contact tables separately
1152 * (Currently the construct method incorrectly melds them - this is an interim
1153 * refactor in order to get this under ReportTemplateTests)
1155 protected function buildSourceFrom() {
1156 $activityContacts = CRM_Activity_BAO_ActivityContact
::buildOptions('record_type_id', 'validate');
1157 $sourceID = CRM_Utils_Array
::key('Activity Source', $activityContacts);
1159 FROM civicrm_activity {$this->_aliases['civicrm_activity']}
1160 INNER JOIN civicrm_activity_contact {$this->_aliases['civicrm_activity_contact']}
1161 ON {$this->_aliases['civicrm_activity']}.id = {$this->_aliases['civicrm_activity_contact']}.activity_id AND
1162 {$this->_aliases['civicrm_activity_contact']}.record_type_id = {$sourceID}
1163 INNER JOIN civicrm_contact civicrm_contact_source
1164 ON {$this->_aliases['civicrm_activity_contact']}.contact_id = civicrm_contact_source.id
1167 if ($this->isTableSelected('civicrm_email')) {
1169 LEFT JOIN civicrm_email civicrm_email_source
1170 ON {$this->_aliases['civicrm_activity_contact']}.contact_id = civicrm_email_source.contact_id AND
1171 civicrm_email_source.is_primary = 1";
1173 if ($this->isTableSelected('civicrm_phone')) {
1175 LEFT JOIN civicrm_phone civicrm_phone_source
1176 ON {$this->_aliases['civicrm_activity_contact']}.contact_id = civicrm_phone_source.contact_id AND
1177 civicrm_phone_source.is_primary = 1 ";
1179 $this->_aliases
['civicrm_contact'] = 'civicrm_contact_source';
1180 $this->joinAddressFromContact();