Fix MailingLabels to use token processor
authorEileen McNaughton <emcnaughton@wikimedia.org>
Thu, 7 Dec 2023 22:50:11 +0000 (11:50 +1300)
committerEileen McNaughton <emcnaughton@wikimedia.org>
Thu, 7 Dec 2023 23:40:34 +0000 (12:40 +1300)
CRM/Utils/Address.php
Civi/Token/TokenCompatSubscriber.php
tests/phpunit/CRM/Contact/Form/Task/PrintMailingLabelTest.php

index 2b79d40a599bc00fa777718797b5509ed2ebd09c..5323f5a0064a3f7fac1b77e588fe823d8e6c3810 100644 (file)
@@ -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);
   }
 
   /**
index 9c81b9180ed1398063c6f5ecfce4914dcac00612..cb0506e1ad1da78f2be70c5a475a59f4bb361808 100644 (file)
@@ -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).
index 191406ff618c060d569eb6278c3966f15a209d83..ac1e258c4c38c01ce6774106bfa7fe3d72aa9c9d 100644 (file)
@@ -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]);