Added Entity and Entity Reference Revisions which got dropped somewhere along the...
[yaffs-website] / web / modules / contrib / entity / src / QueryAccess / QueryAccessHandlerBase.php
1 <?php
2
3 namespace Drupal\entity\QueryAccess;
4
5 use Drupal\Core\Entity\EntityHandlerInterface;
6 use Drupal\Core\Entity\EntityPublishedInterface;
7 use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
8 use Drupal\Core\Entity\EntityTypeInterface;
9 use Drupal\Core\Session\AccountInterface;
10 use Drupal\user\EntityOwnerInterface;
11 use Symfony\Component\DependencyInjection\ContainerInterface;
12 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
13
14 /**
15  * Provides common logic for query access handlers.
16  *
17  * @see \Drupal\entity\QueryAccess\QueryAccessHandler
18  * @see \Drupal\entity\QueryAccess\UncacheableQueryAccessHandler
19  */
20 abstract class QueryAccessHandlerBase implements EntityHandlerInterface, QueryAccessHandlerInterface {
21
22   /**
23    * The entity type.
24    *
25    * @var \Drupal\Core\Entity\EntityTypeInterface
26    */
27   protected $entityType;
28
29   /**
30    * The entity type bundle info.
31    *
32    * @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface
33    */
34   protected $bundleInfo;
35
36   /**
37    * The event dispatcher.
38    *
39    * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
40    */
41   protected $eventDispatcher;
42
43   /**
44    * The current user.
45    *
46    * @var \Drupal\Core\Session\AccountInterface
47    */
48   protected $currentUser;
49
50   /**
51    * Constructs a new QueryAccessHandlerBase object.
52    *
53    * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
54    *   The entity type.
55    * @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $bundle_info
56    *   The entity type bundle info.
57    * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
58    *   The event dispatcher.
59    * @param \Drupal\Core\Session\AccountInterface $current_user
60    *   The current user.
61    */
62   public function __construct(EntityTypeInterface $entity_type, EntityTypeBundleInfoInterface $bundle_info, EventDispatcherInterface $event_dispatcher, AccountInterface $current_user) {
63     $this->entityType = $entity_type;
64     $this->bundleInfo = $bundle_info;
65     $this->eventDispatcher = $event_dispatcher;
66     $this->currentUser = $current_user;
67   }
68
69   /**
70    * {@inheritdoc}
71    */
72   public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
73     return new static(
74       $entity_type,
75       $container->get('entity_type.bundle.info'),
76       $container->get('event_dispatcher'),
77       $container->get('current_user')
78     );
79   }
80
81   /**
82    * {@inheritdoc}
83    */
84   public function getConditions($operation, AccountInterface $account = NULL) {
85     $account = $account ?: $this->currentUser;
86     $entity_type_id = $this->entityType->id();
87     $conditions = $this->buildConditions($operation, $account);
88
89     // Allow other modules to modify the conditions before they are used.
90     $event = new QueryAccessEvent($conditions, $operation, $account);
91     $this->eventDispatcher->dispatch("entity.query_access.{$entity_type_id}", $event);
92
93     return $conditions;
94   }
95
96   /**
97    * Builds the conditions for the given operation and user.
98    *
99    * @param string $operation
100    *   The access operation. Usually one of "view", "update" or "delete".
101    * @param \Drupal\Core\Session\AccountInterface $account
102    *   The user for which to restrict access.
103    *
104    * @return \Drupal\entity\QueryAccess\ConditionGroup
105    *   The conditions.
106    */
107   public function buildConditions($operation, AccountInterface $account) {
108     $entity_type_id = $this->entityType->id();
109     $has_owner = $this->entityType->entityClassImplements(EntityOwnerInterface::class);
110
111     if ($account->hasPermission("administer {$entity_type_id}")) {
112       // The user has full access to all operations, no conditions needed.
113       $conditions = new ConditionGroup('OR');
114       $conditions->addCacheContexts(['user.permissions']);
115       return $conditions;
116     }
117
118     if ($has_owner) {
119       $entity_conditions = $this->buildEntityOwnerConditions($operation, $account);
120     }
121     else {
122       $entity_conditions = $this->buildEntityConditions($operation, $account);
123     }
124
125     $conditions = NULL;
126     if ($operation == 'view' && $this->entityType->entityClassImplements(EntityPublishedInterface::class)) {
127       $uid_key = $this->entityType->getKey('uid');
128       $published_key = $this->entityType->getKey('published');
129       $published_conditions = NULL;
130       $unpublished_conditions = NULL;
131
132       if ($entity_conditions) {
133         // Restrict the existing conditions to published entities only.
134         $published_conditions = new ConditionGroup('AND');
135         $published_conditions->addCacheContexts(['user.permissions']);
136         $published_conditions->addCondition($entity_conditions);
137         $published_conditions->addCondition($published_key, '1');
138       }
139       if ($has_owner && $account->hasPermission("view own unpublished $entity_type_id")) {
140         $unpublished_conditions = new ConditionGroup('AND');
141         $unpublished_conditions->addCacheContexts(['user']);
142         $unpublished_conditions->addCondition($uid_key, $account->id());
143         $unpublished_conditions->addCondition($published_key, '0');
144       }
145
146       if ($published_conditions && $unpublished_conditions) {
147         $conditions = new ConditionGroup('OR');
148         $conditions->addCondition($published_conditions);
149         $conditions->addCondition($unpublished_conditions);
150       }
151       elseif ($published_conditions) {
152         $conditions = $published_conditions;
153       }
154       elseif ($unpublished_conditions) {
155         $conditions = $unpublished_conditions;
156       }
157     }
158     else {
159       $conditions = $entity_conditions;
160     }
161
162     if (!$conditions) {
163       // The user doesn't have access to any entities.
164       // Falsify the query to ensure no results are returned.
165       $conditions = new ConditionGroup('OR');
166       $conditions->addCacheContexts(['user.permissions']);
167       $conditions->alwaysFalse();
168     }
169
170     return $conditions;
171   }
172
173   /**
174    * Builds the conditions for entities that have an owner.
175    *
176    * @param string $operation
177    *   The access operation. Usually one of "view", "update" or "delete".
178    * @param \Drupal\Core\Session\AccountInterface $account
179    *   The user for which to restrict access.
180    *
181    * @return \Drupal\entity\QueryAccess\ConditionGroup|null
182    *   The conditions, or NULL if the user doesn't have access to any entity.
183    */
184   protected function buildEntityOwnerConditions($operation, AccountInterface $account) {
185     $entity_type_id = $this->entityType->id();
186     $uid_key = $this->entityType->getKey('uid');
187     $bundle_key = $this->entityType->getKey('bundle');
188
189     $conditions = new ConditionGroup('OR');
190     $conditions->addCacheContexts(['user.permissions']);
191     // Any $entity_type permission.
192     if ($account->hasPermission("$operation any $entity_type_id")) {
193       // The user has full access, no conditions needed.
194       return $conditions;
195     }
196
197     // Own $entity_type permission.
198     if ($account->hasPermission("$operation own $entity_type_id")) {
199       $conditions->addCacheContexts(['user']);
200       $conditions->addCondition($uid_key, $account->id());
201     }
202
203     $bundles = array_keys($this->bundleInfo->getBundleInfo($entity_type_id));
204     $bundles_with_any_permission = [];
205     $bundles_with_own_permission = [];
206     foreach ($bundles as $bundle) {
207       if ($account->hasPermission("$operation any $bundle $entity_type_id")) {
208         $bundles_with_any_permission[] = $bundle;
209       }
210       if ($account->hasPermission("$operation own $bundle $entity_type_id")) {
211         $bundles_with_own_permission[] = $bundle;
212       }
213     }
214     // Any $bundle permission.
215     if ($bundles_with_any_permission) {
216       $conditions->addCondition($bundle_key, $bundles_with_any_permission);
217     }
218     // Own $bundle permission.
219     if ($bundles_with_own_permission) {
220       $conditions->addCacheContexts(['user']);
221       $conditions->addCondition((new ConditionGroup('AND'))
222         ->addCondition($uid_key, $account->id())
223         ->addCondition($bundle_key, $bundles_with_own_permission)
224       );
225     }
226
227     return $conditions->count() ? $conditions : NULL;
228   }
229
230   /**
231    * Builds the conditions for entities that do not have an owner.
232    *
233    * @param string $operation
234    *   The access operation. Usually one of "view", "update" or "delete".
235    * @param \Drupal\Core\Session\AccountInterface $account
236    *   The user for which to restrict access.
237    *
238    * @return \Drupal\entity\QueryAccess\ConditionGroup|null
239    *   The conditions, or NULL if the user doesn't have access to any entity.
240    */
241   protected function buildEntityConditions($operation, AccountInterface $account) {
242     $entity_type_id = $this->entityType->id();
243     $bundle_key = $this->entityType->getKey('bundle');
244
245     $conditions = new ConditionGroup('OR');
246     $conditions->addCacheContexts(['user.permissions']);
247     // The $entity_type permission.
248     if ($account->hasPermission("$operation $entity_type_id")) {
249       // The user has full access, no conditions needed.
250       return $conditions;
251     }
252
253     $bundles = array_keys($this->bundleInfo->getBundleInfo($entity_type_id));
254     $bundles_with_any_permission = [];
255     foreach ($bundles as $bundle) {
256       if ($account->hasPermission("$operation $bundle $entity_type_id")) {
257         $bundles_with_any_permission[] = $bundle;
258       }
259     }
260     // The $bundle permission.
261     if ($bundles_with_any_permission) {
262       $conditions->addCondition($bundle_key, $bundles_with_any_permission);
263     }
264
265     return $conditions->count() ? $conditions : NULL;
266   }
267
268 }