Skip to content

Commit

Permalink
Merge pull request #1012 from compucorp/FOSFA-283-customfield-tokens
Browse files Browse the repository at this point in the history
FOSFA-283: Resolve issue with case tokens not resolved via webform
  • Loading branch information
olayiwola-compucorp authored May 30, 2024
2 parents 2a959b3 + 0a2a72a commit ccc6848
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 199 deletions.
135 changes: 48 additions & 87 deletions CRM/Civicase/Hook/Tokens/AddCaseCustomFieldsTokenValues.php
Original file line number Diff line number Diff line change
@@ -1,113 +1,74 @@
<?php

use CRM_Civicase_Hook_Tokens_Helper_CaseTokenValues as CaseTokenValuesHelper;
use Civi\Token\Event\TokenValueEvent;

/**
* Class for adding case custom fields token values.
*/
class CRM_Civicase_Hook_Tokens_AddCaseCustomFieldsTokenValues {

/**
* Case Id.
* Evaluate custom fields and case role tokens.
*
* @var int|null
* Case Id.
* @param \Civi\Token\Event\TokenValueEvent $e
* TokenValue Event.
*/
private $caseId;
public static function evaluateCaseCustomFieldsTokens(TokenValueEvent $e) {
$context = $e->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;
}

}
21 changes: 20 additions & 1 deletion CRM/Civicase/Hook/Tokens/AddCaseTokenCategory.php
Original file line number Diff line number Diff line change
@@ -1,10 +1,26 @@
<?php

use CRM_Civicase_Hook_Helper_CaseTypeCategory as CaseTypeCategoryHelper;

/**
* Add Case token category class.
*/
class CRM_Civicase_Hook_Tokens_AddCaseTokenCategory {

const TOKEN_KEY = 'case_cf';

/**
* CRM_Civicase_Hook_Tokens_AddCaseTokenCategory constructor.
*
* @param CRM_Civicase_Service_CaseCustomFieldsProvider $caseCustomFieldsService
* Service for fetching contact custom fields.
*/
public function __construct(
private CRM_Civicase_Service_CaseCustomFieldsProvider $caseCustomFieldsService,
) {
$this->caseCustomFieldsService = $caseCustomFieldsService;
}

/**
* Sets Case Token Category.
*
Expand Down Expand Up @@ -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)));
}
}

/**
Expand Down
48 changes: 48 additions & 0 deletions CRM/Civicase/Service/CaseCustomFieldsProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

/**
* Provides contacts custom fields.
*/
class CRM_Civicase_Service_CaseCustomFieldsProvider {

/**
* Provides contacts custom fields.
*
* @return array
* List of custom fields that extends contacts.
*/
public function get() {
$fields = [];
$customFields = $this->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;
}

}
13 changes: 10 additions & 3 deletions civicase.php
Original file line number Diff line number Diff line change
Expand Up @@ -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',
]
);
}

/**
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down

This file was deleted.

0 comments on commit ccc6848

Please sign in to comment.