You are here

protected function ContentAccess::addNodeAccess in Search API 8

Adds a node access filter to a search query, if applicable.

Parameters

\Drupal\search_api\Query\QueryInterface $query: The query to which a node access filter should be added, if applicable.

\Drupal\Core\Session\AccountInterface $account: The user for whom the search is executed.

1 call to ContentAccess::addNodeAccess()
ContentAccess::preprocessSearchQuery in src/Plugin/search_api/processor/ContentAccess.php
Preprocesses a search query.

File

src/Plugin/search_api/processor/ContentAccess.php, line 266

Class

ContentAccess
Adds content access checks for nodes and comments.

Namespace

Drupal\search_api\Plugin\search_api\processor

Code

protected function addNodeAccess(QueryInterface $query, AccountInterface $account) {

  // Don't do anything if the user can access all content.
  if ($account
    ->hasPermission('bypass node access')) {
    return;
  }

  // Gather the affected datasources, grouped by entity type, as well as the
  // unaffected ones.
  $affected_datasources = [];
  $unaffected_datasources = [];
  foreach ($this->index
    ->getDatasources() as $datasource_id => $datasource) {
    $entity_type = $datasource
      ->getEntityTypeId();
    if (in_array($entity_type, [
      'node',
      'comment',
    ])) {
      $affected_datasources[$entity_type][] = $datasource_id;
    }
    else {
      $unaffected_datasources[] = $datasource_id;
    }
  }

  // The filter structure we want looks like this:
  //   [belongs to other datasource]
  //   OR
  //   (
  //     [is enabled (or was created by the user, if applicable)]
  //     AND
  //     [grants view access to one of the user's gid/realm combinations]
  //   )
  // If there are no "other" datasources, we don't need the nested OR,
  // however, and can add the inner conditions directly to the query.
  if ($unaffected_datasources) {
    $outer_conditions = $query
      ->createConditionGroup('OR', [
      'content_access',
    ]);
    $query
      ->addConditionGroup($outer_conditions);
    foreach ($unaffected_datasources as $datasource_id) {
      $outer_conditions
        ->addCondition('search_api_datasource', $datasource_id);
    }
    $access_conditions = $query
      ->createConditionGroup('AND');
    $outer_conditions
      ->addConditionGroup($access_conditions);
  }
  else {
    $access_conditions = $query;
  }
  if (!$account
    ->hasPermission('access content')) {
    unset($affected_datasources['node']);
  }
  if (!$account
    ->hasPermission('access comments')) {
    unset($affected_datasources['comment']);
  }

  // If the user does not have the permission to see any content at all, deny
  // access to all items from affected datasources.
  if (!$affected_datasources) {

    // If there were "other" datasources, the existing filter will already
    // remove all results of node or comment datasources. Otherwise, we should
    // not return any results at all.
    if (!$unaffected_datasources) {
      $query
        ->abort($this
        ->t('You have no access to any results in this search.'));
    }
    return;
  }

  // Collect all the required fields that need to be part of the index.
  $unpublished_own = $account
    ->hasPermission('view own unpublished content');
  $enabled_conditions = $query
    ->createConditionGroup('OR', [
    'content_access_enabled',
  ]);
  foreach ($affected_datasources as $entity_type => $datasources) {
    foreach ($datasources as $datasource_id) {

      // If this is a comment datasource, or users cannot view their own
      // unpublished nodes, a simple filter on "status" is enough. Otherwise,
      // it's a bit more complicated.
      $status_field = $this
        ->findField($datasource_id, 'status', 'boolean');
      if ($status_field) {
        $enabled_conditions
          ->addCondition($status_field
          ->getFieldIdentifier(), TRUE);
      }
      if ($entity_type == 'node' && $unpublished_own) {
        $author_field = $this
          ->findField($datasource_id, 'uid', 'integer');
        if ($author_field) {
          $enabled_conditions
            ->addCondition($author_field
            ->getFieldIdentifier(), $account
            ->id());
        }
      }
    }
  }
  $access_conditions
    ->addConditionGroup($enabled_conditions);

  // Filter by the user's node access grants.
  $node_grants_field = $this
    ->findField(NULL, 'search_api_node_grants', 'string');
  if (!$node_grants_field) {
    return;
  }
  $node_grants_field_id = $node_grants_field
    ->getFieldIdentifier();
  $grants_conditions = $query
    ->createConditionGroup('OR', [
    'content_access_grants',
  ]);
  $grants = node_access_grants('view', $account);
  foreach ($grants as $realm => $gids) {
    foreach ($gids as $gid) {
      $grants_conditions
        ->addCondition($node_grants_field_id, "node_access_{$realm}:{$gid}");
    }
  }

  // Also add items that are accessible for everyone by checking the "access
  // all" pseudo grant.
  $grants_conditions
    ->addCondition($node_grants_field_id, 'node_access__all');
  $access_conditions
    ->addConditionGroup($grants_conditions);
}