annotate core/modules/layout_builder/src/EventSubscriber/SetInlineBlockDependency.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents af1871eacc83
children
rev   line source
Chris@17 1 <?php
Chris@17 2
Chris@17 3 namespace Drupal\layout_builder\EventSubscriber;
Chris@17 4
Chris@17 5 use Drupal\block_content\BlockContentEvents;
Chris@17 6 use Drupal\block_content\BlockContentInterface;
Chris@17 7 use Drupal\block_content\Event\BlockContentGetDependencyEvent;
Chris@17 8 use Drupal\Core\Database\Connection;
Chris@17 9 use Drupal\Core\Entity\EntityInterface;
Chris@17 10 use Drupal\Core\Entity\EntityTypeManagerInterface;
Chris@18 11 use Drupal\layout_builder\InlineBlockUsageInterface;
Chris@17 12 use Drupal\layout_builder\LayoutEntityHelperTrait;
Chris@18 13 use Drupal\layout_builder\SectionStorage\SectionStorageManagerInterface;
Chris@17 14 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
Chris@17 15
Chris@17 16 /**
Chris@17 17 * An event subscriber that returns an access dependency for inline blocks.
Chris@17 18 *
Chris@17 19 * When used within the layout builder the access dependency for inline blocks
Chris@17 20 * will be explicitly set but if access is evaluated outside of the layout
Chris@17 21 * builder then the dependency may not have been set.
Chris@17 22 *
Chris@17 23 * A known example of when the access dependency will not have been set is when
Chris@17 24 * determining 'view' or 'download' access to a file entity that is attached
Chris@17 25 * to a content block via a field that is using the private file system. The
Chris@17 26 * file access handler will evaluate access on the content block without setting
Chris@17 27 * the dependency.
Chris@17 28 *
Chris@17 29 * @internal
Chris@18 30 * Tagged services are internal.
Chris@17 31 *
Chris@17 32 * @see \Drupal\file\FileAccessControlHandler::checkAccess()
Chris@17 33 * @see \Drupal\block_content\BlockContentAccessControlHandler::checkAccess()
Chris@17 34 */
Chris@17 35 class SetInlineBlockDependency implements EventSubscriberInterface {
Chris@17 36
Chris@17 37 use LayoutEntityHelperTrait;
Chris@17 38
Chris@17 39 /**
Chris@17 40 * The entity type manager.
Chris@17 41 *
Chris@17 42 * @var \Drupal\Core\Entity\EntityTypeManagerInterface
Chris@17 43 */
Chris@17 44 protected $entityTypeManager;
Chris@17 45
Chris@17 46 /**
Chris@17 47 * The database connection.
Chris@17 48 *
Chris@17 49 * @var \Drupal\Core\Database\Connection
Chris@17 50 */
Chris@17 51 protected $database;
Chris@17 52
Chris@17 53 /**
Chris@17 54 * The inline block usage service.
Chris@17 55 *
Chris@18 56 * @var \Drupal\layout_builder\InlineBlockUsageInterface
Chris@17 57 */
Chris@17 58 protected $usage;
Chris@17 59
Chris@17 60 /**
Chris@17 61 * Constructs SetInlineBlockDependency object.
Chris@17 62 *
Chris@17 63 * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
Chris@17 64 * The entity type manager.
Chris@17 65 * @param \Drupal\Core\Database\Connection $database
Chris@17 66 * The database connection.
Chris@18 67 * @param \Drupal\layout_builder\InlineBlockUsageInterface $usage
Chris@17 68 * The inline block usage service.
Chris@18 69 * @param \Drupal\layout_builder\SectionStorage\SectionStorageManagerInterface $section_storage_manager
Chris@18 70 * The section storage manager.
Chris@17 71 */
Chris@18 72 public function __construct(EntityTypeManagerInterface $entity_type_manager, Connection $database, InlineBlockUsageInterface $usage, SectionStorageManagerInterface $section_storage_manager) {
Chris@17 73 $this->entityTypeManager = $entity_type_manager;
Chris@17 74 $this->database = $database;
Chris@17 75 $this->usage = $usage;
Chris@18 76 $this->sectionStorageManager = $section_storage_manager;
Chris@17 77 }
Chris@17 78
Chris@17 79 /**
Chris@17 80 * {@inheritdoc}
Chris@17 81 */
Chris@17 82 public static function getSubscribedEvents() {
Chris@17 83 return [
Chris@17 84 BlockContentEvents::BLOCK_CONTENT_GET_DEPENDENCY => 'onGetDependency',
Chris@17 85 ];
Chris@17 86 }
Chris@17 87
Chris@17 88 /**
Chris@17 89 * Handles the BlockContentEvents::INLINE_BLOCK_GET_DEPENDENCY event.
Chris@17 90 *
Chris@17 91 * @param \Drupal\block_content\Event\BlockContentGetDependencyEvent $event
Chris@17 92 * The event.
Chris@17 93 */
Chris@17 94 public function onGetDependency(BlockContentGetDependencyEvent $event) {
Chris@17 95 if ($dependency = $this->getInlineBlockDependency($event->getBlockContentEntity())) {
Chris@17 96 $event->setAccessDependency($dependency);
Chris@17 97 }
Chris@17 98 }
Chris@17 99
Chris@17 100 /**
Chris@17 101 * Get the access dependency of an inline block.
Chris@17 102 *
Chris@17 103 * If the block is used in an entity that entity will be returned as the
Chris@17 104 * dependency.
Chris@17 105 *
Chris@17 106 * For revisionable entities the entity will only be returned if it is used in
Chris@17 107 * the latest revision of the entity. For inline blocks that are not used in
Chris@17 108 * the latest revision but are used in a previous revision the entity will not
Chris@17 109 * be returned because calling
Chris@17 110 * \Drupal\Core\Access\AccessibleInterface::access() will only check access on
Chris@17 111 * the latest revision. Therefore if the previous revision of the entity was
Chris@17 112 * returned as the dependency access would be granted to inline block
Chris@17 113 * regardless of whether the user has access to the revision in which the
Chris@17 114 * inline block was used.
Chris@17 115 *
Chris@17 116 * @param \Drupal\block_content\BlockContentInterface $block_content
Chris@17 117 * The block content entity.
Chris@17 118 *
Chris@17 119 * @return \Drupal\Core\Entity\EntityInterface|null
Chris@17 120 * Returns the layout dependency.
Chris@17 121 *
Chris@17 122 * @see \Drupal\block_content\BlockContentAccessControlHandler::checkAccess()
Chris@17 123 * @see \Drupal\layout_builder\EventSubscriber\BlockComponentRenderArray::onBuildRender()
Chris@17 124 */
Chris@17 125 protected function getInlineBlockDependency(BlockContentInterface $block_content) {
Chris@17 126 $layout_entity_info = $this->usage->getUsage($block_content->id());
Chris@17 127 if (empty($layout_entity_info)) {
Chris@17 128 // If the block does not have usage information then we cannot set a
Chris@17 129 // dependency. It may be used by another module besides layout builder.
Chris@17 130 return NULL;
Chris@17 131 }
Chris@17 132 $layout_entity_storage = $this->entityTypeManager->getStorage($layout_entity_info->layout_entity_type);
Chris@17 133 $layout_entity = $layout_entity_storage->load($layout_entity_info->layout_entity_id);
Chris@17 134 if ($this->isLayoutCompatibleEntity($layout_entity)) {
Chris@17 135 if ($this->isBlockRevisionUsedInEntity($layout_entity, $block_content)) {
Chris@17 136 return $layout_entity;
Chris@17 137 }
Chris@17 138
Chris@17 139 }
Chris@17 140 return NULL;
Chris@17 141 }
Chris@17 142
Chris@17 143 /**
Chris@17 144 * Determines if a block content revision is used in an entity.
Chris@17 145 *
Chris@17 146 * @param \Drupal\Core\Entity\EntityInterface $layout_entity
Chris@17 147 * The layout entity.
Chris@17 148 * @param \Drupal\block_content\BlockContentInterface $block_content
Chris@17 149 * The block content revision.
Chris@17 150 *
Chris@17 151 * @return bool
Chris@17 152 * TRUE if the block content revision is used as an inline block in the
Chris@17 153 * layout entity.
Chris@17 154 */
Chris@17 155 protected function isBlockRevisionUsedInEntity(EntityInterface $layout_entity, BlockContentInterface $block_content) {
Chris@17 156 $sections_blocks_revision_ids = $this->getInlineBlockRevisionIdsInSections($this->getEntitySections($layout_entity));
Chris@17 157 return in_array($block_content->getRevisionId(), $sections_blocks_revision_ids);
Chris@17 158 }
Chris@17 159
Chris@17 160 }