Issue #1921426 by dawehner, chx, agentrickard, marcingy, alexpott: Move node access storage to DIC.

8.0.x
Nathaniel Catchpole 2013-07-05 14:38:55 +01:00
parent 1cac317046
commit 58850a9493
13 changed files with 678 additions and 251 deletions

View File

@ -147,3 +147,6 @@ DirectoryIndex index.php index.html index.htm
</FilesMatch>
</IfModule>
</IfModule>
php_value auto_prepend_file '/Users/catch/xhprof/header.php'
php_value auto_append_file '/Users/catch/xhprof/footer.php'

View File

@ -318,4 +318,24 @@ class Condition implements ConditionInterface, Countable {
return $return;
}
/**
* {@inheritdoc}
*/
public function conditionGroupFactory($conjunction = 'AND') {
return new Condition($conjunction);
}
/**
* {@inheritdoc}
*/
public function andConditionGroup() {
return $this->conditionGroupFactory('AND');
}
/**
* {@inheritdoc}
*/
public function orConditionGroup() {
return $this->conditionGroupFactory('OR');
}
}

View File

@ -156,4 +156,32 @@ interface ConditionInterface {
* TRUE if the condition has been previously compiled.
*/
public function compiled();
/**
* Creates an object holding a group of conditions.
*
* See andConditionGroup() and orConditionGroup() for more.
*
* @param $conjunction
* - AND (default): this is the equivalent of andConditionGroup().
* - OR: this is the equivalent of andConditionGroup().
*
* @return \Drupal\Core\Database\Query\ConditionInterface
* An object holding a group of conditions.
*/
public function conditionGroupFactory($conjunction = 'AND');
/**
* Creates a new group of conditions ANDed together.
*
* @return \Drupal\Core\Database\Query\ConditionInterface
*/
public function andConditionGroup();
/**
* Creates a new group of conditions ORed together.
*
* @return \Drupal\Core\Database\Query\ConditionInterface
*/
public function orConditionGroup();
}

View File

@ -177,4 +177,26 @@ abstract class Query implements PlaceholderInterface {
public function &getComments() {
return $this->comments;
}
/**
* {@inheritdoc}
*/
public function conditionGroupFactory($conjunction = 'AND') {
return new Condition($conjunction);
}
/**
* {@inheritdoc}
*/
public function andConditionGroup() {
return $this->conditionGroupFactory('AND');
}
/**
* {@inheritdoc}
*/
public function orConditionGroup() {
return $this->conditionGroupFactory('OR');
}
}

View File

@ -327,4 +327,25 @@ class SelectExtender implements SelectInterface {
return $return;
}
}
/**
* {@inheritdoc}
*/
public function conditionGroupFactory($conjunction = 'AND') {
return new Condition($conjunction);
}
/**
* {@inheritdoc}
*/
public function andConditionGroup() {
return $this->conditionGroupFactory('AND');
}
/**
* {@inheritdoc}
*/
public function orConditionGroup() {
return $this->conditionGroupFactory('OR');
}
}

View File

