From adb130937fa5214a074c9a9175906a41977616d7 Mon Sep 17 00:00:00 2001 From: Eileen McNaughton Date: Fri, 8 Dec 2023 11:50:11 +1300 Subject: [PATCH] Fix MailingLabels to use token processor --- CRM/Utils/Address.php | 150 ++---------------- Civi/Token/TokenCompatSubscriber.php | 7 + .../Form/Task/PrintMailingLabelTest.php | 9 +- 3 files changed, 31 insertions(+), 135 deletions(-) diff --git a/CRM/Utils/Address.php b/CRM/Utils/Address.php index 2b79d40a59..5323f5a006 100644 --- a/CRM/Utils/Address.php +++ b/CRM/Utils/Address.php @@ -9,6 +9,8 @@ +--------------------------------------------------------------------+ */ +use Civi\Token\TokenProcessor; + /** * Address Utilities * @@ -247,45 +249,14 @@ class CRM_Utils_Address { * anywhere else as it is changing. * * @param array $fields - * The address fields. - * @param null $format - * Unused var. - * @param bool $microformat - * Unused var. - * @param bool $mailing - * Unused var. - * @param null $tokenFields * * @return string * formatted address string * */ - public static function formatMailingLabel( - $fields, - $format = NULL, - $microformat = FALSE, - $mailing = FALSE, - $tokenFields = NULL - ) { - $formatted = Civi::settings()->get('mailing_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; - } + public static function formatMailingLabel($fields) { + if (!empty($fields['postal_code_suffix']) && !empty($fields['postal_code'])) { + $fields['postal_code'] .= "-$fields[postal_code_suffix]"; } //CRM-16876 Display countries in all caps when in mailing mode. @@ -300,114 +271,25 @@ class CRM_Utils_Address { } else { //Capitalization display on uppercase to contries with special characters - $fields['country'] = mb_convert_case($fields['country'], MB_CASE_UPPER, "UTF-8"); + $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"); + $fields['country'] = mb_convert_case($fields['country'], MB_CASE_UPPER, 'UTF-8'); } } - // 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.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, - ]; - - // 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 - $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); + $tokenProcessor = new TokenProcessor(\Civi::dispatcher(), [ + 'class' => __CLASS__, + 'schema' => ['contactId'], + ]); + $tokenProcessor->addRow(['contactId' => $fields['id'] ?? 0, 'contact' => $fields]); + $tokenProcessor->addMessage('label', Civi::settings()->get('mailing_format'), 'text/plain'); + $tokenProcessor->evaluate(); + $text = $tokenProcessor->getRow(0)->render('label'); // drop any empty lines left after the replacements - $formatted = preg_replace('/^[ \t]*[\r\n]+/m', '', $formatted); - - $finalFormatted = $formatted; - return $finalFormatted; + return preg_replace('/^[ \t]*[\r\n]+/m', '', $text); } /** diff --git a/Civi/Token/TokenCompatSubscriber.php b/Civi/Token/TokenCompatSubscriber.php index 9c81b9180e..cb0506e1ad 100644 --- a/Civi/Token/TokenCompatSubscriber.php +++ b/Civi/Token/TokenCompatSubscriber.php @@ -82,6 +82,13 @@ class TokenCompatSubscriber implements EventSubscriberInterface { // If it is repeated it will be replaced by the first input - // ie { }{ } will be replaced by the content of the latter token. // Check testGenerateDisplayNameCustomFormats for test cover. + // and testMailingLabel + // This first regex targets anything like {, }{ } - where the presence of the space + // one tells us we could be in a situation like + // {contact.address_primary.city}{, }{contact.address_primary.state_province_id:label}{ }{contact.address_primary.postal_code} + // Where state_province is not present. No perfect solution here but we + // do want to keep the comma in this case. + $e->string = preg_replace('/\\\\|{(\s*([,`~()\-*|])*\s*)?}}*({\s})/', '$1', $e->string); $e->string = preg_replace('/\\\\|{(\s*(\s|,|`|~|\(|\)|-|\*|\|)*\s*)?\}}*(?=[^{\s])/', '$1', $e->string); // Now do a another pass, removing any remaining instances (which will get rid of any that were not // followed by something). diff --git a/tests/phpunit/CRM/Contact/Form/Task/PrintMailingLabelTest.php b/tests/phpunit/CRM/Contact/Form/Task/PrintMailingLabelTest.php index 191406ff61..ac1e258c4c 100644 --- a/tests/phpunit/CRM/Contact/Form/Task/PrintMailingLabelTest.php +++ b/tests/phpunit/CRM/Contact/Form/Task/PrintMailingLabelTest.php @@ -73,7 +73,14 @@ class CRM_Contact_Form_Task_PrintMailingLabelTest extends CiviUnitTestCase { catch (CRM_Core_Exception_PrematureExitException $e) { $rows = $e->errorData['contactRows']; } - + $this->assertEquals('Mr. Antonia J. D`souza II +Main Street 23 +Brummen, 6971 BN +NETHERLANDS', $rows[$contactIDs[0]][0]); + $this->assertEquals('Mr. Anthony J. Collins II +Main Street 23 +Brummen, 6971 BN +NETHERLANDS', $rows[$contactIDs[1]][0]); foreach ($contactIDs as $contactID) { // ensure that the address printed in the mailing labe is always primary if 'location_type_id' - none (as Primary) is chosen $this->assertStringContainsString($addresses[$contactID]['primary']['street_address'], $rows[$contactID][0]); -- 2.25.1