3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.5 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2014 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
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. |
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. |
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 +--------------------------------------------------------------------+
31 * @copyright CiviCRM LLC (c) 2004-2014
37 * This is class to handle address related functions
39 class CRM_Core_BAO_Address
extends CRM_Core_DAO_Address
{
42 * takes an associative array and creates a address
44 * @param array $params (reference ) an assoc array of name/value pairs
45 * @param boolean $fixAddress true if you need to fix (format) address values
46 * before inserting in db
48 * @return array $blocks array of created address
52 static function create(&$params, $fixAddress, $entity = NULL) {
53 if (!isset($params['address']) ||
!is_array($params['address'])) {
56 CRM_Core_BAO_Block
::sortPrimaryFirst($params['address']);
60 $updateBlankLocInfo = CRM_Utils_Array
::value('updateBlankLocInfo', $params, FALSE);
62 $contactId = $params['contact_id'];
63 //get all the addresses for this contact
64 $addresses = self
::allAddress($contactId, $updateBlankLocInfo);
67 // get all address from location block
68 $entityElements = array(
69 'entity_table' => $params['entity_table'],
70 'entity_id' => $params['entity_id'],
72 $addresses = self
::allEntityAddress($entityElements);
75 $isPrimary = $isBilling = TRUE;
77 foreach ($params['address'] as $key => $value) {
78 if (!is_array($value)) {
82 $addressExists = self
::dataExists($value);
83 if (empty($value['id'])) {
84 if ($updateBlankLocInfo) {
85 if ((!empty($addresses) ||
!$addressExists) && array_key_exists($key, $addresses)) {
86 $value['id'] = $addresses[$key];
90 if (!empty($addresses) && array_key_exists(CRM_Utils_Array
::value('location_type_id', $value), $addresses)) {
91 $value['id'] = $addresses[CRM_Utils_Array
::value('location_type_id', $value)];
96 // Note there could be cases when address info already exist ($value[id] is set) for a contact/entity
97 // BUT info is not present at this time, and therefore we should be really careful when deleting the block.
98 // $updateBlankLocInfo will help take appropriate decision. CRM-5969
99 if (isset($value['id']) && !$addressExists && $updateBlankLocInfo) {
100 //delete the existing record
101 CRM_Core_BAO_Block
::blockDelete('Address', array('id' => $value['id']));
104 elseif (!$addressExists) {
108 if ($isPrimary && !empty($value['is_primary'])) {
112 $value['is_primary'] = 0;
115 if ($isBilling && !empty($value['is_billing'])) {
119 $value['is_billing'] = 0;
122 if (empty($value['manual_geo_code'])) {
123 $value['manual_geo_code'] = 0;
125 $value['contact_id'] = $contactId;
126 $blocks[] = self
::add($value, $fixAddress);
133 * takes an associative array and adds address
135 * @param array $params (reference ) an assoc array of name/value pairs
136 * @param boolean $fixAddress true if you need to fix (format) address values
137 * before inserting in db
139 * @return object CRM_Core_BAO_Address object on success, null otherwise
143 static function add(&$params, $fixAddress) {
144 static $customFields = NULL;
145 $address = new CRM_Core_DAO_Address();
147 // fixAddress mode to be done
149 CRM_Core_BAO_Address
::fixAddress($params);
152 $hook = empty($params['id']) ?
'create' : 'edit';
153 CRM_Utils_Hook
::pre($hook, 'Address', CRM_Utils_Array
::value('id', $params), $params);
155 // if id is set & is_primary isn't we can assume no change
156 if (is_numeric(CRM_Utils_Array
::value('is_primary', $params)) ||
empty($params['id'])) {
157 CRM_Core_BAO_Block
::handlePrimary($params, get_class());
159 $config = CRM_Core_Config
::singleton();
160 $address->copyValues($params);
165 if (!$customFields) {
166 $customFields = CRM_Core_BAO_CustomField
::getFields('Address', FALSE, TRUE);
168 if (!empty($customFields)) {
169 $addressCustom = CRM_Core_BAO_CustomField
::postProcess($params,
176 if (!empty($addressCustom)) {
177 CRM_Core_BAO_CustomValueTable
::store($addressCustom, 'civicrm_address', $address->id
);
180 //call the function to sync shared address
181 self
::processSharedAddress($address->id
, $params);
183 // call the function to create shared relationships
184 // we only create create relationship if address is shared by Individual
185 if ($address->master_id
!= 'null') {
186 self
::processSharedAddressRelationship($address->master_id
, $params);
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);
197 * format the address params to have reasonable values
199 * @param array $params (reference ) an assoc array of name/value pairs
205 static function fixAddress(&$params) {
206 if (!empty($params['billing_street_address'])) {
207 //Check address is comming from online contribution / registration page
210 'street_address' => 'billing_street_address',
211 'city' => 'billing_city',
212 'postal_code' => 'billing_postal_code',
213 'state_province' => 'billing_state_province',
214 'state_province_id' => 'billing_state_province_id',
215 'country' => 'billing_country',
216 'country_id' => 'billing_country_id',
219 foreach ($billing as $key => $val) {
220 if ($value = CRM_Utils_Array
::value($val, $params)) {
221 if (!empty($params[$key])) {
222 unset($params[$val]);
225 //add new key and removed old
226 $params[$key] = $value;
227 unset($params[$val]);
233 /* Split the zip and +4, if it's in US format */
234 if (!empty($params['postal_code']) &&
235 preg_match('/^(\d{4,5})[+-](\d{4})$/',
236 $params['postal_code'],
240 $params['postal_code'] = $match[1];
241 $params['postal_code_suffix'] = $match[2];
244 // add country id if not set
245 if ((!isset($params['country_id']) ||
!is_numeric($params['country_id'])) &&
246 isset($params['country'])
248 $country = new CRM_Core_DAO_Country();
249 $country->name
= $params['country'];
250 if (!$country->find(TRUE)) {
251 $country->name
= NULL;
252 $country->iso_code
= $params['country'];
253 $country->find(TRUE);
255 $params['country_id'] = $country->id
;
258 // add state_id if state is set
259 if ((!isset($params['state_province_id']) ||
!is_numeric($params['state_province_id']))
260 && isset($params['state_province'])
262 if (!empty($params['state_province'])) {
263 $state_province = new CRM_Core_DAO_StateProvince();
264 $state_province->name
= $params['state_province'];
266 // add country id if present
267 if (!empty($params['country_id'])) {
268 $state_province->country_id
= $params['country_id'];
271 if (!$state_province->find(TRUE)) {
272 unset($state_province->name
);
273 $state_province->abbreviation
= $params['state_province'];
274 $state_province->find(TRUE);
276 $params['state_province_id'] = $state_province->id
;
277 if (empty($params['country_id'])) {
278 // set this here since we have it
279 $params['country_id'] = $state_province->country_id
;
283 $params['state_province_id'] = 'null';
287 // add county id if county is set
289 if ((!isset($params['county_id']) ||
!is_numeric($params['county_id']))
290 && isset($params['county']) && !empty($params['county'])
292 $county = new CRM_Core_DAO_County();
293 $county->name
= $params['county'];
295 if (isset($params['state_province_id'])) {
296 $county->state_province_id
= $params['state_province_id'];
299 if ($county->find(TRUE)) {
300 $params['county_id'] = $county->id
;
304 // currently copy values populates empty fields with the string "null"
305 // and hence need to check for the string null
306 if (isset($params['state_province_id']) &&
307 is_numeric($params['state_province_id']) &&
308 (!isset($params['country_id']) ||
empty($params['country_id']))
310 // since state id present and country id not present, hence lets populate it
311 // jira issue http://issues.civicrm.org/jira/browse/CRM-56
312 $params['country_id'] = CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_StateProvince',
313 $params['state_province_id'],
318 //special check to ignore non numeric values if they are not
319 //detected by formRule(sometimes happens due to internet latency), also allow user to unselect state/country
320 if (isset($params['state_province_id'])) {
321 if (empty($params['state_province_id'])) {
322 $params['state_province_id'] = 'null';
324 elseif (!is_numeric($params['state_province_id']) ||
325 ((int ) $params['state_province_id'] < 1000)
327 // CRM-3393 ( the hacky 1000 check)
328 $params['state_province_id'] = 'null';
332 if (isset($params['country_id'])) {
333 if (empty($params['country_id'])) {
334 $params['country_id'] = 'null';
336 elseif (!is_numeric($params['country_id']) ||
337 ((int ) $params['country_id'] < 1000)
339 // CRM-3393 ( the hacky 1000 check)
340 $params['country_id'] = 'null';
344 // add state and country names from the ids
345 if (isset($params['state_province_id']) && is_numeric($params['state_province_id'])) {
346 $params['state_province'] = CRM_Core_PseudoConstant
::stateProvinceAbbreviation($params['state_province_id']);
349 if (isset($params['country_id']) && is_numeric($params['country_id'])) {
350 $params['country'] = CRM_Core_PseudoConstant
::country($params['country_id']);
353 $config = CRM_Core_Config
::singleton();
355 $asp = CRM_Core_BAO_Setting
::getItem(CRM_Core_BAO_Setting
::ADDRESS_STANDARDIZATION_PREFERENCES_NAME
,
356 'address_standardization_provider'
358 // clean up the address via USPS web services if enabled
359 if ($asp === 'USPS' &&
360 $params['country_id'] == 1228
362 CRM_Utils_Address_USPS
::checkAddress($params);
364 // do street parsing again if enabled, since street address might have changed
365 $parseStreetAddress =
366 CRM_Utils_Array
::value(
367 'street_address_parsing',
368 CRM_Core_BAO_Setting
::valueOptions(
369 CRM_Core_BAO_Setting
::SYSTEM_PREFERENCES_NAME
,
375 if ($parseStreetAddress && !empty($params['street_address'])) {
377 'street_number', 'street_name', 'street_unit', 'street_number_suffix') as $fld) {
378 unset($params[$fld]);
380 // main parse string.
381 $parseString = CRM_Utils_Array
::value('street_address', $params);
382 $parsedFields = CRM_Core_BAO_Address
::parseStreetAddress($parseString);
384 // merge parse address in to main address block.
385 $params = array_merge($params, $parsedFields);
389 // add latitude and longitude and format address if needed
390 if (!empty($config->geocodeMethod
) && ($config->geocodeMethod
!= 'CRM_Utils_Geocode_OpenStreetMaps') && empty($params['manual_geo_code'])) {
391 $class = $config->geocodeMethod
;
392 $class::format($params);
397 * Check if there is data to create the object
399 * @param array $params (reference ) an assoc array of name/value pairs
406 static function dataExists(&$params) {
407 //check if location type is set if not return false
408 if (!isset($params['location_type_id'])) {
412 $config = CRM_Core_Config
::singleton();
413 foreach ($params as $name => $value) {
414 if (in_array($name, array(
415 'is_primary', 'location_type_id', 'id', 'contact_id', 'is_billing', 'display', 'master_id'))) {
418 elseif (!CRM_Utils_System
::isNull($value)) {
419 // name could be country or country id
420 if (substr($name, 0, 7) == 'country') {
421 // make sure its different from the default country
423 $defaultCountry = $config->defaultContactCountry();
425 $defaultCountryName = $config->defaultContactCountryName();
427 if ($defaultCountry) {
428 if ($value == $defaultCountry ||
429 $value == $defaultCountryName ||
430 $value == $config->defaultContactCountry
439 // return if null default
453 * Given the list of params in the params array, fetch the object
454 * and store the values in the values array
456 * @param array $entityBlock associated array of fields
457 * @param boolean $microformat if microformat output is required
458 * @param int $fieldName conditional field name
460 * @return array $addresses array with address fields
464 static function &getValues(&$entityBlock, $microformat = FALSE, $fieldName = 'contact_id') {
465 if (empty($entityBlock)) {
468 $addresses = array();
469 $address = new CRM_Core_BAO_Address();
471 if (empty($entityBlock['entity_table'])) {
472 $address->$fieldName = CRM_Utils_Array
::value($fieldName, $entityBlock);
475 $addressIds = array();
476 $addressIds = self
::allEntityAddress($entityBlock);
478 if (!empty($addressIds[1])) {
479 $address->id
= $addressIds[1];
485 //get primary address as a first block.
486 $address->orderBy('is_primary desc, id');
491 while ($address->fetch()) {
492 // deprecate reference.
495 'state', 'state_name', 'country', 'world_region') as $fld) {
496 if (isset($address->$fld))unset($address->$fld);
499 $stree = $address->street_address
;
501 CRM_Core_DAO
::storeValues($address, $values);
503 // add state and country information: CRM-369
504 if (!empty($address->state_province_id
)) {
505 $address->state
= CRM_Core_PseudoConstant
::stateProvinceAbbreviation($address->state_province_id
, FALSE);
506 $address->state_name
= CRM_Core_PseudoConstant
::stateProvince($address->state_province_id
, FALSE);
509 if (!empty($address->country_id
)) {
510 $address->country
= CRM_Core_PseudoConstant
::country($address->country_id
);
513 $regionId = CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_Country', $address->country_id
, 'region_id');
515 $address->world_region
= CRM_Core_PseudoConstant
::worldregion($regionId);
518 $address->addDisplay($microformat);
520 $values['display'] = $address->display
;
521 $values['display_text'] = $address->display_text
;
523 if (is_numeric($address->master_id
)) {
524 $values['use_shared_address'] = 1;
527 $addresses[$count] = $values;
529 //unset is_primary after first block. Due to some bug in earlier version
530 //there might be more than one primary blocks, hence unset is_primary other than first
532 unset($addresses[$count]['is_primary']);
542 * Add the formatted address to $this-> display
551 function addDisplay($microformat = FALSE) {
553 // added this for CRM 1200
554 'address_id' => $this->id
,
556 'address_name' => str_replace('\ 1', ' ', $this->name
),
557 'street_address' => $this->street_address
,
558 'supplemental_address_1' => $this->supplemental_address_1
,
559 'supplemental_address_2' => $this->supplemental_address_2
,
560 'city' => $this->city
,
561 'state_province_name' => isset($this->state_name
) ?
$this->state_name
: "",
562 'state_province' => isset($this->state
) ?
$this->state
: "",
563 'postal_code' => isset($this->postal_code
) ?
$this->postal_code
: "",
564 'postal_code_suffix' => isset($this->postal_code_suffix
) ?
$this->postal_code_suffix
: "",
565 'country' => isset($this->country
) ?
$this->country
: "",
566 'world_region' => isset($this->world_region
) ?
$this->world_region
: "",
569 if (isset($this->county_id
) && $this->county_id
) {
570 $fields['county'] = CRM_Core_PseudoConstant
::county($this->county_id
);
573 $fields['county'] = NULL;
576 $this->display
= CRM_Utils_Address
::format($fields, NULL, $microformat);
577 $this->display_text
= CRM_Utils_Address
::format($fields);
581 * Get all the addresses for a specified contact_id, with the primary address being first
583 * @param int $id the contact id
585 * @return array the array of adrress data
589 static function allAddress($id, $updateBlankLocInfo = FALSE) {
595 SELECT civicrm_address.id as address_id, civicrm_address.location_type_id as location_type_id
596 FROM civicrm_contact, civicrm_address
597 WHERE civicrm_address.contact_id = civicrm_contact.id AND civicrm_contact.id = %1
598 ORDER BY civicrm_address.is_primary DESC, address_id ASC";
599 $params = array(1 => array($id, 'Integer'));
601 $addresses = array();
602 $dao = CRM_Core_DAO
::executeQuery($query, $params);
604 while ($dao->fetch()) {
605 if ($updateBlankLocInfo) {
606 $addresses[$count++
] = $dao->address_id
;
609 $addresses[$dao->location_type_id
] = $dao->address_id
;
616 * Get all the addresses for a specified location_block id, with the primary address being first
618 * @param array $entityElements the array containing entity_id and
621 * @return array the array of adrress data
625 static function allEntityAddress(&$entityElements) {
626 if (empty($entityElements)) {
630 $entityId = $entityElements['entity_id'];
631 $entityTable = $entityElements['entity_table'];
634 SELECT civicrm_address.id as address_id
635 FROM civicrm_loc_block loc, civicrm_location_type ltype, civicrm_address, {$entityTable} ev
637 AND loc.id = ev.loc_block_id
638 AND civicrm_address.id IN (loc.address_id, loc.address_2_id)
639 AND ltype.id = civicrm_address.location_type_id
640 ORDER BY civicrm_address.is_primary DESC, civicrm_address.location_type_id DESC, address_id ASC ";
642 $params = array(1 => array($entityId, 'Integer'));
643 $addresses = array();
644 $dao = CRM_Core_DAO
::executeQuery($sql, $params);
646 while ($dao->fetch()) {
647 $addresses[$locationCount] = $dao->address_id
;
653 static function addStateCountryMap(&$stateCountryMap, $defaults = NULL) {
654 // first fix the statecountry map if needed
655 if (empty($stateCountryMap)) {
659 $config = CRM_Core_Config
::singleton();
660 if (!isset($config->stateCountryMap
)) {
661 $config->stateCountryMap
= array();
664 $config->stateCountryMap
= array_merge($config->stateCountryMap
, $stateCountryMap);
667 static function fixAllStateSelects(&$form, $defaults, $batchFieldNames = false) {
668 $config = CRM_Core_Config
::singleton();
670 if (is_array($batchFieldNames)) {
671 $map = $batchFieldNames;
673 elseif (!empty($config->stateCountryMap
)) {
674 $map = $config->stateCountryMap
;
677 foreach ($map as $index => $match) {
678 if (array_key_exists('state_province', $match)
679 ||
array_key_exists('country', $match)
680 ||
array_key_exists('county', $match)
682 $countryElementName = CRM_Utils_Array
::value('country', $match);
683 $stateProvinceElementName = CRM_Utils_Array
::value('state_province', $match);
684 $countyElementName = CRM_Utils_Array
::value('county', $match);
685 CRM_Contact_Form_Edit_Address
::fixStateSelect(
688 $stateProvinceElementName,
690 CRM_Utils_Array
::value($countryElementName, $defaults),
691 CRM_Utils_Array
::value($stateProvinceElementName, $defaults)
695 unset($config->stateCountryMap
[$index]);
702 * Function to get address sequence
704 * @return array of address sequence.
706 static function addressSequence() {
707 $config = CRM_Core_Config
::singleton();
708 $addressSequence = $config->addressSequence();
710 $countryState = $cityPostal = FALSE;
711 foreach ($addressSequence as $key => $field) {
713 in_array($field, array('country', 'state_province')) &&
716 $countryState = TRUE;
717 $addressSequence[$key] = 'country_state_province';
720 in_array($field, array('city', 'postal_code')) &&
724 $addressSequence[$key] = 'city_postal_code';
727 in_array($field, array('country', 'state_province', 'city', 'postal_code'))
729 unset($addressSequence[$key]);
733 return $addressSequence;
737 * Parse given street address string in to street_name,
738 * street_unit, street_number and street_number_suffix
739 * eg "54A Excelsior Ave. Apt 1C", or "917 1/2 Elm Street"
741 * NB: civic street formats for en_CA and fr_CA used by default if those locales are active
742 * otherwise en_US format is default action
744 * @param string Street address including number and apt
745 * @param string Locale - to set locale used to parse address
747 * @return array $parseFields parsed fields values.
751 static function parseStreetAddress($streetAddress, $locale = NULL) {
752 $config = CRM_Core_Config
::singleton();
754 /* locales supported include:
755 * en_US - http://pe.usps.com/cpim/ftp/pubs/pub28/pub28.pdf
756 * en_CA - http://www.canadapost.ca/tools/pg/manual/PGaddress-e.asp
757 * fr_CA - http://www.canadapost.ca/tools/pg/manual/PGaddress-f.asp
758 * NB: common use of comma after street number also supported
762 $supportedLocalesForParsing = array('en_US', 'en_CA', 'fr_CA');
764 $locale = $config->lcMessages
;
766 // as different locale explicitly requested but is not available, display warning message and set $locale = 'en_US'
767 if (!in_array($locale, $supportedLocalesForParsing)) {
768 CRM_Core_Session
::setStatus(ts('Unsupported locale specified to parseStreetAddress: %1. Proceeding with en_US locale.', array(1 => $locale)), ts('Unsupported Locale'), 'alert');
771 $parseFields = array(
774 'street_number' => '',
775 'street_number_suffix' => '',
778 if (empty($streetAddress)) {
782 $streetAddress = trim($streetAddress);
785 if (in_array($locale, array(
786 'en_CA', 'fr_CA')) && preg_match('/^([A-Za-z0-9]+)[ ]*\-[ ]*/', $streetAddress, $matches)) {
787 $parseFields['street_unit'] = $matches[1];
788 // unset from rest of street address
789 $streetAddress = preg_replace('/^([A-Za-z0-9]+)[ ]*\-[ ]*/', '', $streetAddress);
792 // get street number and suffix.
794 //alter street number/suffix handling so that we accept -digit
795 if (preg_match('/^[A-Za-z0-9]+([\S]+)/', $streetAddress, $matches)) {
796 // check that $matches[0] is numeric, else assume no street number
797 if (preg_match('/^(\d+)/', $matches[0])) {
798 $streetNumAndSuffix = $matches[0];
800 // get street number.
802 if (preg_match('/^(\d+)/', $streetNumAndSuffix, $matches)) {
803 $parseFields['street_number'] = $matches[0];
804 $suffix = preg_replace('/^(\d+)/', '', $streetNumAndSuffix);
805 $parseFields['street_number_suffix'] = trim($suffix);
808 // unset from main street address.
809 $streetAddress = preg_replace('/^[A-Za-z0-9]+([\S]+)/', '', $streetAddress);
810 $streetAddress = trim($streetAddress);
813 elseif (preg_match('/^(\d+)/', $streetAddress, $matches)) {
814 $parseFields['street_number'] = $matches[0];
815 // unset from main street address.
816 $streetAddress = preg_replace('/^(\d+)/', '', $streetAddress);
817 $streetAddress = trim($streetAddress);
820 // suffix might be like 1/2
822 if (preg_match('/^\d\/\d/', $streetAddress, $matches)) {
823 $parseFields['street_number_suffix'] .= $matches[0];
825 // unset from main street address.
826 $streetAddress = preg_replace('/^\d+\/\d+/', '', $streetAddress);
827 $streetAddress = trim($streetAddress);
830 // now get the street unit.
831 // supportable street unit formats.
832 $streetUnitFormats = array(
833 'APT', 'APARTMENT', 'BSMT', 'BASEMENT', 'BLDG', 'BUILDING',
834 'DEPT', 'DEPARTMENT', 'FL', 'FLOOR', 'FRNT', 'FRONT',
835 'HNGR', 'HANGER', 'LBBY', 'LOBBY', 'LOWR', 'LOWER',
836 'OFC', 'OFFICE', 'PH', 'PENTHOUSE', 'TRLR', 'TRAILER',
837 'UPPR', 'RM', 'ROOM', 'SIDE', 'SLIP', 'KEY',
838 'LOT', 'PIER', 'REAR', 'SPC', 'SPACE',
839 'STOP', 'STE', 'SUITE', 'UNIT', '#',
842 // overwriting $streetUnitFormats for 'en_CA' and 'fr_CA' locale
843 if (in_array($locale, array(
844 'en_CA', 'fr_CA'))) {
845 $streetUnitFormats = array('APT', 'APP', 'SUITE', 'BUREAU', 'UNIT');
848 $streetUnitPreg = '/(' . implode('|\s', $streetUnitFormats) . ')(.+)?/i';
850 if (preg_match($streetUnitPreg, $streetAddress, $matches)) {
851 $parseFields['street_unit'] = trim($matches[0]);
852 $streetAddress = str_replace($matches[0], '', $streetAddress);
853 $streetAddress = trim($streetAddress);
856 // consider remaining string as street name.
857 $parseFields['street_name'] = $streetAddress;
859 //run parsed fields through stripSpaces to clean
860 foreach ($parseFields as $parseField => $value) {
861 $parseFields[$parseField] = CRM_Utils_String
::stripSpaces($value);
868 * Validate the address fields based on the address options enabled
869 * in the Address Settings
871 * @param array $fields an array of importable/exportable contact fields
873 * @return array $fields an array of contact fields and only the enabled address options
877 static function validateAddressOptions($fields) {
878 static $addressOptions = NULL;
879 if (!$addressOptions) {
881 CRM_Core_BAO_Setting
::valueOptions(
882 CRM_Core_BAO_Setting
::SYSTEM_PREFERENCES_NAME
,
887 if (is_array($fields) && !empty($fields)) {
888 foreach ($addressOptions as $key => $value) {
889 if (!$value && isset($fields[$key])) {
890 unset($fields[$key]);
898 * Check if current address is used by any other contacts
900 * @param int $addressId address id
902 * @return count of contacts that use this shared address
906 static function checkContactSharedAddress($addressId) {
907 $query = 'SELECT count(id) FROM civicrm_address WHERE master_id = %1';
908 return CRM_Core_DAO
::singleValueQuery($query, array(1 => array($addressId, 'Integer')));
912 * Function to check if current address fields are shared with any other address
914 * @param array $fields address fields in profile
915 * @param int $contactId contact id
920 static function checkContactSharedAddressFields(&$fields, $contactId) {
921 if (!$contactId ||
!is_array($fields) ||
empty($fields)) {
925 $sharedLocations = array();
931 WHERE contact_id = %1
932 AND master_id IS NOT NULL";
934 $dao = CRM_Core_DAO
::executeQuery($query, array(1 => array($contactId, 'Positive')));
935 while ($dao->fetch()) {
936 $sharedLocations[$dao->location_type_id
] = $dao->location_type_id
;
937 if ($dao->is_primary
) {
938 $sharedLocations['Primary'] = 'Primary';
942 //no need to process further.
943 if (empty($sharedLocations)) {
947 $addressFields = array(
957 'postal_code_suffix',
958 'supplemental_address_1',
959 'supplemental_address_2',
962 foreach ($fields as $name => & $values) {
963 if (!is_array($values) ||
empty($values)) {
967 $nameVal = explode('-', $values['name']);
968 $fldName = CRM_Utils_Array
::value(0, $nameVal);
969 $locType = CRM_Utils_Array
::value(1, $nameVal);
970 if (!empty($values['location_type_id'])) {
971 $locType = $values['location_type_id'];
974 if (in_array($fldName, $addressFields) &&
975 in_array($locType, $sharedLocations)
977 $values['is_shared'] = TRUE;
983 * Function to update the shared addresses if master address is modified
985 * @param int $addressId address id
986 * @param array $params associated array of address params
992 static function processSharedAddress($addressId, $params) {
993 $query = 'SELECT id FROM civicrm_address WHERE master_id = %1';
994 $dao = CRM_Core_DAO
::executeQuery($query, array(1 => array($addressId, 'Integer')));
997 $skipFields = array('is_primary', 'location_type_id', 'is_billing', 'master_id', 'contact_id');
998 foreach ($skipFields as $value) {
999 unset($params[$value]);
1002 $addressDAO = new CRM_Core_DAO_Address();
1003 while ($dao->fetch()) {
1004 $addressDAO->copyValues($params);
1005 $addressDAO->id
= $dao->id
;
1006 $addressDAO->save();
1007 $addressDAO->free();
1012 * Function to create relationship between contacts who share an address
1014 * Note that currently we create relationship only for Individual contacts
1015 * Individual + Household and Individual + Orgnization
1017 * @param int $masterAddressId master address id
1018 * @param array $params associated array of submitted values
1024 static function processSharedAddressRelationship($masterAddressId, $params) {
1025 if (!$masterAddressId) {
1028 // get the contact type of contact being edited / created
1029 $currentContactType = CRM_Contact_BAO_Contact
::getContactType($params['contact_id']);
1030 $currentContactId = $params['contact_id'];
1032 // if current contact is not of type individual return
1033 if ($currentContactType != 'Individual') {
1037 // get the contact id and contact type of shared contact
1038 // check the contact type of shared contact, return if it is of type Individual
1040 $query = 'SELECT cc.id, cc.contact_type
1041 FROM civicrm_contact cc INNER JOIN civicrm_address ca ON cc.id = ca.contact_id
1044 $dao = CRM_Core_DAO
::executeQuery($query, array(1 => array($masterAddressId, 'Integer')));
1048 // if current contact is not of type individual return, since we don't create relationship between
1050 if ($dao->contact_type
== 'Individual') {
1053 $sharedContactType = $dao->contact_type
;
1054 $sharedContactId = $dao->id
;
1056 // create relationship between ontacts who share an address
1057 if ($sharedContactType == 'Organization') {
1058 return CRM_Contact_BAO_Contact_Utils
::createCurrentEmployerRelationship($currentContactId, $sharedContactId);
1061 // get the relationship type id of "Household Member of"
1062 $relationshipType = 'Household Member of';
1065 $cid = array('contact' => $currentContactId);
1067 $relTypeId = CRM_Core_DAO
::getFieldValue('CRM_Contact_DAO_RelationshipType', $relationshipType, 'id', 'name_a_b');
1070 CRM_Core_Error
::fatal(ts("You seem to have deleted the relationship type '%1'", array(1 => $relationshipType)));
1073 // create relationship
1074 $relationshipParams = array(
1075 'is_active' => TRUE,
1076 'relationship_type_id' => $relTypeId . '_a_b',
1077 'contact_check' => array($sharedContactId => TRUE),
1080 list($valid, $invalid, $duplicate,
1081 $saved, $relationshipIds
1082 ) = CRM_Contact_BAO_Relationship
::create($relationshipParams, $cid);
1086 * Function to check and set the status for shared address delete
1088 * @param int $addressId address id
1089 * @param int $contactId contact id
1090 * @param boolean $returnStatus by default false
1092 * @return string $statusMessage
1096 static function setSharedAddressDeleteStatus($addressId = NULL, $contactId = NULL, $returnStatus = FALSE) {
1097 // check if address that is being deleted has any shared
1099 $entityId = $addressId;
1100 $query = 'SELECT cc.id, cc.display_name
1101 FROM civicrm_contact cc INNER JOIN civicrm_address ca ON cc.id = ca.contact_id
1102 WHERE ca.master_id = %1';
1105 $entityId = $contactId;
1106 $query = 'SELECT cc.id, cc.display_name
1107 FROM civicrm_address ca1
1108 INNER JOIN civicrm_address ca2 ON ca1.id = ca2.master_id
1109 INNER JOIN civicrm_contact cc ON ca2.contact_id = cc.id
1110 WHERE ca1.contact_id = %1';
1113 $dao = CRM_Core_DAO
::executeQuery($query, array(1 => array($entityId, 'Integer')));
1115 $deleteStatus = array();
1116 $sharedContactList = array();
1117 $statusMessage = NULL;
1119 while ($dao->fetch()) {
1120 if (empty($deleteStatus)) {
1121 $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.');
1124 $contactViewUrl = CRM_Utils_System
::url('civicrm/contact/view', "reset=1&cid={$dao->id}");
1125 $sharedContactList[] = "<a href='{$contactViewUrl}'>{$dao->display_name}</a>";
1126 $deleteStatus[] = "<a href='{$contactViewUrl}'>{$dao->display_name}</a>";
1131 if (!empty($deleteStatus)) {
1132 $statusMessage = implode('<br/>', $deleteStatus) . '<br/>';
1135 if (!$returnStatus) {
1136 CRM_Core_Session
::setStatus($statusMessage, '', 'info');
1140 'contactList' => $sharedContactList,
1141 'count' => $addressCount,
1147 * Call common delete function
1149 static function del($id) {
1150 return CRM_Contact_BAO_Contact
::deleteObjectWithPrimary('Address', $id);
1154 * Get options for a given address field.
1155 * @see CRM_Core_DAO::buildOptions
1157 * TODO: Should we always assume chainselect? What fn should be responsible for controlling that flow?
1158 * TODO: In context of chainselect, what to return if e.g. a country has no states?
1160 * @param String $fieldName
1161 * @param String $context: @see CRM_Core_DAO::buildOptionsContext
1162 * @param Array $props: whatever is known about this dao object
1164 public static function buildOptions($fieldName, $context = NULL, $props = array()) {
1166 // Special logic for fields whose options depend on context or properties
1167 switch ($fieldName) {
1168 // Filter state_province list based on chosen country or site defaults
1169 case 'state_province_id':
1170 if (empty($props['country_id'])) {
1171 $config = CRM_Core_Config
::singleton();
1172 if (!empty($config->provinceLimit
)) {
1173 $props['country_id'] = $config->provinceLimit
;
1176 $props['country_id'] = $config->defaultContactCountry
;
1179 if (!empty($props['country_id'])) {
1180 $params['condition'] = 'country_id IN (' . implode(',', (array) $props['country_id']) . ')';
1183 // Filter country list based on site defaults
1185 if ($context != 'get' && $context != 'validate') {
1186 $config = CRM_Core_Config
::singleton();
1187 if (!empty($config->countryLimit
) && is_array($config->countryLimit
)) {
1188 $params['condition'] = 'id IN (' . implode(',', $config->countryLimit
) . ')';
1192 // Filter county list based on chosen state
1194 if (!empty($props['state_province_id'])) {
1195 $params['condition'] = 'state_province_id IN (' . implode(',', (array) $props['state_province_id']) . ')';
1198 // Not a real field in this entity
1199 case 'world_region':
1200 return CRM_Core_PseudoConstant
::worldRegion();
1203 return CRM_Core_PseudoConstant
::get(__CLASS__
, $fieldName, $params, $context);