Fixes for contact matching
[civicrm-core.git] / CRM / Contact / Selector / Custom.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
bc77d7c0 4 | Copyright CiviCRM LLC. All rights reserved. |
6a488035 5 | |
bc77d7c0
TO
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 |
6a488035 9 +--------------------------------------------------------------------+
d25dd0ee 10 */
6a488035
TO
11
12/**
13 *
14 * @package CRM
ca5cec67 15 * @copyright CiviCRM LLC https://civicrm.org/licensing
6a488035 16 * $Id: Selector.php 11510 2007-09-18 09:21:34Z lobo $
6a488035
TO
17 */
18
19/**
20 * This class is used to retrieve and display a range of
21 * contacts that match the given criteria (specifically for
22 * results of advanced search options.
6a488035
TO
23 */
24class CRM_Contact_Selector_Custom extends CRM_Contact_Selector {
25
26 /**
27 * This defines two actions- View and Edit.
28 *
29 * @var array
6a488035 30 */
69078420 31 public static $_links = NULL;
6a488035
TO
32
33 /**
100fef9d 34 * We use desc to remind us what that column is, name is used in the tpl
6a488035
TO
35 *
36 * @var array
6a488035 37 */
69078420 38 public static $_columnHeaders;
6a488035
TO
39
40 /**
41 * Properties of contact we're interested in displaying
42 * @var array
6a488035 43 */
69078420 44 public static $_properties = ['contact_id', 'contact_type', 'display_name'];
6a488035
TO
45
46 /**
100fef9d 47 * FormValues is the array returned by exportValues called on
6a488035
TO
48 * the HTML_QuickForm_Controller for that page.
49 *
50 * @var array
6a488035
TO
51 */
52 public $_formValues;
53
54 /**
100fef9d 55 * Params is the array in a value used by the search query creator
6a488035
TO
56 *
57 * @var array
6a488035
TO
58 */
59 public $_params;
60
61 /**
100fef9d 62 * Represent the type of selector
6a488035
TO
63 *
64 * @var int
6a488035
TO
65 */
66 protected $_action;
67
68 protected $_query;
69
70 /**
100fef9d 71 * The public visible fields to be shown to the user
6a488035
TO
72 *
73 * @var array
6a488035
TO
74 */
75 protected $_fields;
76
77 /**
78 * The object that implements the search interface
69078420 79 * @var object
6a488035
TO
80 */
81 protected $_search;
82
83 protected $_customSearchClass;
84
85 /**
5c9ff055 86 * Class constructor.
6a488035 87 *
6c8f6e67 88 * @param $customSearchClass
77c5b619
TO
89 * @param array $formValues
90 * Array of form values imported.
91 * @param array $params
92 * Array of parameters for query.
6c8f6e67
EM
93 * @param null $returnProperties
94 * @param \const|int $action - action of search basic or advanced.
6a488035 95 *
6c8f6e67
EM
96 * @param bool $includeContactIds
97 * @param bool $searchChildGroups
98 * @param string $searchContext
99 * @param null $contextMenu
100 *
101 * @return \CRM_Contact_Selector_Custom
6a488035 102 */
db7de9c1 103 public function __construct(
6a488035 104 $customSearchClass,
ce80b209
TO
105 $formValues = NULL,
106 $params = NULL,
107 $returnProperties = NULL,
108 $action = CRM_Core_Action::NONE,
6a488035
TO
109 $includeContactIds = FALSE,
110 $searchChildGroups = TRUE,
ce80b209
TO
111 $searchContext = 'search',
112 $contextMenu = NULL
6a488035
TO
113 ) {
114 $this->_customSearchClass = $customSearchClass;
115 $this->_formValues = $formValues;
116 $this->_includeContactIds = $includeContactIds;
117
118 $ext = CRM_Extension_System::singleton()->getMapper();
119
120 if (!$ext->isExtensionKey($customSearchClass)) {
121 if ($ext->isExtensionClass($customSearchClass)) {
122 $customSearchFile = $ext->classToPath($customSearchClass);
ce80b209 123 require_once $customSearchFile;
6a488035
TO
124 }
125 else {
ce80b209 126 require_once str_replace('_', DIRECTORY_SEPARATOR, $customSearchClass) . '.php';
6a488035 127 }
481a74f4 128 $this->_search = new $customSearchClass($formValues);
6a488035
TO
129 }
130 else {
c7b8b4e4
DL
131 $fnName = $ext->keyToPath;
132 $customSearchFile = $fnName($customSearchClass, 'search');
133 $className = $ext->keyToClass($customSearchClass, 'search');
134 $this->_search = new $className($formValues);
6a488035
TO
135 }
136 }
6a488035
TO
137
138 /**
139 * This method returns the links that are given for each search row.
6a488035 140 *
db7de9c1 141 * Currently the links added for each row are
6a488035
TO
142 * - View
143 * - Edit
144 *
145 * @return array
6a488035 146 */
00be9182 147 public static function &links() {
7c34ab11 148 list($key) = func_get_args();
119c08a8 149 $searchContext = "&context=custom";
7c34ab11 150 $extraParams = ($key) ? "&key={$key}" : NULL;
151
6a488035 152 if (!(self::$_links)) {
be2fb01f
CW
153 self::$_links = [
154 CRM_Core_Action::VIEW => [
6a488035
TO
155 'name' => ts('View'),
156 'url' => 'civicrm/contact/view',
119c08a8 157 'qs' => "reset=1&cid=%%id%%{$extraParams}{$searchContext}",
d3fcd52f 158 'class' => 'no-popup',
6a488035 159 'title' => ts('View Contact Details'),
be2fb01f
CW
160 ],
161 CRM_Core_Action::UPDATE => [
6a488035
TO
162 'name' => ts('Edit'),
163 'url' => 'civicrm/contact/add',
164 'qs' => 'reset=1&action=update&cid=%%id%%',
d3fcd52f 165 'class' => 'no-popup',
6a488035 166 'title' => ts('Edit Contact Details'),
be2fb01f
CW
167 ],
168 ];
6a488035
TO
169
170 $config = CRM_Core_Config::singleton();
030c6f6b
SB
171 //CRM-16552: mapAPIKey is not mandatory as google no longer requires an API Key
172 if ($config->mapProvider && ($config->mapAPIKey || $config->mapProvider == 'Google')) {
be2fb01f 173 self::$_links[CRM_Core_Action::MAP] = [
6a488035
TO
174 'name' => ts('Map'),
175 'url' => 'civicrm/contact/map',
176 'qs' => 'reset=1&cid=%%id%%&searchType=custom',
d3fcd52f 177 'class' => 'no-popup',
6a488035 178 'title' => ts('Map Contact'),
be2fb01f 179 ];
6a488035
TO
180 }
181 }
182 return self::$_links;
183 }
6a488035
TO
184
185 /**
100fef9d 186 * Getter for array of the parameters required for creating pager.
6a488035 187 *
da6b46f4 188 * @param $action
c490a46a 189 * @param array $params
6a488035 190 */
00be9182 191 public function getPagerParams($action, &$params) {
353ffa53 192 $params['status'] = ts('Contact %%StatusMessage%%');
6a488035 193 $params['csvString'] = NULL;
7fa7c084 194 $params['rowCount'] = Civi::settings()->get('default_pager_size');
6a488035
TO
195
196 $params['buttonTop'] = 'PagerTopButton';
197 $params['buttonBottom'] = 'PagerBottomButton';
198 }
6a488035
TO
199
200 /**
db7de9c1
EM
201 * Returns the column headers as an array of tuples.
202 *
203 * Keys are name, sortName, key to the sort array.
6a488035 204 *
77c5b619
TO
205 * @param string $action
206 * The action being performed.
3f8d2862 207 * @param string $output
77c5b619 208 * What should the result set include (web/email/csv).
6a488035 209 *
a6c01b45
CW
210 * @return array
211 * the column headers that need to be displayed
6a488035 212 */
00be9182 213 public function &getColumnHeaders($action = NULL, $output = NULL) {
6a488035 214 $columns = $this->_search->columns();
be2fb01f 215 $headers = [];
9f05e0a7 216 if ($output == CRM_Core_Selector_Controller::EXPORT || $output == CRM_Core_Selector_Controller::SCREEN) {
217 foreach ($columns as $name => $key) {
218 $headers[$key] = $name;
219 }
220 return $headers;
6a488035
TO
221 }
222 else {
6a488035
TO
223 foreach ($columns as $name => $key) {
224 if (!empty($name)) {
be2fb01f 225 $headers[] = [
6a488035
TO
226 'name' => $name,
227 'sort' => $key,
228 'direction' => CRM_Utils_Sort::ASCENDING,
be2fb01f 229 ];
6a488035
TO
230 }
231 else {
be2fb01f 232 $headers[] = [];
6a488035
TO
233 }
234 }
235 return $headers;
236 }
237 }
238
239 /**
240 * Returns total number of rows for the query.
241 *
ee0ce2ef 242 * @param null $action
6a488035 243 *
a6c01b45
CW
244 * @return int
245 * Total number of rows
6a488035 246 */
00be9182 247 public function getTotalCount($action) {
6a488035
TO
248 return $this->_search->count();
249 }
250
251 /**
d8689418 252 * Returns all the rows in the given offset and rowCount.
6a488035 253 *
3f8d2862 254 * @param string $action
77c5b619
TO
255 * The action being performed.
256 * @param int $offset
257 * The row number to start from.
258 * @param int $rowCount
259 * The number of rows to return.
260 * @param string $sort
261 * The sql string that describes the sort order.
3f8d2862 262 * @param string $output
77c5b619 263 * What should the result set include (web/email/csv).
6a488035 264 *
a6c01b45
CW
265 * @return int
266 * the total number of rows for this action
6a488035 267 */
00be9182 268 public function &getRows($action, $offset, $rowCount, $sort, $output = NULL) {
6a488035
TO
269
270 $includeContactIDs = FALSE;
271 if (($output == CRM_Core_Selector_Controller::EXPORT ||
272 $output == CRM_Core_Selector_Controller::SCREEN
273 ) &&
274 $this->_formValues['radio_ts'] == 'ts_sel'
275 ) {
276 $includeContactIDs = TRUE;
277 }
278
279 $sql = $this->_search->all($offset, $rowCount, $sort, $includeContactIDs);
d9ab802d
PJ
280 // contact query object used for creating $sql
281 $contactQueryObj = NULL;
282 if (method_exists($this->_search, 'getQueryObj') &&
353ffa53
TO
283 is_a($this->_search->getQueryObj(), 'CRM_Contact_BAO_Query')
284 ) {
d9ab802d
PJ
285 $contactQueryObj = $this->_search->getQueryObj();
286 }
6a488035 287
8b0533f0 288 $dao = CRM_Core_DAO::executeQuery($sql);
6a488035 289
353ffa53 290 $columns = $this->_search->columns();
6a488035 291 $columnNames = array_values($columns);
353ffa53 292 $links = self::links($this->_key);
6a488035 293
be2fb01f 294 $permissions = [CRM_Core_Permission::getPermission()];
6a488035
TO
295 if (CRM_Core_Permission::check('delete contacts')) {
296 $permissions[] = CRM_Core_Permission::DELETE;
297 }
298 $mask = CRM_Core_Action::mask($permissions);
299
300 $alterRow = FALSE;
301 if (method_exists($this->_customSearchClass,
353ffa53
TO
302 'alterRow'
303 )) {
6a488035
TO
304 $alterRow = TRUE;
305 }
306 $image = FALSE;
307 if (is_a($this->_search, 'CRM_Contact_Form_Search_Custom_Basic')) {
308 $image = TRUE;
309 }
310 // process the result of the query
be2fb01f 311 $rows = [];
6a488035 312 while ($dao->fetch()) {
be2fb01f 313 $row = [];
6a488035
TO
314 $empty = TRUE;
315
d9ab802d
PJ
316 // if contact query object present
317 // process pseudo constants
318 if ($contactQueryObj) {
319 $contactQueryObj->convertToPseudoNames($dao);
320 }
321
6a488035
TO
322 // the columns we are interested in
323 foreach ($columnNames as $property) {
37990ecd
JV
324 // Get part of name after last . (if any)
325 $unqualified_property = CRM_Utils_Array::First(array_slice(explode('.', $property), -1));
326 $row[$property] = $dao->$unqualified_property;
327 if (!empty($dao->$unqualified_property)) {
6a488035
TO
328 $empty = FALSE;
329 }
330 }
331 if (!$empty) {
2e1f50d6 332 $contactID = $dao->contact_id ?? NULL;
6a488035
TO
333
334 $row['checkbox'] = CRM_Core_Form::CB_PREFIX . $contactID;
335 $row['action'] = CRM_Core_Action::formLink($links,
336 $mask,
be2fb01f 337 ['id' => $contactID],
87dab4a4
AH
338 ts('more'),
339 FALSE,
340 'contact.custom.actions',
341 'Contact',
342 $contactID
6a488035
TO
343 );
344 $row['contact_id'] = $contactID;
345
346 if ($alterRow) {
347 $this->_search->alterRow($row);
348 }
349
350 if ($image) {
ce80b209 351 $row['contact_type'] = CRM_Contact_BAO_Contact_Utils::getImage($dao->contact_sub_type ? $dao->contact_sub_type : $dao->contact_type, FALSE, $contactID
6a488035
TO
352 );
353 }
354 $rows[] = $row;
355 }
356 }
357
358 $this->buildPrevNextCache($sort);
359
360 return $rows;
361 }
362
363 /**
d8689418 364 * Given the current formValues, gets the query in local language.
6a488035 365 *
a6c01b45
CW
366 * @return array
367 * which contains an array of strings
6a488035
TO
368 */
369 public function getQILL() {
370 return NULL;
371 }
372
86538308 373 /**
5c9ff055
EM
374 * Get summary.
375 *
86538308
EM
376 * @return mixed
377 */
6a488035
TO
378 public function getSummary() {
379 return $this->_search->summary();
380 }
381
382 /**
100fef9d 383 * Name of export file.
6a488035 384 *
77c5b619
TO
385 * @param string $output
386 * Type of output.
6a488035 387 *
a6c01b45
CW
388 * @return string
389 * name of the file
6a488035 390 */
00be9182 391 public function getExportFileName($output = 'csv') {
6a488035
TO
392 return ts('CiviCRM Custom Search');
393 }
394
86538308 395 /**
5c9ff055
EM
396 * Do nothing.
397 *
86538308
EM
398 * @return null
399 */
00be9182 400 public function alphabetQuery() {
6a488035
TO
401 return NULL;
402 }
403
86538308 404 /**
0965e988
EM
405 * Generate contact ID query.
406 *
c490a46a 407 * @param array $params
86538308 408 * @param $action
100fef9d 409 * @param int $sortID
86538308
EM
410 * @param null $displayRelationshipType
411 * @param string $queryOperator
412 *
413 * @return Object
414 */
ae066a2b 415 public function contactIDQuery($params, $sortID, $displayRelationshipType = NULL, $queryOperator = 'AND') {
f57a5178
JV
416 // $action, $displayRelationshipType and $queryOperator are unused. I have
417 // no idea why they are there.
418
419 // I wonder whether there is some helper function for this:
be2fb01f 420 $matches = [];
f57a5178
JV
421 if (preg_match('/([0-9]*)(_(u|d))?/', $sortID, $matches)) {
422 $columns = array_values($this->_search->columns());
423 $sort = $columns[$matches[1] - 1];
424 if (array_key_exists(3, $matches) && $matches[3] == 'd') {
425 $sort .= " DESC";
426 }
427 }
428 else {
429 $sort = NULL;
430 }
6a488035 431
f57a5178
JV
432 $sql = $this->_search->contactIDs(0, 0, $sort);
433 return CRM_Core_DAO::executeQuery($sql);
6a488035
TO
434 }
435
86538308 436 /**
0965e988
EM
437 * Add actions.
438 *
439 * @param array $rows
86538308 440 */
00be9182 441 public function addActions(&$rows) {
7c34ab11 442 $links = self::links($this->_key);
6a488035 443
be2fb01f 444 $permissions = [CRM_Core_Permission::getPermission()];
6a488035
TO
445 if (CRM_Core_Permission::check('delete contacts')) {
446 $permissions[] = CRM_Core_Permission::DELETE;
447 }
448 $mask = CRM_Core_Action::mask($permissions);
449
450 foreach ($rows as $id => & $row) {
451 $row['action'] = CRM_Core_Action::formLink($links,
452 $mask,
be2fb01f 453 ['id' => $row['contact_id']],
87dab4a4
AH
454 ts('more'),
455 FALSE,
456 'contact.custom.actions',
457 'Contact',
458 $row['contact_id']
6a488035
TO
459 );
460 }
461 }
462
86538308 463 /**
0965e988
EM
464 * Remove actions.
465 *
466 * @param array $rows
86538308 467 */
00be9182 468 public function removeActions(&$rows) {
6a488035
TO
469 foreach ($rows as $rid => & $rValue) {
470 unset($rValue['action']);
471 }
472 }
96025800 473
6a488035 474}