From e85f89c3ccc8c521b0d9889bbb45e6d47c22adc3 Mon Sep 17 00:00:00 2001
From: Dries <dries@buytaert.net>
Date: Thu, 11 Jul 2013 22:04:19 -0400
Subject: [PATCH] Issue #1946434 by tim.plunkett: Convert all of confirm_form()
 in node.admin.inc and node.pages.inc to the new form interface.

---
 .../node/Access/NodeRevisionAccessCheck.php   | 158 ++++++++++++++++++
 .../lib/Drupal/node/Form/NodeDeleteForm.php   | 106 ++++++++++++
 .../node/Form/NodeRevisionDeleteForm.php      | 130 ++++++++++++++
 .../node/Form/NodeRevisionRevertForm.php      | 119 +++++++++++++
 .../node/Form/RebuildPermissionsForm.php      |  57 +++++++
 .../Drupal/node/Plugin/Core/Entity/Node.php   |   1 +
 core/modules/node/node.admin.inc              |  23 ---
 core/modules/node/node.module                 |  88 +---------
 core/modules/node/node.pages.inc              | 115 -------------
 core/modules/node/node.routing.yml            |  28 ++++
 core/modules/node/node.services.yml           |   5 +
 11 files changed, 608 insertions(+), 222 deletions(-)
 create mode 100644 core/modules/node/lib/Drupal/node/Access/NodeRevisionAccessCheck.php
 create mode 100644 core/modules/node/lib/Drupal/node/Form/NodeDeleteForm.php
 create mode 100644 core/modules/node/lib/Drupal/node/Form/NodeRevisionDeleteForm.php
 create mode 100644 core/modules/node/lib/Drupal/node/Form/NodeRevisionRevertForm.php
 create mode 100644 core/modules/node/lib/Drupal/node/Form/RebuildPermissionsForm.php

diff --git a/core/modules/node/lib/Drupal/node/Access/NodeRevisionAccessCheck.php b/core/modules/node/lib/Drupal/node/Access/NodeRevisionAccessCheck.php
new file mode 100644
index 000000000000..aca491d7bdbe
--- /dev/null
+++ b/core/modules/node/lib/Drupal/node/Access/NodeRevisionAccessCheck.php
@@ -0,0 +1,158 @@
+<?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];
+  }
+
+}
diff --git a/core/modules/node/lib/Drupal/node/Form/NodeDeleteForm.php b/core/modules/node/lib/Drupal/node/Form/NodeDeleteForm.php
new file mode 100644
index 000000000000..5a3fdb040400
--- /dev/null
+++ b/core/modules/node/lib/Drupal/node/Form/NodeDeleteForm.php
@@ -0,0 +1,106 @@
+<?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>';
+  }
+
+}
diff --git a/core/modules/node/lib/Drupal/node/Form/NodeRevisionDeleteForm.php b/core/modules/node/lib/Drupal/node/Form/NodeRevisionDeleteForm.php
new file mode 100644
index 000000000000..8c06d4995d6b
--- /dev/null
+++ b/core/modules/node/lib/Drupal/node/Form/NodeRevisionDeleteForm.php
@@ -0,0 +1,130 @@
+<?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';
+    }
+  }
+
+}
diff --git a/core/modules/node/lib/Drupal/node/Form/NodeRevisionRevertForm.php b/core/modules/node/lib/Drupal/node/Form/NodeRevisionRevertForm.php
new file mode 100644
index 000000000000..d4c69ec9e276
--- /dev/null
+++ b/core/modules/node/lib/Drupal/node/Form/NodeRevisionRevertForm.php
@@ -0,0 +1,119 @@
+<?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';
+  }
+
+}
diff --git a/core/modules/node/lib/Drupal/node/Form/RebuildPermissionsForm.php b/core/modules/node/lib/Drupal/node/Form/RebuildPermissionsForm.php
new file mode 100644
index 000000000000..d104fab287eb
--- /dev/null
+++ b/core/modules/node/lib/Drupal/node/Form/RebuildPermissionsForm.php
@@ -0,0 +1,57 @@
+<?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';
+  }
+
+}
diff --git a/core/modules/node/lib/Drupal/node/Plugin/Core/Entity/Node.php b/core/modules/node/lib/Drupal/node/Plugin/Core/Entity/Node.php
index 82023196512d..01a35350b1e1 100644
--- a/core/modules/node/lib/Drupal/node/Plugin/Core/Entity/Node.php
+++ b/core/modules/node/lib/Drupal/node/Plugin/Core/Entity/Node.php
@@ -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"
diff --git a/core/modules/node/node.admin.inc b/core/modules/node/node.admin.inc
index eeec22becc68..3a30fafcb62f 100644
--- a/core/modules/node/node.admin.inc
+++ b/core/modules/node/node.admin.inc
@@ -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.
  *
diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index e83670076273..293c64e4af2a 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -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,
     'context' => MENU_CONTEXT_INLINE,
-    'file' => 'node.pages.inc',
   );
   $items['node/%node/revisions'] = array(
     'title' => 'Revisions',
@@ -1323,19 +1251,11 @@ function node_menu() {
   );
   $items['node/%node/revisions/%node_revision/revert'] = array(
     'title' => 'Revert to earlier revision',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('node_revision_revert_confirm', 3),
-    'access callback' => '_node_revision_access',
-    'access arguments' => array(3, 'update'),
-    'file' => 'node.pages.inc',
+    'route_name' => 'node_revision_revert_confirm',
   );
   $items['node/%node/revisions/%node_revision/delete'] = array(
     'title' => 'Delete earlier revision',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('node_revision_delete_confirm', 3),
-    'access callback' => '_node_revision_access',
-    'access arguments' => array(3, 'delete'),
-    'file' => 'node.pages.inc',
+    'route_name' => 'node_revision_delete_confirm',
   );
   return $items;
 }
diff --git a/core/modules/node/node.pages.inc b/core/modules/node/node.pages.inc
index 0532aa2592c0..7dff75d1e518 100644
--- a/core/modules/node/node.pages.inc
+++ b/core/modules/node/node.pages.inc
@@ -180,44 +180,6 @@ function theme_node_preview($variables) {
   return $output;
 }
 
-/**
- * Page callback: Form constructor for node deletion confirmation form.
- *
- * @param object $node
- *   A node object.
- *
- * @return array
- *   A form array.
- *
- * @see node_delete_confirm_submit()
- * @see node_menu()
- */
-function node_delete_confirm($form, &$form_state, $node) {
-  // Always provide entity id in the same form key as in the entity edit form.
-  $form['nid'] = array('#type' => 'value', '#value' => $node->nid);
-  return confirm_form($form,
-    t('Are you sure you want to delete %title?', array('%title' => $node->label())),
-    'node/' . $node->nid,
-    t('This action cannot be undone.'),
-    t('Delete'),
-    t('Cancel')
-  );
-}
-
-/**
- * Form submission handler for node_delete_confirm().
- */
-function node_delete_confirm_submit($form, &$form_state) {
-  if ($form_state['values']['confirm']) {
-    $node = node_load($form_state['values']['nid']);
-    $node->delete();
-    watchdog('content', '@type: deleted %title.', array('@type' => $node->type, '%title' => $node->label()));
-    drupal_set_message(t('@type %title has been deleted.', array('@type' => node_get_type_label($node), '%title' => $node->label())));
-  }
-
-  $form_state['redirect'] = '<front>';
-}
-
 /**
  * Page callback: Generates an overview table of older revisions of a node.
  *
@@ -300,80 +262,3 @@ function node_revision_overview($node) {
 
   return $build;
 }
-
-/**
- * Page callback: Form constructor for the reversion confirmation form.
- *
- * This form prevents against CSRF attacks.
- *
- * @param int $node_revision
- *   The node revision ID.
- *
- * @return array
- *   An array as expected by drupal_render().
- *
- * @see node_menu()
- * @see node_revision_revert_confirm_submit()
- * @ingroup forms
- */
-function node_revision_revert_confirm($form, $form_state, $node_revision) {
-  $form['#node_revision'] = $node_revision;
-  return confirm_form($form, t('Are you sure you want to revert to the revision from %revision-date?', array('%revision-date' => format_date($node_revision->revision_timestamp))), 'node/' . $node_revision->nid . '/revisions', '', t('Revert'), t('Cancel'));
-}
-
-/**
- * Form submission handler for node_revision_revert_confirm().
- */
-function node_revision_revert_confirm_submit($form, &$form_state) {
-  $node_revision = $form['#node_revision'];
-  $node_revision->setNewRevision();
-  // Make this the new default revision for the node.
-  $node_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 = $node_revision->revision_timestamp;
-
-  $node_revision->log = t('Copy of the revision from %date.', array('%date' => format_date($original_revision_timestamp)));
-
-  $node_revision->save();
-
-  watchdog('content', '@type: reverted %title revision %revision.', array('@type' => $node_revision->type, '%title' => $node_revision->label(), '%revision' => $node_revision->vid));
-  drupal_set_message(t('@type %title has been reverted back to the revision from %revision-date.', array('@type' => node_get_type_label($node_revision), '%title' => $node_revision->label(), '%revision-date' => format_date($original_revision_timestamp))));
-  $form_state['redirect'] = 'node/' . $node_revision->nid . '/revisions';
-}
-
-/**
- * Page callback: Form constructor for the revision deletion confirmation form.
- *
- * This form prevents against CSRF attacks.
- *
- * @param $node_revision
- *   The node revision ID.
- *
- * @return
- *   An array as expected by drupal_render().
- *
- * @see node_menu()
- * @see node_revision_delete_confirm_submit()
- * @ingroup forms
- */
-function node_revision_delete_confirm($form, $form_state, $node_revision) {
-  $form['#node_revision'] = $node_revision;
-  return confirm_form($form, t('Are you sure you want to delete the revision from %revision-date?', array('%revision-date' => format_date($node_revision->revision_timestamp))), 'node/' . $node_revision->nid . '/revisions', t('This action cannot be undone.'), t('Delete'), t('Cancel'));
-}
-
-/**
- * Form submission handler for node_revision_delete_confirm().
- */
-function node_revision_delete_confirm_submit($form, &$form_state) {
-  $node_revision = $form['#node_revision'];
-  node_revision_delete($node_revision->vid);
-
-  watchdog('content', '@type: deleted %title revision %revision.', array('@type' => $node_revision->type, '%title' => $node_revision->label(), '%revision' => $node_revision->vid));
-  drupal_set_message(t('Revision from %revision-date of @type %title has been deleted.', array('%revision-date' => format_date($node_revision->revision_timestamp), '@type' => node_get_type_label($node_revision), '%title' => $node_revision->label())));
-  $form_state['redirect'] = 'node/' . $node_revision->nid;
-  if (db_query('SELECT COUNT(DISTINCT vid) FROM {node_field_revision} WHERE nid = :nid', array(':nid' => $node_revision->nid))->fetchField() > 1) {
-    $form_state['redirect'] .= '/revisions';
-  }
-}
diff --git a/core/modules/node/node.routing.yml b/core/modules/node/node.routing.yml
index 9204a2b90678..dfdc0a6037fd 100644
--- a/core/modules/node/node.routing.yml
+++ b/core/modules/node/node.routing.yml
@@ -12,6 +12,27 @@ node_page_edit:
   requirements:
     _entity_access: 'node.update'
 
