lb_plus-1.0.x-dev/src/EventSubscriber/SetInlineBlockDependency.php

src/EventSubscriber/SetInlineBlockDependency.php
<?php

namespace Drupal\lb_plus\EventSubscriber;

use Drupal\Core\Database\Connection;
use Drupal\Core\Entity\EntityInterface;
use Drupal\lb_plus\SectionStorageHandler;
use Drupal\Core\Access\AccessibleInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\block_content\BlockContentInterface;
use Drupal\Core\Entity\EntityRepositoryInterface;
use Drupal\layout_builder\InlineBlockUsageInterface;
use Drupal\layout_builder\Access\LayoutPreviewAccessAllowed;
use Drupal\block_content\Event\BlockContentGetDependencyEvent;
use Drupal\layout_builder\SectionStorage\SectionStorageManagerInterface;
use Drupal\layout_builder\EventSubscriber\SetInlineBlockDependency as CoreSetInlineBlockDependency;

/**
 * Set inline block dependency.
 *
 * Extends Drupal core's SetInlineBlockDependency to provide recursive
 * dependency resolution for nested layout blocks. Instead of stopping
 * at the immediate parent Layout Block, it follows the dependency chain all the
 * way to the main entity (usually a Node or Page Variant).
 *
 * This ensures that nested inline blocks inherit access permissions from the
 * main entity rather than intermediate Layout Blocks.
 *
 * @see \Drupal\layout_builder\EventSubscriber\SetInlineBlockDependency
 *
 * @internal
 *   Tagged services are internal.
 */
class SetInlineBlockDependency extends CoreSetInlineBlockDependency {

  /**
   * Maximum recursion depth to prevent infinite loops.
   */
  const MAX_RECURSION_DEPTH = 10;

  /**
   * The section storage handler.
   *
   * @var \Drupal\lb_plus\SectionStorageHandler
   */
  protected SectionStorageHandler $sectionStorageHandler;

  /**
   * Constructs a new SetInlineBlockDependency.
   *
   * @param \Drupal\Core\Entity\EntityRepositoryInterface $entity_repository
   *   The entity repository service.
   * @param \Drupal\Core\Database\Connection $database
   *   The database connection.
   * @param \Drupal\layout_builder\InlineBlockUsageInterface $usage
   *   The inline block usage service.
   * @param \Drupal\layout_builder\SectionStorage\SectionStorageManagerInterface $section_storage_manager
   *   The section storage manager.
   * @param \Drupal\lb_plus\SectionStorageHandler $section_storage_handler
   *   The section storage handler.
   * @param \Drupal\Core\Routing\RouteMatchInterface|null $current_route_match
   *    The current route match.
   */
  public function __construct(
    EntityRepositoryInterface $entity_repository,
    Connection $database,
    InlineBlockUsageInterface $usage,
    SectionStorageManagerInterface $section_storage_manager,
    SectionStorageHandler $section_storage_handler,
    ?RouteMatchInterface $current_route_match,
  ) {
    parent::__construct($entity_repository, $database, $usage, $section_storage_manager, $current_route_match);
    $this->sectionStorageHandler = $section_storage_handler;
  }

  /**
   * Handles the BlockContentEvents::BLOCK_CONTENT_GET_DEPENDENCY event.
   *
   * @param \Drupal\block_content\Event\BlockContentGetDependencyEvent $event
   *   The event.
   */
  public function onGetDependency(BlockContentGetDependencyEvent $event) {
    if ($dependency = $this->getRecursiveInlineBlockDependency($event->getBlockContentEntity(), $event->getOperation())) {
      $event->setAccessDependency($dependency);
    }
  }

  /**
   * Get the recursive access dependency of an inline block.
   *
   * This method follows the dependency chain recursively until it finds
   * the main entity.
   *
   * @param \Drupal\block_content\BlockContentInterface $block_content
   *   The block content entity.
   * @param string $operation
   *   The operation to be performed on the block.
   * @param array $visited
   *   Array of already visited block IDs to prevent circular references.
   * @param int $depth
   *   Current recursion depth.
   *
   * @return \Drupal\Core\Access\AccessibleInterface|null
   *   The main entity that should be used for access dependency, or NULL
   *   if no dependency can be determined.
   */
  protected function getRecursiveInlineBlockDependency(
    BlockContentInterface $block_content,
    string $operation,
    array $visited = [],
    int $depth = 0
  ): ?AccessibleInterface {

    if ($depth >= self::MAX_RECURSION_DEPTH) {
      \Drupal::logger('lb_plus')->warning(
        'Maximum recursion depth reached while resolving dependency for block @id',
        ['@id' => $block_content->id()]
      );
      return null;
    }

    $block_id = $block_content->id();
    if (in_array($block_id, $visited)) {
      \Drupal::logger('lb_plus')->warning(
        'Circular reference detected in dependency chain for block @id. Chain: @chain',
        ['@id' => $block_id, '@chain' => implode(' -> ', $visited)]
      );
      return null;
    }

    $visited[] = $block_id;

    $core_dependency = $this->getInlineBlockDependency($block_content, $operation);

    if (!$core_dependency) {
      return null;
    }

    // If the core dependency is layout-compatible AND is a block_content entity,
    // it's likely a Layout Block that might have its own parent - recurse.
    if ($this->isLayoutCompatibleEntity($core_dependency) && $core_dependency instanceof BlockContentInterface) {
      $recursive_dependency = $this->getRecursiveInlineBlockDependency($core_dependency, $operation, $visited, $depth + 1);

      if ($recursive_dependency) {
        return 'view' === $operation && $this->isAjax() ? new LayoutPreviewAccessAllowed() : $recursive_dependency;
      }
    }

    return 'view' === $operation && $this->isAjax() ? new LayoutPreviewAccessAllowed() : $core_dependency;
  }

  /**
   * Overrides LayoutEntityHelperTrait->getEntitySections.
   *
   * Makes the dependency setting aware of sections in nested layouts.
   *
   * @param \Drupal\Core\Entity\EntityInterface $entity
   *   The layout builder enabled entity.
   *
   * @return array|\Drupal\layout_builder\Section[]
   *   All sections within this entity.
   *
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   */
  protected function getEntitySections(EntityInterface $entity) {
    $section_storage = $this->getSectionStorageForEntity($entity);
    return $section_storage ? $this->sectionStorageHandler->getAllSections($section_storage) : [];
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc