From 29f2b53e96810cc462c6e78e87fa1154fcf60243 Mon Sep 17 00:00:00 2001 From: Eileen McNaughton Date: Fri, 6 Aug 2021 13:55:04 +1200 Subject: [PATCH] Switch to using apiv4 for metadata --- CRM/Contribute/Tokens.php | 71 +++++++-------------- CRM/Core/EntityTokens.php | 128 +++++++++++++++++++++++++++++++++++--- 2 files changed, 140 insertions(+), 59 deletions(-) diff --git a/CRM/Contribute/Tokens.php b/CRM/Contribute/Tokens.php index e37b637f5b..cc40775a23 100644 --- a/CRM/Contribute/Tokens.php +++ b/CRM/Contribute/Tokens.php @@ -32,10 +32,22 @@ class CRM_Contribute_Tokens extends CRM_Core_EntityTokens { } /** - * Get the relevant bao name. + * @return string + */ + protected function getEntityAlias(): string { + return 'contrib_'; + } + + /** + * Get the entity name for api v4 calls. + * + * In practice this IS just ucfirst($this->GetEntityName) + * but declaring it seems more legible. + * + * @return string */ - public function getBAOName(): string { - return CRM_Core_DAO_AllCoreTables::getFullName(ucfirst($this->getEntityName())); + protected function getApiEntityName(): string { + return 'Contribution'; } /** @@ -91,24 +103,6 @@ class CRM_Contribute_Tokens extends CRM_Core_EntityTokens { return $return; } - /** - * Get pseudoTokens - it tokens that reflect the name or label of a pseudoconstant. - * - * @internal - this function will likely be made protected soon. - * - * @return array - */ - public function getPseudoTokens(): array { - $return = []; - foreach (array_keys($this->getBasicTokens()) as $fieldName) { - if (!empty($this->fieldMetadata[$fieldName]['pseudoconstant'])) { - $return[$fieldName . ':label'] = $this->fieldMetadata[$fieldName]['html']['label']; - $return[$fieldName . ':name'] = ts('Machine name') . ': ' . $this->fieldMetadata[$fieldName]['html']['label']; - } - } - return $return; - } - /** * Class constructor. */ @@ -145,11 +139,11 @@ class CRM_Contribute_Tokens extends CRM_Core_EntityTokens { $fields = $this->getFieldMetadata(); foreach ($this->getPassthruTokens() as $token) { - $e->query->select("e." . $fields[$token]['name'] . " AS contrib_{$token}"); + $e->query->select('e.' . $fields[$token]['name'] . ' AS ' . $this->getEntityAlias() . $token); } foreach (array_keys($this->getPseudoTokens()) as $token) { $split = explode(':', $token); - $e->query->select("e." . $fields[$split[0]]['name'] . " AS contrib_{$split[0]}"); + $e->query->select('e.' . $fields[$split[0]]['name'] . ' AS ' . $this->getEntityAlias() . $split[0]); } } @@ -159,9 +153,10 @@ class CRM_Contribute_Tokens extends CRM_Core_EntityTokens { */ public function evaluateToken(TokenRow $row, $entity, $field, $prefetch = NULL) { $actionSearchResult = $row->context['actionSearchResult']; - $fieldValue = $actionSearchResult->{"contrib_$field"} ?? NULL; + $aliasedField = $this->getEntityAlias() . $field; + $fieldValue = $actionSearchResult->{$aliasedField} ?? NULL; - if (array_key_exists($field, $this->getPseudoTokens())) { + if ($this->isPseudoField($field)) { $split = explode(':', $field); return $row->tokens($entity, $field, $this->getPseudoValue($split[0], $split[1], $actionSearchResult->{"contrib_$split[0]"} ?? NULL)); } @@ -172,34 +167,12 @@ class CRM_Contribute_Tokens extends CRM_Core_EntityTokens { if ($this->isDateField($field)) { return $row->format('text/plain')->tokens($entity, $field, \CRM_Utils_Date::customFormat($fieldValue)); } - if ($cfID = \CRM_Core_BAO_CustomField::getKeyID($field)) { - $row->customToken($entity, $cfID, $actionSearchResult->entity_id); + if ($this->isCustomField($field)) { + $row->customToken($entity, \CRM_Core_BAO_CustomField::getKeyID($field), $actionSearchResult->entity_id); } else { $row->format('text/plain')->tokens($entity, $field, (string) $fieldValue); } } - /** - * Get the value for the relevant pseudo field. - * - * @param string $realField e.g contribution_status_id - * @param string $pseudoKey e.g name - * @param int|string $fieldValue e.g 1 - * - * @return string - * Eg. 'Completed' in the example above. - * - * @internal function will likely be protected soon. - */ - public function getPseudoValue(string $realField, string $pseudoKey, $fieldValue): string { - if ($pseudoKey === 'name') { - $fieldValue = (string) CRM_Core_PseudoConstant::getName($this->getBAOName(), $realField, $fieldValue); - } - if ($pseudoKey === 'label') { - $fieldValue = (string) CRM_Core_PseudoConstant::getLabel($this->getBAOName(), $realField, $fieldValue); - } - return (string) $fieldValue; - } - } diff --git a/CRM/Core/EntityTokens.php b/CRM/Core/EntityTokens.php index 58ba7af0d5..37f49afa9c 100644 --- a/CRM/Core/EntityTokens.php +++ b/CRM/Core/EntityTokens.php @@ -33,6 +33,34 @@ class CRM_Core_EntityTokens extends AbstractTokenSubscriber { public function evaluateToken(TokenRow $row, $entity, $field, $prefetch = NULL) { } + /** + * Get the entity name for api v4 calls. + * + * @return string + */ + protected function getApiEntityName(): string { + return ''; + } + + /** + * Get the entity alias to use within queries. + * + * The default has a double underscore which should prevent any + * ambiguity with an existing table name. + * + * @return string + */ + protected function getEntityAlias(): string { + return $this->getApiEntityName() . '__'; + } + + /** + * Get the relevant bao name. + */ + public function getBAOName(): string { + return CRM_Core_DAO_AllCoreTables::getFullName($this->getApiEntityName()); + } + /** * Is the given field a date field. * @@ -41,7 +69,29 @@ class CRM_Core_EntityTokens extends AbstractTokenSubscriber { * @return bool */ public function isDateField(string $fieldName): bool { - return $this->getFieldMetadata()[$fieldName]['type'] === (\CRM_Utils_Type::T_DATE + \CRM_Utils_Type::T_TIME); + return $this->getFieldMetadata()[$fieldName]['data_type'] === 'Timestamp'; + } + + /** + * Is the given field a pseudo field. + * + * @param string $fieldName + * + * @return bool + */ + public function isPseudoField(string $fieldName): bool { + return strpos($fieldName, ':') !== FALSE; + } + + /** + * Is the given field a custom field. + * + * @param string $fieldName + * + * @return bool + */ + public function isCustomField(string $fieldName) : bool { + return (bool) \CRM_Core_BAO_CustomField::getKeyID($fieldName); } /** @@ -52,7 +102,7 @@ class CRM_Core_EntityTokens extends AbstractTokenSubscriber { * @return bool */ public function isMoneyField(string $fieldName): bool { - return $this->getFieldMetadata()[$fieldName]['type'] === (\CRM_Utils_Type::T_MONEY); + return $this->getFieldMetadata()[$fieldName]['data_type'] === 'Money'; } /** @@ -62,17 +112,75 @@ class CRM_Core_EntityTokens extends AbstractTokenSubscriber { */ protected function getFieldMetadata(): array { if (empty($this->fieldMetadata)) { - $baoName = $this->getBAOName(); - - $fields = (array) $baoName::fields(); - // re-index by real field name. I originally wanted to use apiv4 - // getfields - but it returns different stuff for 'type' and - // does not return 'pseudoconstant' as a key so for now... - foreach ($fields as $details) { - $this->fieldMetadata[$details['name']] = $details; + try { + // Tests fail without checkPermissions = FALSE + $this->fieldMetadata = (array) civicrm_api4($this->getApiEntityName(), 'getfields', ['checkPermissions' => FALSE], 'name'); + } + catch (API_Exception $e) { + $this->fieldMetadata = []; } } return $this->fieldMetadata; } + /** + * Get pseudoTokens - it tokens that reflect the name or label of a pseudoconstant. + * + * @internal - this function will likely be made protected soon. + * + * @return array + */ + public function getPseudoTokens(): array { + $return = []; + foreach (array_keys($this->getBasicTokens()) as $fieldName) { + if ($this->isAddPseudoTokens($fieldName)) { + $return[$fieldName . ':label'] = $this->fieldMetadata[$fieldName]['input_attrs']['label']; + $return[$fieldName . ':name'] = ts('Machine name') . ': ' . $this->fieldMetadata[$fieldName]['input_attrs']['label']; + } + } + return $return; + } + + /** + * Is this a field we should add pseudo-tokens to? + * + * Pseudo-tokens allow access to name and label fields - e.g + * + * {contribution.contribution_status_id:name} might resolve to 'Completed' + * + * @param string $fieldName + */ + public function isAddPseudoTokens($fieldName): bool { + if ($fieldName === 'currency') { + // 'currency' is manually added to the skip list as an anomaly. + // name & label aren't that suitable for 'currency' (symbol, which + // possibly maps to 'abbr' would be) and we can't gather that + // from the metadata as yet. + return FALSE; + } + return (bool) $this->getFieldMetadata()[$fieldName]['options']; + } + + /** + * Get the value for the relevant pseudo field. + * + * @param string $realField e.g contribution_status_id + * @param string $pseudoKey e.g name + * @param int|string $fieldValue e.g 1 + * + * @return string + * Eg. 'Completed' in the example above. + * + * @internal function will likely be protected soon. + */ + public function getPseudoValue(string $realField, string $pseudoKey, $fieldValue): string { + if ($pseudoKey === 'name') { + $fieldValue = (string) CRM_Core_PseudoConstant::getName($this->getBAOName(), $realField, $fieldValue); + } + if ($pseudoKey === 'label') { + $fieldValue = (string) CRM_Core_PseudoConstant::getLabel($this->getBAOName(), $realField, $fieldValue); + } + return (string) $fieldValue; + } + } -- 2.25.1