+node_delete_confirm:
+  pattern: '/node/{node}/delete'
+  defaults:
+    _entity_form: 'node.delete'
+  requirements:
+    _entity_access: 'node.delete'
+
+node_revision_revert_confirm:
+  pattern: '/node/{node}/revisions/{node_revision}/revert'
+  defaults:
+    _form: '\Drupal\node\Form\NodeRevisionRevertForm'
+  requirements:
+    _access_node_revision: 'update'
+
+node_revision_delete_confirm:
+  pattern: '/node/{node}/revisions/{node_revision}/delete'
+  defaults:
+    _form: '\Drupal\node\Form\NodeRevisionDeleteForm'
+  requirements:
+    _access_node_revision: 'delete'
+
 node_overview_types:
   pattern: '/admin/structure/types'
   defaults:
@@ -47,3 +68,10 @@ node_type_delete_confirm:
     _entity_form: 'node_type.delete'
   requirements:
     _entity_access: 'node_type.delete'
+
+node_configure_rebuild_confirm:
+  pattern: '/admin/reports/status/rebuild'
+  defaults:
+    _form: 'Drupal\node\Form\RebuildPermissionsForm'
+  requirements:
+    _permission: 'access administration pages'
diff --git a/core/modules/node/node.services.yml b/core/modules/node/node.services.yml
index 6f486f77c92e..ab496171b463 100644
--- a/core/modules/node/node.services.yml
+++ b/core/modules/node/node.services.yml
@@ -2,3 +2,8 @@ services:
   node.grant_storage:
     class: Drupal\node\NodeGrantDatabaseStorage
     arguments: ['@database', '@module_handler']
+  access_check.node.revision:
+    class: Drupal\node\Access\NodeRevisionAccessCheck
+    arguments: ['@plugin.manager.entity', '@database']
+    tags:
+      - { name: access_check }
-- 
GitLab