diff --git a/core/modules/content_moderation/src/EventSubscriber/WorkspaceSubscriber.php b/core/modules/content_moderation/src/EventSubscriber/WorkspaceSubscriber.php index dbc6cf65c884967b069710fa2d7854f01614775c..c03a33e8cb799f2651ba24b45c00884eec4f11c3 100644 --- a/core/modules/content_moderation/src/EventSubscriber/WorkspaceSubscriber.php +++ b/core/modules/content_moderation/src/EventSubscriber/WorkspaceSubscriber.php @@ -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) { diff --git a/core/modules/content_moderation/tests/src/Kernel/WorkspacesContentModerationStateTest.php b/core/modules/content_moderation/tests/src/Kernel/WorkspacesContentModerationStateTest.php index f8d3002889baf2ca12a30c8c859ed1ca905413be..80e274146dbf523c78cccf95fbc1efd2e1e67ee3 100644 --- a/core/modules/content_moderation/tests/src/Kernel/WorkspacesContentModerationStateTest.php +++ b/core/modules/content_moderation/tests/src/Kernel/WorkspacesContentModerationStateTest.php @@ -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. */