diff --git a/core/modules/workspaces/src/Plugin/Validation/Constraint/EntityWorkspaceConflictConstraint.php b/core/modules/workspaces/src/Plugin/Validation/Constraint/EntityWorkspaceConflictConstraint.php index 3b21fe55e05668604cba2b2e3d74043ee92a8ff6..6f7e65a5596a2dc4605025a849d36e10920a3a0b 100644 --- a/core/modules/workspaces/src/Plugin/Validation/Constraint/EntityWorkspaceConflictConstraint.php +++ b/core/modules/workspaces/src/Plugin/Validation/Constraint/EntityWorkspaceConflictConstraint.php @@ -21,6 +21,6 @@ class EntityWorkspaceConflictConstraint extends SymfonyConstraint { * * @var string */ - public $message = 'The content is being edited in the @label workspace. As a result, your changes cannot be saved.'; + public $message = 'The content is being edited in the %label workspace. As a result, your changes cannot be saved.'; } diff --git a/core/modules/workspaces/src/Plugin/Validation/Constraint/EntityWorkspaceConflictConstraintValidator.php b/core/modules/workspaces/src/Plugin/Validation/Constraint/EntityWorkspaceConflictConstraintValidator.php index 742362cbd312dd935f4aac9c3f04d439556e4dbd..332a09d80aa0892b75d1730f4a2c2eb75cc2b862 100644 --- a/core/modules/workspaces/src/Plugin/Validation/Constraint/EntityWorkspaceConflictConstraintValidator.php +++ b/core/modules/workspaces/src/Plugin/Validation/Constraint/EntityWorkspaceConflictConstraintValidator.php @@ -13,17 +13,55 @@ /** * Validates the EntityWorkspaceConflict constraint. - * - * @internal */ class EntityWorkspaceConflictConstraintValidator extends ConstraintValidator implements ContainerInjectionInterface { - public function __construct( - protected readonly EntityTypeManagerInterface $entityTypeManager, - protected readonly WorkspaceManagerInterface $workspaceManager, - protected readonly WorkspaceAssociationInterface $workspaceAssociation, - protected readonly WorkspaceRepositoryInterface $workspaceRepository, - ) {} + /** + * The entity type manager. + * + * @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 repository service. + * + * @var \Drupal\workspaces\WorkspaceRepositoryInterface + */ + protected $workspaceRepository; + + /** + * Constructs an EntityUntranslatableFieldsConstraintValidator object. + * + * @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\WorkspaceRepositoryInterface $workspace_repository + * The Workspace repository service. + */ + public function __construct(EntityTypeManagerInterface $entity_type_manager, WorkspaceManagerInterface $workspace_manager, WorkspaceAssociationInterface $workspace_association, WorkspaceRepositoryInterface $workspace_repository) { + $this->entityTypeManager = $entity_type_manager; + $this->workspaceManager = $workspace_manager; + $this->workspaceAssociation = $workspace_association; + $this->workspaceRepository = $workspace_repository; + } /** * {@inheritdoc} @@ -45,18 +83,22 @@ public function validate($entity, Constraint $constraint): void { if (isset($entity) && !$entity->isNew()) { $active_workspace = $this->workspaceManager->getActiveWorkspace(); - // If the entity is tracked in a workspace, it can only be edited in - // that workspace or one of its descendants. - if ($tracking_workspace_ids = $this->workspaceAssociation->getEntityTrackingWorkspaceIds($entity)) { - $first_tracking_workspace_id = reset($tracking_workspace_ids); - $descendants_and_self = $this->workspaceRepository->getDescendantsAndSelf($first_tracking_workspace_id); + // Get the latest revision of the entity in order to check if it's being + // edited in a different workspace. + $latest_revision = $this->workspaceManager->executeOutsideWorkspace(function () use ($entity) { + /** @var \Drupal\Core\Entity\RevisionableStorageInterface $storage */ + $storage = $this->entityTypeManager->getStorage($entity->getEntityTypeId()); + return $storage->loadRevision($storage->getLatestRevisionId($entity->id())); + }); - if (!$active_workspace || !in_array($active_workspace->id(), $descendants_and_self, TRUE)) { - $workspace = $this->entityTypeManager->getStorage('workspace') - ->load($first_tracking_workspace_id); + // If the latest revision of the entity is tracked in a workspace, it can + // only be edited in that workspace or one of its descendants. + if ($latest_revision_workspace = $latest_revision->workspace->entity) { + $descendants_and_self = $this->workspaceRepository->getDescendantsAndSelf($latest_revision_workspace->id()); + if (!$active_workspace || !in_array($active_workspace->id(), $descendants_and_self, TRUE)) { $this->context->buildViolation($constraint->message) - ->setParameter('@label', $workspace->label()) + ->setParameter('%label', $latest_revision_workspace->label()) ->addViolation(); } } diff --git a/core/modules/workspaces/src/WorkspaceAssociation.php b/core/modules/workspaces/src/WorkspaceAssociation.php index fb69192ddb7b21806e023498b4191be1f8979046..7dc9a420daa47648d172ff7e16447d30e3c625b9 100644 --- a/core/modules/workspaces/src/WorkspaceAssociation.php +++ b/core/modules/workspaces/src/WorkspaceAssociation.php @@ -308,8 +308,7 @@ public function getEntityTrackingWorkspaceIds(RevisionableInterface $entity) { $query = $this->database->select(static::TABLE) ->fields(static::TABLE, ['workspace']) ->condition('target_entity_type_id', $entity->getEntityTypeId()) - ->condition('target_entity_id', $entity->id()) - ->orderBy('target_entity_revision_id', 'DESC'); + ->condition('target_entity_id', $entity->id()); return $query->execute()->fetchCol(); } diff --git a/core/modules/workspaces/tests/src/Kernel/EntityWorkspaceConflictConstraintValidatorTest.php b/core/modules/workspaces/tests/src/Kernel/EntityWorkspaceConflictConstraintValidatorTest.php deleted file mode 100644 index 7a7154c42b6bba3683e246f3b070a725c5468447..0000000000000000000000000000000000000000 --- a/core/modules/workspaces/tests/src/Kernel/EntityWorkspaceConflictConstraintValidatorTest.php +++ /dev/null @@ -1,148 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace Drupal\Tests\workspaces\Kernel; - -use Drupal\Core\Entity\EntityInterface; -use Drupal\Core\Entity\EntityTypeManagerInterface; -use Drupal\entity_test\Entity\EntityTestMulRevPub; -use Drupal\KernelTests\KernelTestBase; -use Drupal\Tests\user\Traits\UserCreationTrait; -use Drupal\workspaces\Entity\Workspace; - -/** - * @coversDefaultClass \Drupal\workspaces\Plugin\Validation\Constraint\EntityWorkspaceConflictConstraintValidator - * @group workspaces - */ -class EntityWorkspaceConflictConstraintValidatorTest extends KernelTestBase { - - use UserCreationTrait; - use WorkspaceTestTrait; - - /** - * {@inheritdoc} - */ - protected static $modules = [ - 'entity_test', - 'path_alias', - 'system', - 'user', - 'workspaces', - ]; - - /** - * The entity type manager. - */ - protected EntityTypeManagerInterface $entityTypeManager; - - /** - * {@inheritdoc} - */ - protected function setUp(): void { - parent::setUp(); - - $this->entityTypeManager = \Drupal::entityTypeManager(); - - $this->installSchema('workspaces', ['workspace_association']); - - $this->installEntitySchema('entity_test_mulrevpub'); - $this->installEntitySchema('workspace'); - $this->installEntitySchema('user'); - $this->createUser(); - } - - /** - * @covers ::validate - */ - public function testNewEntitiesAllowedInDefaultWorkspace(): void { - // Create two top-level workspaces and a second-level one. - $stage = Workspace::create(['id' => 'stage', 'label' => 'Stage']); - $stage->save(); - $dev = Workspace::create(['id' => 'dev', 'label' => 'Dev', 'parent' => 'stage']); - $dev->save(); - $other = Workspace::create(['id' => 'other', 'label' => 'Other']); - $other->save(); - - // Create an entity in Live, and check that the validation is skipped. - $entity = EntityTestMulRevPub::create(); - $this->assertCount(0, $entity->validate()); - $entity->save(); - $entity = $this->reloadEntity($entity); - $this->assertCount(0, $entity->validate()); - - // Edit the entity in Stage. - $this->switchToWorkspace('stage'); - $entity->save(); - $entity = $this->reloadEntity($entity); - $this->assertCount(0, $entity->validate()); - - $expected_message = 'The content is being edited in the Stage workspace. As a result, your changes cannot be saved.'; - - // Check that the entity can no longer be edited in Live. - $this->switchToLive(); - $entity = $this->reloadEntity($entity); - $violations = $entity->validate(); - $this->assertCount(1, $violations); - $this->assertSame($expected_message, (string) $violations->get(0)->getMessage()); - - // Check that the entity can no longer be edited in another top-level - // workspace. - $this->switchToWorkspace('other'); - $entity = $this->reloadEntity($entity); - $violations = $entity->validate(); - $this->assertCount(1, $violations); - $this->assertSame($expected_message, (string) $violations->get(0)->getMessage()); - - // Check that the entity can still be edited in a sub-workspace of Stage. - $this->switchToWorkspace('dev'); - $entity = $this->reloadEntity($entity); - $this->assertCount(0, $entity->validate()); - - // Edit the entity in Dev. - $this->switchToWorkspace('dev'); - $entity->save(); - $entity = $this->reloadEntity($entity); - $this->assertCount(0, $entity->validate()); - - $expected_message = 'The content is being edited in the Dev workspace. As a result, your changes cannot be saved.'; - - // Check that the entity can no longer be edited in Live. - $this->switchToLive(); - $entity = $this->reloadEntity($entity); - $violations = $entity->validate(); - $this->assertCount(1, $violations); - $this->assertSame($expected_message, (string) $violations->get(0)->getMessage()); - - // Check that the entity can no longer be edited in the parent workspace. - $this->switchToWorkspace('stage'); - $entity = $this->reloadEntity($entity); - $violations = $entity->validate(); - $this->assertCount(1, $violations); - $this->assertSame($expected_message, (string) $violations->get(0)->getMessage()); - - // Check that the entity can no longer be edited in another top-level - // workspace. - $this->switchToWorkspace('other'); - $entity = $this->reloadEntity($entity); - $violations = $entity->validate(); - $this->assertCount(1, $violations); - $this->assertSame($expected_message, (string) $violations->get(0)->getMessage()); - } - - /** - * Reloads the given entity from the storage and returns it. - * - * @param \Drupal\Core\Entity\EntityInterface $entity - * The entity to be reloaded. - * - * @return \Drupal\Core\Entity\EntityInterface - * The reloaded entity. - */ - protected function reloadEntity(EntityInterface $entity): EntityInterface { - $storage = $this->entityTypeManager->getStorage($entity->getEntityTypeId()); - $storage->resetCache([$entity->id()]); - return $storage->load($entity->id()); - } - -}