4 +--------------------------------------------------------------------+
5 | Copyright CiviCRM LLC. All rights reserved. |
7 | This work is published under the GNU AGPLv3 license with some |
8 | permitted exceptions and without any warranty. For full license |
9 | and copyright information, see https://civicrm.org/licensing |
10 +--------------------------------------------------------------------+
16 * @copyright CiviCRM LLC https://civicrm.org/licensing
20 namespace Civi\Api4\Utils
;
23 use CRM_Core_DAO_AllCoreTables
as AllCoreTables
;
30 * @return \CRM_Core_DAO|string
31 * The BAO name for use in static calls. Return doc block is hacked to allow
32 * auto-completion of static methods
34 public static function getBAOFromApiName($entityName) {
35 if ($entityName === 'CustomValue' ||
strpos($entityName, 'Custom_') === 0) {
36 return 'CRM_Core_BAO_CustomValue';
38 $dao = self
::getApiClass($entityName)::getInfo()['dao'] ??
NULL;
42 $bao = str_replace("DAO", "BAO", $dao);
43 // Check if this entity actually has a BAO. Fall back on the DAO if not.
44 $file = strtr($bao, '_', '/') . '.php';
45 return stream_resolve_include_path($file) ?
$bao : $dao;
50 * @return string|\Civi\Api4\Generic\AbstractEntity
52 public static function getApiClass($entityName) {
53 if (strpos($entityName, 'Custom_') === 0) {
54 $groupName = substr($entityName, 7);
55 return self
::isCustomEntity($groupName) ?
'Civi\Api4\CustomValue' : NULL;
57 // Because "Case" is a reserved php keyword
58 $className = 'Civi\Api4\\' . ($entityName === 'Case' ?
'CiviCase' : $entityName);
59 return class_exists($className) ?
$className : NULL;
63 * Get table name of given entity
65 * @param string $entityName
69 public static function getTableName($entityName) {
70 if (strpos($entityName, 'Custom_') === 0) {
71 $customGroup = substr($entityName, 7);
72 return \CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_CustomGroup', $customGroup, 'table_name', 'name');
74 return AllCoreTables
::getTableForEntityName($entityName);
78 * Given a sql table name, return the name of the api entity.
83 public static function getApiNameFromTableName($tableName) {
84 $entityName = AllCoreTables
::getBriefName(AllCoreTables
::getClassForTable($tableName));
87 // Verify class exists
88 return self
::getApiClass($entityName) ?
$entityName : NULL;
90 // Multi-value custom group pseudo-entities
91 $customGroup = \CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_CustomGroup', $tableName, 'name', 'table_name');
92 return self
::isCustomEntity($customGroup) ?
"Custom_$customGroup" : NULL;
98 public static function getOperators() {
99 $operators = \CRM_Core_DAO
::acceptedSQLOperators();
100 $operators[] = 'CONTAINS';
101 $operators[] = 'IS EMPTY';
102 $operators[] = 'IS NOT EMPTY';
107 * For a given API Entity, return the types of custom fields it supports and the column they join to.
109 * @param string $entityName
110 * @return array|mixed|null
111 * @throws \API_Exception
112 * @throws \Civi\API\Exception\UnauthorizedException
114 public static function getCustomGroupExtends(string $entityName) {
115 // Custom_group.extends pretty much maps 1-1 with entity names, except for a couple oddballs (Contact, Participant).
116 switch ($entityName) {
119 'extends' => array_merge(['Contact'], array_keys(\CRM_Core_SelectValues
::contactType())),
125 'extends' => ['Participant', 'ParticipantRole', 'ParticipantEventName', 'ParticipantEventType'],
129 case 'RelationshipCache':
131 'extends' => ['Relationship'],
132 'column' => 'relationship_id',
135 if (array_key_exists($entityName, \CRM_Core_SelectValues
::customGroupExtends())) {
137 'extends' => [$entityName],
145 * Checks if a custom group exists and is multivalued
147 * @param $customGroupName
149 * @throws \CRM_Core_Exception
151 private static function isCustomEntity($customGroupName) {
152 return $customGroupName && \CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_CustomGroup', $customGroupName, 'is_multiple', 'name');
156 * Check if current user is authorized to perform specified action on a given entity.
158 * @param string $entityName
159 * @param string $actionName
160 * @param array $record
162 * @throws \API_Exception
163 * @throws \CRM_Core_Exception
164 * @throws \Civi\API\Exception\NotImplementedException
165 * @throws \Civi\API\Exception\UnauthorizedException
167 public static function checkAccess(string $entityName, string $actionName, array $record) {
168 $action = Request
::create($entityName, $actionName, ['version' => 4]);
169 // This checks gatekeeper permissions
170 $granted = $action->isAuthorized();
171 // For get actions, just run a get and ACLs will be applied to the query.
172 // It's a cheap trick and not as efficient as not running the query at all,
173 // but BAO::checkAccess doesn't consistently check permissions for the "get" action.
174 if (is_a($action, '\Civi\Api4\Generic\DAOGetAction')) {
175 $granted = $granted && $action->addSelect('id')->addWhere('id', '=', $record['id'])->execute()->count();
178 // If entity has a BAO, run the BAO::checkAccess function, which will call the hook
179 $baoName = self
::getBAOFromApiName($entityName);
180 // CustomValue also requires the name of the group
181 if ($baoName === 'CRM_Core_BAO_CustomValue') {
182 $granted = \CRM_Core_BAO_CustomValue
::checkAccess($actionName, $record, NULL, $granted, substr($entityName, 7));
185 $granted = $baoName::checkAccess($actionName, $record, NULL, $granted);
187 // Otherwise, call the hook directly
189 \CRM_Utils_Hook
::checkAccess($entityName, $actionName, $record, NULL, $granted);