group-8.x-1.x-dev/src/QueryAccess/EntityQueryAlter.php

src/QueryAccess/EntityQueryAlter.php
<?php

namespace Drupal\group\QueryAccess;

use Drupal\Core\Entity\EntityPublishedInterface;
use Drupal\Core\Entity\Sql\SqlContentEntityStorage;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Defines a class for altering entity queries.
 *
 * @todo Revisit cacheability and see if we can optimize some more.
 *
 * @internal
 */
class EntityQueryAlter extends PluginBasedQueryAlterBase {

  /**
   * The group relation type manager.
   *
   * @var \Drupal\group\Plugin\Group\Relation\GroupRelationTypeManagerInterface
   */
  protected $pluginManager;

  /**
   * The database connection.
   *
   * @var \Drupal\Core\Database\Connection
   */
  protected $database;

  /**
   * The alias for the join to the plugins.
   *
   * @var string
   */
  protected $joinAliasPlugins;

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    $instance = parent::create($container);
    $instance->pluginManager = $container->get('group_relation_type.manager');
    $instance->database = $container->get('database');
    return $instance;
  }

  /**
   * {@inheritdoc}
   */
  protected function doAlter($operation) {
    $entity_type_id = $this->entityType->id();
    $storage = $this->entityTypeManager->getStorage($entity_type_id);
    if (!$storage instanceof SqlContentEntityStorage) {
      return;
    }

    // Find all of the group relations that define access.
    $plugin_ids = $this->pluginManager->getPluginIdsByEntityTypeAccess($entity_type_id);
    if (empty($plugin_ids)) {
      return;
    }

    // If any new relationship entity is added using any of the retrieved
    // plugins, it might change access.
    $cache_tags = [];
    foreach ($plugin_ids as $plugin_id) {
      $cache_tags[] = "group_content_list:plugin:$plugin_id";
    }
    $this->cacheableMetadata->addCacheTags($cache_tags);

    // If there are no relationships using the plugins, there's no point in
    // going any further. The cache tags above will invalidate our result if new
    // group content is created using the plugins that define access. Retrieve
    // the plugin IDs in use to optimize a loop further below.
    $group_relationship_data_table = $this->entityTypeManager->getDefinition('group_content')->getDataTable();
    $plugin_ids_in_use = $this->database
      ->select($group_relationship_data_table, 'gc')
      ->fields('gc', ['plugin_id'])
      ->condition('plugin_id', $plugin_ids, 'IN')
      ->distinct()
      ->execute()
      ->fetchCol();

    if (empty($plugin_ids_in_use)) {
      return;
    }

    // Check if any of the plugins actually support the operation. If not, we
    // can simply bail out here to play nice with other modules that do support
    // the provided operation.
    $operation_is_supported = FALSE;
    foreach ($plugin_ids_in_use as $plugin_id) {
      if ($this->pluginManager->getAccessControlHandler($plugin_id)->supportsOperation($operation, 'entity')) {
        $operation_is_supported = TRUE;
        break;
      }
    }

    if (!$operation_is_supported) {
      return;
    }

    // From this point onward, we know that there are grouped entities and that
    // we need to check access, so we can LEFT JOIN the necessary table.
    $id_key = $this->entityType->getKey('id');

    // Join the relationship table, but only for used plugins.
    $base_table = $this->ensureBaseTable();
    $this->joinAliasPlugins = $this->query->leftJoin(
      $group_relationship_data_table,
      'gcfd',
      "$base_table.$id_key=%alias.entity_id AND %alias.plugin_id IN (:plugin_ids_in_use[])",
      [':plugin_ids_in_use[]' => $plugin_ids_in_use]
    );

    // Retrieve the full list of group permissions for the user.
    $this->cacheableMetadata->addCacheContexts(['user.group_permissions']);
    $calculated_permissions = $this->permissionCalculator->calculateFullPermissions($this->currentUser);

    // We only check unpublished vs published for "view" right now. If we ever
    // start supporting other operations, we need to remove the "view" check.
    $check_published = $operation === 'view' && $this->entityType->entityClassImplements(EntityPublishedInterface::class);

    $owner_key = $this->entityType->getKey('owner');
    $published_key = $this->entityType->getKey('published');

    $allowed_any_ids = $allowed_own_ids = $allowed_any_by_status_ids = $allowed_own_by_status_ids = [];
    foreach ($plugin_ids_in_use as $plugin_id) {
      $handler = $this->pluginManager->getPermissionProvider($plugin_id);
      $admin_permission = $handler->getAdminPermission();
      $any_permission = $handler->getPermission($operation, 'entity', 'any');
      $own_permission = $handler->getPermission($operation, 'entity', 'own');
      if ($check_published) {
        $any_unpublished_permission = $handler->getPermission("$operation unpublished", 'entity', 'any');
        $own_unpublished_permission = $handler->getPermission("$operation unpublished", 'entity', 'own');
      }

      foreach ($calculated_permissions->getItems() as $item) {
        if ($admin_permission !== FALSE && $item->hasPermission($admin_permission)) {
          $allowed_any_ids[$item->getScope()][$plugin_id][] = $item->getIdentifier();
        }
        elseif (!$check_published) {
          if ($any_permission !== FALSE && $item->hasPermission($any_permission)) {
            $allowed_any_ids[$item->getScope()][$plugin_id][] = $item->getIdentifier();
          }
          elseif ($own_permission !== FALSE && $item->hasPermission($own_permission)) {
            $allowed_own_ids[$item->getScope()][$plugin_id][] = $item->getIdentifier();
          }
        }
        else {
          if ($any_permission !== FALSE && $item->hasPermission($any_permission)) {
            $allowed_any_by_status_ids[1][$item->getScope()][$plugin_id][] = $item->getIdentifier();
          }
          elseif ($own_permission !== FALSE && $item->hasPermission($own_permission)) {
            $allowed_own_by_status_ids[1][$item->getScope()][$plugin_id][] = $item->getIdentifier();
          }
          if ($any_unpublished_permission !== FALSE && $item->hasPermission($any_unpublished_permission)) {
            $allowed_any_by_status_ids[0][$item->getScope()][$plugin_id][] = $item->getIdentifier();
          }
          elseif ($own_unpublished_permission !== FALSE && $item->hasPermission($own_unpublished_permission)) {
            $allowed_own_by_status_ids[0][$item->getScope()][$plugin_id][] = $item->getIdentifier();
          }
        }
      }
    }

    $has_regular_ids = !empty($allowed_any_ids);
    $has_own_ids = !empty($allowed_own_ids);
    $has_status_ids = FALSE;

    $statuses_to_check = [];
    foreach ([0, 1] as $status) {
      if (!empty($allowed_any_by_status_ids[$status]) || !empty($allowed_own_by_status_ids[$status])) {
        $has_status_ids = TRUE;
        $statuses_to_check[] = $status;
      }
    }

    // If no group type or group gave access, we deny access altogether.
    if (!$has_regular_ids && !$has_own_ids && !$has_status_ids) {
      $this->query->isNull("$this->joinAliasPlugins.entity_id");
      return;
    }

    // From this point on, we know there is something that will allow access, so
    // we need to alter the query to check that entity_id is null or the group
    // access checks apply.
    $this->query->condition(
      $group_conditions = $this->query->orConditionGroup()
        ->isNull("$this->joinAliasPlugins.entity_id")
    );

    // If we're going to need a data table anyhow, might as well initialize it
    // early so all checks are added to the same table.
    if ($has_own_ids || $has_status_ids) {
      $data_table = $this->ensureDataTable();
    }

    if ($has_regular_ids) {
      $this->addScopedConditions($allowed_any_ids, $group_conditions);
    }

    if ($has_own_ids) {
      $this->cacheableMetadata->addCacheContexts(['user']);
      $group_conditions->condition($owner_conditions = $this->query->andConditionGroup());
      $owner_conditions->condition("$data_table.$owner_key", $this->currentUser->id());
      $this->addScopedConditions($allowed_own_ids, $owner_conditions);
    }

    if ($has_status_ids) {
      foreach ($statuses_to_check as $status) {
        $group_conditions->condition($status_conditions = $this->query->andConditionGroup());
        $status_conditions->condition("$data_table.$published_key", $status);
        $status_conditions->condition($status_sub_conditions = $this->query->orConditionGroup());

        if (!empty($allowed_any_by_status_ids[$status])) {
          $this->addScopedConditions($allowed_any_by_status_ids[$status], $status_sub_conditions);
        }

        if (!empty($allowed_own_by_status_ids[$status])) {
          $this->cacheableMetadata->addCacheContexts(['user']);
          $status_sub_conditions->condition($status_owner_conditions = $this->query->andConditionGroup());
          $status_owner_conditions->condition("$data_table.$owner_key", $this->currentUser->id());
          $this->addScopedConditions($allowed_own_by_status_ids[$status], $status_owner_conditions);
        }
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  protected function getPluginDataTable() {
    return $this->joinAliasPlugins;
  }

  /**
   * {@inheritdoc}
   */
  protected function getMembershipJoinTable() {
    return $this->joinAliasPlugins;
  }

  /**
   * {@inheritdoc}
   */
  protected function getMembershipJoinLeftField() {
    return 'gid';
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc