/core/modules/block/src/BlockAccessControlHandler.php

https://gitlab.com/geeta7/drupal · PHP · 176 lines · 91 code · 18 blank · 67 comment · 9 complexity · ab44042b817b90df337ca7d2ad97198b MD5 · raw file

  1. <?php
  2. /**
  3. * @file
  4. * Contains \Drupal\block\BlockAccessControlHandler.
  5. */
  6. namespace Drupal\block;
  7. use Drupal\Component\Plugin\Exception\ContextException;
  8. use Drupal\Core\Access\AccessResult;
  9. use Drupal\Core\Cache\Cache;
  10. use Drupal\Core\Cache\CacheableDependencyInterface;
  11. use Drupal\Core\Condition\ConditionAccessResolverTrait;
  12. use Drupal\Core\Entity\EntityAccessControlHandler;
  13. use Drupal\Core\Entity\EntityHandlerInterface;
  14. use Drupal\Core\Entity\EntityInterface;
  15. use Drupal\Core\Entity\EntityTypeInterface;
  16. use Drupal\Core\Executable\ExecutableManagerInterface;
  17. use Drupal\Core\Plugin\Context\ContextHandlerInterface;
  18. use Drupal\Core\Plugin\Context\ContextRepositoryInterface;
  19. use Drupal\Core\Plugin\ContextAwarePluginInterface;
  20. use Drupal\Core\Session\AccountInterface;
  21. use Symfony\Component\DependencyInjection\ContainerInterface;
  22. /**
  23. * Defines the access control handler for the block entity type.
  24. *
  25. * @see \Drupal\block\Entity\Block
  26. */
  27. class BlockAccessControlHandler extends EntityAccessControlHandler implements EntityHandlerInterface {
  28. use ConditionAccessResolverTrait;
  29. /**
  30. * The condition plugin manager.
  31. *
  32. * @var \Drupal\Core\Executable\ExecutableManagerInterface
  33. */
  34. protected $manager;
  35. /**
  36. * The plugin context handler.
  37. *
  38. * @var \Drupal\Core\Plugin\Context\ContextHandlerInterface
  39. */
  40. protected $contextHandler;
  41. /**
  42. * The context manager service.
  43. *
  44. * @var \Drupal\Core\Plugin\Context\ContextRepositoryInterface
  45. */
  46. protected $contextRepository;
  47. /**
  48. * {@inheritdoc}
  49. */
  50. public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
  51. return new static(
  52. $entity_type,
  53. $container->get('plugin.manager.condition'),
  54. $container->get('context.handler'),
  55. $container->get('context.repository')
  56. );
  57. }
  58. /**
  59. * Constructs the block access control handler instance
  60. *
  61. * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
  62. * The entity type definition.
  63. * @param \Drupal\Core\Executable\ExecutableManagerInterface $manager
  64. * The ConditionManager for checking visibility of blocks.
  65. * @param \Drupal\Core\Plugin\Context\ContextHandlerInterface $context_handler
  66. * The ContextHandler for applying contexts to conditions properly.
  67. * @param \Drupal\Core\Plugin\Context\ContextRepositoryInterface $context_repository
  68. * The lazy context repository service.
  69. */
  70. public function __construct(EntityTypeInterface $entity_type, ExecutableManagerInterface $manager, ContextHandlerInterface $context_handler, ContextRepositoryInterface $context_repository ) {
  71. parent::__construct($entity_type);
  72. $this->manager = $manager;
  73. $this->contextHandler = $context_handler;
  74. $this->contextRepository = $context_repository;
  75. }
  76. /**
  77. * {@inheritdoc}
  78. */
  79. protected function checkAccess(EntityInterface $entity, $operation, AccountInterface $account) {
  80. /** @var \Drupal\block\BlockInterface $entity */
  81. if ($operation != 'view') {
  82. return parent::checkAccess($entity, $operation, $account);
  83. }
  84. // Don't grant access to disabled blocks.
  85. if (!$entity->status()) {
  86. return AccessResult::forbidden()->cacheUntilEntityChanges($entity);
  87. }
  88. else {
  89. $conditions = [];
  90. $missing_context = FALSE;
  91. foreach ($entity->getVisibilityConditions() as $condition_id => $condition) {
  92. if ($condition instanceof ContextAwarePluginInterface) {
  93. try {
  94. $contexts = $this->contextRepository->getRuntimeContexts(array_values($condition->getContextMapping()));
  95. $this->contextHandler->applyContextMapping($condition, $contexts);
  96. }
  97. catch (ContextException $e) {
  98. $missing_context = TRUE;
  99. }
  100. }
  101. $conditions[$condition_id] = $condition;
  102. }
  103. if ($missing_context) {
  104. // If any context is missing then we might be missing cacheable
  105. // metadata, and don't know based on what conditions the block is
  106. // accessible or not. For example, blocks that have a node type
  107. // condition will have a missing context on any non-node route like the
  108. // frontpage.
  109. // @todo Avoid setting max-age 0 for some or all cases, for example by
  110. // treating available contexts without value differently in
  111. // https://www.drupal.org/node/2521956.
  112. $access = AccessResult::forbidden()->setCacheMaxAge(0);
  113. }
  114. elseif ($this->resolveConditions($conditions, 'and') !== FALSE) {
  115. // Delegate to the plugin.
  116. $block_plugin = $entity->getPlugin();
  117. try {
  118. if ($block_plugin instanceof ContextAwarePluginInterface) {
  119. $contexts = $this->contextRepository->getRuntimeContexts(array_values($block_plugin->getContextMapping()));
  120. $this->contextHandler->applyContextMapping($block_plugin, $contexts);
  121. }
  122. $access = $block_plugin->access($account, TRUE);
  123. }
  124. catch (ContextException $e) {
  125. // Setting access to forbidden if any context is missing for the same
  126. // reasons as with conditions (described in the comment above).
  127. // @todo Avoid setting max-age 0 for some or all cases, for example by
  128. // treating available contexts without value differently in
  129. // https://www.drupal.org/node/2521956.
  130. $access = AccessResult::forbidden()->setCacheMaxAge(0);
  131. }
  132. }
  133. else {
  134. $access = AccessResult::forbidden();
  135. }
  136. $this->mergeCacheabilityFromConditions($access, $conditions);
  137. // Ensure that access is evaluated again when the block changes.
  138. return $access->cacheUntilEntityChanges($entity);
  139. }
  140. }
  141. /**
  142. * Merges cacheable metadata from conditions onto the access result object.
  143. *
  144. * @param \Drupal\Core\Access\AccessResult $access
  145. * The access result object.
  146. * @param \Drupal\Core\Condition\ConditionInterface[] $conditions
  147. * List of visibility conditions.
  148. */
  149. protected function mergeCacheabilityFromConditions(AccessResult $access, array $conditions) {
  150. foreach ($conditions as $condition) {
  151. if ($condition instanceof CacheableDependencyInterface) {
  152. $access->addCacheTags($condition->getCacheTags());
  153. $access->addCacheContexts($condition->getCacheContexts());
  154. $access->setCacheMaxAge(Cache::mergeMaxAges($access->getCacheMaxAge(), $condition->getCacheMaxAge()));
  155. }
  156. }
  157. }
  158. }