comparison core/modules/layout_builder/src/EventSubscriber/SetInlineBlockDependency.php @ 17:129ea1e6d783

Update, including to Drupal core 8.6.10
author Chris Cannam
date Thu, 28 Feb 2019 13:21:36 +0000
parents
children af1871eacc83
comparison
equal deleted inserted replaced
16:c2387f117808 17:129ea1e6d783
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 }