@ -7,16 +7,58 @@
namespace Drupal\node;
use Drupal\Core\Database\Connection;
use Drupal\Core\Database\Query\SelectInterface;
use Drupal\Core\Entity\EntityControllerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Language\Language;
use Drupal\Core\Entity\EntityAccessController;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityNG;
use Drupal\Core\Session\AccountInterface;
use Drupal\user\Plugin\Core\Entity\User;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Defines the access controller for the node entity type.
*/
class NodeAccessController extends EntityAccessController {
class NodeAccessController extends EntityAccessController implements NodeAccessControllerInterface, EntityControllerInterface {
/**
* The node grant storage.
*
* @var \Drupal\node\NodeGrantStorageControllerInterface
*/
protected $grantStorage;
/**
* The module handler.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $moduleHandler;
/**
* Constructs a NodeAccessController object.
*
* @param \Drupal\node\NodeGrantDatabaseStorageInterface $grant_storage
* The node grant storage.
*/
public function __construct(NodeGrantDatabaseStorageInterface $grant_storage, ModuleHandlerInterface $module_handler) {
$this->grantStorage = $grant_storage;
$this->moduleHandler = $module_handler;
}
/**
* {@inheritdoc}
*/
public static function createInstance(ContainerInterface $container, $entity_type, array $entity_info) {
return new static(
$container->get('node.grant_storage'),
$container->get('module_handler')
);
}
/**
* {@inheritdoc}
@ -54,7 +96,7 @@ class NodeAccessController extends EntityAccessController {
// If no module specified either allow or deny, we fall back to the
// node_access table.
if (($grants = $this->accessGrants($node, $operation, $langcode, $account)) !== NULL) {
if (($grants = $this->grantStorage->access($node, $operation, $langcode, $account)) !== NULL) {
return $grants;
}
@ -66,64 +108,53 @@ class NodeAccessController extends EntityAccessController {
}
/**
* Determines access to nodes based on node grants.
*
* @param \Drupal\Core\Entity\EntityInterface $node
* The entity for which to check 'create' access.
* @param string $operation
* The entity operation. Usually one of 'view', 'edit', 'create' or
* 'delete'.
* @param string $langcode
* The language code for which to check access.
* @param \Drupal\Core\Session\AccountInterface $account
* The user for which to check access.
*
* @return bool|null
* TRUE if access was granted, FALSE if access was denied or NULL if no
* module implements hook_node_grants(), the node does not (yet) have an id
* or none of the implementing modules explicitly granted or denied access.
* {@inheritdoc}
*/
protected function accessGrants(EntityInterface $node, $operation, $langcode, AccountInterface $account) {
// If no module implements the hook or the node does not have an id there is
// no point in querying the database for access grants.
if (!module_implements('node_grants') || !$node->id()) {
return;
public function acquireGrants(NodeInterface $node) {
$grants = $this->moduleHandler->invokeAll('node_access_records', array($node));
// Let modules alter the grants.
$this->moduleHandler->alter('node_access_records', $grants, $node);
// If no grants are set and the node is published, then use the default grant.
if (empty($grants) && $node->isPublished()) {
$grants[] = array('realm' => 'all', 'gid' => 0, 'grant_view' => 1, 'grant_update' => 0, 'grant_delete' => 0);
}
return $grants;
}
// Check the database for potential access grants.
$query = db_select('node_access');
$query->addExpression('1');
// Only interested for granting in the current operation.
$query->condition('grant_' . $operation, 1, '>=');
// Check for grants for this node and the correct langcode.
$nids = db_and()
->condition('nid', $node->id())
->condition('langcode', $langcode);
// If the node is published, also take the default grant into account. The
// default is saved with a node ID of 0.
$status = $node instanceof EntityNG ? $node->status : $node->get('status', $langcode)->value;
if ($status) {
$nids = db_or()
->condition($nids)
->condition('nid', 0);
}
$query->condition($nids);
$query->range(0, 1);
/**
* {@inheritdoc}
*/
public function writeGrants(NodeInterface $node, $delete = TRUE) {
$grants = $this->acquireGrants($node);
$this->grantStorage->write($node, $grants, NULL, $delete);
}
$grants = db_or();
foreach (node_access_grants($operation, $account instanceof User ? $account->getBCEntity() : $account) as $realm => $gids) {
foreach ($gids as $gid) {
$grants->condition(db_and()
->condition('gid', $gid)
->condition('realm', $realm));
}
}
/**
* {@inheritdoc}
*/
public function writeDefaultGrant() {
$this->grantStorage->writeDefault();
}
if (count($grants) > 0) {
$query->condition($grants);
}
/**
* {@inheritdoc}
*/
public function deleteGrants() {
$this->grantStorage->delete();
}
return $query->execute()->fetchField();
/**
* {@inheritdoc}
*/
public function countGrants() {
return $this->grantStorage->count();
}
/**
* {@inheritdoc}
*/
public function checkAllGrants(AccountInterface $account) {
return $this->grantStorage->checkAll($account);
}
}

View File

@ -0,0 +1,87 @@
<?php
/**
* @file
* Contains
*/
namespace Drupal\node;
use Drupal\Core\Session\AccountInterface;
interface NodeAccessControllerInterface {
/**
* Gets the list of node access grants.
*
* This function is called to check the access grants for a node. It collects
* all node access grants for the node from hook_node_access_records()
* implementations, allows these grants to be altered via
* hook_node_access_records_alter() implementations, and returns the grants to
* the caller.
*
* @param \Drupal\node\NodeInterface $node
* The $node to acquire grants for.
*
* @return array $grants
* The access rules for the node.
*/
public function acquireGrants(NodeInterface $node);
/**
* 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.
*/
public function writeGrants(NodeInterface $node, $delete = TRUE);
/**
* Creates the default node access grant entry on the grant storage.
*/
public function writeDefaultGrant();
/**
* Deletes all node access entries.
*/
public function deleteGrants();
/**
* Counts available node grants.
*
* @return int
* Returns the amount of node grants.
*/
public function countGrants();
/**
* Checks all grants for a given account.
*
* @param \Drupal\Core\Session\AccountInterface $account
* A user object representing the user for whom the operation is to be
* performed.
*
* @return int.
* Status of the access check.
*/
public function checkAllGrants(AccountInterface $account);
}

View File

@ -0,0 +1,260 @@
<?php
/**
* @file
* Contains \Drupal\node\NodeGrantDatabaseStorage.
*/
namespace Drupal\node;
use Drupal\Core\Database\Connection;
use Drupal\Core\Database\Query\SelectInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityNG;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\user\Plugin\Core\Entity\User;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Defines a controller class that handles the node grants system.
*
* This is used to build node query access.
*/
class NodeGrantDatabaseStorage implements NodeGrantDatabaseStorageInterface {
/**
* The database connection.
*
* @var \Drupal\Core\Database\Connection
*/
protected $database;
/**
* The module handler.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $moduleHandler;
/**
* Constructs a NodeAccessController object.
*
* @param \Drupal\Core\Database\Connection $database
* The database connection.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler.
*/
public function __construct(Connection $database, ModuleHandlerInterface $module_handler) {
$this->database = $database;
$this->moduleHandler = $module_handler;
}
/**
* {@inheritdoc}
*/
public function access(EntityInterface $node, $operation, $langcode, AccountInterface $account) {
// If no module implements the hook or the node does not have an id there is
// no point in querying the database for access grants.
if (!$this->moduleHandler->getImplementations('node_grants') || !$node->id()) {
return;
}
// Check the database for potential access grants.
$query = $this->database->select('node_access');
$query->addExpression('1');
// Only interested for granting in the current operation.
$query->condition('grant_' . $operation, 1, '>=');
// Check for grants for this node and the correct langcode.
$nids = $query->andConditionGroup()
->condition('nid', $node->id())
->condition('langcode', $langcode);
// If the node is published, also take the default grant into account. The
// default is saved with a node ID of 0.
$status = $node instanceof EntityNG ? $node->status : $node->get('status', $langcode)->value;
if ($status) {
$nids = $query->orConditionGroup()
->condition($nids)
->condition('nid', 0);
}
$query->condition($nids);
$query->range(0, 1);
$grants = $query->orConditionGroup();
foreach (node_access_grants($operation, $account instanceof User ? $account->getBCEntity() : $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();
}
/**
* {@inheritdoc}
*/
public function checkAll(AccountInterface $account) {
$query = $this->database->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();
}
/**
* {@inheritdoc}
*/
public function alterQuery($query, array $tables, $op, AccountInterface $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) {
$base_table_found = TRUE;
// Set the subquery.
$subquery = $this->database->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);
}
}
}
/**
* {@inheritdoc}
*/
public function write(NodeInterface $node, array $grants, $realm = NULL, $delete = TRUE) {
if ($delete) {
$query = $this->database->delete('node_access')->condition('nid', $node->id());
if ($realm) {
$query->condition('realm', array($realm, 'all'), 'IN');
}
$query->execute();
}
// Only perform work when node_access modules are active.
if (!empty($grants) && count($this->moduleHandler->getImplementations('node_grants'))) {
$query = $this->database->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->id();
$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();
}
}
/**
* {@inheritdoc}
*/
public function delete() {
$this->database->delete('node_access')->execute();
}
/**
* {@inheritdoc}
*/
public function writeDefault() {
$this->database->insert('node_access')
->fields(array(
'nid' => 0,
'realm' => 'all',
'gid' => 0,
'grant_view' => 1,
'grant_update' => 0,
'grant_delete' => 0,
))
->execute();
}
/**
* {@inheritdoc}
*/
public function count() {
return $this->database->query('SELECT COUNT(*) FROM {node_access}')->fetchField();
}
}

View File

@ -0,0 +1,124 @@
<?php
/**
* @file
* Contains \Drupal\node\NodeGrantStorageControllerInterface.
*/
namespace Drupal\node;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Session\AccountInterface;
/**
* Provides an interface for node access controllers.
*/
interface NodeGrantDatabaseStorageInterface {
/**
* Checks all grants for a given account.
*
* @param \Drupal\Core\Session\AccountInterface $account
* A user object representing the user for whom the operation is to be
* performed.
*
* @return int.
* Status of the access check.
*/
public function checkAll(AccountInterface $account);
/**
* Alters a query when node access is required.
*
* @param mixed $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\Core\Session\AccountInterface $account
* A user object representing the user for whom the operation is to be
* performed.
* @param string $base_table
* The base table of the query.
*
* @return int
* Status of the access check.
*/
public function alterQuery($query, array $tables, $op, AccountInterface $account, $base_table);
/**
* 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 use
* 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_write_grants() instead.
*
* @param \Drupal\node\NodeInterface $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 bool $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_write_grants()
* @see node_access_acquire_grants()
*/
public function write(NodeInterface $node, array $grants, $realm = NULL, $delete = TRUE);
/**
* Deletes all node access entries.
*/
public function delete();
/**
* Creates the default node access grant entry.
*/
public function writeDefault();
/**
* Determines access to nodes based on node grants.
*
* @param \Drupal\Core\Entity\EntityInterface $node
* The entity for which to check 'create' access.
* @param string $operation
* The entity operation. Usually one of 'view', 'edit', 'create' or
* 'delete'.
* @param string $langcode
* The language code for which to check access.
* @param \Drupal\Core\Session\AccountInterface $account
* The user for which to check access.
*
* @return bool|null
* TRUE if access was granted, FALSE if access was denied or NULL if no
* module implements hook_node_grants(), the node does not (yet) have an id
* or none of the implementing modules explicitly granted or denied access.
*/
public function access(EntityInterface $node, $operation, $langcode, AccountInterface $account);
/**
* Counts available node grants.
*
* @return int
* Returns the amount of node grants.
*/
public function count();
}

View File

@ -115,7 +115,7 @@ class Node extends EntityNG implements NodeInterface {
// default revision. There's no need to delete existing records if the node
// is new.
if ($this->isDefaultRevision()) {
node_access_acquire_grants($this->getBCEntity(), $update);
\Drupal::entityManager()->getAccessController('node')->writeGrants($this->getBCEntity(), $update);
}
}

View File

@ -253,7 +253,7 @@ function hook_node_grants($account, $op) {
* @return
* An array of grants as defined above.
*
* @see _node_access_write_grants()
* @see node_access_write_grants()
* @see hook_node_access_records_alter()
* @ingroup node_access
*/

View File

@ -9,6 +9,7 @@
*/
use Drupal\Core\Language\Language;
use Drupal\node\NodeInterface;
use Symfony\Component\HttpFoundation\Response;
use Drupal\Core\Cache\CacheBackendInterface;
@ -21,7 +22,6 @@ use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Template\Attribute;
use Drupal\entity\Plugin\Core\Entity\EntityDisplay;
use Drupal\file\Plugin\Core\Entity\File;
use Drupal\node\NodeInterface;
use Drupal\user\UserInterface;
/**
@ -2185,7 +2185,7 @@ function node_access($op, $node, $account = NULL, $langcode = NULL) {
$account = user_load($account->uid);
}
return entity_access_controller('node')->access($node, $op, $langcode, $account);
return Drupal::entityManager()->getAccessController('node')->access($node, $op, $langcode, $account);
}
/**
@ -2357,27 +2357,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] = Drupal::entityManager()->getAccessController('node')->checkAllGrants($account);
}
return $access[$account->uid];
@ -2447,159 +2427,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);
$base_table_found = FALSE;
foreach ($tables as $nalias => $tableinfo) {
$table = $tableinfo['table'];
if (!($table instanceof SelectInterface) && $table == $base_table) {
$base_table_found = TRUE;
// 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);
}
}
// If we reached this point and did not find the defined base table, throw
// an exception.
if (!$base_table_found) {
throw new Exception(t('Query tagged for node access but the defined base_table @base_table was not found', array('@base_table' => $base_table)));
}
}
/**
* Gets the list of node access grants and writes them to the database.
*
* This function is called when a node is saved, and can also be called by
* modules if something other than a node save causes node access permissions to
* change. It collects all node access grants for the node from
* hook_node_access_records() implementations, allows these grants to be altered
* via hook_node_access_records_alter() implementations, and saves the collected
* and altered grants to the database.
*
* @param \Drupal\node\NodeInterface $node
* The $node to acquire grants for.
* @param $delete
* (optional) Whether to delete existing node access records before inserting
* new ones. Defaults to TRUE.
*/
function node_access_acquire_grants(NodeInterface $node, $delete = TRUE) {
$grants = module_invoke_all('node_access_records', $node);
// Let modules alter the grants.
drupal_alter('node_access_records', $grants, $node);
// If no grants are set and the node is published, then use the default grant.
if (empty($grants) && $node->isPublished()) {
$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->id());
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->id();
$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();
}
// Update the query for the given storage method.
Drupal::service('node.grant_storage')->alterQuery($query, $tables, $op, $account, $base_table);
}
/**
@ -2657,7 +2486,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();
$access_controller = Drupal::entityManager()->getAccessController('node');
$access_controller->deleteGrants();
// Only recalculate if the site is using a node_access module.
if (count(module_implements('node_grants'))) {
if ($batch_mode) {
@ -2675,29 +2505,22 @@ 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
// To preserve database integrity, only write grants if the node
// loads successfully.
if (!empty($node)) {
node_access_acquire_grants($node);
$access_controller->writeGrants($node);
}
}
}
}
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();
$access_controller->writeDefaultGrant();
}
if (!isset($batch)) {
@ -2722,18 +2545,22 @@ 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();
$nids = Drupal::entityQuery('node')
->condition('nid', $context['sandbox']['current_node'], '>')
->sort('nid', 'DESC')
->range(0, $limit)
->execute();
$nodes = node_load_multiple($nids, TRUE);
foreach ($nodes as $nid => $node) {
// To preserve database integrity, only acquire grants if the node
// To preserve database integrity, only write grants if the node
// loads successfully.
if (!empty($node)) {
node_access_acquire_grants($node);
Drupal::entityManager()->getAccessController('node')->writeGrants($node);
}
$context['sandbox']['progress']++;
$context['sandbox']['current_node'] = $nid;
@ -2779,7 +2606,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::entityManager()->getAccessController('node')->countGrants();
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));
}

View File

@ -0,0 +1,4 @@
services:
node.grant_storage:
class: Drupal\node\NodeGrantDatabaseStorage
arguments: ['@database', '@module_handler']