Merge pull request #15699 from mattwire/participant_cleanup_completeOrderPBRef
[civicrm-core.git] / CRM / Profile / Page / Listings.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 5 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2020 |
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 |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
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 |
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 +--------------------------------------------------------------------+
26 */
27
28 /**
29 *
30 * @package CRM
31 * @copyright CiviCRM LLC (c) 2004-2020
32 *
33 */
34
35 /**
36 * This implements the profile page for all contacts. It uses a selector
37 * object to do the actual dispay. The fields displayd are controlled by
38 * the admin.
39 */
40 class CRM_Profile_Page_Listings extends CRM_Core_Page {
41
42 /**
43 * All the fields that are listings related.
44 *
45 * @var array
46 */
47 protected $_fields;
48
49 /**
50 * The custom fields for this domain.
51 *
52 * @var array
53 */
54 protected $_customFields;
55
56 /**
57 * The input params from the request.
58 *
59 * @var array
60 */
61 protected $_params;
62
63 /**
64 * The group id that we are editing.
65 *
66 * @var int
67 */
68 protected $_gid;
69
70 /**
71 * State whether to display search form or not.
72 *
73 * @var int
74 */
75 protected $_search;
76
77 /**
78 * Should we display a map.
79 *
80 * @var int
81 */
82 protected $_map;
83
84 /**
85 * Store profile ids if multiple profile ids are passed using comma separated.
86 * Currently lets implement this functionality only for dialog mode
87 * @var array
88 */
89 protected $_profileIds = [];
90
91 /**
92 * Extracts the parameters from the request and constructs information for
93 * the selector object to do a query
94 *
95 */
96 public function preProcess() {
97
98 $this->_search = TRUE;
99
100 $search = CRM_Utils_Request::retrieve('search', 'Boolean', $this, FALSE, 0, 'GET');
101 if (isset($search) && $search == 0) {
102 $this->_search = FALSE;
103 }
104
105 $this->_gid = $this->get('gid');
106 $this->_profileIds = $this->get('profileIds');
107
108 $gids = explode(',', CRM_Utils_Request::retrieve('gid', 'String', CRM_Core_DAO::$_nullObject, FALSE, 0, 'GET'));
109
110 if ((count($gids) > 1) && !$this->_profileIds && empty($this->_profileIds)) {
111 if (!empty($gids)) {
112 foreach ($gids as $pfId) {
113 $this->_profileIds[] = CRM_Utils_Type::escape($pfId, 'Positive');
114 }
115 }
116
117 // check if we are rendering mixed profiles
118 if (CRM_Core_BAO_UFGroup::checkForMixProfiles($this->_profileIds)) {
119 CRM_Core_Error::fatal(ts('You cannot combine profiles of multiple types.'));
120 }
121
122 $this->_gid = $this->_profileIds[0];
123 $this->set('profileIds', $this->_profileIds);
124 $this->set('gid', $this->_gid);
125 }
126
127 if (!$this->_gid) {
128 $this->_gid = CRM_Utils_Request::retrieve('gid', 'Positive', $this, FALSE, 0, 'GET');
129 }
130
131 if (empty($this->_profileIds)) {
132 $gids = $this->_gid;
133 }
134 else {
135 $gids = $this->_profileIds;
136 }
137
138 $this->_fields = CRM_Core_BAO_UFGroup::getListingFields(CRM_Core_Action::UPDATE,
139 CRM_Core_BAO_UFGroup::PUBLIC_VISIBILITY | CRM_Core_BAO_UFGroup::LISTINGS_VISIBILITY,
140 FALSE, $gids, FALSE, 'Profile',
141 CRM_Core_Permission::SEARCH
142 );
143
144 $this->_customFields = CRM_Core_BAO_CustomField::getFieldsForImport(NULL, FALSE, FALSE, FALSE, TRUE, TRUE);
145 $this->_params = [];
146
147 $resetArray = [
148 'group',
149 'tag',
150 'preferred_communication_method',
151 'do_not_phone',
152 'do_not_email',
153 'do_not_mail',
154 'do_not_sms',
155 'do_not_trade',
156 'gender',
157 ];
158
159 foreach ($this->_fields as $name => $field) {
160 if ((substr($name, 0, 6) == 'custom') && !empty($field['is_search_range'])) {
161 $from = CRM_Utils_Request::retrieve($name . '_from', 'String', $this);
162 $to = CRM_Utils_Request::retrieve($name . '_to', 'String', $this);
163 $value = [];
164 if ($from && $to) {
165 $value[$name . '_from'] = $from;
166 $value[$name . '_to'] = $to;
167 }
168 elseif ($from) {
169 $value[$name . '_from'] = $from;
170 }
171 elseif ($to) {
172 $value[$name . '_to'] = $to;
173 }
174 }
175 elseif ((substr($name, 0, 7) == 'custom_') &&
176 (CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomField',
177 substr($name, 7), 'html_type'
178 ) == 'TextArea')
179 ) {
180 $value = trim(CRM_Utils_Request::retrieve($name, 'String',
181 $this, FALSE, NULL, 'REQUEST'
182 ));
183 if (!empty($value) &&
184 !((substr($value, 0, 1) == '%') &&
185 (substr($value, -1, 1) == '%')
186 )
187 ) {
188 $value = '%' . $value . '%';
189 }
190 }
191 elseif (CRM_Utils_Array::value('html_type', $field) == 'Multi-Select State/Province'
192 || CRM_Utils_Array::value('html_type', $field) == 'Multi-Select Country'
193 ) {
194 $value = CRM_Utils_Request::retrieve($name, 'String', $this, FALSE, NULL, 'REQUEST');
195 if (!is_array($value)) {
196 $value = explode(CRM_Core_DAO::VALUE_SEPARATOR, substr($value, 1, -1));
197 }
198 }
199 elseif ($name == 'contact_sub_type') {
200 $v = CRM_Utils_Request::retrieve($name, 'String', $this, FALSE, NULL, 'REQUEST');
201 if ($v && !is_array($v)) {
202 $v = explode(CRM_Core_DAO::VALUE_SEPARATOR, trim($v, CRM_Core_DAO::VALUE_SEPARATOR));
203 }
204 if (!empty($v)) {
205 foreach ($v as $item) {
206 $value[$item] = 1;
207 }
208 }
209 }
210 else {
211 $value = CRM_Utils_Request::retrieve($name, 'String',
212 $this, FALSE, NULL, 'REQUEST'
213 );
214 }
215
216 if (($name == 'group' || $name == 'tag') && !empty($value) && !is_array($value)) {
217 $v = explode(',', $value);
218 $value = [];
219 foreach ($v as $item) {
220 $value[$item] = 1;
221 }
222 }
223
224 $customField = CRM_Utils_Array::value($name, $this->_customFields);
225
226 if (!empty($_POST) && empty($_POST[$name])) {
227 if ($customField) {
228 // reset checkbox/radio because a form does not send null checkbox values
229 if (in_array($customField['html_type'],
230 ['Multi-Select', 'CheckBox', 'Multi-Select State/Province', 'Multi-Select Country', 'Radio', 'Select']
231 )) {
232 // only reset on a POST submission if we don't see any value
233 $value = NULL;
234 $this->set($name, $value);
235 }
236 }
237 elseif (in_array($name, $resetArray)) {
238 $value = NULL;
239 $this->set($name, $value);
240 }
241 }
242
243 if (isset($value) && $value != NULL) {
244 if (!is_array($value)) {
245 $value = trim($value);
246 }
247 $operator = CRM_Utils_Request::retrieve($name . '_operator', 'String', $this);
248 if ($operator) {
249 $this->_params[$name . '_operator'] = $operator;
250 }
251 if ((substr($name, 0, 6) == 'custom') && !empty($field['is_search_range'])) {
252 $this->_params += $value;
253 }
254 else {
255 $this->_params[$name] = $this->_fields[$name]['value'] = $value;
256 }
257 }
258 }
259
260 // set the prox params
261 // need to ensure proximity searching is enabled
262 $proximityVars = [
263 'street_address',
264 'city',
265 'postal_code',
266 'state_province_id',
267 'country_id',
268 'distance',
269 'distance_unit',
270 ];
271 foreach ($proximityVars as $var) {
272 $value = CRM_Utils_Request::retrieve("prox_{$var}",
273 'String',
274 $this, FALSE, NULL, 'REQUEST'
275 );
276 if ($value) {
277 $this->_params["prox_{$var}"] = $value;
278 }
279 }
280
281 // set the params in session
282 $session = CRM_Core_Session::singleton();
283 $session->set('profileParams', $this->_params);
284 }
285
286 /**
287 * Run this page (figure out the action needed and perform it).
288 *
289 */
290 public function run() {
291 $this->preProcess();
292
293 $this->assign('recentlyViewed', FALSE);
294 // override later (if possible):
295 $this->assign('ufGroupName', 'unknown');
296
297 if ($this->_gid) {
298 $ufgroupDAO = new CRM_Core_DAO_UFGroup();
299 $ufgroupDAO->id = $this->_gid;
300 if (!$ufgroupDAO->find(TRUE)) {
301 CRM_Core_Error::fatal();
302 }
303 }
304
305 if ($this->_gid) {
306 // set the title of the page
307 if ($ufgroupDAO->title) {
308 CRM_Utils_System::setTitle($ufgroupDAO->title);
309 }
310 if ($ufgroupDAO->name) {
311 $this->assign('ufGroupName', $ufgroupDAO->name);
312 }
313 }
314
315 $this->assign('isReset', TRUE);
316
317 $formController = new CRM_Core_Controller_Simple('CRM_Profile_Form_Search',
318 ts('Search Profile'),
319 CRM_Core_Action::ADD
320 );
321 $formController->setEmbedded(TRUE);
322 $formController->set('gid', $this->_gid);
323 $formController->process();
324
325 $searchError = FALSE;
326 // check if there is a POST
327 if (!empty($_POST)) {
328 if ($formController->validate() !== TRUE) {
329 $searchError = TRUE;
330 }
331 }
332
333 // also get the search tpl name
334 $this->assign('searchTPL', $formController->getHookedTemplateFileName());
335
336 $this->assign('search', $this->_search);
337
338 // search if search returned a form error?
339 if ((empty($_GET['reset']) || !empty($_GET['force'])) &&
340 !$searchError
341 ) {
342 $this->assign('isReset', FALSE);
343
344 $gidString = $this->_gid;
345 if (empty($this->_profileIds)) {
346 $gids = $this->_gid;
347 }
348 else {
349 $gids = $this->_profileIds;
350 $gidString = implode(',', $this->_profileIds);
351 }
352
353 $map = 0;
354 $linkToUF = 0;
355 $editLink = FALSE;
356 if ($this->_gid) {
357 $map = $ufgroupDAO->is_map;
358 $linkToUF = $ufgroupDAO->is_uf_link;
359 $editLink = $ufgroupDAO->is_edit_link;
360 }
361
362 if ($map) {
363 $this->assign('mapURL',
364 CRM_Utils_System::url('civicrm/profile/map',
365 "map=1&gid={$gidString}&reset=1"
366 )
367 );
368 }
369 if (!empty($this->_params['group'])) {
370 foreach ($this->_params['group'] as $key => $val) {
371 if (!$val) {
372 unset($this->_params['group'][$key]);
373 }
374 }
375 }
376
377 // the selector will override this if the user does have
378 // edit permissions as determined by the mask, CRM-4341
379 // do not allow edit for anon users in joomla frontend, CRM-4668
380 $config = CRM_Core_Config::singleton();
381 if (!CRM_Core_Permission::check('access CiviCRM') ||
382 $config->userFrameworkFrontend == 1
383 ) {
384 $editLink = FALSE;
385 }
386
387 $selector = new CRM_Profile_Selector_Listings($this->_params, $this->_customFields, $gids,
388 $map, $editLink, $linkToUF
389 );
390
391 $controller = new CRM_Core_Selector_Controller($selector,
392 $this->get(CRM_Utils_Pager::PAGE_ID),
393 $this->get(CRM_Utils_Sort::SORT_ID),
394 CRM_Core_Action::VIEW,
395 $this,
396 CRM_Core_Selector_Controller::TEMPLATE
397 );
398 $controller->setEmbedded(TRUE);
399 $controller->run();
400 }
401
402 //CRM-6862 -run form controller after
403 //selector, since it erase $_POST
404 $formController->run();
405
406 return parent::run();
407 }
408
409 /**
410 * Get the list of contacts for a profile.
411 *
412 * @param int $gid
413 *
414 * @return array
415 */
416 public static function getProfileContact($gid) {
417 $session = CRM_Core_Session::singleton();
418 $params = $session->get('profileParams');
419
420 $details = [];
421 $ufGroupParam = ['id' => $gid];
422 CRM_Core_BAO_UFGroup::retrieve($ufGroupParam, $details);
423
424 // make sure this group can be mapped
425 if (!$details['is_map']) {
426 CRM_Core_Error::statusBounce(ts('This profile does not have the map feature turned on.'));
427 }
428
429 $groupId = CRM_Utils_Array::value('limit_listings_group_id', $details);
430
431 // add group id to params if a uf group belong to a any group
432 if ($groupId) {
433 if (!empty($params['group'])) {
434 $params['group'][$groupId] = 1;
435 }
436 else {
437 $params['group'] = [$groupId => 1];
438 }
439 }
440
441 $fields = CRM_Core_BAO_UFGroup::getListingFields(
442 CRM_Core_Action::VIEW,
443 CRM_Core_BAO_UFGroup::PUBLIC_VISIBILITY | CRM_Core_BAO_UFGroup::LISTINGS_VISIBILITY,
444 FALSE,
445 $gid
446 );
447
448 $returnProperties = CRM_Contact_BAO_Contact::makeHierReturnProperties($fields);
449 $returnProperties['contact_type'] = 1;
450 $returnProperties['sort_name'] = 1;
451
452 $queryParams = CRM_Contact_BAO_Query::convertFormValues($params, 1);
453 $query = new CRM_Contact_BAO_Query($queryParams, $returnProperties, $fields);
454
455 $additionalWhereClause = 'contact_a.is_deleted = 0';
456
457 $ids = $query->searchQuery(0, 0, NULL,
458 FALSE, FALSE, FALSE,
459 TRUE, FALSE,
460 $additionalWhereClause
461 );
462
463 $contactIds = explode(',', $ids);
464
465 return $contactIds;
466 }
467
468 /**
469 * @param string $suffix
470 *
471 * @return null|string
472 */
473 public function checkTemplateFileExists($suffix = '') {
474 if ($this->_gid) {
475 $templateFile = "CRM/Profile/Page/{$this->_gid}/Listings.{$suffix}tpl";
476 $template = CRM_Core_Page::getTemplate();
477 if ($template->template_exists($templateFile)) {
478 return $templateFile;
479 }
480
481 // lets see if we have customized by name
482 $ufGroupName = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_UFGroup', $this->_gid, 'name');
483 if ($ufGroupName) {
484 $templateFile = "CRM/Profile/Page/{$ufGroupName}/Listings.{$suffix}tpl";
485 if ($template->template_exists($templateFile)) {
486 return $templateFile;
487 }
488 }
489 }
490 return NULL;
491 }
492
493 /**
494 * Use the form name to create the tpl file name.
495 *
496 * @return string
497 */
498 public function getTemplateFileName() {
499 $fileName = $this->checkTemplateFileExists();
500 return $fileName ? $fileName : parent::getTemplateFileName();
501 }
502
503 /**
504 * Default extra tpl file basically just replaces .tpl with .extra.tpl
505 * i.e. we dont override
506 *
507 * @return string
508 */
509 public function overrideExtraTemplateFileName() {
510 $fileName = $this->checkTemplateFileExists('extra.');
511 return $fileName ? $fileName : parent::overrideExtraTemplateFileName();
512 }
513
514 }