From 68c1b1f5de66300f649cae18a3ccb2d324e35da2 Mon Sep 17 00:00:00 2001 From: Eileen McNaughton Date: Tue, 11 Jan 2022 11:40:51 +1300 Subject: [PATCH] Split mailing label formatting out from main format Splitting these 2 functions out allows cleanup & eventual decommissioning of the mailing label function. There are siginificant usage differences depending on context so this will permit cleanup - I did a universe search & these are the only 2 times it is called with mailing = TRUE --- CRM/Contact/Form/Task/Label.php | 2 +- CRM/Member/Form/Task/Label.php | 2 +- CRM/Utils/Address.php | 252 +++++++++++++++++++++++- tests/phpunit/CRM/Utils/AddressTest.php | 4 +- 4 files changed, 255 insertions(+), 5 deletions(-) diff --git a/CRM/Contact/Form/Task/Label.php b/CRM/Contact/Form/Task/Label.php index a8905fa444..e615a7a845 100644 --- a/CRM/Contact/Form/Task/Label.php +++ b/CRM/Contact/Form/Task/Label.php @@ -314,7 +314,7 @@ class CRM_Contact_Form_Task_Label extends CRM_Contact_Form_Task { $row['preferred_communication_method'] = implode(', ', $temp); } $row['id'] = $id; - $formatted = CRM_Utils_Address::format($row, 'mailing_format', FALSE, TRUE, $tokenFields); + $formatted = CRM_Utils_Address::formatMailingLabel($row, 'mailing_format', FALSE, TRUE, $tokenFields); $rows[$id] = [$formatted]; } diff --git a/CRM/Member/Form/Task/Label.php b/CRM/Member/Form/Task/Label.php index 4ba5fc8525..c6fddc7c0b 100644 --- a/CRM/Member/Form/Task/Label.php +++ b/CRM/Member/Form/Task/Label.php @@ -104,7 +104,7 @@ class CRM_Member_Form_Task_Label extends CRM_Member_Form_Task { $row['preferred_communication_method'] = implode(', ', $temp); } $row['id'] = $id; - $formatted = CRM_Utils_Address::format($row, 'mailing_format', FALSE, TRUE, $tokenFields); + $formatted = CRM_Utils_Address::formatMailingLabel($row, 'mailing_format', FALSE, TRUE, $tokenFields); $rows[$id] = [$formatted]; } if ($isPerMembership) { diff --git a/CRM/Utils/Address.php b/CRM/Utils/Address.php index efba5738dc..ff30f95cb1 100644 --- a/CRM/Utils/Address.php +++ b/CRM/Utils/Address.php @@ -33,7 +33,7 @@ class CRM_Utils_Address { * @param bool $microformat * If true indicates, the address to be built in hcard-microformat standard. * @param bool $mailing - * If true indicates, the call has been made from mailing label. + * Should ALWAYS be false. * @param string[] $tokenFields * * @return string @@ -48,6 +48,256 @@ class CRM_Utils_Address { $tokenFields = NULL ) { static $config = NULL; + $mailing = FALSE; + + if (!$format) { + $format = Civi::settings()->get('address_format'); + } + + if ($mailing) { + $format = Civi::settings()->get('mailing_format'); + } + + $formatted = $format; + + $fullPostalCode = $fields['postal_code'] ?? NULL; + if (!empty($fields['postal_code_suffix'])) { + $fullPostalCode .= "-$fields[postal_code_suffix]"; + } + + // make sure that some of the fields do have values + $emptyFields = [ + 'supplemental_address_1', + 'supplemental_address_2', + 'supplemental_address_3', + 'state_province_name', + 'county', + ]; + foreach ($emptyFields as $f) { + if (!isset($fields[$f])) { + $fields[$f] = NULL; + } + } + + //CRM-16876 Display countries in all caps when in mailing mode. + if ($mailing && !empty($fields['country'])) { + if (Civi::settings()->get('hideCountryMailingLabels')) { + $domain = CRM_Core_BAO_Domain::getDomain(); + $domainLocation = CRM_Core_BAO_Location::getValues(['contact_id' => $domain->contact_id]); + $domainAddress = $domainLocation['address'][1]; + $domainCountryId = $domainAddress['country_id']; + if ($fields['country'] == CRM_Core_PseudoConstant::country($domainCountryId)) { + $fields['country'] = NULL; + } + else { + //Capitalization display on uppercase to contries with special characters + $fields['country'] = mb_convert_case($fields['country'], MB_CASE_UPPER, "UTF-8"); + } + } + else { + $fields['country'] = mb_convert_case($fields['country'], MB_CASE_UPPER, "UTF-8"); + } + } + + if (!$microformat) { + // replacements in case of Individual Name Format + $replacements = [ + 'contact.display_name' => $fields['display_name'] ?? NULL, + 'contact.individual_prefix' => $fields['individual_prefix'] ?? NULL, + 'contact.formal_title' => $fields['formal_title'] ?? NULL, + 'contact.first_name' => $fields['first_name'] ?? NULL, + 'contact.middle_name' => $fields['middle_name'] ?? NULL, + 'contact.last_name' => $fields['last_name'] ?? NULL, + 'contact.individual_suffix' => $fields['individual_suffix'] ?? NULL, + 'contact.address_name' => $fields['address_name'] ?? NULL, + 'contact.street_address' => $fields['street_address'] ?? NULL, + 'contact.supplemental_address_1' => $fields['supplemental_address_1'] ?? NULL, + 'contact.supplemental_address_2' => $fields['supplemental_address_2'] ?? NULL, + 'contact.supplemental_address_3' => $fields['supplemental_address_3'] ?? NULL, + 'contact.city' => $fields['city'] ?? NULL, + 'contact.state_province_name' => $fields['state_province_name'] ?? NULL, + 'contact.county' => $fields['county'] ?? NULL, + 'contact.state_province' => $fields['state_province'] ?? NULL, + 'contact.postal_code' => $fullPostalCode, + 'contact.country' => $fields['country'] ?? NULL, + 'contact.world_region' => $fields['world_region'] ?? NULL, + 'contact.geo_code_1' => $fields['geo_code_1'] ?? NULL, + 'contact.geo_code_2' => $fields['geo_code_2'] ?? NULL, + 'contact.current_employer' => $fields['current_employer'] ?? NULL, + 'contact.nick_name' => $fields['nick_name'] ?? NULL, + 'contact.email' => $fields['email'] ?? NULL, + 'contact.im' => $fields['im'] ?? NULL, + 'contact.do_not_email' => $fields['do_not_email'] ?? NULL, + 'contact.do_not_phone' => $fields['do_not_phone'] ?? NULL, + 'contact.do_not_mail' => $fields['do_not_mail'] ?? NULL, + 'contact.do_not_sms' => $fields['do_not_sms'] ?? NULL, + 'contact.do_not_trade' => $fields['do_not_trade'] ?? NULL, + 'contact.job_title' => $fields['job_title'] ?? NULL, + 'contact.birth_date' => $fields['birth_date'] ?? NULL, + 'contact.gender' => $fields['gender'] ?? NULL, + 'contact.is_opt_out' => $fields['is_opt_out'] ?? NULL, + 'contact.preferred_mail_format' => $fields['preferred_mail_format'] ?? NULL, + 'contact.phone' => $fields['phone'] ?? NULL, + 'contact.home_URL' => $fields['home_URL'] ?? NULL, + 'contact.contact_source' => $fields['contact_source'] ?? NULL, + 'contact.external_identifier' => $fields['external_identifier'] ?? NULL, + 'contact.contact_id' => $fields['id'] ?? NULL, + 'contact.household_name' => $fields['household_name'] ?? NULL, + 'contact.organization_name' => $fields['organization_name'] ?? NULL, + 'contact.legal_name' => $fields['legal_name'] ?? NULL, + 'contact.preferred_communication_method' => $fields['preferred_communication_method'] ?? NULL, + 'contact.communication_style' => $fields['communication_style'] ?? NULL, + 'contact.addressee' => $fields['addressee_display'] ?? NULL, + 'contact.email_greeting' => $fields['email_greeting_display'] ?? NULL, + 'contact.postal_greeting' => $fields['postal_greeting_display'] ?? NULL, + ]; + } + else { + $replacements = [ + 'contact.address_name' => "" . $fields['address_name'] . "", + 'contact.street_address' => "" . $fields['street_address'] . "", + 'contact.supplemental_address_1' => "" . $fields['supplemental_address_1'] . "", + 'contact.supplemental_address_2' => $fields['supplemental_address_2'], + 'contact.supplemental_address_3' => $fields['supplemental_address_3'], + 'contact.city' => "" . $fields['city'] . "", + 'contact.state_province_name' => "" . $fields['state_province_name'] . "", + 'contact.county' => "" . $fields['county'], + 'contact.state_province' => "" . $fields['state_province'] . "", + 'contact.postal_code' => "" . $fullPostalCode . "", + 'contact.country' => "" . $fields['country'] . "", + 'contact.world_region' => "" . $fields['world_region'] . "", + ]; + + // erase all empty ones, so we dont get blank lines + foreach (array_keys($replacements) as $key) { + $exactKey = substr($key, 0, 8) == 'contact.' ? substr($key, 8) : $key; + if ($key != 'contact.postal_code' && + CRM_Utils_Array::value($exactKey, $fields) == NULL + ) { + $replacements[$key] = ''; + } + } + if (empty($fullPostalCode)) { + $replacements['contact.postal_code'] = ''; + } + } + + // replacements in case of Custom Token + if (stristr($formatted, 'custom_')) { + $customToken = array_keys($fields); + foreach ($customToken as $value) { + if (substr($value, 0, 7) == 'custom_') { + $replacements["contact.{$value}"] = $fields["{$value}"]; + } + } + } + + // also sub all token fields + if ($tokenFields) { + foreach ($tokenFields as $token) { + $replacements["{$token}"] = $fields["{$token}"] ?? NULL; + } + } + + // for every token, replace {fooTOKENbar} with fooVALUEbar if + // the value is not empty, otherwise drop the whole {fooTOKENbar} + foreach ($replacements as $token => $value) { + if ($value && is_string($value) || is_numeric($value)) { + $formatted = preg_replace("/{([^{}]*)\b{$token}\b([^{}]*)}/u", "\${1}{$value}\${2}", $formatted); + } + else { + $formatted = preg_replace("/{[^{}]*\b{$token}\b[^{}]*}/u", '', $formatted); + } + } + + // drop any {...} constructs from lines' ends + if (!$microformat) { + $formatted = "\n$formatted\n"; + } + else { + if ($microformat == 1) { + $formatted = "\n
\n$formatted
\n"; + } + else { + $formatted = "\n
$formatted
\n"; + } + } + + $formatted = preg_replace('/\n{[^{}]*}/u', "\n", $formatted); + $formatted = preg_replace('/{[^{}]*}\n/u', "\n", $formatted); + + // if there are any 'sibling' {...} constructs, replace them with the + // contents of the first one; for example, when there's no state_province: + // 1. {city}{, }{state_province}{ }{postal_code} + // 2. San Francisco{, }{ }12345 + // 3. San Francisco, 12345 + $formatted = preg_replace('/{([^{}]*)}({[^{}]*})+/u', '\1', $formatted); + + // drop any remaining curly braces leaving their contents + $formatted = str_replace(['{', '}'], '', $formatted); + + // drop any empty lines left after the replacements + $formatted = preg_replace('/^[ \t]*[\r\n]+/m', '', $formatted); + + if (!$microformat) { + $finalFormatted = $formatted; + } + else { + // remove \n from each line and only add at the end + // this hack solves formatting issue, when we convert nl2br + $lines = []; + $count = 1; + $finalFormatted = NULL; + $formattedArray = explode("\n", $formatted); + $formattedArray = array_filter($formattedArray); + + foreach ($formattedArray as $line) { + $line = trim($line); + if ($line) { + if ($count > 1 && $count < count($formattedArray)) { + $line = "$line\n"; + } + $finalFormatted .= $line; + $count++; + } + } + } + return $finalFormatted; + } + + /** + * Format a mailing label. + * + * @internal + * + * This function is split off from format() which is doing too much for cleanup. + * + * It is ONLY called from 2 label task classes and MUST NOT be called from + * anywhere else as it is changing. + * + * @param array $fields + * The address fields. + * @param string $format + * The desired address format. + * @param bool $microformat + * If true indicates, the address to be built in hcard-microformat standard. + * @param bool $mailing + * If true indicates, the call has been made from mailing label. + * @param null $tokenFields + * + * @return string + * formatted address string + * + */ + public static function formatMailingLabel( + $fields, + $format = NULL, + $microformat = FALSE, + $mailing = FALSE, + $tokenFields = NULL + ) { + static $config = NULL; + $microformat = FALSE; if (!$format) { $format = Civi::settings()->get('address_format'); diff --git a/tests/phpunit/CRM/Utils/AddressTest.php b/tests/phpunit/CRM/Utils/AddressTest.php index 0300616661..803e9387a4 100644 --- a/tests/phpunit/CRM/Utils/AddressTest.php +++ b/tests/phpunit/CRM/Utils/AddressTest.php @@ -6,7 +6,7 @@ */ class CRM_Utils_AddressTest extends CiviUnitTestCase { - public function testAddressFormat() { + public function testAddressFormat(): void { $contact = $this->callAPISuccess('contact', 'create', [ 'first_name' => 'Micky', 'last_name' => 'mouse', @@ -25,7 +25,7 @@ class CRM_Utils_AddressTest extends CiviUnitTestCase { $addressDetails = $address['values'][$address['id']]; $countries = CRM_Core_PseudoConstant::country(); $addressDetails['country'] = $countries[$addressDetails['country_id']]; - $formatted_address = CRM_Utils_Address::format($addressDetails, 'mailing_format', FALSE, TRUE); + $formatted_address = CRM_Utils_Address::formatMailingLabel($addressDetails, 'mailing_format', FALSE, TRUE); $this->assertTrue((bool) strstr($formatted_address, 'UNITED STATES')); } -- 2.25.1