Added Entity and Entity Reference Revisions which got dropped somewhere along the...
[yaffs-website] / web / modules / contrib / entity / src / QueryAccess / QueryAccessHandlerBase.php
diff --git a/web/modules/contrib/entity/src/QueryAccess/QueryAccessHandlerBase.php b/web/modules/contrib/entity/src/QueryAccess/QueryAccessHandlerBase.php
new file mode 100644 (file)
index 0000000..aaa1315
--- /dev/null
@@ -0,0 +1,268 @@
+<?php
+
+namespace Drupal\entity\QueryAccess;
+
+use Drupal\Core\Entity\EntityHandlerInterface;
+use Drupal\Core\Entity\EntityPublishedInterface;
+use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\user\EntityOwnerInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
+
+/**
+ * Provides common logic for query access handlers.
+ *
+ * @see \Drupal\entity\QueryAccess\QueryAccessHandler
+ * @see \Drupal\entity\QueryAccess\UncacheableQueryAccessHandler
+ */
+abstract class QueryAccessHandlerBase implements EntityHandlerInterface, QueryAccessHandlerInterface {
+
+  /**
+   * The entity type.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeInterface
+   */
+  protected $entityType;
+
+  /**
+   * The entity type bundle info.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface
+   */
+  protected $bundleInfo;
+
+  /**
+   * The event dispatcher.
+   *
+   * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
+   */
+  protected $eventDispatcher;
+
+  /**
+   * The current user.
+   *
+   * @var \Drupal\Core\Session\AccountInterface
+   */
+  protected $currentUser;
+
+  /**
+   * Constructs a new QueryAccessHandlerBase object.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
+   *   The entity type.
+   * @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $bundle_info
+   *   The entity type bundle info.
+   * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
+   *   The event dispatcher.
+   * @param \Drupal\Core\Session\AccountInterface $current_user
+   *   The current user.
+   */
+  public function __construct(EntityTypeInterface $entity_type, EntityTypeBundleInfoInterface $bundle_info, EventDispatcherInterface $event_dispatcher, AccountInterface $current_user) {
+    $this->entityType = $entity_type;
+    $this->bundleInfo = $bundle_info;
+    $this->eventDispatcher = $event_dispatcher;
+    $this->currentUser = $current_user;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
+    return new static(
+      $entity_type,
+      $container->get('entity_type.bundle.info'),
+      $container->get('event_dispatcher'),
+      $container->get('current_user')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getConditions($operation, AccountInterface $account = NULL) {
+    $account = $account ?: $this->currentUser;
+    $entity_type_id = $this->entityType->id();
+    $conditions = $this->buildConditions($operation, $account);
+
+    // Allow other modules to modify the conditions before they are used.
+    $event = new QueryAccessEvent($conditions, $operation, $account);
+    $this->eventDispatcher->dispatch("entity.query_access.{$entity_type_id}", $event);
+
+    return $conditions;
+  }
+
+  /**
+   * Builds the conditions for the given operation and user.
+   *
+   * @param string $operation
+   *   The access operation. Usually one of "view", "update" or "delete".
+   * @param \Drupal\Core\Session\AccountInterface $account
+   *   The user for which to restrict access.
+   *
+   * @return \Drupal\entity\QueryAccess\ConditionGroup
+   *   The conditions.
+   */
+  public function buildConditions($operation, AccountInterface $account) {
+    $entity_type_id = $this->entityType->id();
+    $has_owner = $this->entityType->entityClassImplements(EntityOwnerInterface::class);
+
+    if ($account->hasPermission("administer {$entity_type_id}")) {
+      // The user has full access to all operations, no conditions needed.
+      $conditions = new ConditionGroup('OR');
+      $conditions->addCacheContexts(['user.permissions']);
+      return $conditions;
+    }
+
+    if ($has_owner) {
+      $entity_conditions = $this->buildEntityOwnerConditions($operation, $account);
+    }
+    else {
+      $entity_conditions = $this->buildEntityConditions($operation, $account);
+    }
+
+    $conditions = NULL;
+    if ($operation == 'view' && $this->entityType->entityClassImplements(EntityPublishedInterface::class)) {
+      $uid_key = $this->entityType->getKey('uid');
+      $published_key = $this->entityType->getKey('published');
+      $published_conditions = NULL;
+      $unpublished_conditions = NULL;
+
+      if ($entity_conditions) {
+        // Restrict the existing conditions to published entities only.
+        $published_conditions = new ConditionGroup('AND');
+        $published_conditions->addCacheContexts(['user.permissions']);
+        $published_conditions->addCondition($entity_conditions);
+        $published_conditions->addCondition($published_key, '1');
+      }
+      if ($has_owner && $account->hasPermission("view own unpublished $entity_type_id")) {
+        $unpublished_conditions = new ConditionGroup('AND');
+        $unpublished_conditions->addCacheContexts(['user']);
+        $unpublished_conditions->addCondition($uid_key, $account->id());
+        $unpublished_conditions->addCondition($published_key, '0');
+      }
+
+      if ($published_conditions && $unpublished_conditions) {
+        $conditions = new ConditionGroup('OR');
+        $conditions->addCondition($published_conditions);
+        $conditions->addCondition($unpublished_conditions);
+      }
+      elseif ($published_conditions) {
+        $conditions = $published_conditions;
+      }
+      elseif ($unpublished_conditions) {
+        $conditions = $unpublished_conditions;
+      }
+    }
+    else {
+      $conditions = $entity_conditions;
+    }
+
+    if (!$conditions) {
+      // The user doesn't have access to any entities.
+      // Falsify the query to ensure no results are returned.
+      $conditions = new ConditionGroup('OR');
+      $conditions->addCacheContexts(['user.permissions']);
+      $conditions->alwaysFalse();
+    }
+
+    return $conditions;
+  }
+
+  /**
+   * Builds the conditions for entities that have an owner.
+   *
+   * @param string $operation
+   *   The access operation. Usually one of "view", "update" or "delete".
+   * @param \Drupal\Core\Session\AccountInterface $account
+   *   The user for which to restrict access.
+   *
+   * @return \Drupal\entity\QueryAccess\ConditionGroup|null
+   *   The conditions, or NULL if the user doesn't have access to any entity.
+   */
+  protected function buildEntityOwnerConditions($operation, AccountInterface $account) {
+    $entity_type_id = $this->entityType->id();
+    $uid_key = $this->entityType->getKey('uid');
+    $bundle_key = $this->entityType->getKey('bundle');
+
+    $conditions = new ConditionGroup('OR');
+    $conditions->addCacheContexts(['user.permissions']);
+    // Any $entity_type permission.
+    if ($account->hasPermission("$operation any $entity_type_id")) {
+      // The user has full access, no conditions needed.
+      return $conditions;
+    }
+
+    // Own $entity_type permission.
+    if ($account->hasPermission("$operation own $entity_type_id")) {
+      $conditions->addCacheContexts(['user']);
+      $conditions->addCondition($uid_key, $account->id());
+    }
+
+    $bundles = array_keys($this->bundleInfo->getBundleInfo($entity_type_id));
+    $bundles_with_any_permission = [];
+    $bundles_with_own_permission = [];
+    foreach ($bundles as $bundle) {
+      if ($account->hasPermission("$operation any $bundle $entity_type_id")) {
+        $bundles_with_any_permission[] = $bundle;
+      }
+      if ($account->hasPermission("$operation own $bundle $entity_type_id")) {
+        $bundles_with_own_permission[] = $bundle;
+      }
+    }
+    // Any $bundle permission.
+    if ($bundles_with_any_permission) {
+      $conditions->addCondition($bundle_key, $bundles_with_any_permission);
+    }
+    // Own $bundle permission.
+    if ($bundles_with_own_permission) {
+      $conditions->addCacheContexts(['user']);
+      $conditions->addCondition((new ConditionGroup('AND'))
+        ->addCondition($uid_key, $account->id())
+        ->addCondition($bundle_key, $bundles_with_own_permission)
+      );
+    }
+
+    return $conditions->count() ? $conditions : NULL;
+  }
+
+  /**
+   * Builds the conditions for entities that do not have an owner.
+   *
+   * @param string $operation
+   *   The access operation. Usually one of "view", "update" or "delete".
+   * @param \Drupal\Core\Session\AccountInterface $account
+   *   The user for which to restrict access.
+   *
+   * @return \Drupal\entity\QueryAccess\ConditionGroup|null
+   *   The conditions, or NULL if the user doesn't have access to any entity.
+   */
+  protected function buildEntityConditions($operation, AccountInterface $account) {
+    $entity_type_id = $this->entityType->id();
+    $bundle_key = $this->entityType->getKey('bundle');
+
+    $conditions = new ConditionGroup('OR');
+    $conditions->addCacheContexts(['user.permissions']);
+    // The $entity_type permission.
+    if ($account->hasPermission("$operation $entity_type_id")) {
+      // The user has full access, no conditions needed.
+      return $conditions;
+    }
+
+    $bundles = array_keys($this->bundleInfo->getBundleInfo($entity_type_id));
+    $bundles_with_any_permission = [];
+    foreach ($bundles as $bundle) {
+      if ($account->hasPermission("$operation $bundle $entity_type_id")) {
+        $bundles_with_any_permission[] = $bundle;
+      }
+    }
+    // The $bundle permission.
+    if ($bundles_with_any_permission) {
+      $conditions->addCondition($bundle_key, $bundles_with_any_permission);
+    }
+
+    return $conditions->count() ? $conditions : NULL;
+  }
+
+}