diff --git a/core/modules/layout_builder/layout_builder.module b/core/modules/layout_builder/layout_builder.module index 0ba9a21f6b..1ab27fab41 100644 --- a/core/modules/layout_builder/layout_builder.module +++ b/core/modules/layout_builder/layout_builder.module @@ -9,6 +9,9 @@ use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\FieldableEntityInterface; use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Plugin\Context\Context; +use Drupal\Core\Plugin\Context\ContextDefinition; +use Drupal\Core\StringTranslation\TranslatableMarkup; use Drupal\field\Entity\FieldConfig; use Drupal\field\Entity\FieldStorageConfig; @@ -166,9 +169,13 @@ function layout_builder_add_layout_section_field($entity_type_id, $bundle, $fiel */ function layout_builder_entity_view_alter(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display) { if ($display->getThirdPartySetting('layout_builder', 'allow_custom', FALSE) && !$entity->layout_builder__layout->isEmpty()) { + $contexts = \Drupal::service('context.repository')->getAvailableContexts(); + // @todo Use EntityContextDefinition after resolving + // https://www.drupal.org/node/2932462. + $contexts['layout_builder.entity'] = new Context(new ContextDefinition("entity:{$entity->getEntityTypeId()}", new TranslatableMarkup('@entity being viewed', ['@entity' => $entity->getEntityType()->getLabel()])), $entity); $sections = $entity->layout_builder__layout->getSections(); foreach ($sections as $delta => $section) { - $build['_layout_builder'][$delta] = $section->toRenderArray(); + $build['_layout_builder'][$delta] = $section->toRenderArray($contexts); } // If field layout is active, that is all that needs to be removed. diff --git a/core/modules/layout_builder/src/Context/LayoutBuilderContextTrait.php b/core/modules/layout_builder/src/Context/LayoutBuilderContextTrait.php new file mode 100644 index 0000000000..3c16332def --- /dev/null +++ b/core/modules/layout_builder/src/Context/LayoutBuilderContextTrait.php @@ -0,0 +1,53 @@ +contextRepository) { + $this->contextRepository = \Drupal::service('context.repository'); + } + return $this->contextRepository; + } + + /** + * Provides all available contexts, both global and section_storage-specific. + * + * @param \Drupal\layout_builder\SectionStorageInterface $section_storage + * The section storage. + * + * @return \Drupal\Core\Plugin\Context\ContextInterface[] + * The array of context objects. + */ + protected function getAvailableContexts(SectionStorageInterface $section_storage) { + // Get all globally available contexts that have a defined value. + $contexts = array_filter($this->contextRepository()->getAvailableContexts(), function (ContextInterface $context) { + return $context->hasContextValue(); + }); + + // Add in the per-section_storage contexts. + $contexts += $section_storage->getContexts(); + return $contexts; + } + +} diff --git a/core/modules/layout_builder/src/Controller/ChooseBlockController.php b/core/modules/layout_builder/src/Controller/ChooseBlockController.php index f28ff5c70d..5287be25fc 100644 --- a/core/modules/layout_builder/src/Controller/ChooseBlockController.php +++ b/core/modules/layout_builder/src/Controller/ChooseBlockController.php @@ -5,6 +5,7 @@ use Drupal\Core\Block\BlockManagerInterface; use Drupal\Core\DependencyInjection\ContainerInjectionInterface; use Drupal\Core\Url; +use Drupal\layout_builder\Context\LayoutBuilderContextTrait; use Drupal\layout_builder\SectionStorageInterface; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -16,6 +17,7 @@ class ChooseBlockController implements ContainerInjectionInterface { use AjaxHelperTrait; + use LayoutBuilderContextTrait; /** * The block manager. @@ -60,7 +62,8 @@ public function build(SectionStorageInterface $section_storage, $delta, $region) $build['#type'] = 'container'; $build['#attributes']['class'][] = 'block-categories'; - foreach ($this->blockManager->getGroupedDefinitions() as $category => $blocks) { + $definitions = $this->blockManager->getDefinitionsForContexts($this->getAvailableContexts($section_storage)); + foreach ($this->blockManager->getGroupedDefinitions($definitions) as $category => $blocks) { $build[$category]['#type'] = 'details'; $build[$category]['#open'] = TRUE; $build[$category]['#title'] = $category; diff --git a/core/modules/layout_builder/src/Controller/LayoutBuilderController.php b/core/modules/layout_builder/src/Controller/LayoutBuilderController.php index ff2d17d2ac..3f3b95e49b 100644 --- a/core/modules/layout_builder/src/Controller/LayoutBuilderController.php +++ b/core/modules/layout_builder/src/Controller/LayoutBuilderController.php @@ -6,6 +6,7 @@ use Drupal\Core\Plugin\PluginFormInterface; use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\Core\Url; +use Drupal\layout_builder\Context\LayoutBuilderContextTrait; use Drupal\layout_builder\LayoutTempstoreRepositoryInterface; use Drupal\layout_builder\Section; use Drupal\layout_builder\SectionStorageInterface; @@ -19,6 +20,7 @@ */ class LayoutBuilderController implements ContainerInjectionInterface { + use LayoutBuilderContextTrait; use StringTranslationTrait; /** @@ -170,7 +172,7 @@ protected function buildAdministrativeSection(SectionStorageInterface $section_s $section = $section_storage->getSection($delta); $layout = $section->getLayout(); - $build = $section->toRenderArray(); + $build = $section->toRenderArray($this->getAvailableContexts($section_storage)); $layout_definition = $layout->getPluginDefinition(); foreach ($layout_definition->getRegions() as $region => $info) { diff --git a/core/modules/layout_builder/src/Field/LayoutSectionItemList.php b/core/modules/layout_builder/src/Field/LayoutSectionItemList.php index 71b22b78bc..5aabb02828 100644 --- a/core/modules/layout_builder/src/Field/LayoutSectionItemList.php +++ b/core/modules/layout_builder/src/Field/LayoutSectionItemList.php @@ -3,6 +3,9 @@ namespace Drupal\layout_builder\Field; use Drupal\Core\Field\FieldItemList; +use Drupal\Core\Plugin\Context\Context; +use Drupal\Core\Plugin\Context\ContextDefinition; +use Drupal\Core\StringTranslation\TranslatableMarkup; use Drupal\layout_builder\Section; use Drupal\layout_builder\SectionStorageInterface; @@ -74,6 +77,17 @@ public function removeSection($delta) { return $this; } + /** + * {@inheritdoc} + */ + public function getContexts() { + $entity = $this->getEntity(); + // @todo Use EntityContextDefinition after resolving + // https://www.drupal.org/node/2932462. + $contexts['layout_builder.entity'] = new Context(new ContextDefinition("entity:{$entity->getEntityTypeId()}", new TranslatableMarkup('@entity being viewed', ['@entity' => $entity->getEntityType()->getLabel()])), $entity); + return $contexts; + } + /** * {@inheritdoc} */ diff --git a/core/modules/layout_builder/src/Form/ConfigureBlockFormBase.php b/core/modules/layout_builder/src/Form/ConfigureBlockFormBase.php index 222c5bd766..c16f41ade8 100644 --- a/core/modules/layout_builder/src/Form/ConfigureBlockFormBase.php +++ b/core/modules/layout_builder/src/Form/ConfigureBlockFormBase.php @@ -14,6 +14,7 @@ use Drupal\Core\Plugin\ContextAwarePluginInterface; use Drupal\Core\Plugin\PluginFormFactoryInterface; use Drupal\Core\Plugin\PluginWithFormsInterface; +use Drupal\layout_builder\Context\LayoutBuilderContextTrait; use Drupal\layout_builder\Controller\LayoutRebuildTrait; use Drupal\layout_builder\LayoutTempstoreRepositoryInterface; use Drupal\layout_builder\Section; @@ -29,6 +30,7 @@ use AjaxFormHelperTrait; use ContextAwarePluginAssignmentTrait; + use LayoutBuilderContextTrait; use LayoutRebuildTrait; /** @@ -179,7 +181,7 @@ public function buildForm(array $form, FormStateInterface $form_state, SectionSt $this->region = $region; $this->block = $this->prepareBlock($plugin_id, $configuration); - $form_state->setTemporaryValue('gathered_contexts', $this->contextRepository->getAvailableContexts()); + $form_state->setTemporaryValue('gathered_contexts', $this->getAvailableContexts($section_storage)); // @todo Remove once https://www.drupal.org/node/2268787 is resolved. $form_state->set('block_theme', $this->config('system.theme')->get('default')); diff --git a/core/modules/layout_builder/src/Section.php b/core/modules/layout_builder/src/Section.php index 55204ddc9e..02f274e784 100644 --- a/core/modules/layout_builder/src/Section.php +++ b/core/modules/layout_builder/src/Section.php @@ -66,13 +66,16 @@ public function __construct($layout_id, array $layout_settings = [], array $comp /** * Returns the renderable array for this section. * + * @param \Drupal\Core\Plugin\Context\ContextInterface[] $contexts + * An array of available contexts. + * * @return array * A renderable array representing the content of the section. */ - public function toRenderArray() { + public function toRenderArray(array $contexts = []) { $regions = []; foreach ($this->getComponents() as $component) { - if ($output = $component->toRenderArray()) { + if ($output = $component->toRenderArray($contexts)) { $regions[$component->getRegion()][$component->getUuid()] = $output; } } diff --git a/core/modules/layout_builder/src/SectionComponent.php b/core/modules/layout_builder/src/SectionComponent.php index caad0a2b61..6a0c238c70 100644 --- a/core/modules/layout_builder/src/SectionComponent.php +++ b/core/modules/layout_builder/src/SectionComponent.php @@ -88,13 +88,16 @@ public function __construct($uuid, $region, array $configuration = [], array $ad /** * Returns the renderable array for this component. * + * @param \Drupal\Core\Plugin\Context\ContextInterface[] $contexts + * An array of available contexts. + * * @return array * A renderable array representing the content of the component. */ - public function toRenderArray() { + public function toRenderArray(array $contexts = []) { $output = []; - $plugin = $this->getPlugin(); + $plugin = $this->getPlugin($contexts); // @todo Figure out the best way to unify fields and blocks and components // in https://www.drupal.org/node/1875974. if ($plugin instanceof BlockPluginInterface) { @@ -259,13 +262,15 @@ public function getUuid() { /** * Gets the plugin for this component. * + * @param \Drupal\Core\Plugin\Context\ContextInterface[] $contexts + * An array of contexts to set on the plugin. + * * @return \Drupal\Component\Plugin\PluginInspectionInterface * The plugin. */ - public function getPlugin() { + public function getPlugin(array $contexts = []) { $plugin = $this->pluginManager()->createInstance($this->getPluginId(), $this->getConfiguration()); - if ($plugin instanceof ContextAwarePluginInterface) { - $contexts = $this->contextRepository()->getRuntimeContexts(array_values($plugin->getContextMapping())); + if ($contexts && $plugin instanceof ContextAwarePluginInterface) { $this->contextHandler()->applyContextMapping($plugin, $contexts); } return $plugin; @@ -281,16 +286,6 @@ protected function pluginManager() { return \Drupal::service('plugin.manager.block'); } - /** - * Wraps the context repository. - * - * @return \Drupal\Core\Plugin\Context\ContextRepositoryInterface - * The context repository. - */ - protected function contextRepository() { - return \Drupal::service('context.repository'); - } - /** * Wraps the context handler. * diff --git a/core/modules/layout_builder/src/SectionStorageInterface.php b/core/modules/layout_builder/src/SectionStorageInterface.php index 56a4e9ecbd..13217d82b4 100644 --- a/core/modules/layout_builder/src/SectionStorageInterface.php +++ b/core/modules/layout_builder/src/SectionStorageInterface.php @@ -68,6 +68,14 @@ public function insertSection($delta, Section $section); */ public function removeSection($delta); + /** + * Provides any available contexts for the object using the sections. + * + * @return \Drupal\Core\Plugin\Context\ContextInterface[] + * The array of context objects. + */ + public function getContexts(); + /** * Returns an identifier for this storage. * diff --git a/core/modules/layout_builder/tests/src/Functional/LayoutSectionTest.php b/core/modules/layout_builder/tests/src/Functional/LayoutSectionTest.php index 3f0bf9477e..b2b5f9c781 100644 --- a/core/modules/layout_builder/tests/src/Functional/LayoutSectionTest.php +++ b/core/modules/layout_builder/tests/src/Functional/LayoutSectionTest.php @@ -55,7 +55,7 @@ protected function setUp() { */ public function providerTestLayoutSectionFormatter() { $data = []; - $data['block_with_context'] = [ + $data['block_with_global_context'] = [ [ [ 'section' => new Section('layout_onecol', [], [ @@ -80,6 +80,31 @@ public function providerTestLayoutSectionFormatter() { 'user:2', 'UNCACHEABLE', ]; + $data['block_with_entity_context'] = [ + [ + [ + 'section' => new Section('layout_onecol', [], [ + 'baz' => new SectionComponent('baz', 'content', [ + 'id' => 'field_block:node:body', + 'context_mapping' => [ + 'entity' => 'layout_builder.entity', + ], + ]), + ]), + ], + ], + [ + '.layout--onecol', + '.field--name-body', + ], + [ + 'Body', + 'The node body', + ], + '', + '', + 'MISS', + ]; $data['single_section_single_block'] = [ [ [