From 74c303ca1bb80e6303eec972a6d7dcf9695921b9 Mon Sep 17 00:00:00 2001 From: eileen Date: Sat, 25 Apr 2020 12:56:00 +1200 Subject: [PATCH] [REF] Cleanup and extract string functions specific to entity names. The function CRM_Utils_String::convertStringToCamel was being stretched to also work in entity name oddities And an api3 utility function was being over-used. This adds symmetrical utility functions in AllCoreTables to covert entity names between the 2 formats. --- CRM/Admin/Page/APIExplorer.php | 2 +- CRM/Core/DAO/AllCoreTables.php | 60 +++++++++++++++++++ CRM/Core/Form.php | 2 +- CRM/Utils/String.php | 36 +++-------- Civi/API/Request.php | 2 +- .../API/Subscriber/DynamicFKAuthorization.php | 5 +- Civi/Api4/Generic/AbstractEntity.php | 5 +- Civi/Test/Api3TestTrait.php | 23 ++----- api/api.php | 19 ++---- api/v3/utils.php | 2 +- 10 files changed, 87 insertions(+), 69 deletions(-) diff --git a/CRM/Admin/Page/APIExplorer.php b/CRM/Admin/Page/APIExplorer.php index 13363ee231..54b6c66856 100644 --- a/CRM/Admin/Page/APIExplorer.php +++ b/CRM/Admin/Page/APIExplorer.php @@ -177,7 +177,7 @@ class CRM_Admin_Page_APIExplorer extends CRM_Core_Page { // Fetch block for a specific action else { $action = strtolower($action); - $fnName = 'civicrm_api3_' . _civicrm_api_get_entity_name_from_camel($entity) . '_' . $action; + $fnName = 'civicrm_api3_' . CRM_Core_DAO_AllCoreTables::convertEntityNameToLower($entity) . '_' . $action; // Support the alternate "1 file per action" structure $actionFile = "api/v3/$entity/" . ucfirst($action) . '.php'; $actionFileContents = file_get_contents("api/v3/$entity/" . ucfirst($action) . '.php', FILE_USE_INCLUDE_PATH); diff --git a/CRM/Core/DAO/AllCoreTables.php b/CRM/Core/DAO/AllCoreTables.php index f6a8e2b657..ae56d40c39 100644 --- a/CRM/Core/DAO/AllCoreTables.php +++ b/CRM/Core/DAO/AllCoreTables.php @@ -208,6 +208,66 @@ class CRM_Core_DAO_AllCoreTables { return class_exists($baoName) ? $baoName : $daoName; } + /** + * Convert possibly underscore separated words to camel case with special handling for 'UF' + * e.g membership_payment returns MembershipPayment + * + * @param string $name + * @param bool $legacyV3 + * @return string + */ + public static function convertEntityNameToCamel(string $name, $legacyV3 = FALSE): string { + // This map only applies to APIv3 + $map = [ + 'acl' => 'Acl', + 'ACL' => 'Acl', + 'im' => 'Im', + 'IM' => 'Im', + ]; + if ($legacyV3 && isset($map[$name])) { + return $map[$name]; + } + + $fragments = explode('_', $name); + foreach ($fragments as & $fragment) { + $fragment = ucfirst($fragment); + // Special case: UFGroup, UFJoin, UFMatch, UFField (if passed in without underscores) + if (strpos($fragment, 'Uf') === 0 && strlen($name) > 2) { + $fragment = 'UF' . ucfirst(substr($fragment, 2)); + } + } + // Special case: UFGroup, UFJoin, UFMatch, UFField (if passed in underscore-separated) + if ($fragments[0] === 'Uf') { + $fragments[0] = 'UF'; + } + return implode('', $fragments); + } + + /** + * Convert CamelCase to snake_case, with special handling for some entity names. + * + * Eg. Activity returns activity + * UFGroup returns uf_group + * OptionValue returns option_value + * + * @param string $name + * + * @return string + */ + public static function convertEntityNameToLower(string $name): string { + if ($name === strtolower($name)) { + return $name; + } + if ($name === 'PCP' || $name === 'IM' || $name === 'ACL') { + return strtolower($name); + } + return strtolower(ltrim(str_replace('U_F', + 'uf', + // That's CamelCase, beside an odd UFCamel that is expected as uf_camel + preg_replace('/(?=[A-Z])/', '_$0', $name) + ), '_')); + } + /** * Get a list of all DAO classes. * diff --git a/CRM/Core/Form.php b/CRM/Core/Form.php index 416085c733..4f9dbc3047 100644 --- a/CRM/Core/Form.php +++ b/CRM/Core/Form.php @@ -2015,7 +2015,7 @@ class CRM_Core_Form extends HTML_QuickForm_Page { public function addEntityRef($name, $label = '', $props = [], $required = FALSE) { // Default properties $props['api'] = CRM_Utils_Array::value('api', $props, []); - $props['entity'] = CRM_Utils_String::convertStringToCamel(CRM_Utils_Array::value('entity', $props, 'Contact')); + $props['entity'] = CRM_Core_DAO_AllCoreTables::convertEntityNameToCamel(CRM_Utils_Array::value('entity', $props, 'Contact')); $props['class'] = ltrim(CRM_Utils_Array::value('class', $props, '') . ' crm-form-entityref'); if (array_key_exists('create', $props) && empty($props['create'])) { diff --git a/CRM/Utils/String.php b/CRM/Utils/String.php index 18d4904b1f..e0cac9a763 100644 --- a/CRM/Utils/String.php +++ b/CRM/Utils/String.php @@ -86,37 +86,17 @@ class CRM_Utils_String { } /** - * Convert possibly underscore separated words to camel case with special handling for 'UF' - * e.g membership_payment returns MembershipPayment - * - * @param string $string + * Convert possibly underscore separated words to camel case. * + * @param string $str + * @param bool $ucFirst + * Should the first letter be capitalized like `CamelCase` or lower like `camelCase` * @return string */ - public static function convertStringToCamel($string) { - $map = [ - 'acl' => 'Acl', - 'ACL' => 'Acl', - 'im' => 'Im', - 'IM' => 'Im', - ]; - if (isset($map[$string])) { - return $map[$string]; - } - - $fragments = explode('_', $string); - foreach ($fragments as & $fragment) { - $fragment = ucfirst($fragment); - // Special case: UFGroup, UFJoin, UFMatch, UFField (if passed in without underscores) - if (strpos($fragment, 'Uf') === 0 && strlen($string) > 2) { - $fragment = 'UF' . ucfirst(substr($fragment, 2)); - } - } - // Special case: UFGroup, UFJoin, UFMatch, UFField (if passed in underscore-separated) - if ($fragments[0] === 'Uf') { - $fragments[0] = 'UF'; - } - return implode('', $fragments); + public static function convertStringToCamel($str, $ucFirst = TRUE) { + $fragments = explode('_', $str); + $camel = implode('', array_map('ucfirst', $fragments)); + return $ucFirst ? $camel : lcfirst($camel); } /** diff --git a/Civi/API/Request.php b/Civi/API/Request.php index 22fa382a71..5d0457ef7d 100644 --- a/Civi/API/Request.php +++ b/Civi/API/Request.php @@ -74,7 +74,7 @@ class Request { * @return string */ public static function normalizeEntityName($entity) { - return \CRM_Utils_String::convertStringToCamel(\CRM_Utils_String::munge($entity)); + return \CRM_Core_DAO_AllCoreTables::convertEntityNameToCamel(\CRM_Utils_String::munge($entity), TRUE); } /** diff --git a/Civi/API/Subscriber/DynamicFKAuthorization.php b/Civi/API/Subscriber/DynamicFKAuthorization.php index 6a19cded03..3aaa1a89c6 100644 --- a/Civi/API/Subscriber/DynamicFKAuthorization.php +++ b/Civi/API/Subscriber/DynamicFKAuthorization.php @@ -12,6 +12,7 @@ namespace Civi\API\Subscriber; use Civi\API\Events; +use CRM_Core_DAO_AllCoreTables as AllCoreTables; use Symfony\Component\EventDispatcher\EventSubscriberInterface; /** @@ -123,7 +124,7 @@ class DynamicFKAuthorization implements EventSubscriberInterface { */ public function __construct($kernel, $entityName, $actions, $lookupDelegateSql, $lookupCustomFieldSql, $allowedDelegates = NULL) { $this->kernel = $kernel; - $this->entityName = \CRM_Utils_String::convertStringToCamel($entityName); + $this->entityName = AllCoreTables::convertEntityNameToCamel($entityName, TRUE); $this->actions = $actions; $this->lookupDelegateSql = $lookupDelegateSql; $this->lookupCustomFieldSql = $lookupCustomFieldSql; @@ -138,7 +139,7 @@ class DynamicFKAuthorization implements EventSubscriberInterface { */ public function onApiAuthorize(\Civi\API\Event\AuthorizeEvent $event) { $apiRequest = $event->getApiRequest(); - if ($apiRequest['version'] == 3 && \CRM_Utils_String::convertStringToCamel($apiRequest['entity']) == $this->entityName && in_array(strtolower($apiRequest['action']), $this->actions)) { + if ($apiRequest['version'] == 3 && AllCoreTables::convertEntityNameToCamel($apiRequest['entity'], TRUE) == $this->entityName && in_array(strtolower($apiRequest['action']), $this->actions)) { if (isset($apiRequest['params']['field_name'])) { $fldIdx = \CRM_Utils_Array::index(['field_name'], $this->getCustomFields()); if (empty($fldIdx[$apiRequest['params']['field_name']])) { diff --git a/Civi/Api4/Generic/AbstractEntity.php b/Civi/Api4/Generic/AbstractEntity.php index 74d28bfb33..8a1ed4e862 100644 --- a/Civi/Api4/Generic/AbstractEntity.php +++ b/Civi/Api4/Generic/AbstractEntity.php @@ -66,10 +66,9 @@ abstract class AbstractEntity { $permissions = \CRM_Core_Permission::getEntityActionPermissions(); // For legacy reasons the permissions are keyed by lowercase entity name - // Note: Convert to camel & back in order to circumvent all the api3 naming oddities - $lcentity = _civicrm_api_get_entity_name_from_camel(\CRM_Utils_String::convertStringToCamel(self::getEntityName())); + $lcentity = \CRM_Core_DAO_AllCoreTables::convertEntityNameToLower(self::getEntityName()); // Merge permissions for this entity with the defaults - return \CRM_Utils_Array::value($lcentity, $permissions, []) + $permissions['default']; + return ($permissions[$lcentity] ?? []) + $permissions['default']; } /** diff --git a/Civi/Test/Api3TestTrait.php b/Civi/Test/Api3TestTrait.php index 6d5421053d..fbe73cd0fe 100644 --- a/Civi/Test/Api3TestTrait.php +++ b/Civi/Test/Api3TestTrait.php @@ -302,7 +302,7 @@ trait Api3TestTrait { * @throws \Exception */ public function runApi4Legacy($v3Entity, $v3Action, $v3Params = []) { - $v4Entity = self::convertEntityNameToApi4($v3Entity); + $v4Entity = \CRM_Core_DAO_AllCoreTables::convertEntityNameToCamel($v3Entity); $v4Action = $v3Action = strtolower($v3Action); $v4Params = ['checkPermissions' => isset($v3Params['check_permissions']) ? (bool) $v3Params['check_permissions'] : FALSE]; $sequential = !empty($v3Params['sequential']); @@ -641,9 +641,9 @@ trait Api3TestTrait { // Handle single api call list(, $chainEntity, $chainAction) = explode('.', $key); - $lcChainEntity = \_civicrm_api_get_entity_name_from_camel($chainEntity); - $chainEntity = self::convertEntityNameToApi4($chainEntity); - $lcMainEntity = \_civicrm_api_get_entity_name_from_camel($mainEntity); + $lcChainEntity = \CRM_Core_DAO_AllCoreTables::convertEntityNameToLower($chainEntity); + $chainEntity = \CRM_Core_DAO_AllCoreTables::convertEntityNameToCamel($chainEntity); + $lcMainEntity = \CRM_Core_DAO_AllCoreTables::convertEntityNameToLower($mainEntity); $params = is_array($params) ? $params : []; // Api3 expects this to be inherited @@ -677,19 +677,4 @@ trait Api3TestTrait { return $this->runApi4Legacy($chainEntity, $chainAction, $params); } - /** - * Fix the naming differences between api3 & api4 entities. - * - * @param string $legacyName - * @return string - */ - public static function convertEntityNameToApi4($legacyName) { - $api4Name = \CRM_Utils_String::convertStringToCamel($legacyName); - $map = [ - 'Im' => 'IM', - 'Acl' => 'ACL', - ]; - return $map[$api4Name] ?? $api4Name; - } - } diff --git a/api/api.php b/api/api.php index 8208af08db..0eba312a55 100644 --- a/api/api.php +++ b/api/api.php @@ -281,22 +281,15 @@ function _civicrm_api_replace_variable($value, $parentResult, $separator) { * * @return string * Entity name in underscore separated format. + * + * @deprecated */ function _civicrm_api_get_entity_name_from_camel($entity) { - if (!$entity || $entity === strtolower($entity)) { - return $entity; - } - elseif ($entity == 'PCP') { - return 'pcp'; - } - else { - $entity = ltrim(strtolower(str_replace('U_F', - 'uf', - // That's CamelCase, beside an odd UFCamel that is expected as uf_camel - preg_replace('/(?=[A-Z])/', '_$0', $entity) - )), '_'); + if (!$entity) { + // @todo - this should not be called when empty. + return ''; } - return $entity; + return CRM_Core_DAO_AllCoreTables::convertEntityNameToLower($entity); } /** diff --git a/api/v3/utils.php b/api/v3/utils.php index 3101e29480..2eb06c9937 100644 --- a/api/v3/utils.php +++ b/api/v3/utils.php @@ -2367,7 +2367,7 @@ function _civicrm_api3_api_resolve_alias($entity, $fieldName, $action = 'create' if (strpos($fieldName, 'custom_') === 0 && is_numeric($fieldName[7])) { return $fieldName; } - if ($fieldName == _civicrm_api_get_entity_name_from_camel($entity) . '_id') { + if ($fieldName === (CRM_Core_DAO_AllCoreTables::convertEntityNameToLower($entity) . '_id')) { return 'id'; } $result = civicrm_api($entity, 'getfields', [ -- 2.25.1