You are here

function _file_entity_query_file_entity_access_alter in File Entity (fieldable files) 7.2

Same name and namespace in other branches
  1. 7.3 file_entity.module \_file_entity_query_file_entity_access_alter()

Helper for file entity access functions.

Parameters

$query: The query to add conditions to.

$type: Either 'file' or 'entity' depending on what sort of query it is. See file_entity_query_file_entity_access_alter() and file_entity_query_entity_field_access_alter() for more.

Related topics

1 call to _file_entity_query_file_entity_access_alter()
file_entity_query_file_access_alter in ./file_entity.module
Implements hook_query_TAG_alter().

File

./file_entity.module, line 1915
Extends Drupal file entities to be fieldable and viewable.

Code

function _file_entity_query_file_entity_access_alter($query, $type) {
  global $user;

  // Read meta-data from query, if provided.
  if (!($account = $query
    ->getMetaData('account'))) {
    $account = $user;
  }

  // If $account can bypass file access, we don't need to alter the query.
  if (user_access('bypass file access', $account)) {
    return;
  }

  // A conflict with og_query_og_membership_alter() causes a fatal error
  // if both hooks alter the query.
  if (module_exists('og') && $query
    ->hasTag('og_membership')) {
    foreach ($query
      ->getMetaData('entity_field_query')->fields as $field) {
      if (og_is_group_audience_field($field['field_name'])) {
        return;
      }
    }
  }
  $tables = $query
    ->getTables();
  $base_table = $query
    ->getMetaData('base_table');

  // If no base table is specified explicitly, search for one.
  if (!$base_table) {
    $fallback = '';
    foreach ($tables as $alias => $table_info) {
      if (!($table_info instanceof SelectQueryInterface || $table_info['table'] instanceof SelectQueryInterface)) {
        $table = $table_info['table'];

        // If the file_managed table is in the query, it wins immediately.
        if ($table == 'file_managed') {
          $base_table = $table;
          break;
        }

        // Check whether the table has a foreign key to file_managed.fid. If it
        // does, do not run this check again as we found a base table and only
        // file_managed can triumph that.
        if (!$base_table) {

          // The schema is cached.
          $schema = drupal_get_schema($table);
          if (isset($schema['fields']['fid'])) {
            if (isset($schema['foreign keys'])) {
              foreach ($schema['foreign keys'] as $relation) {
                if ($relation['table'] === 'file_managed' && $relation['columns'] === array(
                  'fid' => 'fid',
                )) {
                  $base_table = $table;
                }
              }
            }
            else {

              // At least it's a fid. A table with a field called fid is very
              // very likely to be a file_managed.fid in a file access query.
              $fallback = $table;
            }
          }
        }
      }
    }

    // If there is nothing else, use the fallback.
    if (!$base_table) {
      if ($fallback) {
        watchdog('security', 'Your file listing query is using @fallback as a base table in a query tagged for file access. This might not be secure and might not even work. Specify foreign keys in your schema to file_managed.fid ', array(
          '@fallback' => $fallback,
        ), WATCHDOG_WARNING);
        $base_table = $fallback;
      }
      else {
        throw new Exception(t('Query tagged for file access but there is no fid. Add foreign keys to file_managed.fid in schema to fix.'));
      }
    }
  }
  if ($type == 'entity') {

    // The original query looked something like:
    // @code
    //  SELECT fid FROM sometable s
    //  WHERE ($file_access_conditions)
    // @endcode
    //
    // Our query will look like:
    // @code
    //  SELECT entity_type, entity_id
    //  FROM field_data_something s
    //  WHERE (entity_type = 'file' AND $file_access_conditions) OR (entity_type <> 'file')
    // @endcode
    //
    // So instead of directly adding to the query object, we need to collect
    // all of the file access conditions in a separate db_and() object and
    // then add it to the query at the end.
    $file_conditions = db_and();
  }
  foreach ($tables as $falias => $tableinfo) {
    $table = $tableinfo['table'];
    if (!$table instanceof SelectQueryInterface && $table == $base_table) {
      $subquery = db_select('file_managed', 'fm_access')
        ->fields('fm_access', array(
        'fid',
      ));
      $subquery_conditions = db_or();
      $wrappers = file_entity_get_public_and_private_stream_wrapper_names();
      if (!empty($wrappers['public'])) {
        if (user_access('view files', $account)) {
          foreach (array_keys($wrappers['public']) as $wrapper) {
            $subquery_conditions
              ->condition('fm_access.uri', $wrapper . '%', 'LIKE');
          }
        }
        elseif (user_access('view own files', $account)) {
          foreach (array_keys($wrappers['public']) as $wrapper) {
            $subquery_conditions
              ->condition(db_and()
              ->condition('fm_access.uri', $wrapper . '%', 'LIKE')
              ->condition('fm_access.uid', $account->uid));
          }
        }
      }
      if (!empty($wrappers['private'])) {
        if (user_access('view private files', $account)) {
          foreach (array_keys($wrappers['private']) as $wrapper) {
            $subquery_conditions
              ->condition('fm_access.uri', $wrapper . '%', 'LIKE');
          }
        }
        elseif (user_access('view own private files', $account)) {
          foreach (array_keys($wrappers['private']) as $wrapper) {
            $subquery_conditions
              ->condition(db_and()
              ->condition('fm_access.uri', $wrapper . '%', 'LIKE')
              ->condition('fm_access.uid', $account->uid));
          }
        }
      }
      if ($subquery_conditions
        ->count()) {
        $subquery
          ->condition($subquery_conditions);
        $field = 'fid';

        // Now handle entities.
        if ($type == 'entity') {

          // Set a common alias for entities.
          $base_alias = $falias;
          $field = 'entity_id';
        }
        $subquery
          ->where("{$falias}.{$field} = fm_access.fid");

        // For an entity query, attach the subquery to entity conditions.
        if ($type == 'entity') {
          $file_conditions
            ->exists($subquery);
        }
        elseif ($table == 'file_managed') {

          // Fix for https://drupal.org/node/2073085
          $db_or = db_or();
          $db_or
            ->exists($subquery);
          $db_or
            ->isNull($falias . '.' . $field);
          $query
            ->condition($db_or);
        }
        else {
          $query
            ->exists($subquery);
        }
      }
    }
  }
  if ($type == 'entity' && $file_conditions
    ->count()) {

    // All the file access conditions are only for field values belonging to
    // files.
    $file_conditions
      ->condition("{$base_alias}.entity_type", 'file');
    $or = db_or();
    $or
      ->condition($file_conditions);

    // If the field value belongs to a non-file entity type then this function
    // does not do anything with it.
    $or
      ->condition("{$base_alias}.entity_type", 'file', '<>');

    // Add the compiled set of rules to the query.
    $query
      ->condition($or);
  }
}