You are here

public function BlockComponentRenderArray::onBuildRender in Drupal 10

Same name and namespace in other branches
  1. 8 core/modules/layout_builder/src/EventSubscriber/BlockComponentRenderArray.php \Drupal\layout_builder\EventSubscriber\BlockComponentRenderArray::onBuildRender()
  2. 9 core/modules/layout_builder/src/EventSubscriber/BlockComponentRenderArray.php \Drupal\layout_builder\EventSubscriber\BlockComponentRenderArray::onBuildRender()

Builds render arrays for block plugins and sets it on the event.

Parameters

\Drupal\layout_builder\Event\SectionComponentBuildRenderArrayEvent $event: The section component render event.

File

core/modules/layout_builder/src/EventSubscriber/BlockComponentRenderArray.php, line 61

Class

BlockComponentRenderArray
Builds render arrays and handles access for all block components.

Namespace

Drupal\layout_builder\EventSubscriber

Code

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();

    // 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(),
      '#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);
  }
}