currentUser = $current_user; } /** * {@inheritdoc} */ public static function getSubscribedEvents(): array { $events[LayoutBuilderEvents::SECTION_COMPONENT_BUILD_RENDER_ARRAY] = ['onBuildRender', 100]; return $events; } /** * Builds render arrays for block plugins and sets it on the event. * * @param \Drupal\layout_builder\Event\SectionComponentBuildRenderArrayEvent $event * The section component render event. */ public function onBuildRender(SectionComponentBuildRenderArrayEvent $event) { $block = $event->getPlugin(); if (!$block instanceof BlockPluginInterface) { return; } // Set block access dependency even if we are not checking access on // this level. The block itself may render another // RefinableDependentAccessInterface object and need to pass on this value. if ($block instanceof RefinableDependentAccessInterface) { $contexts = $event->getContexts(); if (isset($contexts['layout_builder.entity'])) { if ($entity = $contexts['layout_builder.entity']->getContextValue()) { if ($event->inPreview()) { // If previewing in Layout Builder allow access. $block->setAccessDependency(new LayoutPreviewAccessAllowed()); } else { $block->setAccessDependency($entity); } } } } // Only check access if the component is not being previewed. if ($event->inPreview()) { $access = AccessResult::allowed()->setCacheMaxAge(0); } else { $access = $block->access($this->currentUser, TRUE); } $event->addCacheableDependency($access); if ($access->isAllowed()) { $event->addCacheableDependency($block); // @todo Revisit after https://www.drupal.org/node/3027653, as this will // provide a better way to remove contextual links from Views blocks. // Currently, doing this requires setting // \Drupal\views\ViewExecutable::$showAdminLinks() to false before the // Views block is built. if ($block instanceof ViewsBlock && $event->inPreview()) { $block->getViewExecutable()->setShowAdminLinks(FALSE); } $content = $block->build(); // @todo Remove when https://www.drupal.org/node/3164389 is resolved. if (!is_array($content)) { throw new \UnexpectedValueException(sprintf('The block "%s" did not return an array', get_class($block))); } // We don't output the block render data if there are no render elements // found, but we want to capture the cache metadata from the block // regardless. $event->addCacheableDependency(CacheableMetadata::createFromRenderArray($content)); $is_content_empty = Element::isEmpty($content); $is_placeholder_ready = $event->inPreview() && $block instanceof PreviewFallbackInterface; // If the content is empty and no placeholder is available, return. if ($is_content_empty && !$is_placeholder_ready) { return; } $build = [ // @todo Move this to BlockBase in https://www.drupal.org/node/2931040. '#theme' => 'block', '#configuration' => $block->getConfiguration(), '#plugin_id' => $block->getPluginId(), '#base_plugin_id' => $block->getBaseId(), '#derivative_plugin_id' => $block->getDerivativeId(), '#in_preview' => $event->inPreview(), '#weight' => $event->getComponent()->getWeight(), ]; // Place the $content returned by the block plugin into a 'content' child // element, as a way to allow the plugin to have complete control of its // properties and rendering (for instance, its own #theme) without // conflicting with the properties used above, or alternate ones used by // alternate block rendering approaches in contributed modules. However, // the use of a child element is an implementation detail of this // particular block rendering approach. Semantically, the content returned // by the block plugin, and in particular, attributes and contextual links // are information that belong to the entire block. Therefore, we must // move these properties from $content and merge them into the top-level // element. if (isset($content['#attributes'])) { $build['#attributes'] = $content['#attributes']; unset($content['#attributes']); } // Hide contextual links for inline blocks until the UX issues surrounding // editing them directly are resolved. // @see https://www.drupal.org/project/drupal/issues/3075308 if (!$block instanceof InlineBlock && !empty($content['#contextual_links'])) { $build['#contextual_links'] = $content['#contextual_links']; } $build['content'] = $content; if ($event->inPreview()) { if ($block instanceof PreviewFallbackInterface) { $preview_fallback_string = $block->getPreviewFallbackString(); } else { $preview_fallback_string = $this->t('"@block" block', ['@block' => $block->label()]); } // @todo Use new label methods so // data-layout-content-preview-placeholder-label doesn't have to use // preview fallback in https://www.drupal.org/node/2025649. $build['#attributes']['data-layout-content-preview-placeholder-label'] = $preview_fallback_string; if ($is_content_empty && $is_placeholder_ready) { $build['content']['#markup'] = $this->t('Placeholder for the @preview_fallback', ['@preview_fallback' => $block->getPreviewFallbackString()]); } } $event->setBuild($build); } } }