added sudo constant for sms api type
[civicrm-core.git] / CRM / Report / Form / Activity.php
... / ...
CommitLineData
1<?php
2/*
3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
5 | |
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
9 +--------------------------------------------------------------------+
10 */
11
12/**
13 *
14 * @package CRM
15 * @copyright CiviCRM LLC https://civicrm.org/licensing
16 */
17class CRM_Report_Form_Activity extends CRM_Report_Form {
18 protected $_selectAliasesTotal = [];
19
20 protected $_customGroupExtends = [
21 'Activity',
22 ];
23
24 protected $_nonDisplayFields = [];
25
26 /**
27 * This report has not been optimised for group filtering.
28 *
29 * The functionality for group filtering has been improved but not
30 * all reports have been adjusted to take care of it. This report has not
31 * and will run an inefficient query until fixed.
32 *
33 * CRM-19170
34 *
35 * @var bool
36 */
37 protected $groupFilterNotOptimised = TRUE;
38
39 /**
40 * Class constructor.
41 */
42 public function __construct() {
43 // There could be multiple contacts. We not clear on which contact id to display.
44 // Lets hide it for now.
45 $this->_exposeContactID = FALSE;
46 // if navigated from count link of activity summary reports.
47 $this->_resetDateFilter = CRM_Utils_Request::retrieve('resetDateFilter', 'Boolean');
48
49 $config = CRM_Core_Config::singleton();
50 $campaignEnabled = in_array("CiviCampaign", $config->enableComponents);
51 $caseEnabled = in_array("CiviCase", $config->enableComponents);
52 if ($campaignEnabled) {
53 $this->engagementLevels = CRM_Campaign_PseudoConstant::engagementLevel();
54 }
55
56 $components = CRM_Core_Component::getEnabledComponents();
57 foreach ($components as $componentName => $componentInfo) {
58 // CRM-19201: Add support for reporting CiviCampaign activities
59 // For CiviCase, "access all cases and activities" is required here
60 // rather than "access my cases and activities" to prevent those with
61 // only the later permission from seeing a list of all cases which might
62 // present a privacy issue.
63 if (CRM_Core_Permission::access($componentName, TRUE, TRUE)) {
64 $accessAllowed[] = $componentInfo->componentID;
65 }
66 }
67
68 $include = '';
69 if (!empty($accessAllowed)) {
70 $include = 'OR v.component_id IN (' . implode(', ', $accessAllowed) . ')';
71 }
72 $condition = " AND ( v.component_id IS NULL {$include} )";
73 $this->activityTypes = CRM_Core_OptionGroup::values('activity_type', FALSE, FALSE, FALSE, $condition);
74 asort($this->activityTypes);
75
76 // @todo split the 3 different contact tables into their own array items.
77 // this will massively simplify the needs of this report.
78 $this->_columns = [
79 'civicrm_contact' => [
80 'dao' => 'CRM_Contact_DAO_Contact',
81 'fields' => [
82 'contact_source' => [
83 'name' => 'sort_name',
84 'title' => ts('Source Name'),
85 'alias' => 'civicrm_contact_source',
86 'no_repeat' => TRUE,
87 ],
88 'contact_assignee' => [
89 'name' => 'sort_name',
90 'title' => ts('Assignee Name'),
91 'alias' => 'civicrm_contact_assignee',
92 'dbAlias' => "civicrm_contact_assignee.sort_name",
93 'default' => TRUE,
94 ],
95 'contact_target' => [
96 'name' => 'sort_name',
97 'title' => ts('Target Name'),
98 'alias' => 'civicrm_contact_target',
99 'dbAlias' => "civicrm_contact_target.sort_name",
100 'default' => TRUE,
101 ],
102 'contact_source_id' => [
103 'name' => 'id',
104 'alias' => 'civicrm_contact_source',
105 'dbAlias' => "civicrm_contact_source.id",
106 'no_display' => TRUE,
107 'default' => TRUE,
108 'required' => TRUE,
109 ],
110 'contact_assignee_id' => [
111 'name' => 'id',
112 'alias' => 'civicrm_contact_assignee',
113 'dbAlias' => "civicrm_contact_assignee.id",
114 'no_display' => TRUE,
115 'default' => TRUE,
116 'required' => TRUE,
117 ],
118 'contact_target_id' => [
119 'name' => 'id',
120 'alias' => 'civicrm_contact_target',
121 'dbAlias' => "civicrm_contact_target.id",
122 'no_display' => TRUE,
123 'default' => TRUE,
124 'required' => TRUE,
125 ],
126 ],
127 'filters' => [
128 'contact_source' => [
129 'name' => 'sort_name',
130 'alias' => 'civicrm_contact_source',
131 'title' => ts('Source Name'),
132 'operator' => 'like',
133 'type' => CRM_Report_Form::OP_STRING,
134 ],
135 'contact_assignee' => [
136 'name' => 'sort_name',
137 'alias' => 'civicrm_contact_assignee',
138 'title' => ts('Assignee Name'),
139 'operator' => 'like',
140 'type' => CRM_Report_Form::OP_STRING,
141 ],
142 'contact_target' => [
143 'name' => 'sort_name',
144 'alias' => 'civicrm_contact_target',
145 'title' => ts('Target Name'),
146 'operator' => 'like',
147 'type' => CRM_Report_Form::OP_STRING,
148 ],
149 'current_user' => [
150 'name' => 'current_user',
151 'title' => ts('Limit To Current User'),
152 'type' => CRM_Utils_Type::T_INT,
153 'operatorType' => CRM_Report_Form::OP_SELECT,
154 'options' => ['0' => ts('No'), '1' => ts('Yes')],
155 ],
156 ],
157 'grouping' => 'contact-fields',
158 ],
159 'civicrm_email' => [
160 'dao' => 'CRM_Core_DAO_Email',
161 'fields' => [
162 'contact_source_email' => [
163 'name' => 'email',
164 'title' => ts('Source Email'),
165 'alias' => 'civicrm_email_source',
166 ],
167 'contact_assignee_email' => [
168 'name' => 'email',
169 'title' => ts('Assignee Email'),
170 'alias' => 'civicrm_email_assignee',
171 ],
172 'contact_target_email' => [
173 'name' => 'email',
174 'title' => ts('Target Email'),
175 'alias' => 'civicrm_email_target',
176 ],
177 ],
178 'order_bys' => [
179 'source_contact_email' => [
180 'name' => 'email',
181 'title' => ts('Source Email'),
182 'dbAlias' => 'civicrm_email_contact_source_email',
183 ],
184 ],
185 ],
186 'civicrm_phone' => [
187 'dao' => 'CRM_Core_DAO_Phone',
188 'fields' => [
189 'contact_source_phone' => [
190 'name' => 'phone',
191 'title' => ts('Source Phone'),
192 'alias' => 'civicrm_phone_source',
193 ],
194 'contact_assignee_phone' => [
195 'name' => 'phone',
196 'title' => ts('Assignee Phone'),
197 'alias' => 'civicrm_phone_assignee',
198 ],
199 'contact_target_phone' => [
200 'name' => 'phone',
201 'title' => ts('Target Phone'),
202 'alias' => 'civicrm_phone_target',
203 ],
204 ],
205 ],
206 'civicrm_activity' => [
207 'dao' => 'CRM_Activity_DAO_Activity',
208 'fields' => [
209 'id' => [
210 'no_display' => TRUE,
211 'title' => ts('Activity ID'),
212 'required' => TRUE,
213 ],
214 'source_record_id' => [
215 'no_display' => TRUE,
216 'required' => TRUE,
217 ],
218 'activity_type_id' => [
219 'title' => ts('Activity Type'),
220 'required' => TRUE,
221 'type' => CRM_Utils_Type::T_STRING,
222 ],
223 'activity_subject' => [
224 'title' => ts('Subject'),
225 'default' => TRUE,
226 ],
227 'activity_date_time' => [
228 'title' => ts('Activity Date'),
229 'required' => TRUE,
230 ],
231 'status_id' => [
232 'title' => ts('Activity Status'),
233 'default' => TRUE,
234 'type' => CRM_Utils_Type::T_STRING,
235 ],
236 'duration' => [
237 'title' => ts('Duration'),
238 'type' => CRM_Utils_Type::T_INT,
239 ],
240 'location' => [
241 'title' => ts('Location'),
242 'type' => CRM_Utils_Type::T_STRING,
243 ],
244 'details' => [
245 'title' => ts('Activity Details'),
246 ],
247 'priority_id' => [
248 'title' => ts('Priority'),
249 'default' => TRUE,
250 'type' => CRM_Utils_Type::T_STRING,
251 ],
252 ],
253 'filters' => [
254 'activity_date_time' => [
255 'default' => 'this.month',
256 'operatorType' => CRM_Report_Form::OP_DATE,
257 ],
258 'activity_subject' => ['title' => ts('Activity Subject')],
259 'activity_type_id' => [
260 'title' => ts('Activity Type'),
261 'operatorType' => CRM_Report_Form::OP_MULTISELECT,
262 'options' => $this->activityTypes,
263 ],
264 'status_id' => [
265 'title' => ts('Activity Status'),
266 'type' => CRM_Utils_Type::T_STRING,
267 'operatorType' => CRM_Report_Form::OP_MULTISELECT,
268 'options' => CRM_Core_PseudoConstant::activityStatus(),
269 ],
270 'location' => [
271 'title' => ts('Location'),
272 'type' => CRM_Utils_Type::T_TEXT,
273 ],
274 'details' => [
275 'title' => ts('Activity Details'),
276 'type' => CRM_Utils_Type::T_TEXT,
277 ],
278 'priority_id' => [
279 'title' => ts('Activity Priority'),
280 'type' => CRM_Utils_Type::T_STRING,
281 'operatorType' => CRM_Report_Form::OP_MULTISELECT,
282 'options' => CRM_Core_PseudoConstant::get('CRM_Activity_DAO_Activity', 'priority_id'),
283 ],
284 ],
285 'order_bys' => [
286 'activity_date_time' => [
287 'title' => ts('Activity Date'),
288 'default_weight' => '1',
289 'dbAlias' => 'civicrm_activity_activity_date_time',
290 ],
291 'activity_type_id' => [
292 'title' => ts('Activity Type'),
293 'default_weight' => '2',
294 'dbAlias' => 'field(civicrm_activity_activity_type_id, ' . implode(', ', array_keys($this->activityTypes)) . ')',
295 ],
296 ],
297 'grouping' => 'activity-fields',
298 'alias' => 'activity',
299 ],
300 // Hack to get $this->_alias populated for the table.
301 'civicrm_activity_contact' => [
302 'dao' => 'CRM_Activity_DAO_ActivityContact',
303 'fields' => [],
304 ],
305 ] + $this->addressFields(TRUE);
306
307 if ($caseEnabled && CRM_Core_Permission::check('access all cases and activities')) {
308 $this->_columns['civicrm_activity']['filters']['include_case_activities'] = [
309 'name' => 'include_case_activities',
310 'title' => ts('Include Case Activities'),
311 'type' => CRM_Utils_Type::T_INT,
312 'operatorType' => CRM_Report_Form::OP_SELECT,
313 'options' => ['0' => ts('No'), '1' => ts('Yes')],
314 ];
315 }
316
317 if ($campaignEnabled) {
318 // Add display column and filter for Survey Results, Campaign and Engagement Index if CiviCampaign is enabled
319
320 $this->_columns['civicrm_activity']['fields']['result'] = [
321 'title' => ts('Survey Result'),
322 'default' => 'false',
323 ];
324 $this->_columns['civicrm_activity']['filters']['result'] = [
325 'title' => ts('Survey Result'),
326 'operator' => 'like',
327 'type' => CRM_Utils_Type::T_STRING,
328 ];
329 // If we have campaigns enabled, add those elements to both the fields, filters.
330 $this->addCampaignFields('civicrm_activity');
331
332 if (!empty($this->engagementLevels)) {
333 $this->_columns['civicrm_activity']['fields']['engagement_level'] = [
334 'title' => ts('Engagement Index'),
335 'default' => 'false',
336 ];
337 $this->_columns['civicrm_activity']['filters']['engagement_level'] = [
338 'title' => ts('Engagement Index'),
339 'type' => CRM_Utils_Type::T_INT,
340 'operatorType' => CRM_Report_Form::OP_MULTISELECT,
341 'options' => $this->engagementLevels,
342 ];
343 }
344 }
345 $this->_groupFilter = TRUE;
346 $this->_tagFilter = TRUE;
347 $this->_tagFilterTable = 'civicrm_activity';
348
349 parent::__construct();
350 }
351
352 public function preProcess() {
353 // Is "Include Case Activities" selected? If yes, include the case_id as a hidden column
354 $formToUse = $this->noController ? NULL : $this;
355 $includeCaseActivities = CRM_Utils_Request::retrieve('include_case_activities_value', 'Boolean', $formToUse);
356 if (!empty($includeCaseActivities)) {
357 $this->_columns['civicrm_case_activity'] = [
358 'dao' => 'CRM_Case_DAO_CaseActivity',
359 'fields' => [
360 'case_id' => [
361 'no_display' => TRUE,
362 'required' => TRUE,
363 ],
364 ],
365 ];
366 }
367 parent::preProcess();
368 }
369
370 /**
371 * Adding address fields with dbAlias for order clause.
372 *
373 * @param bool $orderBy
374 *
375 * @return array
376 * Address fields
377 */
378 public function addressFields($orderBy = FALSE) {
379 $address = parent::addAddressFields(FALSE, TRUE);
380 if ($orderBy) {
381 foreach ($address['civicrm_address']['order_bys'] as $fieldName => $field) {
382 $address['civicrm_address']['order_bys'][$fieldName]['dbAlias'] = "civicrm_address_{$fieldName}";
383 }
384 }
385 return $address;
386 }
387
388 /**
389 * Build select clause.
390 *
391 * @todo get rid of $recordType param. It's only because 3 separate contact tables
392 * are mis-declared as one that we need it.
393 *
394 * @param null $recordType deprecated
395 * Parameter to hack around the bad decision made in construct to misrepresent
396 * different tables as the same table.
397 */
398 public function select($recordType = 'target') {
399 if (!array_key_exists("contact_{$recordType}", $this->_params['fields']) &&
400 $recordType != 'final'
401 ) {
402 $this->_nonDisplayFields[] = "civicrm_contact_contact_{$recordType}";
403 }
404 parent::select();
405
406 if ($recordType == 'final' && !empty($this->_nonDisplayFields)) {
407 foreach ($this->_nonDisplayFields as $fieldName) {
408 unset($this->_columnHeaders[$fieldName]);
409 }
410 }
411
412 if (empty($this->_selectAliasesTotal)) {
413 $this->_selectAliasesTotal = $this->_selectAliases;
414 }
415
416 $removeKeys = [];
417 if ($recordType == 'target') {
418 // @todo - fix up the way the tables are declared in construct & remove this.
419 foreach ($this->_selectClauses as $key => $clause) {
420 if (strstr($clause, 'civicrm_contact_assignee.') ||
421 strstr($clause, 'civicrm_contact_source.') ||
422 strstr($clause, 'civicrm_email_assignee.') ||
423 strstr($clause, 'civicrm_email_source.') ||
424 strstr($clause, 'civicrm_phone_assignee.') ||
425 strstr($clause, 'civicrm_phone_source.')
426 ) {
427 $removeKeys[] = $key;
428 unset($this->_selectClauses[$key]);
429 }
430 }
431 }
432 elseif ($recordType == 'assignee') {
433 // @todo - fix up the way the tables are declared in construct & remove this.
434 foreach ($this->_selectClauses as $key => $clause) {
435 if (strstr($clause, 'civicrm_contact_target.') ||
436 strstr($clause, 'civicrm_contact_source.') ||
437 strstr($clause, 'civicrm_email_target.') ||
438 strstr($clause, 'civicrm_email_source.') ||
439 strstr($clause, 'civicrm_phone_target.') ||
440 strstr($clause, 'civicrm_phone_source.') ||
441 strstr($clause, 'civicrm_address_')
442 ) {
443 $removeKeys[] = $key;
444 unset($this->_selectClauses[$key]);
445 }
446 }
447 }
448 elseif ($recordType == 'source') {
449 // @todo - fix up the way the tables are declared in construct & remove this.
450 foreach ($this->_selectClauses as $key => $clause) {
451 if (strstr($clause, 'civicrm_contact_target.') ||
452 strstr($clause, 'civicrm_contact_assignee.') ||
453 strstr($clause, 'civicrm_email_target.') ||
454 strstr($clause, 'civicrm_email_assignee.') ||
455 strstr($clause, 'civicrm_phone_target.') ||
456 strstr($clause, 'civicrm_phone_assignee.') ||
457 strstr($clause, 'civicrm_address_')
458 ) {
459 $removeKeys[] = $key;
460 unset($this->_selectClauses[$key]);
461 }
462 }
463 }
464 elseif ($recordType == 'final') {
465 $this->_selectClauses = $this->_selectAliasesTotal;
466 foreach ($this->_selectClauses as $key => $clause) {
467 // @todo - fix up the way the tables are declared in construct & remove this.
468 if (strstr($clause, 'civicrm_contact_contact_target') ||
469 strstr($clause, 'civicrm_contact_contact_assignee') ||
470 strstr($clause, 'civicrm_contact_contact_source') ||
471 strstr($clause, 'civicrm_phone_contact_source_phone') ||
472 strstr($clause, 'civicrm_phone_contact_assignee_phone') ||
473 strstr($clause, 'civicrm_email_contact_source_email') ||
474 strstr($clause, 'civicrm_email_contact_assignee_email') ||
475 strstr($clause, 'civicrm_email_contact_target_email') ||
476 strstr($clause, 'civicrm_phone_contact_target_phone') ||
477 strstr($clause, 'civicrm_address_')
478 ) {
479 $this->_selectClauses[$key] = "GROUP_CONCAT(DISTINCT $clause SEPARATOR ';') as $clause";
480 }
481 }
482 }
483
484 if ($recordType) {
485 foreach ($removeKeys as $key) {
486 unset($this->_selectAliases[$key]);
487 }
488
489 if ($recordType == 'target') {
490 foreach ($this->_columns['civicrm_address']['order_bys'] as $fieldName => $field) {
491 $orderByFld = $this->_columns['civicrm_address']['order_bys'][$fieldName];
492 $fldInfo = $this->_columns['civicrm_address']['fields'][$fieldName];
493 $this->_selectAliases[] = $orderByFld['dbAlias'];
494 $this->_selectClauses[] = "{$fldInfo['dbAlias']} as {$orderByFld['dbAlias']}";
495 }
496 $this->_selectAliases = array_unique($this->_selectAliases);
497 $this->_selectClauses = array_unique($this->_selectClauses);
498 }
499 $this->_select = "SELECT " . implode(', ', $this->_selectClauses) . " ";
500 }
501 }
502
503 /**
504 * Build from clause.
505 * @todo remove this function & declare the 3 contact tables separately
506 */
507 public function from() {
508 $this->buildFrom('target');
509 }
510
511 /**
512 * Build where clause.
513 *
514 * @todo get rid of $recordType param. It's only because 3 separate contact tables
515 * are mis-declared as one that we need it.
516 *
517 * @param string $recordType
518 */
519 public function where($recordType = NULL) {
520 $this->_where = " WHERE {$this->_aliases['civicrm_activity']}.is_test = 0 AND
521 {$this->_aliases['civicrm_activity']}.is_deleted = 0 AND
522 {$this->_aliases['civicrm_activity']}.is_current_revision = 1";
523
524 $clauses = [];
525 foreach ($this->_columns as $tableName => $table) {
526 if (array_key_exists('filters', $table)) {
527
528 foreach ($table['filters'] as $fieldName => $field) {
529 $clause = NULL;
530 if ($fieldName != 'contact_' . $recordType &&
531 (strstr($fieldName, '_target') ||
532 strstr($fieldName, '_assignee') ||
533 strstr($fieldName, '_source')
534 )
535 ) {
536 continue;
537 }
538 if (CRM_Utils_Array::value('type', $field) & CRM_Utils_Type::T_DATE) {
539 $relative = CRM_Utils_Array::value("{$fieldName}_relative", $this->_params);
540 $from = CRM_Utils_Array::value("{$fieldName}_from", $this->_params);
541 $to = CRM_Utils_Array::value("{$fieldName}_to", $this->_params);
542
543 $clause = $this->dateClause($field['name'], $relative, $from, $to, $field['type']);
544 }
545 else {
546 $op = CRM_Utils_Array::value("{$fieldName}_op", $this->_params);
547 if ($op && ($op != 'nnll' && $op != 'nll')) {
548 $clause = $this->whereClause($field,
549 $op,
550 CRM_Utils_Array::value("{$fieldName}_value", $this->_params),
551 CRM_Utils_Array::value("{$fieldName}_min", $this->_params),
552 CRM_Utils_Array::value("{$fieldName}_max", $this->_params)
553 );
554 if ($field['name'] == 'include_case_activities') {
555 $clause = NULL;
556 }
557 if ($fieldName == 'activity_type_id' &&
558 empty($this->_params['activity_type_id_value'])
559 ) {
560 if (empty($this->_params['include_case_activities_value'])) {
561 $this->activityTypes = CRM_Core_PseudoConstant::activityType(TRUE, FALSE, FALSE, 'label', TRUE);
562 }
563 $actTypes = array_flip($this->activityTypes);
564 $clause = "( {$this->_aliases['civicrm_activity']}.activity_type_id IN (" .
565 implode(',', $actTypes) . ") )";
566 }
567 }
568 }
569
570 if ($field['name'] == 'current_user') {
571 if (CRM_Utils_Array::value("{$fieldName}_value", $this->_params) ==
572 1
573 ) {
574 // get current user
575 $session = CRM_Core_Session::singleton();
576 if ($contactID = $session->get('userID')) {
577 $clause = "{$this->_aliases['civicrm_activity_contact']}.activity_id IN
578 (SELECT activity_id FROM civicrm_activity_contact WHERE contact_id = {$contactID})";
579 }
580 else {
581 $clause = NULL;
582 }
583 }
584 else {
585 $clause = NULL;
586 }
587 }
588 if (!empty($clause)) {
589 $clauses[] = $clause;
590 }
591 }
592 }
593 }
594
595 if (empty($clauses)) {
596 $this->_where .= " ";
597 }
598 else {
599 $this->_where .= " AND " . implode(' AND ', $clauses);
600 }
601
602 if ($this->_aclWhere) {
603 $this->_where .= " AND {$this->_aclWhere} ";
604 }
605 }
606
607 /**
608 * Override group by function.
609 */
610 public function groupBy() {
611 $this->_groupBy = CRM_Contact_BAO_Query::getGroupByFromSelectColumns($this->_selectClauses, "{$this->_aliases['civicrm_activity']}.id");
612 }
613
614 /**
615 * Build ACL clause.
616 *
617 * @param string $tableAlias
618 */
619 public function buildACLClause($tableAlias = 'contact_a') {
620 //override for ACL( Since Contact may be source
621 //contact/assignee or target also it may be null )
622
623 if (CRM_Core_Permission::check('view all contacts')) {
624 $this->_aclFrom = $this->_aclWhere = NULL;
625 return;
626 }
627
628 $session = CRM_Core_Session::singleton();
629 $contactID = $session->get('userID');
630 if (!$contactID) {
631 $contactID = 0;
632 }
633 $contactID = CRM_Utils_Type::escape($contactID, 'Integer');
634
635 CRM_Contact_BAO_Contact_Permission::cache($contactID);
636 $clauses = [];
637 foreach ($tableAlias as $k => $alias) {
638 $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 ";
639 }
640
641 $this->_aclFrom = implode(" ", $clauses);
642 $this->_aclWhere = NULL;
643 }
644
645 /**
646 * @param int $groupID
647 *
648 * @throws Exception
649 */
650 public function add2group($groupID) {
651 if (CRM_Utils_Array::value("contact_target_op", $this->_params) == 'nll') {
652 CRM_Core_Error::fatal(ts('Current filter criteria didn\'t have any target contact to add to group'));
653 }
654
655 $new_select = 'AS addtogroup_contact_id';
656 $select = str_ireplace('AS civicrm_contact_contact_target_id', $new_select, $this->_select);
657 $new_having = ' addtogroup_contact_id';
658 $having = str_ireplace(' civicrm_contact_contact_target_id', $new_having, $this->_having);
659 $query = "$select
660FROM {$this->temporaryTables['activity_temp_table']['name']} tar
661GROUP BY civicrm_activity_id $having {$this->_orderBy}";
662 $select = 'AS addtogroup_contact_id';
663 $query = str_ireplace('AS civicrm_contact_contact_target_id', $select, $query);
664 CRM_Core_DAO::disableFullGroupByMode();
665 $dao = $this->executeReportQuery($query);
666 CRM_Core_DAO::reenableFullGroupByMode();
667
668 $contactIDs = [];
669 // Add resulting contacts to group
670 while ($dao->fetch()) {
671 if ($dao->addtogroup_contact_id) {
672 $contact_id = explode(';', $dao->addtogroup_contact_id);
673 if ($contact_id[0]) {
674 $contactIDs[$contact_id[0]] = $contact_id[0];
675 }
676 }
677 }
678
679 if (!empty($contactIDs)) {
680 CRM_Contact_BAO_GroupContact::addContactsToGroup($contactIDs, $groupID);
681 CRM_Core_Session::setStatus(ts("Listed contact(s) have been added to the selected group."), ts('Contacts Added'), 'success');
682 }
683 else {
684 CRM_Core_Session::setStatus(ts("The listed records(s) cannot be added to the group."));
685 }
686 }
687
688 /**
689 * @param $fields
690 * @param $files
691 * @param $self
692 *
693 * @return array
694 */
695 public static function formRule($fields, $files, $self) {
696 $errors = [];
697 $config = CRM_Core_Config::singleton();
698 if (in_array("CiviCase", $config->enableComponents)) {
699 $componentId = CRM_Core_Component::getComponentID('CiviCase');
700 $caseActivityTypes = CRM_Core_OptionGroup::values('activity_type', TRUE, FALSE, FALSE, " AND v.component_id={$componentId}");
701 if (!empty($fields['activity_type_id_value']) && is_array($fields['activity_type_id_value']) && empty($fields['include_case_activities_value'])) {
702 foreach ($fields['activity_type_id_value'] as $activityTypeId) {
703 if (in_array($activityTypeId, $caseActivityTypes)) {
704 $errors['fields'] = ts("Please enable 'Include Case Activities' to filter with Case Activity types.");
705 }
706 }
707 }
708 }
709 return $errors;
710 }
711
712 /**
713 * @param $applyLimit
714 *
715 * @return string
716 */
717 public function buildQuery($applyLimit = TRUE) {
718 $activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate');
719 $sourceID = CRM_Utils_Array::key('Activity Source', $activityContacts);
720
721 //Assign those recordtype to array which have filter operator as 'Is not empty' or 'Is empty'
722 $nullFilters = [];
723 foreach (['target', 'source', 'assignee'] as $type) {
724 if (CRM_Utils_Array::value("contact_{$type}_op", $this->_params) ==
725 'nnll' || !empty($this->_params["contact_{$type}_value"])
726 ) {
727 $nullFilters[] = " civicrm_contact_contact_{$type}_id IS NOT NULL ";
728 }
729 elseif (CRM_Utils_Array::value("contact_{$type}_op", $this->_params) ==
730 'nll'
731 ) {
732 $nullFilters[] = " civicrm_contact_contact_{$type}_id IS NULL ";
733 }
734 }
735
736 // @todo - all this temp table stuff is here because pre 4.4 the activity contact
737 // form did not exist.
738 // Fixing the way the construct method declares them will make all this redundant.
739 // 1. fill temp table with target results
740 $this->buildACLClause(['civicrm_contact_target']);
741 $this->select('target');
742 $this->from();
743 $this->customDataFrom();
744 $this->where('target');
745 $tempTableName = $this->createTemporaryTable('activity_temp_table', "{$this->_select} {$this->_from} {$this->_where}");
746
747 // 2. add new columns to hold assignee and source results
748 // fixme: add when required
749 $tempQuery = "
750 ALTER TABLE $tempTableName
751 MODIFY COLUMN civicrm_contact_contact_target_id VARCHAR(128),
752 ADD COLUMN civicrm_contact_contact_assignee VARCHAR(128),
753 ADD COLUMN civicrm_contact_contact_source VARCHAR(128),
754 ADD COLUMN civicrm_contact_contact_assignee_id VARCHAR(128),
755 ADD COLUMN civicrm_contact_contact_source_id VARCHAR(128),
756 ADD COLUMN civicrm_phone_contact_assignee_phone VARCHAR(128),
757 ADD COLUMN civicrm_phone_contact_source_phone VARCHAR(128),
758 ADD COLUMN civicrm_email_contact_assignee_email VARCHAR(128),
759 ADD COLUMN civicrm_email_contact_source_email VARCHAR(128)";
760 $this->executeReportQuery($tempQuery);
761
762 // 3. fill temp table with assignee results
763 $this->buildACLClause(['civicrm_contact_assignee']);
764 $this->select('assignee');
765 $this->buildAssigneeFrom();
766
767 $this->customDataFrom();
768 $this->where('assignee');
769 $insertCols = implode(',', $this->_selectAliases);
770 $tempQuery = "INSERT INTO $tempTableName ({$insertCols})
771{$this->_select}
772{$this->_from} {$this->_where}";
773 $this->executeReportQuery($tempQuery);
774
775 // 4. fill temp table with source results
776 $this->buildACLClause(['civicrm_contact_source']);
777 $this->select('source');
778 $this->buildSourceFrom();
779 $this->customDataFrom();
780 $this->where('source');
781 $insertCols = implode(',', $this->_selectAliases);
782 $tempQuery = "INSERT INTO $tempTableName ({$insertCols})
783{$this->_select}
784{$this->_from} {$this->_where}";
785 $this->executeReportQuery($tempQuery);
786
787 // 5. show final result set from temp table
788 $rows = [];
789 $this->select('final');
790 $this->_having = "";
791 if (!empty($nullFilters)) {
792 $this->_having = "HAVING " . implode(' AND ', $nullFilters);
793 }
794 $this->orderBy();
795 foreach ($this->_sections as $alias => $section) {
796 if (!empty($section) && $section['name'] == 'activity_date_time') {
797 $this->alterSectionHeaderForDateTime($tempTableName, $section['tplField']);
798 }
799 }
800
801 if ($applyLimit) {
802 $this->limit();
803 }
804
805 $groupByFromSelect = CRM_Contact_BAO_Query::getGroupByFromSelectColumns($this->_selectClauses, 'civicrm_activity_id');
806
807 $this->_where = " WHERE (1)";
808 $this->buildPermissionClause();
809 if ($this->_aclWhere) {
810 $this->_where .= " AND {$this->_aclWhere} ";
811 }
812
813 $caseJoin = '';
814 if (!empty($this->_params['include_case_activities_value'])) {
815 $caseJoin = "LEFT JOIN civicrm_case_activity {$this->_aliases['civicrm_case_activity']} ON {$this->_aliases['civicrm_activity']}.id = {$this->_aliases['civicrm_case_activity']}.activity_id";
816 }
817
818 $sql = "{$this->_select}
819 FROM $tempTableName tar
820 INNER JOIN civicrm_activity {$this->_aliases['civicrm_activity']} ON {$this->_aliases['civicrm_activity']}.id = tar.civicrm_activity_id
821 INNER JOIN civicrm_activity_contact {$this->_aliases['civicrm_activity_contact']} ON {$this->_aliases['civicrm_activity_contact']}.activity_id = {$this->_aliases['civicrm_activity']}.id
822 AND {$this->_aliases['civicrm_activity_contact']}.record_type_id = {$sourceID}
823 LEFT JOIN civicrm_contact contact_civireport ON contact_civireport.id = {$this->_aliases['civicrm_activity_contact']}.contact_id
824 {$caseJoin}
825 {$this->_where} {$groupByFromSelect} {$this->_having} {$this->_orderBy} {$this->_limit}";
826
827 CRM_Utils_Hook::alterReportVar('sql', $this, $this);
828 $this->addToDeveloperTab($sql);
829
830 return $sql;
831 }
832
833 public function postProcess() {
834 //reset value of activity_date
835 if (!empty($this->_resetDateFilter)) {
836 $this->_formValues["activity_date_time_relative"] = NULL;
837 }
838
839 $this->beginPostProcess();
840 $sql = $this->buildQuery(TRUE);
841 $this->buildRows($sql, $rows);
842
843 // format result set.
844 $this->formatDisplay($rows);
845
846 // assign variables to templates
847 $this->doTemplateAssignment($rows);
848
849 // do print / pdf / instance stuff if needed
850 $this->endPostProcess($rows);
851 }
852
853 /**
854 * Alter display of rows.
855 *
856 * Iterate through the rows retrieved via SQL and make changes for display purposes,
857 * such as rendering contacts as links.
858 *
859 * @param array $rows
860 * Rows generated by SQL, with an array for each row.
861 */
862 public function alterDisplay(&$rows) {
863 $entryFound = FALSE;
864 $activityType = CRM_Core_PseudoConstant::activityType(TRUE, TRUE, FALSE, 'label', TRUE);
865 $activityStatus = CRM_Core_PseudoConstant::activityStatus();
866 $priority = CRM_Core_PseudoConstant::get('CRM_Activity_DAO_Activity', 'priority_id');
867 $viewLinks = FALSE;
868
869 // Would we ever want to retrieve from the form controller??
870 $form = $this->noController ? NULL : $this;
871 $context = CRM_Utils_Request::retrieve('context', 'Alphanumeric', $form, FALSE, 'report');
872 $actUrl = '';
873
874 if (CRM_Core_Permission::check('access CiviCRM')) {
875 $viewLinks = TRUE;
876 $onHover = ts('View Contact Summary for this Contact');
877 $onHoverAct = ts('View Activity Record');
878 }
879 foreach ($rows as $rowNum => $row) {
880 // if we have an activity type, format the View Activity link for use in various columns
881 if ($viewLinks &&
882 array_key_exists('civicrm_activity_activity_type_id', $row)
883 ) {
884 // Check for target contact id(s) and use the first contact id in that list for view activity link if found,
885 // else use source contact id
886 if (!empty($rows[$rowNum]['civicrm_contact_contact_target_id'])) {
887 $targets = explode(';', $rows[$rowNum]['civicrm_contact_contact_target_id']);
888 $cid = $targets[0];
889 }
890 else {
891 $cid = $rows[$rowNum]['civicrm_contact_contact_source_id'];
892 }
893
894 if (empty($this->_params['include_case_activities_value']) || empty($rows[$rowNum]['civicrm_case_activity_case_id'])) {
895 // Generate a "view activity" link
896 $actActionLinks = CRM_Activity_Selector_Activity::actionLinks($row['civicrm_activity_activity_type_id'],
897 CRM_Utils_Array::value('civicrm_activity_source_record_id', $rows[$rowNum]),
898 FALSE,
899 $rows[$rowNum]['civicrm_activity_id']
900 );
901
902 $actLinkValues = [
903 'id' => $rows[$rowNum]['civicrm_activity_id'],
904 'cid' => $cid,
905 'cxt' => $context,
906 ];
907 $actUrl = CRM_Utils_System::url($actActionLinks[CRM_Core_Action::VIEW]['url'],
908 CRM_Core_Action::replace($actActionLinks[CRM_Core_Action::VIEW]['qs'], $actLinkValues), TRUE
909 );
910 }
911 else {
912 // Generate a "view case activity" link
913 $caseActionLinks = CRM_Case_Selector_Search::actionLinks();
914 $caseLinkValues = [
915 'aid' => $rows[$rowNum]['civicrm_activity_id'],
916 'caseid' => $rows[$rowNum]['civicrm_case_activity_case_id'],
917 'cid' => $cid,
918 'cxt' => $context,
919 ];
920 $actUrl = CRM_Utils_System::url($caseActionLinks[CRM_Core_Action::VIEW]['url'],
921 CRM_Core_Action::replace($caseActionLinks[CRM_Core_Action::VIEW]['qs'], $caseLinkValues), TRUE
922 );
923 }
924 }
925
926 if (array_key_exists('civicrm_contact_contact_source', $row)) {
927 if ($value = $row['civicrm_contact_contact_source_id']) {
928 if ($viewLinks) {
929 $url = CRM_Utils_System::url("civicrm/contact/view",
930 'reset=1&cid=' . $value,
931 $this->_absoluteUrl
932 );
933 $rows[$rowNum]['civicrm_contact_contact_source_link'] = $url;
934 $rows[$rowNum]['civicrm_contact_contact_source_hover'] = $onHover;
935 }
936 $entryFound = TRUE;
937 }
938 }
939
940 if (array_key_exists('civicrm_contact_contact_assignee', $row)) {
941 $assigneeNames = explode(';', $row['civicrm_contact_contact_assignee']);
942 if ($value = $row['civicrm_contact_contact_assignee_id']) {
943 $assigneeContactIds = explode(';', $value);
944 $link = [];
945 if ($viewLinks) {
946 foreach ($assigneeContactIds as $id => $value) {
947 if (isset($value) && isset($assigneeNames[$id])) {
948 $url = CRM_Utils_System::url("civicrm/contact/view",
949 'reset=1&cid=' . $value,
950 $this->_absoluteUrl
951 );
952 $link[] = "<a title='" . $onHover . "' href='" . $url .
953 "'>{$assigneeNames[$id]}</a>";
954 }
955 }
956 $rows[$rowNum]['civicrm_contact_contact_assignee'] = implode('; ', $link);
957 }
958 $entryFound = TRUE;
959 }
960 }
961
962 if (array_key_exists('civicrm_contact_contact_target', $row)) {
963 $targetNames = explode(';', $row['civicrm_contact_contact_target']);
964 if ($value = $row['civicrm_contact_contact_target_id']) {
965 $targetContactIds = explode(';', $value);
966 $link = [];
967 if ($viewLinks) {
968 foreach ($targetContactIds as $id => $value) {
969 if (isset($value) && isset($targetNames[$id])) {
970 $url = CRM_Utils_System::url("civicrm/contact/view",
971 'reset=1&cid=' . $value,
972 $this->_absoluteUrl
973 );
974 $link[] = "<a title='" . $onHover . "' href='" . $url .
975 "'>{$targetNames[$id]}</a>";
976 }
977 }
978 $rows[$rowNum]['civicrm_contact_contact_target'] = implode('; ', $link);
979 }
980 $entryFound = TRUE;
981 }
982 }
983
984 if (array_key_exists('civicrm_activity_activity_type_id', $row)) {
985 if ($value = $row['civicrm_activity_activity_type_id']) {
986 $rows[$rowNum]['civicrm_activity_activity_type_id'] = $activityType[$value];
987 if ($viewLinks) {
988 $rows[$rowNum]['civicrm_activity_activity_type_id_link'] = $actUrl;
989 $rows[$rowNum]['civicrm_activity_activity_type_id_hover'] = $onHoverAct;
990 }
991 $entryFound = TRUE;
992 }
993 }
994
995 if (array_key_exists('civicrm_activity_status_id', $row)) {
996 if ($value = $row['civicrm_activity_status_id']) {
997 $rows[$rowNum]['civicrm_activity_status_id'] = $activityStatus[$value];
998 $entryFound = TRUE;
999 }
1000 }
1001
1002 if (array_key_exists('civicrm_activity_priority_id', $row)) {
1003 if ($value = $row['civicrm_activity_priority_id']) {
1004 $rows[$rowNum]['civicrm_activity_priority_id'] = $priority[$value];
1005 $entryFound = TRUE;
1006 }
1007 }
1008
1009 if (array_key_exists('civicrm_activity_details', $row) && $this->_outputMode == 'html') {
1010 if ($value = $row['civicrm_activity_details']) {
1011 $fullDetails = $rows[$rowNum]['civicrm_activity_details'];
1012 $rows[$rowNum]['civicrm_activity_details'] = substr($fullDetails, 0, strrpos(substr($fullDetails, 0, 80), ' '));
1013 if ($actUrl) {
1014 $rows[$rowNum]['civicrm_activity_details'] .= " <a href='{$actUrl}' title='{$onHoverAct}'>(more)</a>";
1015 }
1016 $entryFound = TRUE;
1017 }
1018 }
1019
1020 if (array_key_exists('civicrm_activity_campaign_id', $row)) {
1021 if ($value = $row['civicrm_activity_campaign_id']) {
1022 $rows[$rowNum]['civicrm_activity_campaign_id'] = $this->campaigns[$value];
1023 $entryFound = TRUE;
1024 }
1025 }
1026
1027 if (array_key_exists('civicrm_activity_engagement_level', $row)) {
1028 if ($value = $row['civicrm_activity_engagement_level']) {
1029 $rows[$rowNum]['civicrm_activity_engagement_level'] = $this->engagementLevels[$value];
1030 $entryFound = TRUE;
1031 }
1032 }
1033
1034 if (array_key_exists('civicrm_activity_activity_date_time', $row) &&
1035 array_key_exists('civicrm_activity_status_id', $row)
1036 ) {
1037 if (CRM_Utils_Date::overdue($rows[$rowNum]['civicrm_activity_activity_date_time']) &&
1038 $activityStatus[$row['civicrm_activity_status_id']] != 'Completed'
1039 ) {
1040 $rows[$rowNum]['class'] = "status-overdue";
1041 $entryFound = TRUE;
1042 }
1043 }
1044
1045 $entryFound = $this->alterDisplayAddressFields($row, $rows, $rowNum, 'activity', 'List all activities for this', ';') ? TRUE : $entryFound;
1046
1047 if (!$entryFound) {
1048 break;
1049 }
1050 }
1051 }
1052
1053 public function sectionTotals() {
1054 // Reports using order_bys with sections must populate $this->_selectAliases in select() method.
1055 if (empty($this->_selectAliases)) {
1056 return;
1057 }
1058
1059 if (!empty($this->_sections)) {
1060 // pull section aliases out of $this->_sections
1061 $sectionAliases = array_keys($this->_sections);
1062
1063 $ifnulls = [];
1064 foreach (array_merge($sectionAliases, $this->_selectAliases) as $alias) {
1065 $ifnulls[] = "ifnull($alias, '') as $alias";
1066 }
1067 $this->_select = "SELECT " . implode(", ", $ifnulls);
1068 $this->_select = CRM_Contact_BAO_Query::appendAnyValueToSelect($ifnulls, $sectionAliases);
1069
1070 $query = $this->_select .
1071 ", count(DISTINCT civicrm_activity_id) as ct from {$this->temporaryTables['activity_temp_table']['name']} group by " .
1072 implode(", ", $sectionAliases);
1073
1074 // initialize array of total counts
1075 $totals = [];
1076 $dao = $this->executeReportQuery($query);
1077 while ($dao->fetch()) {
1078 // let $this->_alterDisplay translate any integer ids to human-readable values.
1079 $rows[0] = $dao->toArray();
1080 $this->alterDisplay($rows);
1081 $row = $rows[0];
1082
1083 // add totals for all permutations of section values
1084 $values = [];
1085 $i = 1;
1086 $aliasCount = count($sectionAliases);
1087 foreach ($sectionAliases as $alias) {
1088 $values[] = $row[$alias];
1089 $key = implode(CRM_Core_DAO::VALUE_SEPARATOR, $values);
1090 if ($i == $aliasCount) {
1091 // the last alias is the lowest-level section header; use count as-is
1092 $totals[$key] = $dao->ct;
1093 }
1094 else {
1095 // other aliases are higher level; roll count into their total
1096 $totals[$key] += $dao->ct;
1097 }
1098 }
1099 }
1100 $this->assign('sectionTotals', $totals);
1101 }
1102 }
1103
1104 /**
1105 * @todo remove this function & declare the 3 contact tables separately
1106 *
1107 * (Currently the construct method incorrectly melds them - this is an interim
1108 * refactor in order to get this under ReportTemplateTests)
1109 */
1110 protected function buildAssigneeFrom() {
1111 $this->buildFrom('assignee');
1112 }
1113
1114 /**
1115 * @todo remove this function & declare the 3 contact tables separately
1116 *
1117 * (Currently the construct method incorrectly melds them - this is an interim
1118 * refactor in order to get this under ReportTemplateTests)
1119 */
1120 protected function buildSourceFrom() {
1121 $this->buildFrom('source');
1122 }
1123
1124 /**
1125 * Shared function to build the from clause
1126 *
1127 * @param string $recordType (one of 'source', 'activity', 'target')
1128 */
1129 protected function buildFrom($recordType) {
1130 $activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate');
1131 switch ($recordType) {
1132 case 'target':
1133 $recordTypeID = CRM_Utils_Array::key('Activity Targets', $activityContacts);
1134 break;
1135
1136 case 'source':
1137 $recordTypeID = CRM_Utils_Array::key('Activity Source', $activityContacts);
1138 break;
1139
1140 case 'assignee':
1141 $recordTypeID = CRM_Utils_Array::key('Activity Assignees', $activityContacts);
1142 break;
1143
1144 }
1145
1146 $this->_from = "
1147 FROM civicrm_activity {$this->_aliases['civicrm_activity']}
1148 INNER JOIN civicrm_activity_contact {$this->_aliases['civicrm_activity_contact']}
1149 ON {$this->_aliases['civicrm_activity']}.id = {$this->_aliases['civicrm_activity_contact']}.activity_id AND
1150 {$this->_aliases['civicrm_activity_contact']}.record_type_id = {$recordTypeID}
1151 INNER JOIN civicrm_contact civicrm_contact_{$recordType}
1152 ON {$this->_aliases['civicrm_activity_contact']}.contact_id = civicrm_contact_{$recordType}.id
1153 {$this->_aclFrom}";
1154 if (!empty($this->_params['include_case_activities_value'])) {
1155 $this->_from .= "
1156 LEFT JOIN civicrm_case_activity {$this->_aliases['civicrm_case_activity']}
1157 ON {$this->_aliases['civicrm_case_activity']}.activity_id = {$this->_aliases['civicrm_activity']}.id";
1158 }
1159
1160 if ($this->isTableSelected('civicrm_email')) {
1161 $this->_from .= "
1162 LEFT JOIN civicrm_email civicrm_email_{$recordType}
1163 ON {$this->_aliases['civicrm_activity_contact']}.contact_id = civicrm_email_{$recordType}.contact_id AND
1164 civicrm_email_{$recordType}.is_primary = 1";
1165 }
1166
1167 if ($this->isTableSelected('civicrm_phone')) {
1168 $this->_from .= "
1169 LEFT JOIN civicrm_phone civicrm_phone_{$recordType}
1170 ON {$this->_aliases['civicrm_activity_contact']}.contact_id = civicrm_phone_{$recordType}.contact_id AND
1171 civicrm_phone_{$recordType}.is_primary = 1 ";
1172 }
1173 $this->_aliases['civicrm_contact'] = "civicrm_contact_{$recordType}";
1174
1175 $this->joinAddressFromContact();
1176 }
1177
1178}