Updated Drupal to 8.6. This goes with the following updates because it's possible...
[yaffs-website] / web / core / modules / layout_builder / src / EventSubscriber / SetInlineBlockDependency.php
diff --git a/web/core/modules/layout_builder/src/EventSubscriber/SetInlineBlockDependency.php b/web/core/modules/layout_builder/src/EventSubscriber/SetInlineBlockDependency.php
new file mode 100644 (file)
index 0000000..edc05f8
--- /dev/null
@@ -0,0 +1,156 @@
+<?php
+
+namespace Drupal\layout_builder\EventSubscriber;
+
+use Drupal\block_content\BlockContentEvents;
+use Drupal\block_content\BlockContentInterface;
+use Drupal\block_content\Event\BlockContentGetDependencyEvent;
+use Drupal\Core\Database\Connection;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\layout_builder\InlineBlockUsage;
+use Drupal\layout_builder\LayoutEntityHelperTrait;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * An event subscriber that returns an access dependency for inline blocks.
+ *
+ * When used within the layout builder the access dependency for inline blocks
+ * will be explicitly set but if access is evaluated outside of the layout
+ * builder then the dependency may not have been set.
+ *
+ * A known example of when the access dependency will not have been set is when
+ * determining 'view' or 'download' access to a file entity that is attached
+ * to a content block via a field that is using the private file system. The
+ * file access handler will evaluate access on the content block without setting
+ * the dependency.
+ *
+ * @internal
+ *
+ * @see \Drupal\file\FileAccessControlHandler::checkAccess()
+ * @see \Drupal\block_content\BlockContentAccessControlHandler::checkAccess()
+ */
+class SetInlineBlockDependency implements EventSubscriberInterface {
+
+  use LayoutEntityHelperTrait;
+
+  /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * The database connection.
+   *
+   * @var \Drupal\Core\Database\Connection
+   */
+  protected $database;
+
+  /**
+   * The inline block usage service.
+   *
+   * @var \Drupal\layout_builder\InlineBlockUsage
+   */
+  protected $usage;
+
+  /**
+   * Constructs SetInlineBlockDependency object.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity type manager.
+   * @param \Drupal\Core\Database\Connection $database
+   *   The database connection.
+   * @param \Drupal\layout_builder\InlineBlockUsage $usage
+   *   The inline block usage service.
+   */
+  public function __construct(EntityTypeManagerInterface $entity_type_manager, Connection $database, InlineBlockUsage $usage) {
+    $this->entityTypeManager = $entity_type_manager;
+    $this->database = $database;
+    $this->usage = $usage;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getSubscribedEvents() {
+    return [
+      BlockContentEvents::BLOCK_CONTENT_GET_DEPENDENCY => 'onGetDependency',
+    ];
+  }
+
+  /**
+   * Handles the BlockContentEvents::INLINE_BLOCK_GET_DEPENDENCY event.
+   *
+   * @param \Drupal\block_content\Event\BlockContentGetDependencyEvent $event
+   *   The event.
+   */
+  public function onGetDependency(BlockContentGetDependencyEvent $event) {
+    if ($dependency = $this->getInlineBlockDependency($event->getBlockContentEntity())) {
+      $event->setAccessDependency($dependency);
+    }
+  }
+
+  /**
+   * Get the access dependency of an inline block.
+   *
+   * If the block is used in an entity that entity will be returned as the
+   * dependency.
+   *
+   * For revisionable entities the entity will only be returned if it is used in
+   * the latest revision of the entity. For inline blocks that are not used in
+   * the latest revision but are used in a previous revision the entity will not
+   * be returned because calling
+   * \Drupal\Core\Access\AccessibleInterface::access() will only check access on
+   * the latest revision. Therefore if the previous revision of the entity was
+   * returned as the dependency access would be granted to inline block
+   * regardless of whether the user has access to the revision in which the
+   * inline block was used.
+   *
+   * @param \Drupal\block_content\BlockContentInterface $block_content
+   *   The block content entity.
+   *
+   * @return \Drupal\Core\Entity\EntityInterface|null
+   *   Returns the layout dependency.
+   *
+   * @see \Drupal\block_content\BlockContentAccessControlHandler::checkAccess()
+   * @see \Drupal\layout_builder\EventSubscriber\BlockComponentRenderArray::onBuildRender()
+   */
+  protected function getInlineBlockDependency(BlockContentInterface $block_content) {
+    $layout_entity_info = $this->usage->getUsage($block_content->id());
+    if (empty($layout_entity_info)) {
+      // If the block does not have usage information then we cannot set a
+      // dependency. It may be used by another module besides layout builder.
+      return NULL;
+    }
+    /** @var \Drupal\layout_builder\InlineBlockUsage $usage */
+    $layout_entity_storage = $this->entityTypeManager->getStorage($layout_entity_info->layout_entity_type);
+    $layout_entity = $layout_entity_storage->load($layout_entity_info->layout_entity_id);
+    if ($this->isLayoutCompatibleEntity($layout_entity)) {
+      if ($this->isBlockRevisionUsedInEntity($layout_entity, $block_content)) {
+        return $layout_entity;
+      }
+
+    }
+    return NULL;
+  }
+
+  /**
+   * Determines if a block content revision is used in an entity.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $layout_entity
+   *   The layout entity.
+   * @param \Drupal\block_content\BlockContentInterface $block_content
+   *   The block content revision.
+   *
+   * @return bool
+   *   TRUE if the block content revision is used as an inline block in the
+   *   layout entity.
+   */
+  protected function isBlockRevisionUsedInEntity(EntityInterface $layout_entity, BlockContentInterface $block_content) {
+    $sections_blocks_revision_ids = $this->getInlineBlockRevisionIdsInSections($this->getEntitySections($layout_entity));
+    return in_array($block_content->getRevisionId(), $sections_blocks_revision_ids);
+  }
+
+}