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