diff --git a/core/modules/block_content/src/Entity/BlockContentType.php b/core/modules/block_content/src/Entity/BlockContentType.php index acaa64039dd..206f403175f 100644 --- a/core/modules/block_content/src/Entity/BlockContentType.php +++ b/core/modules/block_content/src/Entity/BlockContentType.php @@ -26,7 +26,8 @@ use Drupal\block_content\BlockContentTypeInterface; * "delete" = "Drupal\block_content\Form\BlockContentTypeDeleteForm" * }, * "route_provider" = { - * "html" = "Drupal\Core\Entity\Routing\AdminHtmlRouteProvider" + * "html" = "Drupal\Core\Entity\Routing\AdminHtmlRouteProvider", + * "permissions" = "Drupal\user\Entity\EntityPermissionsRouteProviderWithCheck", * }, * "list_builder" = "Drupal\block_content\BlockContentTypeListBuilder" * }, @@ -40,6 +41,7 @@ use Drupal\block_content\BlockContentTypeInterface; * links = { * "delete-form" = "/admin/structure/block/block-content/manage/{block_content_type}/delete", * "edit-form" = "/admin/structure/block/block-content/manage/{block_content_type}", + * "entity-permissions-form" = "/admin/structure/block/block-content/manage/{block_content_type}/permissions", * "collection" = "/admin/structure/block/block-content/types", * }, * config_export = { diff --git a/core/modules/comment/src/Entity/CommentType.php b/core/modules/comment/src/Entity/CommentType.php index e8978520bca..77aa049538b 100644 --- a/core/modules/comment/src/Entity/CommentType.php +++ b/core/modules/comment/src/Entity/CommentType.php @@ -24,6 +24,9 @@ use Drupal\comment\CommentTypeInterface; * "edit" = "Drupal\comment\CommentTypeForm", * "delete" = "Drupal\comment\Form\CommentTypeDeleteForm" * }, + * "route_provider" = { + * "permissions" = "Drupal\user\Entity\EntityPermissionsRouteProviderWithCheck", + * }, * "list_builder" = "Drupal\comment\CommentTypeListBuilder" * }, * admin_permission = "administer comment types", @@ -37,6 +40,7 @@ use Drupal\comment\CommentTypeInterface; * "delete-form" = "/admin/structure/comment/manage/{comment_type}/delete", * "edit-form" = "/admin/structure/comment/manage/{comment_type}", * "add-form" = "/admin/structure/comment/types/add", + * "entity-permissions-form" = "/admin/structure/comment/manage/{comment_type}/permissions", * "collection" = "/admin/structure/comment", * }, * config_export = { diff --git a/core/modules/contact/src/Entity/ContactForm.php b/core/modules/contact/src/Entity/ContactForm.php index 91364b04280..514455b4d7d 100644 --- a/core/modules/contact/src/Entity/ContactForm.php +++ b/core/modules/contact/src/Entity/ContactForm.php @@ -26,6 +26,9 @@ use Drupal\Core\Url; * "add" = "Drupal\contact\ContactFormEditForm", * "edit" = "Drupal\contact\ContactFormEditForm", * "delete" = "Drupal\Core\Entity\EntityDeleteForm" + * }, + * "route_provider" = { + * "permissions" = "Drupal\user\Entity\EntityPermissionsRouteProviderWithCheck", * } * }, * config_prefix = "form", @@ -38,6 +41,7 @@ use Drupal\Core\Url; * links = { * "delete-form" = "/admin/structure/contact/manage/{contact_form}/delete", * "edit-form" = "/admin/structure/contact/manage/{contact_form}", + * "entity-permissions-form" = "/admin/structure/contact/manage/{contact_form}/permissions", * "collection" = "/admin/structure/contact", * "canonical" = "/contact/{contact_form}", * }, diff --git a/core/modules/media/src/Entity/MediaType.php b/core/modules/media/src/Entity/MediaType.php index 3a5add6e5ca..68f2508b9e0 100644 --- a/core/modules/media/src/Entity/MediaType.php +++ b/core/modules/media/src/Entity/MediaType.php @@ -30,6 +30,7 @@ use Drupal\media\MediaTypeInterface; * "list_builder" = "Drupal\media\MediaTypeListBuilder", * "route_provider" = { * "html" = "Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider", + * "permissions" = "Drupal\user\Entity\EntityPermissionsRouteProvider", * } * }, * admin_permission = "administer media types", @@ -55,6 +56,7 @@ use Drupal\media\MediaTypeInterface; * "add-form" = "/admin/structure/media/add", * "edit-form" = "/admin/structure/media/manage/{media_type}", * "delete-form" = "/admin/structure/media/manage/{media_type}/delete", + * "entity-permissions-form" = "/admin/structure/media/manage/{media_type}/permissions", * "collection" = "/admin/structure/media", * }, * ) diff --git a/core/modules/node/src/Entity/NodeType.php b/core/modules/node/src/Entity/NodeType.php index f30c995ea18..12e730e9488 100644 --- a/core/modules/node/src/Entity/NodeType.php +++ b/core/modules/node/src/Entity/NodeType.php @@ -26,6 +26,9 @@ use Drupal\node\NodeTypeInterface; * "edit" = "Drupal\node\NodeTypeForm", * "delete" = "Drupal\node\Form\NodeTypeDeleteConfirm" * }, + * "route_provider" = { + * "permissions" = "Drupal\user\Entity\EntityPermissionsRouteProvider", + * }, * "list_builder" = "Drupal\node\NodeTypeListBuilder", * }, * admin_permission = "administer content types", @@ -38,6 +41,7 @@ use Drupal\node\NodeTypeInterface; * links = { * "edit-form" = "/admin/structure/types/manage/{node_type}", * "delete-form" = "/admin/structure/types/manage/{node_type}/delete", + * "entity-permissions-form" = "/admin/structure/types/manage/{node_type}/permissions", * "collection" = "/admin/structure/types", * }, * config_export = { diff --git a/core/modules/system/tests/src/Functional/Menu/LocalTasksTest.php b/core/modules/system/tests/src/Functional/Menu/LocalTasksTest.php index 5375e240509..01ccdc0c499 100644 --- a/core/modules/system/tests/src/Functional/Menu/LocalTasksTest.php +++ b/core/modules/system/tests/src/Functional/Menu/LocalTasksTest.php @@ -272,7 +272,7 @@ class LocalTasksTest extends BrowserTestBase { $this->drupalGet('/admin/structure/types/manage/page'); $this->assertLocalTasks([ ['entity.node_type.edit_form', ['node_type' => 'page']], - ['entity.node_type.permission_form', ['node_type' => 'page']], + ['entity.node_type.entity_permissions_form', ['node_type' => 'page']], ]); // Field UI adds the usual Manage fields etc tabs. @@ -283,7 +283,7 @@ class LocalTasksTest extends BrowserTestBase { ['entity.node.field_ui_fields', ['node_type' => 'page']], ['entity.entity_form_display.node.default', ['node_type' => 'page']], ['entity.entity_view_display.node.default', ['node_type' => 'page']], - ['entity.node_type.permission_form', ['node_type' => 'page']], + ['entity.node_type.entity_permissions_form', ['node_type' => 'page']], ]); } diff --git a/core/modules/taxonomy/src/Entity/Vocabulary.php b/core/modules/taxonomy/src/Entity/Vocabulary.php index a7c94acdfd9..bcd8b596f0d 100644 --- a/core/modules/taxonomy/src/Entity/Vocabulary.php +++ b/core/modules/taxonomy/src/Entity/Vocabulary.php @@ -31,6 +31,7 @@ use Drupal\taxonomy\VocabularyInterface; * }, * "route_provider" = { * "html" = "Drupal\taxonomy\Entity\Routing\VocabularyRouteProvider", + * "permissions" = "Drupal\user\Entity\EntityPermissionsRouteProvider", * } * }, * admin_permission = "administer taxonomy", @@ -47,6 +48,7 @@ use Drupal\taxonomy\VocabularyInterface; * "reset-form" = "/admin/structure/taxonomy/manage/{taxonomy_vocabulary}/reset", * "overview-form" = "/admin/structure/taxonomy/manage/{taxonomy_vocabulary}/overview", * "edit-form" = "/admin/structure/taxonomy/manage/{taxonomy_vocabulary}", + * "entity-permissions-form" = "/admin/structure/taxonomy/manage/{taxonomy_vocabulary}/overview/permissions", * "collection" = "/admin/structure/taxonomy", * }, * config_export = { diff --git a/core/modules/user/src/Entity/EntityPermissionsRouteProvider.php b/core/modules/user/src/Entity/EntityPermissionsRouteProvider.php new file mode 100644 index 00000000000..09ce89159bc --- /dev/null +++ b/core/modules/user/src/Entity/EntityPermissionsRouteProvider.php @@ -0,0 +1,117 @@ +entityTypeManager = $entity_type_manager; + } + + /** + * {@inheritdoc} + */ + public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) { + return new static( + $container->get('entity_type.manager') + ); + } + + /** + * {@inheritdoc} + */ + public function getRoutes(EntityTypeInterface $entity_type) { + $collection = new RouteCollection(); + + $entity_type_id = $entity_type->id(); + + if ($entity_permissions_route = $this->getEntityPermissionsRoute($entity_type)) { + $collection->add("entity.$entity_type_id.entity_permissions_form", $entity_permissions_route); + } + + return $collection; + } + + /** + * Gets the entity permissions route. + * + * Built only for entity types that are bundles of other entity types and + * define the 'entity-permissions-form' link template. + * + * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type + * The entity type. + * + * @return \Symfony\Component\Routing\Route|null + * The generated route, if available. + */ + protected function getEntityPermissionsRoute(EntityTypeInterface $entity_type): ?Route { + if (!$entity_type->hasLinkTemplate('entity-permissions-form')) { + return NULL; + } + + if (!$bundle_of_id = $entity_type->getBundleOf()) { + return NULL; + } + + $entity_type_id = $entity_type->id(); + $route = new Route( + $entity_type->getLinkTemplate('entity-permissions-form'), + [ + '_title' => 'Manage permissions', + '_form' => 'Drupal\user\Form\EntityPermissionsForm', + 'entity_type_id' => $bundle_of_id, + 'bundle_entity_type' => $entity_type_id, + ], + [ + '_permission' => 'administer permissions', + ], + [ + // Indicate that Drupal\Core\Entity\Enhancer\EntityBundleRouteEnhancer should + // set the bundle parameter. + '_field_ui' => TRUE, + 'parameters' => [ + $entity_type_id => [ + 'type' => "entity:$entity_type_id", + 'with_config_overrides' => TRUE, + ], + ], + '_admin_route' => TRUE, + ] + ); + + return $route; + } + +} diff --git a/core/modules/user/src/Entity/EntityPermissionsRouteProviderWithCheck.php b/core/modules/user/src/Entity/EntityPermissionsRouteProviderWithCheck.php new file mode 100644 index 00000000000..be949001f73 --- /dev/null +++ b/core/modules/user/src/Entity/EntityPermissionsRouteProviderWithCheck.php @@ -0,0 +1,31 @@ +setRequirement('_custom_access', '\Drupal\user\Form\EntityPermissionsForm::access'); + } + return $route; + } + +} diff --git a/core/modules/user/src/Form/UserPermissionsBundleForm.php b/core/modules/user/src/Form/EntityPermissionsForm.php similarity index 93% rename from core/modules/user/src/Form/UserPermissionsBundleForm.php rename to core/modules/user/src/Form/EntityPermissionsForm.php index d8bb8e3276e..a787fe32391 100644 --- a/core/modules/user/src/Form/UserPermissionsBundleForm.php +++ b/core/modules/user/src/Form/EntityPermissionsForm.php @@ -22,7 +22,7 @@ use Symfony\Component\Routing\Route; * * @internal */ -class UserPermissionsBundleForm extends UserPermissionsForm { +class EntityPermissionsForm extends UserPermissionsForm { /** * The configuration entity manager. @@ -46,7 +46,7 @@ class UserPermissionsBundleForm extends UserPermissionsForm { protected $bundle; /** - * Constructs a new UserPermissionsBundleForm. + * Constructs a new EntityPermissionsForm. * * @param \Drupal\user\PermissionHandlerInterface $permission_handler * The permission handler. @@ -170,12 +170,7 @@ class UserPermissionsBundleForm extends UserPermissionsForm { return AccessResult::forbidden(); } - $granularity = $this->entityTypeManager - ->getDefinition($this->bundle->getEntityType()->getBundleOf()) - ->getPermissionGranularity(); - $bundle_has_permissions = $granularity === 'bundle' || (bool) $this->permissionsByProvider(); - - return AccessResult::allowedIf($bundle_has_permissions); + return AccessResult::allowedIf((bool) $this->permissionsByProvider()); } } diff --git a/core/modules/user/src/Plugin/Derivative/UserLocalTask.php b/core/modules/user/src/Plugin/Derivative/UserLocalTask.php index cd67bfbe9b6..153fedd9277 100644 --- a/core/modules/user/src/Plugin/Derivative/UserLocalTask.php +++ b/core/modules/user/src/Plugin/Derivative/UserLocalTask.php @@ -51,17 +51,23 @@ class UserLocalTask extends DeriverBase implements ContainerDeriverInterface { public function getDerivativeDefinitions($base_plugin_definition) { $this->derivatives = []; - foreach ($this->entityTypeManager->getDefinitions() as $entity_type) { + $entity_definitions = $this->entityTypeManager->getDefinitions(); + foreach ($entity_definitions as $bundle_type_id => $bundle_entity_type) { + if (!$bundle_entity_type->hasLinkTemplate('entity-permissions-form')) { + continue; + } + + if (!$entity_type_id = $bundle_entity_type->getBundleOf()) { + continue; + } + + $entity_type = $entity_definitions[$entity_type_id]; if (!$base_route = $entity_type->get('field_ui_base_route')) { continue; } - if (!$bundle_entity_type = $entity_type->getBundleEntityType()) { - continue; - } - - $this->derivatives["permissions_$bundle_entity_type"] = [ - 'route_name' => "entity.$bundle_entity_type.permission_form", + $this->derivatives["permissions_$bundle_type_id"] = [ + 'route_name' => "entity.$bundle_type_id.entity_permissions_form", 'weight' => 10, 'title' => $this->t('Manage permissions'), 'base_route' => $base_route, diff --git a/core/modules/user/src/Routing/RouteSubscriber.php b/core/modules/user/src/Routing/RouteSubscriber.php deleted file mode 100644 index c7545ce1514..00000000000 --- a/core/modules/user/src/Routing/RouteSubscriber.php +++ /dev/null @@ -1,78 +0,0 @@ -entityTypeManager = $entity_type_manager; - } - - /** - * {@inheritdoc} - */ - protected function alterRoutes(RouteCollection $collection) { - foreach ($this->entityTypeManager->getDefinitions() as $entity_type_id => $entity_type) { - if (!$route_name = $entity_type->get('field_ui_base_route')) { - continue; - } - - if (!$bundle_entity_type = $entity_type->getBundleEntityType()) { - continue; - } - - // Try to get the route from the current collection. - if (!$entity_route = $collection->get($route_name)) { - continue; - } - - $route = new Route( - $entity_route->getPath() . '/permissions', - [ - '_title' => 'Manage permissions', - '_form' => 'Drupal\user\Form\UserPermissionsBundleForm', - 'entity_type_id' => $entity_type_id, - 'bundle_entity_type' => $bundle_entity_type, - ], - [ - '_permission' => 'administer permissions', - '_custom_access' => '\Drupal\user\Form\UserPermissionsBundleForm::access', - ], - [ - // Indicate that Drupal\Core\Entity\EntityBundleRouteEnhancer should - // set the bundle parameter. - '_field_ui' => TRUE, - 'parameters' => [ - $bundle_entity_type => [ - 'type' => "entity:$bundle_entity_type", - 'with_config_overrides' => TRUE, - ], - ], - ] + $entity_route->getOptions() - ); - $collection->add("entity.$bundle_entity_type.permission_form", $route); - } - } - -} diff --git a/core/modules/user/tests/src/Unit/Form/UserPermissionsBundleFormTest.php b/core/modules/user/tests/src/Unit/Form/EntityPermissionsFormTest.php similarity index 92% rename from core/modules/user/tests/src/Unit/Form/UserPermissionsBundleFormTest.php rename to core/modules/user/tests/src/Unit/Form/EntityPermissionsFormTest.php index 148788f0a70..b97d2614b3d 100644 --- a/core/modules/user/tests/src/Unit/Form/UserPermissionsBundleFormTest.php +++ b/core/modules/user/tests/src/Unit/Form/EntityPermissionsFormTest.php @@ -11,7 +11,7 @@ use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Tests\UnitTestCase; -use Drupal\user\Form\UserPermissionsBundleForm; +use Drupal\user\Form\EntityPermissionsForm; use Drupal\user\PermissionHandlerInterface; use Drupal\user\RoleStorageInterface; use Symfony\Component\Routing\Route; @@ -19,10 +19,10 @@ use Symfony\Component\Routing\Route; /** * Tests the permissions administration form for a bundle. * - * @coversDefaultClass \Drupal\user\Form\UserPermissionsBundleForm + * @coversDefaultClass \Drupal\user\Form\EntityPermissionsForm * @group user */ -class UserPermissionsBundleFormTest extends UnitTestCase { +class EntityPermissionsFormTest extends UnitTestCase { /** * Tests generating the permissions list. @@ -73,7 +73,7 @@ class UserPermissionsBundleFormTest extends UnitTestCase { ->willReturn($entity_type); $entity_type_manager = $prophecy->reveal(); - $bundle_form = new UserPermissionsBundleForm($permission_handler, $role_storage, $module_handler, $config_manager, $entity_type_manager); + $bundle_form = new EntityPermissionsForm($permission_handler, $role_storage, $module_handler, $config_manager, $entity_type_manager); // Mock the method parameters. $route = new Route('some.path'); diff --git a/core/modules/user/tests/src/Unit/Plugin/Derivative/UserLocalTaskTest.php b/core/modules/user/tests/src/Unit/Plugin/Derivative/UserLocalTaskTest.php index c90b5228ef2..ac4902d8524 100644 --- a/core/modules/user/tests/src/Unit/Plugin/Derivative/UserLocalTaskTest.php +++ b/core/modules/user/tests/src/Unit/Plugin/Derivative/UserLocalTaskTest.php @@ -26,24 +26,30 @@ class UserLocalTaskTest extends UnitTestCase { parent::setUp(); $prophecy = $this->prophesize(EntityTypeInterface::class); - $prophecy->get('field_ui_base_route')->willReturn(NULL); - $entity_no_bundle_type = $prophecy->reveal(); + $prophecy->hasLinkTemplate('entity-permissions-form')->willReturn(FALSE); + $entity_no_link_template = $prophecy->reveal(); $prophecy = $this->prophesize(EntityTypeInterface::class); - $prophecy->get('field_ui_base_route')->willReturn('field_ui.base_route'); - $prophecy->getBundleEntityType()->willReturn(NULL); - $entity_bundle_type = $prophecy->reveal(); + $prophecy->hasLinkTemplate('entity-permissions-form')->willReturn(TRUE); + $prophecy->getBundleOf()->willReturn(NULL); + $entity_no_bundle_of = $prophecy->reveal(); $prophecy = $this->prophesize(EntityTypeInterface::class); + $prophecy->hasLinkTemplate('entity-permissions-form')->willReturn(TRUE); + $prophecy->getBundleOf()->willReturn('content_entity_type_id'); + $entity_bundle_of = $prophecy->reveal(); + + $prophecy = $this->prophesize(EntityTypeInterface::class); + $prophecy->hasLinkTemplate('entity-permissions-form')->willReturn(FALSE); $prophecy->get('field_ui_base_route')->willReturn('field_ui.base_route'); - $prophecy->getBundleEntityType()->willReturn('field_ui_bundle_type'); - $field_ui_bundle_type = $prophecy->reveal(); + $content_entity_type = $prophecy->reveal(); $prophecy = $this->prophesize(EntityTypeManagerInterface::class); $prophecy->getDefinitions()->willReturn([ - 'case_no_bundle_type' => $entity_no_bundle_type, - 'case_bundle_type' => $entity_bundle_type, - 'case_field_ui' => $field_ui_bundle_type, + 'entity_no_link_template_id' => $entity_no_link_template, + 'entity_no_bundle_of_id' => $entity_no_bundle_of, + 'entity_bundle_of_id' => $entity_bundle_of, + 'content_entity_type_id' => $content_entity_type, ]); $entity_type_manager = $prophecy->reveal(); @@ -57,8 +63,8 @@ class UserLocalTaskTest extends UnitTestCase { */ public function testGetDerivativeDefinitions() { $expected = [ - 'permissions_field_ui_bundle_type' => [ - 'route_name' => 'entity.field_ui_bundle_type.permission_form', + 'permissions_entity_bundle_of_id' => [ + 'route_name' => 'entity.entity_bundle_of_id.entity_permissions_form', 'weight' => 10, 'title' => $this->getStringTranslationStub()->translate('Manage permissions'), 'base_route' => 'field_ui.base_route', diff --git a/core/modules/user/user.link_relation_types.yml b/core/modules/user/user.link_relation_types.yml new file mode 100644 index 00000000000..f199ec1ec7d --- /dev/null +++ b/core/modules/user/user.link_relation_types.yml @@ -0,0 +1,5 @@ +# User extension relation types. +# See https://tools.ietf.org/html/rfc5988#section-4.2. +entity-permissions-form: + uri: https://drupal.org/link-relations/permissions + description: A form where bundle-related permissions can be managed. diff --git a/core/modules/user/user.module b/core/modules/user/user.module index 833cdc5be64..6a37e07e0f6 100644 --- a/core/modules/user/user.module +++ b/core/modules/user/user.module @@ -1331,12 +1331,14 @@ function user_filter_format_disable(FilterFormatInterface $filter_format) { * Implements hook_entity_operation(). */ function user_entity_operation(EntityInterface $entity) { - // Add Manage permissions link if this entity type is the bundle of another. - if (!$bundle_entity_type = $entity->bundle()) { + // Add Manage permissions link if this entity type defines the permissions + // link template. + if (!$entity->hasLinkTemplate('entity-permissions-form')) { return []; } - $route = "entity.$bundle_entity_type.permission_form"; + $bundle_entity_type = $entity->bundle(); + $route = "entity.$bundle_entity_type.entity_permissions_form"; if (empty(\Drupal::service('router.route_provider')->getRoutesByNames([$route]))) { return []; } diff --git a/core/modules/user/user.services.yml b/core/modules/user/user.services.yml index 2f82493c44d..f8f7d15f4e1 100644 --- a/core/modules/user/user.services.yml +++ b/core/modules/user/user.services.yml @@ -52,11 +52,6 @@ services: user.permissions: class: Drupal\user\PermissionHandler arguments: ['@module_handler', '@string_translation', '@controller_resolver'] - user.route_subscriber: - class: Drupal\user\Routing\RouteSubscriber - arguments: ['@entity_type.manager'] - tags: - - { name: event_subscriber } user.current_user_context: class: Drupal\user\ContextProvider\CurrentUserContext arguments: ['@current_user', '@entity_type.manager']