3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.3 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2013 |
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-2013
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 ( !CRM_Utils_Array
::value('id', $value) ) {
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 && CRM_Utils_Array
::value('is_primary', $value)) {
112 $value['is_primary'] = 0;
115 if ($isBilling && CRM_Utils_Array
::value('is_billing', $value)) {
119 $value['is_billing'] = 0;
122 if (!CRM_Utils_Array
::value('manual_geo_code', $value)) {
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 (CRM_Utils_Array
::value('billing_street_address', $params)) {
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 (CRM_Utils_Array
::value($key, $params)) {
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 (CRM_Utils_Array
::value('postal_code', $params) &&
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 (!CRM_Utils_Array
::value('entity_table', $entityBlock)) {
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();
671 if (is_array($batchFieldNames)) {
672 $map = $batchFieldNames;
674 else if (!empty($config->stateCountryMap
)) {
675 $map = $config->stateCountryMap
;
679 foreach ($map as $index => $match) {
681 array_key_exists('state_province', $match) &&
682 array_key_exists('country', $match)
684 CRM_Contact_Form_Edit_Address
::fixStateSelect(
687 $match['state_province'],
688 CRM_Utils_Array
::value('county', $match),
689 CRM_Utils_Array
::value($match['country'], $defaults),
690 CRM_Utils_Array
::value($match['state_province'], $defaults)
694 unset($config->stateCountryMap
[$index]);
701 * Function to get address sequence
703 * @return array of address sequence.
705 static function addressSequence() {
706 $config = CRM_Core_Config
::singleton();
707 $addressSequence = $config->addressSequence();
709 $countryState = $cityPostal = FALSE;
710 foreach ($addressSequence as $key => $field) {
712 in_array($field, array('country', 'state_province')) &&
715 $countryState = TRUE;
716 $addressSequence[$key] = 'country_state_province';
719 in_array($field, array('city', 'postal_code')) &&
723 $addressSequence[$key] = 'city_postal_code';
726 in_array($field, array('country', 'state_province', 'city', 'postal_code'))
728 unset($addressSequence[$key]);
732 return $addressSequence;
736 * Parse given street address string in to street_name,
737 * street_unit, street_number and street_number_suffix
738 * eg "54A Excelsior Ave. Apt 1C", or "917 1/2 Elm Street"
740 * NB: civic street formats for en_CA and fr_CA used by default if those locales are active
741 * otherwise en_US format is default action
743 * @param string Street address including number and apt
744 * @param string Locale - to set locale used to parse address
746 * @return array $parseFields parsed fields values.
750 static function parseStreetAddress($streetAddress, $locale = NULL) {
751 $config = CRM_Core_Config
::singleton();
753 /* locales supported include:
754 * en_US - http://pe.usps.com/cpim/ftp/pubs/pub28/pub28.pdf
755 * en_CA - http://www.canadapost.ca/tools/pg/manual/PGaddress-e.asp
756 * fr_CA - http://www.canadapost.ca/tools/pg/manual/PGaddress-f.asp
757 * NB: common use of comma after street number also supported
761 $supportedLocalesForParsing = array('en_US', 'en_CA', 'fr_CA');
763 $locale = $config->lcMessages
;
765 // as different locale explicitly requested but is not available, display warning message and set $locale = 'en_US'
766 if (!in_array($locale, $supportedLocalesForParsing)) {
767 CRM_Core_Session
::setStatus(ts('Unsupported locale specified to parseStreetAddress: %1. Proceeding with en_US locale.', array(1 => $locale)), ts('Unsupported Locale'), 'alert');
770 $parseFields = array(
773 'street_number' => '',
774 'street_number_suffix' => '',
777 if (empty($streetAddress)) {
781 $streetAddress = trim($streetAddress);
784 if (in_array($locale, array(
785 'en_CA', 'fr_CA')) && preg_match('/^([A-Za-z0-9]+)[ ]*\-[ ]*/', $streetAddress, $matches)) {
786 $parseFields['street_unit'] = $matches[1];
787 // unset from rest of street address
788 $streetAddress = preg_replace('/^([A-Za-z0-9]+)[ ]*\-[ ]*/', '', $streetAddress);
791 // get street number and suffix.
793 //alter street number/suffix handling so that we accept -digit
794 if (preg_match('/^[A-Za-z0-9]+([\S]+)/', $streetAddress, $matches)) {
795 // check that $matches[0] is numeric, else assume no street number
796 if (preg_match('/^(\d+)/', $matches[0])) {
797 $streetNumAndSuffix = $matches[0];
799 // get street number.
801 if (preg_match('/^(\d+)/', $streetNumAndSuffix, $matches)) {
802 $parseFields['street_number'] = $matches[0];
803 $suffix = preg_replace('/^(\d+)/', '', $streetNumAndSuffix);
804 $parseFields['street_number_suffix'] = trim($suffix);
807 // unset from main street address.
808 $streetAddress = preg_replace('/^[A-Za-z0-9]+([\S]+)/', '', $streetAddress);
809 $streetAddress = trim($streetAddress);
812 elseif (preg_match('/^(\d+)/', $streetAddress, $matches)) {
813 $parseFields['street_number'] = $matches[0];
814 // unset from main street address.
815 $streetAddress = preg_replace('/^(\d+)/', '', $streetAddress);
816 $streetAddress = trim($streetAddress);
819 // suffix might be like 1/2
821 if (preg_match('/^\d\/\d/', $streetAddress, $matches)) {
822 $parseFields['street_number_suffix'] .= $matches[0];
824 // unset from main street address.
825 $streetAddress = preg_replace('/^\d+\/\d+/', '', $streetAddress);
826 $streetAddress = trim($streetAddress);
829 // now get the street unit.
830 // supportable street unit formats.
831 $streetUnitFormats = array(
832 'APT', 'APARTMENT', 'BSMT', 'BASEMENT', 'BLDG', 'BUILDING',
833 'DEPT', 'DEPARTMENT', 'FL', 'FLOOR', 'FRNT', 'FRONT',
834 'HNGR', 'HANGER', 'LBBY', 'LOBBY', 'LOWR', 'LOWER',
835 'OFC', 'OFFICE', 'PH', 'PENTHOUSE', 'TRLR', 'TRAILER',
836 'UPPR', 'RM', 'ROOM', 'SIDE', 'SLIP', 'KEY',
837 'LOT', 'PIER', 'REAR', 'SPC', 'SPACE',
838 'STOP', 'STE', 'SUITE', 'UNIT', '#',
841 // overwriting $streetUnitFormats for 'en_CA' and 'fr_CA' locale
842 if (in_array($locale, array(
843 'en_CA', 'fr_CA'))) {
844 $streetUnitFormats = array('APT', 'APP', 'SUITE', 'BUREAU', 'UNIT');
847 $streetUnitPreg = '/(' . implode('|\s', $streetUnitFormats) . ')(.+)?/i';
849 if (preg_match($streetUnitPreg, $streetAddress, $matches)) {
850 $parseFields['street_unit'] = trim($matches[0]);
851 $streetAddress = str_replace($matches[0], '', $streetAddress);
852 $streetAddress = trim($streetAddress);
855 // consider remaining string as street name.
856 $parseFields['street_name'] = $streetAddress;
858 //run parsed fields through stripSpaces to clean
859 foreach ($parseFields as $parseField => $value) {
860 $parseFields[$parseField] = CRM_Utils_String
::stripSpaces($value);
867 * Validate the address fields based on the address options enabled
868 * in the Address Settings
870 * @param array $fields an array of importable/exportable contact fields
872 * @return array $fields an array of contact fields and only the enabled address options
876 static function validateAddressOptions($fields) {
877 static $addressOptions = NULL;
878 if (!$addressOptions) {
880 CRM_Core_BAO_Setting
::valueOptions(
881 CRM_Core_BAO_Setting
::SYSTEM_PREFERENCES_NAME
,
886 if (is_array($fields) && !empty($fields)) {
887 foreach ($addressOptions as $key => $value) {
888 if (!$value && isset($fields[$key])) {
889 unset($fields[$key]);
897 * Check if current address is used by any other contacts
899 * @param int $addressId address id
901 * @return count of contacts that use this shared address
905 static function checkContactSharedAddress($addressId) {
906 $query = 'SELECT count(id) FROM civicrm_address WHERE master_id = %1';
907 return CRM_Core_DAO
::singleValueQuery($query, array(1 => array($addressId, 'Integer')));
911 * Function to check if current address fields are shared with any other address
913 * @param array $fields address fields in profile
914 * @param int $contactId contact id
919 static function checkContactSharedAddressFields(&$fields, $contactId) {
920 if (!$contactId ||
!is_array($fields) ||
empty($fields)) {
924 $sharedLocations = array();
930 WHERE contact_id = %1
931 AND master_id IS NOT NULL";
933 $dao = CRM_Core_DAO
::executeQuery($query, array(1 => array($contactId, 'Positive')));
934 while ($dao->fetch()) {
935 $sharedLocations[$dao->location_type_id
] = $dao->location_type_id
;
936 if ($dao->is_primary
) {
937 $sharedLocations['Primary'] = 'Primary';
941 //no need to process further.
942 if (empty($sharedLocations)) {
946 $addressFields = array(
956 'postal_code_suffix',
957 'supplemental_address_1',
958 'supplemental_address_2',
961 foreach ($fields as $name => & $values) {
962 if (!is_array($values) ||
empty($values)) {
966 $nameVal = explode('-', $values['name']);
967 $fldName = CRM_Utils_Array
::value(0, $nameVal);
968 $locType = CRM_Utils_Array
::value(1, $nameVal);
969 if (CRM_Utils_Array
::value('location_type_id', $values)) {
970 $locType = $values['location_type_id'];
973 if (in_array($fldName, $addressFields) &&
974 in_array($locType, $sharedLocations)
976 $values['is_shared'] = TRUE;
982 * Function to update the shared addresses if master address is modified
984 * @param int $addressId address id
985 * @param array $params associated array of address params
991 static function processSharedAddress($addressId, $params) {
992 $query = 'SELECT id FROM civicrm_address WHERE master_id = %1';
993 $dao = CRM_Core_DAO
::executeQuery($query, array(1 => array($addressId, 'Integer')));
996 $skipFields = array('is_primary', 'location_type_id', 'is_billing', 'master_id', 'contact_id');
997 foreach ($skipFields as $value) {
998 unset($params[$value]);
1001 $addressDAO = new CRM_Core_DAO_Address();
1002 while ($dao->fetch()) {
1003 $addressDAO->copyValues($params);
1004 $addressDAO->id
= $dao->id
;
1005 $addressDAO->save();
1006 $addressDAO->free();
1011 * Function to create relationship between contacts who share an address
1013 * Note that currently we create relationship only for Individual contacts
1014 * Individual + Household and Individual + Orgnization
1016 * @param int $masterAddressId master address id
1017 * @param array $params associated array of submitted values
1023 static function processSharedAddressRelationship($masterAddressId, $params) {
1024 if (!$masterAddressId) {
1027 // get the contact type of contact being edited / created
1028 $currentContactType = CRM_Contact_BAO_Contact
::getContactType($params['contact_id']);
1029 $currentContactId = $params['contact_id'];
1031 // if current contact is not of type individual return
1032 if ($currentContactType != 'Individual') {
1036 // get the contact id and contact type of shared contact
1037 // check the contact type of shared contact, return if it is of type Individual
1039 $query = 'SELECT cc.id, cc.contact_type
1040 FROM civicrm_contact cc INNER JOIN civicrm_address ca ON cc.id = ca.contact_id
1043 $dao = CRM_Core_DAO
::executeQuery($query, array(1 => array($masterAddressId, 'Integer')));
1047 // if current contact is not of type individual return, since we don't create relationship between
1049 if ($dao->contact_type
== 'Individual') {
1052 $sharedContactType = $dao->contact_type
;
1053 $sharedContactId = $dao->id
;
1055 // create relationship between ontacts who share an address
1056 if ($sharedContactType == 'Organization') {
1057 return CRM_Contact_BAO_Contact_Utils
::createCurrentEmployerRelationship($currentContactId, $sharedContactId);
1060 // get the relationship type id of "Household Member of"
1061 $relationshipType = 'Household Member of';
1064 $cid = array('contact' => $currentContactId);
1066 $relTypeId = CRM_Core_DAO
::getFieldValue('CRM_Contact_DAO_RelationshipType', $relationshipType, 'id', 'name_a_b');
1069 CRM_Core_Error
::fatal(ts("You seem to have deleted the relationship type '%1'", array(1 => $relationshipType)));
1072 // create relationship
1073 $relationshipParams = array(
1074 'is_active' => TRUE,
1075 'relationship_type_id' => $relTypeId . '_a_b',
1076 'contact_check' => array($sharedContactId => TRUE),
1079 list($valid, $invalid, $duplicate,
1080 $saved, $relationshipIds
1081 ) = CRM_Contact_BAO_Relationship
::create($relationshipParams, $cid);
1085 * Function to check and set the status for shared address delete
1087 * @param int $addressId address id
1088 * @param int $contactId contact id
1089 * @param boolean $returnStatus by default false
1091 * @return string $statusMessage
1095 static function setSharedAddressDeleteStatus($addressId = NULL, $contactId = NULL, $returnStatus = FALSE) {
1096 // check if address that is being deleted has any shared
1098 $entityId = $addressId;
1099 $query = 'SELECT cc.id, cc.display_name
1100 FROM civicrm_contact cc INNER JOIN civicrm_address ca ON cc.id = ca.contact_id
1101 WHERE ca.master_id = %1';
1104 $entityId = $contactId;
1105 $query = 'SELECT cc.id, cc.display_name
1106 FROM civicrm_address ca1
1107 INNER JOIN civicrm_address ca2 ON ca1.id = ca2.master_id
1108 INNER JOIN civicrm_contact cc ON ca2.contact_id = cc.id
1109 WHERE ca1.contact_id = %1';
1112 $dao = CRM_Core_DAO
::executeQuery($query, array(1 => array($entityId, 'Integer')));
1114 $deleteStatus = array();
1115 $sharedContactList = array();
1116 $statusMessage = NULL;
1118 while ($dao->fetch()) {
1119 if (empty($deleteStatus)) {
1120 $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.');
1123 $contactViewUrl = CRM_Utils_System
::url('civicrm/contact/view', "reset=1&cid={$dao->id}");
1124 $sharedContactList[] = "<a href='{$contactViewUrl}'>{$dao->display_name}</a>";
1125 $deleteStatus[] = "<a href='{$contactViewUrl}'>{$dao->display_name}</a>";
1130 if (!empty($deleteStatus)) {
1131 $statusMessage = implode('<br/>', $deleteStatus) . '<br/>';
1134 if (!$returnStatus) {
1135 CRM_Core_Session
::setStatus($statusMessage, '', 'info');
1139 'contactList' => $sharedContactList,
1140 'count' => $addressCount,
1146 * Call common delete function
1148 static function del($id) {
1149 return CRM_Contact_BAO_Contact
::deleteObjectWithPrimary('Address', $id);