Added Entity and Entity Reference Revisions which got dropped somewhere along the...
[yaffs-website] / web / modules / contrib / entity / src / QueryAccess / ViewsQueryAlter.php
diff --git a/web/modules/contrib/entity/src/QueryAccess/ViewsQueryAlter.php b/web/modules/contrib/entity/src/QueryAccess/ViewsQueryAlter.php
new file mode 100644 (file)
index 0000000..3d660fd
--- /dev/null
@@ -0,0 +1,251 @@
+<?php
+
+namespace Drupal\entity\QueryAccess;
+
+use Drupal\Core\Cache\CacheableMetadata;
+use Drupal\Core\Database\Connection;
+use Drupal\Core\Database\Query\Condition as SqlCondition;
+use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
+use Drupal\Core\Entity\EntityFieldManagerInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Entity\Sql\DefaultTableMapping;
+use Drupal\Core\Entity\Sql\SqlContentEntityStorage;
+use Drupal\Core\Render\RendererInterface;
+use Drupal\views\Plugin\views\query\Sql;
+use Drupal\views\ViewExecutable;
+use Drupal\views\Views;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpFoundation\RequestStack;
+
+/**
+ * Defines a class for altering views queries.
+ *
+ * @internal
+ */
+class ViewsQueryAlter implements ContainerInjectionInterface {
+
+  /**
+   * The database connection.
+   *
+   * @var \Drupal\Core\Database\Connection
+   */
+  protected $connection;
+
+  /**
+   * The entity field manager.
+   *
+   * @var \Drupal\Core\Entity\EntityFieldManagerInterface
+   */
+  protected $entityFieldManager;
+
+  /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * The renderer.
+   *
+   * @var \Drupal\Core\Render\RendererInterface
+   */
+  protected $renderer;
+
+  /**
+   * The request stack.
+   *
+   * @var \Symfony\Component\HttpFoundation\RequestStack
+   */
+  protected $requestStack;
+
+  /**
+   * Constructs a new ViewsQueryAlter object.
+   *
+   * @param \Drupal\Core\Database\Connection $connection
+   *   The database connection.
+   * @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager
+   *   The entity field manager.
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity type manager.
+   * @param \Drupal\Core\Render\RendererInterface $renderer
+   *   The renderer.
+   * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
+   *   The request stack.
+   */
+  public function __construct(Connection $connection, EntityFieldManagerInterface $entity_field_manager, EntityTypeManagerInterface $entity_type_manager, RendererInterface $renderer, RequestStack $request_stack) {
+    $this->connection = $connection;
+    $this->entityFieldManager = $entity_field_manager;
+    $this->entityTypeManager = $entity_type_manager;
+    $this->renderer = $renderer;
+    $this->requestStack = $request_stack;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('database'),
+      $container->get('entity_field.manager'),
+      $container->get('entity_type.manager'),
+      $container->get('renderer'),
+      $container->get('request_stack')
+    );
+  }
+
+  /**
+   * Alters the given views query.
+   *
+   * @param \Drupal\views\Plugin\views\query\Sql $query
+   *   The views query.
+   * @param \Drupal\views\ViewExecutable $view
+   *   The view.
+   */
+  public function alter(Sql $query, ViewExecutable $view) {
+    $table_info = $query->getEntityTableInfo();
+    $base_table = reset($table_info);
+    if (empty($base_table['entity_type']) || $base_table['relationship_id'] != 'none') {
+      return;
+    }
+    $entity_type_id = $base_table['entity_type'];
+    $entity_type = $this->entityTypeManager->getDefinition($entity_type_id);
+    if (!$entity_type->hasHandlerClass('query_access')) {
+      return;
+    }
+    $storage = $this->entityTypeManager->getStorage($entity_type_id);
+    if (!$storage instanceof SqlContentEntityStorage) {
+      return;
+    }
+
+    /** @var \Drupal\entity\QueryAccess\QueryAccessHandlerInterface $query_access */
+    $query_access = $this->entityTypeManager->getHandler($entity_type_id, 'query_access');
+    $conditions = $query_access->getConditions('view');
+    if ($conditions->isAlwaysFalse()) {
+      $query->addWhereExpression(0, '1 = 0');
+    }
+    elseif (count($conditions)) {
+      // Store the data table, in case mapConditions() needs to join it in.
+      $base_table['data_table'] = $entity_type->getDataTable();
+      $field_storage_definitions = $this->entityFieldManager->getFieldStorageDefinitions($entity_type_id);
+      /** @var \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */
+      $table_mapping = $storage->getTableMapping();
+      $sql_conditions = $this->mapConditions($conditions, $query, $base_table, $field_storage_definitions, $table_mapping);
+      $query->addWhere(0, $sql_conditions);
+    }
+
+    $this->applyCacheability(CacheableMetadata::createFromObject($conditions));
+  }
+
+  /**
+   * Maps an entity type's access conditions to views SQL conditions.
+   *
+   * @param \Drupal\entity\QueryAccess\ConditionGroup $conditions
+   *   The access conditions.
+   * @param \Drupal\views\Plugin\views\query\Sql $query
+   *   The views query.
+   * @param array $base_table
+   *   The base table information.
+   * @param \Drupal\Core\Field\FieldStorageDefinitionInterface[] $field_storage_definitions
+   *   The field storage definitions.
+   * @param \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping
+   *   The table mapping.
+   *
+   * @return \Drupal\Core\Database\Query\ConditionInterface
+   *   The SQL conditions.
+   */
+  protected function mapConditions(ConditionGroup $conditions, Sql $query, array $base_table, array $field_storage_definitions, DefaultTableMapping $table_mapping) {
+    $sql_condition = new SqlCondition($conditions->getConjunction());
+    foreach ($conditions->getConditions() as $condition) {
+      if ($condition instanceof ConditionGroup) {
+        $nested_sql_conditions = $this->mapConditions($condition, $query, $base_table, $field_storage_definitions, $table_mapping);
+        $sql_condition->condition($nested_sql_conditions);
+      }
+      else {
+        $field = $condition->getField();
+        $property_name = NULL;
+        if (strpos($field, '.') !== FALSE) {
+          list($field, $property_name) = explode('.', $field);
+        }
+        // Skip unknown fields.
+        if (!isset($field_storage_definitions[$field])) {
+          continue;
+        }
+        $field_storage_definition = $field_storage_definitions[$field];
+        if (!$property_name) {
+          $property_name = $field_storage_definition->getMainPropertyName();
+        }
+
+        $column = $table_mapping->getFieldColumnName($field_storage_definition, $property_name);
+        if ($table_mapping->requiresDedicatedTableStorage($field_storage_definitions[$field])) {
+          if ($base_table['revision']) {
+            $dedicated_table = $table_mapping->getDedicatedRevisionTableName($field_storage_definition);
+          }
+          else {
+            $dedicated_table = $table_mapping->getDedicatedDataTableName($field_storage_definition);
+          }
+          // Views defaults to LEFT JOIN. For simplicity, we don't try to
+          // use an INNER JOIN when it's safe to do so (AND conjunctions).
+          $alias = $query->ensureTable($dedicated_table);
+        }
+        elseif ($base_table['revision'] && !$field_storage_definition->isRevisionable()) {
+          // Workaround for #2652652, which causes $query->ensureTable()
+          // to not work in this case, due to a missing relationship.
+          if ($data_table = $query->getTableInfo($base_table['data_table'])) {
+            $alias = $data_table['alias'];
+          }
+          else {
+            $configuration = [
+              'type' => 'INNER',
+              'table' => $base_table['data_table'],
+              'field' => 'id',
+              'left_table' => $base_table['alias'],
+              'left_field' => 'id',
+            ];
+            /** @var \Drupal\Views\Plugin\views\join\JoinPluginBase $join */
+            $join = Views::pluginManager('join')->createInstance('standard', $configuration);
+            $alias = $query->addRelationship($base_table['data_table'], $join, $data_table);
+          }
+        }
+        else {
+          $alias = $base_table['alias'];
+        }
+
+        $value = $condition->getValue();
+        $operator = $condition->getOperator();
+        // Using LIKE/NOT LIKE ensures a case insensitive comparison.
+        // @see \Drupal\Core\Entity\Query\Sql\Condition::translateCondition().
+        $property_definitions = $field_storage_definition->getPropertyDefinitions();
+        $case_sensitive = $property_definitions[$property_name]->getSetting('case_sensitive');
+        $operator_map = [
+          '=' => 'LIKE',
+          '<>' => 'NOT LIKE',
+        ];
+        if (!$case_sensitive && isset($operator_map[$operator])) {
+          $operator = $operator_map[$operator];
+          $value = $this->connection->escapeLike($value);
+        }
+
+        $sql_condition->condition("$alias.$column", $value, $operator);
+      }
+    }
+
+    return $sql_condition;
+  }
+
+  /**
+   * Applies the cacheablity metadata to the current request.
+   *
+   * @param \Drupal\Core\Cache\CacheableMetadata $cacheable_metadata
+   *   The cacheability metadata.
+   */
+  protected function applyCacheability(CacheableMetadata $cacheable_metadata) {
+    $request = $this->requestStack->getCurrentRequest();
+    if ($request->isMethodCacheable() && $this->renderer->hasRenderContext()) {
+      $build = [];
+      $cacheable_metadata->applyTo($build);
+      $this->renderer->render($build);
+    }
+  }
+
+}