diff --git a/CRM/Contact/BAO/Contact.php b/CRM/Contact/BAO/Contact.php index 4dd1a8634379..3c10894a6a9a 100644 --- a/CRM/Contact/BAO/Contact.php +++ b/CRM/Contact/BAO/Contact.php @@ -782,17 +782,6 @@ protected static function contactTrash($contact): bool { */ public static function resolveDefaults(&$defaults, $reverse = FALSE) { - //lookup value of email/postal greeting, addressee, CRM-4575 - foreach (self::$_greetingTypes as $greeting) { - $filterCondition = [ - 'contact_type' => $defaults['contact_type'] ?? NULL, - 'greeting_type' => $greeting, - ]; - CRM_Utils_Array::lookupValue($defaults, $greeting, - CRM_Core_PseudoConstant::greeting($filterCondition), $reverse - ); - } - $blocks = ['address']; foreach ($blocks as $name) { if (!array_key_exists($name, $defaults) || !is_array($defaults[$name])) { diff --git a/CRM/Contact/Import/Parser/Contact.php b/CRM/Contact/Import/Parser/Contact.php index e8ca7903726d..dde23ed58a87 100644 --- a/CRM/Contact/Import/Parser/Contact.php +++ b/CRM/Contact/Import/Parser/Contact.php @@ -108,6 +108,12 @@ class CRM_Contact_Import_Parser_Contact extends CRM_Import_Parser { 'email', 'website', 'url', + 'email_greeting', + 'email_greeting_id', + 'postal_greeting', + 'postal_greeting_id', + 'addressee', + 'addressee_id', ]; /** @@ -288,10 +294,10 @@ public function import($onDuplicate, &$values) { } $params = $this->getMappedRow($values); - $formatted = array_intersect_key($params, array_fill_keys($this->metadataHandledFields, 1)); - foreach ($formatted as $key => $value) { - if (!is_array($value) && !strlen($value)) { - unset($formatted[$key]); + $formatted = []; + foreach ($params as $key => $value) { + if ($value !== '') { + $formatted[$key] = $value; } } @@ -972,66 +978,6 @@ public function isErrorInCoreData($params, &$errorMessage) { } break; - //check for any error in email/postal greeting, addressee, - //custom email/postal greeting, custom addressee, CRM-4575 - - case 'email_greeting': - $emailGreetingFilter = [ - 'contact_type' => $this->_contactType, - 'greeting_type' => 'email_greeting', - ]; - if (!self::in_value($value, CRM_Core_PseudoConstant::greeting($emailGreetingFilter))) { - $errors[] = ts('Email Greeting must be one of the configured format options. Check Administer >> System Settings >> Option Groups >> Email Greetings for valid values'); - } - break; - - case 'postal_greeting': - $postalGreetingFilter = [ - 'contact_type' => $this->_contactType, - 'greeting_type' => 'postal_greeting', - ]; - if (!self::in_value($value, CRM_Core_PseudoConstant::greeting($postalGreetingFilter))) { - $errors[] = ts('Postal Greeting must be one of the configured format options. Check Administer >> System Settings >> Option Groups >> Postal Greetings for valid values'); - } - break; - - case 'addressee': - $addresseeFilter = [ - 'contact_type' => $this->_contactType, - 'greeting_type' => 'addressee', - ]; - if (!self::in_value($value, CRM_Core_PseudoConstant::greeting($addresseeFilter))) { - $errors[] = ts('Addressee must be one of the configured format options. Check Administer >> System Settings >> Option Groups >> Addressee for valid values'); - } - break; - - case 'email_greeting_custom': - if (array_key_exists('email_greeting', $params)) { - $emailGreetingLabel = key(CRM_Core_OptionGroup::values('email_greeting', TRUE, NULL, NULL, 'AND v.name = "Customized"')); - if (CRM_Utils_Array::value('email_greeting', $params) != $emailGreetingLabel) { - $errors[] = ts('Email Greeting - Custom'); - } - } - break; - - case 'postal_greeting_custom': - if (array_key_exists('postal_greeting', $params)) { - $postalGreetingLabel = key(CRM_Core_OptionGroup::values('postal_greeting', TRUE, NULL, NULL, 'AND v.name = "Customized"')); - if (CRM_Utils_Array::value('postal_greeting', $params) != $postalGreetingLabel) { - $errors[] = ts('Postal Greeting - Custom'); - } - } - break; - - case 'addressee_custom': - if (array_key_exists('addressee', $params)) { - $addresseeLabel = key(CRM_Core_OptionGroup::values('addressee', TRUE, NULL, NULL, 'AND v.name = "Customized"')); - if (CRM_Utils_Array::value('addressee', $params) != $addresseeLabel) { - $errors[] = ts('Addressee - Custom'); - } - } - break; - case 'do_not_email': case 'do_not_phone': case 'do_not_mail': @@ -1373,11 +1319,7 @@ private function formatProfileContactParams( } } else { - if (in_array($key, CRM_Contact_BAO_Contact::$_greetingTypes, TRUE)) { - //save email/postal greeting and addressee values if any, CRM-4575 - $data[$key . '_id'] = $value; - } - elseif (($customFieldId = CRM_Core_BAO_CustomField::getKeyID($key))) { + if (($customFieldId = CRM_Core_BAO_CustomField::getKeyID($key))) { // for autocomplete transfer hidden value instead of label if ($params[$key] && isset($params[$key . '_id'])) { $value = $params[$key . '_id']; @@ -2048,43 +1990,6 @@ protected function formatContactParameters(&$values, &$params) { // @todo - remove this after confirming this is just a compilation of other-wise-cached fields. static $fields = []; - // CRM-4575 - if (isset($values['email_greeting'])) { - if (!empty($params['email_greeting_id'])) { - $emailGreetingFilter = [ - 'contact_type' => $params['contact_type'] ?? NULL, - 'greeting_type' => 'email_greeting', - ]; - $emailGreetings = CRM_Core_PseudoConstant::greeting($emailGreetingFilter); - $params['email_greeting'] = $emailGreetings[$params['email_greeting_id']]; - } - else { - $params['email_greeting'] = $values['email_greeting']; - } - - return TRUE; - } - - if (isset($values['postal_greeting'])) { - if (!empty($params['postal_greeting_id'])) { - $postalGreetingFilter = [ - 'contact_type' => $params['contact_type'] ?? NULL, - 'greeting_type' => 'postal_greeting', - ]; - $postalGreetings = CRM_Core_PseudoConstant::greeting($postalGreetingFilter); - $params['postal_greeting'] = $postalGreetings[$params['postal_greeting_id']]; - } - else { - $params['postal_greeting'] = $values['postal_greeting']; - } - return TRUE; - } - - if (isset($values['addressee'])) { - $params['addressee'] = $values['addressee']; - return TRUE; - } - if (isset($values['note'])) { // add a note field if (!isset($params['note'])) { @@ -2620,6 +2525,7 @@ private function addFieldToParams(array &$contactArray, array $locationValues, s } } else { + $fieldName = array_search($fieldName, $this->getOddlyMappedMetadataFields(), TRUE) ?: $fieldName; $contactArray[$fieldName] = $this->getTransformedFieldValue($fieldName, $importedValue); } } diff --git a/CRM/Import/Parser.php b/CRM/Import/Parser.php index 0549390315aa..6aa5255601b8 100644 --- a/CRM/Import/Parser.php +++ b/CRM/Import/Parser.php @@ -9,6 +9,7 @@ +--------------------------------------------------------------------+ */ +use Civi\Api4\CustomField; use Civi\Api4\UserJob; /** @@ -818,43 +819,6 @@ private function _civicrm_api3_deprecated_add_formatted_param(&$values, &$params return TRUE; } - // CRM-4575 - if (isset($values['email_greeting'])) { - if (!empty($params['email_greeting_id'])) { - $emailGreetingFilter = [ - 'contact_type' => $params['contact_type'] ?? NULL, - 'greeting_type' => 'email_greeting', - ]; - $emailGreetings = CRM_Core_PseudoConstant::greeting($emailGreetingFilter); - $params['email_greeting'] = $emailGreetings[$params['email_greeting_id']]; - } - else { - $params['email_greeting'] = $values['email_greeting']; - } - - return TRUE; - } - - if (isset($values['postal_greeting'])) { - if (!empty($params['postal_greeting_id'])) { - $postalGreetingFilter = [ - 'contact_type' => $params['contact_type'] ?? NULL, - 'greeting_type' => 'postal_greeting', - ]; - $postalGreetings = CRM_Core_PseudoConstant::greeting($postalGreetingFilter); - $params['postal_greeting'] = $postalGreetings[$params['postal_greeting_id']]; - } - else { - $params['postal_greeting'] = $values['postal_greeting']; - } - return TRUE; - } - - if (isset($values['addressee'])) { - $params['addressee'] = $values['addressee']; - return TRUE; - } - if (isset($values['gender'])) { if (!empty($params['gender_id'])) { $genders = CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', 'gender_id'); @@ -1268,15 +1232,27 @@ protected function getFieldOptions(string $fieldName) { */ protected function getFieldMetadata(string $fieldName, bool $loadOptions = FALSE, $limitToContactType = FALSE): array { - $fieldMap = ['country_id' => 'country']; + $fieldMap = $this->getOddlyMappedMetadataFields(); $fieldMapName = empty($fieldMap[$fieldName]) ? $fieldName : $fieldMap[$fieldName]; - $fieldMetadata = $this->getImportableFieldsMetadata()[$fieldMapName] ?? ($limitToContactType ? NULL : CRM_Contact_BAO_Contact::importableFields('All')[$fieldMapName]); - if ($loadOptions && !isset($fieldMetadata['options'])) { + // This whole business of only loading metadata for one type when we actually need it for all is ... dubious. + if (empty($this->getImportableFieldsMetadata()[$fieldMapName])) { + if ($loadOptions || !$limitToContactType) { + $this->importableFieldsMetadata[$fieldMapName] = CRM_Contact_BAO_Contact::importableFields('All')[$fieldMapName]; + } + } + $fieldMetadata = $this->getImportableFieldsMetadata()[$fieldMapName]; + if ($loadOptions && !isset($fieldMetadata['options'])) { + $optionFieldName = empty($fieldMap[$fieldName]) ? $fieldMetadata['name'] : $fieldName; + if (!empty($fieldMetadata['custom_group_id'])) { + $customField = CustomField::get(FALSE)->addWhere('id', '=', $fieldMetadata['custom_field_id']) + ->addSelect('name', 'custom_group_id.name')->execute()->first(); + $optionFieldName = $customField['custom_group_id.name'] . '.' . $customField['name']; + } $options = civicrm_api4($this->getFieldEntity($fieldName), 'getFields', [ - 'loadOptions' => ['id', 'name', 'label'], - 'where' => [['name', '=', empty($fieldMap[$fieldName]) ? $fieldMetadata['name'] : $fieldName]], + 'loadOptions' => ['id', 'name', 'label', 'abbr'], + 'where' => [['name', '=', $optionFieldName]], 'select' => ['options'], ])->first()['options']; if (is_array($options)) { @@ -1284,9 +1260,15 @@ protected function getFieldMetadata(string $fieldName, bool $loadOptions = FALSE // name AND label as either might be used. We also lower case before checking $values = []; foreach ($options as $option) { - $values[$option['id']] = $option['id']; - $values[mb_strtolower($option['name'])] = $option['id']; - $values[mb_strtolower($option['label'])] = $option['id']; + $idKey = is_numeric($option['id']) ? $option['id'] : mb_strtolower($option['id']); + $values[$idKey] = $option['id']; + foreach (['name', 'label', 'abbr'] as $key) { + $optionValue = mb_strtolower($option[$key] ?? ''); + if ($optionValue !== '') { + $values[$optionValue] = $option['id']; + } + } + } $this->importableFieldsMetadata[$fieldMapName]['options'] = $values; } @@ -1385,6 +1367,9 @@ protected function getFieldEntity(string $fieldName) { if ($fieldName === 'do_not_import') { return NULL; } + if (in_array($fieldName, ['email_greeting_id', 'postal_greeting_id', 'addressee_id'], TRUE)) { + return 'Contact'; + } $metadata = $this->getFieldMetadata($fieldName); if (!isset($metadata['entity'])) { return in_array($metadata['extends'], ['Individual', 'Organization', 'Household'], TRUE) ? 'Contact' : $metadata['extends']; @@ -1443,4 +1428,20 @@ protected function getAvailableCountries() { return $this->availableCountries; } + /** + * Get the metadata field for which importable fields does not key the actual field name. + * + * @return string[] + */ + protected function getOddlyMappedMetadataFields(): array { + return [ + 'country_id' => 'country', + 'state_province_id' => 'state_province', + 'county_id' => 'county', + 'email_greeting_id' => 'email_greeting', + 'postal_greeting_id' => 'postal_greeting', + 'addressee_id' => 'addressee', + ]; + } + } diff --git a/tests/phpunit/CRM/Contact/Import/Parser/ContactTest.php b/tests/phpunit/CRM/Contact/Import/Parser/ContactTest.php index eb9e33a4ae24..d329655483c9 100644 --- a/tests/phpunit/CRM/Contact/Import/Parser/ContactTest.php +++ b/tests/phpunit/CRM/Contact/Import/Parser/ContactTest.php @@ -637,6 +637,49 @@ public function testGenderLabel() { $this->callAPISuccessGetSingle('Contact', $contactValues); } + /** + * Test greeting imports. + * + * @throws \API_Exception + * @throws \CRM_Core_Exception + * @throws \CiviCRM_API3_Exception + */ + public function testGreetings(): void { + $contactValues = [ + 'first_name' => 'Bill', + 'last_name' => 'Gates', + // id = 2 + 'email_greeting' => 'Dear {contact.prefix_id:label} {contact.first_name} {contact.last_name}', + // id = 3 + 'postal_greeting' => 'Dear {contact.prefix_id:label} {contact.last_name}', + // id = 1 + 'addressee' => '{contact.prefix_id:label}{ }{contact.first_name}{ }{contact.middle_name}{ }{contact.last_name}{ }{contact.suffix_id:label}', + 5 => 1, + ]; + $userJobID = $this->getUserJobID(['mapper' => [['first_name'], ['last_name'], ['email_greeting'], ['postal_greeting'], ['addressee']]]); + $parser = new CRM_Contact_Import_Parser_Contact(array_keys($contactValues)); + $parser->setUserJobID($userJobID); + $values = array_values($contactValues); + $parser->import(CRM_Import_Parser::DUPLICATE_UPDATE, $values); + $contact = Contact::get(FALSE)->addWhere('last_name', '=', 'Gates')->addSelect('email_greeting_id', 'postal_greeting_id', 'addressee_id')->execute()->first(); + $this->assertEquals(2, $contact['email_greeting_id']); + $this->assertEquals(3, $contact['postal_greeting_id']); + $this->assertEquals(1, $contact['addressee_id']); + + Contact::delete()->addWhere('id', '=', $contact['id'])->setUseTrash(TRUE)->execute(); + + // Now try again with numbers. + $values[2] = 2; + $values[3] = 3; + $values[4] = 1; + $parser->import(CRM_Import_Parser::DUPLICATE_UPDATE, $values); + $contact = Contact::get(FALSE)->addWhere('last_name', '=', 'Gates')->addSelect('email_greeting_id', 'postal_greeting_id', 'addressee_id')->execute()->first(); + $this->assertEquals(2, $contact['email_greeting_id']); + $this->assertEquals(3, $contact['postal_greeting_id']); + $this->assertEquals(1, $contact['addressee_id']); + + } + /** * Test prefix & suffix work when you specify the label. *