diff --git a/CRM/Contact/Tokens.php b/CRM/Contact/Tokens.php index 0c558d202f7e..bed30b86f737 100644 --- a/CRM/Contact/Tokens.php +++ b/CRM/Contact/Tokens.php @@ -397,14 +397,17 @@ protected function getTokenMetadata(): array { 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 @@ protected function getContact(int $contactId, array $requiredFields, bool $getAl 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']; diff --git a/CRM/Core/EntityTokens.php b/CRM/Core/EntityTokens.php index af3ba17efd7b..9672f08c1407 100644 --- a/CRM/Core/EntityTokens.php +++ b/CRM/Core/EntityTokens.php @@ -611,7 +611,8 @@ public function getActiveTokens(TokenValueEvent $e) { * @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 @@ protected function addFieldToTokenMetadata(array &$tokensMetadata, array $field, $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. diff --git a/tests/phpunit/CRM/Utils/TokenConsistencyTest.php b/tests/phpunit/CRM/Utils/TokenConsistencyTest.php index 0840d6cca99b..5c7af04c1be5 100644 --- a/tests/phpunit/CRM/Utils/TokenConsistencyTest.php +++ b/tests/phpunit/CRM/Utils/TokenConsistencyTest.php @@ -272,6 +272,24 @@ public function getUnadvertisedTokens(): array { ]; } + /** + * 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 @@ protected function getMembershipID(): int { /** * Get expected output from token parsing. * + * @param int|null $participantCreatedID + * * @return string */ protected function getExpectedParticipantTokenOutput(int $participantCreatedID = NULL): string { @@ -811,13 +831,8 @@ public function testEventTokenConsistency(): void { $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 @@ public function testEscaping() { $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'); + } + }