diff --git a/src/Plugin/DisplayVariant/BlockDisplayVariant.php b/src/Plugin/DisplayVariant/BlockDisplayVariant.php index 238aa46..87ce680 100644 --- a/src/Plugin/DisplayVariant/BlockDisplayVariant.php +++ b/src/Plugin/DisplayVariant/BlockDisplayVariant.php @@ -68,6 +68,15 @@ protected $token; /** + * An array of collected contexts. + * + * This is only used on runtime, and is not stored. + * + * @var \Drupal\Component\Plugin\Context\ContextInterface[] + */ + protected $contexts = []; + + /** * Constructs a new BlockDisplayVariant. * * @param array $configuration @@ -112,26 +121,6 @@ public static function create(ContainerInterface $container, array $configuratio /** * {@inheritdoc} */ - public function build() { - // Set default page cache keys that include the display. - $build['#cache']['keys'] = [ - 'ctools_block_display', - // The UUID of this display. - // @todo should have an API for this? - $this->configuration['uuid'], - ]; - $build['#pre_render'][] = [$this, 'buildRegions']; - return $build; - } - - /** - * #pre_render callback for building the regions. - */ - abstract public function buildRegions(array $build); - - /** - * {@inheritdoc} - */ public function access(AccountInterface $account = NULL) { // Delegate to the conditions. return $this->determineSelectionAccess($this->getContexts()); @@ -180,6 +169,29 @@ public function getSelectionLogic() { } /** + * Gets the contexts. + * + * @return \Drupal\Component\Plugin\Context\ContextInterface[] + * An array of set contexts, keyed by context name. + */ + public function getContexts() { + return $this->contexts; + } + + /** + * Sets the contexts. + * + * @param \Drupal\Component\Plugin\Context\ContextInterface[] $contexts + * An array of contexts, keyed by context name. + * + * @return $this + */ + public function setContexts(array $contexts) { + $this->contexts = $contexts; + return $this; + } + + /** * {@inheritdoc} */ protected function contextHandler() { diff --git a/tests/src/Unit/BlockDisplayVariantTest.php b/tests/src/Unit/BlockDisplayVariantTest.php index b2dd2d7..282b116 100644 --- a/tests/src/Unit/BlockDisplayVariantTest.php +++ b/tests/src/Unit/BlockDisplayVariantTest.php @@ -8,18 +8,12 @@ namespace Drupal\Tests\ctools\Unit; use Drupal\Component\Uuid\UuidInterface; -use Drupal\Core\Access\AccessResult; -use Drupal\Core\Block\BlockPluginInterface; -use Drupal\Core\Cache\Context\CacheContextsManager; use Drupal\Core\Plugin\Context\ContextHandlerInterface; -use Drupal\Core\Plugin\ContextAwarePluginInterface; use Drupal\Core\Form\FormState; use Drupal\Core\Session\AccountInterface; use Drupal\Core\Utility\Token; -use Drupal\ctools\Plugin\BlockPluginCollection; use Drupal\ctools\Plugin\DisplayVariant\BlockDisplayVariant; use Drupal\Tests\UnitTestCase; -use Symfony\Component\DependencyInjection\ContainerBuilder; /** * Tests the block display variant plugin. @@ -56,162 +50,6 @@ public function testAccess() { } /** - * Tests the build() method when a block is empty. - * - * @covers ::build - */ - public function testBuildEmptyBlock() { - $account = $this->prophesize(AccountInterface::class); - $block1 = $this->prophesize(BlockPluginInterface::class); - $block1->access($account)->willReturn(TRUE); - - // Building a block with empty content. - $block1->build()->willReturn(['#cache' => [ 'tags' => [ 0 => 'tag_to_be_merged']]]); - - $context_handler = $this->prophesize(ContextHandlerInterface::class); - $uuid_generator = $this->prophesize(UuidInterface::class); - $token = $this->prophesize(Token::class); - - $display_variant = new TestBlockDisplayVariant([], '', [], $context_handler->reveal(), $account->reveal(), $uuid_generator->reveal(), $token->reveal()); - - // Empty block. - $expected_build = [ - '#markup' => '', - '#cache' => [ - 'tags' => [ - 'block_plugin:block_plugin_id', - 'page:page_id', - 'tag_to_be_merged', - ], - 'contexts' => [], - 'max-age' => -1, - ], - ]; - - $build = [ - '#block_plugin' => $block1->reveal(), - '#cache' => [ - 'tags' => [ - 'page:page_id', - 'block_plugin:block_plugin_id', - ], - ], - ]; - - $build = $display_variant->buildBlock($build); - - // Assert that cacheability metadata is merged. - $this->assertSame($expected_build, $build); - } - - /** - * Tests the build() method when blocks can be cached. - * - * @covers ::build - */ - public function testBuild() { - $container = new ContainerBuilder(); - $cache_contexts = $this->prophesize(CacheContextsManager::class); - $container->set('cache_contexts_manager', $cache_contexts->reveal()); - \Drupal::setContainer($container); - - $account = $this->prophesize(AccountInterface::class); - - // Define one block that allows access, access varies by permissions. - $block1 = $this->prophesize(BlockPluginInterface::class); - $block1->access($account, TRUE)->willReturn(AccessResult::allowed()->cachePerPermissions()); - $block1->getConfiguration()->willReturn(['label' => 'Block label']); - $block1->getPluginId()->willReturn('block_plugin_id'); - $block1->getBaseId()->willReturn('block_base_plugin_id'); - $block1->getDerivativeId()->willReturn('block_derivative_plugin_id'); - $block1->getCacheTags()->willReturn(['block_plugin1:block_plugin_id']); - $block1->getCacheMaxAge()->willReturn(3600); - $block1->getCacheContexts()->willReturn(['url']); - - // Define another block that doesn't allow access - $block2 = $this->prophesize()->willImplement(ContextAwarePluginInterface::class)->willImplement(BlockPluginInterface::class); - $block2->access($account, TRUE)->willReturn(AccessResult::forbidden()->cachePerUser()); - $block2->getConfiguration()->willReturn([]); - $block2->getPluginId()->willReturn('block_plugin_id'); - $block2->getBaseId()->willReturn('block_base_plugin_id'); - $block2->getDerivativeId()->willReturn('block_derivative_plugin_id'); - // The block is not shown, so cacheability metadata is not collected. - $block2->getCacheContexts()->shouldNotBeCalled(); - $block2->getCacheMaxAge()->shouldNotBeCalled(); - $block2->getCacheTags()->shouldNotBeCalled(); - $blocks = [ - 'top' => [ - 'block1' => $block1->reveal(), - 'block2' => $block2->reveal(), - ], - ]; - $block_collection = $this->getMockBuilder(BlockPluginCollection::class) - ->disableOriginalConstructor() - ->getMock(); - $block_collection->expects($this->once()) - ->method('getAllByRegion') - ->willReturn($blocks); - - $context_handler = $this->prophesize(ContextHandlerInterface::class); - $context_handler->applyContextMapping($block2->reveal(), [])->shouldBeCalledTimes(1); - - $uuid_generator = $this->prophesize(UuidInterface::class); - $page_title = 'Page title'; - $token = $this->getMockBuilder(Token::class) - ->disableOriginalConstructor() - ->getMock(); - $display_variant = $this->getMockBuilder(BlockDisplayVariant::class) - ->setConstructorArgs([['page_title' => $page_title, 'uuid' => 'UUID'], 'test', [], $context_handler->reveal(), $account->reveal(), $uuid_generator->reveal(), $token]) - ->setMethods(['getBlockCollection', 'renderPageTitle']) - ->getMock(); - - $page = $this->prophesize(PageInterface::class); - $page->id()->willReturn('page_id'); - - $display_variant->expects($this->once()) - ->method('getBlockCollection') - ->willReturn($block_collection); - $display_variant->expects($this->once()) - ->method('renderPageTitle') - ->with($page_title) - ->willReturn($page_title); - - $expected_cache_block1 = [ - 'keys' => ['page_manager_block_display', 'UUID', 'block', 'block1'], - 'tags' => ['block_plugin1:block_plugin_id'], - 'contexts' => ['url'], - 'max-age' => 3600, - ]; - - // The page cacheability metadata contains the access cacheability metadata - // of accessible and non-accessible blocks. Additionally, the cacheability - // metadata of accessible blocks is merged to avoid cache redirects when - // possible. - $expected_cache_page = [ - 'keys' => ['page_manager_block_display', 'UUID'], - 'contexts' => ['url', 'user', 'user.permissions'], - 'tags' => ['block_plugin1:block_plugin_id'], - 'max-age' => 3600, - ]; - - // Build the variant and ensure that pre_render is set only for the first - // block. - $build = $display_variant->build(); - $build = $display_variant->buildRegions($build); - $this->assertSame([$display_variant, 'buildBlock'], $build['top']['block1']['#pre_render'][0]); - $this->assertTrue(empty($build['top']['block2'])); - $this->assertSame($expected_cache_block1, $build['top']['block1']['#cache']); - $this->assertSame($expected_cache_page, $build['#cache']); - - // Ensure that building the block returns the correct markup. - $block1->build()->willReturn([ - '#markup' => 'block1_build_value', - ]); - $block1_build = $display_variant->buildBlock($build['top']['block1']); - $this->assertSame(['#markup' => 'block1_build_value'], $block1_build['content']); - } - - /** * Tests the submitConfigurationForm() method. * * @covers ::submitConfigurationForm @@ -224,7 +62,7 @@ public function testSubmitConfigurationForm($values) { $uuid_generator = $this->prophesize(UuidInterface::class); $token = $this->prophesize(Token::class); - $display_variant = new BlockDisplayVariant([], '', [], $context_handler->reveal(), $account->reveal(), $uuid_generator->reveal(), $token->reveal()); + $display_variant = new TestBlockDisplayVariant([], '', [], $context_handler->reveal(), $account->reveal(), $uuid_generator->reveal(), $token->reveal()); $form = []; $form_state = (new FormState())->setValues($values); @@ -265,158 +103,14 @@ class TestBlockDisplayVariant extends BlockDisplayVariant { * {@inheritdoc} */ public function build() { - // Set default page cache keys that include the display. - $build['#cache']['keys'] = [ - 'test_block_display', - // The UUID of this display. - // @todo should have an API for this? - $this->configuration['uuid'], - ]; - $build['#pre_render'][] = [$this, 'buildRegions']; - return $build; + return []; } - /** - * #pre_render callback for building the regions. - */ - public function buildRegions(array $build) { - $cacheability = CacheableMetadata::createFromRenderArray($build) - ->addCacheableDependency($this); - - $contexts = $this->getContexts(); - foreach ($this->getRegionAssignments() as $region => $blocks) { - if (!$blocks) { - continue; - } - - $region_name = Html::getClass("block-region-$region"); - $build[$region]['#prefix'] = '