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 }
|