Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Align most of the tokens in the token processor handling with the legacy handling #19806

Merged
merged 3 commits into from
Mar 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 24 additions & 14 deletions CRM/Utils/Token.php
Original file line number Diff line number Diff line change
Expand Up @@ -732,22 +732,12 @@ public static function getContactTokenReplacement(
$value = "cs={$cs}";
}
else {
$value = CRM_Utils_Array::retrieveValueRecursive($contact, $token);
$value = (array) CRM_Utils_Array::retrieveValueRecursive($contact, $token);

// FIXME: for some pseudoconstants we get array ( 0 => id, 1 => label )
if (is_array($value)) {
$value = $value[1];
Copy link
Contributor Author

@eileenmcnaughton eileenmcnaughton Mar 15, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line causes an enotice without this cleanup - it was added as part of a fix for https://issues.civicrm.org/jira/browse/CRM-13161 - however, all those greetings and gender fields are now tested & don't appear to have regressed (based on the tests I ran locally) with this change and I don't think the format described is used anymore.

The enotice directly mapped to the token processor having multi-value custom field data

}
// Convert pseudoconstants using metadata
elseif ($value && is_numeric($value)) {
$allFields = CRM_Contact_BAO_Contact::exportableFields('All');
if (!empty($allFields[$token]['pseudoconstant'])) {
$value = CRM_Core_PseudoConstant::getLabel('CRM_Contact_BAO_Contact', $token, $value);
}
}
elseif ($value && CRM_Utils_String::endsWith($token, '_date')) {
$value = CRM_Utils_Date::customFormat($value);
foreach ($value as $index => $item) {
$value[$index] = self::convertPseudoConstantsUsingMetadata($value[$index], $token);
}
$value = implode(', ', $value);
}

if (!$html) {
Expand Down Expand Up @@ -1897,4 +1887,24 @@ public static function formatTokensForDisplay($tokens) {
return $output;
}

/**
* @param $value
* @param $token
*
* @return bool|int|mixed|string|null
*/
protected static function convertPseudoConstantsUsingMetadata($value, $token) {
// Convert pseudoconstants using metadata
if ($value && is_numeric($value)) {
$allFields = CRM_Contact_BAO_Contact::exportableFields('All');
if (!empty($allFields[$token]['pseudoconstant'])) {
$value = CRM_Core_PseudoConstant::getLabel('CRM_Contact_BAO_Contact', $token, $value);
}
}
elseif ($value && CRM_Utils_String::endsWith($token, '_date')) {
$value = CRM_Utils_Date::customFormat($value);
}
return $value;
}

}
15 changes: 9 additions & 6 deletions Civi/Token/TokenCompatSubscriber.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ public function onEvaluate(TokenValueEvent $e) {
$e->getTokenProcessor()->context['hookTokenCategories'] = $categories;

$messageTokens = $e->getTokenProcessor()->getMessageTokens();
$returnProperties = array_fill_keys($messageTokens['contact'] ?? [], 1);
$returnProperties = array_merge(\CRM_Contact_BAO_Query::defaultReturnProperties(), $returnProperties);

foreach ($e->getRows() as $row) {
if (empty($row->context['contactId'])) {
Expand All @@ -58,23 +60,24 @@ public function onEvaluate(TokenValueEvent $e) {
$params = [
['contact_id', '=', $contactId, 0, 0],
];
[$contact] = \CRM_Contact_BAO_Query::apiQuery($params);
[$contact] = \CRM_Contact_BAO_Query::apiQuery($params, $returnProperties ?? NULL);
//CRM-4524
$contact = reset($contact);
// Test cover for greeting in CRM_Core_BAO_ActionScheduleTest::testMailer
$contact['email_greeting'] = $contact['email_greeting_display'] ?? '';
$contact['postal_greeting'] = $contact['postal_greeting_display'] ?? '';
$contact['addressee'] = $contact['address_display'] ?? '';
if (!$contact || is_a($contact, 'CRM_Core_Error')) {
// FIXME: Need to differentiate errors which kill the batch vs the individual row.
\Civi::log()->debug("Failed to generate token data. Invalid contact ID: " . $row->context['contactId']);
\Civi::log()->debug('Failed to generate token data. Invalid contact ID: ' . $row->context['contactId']);
continue;
}

//update value of custom field token
if (!empty($messageTokens['contact'])) {
foreach ($messageTokens['contact'] as $token) {
if (\CRM_Core_BAO_CustomField::getKeyID($token)) {
$contact[$token] = civicrm_api3('Contact', 'getvalue', [
'return' => $token,
'id' => $contactId,
]);
$contact[$token] = \CRM_Core_BAO_CustomField::displayValue($contact[$token], \CRM_Core_BAO_CustomField::getKeyID($token));
}
}
}
Expand Down
257 changes: 164 additions & 93 deletions tests/phpunit/CRM/Core/BAO/MessageTemplateTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

use Civi\Api4\Address;
use Civi\Api4\Contact;
use Civi\Token\TokenProcessor;

/**
* Class CRM_Core_BAO_MessageTemplateTest
Expand All @@ -17,7 +18,7 @@ class CRM_Core_BAO_MessageTemplateTest extends CiviUnitTestCase {
* @throws \CRM_Core_Exception
*/
public function tearDown():void {
$this->quickCleanup(['civicrm_address', 'civicrm_phone', 'civicrm_im', 'civicrm_website', 'civicrm_openid', 'civicrm_email']);
$this->quickCleanup(['civicrm_address', 'civicrm_phone', 'civicrm_im', 'civicrm_website', 'civicrm_openid', 'civicrm_email'], TRUE);
parent::tearDown();
}

Expand Down Expand Up @@ -141,13 +142,7 @@ public function testRenderMessageTemplateIgnoreSmarty(): void {
public function testContactTokens(): void {
$this->createCustomGroupWithFieldsOfAllTypes([]);
$tokenData = $this->getAllContactTokens();
$this->callAPISuccess('Contact', 'create', $tokenData);
$address = $this->callAPISuccess('Address', 'create', array_merge($tokenData, ['is_primary' => TRUE]));
$this->callAPISuccess('Phone', 'create', array_merge($tokenData, ['is_primary' => TRUE]));
$this->callAPISuccess('Email', 'create', array_merge($tokenData, ['is_primary' => TRUE]));
$this->callAPISuccess('Website', 'create', array_merge($tokenData, ['is_primary' => TRUE]));
$this->callAPISuccess('Im', 'create', ['is_primary' => TRUE, 'name' => $tokenData['im'], 'provider_id' => $tokenData['im_provider'], 'contact_id' => $tokenData['contact_id']]);
$this->callAPISuccess('OpenID', 'create', array_merge($tokenData, ['is_primary' => TRUE, 'contact_id' => $tokenData['contact_id'], 'openid' => $tokenData['openid']]));
$address = $this->setupContactFromTokeData($tokenData);

CRM_Core_Smarty::singleton()->assign('pre_assigned_smarty', 'wee');
// This string contains the 4 types of possible replaces just to be sure they
Expand All @@ -164,99 +159,49 @@ public function testContactTokens(): void {
'subject' => $tokenString . ' ',
'text' => $tokenString,
], FALSE, $tokenData['contact_id'], ['passed_smarty' => 'whoa']);
$checksum = substr($messageContent['html'], (strpos($messageContent['html'], 'cs=') + 3), 47);
$contact = Contact::get(FALSE)->addWhere('id', '=', $tokenData['contact_id'])->setSelect(['modified_date', 'employer_id'])->execute()->first();
$expected = 'weewhoa
Default Domain Name
contact_type:Individual
do_not_email:1
do_not_phone:
do_not_mail:1
do_not_sms:1
do_not_trade:1
is_opt_out:1
external_identifier:blah
sort_name:Smith, Robert
display_name:Mr. Robert Smith II
nick_name:Bob
image_URL:https://example.com
preferred_communication_method:
preferred_language:fr_CA
preferred_mail_format:Both
hash:xyz
contact_source:Contact Source
first_name:Robert
middle_name:Frank
last_name:Smith
individual_prefix:Mr.
individual_suffix:II
formal_title:Dogsbody
communication_style:Formal
job_title:Busy person
gender:Female
birth_date:December 31st, 1998
current_employer_id:' . $contact['employer_id'] . '
contact_is_deleted:
created_date:January 1st, 2020 12:00 AM
modified_date:' . CRM_Utils_Date::customFormat($contact['modified_date']) . '
addressee:Mr. Robert Frank Smith II
email_greeting:Dear Robert
postal_greeting:Dear Robert
current_employer:Unit Test Organization
location_type:Home
address_id:' . $address['id'] . '
street_address:Street Address
street_number:123
street_number_suffix:S
street_name:Main St
street_unit:45B
supplemental_address_1:Round the corner
supplemental_address_2:Up the road
supplemental_address_3:By the big tree
city:New York
postal_code_suffix:4578
postal_code:90210
geo_code_1:48.858093
geo_code_2:2.294694
manual_geo_code:1
address_name:The white house
master_id:' . $tokenData['master_id'] . '
county:
state_province:TX
country:United States
phone:123-456
phone_ext:77
phone_type_id:
phone_type:Mobile
email:anthony_anderson@civicrm.org
on_hold:
signature_text:Yours sincerely
signature_html:<p>Yours</p>
im_provider:1
im:IM Screen Name
openid:OpenID
world_region:America South, Central, North and Caribbean
url:http://civicrm.org
custom_1:Bobsled
custom_2:Red
custom_3:01/20/2021 12:00AM
custom_4:999
custom_5:<a href="http://civicrm.org" target="_blank">http://civicrm.org</a>
custom_7:New Zealand
custom_8:France, Canada
custom_9:Mr. Spider Man II
custom_10:Queensland
custom_11:Victoria, New South Wales
custom_12:Yes
custom_13:Purple
checksum:cs=' . $checksum . '
contact_id:' . $tokenData['contact_id'] . '
';
$expected .= $this->getExpectedContactOutput($address['id'], $tokenData, $messageContent['html']);
$this->assertEquals($expected, $messageContent['html']);
$this->assertEquals($expected, $messageContent['text']);
$this->assertEquals(rtrim(str_replace("\n", ' ', $expected)), $messageContent['subject']);
}

/**
* Test the contact tokens rendered by the token processor.
*
* This test will be obsolete once the renderMessageTemplate
* function uses the token processor - at that point the test above
* will be testing the same thing.
*
* @throws \API_Exception
* @throws \CRM_Core_Exception
* @throws \CiviCRM_API3_Exception
*/
public function testContactTokensRenderedByTokenProcessor(): void {
$this->createCustomGroupWithFieldsOfAllTypes([]);
$tokenData = $this->getAllContactTokens();
$address = $this->setupContactFromTokeData($tokenData);
$tokenString = '';
foreach (array_keys($tokenData) as $key) {
$tokenString .= "{$key}:{contact.{$key}}\n";
}
$tokenProcessor = new TokenProcessor(\Civi::dispatcher(), []);
$tokenProcessor->addMessage('html', $tokenString, 'text/html');
$tokenProcessor->addRow(['contactId' => $tokenData['contact_id']]);
$tokenProcessor->evaluate();
$rendered = '';
foreach ($tokenProcessor->getRows() as $row) {
$rendered = (string) $row->render('html');
}
$expected = $this->getExpectedContactOutput($address['id'], $tokenData, $rendered);
// @todo - this works better in token processor than in CRM_Core_Token.
// once synced we can fix $this->getExpectedContactOutput to return the right thing.
$expected = str_replace("preferred_communication_method:\n", "preferred_communication_method:Phone\n", $expected);
$this->assertEquals($expected, $rendered);
}

/**
* Gets the values needed to render domain tokens.
*
Expand Down Expand Up @@ -402,4 +347,130 @@ public function getAllContactTokens(): array {
];
}

/**
* @param array $tokenData
*
* @return array|int
* @throws \CRM_Core_Exception
*/
protected function setupContactFromTokeData(array $tokenData) {
$this->callAPISuccess('Contact', 'create', $tokenData);
$address = $this->callAPISuccess('Address', 'create', array_merge($tokenData, ['is_primary' => TRUE]));
$this->callAPISuccess('Phone', 'create', array_merge($tokenData, ['is_primary' => TRUE]));
$this->callAPISuccess('Email', 'create', array_merge($tokenData, ['is_primary' => TRUE]));
$this->callAPISuccess('Website', 'create', array_merge($tokenData, ['is_primary' => TRUE]));
$this->callAPISuccess('Im', 'create', [
'is_primary' => TRUE,
'name' => $tokenData['im'],
'provider_id' => $tokenData['im_provider'],
'contact_id' => $tokenData['contact_id'],
]);
$this->callAPISuccess('OpenID', 'create', array_merge($tokenData, [
'is_primary' => TRUE,
'contact_id' => $tokenData['contact_id'],
'openid' => $tokenData['openid'],
]));
return $address;
}

/**
* Get the expected rendered string.
*
* @param int $id
* @param array $tokenData
* @param string $actualOutput
*
* @return string
* @throws \API_Exception
*/
protected function getExpectedContactOutput($id, array $tokenData, string $actualOutput): string {
$checksum = substr($actualOutput, (strpos($actualOutput, 'cs=') + 3), 47);
$contact = Contact::get(FALSE)->addWhere('id', '=', $tokenData['contact_id'])->setSelect(['modified_date', 'employer_id'])->execute()->first();
$expected = 'contact_type:Individual
do_not_email:1
do_not_phone:
do_not_mail:1
do_not_sms:1
do_not_trade:1
is_opt_out:1
external_identifier:blah
sort_name:Smith, Robert
display_name:Mr. Robert Smith II
nick_name:Bob
image_URL:https://example.com
preferred_communication_method:
preferred_language:fr_CA
preferred_mail_format:Both
hash:xyz
contact_source:Contact Source
first_name:Robert
middle_name:Frank
last_name:Smith
individual_prefix:Mr.
individual_suffix:II
formal_title:Dogsbody
communication_style:Formal
job_title:Busy person
gender:Female
birth_date:December 31st, 1998
current_employer_id:' . $contact['employer_id'] . '
contact_is_deleted:
created_date:January 1st, 2020 12:00 AM
modified_date:' . CRM_Utils_Date::customFormat($contact['modified_date']) . '
addressee:Mr. Robert Frank Smith II
email_greeting:Dear Robert
postal_greeting:Dear Robert
current_employer:Unit Test Organization
location_type:Home
address_id:' . $id . '
street_address:Street Address
street_number:123
street_number_suffix:S
street_name:Main St
street_unit:45B
supplemental_address_1:Round the corner
supplemental_address_2:Up the road
supplemental_address_3:By the big tree
city:New York
postal_code_suffix:4578
postal_code:90210
geo_code_1:48.858093
geo_code_2:2.294694
manual_geo_code:1
address_name:The white house
master_id:' . $tokenData['master_id'] . '
county:
state_province:TX
country:United States
phone:123-456
phone_ext:77
phone_type_id:
phone_type:Mobile
email:anthony_anderson@civicrm.org
on_hold:
signature_text:Yours sincerely
signature_html:&lt;p&gt;Yours&lt;/p&gt;
im_provider:1
im:IM Screen Name
openid:OpenID
world_region:America South, Central, North and Caribbean
url:http://civicrm.org
custom_1:Bobsled
custom_2:Red
custom_3:01/20/2021 12:00AM
custom_4:999
custom_5:<a href="http://civicrm.org" target="_blank">http://civicrm.org</a>
custom_7:New Zealand
custom_8:France, Canada
custom_9:Mr. Spider Man II
custom_10:Queensland
custom_11:Victoria, New South Wales
custom_12:Yes
custom_13:Purple
checksum:cs=' . $checksum . '
contact_id:' . $tokenData['contact_id'] . '
';
return $expected;
}

}