| 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 | * This api exposes CiviCRM contacts. |
| 14 | * |
| 15 | * Contacts are the main entity in CiviCRM and this api is more robust than most. |
| 16 | * - Get action allows all params supported by advanced search. |
| 17 | * - Create action allows creating several related entities at once (e.g. email). |
| 18 | * - Create allows checking for duplicate contacts. |
| 19 | * Use getfields to list the full range of parameters and options supported by each action. |
| 20 | * |
| 21 | * @package CiviCRM_APIv3 |
| 22 | */ |
| 23 | |
| 24 | /** |
| 25 | * Create or update a Contact. |
| 26 | * |
| 27 | * @param array $params |
| 28 | * Input parameters. |
| 29 | * |
| 30 | * @throws API_Exception |
| 31 | * |
| 32 | * @return array |
| 33 | * API Result Array |
| 34 | */ |
| 35 | function civicrm_api3_contact_create($params) { |
| 36 | $contactID = CRM_Utils_Array::value('contact_id', $params, CRM_Utils_Array::value('id', $params)); |
| 37 | |
| 38 | if ($contactID && !empty($params['check_permissions']) && !CRM_Contact_BAO_Contact_Permission::allow($contactID, CRM_Core_Permission::EDIT)) { |
| 39 | throw new \Civi\API\Exception\UnauthorizedException('Permission denied to modify contact record'); |
| 40 | } |
| 41 | |
| 42 | if (!empty($params['dupe_check'])) { |
| 43 | $ids = CRM_Contact_BAO_Contact::getDuplicateContacts($params, $params['contact_type'], 'Unsupervised', [], $params['check_permission']); |
| 44 | if (count($ids) > 0) { |
| 45 | throw new API_Exception("Found matching contacts: " . implode(',', $ids), "duplicate", ["ids" => $ids]); |
| 46 | } |
| 47 | } |
| 48 | |
| 49 | $values = _civicrm_api3_contact_check_params($params); |
| 50 | if ($values) { |
| 51 | return $values; |
| 52 | } |
| 53 | |
| 54 | if (!$contactID) { |
| 55 | // If we get here, we're ready to create a new contact |
| 56 | if (($email = CRM_Utils_Array::value('email', $params)) && !is_array($params['email'])) { |
| 57 | $defLocType = CRM_Core_BAO_LocationType::getDefault(); |
| 58 | $params['email'] = [ |
| 59 | 1 => [ |
| 60 | 'email' => $email, |
| 61 | 'is_primary' => 1, |
| 62 | 'location_type_id' => ($defLocType->id) ? $defLocType->id : 1, |
| 63 | ], |
| 64 | ]; |
| 65 | } |
| 66 | } |
| 67 | |
| 68 | if (!empty($params['home_url'])) { |
| 69 | $websiteTypes = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Website', 'website_type_id'); |
| 70 | $params['website'] = [ |
| 71 | 1 => [ |
| 72 | 'website_type_id' => key($websiteTypes), |
| 73 | 'url' => $params['home_url'], |
| 74 | ], |
| 75 | ]; |
| 76 | } |
| 77 | |
| 78 | _civicrm_api3_greeting_format_params($params); |
| 79 | |
| 80 | $values = []; |
| 81 | |
| 82 | if (empty($params['contact_type']) && $contactID) { |
| 83 | $params['contact_type'] = CRM_Contact_BAO_Contact::getContactType($contactID); |
| 84 | if (!$params['contact_type']) { |
| 85 | throw new API_Exception('Contact id ' . $contactID . ' not found.'); |
| 86 | } |
| 87 | } |
| 88 | |
| 89 | if (!isset($params['contact_sub_type']) && $contactID) { |
| 90 | $params['contact_sub_type'] = CRM_Contact_BAO_Contact::getContactSubType($contactID); |
| 91 | } |
| 92 | |
| 93 | _civicrm_api3_custom_format_params($params, $values, $params['contact_type'], $contactID); |
| 94 | |
| 95 | $params = array_merge($params, $values); |
| 96 | //@todo we should just call basic_create here - but need to make contact:create accept 'id' on the bao |
| 97 | $contact = _civicrm_api3_contact_update($params, $contactID); |
| 98 | |
| 99 | if (is_a($contact, 'CRM_Core_Error')) { |
| 100 | throw new API_Exception($contact->_errors[0]['message']); |
| 101 | } |
| 102 | else { |
| 103 | $values = []; |
| 104 | _civicrm_api3_object_to_array_unique_fields($contact, $values[$contact->id]); |
| 105 | } |
| 106 | |
| 107 | return civicrm_api3_create_success($values, $params, 'Contact', 'create'); |
| 108 | } |
| 109 | |
| 110 | /** |
| 111 | * Adjust Metadata for Create action. |
| 112 | * |
| 113 | * @param array $params |
| 114 | * Array of parameters determined by getfields. |
| 115 | */ |
| 116 | function _civicrm_api3_contact_create_spec(&$params) { |
| 117 | $params['contact_type']['api.required'] = 1; |
| 118 | $params['id']['api.aliases'] = ['contact_id']; |
| 119 | $params['current_employer'] = [ |
| 120 | 'title' => 'Current Employer', |
| 121 | 'description' => 'Name of Current Employer', |
| 122 | 'type' => CRM_Utils_Type::T_STRING, |
| 123 | ]; |
| 124 | $params['dupe_check'] = [ |
| 125 | 'title' => 'Check for Duplicates', |
| 126 | 'description' => 'Throw error if contact create matches dedupe rule', |
| 127 | 'type' => CRM_Utils_Type::T_BOOLEAN, |
| 128 | ]; |
| 129 | $params['skip_greeting_processing'] = [ |
| 130 | 'title' => 'Skip Greeting processing', |
| 131 | 'description' => 'Do not process greetings, (these can be done by scheduled job and there may be a preference to do so for performance reasons)', |
| 132 | 'type' => CRM_Utils_Type::T_BOOLEAN, |
| 133 | 'api.default' => 0, |
| 134 | ]; |
| 135 | $params['prefix_id']['api.aliases'] = [ |
| 136 | 'individual_prefix', |
| 137 | 'individual_prefix_id', |
| 138 | ]; |
| 139 | $params['suffix_id']['api.aliases'] = [ |
| 140 | 'individual_suffix', |
| 141 | 'individual_suffix_id', |
| 142 | ]; |
| 143 | $params['gender_id']['api.aliases'] = ['gender']; |
| 144 | } |
| 145 | |
| 146 | /** |
| 147 | * Retrieve one or more contacts, given a set of search params. |
| 148 | * |
| 149 | * @param array $params |
| 150 | * |
| 151 | * @return array |
| 152 | * API Result Array |
| 153 | * |
| 154 | * @throws \API_Exception |
| 155 | */ |
| 156 | function civicrm_api3_contact_get($params) { |
| 157 | $options = []; |
| 158 | _civicrm_api3_contact_get_supportanomalies($params, $options); |
| 159 | $contacts = _civicrm_api3_get_using_query_object('Contact', $params, $options); |
| 160 | if (!empty($params['check_permissions'])) { |
| 161 | CRM_Contact_BAO_Contact::unsetProtectedFields($contacts); |
| 162 | } |
| 163 | return civicrm_api3_create_success($contacts, $params, 'Contact'); |
| 164 | } |
| 165 | |
| 166 | /** |
| 167 | * Get number of contacts matching the supplied criteria. |
| 168 | * |
| 169 | * @param array $params |
| 170 | * |
| 171 | * @return int |
| 172 | */ |
| 173 | function civicrm_api3_contact_getcount($params) { |
| 174 | $options = []; |
| 175 | _civicrm_api3_contact_get_supportanomalies($params, $options); |
| 176 | $count = _civicrm_api3_get_using_query_object('Contact', $params, $options, 1); |
| 177 | return (int) $count; |
| 178 | } |
| 179 | |
| 180 | /** |
| 181 | * Adjust Metadata for Get action. |
| 182 | * |
| 183 | * @param array $params |
| 184 | * Array of parameters determined by getfields. |
| 185 | */ |
| 186 | function _civicrm_api3_contact_get_spec(&$params) { |
| 187 | $params['contact_is_deleted']['api.default'] = 0; |
| 188 | |
| 189 | // We declare all these pseudoFields as there are other undocumented fields accessible |
| 190 | // via the api - but if check permissions is set we only allow declared fields |
| 191 | $params['address_id'] = [ |
| 192 | 'title' => 'Primary Address ID', |
| 193 | 'type' => CRM_Utils_Type::T_INT, |
| 194 | ]; |
| 195 | $params['street_address'] = [ |
| 196 | 'title' => 'Primary Address Street Address', |
| 197 | 'type' => CRM_Utils_Type::T_STRING, |
| 198 | ]; |
| 199 | $params['supplemental_address_1'] = [ |
| 200 | 'title' => 'Primary Address Supplemental Address 1', |
| 201 | 'type' => CRM_Utils_Type::T_STRING, |
| 202 | ]; |
| 203 | $params['supplemental_address_2'] = [ |
| 204 | 'title' => 'Primary Address Supplemental Address 2', |
| 205 | 'type' => CRM_Utils_Type::T_STRING, |
| 206 | ]; |
| 207 | $params['supplemental_address_3'] = [ |
| 208 | 'title' => 'Primary Address Supplemental Address 3', |
| 209 | 'type' => CRM_Utils_Type::T_STRING, |
| 210 | ]; |
| 211 | $params['current_employer'] = [ |
| 212 | 'title' => 'Current Employer', |
| 213 | 'type' => CRM_Utils_Type::T_STRING, |
| 214 | ]; |
| 215 | $params['city'] = [ |
| 216 | 'title' => 'Primary Address City', |
| 217 | 'type' => CRM_Utils_Type::T_STRING, |
| 218 | ]; |
| 219 | $params['postal_code_suffix'] = [ |
| 220 | 'title' => 'Primary Address Post Code Suffix', |
| 221 | 'type' => CRM_Utils_Type::T_STRING, |
| 222 | ]; |
| 223 | $params['postal_code'] = [ |
| 224 | 'title' => 'Primary Address Post Code', |
| 225 | 'type' => CRM_Utils_Type::T_STRING, |
| 226 | ]; |
| 227 | $params['geo_code_1'] = [ |
| 228 | 'title' => 'Primary Address Latitude', |
| 229 | 'type' => CRM_Utils_Type::T_STRING, |
| 230 | ]; |
| 231 | $params['geo_code_2'] = [ |
| 232 | 'title' => 'Primary Address Longitude', |
| 233 | 'type' => CRM_Utils_Type::T_STRING, |
| 234 | ]; |
| 235 | $params['state_province_id'] = [ |
| 236 | 'title' => 'Primary Address State Province ID', |
| 237 | 'type' => CRM_Utils_Type::T_INT, |
| 238 | 'pseudoconstant' => [ |
| 239 | 'table' => 'civicrm_state_province', |
| 240 | ], |
| 241 | ]; |
| 242 | $params['state_province_name'] = [ |
| 243 | 'title' => 'Primary Address State Province Name', |
| 244 | 'type' => CRM_Utils_Type::T_STRING, |
| 245 | 'pseudoconstant' => [ |
| 246 | 'table' => 'civicrm_state_province', |
| 247 | ], |
| 248 | ]; |
| 249 | $params['state_province'] = [ |
| 250 | 'title' => 'Primary Address State Province', |
| 251 | 'type' => CRM_Utils_Type::T_STRING, |
| 252 | 'pseudoconstant' => [ |
| 253 | 'table' => 'civicrm_state_province', |
| 254 | ], |
| 255 | ]; |
| 256 | $params['country_id'] = [ |
| 257 | 'title' => 'Primary Address Country ID', |
| 258 | 'type' => CRM_Utils_Type::T_INT, |
| 259 | 'pseudoconstant' => [ |
| 260 | 'table' => 'civicrm_country', |
| 261 | ], |
| 262 | ]; |
| 263 | $params['country'] = [ |
| 264 | 'title' => 'Primary Address country', |
| 265 | 'type' => CRM_Utils_Type::T_STRING, |
| 266 | 'pseudoconstant' => [ |
| 267 | 'table' => 'civicrm_country', |
| 268 | ], |
| 269 | ]; |
| 270 | $params['worldregion_id'] = [ |
| 271 | 'title' => 'Primary Address World Region ID', |
| 272 | 'type' => CRM_Utils_Type::T_INT, |
| 273 | 'pseudoconstant' => [ |
| 274 | 'table' => 'civicrm_world_region', |
| 275 | ], |
| 276 | ]; |
| 277 | $params['worldregion'] = [ |
| 278 | 'title' => 'Primary Address World Region', |
| 279 | 'type' => CRM_Utils_Type::T_STRING, |
| 280 | 'pseudoconstant' => [ |
| 281 | 'table' => 'civicrm_world_region', |
| 282 | ], |
| 283 | ]; |
| 284 | $params['phone_id'] = [ |
| 285 | 'title' => 'Primary Phone ID', |
| 286 | 'type' => CRM_Utils_Type::T_INT, |
| 287 | ]; |
| 288 | $params['phone'] = [ |
| 289 | 'title' => 'Primary Phone', |
| 290 | 'type' => CRM_Utils_Type::T_STRING, |
| 291 | ]; |
| 292 | $params['phone_type_id'] = [ |
| 293 | 'title' => 'Primary Phone Type ID', |
| 294 | 'type' => CRM_Utils_Type::T_INT, |
| 295 | ]; |
| 296 | $params['provider_id'] = [ |
| 297 | 'title' => 'Primary Phone Provider ID', |
| 298 | 'type' => CRM_Utils_Type::T_INT, |
| 299 | ]; |
| 300 | $params['email_id'] = [ |
| 301 | 'title' => 'Primary Email ID', |
| 302 | 'type' => CRM_Utils_Type::T_INT, |
| 303 | ]; |
| 304 | $params['email'] = [ |
| 305 | 'title' => 'Primary Email', |
| 306 | 'type' => CRM_Utils_Type::T_STRING, |
| 307 | ]; |
| 308 | $params['on_hold'] = [ |
| 309 | 'title' => 'Primary Email On Hold', |
| 310 | 'type' => CRM_Utils_Type::T_BOOLEAN, |
| 311 | ]; |
| 312 | $params['im'] = [ |
| 313 | 'title' => 'Primary Instant Messenger', |
| 314 | 'type' => CRM_Utils_Type::T_STRING, |
| 315 | ]; |
| 316 | $params['im_id'] = [ |
| 317 | 'title' => 'Primary Instant Messenger ID', |
| 318 | 'type' => CRM_Utils_Type::T_INT, |
| 319 | ]; |
| 320 | $params['group'] = [ |
| 321 | 'title' => 'Group', |
| 322 | 'pseudoconstant' => [ |
| 323 | 'table' => 'civicrm_group', |
| 324 | ], |
| 325 | ]; |
| 326 | $params['tag'] = [ |
| 327 | 'title' => 'Tags', |
| 328 | 'pseudoconstant' => [ |
| 329 | 'table' => 'civicrm_tag', |
| 330 | ], |
| 331 | ]; |
| 332 | $params['uf_user'] = [ |
| 333 | 'title' => 'CMS User', |
| 334 | 'type' => CRM_Utils_Type::T_BOOLEAN, |
| 335 | ]; |
| 336 | $params['birth_date_low'] = [ |
| 337 | 'name' => 'birth_date_low', |
| 338 | 'type' => CRM_Utils_Type::T_DATE, |
| 339 | 'title' => ts('Birth Date is equal to or greater than'), |
| 340 | ]; |
| 341 | $params['birth_date_high'] = [ |
| 342 | 'name' => 'birth_date_high', |
| 343 | 'type' => CRM_Utils_Type::T_DATE, |
| 344 | 'title' => ts('Birth Date is equal to or less than'), |
| 345 | ]; |
| 346 | $params['deceased_date_low'] = [ |
| 347 | 'name' => 'deceased_date_low', |
| 348 | 'type' => CRM_Utils_Type::T_DATE, |
| 349 | 'title' => ts('Deceased Date is equal to or greater than'), |
| 350 | ]; |
| 351 | $params['deceased_date_high'] = [ |
| 352 | 'name' => 'deceased_date_high', |
| 353 | 'type' => CRM_Utils_Type::T_DATE, |
| 354 | 'title' => ts('Deceased Date is equal to or less than'), |
| 355 | ]; |
| 356 | } |
| 357 | |
| 358 | /** |
| 359 | * Support for historical oddities. |
| 360 | * |
| 361 | * We are supporting 'showAll' = 'all', 'trash' or 'active' for Contact get |
| 362 | * and for getcount |
| 363 | * - hopefully some day we'll come up with a std syntax for the 3-way-boolean of |
| 364 | * 0, 1 or not set |
| 365 | * |
| 366 | * We also support 'filter_group_id' & 'filter.group_id' |
| 367 | * |
| 368 | * @param array $params |
| 369 | * As passed into api get or getcount function. |
| 370 | * @param array $options |
| 371 | * Array of options (so we can modify the filter). |
| 372 | */ |
| 373 | function _civicrm_api3_contact_get_supportanomalies(&$params, &$options) { |
| 374 | if (!empty($params['email']) && !is_array($params['email'])) { |
| 375 | // Fix this to be in array format so the query object does not add LIKE |
| 376 | // I think there is a better fix that I will do for master. |
| 377 | $params['email'] = ['=' => $params['email']]; |
| 378 | } |
| 379 | if (isset($params['showAll'])) { |
| 380 | if (strtolower($params['showAll']) == "active") { |
| 381 | $params['contact_is_deleted'] = 0; |
| 382 | } |
| 383 | if (strtolower($params['showAll']) == "trash") { |
| 384 | $params['contact_is_deleted'] = 1; |
| 385 | } |
| 386 | if (strtolower($params['showAll']) == "all" && isset($params['contact_is_deleted'])) { |
| 387 | unset($params['contact_is_deleted']); |
| 388 | } |
| 389 | } |
| 390 | // support for group filters |
| 391 | if (array_key_exists('filter_group_id', $params)) { |
| 392 | $params['filter.group_id'] = $params['filter_group_id']; |
| 393 | unset($params['filter_group_id']); |
| 394 | } |
| 395 | // filter.group_id works both for 1,2,3 and array (1,2,3) |
| 396 | if (array_key_exists('filter.group_id', $params)) { |
| 397 | if (is_array($params['filter.group_id'])) { |
| 398 | $groups = $params['filter.group_id']; |
| 399 | } |
| 400 | else { |
| 401 | $groups = explode(',', $params['filter.group_id']); |
| 402 | } |
| 403 | unset($params['filter.group_id']); |
| 404 | $options['input_params']['group'] = $groups; |
| 405 | } |
| 406 | if (isset($params['group'])) { |
| 407 | $groups = $params['group']; |
| 408 | $groupsByTitle = CRM_Core_PseudoConstant::group(); |
| 409 | $groupsByName = CRM_Contact_BAO_GroupContact::buildOptions('group_id', 'validate'); |
| 410 | $allGroups = array_merge(array_flip($groupsByTitle), array_flip($groupsByName)); |
| 411 | if (is_array($groups) && in_array(key($groups), CRM_Core_DAO::acceptedSQLOperators(), TRUE)) { |
| 412 | // Get the groups array. |
| 413 | $groupsArray = $groups[key($groups)]; |
| 414 | foreach ($groupsArray as &$group) { |
| 415 | if (!is_numeric($group) && !empty($allGroups[$group])) { |
| 416 | $group = $allGroups[$group]; |
| 417 | } |
| 418 | } |
| 419 | // Now reset the $groups array with the ids not the titles. |
| 420 | $groups[key($groups)] = $groupsArray; |
| 421 | } |
| 422 | // handle format like 'group' => array('title1', 'title2'). |
| 423 | elseif (is_array($groups)) { |
| 424 | foreach ($groups as $k => &$group) { |
| 425 | if (!is_numeric($group) && !empty($allGroups[$group])) { |
| 426 | $group = $allGroups[$group]; |
| 427 | } |
| 428 | if (!is_numeric($k) && !empty($allGroups[$k])) { |
| 429 | unset($groups[$k]); |
| 430 | $groups[$allGroups[$k]] = $group; |
| 431 | } |
| 432 | } |
| 433 | } |
| 434 | elseif (!is_numeric($groups) && !empty($allGroups[$groups])) { |
| 435 | $groups = $allGroups[$groups]; |
| 436 | } |
| 437 | $params['group'] = $groups; |
| 438 | } |
| 439 | } |
| 440 | |
| 441 | /** |
| 442 | * Delete a Contact with given contact_id. |
| 443 | * |
| 444 | * @param array $params |
| 445 | * input parameters per getfields |
| 446 | * |
| 447 | * @throws \Civi\API\Exception\UnauthorizedException |
| 448 | * @return array |
| 449 | * API Result Array |
| 450 | */ |
| 451 | function civicrm_api3_contact_delete($params) { |
| 452 | $contactID = CRM_Utils_Array::value('id', $params); |
| 453 | |
| 454 | if (!empty($params['check_permissions']) && !CRM_Contact_BAO_Contact_Permission::allow($contactID, CRM_Core_Permission::DELETE)) { |
| 455 | throw new \Civi\API\Exception\UnauthorizedException('Permission denied to modify contact record'); |
| 456 | } |
| 457 | |
| 458 | $session = CRM_Core_Session::singleton(); |
| 459 | if ($contactID == $session->get('userID')) { |
| 460 | return civicrm_api3_create_error('This contact record is linked to the currently logged in user account - and cannot be deleted.'); |
| 461 | } |
| 462 | $restore = !empty($params['restore']) ? $params['restore'] : FALSE; |
| 463 | $skipUndelete = !empty($params['skip_undelete']) ? $params['skip_undelete'] : FALSE; |
| 464 | |
| 465 | // CRM-12929 |
| 466 | // restrict permanent delete if a contact has financial trxn associated with it |
| 467 | $error = NULL; |
| 468 | if ($skipUndelete && CRM_Financial_BAO_FinancialItem::checkContactPresent([$contactID], $error)) { |
| 469 | return civicrm_api3_create_error($error['_qf_default']); |
| 470 | } |
| 471 | if (CRM_Contact_BAO_Contact::deleteContact($contactID, $restore, $skipUndelete, |
| 472 | CRM_Utils_Array::value('check_permissions', $params))) { |
| 473 | return civicrm_api3_create_success(); |
| 474 | } |
| 475 | else { |
| 476 | return civicrm_api3_create_error('Could not delete contact'); |
| 477 | } |
| 478 | } |
| 479 | |
| 480 | /** |
| 481 | * Check parameters passed in. |
| 482 | * |
| 483 | * This function is on it's way out. |
| 484 | * |
| 485 | * @param array $params |
| 486 | * |
| 487 | * @return null |
| 488 | * @throws API_Exception |
| 489 | * @throws CiviCRM_API3_Exception |
| 490 | */ |
| 491 | function _civicrm_api3_contact_check_params(&$params) { |
| 492 | |
| 493 | switch (strtolower(CRM_Utils_Array::value('contact_type', $params))) { |
| 494 | case 'household': |
| 495 | civicrm_api3_verify_mandatory($params, NULL, ['household_name']); |
| 496 | break; |
| 497 | |
| 498 | case 'organization': |
| 499 | civicrm_api3_verify_mandatory($params, NULL, ['organization_name']); |
| 500 | break; |
| 501 | |
| 502 | case 'individual': |
| 503 | civicrm_api3_verify_one_mandatory($params, NULL, [ |
| 504 | 'first_name', |
| 505 | 'last_name', |
| 506 | 'email', |
| 507 | 'display_name', |
| 508 | ]); |
| 509 | break; |
| 510 | } |
| 511 | |
| 512 | if (!empty($params['contact_sub_type']) && !empty($params['contact_type'])) { |
| 513 | if (!(CRM_Contact_BAO_ContactType::isExtendsContactType($params['contact_sub_type'], $params['contact_type']))) { |
| 514 | throw new API_Exception("Invalid or Mismatched Contact Subtype: " . implode(', ', (array) $params['contact_sub_type'])); |
| 515 | } |
| 516 | } |
| 517 | |
| 518 | // The BAO no longer supports the legacy param "current_employer" so here is a shim for api backward-compatability |
| 519 | if (!empty($params['current_employer'])) { |
| 520 | $organizationParams = [ |
| 521 | 'organization_name' => $params['current_employer'], |
| 522 | ]; |
| 523 | |
| 524 | $dupeIds = CRM_Contact_BAO_Contact::getDuplicateContacts($organizationParams, 'Organization', 'Supervised', [], FALSE); |
| 525 | |
| 526 | // check for mismatch employer name and id |
| 527 | if (!empty($params['employer_id']) && !in_array($params['employer_id'], $dupeIds)) { |
| 528 | throw new API_Exception('Employer name and Employer id Mismatch'); |
| 529 | } |
| 530 | |
| 531 | // show error if multiple organisation with same name exist |
| 532 | if (empty($params['employer_id']) && (count($dupeIds) > 1)) { |
| 533 | throw new API_Exception('Found more than one Organisation with same Name.'); |
| 534 | } |
| 535 | |
| 536 | if ($dupeIds) { |
| 537 | $params['employer_id'] = $dupeIds[0]; |
| 538 | } |
| 539 | else { |
| 540 | $result = civicrm_api3('Contact', 'create', [ |
| 541 | 'organization_name' => $params['current_employer'], |
| 542 | 'contact_type' => 'Organization', |
| 543 | ]); |
| 544 | $params['employer_id'] = $result['id']; |
| 545 | } |
| 546 | } |
| 547 | |
| 548 | return NULL; |
| 549 | } |
| 550 | |
| 551 | /** |
| 552 | * Helper function for Contact create. |
| 553 | * |
| 554 | * @param array $params |
| 555 | * (reference ) an assoc array of name/value pairs. |
| 556 | * @param int $contactID |
| 557 | * If present the contact with that ID is updated. |
| 558 | * |
| 559 | * @return CRM_Contact_BAO_Contact|CRM_Core_Error |
| 560 | */ |
| 561 | function _civicrm_api3_contact_update($params, $contactID = NULL) { |
| 562 | //@todo - doesn't contact create support 'id' which is already set- check & remove |
| 563 | if ($contactID) { |
| 564 | $params['contact_id'] = $contactID; |
| 565 | } |
| 566 | |
| 567 | return CRM_Contact_BAO_Contact::create($params); |
| 568 | } |
| 569 | |
| 570 | /** |
| 571 | * Validate the addressee or email or postal greetings. |
| 572 | * |
| 573 | * @param array $params |
| 574 | * Array per getfields metadata. |
| 575 | * |
| 576 | * @throws API_Exception |
| 577 | */ |
| 578 | function _civicrm_api3_greeting_format_params($params) { |
| 579 | $greetingParams = ['', '_id', '_custom']; |
| 580 | foreach (['email', 'postal', 'addressee'] as $key) { |
| 581 | $greeting = '_greeting'; |
| 582 | if ($key == 'addressee') { |
| 583 | $greeting = ''; |
| 584 | } |
| 585 | |
| 586 | $formatParams = FALSE; |
| 587 | // Unset display value from params. |
| 588 | if (isset($params["{$key}{$greeting}_display"])) { |
| 589 | unset($params["{$key}{$greeting}_display"]); |
| 590 | } |
| 591 | |
| 592 | // check if greetings are present in present |
| 593 | foreach ($greetingParams as $greetingValues) { |
| 594 | if (array_key_exists("{$key}{$greeting}{$greetingValues}", $params)) { |
| 595 | $formatParams = TRUE; |
| 596 | break; |
| 597 | } |
| 598 | } |
| 599 | |
| 600 | if (!$formatParams) { |
| 601 | continue; |
| 602 | } |
| 603 | |
| 604 | $nullValue = FALSE; |
| 605 | $filter = [ |
| 606 | 'greeting_type' => "{$key}{$greeting}", |
| 607 | ]; |
| 608 | |
| 609 | $greetings = CRM_Core_PseudoConstant::greeting($filter); |
| 610 | $greetingId = CRM_Utils_Array::value("{$key}{$greeting}_id", $params); |
| 611 | $greetingVal = CRM_Utils_Array::value("{$key}{$greeting}", $params); |
| 612 | $customGreeting = CRM_Utils_Array::value("{$key}{$greeting}_custom", $params); |
| 613 | |
| 614 | if (!$greetingId && $greetingVal) { |
| 615 | $params["{$key}{$greeting}_id"] = CRM_Utils_Array::key($params["{$key}{$greeting}"], $greetings); |
| 616 | } |
| 617 | |
| 618 | if ($customGreeting && $greetingId && |
| 619 | ($greetingId != array_search('Customized', $greetings)) |
| 620 | ) { |
| 621 | throw new API_Exception(ts('Provide either %1 greeting id and/or %1 greeting or custom %1 greeting', |
| 622 | [1 => $key] |
| 623 | )); |
| 624 | } |
| 625 | |
| 626 | if ($greetingVal && $greetingId && |
| 627 | ($greetingId != CRM_Utils_Array::key($greetingVal, $greetings)) |
| 628 | ) { |
| 629 | throw new API_Exception(ts('Mismatch in %1 greeting id and %1 greeting', |
| 630 | [1 => $key] |
| 631 | )); |
| 632 | } |
| 633 | |
| 634 | if ($greetingId) { |
| 635 | if (!$customGreeting && ($greetingId == array_search('Customized', $greetings))) { |
| 636 | throw new API_Exception(ts('Please provide a custom value for %1 greeting', |
| 637 | [1 => $key] |
| 638 | )); |
| 639 | } |
| 640 | } |
| 641 | elseif ($greetingVal) { |
| 642 | |
| 643 | if (!in_array($greetingVal, $greetings)) { |
| 644 | throw new API_Exception(ts('Invalid %1 greeting', [1 => $key])); |
| 645 | } |
| 646 | |
| 647 | $greetingId = CRM_Utils_Array::key($greetingVal, $greetings); |
| 648 | } |
| 649 | |
| 650 | if ($customGreeting) { |
| 651 | $greetingId = CRM_Utils_Array::key('Customized', $greetings); |
| 652 | } |
| 653 | |
| 654 | $customValue = isset($params['contact_id']) ? CRM_Core_DAO::getFieldValue( |
| 655 | 'CRM_Contact_DAO_Contact', |
| 656 | $params['contact_id'], |
| 657 | "{$key}{$greeting}_custom" |
| 658 | ) : FALSE; |
| 659 | |
| 660 | if (array_key_exists("{$key}{$greeting}_id", $params) && empty($params["{$key}{$greeting}_id"])) { |
| 661 | $nullValue = TRUE; |
| 662 | } |
| 663 | elseif (array_key_exists("{$key}{$greeting}", $params) && empty($params["{$key}{$greeting}"])) { |
| 664 | $nullValue = TRUE; |
| 665 | } |
| 666 | elseif ($customValue && array_key_exists("{$key}{$greeting}_custom", $params) |
| 667 | && empty($params["{$key}{$greeting}_custom"]) |
| 668 | ) { |
| 669 | $nullValue = TRUE; |
| 670 | } |
| 671 | |
| 672 | $params["{$key}{$greeting}_id"] = $greetingId; |
| 673 | |
| 674 | if (!$customValue && !$customGreeting && array_key_exists("{$key}{$greeting}_custom", $params)) { |
| 675 | unset($params["{$key}{$greeting}_custom"]); |
| 676 | } |
| 677 | |
| 678 | if ($nullValue) { |
| 679 | $params["{$key}{$greeting}_id"] = ''; |
| 680 | $params["{$key}{$greeting}_custom"] = ''; |
| 681 | } |
| 682 | |
| 683 | if (isset($params["{$key}{$greeting}"])) { |
| 684 | unset($params["{$key}{$greeting}"]); |
| 685 | } |
| 686 | } |
| 687 | } |
| 688 | |
| 689 | /** |
| 690 | * Adjust Metadata for Get action. |
| 691 | * |
| 692 | * @param array $params |
| 693 | * Array of parameters determined by getfields. |
| 694 | */ |
| 695 | function _civicrm_api3_contact_getquick_spec(&$params) { |
| 696 | $params['name']['api.required'] = TRUE; |
| 697 | $params['name']['title'] = ts('String to search on'); |
| 698 | $params['name']['type'] = CRM_Utils_Type::T_STRING; |
| 699 | $params['field']['type'] = CRM_Utils_Type::T_STRING; |
| 700 | $params['field']['title'] = ts('Field to search on'); |
| 701 | $params['field']['options'] = [ |
| 702 | '', |
| 703 | 'id', |
| 704 | 'contact_id', |
| 705 | 'external_identifier', |
| 706 | 'first_name', |
| 707 | 'last_name', |
| 708 | 'job_title', |
| 709 | 'postal_code', |
| 710 | 'street_address', |
| 711 | 'email', |
| 712 | 'city', |
| 713 | 'phone_numeric', |
| 714 | ]; |
| 715 | $params['table_name']['type'] = CRM_Utils_Type::T_STRING; |
| 716 | $params['table_name']['title'] = ts('Table alias to search on'); |
| 717 | $params['table_name']['api.default'] = 'cc'; |
| 718 | } |
| 719 | |
| 720 | /** |
| 721 | * Old Contact quick search api. |
| 722 | * |
| 723 | * @deprecated |
| 724 | * |
| 725 | * @param array $params |
| 726 | * |
| 727 | * @return array |
| 728 | * @throws \API_Exception |
| 729 | */ |
| 730 | function civicrm_api3_contact_getquick($params) { |
| 731 | $name = CRM_Utils_Type::escape(CRM_Utils_Array::value('name', $params), 'String'); |
| 732 | $table_name = CRM_Utils_String::munge($params['table_name']); |
| 733 | // get the autocomplete options from settings |
| 734 | $acpref = explode(CRM_Core_DAO::VALUE_SEPARATOR, |
| 735 | CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, |
| 736 | 'contact_autocomplete_options' |
| 737 | ) |
| 738 | ); |
| 739 | |
| 740 | $table_names = [ |
| 741 | 'email' => 'eml', |
| 742 | 'phone_numeric' => 'phe', |
| 743 | 'street_address' => 'sts', |
| 744 | 'city' => 'sts', |
| 745 | 'postal_code' => 'sts', |
| 746 | ]; |
| 747 | |
| 748 | // get the option values for contact autocomplete |
| 749 | $acOptions = CRM_Core_OptionGroup::values('contact_autocomplete_options', FALSE, FALSE, FALSE, NULL, 'name'); |
| 750 | |
| 751 | $list = $from = []; |
| 752 | foreach ($acpref as $value) { |
| 753 | if ($value && !empty($acOptions[$value])) { |
| 754 | $list[$value] = $acOptions[$value]; |
| 755 | } |
| 756 | } |
| 757 | // If we are doing quicksearch by a field other than name, make sure that field is added to results |
| 758 | if (!empty($params['field_name'])) { |
| 759 | $field_name = CRM_Utils_String::munge($params['field_name']); |
| 760 | // Unique name contact_id = id |
| 761 | if ($field_name == 'contact_id') { |
| 762 | $field_name = 'id'; |
| 763 | } |
| 764 | // core#1420 : trim non-numeric character from phone search string |
| 765 | elseif ($field_name == 'phone_numeric') { |
| 766 | $name = preg_replace('/[^\d]/', '', $name); |
| 767 | } |
| 768 | if (isset($table_names[$field_name])) { |
| 769 | $table_name = $table_names[$field_name]; |
| 770 | } |
| 771 | elseif (strpos($field_name, 'custom_') === 0) { |
| 772 | $customField = civicrm_api3('CustomField', 'getsingle', [ |
| 773 | 'id' => substr($field_name, 7), |
| 774 | 'return' => [ |
| 775 | 'custom_group_id.table_name', |
| 776 | 'column_name', |
| 777 | 'data_type', |
| 778 | 'option_group_id', |
| 779 | 'html_type', |
| 780 | ], |
| 781 | ]); |
| 782 | $field_name = $customField['column_name']; |
| 783 | $table_name = CRM_Utils_String::munge($customField['custom_group_id.table_name']); |
| 784 | $from[$field_name] = "LEFT JOIN `$table_name` ON cc.id = `$table_name`.entity_id"; |
| 785 | if (CRM_Core_BAO_CustomField::hasOptions($customField)) { |
| 786 | $customOptionsWhere = []; |
| 787 | $customFieldOptions = CRM_Contact_BAO_Contact::buildOptions('custom_' . $customField['id'], 'search'); |
| 788 | $isMultivalueField = CRM_Core_BAO_CustomField::isSerialized($customField); |
| 789 | $sep = CRM_Core_DAO::VALUE_SEPARATOR; |
| 790 | foreach ($customFieldOptions as $optionKey => $optionLabel) { |
| 791 | if (mb_stripos($optionLabel, $name) !== FALSE) { |
| 792 | $customOptionsWhere[$optionKey] = "$table_name.$field_name " . ($isMultivalueField ? "LIKE '%{$sep}{$optionKey}{$sep}%'" : "= '$optionKey'"); |
| 793 | } |
| 794 | } |
| 795 | } |
| 796 | } |
| 797 | // phone_numeric should be phone |
| 798 | $searchField = str_replace('_numeric', '', $field_name); |
| 799 | if (!in_array($searchField, $list)) { |
| 800 | $list[] = $searchField; |
| 801 | } |
| 802 | } |
| 803 | else { |
| 804 | // Set field name to first name for exact match checking. |
| 805 | $field_name = 'sort_name'; |
| 806 | } |
| 807 | |
| 808 | $select = $actualSelectElements = ['sort_name']; |
| 809 | $where = ''; |
| 810 | foreach ($list as $value) { |
| 811 | $suffix = substr($value, 0, 2) . substr($value, -1); |
| 812 | switch ($value) { |
| 813 | case 'street_address': |
| 814 | case 'city': |
| 815 | case 'postal_code': |
| 816 | $selectText = $value; |
| 817 | $value = "address"; |
| 818 | $suffix = 'sts'; |
| 819 | case 'phone': |
| 820 | case 'email': |
| 821 | $actualSelectElements[] = $select[] = ($value == 'address') ? $selectText : $value; |
| 822 | if ($value == 'phone') { |
| 823 | $actualSelectElements[] = $select[] = 'phone_ext'; |
| 824 | } |
| 825 | $from[$value] = "LEFT JOIN civicrm_{$value} {$suffix} ON ( cc.id = {$suffix}.contact_id AND {$suffix}.is_primary = 1 ) "; |
| 826 | break; |
| 827 | |
| 828 | case 'country': |
| 829 | case 'state_province': |
| 830 | $select[] = "{$suffix}.name as {$value}"; |
| 831 | $actualSelectElements[] = "{$suffix}.name"; |
| 832 | if (!in_array('address', $from)) { |
| 833 | $from['address'] = 'LEFT JOIN civicrm_address sts ON ( cc.id = sts.contact_id AND sts.is_primary = 1) '; |
| 834 | } |
| 835 | $from[$value] = " LEFT JOIN civicrm_{$value} {$suffix} ON ( sts.{$value}_id = {$suffix}.id ) "; |
| 836 | break; |
| 837 | |
| 838 | default: |
| 839 | if ($value == 'id') { |
| 840 | $actualSelectElements[] = 'cc.id'; |
| 841 | } |
| 842 | elseif ($value != 'sort_name') { |
| 843 | $suffix = 'cc'; |
| 844 | if ($field_name == $value) { |
| 845 | $suffix = $table_name; |
| 846 | } |
| 847 | $actualSelectElements[] = $select[] = $suffix . '.' . $value; |
| 848 | } |
| 849 | break; |
| 850 | } |
| 851 | } |
| 852 | |
| 853 | $config = CRM_Core_Config::singleton(); |
| 854 | $as = $select; |
| 855 | $select = implode(', ', $select); |
| 856 | if (!empty($select)) { |
| 857 | $select = ", $select"; |
| 858 | } |
| 859 | $actualSelectElements = implode(', ', $actualSelectElements); |
| 860 | $from = implode(' ', $from); |
| 861 | $limit = (int) ($params['limit'] ?? 0); |
| 862 | $limit = $limit > 0 ? $limit : Civi::settings()->get('search_autocomplete_count'); |
| 863 | |
| 864 | // add acl clause here |
| 865 | list($aclFrom, $aclWhere) = CRM_Contact_BAO_Contact_Permission::cacheClause('cc'); |
| 866 | |
| 867 | if ($aclWhere) { |
| 868 | $where .= " AND $aclWhere "; |
| 869 | } |
| 870 | $isPrependWildcard = \Civi::settings()->get('includeWildCardInName'); |
| 871 | |
| 872 | if (!empty($params['org'])) { |
| 873 | $where .= " AND contact_type = \"Organization\""; |
| 874 | |
| 875 | // CRM-7157, hack: get current employer details when |
| 876 | // employee_id is present. |
| 877 | $currEmpDetails = []; |
| 878 | if (!empty($params['employee_id'])) { |
| 879 | if ($currentEmployer = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', |
| 880 | (int) $params['employee_id'], |
| 881 | 'employer_id' |
| 882 | )) { |
| 883 | if ($isPrependWildcard) { |
| 884 | $strSearch = "%$name%"; |
| 885 | } |
| 886 | else { |
| 887 | $strSearch = "$name%"; |
| 888 | } |
| 889 | |
| 890 | // get current employer details |
| 891 | $dao = CRM_Core_DAO::executeQuery("SELECT cc.id as id, CONCAT_WS( ' :: ', {$actualSelectElements} ) as data, sort_name |
| 892 | FROM civicrm_contact cc {$from} WHERE cc.contact_type = \"Organization\" AND cc.id = {$currentEmployer} AND cc.sort_name LIKE '$strSearch'"); |
| 893 | if ($dao->fetch()) { |
| 894 | $currEmpDetails = [ |
| 895 | 'id' => $dao->id, |
| 896 | 'data' => $dao->data, |
| 897 | ]; |
| 898 | } |
| 899 | } |
| 900 | } |
| 901 | } |
| 902 | |
| 903 | if (!empty($params['contact_sub_type'])) { |
| 904 | $contactSubType = CRM_Utils_Type::escape($params['contact_sub_type'], 'String'); |
| 905 | $where .= " AND cc.contact_sub_type = '{$contactSubType}'"; |
| 906 | } |
| 907 | |
| 908 | if (!empty($params['contact_type'])) { |
| 909 | $contactType = CRM_Utils_Type::escape($params['contact_type'], 'String'); |
| 910 | $where .= " AND cc.contact_type LIKE '{$contactType}'"; |
| 911 | } |
| 912 | |
| 913 | // Set default for current_employer or return contact with particular id |
| 914 | if (!empty($params['id'])) { |
| 915 | $where .= " AND cc.id = " . (int) $params['id']; |
| 916 | } |
| 917 | |
| 918 | if (!empty($params['cid'])) { |
| 919 | $where .= " AND cc.id <> " . (int) $params['cid']; |
| 920 | } |
| 921 | |
| 922 | // Contact's based of relationhip type |
| 923 | $relType = NULL; |
| 924 | if (!empty($params['rel'])) { |
| 925 | $relation = explode('_', CRM_Utils_Array::value('rel', $params)); |
| 926 | $relType = CRM_Utils_Type::escape($relation[0], 'Integer'); |
| 927 | $rel = CRM_Utils_Type::escape($relation[2], 'String'); |
| 928 | } |
| 929 | |
| 930 | if ($isPrependWildcard) { |
| 931 | $strSearch = "%$name%"; |
| 932 | } |
| 933 | else { |
| 934 | $strSearch = "$name%"; |
| 935 | } |
| 936 | $includeEmailFrom = $includeNickName = ''; |
| 937 | if ($config->includeNickNameInName) { |
| 938 | $includeNickName = " OR nick_name LIKE '$strSearch'"; |
| 939 | } |
| 940 | |
| 941 | if (isset($customOptionsWhere)) { |
| 942 | $customOptionsWhere = $customOptionsWhere ?: [0]; |
| 943 | $whereClause = " WHERE (" . implode(' OR ', $customOptionsWhere) . ") $where"; |
| 944 | } |
| 945 | elseif (!empty($params['field_name']) && !empty($params['table_name']) && $params['field_name'] != 'sort_name') { |
| 946 | $whereClause = " WHERE ( $table_name.$field_name LIKE '$strSearch') {$where}"; |
| 947 | // Search by id should be exact |
| 948 | if ($field_name == 'id' || $field_name == 'external_identifier') { |
| 949 | $whereClause = " WHERE ( $table_name.$field_name = '$name') {$where}"; |
| 950 | } |
| 951 | } |
| 952 | else { |
| 953 | $whereClause = " WHERE ( sort_name LIKE '$strSearch' $includeNickName ) {$where} "; |
| 954 | if ($config->includeEmailInName) { |
| 955 | if (!in_array('email', $list)) { |
| 956 | $includeEmailFrom = "LEFT JOIN civicrm_email eml ON ( cc.id = eml.contact_id AND eml.is_primary = 1 )"; |
| 957 | } |
| 958 | $emailWhere = " WHERE email LIKE '$strSearch'"; |
| 959 | } |
| 960 | } |
| 961 | |
| 962 | $additionalFrom = ''; |
| 963 | if ($relType) { |
| 964 | $additionalFrom = " |
| 965 | INNER JOIN civicrm_relationship_type r ON ( |
| 966 | r.id = {$relType} |
| 967 | AND ( cc.contact_type = r.contact_type_{$rel} OR r.contact_type_{$rel} IS NULL ) |
| 968 | AND ( cc.contact_sub_type = r.contact_sub_type_{$rel} OR r.contact_sub_type_{$rel} IS NULL ) |
| 969 | )"; |
| 970 | } |
| 971 | |
| 972 | // check if only CMS users are requested |
| 973 | if (!empty($params['cmsuser'])) { |
| 974 | $additionalFrom = " |
| 975 | INNER JOIN civicrm_uf_match um ON (um.contact_id=cc.id) |
| 976 | "; |
| 977 | } |
| 978 | $orderBy = _civicrm_api3_quicksearch_get_order_by($name, $isPrependWildcard, $field_name); |
| 979 | |
| 980 | //CRM-5954 |
| 981 | $query = " |
| 982 | SELECT DISTINCT(id), data, sort_name, exactFirst |
| 983 | FROM ( |
| 984 | ( SELECT IF($table_name.$field_name = '{$name}', 0, 1) as exactFirst, cc.id as id, CONCAT_WS( ' :: ', |
| 985 | {$actualSelectElements} ) |
| 986 | as data |
| 987 | {$select} |
| 988 | FROM civicrm_contact cc {$from} |
| 989 | {$aclFrom} |
| 990 | {$additionalFrom} |
| 991 | {$whereClause} |
| 992 | {$orderBy} |
| 993 | LIMIT 0, {$limit} ) |
| 994 | "; |
| 995 | |
| 996 | if (!empty($emailWhere)) { |
| 997 | $query .= " |
| 998 | UNION ( |
| 999 | SELECT IF($table_name.$field_name = '{$name}', 0, 1) as exactFirst, cc.id as id, CONCAT_WS( ' :: ', |
| 1000 | {$actualSelectElements} ) |
| 1001 | as data |
| 1002 | {$select} |
| 1003 | FROM civicrm_contact cc {$from} |
| 1004 | {$aclFrom} |
| 1005 | {$additionalFrom} {$includeEmailFrom} |
| 1006 | {$emailWhere} AND cc.is_deleted = 0 " . ($aclWhere ? " AND $aclWhere " : '') . " |
| 1007 | {$orderBy} |
| 1008 | LIMIT 0, {$limit} |
| 1009 | ) |
| 1010 | "; |
| 1011 | } |
| 1012 | $query .= ") t |
| 1013 | {$orderBy} |
| 1014 | LIMIT 0, {$limit} |
| 1015 | "; |
| 1016 | |
| 1017 | // send query to hook to be modified if needed |
| 1018 | CRM_Utils_Hook::contactListQuery($query, |
| 1019 | $name, |
| 1020 | empty($params['context']) ? NULL : CRM_Utils_Type::escape($params['context'], 'String'), |
| 1021 | empty($params['id']) ? NULL : $params['id'] |
| 1022 | ); |
| 1023 | |
| 1024 | $dao = CRM_Core_DAO::executeQuery($query); |
| 1025 | |
| 1026 | $contactList = []; |
| 1027 | $listCurrentEmployer = TRUE; |
| 1028 | while ($dao->fetch()) { |
| 1029 | $t = ['id' => $dao->id]; |
| 1030 | foreach ($as as $k) { |
| 1031 | $t[$k] = isset($dao->$k) ? $dao->$k : ''; |
| 1032 | } |
| 1033 | $t['data'] = $dao->data; |
| 1034 | // Replace keys with values when displaying fields from an option list |
| 1035 | if (!empty($customOptionsWhere)) { |
| 1036 | $data = explode(' :: ', $dao->data); |
| 1037 | $pos = count($data) - 1; |
| 1038 | $customValue = array_intersect(CRM_Utils_Array::explodePadded($data[$pos]), array_keys($customOptionsWhere)); |
| 1039 | $data[$pos] = implode(', ', array_intersect_key($customFieldOptions, array_flip($customValue))); |
| 1040 | $t['data'] = implode(' :: ', $data); |
| 1041 | } |
| 1042 | $contactList[] = $t; |
| 1043 | if (!empty($params['org']) && |
| 1044 | !empty($currEmpDetails) && |
| 1045 | $dao->id == $currEmpDetails['id'] |
| 1046 | ) { |
| 1047 | $listCurrentEmployer = FALSE; |
| 1048 | } |
| 1049 | } |
| 1050 | |
| 1051 | //return organization name if doesn't exist in db |
| 1052 | if (empty($contactList)) { |
| 1053 | if (!empty($params['org'])) { |
| 1054 | if ($listCurrentEmployer && !empty($currEmpDetails)) { |
| 1055 | $contactList = [ |
| 1056 | [ |
| 1057 | 'data' => $currEmpDetails['data'], |
| 1058 | 'id' => $currEmpDetails['id'], |
| 1059 | ], |
| 1060 | ]; |
| 1061 | } |
| 1062 | else { |
| 1063 | $contactList = [ |
| 1064 | [ |
| 1065 | 'data' => $name, |
| 1066 | 'id' => $name, |
| 1067 | ], |
| 1068 | ]; |
| 1069 | } |
| 1070 | } |
| 1071 | } |
| 1072 | |
| 1073 | return civicrm_api3_create_success($contactList, $params, 'Contact', 'getquick'); |
| 1074 | } |
| 1075 | |
| 1076 | /** |
| 1077 | * Get the order by string for the quicksearch query. |
| 1078 | * |
| 1079 | * Get the order by string. The string might be |
| 1080 | * - sort name if there is no search value provided and the site is configured |
| 1081 | * to search by sort name |
| 1082 | * - empty if there is no search value provided and the site is not configured |
| 1083 | * to search by sort name |
| 1084 | * - exactFirst and then sort name if a search value is provided and the site is configured |
| 1085 | * to search by sort name |
| 1086 | * - exactFirst if a search value is provided and the site is not configured |
| 1087 | * to search by sort name |
| 1088 | * |
| 1089 | * exactFirst means 'yes if the search value exactly matches the searched field. else no'. |
| 1090 | * It is intended to prioritise exact matches for the entered string so on a first name search |
| 1091 | * for 'kath' contacts with a first name of exactly Kath rise to the top. |
| 1092 | * |
| 1093 | * On short strings it is expensive. Per CRM-19547 there is still an open question |
| 1094 | * as to whether we should only do exactMatch on a minimum length or on certain fields. |
| 1095 | * |
| 1096 | * However, we have mitigated this somewhat by not doing an exact match search on |
| 1097 | * empty strings, non-wildcard sort-name searches and email searches where there is |
| 1098 | * no @ after the first character. |
| 1099 | * |
| 1100 | * For the user it is further mitigated by the fact they just don't know the |
| 1101 | * slower queries are firing. If they type 'smit' slowly enough 4 queries will trigger |
| 1102 | * but if the first 3 are slow the first result they see may be off the 4th query. |
| 1103 | * |
| 1104 | * @param string $name |
| 1105 | * @param bool $isPrependWildcard |
| 1106 | * @param string $field_name |
| 1107 | * |
| 1108 | * @return string |
| 1109 | */ |
| 1110 | function _civicrm_api3_quicksearch_get_order_by($name, $isPrependWildcard, $field_name) { |
| 1111 | $skipExactMatch = ($name === '%'); |
| 1112 | if ($field_name === 'email' && !strpos('@', $name)) { |
| 1113 | $skipExactMatch = TRUE; |
| 1114 | } |
| 1115 | |
| 1116 | if (!\Civi::settings()->get('includeOrderByClause')) { |
| 1117 | return $skipExactMatch ? '' : "ORDER BY exactFirst"; |
| 1118 | } |
| 1119 | if ($skipExactMatch || (!$isPrependWildcard && $field_name === 'sort_name')) { |
| 1120 | // If there is no wildcard then sorting by exactFirst would have the same |
| 1121 | // effect as just a sort_name search, but slower. |
| 1122 | return "ORDER BY sort_name"; |
| 1123 | } |
| 1124 | |
| 1125 | return "ORDER BY exactFirst, sort_name"; |
| 1126 | } |
| 1127 | |
| 1128 | /** |
| 1129 | * Declare deprecated api functions. |
| 1130 | * |
| 1131 | * @deprecated api notice |
| 1132 | * @return array |
| 1133 | * Array of deprecated actions |
| 1134 | */ |
| 1135 | function _civicrm_api3_contact_deprecation() { |
| 1136 | return ['getquick' => 'The "getquick" action is deprecated in favor of "getlist".']; |
| 1137 | } |
| 1138 | |
| 1139 | /** |
| 1140 | * Merges given pair of duplicate contacts. |
| 1141 | * |
| 1142 | * @param array $params |
| 1143 | * Allowed array keys are: |
| 1144 | * -int main_id: main contact id with whom merge has to happen |
| 1145 | * -int other_id: duplicate contact which would be deleted after merge operation |
| 1146 | * -string mode: "safe" skips the merge if there are no conflicts. Does a force merge otherwise. |
| 1147 | * |
| 1148 | * @return array |
| 1149 | * API Result Array |
| 1150 | * @throws API_Exception |
| 1151 | */ |
| 1152 | function civicrm_api3_contact_merge($params) { |
| 1153 | if (($result = CRM_Dedupe_Merger::merge( |
| 1154 | [['srcID' => $params['to_remove_id'], 'dstID' => $params['to_keep_id']]], |
| 1155 | [], |
| 1156 | $params['mode'], |
| 1157 | FALSE, |
| 1158 | CRM_Utils_Array::value('check_permissions', $params) |
| 1159 | )) != FALSE) { |
| 1160 | |
| 1161 | return civicrm_api3_create_success($result, $params); |
| 1162 | } |
| 1163 | throw new API_Exception('Merge failed'); |
| 1164 | } |
| 1165 | |
| 1166 | /** |
| 1167 | * Adjust metadata for contact_merge api function. |
| 1168 | * |
| 1169 | * @param array $params |
| 1170 | */ |
| 1171 | function _civicrm_api3_contact_merge_spec(&$params) { |
| 1172 | $params['to_remove_id'] = [ |
| 1173 | 'title' => ts('ID of the contact to merge & remove'), |
| 1174 | 'description' => ts('Wow - these 2 aliased params are the logical reverse of what I expect - but what to do?'), |
| 1175 | 'api.required' => 1, |
| 1176 | 'type' => CRM_Utils_Type::T_INT, |
| 1177 | 'api.aliases' => ['main_id'], |
| 1178 | ]; |
| 1179 | $params['to_keep_id'] = [ |
| 1180 | 'title' => ts('ID of the contact to keep'), |
| 1181 | 'description' => ts('Wow - these 2 aliased params are the logical reverse of what I expect - but what to do?'), |
| 1182 | 'api.required' => 1, |
| 1183 | 'type' => CRM_Utils_Type::T_INT, |
| 1184 | 'api.aliases' => ['other_id'], |
| 1185 | ]; |
| 1186 | $params['mode'] = [ |
| 1187 | 'title' => ts('Dedupe mode'), |
| 1188 | 'description' => ts("In 'safe' mode conflicts will result in no merge. In 'aggressive' mode the merge will still proceed (hook dependent)"), |
| 1189 | 'api.default' => 'safe', |
| 1190 | 'options' => ['safe' => ts('Abort on unhandled conflict'), 'aggressive' => ts('Proceed on unhandled conflict. Note hooks may change handling here.')], |
| 1191 | ]; |
| 1192 | } |
| 1193 | |
| 1194 | /** |
| 1195 | * Determines if given pair of contaacts have conflicts that would affect merging them. |
| 1196 | * |
| 1197 | * @param array $params |
| 1198 | * Allowed array keys are: |
| 1199 | * -int main_id: main contact id with whom merge has to happen |
| 1200 | * -int other_id: duplicate contact which would be deleted after merge operation |
| 1201 | * -string mode: "safe" skips the merge if there are no conflicts. Does a force merge otherwise. |
| 1202 | * |
| 1203 | * @return array |
| 1204 | * API Result Array |
| 1205 | * |
| 1206 | * @throws \CRM_Core_Exception |
| 1207 | * @throws \CiviCRM_API3_Exception |
| 1208 | * @throws \API_Exception |
| 1209 | */ |
| 1210 | function civicrm_api3_contact_get_merge_conflicts($params) { |
| 1211 | $migrationInfo = []; |
| 1212 | $result = []; |
| 1213 | foreach ((array) $params['mode'] as $mode) { |
| 1214 | $result[$mode] = CRM_Dedupe_Merger::getConflicts( |
| 1215 | $migrationInfo, |
| 1216 | $params['to_remove_id'], $params['to_keep_id'], |
| 1217 | $mode |
| 1218 | ); |
| 1219 | } |
| 1220 | return civicrm_api3_create_success($result, $params); |
| 1221 | } |
| 1222 | |
| 1223 | /** |
| 1224 | * Adjust metadata for contact_merge api function. |
| 1225 | * |
| 1226 | * @param array $params |
| 1227 | */ |
| 1228 | function _civicrm_api3_contact_get_merge_conflicts_spec(&$params) { |
| 1229 | $params['to_remove_id'] = [ |
| 1230 | 'title' => ts('ID of the contact to merge & remove'), |
| 1231 | 'api.required' => 1, |
| 1232 | 'type' => CRM_Utils_Type::T_INT, |
| 1233 | ]; |
| 1234 | $params['to_keep_id'] = [ |
| 1235 | 'title' => ts('ID of the contact to keep'), |
| 1236 | 'api.required' => 1, |
| 1237 | 'type' => CRM_Utils_Type::T_INT, |
| 1238 | ]; |
| 1239 | $params['mode'] = [ |
| 1240 | 'title' => ts('Dedupe mode'), |
| 1241 | 'description' => ts("'safe' or 'aggressive' - these modes map to the merge actions & may affect resolution done by hooks "), |
| 1242 | 'api.default' => 'safe', |
| 1243 | ]; |
| 1244 | } |
| 1245 | |
| 1246 | /** |
| 1247 | * Get the ultimate contact a contact was merged to. |
| 1248 | * |
| 1249 | * @param array $params |
| 1250 | * |
| 1251 | * @return array |
| 1252 | * API Result Array |
| 1253 | * @throws API_Exception |
| 1254 | */ |
| 1255 | function civicrm_api3_contact_getmergedto($params) { |
| 1256 | $contactID = _civicrm_api3_contact_getmergedto($params); |
| 1257 | if ($contactID) { |
| 1258 | $values = [$contactID => ['id' => $contactID]]; |
| 1259 | } |
| 1260 | else { |
| 1261 | $values = []; |
| 1262 | } |
| 1263 | return civicrm_api3_create_success($values, $params); |
| 1264 | } |
| 1265 | |
| 1266 | /** |
| 1267 | * Get the contact our contact was finally merged to. |
| 1268 | * |
| 1269 | * If the contact has been merged multiple times the crucial parent activity will have |
| 1270 | * wound up on the ultimate contact so we can figure out the final resting place of the |
| 1271 | * contact with only 2 activities even if 50 merges took place. |
| 1272 | * |
| 1273 | * @param array $params |
| 1274 | * |
| 1275 | * @return int|false |
| 1276 | */ |
| 1277 | function _civicrm_api3_contact_getmergedto($params) { |
| 1278 | $contactID = FALSE; |
| 1279 | $deleteActivity = civicrm_api3('ActivityContact', 'get', [ |
| 1280 | 'contact_id' => $params['contact_id'], |
| 1281 | 'activity_id.activity_type_id' => 'Contact Deleted By Merge', |
| 1282 | 'is_deleted' => 0, |
| 1283 | 'is_test' => $params['is_test'], |
| 1284 | 'record_type_id' => 'Activity Targets', |
| 1285 | 'return' => ['activity_id.parent_id'], |
| 1286 | 'sequential' => 1, |
| 1287 | 'options' => [ |
| 1288 | 'limit' => 1, |
| 1289 | 'sort' => 'activity_id.activity_date_time DESC', |
| 1290 | ], |
| 1291 | ])['values']; |
| 1292 | if (!empty($deleteActivity)) { |
| 1293 | $contactID = civicrm_api3('ActivityContact', 'getvalue', [ |
| 1294 | 'activity_id' => $deleteActivity[0]['activity_id.parent_id'], |
| 1295 | 'record_type_id' => 'Activity Targets', |
| 1296 | 'return' => 'contact_id', |
| 1297 | ]); |
| 1298 | } |
| 1299 | return $contactID; |
| 1300 | } |
| 1301 | |
| 1302 | /** |
| 1303 | * Adjust metadata for contact_merge api function. |
| 1304 | * |
| 1305 | * @param array $params |
| 1306 | */ |
| 1307 | function _civicrm_api3_contact_getmergedto_spec(&$params) { |
| 1308 | $params['contact_id'] = [ |
| 1309 | 'title' => ts('ID of contact to find ultimate contact for'), |
| 1310 | 'type' => CRM_Utils_Type::T_INT, |
| 1311 | 'api.required' => TRUE, |
| 1312 | ]; |
| 1313 | $params['is_test'] = [ |
| 1314 | 'title' => ts('Get test deletions rather than live?'), |
| 1315 | 'type' => CRM_Utils_Type::T_BOOLEAN, |
| 1316 | 'api.default' => 0, |
| 1317 | ]; |
| 1318 | } |
| 1319 | |
| 1320 | /** |
| 1321 | * Get the ultimate contact a contact was merged to. |
| 1322 | * |
| 1323 | * @param array $params |
| 1324 | * |
| 1325 | * @return array |
| 1326 | * API Result Array |
| 1327 | * @throws API_Exception |
| 1328 | */ |
| 1329 | function civicrm_api3_contact_getmergedfrom($params) { |
| 1330 | $contacts = _civicrm_api3_contact_getmergedfrom($params); |
| 1331 | return civicrm_api3_create_success($contacts, $params); |
| 1332 | } |
| 1333 | |
| 1334 | /** |
| 1335 | * Get all the contacts merged into our contact. |
| 1336 | * |
| 1337 | * @param array $params |
| 1338 | * |
| 1339 | * @return array |
| 1340 | */ |
| 1341 | function _civicrm_api3_contact_getmergedfrom($params) { |
| 1342 | $activities = []; |
| 1343 | $deleteActivities = civicrm_api3('ActivityContact', 'get', [ |
| 1344 | 'contact_id' => $params['contact_id'], |
| 1345 | 'activity_id.activity_type_id' => 'Contact Merged', |
| 1346 | 'is_deleted' => 0, |
| 1347 | 'is_test' => $params['is_test'], |
| 1348 | 'record_type_id' => 'Activity Targets', |
| 1349 | 'return' => 'activity_id', |
| 1350 | ])['values']; |
| 1351 | |
| 1352 | foreach ($deleteActivities as $deleteActivity) { |
| 1353 | $activities[] = $deleteActivity['activity_id']; |
| 1354 | } |
| 1355 | if (empty($activities)) { |
| 1356 | return []; |
| 1357 | } |
| 1358 | |
| 1359 | $activityContacts = civicrm_api3('ActivityContact', 'get', [ |
| 1360 | 'activity_id.parent_id' => ['IN' => $activities], |
| 1361 | 'record_type_id' => 'Activity Targets', |
| 1362 | 'return' => 'contact_id', |
| 1363 | ])['values']; |
| 1364 | $contacts = []; |
| 1365 | foreach ($activityContacts as $activityContact) { |
| 1366 | $contacts[$activityContact['contact_id']] = ['id' => $activityContact['contact_id']]; |
| 1367 | } |
| 1368 | return $contacts; |
| 1369 | } |
| 1370 | |
| 1371 | /** |
| 1372 | * Adjust metadata for contact_merge api function. |
| 1373 | * |
| 1374 | * @param array $params |
| 1375 | */ |
| 1376 | function _civicrm_api3_contact_getmergedfrom_spec(&$params) { |
| 1377 | $params['contact_id'] = [ |
| 1378 | 'title' => ts('ID of contact to find ultimate contact for'), |
| 1379 | 'type' => CRM_Utils_Type::T_INT, |
| 1380 | 'api.required' => TRUE, |
| 1381 | ]; |
| 1382 | $params['is_test'] = [ |
| 1383 | 'title' => ts('Get test deletions rather than live?'), |
| 1384 | 'type' => CRM_Utils_Type::T_BOOLEAN, |
| 1385 | 'api.default' => 0, |
| 1386 | ]; |
| 1387 | } |
| 1388 | |
| 1389 | /** |
| 1390 | * Adjust metadata for contact_proximity api function. |
| 1391 | * |
| 1392 | * @param array $params |
| 1393 | */ |
| 1394 | function _civicrm_api3_contact_proximity_spec(&$params) { |
| 1395 | $params['latitude'] = [ |
| 1396 | 'title' => 'Latitude', |
| 1397 | 'api.required' => 1, |
| 1398 | 'type' => CRM_Utils_Type::T_STRING, |
| 1399 | ]; |
| 1400 | $params['longitude'] = [ |
| 1401 | 'title' => 'Longitude', |
| 1402 | 'api.required' => 1, |
| 1403 | 'type' => CRM_Utils_Type::T_STRING, |
| 1404 | ]; |
| 1405 | |
| 1406 | $params['unit'] = [ |
| 1407 | 'title' => 'Unit of Measurement', |
| 1408 | 'api.default' => 'meter', |
| 1409 | 'type' => CRM_Utils_Type::T_STRING, |
| 1410 | ]; |
| 1411 | } |
| 1412 | |
| 1413 | /** |
| 1414 | * Get contacts by proximity. |
| 1415 | * |
| 1416 | * @param array $params |
| 1417 | * |
| 1418 | * @return array |
| 1419 | * @throws Exception |
| 1420 | */ |
| 1421 | function civicrm_api3_contact_proximity($params) { |
| 1422 | $latitude = CRM_Utils_Array::value('latitude', $params); |
| 1423 | $longitude = CRM_Utils_Array::value('longitude', $params); |
| 1424 | $distance = CRM_Utils_Array::value('distance', $params); |
| 1425 | |
| 1426 | $unit = CRM_Utils_Array::value('unit', $params); |
| 1427 | |
| 1428 | // check and ensure that lat/long and distance are floats |
| 1429 | if ( |
| 1430 | !CRM_Utils_Rule::numeric($latitude) || |
| 1431 | !CRM_Utils_Rule::numeric($longitude) || |
| 1432 | !CRM_Utils_Rule::numeric($distance) |
| 1433 | ) { |
| 1434 | throw new Exception(ts('Latitude, Longitude and Distance should exist and be numeric')); |
| 1435 | } |
| 1436 | |
| 1437 | if ($unit == "mile") { |
| 1438 | $conversionFactor = 1609.344; |
| 1439 | } |
| 1440 | else { |
| 1441 | $conversionFactor = 1000; |
| 1442 | } |
| 1443 | //Distance in meters |
| 1444 | $distance = $distance * $conversionFactor; |
| 1445 | |
| 1446 | $whereClause = CRM_Contact_BAO_ProximityQuery::where($latitude, $longitude, $distance); |
| 1447 | |
| 1448 | $query = " |
| 1449 | SELECT civicrm_contact.id as contact_id, |
| 1450 | civicrm_contact.display_name as display_name |
| 1451 | FROM civicrm_contact |
| 1452 | LEFT JOIN civicrm_address ON civicrm_contact.id = civicrm_address.contact_id |
| 1453 | WHERE $whereClause |
| 1454 | "; |
| 1455 | |
| 1456 | $dao = CRM_Core_DAO::executeQuery($query); |
| 1457 | $contacts = []; |
| 1458 | while ($dao->fetch()) { |
| 1459 | $contacts[] = $dao->toArray(); |
| 1460 | } |
| 1461 | |
| 1462 | return civicrm_api3_create_success($contacts, $params, 'Contact', 'get_by_location', $dao); |
| 1463 | } |
| 1464 | |
| 1465 | /** |
| 1466 | * Get parameters for getlist function. |
| 1467 | * |
| 1468 | * @see _civicrm_api3_generic_getlist_params |
| 1469 | * |
| 1470 | * @param array $request |
| 1471 | */ |
| 1472 | function _civicrm_api3_contact_getlist_params(&$request) { |
| 1473 | // get the autocomplete options from settings |
| 1474 | $acpref = explode(CRM_Core_DAO::VALUE_SEPARATOR, |
| 1475 | CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, |
| 1476 | 'contact_autocomplete_options' |
| 1477 | ) |
| 1478 | ); |
| 1479 | |
| 1480 | // get the option values for contact autocomplete |
| 1481 | $acOptions = CRM_Core_OptionGroup::values('contact_autocomplete_options', FALSE, FALSE, FALSE, NULL, 'name'); |
| 1482 | |
| 1483 | $list = []; |
| 1484 | foreach ($acpref as $value) { |
| 1485 | if ($value && !empty($acOptions[$value])) { |
| 1486 | $list[] = $acOptions[$value]; |
| 1487 | } |
| 1488 | } |
| 1489 | // If we are doing quicksearch by a field other than name, make sure that field is added to results |
| 1490 | $field_name = CRM_Utils_String::munge($request['search_field']); |
| 1491 | // Unique name contact_id = id |
| 1492 | if ($field_name == 'contact_id') { |
| 1493 | $field_name = 'id'; |
| 1494 | } |
| 1495 | // phone_numeric should be phone |
| 1496 | $searchField = str_replace('_numeric', '', $field_name); |
| 1497 | if (!in_array($searchField, $list)) { |
| 1498 | $list[] = $searchField; |
| 1499 | } |
| 1500 | $request['description_field'] = $list; |
| 1501 | $list[] = 'contact_type'; |
| 1502 | $request['params']['return'] = array_unique(array_merge($list, $request['extra'])); |
| 1503 | $request['params']['options']['sort'] = 'sort_name'; |
| 1504 | // Contact api doesn't support array(LIKE => 'foo') syntax |
| 1505 | if (!empty($request['input'])) { |
| 1506 | $request['params'][$request['search_field']] = $request['input']; |
| 1507 | // Temporarily override wildcard setting |
| 1508 | if (Civi::settings()->get('includeWildCardInName') != $request['add_wildcard']) { |
| 1509 | Civi::$statics['civicrm_api3_contact_getlist']['override_wildcard'] = !$request['add_wildcard']; |
| 1510 | Civi::settings()->set('includeWildCardInName', $request['add_wildcard']); |
| 1511 | } |
| 1512 | } |
| 1513 | } |
| 1514 | |
| 1515 | /** |
| 1516 | * Get output for getlist function. |
| 1517 | * |
| 1518 | * @see _civicrm_api3_generic_getlist_output |
| 1519 | * |
| 1520 | * @param array $result |
| 1521 | * @param array $request |
| 1522 | * |
| 1523 | * @return array |
| 1524 | */ |
| 1525 | function _civicrm_api3_contact_getlist_output($result, $request) { |
| 1526 | $output = []; |
| 1527 | if (!empty($result['values'])) { |
| 1528 | $addressFields = array_intersect([ |
| 1529 | 'street_address', |
| 1530 | 'city', |
| 1531 | 'state_province', |
| 1532 | 'country', |
| 1533 | ], |
| 1534 | $request['params']['return']); |
| 1535 | foreach ($result['values'] as $row) { |
| 1536 | $data = [ |
| 1537 | 'id' => $row[$request['id_field']], |
| 1538 | 'label' => $row[$request['label_field']], |
| 1539 | 'description' => [], |
| 1540 | ]; |
| 1541 | foreach ($request['description_field'] as $item) { |
| 1542 | if (!strpos($item, '_name') && !in_array($item, $addressFields) && !empty($row[$item])) { |
| 1543 | $data['description'][] = $row[$item]; |
| 1544 | } |
| 1545 | } |
| 1546 | $address = []; |
| 1547 | foreach ($addressFields as $item) { |
| 1548 | if (!empty($row[$item])) { |
| 1549 | $address[] = $row[$item]; |
| 1550 | } |
| 1551 | } |
| 1552 | if ($address) { |
| 1553 | $data['description'][] = implode(' ', $address); |
| 1554 | } |
| 1555 | if (!empty($request['image_field'])) { |
| 1556 | $data['image'] = isset($row[$request['image_field']]) ? $row[$request['image_field']] : ''; |
| 1557 | } |
| 1558 | else { |
| 1559 | $data['icon_class'] = $row['contact_type']; |
| 1560 | } |
| 1561 | $output[] = $data; |
| 1562 | } |
| 1563 | } |
| 1564 | // Restore wildcard override by _civicrm_api3_contact_getlist_params |
| 1565 | if (isset(Civi::$statics['civicrm_api3_contact_getlist']['override_wildcard'])) { |
| 1566 | Civi::settings()->set('includeWildCardInName', Civi::$statics['civicrm_api3_contact_getlist']['override_wildcard']); |
| 1567 | unset(Civi::$statics['civicrm_api3_contact_getlist']['override_wildcard']); |
| 1568 | } |
| 1569 | return $output; |
| 1570 | } |
| 1571 | |
| 1572 | /** |
| 1573 | * Check for duplicate contacts. |
| 1574 | * |
| 1575 | * @param array $params |
| 1576 | * Params per getfields metadata. |
| 1577 | * |
| 1578 | * @return array |
| 1579 | * API formatted array |
| 1580 | */ |
| 1581 | function civicrm_api3_contact_duplicatecheck($params) { |
| 1582 | $dupes = CRM_Contact_BAO_Contact::getDuplicateContacts( |
| 1583 | $params['match'], |
| 1584 | $params['match']['contact_type'], |
| 1585 | $params['rule_type'], |
| 1586 | CRM_Utils_Array::value('exclude', $params, []), |
| 1587 | CRM_Utils_Array::value('check_permissions', $params), |
| 1588 | CRM_Utils_Array::value('dedupe_rule_id', $params) |
| 1589 | ); |
| 1590 | $values = []; |
| 1591 | if ($dupes && !empty($params['return'])) { |
| 1592 | return civicrm_api3('Contact', 'get', [ |
| 1593 | 'return' => $params['return'], |
| 1594 | 'id' => ['IN' => $dupes], |
| 1595 | 'options' => $params['options'] ?? NULL, |
| 1596 | 'sequential' => $params['sequential'] ?? NULL, |
| 1597 | 'check_permissions' => $params['check_permissions'] ?? NULL, |
| 1598 | ]); |
| 1599 | } |
| 1600 | foreach ($dupes as $dupe) { |
| 1601 | $values[$dupe] = ['id' => $dupe]; |
| 1602 | } |
| 1603 | return civicrm_api3_create_success($values, $params, 'Contact', 'duplicatecheck'); |
| 1604 | } |
| 1605 | |
| 1606 | /** |
| 1607 | * Declare metadata for contact dedupe function. |
| 1608 | * |
| 1609 | * @param $params |
| 1610 | */ |
| 1611 | function _civicrm_api3_contact_duplicatecheck_spec(&$params) { |
| 1612 | $params['dedupe_rule_id'] = [ |
| 1613 | 'title' => 'Dedupe Rule ID (optional)', |
| 1614 | 'description' => 'This will default to the built in unsupervised rule', |
| 1615 | 'type' => CRM_Utils_Type::T_INT, |
| 1616 | ]; |
| 1617 | $params['rule_type'] = [ |
| 1618 | 'title' => 'Dedupe Rule Type', |
| 1619 | 'description' => 'If no rule id specified, pass "Unsupervised" or "Supervised"', |
| 1620 | 'type' => CRM_Utils_Type::T_STRING, |
| 1621 | 'api.default' => 'Unsupervised', |
| 1622 | ]; |
| 1623 | // @todo declare 'match' parameter. We don't have a standard for type = array yet. |
| 1624 | } |