From 492f6ff29285d26c6731de5815f98b631bfab88e Mon Sep 17 00:00:00 2001 From: catch <6915-catch@users.noreply.drupalcode.org> Date: Tue, 31 Dec 2024 20:55:39 +0000 Subject: [PATCH] Issue #3488093 by amateescu, nicxvan, longwave: Clean up hook implementations in the Workspaces module --- core/.phpstan-baseline.php | 186 ---------------- core/modules/workspaces/src/EntityAccess.php | 152 ------------- .../workspaces/src/Hook/EntityAccess.php | 87 ++++++++ .../src/{ => Hook}/EntityOperations.php | 198 +++++++---------- .../src/{ => Hook}/EntityTypeInfo.php | 77 +++---- .../src/{ => Hook}/FormOperations.php | 56 +---- .../ViewsOperations.php} | 139 +++--------- .../workspaces/src/Hook/WorkspacesHooks.php | 208 +++--------------- .../WorkspaceContentTranslationTest.php | 2 +- .../Kernel/WorkspaceViewsIntegrationTest.php | 4 +- core/modules/workspaces/workspaces.module | 4 +- 11 files changed, 271 insertions(+), 842 deletions(-) delete mode 100644 core/modules/workspaces/src/EntityAccess.php create mode 100644 core/modules/workspaces/src/Hook/EntityAccess.php rename core/modules/workspaces/src/{ => Hook}/EntityOperations.php (70%) rename core/modules/workspaces/src/{ => Hook}/EntityTypeInfo.php (65%) rename core/modules/workspaces/src/{ => Hook}/FormOperations.php (60%) rename core/modules/workspaces/src/{ViewsQueryAlter.php => Hook/ViewsOperations.php} (78%) diff --git a/core/.phpstan-baseline.php b/core/.phpstan-baseline.php index 1b3583fcddff..aa2be3f4acca 100644 --- a/core/.phpstan-baseline.php +++ b/core/.phpstan-baseline.php @@ -53337,60 +53337,6 @@ 'count' => 1, 'path' => __DIR__ . '/modules/workspaces/src/Entity/Workspace.php', ]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\workspaces\\\\EntityAccess\\:\\:create\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/workspaces/src/EntityAccess.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\workspaces\\\\EntityOperations\\:\\:create\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/workspaces/src/EntityOperations.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\workspaces\\\\EntityOperations\\:\\:entityFormAlter\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/workspaces/src/EntityOperations.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\workspaces\\\\EntityOperations\\:\\:entityFormEntityBuild\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/workspaces/src/EntityOperations.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\workspaces\\\\EntityOperations\\:\\:entityInsert\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/workspaces/src/EntityOperations.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\workspaces\\\\EntityOperations\\:\\:entityPredelete\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/workspaces/src/EntityOperations.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\workspaces\\\\EntityOperations\\:\\:entityPreload\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/workspaces/src/EntityOperations.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\workspaces\\\\EntityOperations\\:\\:entityPresave\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/workspaces/src/EntityOperations.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\workspaces\\\\EntityOperations\\:\\:entityUpdate\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/workspaces/src/EntityOperations.php', -]; $ignoreErrors[] = [ 'message' => '#^Method Drupal\\\\workspaces\\\\EntityQuery\\\\Query\\:\\:traitPrepare\\(\\) has no return type specified\\.$#', 'identifier' => 'missingType.return', @@ -53403,36 +53349,6 @@ 'count' => 1, 'path' => __DIR__ . '/modules/workspaces/src/EntityQuery/QueryAggregate.php', ]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\workspaces\\\\EntityTypeInfo\\:\\:create\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/workspaces/src/EntityTypeInfo.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\workspaces\\\\EntityTypeInfo\\:\\:entityBaseFieldInfo\\(\\) should return array\\<Drupal\\\\Core\\\\Field\\\\FieldDefinitionInterface\\> but return statement is missing\\.$#', - 'identifier' => 'return.missing', - 'count' => 1, - 'path' => __DIR__ . '/modules/workspaces/src/EntityTypeInfo.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\workspaces\\\\EntityTypeInfo\\:\\:entityTypeAlter\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/workspaces/src/EntityTypeInfo.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\workspaces\\\\EntityTypeInfo\\:\\:entityTypeBuild\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/workspaces/src/EntityTypeInfo.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\workspaces\\\\EntityTypeInfo\\:\\:fieldInfoAlter\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/workspaces/src/EntityTypeInfo.php', -]; $ignoreErrors[] = [ 'message' => '#^Method Drupal\\\\workspaces\\\\EventSubscriber\\\\EntitySchemaSubscriber\\:\\:addRevisionMetadataField\\(\\) has no return type specified\\.$#', 'identifier' => 'missingType.return', @@ -53577,84 +53493,6 @@ 'count' => 1, 'path' => __DIR__ . '/modules/workspaces/src/Form/WorkspaceSwitcherForm.php', ]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\workspaces\\\\FormOperations\\:\\:addWorkspaceValidation\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/workspaces/src/FormOperations.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\workspaces\\\\FormOperations\\:\\:create\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/workspaces/src/FormOperations.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\workspaces\\\\FormOperations\\:\\:formAlter\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/workspaces/src/FormOperations.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\workspaces\\\\FormOperations\\:\\:validateDefaultWorkspace\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/workspaces/src/FormOperations.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\workspaces\\\\Hook\\\\WorkspacesHooks\\:\\:entityBaseFieldInfo\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/workspaces/src/Hook/WorkspacesHooks.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\workspaces\\\\Hook\\\\WorkspacesHooks\\:\\:entityDelete\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/workspaces/src/Hook/WorkspacesHooks.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\workspaces\\\\Hook\\\\WorkspacesHooks\\:\\:entityInsert\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/workspaces/src/Hook/WorkspacesHooks.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\workspaces\\\\Hook\\\\WorkspacesHooks\\:\\:entityPredelete\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/workspaces/src/Hook/WorkspacesHooks.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\workspaces\\\\Hook\\\\WorkspacesHooks\\:\\:entityPresave\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/workspaces/src/Hook/WorkspacesHooks.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\workspaces\\\\Hook\\\\WorkspacesHooks\\:\\:entityRevisionDelete\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/workspaces/src/Hook/WorkspacesHooks.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\workspaces\\\\Hook\\\\WorkspacesHooks\\:\\:entityUpdate\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/workspaces/src/Hook/WorkspacesHooks.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\workspaces\\\\Hook\\\\WorkspacesHooks\\:\\:help\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/workspaces/src/Hook/WorkspacesHooks.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\workspaces\\\\Hook\\\\WorkspacesHooks\\:\\:menuLinkContentUpdate\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/workspaces/src/Hook/WorkspacesHooks.php', -]; $ignoreErrors[] = [ 'message' => '#^Method Drupal\\\\workspaces\\\\Negotiator\\\\SessionWorkspaceNegotiator\\:\\:getActiveWorkspace\\(\\) has no return type specified\\.$#', 'identifier' => 'missingType.return', @@ -53715,30 +53553,6 @@ 'count' => 1, 'path' => __DIR__ . '/modules/workspaces/src/Plugin/Validation/Constraint/EntityWorkspaceConflictConstraintValidator.php', ]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\workspaces\\\\ViewsQueryAlter\\:\\:alterQuery\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/workspaces/src/ViewsQueryAlter.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\workspaces\\\\ViewsQueryAlter\\:\\:alterQueryForEntityType\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/workspaces/src/ViewsQueryAlter.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\workspaces\\\\ViewsQueryAlter\\:\\:create\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/workspaces/src/ViewsQueryAlter.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\workspaces\\\\ViewsQueryAlter\\:\\:moveEntityTable\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/workspaces/src/ViewsQueryAlter.php', -]; $ignoreErrors[] = [ 'message' => '#^Method Drupal\\\\workspaces\\\\WorkspaceAssociation\\:\\:deleteAssociations\\(\\) has no return type specified\\.$#', 'identifier' => 'missingType.return', diff --git a/core/modules/workspaces/src/EntityAccess.php b/core/modules/workspaces/src/EntityAccess.php deleted file mode 100644 index baef5bcc4dd4..000000000000 --- a/core/modules/workspaces/src/EntityAccess.php +++ /dev/null @@ -1,152 +0,0 @@ -<?php - -namespace Drupal\workspaces; - -use Drupal\Core\Access\AccessResult; -use Drupal\Core\DependencyInjection\ContainerInjectionInterface; -use Drupal\Core\Entity\EntityInterface; -use Drupal\Core\Entity\EntityTypeManagerInterface; -use Drupal\Core\Session\AccountInterface; -use Drupal\Core\StringTranslation\StringTranslationTrait; -use Symfony\Component\DependencyInjection\ContainerInterface; - -/** - * Service wrapper for hooks relating to entity access control. - * - * @internal - */ -class EntityAccess implements ContainerInjectionInterface { - - use StringTranslationTrait; - - /** - * The entity type manager service. - * - * @var \Drupal\Core\Entity\EntityTypeManagerInterface - */ - protected $entityTypeManager; - - /** - * The workspace manager service. - * - * @var \Drupal\workspaces\WorkspaceManagerInterface - */ - protected $workspaceManager; - - /** - * The workspace information service. - * - * @var \Drupal\workspaces\WorkspaceInformationInterface - */ - protected $workspaceInfo; - - /** - * Constructs a new EntityAccess instance. - * - * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager - * The entity type manager service. - * @param \Drupal\workspaces\WorkspaceManagerInterface $workspace_manager - * The workspace manager service. - * @param \Drupal\workspaces\WorkspaceInformationInterface $workspace_information - * The workspace information service. - */ - public function __construct(EntityTypeManagerInterface $entity_type_manager, WorkspaceManagerInterface $workspace_manager, WorkspaceInformationInterface $workspace_information) { - $this->entityTypeManager = $entity_type_manager; - $this->workspaceManager = $workspace_manager; - $this->workspaceInfo = $workspace_information; - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container) { - return new static( - $container->get('entity_type.manager'), - $container->get('workspaces.manager'), - $container->get('workspaces.information') - ); - } - - /** - * Implements a hook bridge for hook_entity_access(). - * - * @param \Drupal\Core\Entity\EntityInterface $entity - * The entity to check access for. - * @param string $operation - * The operation being performed. - * @param \Drupal\Core\Session\AccountInterface $account - * The user account making the to check access for. - * - * @return \Drupal\Core\Access\AccessResult - * The result of the access check. - * - * @see hook_entity_access() - */ - public function entityOperationAccess(EntityInterface $entity, $operation, AccountInterface $account) { - // Workspaces themselves are handled by their own access handler and we - // should not try to do any access checks for entity types that can not - // belong to a workspace. - if (!$this->workspaceInfo->isEntitySupported($entity) || !$this->workspaceManager->hasActiveWorkspace()) { - return AccessResult::neutral(); - } - - // Prevent the deletion of entities with a published default revision. - if ($operation === 'delete') { - $active_workspace = $this->workspaceManager->getActiveWorkspace(); - $is_deletable = $this->workspaceInfo->isEntityDeletable($entity, $active_workspace); - - return AccessResult::forbiddenIf(!$is_deletable) - ->addCacheableDependency($entity) - ->addCacheableDependency($active_workspace); - } - - return $this->bypassAccessResult($account); - } - - /** - * Implements a hook bridge for hook_entity_create_access(). - * - * @param \Drupal\Core\Session\AccountInterface $account - * The user account making the to check access for. - * @param array $context - * The context of the access check. - * @param string $entity_bundle - * The bundle of the entity. - * - * @return \Drupal\Core\Access\AccessResult - * The result of the access check. - * - * @see hook_entity_create_access() - */ - public function entityCreateAccess(AccountInterface $account, array $context, $entity_bundle) { - // Workspaces themselves are handled by their own access handler and we - // should not try to do any access checks for entity types that can not - // belong to a workspace. - $entity_type = $this->entityTypeManager->getDefinition($context['entity_type_id']); - if (!$this->workspaceInfo->isEntityTypeSupported($entity_type) || !$this->workspaceManager->hasActiveWorkspace()) { - return AccessResult::neutral(); - } - - return $this->bypassAccessResult($account); - } - - /** - * Checks the 'bypass' permissions. - * - * @param \Drupal\Core\Session\AccountInterface $account - * The user account making the to check access for. - * - * @return \Drupal\Core\Access\AccessResult - * The result of the access check. - */ - protected function bypassAccessResult(AccountInterface $account) { - // This approach assumes that the current "global" active workspace is - // correct, i.e. if you're "in" a given workspace then you get ALL THE PERMS - // to ALL THE THINGS! That's why this is a dangerous permission. - $active_workspace = $this->workspaceManager->getActiveWorkspace(); - - return AccessResult::allowedIf($active_workspace->getOwnerId() == $account->id())->cachePerUser()->addCacheableDependency($active_workspace) - ->andIf(AccessResult::allowedIfHasPermission($account, 'bypass entity access own workspace')); - } - -} diff --git a/core/modules/workspaces/src/Hook/EntityAccess.php b/core/modules/workspaces/src/Hook/EntityAccess.php new file mode 100644 index 000000000000..f62839adc26b --- /dev/null +++ b/core/modules/workspaces/src/Hook/EntityAccess.php @@ -0,0 +1,87 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\workspaces\Hook; + +use Drupal\Core\Access\AccessResult; +use Drupal\Core\Access\AccessResultInterface; +use Drupal\Core\Entity\EntityInterface; +use Drupal\Core\Entity\EntityTypeManagerInterface; +use Drupal\Core\Hook\Attribute\Hook; +use Drupal\Core\Session\AccountInterface; +use Drupal\workspaces\WorkspaceInformationInterface; +use Drupal\workspaces\WorkspaceManagerInterface; + +/** + * Defines a class for reacting to entity access control hooks. + */ +class EntityAccess { + + public function __construct( + protected EntityTypeManagerInterface $entityTypeManager, + protected WorkspaceManagerInterface $workspaceManager, + protected WorkspaceInformationInterface $workspaceInfo, + ) {} + + /** + * Implements hook_entity_access(). + */ + #[Hook('entity_access')] + public function entityAccess(EntityInterface $entity, $operation, AccountInterface $account): AccessResultInterface { + // Workspaces themselves are handled by their own access handler and we + // should not try to do any access checks for entity types that can not + // belong to a workspace. + if (!$this->workspaceInfo->isEntitySupported($entity) || !$this->workspaceManager->hasActiveWorkspace()) { + return AccessResult::neutral(); + } + + // Prevent the deletion of entities with a published default revision. + if ($operation === 'delete') { + $active_workspace = $this->workspaceManager->getActiveWorkspace(); + $is_deletable = $this->workspaceInfo->isEntityDeletable($entity, $active_workspace); + + return AccessResult::forbiddenIf(!$is_deletable) + ->addCacheableDependency($entity) + ->addCacheableDependency($active_workspace); + } + + return $this->bypassAccessResult($account); + } + + /** + * Implements hook_entity_create_access(). + */ + #[Hook('entity_create_access')] + public function entityCreateAccess(AccountInterface $account, array $context, $entity_bundle): AccessResultInterface { + // Workspaces themselves are handled by their own access handler and we + // should not try to do any access checks for entity types that can not + // belong to a workspace. + $entity_type = $this->entityTypeManager->getDefinition($context['entity_type_id']); + if (!$this->workspaceInfo->isEntityTypeSupported($entity_type) || !$this->workspaceManager->hasActiveWorkspace()) { + return AccessResult::neutral(); + } + + return $this->bypassAccessResult($account); + } + + /** + * Checks the 'bypass' permissions. + * + * @param \Drupal\Core\Session\AccountInterface $account + * The user account making the to check access for. + * + * @return \Drupal\Core\Access\AccessResultInterface + * The result of the access check. + */ + protected function bypassAccessResult(AccountInterface $account): AccessResultInterface { + // This approach assumes that the current "global" active workspace is + // correct, i.e. if you're "in" a given workspace then you get ALL THE PERMS + // to ALL THE THINGS! That's why this is a dangerous permission. + $active_workspace = $this->workspaceManager->getActiveWorkspace(); + + return AccessResult::allowedIf($active_workspace->getOwnerId() == $account->id())->cachePerUser()->addCacheableDependency($active_workspace) + ->andIf(AccessResult::allowedIfHasPermission($account, 'bypass entity access own workspace')); + } + +} diff --git a/core/modules/workspaces/src/EntityOperations.php b/core/modules/workspaces/src/Hook/EntityOperations.php similarity index 70% rename from core/modules/workspaces/src/EntityOperations.php rename to core/modules/workspaces/src/Hook/EntityOperations.php index 588520bc833a..9c690d6ff9e2 100644 --- a/core/modules/workspaces/src/EntityOperations.php +++ b/core/modules/workspaces/src/Hook/EntityOperations.php @@ -1,90 +1,39 @@ <?php -namespace Drupal\workspaces; +declare(strict_types=1); -use Drupal\Core\DependencyInjection\ContainerInjectionInterface; +namespace Drupal\workspaces\Hook; + +use Drupal\Core\Entity\EntityFormInterface; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityPublishedInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Entity\RevisionableInterface; use Drupal\Core\Form\FormStateInterface; -use Drupal\Core\StringTranslation\StringTranslationTrait; -use Symfony\Component\DependencyInjection\ContainerInterface; +use Drupal\Core\Hook\Attribute\Hook; +use Drupal\workspaces\WorkspaceAssociationInterface; +use Drupal\workspaces\WorkspaceInformationInterface; +use Drupal\workspaces\WorkspaceManagerInterface; +use Drupal\workspaces\WorkspaceRepositoryInterface; /** - * Defines a class for reacting to entity events. - * - * @internal + * Defines a class for reacting to entity runtime hooks. */ -class EntityOperations implements ContainerInjectionInterface { - - use StringTranslationTrait; - - /** - * The entity type manager service. - * - * @var \Drupal\Core\Entity\EntityTypeManagerInterface - */ - protected $entityTypeManager; - - /** - * The workspace manager service. - * - * @var \Drupal\workspaces\WorkspaceManagerInterface - */ - protected $workspaceManager; - - /** - * The workspace association service. - * - * @var \Drupal\workspaces\WorkspaceAssociationInterface - */ - protected $workspaceAssociation; - - /** - * The workspace information service. - * - * @var \Drupal\workspaces\WorkspaceInformationInterface - */ - protected $workspaceInfo; - - /** - * Constructs a new EntityOperations instance. - * - * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager - * The entity type manager service. - * @param \Drupal\workspaces\WorkspaceManagerInterface $workspace_manager - * The workspace manager service. - * @param \Drupal\workspaces\WorkspaceAssociationInterface $workspace_association - * The workspace association service. - * @param \Drupal\workspaces\WorkspaceInformationInterface $workspace_information - * The workspace information service. - */ - public function __construct(EntityTypeManagerInterface $entity_type_manager, WorkspaceManagerInterface $workspace_manager, WorkspaceAssociationInterface $workspace_association, WorkspaceInformationInterface $workspace_information) { - $this->entityTypeManager = $entity_type_manager; - $this->workspaceManager = $workspace_manager; - $this->workspaceAssociation = $workspace_association; - $this->workspaceInfo = $workspace_information; - } +class EntityOperations { - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container) { - return new static( - $container->get('entity_type.manager'), - $container->get('workspaces.manager'), - $container->get('workspaces.association'), - $container->get('workspaces.information') - ); - } + public function __construct( + protected EntityTypeManagerInterface $entityTypeManager, + protected WorkspaceManagerInterface $workspaceManager, + protected WorkspaceAssociationInterface $workspaceAssociation, + protected WorkspaceInformationInterface $workspaceInfo, + protected WorkspaceRepositoryInterface $workspaceRepository, + ) {} /** - * Acts on entity IDs before they are loaded. - * - * @see hook_entity_preload() + * Implements hook_entity_preload(). */ - public function entityPreload(array $ids, $entity_type_id) { + #[Hook('entity_preload')] + public function entityPreload(array $ids, string $entity_type_id): array { $entities = []; $entity_type = $this->entityTypeManager->getDefinition($entity_type_id); @@ -115,14 +64,10 @@ public function entityPreload(array $ids, $entity_type_id) { } /** - * Acts on an entity before it is created or updated. - * - * @param \Drupal\Core\Entity\EntityInterface $entity - * The entity being saved. - * - * @see hook_entity_presave() + * Implements hook_entity_presave(). */ - public function entityPresave(EntityInterface $entity) { + #[Hook('entity_presave')] + public function entityPresave(EntityInterface $entity): void { if ($this->shouldSkipOperations($entity)) { return; } @@ -178,14 +123,15 @@ public function entityPresave(EntityInterface $entity) { } /** - * Responds to the creation of a new entity. - * - * @param \Drupal\Core\Entity\EntityInterface $entity - * The entity that was just saved. - * - * @see hook_entity_insert() + * Implements hook_entity_insert(). */ - public function entityInsert(EntityInterface $entity) { + #[Hook('entity_insert')] + public function entityInsert(EntityInterface $entity): void { + if ($entity->getEntityTypeId() === 'workspace') { + $this->workspaceAssociation->workspaceInsert($entity); + $this->workspaceRepository->resetCache(); + } + if ($this->shouldSkipOperations($entity) || !$this->workspaceInfo->isEntitySupported($entity)) { return; } @@ -215,14 +161,14 @@ public function entityInsert(EntityInterface $entity) { } /** - * Responds to updates to an entity. - * - * @param \Drupal\Core\Entity\EntityInterface $entity - * The entity that was just saved. - * - * @see hook_entity_update() + * Implements hook_entity_update(). */ - public function entityUpdate(EntityInterface $entity) { + #[Hook('entity_update')] + public function entityUpdate(EntityInterface $entity): void { + if ($entity->getEntityTypeId() === 'workspace') { + $this->workspaceRepository->resetCache(); + } + if ($this->shouldSkipOperations($entity) || !$this->workspaceInfo->isEntitySupported($entity)) { return; } @@ -235,13 +181,9 @@ public function entityUpdate(EntityInterface $entity) { } /** - * Acts after an entity translation has been added. - * - * @param \Drupal\Core\Entity\EntityInterface $translation - * The translation that was added. - * - * @see hook_entity_translation_insert() + * Implements hook_entity_translation_insert(). */ + #[Hook('entity_translation_insert')] public function entityTranslationInsert(EntityInterface $translation): void { if ($this->shouldSkipOperations($translation) || !$this->workspaceInfo->isEntitySupported($translation) @@ -269,14 +211,14 @@ public function entityTranslationInsert(EntityInterface $translation): void { } /** - * Acts on an entity before it is deleted. - * - * @param \Drupal\Core\Entity\EntityInterface $entity - * The entity being deleted. - * - * @see hook_entity_predelete() + * Implements hook_entity_predelete(). */ - public function entityPredelete(EntityInterface $entity) { + #[Hook('entity_predelete')] + public function entityPredelete(EntityInterface $entity): void { + if ($entity->getEntityTypeId() === 'workspace') { + $this->workspaceRepository->resetCache(); + } + if ($this->shouldSkipOperations($entity)) { return; } @@ -291,18 +233,36 @@ public function entityPredelete(EntityInterface $entity) { } /** - * Alters entity forms to disallow concurrent editing in multiple workspaces. - * - * @param array $form - * An associative array containing the structure of the form. - * @param \Drupal\Core\Form\FormStateInterface $form_state - * The current state of the form. - * @param string $form_id - * The form ID. + * Implements hook_entity_delete(). + */ + #[Hook('entity_delete')] + public function entityDelete(EntityInterface $entity): void { + if ($this->workspaceInfo->isEntityTypeSupported($entity->getEntityType())) { + $this->workspaceAssociation->deleteAssociations(NULL, $entity->getEntityTypeId(), [$entity->id()]); + } + } + + /** + * Implements hook_entity_revision_delete(). + */ + #[Hook('entity_revision_delete')] + public function entityRevisionDelete(EntityInterface $entity): void { + if ($this->workspaceInfo->isEntityTypeSupported($entity->getEntityType())) { + $this->workspaceAssociation->deleteAssociations(NULL, $entity->getEntityTypeId(), [$entity->id()], [$entity->getRevisionId()]); + } + } + + /** + * Implements hook_form_alter(). * - * @see hook_form_alter() + * Alters entity forms to disallow concurrent editing in multiple workspaces. */ - public function entityFormAlter(array &$form, FormStateInterface $form_state, $form_id) { + #[Hook('form_alter')] + public function entityFormAlter(array &$form, FormStateInterface $form_state, string $form_id): void { + if (!$form_state->getFormObject() instanceof EntityFormInterface) { + return; + } + $entity = $form_state->getFormObject()->getEntity(); if (!$this->workspaceInfo->isEntitySupported($entity) && !$this->workspaceInfo->isEntityIgnored($entity)) { return; @@ -310,7 +270,7 @@ public function entityFormAlter(array &$form, FormStateInterface $form_state, $f // For supported and ignored entity types, signal the fact that this form is // safe to use in a workspace. - // @see \Drupal\workspaces\FormOperations::validateForm() + // @see \Drupal\workspaces\Hook\FormOperations::formAlter() $form_state->set('workspace_safe', TRUE); // There is nothing more to do for ignored entity types. @@ -331,7 +291,7 @@ public function entityFormAlter(array &$form, FormStateInterface $form_state, $f /** * Entity builder that marks all supported entities as pending revisions. */ - public static function entityFormEntityBuild($entity_type_id, RevisionableInterface $entity, &$form, FormStateInterface &$form_state) { + public static function entityFormEntityBuild(string $entity_type_id, RevisionableInterface $entity, array &$form, FormStateInterface &$form_state): void { // Ensure that all entity forms are signaling that a new revision will be // created. $entity->setNewRevision(TRUE); @@ -350,7 +310,7 @@ public static function entityFormEntityBuild($entity_type_id, RevisionableInterf * @return bool * Returns TRUE if entity operations should not be altered, FALSE otherwise. */ - protected function shouldSkipOperations(EntityInterface $entity) { + protected function shouldSkipOperations(EntityInterface $entity): bool { // We should not react on entity operations when the entity is ignored or // when we're not in a workspace context. return $this->workspaceInfo->isEntityIgnored($entity) || !$this->workspaceManager->hasActiveWorkspace(); diff --git a/core/modules/workspaces/src/EntityTypeInfo.php b/core/modules/workspaces/src/Hook/EntityTypeInfo.php similarity index 65% rename from core/modules/workspaces/src/EntityTypeInfo.php rename to core/modules/workspaces/src/Hook/EntityTypeInfo.php index 4bd7e0c426e9..6b5ad43ddbb1 100644 --- a/core/modules/workspaces/src/EntityTypeInfo.php +++ b/core/modules/workspaces/src/Hook/EntityTypeInfo.php @@ -1,51 +1,38 @@ <?php -namespace Drupal\workspaces; +declare(strict_types=1); + +namespace Drupal\workspaces\Hook; -use Drupal\Core\DependencyInjection\ContainerInjectionInterface; use Drupal\Core\Entity\EntityPublishedInterface; use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Field\BaseFieldDefinition; +use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\StringTranslation\TranslatableMarkup; use Drupal\workspaces\Entity\Handler\BlockContentWorkspaceHandler; use Drupal\workspaces\Entity\Handler\DefaultWorkspaceHandler; use Drupal\workspaces\Entity\Handler\IgnoredWorkspaceHandler; -use Symfony\Component\DependencyInjection\ContainerInterface; +use Drupal\workspaces\WorkspaceInformationInterface; /** - * Manipulates entity type information. - * - * This class contains primarily bridged hooks for compile-time or - * cache-clear-time hooks. Runtime hooks should be placed in EntityOperations. + * Defines a class for reacting to entity type information hooks. * - * @internal + * This class contains primarily compile-time or cache-clear-time hooks. Runtime + * hooks should be placed in EntityOperations. */ -class EntityTypeInfo implements ContainerInjectionInterface { +class EntityTypeInfo { public function __construct( - protected readonly WorkspaceInformationInterface $workspaceInfo, - ) { - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container) { - return new static( - $container->get('workspaces.information') - ); - } + protected WorkspaceInformationInterface $workspaceInfo, + ) {} /** - * Adds workspace support info to eligible entity types. + * Implements hook_entity_type_build(). * - * @param \Drupal\Core\Entity\EntityTypeInterface[] $entity_types - * An associative array of all entity type definitions, keyed by the entity - * type name. Passed by reference. - * - * @see hook_entity_type_build() + * Adds workspace support info to eligible entity types. */ - public function entityTypeBuild(array &$entity_types) { + #[Hook('entity_type_build')] + public function entityTypeBuild(array &$entity_types): void { foreach ($entity_types as $entity_type) { if ($entity_type->hasHandlerClass('workspace')) { continue; @@ -77,14 +64,12 @@ public function entityTypeBuild(array &$entity_types) { } /** - * Adds Workspace configuration to appropriate entity types. + * Implements hook_entity_type_alter(). * - * @param \Drupal\Core\Entity\EntityTypeInterface[] $entity_types - * An array of entity types. - * - * @see hook_entity_type_alter() + * Adds workspace configuration to appropriate entity types. */ - public function entityTypeAlter(array &$entity_types) { + #[Hook('entity_type_alter')] + public function entityTypeAlter(array &$entity_types): void { foreach ($entity_types as $entity_type) { if (!$this->workspaceInfo->isEntityTypeSupported($entity_type)) { continue; @@ -105,14 +90,10 @@ public function entityTypeAlter(array &$entity_types) { } /** - * Alters field plugin definitions. - * - * @param array[] $definitions - * An array of field plugin definitions. - * - * @see hook_field_info_alter() + * Implements hook_field_info_alter(). */ - public function fieldInfoAlter(&$definitions) { + #[Hook('field_info_alter')] + public function fieldInfoAlter(array &$definitions): void { if (isset($definitions['entity_reference'])) { $definitions['entity_reference']['constraints']['EntityReferenceSupportedNewEntities'] = []; } @@ -124,17 +105,10 @@ public function fieldInfoAlter(&$definitions) { } /** - * Provides custom base field definitions for a content entity type. - * - * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type - * The entity type definition. - * - * @return \Drupal\Core\Field\FieldDefinitionInterface[] - * An array of field definitions, keyed by field name. - * - * @see hook_entity_base_field_info() + * Implements hook_entity_base_field_info(). */ - public function entityBaseFieldInfo(EntityTypeInterface $entity_type) { + #[Hook('entity_base_field_info')] + public function entityBaseFieldInfo(EntityTypeInterface $entity_type): array { if ($this->workspaceInfo->isEntityTypeSupported($entity_type)) { $field_name = $entity_type->getRevisionMetadataKey('workspace'); $fields[$field_name] = BaseFieldDefinition::create('entity_reference') @@ -147,6 +121,7 @@ public function entityBaseFieldInfo(EntityTypeInterface $entity_type) { return $fields; } + return []; } } diff --git a/core/modules/workspaces/src/FormOperations.php b/core/modules/workspaces/src/Hook/FormOperations.php similarity index 60% rename from core/modules/workspaces/src/FormOperations.php rename to core/modules/workspaces/src/Hook/FormOperations.php index 6d06ec6aa058..85f374582398 100644 --- a/core/modules/workspaces/src/FormOperations.php +++ b/core/modules/workspaces/src/Hook/FormOperations.php @@ -1,61 +1,29 @@ <?php -namespace Drupal\workspaces; +namespace Drupal\workspaces\Hook; -use Drupal\Core\DependencyInjection\ContainerInjectionInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\WorkspaceDynamicSafeFormInterface; use Drupal\Core\Form\WorkspaceSafeFormInterface; +use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Render\Element; use Drupal\Core\StringTranslation\TranslatableMarkup; -use Symfony\Component\DependencyInjection\ContainerInterface; +use Drupal\workspaces\WorkspaceManagerInterface; /** * Defines a class for reacting to form operations. - * - * @internal */ -class FormOperations implements ContainerInjectionInterface { +class FormOperations { - /** - * The workspace manager service. - * - * @var \Drupal\workspaces\WorkspaceManagerInterface - */ - protected $workspaceManager; - - /** - * Constructs a new FormOperations instance. - * - * @param \Drupal\workspaces\WorkspaceManagerInterface $workspace_manager - * The workspace manager service. - */ - public function __construct(WorkspaceManagerInterface $workspace_manager) { - $this->workspaceManager = $workspace_manager; - } + public function __construct( + protected WorkspaceManagerInterface $workspaceManager, + ) {} /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container) { - return new static( - $container->get('workspaces.manager') - ); - } - - /** - * Alters forms to disallow editing in non-default workspaces. - * - * @param array $form - * An associative array containing the structure of the form. - * @param \Drupal\Core\Form\FormStateInterface $form_state - * The current state of the form. - * @param string $form_id - * The form ID. - * - * @see hook_form_alter() + * Implements hook_form_alter(). */ - public function formAlter(array &$form, FormStateInterface $form_state, $form_id) { + #[Hook('form_alter')] + public function formAlter(array &$form, FormStateInterface $form_state, $form_id): void { // No alterations are needed if we're not in a workspace context. if (!$this->workspaceManager->hasActiveWorkspace()) { return; @@ -83,7 +51,7 @@ public function formAlter(array &$form, FormStateInterface $form_state, $form_id * @param array &$element * An associative array containing the structure of the form. */ - protected function addWorkspaceValidation(array &$element) { + protected function addWorkspaceValidation(array &$element): void { // Recurse through all children and add our validation handler if needed. foreach (Element::children($element) as $key) { if (isset($element[$key]) && $element[$key]) { @@ -99,7 +67,7 @@ protected function addWorkspaceValidation(array &$element) { /** * Validation handler which sets a validation error for all unsupported forms. */ - public static function validateDefaultWorkspace(array &$form, FormStateInterface $form_state) { + public static function validateDefaultWorkspace(array &$form, FormStateInterface $form_state): void { if ($form_state->get('workspace_safe') !== TRUE) { $form_state->setError($form, new TranslatableMarkup('This form can only be submitted in the default workspace.')); } diff --git a/core/modules/workspaces/src/ViewsQueryAlter.php b/core/modules/workspaces/src/Hook/ViewsOperations.php similarity index 78% rename from core/modules/workspaces/src/ViewsQueryAlter.php rename to core/modules/workspaces/src/Hook/ViewsOperations.php index f409a20b0d9c..dcf86d7bf603 100644 --- a/core/modules/workspaces/src/ViewsQueryAlter.php +++ b/core/modules/workspaces/src/Hook/ViewsOperations.php @@ -1,132 +1,55 @@ <?php -namespace Drupal\workspaces; +declare(strict_types=1); + +namespace Drupal\workspaces\Hook; -use Drupal\Core\DependencyInjection\ContainerInjectionInterface; use Drupal\Core\Entity\EntityFieldManagerInterface; use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; +use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Language\LanguageManagerInterface; +use Drupal\views\Plugin\ViewsHandlerManager; +use Drupal\views\Plugin\views\join\JoinPluginInterface; use Drupal\views\Plugin\views\query\QueryPluginBase; use Drupal\views\Plugin\views\query\Sql; -use Drupal\views\Plugin\ViewsHandlerManager; use Drupal\views\ViewExecutable; use Drupal\views\ViewsData; -use Symfony\Component\DependencyInjection\ContainerInterface; +use Drupal\workspaces\WorkspaceAssociation; +use Drupal\workspaces\WorkspaceInformationInterface; +use Drupal\workspaces\WorkspaceManagerInterface; +use Symfony\Component\DependencyInjection\Attribute\Autowire; /** * Defines a class for altering views queries. - * - * @internal */ -class ViewsQueryAlter implements ContainerInjectionInterface { - - /** - * The entity type manager service. - * - * @var \Drupal\Core\Entity\EntityTypeManagerInterface - */ - protected $entityTypeManager; - - /** - * The entity field manager. - * - * @var \Drupal\Core\Entity\EntityFieldManagerInterface - */ - protected $entityFieldManager; - - /** - * The workspace manager service. - * - * @var \Drupal\workspaces\WorkspaceManagerInterface - */ - protected $workspaceManager; - - /** - * The views data. - * - * @var \Drupal\views\ViewsData - */ - protected $viewsData; - - /** - * A plugin manager which handles instances of views join plugins. - * - * @var \Drupal\views\Plugin\ViewsHandlerManager - */ - protected $viewsJoinPluginManager; - - /** - * The language manager. - * - * @var \Drupal\Core\Language\LanguageManagerInterface - */ - protected $languageManager; - - /** - * The workspace information service. - * - * @var \Drupal\workspaces\WorkspaceInformationInterface - */ - protected WorkspaceInformationInterface $workspaceInfo; +class ViewsOperations { /** * An array of tables adjusted for workspace_association join. * * @var \WeakMap */ - protected \WeakMap $adjustedTables; - - /** - * Constructs a new ViewsQueryAlter instance. - * - * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager - * The entity type manager service. - * @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager - * The entity field manager. - * @param \Drupal\workspaces\WorkspaceManagerInterface $workspace_manager - * The workspace manager service. - * @param \Drupal\views\ViewsData $views_data - * The views data. - * @param \Drupal\views\Plugin\ViewsHandlerManager $views_join_plugin_manager - * The views join plugin manager. - * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager - * The language manager. - * @param \Drupal\workspaces\WorkspaceInformationInterface $workspace_information - * The workspace information service. - */ - public function __construct(EntityTypeManagerInterface $entity_type_manager, EntityFieldManagerInterface $entity_field_manager, WorkspaceManagerInterface $workspace_manager, ViewsData $views_data, ViewsHandlerManager $views_join_plugin_manager, LanguageManagerInterface $language_manager, WorkspaceInformationInterface $workspace_information) { - $this->entityTypeManager = $entity_type_manager; - $this->entityFieldManager = $entity_field_manager; - $this->workspaceManager = $workspace_manager; - $this->viewsData = $views_data; - $this->viewsJoinPluginManager = $views_join_plugin_manager; - $this->languageManager = $language_manager; - $this->workspaceInfo = $workspace_information; + private \WeakMap $adjustedTables; + + public function __construct( + protected EntityTypeManagerInterface $entityTypeManager, + protected EntityFieldManagerInterface $entityFieldManager, + protected WorkspaceManagerInterface $workspaceManager, + protected WorkspaceInformationInterface $workspaceInfo, + protected LanguageManagerInterface $languageManager, + protected ?ViewsData $viewsData = NULL, + #[Autowire(service: 'plugin.manager.views.join')] + protected ?ViewsHandlerManager $viewsJoinPluginManager = NULL, + ) { $this->adjustedTables = new \WeakMap(); } /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container) { - return new static( - $container->get('entity_type.manager'), - $container->get('entity_field.manager'), - $container->get('workspaces.manager'), - $container->get('views.views_data'), - $container->get('plugin.manager.views.join'), - $container->get('language_manager'), - $container->get('workspaces.information') - ); - } - - /** - * Implements a hook bridge for hook_views_query_alter(). - * - * @see hook_views_query_alter() + * Implements hook_views_query_alter(). */ - public function alterQuery(ViewExecutable $view, QueryPluginBase $query) { + #[Hook('views_query_alter')] + public function viewsQueryAlter(ViewExecutable $view, QueryPluginBase $query): void { // Don't alter any views queries if we're not in a workspace context. if (!$this->workspaceManager->hasActiveWorkspace()) { return; @@ -168,7 +91,7 @@ public function alterQuery(ViewExecutable $view, QueryPluginBase $query) { * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type * The entity type definition. */ - protected function alterQueryForEntityType(Sql $query, EntityTypeInterface $entity_type) { + protected function alterQueryForEntityType(Sql $query, EntityTypeInterface $entity_type): void { /** @var \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */ $table_mapping = $this->entityTypeManager->getStorage($entity_type->id())->getTableMapping(); $field_storage_definitions = $this->entityFieldManager->getFieldStorageDefinitions($entity_type->id()); @@ -301,7 +224,7 @@ protected function alterQueryForEntityType(Sql $query, EntityTypeInterface $enti * @return string * The alias of the 'workspace_association' table. */ - protected function ensureWorkspaceAssociationTable($entity_type_id, Sql $query, $relationship) { + protected function ensureWorkspaceAssociationTable(string $entity_type_id, Sql $query, string $relationship): string { if (isset($query->tables[$relationship]['workspace_association'])) { return $query->tables[$relationship]['workspace_association']['alias']; } @@ -346,7 +269,7 @@ protected function ensureWorkspaceAssociationTable($entity_type_id, Sql $query, * @return string * The alias of the relationship. */ - protected function ensureRevisionTable(EntityTypeInterface $entity_type, Sql $query, $relationship) { + protected function ensureRevisionTable(EntityTypeInterface $entity_type, Sql $query, string $relationship): string { // Get the alias for the 'workspace_association' table we chain off of in // the COALESCE. $workspace_association_table = $this->ensureWorkspaceAssociationTable($entity_type->id(), $query, $relationship); @@ -400,7 +323,7 @@ protected function ensureRevisionTable(EntityTypeInterface $entity_type, Sql $qu * * @throws \Drupal\Component\Plugin\Exception\PluginException */ - protected function getRevisionTableJoin($relationship, $table, $field, $workspace_association_table, EntityTypeInterface $entity_type) { + protected function getRevisionTableJoin(string $relationship, string $table, string $field, string $workspace_association_table, EntityTypeInterface $entity_type): JoinPluginInterface { $definition = [ 'table' => $table, 'field' => $field, @@ -438,7 +361,7 @@ protected function getRevisionTableJoin($relationship, $table, $field, $workspac * @param string $alias * The alias of the table it needs to appear before. */ - protected function moveEntityTable(Sql $query, $workspace_association_table, $alias) { + protected function moveEntityTable(Sql $query, string $workspace_association_table, string $alias): void { $table_queue =& $query->getTableQueue(); $keys = array_keys($table_queue); $current_index = array_search($workspace_association_table, $keys); diff --git a/core/modules/workspaces/src/Hook/WorkspacesHooks.php b/core/modules/workspaces/src/Hook/WorkspacesHooks.php index 2a382eb0b317..b945a24163fd 100644 --- a/core/modules/workspaces/src/Hook/WorkspacesHooks.php +++ b/core/modules/workspaces/src/Hook/WorkspacesHooks.php @@ -1,210 +1,72 @@ <?php +declare(strict_types=1); + namespace Drupal\workspaces\Hook; -use Drupal\Core\Access\AccessResultInterface; -use Drupal\workspaces\ViewsQueryAlter; -use Drupal\views\Plugin\views\query\QueryPluginBase; -use Drupal\views\ViewExecutable; use Drupal\Core\Cache\Cache; -use Drupal\workspaces\EntityAccess; -use Drupal\Core\Session\AccountInterface; +use Drupal\Core\Cache\CacheTagsInvalidatorInterface; +use Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface; use Drupal\Core\Entity\EntityInterface; -use Drupal\Core\Entity\EntityTypeInterface; -use Drupal\workspaces\FormOperations; -use Drupal\workspaces\EntityOperations; -use Drupal\Core\Entity\EntityFormInterface; -use Drupal\Core\Form\FormStateInterface; -use Drupal\workspaces\EntityTypeInfo; -use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Hook\Attribute\Hook; +use Drupal\Core\Routing\RouteMatchInterface; +use Drupal\Core\StringTranslation\StringTranslationTrait; +use Drupal\workspaces\WorkspaceInformationInterface; +use Drupal\workspaces\WorkspaceManagerInterface; /** * Hook implementations for workspaces. */ class WorkspacesHooks { + use StringTranslationTrait; + + public function __construct( + protected WorkspaceManagerInterface $workspaceManager, + protected WorkspaceInformationInterface $workspaceInfo, + protected EntityDefinitionUpdateManagerInterface $entityDefinitionUpdateManager, + protected CacheTagsInvalidatorInterface $cacheTagsInvalidator, + ) {} + /** * Implements hook_help(). */ #[Hook('help')] - public function help($route_name, RouteMatchInterface $route_match) { + public function help(string $route_name, RouteMatchInterface $route_match): string { + $output = ''; switch ($route_name) { // Main module help for the Workspaces module. case 'help.page.workspaces': $output = ''; - $output .= '<h2>' . t('About') . '</h2>'; - $output .= '<p>' . t('The Workspaces module allows workspaces to be defined and switched between. Content is then assigned to the active workspace when created. For more information, see the <a href=":workspaces">online documentation for the Workspaces module</a>.', [':workspaces' => 'https://www.drupal.org/docs/8/core/modules/workspace/overview']) . '</p>'; - return $output; + $output .= '<h2>' . $this->t('About') . '</h2>'; + $output .= '<p>' . $this->t('The Workspaces module allows workspaces to be defined and switched between. Content is then assigned to the active workspace when created. For more information, see the <a href=":workspaces">online documentation for the Workspaces module</a>.', [':workspaces' => 'https://www.drupal.org/docs/8/core/modules/workspace/overview']) . '</p>'; + break; } + return $output; } /** * Implements hook_module_preinstall(). */ #[Hook('module_preinstall')] - public function modulePreinstall($module): void { + public function modulePreinstall(string $module): void { if ($module !== 'workspaces') { return; } - /** @var \Drupal\workspaces\WorkspaceInformationInterface $workspace_info */ - $workspace_info = \Drupal::service('workspaces.information'); - $entity_definition_update_manager = \Drupal::entityDefinitionUpdateManager(); - foreach ($entity_definition_update_manager->getEntityTypes() as $entity_type) { - if ($workspace_info->isEntityTypeSupported($entity_type)) { + foreach ($this->entityDefinitionUpdateManager->getEntityTypes() as $entity_type) { + if ($this->workspaceInfo->isEntityTypeSupported($entity_type)) { $entity_type->setRevisionMetadataKey('workspace', 'workspace'); - $entity_definition_update_manager->updateEntityType($entity_type); + $this->entityDefinitionUpdateManager->updateEntityType($entity_type); } } } /** - * Implements hook_entity_type_build(). - */ - #[Hook('entity_type_build')] - public function entityTypeBuild(array &$entity_types): void { - \Drupal::service('class_resolver')->getInstanceFromDefinition(EntityTypeInfo::class)->entityTypeBuild($entity_types); - } - - /** - * Implements hook_entity_type_alter(). - */ - #[Hook('entity_type_alter')] - public function entityTypeAlter(array &$entity_types) : void { - \Drupal::service('class_resolver')->getInstanceFromDefinition(EntityTypeInfo::class)->entityTypeAlter($entity_types); - } - - /** - * Implements hook_form_alter(). - */ - #[Hook('form_alter')] - public function formAlter(&$form, FormStateInterface $form_state, $form_id) : void { - if ($form_state->getFormObject() instanceof EntityFormInterface) { - \Drupal::service('class_resolver')->getInstanceFromDefinition(EntityOperations::class)->entityFormAlter($form, $form_state, $form_id); - } - \Drupal::service('class_resolver')->getInstanceFromDefinition(FormOperations::class)->formAlter($form, $form_state, $form_id); - } - - /** - * Implements hook_field_info_alter(). - */ - #[Hook('field_info_alter')] - public function fieldInfoAlter(&$definitions): void { - \Drupal::service('class_resolver')->getInstanceFromDefinition(EntityTypeInfo::class)->fieldInfoAlter($definitions); - } - - /** - * Implements hook_entity_base_field_info(). - */ - #[Hook('entity_base_field_info')] - public function entityBaseFieldInfo(EntityTypeInterface $entity_type) { - return \Drupal::service('class_resolver')->getInstanceFromDefinition(EntityTypeInfo::class)->entityBaseFieldInfo($entity_type); - } - - /** - * Implements hook_entity_preload(). - */ - #[Hook('entity_preload')] - public function entityPreload(array $ids, $entity_type_id): array { - return \Drupal::service('class_resolver')->getInstanceFromDefinition(EntityOperations::class)->entityPreload($ids, $entity_type_id); - } - - /** - * Implements hook_entity_presave(). - */ - #[Hook('entity_presave')] - public function entityPresave(EntityInterface $entity) { - return \Drupal::service('class_resolver')->getInstanceFromDefinition(EntityOperations::class)->entityPresave($entity); - } - - /** - * Implements hook_entity_insert(). - */ - #[Hook('entity_insert')] - public function entityInsert(EntityInterface $entity) { - if ($entity->getEntityTypeId() === 'workspace') { - \Drupal::service('workspaces.association')->workspaceInsert($entity); - \Drupal::service('workspaces.repository')->resetCache(); - } - return \Drupal::service('class_resolver')->getInstanceFromDefinition(EntityOperations::class)->entityInsert($entity); - } - - /** - * Implements hook_entity_update(). - */ - #[Hook('entity_update')] - public function entityUpdate(EntityInterface $entity) { - if ($entity->getEntityTypeId() === 'workspace') { - \Drupal::service('workspaces.repository')->resetCache(); - } - return \Drupal::service('class_resolver')->getInstanceFromDefinition(EntityOperations::class)->entityUpdate($entity); - } - - /** - * Implements hook_entity_translation_insert(). - */ - #[Hook('entity_translation_insert')] - public function entityTranslationInsert(EntityInterface $translation) : void { - \Drupal::service('class_resolver')->getInstanceFromDefinition(EntityOperations::class)->entityTranslationInsert($translation); - } - - /** - * Implements hook_entity_predelete(). - */ - #[Hook('entity_predelete')] - public function entityPredelete(EntityInterface $entity) { - if ($entity->getEntityTypeId() === 'workspace') { - \Drupal::service('workspaces.repository')->resetCache(); - } - return \Drupal::service('class_resolver')->getInstanceFromDefinition(EntityOperations::class)->entityPredelete($entity); - } - - /** - * Implements hook_entity_delete(). - */ - #[Hook('entity_delete')] - public function entityDelete(EntityInterface $entity) { - if (\Drupal::service('workspaces.information')->isEntityTypeSupported($entity->getEntityType())) { - \Drupal::service('workspaces.association')->deleteAssociations(NULL, $entity->getEntityTypeId(), [$entity->id()]); - } - } - - /** - * Implements hook_entity_revision_delete(). - */ - #[Hook('entity_revision_delete')] - public function entityRevisionDelete(EntityInterface $entity) { - if (\Drupal::service('workspaces.information')->isEntityTypeSupported($entity->getEntityType())) { - \Drupal::service('workspaces.association')->deleteAssociations(NULL, $entity->getEntityTypeId(), [$entity->id()], [$entity->getRevisionId()]); - } - } - - /** - * Implements hook_entity_access(). - * - * @see \Drupal\workspaces\EntityAccess - */ - #[Hook('entity_access')] - public function entityAccess(EntityInterface $entity, $operation, AccountInterface $account): AccessResultInterface { - return \Drupal::service('class_resolver')->getInstanceFromDefinition(EntityAccess::class)->entityOperationAccess($entity, $operation, $account); - } - - /** - * Implements hook_entity_create_access(). - * - * @see \Drupal\workspaces\EntityAccess - */ - #[Hook('entity_create_access')] - public function entityCreateAccess(AccountInterface $account, array $context, $entity_bundle): AccessResultInterface { - return \Drupal::service('class_resolver')->getInstanceFromDefinition(EntityAccess::class)->entityCreateAccess($account, $context, $entity_bundle); - } - - /** - * Implements hook_ENTITY_TYPE_update() for the 'menu_link_content' entity type. + * Implements hook_ENTITY_TYPE_update() for 'menu_link_content' entities. */ #[Hook('menu_link_content_update')] - public function menuLinkContentUpdate(EntityInterface $entity) { + public function menuLinkContentUpdate(EntityInterface $entity): void { /** @var \Drupal\menu_link_content\MenuLinkContentInterface $entity */ if ($entity->getLoadedRevisionId() != $entity->getRevisionId()) { // We are not updating the menu tree definitions when a custom menu link @@ -213,24 +75,16 @@ public function menuLinkContentUpdate(EntityInterface $entity) { // inserting or deleting a custom menu link updates the menu tree // definitions, so we don't have to do anything in those cases. $cache_tags = Cache::buildTags('config:system.menu', [$entity->getMenuName()], '.'); - \Drupal::service('cache_tags.invalidator')->invalidateTags($cache_tags); + $this->cacheTagsInvalidator->invalidateTags($cache_tags); } } - /** - * Implements hook_views_query_alter(). - */ - #[Hook('views_query_alter')] - public function viewsQueryAlter(ViewExecutable $view, QueryPluginBase $query): void { - \Drupal::service('class_resolver')->getInstanceFromDefinition(ViewsQueryAlter::class)->alterQuery($view, $query); - } - /** * Implements hook_cron(). */ #[Hook('cron')] public function cron(): void { - \Drupal::service('workspaces.manager')->purgeDeletedWorkspacesBatch(); + $this->workspaceManager->purgeDeletedWorkspacesBatch(); } } diff --git a/core/modules/workspaces/tests/src/Kernel/WorkspaceContentTranslationTest.php b/core/modules/workspaces/tests/src/Kernel/WorkspaceContentTranslationTest.php index eabe058b9160..3aa8b9a5bab7 100644 --- a/core/modules/workspaces/tests/src/Kernel/WorkspaceContentTranslationTest.php +++ b/core/modules/workspaces/tests/src/Kernel/WorkspaceContentTranslationTest.php @@ -65,7 +65,7 @@ protected function setUp(): void { /** * Tests translations created in a workspace. * - * @covers \Drupal\workspaces\EntityOperations::entityTranslationInsert + * @covers \Drupal\workspaces\Hook\EntityOperations::entityTranslationInsert */ public function testTranslations(): void { $storage = $this->entityTypeManager->getStorage('entity_test_mulrevpub'); diff --git a/core/modules/workspaces/tests/src/Kernel/WorkspaceViewsIntegrationTest.php b/core/modules/workspaces/tests/src/Kernel/WorkspaceViewsIntegrationTest.php index 7ecc77559891..b8c0ca4b3d03 100644 --- a/core/modules/workspaces/tests/src/Kernel/WorkspaceViewsIntegrationTest.php +++ b/core/modules/workspaces/tests/src/Kernel/WorkspaceViewsIntegrationTest.php @@ -100,8 +100,8 @@ protected function setUp($import_test_views = TRUE): void { /** * Tests workspace query alter for views. * - * @covers \Drupal\workspaces\ViewsQueryAlter::alterQueryForEntityType - * @covers \Drupal\workspaces\ViewsQueryAlter::getRevisionTableJoin + * @covers \Drupal\workspaces\Hook\ViewsOperations::alterQueryForEntityType + * @covers \Drupal\workspaces\Hook\ViewsOperations::getRevisionTableJoin */ public function testViewsQueryAlter(): void { // Create a test entity and two nodes. diff --git a/core/modules/workspaces/workspaces.module b/core/modules/workspaces/workspaces.module index 2b6d101ca394..a053105f20c3 100644 --- a/core/modules/workspaces/workspaces.module +++ b/core/modules/workspaces/workspaces.module @@ -10,7 +10,7 @@ function workspaces_module_implements_alter(&$implementations, $hook): void { // Move our 'hook_entity_presave' implementation at the beginning to ensure // that other presave implementations are aware of the changes done in - // \Drupal\workspaces\EntityOperations::entityPresave(). + // \Drupal\workspaces\Hook\EntityOperations::entityPresave(). if ($hook === 'entity_presave') { $implementation = $implementations['workspaces']; $implementations = ['workspaces' => $implementation] + $implementations; @@ -26,7 +26,7 @@ function workspaces_module_implements_alter(&$implementations, $hook): void { // Move our 'hook_entity_insert' implementation at the end to ensure that // the second (pending) revision created for published entities is not used // by other 'hook_entity_insert' implementations. - // @see \Drupal\workspaces\EntityOperations::entityInsert() + // @see \Drupal\workspaces\Hook\EntityOperations::entityInsert() if ($hook === 'entity_insert') { $group = $implementations['workspaces']; unset($implementations['workspaces']); -- GitLab