Commit 9efb68a7 authored by catch's avatar catch
Browse files

Issue #3546740 by amateescu, dcam: Initial workspace-published revisions are marked as new too late

(cherry picked from commit e9a47ab0)
parent e0f3aad5
Loading
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -168,6 +168,10 @@ public function entityInsert(EntityInterface $entity): void {
      }

      $entity->setPublished();
      // Ensure that the second (workspace-specific) revision is marked as new
      // early, so operations that are executed before the entity presave hook
      // (e.g. field-level presave) can take that into account.
      $entity->setNewRevision();
      $entity->isDefaultRevision(FALSE);
      $entity->save();
    }
+39 −0
Original line number Diff line number Diff line
@@ -5,8 +5,12 @@
namespace Drupal\workspaces_test\Hook;

use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Hook\Attribute\Hook;
use Drupal\Core\KeyValueStore\KeyValueFactoryInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\workspaces\WorkspaceInformationInterface;
use Symfony\Component\DependencyInjection\Attribute\Autowire;

/**
@@ -17,6 +21,7 @@ class WorkspacesTestHooks {
  public function __construct(
    #[Autowire(service: 'keyvalue')]
    protected readonly KeyValueFactoryInterface $keyValueFactory,
    protected readonly WorkspaceInformationInterface $workspaceInformation,
  ) {}

  /**
@@ -32,6 +37,24 @@ public function entityTypeAlter(array &$entity_types) : void {
    }
  }

  /**
   * Implements hook_entity_base_field_info().
   */
  #[Hook('entity_base_field_info')]
  public function entityBaseFieldInfo(EntityTypeInterface $entity_type): array {
    $fields = [];

    // Add the workspace revision field test to entity_test_mulrevpub.
    if ($entity_type->id() === 'entity_test_mulrevpub') {
      $fields['revision_test_field'] = BaseFieldDefinition::create('revision_test_field')
        ->setLabel(new TranslatableMarkup('Workspace Revision Field Test'))
        ->setDescription(new TranslatableMarkup('A test field that tracks isNewRevision() status during field presave.'))
        ->setRevisionable(TRUE);
    }

    return $fields;
  }

  /**
   * Implements hook_ENTITY_TYPE_translation_create() for 'entity_test_mulrevpub'.
   */
@@ -56,6 +79,22 @@ public function entityCreate(EntityInterface $entity): void {
  #[Hook('entity_presave')]
  public function entityPresave(EntityInterface $entity): void {
    $this->incrementHookCount('hook_entity_presave', $entity);

    if (!$this->workspaceInformation->isEntitySupported($entity)) {
      return;
    }

    /** @var \Drupal\Core\Entity\RevisionableInterface|\Drupal\Core\Entity\EntityPublishedInterface $entity */
    // Track the sequence of revisions created for this entity.
    $sequence_key = $entity->getEntityTypeId() . '.' . $entity->uuid() . '.revision_sequence';
    $sequence = $this->keyValueFactory->get('ws_test')->get($sequence_key, []);
    $sequence[] = [
      'is_new' => $entity->isNew(),
      'is_new_revision' => $entity->isNewRevision(),
      'is_published' => $entity->isPublished(),
      'is_default_revision' => $entity->isDefaultRevision(),
    ];
    $this->keyValueFactory->get('ws_test')->set($sequence_key, $sequence);
  }

  /**
+46 −0
Original line number Diff line number Diff line
<?php

declare(strict_types=1);

namespace Drupal\workspaces_test\Plugin\Field\FieldType;

use Drupal\Core\Field\Attribute\FieldType;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\entity_test\Plugin\Field\FieldType\FieldTestItem;

/**
 * Defines the 'revision_test_field' field type.
 *
 * This field type records entity revision status during preSave() for
 * testing workspace entity save operations.
 */
#[FieldType(
  id: "revision_test_field",
  label: new TranslatableMarkup("Revision Test Field"),
  description: new TranslatableMarkup("A test field that tracks entity revision status during saves."),
)]
class RevisionTestItem extends FieldTestItem {

  /**
   * {@inheritdoc}
   */
  public function preSave(): void {
    parent::preSave();

    /** @var \Drupal\Core\Entity\RevisionableInterface|\Drupal\Core\Entity\EntityPublishedInterface $entity */
    $entity = $this->getEntity();

    // Track the revision sequence of field pre-save for this entity.
    // @see \Drupal\workspaces_test\Hook\WorkspacesTestHooks::entityPresave()
    $sequence_key = $entity->getEntityTypeId() . '.' . $entity->uuid() . '.field_revision_sequence';
    $sequence = \Drupal::keyValue('ws_test')->get($sequence_key, []);
    $sequence[] = [
      'is_new' => $entity->isNew(),
      'is_new_revision' => $entity->isNewRevision(),
      'is_published' => $entity->isPublished(),
      'is_default_revision' => $entity->isDefaultRevision(),
    ];
    \Drupal::keyValue('ws_test')->set($sequence_key, $sequence);
  }

}
+123 −0
Original line number Diff line number Diff line
<?php

