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
50 * @return array $blocks array of created address
54 static function create(&$params, $fixAddress = TRUE, $entity = NULL) {
55 if (!isset($params['address']) ||
!is_array($params['address'])) {
58 CRM_Core_BAO_Block
::sortPrimaryFirst($params['address']);
62 $updateBlankLocInfo = CRM_Utils_Array
::value('updateBlankLocInfo', $params, FALSE);
64 $contactId = $params['contact_id'];
65 //get all the addresses for this contact
66 $addresses = self
::allAddress($contactId, $updateBlankLocInfo);
69 // get all address from location block
70 $entityElements = array(
71 'entity_table' => $params['entity_table'],
72 'entity_id' => $params['entity_id'],
74 $addresses = self
::allEntityAddress($entityElements);
77 $isPrimary = $isBilling = TRUE;
79 foreach ($params['address'] as $key => $value) {
80 if (!is_array($value)) {
84 $addressExists = self
::dataExists($value);
85 if (empty($value['id'])) {
86 if ($updateBlankLocInfo) {
87 if ((!empty($addresses) ||
!$addressExists) && array_key_exists($key, $addresses)) {
88 $value['id'] = $addresses[$key];
92 if (!empty($addresses) && array_key_exists(CRM_Utils_Array
::value('location_type_id', $value), $addresses)) {
93 $value['id'] = $addresses[CRM_Utils_Array
::value('location_type_id', $value)];
98 // Note there could be cases when address info already exist ($value[id] is set) for a contact/entity
99 // BUT info is not present at this time, and therefore we should be really careful when deleting the block.
100 // $updateBlankLocInfo will help take appropriate decision. CRM-5969
101 if (isset($value['id']) && !$addressExists && $updateBlankLocInfo) {
102 //delete the existing record
103 CRM_Core_BAO_Block
::blockDelete('Address', array('id' => $value['id']));
106 elseif (!$addressExists) {
110 if ($isPrimary && !empty($value['is_primary'])) {
114 $value['is_primary'] = 0;
117 if ($isBilling && !empty($value['is_billing'])) {
121 $value['is_billing'] = 0;
124 if (empty($value['manual_geo_code'])) {
125 $value['manual_geo_code'] = 0;
127 $value['contact_id'] = $contactId;
128 $blocks[] = self
::add($value, $fixAddress);
135 * takes an associative array and adds address
137 * @param array $params (reference ) an assoc array of name/value pairs
138 * @param boolean $fixAddress true if you need to fix (format) address values
139 * before inserting in db
141 * @return object CRM_Core_BAO_Address object on success, null otherwise
145 static function add(&$params, $fixAddress) {
146 static $customFields = NULL;
147 $address = new CRM_Core_DAO_Address();
149 // fixAddress mode to be done
151 CRM_Core_BAO_Address
::fixAddress($params);
154 $hook = empty($params['id']) ?
'create' : 'edit';
155 CRM_Utils_Hook
::pre($hook, 'Address', CRM_Utils_Array
::value('id', $params), $params);
157 // if id is set & is_primary isn't we can assume no change
158 if (is_numeric(CRM_Utils_Array
::value('is_primary', $params)) ||
empty($params['id'])) {
159 CRM_Core_BAO_Block
::handlePrimary($params, get_class());
161 $config = CRM_Core_Config
::singleton();
162 $address->copyValues($params);
167 if (!$customFields) {
168 $customFields = CRM_Core_BAO_CustomField
::getFields('Address', FALSE, TRUE);
170 if (!empty($customFields)) {
171 $addressCustom = CRM_Core_BAO_CustomField
::postProcess($params,
178 if (!empty($addressCustom)) {
179 CRM_Core_BAO_CustomValueTable
::store($addressCustom, 'civicrm_address', $address->id
);
182 //call the function to sync shared address
183 self
::processSharedAddress($address->id
, $params);
185 // call the function to create shared relationships
186 // we only create create relationship if address is shared by Individual
187 if ($address->master_id
!= 'null') {
188 self
::processSharedAddressRelationship($address->master_id
, $params);
191 // lets call the post hook only after we've done all the follow on processing
192 CRM_Utils_Hook
::post($hook, 'Address', $address->id
, $address);
199 * format the address params to have reasonable values
201 * @param array $params (reference ) an assoc array of name/value pairs
207 static function fixAddress(&$params) {
208 if (!empty($params['billing_street_address'])) {
209 //Check address is comming from online contribution / registration page
212 'street_address' => 'billing_street_address',
213 'city' => 'billing_city',
214 'postal_code' => 'billing_postal_code',
215 'state_province' => 'billing_state_province',
216 'state_province_id' => 'billing_state_province_id',
217 'country' => 'billing_country',
218 'country_id' => 'billing_country_id',
221 foreach ($billing as $key => $val) {
222 if ($value = CRM_Utils_Array
::value($val, $params)) {
223 if (!empty($params[$key])) {
224 unset($params[$val]);
227 //add new key and removed old
228 $params[$key] = $value;
229 unset($params[$val]);
235 /* Split the zip and +4, if it's in US format */
236 if (!empty($params['postal_code']) &&
237 preg_match('/^(\d{4,5})[+-](\d{4})$/',
238 $params['postal_code'],
242 $params['postal_code'] = $match[1];
243 $params['postal_code_suffix'] = $match[2];
246 // add country id if not set
247 if ((!isset($params['country_id']) ||
!is_numeric($params['country_id'])) &&
248 isset($params['country'])
250 $country = new CRM_Core_DAO_Country();
251 $country->name
= $params['country'];
252 if (!$country->find(TRUE)) {
253 $country->name
= NULL;
254 $country->iso_code
= $params['country'];
255 $country->find(TRUE);
257 $params['country_id'] = $country->id
;
260 // add state_id if state is set
261 if ((!isset($params['state_province_id']) ||
!is_numeric($params['state_province_id']))
262 && isset($params['state_province'])
264 if (!empty($params['state_province'])) {
265 $state_province = new CRM_Core_DAO_StateProvince();
266 $state_province->name
= $params['state_province'];
268 // add country id if present
269 if (!empty($params['country_id'])) {
270 $state_province->country_id
= $params['country_id'];
273 if (!$state_province->find(TRUE)) {
274 unset($state_province->name
);
275 $state_province->abbreviation
= $params['state_province'];
276 $state_province->find(TRUE);
278 $params['state_province_id'] = $state_province->id
;
279 if (empty($params['country_id'])) {
280 // set this here since we have it
281 $params['country_id'] = $state_province->country_id
;
285 $params['state_province_id'] = 'null';
289 // add county id if county is set
291 if ((!isset($params['county_id']) ||
!is_numeric($params['county_id']))
292 && isset($params['county']) && !empty($params['county'])
294 $county = new CRM_Core_DAO_County();
295 $county->name
= $params['county'];
297 if (isset($params['state_province_id'])) {
298 $county->state_province_id
= $params['state_province_id'];
301 if ($county->find(TRUE)) {
302 $params['county_id'] = $county->id
;
306 // currently copy values populates empty fields with the string "null"
307 // and hence need to check for the string null
308 if (isset($params['state_province_id']) &&
309 is_numeric($params['state_province_id']) &&
310 (!isset($params['country_id']) ||
empty($params['country_id']))
312 // since state id present and country id not present, hence lets populate it
313 // jira issue http://issues.civicrm.org/jira/browse/CRM-56
314 $params['country_id'] = CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_StateProvince',
315 $params['state_province_id'],
320 //special check to ignore non numeric values if they are not
321 //detected by formRule(sometimes happens due to internet latency), also allow user to unselect state/country
322 if (isset($params['state_province_id'])) {
323 if (empty($params['state_province_id'])) {
324 $params['state_province_id'] = 'null';
326 elseif (!is_numeric($params['state_province_id']) ||
327 ((int ) $params['state_province_id'] < 1000)
329 // CRM-3393 ( the hacky 1000 check)
330 $params['state_province_id'] = 'null';
334 if (isset($params['country_id'])) {
335 if (empty($params['country_id'])) {
336 $params['country_id'] = 'null';
338 elseif (!is_numeric($params['country_id']) ||
339 ((int ) $params['country_id'] < 1000)
341 // CRM-3393 ( the hacky 1000 check)
342 $params['country_id'] = 'null';
346 // add state and country names from the ids
347 if (isset($params['state_province_id']) && is_numeric($params['state_province_id'])) {
348 $params['state_province'] = CRM_Core_PseudoConstant
::stateProvinceAbbreviation($params['state_province_id']);
351 if (isset($params['country_id']) && is_numeric($params['country_id'])) {
352 $params['country'] = CRM_Core_PseudoConstant
::country($params['country_id']);
355 $config = CRM_Core_Config
::singleton();
357 $asp = CRM_Core_BAO_Setting
::getItem(CRM_Core_BAO_Setting
::ADDRESS_STANDARDIZATION_PREFERENCES_NAME
,
358 'address_standardization_provider'
360 // clean up the address via USPS web services if enabled
361 if ($asp === 'USPS' &&
362 $params['country_id'] == 1228
364 CRM_Utils_Address_USPS
::checkAddress($params);
366 // do street parsing again if enabled, since street address might have changed
367 $parseStreetAddress =
368 CRM_Utils_Array
::value(
369 'street_address_parsing',
370 CRM_Core_BAO_Setting
::valueOptions(
371 CRM_Core_BAO_Setting
::SYSTEM_PREFERENCES_NAME
,
377 if ($parseStreetAddress && !empty($params['street_address'])) {
379 'street_number', 'street_name', 'street_unit', 'street_number_suffix') as $fld) {
380 unset($params[$fld]);
382 // main parse string.
383 $parseString = CRM_Utils_Array
::value('street_address', $params);
384 $parsedFields = CRM_Core_BAO_Address
::parseStreetAddress($parseString);
386 // merge parse address in to main address block.
387 $params = array_merge($params, $parsedFields);
391 // add latitude and longitude and format address if needed
392 if (!empty($config->geocodeMethod
) && ($config->geocodeMethod
!= 'CRM_Utils_Geocode_OpenStreetMaps') && empty($params['manual_geo_code'])) {
393 $class = $config->geocodeMethod
;
394 $class::format($params);
399 * Check if there is data to create the object
401 * @param array $params (reference ) an assoc array of name/value pairs
408 static function dataExists(&$params) {
409 //check if location type is set if not return false
410 if (!isset($params['location_type_id'])) {
414 $config = CRM_Core_Config
::singleton();
415 foreach ($params as $name => $value) {
416 if (in_array($name, array(
417 'is_primary', 'location_type_id', 'id', 'contact_id', 'is_billing', 'display', 'master_id'))) {
420 elseif (!CRM_Utils_System
::isNull($value)) {
421 // name could be country or country id
422 if (substr($name, 0, 7) == 'country') {
423 // make sure its different from the default country
425 $defaultCountry = $config->defaultContactCountry();
427 $defaultCountryName = $config->defaultContactCountryName();
429 if ($defaultCountry) {
430 if ($value == $defaultCountry ||
431 $value == $defaultCountryName ||
432 $value == $config->defaultContactCountry
441 // return if null default
455 * Given the list of params in the params array, fetch the object
456 * and store the values in the values array
458 * @param array $entityBlock associated array of fields
459 * @param boolean $microformat if microformat output is required
460 * @param int|string $fieldName conditional field name
462 * @return array $addresses array with address fields
466 static function &getValues(&$entityBlock, $microformat = FALSE, $fieldName = 'contact_id') {
467 if (empty($entityBlock)) {
470 $addresses = array();
471 $address = new CRM_Core_BAO_Address();
473 if (empty($entityBlock['entity_table'])) {
474 $address->$fieldName = CRM_Utils_Array
::value($fieldName, $entityBlock);
477 $addressIds = array();
478 $addressIds = self
::allEntityAddress($entityBlock);
480 if (!empty($addressIds[1])) {
481 $address->id
= $addressIds[1];
487 //get primary address as a first block.
488 $address->orderBy('is_primary desc, id');
493 while ($address->fetch()) {
494 // deprecate reference.
497 'state', 'state_name', 'country', 'world_region') as $fld) {
498 if (isset($address->$fld))unset($address->$fld);
501 $stree = $address->street_address
;
503 CRM_Core_DAO
::storeValues($address, $values);
505 // add state and country information: CRM-369
506 if (!empty($address->state_province_id
)) {
507 $address->state
= CRM_Core_PseudoConstant
::stateProvinceAbbreviation($address->state_province_id
, FALSE);
508 $address->state_name
= CRM_Core_PseudoConstant
::stateProvince($address->state_province_id
, FALSE);
511 if (!empty($address->country_id
)) {
512 $address->country
= CRM_Core_PseudoConstant
::country($address->country_id
);
515 $regionId = CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_Country', $address->country_id
, 'region_id');
517 $address->world_region
= CRM_Core_PseudoConstant
::worldregion($regionId);
520 $address->addDisplay($microformat);
522 $values['display'] = $address->display
;
523 $values['display_text'] = $address->display_text
;
525 if (is_numeric($address->master_id
)) {
526 $values['use_shared_address'] = 1;
529 $addresses[$count] = $values;
531 //unset is_primary after first block. Due to some bug in earlier version
532 //there might be more than one primary blocks, hence unset is_primary other than first
534 unset($addresses[$count]['is_primary']);
544 * Add the formatted address to $this-> display
546 * @param bool $microformat
548 * @internal param $NULL
554 function addDisplay($microformat = FALSE) {
556 // added this for CRM 1200
557 'address_id' => $this->id
,
559 'address_name' => str_replace('\ 1', ' ', $this->name
),
560 'street_address' => $this->street_address
,
561 'supplemental_address_1' => $this->supplemental_address_1
,
562 'supplemental_address_2' => $this->supplemental_address_2
,
563 'city' => $this->city
,
564 'state_province_name' => isset($this->state_name
) ?
$this->state_name
: "",
565 'state_province' => isset($this->state
) ?
$this->state
: "",
566 'postal_code' => isset($this->postal_code
) ?
$this->postal_code
: "",
567 'postal_code_suffix' => isset($this->postal_code_suffix
) ?
$this->postal_code_suffix
: "",
568 'country' => isset($this->country
) ?
$this->country
: "",
569 'world_region' => isset($this->world_region
) ?
$this->world_region
: "",
572 if (isset($this->county_id
) && $this->county_id
) {
573 $fields['county'] = CRM_Core_PseudoConstant
::county($this->county_id
);
576 $fields['county'] = NULL;
579 $this->display
= CRM_Utils_Address
::format($fields, NULL, $microformat);
580 $this->display_text
= CRM_Utils_Address
::format($fields);
584 * Get all the addresses for a specified contact_id, with the primary address being first
586 * @param int $id the contact id
588 * @param bool $updateBlankLocInfo
590 * @return array the array of adrress data
594 static function allAddress($id, $updateBlankLocInfo = FALSE) {
600 SELECT civicrm_address.id as address_id, civicrm_address.location_type_id as location_type_id
601 FROM civicrm_contact, civicrm_address
602 WHERE civicrm_address.contact_id = civicrm_contact.id AND civicrm_contact.id = %1
603 ORDER BY civicrm_address.is_primary DESC, address_id ASC";
604 $params = array(1 => array($id, 'Integer'));
606 $addresses = array();
607 $dao = CRM_Core_DAO
::executeQuery($query, $params);
609 while ($dao->fetch()) {
610 if ($updateBlankLocInfo) {
611 $addresses[$count++
] = $dao->address_id
;
614 $addresses[$dao->location_type_id
] = $dao->address_id
;
621 * Get all the addresses for a specified location_block id, with the primary address being first
623 * @param array $entityElements the array containing entity_id and
626 * @return array the array of adrress data
630 static function allEntityAddress(&$entityElements) {
631 $addresses = array();
632 if (empty($entityElements)) {
636 $entityId = $entityElements['entity_id'];
637 $entityTable = $entityElements['entity_table'];
640 SELECT civicrm_address.id as address_id
641 FROM civicrm_loc_block loc, civicrm_location_type ltype, civicrm_address, {$entityTable} ev
643 AND loc.id = ev.loc_block_id
644 AND civicrm_address.id IN (loc.address_id, loc.address_2_id)
645 AND ltype.id = civicrm_address.location_type_id
646 ORDER BY civicrm_address.is_primary DESC, civicrm_address.location_type_id DESC, address_id ASC ";
648 $params = array(1 => array($entityId, 'Integer'));
649 $dao = CRM_Core_DAO
::executeQuery($sql, $params);
651 while ($dao->fetch()) {
652 $addresses[$locationCount] = $dao->address_id
;
659 * Function to get address sequence
661 * @return array of address sequence.
663 static function addressSequence() {
664 $config = CRM_Core_Config
::singleton();
665 $addressSequence = $config->addressSequence();
667 $countryState = $cityPostal = FALSE;
668 foreach ($addressSequence as $key => $field) {
670 in_array($field, array('country', 'state_province')) &&
673 $countryState = TRUE;
674 $addressSequence[$key] = 'country_state_province';
677 in_array($field, array('city', 'postal_code')) &&
681 $addressSequence[$key] = 'city_postal_code';
684 in_array($field, array('country', 'state_province', 'city', 'postal_code'))
686 unset($addressSequence[$key]);
690 return $addressSequence;
694 * Parse given street address string in to street_name,
695 * street_unit, street_number and street_number_suffix
696 * eg "54A Excelsior Ave. Apt 1C", or "917 1/2 Elm Street"
698 * NB: civic street formats for en_CA and fr_CA used by default if those locales are active
699 * otherwise en_US format is default action
701 * @param string Street address including number and apt
702 * @param string Locale - to set locale used to parse address
704 * @return array $parseFields parsed fields values.
708 static function parseStreetAddress($streetAddress, $locale = NULL) {
709 $config = CRM_Core_Config
::singleton();
711 /* locales supported include:
712 * en_US - http://pe.usps.com/cpim/ftp/pubs/pub28/pub28.pdf
713 * en_CA - http://www.canadapost.ca/tools/pg/manual/PGaddress-e.asp
714 * fr_CA - http://www.canadapost.ca/tools/pg/manual/PGaddress-f.asp
715 * NB: common use of comma after street number also supported
719 $supportedLocalesForParsing = array('en_US', 'en_CA', 'fr_CA');
721 $locale = $config->lcMessages
;
723 // as different locale explicitly requested but is not available, display warning message and set $locale = 'en_US'
724 if (!in_array($locale, $supportedLocalesForParsing)) {
725 CRM_Core_Session
::setStatus(ts('Unsupported locale specified to parseStreetAddress: %1. Proceeding with en_US locale.', array(1 => $locale)), ts('Unsupported Locale'), 'alert');
728 $emptyParseFields = $parseFields = array(
731 'street_number' => '',
732 'street_number_suffix' => '',
735 if (empty($streetAddress)) {
739 $streetAddress = trim($streetAddress);
742 if (in_array($locale, array(
743 'en_CA', 'fr_CA')) && preg_match('/^([A-Za-z0-9]+)[ ]*\-[ ]*/', $streetAddress, $matches)) {
744 $parseFields['street_unit'] = $matches[1];
745 // unset from rest of street address
746 $streetAddress = preg_replace('/^([A-Za-z0-9]+)[ ]*\-[ ]*/', '', $streetAddress);
749 // get street number and suffix.
751 //alter street number/suffix handling so that we accept -digit
752 if (preg_match('/^[A-Za-z0-9]+([\S]+)/', $streetAddress, $matches)) {
753 // check that $matches[0] is numeric, else assume no street number
754 if (preg_match('/^(\d+)/', $matches[0])) {
755 $streetNumAndSuffix = $matches[0];
757 // get street number.
759 if (preg_match('/^(\d+)/', $streetNumAndSuffix, $matches)) {
760 $parseFields['street_number'] = $matches[0];
761 $suffix = preg_replace('/^(\d+)/', '', $streetNumAndSuffix);
762 $parseFields['street_number_suffix'] = trim($suffix);
765 // unset from main street address.
766 $streetAddress = preg_replace('/^[A-Za-z0-9]+([\S]+)/', '', $streetAddress);
767 $streetAddress = trim($streetAddress);
770 elseif (preg_match('/^(\d+)/', $streetAddress, $matches)) {
771 $parseFields['street_number'] = $matches[0];
772 // unset from main street address.
773 $streetAddress = preg_replace('/^(\d+)/', '', $streetAddress);
774 $streetAddress = trim($streetAddress);
777 // suffix might be like 1/2
779 if (preg_match('/^\d\/\d/', $streetAddress, $matches)) {
780 $parseFields['street_number_suffix'] .= $matches[0];
782 // unset from main street address.
783 $streetAddress = preg_replace('/^\d+\/\d+/', '', $streetAddress);
784 $streetAddress = trim($streetAddress);
787 // now get the street unit.
788 // supportable street unit formats.
789 $streetUnitFormats = array(
790 'APT', 'APARTMENT', 'BSMT', 'BASEMENT', 'BLDG', 'BUILDING',
791 'DEPT', 'DEPARTMENT', 'FL', 'FLOOR', 'FRNT', 'FRONT',
792 'HNGR', 'HANGER', 'LBBY', 'LOBBY', 'LOWR', 'LOWER',
793 'OFC', 'OFFICE', 'PH', 'PENTHOUSE', 'TRLR', 'TRAILER',
794 'UPPR', 'RM', 'ROOM', 'SIDE', 'SLIP', 'KEY',
795 'LOT', 'PIER', 'REAR', 'SPC', 'SPACE',
796 'STOP', 'STE', 'SUITE', 'UNIT', '#',
799 // overwriting $streetUnitFormats for 'en_CA' and 'fr_CA' locale
800 if (in_array($locale, array(
801 'en_CA', 'fr_CA'))) {
802 $streetUnitFormats = array('APT', 'APP', 'SUITE', 'BUREAU', 'UNIT');
804 //@todo per CRM-14459 this regex picks up words with the string in them - e.g APT picks up
805 //Captain - presuming fixing regex (& adding test) to ensure a-z does not preced string will fix
806 $streetUnitPreg = '/(' . implode('|\s', $streetUnitFormats) . ')(.+)?/i';
808 if (preg_match($streetUnitPreg, $streetAddress, $matches)) {
809 $parseFields['street_unit'] = trim($matches[0]);
810 $streetAddress = str_replace($matches[0], '', $streetAddress);
811 $streetAddress = trim($streetAddress);
814 // consider remaining string as street name.
815 $parseFields['street_name'] = $streetAddress;
817 //run parsed fields through stripSpaces to clean
818 foreach ($parseFields as $parseField => $value) {
819 $parseFields[$parseField] = CRM_Utils_String
::stripSpaces($value);
821 //CRM-14459 if the field is too long we should assume it didn't get it right & skip rather than allow
823 $fields = CRM_Core_BAO_Address
::fields();
824 foreach ($fields as $fieldname => $field) {
825 if(!empty($field['maxlength']) && strlen(CRM_Utils_Array
::value($fieldname, $parseFields)) > $field['maxlength']) {
826 return $emptyParseFields;
834 * Validate the address fields based on the address options enabled
835 * in the Address Settings
837 * @param array $fields an array of importable/exportable contact fields
839 * @return array $fields an array of contact fields and only the enabled address options
843 static function validateAddressOptions($fields) {
844 static $addressOptions = NULL;
845 if (!$addressOptions) {
847 CRM_Core_BAO_Setting
::valueOptions(
848 CRM_Core_BAO_Setting
::SYSTEM_PREFERENCES_NAME
,
853 if (is_array($fields) && !empty($fields)) {
854 foreach ($addressOptions as $key => $value) {
855 if (!$value && isset($fields[$key])) {
856 unset($fields[$key]);
864 * Check if current address is used by any other contacts
866 * @param int $addressId address id
868 * @return count of contacts that use this shared address
872 static function checkContactSharedAddress($addressId) {
873 $query = 'SELECT count(id) FROM civicrm_address WHERE master_id = %1';
874 return CRM_Core_DAO
::singleValueQuery($query, array(1 => array($addressId, 'Integer')));
878 * Function to check if current address fields are shared with any other address
880 * @param array $fields address fields in profile
881 * @param int $contactId contact id
886 static function checkContactSharedAddressFields(&$fields, $contactId) {
887 if (!$contactId ||
!is_array($fields) ||
empty($fields)) {
891 $sharedLocations = array();
897 WHERE contact_id = %1
898 AND master_id IS NOT NULL";
900 $dao = CRM_Core_DAO
::executeQuery($query, array(1 => array($contactId, 'Positive')));
901 while ($dao->fetch()) {
902 $sharedLocations[$dao->location_type_id
] = $dao->location_type_id
;
903 if ($dao->is_primary
) {
904 $sharedLocations['Primary'] = 'Primary';
908 //no need to process further.
909 if (empty($sharedLocations)) {
913 $addressFields = array(
923 'postal_code_suffix',
924 'supplemental_address_1',
925 'supplemental_address_2',
928 foreach ($fields as $name => & $values) {
929 if (!is_array($values) ||
empty($values)) {
933 $nameVal = explode('-', $values['name']);
934 $fldName = CRM_Utils_Array
::value(0, $nameVal);
935 $locType = CRM_Utils_Array
::value(1, $nameVal);
936 if (!empty($values['location_type_id'])) {
937 $locType = $values['location_type_id'];
940 if (in_array($fldName, $addressFields) &&
941 in_array($locType, $sharedLocations)
943 $values['is_shared'] = TRUE;
949 * Function to update the shared addresses if master address is modified
951 * @param int $addressId address id
952 * @param array $params associated array of address params
958 static function processSharedAddress($addressId, $params) {
959 $query = 'SELECT id FROM civicrm_address WHERE master_id = %1';
960 $dao = CRM_Core_DAO
::executeQuery($query, array(1 => array($addressId, 'Integer')));
963 $skipFields = array('is_primary', 'location_type_id', 'is_billing', 'master_id', 'contact_id');
964 foreach ($skipFields as $value) {
965 unset($params[$value]);
968 $addressDAO = new CRM_Core_DAO_Address();
969 while ($dao->fetch()) {
970 $addressDAO->copyValues($params);
971 $addressDAO->id
= $dao->id
;
978 * Function to create relationship between contacts who share an address
980 * Note that currently we create relationship only for Individual contacts
981 * Individual + Household and Individual + Orgnization
983 * @param int $masterAddressId master address id
984 * @param array $params associated array of submitted values
990 static function processSharedAddressRelationship($masterAddressId, $params) {
991 if (!$masterAddressId) {
994 // get the contact type of contact being edited / created
995 $currentContactType = CRM_Contact_BAO_Contact
::getContactType($params['contact_id']);
996 $currentContactId = $params['contact_id'];
998 // if current contact is not of type individual return
999 if ($currentContactType != 'Individual') {
1003 // get the contact id and contact type of shared contact
1004 // check the contact type of shared contact, return if it is of type Individual
1006 $query = 'SELECT cc.id, cc.contact_type
1007 FROM civicrm_contact cc INNER JOIN civicrm_address ca ON cc.id = ca.contact_id
1010 $dao = CRM_Core_DAO
::executeQuery($query, array(1 => array($masterAddressId, 'Integer')));
1014 // if current contact is not of type individual return, since we don't create relationship between
1016 if ($dao->contact_type
== 'Individual') {
1019 $sharedContactType = $dao->contact_type
;
1020 $sharedContactId = $dao->id
;
1022 // create relationship between ontacts who share an address
1023 if ($sharedContactType == 'Organization') {
1024 return CRM_Contact_BAO_Contact_Utils
::createCurrentEmployerRelationship($currentContactId, $sharedContactId);
1027 // get the relationship type id of "Household Member of"
1028 $relationshipType = 'Household Member of';
1031 $cid = array('contact' => $currentContactId);
1033 $relTypeId = CRM_Core_DAO
::getFieldValue('CRM_Contact_DAO_RelationshipType', $relationshipType, 'id', 'name_a_b');
1036 CRM_Core_Error
::fatal(ts("You seem to have deleted the relationship type '%1'", array(1 => $relationshipType)));
1039 // create relationship
1040 $relationshipParams = array(
1041 'is_active' => TRUE,
1042 'relationship_type_id' => $relTypeId . '_a_b',
1043 'contact_check' => array($sharedContactId => TRUE),
1046 list($valid, $invalid, $duplicate,
1047 $saved, $relationshipIds
1048 ) = CRM_Contact_BAO_Relationship
::create($relationshipParams, $cid);
1052 * Function to check and set the status for shared address delete
1054 * @param int $addressId address id
1055 * @param int $contactId contact id
1056 * @param boolean $returnStatus by default false
1058 * @return string $statusMessage
1062 static function setSharedAddressDeleteStatus($addressId = NULL, $contactId = NULL, $returnStatus = FALSE) {
1063 // check if address that is being deleted has any shared
1065 $entityId = $addressId;
1066 $query = 'SELECT cc.id, cc.display_name
1067 FROM civicrm_contact cc INNER JOIN civicrm_address ca ON cc.id = ca.contact_id
1068 WHERE ca.master_id = %1';
1071 $entityId = $contactId;
1072 $query = 'SELECT cc.id, cc.display_name
1073 FROM civicrm_address ca1
1074 INNER JOIN civicrm_address ca2 ON ca1.id = ca2.master_id
1075 INNER JOIN civicrm_contact cc ON ca2.contact_id = cc.id
1076 WHERE ca1.contact_id = %1';
1079 $dao = CRM_Core_DAO
::executeQuery($query, array(1 => array($entityId, 'Integer')));
1081 $deleteStatus = array();
1082 $sharedContactList = array();
1083 $statusMessage = NULL;
1085 while ($dao->fetch()) {
1086 if (empty($deleteStatus)) {
1087 $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.');
1090 $contactViewUrl = CRM_Utils_System
::url('civicrm/contact/view', "reset=1&cid={$dao->id}");
1091 $sharedContactList[] = "<a href='{$contactViewUrl}'>{$dao->display_name}</a>";
1092 $deleteStatus[] = "<a href='{$contactViewUrl}'>{$dao->display_name}</a>";
1097 if (!empty($deleteStatus)) {
1098 $statusMessage = implode('<br/>', $deleteStatus) . '<br/>';
1101 if (!$returnStatus) {
1102 CRM_Core_Session
::setStatus($statusMessage, '', 'info');
1106 'contactList' => $sharedContactList,
1107 'count' => $addressCount,
1113 * Call common delete function
1115 static function del($id) {
1116 return CRM_Contact_BAO_Contact
::deleteObjectWithPrimary('Address', $id);
1120 * Get options for a given address field.
1121 * @see CRM_Core_DAO::buildOptions
1123 * TODO: Should we always assume chainselect? What fn should be responsible for controlling that flow?
1124 * TODO: In context of chainselect, what to return if e.g. a country has no states?
1126 * @param String $fieldName
1127 * @param String $context : @see CRM_Core_DAO::buildOptionsContext
1128 * @param Array $props : whatever is known about this dao object
1130 * @return Array|bool
1132 public static function buildOptions($fieldName, $context = NULL, $props = array()) {
1134 // Special logic for fields whose options depend on context or properties
1135 switch ($fieldName) {
1136 // Filter state_province list based on chosen country or site defaults
1137 case 'state_province_id':
1138 if (empty($props['country_id'])) {
1139 $config = CRM_Core_Config
::singleton();
1140 if (!empty($config->provinceLimit
)) {
1141 $props['country_id'] = $config->provinceLimit
;
1144 $props['country_id'] = $config->defaultContactCountry
;
1147 if (!empty($props['country_id'])) {
1148 $params['condition'] = 'country_id IN (' . implode(',', (array) $props['country_id']) . ')';
1151 // Filter country list based on site defaults
1153 if ($context != 'get' && $context != 'validate') {
1154 $config = CRM_Core_Config
::singleton();
1155 if (!empty($config->countryLimit
) && is_array($config->countryLimit
)) {
1156 $params['condition'] = 'id IN (' . implode(',', $config->countryLimit
) . ')';
1160 // Filter county list based on chosen state
1162 if (!empty($props['state_province_id'])) {
1163 $params['condition'] = 'state_province_id IN (' . implode(',', (array) $props['state_province_id']) . ')';
1166 // Not a real field in this entity
1167 case 'world_region':
1168 return CRM_Core_PseudoConstant
::worldRegion();
1171 return CRM_Core_PseudoConstant
::get(__CLASS__
, $fieldName, $params, $context);