Commit 79054d8a authored by catch's avatar catch
Browse files

Issue #3179199 by ekes, acrazyanimal, adriancid, smustgrave, sam152,...

Issue #3179199 by ekes, acrazyanimal, adriancid, smustgrave, sam152, amateescu, ravi.shankar, alexj12, s_leu, renatog: Content Moderation prevents workspace deployment

(cherry picked from commit 694cb802)
parent 2ffb236b
Loading
Loading
Loading
Loading
Loading
+15 −4
Original line number Diff line number Diff line
@@ -39,9 +39,6 @@ public function onWorkspacePrePublish(WorkspacePublishEvent $event): void {
    $workspace = $event->getWorkspace();

    $tracked_revisions = $this->workspaceAssociation->getTrackedEntities($workspace->id());
    // Extract all the second-level keys (revision IDs) of the two-dimensional
    // array.
    $tracked_revision_ids = array_reduce(array_map('array_keys', $tracked_revisions), 'array_merge', []);

    // Gather a list of moderation states that don't create a default revision.
    $workflow_non_default_states = [];
@@ -57,12 +54,26 @@ public function onWorkspacePrePublish(WorkspacePublishEvent $event): void {
      }
    }

    // If no tracked revisions are moderated then no status check is necessary.
    if (empty($workflow_non_default_states)) {
      return;
    }

    // Check if any revisions that are about to be published are in a
    // non-default revision moderation state.
    $query = $this->entityTypeManager->getStorage('content_moderation_state')->getQuery()
      ->allRevisions()
      ->accessCheck(FALSE);
    $query->condition('content_entity_revision_id', $tracked_revision_ids, 'IN');

    $tracked_revisions_condition_group = $query->orConditionGroup();
    foreach ($tracked_revisions as $tracked_type => $tracked_revision_ids) {
      $entity_type_group = $query->andConditionGroup()
        ->condition('content_entity_type_id', $tracked_type)
        ->condition('content_entity_revision_id', array_keys($tracked_revision_ids), 'IN');

      $tracked_revisions_condition_group->condition($entity_type_group);
    }
    $query->condition($tracked_revisions_condition_group);

    $workflow_condition_group = $query->orConditionGroup();
    foreach ($workflow_non_default_states as $workflow_id => $non_default_states) {
+81 −2
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@
namespace Drupal\Tests\content_moderation\Kernel;

use Drupal\Core\Entity\EntityInterface;
use Drupal\entity_test\Entity\EntityTestMulRevPub;
use Drupal\node\Entity\Node;
use Drupal\Tests\content_moderation\Traits\ContentModerationTestTrait;
use Drupal\Tests\node\Traits\ContentTypeCreationTrait;
@@ -84,8 +85,6 @@ public function testWorkspaceEntityTypeModeration(): void {

  /**
   * Tests the integration between Content Moderation and Workspaces.
   *
   * @see content_moderation_workspace_access()
   */
  public function testContentModerationIntegrationWithWorkspaces(): void {
    $editorial = $this->createEditorialWorkflow();
@@ -174,6 +173,86 @@ public function testContentModerationIntegrationWithWorkspaces(): void {
    $this->workspaces['stage']->publish();
  }

  /**
   * Publish a workspace with workflows including no tracked default revisions.
   */
  public function testContentModerationWithoutDefaultRevisionsInWorkspaces(): void {
    $access_handler = $this->container->get('entity_type.manager')->getAccessControlHandler('workspace');
    // Create a workflow which has the same states as the 'editorial' one,
    // but it doesn't create any default revisions. This covers the case when a
    // workspace is published containing no tracked types. This has to be the
    // only workflow.
    $editorial = $this->createEditorialWorkflow();
    $type_settings = $editorial->get('type_settings');
    $type_settings['states']['draft']['default_revision'] = FALSE;
    $type_settings['states']['archived']['default_revision'] = FALSE;
    $this->workspaceManager->executeOutsideWorkspace(function () use ($editorial) {
      $editorial->save();
    });
    // Create an node bundle 'note' that uses non-default workflow.
    $this->createContentType(['type' => 'note']);

    // Create content in all states none with default revisions.
    $note_archived = Node::create(['type' => 'note', 'title' => 'Test note - archived', 'moderation_state' => 'archived']);
    $note_archived->save();
    $note_draft = Node::create(['type' => 'note', 'title' => 'Test note - draft', 'moderation_state' => 'draft']);
    $note_draft->save();
    $note_published = Node::create(['type' => 'note', 'title' => 'Test note - published', 'moderation_state' => 'published']);
    $note_published->save();

    // Check workspace can be published.
    $access_handler->resetCache();
    $this->workspaces['stage']->publish();
  }

  /**
   * Publish a workspace with multiple entities from different entity types.
   */
  public function testContentModerationMultipleEntityTypesWithWorkspaces(): void {
    $editorial = $this->createEditorialWorkflow();
    $this->createContentType(['type' => 'page']);
    $this->addEntityTypeAndBundleToWorkflow($editorial, 'node', 'page');
    $this->addEntityTypeAndBundleToWorkflow($editorial, 'entity_test_mulrevpub', 'entity_test_mulrevpub');

    // Create an entity with a previous revision that is tracked in unpublished
    // state.
    $entity_with_revision = EntityTestMulRevPub::create([
      'title' => 'Test entity mulrevpub',
      'type' => 'entity_test_mulrevpub',
      'moderation_state' => 'draft',
    ]);
    $entity_with_revision->save();
    $entity_with_revision->save();
    $entity_with_revision = $this->reloadEntity($entity_with_revision);
    // Confirm unpublished earlier revision.
    $this->assertEquals('draft', $entity_with_revision->moderation_state->value);
    $earlier_revision_id = $entity_with_revision->getRevisionId();
    // Publish.
    $entity_with_revision->moderation_state->value = 'published';
    $entity_with_revision->save();
    $entity_with_revision = $this->reloadEntity($entity_with_revision);
    // Confirm publish revision.
    $this->assertEquals('published', $entity_with_revision->moderation_state->value);
    $published_revision_id = $entity_with_revision->getRevisionId();
    $this->assertNotEquals($earlier_revision_id, $published_revision_id);

    // Create an entity that has a default revision id the same as the previous
    // entity's old revision.
    $entity_without_revision = Node::create([
      'title' => 'Test node page',
      'type' => 'page',
      'moderation_state' => 'published',
    ]);
    $entity_without_revision->save();
    $entity_without_revision = $this->reloadEntity($entity_without_revision);
    $this->assertEquals('published', $entity_without_revision->moderation_state->value);

    // Current published revisions of second entity has the same revision as
    // earlier unpublished revision of first entity.
    $this->assertEquals($entity_without_revision->getRevisionId(), $earlier_revision_id);
    $this->workspaces['stage']->publish();
  }

  /**
   * Test cases for basic moderation test.
   */