diff --git a/core/modules/content_moderation/content_moderation.module b/core/modules/content_moderation/content_moderation.module index 8fc2c4ceb6913972e868f1f65c37d0c2f1f3606a..846575382be42e5a97e7ecd5ce4ada95f918583a 100644 --- a/core/modules/content_moderation/content_moderation.module +++ b/core/modules/content_moderation/content_moderation.module @@ -11,6 +11,7 @@ use Drupal\content_moderation\Plugin\Action\ModerationOptOutPublish; use Drupal\content_moderation\Plugin\Action\ModerationOptOutUnpublish; use Drupal\Core\Access\AccessResult; +use Drupal\Core\Entity\Display\EntityFormDisplayInterface; use Drupal\Core\Entity\Display\EntityViewDisplayInterface; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityPublishedInterface; @@ -191,6 +192,17 @@ function content_moderation_entity_view(array &$build, EntityInterface $entity, ->entityView($build, $entity, $display, $view_mode); } +/** + * Implements hook_layout_builder_overrides_entity_form_display_alter(). + */ +function content_moderation_layout_builder_overrides_entity_form_display_alter(EntityFormDisplayInterface $display) { + $display->setComponent('moderation_state', [ + 'type' => 'moderation_state_default', + 'weight' => -900, + 'settings' => [], + ]); +} + /** * Implements hook_entity_access(). * diff --git a/core/modules/content_moderation/src/EntityTypeInfo.php b/core/modules/content_moderation/src/EntityTypeInfo.php index 30a0a51ca4507719a961e9846b198b73d032f8d3..f994a3ca47ae86c89086e4b66f107b2e37446430 100644 --- a/core/modules/content_moderation/src/EntityTypeInfo.php +++ b/core/modules/content_moderation/src/EntityTypeInfo.php @@ -340,7 +340,7 @@ public function formAlter(array &$form, FormStateInterface $form_state, $form_id // Move the 'moderation_state' field widget to the footer region, if // available. - if (isset($form['footer'])) { + if (isset($form['footer']) && in_array($form_object->getOperation(), ['edit', 'default'], TRUE)) { $form['moderation_state']['#group'] = 'footer'; } @@ -364,7 +364,7 @@ public function formAlter(array &$form, FormStateInterface $form_state, $form_id */ protected function isModeratedEntityEditForm(FormInterface $form_object) { return $form_object instanceof ContentEntityFormInterface && - in_array($form_object->getOperation(), ['edit', 'default'], TRUE) && + in_array($form_object->getOperation(), ['edit', 'default', 'layout_builder'], TRUE) && $this->moderationInfo->isModeratedEntity($form_object->getEntity()); } diff --git a/core/modules/content_moderation/tests/src/Functional/LayoutBuilderContentModerationIntegrationTest.php b/core/modules/content_moderation/tests/src/Functional/LayoutBuilderContentModerationIntegrationTest.php new file mode 100644 index 0000000000000000000000000000000000000000..e1a30e3af1076f5af4daa1bb6713840db49b0363 --- /dev/null +++ b/core/modules/content_moderation/tests/src/Functional/LayoutBuilderContentModerationIntegrationTest.php @@ -0,0 +1,129 @@ +<?php + +namespace Drupal\Tests\content_moderation\Functional; + +use Drupal\layout_builder\Entity\LayoutBuilderEntityViewDisplay; +use Drupal\Tests\BrowserTestBase; +use Drupal\Tests\content_moderation\Traits\ContentModerationTestTrait; + +/** + * Tests Content Moderation's integration with Layout Builder. + * + * @group content_moderation + * @group layout_builder + */ +class LayoutBuilderContentModerationIntegrationTest extends BrowserTestBase { + + use ContentModerationTestTrait; + + /** + * {@inheritdoc} + */ + protected static $modules = [ + 'layout_builder', + 'node', + 'content_moderation', + ]; + + /** + * {@inheritdoc} + */ + protected function setUp() { + parent::setUp(); + + // @todo The Layout Builder UI relies on local tasks; fix in + // https://www.drupal.org/project/drupal/issues/2917777. + $this->drupalPlaceBlock('local_tasks_block'); + + // Add a new bundle and add an editorial workflow. + $this->createContentType(['type' => 'bundle_with_section_field']); + $workflow = $this->createEditorialWorkflow(); + $workflow->getTypePlugin()->addEntityTypeAndBundle('node', 'bundle_with_section_field'); + $workflow->save(); + + // Enable layout overrides. + LayoutBuilderEntityViewDisplay::load('node.bundle_with_section_field.default') + ->enableLayoutBuilder() + ->setOverridable() + ->save(); + + $this->drupalLogin($this->drupalCreateUser([ + 'configure any layout', + 'edit any bundle_with_section_field content', + 'view bundle_with_section_field revisions', + 'revert bundle_with_section_field revisions', + 'view own unpublished content', + 'view latest version', + 'use editorial transition create_new_draft', + 'use editorial transition publish', + ])); + } + + /** + * Tests that Layout changes are respected by Content Moderation. + */ + public function testLayoutModeration() { + $page = $this->getSession()->getPage(); + $assert_session = $this->assertSession(); + + // Create an unpublished node. Revision count: 1. + $node = $this->createNode([ + 'type' => 'bundle_with_section_field', + 'title' => 'The first node title', + 'body' => [ + [ + 'value' => 'The first node body', + ], + ], + ]); + + $this->drupalGet($node->toUrl()); + // Publish the node. Revision count: 2. + $page->fillField('new_state', 'published'); + $page->pressButton('Apply'); + + // Modify the layout. + $page->clickLink('Layout'); + $assert_session->checkboxChecked('revision'); + $assert_session->fieldDisabled('revision'); + + $page->clickLink('Add Block'); + $page->clickLink('Powered by Drupal'); + $page->pressButton('Add Block'); + // Save the node as a draft. Revision count: 3. + $page->fillField('moderation_state[0][state]', 'draft'); + $page->pressButton('Save layout'); + + // Block is visible on the revision page. + $assert_session->addressEquals("node/{$node->id()}/latest"); + $assert_session->pageTextContains('Powered by Drupal'); + + // Block is visible on the layout form. + $page->clickLink('Layout'); + $assert_session->pageTextContains('Powered by Drupal'); + + // Block is not visible on the live node page. + $page->clickLink('View'); + $assert_session->pageTextNotContains('Powered by Drupal'); + + // Publish the node. Revision count: 4. + $page->clickLink('Latest version'); + $page->fillField('new_state', 'published'); + $page->pressButton('Apply'); + + // Block is visible on the live node page. + $assert_session->pageTextContains('Powered by Drupal'); + + // Revert to the previous revision. + $page->clickLink('Revisions'); + // Assert that there are 4 total revisions and 3 revert links. + $assert_session->elementsCount('named', ['link', 'Revert'], 3); + // Revert to the 2nd revision before modifying the layout. + $this->clickLink('Revert', 1); + $page->pressButton('Revert'); + + $page->clickLink('View'); + $assert_session->pageTextNotContains('Powered by Drupal'); + } + +}