3 namespace Drupal\layout_builder\EventSubscriber;
5 use Drupal\block_content\BlockContentEvents;
6 use Drupal\block_content\BlockContentInterface;
7 use Drupal\block_content\Event\BlockContentGetDependencyEvent;
8 use Drupal\Core\Database\Connection;
9 use Drupal\Core\Entity\EntityInterface;
10 use Drupal\Core\Entity\EntityTypeManagerInterface;
11 use Drupal\layout_builder\InlineBlockUsage;
12 use Drupal\layout_builder\LayoutEntityHelperTrait;
13 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
16 * An event subscriber that returns an access dependency for inline blocks.
18 * When used within the layout builder the access dependency for inline blocks
19 * will be explicitly set but if access is evaluated outside of the layout
20 * builder then the dependency may not have been set.
22 * A known example of when the access dependency will not have been set is when
23 * determining 'view' or 'download' access to a file entity that is attached
24 * to a content block via a field that is using the private file system. The
25 * file access handler will evaluate access on the content block without setting
30 * @see \Drupal\file\FileAccessControlHandler::checkAccess()
31 * @see \Drupal\block_content\BlockContentAccessControlHandler::checkAccess()
33 class SetInlineBlockDependency implements EventSubscriberInterface {
35 use LayoutEntityHelperTrait;
38 * The entity type manager.
40 * @var \Drupal\Core\Entity\EntityTypeManagerInterface
42 protected $entityTypeManager;
45 * The database connection.
47 * @var \Drupal\Core\Database\Connection
52 * The inline block usage service.
54 * @var \Drupal\layout_builder\InlineBlockUsage
59 * Constructs SetInlineBlockDependency object.
61 * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
62 * The entity type manager.
63 * @param \Drupal\Core\Database\Connection $database
64 * The database connection.
65 * @param \Drupal\layout_builder\InlineBlockUsage $usage
66 * The inline block usage service.
68 public function __construct(EntityTypeManagerInterface $entity_type_manager, Connection $database, InlineBlockUsage $usage) {
69 $this->entityTypeManager = $entity_type_manager;
70 $this->database = $database;
71 $this->usage = $usage;
77 public static function getSubscribedEvents() {
79 BlockContentEvents::BLOCK_CONTENT_GET_DEPENDENCY => 'onGetDependency',
84 * Handles the BlockContentEvents::INLINE_BLOCK_GET_DEPENDENCY event.
86 * @param \Drupal\block_content\Event\BlockContentGetDependencyEvent $event
89 public function onGetDependency(BlockContentGetDependencyEvent $event) {
90 if ($dependency = $this->getInlineBlockDependency($event->getBlockContentEntity())) {
91 $event->setAccessDependency($dependency);
96 * Get the access dependency of an inline block.
98 * If the block is used in an entity that entity will be returned as the
101 * For revisionable entities the entity will only be returned if it is used in
102 * the latest revision of the entity. For inline blocks that are not used in
103 * the latest revision but are used in a previous revision the entity will not
104 * be returned because calling
105 * \Drupal\Core\Access\AccessibleInterface::access() will only check access on
106 * the latest revision. Therefore if the previous revision of the entity was
107 * returned as the dependency access would be granted to inline block
108 * regardless of whether the user has access to the revision in which the
109 * inline block was used.
111 * @param \Drupal\block_content\BlockContentInterface $block_content
112 * The block content entity.
114 * @return \Drupal\Core\Entity\EntityInterface|null
115 * Returns the layout dependency.
117 * @see \Drupal\block_content\BlockContentAccessControlHandler::checkAccess()
118 * @see \Drupal\layout_builder\EventSubscriber\BlockComponentRenderArray::onBuildRender()
120 protected function getInlineBlockDependency(BlockContentInterface $block_content) {
121 $layout_entity_info = $this->usage->getUsage($block_content->id());
122 if (empty($layout_entity_info)) {
123 // If the block does not have usage information then we cannot set a
124 // dependency. It may be used by another module besides layout builder.
127 /** @var \Drupal\layout_builder\InlineBlockUsage $usage */
128 $layout_entity_storage = $this->entityTypeManager->getStorage($layout_entity_info->layout_entity_type);
129 $layout_entity = $layout_entity_storage->load($layout_entity_info->layout_entity_id);
130 if ($this->isLayoutCompatibleEntity($layout_entity)) {
131 if ($this->isBlockRevisionUsedInEntity($layout_entity, $block_content)) {
132 return $layout_entity;
140 * Determines if a block content revision is used in an entity.
142 * @param \Drupal\Core\Entity\EntityInterface $layout_entity
144 * @param \Drupal\block_content\BlockContentInterface $block_content
145 * The block content revision.
148 * TRUE if the block content revision is used as an inline block in the
151 protected function isBlockRevisionUsedInEntity(EntityInterface $layout_entity, BlockContentInterface $block_content) {
152 $sections_blocks_revision_ids = $this->getInlineBlockRevisionIdsInSections($this->getEntitySections($layout_entity));
153 return in_array($block_content->getRevisionId(), $sections_blocks_revision_ids);