diff --git a/core/modules/block_content/src/Event/BlockContentGetDependencyEvent.php b/core/modules/block_content/src/Event/BlockContentGetDependencyEvent.php index ebf8009909..ebf37317ac 100644 --- a/core/modules/block_content/src/Event/BlockContentGetDependencyEvent.php +++ b/core/modules/block_content/src/Event/BlockContentGetDependencyEvent.php @@ -28,7 +28,7 @@ class BlockContentGetDependencyEvent extends Event { protected $accessDependency; /** - * The operation. + * The access operation to load the block content dependency for. * * @var string */ @@ -40,7 +40,7 @@ class BlockContentGetDependencyEvent extends Event { * @param \Drupal\block_content\BlockContentInterface $blockContent * The block content entity. * @param string $operation - * The access operation. + * The access operation to load the block content dependency for. */ public function __construct(BlockContentInterface $blockContent, $operation) { $this->blockContent = $blockContent; @@ -58,7 +58,7 @@ public function getBlockContentEntity() { } /** - * Get the access operation. + * Get the access operation for this dependency event. * * @return string * The access operation. diff --git a/core/modules/layout_builder/src/EventSubscriber/SetInlineBlockDependency.php b/core/modules/layout_builder/src/EventSubscriber/SetInlineBlockDependency.php index 8544aec9d2..84a0db20f2 100644 --- a/core/modules/layout_builder/src/EventSubscriber/SetInlineBlockDependency.php +++ b/core/modules/layout_builder/src/EventSubscriber/SetInlineBlockDependency.php @@ -117,7 +117,7 @@ public function onGetDependency(BlockContentGetDependencyEvent $event) { * @param \Drupal\block_content\BlockContentInterface $block_content * The block content entity. * @param string $operation - * The operation. + * The access operation to load the inline block dependency for. * * @return \Drupal\Core\Entity\EntityInterface|null * Returns the layout dependency. @@ -132,7 +132,10 @@ protected function getInlineBlockDependency(BlockContentInterface $block_content // dependency. It may be used by another module besides layout builder. return NULL; } - if (in_array($operation, ['update', 'delete'])) { + // When updating or deleting an inline block, resolve the inline block + // dependency via the active revision, since it is the revision that should + // be loaded for editing purposes. + if (in_array($operation, ['update', 'delete'], TRUE)) { $layout_entity = $this->entityRepository->getActive($layout_entity_info->layout_entity_type, $layout_entity_info->layout_entity_id); } else { diff --git a/core/modules/layout_builder/tests/src/Kernel/SetInlineBlockDependencyTest.php b/core/modules/layout_builder/tests/src/Kernel/SetInlineBlockDependencyTest.php new file mode 100644 index 0000000000..657d2bea7e --- /dev/null +++ b/core/modules/layout_builder/tests/src/Kernel/SetInlineBlockDependencyTest.php @@ -0,0 +1,182 @@ +setUpCurrentUser(); + $this->installSchema('system', ['key_value_expire']); + $this->installSchema('layout_builder', ['inline_block_usage']); + + $this->installEntitySchema('entity_test_mulrevpub'); + $this->installEntitySchema('block_content'); + $this->installEntitySchema('content_moderation_state'); + + BlockContentType::create([ + 'id' => 'basic', + 'label' => 'Basic block', + 'revision' => 1, + ])->save(); + + $display = LayoutBuilderEntityViewDisplay::create([ + 'targetEntityType' => 'entity_test_mulrevpub', + 'bundle' => 'entity_test_mulrevpub', + 'mode' => 'default', + 'status' => TRUE, + ]); + $display->enableLayoutBuilder(); + $display->setOverridable(); + $display->save(); + + $workflow = $this->createEditorialWorkflow(); + $workflow->getTypePlugin()->addEntityTypeAndBundle('entity_test_mulrevpub', 'entity_test_mulrevpub'); + $workflow->save(); + } + + /** + * Test inline block dependencies with a default revision entity host. + */ + public function testInlineBlockDependencyDefaultRevision() { + $entity = EntityTestMulRevPub::create(); + $entity->save(); + $block = $this->addInlineBlockToOverrideLayout($entity); + $account = $this->createUser([ + 'create and edit custom blocks', + 'view test entity', + 'use editorial transition create_new_draft', + 'use editorial transition publish', + ]); + $this->assertTrue($block->access('view', $account)); + $this->assertTrue($block->access('update', $account)); + $this->assertTrue($block->access('delete', $account)); + } + + /** + * Test inline block dependencies with a non-default revision entity host. + */ + public function testInlineBlockDependencyNonDefaultActiveRevision() { + // Create the canonical revision. + $entity = EntityTestMulRevPub::create(['moderation_state' => 'published']); + $entity->save(); + + // Create and add a custom block to a new active revision. + $entity->moderation_state = 'draft'; + $block = $this->addInlineBlockToOverrideLayout($entity); + + $account = $this->createUser([ + 'create and edit custom blocks', + 'view test entity', + 'use editorial transition create_new_draft', + 'use editorial transition publish', + ]); + // The block does not exist on the canonical revision, so access will not be + // granted since the custom block will not have a resolved dependency via + // the canonical revision. Some components may choose to manually set a + // different revision as the block dependent when displaying a non-canonical + // revision of the entity, such as the content moderation latest-version + // route. @see + // \Drupal\layout_builder\EventSubscriber\BlockComponentRenderArray::onBuildRender. + $this->assertFalse($block->access('view', $account)); + // Access to update the block is resolved and granted via the 'active' + // revision of the entity. Update access on the content block itself must be + // granted so that access checks outside of the layout builder routes are + // correctly granted. + $this->assertTrue($block->access('update', $account)); + $this->assertTrue($block->access('delete', $account)); + } + + /** + * Test the inline block dependency when removed from the active revision. + */ + public function testInlineBlockDependencyRemovedInActiveRevision() { + // Create the canonical revision with an inline block. + $entity = EntityTestMulRevPub::create(['moderation_state' => 'published']); + $entity->save(); + $block = $this->addInlineBlockToOverrideLayout($entity); + + // Create an active revision that removes the inline block. + $entity->{OverridesSectionStorage::FIELD_NAME} = []; + $entity->moderation_state = 'draft'; + $entity->save(); + + $account = $this->createUser([ + 'create and edit custom blocks', + 'view test entity', + 'use editorial transition create_new_draft', + 'use editorial transition publish', + ]); + // Access to update the block will be resolved through the active revision + // and denied, since the block has been removed from the layout. + $this->assertFalse($block->access('update', $account)); + $this->assertFalse($block->access('delete', $account)); + // Access to view the block will be resolved through the canonical revision + // and granted, since the block still exists on the canonical revision. + $this->assertTrue($block->access('view', $account)); + } + + /** + * Add an inline block to an override layout of an entity. + * + * @param \Drupal\entity_test\Entity\EntityTestMulRevPub $entity + * The entity to add an inline block to. + * + * @return \Drupal\block_content\Entity\BlockContent + * The loaded block content revision attached to the layout. + */ + protected function addInlineBlockToOverrideLayout($entity) { + $block = BlockContent::create([ + 'type' => 'basic', + 'reusable' => FALSE, + ]); + $section_data = new Section('layout_default', [], [ + 'first-uuid' => new SectionComponent('first-uuid', 'content', [ + 'id' => sprintf('inline_block:basic'), + 'block_serialized' => serialize($block), + ]), + ]); + $entity->{OverridesSectionStorage::FIELD_NAME} = $section_data; + $entity->save(); + $inline_block_revision_id = $entity->{OverridesSectionStorage::FIELD_NAME}->getSections()[0]->getComponent('first-uuid')->getPlugin()->getConfiguration()['block_revision_id']; + return $this->container->get('entity_type.manager')->getStorage('block_content')->loadRevision($inline_block_revision_id); + } + +}