Skip to content
Snippets Groups Projects
Commit 8f28c48d authored by catch's avatar catch
Browse files

Issue #3300639 by amateescu, acrazyanimal, smustgrave: Improve and add...

Issue #3300639 by amateescu, acrazyanimal, smustgrave: Improve and add explicit test coverage for the workspace conflict validator
parent 1916b786
No related branches found
No related tags found
27 merge requests!11131[10.4.x-only-DO-NOT-MERGE]: Issue ##2842525 Ajax attached to Views exposed filter form does not trigger callbacks,!9470[10.3.x-only-DO-NOT-MERGE]: #3331771 Fix file_get_contents(): Passing null to parameter,!8540Issue #3457061: Bootstrap Modal dialog Not closing after 10.3.0 Update,!8528Issue #3456871 by Tim Bozeman: Support NULL services,!8373Issue #3427374 by danflanagan8, Vighneshh: taxonomy_tid ViewsArgumentDefault...,!7526Expose roles in response,!7352Draft: Resolve #3203489 "Set filename as",!3878Removed unused condition head title for views,!3818Issue #2140179: $entity->original gets stale between updates,!3742Issue #3328429: Create item list field formatter for displaying ordered and unordered lists,!3731Claro: role=button on status report items,!3651Issue #3347736: Create new SDC component for Olivero (header-search),!3531Issue #3336994: StringFormatter always displays links to entity even if the user in context does not have access,!3355Issue #3209129: Scrolling problems when adding a block via layout builder,!3154Fixes #2987987 - CSRF token validation broken on routes with optional parameters.,!3133core/modules/system/css/components/hidden.module.css,!2812Issue #3312049: [Followup] Fix Drupal.Commenting.FunctionComment.MissingReturnType returns for NULL,!2794Issue #3100732: Allow specifying `meta` data on JSON:API objects,!2378Issue #2875033: Optimize joins and table selection in SQL entity query implementation,!2062Issue #3246454: Add weekly granularity to views date sort,!1105Issue #3025039: New non translatable field on translatable content throws error,!1073issue #3191727: Focus states on mobile second level navigation items fixed,!877Issue #2708101: Default value for link text is not saved,!617Issue #3043725: Provide a Entity Handler for user cancelation,!579Issue #2230909: Simple decimals fail to pass validation,!560Move callback classRemove outside of the loop,!555Issue #3202493
Pipeline #132008 passed
Pipeline: drupal

#132011

    ......@@ -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.';
    }
    ......@@ -13,55 +13,17 @@
    /**
    * Validates the EntityWorkspaceConflict constraint.
    *
    * @internal
    */
    class EntityWorkspaceConflictConstraintValidator extends ConstraintValidator implements ContainerInjectionInterface {
    /**
    * 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;
    }
    public function __construct(
    protected readonly EntityTypeManagerInterface $entityTypeManager,
    protected readonly WorkspaceManagerInterface $workspaceManager,
    protected readonly WorkspaceAssociationInterface $workspaceAssociation,
    protected readonly WorkspaceRepositoryInterface $workspaceRepository,
    ) {}
    /**
    * {@inheritdoc}
    ......@@ -83,22 +45,18 @@ public function validate($entity, Constraint $constraint) {
    if (isset($entity) && !$entity->isNew()) {
    $active_workspace = $this->workspaceManager->getActiveWorkspace();
    // 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 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 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);
    if (!$active_workspace || !in_array($active_workspace->id(), $descendants_and_self, TRUE)) {
    $workspace = $this->entityTypeManager->getStorage('workspace')
    ->load($first_tracking_workspace_id);
    $this->context->buildViolation($constraint->message)
    ->setParameter('%label', $latest_revision_workspace->label())
    ->setParameter('@label', $workspace->label())
    ->addViolation();
    }
    }
    ......
    ......@@ -308,7 +308,8 @@ 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());
    ->condition('target_entity_id', $entity->id())
    ->orderBy('target_entity_revision_id', 'DESC');
    return $query->execute()->fetchCol();
    }
    ......
    <?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());
    }
    }
    0% Loading or .
    You are about to add 0 people to the discussion. Proceed with caution.
    Finish editing this message first!
    Please register or to comment