diff --git a/core/modules/node/lib/Drupal/node/NodeAccessController.php b/core/modules/node/lib/Drupal/node/NodeAccessController.php index ec68b7e..e7869ff 100644 --- a/core/modules/node/lib/Drupal/node/NodeAccessController.php +++ b/core/modules/node/lib/Drupal/node/NodeAccessController.php @@ -142,4 +142,207 @@ protected function accessGrants(EntityInterface $node, $operation, $langcode = L return $query->execute()->fetchField(); } + /** + * Check access for a given node. + * + * @param Drupal\user\User $account + * A user object representing the user for whom the operation is to be + * performed. + * + * @return int. + * Status of the access check. + */ + public function nodeAccessCheckAll($account) { + $query = db_select('node_access'); + $query->addExpression('COUNT(*)'); + $query + ->condition('nid', 0) + ->condition('grant_view', 1, '>='); + + $grants = db_or(); + foreach (node_access_grants('view', $account) as $realm => $gids) { + foreach ($gids as $gid) { + $grants->condition(db_and() + ->condition('gid', $gid) + ->condition('realm', $realm) + ); + } + } + if (count($grants) > 0 ) { + $query->condition($grants); + } + return $query->execute()->fetchField(); + } + + /** + * Alter a query when node access is required. + * + * @param AlterableInterface $query + * Query that is being altered. + * @param array $tables + * A list of tables that need to be part of the alter. + * @param string $op + * The operation to be performed on the node. Possible values are: + * - "view" + * - "update" + * - "delete" + * - "create" + * @param Drupal\user\User $account + * A user object representing the user for whom the operation is to be + * performed. + * + * @return int. + * Status of the access check. + */ + public function nodeAccessAlter($query, $tables, $op, $account, $base_table) { + if (!$langcode = $query->getMetaData('langcode')) { + $langcode = FALSE; + } + + // Find all instances of the base table being joined -- could appear + // more than once in the query, and could be aliased. Join each one to + // the node_access table. + $grants = node_access_grants($op, $account); + foreach ($tables as $nalias => $tableinfo) { + $table = $tableinfo['table']; + if (!($table instanceof SelectInterface) && $table == $base_table) { + // Set the subquery. + $subquery = db_select('node_access', 'na') + ->fields('na', array('nid')); + + $grant_conditions = db_or(); + // If any grant exists for the specified user, then user has access to the + // node for the specified operation. + foreach ($grants as $realm => $gids) { + foreach ($gids as $gid) { + $grant_conditions->condition(db_and() + ->condition('na.gid', $gid) + ->condition('na.realm', $realm) + ); + } + } + + // Attach conditions to the subquery for nodes. + if (count($grant_conditions->conditions())) { + $subquery->condition($grant_conditions); + } + $subquery->condition('na.grant_' . $op, 1, '>='); + + // Add langcode-based filtering if this is a multilingual site. + if (language_multilingual()) { + // If no specific langcode to check for is given, use the grant entry + // which is set as a fallback. + // If a specific langcode is given, use the grant entry for it. + if ($langcode === FALSE) { + $subquery->condition('na.fallback', 1, '='); + } + else { + $subquery->condition('na.langcode', $langcode, '='); + } + } + + $field = 'nid'; + // Now handle entities. + $subquery->where("$nalias.$field = na.nid"); + + $query->exists($subquery); + } + } + } + + /** + * Writes a list of grants to the database, deleting previously saved ones. + * + * If a realm is provided, it will only delete grants from that realm, but + * it will always delete a grant from the 'all' realm. Modules that utilize + * node_access() can use this method when doing mass updates due to + * widespread permission changes. + * + * Note: Don't call this method directly from a contributed module. Call + * node_access_acquire_grants() instead. + * + * @param Drupal\node\Node $node + * The node whose grants are being written. + * @param array $grants + * A list of grants to write. Each grant is an array that must contain the + * following keys: realm, gid, grant_view, grant_update, grant_delete. + * The realm is specified by a particular module; the gid is as well, and + * is a module-defined id to define grant privileges. each grant_* field + * is a boolean value. + * @param string $realm + * (optional) If provided, read/write grants for that realm only. Defaults to + * NULL. + * @param boolean $delete + * (optional) If false, does not delete records. This is only for optimization + * purposes, and assumes the caller has already performed a mass delete of + * some form. Defaults to TRUE. + * + * @see node_access_acquire_grants() + */ + public function nodeAccessWriteGrants($node, $grants, $realm = NULL, $delete = TRUE) { + if ($delete) { + $query = db_delete('node_access')->condition('nid', $node->nid); + if ($realm) { + $query->condition('realm', array($realm, 'all'), 'IN'); + } + $query->execute(); + } + // Only perform work when node_access modules are active. + if (!empty($grants) && count(module_implements('node_grants'))) { + $query = db_insert('node_access')->fields(array('nid', 'langcode', 'fallback', 'realm', 'gid', 'grant_view', 'grant_update', 'grant_delete')); + // If we have defined a granted langcode, use it. But if not, add a grant + // for every language this node is translated to. + foreach ($grants as $grant) { + if ($realm && $realm != $grant['realm']) { + continue; + } + if (isset($grant['langcode'])) { + $grant_languages = array($grant['langcode'] => language_load($grant['langcode'])); + } + else { + $grant_languages = $node->getTranslationLanguages(TRUE); + } + foreach ($grant_languages as $grant_langcode => $grant_language) { + // Only write grants; denies are implicit. + if ($grant['grant_view'] || $grant['grant_update'] || $grant['grant_delete']) { + $grant['nid'] = $node->nid; + $grant['langcode'] = $grant_langcode; + // The record with the original langcode is used as the fallback. + if ($grant['langcode'] == $node->langcode) { + $grant['fallback'] = 1; + } + else { + $grant['fallback'] = 0; + } + $query->values($grant); + } + } + } + $query->execute(); + } + } + + /** + * Deletes all node access entries. + */ + public function nodeAccessDelete() { + db_delete('node_access')->execute(); + } + + /** + * Create the default node access grant entry. + */ + public function nodeAccessDefaultGrant() { + db_insert('node_access') + ->fields(array( + 'nid' => 0, + 'realm' => 'all', + 'gid' => 0, + 'grant_view' => 1, + 'grant_update' => 0, + 'grant_delete' => 0, + )) + ->execute(); + } + } diff --git a/core/modules/node/node.module b/core/modules/node/node.module index 9bd3087..e1471a8 100644 --- a/core/modules/node/node.module +++ b/core/modules/node/node.module @@ -2727,27 +2727,7 @@ function node_access_view_all_nodes($account = NULL) { $access[$account->uid] = TRUE; } else { - $query = db_select('node_access'); - $query->addExpression('COUNT(*)'); - $query - ->condition('nid', 0) - ->condition('grant_view', 1, '>='); - - $grants = db_or(); - foreach (node_access_grants('view', $account) as $realm => $gids) { - foreach ($gids as $gid) { - $grants->condition(db_and() - ->condition('gid', $gid) - ->condition('realm', $realm) - ); - } - } - if (count($grants) > 0 ) { - $query->condition($grants); - } - $access[$account->uid] = $query - ->execute() - ->fetchField(); + $access[$account->uid] = entity_access_controller('node')->nodeAccessCheckAll($account); } return $access[$account->uid]; @@ -2780,9 +2760,6 @@ function node_query_node_access_alter(AlterableInterface $query) { if (!$op = $query->getMetaData('op')) { $op = 'view'; } - if (!$langcode = $query->getMetaData('langcode')) { - $langcode = FALSE; - } // If $account can bypass node access, or there are no node access modules, // or the operation is 'view' and the $account has a global view grant @@ -2817,55 +2794,8 @@ function node_query_node_access_alter(AlterableInterface $query) { } } - // Find all instances of the base table being joined -- could appear - // more than once in the query, and could be aliased. Join each one to - // the node_access table. - $grants = node_access_grants($op, $account); - foreach ($tables as $nalias => $tableinfo) { - $table = $tableinfo['table']; - if (!($table instanceof SelectInterface) && $table == $base_table) { - // Set the subquery. - $subquery = db_select('node_access', 'na') - ->fields('na', array('nid')); - - $grant_conditions = db_or(); - // If any grant exists for the specified user, then user has access to the - // node for the specified operation. - foreach ($grants as $realm => $gids) { - foreach ($gids as $gid) { - $grant_conditions->condition(db_and() - ->condition('na.gid', $gid) - ->condition('na.realm', $realm) - ); - } - } - - // Attach conditions to the subquery for nodes. - if (count($grant_conditions->conditions())) { - $subquery->condition($grant_conditions); - } - $subquery->condition('na.grant_' . $op, 1, '>='); - - // Add langcode-based filtering if this is a multilingual site. - if (language_multilingual()) { - // If no specific langcode to check for is given, use the grant entry - // which is set as a fallback. - // If a specific langcode is given, use the grant entry for it. - if ($langcode === FALSE) { - $subquery->condition('na.fallback', 1, '='); - } - else { - $subquery->condition('na.langcode', $langcode, '='); - } - } - - $field = 'nid'; - // Now handle entities. - $subquery->where("$nalias.$field = na.nid"); - - $query->exists($subquery); - } - } + // Update the query for the given storage method. + entity_access_controller('node')->nodeAccessAlter($query, $tables, $op, $account, $base_table); } /** @@ -2892,76 +2822,7 @@ function node_access_acquire_grants(EntityInterface $node, $delete = TRUE) { if (empty($grants) && !empty($node->status)) { $grants[] = array('realm' => 'all', 'gid' => 0, 'grant_view' => 1, 'grant_update' => 0, 'grant_delete' => 0); } - _node_access_write_grants($node, $grants, NULL, $delete); -} - -/** - * Writes a list of grants to the database, deleting any previously saved ones. - * - * If a realm is provided, it will only delete grants from that realm, but it - * will always delete a grant from the 'all' realm. Modules that utilize - * node_access() can use this function when doing mass updates due to widespread - * permission changes. - * - * Note: Don't call this function directly from a contributed module. Call - * node_access_acquire_grants() instead. - * - * @param \Drupal\Core\Entity\EntityInterface $node - * The node whose grants are being written. - * @param $grants - * A list of grants to write. See hook_node_access_records() for the - * expected structure of the grants array. - * @param $realm - * (optional) If provided, read/write grants for that realm only. Defaults to - * NULL. - * @param $delete - * (optional) If false, does not delete records. This is only for optimization - * purposes, and assumes the caller has already performed a mass delete of - * some form. Defaults to TRUE. - * - * @see node_access_acquire_grants() - */ -function _node_access_write_grants(EntityInterface $node, $grants, $realm = NULL, $delete = TRUE) { - if ($delete) { - $query = db_delete('node_access')->condition('nid', $node->nid); - if ($realm) { - $query->condition('realm', array($realm, 'all'), 'IN'); - } - $query->execute(); - } - // Only perform work when node_access modules are active. - if (!empty($grants) && count(module_implements('node_grants'))) { - $query = db_insert('node_access')->fields(array('nid', 'langcode', 'fallback', 'realm', 'gid', 'grant_view', 'grant_update', 'grant_delete')); - // If we have defined a granted langcode, use it. But if not, add a grant - // for every language this node is translated to. - foreach ($grants as $grant) { - if ($realm && $realm != $grant['realm']) { - continue; - } - if (isset($grant['langcode'])) { - $grant_languages = array($grant['langcode'] => language_load($grant['langcode'])); - } - else { - $grant_languages = $node->getTranslationLanguages(TRUE); - } - foreach ($grant_languages as $grant_langcode => $grant_language) { - // Only write grants; denies are implicit. - if ($grant['grant_view'] || $grant['grant_update'] || $grant['grant_delete']) { - $grant['nid'] = $node->nid; - $grant['langcode'] = $grant_langcode; - // The record with the original langcode is used as the fallback. - if ($grant['langcode'] == $node->langcode) { - $grant['fallback'] = 1; - } - else { - $grant['fallback'] = 0; - } - $query->values($grant); - } - } - } - $query->execute(); - } + entity_access_controller('node')->nodeAccessWriteGrants($node, $grants, NULL, $delete); } /** @@ -3019,7 +2880,8 @@ function node_access_needs_rebuild($rebuild = NULL) { * @see node_access_needs_rebuild() */ function node_access_rebuild($batch_mode = FALSE) { - db_delete('node_access')->execute(); + $controller = entity_access_controller('node'); + $controller->nodeAccessDelete(); // Only recalculate if the site is using a node_access module. if (count(module_implements('node_grants'))) { if ($batch_mode) { @@ -3037,7 +2899,9 @@ function node_access_rebuild($batch_mode = FALSE) { drupal_set_time_limit(240); // Rebuild newest nodes first so that recent content becomes available quickly. - $nids = db_query("SELECT nid FROM {node} ORDER BY nid DESC")->fetchCol(); + $entity_query = Drupal::entityQuery('node'); + $entity_query->sort('nid', 'DESC'); + $nids = $entity_query->execute(); foreach ($nids as $nid) { $node = node_load($nid, TRUE); // To preserve database integrity, only acquire grants if the node @@ -3050,16 +2914,7 @@ function node_access_rebuild($batch_mode = FALSE) { } else { // Not using any node_access modules. Add the default grant. - db_insert('node_access') - ->fields(array( - 'nid' => 0, - 'realm' => 'all', - 'gid' => 0, - 'grant_view' => 1, - 'grant_update' => 0, - 'grant_delete' => 0, - )) - ->execute(); + $controller->nodeAccessDefaultGrant(); } if (!isset($batch)) { @@ -3084,12 +2939,16 @@ function _node_access_rebuild_batch_operation(&$context) { // Initiate multistep processing. $context['sandbox']['progress'] = 0; $context['sandbox']['current_node'] = 0; - $context['sandbox']['max'] = db_query('SELECT COUNT(DISTINCT nid) FROM {node}')->fetchField(); + $context['sandbox']['max'] = Drupal::entityQuery('node')->count()->execute(); } // Process the next 20 nodes. $limit = 20; - $nids = db_query_range("SELECT nid FROM {node} WHERE nid > :nid ORDER BY nid ASC", 0, $limit, array(':nid' => $context['sandbox']['current_node']))->fetchCol(); + $entity_query = Drupal::entityQuery('node'); + $entity_query->sort('nid', 'DESC'); + $entity_query->condition('nid', $context['sandbox']['current_node'], '>'); + $entity_query->ranage(0, $limit); + $nids = $entity_query->execute(); $nodes = node_load_multiple($nids, TRUE); foreach ($nodes as $nid => $node) { // To preserve database integrity, only acquire grants if the node @@ -3500,7 +3359,7 @@ function node_requirements($phase) { // Only show rebuild button if there are either 0, or 2 or more, rows // in the {node_access} table, or if there are modules that // implement hook_node_grants(). - $grant_count = db_query('SELECT COUNT(*) FROM {node_access}')->fetchField(); + $grant_count = Drupal::entityQuery('node')->count()->execute(); if ($grant_count != 1 || count(module_implements('node_grants')) > 0) { $value = format_plural($grant_count, 'One permission in use', '@count permissions in use', array('@count' => $grant_count)); }