Merge pull request #15411 from systopia/dev_l10nx
[civicrm-core.git] / CRM / Core / BAO / Address.php
CommitLineData
6a488035 1<?php
6a488035
TO
2/*
3 +--------------------------------------------------------------------+
fee14197 4 | CiviCRM version 5 |
6a488035 5 +--------------------------------------------------------------------+
6b83d5bd 6 | Copyright CiviCRM LLC (c) 2004-2019 |
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
6b83d5bd 31 * @copyright CiviCRM LLC (c) 2004-2019
6a488035
TO
32 */
33
34/**
192d36c5 35 * This is class to handle address related functions.
6a488035
TO
36 */
37class CRM_Core_BAO_Address extends CRM_Core_DAO_Address {
38
39 /**
fe482240 40 * Takes an associative array and creates a address.
6a488035 41 *
6a0b768e
TO
42 * @param array $params
43 * (reference ) an assoc array of name/value pairs.
44 * @param bool $fixAddress
45 * True if you need to fix (format) address values.
6a488035
TO
46 * before inserting in db
47 *
e63aff1c
EM
48 * @param null $entity
49 *
a1a2a83d 50 * @return array|NULL
a6c01b45 51 * array of created address
6a488035 52 */
00be9182 53 public static function create(&$params, $fixAddress = TRUE, $entity = NULL) {
6a488035 54 if (!isset($params['address']) || !is_array($params['address'])) {
a1a2a83d 55 return NULL;
6a488035
TO
56 }
57 CRM_Core_BAO_Block::sortPrimaryFirst($params['address']);
be2fb01f 58 $addresses = [];
6a488035
TO
59 $contactId = NULL;
60
61 $updateBlankLocInfo = CRM_Utils_Array::value('updateBlankLocInfo', $params, FALSE);
62 if (!$entity) {
63 $contactId = $params['contact_id'];
64 //get all the addresses for this contact
85c882c5 65 $addresses = self::allAddress($contactId);
6a488035
TO
66 }
67 else {
68 // get all address from location block
be2fb01f 69 $entityElements = [
6a488035
TO
70 'entity_table' => $params['entity_table'],
71 'entity_id' => $params['entity_id'],
be2fb01f 72 ];
6a488035
TO
73 $addresses = self::allEntityAddress($entityElements);
74 }
75
76 $isPrimary = $isBilling = TRUE;
be2fb01f 77 $blocks = [];
6a488035
TO
78 foreach ($params['address'] as $key => $value) {
79 if (!is_array($value)) {
80 continue;
81 }
82
83 $addressExists = self::dataExists($value);
dfcbddc8 84 if (empty($value['id'])) {
85c882c5 85 if (!empty($addresses) && array_key_exists(CRM_Utils_Array::value('location_type_id', $value), $addresses)) {
86 $value['id'] = $addresses[CRM_Utils_Array::value('location_type_id', $value)];
6a488035
TO
87 }
88 }
89
90 // Note there could be cases when address info already exist ($value[id] is set) for a contact/entity
91 // BUT info is not present at this time, and therefore we should be really careful when deleting the block.
92 // $updateBlankLocInfo will help take appropriate decision. CRM-5969
dfcbddc8 93 if (isset($value['id']) && !$addressExists && $updateBlankLocInfo) {
6a488035 94 //delete the existing record
be2fb01f 95 CRM_Core_BAO_Block::blockDelete('Address', ['id' => $value['id']]);
6a488035
TO
96 continue;
97 }
98 elseif (!$addressExists) {
99 continue;
100 }
101
8cc574cf 102 if ($isPrimary && !empty($value['is_primary'])) {
6a488035
TO
103 $isPrimary = FALSE;
104 }
105 else {
106 $value['is_primary'] = 0;
107 }
108
8cc574cf 109 if ($isBilling && !empty($value['is_billing'])) {
6a488035
TO
110 $isBilling = FALSE;
111 }
112 else {
113 $value['is_billing'] = 0;
114 }
115
a7488080 116 if (empty($value['manual_geo_code'])) {
6a488035
TO
117 $value['manual_geo_code'] = 0;
118 }
119 $value['contact_id'] = $contactId;
120 $blocks[] = self::add($value, $fixAddress);
121 }
122
123 return $blocks;
124 }
125
126 /**
fe482240 127 * Takes an associative array and adds address.
6a488035 128 *
6a0b768e
TO
129 * @param array $params
130 * (reference ) an assoc array of name/value pairs.
131 * @param bool $fixAddress
132 * True if you need to fix (format) address values.
6a488035
TO
133 * before inserting in db
134 *
906e6120 135 * @return CRM_Core_BAO_Address|null
6a488035 136 */
be6b584c 137 public static function add(&$params, $fixAddress = FALSE) {
e9ff5391 138
6a488035 139 $address = new CRM_Core_DAO_Address();
e9ff5391 140 $checkPermissions = isset($params['check_permissions']) ? $params['check_permissions'] : TRUE;
6a488035
TO
141
142 // fixAddress mode to be done
143 if ($fixAddress) {
144 CRM_Core_BAO_Address::fixAddress($params);
145 }
146
147 $hook = empty($params['id']) ? 'create' : 'edit';
148 CRM_Utils_Hook::pre($hook, 'Address', CRM_Utils_Array::value('id', $params), $params);
149
150 // if id is set & is_primary isn't we can assume no change
151 if (is_numeric(CRM_Utils_Array::value('is_primary', $params)) || empty($params['id'])) {
152 CRM_Core_BAO_Block::handlePrimary($params, get_class());
153 }
e9ff5391 154
be41af0b 155 // (prevent chaining 1 and 3) CRM-21214
6b60d8a3 156 if (isset($params['master_id']) && !CRM_Utils_System::isNull($params['master_id'])) {
38e60804 157 self::fixSharedAddress($params);
810c57a2
D
158 }
159
6a488035
TO
160 $address->copyValues($params);
161
162 $address->save();
163
164 if ($address->id) {
2c59f0ca
CW
165 if (isset($params['custom'])) {
166 $addressCustom = $params['custom'];
167 }
168 else {
169 $customFields = CRM_Core_BAO_CustomField::getFields('Address', FALSE, TRUE, NULL, NULL, FALSE, FALSE, $checkPermissions);
170
171 if (!empty($customFields)) {
172 $addressCustom = CRM_Core_BAO_CustomField::postProcess($params,
173 $address->id,
174 'Address',
175 FALSE,
176 $checkPermissions
177 );
178 }
6a488035
TO
179 }
180 if (!empty($addressCustom)) {
181 CRM_Core_BAO_CustomValueTable::store($addressCustom, 'civicrm_address', $address->id);
182 }
183
810c57a2
D
184 // call the function to sync shared address and create relationships
185 // if address is already shared, share master_id with all children and update relationships accordingly
38e60804 186 // (prevent chaining 2) CRM-21214
6a488035
TO
187 self::processSharedAddress($address->id, $params);
188
6a488035
TO
189 // lets call the post hook only after we've done all the follow on processing
190 CRM_Utils_Hook::post($hook, 'Address', $address->id, $address);
191 }
192
193 return $address;
194 }
195
196 /**
fe482240 197 * Format the address params to have reasonable values.
6a488035 198 *
6a0b768e
TO
199 * @param array $params
200 * (reference ) an assoc array of name/value pairs.
6a488035 201 */
00be9182 202 public static function fixAddress(&$params) {
a7488080 203 if (!empty($params['billing_street_address'])) {
b44e3f84 204 //Check address is coming from online contribution / registration page
6a488035 205 //Fixed :CRM-5076
be2fb01f 206 $billing = [
6a488035
TO
207 'street_address' => 'billing_street_address',
208 'city' => 'billing_city',
209 'postal_code' => 'billing_postal_code',
210 'state_province' => 'billing_state_province',
211 'state_province_id' => 'billing_state_province_id',
212 'country' => 'billing_country',
213 'country_id' => 'billing_country_id',
be2fb01f 214 ];
6a488035
TO
215
216 foreach ($billing as $key => $val) {
217 if ($value = CRM_Utils_Array::value($val, $params)) {
a7488080 218 if (!empty($params[$key])) {
6a488035
TO
219 unset($params[$val]);
220 }
221 else {
222 //add new key and removed old
223 $params[$key] = $value;
224 unset($params[$val]);
225 }
226 }
227 }
228 }
229
230 /* Split the zip and +4, if it's in US format */
a7488080 231 if (!empty($params['postal_code']) &&
6a488035
TO
232 preg_match('/^(\d{4,5})[+-](\d{4})$/',
233 $params['postal_code'],
234 $match
235 )
236 ) {
237 $params['postal_code'] = $match[1];
238 $params['postal_code_suffix'] = $match[2];
239 }
240
241 // add country id if not set
242 if ((!isset($params['country_id']) || !is_numeric($params['country_id'])) &&
243 isset($params['country'])
244 ) {
245 $country = new CRM_Core_DAO_Country();
246 $country->name = $params['country'];
247 if (!$country->find(TRUE)) {
248 $country->name = NULL;
249 $country->iso_code = $params['country'];
250 $country->find(TRUE);
251 }
252 $params['country_id'] = $country->id;
253 }
254
255 // add state_id if state is set
256 if ((!isset($params['state_province_id']) || !is_numeric($params['state_province_id']))
257 && isset($params['state_province'])
258 ) {
259 if (!empty($params['state_province'])) {
260 $state_province = new CRM_Core_DAO_StateProvince();
261 $state_province->name = $params['state_province'];
262
263 // add country id if present
264 if (!empty($params['country_id'])) {
265 $state_province->country_id = $params['country_id'];
266 }
267
268 if (!$state_province->find(TRUE)) {
269 unset($state_province->name);
270 $state_province->abbreviation = $params['state_province'];
271 $state_province->find(TRUE);
272 }
273 $params['state_province_id'] = $state_province->id;
274 if (empty($params['country_id'])) {
275 // set this here since we have it
276 $params['country_id'] = $state_province->country_id;
277 }
278 }
279 else {
280 $params['state_province_id'] = 'null';
281 }
282 }
283
284 // add county id if county is set
285 // CRM-7837
286 if ((!isset($params['county_id']) || !is_numeric($params['county_id']))
287 && isset($params['county']) && !empty($params['county'])
288 ) {
289 $county = new CRM_Core_DAO_County();
290 $county->name = $params['county'];
291
292 if (isset($params['state_province_id'])) {
293 $county->state_province_id = $params['state_province_id'];
294 }
295
296 if ($county->find(TRUE)) {
297 $params['county_id'] = $county->id;
298 }
299 }
300
301 // currently copy values populates empty fields with the string "null"
302 // and hence need to check for the string null
303 if (isset($params['state_province_id']) &&
304 is_numeric($params['state_province_id']) &&
305 (!isset($params['country_id']) || empty($params['country_id']))
306 ) {
307 // since state id present and country id not present, hence lets populate it
308 // jira issue http://issues.civicrm.org/jira/browse/CRM-56
309 $params['country_id'] = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_StateProvince',
310 $params['state_province_id'],
311 'country_id'
312 );
313 }
314
315 //special check to ignore non numeric values if they are not
316 //detected by formRule(sometimes happens due to internet latency), also allow user to unselect state/country
317 if (isset($params['state_province_id'])) {
318 if (empty($params['state_province_id'])) {
319 $params['state_province_id'] = 'null';
320 }
321 elseif (!is_numeric($params['state_province_id']) ||
322 ((int ) $params['state_province_id'] < 1000)
323 ) {
324 // CRM-3393 ( the hacky 1000 check)
325 $params['state_province_id'] = 'null';
326 }
327 }
328
329 if (isset($params['country_id'])) {
330 if (empty($params['country_id'])) {
331 $params['country_id'] = 'null';
332 }
333 elseif (!is_numeric($params['country_id']) ||
334 ((int ) $params['country_id'] < 1000)
335 ) {
336 // CRM-3393 ( the hacky 1000 check)
337 $params['country_id'] = 'null';
338 }
339 }
340
341 // add state and country names from the ids
342 if (isset($params['state_province_id']) && is_numeric($params['state_province_id'])) {
343 $params['state_province'] = CRM_Core_PseudoConstant::stateProvinceAbbreviation($params['state_province_id']);
344 }
345
346 if (isset($params['country_id']) && is_numeric($params['country_id'])) {
347 $params['country'] = CRM_Core_PseudoConstant::country($params['country_id']);
348 }
349
aaffa79f 350 $asp = Civi::settings()->get('address_standardization_provider');
6a488035
TO
351 // clean up the address via USPS web services if enabled
352 if ($asp === 'USPS' &&
353 $params['country_id'] == 1228
354 ) {
355 CRM_Utils_Address_USPS::checkAddress($params);
e6f202ae
JM
356 }
357 // do street parsing again if enabled, since street address might have changed
358 $parseStreetAddress = CRM_Utils_Array::value(
359 'street_address_parsing',
360 CRM_Core_BAO_Setting::valueOptions(
361 CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME,
362 'address_options'
363 ),
364 FALSE
365 );
366
367 if ($parseStreetAddress && !empty($params['street_address'])) {
cf9ccf98 368 foreach (['street_number', 'street_name', 'street_unit', 'street_number_suffix'] as $fld) {
e6f202ae 369 unset($params[$fld]);
6a488035 370 }
e6f202ae
JM
371 // main parse string.
372 $parseString = CRM_Utils_Array::value('street_address', $params);
373 $parsedFields = CRM_Core_BAO_Address::parseStreetAddress($parseString);
374
375 // merge parse address in to main address block.
376 $params = array_merge($params, $parsedFields);
6a488035
TO
377 }
378
4882d275
FG
379 // skip_geocode is an optional parameter through the api.
380 // manual_geo_code is on the contact edit form. They do the same thing....
381 if (empty($params['skip_geocode']) && empty($params['manual_geo_code'])) {
382 self::addGeocoderData($params);
6a488035 383 }
4882d275 384
6a488035
TO
385 }
386
387 /**
fe482240 388 * Check if there is data to create the object.
6a488035 389 *
6a0b768e
TO
390 * @param array $params
391 * (reference ) an assoc array of name/value pairs.
6a488035 392 *
5c766a0b 393 * @return bool
6a488035 394 */
00be9182 395 public static function dataExists(&$params) {
6a488035
TO
396 //check if location type is set if not return false
397 if (!isset($params['location_type_id'])) {
398 return FALSE;
399 }
400
401 $config = CRM_Core_Config::singleton();
402 foreach ($params as $name => $value) {
be2fb01f 403 if (in_array($name, [
353ffa53
TO
404 'is_primary',
405 'location_type_id',
406 'id',
407 'contact_id',
408 'is_billing',
409 'display',
af9b09df 410 'master_id',
be2fb01f 411 ])) {
6a488035
TO
412 continue;
413 }
414 elseif (!CRM_Utils_System::isNull($value)) {
415 // name could be country or country id
416 if (substr($name, 0, 7) == 'country') {
417 // make sure its different from the default country
418 // iso code
d14f0a66 419 $defaultCountry = CRM_Core_BAO_Country::defaultContactCountry();
6a488035 420 // full name
d14f0a66 421 $defaultCountryName = CRM_Core_BAO_Country::defaultContactCountryName();
6a488035
TO
422
423 if ($defaultCountry) {
424 if ($value == $defaultCountry ||
425 $value == $defaultCountryName ||
426 $value == $config->defaultContactCountry
427 ) {
428 // do nothing
429 }
430 else {
431 return TRUE;
432 }
433 }
434 else {
435 // return if null default
436 return TRUE;
437 }
438 }
439 else {
440 return TRUE;
441 }
442 }
443 }
444
445 return FALSE;
446 }
447
448 /**
449 * Given the list of params in the params array, fetch the object
450 * and store the values in the values array
451 *
6a0b768e
TO
452 * @param array $entityBlock
453 * Associated array of fields.
454 * @param bool $microformat
455 * If microformat output is required.
e63aff1c 456 * @param int|string $fieldName conditional field name
6a488035 457 *
a6c01b45
CW
458 * @return array
459 * array with address fields
6a488035 460 */
00be9182 461 public static function &getValues($entityBlock, $microformat = FALSE, $fieldName = 'contact_id') {
6a488035
TO
462 if (empty($entityBlock)) {
463 return NULL;
464 }
be2fb01f 465 $addresses = [];
6a488035
TO
466 $address = new CRM_Core_BAO_Address();
467
a7488080 468 if (empty($entityBlock['entity_table'])) {
6a488035
TO
469 $address->$fieldName = CRM_Utils_Array::value($fieldName, $entityBlock);
470 }
471 else {
be2fb01f 472 $addressIds = [];
6a488035
TO
473 $addressIds = self::allEntityAddress($entityBlock);
474
475 if (!empty($addressIds[1])) {
476 $address->id = $addressIds[1];
477 }
478 else {
479 return $addresses;
480 }
481 }
001a6635
JV
482 if (isset($entityBlock['is_billing']) && $entityBlock['is_billing'] == 1) {
483 $address->orderBy('is_billing desc, id');
484 }
485 else {
486 //get primary address as a first block.
487 $address->orderBy('is_primary desc, id');
488 }
6a488035
TO
489
490 $address->find();
491
ac44bb1c 492 $locationTypes = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id');
6a488035
TO
493 $count = 1;
494 while ($address->fetch()) {
495 // deprecate reference.
496 if ($count > 1) {
cf9ccf98 497 foreach (['state', 'state_name', 'country', 'world_region'] as $fld) {
4f99ca55
TO
498 if (isset($address->$fld)) {
499 unset($address->$fld);
2aa397bc 500 }
6a488035
TO
501 }
502 }
503 $stree = $address->street_address;
be2fb01f 504 $values = [];
6a488035
TO
505 CRM_Core_DAO::storeValues($address, $values);
506
507 // add state and country information: CRM-369
ac44bb1c
WA
508 if (!empty($address->location_type_id)) {
509 $values['location_type'] = CRM_Utils_Array::value($address->location_type_id, $locationTypes);
510 }
6a488035
TO
511 if (!empty($address->state_province_id)) {
512 $address->state = CRM_Core_PseudoConstant::stateProvinceAbbreviation($address->state_province_id, FALSE);
513 $address->state_name = CRM_Core_PseudoConstant::stateProvince($address->state_province_id, FALSE);
514 }
515
516 if (!empty($address->country_id)) {
517 $address->country = CRM_Core_PseudoConstant::country($address->country_id);
518
519 //get world region
520 $regionId = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_Country', $address->country_id, 'region_id');
521
522 $address->world_region = CRM_Core_PseudoConstant::worldregion($regionId);
523 }
524
525 $address->addDisplay($microformat);
526
527 $values['display'] = $address->display;
528 $values['display_text'] = $address->display_text;
529
6b60d8a3 530 if (isset($address->master_id) && !CRM_Utils_System::isNull($address->master_id)) {
6a488035
TO
531 $values['use_shared_address'] = 1;
532 }
533
534 $addresses[$count] = $values;
535
96ca8376
MW
536 //There should never be more than one primary blocks, hence set is_primary = 0 other than first
537 // Calling functions expect the key is_primary to be set, so do not unset it here!
6a488035 538 if ($count > 1) {
96ca8376 539 $addresses[$count]['is_primary'] = 0;
6a488035
TO
540 }
541
542 $count++;
543 }
544
545 return $addresses;
546 }
547
548 /**
192d36c5 549 * Add the formatted address to $this-> display.
6a488035 550 *
2a6da8d7 551 * @param bool $microformat
192d36c5 552 * Unexplained parameter that I've always wondered about.
6a488035 553 */
00be9182 554 public function addDisplay($microformat = FALSE) {
be2fb01f 555 $fields = [
6a488035
TO
556 // added this for CRM 1200
557 'address_id' => $this->id,
558 // CRM-4003
559 'address_name' => str_replace('\ 1', ' ', $this->name),
560 'street_address' => $this->street_address,
561 'supplemental_address_1' => $this->supplemental_address_1,
562 'supplemental_address_2' => $this->supplemental_address_2,
207f62c6 563 'supplemental_address_3' => $this->supplemental_address_3,
6a488035
TO
564 'city' => $this->city,
565 'state_province_name' => isset($this->state_name) ? $this->state_name : "",
566 'state_province' => isset($this->state) ? $this->state : "",
567 'postal_code' => isset($this->postal_code) ? $this->postal_code : "",
568 'postal_code_suffix' => isset($this->postal_code_suffix) ? $this->postal_code_suffix : "",
569 'country' => isset($this->country) ? $this->country : "",
570 'world_region' => isset($this->world_region) ? $this->world_region : "",
be2fb01f 571 ];
6a488035
TO
572
573 if (isset($this->county_id) && $this->county_id) {
574 $fields['county'] = CRM_Core_PseudoConstant::county($this->county_id);
575 }
576 else {
577 $fields['county'] = NULL;
578 }
579
580 $this->display = CRM_Utils_Address::format($fields, NULL, $microformat);
581 $this->display_text = CRM_Utils_Address::format($fields);
582 }
583
584 /**
585 * Get all the addresses for a specified contact_id, with the primary address being first
586 *
6a0b768e
TO
587 * @param int $id
588 * The contact id.
6a488035 589 *
dfcbddc8
DG
590 * @param bool $updateBlankLocInfo
591 *
a6c01b45 592 * @return array
dfcbddc8 593 * the array of adrress data
6a488035 594 */
dfcbddc8 595 public static function allAddress($id, $updateBlankLocInfo = FALSE) {
6a488035
TO
596 if (!$id) {
597 return NULL;
598 }
599
600 $query = "
601SELECT civicrm_address.id as address_id, civicrm_address.location_type_id as location_type_id
602FROM civicrm_contact, civicrm_address
603WHERE civicrm_address.contact_id = civicrm_contact.id AND civicrm_contact.id = %1
604ORDER BY civicrm_address.is_primary DESC, address_id ASC";
be2fb01f 605 $params = [1 => [$id, 'Integer']];
dfcbddc8 606
be2fb01f 607 $addresses = [];
6a488035 608 $dao = CRM_Core_DAO::executeQuery($query, $params);
dfcbddc8 609 $count = 1;
6a488035 610 while ($dao->fetch()) {
dfcbddc8
DG
611 if ($updateBlankLocInfo) {
612 $addresses[$count++] = $dao->address_id;
613 }
614 else {
615 $addresses[$dao->location_type_id] = $dao->address_id;
616 }
6a488035
TO
617 }
618 return $addresses;
619 }
620
621 /**
622 * Get all the addresses for a specified location_block id, with the primary address being first
623 *
6a0b768e
TO
624 * @param array $entityElements
625 * The array containing entity_id and.
16b10e64 626 * entity_table name
6a488035 627 *
a6c01b45 628 * @return array
dfcbddc8 629 * the array of adrress data
6a488035 630 */
00be9182 631 public static function allEntityAddress(&$entityElements) {
be2fb01f 632 $addresses = [];
6a488035
TO
633 if (empty($entityElements)) {
634 return $addresses;
635 }
636
637 $entityId = $entityElements['entity_id'];
638 $entityTable = $entityElements['entity_table'];
639
640 $sql = "
dfcbddc8 641SELECT civicrm_address.id as address_id
6a488035
TO
642FROM civicrm_loc_block loc, civicrm_location_type ltype, civicrm_address, {$entityTable} ev
643WHERE ev.id = %1
644 AND loc.id = ev.loc_block_id
645 AND civicrm_address.id IN (loc.address_id, loc.address_2_id)
646 AND ltype.id = civicrm_address.location_type_id
647ORDER BY civicrm_address.is_primary DESC, civicrm_address.location_type_id DESC, address_id ASC ";
648
be2fb01f 649 $params = [1 => [$entityId, 'Integer']];
6a488035 650 $dao = CRM_Core_DAO::executeQuery($sql, $params);
dfcbddc8 651 $locationCount = 1;
6a488035 652 while ($dao->fetch()) {
dfcbddc8
DG
653 $addresses[$locationCount] = $dao->address_id;
654 $locationCount++;
6a488035
TO
655 }
656 return $addresses;
657 }
658
6a488035 659 /**
fe482240 660 * Get address sequence.
6a488035 661 *
a6c01b45 662 * @return array
16b10e64 663 * Array of address sequence.
6a488035 664 */
00be9182 665 public static function addressSequence() {
9b118398 666 $addressSequence = CRM_Utils_Address::sequence(\Civi::settings()->get('address_format'));
6a488035
TO
667
668 $countryState = $cityPostal = FALSE;
669 foreach ($addressSequence as $key => $field) {
670 if (
be2fb01f 671 in_array($field, ['country', 'state_province']) &&
6a488035
TO
672 !$countryState
673 ) {
674 $countryState = TRUE;
675 $addressSequence[$key] = 'country_state_province';
676 }
677 elseif (
be2fb01f 678 in_array($field, ['city', 'postal_code']) &&
6a488035
TO
679 !$cityPostal
680 ) {
681 $cityPostal = TRUE;
682 $addressSequence[$key] = 'city_postal_code';
683 }
684 elseif (
be2fb01f 685 in_array($field, ['country', 'state_province', 'city', 'postal_code'])
6a488035
TO
686 ) {
687 unset($addressSequence[$key]);
688 }
689 }
690
691 return $addressSequence;
692 }
693
694 /**
695 * Parse given street address string in to street_name,
696 * street_unit, street_number and street_number_suffix
697 * eg "54A Excelsior Ave. Apt 1C", or "917 1/2 Elm Street"
698 *
699 * NB: civic street formats for en_CA and fr_CA used by default if those locales are active
700 * otherwise en_US format is default action
701 *
6c552737
TO
702 * @param string $streetAddress
703 * Street address including number and apt.
704 * @param string $locale
705 * Locale used to parse address.
6a488035 706 *
a6c01b45
CW
707 * @return array
708 * parsed fields values.
6a488035 709 */
00be9182 710 public static function parseStreetAddress($streetAddress, $locale = NULL) {
1d148cb9
O
711 // use 'en_US' for address parsing if the requested locale is not supported.
712 if (!self::isSupportedParsingLocale($locale)) {
6a488035
TO
713 $locale = 'en_US';
714 }
1d148cb9 715
be2fb01f 716 $emptyParseFields = $parseFields = [
6a488035
TO
717 'street_name' => '',
718 'street_unit' => '',
719 'street_number' => '',
720 'street_number_suffix' => '',
be2fb01f 721 ];
6a488035
TO
722
723 if (empty($streetAddress)) {
724 return $parseFields;
725 }
726
727 $streetAddress = trim($streetAddress);
728
be2fb01f 729 $matches = [];
cf9ccf98
TO
730 if (in_array($locale, ['en_CA', 'fr_CA'])
731 && preg_match('/^([A-Za-z0-9]+)[ ]*\-[ ]*/', $streetAddress, $matches)
353ffa53 732 ) {
6a488035
TO
733 $parseFields['street_unit'] = $matches[1];
734 // unset from rest of street address
735 $streetAddress = preg_replace('/^([A-Za-z0-9]+)[ ]*\-[ ]*/', '', $streetAddress);
736 }
737
738 // get street number and suffix.
be2fb01f 739 $matches = [];
6a488035
TO
740 //alter street number/suffix handling so that we accept -digit
741 if (preg_match('/^[A-Za-z0-9]+([\S]+)/', $streetAddress, $matches)) {
742 // check that $matches[0] is numeric, else assume no street number
743 if (preg_match('/^(\d+)/', $matches[0])) {
744 $streetNumAndSuffix = $matches[0];
745
746 // get street number.
be2fb01f 747 $matches = [];
6a488035
TO
748 if (preg_match('/^(\d+)/', $streetNumAndSuffix, $matches)) {
749 $parseFields['street_number'] = $matches[0];
750 $suffix = preg_replace('/^(\d+)/', '', $streetNumAndSuffix);
751 $parseFields['street_number_suffix'] = trim($suffix);
752 }
753
754 // unset from main street address.
755 $streetAddress = preg_replace('/^[A-Za-z0-9]+([\S]+)/', '', $streetAddress);
756 $streetAddress = trim($streetAddress);
757 }
758 }
759 elseif (preg_match('/^(\d+)/', $streetAddress, $matches)) {
760 $parseFields['street_number'] = $matches[0];
761 // unset from main street address.
762 $streetAddress = preg_replace('/^(\d+)/', '', $streetAddress);
763 $streetAddress = trim($streetAddress);
764 }
765
d01ae020 766 // If street number is too large, we cannot store it.
767 if ($parseFields['street_number'] > CRM_Utils_Type::INT_MAX) {
768 return $emptyParseFields;
769 }
770
6a488035 771 // suffix might be like 1/2
be2fb01f 772 $matches = [];
6a488035
TO
773 if (preg_match('/^\d\/\d/', $streetAddress, $matches)) {
774 $parseFields['street_number_suffix'] .= $matches[0];
775
776 // unset from main street address.
777 $streetAddress = preg_replace('/^\d+\/\d+/', '', $streetAddress);
778 $streetAddress = trim($streetAddress);
779 }
780
781 // now get the street unit.
782 // supportable street unit formats.
be2fb01f 783 $streetUnitFormats = [
353ffa53
TO
784 'APT',
785 'APARTMENT',
786 'BSMT',
787 'BASEMENT',
788 'BLDG',
789 'BUILDING',
790 'DEPT',
791 'DEPARTMENT',
792 'FL',
793 'FLOOR',
794 'FRNT',
795 'FRONT',
796 'HNGR',
797 'HANGER',
798 'LBBY',
799 'LOBBY',
800 'LOWR',
801 'LOWER',
802 'OFC',
803 'OFFICE',
804 'PH',
805 'PENTHOUSE',
806 'TRLR',
807 'TRAILER',
808 'UPPR',
809 'RM',
810 'ROOM',
811 'SIDE',
812 'SLIP',
813 'KEY',
814 'LOT',
815 'PIER',
816 'REAR',
817 'SPC',
818 'SPACE',
819 'STOP',
820 'STE',
821 'SUITE',
822 'UNIT',
823 '#',
be2fb01f 824 ];
6a488035
TO
825
826 // overwriting $streetUnitFormats for 'en_CA' and 'fr_CA' locale
be2fb01f 827 if (in_array($locale, [
353ffa53 828 'en_CA',
af9b09df 829 'fr_CA',
be2fb01f
CW
830 ])) {
831 $streetUnitFormats = ['APT', 'APP', 'SUITE', 'BUREAU', 'UNIT'];
6a488035 832 }
689cecfd
EM
833 //@todo per CRM-14459 this regex picks up words with the string in them - e.g APT picks up
834 //Captain - presuming fixing regex (& adding test) to ensure a-z does not preced string will fix
6a488035 835 $streetUnitPreg = '/(' . implode('|\s', $streetUnitFormats) . ')(.+)?/i';
be2fb01f 836 $matches = [];
6a488035
TO
837 if (preg_match($streetUnitPreg, $streetAddress, $matches)) {
838 $parseFields['street_unit'] = trim($matches[0]);
839 $streetAddress = str_replace($matches[0], '', $streetAddress);
840 $streetAddress = trim($streetAddress);
841 }
842
843 // consider remaining string as street name.
844 $parseFields['street_name'] = $streetAddress;
845
846 //run parsed fields through stripSpaces to clean
847 foreach ($parseFields as $parseField => $value) {
848 $parseFields[$parseField] = CRM_Utils_String::stripSpaces($value);
849 }
689cecfd
EM
850 //CRM-14459 if the field is too long we should assume it didn't get it right & skip rather than allow
851 // the DB to fatal
852 $fields = CRM_Core_BAO_Address::fields();
853 foreach ($fields as $fieldname => $field) {
22e263ad 854 if (!empty($field['maxlength']) && strlen(CRM_Utils_Array::value($fieldname, $parseFields)) > $field['maxlength']) {
689cecfd
EM
855 return $emptyParseFields;
856 }
857 }
6a488035
TO
858
859 return $parseFields;
860 }
861
1d148cb9
O
862 /**
863 * Determines if the specified locale is
864 * supported by address parsing.
865 * If no locale is specified then it
866 * will check the default configured locale.
867 *
868 * locales supported include:
869 * en_US - http://pe.usps.com/cpim/ftp/pubs/pub28/pub28.pdf
870 * en_CA - http://www.canadapost.ca/tools/pg/manual/PGaddress-e.asp
871 * fr_CA - http://www.canadapost.ca/tools/pg/manual/PGaddress-f.asp
872 * NB: common use of comma after street number also supported
873 *
874 * @param string $locale
875 * The locale to be checked
876 *
9a208ac3 877 * @return bool
1d148cb9
O
878 */
879 public static function isSupportedParsingLocale($locale = NULL) {
880 if (!$locale) {
881 $config = CRM_Core_Config::singleton();
882 $locale = $config->lcMessages;
883 }
884
be2fb01f 885 $parsingSupportedLocales = ['en_US', 'en_CA', 'fr_CA'];
1d148cb9
O
886
887 if (in_array($locale, $parsingSupportedLocales)) {
888 return TRUE;
889 }
890
891 return FALSE;
892 }
893
6a488035 894 /**
eceb18cc 895 * Validate the address fields based on the address options enabled.
6a488035
TO
896 * in the Address Settings
897 *
6a0b768e
TO
898 * @param array $fields
899 * An array of importable/exportable contact fields.
6a488035 900 *
a6c01b45
CW
901 * @return array
902 * an array of contact fields and only the enabled address options
6a488035 903 */
00be9182 904 public static function validateAddressOptions($fields) {
6a488035
TO
905 static $addressOptions = NULL;
906 if (!$addressOptions) {
6c552737
TO
907 $addressOptions = CRM_Core_BAO_Setting::valueOptions(
908 CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME,
909 'address_options'
910 );
6a488035
TO
911 }
912
913 if (is_array($fields) && !empty($fields)) {
914 foreach ($addressOptions as $key => $value) {
915 if (!$value && isset($fields[$key])) {
916 unset($fields[$key]);
917 }
918 }
919 }
920 return $fields;
921 }
922
923 /**
fe482240 924 * Check if current address is used by any other contacts.
6a488035 925 *
6a0b768e
TO
926 * @param int $addressId
927 * Address id.
6a488035 928 *
72b3a70c
CW
929 * @return int
930 * count of contacts that use this shared address
6a488035 931 */
00be9182 932 public static function checkContactSharedAddress($addressId) {
6a488035 933 $query = 'SELECT count(id) FROM civicrm_address WHERE master_id = %1';
be2fb01f 934 return CRM_Core_DAO::singleValueQuery($query, [1 => [$addressId, 'Integer']]);
6a488035
TO
935 }
936
937 /**
fe482240 938 * Check if current address fields are shared with any other address.
6a488035 939 *
6a0b768e
TO
940 * @param array $fields
941 * Address fields in profile.
942 * @param int $contactId
943 * Contact id.
6a488035 944 *
6a488035 945 */
00be9182 946 public static function checkContactSharedAddressFields(&$fields, $contactId) {
6a488035
TO
947 if (!$contactId || !is_array($fields) || empty($fields)) {
948 return;
949 }
950
be2fb01f 951 $sharedLocations = [];
6a488035
TO
952
953 $query = "
954SELECT is_primary,
955 location_type_id
956 FROM civicrm_address
957 WHERE contact_id = %1
958 AND master_id IS NOT NULL";
959
be2fb01f 960 $dao = CRM_Core_DAO::executeQuery($query, [1 => [$contactId, 'Positive']]);
6a488035
TO
961 while ($dao->fetch()) {
962 $sharedLocations[$dao->location_type_id] = $dao->location_type_id;
963 if ($dao->is_primary) {
964 $sharedLocations['Primary'] = 'Primary';
965 }
966 }
967
968 //no need to process further.
969 if (empty($sharedLocations)) {
970 return;
971 }
972
be2fb01f 973 $addressFields = [
6a488035
TO
974 'city',
975 'county',
976 'country',
977 'geo_code_1',
978 'geo_code_2',
979 'postal_code',
980 'address_name',
981 'state_province',
982 'street_address',
983 'postal_code_suffix',
984 'supplemental_address_1',
985 'supplemental_address_2',
207f62c6 986 'supplemental_address_3',
be2fb01f 987 ];
6a488035
TO
988
989 foreach ($fields as $name => & $values) {
990 if (!is_array($values) || empty($values)) {
991 continue;
992 }
993
994 $nameVal = explode('-', $values['name']);
995 $fldName = CRM_Utils_Array::value(0, $nameVal);
996 $locType = CRM_Utils_Array::value(1, $nameVal);
a7488080 997 if (!empty($values['location_type_id'])) {
6a488035
TO
998 $locType = $values['location_type_id'];
999 }
1000
1001 if (in_array($fldName, $addressFields) &&
1002 in_array($locType, $sharedLocations)
1003 ) {
1004 $values['is_shared'] = TRUE;
1005 }
1006 }
1007 }
1008
38e60804
D
1009 /**
1010 * Fix the shared address if address is already shared
1011 * or if address will be shared with itself.
1012 *
1013 * @param array $params
1014 * Associated array of address params.
1015 */
1016 public static function fixSharedAddress(&$params) {
be41af0b
D
1017 // if address master address is shared, use its master (prevent chaining 1) CRM-21214
1018 $masterMasterId = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_Address', $params['master_id'], 'master_id');
1019 if ($masterMasterId > 0) {
1020 $params['master_id'] = $masterMasterId;
38e60804
D
1021 }
1022
be41af0b
D
1023 // prevent an endless chain between two shared addresses (prevent chaining 3) CRM-21214
1024 if (CRM_Utils_Array::value('id', $params) == $params['master_id']) {
68499476 1025 $params['master_id'] = NULL;
38e60804
D
1026 CRM_Core_Session::setStatus(ts("You can't connect an address to itself"), '', 'warning');
1027 }
1028 }
01853fc8 1029
6a488035 1030 /**
fe482240 1031 * Update the shared addresses if master address is modified.
6a488035 1032 *
6a0b768e
TO
1033 * @param int $addressId
1034 * Address id.
1035 * @param array $params
1036 * Associated array of address params.
6a488035 1037 */
00be9182 1038 public static function processSharedAddress($addressId, $params) {
810c57a2 1039 $query = 'SELECT id, contact_id FROM civicrm_address WHERE master_id = %1';
be2fb01f 1040 $dao = CRM_Core_DAO::executeQuery($query, [1 => [$addressId, 'Integer']]);
6a488035 1041
e0fb9216
CW
1042 // Default to TRUE if not set to maintain api backward compatibility.
1043 $createRelationship = isset($params['update_current_employer']) ? $params['update_current_employer'] : TRUE;
1044
6a488035 1045 // unset contact id
be2fb01f 1046 $skipFields = ['is_primary', 'location_type_id', 'is_billing', 'contact_id'];
6b60d8a3 1047 if (isset($params['master_id']) && !CRM_Utils_System::isNull($params['master_id'])) {
e0fb9216
CW
1048 if ($createRelationship) {
1049 // call the function to create a relationship for the new shared address
1050 self::processSharedAddressRelationship($params['master_id'], $params['contact_id']);
1051 }
8156e86b
D
1052 }
1053 else {
be41af0b 1054 // else no new shares will be created, only update shared addresses
810c57a2
D
1055 $skipFields[] = 'master_id';
1056 }
6a488035
TO
1057 foreach ($skipFields as $value) {
1058 unset($params[$value]);
1059 }
1060
1061 $addressDAO = new CRM_Core_DAO_Address();
1062 while ($dao->fetch()) {
b18c70b3 1063 // call the function to update the relationship
e0fb9216 1064 if ($createRelationship && isset($params['master_id']) && !CRM_Utils_System::isNull($params['master_id'])) {
810c57a2
D
1065 self::processSharedAddressRelationship($params['master_id'], $dao->contact_id);
1066 }
6a488035
TO
1067 $addressDAO->copyValues($params);
1068 $addressDAO->id = $dao->id;
1069 $addressDAO->save();
6a488035
TO
1070 }
1071 }
1072
8bdfc216 1073 /**
fe482240 1074 * Merge contacts with the Same address to get one shared label.
6a0b768e
TO
1075 * @param array $rows
1076 * Array[contact_id][contactDetails].
8bdfc216 1077 */
1078 public static function mergeSameAddress(&$rows) {
be2fb01f 1079 $uniqueAddress = [];
8bdfc216 1080 foreach (array_keys($rows) as $rowID) {
1081 // load complete address as array key
6c552737
TO
1082 $address = trim($rows[$rowID]['street_address'])
1083 . trim($rows[$rowID]['city'])
1084 . trim($rows[$rowID]['state_province'])
1085 . trim($rows[$rowID]['postal_code'])
1086 . trim($rows[$rowID]['country']);
8bdfc216 1087 if (isset($rows[$rowID]['last_name'])) {
1088 $name = $rows[$rowID]['last_name'];
1089 }
1090 else {
1091 $name = $rows[$rowID]['display_name'];
1092 }
1093
1094 // CRM-15120
be2fb01f 1095 $formatted = [
8bdfc216 1096 'first_name' => $rows[$rowID]['first_name'],
21dfd5f5 1097 'individual_prefix' => $rows[$rowID]['individual_prefix'],
be2fb01f 1098 ];
aaffa79f 1099 $format = Civi::settings()->get('display_name_format');
4c49535e 1100 $firstNameWithPrefix = CRM_Utils_Address::format($formatted, $format, FALSE, FALSE);
8bdfc216 1101 $firstNameWithPrefix = trim($firstNameWithPrefix);
1102
1103 // fill uniqueAddress array with last/first name tree
1104 if (isset($uniqueAddress[$address])) {
1105 $uniqueAddress[$address]['names'][$name][$firstNameWithPrefix]['first_name'] = $rows[$rowID]['first_name'];
1106 $uniqueAddress[$address]['names'][$name][$firstNameWithPrefix]['addressee_display'] = $rows[$rowID]['addressee_display'];
1107 // drop unnecessary rows
1108 unset($rows[$rowID]);
1109 // this is the first listing at this address
1110 }
1111 else {
1112 $uniqueAddress[$address]['ID'] = $rowID;
1113 $uniqueAddress[$address]['names'][$name][$firstNameWithPrefix]['first_name'] = $rows[$rowID]['first_name'];
1114 $uniqueAddress[$address]['names'][$name][$firstNameWithPrefix]['addressee_display'] = $rows[$rowID]['addressee_display'];
1115 }
1116 }
1117 foreach ($uniqueAddress as $address => $data) {
1118 // copy data back to $rows
1119 $count = 0;
1120 // one last name list per row
1121 foreach ($data['names'] as $last_name => $first_names) {
1122 // too many to list
1123 if ($count > 2) {
1124 break;
1125 }
9b873358 1126 if (count($first_names) == 1) {
2aa397bc
TO
1127 $family = $first_names[current(array_keys($first_names))]['addressee_display'];
1128 }
1129 else {
1130 // collapse the tree to summarize
1131 $family = trim(implode(" & ", array_keys($first_names)) . " " . $last_name);
1132 }
1133 if ($count) {
1134 $processedNames .= "\n" . $family;
1135 }
1136 else {
1137 // build display_name string
1138 $processedNames = $family;
1139 }
8bdfc216 1140 $count++;
1141 }
1142 $rows[$data['ID']]['addressee'] = $rows[$data['ID']]['addressee_display'] = $rows[$data['ID']]['display_name'] = $processedNames;
1143 }
1144 }
1145
6a488035 1146 /**
fe482240 1147 * Create relationship between contacts who share an address.
6a488035 1148 *
810c57a2
D
1149 * Note that currently we create relationship between
1150 * Individual + Household and Individual + Organization
6a488035 1151 *
6a0b768e
TO
1152 * @param int $masterAddressId
1153 * Master address id.
810c57a2
D
1154 * @param int $currentContactId
1155 * Current contact id.
6a488035 1156 */
810c57a2 1157 public static function processSharedAddressRelationship($masterAddressId, $currentContactId) {
6a488035 1158 // get the contact type of contact being edited / created
810c57a2 1159 $currentContactType = CRM_Contact_BAO_Contact::getContactType($currentContactId);
6a488035
TO
1160
1161 // if current contact is not of type individual return
1162 if ($currentContactType != 'Individual') {
1163 return;
1164 }
1165
1166 // get the contact id and contact type of shared contact
1167 // check the contact type of shared contact, return if it is of type Individual
6a488035
TO
1168 $query = 'SELECT cc.id, cc.contact_type
1169 FROM civicrm_contact cc INNER JOIN civicrm_address ca ON cc.id = ca.contact_id
1170 WHERE ca.id = %1';
1171
be2fb01f 1172 $dao = CRM_Core_DAO::executeQuery($query, [1 => [$masterAddressId, 'Integer']]);
6a488035
TO
1173 $dao->fetch();
1174
810c57a2 1175 // master address contact needs to be Household or Organization, otherwise return
6a488035
TO
1176 if ($dao->contact_type == 'Individual') {
1177 return;
1178 }
1179 $sharedContactType = $dao->contact_type;
1180 $sharedContactId = $dao->id;
1181
1182 // create relationship between ontacts who share an address
1183 if ($sharedContactType == 'Organization') {
1184 return CRM_Contact_BAO_Contact_Utils::createCurrentEmployerRelationship($currentContactId, $sharedContactId);
1185 }
6a488035 1186
82ffeed5 1187 // get the relationship type id of "Household Member of"
1188 $relTypeId = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_RelationshipType', 'Household Member of', 'id', 'name_a_b');
6a488035
TO
1189
1190 if (!$relTypeId) {
82ffeed5 1191 CRM_Core_Error::fatal(ts("You seem to have deleted the relationship type 'Household Member of'"));
1192 }
1193
be2fb01f 1194 $relParam = [
82ffeed5 1195 'is_active' => TRUE,
1196 'relationship_type_id' => $relTypeId,
1197 'contact_id_a' => $currentContactId,
1198 'contact_id_b' => $sharedContactId,
be2fb01f 1199 ];
82ffeed5 1200
1201 // If already there is a relationship record of $relParam criteria, avoid creating relationship again or else
1202 // it will casue CRM-16588 as the Duplicate Relationship Exception will revert other contact field values on update
9272d8b5 1203 if (CRM_Contact_BAO_Relationship::checkDuplicateRelationship($relParam, $currentContactId, $sharedContactId)) {
82ffeed5 1204 return;
6a488035
TO
1205 }
1206
a7ef8c9d
EM
1207 try {
1208 // create relationship
82ffeed5 1209 civicrm_api3('relationship', 'create', $relParam);
a7ef8c9d
EM
1210 }
1211 catch (CiviCRM_API3_Exception $e) {
1212 // We catch and ignore here because this has historically been a best-effort relationship create call.
1213 // presumably it could refuse due to duplication or similar and we would ignore that.
1214 }
6a488035
TO
1215 }
1216
1217 /**
fe482240 1218 * Check and set the status for shared address delete.
6a488035 1219 *
6a0b768e
TO
1220 * @param int $addressId
1221 * Address id.
1222 * @param int $contactId
1223 * Contact id.
1224 * @param bool $returnStatus
1225 * By default false.
6a488035 1226 *
a6c01b45 1227 * @return string
6a488035 1228 */
00be9182 1229 public static function setSharedAddressDeleteStatus($addressId = NULL, $contactId = NULL, $returnStatus = FALSE) {
6a488035
TO
1230 // check if address that is being deleted has any shared
1231 if ($addressId) {
1232 $entityId = $addressId;
1233 $query = 'SELECT cc.id, cc.display_name
1234 FROM civicrm_contact cc INNER JOIN civicrm_address ca ON cc.id = ca.contact_id
1235 WHERE ca.master_id = %1';
1236 }
1237 else {
1238 $entityId = $contactId;
1239 $query = 'SELECT cc.id, cc.display_name
1240 FROM civicrm_address ca1
1241 INNER JOIN civicrm_address ca2 ON ca1.id = ca2.master_id
1242 INNER JOIN civicrm_contact cc ON ca2.contact_id = cc.id
1243 WHERE ca1.contact_id = %1';
1244 }
1245
be2fb01f 1246 $dao = CRM_Core_DAO::executeQuery($query, [1 => [$entityId, 'Integer']]);
6a488035 1247
be2fb01f
CW
1248 $deleteStatus = [];
1249 $sharedContactList = [];
6a488035
TO
1250 $statusMessage = NULL;
1251 $addressCount = 0;
1252 while ($dao->fetch()) {
1253 if (empty($deleteStatus)) {
1254 $deleteStatus[] = ts('The following contact(s) have address records which were shared with the address you removed from this contact. These address records are no longer shared - but they have not been removed or altered.');
1255 }
1256
1257 $contactViewUrl = CRM_Utils_System::url('civicrm/contact/view', "reset=1&cid={$dao->id}");
1258 $sharedContactList[] = "<a href='{$contactViewUrl}'>{$dao->display_name}</a>";
1259 $deleteStatus[] = "<a href='{$contactViewUrl}'>{$dao->display_name}</a>";
1260
1261 $addressCount++;
1262 }
1263
1264 if (!empty($deleteStatus)) {
1265 $statusMessage = implode('<br/>', $deleteStatus) . '<br/>';
1266 }
1267
1268 if (!$returnStatus) {
1269 CRM_Core_Session::setStatus($statusMessage, '', 'info');
1270 }
1271 else {
be2fb01f 1272 return [
6a488035
TO
1273 'contactList' => $sharedContactList,
1274 'count' => $addressCount,
be2fb01f 1275 ];
6a488035
TO
1276 }
1277 }
12445e1c
CW
1278
1279 /**
fe482240 1280 * Call common delete function.
ad37ac8e 1281 *
1282 * @param int $id
1283 *
1284 * @return bool
12445e1c 1285 */
00be9182 1286 public static function del($id) {
a65e2e55 1287 return CRM_Contact_BAO_Contact::deleteObjectWithPrimary('Address', $id);
12445e1c 1288 }
dc86f881
CW
1289
1290 /**
1291 * Get options for a given address field.
1292 * @see CRM_Core_DAO::buildOptions
1293 *
1294 * TODO: Should we always assume chainselect? What fn should be responsible for controlling that flow?
1295 * TODO: In context of chainselect, what to return if e.g. a country has no states?
1296 *
6a0b768e
TO
1297 * @param string $fieldName
1298 * @param string $context
a1a2a83d 1299 * @see CRM_Core_DAO::buildOptionsContext
6a0b768e 1300 * @param array $props
16b10e64 1301 * whatever is known about this dao object.
77b97be7 1302 *
5c766a0b 1303 * @return array|bool
dc86f881 1304 */
be2fb01f
CW
1305 public static function buildOptions($fieldName, $context = NULL, $props = []) {
1306 $params = [];
dc86f881
CW
1307 // Special logic for fields whose options depend on context or properties
1308 switch ($fieldName) {
1309 // Filter state_province list based on chosen country or site defaults
1310 case 'state_province_id':
d0bfb983 1311 case 'state_province_name':
1312 case 'state_province':
1313 // change $fieldName to DB specific names.
1314 $fieldName = 'state_province_id';
63b56894 1315 if (empty($props['country_id']) && $context !== 'validate') {
dc86f881
CW
1316 $config = CRM_Core_Config::singleton();
1317 if (!empty($config->provinceLimit)) {
1318 $props['country_id'] = $config->provinceLimit;
1319 }
1320 else {
1321 $props['country_id'] = $config->defaultContactCountry;
1322 }
1323 }
eaf39b47 1324 if (!empty($props['country_id'])) {
62d7cac4
SL
1325 if (!CRM_Utils_Rule::commaSeparatedIntegers(implode(',', (array) $props['country_id']))) {
1326 throw new CRM_Core_Exception(ts('Province limit or default country setting is incorrect'));
1327 }
dc86f881
CW
1328 $params['condition'] = 'country_id IN (' . implode(',', (array) $props['country_id']) . ')';
1329 }
1330 break;
2aa397bc 1331
dc86f881
CW
1332 // Filter country list based on site defaults
1333 case 'country_id':
d0bfb983 1334 case 'country':
1335 // change $fieldName to DB specific names.
1336 $fieldName = 'country_id';
786ad6e1
CW
1337 if ($context != 'get' && $context != 'validate') {
1338 $config = CRM_Core_Config::singleton();
1339 if (!empty($config->countryLimit) && is_array($config->countryLimit)) {
62d7cac4
SL
1340 if (!CRM_Utils_Rule::commaSeparatedIntegers(implode(',', $config->countryLimit))) {
1341 throw new CRM_Core_Exception(ts('Available Country setting is incorrect'));
1342 }
786ad6e1
CW
1343 $params['condition'] = 'id IN (' . implode(',', $config->countryLimit) . ')';
1344 }
dc86f881
CW
1345 }
1346 break;
2aa397bc 1347
dc86f881
CW
1348 // Filter county list based on chosen state
1349 case 'county_id':
1350 if (!empty($props['state_province_id'])) {
62d7cac4
SL
1351 if (!CRM_Utils_Rule::commaSeparatedIntegers(implode(',', (array) $props['state_province_id']))) {
1352 throw new CRM_Core_Exception(ts('Can only accept Integers for state_province_id filtering'));
1353 }
dc86f881
CW
1354 $params['condition'] = 'state_province_id IN (' . implode(',', (array) $props['state_province_id']) . ')';
1355 }
1356 break;
2aa397bc 1357
c7130c3e
CW
1358 // Not a real field in this entity
1359 case 'world_region':
3493947a 1360 case 'worldregion':
1361 case 'worldregion_id':
b06ca616 1362 return CRM_Core_BAO_Country::buildOptions('region_id', $context, $props);
dc86f881 1363 }
786ad6e1 1364 return CRM_Core_PseudoConstant::get(__CLASS__, $fieldName, $params, $context);
dc86f881 1365 }
96025800 1366
6d04b727
FG
1367 /**
1368 * Add data from the configured geocoding provider.
1369 *
1370 * Generally this means latitude & longitude data.
1371 *
1372 * @param array $params
1373 * @return bool
1374 * TRUE if params could be passed to a provider, else FALSE.
1375 */
1376 public static function addGeocoderData(&$params) {
1377 try {
1378 $provider = CRM_Utils_GeocodeProvider::getConfiguredProvider();
1379 }
1380 catch (CRM_Core_Exception $e) {
1381 return FALSE;
1382 }
1383 $provider::format($params);
1384 return TRUE;
1385 }
1386
a7488080 1387}