dev/core#4109 Fix advertised tokens to work
authorEileen McNaughton <emcnaughton@wikimedia.org>
Fri, 10 Feb 2023 03:36:58 +0000 (16:36 +1300)
committerEileen McNaughton <emcnaughton@wikimedia.org>
Fri, 10 Feb 2023 08:05:02 +0000 (21:05 +1300)
CRM/Contact/Tokens.php
CRM/Core/EntityTokens.php
tests/phpunit/CRM/Utils/TokenConsistencyTest.php

index 0c558d202f7ef9132e672a8182f2e0d52455adf5..bed30b86f737bc14d81e64789940ba5fea4b14a5 100644 (file)
@@ -397,14 +397,17 @@ class CRM_Contact_Tokens extends CRM_Core_EntityTokens {
       foreach ($metadata as $field) {
         if ($entity === 'website') {
           // It's not the primary - it's 'just one of them' - so the name is _first not _primary
+          $field['name'] = 'website_first.' . $field['name'];
           $this->addFieldToTokenMetadata($tokensMetadata, $field, $exposedFields, 'website_first');
         }
         else {
+          $field['name'] = $entity . '_primary.' . $field['name'];
           $this->addFieldToTokenMetadata($tokensMetadata, $field, $exposedFields, $entity . '_primary');
           $field['label'] .= ' (' . ts('Billing') . ')';
           // Set audience to sysadmin in case adding them to UI annoys people. If people ask to see this
           // in the UI we could set to 'user'.
           $field['audience'] = 'sysadmin';
+          $field['name'] = $entity . '_billing.' . $field['name'];
           $this->addFieldToTokenMetadata($tokensMetadata, $field, $exposedFields, $entity . '_billing');
         }
       }
@@ -453,13 +456,11 @@ class CRM_Contact_Tokens extends CRM_Core_EntityTokens {
         if ($fieldSpec['table_name'] === 'civicrm_website') {
           $tableAlias = 'website_first';
           $joins[$tableAlias] = $fieldSpec['entity'];
-          $prefix = $tableAlias . '.';
         }
         if ($fieldSpec['table_name'] === 'civicrm_openid') {
           // We could start to deprecate this one maybe..... I've made it un-advertised.
           $tableAlias = 'openid_primary';
           $joins[$tableAlias] = $fieldSpec['entity'];
-          $prefix = $tableAlias . '.';
         }
         if ($fieldSpec['type'] === 'Custom') {
           $customFields['custom_' . $fieldSpec['custom_field_id']] = $fieldSpec['name'];
index af3ba17efd7b3d8736e43a6053f080d7fd30c4bb..9672f08c1407003dd05564242698f893a31d3d36 100644 (file)
@@ -611,7 +611,8 @@ class CRM_Core_EntityTokens extends AbstractTokenSubscriber {
    * @param string $prefix
    */
   protected function addFieldToTokenMetadata(array &$tokensMetadata, array $field, array $exposedFields, string $prefix = ''): void {
-    if ($field['type'] !== 'Custom' && !in_array($field['name'], $exposedFields, TRUE)) {
+    $isExposed = in_array(str_replace($prefix . '.', '', $field['name']), $exposedFields, TRUE);
+    if ($field['type'] !== 'Custom' && !$isExposed) {
       return;
     }
     $field['audience'] = $field['audience'] ?? 'user';
@@ -635,8 +636,9 @@ class CRM_Core_EntityTokens extends AbstractTokenSubscriber {
       $tokensMetadata[$tokenName] = $field;
       return;
     }
-    $tokenName = $prefix ? ($prefix . '.' . $field['name']) : $field['name'];
-    if (in_array($field['name'], $exposedFields, TRUE)) {
+    $tokenName = $field['name'];
+    // Presumably this line can not be reached unless isExposed = TRUE.
+    if ($isExposed) {
       if (
         ($field['options'] || !empty($field['suffixes']))
         // At the time of writing currency didn't have a label option - this may have changed.
index 0840d6cca99b12567f1396244993ef465036b160..5c7af04c1be5e9433556c9c3f635099e220306f4 100644 (file)
@@ -272,6 +272,24 @@ case.custom_1 :' . '
     ];
   }
 
+  /**
+   * Test the standard new location token format - which matches apiv4 return properties.
+   *
+   * @throws \CRM_Core_Exception
+   */
+  public function testLocationTokens(): void {
+    $contactID = $this->individualCreate(['email' => 'me@example.com']);
+    Address::create()->setValues([
+      'contact_id' => $contactID,
+      'is_primary' => TRUE,
+      'street_address' => 'Heartbreak Hotel',
+      'supplemental_address_1' => 'Lonely Street',
+    ])->execute();
+    $text = '{contact.first_name} {contact.email_primary.email} {contact.address_primary.street_address}';
+    $text = $this->renderText(['contactId' => $contactID], $text);
+    $this->assertEquals('Anthony me@example.com Heartbreak Hotel', $text);
+  }
+
   /**
    * Test tokens in 2 ways to ensure consistent handling.
    *
@@ -563,6 +581,8 @@ contribution_recur.payment_instrument_id:name :Check
   /**
    * Get expected output from token parsing.
    *
+   * @param int|null $participantCreatedID
+   *
    * @return string
    */
   protected function getExpectedParticipantTokenOutput(int $participantCreatedID = NULL): string {
@@ -811,13 +831,8 @@ United States', $tokenProcessor->getRow(0)->render('message'));
     $mut = new CiviMailUtils($this);
     $this->setupParticipantScheduledReminder();
 
-    $tokens = CRM_Core_SelectValues::eventTokens();
-    $this->assertEquals(array_merge($this->getEventTokens()), $tokens);
-    $tokenProcessor = new TokenProcessor(\Civi::dispatcher(), [
-      'controller' => __CLASS__,
-      'smarty' => FALSE,
-      'schema' => ['eventId'],
-    ]);
+    $tokens = array_merge($this->getEventTokens());
+    $tokenProcessor = $this->getTokenProcessor(['schema' => ['eventId']]);
     $this->assertEquals(array_merge($tokens, $this->getDomainTokens()), $tokenProcessor->listTokens());
 
     $expectedEventString = $this->getExpectedEventTokenOutput();
@@ -1026,4 +1041,36 @@ United States', $tokenProcessor->getRow(0)->render('message'));
     $this->assertEquals($expected, $rendered);
   }
 
+  /**
+   * @param array $override
+   *
+   * @return \Civi\Token\TokenProcessor
+   */
+  protected function getTokenProcessor(array $override): TokenProcessor {
+    return new TokenProcessor(\Civi::dispatcher(), array_merge([
+      'controller' => __CLASS__,
+    ], $override));
+  }
+
+  /**
+   * Render the text via the token processor.
+   *
+   * @param array $rowContext
+   * @param string $text
+   * @param array $context
+   *
+   * @return string
+   */
+  protected function renderText(array $rowContext, string $text, array $context = []): string {
+    $context['schema'] = $context['schema'] ?? [];
+    foreach (array_keys($rowContext) as $key) {
+      $context['schema'][] = $key;
+    }
+    $tokenProcessor = $this->getTokenProcessor($context);
+    $tokenProcessor->addRow($rowContext);
+    $tokenProcessor->addMessage('text', $text, 'text/html');
+    $tokenProcessor->evaluate();
+    return $tokenProcessor->getRow(0)->render('text');
+  }
+
 }