From fb9c59fedcd1aaceef3da578982a7149f7eba6cc Mon Sep 17 00:00:00 2001 From: Eileen McNaughton Date: Fri, 10 Sep 2021 14:28:00 +1200 Subject: [PATCH] Consolidate building of contact token list --- CRM/Core/SelectValues.php | 76 +---------- Civi/Token/TokenCompatSubscriber.php | 186 ++++++++++++++++++++++++++- 2 files changed, 190 insertions(+), 72 deletions(-) diff --git a/CRM/Core/SelectValues.php b/CRM/Core/SelectValues.php index 6bb4e60eca..a0ffa0d81d 100644 --- a/CRM/Core/SelectValues.php +++ b/CRM/Core/SelectValues.php @@ -9,6 +9,8 @@ +--------------------------------------------------------------------+ */ +use Civi\Token\TokenProcessor; + /** * One place to store frequently used values in Select Elements. Note that * some of the below elements will be dynamic, so we'll probably have a @@ -576,77 +578,9 @@ class CRM_Core_SelectValues { * * @return array */ - public static function contactTokens() { - if (!isset(Civi::$statics[__CLASS__ . __FUNCTION__])) { - $additionalFields = [ - 'checksum' => ['title' => ts('Checksum')], - 'contact_id' => ['title' => ts('Internal Contact ID')], - ]; - $exportFields = array_merge(CRM_Contact_BAO_Contact::exportableFields(), $additionalFields); - - $values = array_merge(array_keys($exportFields)); - unset($values[0]); - - //FIXME:skipping some tokens for time being. - $skipTokens = [ - 'is_bulkmail', - 'group', - 'tag', - 'contact_sub_type', - 'note', - 'is_deceased', - 'deceased_date', - 'legal_identifier', - 'contact_sub_type', - 'user_unique_id', - 'addressee_id', - 'email_greeting_id', - 'postal_greeting_id', - ]; - - $customFields = CRM_Core_BAO_CustomField::getFields(['Individual', 'Address']); - $legacyTokenNames = array_flip(CRM_Utils_Token::legacyContactTokens()); - - foreach ($values as $val) { - if (in_array($val, $skipTokens)) { - continue; - } - //keys for $tokens should be constant. $token Values are changed for Custom Fields. CRM-3734 - $customFieldId = CRM_Core_BAO_CustomField::getKeyID($val); - if ($customFieldId) { - // CRM-15191 - if key is not in $customFields then the field is disabled and should be ignored - if (!empty($customFields[$customFieldId])) { - $tokens["{contact.$val}"] = $customFields[$customFieldId]['label'] . " :: " . $customFields[$customFieldId]['groupTitle']; - } - } - else { - // Support legacy token names - $tokenName = CRM_Utils_Array::value($val, $legacyTokenNames, $val); - $tokens["{contact.$tokenName}"] = $exportFields[$val]['title']; - } - } - - // Get all the hook tokens too - $hookTokens = []; - CRM_Utils_Hook::tokens($hookTokens); - foreach ($hookTokens as $tokenValues) { - foreach ($tokenValues as $key => $value) { - if (is_numeric($key)) { - $key = $value; - } - if (!preg_match('/^\{[^\}]+\}$/', $key)) { - $key = '{' . $key . '}'; - } - if (preg_match('/^\{([^\}]+)\}$/', $value, $matches)) { - $value = $matches[1]; - } - $tokens[$key] = $value; - } - } - Civi::$statics[__CLASS__ . __FUNCTION__] = $tokens; - } - - return Civi::$statics[__CLASS__ . __FUNCTION__]; + public static function contactTokens(): array { + $tokenProcessor = new TokenProcessor(Civi::dispatcher(), ['schema' => ['contactId']]); + return $tokenProcessor->listTokens(); } /** diff --git a/Civi/Token/TokenCompatSubscriber.php b/Civi/Token/TokenCompatSubscriber.php index 88c1221c36..f6abbfd8ae 100644 --- a/Civi/Token/TokenCompatSubscriber.php +++ b/Civi/Token/TokenCompatSubscriber.php @@ -1,6 +1,7 @@ [ ['setupSmartyAliases', 1000], ['onEvaluate'], ], 'civi.token.render' => 'onRender', + 'civi.token.list' => 'registerTokens', + ]; + } + + /** + * Register the declared tokens. + * + * @param \Civi\Token\Event\TokenRegisterEvent $e + * The registration event. Add new tokens using register(). + */ + public function registerTokens(TokenRegisterEvent $e): void { + if (!$this->checkActive($e->getTokenProcessor())) { + return; + } + foreach (array_merge($this->getContactTokens(), $this->getCustomFieldTokens()) as $name => $label) { + $e->register([ + 'entity' => $this->entity, + 'field' => $name, + 'label' => $label, + ]); + } + foreach ($this->getLegacyHookTokens() as $legacyHookToken) { + $e->register([ + 'entity' => $legacyHookToken['category'], + 'field' => $legacyHookToken['name'], + 'label' => $legacyHookToken['label'], + ]); + } + } + + /** + * Determine whether this token-handler should be used with + * the given processor. + * + * To short-circuit token-processing in irrelevant contexts, + * override this. + * + * @param \Civi\Token\TokenProcessor $processor + * @return bool + */ + public function checkActive(\Civi\Token\TokenProcessor $processor) { + return in_array($this->getEntityIDField(), $processor->context['schema'], TRUE); + } + + /** + * @return string + */ + public function getEntityIDField(): string { + return 'contactId'; + } + + /** + * Get functions declared using the legacy hook. + * + * Note that these only extend the contact entity ( + * ie they are based on having a contact ID which they. + * may or may not use, but they don't have other + * entity IDs.) + * + * @return array + */ + public function getLegacyHookTokens(): array { + $tokens = []; + $hookTokens = []; + \CRM_Utils_Hook::tokens($hookTokens); + foreach ($hookTokens as $tokenValues) { + foreach ($tokenValues as $key => $value) { + if (is_numeric($key)) { + // This appears to be an attempt to compensate for + // inconsistencies described in https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_tokenValues/#example + // in effect there is a suggestion that + // Send an Email" and "CiviMail" send different parameters to the tokenValues hook + // As of now 'send an email' renders hooks through this class. + // CiviMail it depends on the use or otherwise of flexmailer. + $key = $value; + } + if (preg_match('/^\{([^\}]+)\}$/', $value, $matches)) { + $value = $matches[1]; + } + $keyParts = explode('.', $key); + $tokens[$key] = [ + 'category' => $keyParts[0], + 'name' => $keyParts[1], + 'label' => $value, + ]; + } + } + return $tokens; + } + + /** + * @return array + * @throws \CRM_Core_Exception + */ + public function getCustomFieldTokens(): array { + $tokens = []; + $customFields = \CRM_Core_BAO_CustomField::getFields(['Individual', 'Address', 'Contact']); + foreach ($customFields as $customField) { + $tokens['custom_' . $customField['id']] = $customField['label'] . " :: " . $customField['groupTitle']; + } + return $tokens; + } + + /** + * Get all tokens advertised as contact tokens. + * + * @return string[] + */ + public function getContactTokens(): array { + return [ + 'contact_type' => 'Contact Type', + 'do_not_email' => 'Do Not Email', + 'do_not_phone' => 'Do Not Phone', + 'do_not_mail' => 'Do Not Mail', + 'do_not_sms' => 'Do Not Sms', + 'do_not_trade' => 'Do Not Trade', + 'is_opt_out' => 'No Bulk Emails (User Opt Out)', + 'external_identifier' => 'External Identifier', + 'sort_name' => 'Sort Name', + 'display_name' => 'Display Name', + 'nick_name' => 'Nickname', + 'image_URL' => 'Image Url', + 'preferred_communication_method' => 'Preferred Communication Method', + 'preferred_language' => 'Preferred Language', + 'preferred_mail_format' => 'Preferred Mail Format', + 'hash' => 'Contact Hash', + 'contact_source' => 'Contact Source', + 'first_name' => 'First Name', + 'middle_name' => 'Middle Name', + 'last_name' => 'Last Name', + 'individual_prefix' => 'Individual Prefix', + 'individual_suffix' => 'Individual Suffix', + 'formal_title' => 'Formal Title', + 'communication_style' => 'Communication Style', + 'job_title' => 'Job Title', + 'gender' => 'Gender ID', + 'birth_date' => 'Birth Date', + 'current_employer_id' => 'Current Employer ID', + 'contact_is_deleted' => 'Contact is in Trash', + 'created_date' => 'Created Date', + 'modified_date' => 'Modified Date', + 'addressee' => 'Addressee', + 'email_greeting' => 'Email Greeting', + 'postal_greeting' => 'Postal Greeting', + 'current_employer' => 'Current Employer', + 'location_type' => 'Location Type', + 'address_id' => 'Address ID', + 'street_address' => 'Street Address', + 'street_number' => 'Street Number', + 'street_number_suffix' => 'Street Number Suffix', + 'street_name' => 'Street Name', + 'street_unit' => 'Street Unit', + 'supplemental_address_1' => 'Supplemental Address 1', + 'supplemental_address_2' => 'Supplemental Address 2', + 'supplemental_address_3' => 'Supplemental Address 3', + 'city' => 'City', + 'postal_code_suffix' => 'Postal Code Suffix', + 'postal_code' => 'Postal Code', + 'geo_code_1' => 'Latitude', + 'geo_code_2' => 'Longitude', + 'manual_geo_code' => 'Is Manually Geocoded', + 'address_name' => 'Address Name', + 'master_id' => 'Master Address ID', + 'county' => 'County', + 'state_province' => 'State', + 'country' => 'Country', + 'phone' => 'Phone', + 'phone_ext' => 'Phone Extension', + 'phone_type_id' => 'Phone Type ID', + 'phone_type' => 'Phone Type', + 'email' => 'Email', + 'on_hold' => 'On Hold', + 'signature_text' => 'Signature Text', + 'signature_html' => 'Signature Html', + 'im_provider' => 'IM Provider', + 'im' => 'IM Screen Name', + 'openid' => 'OpenID', + 'world_region' => 'World Region', + 'url' => 'Website', + 'checksum' => 'Checksum', + 'contact_id' => 'Internal Contact ID', ]; } -- 2.25.1