From 109fe60843cd29d61432979145ff055ebdd2fab6 Mon Sep 17 00:00:00 2001 From: Lee Rowlands <lee.rowlands@previousnext.com.au> Date: Tue, 18 Aug 2020 09:16:31 +1000 Subject: [PATCH] Issue #3053887 by Sam152, Lal_, fenstrat, acbramley, tedbow, larowlan: Add test coverage and document why inline blocks require a new revision to be created when modified, regardless of whether a new revision of the parent has been created --- .../src/InlineBlockEntityOperations.php | 18 ++-- .../src/Plugin/Block/InlineBlock.php | 4 +- .../FunctionalJavascript/InlineBlockTest.php | 86 +++++++++++++++++++ 3 files changed, 94 insertions(+), 14 deletions(-) diff --git a/core/modules/layout_builder/src/InlineBlockEntityOperations.php b/core/modules/layout_builder/src/InlineBlockEntityOperations.php index 86c8adc442ee..9760243bf316 100644 --- a/core/modules/layout_builder/src/InlineBlockEntityOperations.php +++ b/core/modules/layout_builder/src/InlineBlockEntityOperations.php @@ -163,18 +163,10 @@ public function handlePreSave(EntityInterface $entity) { // duplicated. $duplicate_blocks = TRUE; } - $new_revision = FALSE; - if ($entity instanceof RevisionableInterface) { - // If the parent entity will have a new revision create a new revision - // of the block. - // @todo Currently revisions are never created for the parent entity. - // This will be fixed in https://www.drupal.org/node/2937199. - // To work around this always make a revision when the parent entity - // is an instance of RevisionableInterface. After the issue is fixed - // only create a new revision if '$entity->isNewRevision()'. - $new_revision = TRUE; - } - + // Since multiple parent entity revisions may reference common block + // revisions, when a block is modified, it must always result in the + // creation of a new block revision. + $new_revision = $entity instanceof RevisionableInterface; foreach ($this->getInlineBlockComponents($sections) as $component) { $this->saveInlineBlockComponent($entity, $component, $new_revision, $duplicate_blocks); } @@ -252,7 +244,7 @@ protected function getBlockIdsForRevisionIds(array $revision_ids) { * @param \Drupal\layout_builder\SectionComponent $component * The section component with an inline block. * @param bool $new_revision - * Whether a new revision of the block should be created. + * Whether a new revision of the block should be created when modified. * @param bool $duplicate_blocks * Whether the blocks should be duplicated. */ diff --git a/core/modules/layout_builder/src/Plugin/Block/InlineBlock.php b/core/modules/layout_builder/src/Plugin/Block/InlineBlock.php index 0f2288d7b508..9f347174d363 100644 --- a/core/modules/layout_builder/src/Plugin/Block/InlineBlock.php +++ b/core/modules/layout_builder/src/Plugin/Block/InlineBlock.php @@ -263,7 +263,7 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta * Saves the block_content entity for this plugin. * * @param bool $new_revision - * Whether to create new revision. + * Whether to create new revision, if the block was modified. * @param bool $duplicate_block * Whether to duplicate the "block_content" entity. */ @@ -283,6 +283,8 @@ public function saveBlockContent($new_revision = FALSE, $duplicate_block = FALSE } if ($block) { + // Since the custom block is only set if it was unserialized, the flag + // will only effect blocks which were modified or serialized originally. if ($new_revision) { $block->setNewRevision(); } diff --git a/core/modules/layout_builder/tests/src/FunctionalJavascript/InlineBlockTest.php b/core/modules/layout_builder/tests/src/FunctionalJavascript/InlineBlockTest.php index d266386130ce..856a4a83a2a8 100644 --- a/core/modules/layout_builder/tests/src/FunctionalJavascript/InlineBlockTest.php +++ b/core/modules/layout_builder/tests/src/FunctionalJavascript/InlineBlockTest.php @@ -263,6 +263,92 @@ public function testInlineBlocksRevisioning() { $assert_session->pageTextNotContains('The NEW block body'); } + /** + * Tests entity blocks revisioning. + */ + public function testInlineBlocksRevisioningIntegrity() { + $this->drupalLogin($this->drupalCreateUser([ + 'access contextual links', + 'configure any layout', + 'administer node display', + 'view all revisions', + 'access content', + 'create and edit custom blocks', + ])); + $this->drupalPostForm( + static::FIELD_UI_PREFIX . '/display/default', + ['layout[enabled]' => TRUE, 'layout[allow_custom]' => TRUE], + 'Save' + ); + + $block_1_locator = static::INLINE_BLOCK_LOCATOR; + $block_2_locator = sprintf('%s + %s', static::INLINE_BLOCK_LOCATOR, static::INLINE_BLOCK_LOCATOR); + + // Add two blocks to the page and assert the content in each. + $this->drupalGet('node/1/layout'); + $this->addInlineBlockToLayout('Block 1', 'Block 1 original'); + $this->addInlineBlockToLayout('Block 2', 'Block 2 original'); + $this->assertSaveLayout(); + $this->assertNodeRevisionContent(3, ['Block 1 original', 'Block 2 original']); + $this->assertBlockRevisionCountByTitle('Block 1', 1); + $this->assertBlockRevisionCountByTitle('Block 2', 1); + + // Update the contents of one of the blocks and assert the updated content + // appears on the next revision. + $this->drupalGet('node/1/layout'); + $this->configureInlineBlock('Block 2 original', 'Block 2 updated', $block_2_locator); + $this->assertSaveLayout(); + $this->assertNodeRevisionContent(4, ['Block 1 original', 'Block 2 updated']); + $this->assertBlockRevisionCountByTitle('Block 1', 1); + $this->assertBlockRevisionCountByTitle('Block 2', 2); + + // Update block 1 without creating a new revision of the parent. + $this->drupalGet('node/1/layout'); + $this->configureInlineBlock('Block 1 original', 'Block 1 updated', $block_1_locator); + $this->getSession()->getPage()->uncheckField('revision'); + $this->getSession()->getPage()->pressButton('Save layout'); + $this->assertNotEmpty($this->assertSession()->waitForElement('css', '.messages--status')); + $this->assertNodeRevisionContent(4, ['Block 1 updated', 'Block 2 updated']); + $this->assertBlockRevisionCountByTitle('Block 1', 2); + $this->assertBlockRevisionCountByTitle('Block 2', 2); + + // Reassert all of the parent revisions contain the correct block content + // and the integrity of the revisions was preserved. + $this->assertNodeRevisionContent(3, ['Block 1 original', 'Block 2 original']); + } + + /** + * Assert the contents of a node revision. + * + * @param int $revision_id + * The revision ID to assert. + * @param array $content + * The content items to assert on the page. + */ + protected function assertNodeRevisionContent($revision_id, array $content) { + $this->drupalGet("node/1/revisions/$revision_id/view"); + foreach ($content as $content_item) { + $this->assertSession()->pageTextContains($content_item); + } + } + + /** + * Assert the number of block content revisions by the block title. + * + * @param string $block_title + * The block title. + * @param int $expected_revision_count + * The revision count. + */ + protected function assertBlockRevisionCountByTitle($block_title, $expected_revision_count) { + $actual_revision_count = $this->blockStorage->getQuery() + ->condition('info', $block_title) + ->allRevisions() + ->count() + ->execute(); + $this->assertEquals($actual_revision_count, $expected_revision_count); + } + /** * Tests that entity blocks deleted correctly. */ -- GitLab