edc05f83ddf8798f89415aa46db6199dc24a8ae2
[yaffs-website] / web / core / modules / layout_builder / src / EventSubscriber / SetInlineBlockDependency.php
1 <?php
2
3 namespace Drupal\layout_builder\EventSubscriber;
4
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;
14
15 /**
16  * An event subscriber that returns an access dependency for inline blocks.
17  *
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.
21  *
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
26  * the dependency.
27  *
28  * @internal
29  *
30  * @see \Drupal\file\FileAccessControlHandler::checkAccess()
31  * @see \Drupal\block_content\BlockContentAccessControlHandler::checkAccess()
32  */
33 class SetInlineBlockDependency implements EventSubscriberInterface {
34
35   use LayoutEntityHelperTrait;
36
37   /**
38    * The entity type manager.
39    *
40    * @var \Drupal\Core\Entity\EntityTypeManagerInterface
41    */
42   protected $entityTypeManager;
43
44   /**
45    * The database connection.
46    *
47    * @var \Drupal\Core\Database\Connection
48    */
49   protected $database;
50
51   /**
52    * The inline block usage service.
53    *
54    * @var \Drupal\layout_builder\InlineBlockUsage
55    */
56   protected $usage;
57
58   /**
59    * Constructs SetInlineBlockDependency object.
60    *
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.
67    */
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;
72   }
73
74   /**
75    * {@inheritdoc}
76    */
77   public static function getSubscribedEvents() {
78     return [
79       BlockContentEvents::BLOCK_CONTENT_GET_DEPENDENCY => 'onGetDependency',
80     ];
81   }
82
83   /**
84    * Handles the BlockContentEvents::INLINE_BLOCK_GET_DEPENDENCY event.
85    *
86    * @param \Drupal\block_content\Event\BlockContentGetDependencyEvent $event
87    *   The event.
88    */
89   public function onGetDependency(BlockContentGetDependencyEvent $event) {
90     if ($dependency = $this->getInlineBlockDependency($event->getBlockContentEntity())) {
91       $event->setAccessDependency($dependency);
92     }
93   }
94
95   /**
96    * Get the access dependency of an inline block.
97    *
98    * If the block is used in an entity that entity will be returned as the
99    * dependency.
100    *
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.
110    *
111    * @param \Drupal\block_content\BlockContentInterface $block_content
112    *   The block content entity.
113    *
114    * @return \Drupal\Core\Entity\EntityInterface|null
115    *   Returns the layout dependency.
116    *
117    * @see \Drupal\block_content\BlockContentAccessControlHandler::checkAccess()
118    * @see \Drupal\layout_builder\EventSubscriber\BlockComponentRenderArray::onBuildRender()
119    */
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.
125       return NULL;
126     }
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;
133       }
134
135     }
136     return NULL;
137   }
138
139   /**
140    * Determines if a block content revision is used in an entity.
141    *
142    * @param \Drupal\Core\Entity\EntityInterface $layout_entity
143    *   The layout entity.
144    * @param \Drupal\block_content\BlockContentInterface $block_content
145    *   The block content revision.
146    *
147    * @return bool
148    *   TRUE if the block content revision is used as an inline block in the
149    *   layout entity.
150    */
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);
154   }
155
156 }