diff --git a/CRM/Civicase/Hook/Tokens/AddCaseCustomFieldsTokenValues.php b/CRM/Civicase/Hook/Tokens/AddCaseCustomFieldsTokenValues.php index 6056fbf37..57c2c563e 100644 --- a/CRM/Civicase/Hook/Tokens/AddCaseCustomFieldsTokenValues.php +++ b/CRM/Civicase/Hook/Tokens/AddCaseCustomFieldsTokenValues.php @@ -1,6 +1,6 @@ getTokenProcessor()->context; + $caseTokenValuesHelper = new CRM_Civicase_Hook_Tokens_Helper_CaseTokenValues(); + $customTokens = $e->getTokenProcessor()->getMessageTokens()['case_cf'] ?? []; + $caseRoleTokens = $e->getTokenProcessor()->getMessageTokens()['case_roles'] ?? []; + $caseRoleValues = []; - /** - * Case Token Values helper. - * - * @var \CRM_Civicase_Hook_Tokens_Helper_CaseTokenValues - * Case Custom field helper. - */ - private $caseTokenValuesHelper; + if (array_key_exists('schema', $context) && in_array('caseId', $context['schema'])) { + foreach ($e->getRows() as $row) { + if (!empty($row->context['caseId'])) { + $caseId = $row->context['caseId']; + $contactId = $row->context['contactId']; + $customValues = $caseTokenValuesHelper->getCustomFieldValues($caseId, $customTokens); - /** - * Sets required class properties. - * - * @param \CRM_Civicase_Hook_Tokens_Helper_CaseTokenValues $caseTokenValuesHelper - * Case token values helper. - */ - public function __construct(CaseTokenValuesHelper $caseTokenValuesHelper) { - $this->caseTokenValuesHelper = $caseTokenValuesHelper; - } + // Replace custom field tokens with their values. + foreach ($customTokens as $token) { + $value = $caseTokenValuesHelper->getTokenReplacementValue($token, $customValues); + $row->format('text/plain')->tokens('case_cf', $token, $value); + $row->format('text/html')->tokens('case_cf', $token, $value); + } - /** - * Sets case custom field token values. - * - * @param array $values - * Token values. - * @param array $cids - * Contact ids. - * @param int $job - * Job id. - * @param array $tokens - * Available tokens. - * @param string $context - * Context name. - */ - public function run(array &$values, array $cids, $job, array $tokens, $context) { - $this->caseId = $this->caseTokenValuesHelper->getCaseId($values); - if (!$this->shouldRun($tokens)) { - return; - } + // If the token is being resolved through a webform-triggered + // activity, the case role token extension might fail + // to resolve the case role tokens + // due to its inability to locate the case ID. + // To address this, we manually reevaluate the token value here + // by extracting the case ID from the token event. + if (!self::isWebform()) { + continue; + } - $this->setCaseCustomFieldTokenValues($values, $cids, $tokens); - } + if (function_exists('casetokens_civicrm_tokenvalues') && !empty($caseRoleTokens)) { + Civi::$statics['casetokens']['case_id'] = $row->context['caseId']; + casetokens_civicrm_tokenvalues($caseRoleValues, [$contactId], NULL, ['case_roles' => $caseRoleTokens]); + } - /** - * Sets case custom field values. - * - * Normally we would not do this but there is an issue of a mysql max table - * join error on a site with a lot of case custom fields enabled. - * To fix that this class CRM_Civicase_Event_Listener_CaseCustomFields was - * created to prevent the loading of custom fields for cases without the - * specific custom fields being passed. However, when replacing tokens for - * sent emails, Civi expects the Case.get call to also return the case custom - * fields with the other case parameters when no return value is specified. - * - * This function replaces the case custom fields token values as civi is - * not able to do the values replacement because of the custom change we made - * to avoid the 61 max table join error. - * - * @param array $values - * Token values. - * @param array $cids - * Contact ids. - * @param array $tokens - * Available tokens. - */ - private function setCaseCustomFieldTokenValues(array &$values, array $cids, array $tokens) { - $customValues = $this->caseTokenValuesHelper->getCustomFieldValues($this->caseId, $tokens['case_cf']); - if (empty($customValues)) { - return; - } - unset($customValues['id']); + // Replace case role tokens with their values. + if (!empty($caseRoleValues)) { + foreach ($caseRoleTokens as $token) { + $row->format('text/plain')->tokens('case_roles', $token, $caseRoleValues[$contactId]['case_roles.' . $token] ?? ''); + $row->format('text/html')->tokens('case_roles', $token, $caseRoleValues[$contactId]['case_roles.' . $token] ?? ''); + } - // We need to prepend the token category 'case_cf' to the custom field key - // so it can be evaluated for the case_cf category when token is replaced. - $customValuesNew = []; - foreach ($customValues as $key => $customValue) { - $customValuesNew['case_cf.' . $key] = $this->caseTokenValuesHelper->getTokenReplacementValue($key, $customValues); - } + } + } + } - foreach ($cids as $cid) { - $values[$cid] = array_merge($values[$cid], $customValuesNew); } + } /** - * Decides whether the hook should run or not. - * - * @param array $tokens - * Available tokens. - * - * @return bool - * Whether this hook should run or not. + * Detects the token activity is triggered by webform. */ - private function shouldRun(array $tokens) { - return !empty($this->caseId) && !empty($tokens['case_cf']); + private static function isWebform() { + return isset($_POST['form_id']) && stripos($_POST['form_id'], 'webform_client_form_') !== FALSE; } } diff --git a/CRM/Civicase/Hook/Tokens/AddCaseTokenCategory.php b/CRM/Civicase/Hook/Tokens/AddCaseTokenCategory.php index e746edc25..c00087b5a 100644 --- a/CRM/Civicase/Hook/Tokens/AddCaseTokenCategory.php +++ b/CRM/Civicase/Hook/Tokens/AddCaseTokenCategory.php @@ -1,10 +1,26 @@ caseCustomFieldsService = $caseCustomFieldsService; + } + /** * Sets Case Token Category. * @@ -40,7 +56,10 @@ private function setCaseTokenCategory(array &$tokens) { return $tokens['case_cf'] = []; } - $tokens['case_cf'][''] = ''; + foreach ($this->caseCustomFieldsService->get() as $key => $field) { + $tokens[self::TOKEN_KEY]['case_cf.' . $key] = + CaseTypeCategoryHelper::translate(ucwords(str_replace("_", " ", $field))); + } } /** diff --git a/CRM/Civicase/Service/CaseCustomFieldsProvider.php b/CRM/Civicase/Service/CaseCustomFieldsProvider.php new file mode 100644 index 000000000..0ee2c26d3 --- /dev/null +++ b/CRM/Civicase/Service/CaseCustomFieldsProvider.php @@ -0,0 +1,48 @@ +getCustomFields(); + if (!empty($customFields['values'])) { + foreach ($customFields['values'] as $id => $item) { + $fields['custom_' . $id] = $item['name']; + } + } + + return $fields; + } + + /** + * Provides contacts custom fields. + * + * @return array + * List of custom fields that extends contacts. + */ + public function getCustomFields() { + $customFields = []; + try { + $customFields = civicrm_api3('CustomField', 'get', [ + 'custom_group_id.extends' => [ + 'IN' => ['Case'], + ], + 'options' => ['limit' => 0], + ]); + } + catch (Throwable $ex) { + } + + return $customFields; + } + +} diff --git a/civicase.php b/civicase.php index 44e527c15..cbd9a0044 100644 --- a/civicase.php +++ b/civicase.php @@ -89,6 +89,14 @@ function civicase_civicrm_config(&$config) { 'civi.token.eval', ['CRM_Civicase_Hook_Tokens_SalesOrderTokens', 'evaluateSalesOrderTokens'] ); + + Civi::dispatcher()->addListener( + 'civi.token.eval', + [ + 'CRM_Civicase_Hook_Tokens_AddCaseCustomFieldsTokenValues', + 'evaluateCaseCustomFieldsTokens', + ] + ); } /** @@ -371,9 +379,10 @@ function civicase_civicrm_alterAPIPermissions($entity, $action, &$params, &$perm function civicase_civicrm_tokens(&$tokens) { $contactFieldsService = new CRM_Civicase_Service_ContactFieldsProvider(); $contactCustomFieldsService = new CRM_Civicase_Service_ContactCustomFieldsProvider(); + $caseCustomFieldsService = new CRM_Civicase_Service_CaseCustomFieldsProvider(); $hooks = [ new CRM_Civicase_Hook_Tokens_AddContactTokens($contactFieldsService, $contactCustomFieldsService), - new CRM_Civicase_Hook_Tokens_AddCaseTokenCategory(), + new CRM_Civicase_Hook_Tokens_AddCaseTokenCategory($caseCustomFieldsService), ]; foreach ($hooks as &$hook) { $hook->run($tokens); @@ -386,10 +395,8 @@ function civicase_civicrm_tokens(&$tokens) { function civicase_civicrm_tokenValues(&$values, $cids, $job = NULL, $tokens = [], $context = NULL) { $contactFieldsService = new CRM_Civicase_Service_ContactFieldsProvider(); $contactCustomFieldsService = new CRM_Civicase_Service_ContactCustomFieldsProvider(); - $caseTokenValuesHelper = new CRM_Civicase_Hook_Tokens_Helper_CaseTokenValues(); $hooks = [ new CRM_Civicase_Hook_Tokens_AddContactTokensValues($contactFieldsService, $contactCustomFieldsService), - new CRM_Civicase_Hook_Tokens_AddCaseCustomFieldsTokenValues($caseTokenValuesHelper), ]; foreach ($hooks as &$hook) { $hook->run($values, $cids, $job, $tokens, $context); diff --git a/tests/phpunit/CRM/Civicase/Hook/Tokens/AddCaseCustomFieldsTokenValuesTest.php b/tests/phpunit/CRM/Civicase/Hook/Tokens/AddCaseCustomFieldsTokenValuesTest.php deleted file mode 100644 index faff61dd0..000000000 --- a/tests/phpunit/CRM/Civicase/Hook/Tokens/AddCaseCustomFieldsTokenValuesTest.php +++ /dev/null @@ -1,108 +0,0 @@ - 'This is it', - 'custom_99' => 123, - 'custom_50' => 'data', - ]; - $customValuesNew = []; - foreach ($caseCustomFieldValues as $key => $customValue) { - $customValuesNew['case_cf.' . $key] = $customValue; - } - - $caseId = 1; - $caseTokenValuesHelper = $this->getCaseTokenValuesHelperHelperMock($caseCustomFieldValues, $caseId); - $addCaseCustomFieldsTokenValueHook = new AddCaseCustomFieldsTokenValueHook($caseTokenValuesHelper); - - $cid = 1; - $values[$cid] = ['contact_id' => 5, 'display_name' => 'Sample']; - $tokens['case_cf'] = array_merge( - array_keys($caseCustomFieldValues), - ['subject', 'name'] - ); - $expectedResult = array_merge($values[$cid], $customValuesNew); - $addCaseCustomFieldsTokenValueHook->run($values, [$cid], 0, $tokens, NULL); - - $this->assertEquals($expectedResult, $values[$cid]); - } - - /** - * Test the values array is not tampered with. - */ - public function testValuesNotChangedWhenCaseIdNotFound() { - $caseCustomFieldValues = [ - 'custom_10' => 'This is it', - ]; - - $caseCustomFieldValuesHelper = $this->getCaseTokenValuesHelperHelperMock($caseCustomFieldValues, NULL); - $addCaseCustomFieldsTokenValueHook = new AddCaseCustomFieldsTokenValueHook($caseCustomFieldValuesHelper); - - $cid = 1; - $expected = ['contact_id' => 5, 'display_name' => 'Sample']; - $values[$cid] = $expected; - $tokens['case'] = array_merge( - array_keys($caseCustomFieldValues), - ['subject', 'name'] - ); - $addCaseCustomFieldsTokenValueHook->run($values, [$cid], 0, $tokens, NULL); - - $this->assertEquals($expected, $values[$cid]); - } - - /** - * Get mock object for case token values helper. - * - * @param array $customFieldValues - * Custom field values to return. - * @param int $caseId - * Case ID. - * - * @return \PHPUnit_Framework_MockObject_MockObject - * Mock object. - */ - private function getCaseTokenValuesHelperHelperMock(array $customFieldValues, $caseId) { - $caseCustomFieldValuesHelper = $this->getMockBuilder(CaseTokenValuesHelper::class) - ->setMethods( - [ - 'getCustomFieldValues', - 'getCaseId', - 'getTokenReplacementValue', - ] - ) - ->getMock(); - $caseCustomFieldValuesHelper->method('getCustomFieldValues')->willReturn($customFieldValues); - $caseCustomFieldValuesHelper->method('getCaseId')->willReturn($caseId); - $returnValueMap = []; - - foreach ($customFieldValues as $customField => $customFieldValue) { - $returnValueMap[] = [ - $customField, - $customFieldValues, - $customFieldValues[$customField], - ]; - } - - $caseCustomFieldValuesHelper->method('getTokenReplacementValue')->will( - $this->returnValueMap($returnValueMap) - ); - - return $caseCustomFieldValuesHelper; - } - -}