diff --git a/config/schema/page_manager.schema.yml b/config/schema/page_manager.schema.yml index 1fe3464..b0795ca 100644 --- a/config/schema/page_manager.schema.yml +++ b/config/schema/page_manager.schema.yml @@ -101,6 +101,12 @@ page_manager.block_plugin.*: label: 'Context assignments' sequence: - type: string + visibility: + type: sequence + label: 'Visibility Conditions' + sequence: + type: condition.plugin.[id] + label: 'Visibility Condition' # @todo Move to core in https://www.drupal.org/node/2838130. display_variant.plugin.*: diff --git a/page_manager_ui/src/Form/ConditionSubform.php b/page_manager_ui/src/Form/ConditionSubform.php new file mode 100644 index 0000000..f52b38b --- /dev/null +++ b/page_manager_ui/src/Form/ConditionSubform.php @@ -0,0 +1,89 @@ +getValues() as $condition_id => $values) { + // All condition plugins use 'negate' as a Boolean in their schema. + // However, certain form elements may return it as 0/1. Cast here to + // ensure the data is in the expected type. + if (array_key_exists('negate', $values)) { + $form_state->setValue([$condition_id, 'negate'], (bool) $values['negate']); + } + + // Allow the condition to validate the form. + $condition = $form_state->get(['conditions', $condition_id]); + $subform_state = SubformState::createForSubform($form[$condition_id], $form, $form_state); + $condition->validateConfigurationForm($form[$condition_id], $subform_state); + } + } + + /** + * Helper function to independently submit conditions configuration. + * + * The condition plugins should already be added to the form state under the + * key 'conditions'. + * + * @param array $form + * A nested array of form elements comprising the form. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The current state of the form. + * @param \Drupal\Component\Plugin\LazyPluginCollection $condition_collection + * The condition plugin collection. + * + * @see \Drupal\block\BlockForm::submitVisibility() + */ + public function submit(array $form, FormStateInterface $form_state, $condition_collection) { + foreach ($form_state->getValues() as $condition_id => $values) { + // Allow the condition to submit the form. + $condition = $form_state->get(['conditions', $condition_id]); + $subform_state = SubformState::createForSubform($form[$condition_id], $form, $form_state); + $condition->submitConfigurationForm($form[$condition_id], $subform_state); + + // Setting conditions' context mappings is the plugins' responsibility. + // This code exists for backwards compatibility, because + // \Drupal\Core\Condition\ConditionPluginBase::submitConfigurationForm() + // did not set its own mappings until Drupal 8.2 + // @todo Remove the code that sets context mappings in Drupal 9.0.0. + if ($condition instanceof ContextAwarePluginInterface) { + $context_mapping = isset($values['context_mapping']) ? $values['context_mapping'] : []; + $condition->setContextMapping($context_mapping); + } + + $condition_configuration = $condition->getConfiguration(); + // Update the visibility conditions on the block. + $condition_collection->addInstanceId($condition_id, $condition_configuration); + } + } + +} diff --git a/page_manager_ui/src/Form/VariantPluginAddBlockForm.php b/page_manager_ui/src/Form/VariantPluginAddBlockForm.php index a372a12..3108268 100644 --- a/page_manager_ui/src/Form/VariantPluginAddBlockForm.php +++ b/page_manager_ui/src/Form/VariantPluginAddBlockForm.php @@ -9,6 +9,8 @@ namespace Drupal\page_manager_ui\Form; use Drupal\Component\Plugin\PluginManagerInterface; use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Language\LanguageManagerInterface; +use Drupal\Core\Plugin\Context\ContextRepositoryInterface; use Drupal\page_manager\PageVariantInterface; use Drupal\user\SharedTempStoreFactory; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -29,11 +31,18 @@ class VariantPluginAddBlockForm extends VariantPluginConfigureBlockFormBase { /** * Constructs a new VariantPluginFormBase. * + * @param \Drupal\user\SharedTempStoreFactory $tempstore * @param \Drupal\Component\Plugin\PluginManagerInterface $block_manager * The block manager. + * @param \Drupal\Component\Plugin\PluginManagerInterface $condition_manager + * The condition plugin manager. + * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager + * The language manager. + * @param \Drupal\Core\Plugin\Context\ContextRepositoryInterface $context_repository + * The context repository. */ - public function __construct(SharedTempStoreFactory $tempstore, PluginManagerInterface $block_manager) { - parent::__construct($tempstore); + public function __construct(SharedTempStoreFactory $tempstore, PluginManagerInterface $block_manager, PluginManagerInterface $condition_manager = NULL, LanguageManagerInterface $language_manager = NULL, ContextRepositoryInterface $context_repository = NULL) { + parent::__construct($tempstore, $condition_manager, $language_manager, $context_repository); $this->blockManager = $block_manager; } @@ -43,7 +52,10 @@ class VariantPluginAddBlockForm extends VariantPluginConfigureBlockFormBase { public static function create(ContainerInterface $container) { return new static( $container->get('user.shared_tempstore'), - $container->get('plugin.manager.block') + $container->get('plugin.manager.block'), + $container->get('plugin.manager.condition'), + $container->get('language_manager'), + $container->get('context.repository') ); } diff --git a/page_manager_ui/src/Form/VariantPluginConfigureBlockFormBase.php b/page_manager_ui/src/Form/VariantPluginConfigureBlockFormBase.php index 59de6c6..2fcf053 100644 --- a/page_manager_ui/src/Form/VariantPluginConfigureBlockFormBase.php +++ b/page_manager_ui/src/Form/VariantPluginConfigureBlockFormBase.php @@ -7,10 +7,15 @@ namespace Drupal\page_manager_ui\Form; +use Drupal\Component\Plugin\PluginManagerInterface; +use Drupal\Core\Condition\ConditionPluginCollection; use Drupal\Core\Form\FormBase; use Drupal\Core\Form\FormState; use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Form\SubformState; +use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\Plugin\Context\Context; +use Drupal\Core\Plugin\Context\ContextRepositoryInterface; use Drupal\Core\Plugin\ContextAwarePluginAssignmentTrait; use Drupal\Core\Plugin\ContextAwarePluginInterface; use Drupal\page_manager\PageVariantInterface; @@ -19,6 +24,10 @@ use Symfony\Component\DependencyInjection\ContainerInterface; /** * Provides a base form for configuring a block as part of a variant. + * + * @todo Rather than using the same interface as the core visibility UI, it + * might make sense to use a UI more like selection criteria. + * @see https://www.drupal.org/node/2858877#comment-12214040 */ abstract class VariantPluginConfigureBlockFormBase extends FormBase { @@ -46,13 +55,42 @@ abstract class VariantPluginConfigureBlockFormBase extends FormBase { protected $block; /** + * The condition plugin manager. + * + * @var \Drupal\Component\Plugin\PluginManagerInterface + */ + protected $conditionManager; + + /** + * The block's visibility condition collection. + * + * @var \Drupal\Core\Condition\ConditionPluginCollection + */ + protected $visibilityCollection; + + /** + * The visibility condition plugin UI helper object. + * + * @var \Drupal\page_manager_ui\Form\VisibilitySubform + */ + protected $visibilitySubform; + + /** * Constructs a new VariantPluginConfigureBlockFormBase. * * @param \Drupal\user\SharedTempStoreFactory $tempstore * The tempstore factory. */ - public function __construct(SharedTempStoreFactory $tempstore) { + public function __construct(SharedTempStoreFactory $tempstore, PluginManagerInterface $condition_manager = NULL, LanguageManagerInterface $language_manager = NULL, ContextRepositoryInterface $context_repository = NULL) { $this->tempstore = $tempstore; + + // The following parameters were added post-beta and so are optional. + $condition_manager = $condition_manager ?: \Drupal::service('plugin.manager.condition'); + $language_manager = $language_manager ?: \Drupal::service('language_manager'); + $context_repository = $context_repository ?: \Drupal::service('context.repository'); + + $this->conditionManager = $condition_manager; + $this->visibilitySubform = new VisibilitySubform($condition_manager, $language_manager, $context_repository); } /** @@ -60,7 +98,10 @@ abstract class VariantPluginConfigureBlockFormBase extends FormBase { */ public static function create(ContainerInterface $container) { return new static( - $container->get('user.shared_tempstore') + $container->get('user.shared_tempstore'), + $container->get('plugin.manager.condition'), + $container->get('language_manager'), + $container->get('context.repository') ); } @@ -137,6 +178,7 @@ abstract class VariantPluginConfigureBlockFormBase extends FormBase { '#default_value' => $this->getVariantPlugin()->getRegionAssignment($this->block->getConfiguration()['uuid']), '#required' => TRUE, ]; + $form['visibility'] = $this->visibilitySubform->build([], $form_state, $this->getVisibility()); if ($this->block instanceof ContextAwarePluginInterface) { $form['context_mapping'] = $this->addContextAssignmentElement($this->block, $this->getVariantPlugin()->getContexts()); @@ -163,6 +205,9 @@ abstract class VariantPluginConfigureBlockFormBase extends FormBase { $this->block->validateConfigurationForm($form, $settings); // Update the original form values. $form_state->setValue('settings', $settings->getValues()); + // Call the condition plugin validate handlers. + $subform_state = SubformState::createForSubform($form['visibility'], $form, $form_state); + $this->visibilitySubform->validate($form['visibility'], $subform_state); } /** @@ -179,8 +224,13 @@ abstract class VariantPluginConfigureBlockFormBase extends FormBase { if ($this->block instanceof ContextAwarePluginInterface) { $this->block->setContextMapping($form_state->getValue('context_mapping', [])); } + // Submit the visibility subform. + $subform_state = SubformState::createForSubform($form['visibility'], $form, $form_state); + $this->visibilitySubform->submit($form['visibility'], $subform_state, $this->getVisibilityConditions()); - $this->getVariantPlugin()->updateBlock($this->block->getConfiguration()['uuid'], ['region' => $form_state->getValue('region')]); + $configuration = ['region' => $form_state->getValue('region')]; + $configuration['visibility'] = $this->getVisibilityConditions()->getConfiguration(); + $this->getVariantPlugin()->updateBlock($this->block->getConfiguration()['uuid'], $configuration); $cached_values = $this->getTempstore()->get($form_state->get('variant_id')); $cached_values['plugin'] = $this->getVariantPlugin(); @@ -188,6 +238,29 @@ abstract class VariantPluginConfigureBlockFormBase extends FormBase { } /** + * Gets conditions for this block. + * + * @return \Drupal\Core\Condition\ConditionPluginCollection + * A collection of configured condition plugins. + */ + protected function getVisibilityConditions() { + if (!isset($this->visibilityCollection)) { + $this->visibilityCollection = new ConditionPluginCollection($this->conditionManager, $this->getVisibility()); + } + return $this->visibilityCollection; + } + + /** + * Returns an array of visibility condition configurations. + * + * @return array + */ + protected function getVisibility() { + $configuration = $this->block->getConfiguration(); + return empty($configuration['visibility']) ? [] : $configuration['visibility']; + } + + /** * Gets the variant plugin for this page variant entity. * * @return \Drupal\ctools\Plugin\BlockVariantInterface diff --git a/page_manager_ui/src/Form/VisibilitySubform.php b/page_manager_ui/src/Form/VisibilitySubform.php new file mode 100644 index 0000000..c5dc2fa --- /dev/null +++ b/page_manager_ui/src/Form/VisibilitySubform.php @@ -0,0 +1,137 @@ +manager = $condition_manager; + $this->language = $language_manager; + $this->contextRepository = $context_repository; + } + + /** + * Helper function for building the visibility UI form. + * + * @param array $form + * An associative array containing the structure of the form. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The current state of the form. + * + * @return array + * The form array with the visibility UI added in. + * + * @see \Drupal\block\BlockForm::buildVisibilityInterface() + */ + public function build(array $form, FormStateInterface $form_state, array $configuration) { + // Store the gathered contexts in the form state for other objects to use + // during form building. + // It's expected at least by \Drupal\Core\Condition\ConditionPluginBase. + $form_state->setTemporaryValue('gathered_contexts', $this->contextRepository->getAvailableContexts()); + + $form['visibility_tabs'] = [ + '#type' => 'vertical_tabs', + '#title' => $this->t('Visibility'), + '#parents' => ['visibility_tabs'], + '#attached' => [ + 'library' => [ + 'block/drupal.block', + ], + ], + ]; + + // @todo Allow list of conditions to be configured in + // https://www.drupal.org/node/2284687. + $contexts = $this->contextRepository->getAvailableContexts(); + foreach ($this->manager->getDefinitionsForContexts($contexts) as $condition_id => $definition) { + // Don't display the current theme condition. + if ($condition_id == 'current_theme') { + continue; + } + // Don't display the language condition until we have multiple languages. + if ($condition_id == 'language' && !$this->language->isMultilingual()) { + continue; + } + /** @var \Drupal\Core\Condition\ConditionInterface $condition */ + $condition = $this->manager->createInstance($condition_id, isset($configuration[$condition_id]) ? $configuration[$condition_id] : []); + $form_state->set(['conditions', $condition_id], $condition); + $condition_form = $condition->buildConfigurationForm([], $form_state); + $condition_form['#type'] = 'details'; + $condition_form['#title'] = $condition->getPluginDefinition()['label']; + $condition_form['#group'] = 'visibility_tabs'; + $form[$condition_id] = $condition_form; + } + + if (isset($form['node_type'])) { + $form['node_type']['#title'] = $this->t('Content types'); + $form['node_type']['bundles']['#title'] = $this->t('Content types'); + $form['node_type']['negate']['#type'] = 'value'; + $form['node_type']['negate']['#title_display'] = 'invisible'; + $form['node_type']['negate']['#value'] = $form['node_type']['negate']['#default_value']; + } + if (isset($form['user_role'])) { + $form['user_role']['#title'] = $this->t('Roles'); + unset($form['user_role']['roles']['#description']); + $form['user_role']['negate']['#type'] = 'value'; + $form['user_role']['negate']['#value'] = $form['user_role']['negate']['#default_value']; + } + if (isset($form['request_path'])) { + $form['request_path']['#title'] = $this->t('Pages'); + $form['request_path']['negate']['#type'] = 'radios'; + $form['request_path']['negate']['#default_value'] = (int) $form['request_path']['negate']['#default_value']; + $form['request_path']['negate']['#title_display'] = 'invisible'; + $form['request_path']['negate']['#options'] = [ + $this->t('Show for the listed pages'), + $this->t('Hide for the listed pages'), + ]; + } + if (isset($form['language'])) { + $form['language']['negate']['#type'] = 'value'; + $form['language']['negate']['#value'] = $form['language']['negate']['#default_value']; + } + return $form; + } + +} \ No newline at end of file diff --git a/src/ConditionAccessResolver.php b/src/ConditionAccessResolver.php new file mode 100644 index 0000000..ca76626 --- /dev/null +++ b/src/ConditionAccessResolver.php @@ -0,0 +1,121 @@ +contextRepository = $context_repository; + $this->contextHandler = $context_handler; + } + + /** + * Performs access checks on the conditions. + * + * It's the caller's responsibility to add any cacheability metadata from the + * conditions' configuration to the result as appropriate. For example if the + * settings come from a configuration entity, it should be added as a + * cacheable dependency. + * + * @param \Drupal\Core\Condition\ConditionInterface[]|\Traversable $conditions + * An array/traversible of condition plugins. + * + * @return \Drupal\Core\Access\AccessResultInterface + * AccessResult::allowed() if all conditions have their contexts and return + * TRUE; AccessResult::forbidden() otherwise. + * + * @see \Drupal\block\BlockAccessControlHandler::checkAccess() + */ + public function checkAccess($conditions) { + $missing_context = FALSE; + foreach ($conditions as $condition_id => $condition) { + if ($condition instanceof ContextAwarePluginInterface) { + try { + $contexts = $this->contextRepository->getRuntimeContexts(array_values($condition->getContextMapping())); + $this->contextHandler->applyContextMapping($condition, $contexts); + } + catch (ContextException $e) { + $missing_context = TRUE; + } + } + } + + if ($missing_context) { + // If any context is missing then we might be missing cacheable + // metadata, and don't know based on what conditions the block is + // accessible or not. For example, blocks that have a node type + // condition will have a missing context on any non-node route like the + // frontpage. + // @todo Avoid setting max-age 0 for some or all cases, for example by + // treating available contexts without value differently in + // https://www.drupal.org/node/2521956. + $access = AccessResult::forbidden()->setCacheMaxAge(0); + } + elseif ($this->resolveConditions($conditions, 'and') !== FALSE) { + $access = AccessResult::allowed(); + } + else { + $access = AccessResult::forbidden(); + } + + $this->mergeCacheabilityFromConditions($access, $conditions); + return $access; + } + + /** + * Merges cacheable metadata from conditions onto the access result object. + * + * @param \Drupal\Core\Access\AccessResult $access + * The access result object. + * @param \Drupal\Core\Condition\ConditionInterface[] $conditions + * List of visibility conditions. + * + * @see \Drupal\block\BlockAccessControlHandler::mergeCacheabilityFromConditions() + */ + protected function mergeCacheabilityFromConditions(AccessResult $access, $conditions) { + foreach ($conditions as $condition) { + if ($condition instanceof CacheableDependencyInterface) { + $access->addCacheTags($condition->getCacheTags()); + $access->addCacheContexts($condition->getCacheContexts()); + $access->setCacheMaxAge(Cache::mergeMaxAges($access->getCacheMaxAge(), $condition->getCacheMaxAge())); + } + } + } + +} \ No newline at end of file diff --git a/src/Plugin/DisplayVariant/PageBlockDisplayVariant.php b/src/Plugin/DisplayVariant/PageBlockDisplayVariant.php index 4b704d0..8477c9a 100644 --- a/src/Plugin/DisplayVariant/PageBlockDisplayVariant.php +++ b/src/Plugin/DisplayVariant/PageBlockDisplayVariant.php @@ -10,13 +10,17 @@ namespace Drupal\page_manager\Plugin\DisplayVariant; use Drupal\Component\Render\HtmlEscapedText; use Drupal\Component\Uuid\UuidInterface; use Drupal\Component\Utility\Html; +use Drupal\Core\Access\AccessResult; use Drupal\Core\Block\BlockManager; +use Drupal\Core\Block\BlockPluginInterface; use Drupal\Core\Cache\Cache; use Drupal\Core\Cache\CacheableMetadata; use Drupal\Core\Condition\ConditionManager; +use Drupal\Core\Condition\ConditionPluginCollection; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Plugin\Context\ContextHandlerInterface; +use Drupal\Core\Plugin\Context\ContextRepositoryInterface; use Drupal\Core\Plugin\ContextAwarePluginInterface; use Drupal\Core\Render\Element; use Drupal\Core\Render\Markup; @@ -24,6 +28,7 @@ use Drupal\Core\Session\AccountInterface; use Drupal\Core\Utility\Token; use Drupal\ctools\Plugin\DisplayVariant\BlockDisplayVariant; use Drupal\ctools\Plugin\PluginWizardInterface; +use Drupal\page_manager\ConditionAccessResolver; use Drupal\page_manager_ui\Form\VariantPluginContentForm; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -45,6 +50,13 @@ class PageBlockDisplayVariant extends BlockDisplayVariant implements PluginWizar protected $moduleHandler; /** + * The context repository service. + * + * @var \Drupal\Core\Plugin\Context\ContextRepositoryInterface + */ + protected $contextRepository; + + /** * Constructs a new BlockDisplayVariant. * * @param array $configuration @@ -67,11 +79,13 @@ class PageBlockDisplayVariant extends BlockDisplayVariant implements PluginWizar * The condition manager. * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler * The module handler. + * @param \Drupal\Core\Plugin\Context\ContextRepositoryInterface $context_repository */ - public function __construct(array $configuration, $plugin_id, $plugin_definition, ContextHandlerInterface $context_handler, AccountInterface $account, UuidInterface $uuid_generator, Token $token, BlockManager $block_manager, ConditionManager $condition_manager, ModuleHandlerInterface $module_handler) { + public function __construct(array $configuration, $plugin_id, $plugin_definition, ContextHandlerInterface $context_handler, AccountInterface $account, UuidInterface $uuid_generator, Token $token, BlockManager $block_manager, ConditionManager $condition_manager, ModuleHandlerInterface $module_handler, ContextRepositoryInterface $context_repository = NULL) { parent::__construct($configuration, $plugin_id, $plugin_definition, $context_handler, $account, $uuid_generator, $token, $block_manager, $condition_manager); - $this->moduleHandler = $module_handler; + // The context repository was added post-beta and so is optional. + $this->contextRepository = $context_repository ?: \Drupal::service('context.repository'); } /** @@ -88,7 +102,8 @@ class PageBlockDisplayVariant extends BlockDisplayVariant implements PluginWizar $container->get('token'), $container->get('plugin.manager.block'), $container->get('plugin.manager.condition'), - $container->get('module_handler') + $container->get('module_handler'), + $container->get('context.repository') ); } @@ -128,7 +143,8 @@ class PageBlockDisplayVariant extends BlockDisplayVariant implements PluginWizar if ($block instanceof ContextAwarePluginInterface) { $this->contextHandler()->applyContextMapping($block, $contexts); } - $access = $block->access($this->account, TRUE); + $visibility = $this->checkVisibility($block); + $access = $visibility->andIf($block->access($this->account, TRUE)); $cacheability->addCacheableDependency($access); if (!$access->isAllowed()) { continue; @@ -323,6 +339,26 @@ class PageBlockDisplayVariant extends BlockDisplayVariant implements PluginWizar } /** + * Performs a visibility check with condition plugins. + * + * @param array $configuration + * An array of condition plugin configuration. + * + * @return \Drupal\Core\Access\AccessResultInterface + * AccessResult::allowed() if all conditions have their contexts and return + * TRUE; AccessResult::forbidden() otherwise. + */ + protected function checkVisibility(BlockPluginInterface $block) { + $configuration = $block->getConfiguration(); + if (empty($configuration['visibility'])) { + return AccessResult::allowed(); + } + $resolver = new ConditionAccessResolver($this->contextRepository, $this->contextHandler); + $conditions = new ConditionPluginCollection($this->conditionManager, $configuration['visibility']); + return $resolver->checkAccess($conditions); + } + + /** * {@inheritdoc} */ public function __sleep() { diff --git a/tests/src/Unit/PageBlockDisplayVariantTest.php b/tests/src/Unit/PageBlockDisplayVariantTest.php index 2a140ea..6467624 100644 --- a/tests/src/Unit/PageBlockDisplayVariantTest.php +++ b/tests/src/Unit/PageBlockDisplayVariantTest.php @@ -15,6 +15,7 @@ use Drupal\Core\Cache\Context\CacheContextsManager; use Drupal\Core\Condition\ConditionManager; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Plugin\Context\ContextHandlerInterface; +use Drupal\Core\Plugin\Context\ContextRepositoryInterface; use Drupal\Core\Plugin\ContextAwarePluginInterface; use Drupal\Core\Form\FormState; use Drupal\Core\Session\AccountInterface; @@ -50,13 +51,14 @@ class PageBlockDisplayVariantTest extends UnitTestCase { $block1->build()->willReturn(['#cache' => [ 'tags' => [ 0 => 'tag_to_be_merged']]]); $context_handler = $this->prophesize(ContextHandlerInterface::class); + $context_repository = $this->prophesize(ContextRepositoryInterface::class); $uuid_generator = $this->prophesize(UuidInterface::class); $token = $this->prophesize(Token::class); $block_manager = $this->prophesize(BlockManager::class); $condition_manager = $this->prophesize(ConditionManager::class); $module_handler = $this->prophesize(ModuleHandlerInterface::class); - $variant_plugin = new PageBlockDisplayVariant([], '', [], $context_handler->reveal(), $account->reveal(), $uuid_generator->reveal(), $token->reveal(), $block_manager->reveal(), $condition_manager->reveal(), $module_handler->reveal()); + $variant_plugin = new PageBlockDisplayVariant([], '', [], $context_handler->reveal(), $account->reveal(), $uuid_generator->reveal(), $token->reveal(), $block_manager->reveal(), $condition_manager->reveal(), $module_handler->reveal(), $context_repository->reveal()); // Empty block. $expected_build = [ @@ -142,6 +144,7 @@ class PageBlockDisplayVariantTest extends UnitTestCase { $context_handler = $this->prophesize(ContextHandlerInterface::class); $context_handler->applyContextMapping($block2->reveal(), [])->shouldBeCalledTimes(1); + $context_repository = $this->prophesize(ContextRepositoryInterface::class); $module_handler = $this->prophesize(ModuleHandlerInterface::class); $module_handler->alter(); @@ -153,7 +156,7 @@ class PageBlockDisplayVariantTest extends UnitTestCase { $block_manager = $this->prophesize(BlockManager::class); $condition_manager = $this->prophesize(ConditionManager::class); $variant_plugin = $this->getMockBuilder(PageBlockDisplayVariant::class) - ->setConstructorArgs([['page_title' => $page_title, 'uuid' => 'UUID'], 'test', [], $context_handler->reveal(), $account->reveal(), $uuid_generator->reveal(), $token, $block_manager->reveal(), $condition_manager->reveal(), $module_handler->reveal()]) + ->setConstructorArgs([['page_title' => $page_title, 'uuid' => 'UUID'], 'test', [], $context_handler->reveal(), $account->reveal(), $uuid_generator->reveal(), $token, $block_manager->reveal(), $condition_manager->reveal(), $module_handler->reveal(), $context_repository->reveal()]) ->setMethods(['renderPageTitle']) ->getMock(); @@ -214,13 +217,14 @@ class PageBlockDisplayVariantTest extends UnitTestCase { public function testSubmitConfigurationForm() { $account = $this->prophesize(AccountInterface::class); $context_handler = $this->prophesize(ContextHandlerInterface::class); + $context_repository = $this->prophesize(ContextRepositoryInterface::class); $uuid_generator = $this->prophesize(UuidInterface::class); $token = $this->prophesize(Token::class); $block_manager = $this->prophesize(BlockManager::class); $condition_manager = $this->prophesize(ConditionManager::class); $module_handler = $this->prophesize(ModuleHandlerInterface::class); - $variant_plugin = new PageBlockDisplayVariant([], '', [], $context_handler->reveal(), $account->reveal(), $uuid_generator->reveal(), $token->reveal(), $block_manager->reveal(), $condition_manager->reveal(), $module_handler->reveal()); + $variant_plugin = new PageBlockDisplayVariant([], '', [], $context_handler->reveal(), $account->reveal(), $uuid_generator->reveal(), $token->reveal(), $block_manager->reveal(), $condition_manager->reveal(), $module_handler->reveal(), $context_repository->reveal()); $values = ['page_title' => "Go hang a salami, I'm a lasagna hog!"];