From c751c5f171d5ef24fe159e9be11b379d92dc72a5 Mon Sep 17 00:00:00 2001 From: Eileen McNaughton Date: Wed, 30 Aug 2023 12:50:17 +1200 Subject: [PATCH] Add contribution page tokens to contribution tokens --- CRM/Contact/Tokens.php | 4 ++ CRM/Contribute/DAO/ContributionPage.php | 7 +++- CRM/Contribute/Tokens.php | 21 +++++++++- CRM/Core/EntityTokens.php | 14 +++++++ CRM/Core/I18n/SchemaStructure.php | 4 +- .../CRM/Activity/Form/Task/PDFTest.php | 2 - .../Contribute/ActionMapping/ByTypeTest.php | 19 ++++++---- .../CRM/Utils/TokenConsistencyTest.php | 38 +++++++++++++++++++ xml/schema/Contribute/ContributionPage.xml | 5 +++ 9 files changed, 102 insertions(+), 12 deletions(-) diff --git a/CRM/Contact/Tokens.php b/CRM/Contact/Tokens.php index 97d87b2d93b1..232f8661a138 100644 --- a/CRM/Contact/Tokens.php +++ b/CRM/Contact/Tokens.php @@ -641,6 +641,7 @@ protected function getBespokeTokens(): array { 'type' => 'calculated', 'options' => NULL, 'data_type' => 'String', + 'input_type' => NULL, 'audience' => 'user', ], 'employer_id.display_name' => [ @@ -659,6 +660,7 @@ protected function getBespokeTokens(): array { 'api_v3' => 'world_region', 'options' => NULL, 'data_type' => 'String', + 'input_type' => 'Text', 'advertised_name' => 'world_region', 'audience' => 'user', ], @@ -669,6 +671,7 @@ protected function getBespokeTokens(): array { 'type' => 'Field', 'options' => NULL, 'data_type' => 'String', + 'input_type' => 'Text', 'audience' => 'sysadmin', ], // this gets forced out if we specify individual fields @@ -678,6 +681,7 @@ protected function getBespokeTokens(): array { 'type' => 'Field', 'options' => NULL, 'data_type' => 'String', + 'input_type' => 'Text', 'audience' => 'sysadmin', ], ]; diff --git a/CRM/Contribute/DAO/ContributionPage.php b/CRM/Contribute/DAO/ContributionPage.php index 6cc64df78af1..7ce4c8cc8511 100644 --- a/CRM/Contribute/DAO/ContributionPage.php +++ b/CRM/Contribute/DAO/ContributionPage.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Contribute/ContributionPage.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:765f5bf08dbacf12c3d292837d718c39) + * (GenCodeChecksum:84e81e764d43c0d0116ae54d90a18a84) */ /** @@ -904,6 +904,8 @@ public static function &fields() { 'type' => CRM_Utils_Type::T_TEXT, 'title' => ts('Pay Later Receipt'), 'description' => ts('The receipt sent to the user instead of the normal receipt text'), + 'rows' => 8, + 'cols' => 60, 'usage' => [ 'import' => FALSE, 'export' => FALSE, @@ -915,6 +917,9 @@ public static function &fields() { 'entity' => 'ContributionPage', 'bao' => 'CRM_Contribute_BAO_ContributionPage', 'localizable' => 1, + 'html' => [ + 'type' => 'RichTextEditor', + ], 'add' => '2.0', ], 'is_partial_payment' => [ diff --git a/CRM/Contribute/Tokens.php b/CRM/Contribute/Tokens.php index 39f87b123100..5af208998fcd 100644 --- a/CRM/Contribute/Tokens.php +++ b/CRM/Contribute/Tokens.php @@ -10,6 +10,7 @@ +--------------------------------------------------------------------+ */ +use Civi\Api4\ContributionPage; use Civi\Api4\ContributionRecur; /** @@ -55,9 +56,26 @@ public function getCurrencyFieldName() { */ protected function getRelatedTokens(): array { $tokens = []; - if (!in_array('ContributionRecur', array_keys(\Civi::service('action_object_provider')->getEntities()))) { + // Check to make sure CiviContribute is enabled, just in case it remains registered. Eventually this will be moved to the CiviContribute extension + // and this check can hopefully be removed (as long as caching on enable / disable doesn't explode our brains and / or crash the site). + if (!array_key_exists('Contribution', \Civi::service('action_object_provider')->getEntities())) { return $tokens; } + // Ideally we would derive this from 'usage' - but it looks like adding the usage data + // was quite a bit of work & didn't leave the energy to implement - esp expose for + // where clauses (also, it feels like 'hidden+token' would be a good usage. + $tokenList = ['frontend_title', 'pay_later_text', 'pay_later_receipt', 'is_share', 'receipt_text']; + $contributionPageTokens = ContributionPage::getFields(FALSE)->addWhere('name', 'IN', $tokenList)->execute(); + foreach ($contributionPageTokens as $contributionPageToken) { + $tokens['contribution_page_id.' . $contributionPageToken['name']] = [ + 'title' => $contributionPageToken['title'], + 'name' => 'contribution_page_id.' . $contributionPageToken['name'], + 'type' => 'mapped', + 'data_type' => $contributionPageToken['data_type'], + 'input_type' => $contributionPageToken['input_type'], + 'audience' => $contributionPageToken['name'] === 'is_share' ? 'hidden' : 'user', + ]; + } $hiddenTokens = ['modified_date', 'create_date', 'trxn_id', 'invoice_id', 'is_test', 'payment_token_id', 'payment_processor_id', 'payment_instrument_id', 'cycle_day', 'installments', 'processor_id', 'next_sched_contribution_date', 'failure_count', 'failure_retry_date', 'auto_renew', 'is_email_receipt', 'contribution_status_id']; $contributionRecurFields = ContributionRecur::getFields(FALSE)->setLoadOptions(TRUE)->execute(); foreach ($contributionRecurFields as $contributionRecurField) { @@ -67,6 +85,7 @@ protected function getRelatedTokens(): array { 'type' => 'mapped', 'options' => $contributionRecurField['options'] ?? NULL, 'data_type' => $contributionRecurField['data_type'], + 'input_type' => $contributionRecurField['input_type'], 'audience' => in_array($contributionRecurField['name'], $hiddenTokens) ? 'hidden' : 'user', ]; } diff --git a/CRM/Core/EntityTokens.php b/CRM/Core/EntityTokens.php index a5e80be22394..1723ded63b66 100644 --- a/CRM/Core/EntityTokens.php +++ b/CRM/Core/EntityTokens.php @@ -138,9 +138,23 @@ public function evaluateToken(TokenRow $row, $entity, $field, $prefetch = NULL) Civi::log()->info('invalid date token'); } } + if ($this->isHTMLTextField($field)) { + return $row->format('text/html')->tokens($entity, $field, (string) $fieldValue); + } $row->format('text/plain')->tokens($entity, $field, (string) $fieldValue); } + /** + * Is the text stored in html format. + * + * @param string $fieldName + * + * @return bool + */ + public function isHTMLTextField(string $fieldName): bool { + return ($this->getMetadataForField($fieldName)['input_type'] ?? NULL) === 'RichTextEditor'; + } + /** * Metadata about the entity fields. * diff --git a/CRM/Core/I18n/SchemaStructure.php b/CRM/Core/I18n/SchemaStructure.php index cd0b0939f5ea..3dc519a76770 100644 --- a/CRM/Core/I18n/SchemaStructure.php +++ b/CRM/Core/I18n/SchemaStructure.php @@ -447,7 +447,9 @@ public static function &widgets() { 'type' => "Text", ], 'pay_later_receipt' => [ - 'type' => "Text", + 'type' => "RichTextEditor", + 'rows' => "8", + 'cols' => "60", ], 'initial_amount_label' => [ 'label' => "Initial Amount Label", diff --git a/tests/phpunit/CRM/Activity/Form/Task/PDFTest.php b/tests/phpunit/CRM/Activity/Form/Task/PDFTest.php index 6397d46b929d..df6b24999a8d 100644 --- a/tests/phpunit/CRM/Activity/Form/Task/PDFTest.php +++ b/tests/phpunit/CRM/Activity/Form/Task/PDFTest.php @@ -18,8 +18,6 @@ public function setUp(): void { /** * Test create a document with basic tokens. - * - * @throws \CRM_Core_Exception */ public function testCreateDocumentBasicTokens(): void { CRM_Core_BAO_ConfigSetting::enableComponent('CiviCase'); diff --git a/tests/phpunit/CRM/Contribute/ActionMapping/ByTypeTest.php b/tests/phpunit/CRM/Contribute/ActionMapping/ByTypeTest.php index f33292c618b3..6f1cd8e3c094 100644 --- a/tests/phpunit/CRM/Contribute/ActionMapping/ByTypeTest.php +++ b/tests/phpunit/CRM/Contribute/ActionMapping/ByTypeTest.php @@ -9,6 +9,7 @@ +--------------------------------------------------------------------+ */ +use Civi\ActionSchedule\AbstractMappingTest; use Civi\Api4\Contribution; use Civi\Token\TokenProcessor; @@ -23,7 +24,7 @@ * @see \Civi\ActionSchedule\AbstractMappingTest * @group headless */ -class CRM_Contribute_ActionMapping_ByTypeTest extends \Civi\ActionSchedule\AbstractMappingTest { +class CRM_Contribute_ActionMapping_ByTypeTest extends AbstractMappingTest { /** * Generate a list of test cases, where each is a distinct combination of @@ -38,7 +39,7 @@ class CRM_Contribute_ActionMapping_ByTypeTest extends \Civi\ActionSchedule\Abstr * - recipients: array of emails * - subject: regex */ - public function createTestCases() { + public function createTestCases(): array { $cs = []; $cs[] = [ @@ -211,7 +212,7 @@ public function addAliceDues(): void { /** * Create a contribution record for Bob with type "Donation". */ - public function addBobDonation() { + public function addBobDonation(): void { $this->callAPISuccess('Contribution', 'create', [ 'contact_id' => $this->contacts['bob']['id'], 'receive_date' => date('Ymd', strtotime($this->targetDate)), @@ -228,7 +229,7 @@ public function addBobDonation() { /** * Schedule message delivery for contributions of type "Member Dues". */ - public function scheduleForDues() { + public function scheduleForDues(): void { $this->schedule->mapping_id = 'contribtype'; $this->schedule->start_action_date = 'receive_date'; $this->schedule->entity_value = CRM_Utils_Array::implodePadded([1]); @@ -238,7 +239,7 @@ public function scheduleForDues() { /** * Schedule message delivery for contributions of type "Donation". */ - public function scheduleForDonation() { + public function scheduleForDonation(): void { $this->schedule->mapping_id = 'contribtype'; $this->schedule->start_action_date = 'receive_date'; $this->schedule->entity_value = CRM_Utils_Array::implodePadded([2]); @@ -248,7 +249,7 @@ public function scheduleForDonation() { /** * Schedule message delivery for any contribution, regardless of type. */ - public function scheduleForAny() { + public function scheduleForAny(): void { $this->schedule->mapping_id = 'contribtype'; $this->schedule->start_action_date = 'receive_date'; $this->schedule->entity_value = CRM_Utils_Array::implodePadded(NULL); @@ -258,7 +259,7 @@ public function scheduleForAny() { /** * Schedule message delivery to the 'soft credit' assignee. */ - public function scheduleForSoftCreditor() { + public function scheduleForSoftCreditor(): void { $this->schedule->mapping_id = 'contribtype'; $this->schedule->start_action_date = 'receive_date'; $this->schedule->entity_value = CRM_Utils_Array::implodePadded(NULL); @@ -438,6 +439,10 @@ public function testTokenRendering(): void { 'contribution_recur_id.end_date' => 'Recurring Contribution End Date', 'contribution_recur_id.financial_type_id' => 'Financial Type ID', 'contribution_recur_id.campaign_id' => 'Campaign ID', + 'contribution_page_id.frontend_title' => 'Public Title', + 'contribution_page_id.pay_later_text' => 'Pay Later Text', + 'contribution_page_id.pay_later_receipt' => 'Pay Later Receipt', + 'contribution_page_id.receipt_text' => 'Receipt Text', ], $comparison); } diff --git a/tests/phpunit/CRM/Utils/TokenConsistencyTest.php b/tests/phpunit/CRM/Utils/TokenConsistencyTest.php index d33bb0a979fe..98cefe576cd7 100644 --- a/tests/phpunit/CRM/Utils/TokenConsistencyTest.php +++ b/tests/phpunit/CRM/Utils/TokenConsistencyTest.php @@ -200,6 +200,44 @@ public function testContributionRecurTokenConsistency(): void { $this->assertEquals($this->getExpectedContributionRecurTokenOutPut(), $tokenProcessor->getRow(0)->render('html')); } + /** + * Test contribution tokens pulled from the contribution page. + */ + public function testContributionPageTokens(): void { + $tokenValues = [ + 'frontend_title' => 'public title', + 'pay_later_text' => 'pay later text', + 'pay_later_receipt' => '

first line

second line

', + 'is_share' => TRUE, + 'receipt_text' => "Text in\n non html", + ]; + $tokens = []; + foreach (array_keys($tokenValues) as $token) { + $tokens[] = '{contribution.contribution_page_id.' . $token . '}'; + } + $tokenString = trim($this->getTokenString($tokens)); + $this->contributionPageCreate($tokenValues); + + $tokenProcessor = $this->getTokenProcessor(['schema' => ['contributionId']]); + $tokenProcessor->addMessage('text', $tokenString, 'text/plain'); + $tokenProcessor->addMessage('html', $tokenString, 'text/html'); + $tokenProcessor->addRow(['contributionId' => $this->contributionCreate(['contribution_page_id' => $this->ids['ContributionPage']['test'], 'contact_id' => $this->individualCreate()])]); + $tokenProcessor->evaluate(); + $this->assertEquals('contribution.contribution_page_id.frontend_title :public title +contribution.contribution_page_id.pay_later_text :pay later text +contribution.contribution_page_id.pay_later_receipt :

first line

second line

+contribution.contribution_page_id.is_share :1 +contribution.contribution_page_id.receipt_text :Text in + non html', $tokenProcessor->getRow(0)->render('html')); + $this->assertEquals('contribution.contribution_page_id.frontend_title :public title +contribution.contribution_page_id.pay_later_text :pay later text +contribution.contribution_page_id.pay_later_receipt :first linesecond line +contribution.contribution_page_id.is_share :1 +contribution.contribution_page_id.receipt_text :Text in + non html', $tokenProcessor->getRow(0)->render('text')); + + } + /** * Test that contribution recur tokens are consistently rendered. */ diff --git a/xml/schema/Contribute/ContributionPage.xml b/xml/schema/Contribute/ContributionPage.xml index c0532859bf6e..c0e030b048f2 100644 --- a/xml/schema/Contribute/ContributionPage.xml +++ b/xml/schema/Contribute/ContributionPage.xml @@ -222,6 +222,11 @@ Pay Later Receipt text true + + RichTextEditor + 8 + 60 + The receipt sent to the user instead of the normal receipt text 2.0