declare(strict_types=1);

namespace Drupal\Tests\workspaces\Kernel;

use Drupal\entity_test\Entity\EntityTestMulRevPub;
use Drupal\KernelTests\KernelTestBase;
use Drupal\Tests\user\Traits\UserCreationTrait;
use Drupal\workspaces\Entity\Workspace;
use PHPUnit\Framework\Attributes\Group;

/**
 * Tests entity operations with workspaces.
 */
#[Group('workspaces')]
class WorkspaceEntityOperationsTest extends KernelTestBase {

  use UserCreationTrait;
  use WorkspaceTestTrait;

  /**
   * {@inheritdoc}
   */
  protected static $modules = [
    'entity_test',
    'system',
    'user',
    'workspaces',
    'workspaces_test',
  ];

  /**
   * The entity type manager.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * {@inheritdoc}
   */
  protected function setUp(): void {
    parent::setUp();

    $this->entityTypeManager = \Drupal::entityTypeManager();
    $this->workspaceManager = \Drupal::service('workspaces.manager');

    $this->installEntitySchema('entity_test_mulrevpub');
    $this->installEntitySchema('workspace');

    $this->installSchema('workspaces', ['workspace_association', 'workspace_association_revision']);

    $this->installConfig(['system']);

    $this->setUpCurrentUser([], [
      'create workspace',
      'view any workspace',
      'edit any workspace',
      'delete any workspace',
    ]);

    $this->workspaces['stage'] = Workspace::create(['id' => 'stage', 'label' => 'Stage']);
    $this->workspaces['stage']->save();
  }

  /**
   * Test published entity creation in a workspace.
   */
  public function testEntityCreation(): void {
    $this->switchToWorkspace('stage');

    // Create a published entity in the workspace.
    $entity = EntityTestMulRevPub::create([
      'name' => 'Test published entity in workspace',
      'status' => TRUE,
      'revision_test_field' => $this->randomString(),
    ]);
    $entity->save();

    // Get the revision sequence that was tracked during entity saves.
    $sequence_key = 'entity_test_mulrevpub.' . $entity->uuid() . '.revision_sequence';
    $revision_sequence = \Drupal::keyValue('ws_test')->get($sequence_key, []);

    $field_sequence_key = 'entity_test_mulrevpub.' . $entity->uuid() . '.field_revision_sequence';
    $field_revision_sequence = \Drupal::keyValue('ws_test')->get($field_sequence_key, []);

    // We expect exactly 2 presave calls when a published entity is created in a
    // workspace:
    // 1. First save: unpublished default revision.
    // 2. Second save: published pending revision.
    $this->assertCount(2, $revision_sequence);
    $this->assertCount(2, $field_revision_sequence);

    // Verify the is_new_revision status.
    $this->assertTrue($revision_sequence[0]['is_new_revision']);
    $this->assertTrue($revision_sequence[1]['is_new_revision']);
    $this->assertTrue($field_revision_sequence[0]['is_new_revision']);
    $this->assertTrue($field_revision_sequence[1]['is_new_revision']);

    // Verify the is_new status.
    $this->assertTrue($revision_sequence[0]['is_new']);
    $this->assertFalse($revision_sequence[1]['is_new']);
    $this->assertTrue($field_revision_sequence[0]['is_new']);
    $this->assertFalse($field_revision_sequence[1]['is_new']);

    // Verify the publishing status of each revision.
    $this->assertFalse($revision_sequence[0]['is_published']);
    $this->assertTrue($revision_sequence[1]['is_published']);

    // The entity presave hook is fired after the field's presave()
    // implementation, so at this point the first revision is still published.
    $this->assertTrue($field_revision_sequence[0]['is_published']);
    $this->assertTrue($field_revision_sequence[1]['is_published']);

    // Verify the default revision status.
    $this->assertTrue($revision_sequence[0]['is_default_revision']);
    $this->assertFalse($revision_sequence[1]['is_default_revision']);
    $this->assertTrue($field_revision_sequence[0]['is_default_revision']);
    $this->assertFalse($field_revision_sequence[1]['is_default_revision']);
  }

}