Commit e85f89c3 authored by Dries's avatar Dries

Issue #1946434 by tim.plunkett: Convert all of confirm_form() in...

Issue #1946434 by tim.plunkett: Convert all of confirm_form() in node.admin.inc and node.pages.inc to the new form interface.
parent d5b67717
<?php
/**
* @file
* Contains \Drupal\node\Access\NodeRevisionAccessCheck.
*/
namespace Drupal\node\Access;
use Drupal\Core\Access\AccessCheckInterface;
use Drupal\Core\Database\Connection;
use Drupal\Core\Entity\EntityManager;
use Drupal\Core\Session\AccountInterface;
use Drupal\node\NodeInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Route;
/**
* Provides an access checker for node revisions.
*/
class NodeRevisionAccessCheck implements AccessCheckInterface {
/**
* The node storage.
*
* @var \Drupal\Core\Entity\EntityStorageControllerInterface
*/
protected $nodeStorage;
/**
* The node access controller.
*
* @var \Drupal\Core\Entity\EntityAccessControllerInterface
*/
protected $nodeAccess;
/**
* The database connection.
*
* @var \Drupal\Core\Database\Connection
*/
protected $connection;
/**
* A static cache of access checks.
*
* @var array
*/
protected $access = array();
/**
* Constructs a new NodeRevisionAccessCheck.
*
* @param \Drupal\Core\Entity\EntityManager $entity_manager
* The entity manager.
* @param \Drupal\Core\Database\Connection $connection
* The database connection.
*/
public function __construct(EntityManager $entity_manager, Connection $connection) {
$this->nodeStorage = $entity_manager->getStorageController('node');
$this->nodeAccess = $entity_manager->getAccessController('node');
$this->connection = $connection;
}
/**
* {@inheritdoc}
*/
public function applies(Route $route) {
return array_key_exists('_access_node_revision', $route->getRequirements());
}
/**
* {@inheritdoc}
*/
public function access(Route $route, Request $request) {
$revision = $this->nodeStorage->loadRevision($request->attributes->get('node_revision'));
return $this->checkAccess($revision, $route->getRequirement('_access_node_revision')) ? static::ALLOW : static::DENY;
}
/**
* Checks node revision access.
*
* @param \Drupal\node\NodeInterface $node
* The node to check.
* @param string $op
* (optional) The specific operation being checked. Defaults to 'view.'
* @param \Drupal\Core\Session\AccountInterface|null $account
* (optional) A user object representing the user for whom the operation is
* to be performed. Determines access for a user other than the current user.
* Defaults to NULL.
* @param string|null $langcode
* (optional) Language code for the variant of the node. Different language
* variants might have different permissions associated. If NULL, the
* original langcode of the node is used. Defaults to NULL.
*
* @return bool
* TRUE if the operation may be performed, FALSE otherwise.
*/
public function checkAccess(NodeInterface $node, $op = 'view', AccountInterface $account = NULL, $langcode = NULL) {
$map = array(
'view' => 'view all revisions',
'update' => 'revert all revisions',
'delete' => 'delete all revisions',
);
$bundle = $node->bundle();
$type_map = array(
'view' => "view $bundle revisions",
'update' => "revert $bundle revisions",
'delete' => "delete $bundle revisions",
);
if (!$node || !isset($map[$op]) || !isset($type_map[$op])) {
// If there was no node to check against, or the $op was not one of the
// supported ones, we return access denied.
return FALSE;
}
if (!isset($account)) {
$account = $GLOBALS['user'];
}
// If no language code was provided, default to the node revision's langcode.
if (empty($langcode)) {
$langcode = $node->language()->id;
}
// Statically cache access by revision ID, language code, user account ID,
// and operation.
$cid = $node->getRevisionId() . ':' . $langcode . ':' . $account->id() . ':' . $op;
if (!isset($this->access[$cid])) {
// Perform basic permission checks first.
if (!user_access($map[$op], $account) && !user_access($type_map[$op], $account) && !user_access('administer nodes', $account)) {
return $this->access[$cid] = FALSE;
}
// There should be at least two revisions. If the vid of the given node
// and the vid of the default revision differ, then we already have two
// different revisions so there is no need for a separate database check.
// Also, if you try to revert to or delete the default revision, that's
// not good.
if ($node->isDefaultRevision() && ($this->connection->query('SELECT COUNT(*) FROM {node_field_revision} WHERE nid = :nid AND default_langcode = 1', array(':nid' => $node->id()))->fetchField() == 1 || $op == 'update' || $op == 'delete')) {
$this->access[$cid] = FALSE;
}
elseif (user_access('administer nodes', $account)) {
$this->access[$cid] = TRUE;
}
else {
// First check the access to the default revision and finally, if the
// node passed in is not the default revision then access to that, too.
$this->access[$cid] = $this->nodeAccess->access($this->nodeStorage->load($node->id()), $op, $langcode, $account) && ($node->isDefaultRevision() || $this->nodeAccess->access($node, $op, $langcode, $account));
}
}
return $this->access[$cid];
}
}
<?php
/**
* @file
* Contains \Drupal\node\Form\NodeDeleteForm.
*/
namespace Drupal\node\Form;
use Drupal\Core\Entity\EntityControllerInterface;
use Drupal\Core\Entity\EntityNGConfirmFormBase;
use Drupal\Core\Entity\EntityStorageControllerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Routing\PathBasedGeneratorInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
/**
* Provides a form for deleting a node.
*/
class NodeDeleteForm extends EntityNGConfirmFormBase implements EntityControllerInterface {
/**
* The URL generator.
*
* @var \Drupal\Core\Routing\PathBasedGeneratorInterface
*/
protected $urlGenerator;
/**
* The node type storage.
*
* @var \Drupal\Core\Entity\EntityStorageControllerInterface
*/
protected $nodeTypeStorage;
/**
* Constructs a NodeDeleteForm object.
*
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler service.
* @param \Drupal\Core\Routing\PathBasedGeneratorInterface $url_generator
* The URL generator.
* @param \Drupal\Core\Entity\EntityStorageControllerInterface $node_type_storage
* The node type storage.
*/
public function __construct(ModuleHandlerInterface $module_handler, PathBasedGeneratorInterface $url_generator, EntityStorageControllerInterface $node_type_storage) {
parent::__construct($module_handler);
$this->urlGenerator = $url_generator;
$this->nodeTypeStorage = $node_type_storage;
}
/**
* {@inheritdoc}
*/
public static function createInstance(ContainerInterface $container, $entity_type, array $entity_info) {
return new static(
$container->get('module_handler'),
$container->get('url_generator'),
$container->get('plugin.manager.entity')->getStorageController('node_type')
);
}
/**
* {@inheritdoc}
*/
public function getQuestion() {
return t('Are you sure you want to delete %title?', array('%title' => $this->entity->label()));
}
/**
* {@inheritdoc}
*/
public function getCancelPath() {
$uri = $this->entity->uri();
return $this->urlGenerator->generateFromPath($uri['path'], $uri['options']);
}
/**
* {@inheritdoc}
*/
public function getConfirmText() {
return t('Delete');
}
/**
* {@inheritdoc}
*/
public function form(array $form, array &$form_state) {
// Do not attach fields to the delete form.
return $form;
}
/**
* {@inheritdoc}
*/
public function submit(array $form, array &$form_state) {
$this->entity->delete();
watchdog('content', '@type: deleted %title.', array('@type' => $this->entity->bundle(), '%title' => $this->entity->label()));
$node_type = $this->nodeTypeStorage->load($this->entity->bundle())->label();
drupal_set_message(t('@type %title has been deleted.', array('@type' => $node_type, '%title' => $this->entity->label())));
$form_state['redirect'] = '<front>';
}
}
<?php
/**
* @file
* Contains \Drupal\node\Form\NodeRevisionDeleteForm.
*/
namespace Drupal\node\Form;
use Drupal\Core\Controller\ControllerInterface;
use Drupal\Core\Database\Connection;
use Drupal\Core\Entity\EntityStorageControllerInterface;
use Drupal\Core\Form\ConfirmFormBase;
use Drupal\node\NodeInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
/**
* Provides a form for reverting a node revision.
*/
class NodeRevisionDeleteForm extends ConfirmFormBase implements ControllerInterface {
/**
* The node revision.
*
* @var \Drupal\node\NodeInterface
*/
protected $revision;
/**
* The node storage.
*
* @var \Drupal\Core\Entity\EntityStorageControllerInterface
*/
protected $nodeStorage;
/**
* The node type storage.
*
* @var \Drupal\Core\Entity\EntityStorageControllerInterface
*/
protected $nodeTypeStorage;
/**
* The database connection.
*
* @var \Drupal\Core\Database\Connection
*/
protected $connection;
/**
* Constructs a new NodeRevisionDeleteForm.
*
* @param \Drupal\Core\Entity\EntityStorageControllerInterface $node_storage
* The node storage.
* @param \Drupal\Core\Entity\EntityStorageControllerInterface $node_type_storage
* The node type storage.
* @param \Drupal\Core\Database\Connection $connection
* The database connection.
*/
public function __construct(EntityStorageControllerInterface $node_storage, EntityStorageControllerInterface $node_type_storage, Connection $connection) {
$this->nodeStorage = $node_storage;
$this->nodeTypeStorage = $node_type_storage;
$this->connection = $connection;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
$entity_manager = $container->get('plugin.manager.entity');
return new static(
$entity_manager->getStorageController('node'),
$entity_manager->getStorageController('node_type'),
$container->get('database')
);
}
/**
* {@inheritdoc}
*/
public function getFormID() {
return 'node_revision_delete_confirm';
}
/**
* {@inheritdoc}
*/
public function getQuestion() {
return t('Are you sure you want to delete the revision from %revision-date?', array('%revision-date' => format_date($this->revision->getRevisionCreationTime())));
}
/**
* {@inheritdoc}
*/
public function getCancelPath() {
return 'node/' . $this->revision->id() . '/revisions';
}
/**
* {@inheritdoc}
*/
public function getConfirmText() {
return t('Delete');
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, array &$form_state, Request $request = NULL, $node_revision = NULL) {
$this->revision = $this->nodeStorage->loadRevision($node_revision);
return parent::buildForm($form, $form_state, $request);
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, array &$form_state) {
$this->nodeStorage->deleteRevision($this->revision->getRevisionId());
watchdog('content', '@type: deleted %title revision %revision.', array('@type' => $this->revision->bundle(), '%title' => $this->revision->label(), '%revision' => $this->revision->getRevisionId()));
$node_type = $this->nodeTypeStorage->load($this->revision->bundle())->label();
drupal_set_message(t('Revision from %revision-date of @type %title has been deleted.', array('%revision-date' => format_date($this->revision->getRevisionCreationTime()), '@type' => $node_type, '%title' => $this->revision->label())));
$form_state['redirect'] = 'node/' . $this->revision->id();
if ($this->connection->query('SELECT COUNT(DISTINCT vid) FROM {node_field_revision} WHERE nid = :nid', array(':nid' => $this->revision->id()))->fetchField() > 1) {
$form_state['redirect'] .= '/revisions';
}
}
}
<?php
/**
* @file
* Contains \Drupal\node\Form\NodeRevisionDeleteForm.
*/
namespace Drupal\node\Form;
use Drupal\Core\Controller\ControllerInterface;
use Drupal\Core\Entity\EntityStorageControllerInterface;
use Drupal\Core\Form\ConfirmFormBase;
use Drupal\node\NodeInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
/**
* Provides a form for reverting a node revision.
*/
class NodeRevisionRevertForm extends ConfirmFormBase implements ControllerInterface {
/**
* The node revision.
*
* @var \Drupal\node\NodeInterface
*/
protected $revision;
/**
* The node storage.
*
* @var \Drupal\Core\Entity\EntityStorageControllerInterface
*/
protected $nodeStorage;
/**
* Constructs a new NodeRevisionRevertForm.
*
* @param \Drupal\Core\Entity\EntityStorageControllerInterface $node_storage
* The node storage.
*/
public function __construct(EntityStorageControllerInterface $node_storage) {
$this->nodeStorage = $node_storage;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('plugin.manager.entity')->getStorageController('node')
);
}
/**
* {@inheritdoc}
*/
public function getFormID() {
return 'node_revision_revert_confirm';
}
/**
* {@inheritdoc}
*/
public function getQuestion() {
return t('Are you sure you want to revert to the revision from %revision-date?', array('%revision-date' => format_date($this->revision->getRevisionCreationTime())));
}
/**
* {@inheritdoc}
*/
public function getCancelPath() {
return 'node/' . $this->revision->id() . '/revisions';
}
/**
* {@inheritdoc}
*/
public function getConfirmText() {
return t('Revert');
}
/**
* {@inheritdoc}
*/
public function getDescription() {
return '';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, array &$form_state, Request $request = NULL, $node_revision = NULL) {
$this->revision = $this->nodeStorage->loadRevision($node_revision);
return parent::buildForm($form, $form_state, $request);
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, array &$form_state) {
$this->revision->setNewRevision();
// Make this the new default revision for the node.
$this->revision->isDefaultRevision(TRUE);
// The revision timestamp will be updated when the revision is saved. Keep the
// original one for the confirmation message.
$original_revision_timestamp = $this->revision->getRevisionCreationTime();
$this->revision->log = t('Copy of the revision from %date.', array('%date' => format_date($original_revision_timestamp)));
$this->revision->save();
watchdog('content', '@type: reverted %title revision %revision.', array('@type' => $this->revision->bundle(), '%title' => $this->revision->label(), '%revision' => $this->revision->getRevisionId()));
drupal_set_message(t('@type %title has been reverted back to the revision from %revision-date.', array('@type' => node_get_type_label($this->revision), '%title' => $this->revision->label(), '%revision-date' => format_date($original_revision_timestamp))));
$form_state['redirect'] = 'node/' . $this->revision->id() . '/revisions';
}
}
<?php
/**
* @file
* Contains \Drupal\node\Form\RebuildPermissionsForm.
*/
namespace Drupal\node\Form;
use Drupal\Core\Form\ConfirmFormBase;
class RebuildPermissionsForm extends ConfirmFormBase {
/**
* {@inheritdoc}
*/
public function getFormID() {
return 'node_configure_rebuild_confirm';
}
/**
* {@inheritdoc}
*/
public function getQuestion() {
return t('Are you sure you want to rebuild the permissions on site content?');
}
/**
* {@inheritdoc}
*/
public function getCancelPath() {
return 'admin/reports/status';
}
/**
* {@inheritdoc}
*/
public function getConfirmText() {
return t('Rebuild permissions');
}
/**
* {@inheritdoc}
*/
public function getDescription() {
return t('This action rebuilds all permissions on site content, and may be a lengthy process. This action cannot be undone.');
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, array &$form_state) {
node_access_rebuild(TRUE);
$form_state['redirect'] = 'admin/reports/status';
}
}
......@@ -28,6 +28,7 @@
* "access" = "Drupal\node\NodeAccessController",
* "form" = {
* "default" = "Drupal\node\NodeFormController",
* "delete" = "Drupal\node\Form\NodeDeleteForm",
* "edit" = "Drupal\node\NodeFormController"
* },
* "translation" = "Drupal\node\NodeTranslationController"
......
......@@ -8,29 +8,6 @@
use Drupal\Core\Language\Language;
use Drupal\node\NodeInterface;
/**
* Page callback: Form constructor for the permission rebuild confirmation form.
*
* @return array
* An array as expected by drupal_render().
*
* @see node_configure_rebuild_confirm_submit()
* @see node_menu()
* @ingroup forms
*/
function node_configure_rebuild_confirm() {
return confirm_form(array(), t('Are you sure you want to rebuild the permissions on site content?'),
'admin/reports/status', t('This action rebuilds all permissions on site content, and may be a lengthy process. This action cannot be undone.'), t('Rebuild permissions'), t('Cancel'));
}
/**
* Form submission handler for node_configure_rebuild_confirm().
*/
function node_configure_rebuild_confirm_submit($form, &$form_state) {
node_access_rebuild(TRUE);
$form_state['redirect'] = 'admin/reports/status';
}
/**
* Updates all nodes in the passed-in array with the passed-in field values.
*
......
......@@ -1116,64 +1116,7 @@ function theme_node_search_admin($variables) {
* @see node_menu()
*/
function _node_revision_access(EntityInterface $node, $op = 'view', $account = NULL, $langcode = NULL) {
$access = &drupal_static(__FUNCTION__, array());
$map = array(
'view' => 'view all revisions',
'update' => 'revert all revisions',
'delete' => 'delete all revisions',
);
$bundle = $node->bundle();
$type_map = array(
'view' => "view $bundle revisions",
'update' => "revert $bundle revisions",
'delete' => "delete $bundle revisions",
);
if (!$node || !isset($map[$op]) || !isset($type_map[$op])) {
// If there was no node to check against, or the $op was not one of the
// supported ones, we return access denied.
return FALSE;
}
if (!isset($account)) {
$account = $GLOBALS['user'];
}
// If no language code was provided, default to the node revision's langcode.
if (empty($langcode)) {
$langcode = $node->language()->id;
}
// Statically cache access by revision ID, language code, user account ID,
// and operation.
$cid = $node->getRevisionId() . ':' . $langcode . ':' . $account->id() . ':' . $op;
if (!isset($access[$cid])) {
// Perform basic permission checks first.
if (!user_access($map[$op], $account) && !user_access($type_map[$op], $account) && !user_access('administer nodes', $account)) {
return $access[$cid] = FALSE;
}
// There should be at least two revisions. If the vid of the given node
// and the vid of the default revision differ, then we already have two
// different revisions so there is no need for a separate database check.
// Also, if you try to revert to or delete the default revision, that's
// not good.
if ($node->isDefaultRevision() && (db_query('SELECT COUNT(*) FROM {node_field_revision} WHERE nid = :nid AND default_langcode = 1', array(':nid' => $node->id()))->fetchField() == 1 || $op == 'update' || $op == 'delete')) {
$access[$cid] = FALSE;
}
elseif (user_access('administer nodes', $account)) {
$access[$cid] = TRUE;
}
else {
// First check the access to the default revision and finally, if the
// node passed in is not the default revision then access to that, too.
$access[$cid] = node_access($op, node_load($node->id()), $account, $langcode) && ($node->isDefaultRevision() || node_access($op, $node, $account, $langcode));
}
}
return $access[$cid];
return Drupal::service('access_check.node.revision')->checkAccess($node, $op, $account, $langcode);
}
/**
......@@ -1217,17 +1160,6 @@ function node_menu() {
'type' => MENU_DEFAULT_LOCAL_TASK,
);
$items['admin/reports/status/rebuild'] = array(
'title' => 'Rebuild permissions',
'page callback' => 'drupal_get_form',
'page arguments' => array('node_configure_rebuild_confirm'),
// Any user than can potentially trigger a node_access_needs_rebuild(TRUE)
// has to be allowed access to the 'node access rebuild' confirm form.
'access arguments' => array('access administration pages'),
'type' => MENU_CALLBACK,
'file' => 'node.admin.inc',
);
$items['admin/structure/types'] = array(
'title' => 'Content types',
'description' => 'Manage content types, including default status, front page promotion, comment settings, etc.',
......@@ -1295,14 +1227,10 @@ function node_menu() {
);
$items['node/%node/delete'] = array(
'title' => 'Delete',
'page callback' => 'drupal_get_form',
'page arguments' => array('node_delete_confirm', 1),
'access callback' => 'node_access',
'access arguments' => array('delete', 1),
'route_name' => 'node_delete_confirm',
'weight' => 10,
'type' => MENU_LOCAL_TASK,