Merge pull request #15098 from civicrm/5.17
[civicrm-core.git] / CRM / Activity / Selector / Search.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
fee14197 4 | CiviCRM version 5 |
6a488035 5 +--------------------------------------------------------------------+
6b83d5bd 6 | Copyright CiviCRM LLC (c) 2004-2019 |
6a488035
TO
7 +--------------------------------------------------------------------+
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 |
c73475ea 12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
6a488035
TO
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 |
c73475ea
WA
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
6a488035
TO
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 +--------------------------------------------------------------------+
d25dd0ee 26 */
6a488035
TO
27
28/**
29 *
30 * @package CRM
6b83d5bd 31 * @copyright CiviCRM LLC (c) 2004-2019
6a488035
TO
32 */
33
34/**
3819f101 35 * This class is used to retrieve and display a range of contacts that match the given criteria.
6a488035 36 *
3819f101 37 * Specifically for results of advanced search options.
6a488035
TO
38 */
39class CRM_Activity_Selector_Search extends CRM_Core_Selector_Base implements CRM_Core_Selector_API {
40
41 /**
42 * This defines two actions- View and Edit.
43 *
44 * @var array
6a488035 45 */
62d3ee27 46 public static $_links = NULL;
6a488035
TO
47
48 /**
100fef9d 49 * We use desc to remind us what that column is, name is used in the tpl
6a488035
TO
50 *
51 * @var array
6a488035 52 */
62d3ee27 53 public static $_columnHeaders;
6a488035
TO
54
55 /**
56 * Properties of contact we're interested in displaying
57 * @var array
6a488035
TO
58 */
59
62d3ee27 60 public static $_properties = [
6a488035
TO
61 'contact_id',
62 'contact_type',
63 'contact_sub_type',
64 'sort_name',
65 'display_name',
66 'activity_id',
67 'activity_date_time',
68 'activity_status_id',
69 'activity_status',
70 'activity_subject',
6a488035 71 'source_record_id',
6a488035
TO
72 'activity_type_id',
73 'activity_type',
74 'activity_is_test',
75 'activity_campaign_id',
76 'activity_engagement_level',
be2fb01f 77 ];
6a488035
TO
78
79 /**
fe482240 80 * Are we restricting ourselves to a single contact.
6a488035 81 *
d51c6add 82 * @var bool
6a488035
TO
83 */
84 protected $_single = FALSE;
85
86 /**
fe482240 87 * Are we restricting ourselves to a single contact.
6a488035 88 *
d51c6add 89 * @var bool
6a488035
TO
90 */
91 protected $_limit = NULL;
92
93 /**
fe482240 94 * What context are we being invoked from.
6a488035 95 *
6a488035
TO
96 * @var string
97 */
98 protected $_context = NULL;
99
100 /**
fe482240 101 * What component context are we being invoked from.
6a488035 102 *
6a488035
TO
103 * @var string
104 */
105 protected $_compContext = NULL;
106
107 /**
fe482240 108 * QueryParams is the array returned by exportValues called on.
6a488035
TO
109 * the HTML_QuickForm_Controller for that page.
110 *
111 * @var array
6a488035
TO
112 */
113 public $_queryParams;
114
115 /**
fe482240 116 * Represent the type of selector.
6a488035
TO
117 *
118 * @var int
6a488035
TO
119 */
120 protected $_action;
121
122 /**
fe482240 123 * The additional clause that we restrict the search with.
6a488035
TO
124 *
125 * @var string
126 */
127 protected $_activityClause = NULL;
128
129 /**
fe482240 130 * The query object.
6a488035 131 *
c2a377b1 132 * @var \CRM_Contact_BAO_Query
6a488035
TO
133 */
134 protected $_query;
135
136 /**
fe482240 137 * Class constructor.
6a488035 138 *
041ab3d1
TO
139 * @param array $queryParams
140 * Array of parameters for query.
dd244018 141 * @param \const|int $action - action of search basic or advanced.
041ab3d1
TO
142 * @param string $activityClause
143 * If the caller wants to further restrict the search (used in activities).
144 * @param bool $single
145 * Are we dealing only with one contact?.
146 * @param int $limit
147 * How many activities do we want returned.
6a488035 148 *
dd244018
EM
149 * @param string $context
150 * @param null $compContext
151 *
152 * @return \CRM_Activity_Selector_Search
6a488035 153 */
2da40d21 154 public function __construct(
9d5494f7 155 &$queryParams,
32864ccf 156 $action = CRM_Core_Action::NONE,
6a488035 157 $activityClause = NULL,
32864ccf
TO
158 $single = FALSE,
159 $limit = NULL,
160 $context = 'search',
161 $compContext = NULL
6a488035
TO
162 ) {
163 // submitted form values
164 $this->_queryParams = &$queryParams;
165
353ffa53
TO
166 $this->_single = $single;
167 $this->_limit = $limit;
168 $this->_context = $context;
6a488035
TO
169 $this->_compContext = $compContext;
170
171 $this->_activityClause = $activityClause;
172
87767abb 173 // CRM-12675
2f9320ea 174 $components = CRM_Core_Component::getNames();
be2fb01f 175 $componentClause = [];
2f9320ea 176 foreach ($components as $componentID => $componentName) {
f8f5515a
BS
177 // CRM-19201: Add support for searching CiviCampaign and CiviCase
178 // activities. For CiviCase, "access all cases and activities" is
179 // required here rather than "access my cases and activities" to
180 // prevent those with only the later permission from seeing a list
181 // of all cases which might present a privacy issue.
c2a377b1 182 // @todo this is the cause of the current devastatingly bad performance on
183 // activity search - it involves a bad join.
184 // The correct fix is to use the permission infrastrucutre - ie. add in the
185 // clause generated by CRM_Activity_BAO_Query::addSelectWhere
186 // but some testing needs to check that before making the change
187 // see https://github.com/civicrm/civicrm-core/blob/be2fb01f90f5f299dd07402a41fed7c7c7567f00/CRM/Utils/SQL.php#L48
188 // for how it's done in the api kernel.
44d17ec8 189 if (!CRM_Core_Permission::access($componentName, TRUE, TRUE)) {
2f9320ea 190 $componentClause[] = " (activity_type.component_id IS NULL OR activity_type.component_id <> {$componentID}) ";
87767abb 191 }
192 }
193
2f9320ea 194 if (!empty($componentClause)) {
195 $componentRestriction = implode(' AND ', $componentClause);
945c2048 196 if (empty($this->_activityClause)) {
197 $this->_activityClause = $componentRestriction;
198 }
199 else {
200 $this->_activityClause .= ' AND ' . $componentRestriction;
201 }
2f9320ea 202 }
203
6a488035
TO
204 // type of selector
205 $this->_action = $action;
206 $this->_query = new CRM_Contact_BAO_Query($this->_queryParams,
7bd16b05 207 CRM_Activity_BAO_Query::selectorReturnProperties(),
6a488035
TO
208 NULL, FALSE, FALSE,
209 CRM_Contact_BAO_Query::MODE_ACTIVITY
210 );
211 $this->_query->_distinctComponentClause = '( civicrm_activity.id )';
212 $this->_query->_groupByComponentClause = " GROUP BY civicrm_activity.id ";
6a488035 213 }
6a488035
TO
214
215 /**
100fef9d 216 * Getter for array of the parameters required for creating pager.
6a488035 217 *
da6b46f4 218 * @param $action
c490a46a 219 * @param array $params
6a488035 220 */
00be9182 221 public function getPagerParams($action, &$params) {
6a488035
TO
222 $params['status'] = ts('Activities %%StatusMessage%%');
223 $params['csvString'] = NULL;
224 $params['rowCount'] = CRM_Utils_Pager::ROWCOUNT;
225 $params['buttonTop'] = 'PagerTopButton';
226 $params['buttonBottom'] = 'PagerBottomButton';
227 }
6a488035
TO
228
229 /**
230 * Returns total number of rows for the query.
231 *
2e2605fe 232 * @param string $action
6a488035 233 *
a6c01b45
CW
234 * @return int
235 * Total number of rows
6a488035 236 */
00be9182 237 public function getTotalCount($action) {
6a488035
TO
238 return $this->_query->searchQuery(0, 0, NULL,
239 TRUE, FALSE,
240 FALSE, FALSE,
241 FALSE,
242 $this->_activityClause
243 );
244 }
245
246 /**
2e2605fe 247 * Returns all the rows in the given offset and rowCount.
6a488035 248 *
3f8d2862 249 * @param string $action
041ab3d1
TO
250 * The action being performed.
251 * @param int $offset
252 * The row number to start from.
253 * @param int $rowCount
254 * The number of rows to return.
255 * @param string $sort
256 * The sql string that describes the sort order.
3f8d2862 257 * @param string $output
041ab3d1 258 * What should the result set include (web/email/csv).
6a488035 259 *
a6c01b45
CW
260 * @return array
261 * rows in the given offset and rowCount
6a488035 262 */
00be9182 263 public function &getRows($action, $offset, $rowCount, $sort, $output = NULL) {
f04255e4
DL
264 $result = $this->_query->searchQuery(
265 $offset, $rowCount, $sort,
6a488035
TO
266 FALSE, FALSE,
267 FALSE, FALSE,
268 FALSE,
269 $this->_activityClause
270 );
be2fb01f 271 $rows = [];
353ffa53 272 $mailingIDs = CRM_Mailing_BAO_Mailing::mailingACLIDs();
6a488035
TO
273 $accessCiviMail = CRM_Core_Permission::check('access CiviMail');
274
7808aae6 275 // Get all campaigns.
6a488035
TO
276 $allCampaigns = CRM_Campaign_BAO_Campaign::getCampaigns(NULL, NULL, FALSE, FALSE, FALSE, TRUE);
277
278 $engagementLevels = CRM_Campaign_PseudoConstant::engagementLevel();
44f817d4 279 $activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate');
a24b3694 280 $sourceID = CRM_Utils_Array::key('Activity Source', $activityContacts);
281 $assigneeID = CRM_Utils_Array::key('Activity Assignees', $activityContacts);
282 $targetID = CRM_Utils_Array::key('Activity Targets', $activityContacts);
b864360d 283 $bulkActivityTypeID = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Bulk Email');
f813f78e 284
6a488035 285 while ($result->fetch()) {
be2fb01f 286 $row = [];
6a488035 287
7808aae6 288 // Ignore rows where we dont have an activity id.
6a488035
TO
289 if (empty($result->activity_id)) {
290 continue;
291 }
7bd16b05 292 $this->_query->convertToPseudoNames($result);
6a488035
TO
293
294 // the columns we are interested in
295 foreach (self::$_properties as $property) {
296 if (isset($result->$property)) {
297 $row[$property] = $result->$property;
298 }
299 }
300
301 $contactId = CRM_Utils_Array::value('contact_id', $row);
302 if (!$contactId) {
303 $contactId = CRM_Utils_Array::value('source_contact_id', $row);
304 }
305
353ffa53 306 $row['target_contact_name'] = CRM_Activity_BAO_ActivityContact::getNames($row['activity_id'], $targetID);
a24b3694 307 $row['assignee_contact_name'] = CRM_Activity_BAO_ActivityContact::getNames($row['activity_id'], $assigneeID);
308 list($row['source_contact_name'], $row['source_contact_id']) = CRM_Activity_BAO_ActivityContact::getNames($row['activity_id'], $sourceID, TRUE);
f04255e4
DL
309 $row['source_contact_name'] = implode(',', array_values($row['source_contact_name']));
310 $row['source_contact_id'] = implode(',', $row['source_contact_id']);
6a488035 311
6a488035
TO
312 if ($this->_context == 'search') {
313 $row['checkbox'] = CRM_Core_Form::CB_PREFIX . $result->activity_id;
314 }
32864ccf 315 $row['contact_type'] = CRM_Contact_BAO_Contact_Utils::getImage($result->contact_sub_type ? $result->contact_sub_type : $result->contact_type, FALSE, $result->contact_id
6a488035
TO
316 );
317 $accessMailingReport = FALSE;
353ffa53 318 $activityTypeId = $row['activity_type_id'];
6a488035 319 if ($row['activity_is_test']) {
cd355351 320 $row['activity_type'] = CRM_Core_TestEntity::appendTestText($row['activity_type']);
6a488035 321 }
6a488035
TO
322 $row['mailingId'] = '';
323 if (
324 $accessCiviMail &&
325 ($mailingIDs === TRUE || in_array($result->source_record_id, $mailingIDs)) &&
326 ($bulkActivityTypeID == $activityTypeId)
327 ) {
328 $row['mailingId'] = CRM_Utils_System::url('civicrm/mailing/report',
c79f662a 329 "mid={$result->source_record_id}&reset=1&cid={$contactId}&context=activitySelector"
6a488035
TO
330 );
331 $row['recipients'] = ts('(recipients)');
332 $row['target_contact_name'] = '';
333 $row['assignee_contact_name'] = '';
334 $accessMailingReport = TRUE;
335 }
336 $activityActions = new CRM_Activity_Selector_Activity($result->contact_id, NULL);
337 $actionLinks = $activityActions->actionLinks($activityTypeId,
338 CRM_Utils_Array::value('source_record_id', $row),
339 $accessMailingReport,
340 CRM_Utils_Array::value('activity_id', $row),
341 $this->_key,
342 $this->_compContext
343 );
344 $row['action'] = CRM_Core_Action::formLink($actionLinks, NULL,
be2fb01f 345 [
6a488035
TO
346 'id' => $result->activity_id,
347 'cid' => $contactId,
348 'cxt' => $this->_context,
be2fb01f 349 ],
87dab4a4
AH
350 ts('more'),
351 FALSE,
352 'activity.selector.row',
353 'Activity',
354 $result->activity_id
6a488035
TO
355 );
356
7808aae6 357 // Carry campaign to selector.
6a488035
TO
358 $row['campaign'] = CRM_Utils_Array::value($result->activity_campaign_id, $allCampaigns);
359 $row['campaign_id'] = $result->activity_campaign_id;
360
361 if ($engagementLevel = CRM_Utils_Array::value('activity_engagement_level', $row)) {
362 $row['activity_engagement_level'] = CRM_Utils_Array::value($engagementLevel,
363 $engagementLevels, $engagementLevel
364 );
365 }
366
7808aae6 367 // Check if recurring activity.
04374d9d 368 $repeat = CRM_Core_BAO_RecurringEntity::getPositionAndCount($row['activity_id'], 'civicrm_activity');
d8786c71 369 $row['repeat'] = '';
04374d9d 370 if ($repeat) {
be2fb01f 371 $row['repeat'] = ts('Repeating (%1 of %2)', [1 => $repeat[0], 2 => $repeat[1]]);
d8786c71 372 }
6a488035
TO
373 $rows[] = $row;
374 }
375
376 return $rows;
377 }
378
379 /**
a6c01b45
CW
380 * @return array
381 * which contains an array of strings
6a488035
TO
382 */
383 public function getQILL() {
384 return $this->_query->qill();
385 }
386
387 /**
100fef9d 388 * Returns the column headers as an array of tuples:
6a488035
TO
389 * (name, sortName (key to the sort array))
390 *
041ab3d1
TO
391 * @param string $action
392 * The action being performed.
3f8d2862 393 * @param string $output
041ab3d1 394 * What should the result set include (web/email/csv).
6a488035 395 *
a6c01b45
CW
396 * @return array
397 * the column headers that need to be displayed
6a488035
TO
398 */
399 public function &getColumnHeaders($action = NULL, $output = NULL) {
400 if (!isset(self::$_columnHeaders)) {
be2fb01f
CW
401 self::$_columnHeaders = [
402 [
6a488035
TO
403 'name' => ts('Type'),
404 'sort' => 'activity_type_id',
405 'direction' => CRM_Utils_Sort::DONTCARE,
be2fb01f
CW
406 ],
407 [
6a488035 408 'name' => ts('Subject'),
5fa296f7 409 'sort' => 'activity_subject',
6a488035 410 'direction' => CRM_Utils_Sort::DONTCARE,
be2fb01f
CW
411 ],
412 [
6a488035 413 'name' => ts('Added By'),
37eb6ff9 414 'sort' => 'source_contact',
6a488035 415 'direction' => CRM_Utils_Sort::DONTCARE,
be2fb01f
CW
416 ],
417 ['name' => ts('With')],
418 ['name' => ts('Assigned')],
419 [
6a488035
TO
420 'name' => ts('Date'),
421 'sort' => 'activity_date_time',
422 'direction' => CRM_Utils_Sort::DESCENDING,
be2fb01f
CW
423 ],
424 [
6a488035 425 'name' => ts('Status'),
33a5a53d 426 'sort' => 'activity_status',
6a488035 427 'direction' => CRM_Utils_Sort::DONTCARE,
be2fb01f
CW
428 ],
429 [
6a488035 430 'desc' => ts('Actions'),
be2fb01f
CW
431 ],
432 ];
6a488035
TO
433 }
434 return self::$_columnHeaders;
435 }
436
ffd93213
EM
437 /**
438 * @return mixed
439 */
00be9182 440 public function alphabetQuery() {
52cda5dc 441 return $this->_query->alphabetQuery();
6a488035
TO
442 }
443
ffd93213 444 /**
c2a377b1 445 * @return \CRM_Contact_BAO_Query
ffd93213 446 */
00be9182 447 public function &getQuery() {
6a488035
TO
448 return $this->_query;
449 }
450
451 /**
100fef9d 452 * Name of export file.
6a488035 453 *
041ab3d1
TO
454 * @param string $output
455 * Type of output.
6a488035 456 *
a6c01b45
CW
457 * @return string
458 * name of the file
6a488035 459 */
00be9182 460 public function getExportFileName($output = 'csv') {
6a488035
TO
461 return ts('CiviCRM Activity Search');
462 }
96025800 463
6a488035 464}