annotate core/modules/block/src/BlockAccessControlHandler.php @ 19:fa3358dc1485 tip

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