From df96519d48aa3dd350f1003920448b1d10d6f60c Mon Sep 17 00:00:00 2001 From: Seamus Lee Date: Sat, 24 Jun 2023 00:42:48 +1000 Subject: [PATCH 1/3] Add in PseudoConstants for various fields in ACL entity Add in Search Kit and form Builder display for ACLs Update function titles following discussion with coleman and update translation in managed entity --- CRM/ACL/BAO/ACL.php | 43 +++++ CRM/ACL/DAO/ACL.php | 13 +- CRM/Core/DAO.php | 6 +- Civi/Api4/ACL.php | 1 - .../ang/afsearchManageACLs.aff.html | 3 + .../ang/afsearchManageACLs.aff.json | 8 + .../managed/SavedSearch_Manage_ACLs.mgd.php | 179 ++++++++++++++++++ xml/schema/ACL/ACL.xml | 10 + 8 files changed, 258 insertions(+), 5 deletions(-) create mode 100644 ext/civicrm_admin_ui/ang/afsearchManageACLs.aff.html create mode 100644 ext/civicrm_admin_ui/ang/afsearchManageACLs.aff.json create mode 100644 ext/civicrm_admin_ui/managed/SavedSearch_Manage_ACLs.mgd.php diff --git a/CRM/ACL/BAO/ACL.php b/CRM/ACL/BAO/ACL.php index c3dee1f46082..bfd6f9141c5b 100644 --- a/CRM/ACL/BAO/ACL.php +++ b/CRM/ACL/BAO/ACL.php @@ -9,6 +9,8 @@ +--------------------------------------------------------------------+ */ +use Civi\Api4\Utils\CoreUtil; + /** * * @package CRM @@ -529,4 +531,45 @@ private static function getGroupClause(array $groupIDs, string $operation): stri return ''; } + public static function getObjectTableOptions(): array { + return [ + 'civicrm_group' => ts('Group'), + 'civicrm_uf_group' => ts('Profile'), + 'civicrm_event' => ts('Event'), + 'civicrm_custom_group' => ts('Custom Group'), + ]; + } + + public static function getObjectIdOptions($context, $params): array { + if (empty($params['values']['object_table']) && empty($params['values']['object_table:label'])) { + return []; + } + if (!empty($params['values']['object_table:label'])) { + $table_name = array_flip(self::operationTables())[$params['values']['object_table:label']]; + } + else { + $table_name = $params['values']['object_table']; + } + $finalOptions = []; + $entity = CoreUtil::getApiNameFromTableName($table_name); + $label = CoreUtil::getInfoItem($entity, 'label_field'); + $titlePlural = CoreUtil::getInfoItem($entity, 'title_plural'); + $finalOptions[] = [ + 'label' => ts('All %1', [1 => $titlePlural]), + 'id' => 0, + 'name' => 0, + ]; + $options = civicrm_api4($entity, 'get', [ + 'select' => [$label, 'id', 'name'], + ]); + foreach ($options as $option) { + $finalOptions[] = [ + 'label' => $option[$label], + 'id' => $option['id'], + 'name' => $option['name'] ?? $option['id'], + ]; + } + return $finalOptions; + } + } diff --git a/CRM/ACL/DAO/ACL.php b/CRM/ACL/DAO/ACL.php index 9683d71d3c78..ac13bb14c914 100644 --- a/CRM/ACL/DAO/ACL.php +++ b/CRM/ACL/DAO/ACL.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/ACL/ACL.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:689e7e5f58bb2cc9959817ecbbd3beeb) + * (GenCodeChecksum:79ed87b3613a7a626f02242218256dd1) */ /** @@ -294,6 +294,10 @@ public static function &fields() { 'entity' => 'ACL', 'bao' => 'CRM_ACL_BAO_ACL', 'localizable' => 0, + 'pseudoconstant' => [ + 'optionGroupName' => 'acl_role', + 'optionEditPath' => 'civicrm/admin/options/acl_role', + ], 'add' => '1.6', ], 'operation' => [ @@ -341,6 +345,9 @@ public static function &fields() { 'entity' => 'ACL', 'bao' => 'CRM_ACL_BAO_ACL', 'localizable' => 0, + 'pseudoconstant' => [ + 'callback' => 'CRM_ACL_BAO_ACL::getObjectTableOptions', + ], 'add' => '1.6', ], 'object_id' => [ @@ -359,6 +366,10 @@ public static function &fields() { 'entity' => 'ACL', 'bao' => 'CRM_ACL_BAO_ACL', 'localizable' => 0, + 'pseudoconstant' => [ + 'callback' => 'CRM_ACL_BAO_ACL::getObjectIdOptions', + 'prefetch' => 'false', + ], 'add' => '1.6', ], 'acl_table' => [ diff --git a/CRM/Core/DAO.php b/CRM/Core/DAO.php index 77dba37b849d..6d8375861b13 100644 --- a/CRM/Core/DAO.php +++ b/CRM/Core/DAO.php @@ -2776,17 +2776,17 @@ public static function appendPseudoConstantsToFields(&$fields) { * @param string $fieldName * @param string $context * @see CRM_Core_DAO::buildOptionsContext - * @param array $props + * @param array $values * Raw field values; whatever is known about this bao object. * * Note: $props can contain unsanitized input and should not be passed directly to CRM_Core_PseudoConstant::get * * @return array|bool */ - public static function buildOptions($fieldName, $context = NULL, $props = []) { + public static function buildOptions($fieldName, $context = NULL, $values = []) { // If a given bao does not override this function $baoName = get_called_class(); - return CRM_Core_PseudoConstant::get($baoName, $fieldName, [], $context); + return CRM_Core_PseudoConstant::get($baoName, $fieldName, ['values' => $values], $context); } /** diff --git a/Civi/Api4/ACL.php b/Civi/Api4/ACL.php index 76c009424cfd..2416179ee080 100644 --- a/Civi/Api4/ACL.php +++ b/Civi/Api4/ACL.php @@ -23,7 +23,6 @@ * * Creating a new ACL requires at minimum an entity table, entity ID and object_table. * - * @searchable none * @see https://docs.civicrm.org/user/en/latest/initial-set-up/permissions-and-access-control * @since 5.19 * @orderBy priority diff --git a/ext/civicrm_admin_ui/ang/afsearchManageACLs.aff.html b/ext/civicrm_admin_ui/ang/afsearchManageACLs.aff.html new file mode 100644 index 000000000000..4864c1fd40cd --- /dev/null +++ b/ext/civicrm_admin_ui/ang/afsearchManageACLs.aff.html @@ -0,0 +1,3 @@ +
+ +
diff --git a/ext/civicrm_admin_ui/ang/afsearchManageACLs.aff.json b/ext/civicrm_admin_ui/ang/afsearchManageACLs.aff.json new file mode 100644 index 000000000000..9c7f2567ab67 --- /dev/null +++ b/ext/civicrm_admin_ui/ang/afsearchManageACLs.aff.json @@ -0,0 +1,8 @@ +{ + "type": "search", + "title": "Manage ACLs", + "icon": "fa-list-alt", + "server_route": "civicrm/acl", + "permission": "administer CiviCRM", + "navigation": null +} diff --git a/ext/civicrm_admin_ui/managed/SavedSearch_Manage_ACLs.mgd.php b/ext/civicrm_admin_ui/managed/SavedSearch_Manage_ACLs.mgd.php new file mode 100644 index 000000000000..35bff4ca2042 --- /dev/null +++ b/ext/civicrm_admin_ui/managed/SavedSearch_Manage_ACLs.mgd.php @@ -0,0 +1,179 @@ + 'SavedSearch_Manage_ACLs', + 'entity' => 'SavedSearch', + 'cleanup' => 'unused', + 'update' => 'unmodified', + 'params' => [ + 'version' => 4, + 'values' => [ + 'name' => 'Manage_ACLs', + 'label' => E::ts('Manage ACLs'), + 'form_values' => NULL, + 'mapping_id' => NULL, + 'search_custom_id' => NULL, + 'api_entity' => 'ACL', + 'api_params' => [ + 'version' => 4, + 'select' => [ + 'entity_id:label', + 'operation:label', + 'object_table:label', + 'object_id:label', + 'name', + 'is_active', + 'deny', + 'priority', + ], + 'orderBy' => [], + 'where' => [], + 'groupBy' => [], + 'join' => [], + 'having' => [], + ], + 'expires_date' => NULL, + 'description' => NULL, + ], + 'match' => [ + 'name', + ], + ], + ], + [ + 'name' => 'SavedSearch_Manage_ACLs_SearchDisplay_Manage_ACLs_Table_1', + 'entity' => 'SearchDisplay', + 'cleanup' => 'unused', + 'update' => 'unmodified', + 'params' => [ + 'version' => 4, + 'values' => [ + 'name' => 'Manage_ACLs_Table_1', + 'label' => E::ts('Manage ACLs'), + 'saved_search_id.name' => 'Manage_ACLs', + 'type' => 'table', + 'settings' => [ + 'description' => NULL, + 'sort' => [ + [ + 'priority', + 'ASC', + ], + ], + 'limit' => 50, + 'pager' => [], + 'placeholder' => 5, + 'columns' => [ + [ + 'type' => 'field', + 'key' => 'entity_id:label', + 'dataType' => 'Integer', + 'label' => E::ts('Role'), + 'sortable' => TRUE, + ], + [ + 'type' => 'field', + 'key' => 'operation:label', + 'dataType' => 'String', + 'label' => E::ts('Operation'), + 'sortable' => TRUE, + ], + [ + 'type' => 'field', + 'key' => 'object_table:label', + 'dataType' => 'String', + 'label' => E::ts('Type of Data'), + 'sortable' => TRUE, + ], + [ + 'type' => 'field', + 'key' => 'object_id:label', + 'dataType' => 'Integer', + 'label' => E::ts('Which Data'), + 'sortable' => TRUE, + ], + [ + 'type' => 'field', + 'key' => 'name', + 'dataType' => 'String', + 'label' => E::ts('Description'), + 'sortable' => TRUE, + ], + [ + 'type' => 'field', + 'key' => 'is_active', + 'dataType' => 'Boolean', + 'label' => E::ts('Enabled'), + 'sortable' => TRUE, + ], + [ + 'type' => 'field', + 'key' => 'deny', + 'dataType' => 'Boolean', + 'label' => E::ts('Mode'), + 'sortable' => TRUE, + 'rewrite' => '{if "[deny]" eq "1"}' . E::ts('Deny') . '{else}' . E::ts('Allow') . '{/if}', + ], + [ + 'type' => 'field', + 'key' => 'priority', + 'dataType' => 'Integer', + 'label' => E::ts('Priority'), + 'sortable' => TRUE, + ], + [ + 'text' => '', + 'style' => 'default', + 'size' => 'btn-xs', + 'icon' => 'fa-bars', + 'links' => [ + [ + 'entity' => 'ACL', + 'action' => 'update', + 'join' => '', + 'target' => 'crm-popup', + 'icon' => 'fa-pencil', + 'text' => E::ts('Edit ACL'), + 'style' => 'default', + 'path' => '', + 'condition' => [], + ], + [ + 'entity' => 'ACL', + 'action' => 'delete', + 'join' => '', + 'target' => 'crm-popup', + 'icon' => 'fa-trash', + 'text' => E::ts('Delete ACL'), + 'style' => 'danger', + 'path' => '', + 'condition' => [], + ], + ], + 'type' => 'menu', + 'alignment' => 'text-right', + ], + ], + 'actions' => TRUE, + 'classes' => [ + 'table', + 'table-striped', + ], + 'addButton' => [ + 'path' => 'civicrm/acl/edit?reset=1&action=add', + 'text' => E::ts('Add ACL'), + 'icon' => 'fa-plus', + ], + ], + 'acl_bypass' => FALSE, + ], + 'match' => [ + 'name', + 'saved_search_id', + ], + ], + ], +]; diff --git a/xml/schema/ACL/ACL.xml b/xml/schema/ACL/ACL.xml index b054c68777fd..8cb4dbbfb8ef 100644 --- a/xml/schema/ACL/ACL.xml +++ b/xml/schema/ACL/ACL.xml @@ -63,6 +63,9 @@ int unsigned Entity ID ID of the object possessing this ACL + + acl_role + 1.6 @@ -91,6 +94,9 @@ varchar 64 The table of the object controlled by this ACL entry + + CRM_ACL_BAO_ACL::getObjectTableOptions + 1.6 @@ -99,6 +105,10 @@ int unsigned The ID of the object controlled by this ACL entry 1.6 + + CRM_ACL_BAO_ACL::getObjectIdOptions + false + acl_table From 5d5ca570c3628710316a28ae5d1deb81002c3199 Mon Sep 17 00:00:00 2001 From: Seamus Lee Date: Sat, 24 Jun 2023 05:48:34 +1000 Subject: [PATCH 2/3] Add Upgrade step to handle adding of the Everybody option value and modify form appropriately --- CRM/ACL/BAO/ACL.php | 2 +- CRM/ACL/Form/ACL.php | 1 - CRM/Upgrade/Incremental/php/FiveSixtyFour.php | 13 +++++++++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/CRM/ACL/BAO/ACL.php b/CRM/ACL/BAO/ACL.php index bfd6f9141c5b..8ab4959f13ba 100644 --- a/CRM/ACL/BAO/ACL.php +++ b/CRM/ACL/BAO/ACL.php @@ -545,7 +545,7 @@ public static function getObjectIdOptions($context, $params): array { return []; } if (!empty($params['values']['object_table:label'])) { - $table_name = array_flip(self::operationTables())[$params['values']['object_table:label']]; + $table_name = array_flip(self::getObjectTableOptions())[$params['values']['object_table:label']]; } else { $table_name = $params['values']['object_table']; diff --git a/CRM/ACL/Form/ACL.php b/CRM/ACL/Form/ACL.php index 3f65d8d7bcbd..7282113225a0 100644 --- a/CRM/ACL/Form/ACL.php +++ b/CRM/ACL/Form/ACL.php @@ -132,7 +132,6 @@ public function buildQuickForm() { $label = ts('Role'); $role = [ '-1' => ts('- select role -'), - '0' => ts('Everyone'), ] + CRM_Core_OptionGroup::values('acl_role'); $this->add('select', 'entity_id', $label, $role, TRUE); diff --git a/CRM/Upgrade/Incremental/php/FiveSixtyFour.php b/CRM/Upgrade/Incremental/php/FiveSixtyFour.php index fdd22f175764..10d18a85d0b9 100644 --- a/CRM/Upgrade/Incremental/php/FiveSixtyFour.php +++ b/CRM/Upgrade/Incremental/php/FiveSixtyFour.php @@ -31,6 +31,7 @@ public function upgrade_5_64_alpha1($rev): void { $this->addTask('Add priority column onto ACL table', 'addColumn', 'civicrm_acl', 'priority', 'int NOT NULL DEFAULT 0'); $this->addTask(ts('Upgrade DB to %1: SQL', [1 => $rev]), 'runSql', $rev); $this->addTask('Update post_URL/cancel_URL in logging tables', 'updateLogging'); + $this->addTask('Add in Everybody ACL Role option value', 'addEveryBodyAclOptionValue'); } public static function updateLogging($ctx): bool { @@ -44,4 +45,16 @@ public static function updateLogging($ctx): bool { return TRUE; } + public static function addEverybodyAclOptionValue($ctx): bool { + \CRM_Core_BAO_OptionValue::ensureOptionValueExists([ + 'label' => 'Everybody', + 'value' => 0, + 'option_group_id' => 'acl_role', + 'is_active' => 1, + 'name' => 'Everybody', + 'is_reserved' => 1, + ]); + return TRUE; + } + } From cb3054832f969dbc200c955717a8420608b1a0cd Mon Sep 17 00:00:00 2001 From: colemanw Date: Fri, 23 Jun 2023 20:16:03 -0700 Subject: [PATCH 3/3] Fix APIv3 conformanceTest when a field has no options (band-aid) --- tests/phpunit/api/v3/SyntaxConformanceTest.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/phpunit/api/v3/SyntaxConformanceTest.php b/tests/phpunit/api/v3/SyntaxConformanceTest.php index e580a495ec74..2506f0bb9783 100644 --- a/tests/phpunit/api/v3/SyntaxConformanceTest.php +++ b/tests/phpunit/api/v3/SyntaxConformanceTest.php @@ -1453,7 +1453,9 @@ public function testCreateSingleValueAlter($entityName): void { $options[$optionValue[0][$keyColumn]] = 'new option value'; } } - $entity[$field] = array_rand($options); + if ($options) { + $entity[$field] = array_rand($options); + } } if (!empty($specs['FKClassName']) && !empty($specs['pseudoconstant'])) { // in the weird situation where a field has both an fk and pseudoconstant defined,