diff --git a/core/modules/layout_builder/layout_builder.services.yml b/core/modules/layout_builder/layout_builder.services.yml index 38933073d9..011abacd36 100644 --- a/core/modules/layout_builder/layout_builder.services.yml +++ b/core/modules/layout_builder/layout_builder.services.yml @@ -31,3 +31,12 @@ services: arguments: ['@current_route_match'] tags: - { name: cache.context} + render_block_component_subscriber: + class: Drupal\layout_builder\EventSubscriber\RenderBlockComponent + tags: + - { name: event_subscriber } + component_visibility_subscriber: + class: Drupal\layout_builder\EventSubscriber\SectionComponentVisibility + arguments: ['@context.handler', '@plugin.manager.condition'] + tags: + - { name: event_subscriber } diff --git a/core/modules/layout_builder/src/Event/SectionComponentRenderEvent.php b/core/modules/layout_builder/src/Event/SectionComponentRenderEvent.php new file mode 100644 index 0000000000..12b3f9b4dd --- /dev/null +++ b/core/modules/layout_builder/src/Event/SectionComponentRenderEvent.php @@ -0,0 +1,167 @@ +component = $component; + $this->contexts = $contexts; + $this->plugin = $plugin; + $this->access = $access; + $this->cacheability = $cacheability; + $this->preview = $preview; + } + + /** + * Get the section component whose render array is being built. + * + * @return \Drupal\layout_builder\SectionComponent + */ + public function getComponent() { + return $this->component; + } + + /** + * Get the available contexts. + * + * @return array|\Drupal\Core\Plugin\Context\ContextInterface[] + */ + public function getContexts() { + return $this->contexts; + } + + /** + * Get the plugin which will be consulted as part of the build process. + * + * @return \Drupal\Component\Plugin\PluginInspectionInterface + */ + public function getPlugin() { + return $this->plugin; + } + + /** + * Get the access result object. + * + * @return \Drupal\Core\Access\AccessResultInterface + */ + public function getAccess() { + return $this->access; + } + + /** + * Get the cacheability metadata. + * + * @return \Drupal\Core\Cache\CacheableMetadata + */ + public function getCacheability() { + return $this->cacheability; + } + + /** + * Determine if the component is in preview mode. + * + * @return bool + */ + public function isPreview() { + return $this->preview; + } + + /** + * Get the render array in its current state. + * + * @return array + */ + public function getBuild() { + return $this->build; + } + + /** + * Set the render array. + * + * @param array $build + */ + public function setBuild(array $build) { + $this->build = $build; + } + +} diff --git a/core/modules/layout_builder/src/EventSubscriber/RenderBlockComponent.php b/core/modules/layout_builder/src/EventSubscriber/RenderBlockComponent.php new file mode 100644 index 0000000000..28baa9ad12 --- /dev/null +++ b/core/modules/layout_builder/src/EventSubscriber/RenderBlockComponent.php @@ -0,0 +1,47 @@ +getAccess()->isAllowed() && $event->getPlugin() instanceof BlockPluginInterface) { + $event->getCacheability()->addCacheableDependency($event->getPlugin()); + $build = $event->getBuild(); + // @todo Move this to BlockBase in https://www.drupal.org/node/2931040. + $build += [ + '#theme' => 'block', + '#configuration' => $event->getPlugin()->getConfiguration(), + '#plugin_id' => $event->getPlugin()->getPluginId(), + '#base_plugin_id' => $event->getPlugin()->getBaseId(), + '#derivative_plugin_id' => $event->getPlugin()->getDerivativeId(), + '#weight' => $event->getComponent()->getWeight(), + 'content' => $event->getPlugin()->build(), + ]; + $event->setBuild($build); + } + } + +} diff --git a/core/modules/layout_builder/src/EventSubscriber/SectionComponentVisibility.php b/core/modules/layout_builder/src/EventSubscriber/SectionComponentVisibility.php new file mode 100644 index 0000000000..55e85f5036 --- /dev/null +++ b/core/modules/layout_builder/src/EventSubscriber/SectionComponentVisibility.php @@ -0,0 +1,85 @@ +contextHandler = $context_handler; + $this->conditionManager = $condition_manager; + } + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents() { + $events[LayoutBuilderEvents::SECTION_COMPONENT_BUILD_RENDER_ARRAY] = ['onBuildRender', 255]; + return $events; + } + + /** + * Determines the visibility of section components. + * + * @param \Drupal\layout_builder\Event\SectionComponentRenderEvent $event + * The section component render event. + */ + public function onBuildRender(SectionComponentRenderEvent $event) { + $conditions = []; + if (!$event->isPreview()) { + foreach ($event->getComponent()->get('visibility') as $uuid => $condition) { + $condition = $this->conditionManager->createInstance($condition['id'], $condition); + if ($condition instanceof ContextAwarePluginInterface) { + $this->contextHandler->applyContextMapping($condition, $event->getContexts()); + } + $event->getCacheability()->addCacheableDependency($condition); + $conditions[$uuid] = $condition; + } + } + + // @todo Add and/or resolution form element. + if ($conditions && !$this->resolveConditions($conditions, 'and')) { + $build = $event->getBuild(); + $event->getCacheability()->addCacheableDependency($event->getPlugin()); + $event->getCacheability()->applyTo($build); + $event->setBuild($build); + // If conditions do not resolve, do not process other subscribers. + $event->stopPropagation(); + } + } + +} diff --git a/core/modules/layout_builder/src/Form/BlockVisibilityForm.php b/core/modules/layout_builder/src/Form/BlockVisibilityForm.php index 07477d589b..5cce0f98c9 100644 --- a/core/modules/layout_builder/src/Form/BlockVisibilityForm.php +++ b/core/modules/layout_builder/src/Form/BlockVisibilityForm.php @@ -5,6 +5,7 @@ use Drupal\Core\Ajax\AjaxResponse; use Drupal\Core\Ajax\OpenOffCanvasDialogCommand; use Drupal\Core\Condition\ConditionManager; +use Drupal\Core\Executable\ExecutableManagerInterface; use Drupal\Core\Form\FormBase; use Drupal\Core\Form\FormBuilderInterface; use Drupal\Core\Form\FormStateInterface; @@ -15,7 +16,7 @@ use Symfony\Component\HttpFoundation\RedirectResponse; /** - * Provides a form to pick visibility conditions to add to a block. + * Provides a form for applying visibility conditions to a block. * * @internal */ @@ -31,6 +32,13 @@ class BlockVisibilityForm extends FormBase { */ protected $conditionManager; + /** + * The form builder. + * + * @var \Drupal\Core\Form\FormBuilderInterface + */ + protected $formBuilder; + /** * The section storage. * @@ -55,11 +63,12 @@ class BlockVisibilityForm extends FormBase { /** * Constructs a new BlockVisibilityForm. * - * @param \Drupal\Core\Condition\ConditionManager $condition_manager + * @param \Drupal\Core\Executable\ExecutableManagerInterface $condition_manager * The condition plugin manager. */ - public function __construct(ConditionManager $condition_manager) { + public function __construct(ExecutableManagerInterface $condition_manager, FormBuilderInterface $form_builder) { $this->conditionManager = $condition_manager; + $this->formBuilder = $form_builder; } /** @@ -67,7 +76,8 @@ public function __construct(ConditionManager $condition_manager) { */ public static function create(ContainerInterface $container) { return new static( - $container->get('plugin.manager.condition') + $container->get('plugin.manager.condition'), + $container->get('form_builder') ); } @@ -159,7 +169,7 @@ public function buildForm(array $form, FormStateInterface $form_state, SectionSt protected function successfulAjaxSubmit(array $form, FormStateInterface $form_state) { $condition = $form_state->getValue('condition'); $parameters = $this->getParameters($condition); - $new_form = \Drupal::formBuilder()->getForm('\Drupal\layout_builder\Form\ConfigureVisibility', $this->sectionStorage, $parameters['delta'], $parameters['uuid'], $parameters['plugin_id']); + $new_form = $this->formBuilder->getForm('\Drupal\layout_builder\Form\ConfigureVisibility', $this->sectionStorage, $parameters['delta'], $parameters['uuid'], $parameters['plugin_id']); $new_form['#action'] = (new Url('layout_builder.add_visibility', $parameters))->toString(); $new_form['actions']['submit']['#attached']['drupalSettings']['ajax'][$new_form['actions']['submit']['#id']]['url'] = new Url('layout_builder.add_visibility', $parameters, ['query' => [FormBuilderInterface::AJAX_FORM_REQUEST => TRUE]]); $response = new AjaxResponse(); diff --git a/core/modules/layout_builder/src/LayoutBuilderEvents.php b/core/modules/layout_builder/src/LayoutBuilderEvents.php new file mode 100644 index 0000000000..43affd8745 --- /dev/null +++ b/core/modules/layout_builder/src/LayoutBuilderEvents.php @@ -0,0 +1,28 @@ +getPlugin($contexts); // @todo Figure out the best way to unify fields and blocks and components @@ -110,37 +107,11 @@ public function toRenderArray(array $contexts = [], $preview = FALSE) { if ($plugin instanceof BlockPluginInterface) { $access = $plugin->access($this->currentUser(), TRUE); $cacheability = CacheableMetadata::createFromObject($access); - if (!$preview) { - foreach ($this->get('visibility') as $uuid => $condition) { - $condition = $this->conditionManager()->createInstance($condition['id'], $condition); - if ($condition instanceof ContextAwarePluginInterface) { - $this->contextHandler()->applyContextMapping($condition, $contexts); - } - $cacheability->addCacheableDependency($condition); - $conditions[$uuid] = $condition; - } - } - if ($conditions && !$this->resolveConditions($conditions, 'and')) { - $cacheability->addCacheableDependency($plugin); - $cacheability->applyTo($output); - return $output; - } + $event = new SectionComponentRenderEvent($this, $contexts, $plugin, $access, $cacheability, $preview); + $this->eventDispatcher()->dispatch(LayoutBuilderEvents::SECTION_COMPONENT_BUILD_RENDER_ARRAY, $event); - // @todo Add and/or resolution form element. - if ($access->isAllowed()) { - $cacheability->addCacheableDependency($plugin); - // @todo Move this to BlockBase in https://www.drupal.org/node/2931040. - $output = [ - '#theme' => 'block', - '#configuration' => $plugin->getConfiguration(), - '#plugin_id' => $plugin->getPluginId(), - '#base_plugin_id' => $plugin->getBaseId(), - '#derivative_plugin_id' => $plugin->getDerivativeId(), - '#weight' => $this->getWeight(), - 'content' => $plugin->build(), - ]; - } + $output = $event->getBuild(); $cacheability->applyTo($output); } return $output; @@ -311,13 +282,13 @@ protected function pluginManager() { } /** - * Wraps the condition plugin manager. + * Wraps the event dispatcher. + * + * @return \Symfony\Component\EventDispatcher\EventDispatcherInterface * - * @return \Drupal\Core\Condition\ConditionManager - * The condition plugin manager. */ - protected function conditionManager() { - return \Drupal::service('plugin.manager.condition'); + protected function eventDispatcher() { + return \Drupal::service('event_dispatcher'); } /**