Merge pull request #23689 from eileenmcnaughton/member_really_labels
[civicrm-core.git] / CRM / Contact / Selector / Custom.php
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 * $Id: Selector.php 11510 2007-09-18 09:21:34Z lobo $
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.
23 */
24 class CRM_Contact_Selector_Custom extends CRM_Contact_Selector {
25
26 /**
27 * This defines two actions- View and Edit.
28 *
29 * @var array
30 */
31 public static $_links = NULL;
32
33 /**
34 * We use desc to remind us what that column is, name is used in the tpl
35 *
36 * @var array
37 */
38 public static $_columnHeaders;
39
40 /**
41 * Properties of contact we're interested in displaying
42 * @var array
43 */
44 public static $_properties = ['contact_id', 'contact_type', 'display_name'];
45
46 /**
47 * FormValues is the array returned by exportValues called on
48 * the HTML_QuickForm_Controller for that page.
49 *
50 * @var array
51 */
52 public $_formValues;
53
54 /**
55 * Params is the array in a value used by the search query creator
56 *
57 * @var array
58 */
59 public $_params;
60
61 /**
62 * Represent the type of selector
63 *
64 * @var int
65 */
66 protected $_action;
67
68 protected $_query;
69
70 /**
71 * The public visible fields to be shown to the user
72 *
73 * @var array
74 */
75 protected $_fields;
76
77 /**
78 * The object that implements the search interface
79 * @var object
80 */
81 protected $_search;
82
83 protected $_customSearchClass;
84
85 /**
86 * Class constructor.
87 *
88 * @param $customSearchClass
89 * @param array $formValues
90 * Array of form values imported.
91 * @param array $params
92 * Array of parameters for query.
93 * @param null $returnProperties
94 * @param \const|int $action - action of search basic or advanced.
95 *
96 * @param bool $includeContactIds
97 * @param bool $searchChildGroups
98 * @param string $searchContext
99 * @param null $contextMenu
100 *
101 * @return \CRM_Contact_Selector_Custom
102 */
103 public function __construct(
104 $customSearchClass,
105 $formValues = NULL,
106 $params = NULL,
107 $returnProperties = NULL,
108 $action = CRM_Core_Action::NONE,
109 $includeContactIds = FALSE,
110 $searchChildGroups = TRUE,
111 $searchContext = 'search',
112 $contextMenu = NULL
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);
123 require_once $customSearchFile;
124 }
125 else {
126 require_once str_replace('_', DIRECTORY_SEPARATOR, $customSearchClass) . '.php';
127 }
128 $this->_search = new $customSearchClass($formValues);
129 }
130 else {
131 $fnName = $ext->keyToPath;
132 $customSearchFile = $fnName($customSearchClass, 'search');
133 $className = $ext->keyToClass($customSearchClass, 'search');
134 $this->_search = new $className($formValues);
135 }
136 }
137
138 /**
139 * This method returns the links that are given for each search row.
140 *
141 * Currently the links added for each row are
142 * - View
143 * - Edit
144 *
145 * @return array
146 */
147 public static function &links() {
148 list($key) = func_get_args();
149 $searchContext = "&context=custom";
150 $extraParams = ($key) ? "&key={$key}" : NULL;
151
152 if (!(self::$_links)) {
153 self::$_links = [
154 CRM_Core_Action::VIEW => [
155 'name' => ts('View'),
156 'url' => 'civicrm/contact/view',
157 'qs' => "reset=1&cid=%%id%%{$extraParams}{$searchContext}",
158 'class' => 'no-popup',
159 'title' => ts('View Contact Details'),
160 ],
161 CRM_Core_Action::UPDATE => [
162 'name' => ts('Edit'),
163 'url' => 'civicrm/contact/add',
164 'qs' => 'reset=1&action=update&cid=%%id%%',
165 'class' => 'no-popup',
166 'title' => ts('Edit Contact Details'),
167 ],
168 ];
169
170 $config = CRM_Core_Config::singleton();
171 //CRM-16552: mapAPIKey is not mandatory as google no longer requires an API Key
172 if ($config->mapProvider && ($config->mapAPIKey || $config->mapProvider == 'Google')) {
173 self::$_links[CRM_Core_Action::MAP] = [
174 'name' => ts('Map'),
175 'url' => 'civicrm/contact/map',
176 'qs' => 'reset=1&cid=%%id%%&searchType=custom',
177 'class' => 'no-popup',
178 'title' => ts('Map Contact'),
179 ];
180 }
181 }
182 return self::$_links;
183 }
184
185 /**
186 * Getter for array of the parameters required for creating pager.
187 *
188 * @param $action
189 * @param array $params
190 */
191 public function getPagerParams($action, &$params) {
192 $params['status'] = ts('Contact %%StatusMessage%%');
193 $params['csvString'] = NULL;
194 $params['rowCount'] = Civi::settings()->get('default_pager_size');
195
196 $params['buttonTop'] = 'PagerTopButton';
197 $params['buttonBottom'] = 'PagerBottomButton';
198 }
199
200 /**
201 * Returns the column headers as an array of tuples.
202 *
203 * Keys are name, sortName, key to the sort array.
204 *
205 * @param string $action
206 * The action being performed.
207 * @param string $output
208 * What should the result set include (web/email/csv).
209 *
210 * @return array
211 * the column headers that need to be displayed
212 */
213 public function &getColumnHeaders($action = NULL, $output = NULL) {
214 $columns = $this->_search->columns();
215 $headers = [];
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;
221 }
222 else {
223 foreach ($columns as $name => $key) {
224 if (!empty($name)) {
225 $headers[] = [
226 'name' => $name,
227 'sort' => $key,
228 'direction' => CRM_Utils_Sort::ASCENDING,
229 ];
230 }
231 else {
232 $headers[] = [];
233 }
234 }
235 return $headers;
236 }
237 }
238
239 /**
240 * Returns total number of rows for the query.
241 *
242 * @param null $action
243 *
244 * @return int
245 * Total number of rows
246 */
247 public function getTotalCount($action) {
248 return $this->_search->count();
249 }
250
251 /**
252 * Returns all the rows in the given offset and rowCount.
253 *
254 * @param string $action
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.
262 * @param string $output
263 * What should the result set include (web/email/csv).
264 *
265 * @return int
266 * the total number of rows for this action
267 */
268 public function &getRows($action, $offset, $rowCount, $sort, $output = NULL) {
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);
280 // contact query object used for creating $sql
281 $contactQueryObj = NULL;
282 if (method_exists($this->_search, 'getQueryObj') &&
283 is_a($this->_search->getQueryObj(), 'CRM_Contact_BAO_Query')
284 ) {
285 $contactQueryObj = $this->_search->getQueryObj();
286 }
287
288 $dao = CRM_Core_DAO::executeQuery($sql);
289
290 $columns = $this->_search->columns();
291 $columnNames = array_values($columns);
292 $links = self::links($this->_key);
293
294 $permissions = [CRM_Core_Permission::getPermission()];
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,
302 'alterRow'
303 )) {
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
311 $rows = [];
312 while ($dao->fetch()) {
313 $row = [];
314 $empty = TRUE;
315
316 // if contact query object present
317 // process pseudo constants
318 if ($contactQueryObj) {
319 $contactQueryObj->convertToPseudoNames($dao);
320 }
321
322 // the columns we are interested in
323 foreach ($columnNames as $property) {
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)) {
328 $empty = FALSE;
329 }
330 }
331 if (!$empty) {
332 $contactID = $dao->contact_id ?? NULL;
333
334 $row['checkbox'] = CRM_Core_Form::CB_PREFIX . $contactID;
335 $row['action'] = CRM_Core_Action::formLink($links,
336 $mask,
337 ['id' => $contactID],
338 ts('more'),
339 FALSE,
340 'contact.custom.actions',
341 'Contact',
342 $contactID
343 );
344 $row['contact_id'] = $contactID;
345
346 if ($alterRow) {
347 $this->_search->alterRow($row);
348 }
349
350 if ($image) {
351 $row['contact_type'] = CRM_Contact_BAO_Contact_Utils::getImage($dao->contact_sub_type ? $dao->contact_sub_type : $dao->contact_type, FALSE, $contactID
352 );
353 }
354 $rows[] = $row;
355 }
356 }
357
358 $this->buildPrevNextCache($sort);
359
360 return $rows;
361 }
362
363 /**
364 * Given the current formValues, gets the query in local language.
365 *
366 * @return array
367 * which contains an array of strings
368 */
369 public function getQILL() {
370 return NULL;
371 }
372
373 /**
374 * Get summary.
375 *
376 * @return mixed
377 */
378 public function getSummary() {
379 return $this->_search->summary();
380 }
381
382 /**
383 * Name of export file.
384 *
385 * @param string $output
386 * Type of output.
387 *
388 * @return string
389 * name of the file
390 */
391 public function getExportFileName($output = 'csv') {
392 return ts('CiviCRM Custom Search');
393 }
394
395 /**
396 * Do nothing.
397 *
398 * @return null
399 */
400 public function alphabetQuery() {
401 return NULL;
402 }
403
404 /**
405 * Generate contact ID query.
406 *
407 * @param array $params
408 * @param $action
409 * @param int $sortID
410 * @param null $displayRelationshipType
411 * @param string $queryOperator
412 *
413 * @return Object
414 */
415 public function contactIDQuery($params, $sortID, $displayRelationshipType = NULL, $queryOperator = 'AND') {
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:
420 $matches = [];
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 }
431
432 $sql = $this->_search->contactIDs(0, 0, $sort);
433 return CRM_Core_DAO::executeQuery($sql);
434 }
435
436 /**
437 * Add actions.
438 *
439 * @param array $rows
440 */
441 public function addActions(&$rows) {
442 $links = self::links($this->_key);
443
444 $permissions = [CRM_Core_Permission::getPermission()];
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,
453 ['id' => $row['contact_id']],
454 ts('more'),
455 FALSE,
456 'contact.custom.actions',
457 'Contact',
458 $row['contact_id']
459 );
460 }
461 }
462
463 /**
464 * Remove actions.
465 *
466 * @param array $rows
467 */
468 public function removeActions(&$rows) {
469 foreach ($rows as $rid => & $rValue) {
470 unset($rValue['action']);
471 }
472 }
473
474 }