Merge pull request #12495 from eileenmcnaughton/dragon_slayer
[civicrm-core.git] / CRM / Contact / BAO / SavedSearch.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
fee14197 4 | CiviCRM version 5 |
6a488035 5 +--------------------------------------------------------------------+
8c9251b3 6 | Copyright CiviCRM LLC (c) 2004-2018 |
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 |
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 +--------------------------------------------------------------------+
d25dd0ee 26 */
6a488035
TO
27
28/**
29 *
30 * @package CRM
8c9251b3 31 * @copyright CiviCRM LLC (c) 2004-2018
6a488035
TO
32 */
33
34/**
c037736a 35 * Business object for Saved searches.
6a488035
TO
36 */
37class CRM_Contact_BAO_SavedSearch extends CRM_Contact_DAO_SavedSearch {
38
39 /**
fe482240 40 * Class constructor.
6a488035 41 */
00be9182 42 public function __construct() {
6a488035
TO
43 parent::__construct();
44 }
45
46 /**
100fef9d 47 * Query the db for all saved searches.
6a488035 48 *
a6c01b45
CW
49 * @return array
50 * contains the search name as value and and id as key
6a488035 51 */
00be9182 52 public function getAll() {
6a488035
TO
53 $savedSearch = new CRM_Contact_DAO_SavedSearch();
54 $savedSearch->selectAdd();
55 $savedSearch->selectAdd('id, name');
56 $savedSearch->find();
57 while ($savedSearch->fetch()) {
58 $aSavedSearch[$savedSearch->id] = $savedSearch->name;
59 }
60 return $aSavedSearch;
61 }
62
63 /**
fe482240
EM
64 * Retrieve DB object based on input parameters.
65 *
66 * It also stores all the retrieved values in the default array.
6a488035 67 *
77c5b619
TO
68 * @param array $params
69 * (reference ) an assoc array of name/value pairs.
70 * @param array $defaults
71 * (reference ) an assoc array to hold the flattened values.
6a488035 72 *
c490a46a 73 * @return CRM_Contact_BAO_SavedSearch
6a488035 74 */
00be9182 75 public static function retrieve(&$params, &$defaults) {
6a488035
TO
76 $savedSearch = new CRM_Contact_DAO_SavedSearch();
77 $savedSearch->copyValues($params);
78 if ($savedSearch->find(TRUE)) {
79 CRM_Core_DAO::storeValues($savedSearch, $defaults);
80 return $savedSearch;
81 }
82 return NULL;
83 }
84
85 /**
54957108 86 * Given an id, extract the formValues of the saved search.
6a488035 87 *
77c5b619
TO
88 * @param int $id
89 * The id of the saved search.
6a488035 90 *
a6c01b45 91 * @return array
9406ae0b 92 * the values of the posted saved search used as default values in various Search Form
6a488035 93 */
e5ad0335 94 public static function getFormValues($id) {
8f271253
JP
95 $specialDateFields = array(
96 'event_start_date_low' => 'event_date_low',
97 'event_end_date_high' => 'event_date_high',
98 'participant_register_date_low' => 'participant_date_low',
99 'participant_register_date_high' => 'participant_date_high',
100 'case_from_start_date_low' => 'case_from_date_low',
101 'case_from_start_date_high' => 'case_from_date_high',
102 'case_to_end_date_low' => 'case_to_date_low',
103 'case_to_end_date_high' => 'case_to_date_high',
104 );
105
6a488035
TO
106 $fv = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_SavedSearch', $id, 'form_values');
107 $result = NULL;
108 if ($fv) {
109 // make sure u unserialize - since it's stored in serialized form
110 $result = unserialize($fv);
111 }
112
06d67d53 113 $specialFields = array('contact_type', 'group', 'contact_tags', 'member_membership_type_id', 'member_status_id');
114 foreach ($result as $element => $value) {
115 if (CRM_Contact_BAO_Query::isAlreadyProcessedForQueryFormat($value)) {
116 $id = CRM_Utils_Array::value(0, $value);
117 $value = CRM_Utils_Array::value(2, $value);
118 if (is_array($value) && in_array(key($value), CRM_Core_DAO::acceptedSQLOperators(), TRUE)) {
8e2e771b 119 $op = key($value);
120 $value = CRM_Utils_Array::value($op, $value);
121 if (in_array($op, array('BETWEEN', '>=', '<='))) {
122 self::decodeRelativeFields($result, $id, $op, $value);
123 unset($result[$element]);
124 continue;
125 }
06d67d53 126 }
55ea4c30
JM
127 // Check for a date range field, which might be a standard date
128 // range or a relative date.
709355aa 129 if (strpos($id, '_date_low') !== FALSE || strpos($id, '_date_high') !== FALSE) {
5aa7bc38 130 $entityName = strstr($id, '_date', TRUE);
55ea4c30
JM
131
132 // This is the default, for non relative dates. We will overwrite
133 // it if we determine this is a relative date.
134 $result[$id] = $value;
135 $result["{$entityName}_date_relative"] = 0;
136
137 if (!empty($result['relative_dates'])) {
138 if (array_key_exists($entityName, $result['relative_dates'])) {
139 // We have a match from a regular field.
140 $result[$id] = NULL;
141 $result["{$entityName}_date_relative"] = $result['relative_dates'][$entityName];
142 }
143 elseif (!empty($specialDateFields[$id])) {
144 // We may have a match on a special date field.
145 $entityName = strstr($specialDateFields[$id], '_date', TRUE);
146 if (array_key_exists($entityName, $result['relative_dates'])) {
147 $result[$id] = NULL;
148 $result["{$entityName}_relative"] = $result['relative_dates'][$entityName];
149 }
150 }
9451fffa 151 }
709355aa 152 }
153 else {
154 $result[$id] = $value;
155 }
06d67d53 156 unset($result[$element]);
157 continue;
158 }
159 if (!empty($value) && is_array($value)) {
160 if (in_array($element, $specialFields)) {
e5ad0335
E
161 // Remove the element to minimise support for legacy formats. It is stored in $value
162 // so will be re-set with the right name.
163 unset($result[$element]);
06d67d53 164 $element = str_replace('member_membership_type_id', 'membership_type_id', $element);
165 $element = str_replace('member_status_id', 'membership_status_id', $element);
166 CRM_Contact_BAO_Query::legacyConvertFormValues($element, $value);
167 $result[$element] = $value;
168 }
169 // As per the OK (Operator as Key) value format, value array may contain key
170 // as an operator so to ensure the default is always set actual value
171 elseif (in_array(key($value), CRM_Core_DAO::acceptedSQLOperators(), TRUE)) {
172 $result[$element] = CRM_Utils_Array::value(key($value), $value);
173 if (is_string($result[$element])) {
174 $result[$element] = str_replace("%", '', $result[$element]);
6a488035 175 }
6a488035 176 }
06d67d53 177 }
178 if (substr($element, 0, 7) == 'custom_' &&
179 (substr($element, -5, 5) == '_from' || substr($element, -3, 3) == '_to')
180 ) {
181 // Ensure the _relative field is set if from or to are set to ensure custom date
182 // fields with 'from' or 'to' values are displayed when the are set in the smart group
183 // being loaded. (CRM-17116)
184 if (!isset($result[CRM_Contact_BAO_Query::getCustomFieldName($element) . '_relative'])) {
185 $result[CRM_Contact_BAO_Query::getCustomFieldName($element) . '_relative'] = 0;
186 }
187 }
188 // check to see if we need to convert the old privacy array
189 // CRM-9180
190 if (!empty($result['privacy'])) {
191 if (is_array($result['privacy'])) {
192 $result['privacy_operator'] = 'AND';
193 $result['privacy_toggle'] = 1;
194 if (isset($result['privacy']['do_not_toggle'])) {
195 if ($result['privacy']['do_not_toggle']) {
196 $result['privacy_toggle'] = 2;
197 }
198 unset($result['privacy']['do_not_toggle']);
199 }
6a488035 200
06d67d53 201 $result['privacy_options'] = array();
d0d565e0 202 foreach ($result['privacy'] as $name => $val) {
203 if ($val) {
06d67d53 204 $result['privacy_options'][] = $name;
205 }
6a488035
TO
206 }
207 }
06d67d53 208 unset($result['privacy']);
6a488035 209 }
6a488035
TO
210 }
211
3c49839d 212 if ($customSearchClass = CRM_Utils_Array::value('customSearchClass', $result)) {
213 // check if there is a special function - formatSavedSearchFields defined in the custom search form
214 if (method_exists($customSearchClass, 'formatSavedSearchFields')) {
215 $customSearchClass::formatSavedSearchFields($result);
216 }
217 }
218
6a488035
TO
219 return $result;
220 }
221
86538308 222 /**
54957108 223 * Get search parameters.
224 *
100fef9d 225 * @param int $id
86538308
EM
226 *
227 * @return array
228 */
00be9182 229 public static function getSearchParams($id) {
6a488035 230 $fv = self::getFormValues($id);
959528d2 231 //check if the saved search has mapping id
6a488035
TO
232 if (CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_SavedSearch', $id, 'mapping_id')) {
233 return CRM_Core_BAO_Mapping::formattedFields($fv);
234 }
a7488080 235 elseif (!empty($fv['customSearchID'])) {
6a488035
TO
236 return $fv;
237 }
238 else {
239 return CRM_Contact_BAO_Query::convertFormValues($fv);
240 }
241 }
242
243 /**
fe482240 244 * Get the where clause for a saved search.
6a488035 245 *
77c5b619
TO
246 * @param int $id
247 * Saved search id.
248 * @param array $tables
249 * (reference ) add the tables that are needed for the select clause.
250 * @param array $whereTables
251 * (reference ) add the tables that are needed for the where clause.
6a488035 252 *
a6c01b45
CW
253 * @return string
254 * the where clause for this saved search
6a488035 255 */
00be9182 256 public static function whereClause($id, &$tables, &$whereTables) {
6a488035
TO
257 $params = self::getSearchParams($id);
258 if ($params) {
a7488080 259 if (!empty($params['customSearchID'])) {
6a488035 260 // this has not yet been implemented
0db6c3e1
TO
261 }
262 else {
353ffa53
TO
263 return CRM_Contact_BAO_Query::getWhereClause($params, NULL, $tables, $whereTables);
264 }
6a488035
TO
265 }
266 return NULL;
267 }
268
86538308 269 /**
54957108 270 * Contact IDS Sql (whatever that means!).
271 *
100fef9d 272 * @param int $id
86538308
EM
273 *
274 * @return string
275 */
00be9182 276 public static function contactIDsSQL($id) {
6a488035 277 $params = self::getSearchParams($id);
8cc574cf 278 if ($params && !empty($params['customSearchID'])) {
6a488035
TO
279 return CRM_Contact_BAO_SearchCustom::contactIDSQL(NULL, $id);
280 }
281 else {
282 $tables = $whereTables = array('civicrm_contact' => 1);
283 $where = CRM_Contact_BAO_SavedSearch::whereClause($id, $tables, $whereTables);
284 if (!$where) {
285 $where = '( 1 )';
286 }
287 $from = CRM_Contact_BAO_Query::fromClause($whereTables);
288 return "
289SELECT contact_a.id
290$from
291WHERE $where";
292 }
293 }
294
86538308 295 /**
54957108 296 * Get from where email (whatever that means!).
297 *
100fef9d 298 * @param int $id
86538308
EM
299 *
300 * @return array
301 */
00be9182 302 public static function fromWhereEmail($id) {
6a488035
TO
303 $params = self::getSearchParams($id);
304
305 if ($params) {
a7488080 306 if (!empty($params['customSearchID'])) {
6a488035
TO
307 return CRM_Contact_BAO_SearchCustom::fromWhereEmail(NULL, $id);
308 }
309 else {
310 $tables = $whereTables = array('civicrm_contact' => 1, 'civicrm_email' => 1);
353ffa53
TO
311 $where = CRM_Contact_BAO_SavedSearch::whereClause($id, $tables, $whereTables);
312 $from = CRM_Contact_BAO_Query::fromClause($whereTables);
6a488035
TO
313 return array($from, $where);
314 }
315 }
316 else {
317 // fix for CRM-7240
318 $from = "
319FROM civicrm_contact contact_a
320LEFT JOIN civicrm_email ON (contact_a.id = civicrm_email.contact_id AND civicrm_email.is_primary = 1)
321";
322 $where = " ( 1 ) ";
323 $tables['civicrm_contact'] = $whereTables['civicrm_contact'] = 1;
324 $tables['civicrm_email'] = $whereTables['civicrm_email'] = 1;
325 return array($from, $where);
326 }
327 }
328
329 /**
54957108 330 * Given a saved search compute the clause and the tables and store it for future use.
6a488035 331 */
00be9182 332 public function buildClause() {
6a488035
TO
333 $fv = unserialize($this->form_values);
334
335 if ($this->mapping_id) {
336 $params = CRM_Core_BAO_Mapping::formattedFields($fv);
337 }
338 else {
339 $params = CRM_Contact_BAO_Query::convertFormValues($fv);
340 }
341
342 if (!empty($params)) {
343 $tables = $whereTables = array();
344 $this->where_clause = CRM_Contact_BAO_Query::getWhereClause($params, NULL, $tables, $whereTables);
345 if (!empty($tables)) {
346 $this->select_tables = serialize($tables);
347 }
348 if (!empty($whereTables)) {
349 $this->where_tables = serialize($whereTables);
350 }
351 }
6a488035
TO
352 }
353
54957108 354 /**
355 * Save the search.
356 *
357 * @param bool $hook
358 */
8ab09481 359 public function save($hook = TRUE) {
6a488035
TO
360 // first build the computed fields
361 $this->buildClause();
362
8ab09481 363 parent::save($hook);
6a488035
TO
364 }
365
366 /**
e8e8f3ad 367 * Given an id, get the name of the saved search.
6a488035 368 *
77c5b619
TO
369 * @param int $id
370 * The id of the saved search.
6a488035 371 *
77b97be7
EM
372 * @param string $value
373 *
a6c01b45
CW
374 * @return string
375 * the name of the saved search
6a488035 376 */
00be9182 377 public static function getName($id, $value = 'name') {
6a488035
TO
378 $group = new CRM_Contact_DAO_Group();
379 $group->saved_search_id = $id;
380 if ($group->find(TRUE)) {
381 return $group->$value;
382 }
383 return NULL;
384 }
385
386 /**
e8e8f3ad 387 * Create a smart group from normalised values.
54957108 388 *
389 * @param array $params
390 *
391 * @return \CRM_Contact_DAO_SavedSearch
6a488035 392 */
00be9182 393 public static function create(&$params) {
6a488035
TO
394 $savedSearch = new CRM_Contact_DAO_SavedSearch();
395 if (isset($params['formValues']) &&
396 !empty($params['formValues'])
397 ) {
398 $savedSearch->form_values = serialize($params['formValues']);
399 }
400 else {
1c18b4d5 401 $savedSearch->form_values = NULL;
6a488035
TO
402 }
403
404 $savedSearch->is_active = CRM_Utils_Array::value('is_active', $params, 1);
405 $savedSearch->mapping_id = CRM_Utils_Array::value('mapping_id', $params, 'null');
406 $savedSearch->custom_search_id = CRM_Utils_Array::value('custom_search_id', $params, 'null');
407 $savedSearch->id = CRM_Utils_Array::value('id', $params, NULL);
408
409 $savedSearch->save();
410
411 return $savedSearch;
412 }
96025800 413
54957108 414 /**
415 * Assign test value.
416 *
417 * @param string $fieldName
418 * @param array $fieldDef
419 * @param int $counter
420 */
4f0a0e58
JV
421 protected function assignTestValue($fieldName, &$fieldDef, $counter) {
422 if ($fieldName == 'form_values') {
423 // A dummy value for form_values.
424 $this->{$fieldName} = serialize(
425 array('sort_name' => "SortName{$counter}"));
426 }
427 else {
428 parent::assignTestValues($fieldName, $fieldDef, $counter);
429 }
430 }
431
9451fffa 432 /**
433 * Store relative dates in separate array format
434 *
435 * @param array $queryParams
436 * @param array $formValues
437 */
438 public static function saveRelativeDates(&$queryParams, $formValues) {
439 $relativeDates = array('relative_dates' => array());
8f271253 440 $specialDateFields = array('event_relative', 'case_from_relative', 'case_to_relative', 'participant_relative');
9451fffa 441 foreach ($formValues as $id => $value) {
776b1cdf 442 if ((preg_match('/(_date|custom_[0-9]+)_relative$/', $id) || in_array($id, $specialDateFields)) && !empty($value)) {
9451fffa 443 $entityName = strstr($id, '_date', TRUE);
8f271253
JP
444 if (empty($entityName)) {
445 $entityName = strstr($id, '_relative', TRUE);
446 }
9451fffa 447 $relativeDates['relative_dates'][$entityName] = $value;
448 }
449 }
450 // merge with original queryParams if relative date value(s) found
451 if (count($relativeDates['relative_dates'])) {
452 $queryParams = array_merge($queryParams, $relativeDates);
453 }
454 }
455
3f471f2d 456 /**
457 * Store search variables in $queryParams which were skipped while processing query params,
458 * precisely at CRM_Contact_BAO_Query::fixWhereValues(...). But these variable are required in
459 * building smart group criteria otherwise it will cause issues like CRM-18585,CRM-19571
460 *
461 * @param array $queryParams
462 * @param array $formValues
463 */
464 public static function saveSkippedElement(&$queryParams, $formValues) {
465 // these are elements which are skipped in a smart group criteria
466 $specialElements = array(
467 'operator',
468 'component_mode',
469 'display_relationship_type',
470 );
471 foreach ($specialElements as $element) {
472 if (!empty($formValues[$element])) {
473 $queryParams[] = array($element, '=', $formValues[$element], 0, 0);
474 }
475 }
476 }
477
8e2e771b 478 /**
479 * Decode relative custom fields (converted by CRM_Contact_BAO_Query->convertCustomRelativeFields(...))
480 * into desired formValues
481 *
482 * @param array $formValues
483 * @param string $fieldName
484 * @param string $op
485 * @param array|string|int $value
486 */
487 public static function decodeRelativeFields(&$formValues, $fieldName, $op, $value) {
b2b9d9df 488 // check if its a custom date field, if yes then 'searchDate' format the value
489 $isCustomDateField = CRM_Contact_BAO_Query::isCustomDateField($fieldName);
d8766e36 490
b2b9d9df 491 // select date range as default
492 if ($isCustomDateField) {
776b1cdf
JM
493 if (array_key_exists('relative_dates', $formValues) && array_key_exists($fieldName, $formValues['relative_dates'])) {
494 $formValues[$fieldName . '_relative'] = $formValues['relative_dates'][$fieldName];
495 }
496 else {
497 $formValues[$fieldName . '_relative'] = 0;
498 }
d8766e36 499 }
8e2e771b 500 switch ($op) {
501 case 'BETWEEN':
b2b9d9df 502 if ($isCustomDateField) {
503 list($formValues[$fieldName . '_from'], $formValues[$fieldName . '_from_time']) = CRM_Utils_Date::setDateDefaults($value[0], 'searchDate');
504 list($formValues[$fieldName . '_to'], $formValues[$fieldName . '_to_time']) = CRM_Utils_Date::setDateDefaults($value[1], 'searchDate');
505 }
506 else {
507 list($formValues[$fieldName . '_from'], $formValues[$fieldName . '_to']) = $value;
508 }
8e2e771b 509 break;
510
511 case '>=':
b2b9d9df 512 if ($isCustomDateField) {
513 list($formValues[$fieldName . '_from'], $formValues[$fieldName . '_from_time']) = CRM_Utils_Date::setDateDefaults($value, 'searchDate');
514 }
515 else {
516 $formValues[$fieldName . '_from'] = $value;
517 }
8e2e771b 518 break;
519
520 case '<=':
b2b9d9df 521 if ($isCustomDateField) {
522 list($formValues[$fieldName . '_to'], $formValues[$fieldName . '_to_time']) = CRM_Utils_Date::setDateDefaults($value, 'searchDate');
523 }
524 else {
525 $formValues[$fieldName . '_to'] = $value;
526 }
8e2e771b 527 break;
528 }
529 }
530
6a488035 531}