diff --git a/core/modules/node/lib/Drupal/node/Access/NodeAccessCheck.php b/core/modules/node/lib/Drupal/node/Access/NodeAccessCheck.php new file mode 100644 index 0000000..f68d616 --- /dev/null +++ b/core/modules/node/lib/Drupal/node/Access/NodeAccessCheck.php @@ -0,0 +1,110 @@ +connection = $connection; + } + + /** + * {@inheritdoc} + */ + public function applies(Route $route) { + return array_key_exists('_node_revision_access', $route->getRequirements()); + } + + /** + * {@inheritdoc} + */ + public function access(Route $route, Request $request) { + $account = $GLOBALS['user']; + $op = $route->getRequirement('_node_revision_access'); + $node = $request->attributes->get('node'); + if (empty($node->type)) { + return NULL; + } + + $map = array( + 'view' => 'view all revisions', + 'update' => 'revert all revisions', + 'delete' => 'delete all revisions', + ); + + $type_map = array( + 'view' => "view $node->type revisions", + 'update' => "revert $node->type revisions", + 'delete' => "delete $node->type 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 no language code was provided, default to the node revision's langcode. + if (empty($langcode)) { + $langcode = $node->langcode; + } + + // Statically cache access by revision ID, language code, user account ID, + // and operation. + $cid = $node->vid . ':' . $langcode . ':' . $account->uid . ':' . $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. + $num_revisions = $this->connection->query('SELECT COUNT(vid) FROM {node_field_revision} WHERE nid = :nid', array(':nid' => $node->nid))->fetchField(); + if ($node->isDefaultRevision() && ($num_revisions == 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->nid), $account, $langcode) && ($node->isDefaultRevision() || node_access($op, $node, $account, $langcode)); + } + } + + return $access[$cid]; + } + +} diff --git a/core/modules/node/lib/Drupal/node/Controller/NodeController.php b/core/modules/node/lib/Drupal/node/Controller/NodeController.php new file mode 100644 index 0000000..71d66c9 --- /dev/null +++ b/core/modules/node/lib/Drupal/node/Controller/NodeController.php @@ -0,0 +1,98 @@ +moduleHandler = $module_handler; + $this->storageController = $storage_controller; + $this->renderController = $render_controller; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + $entity_manager = $container->get('plugin.manager.entity'); + return new static( + $container->get('module_handler'), + $entity_manager->getStorageController('node'), + $entity_manager->getRenderController('node') + ); + } + + /** + * Generates an array which displays a node detail page. + * + * @param \Drupal\node\NodeInterface $node + * The node being shown. + * @param int $node_revision + * A node entity's revision ID that we should render. + * + * @return array + * A $page element suitable for use by drupal_render(). + */ + public function nodeShow(NodeInterface $node, $node_revision) { + drupal_set_title(t('Revision of %title from %date', array('%title' => $node->label(), '%date' => format_date($node->revision_timestamp))), PASS_THROUGH); + + $node = $this->storageController->loadRevision($node_revision); + + // For markup consistency with other pages, use node_view_multiple() rather than node_view(). + $nodes = array('nodes' => $this->renderController->viewMultiple(array($node->id() => $node), 'full')); + + // Update the history table, stating that this user viewed this node. + if ($this->moduleHandler->moduleExists('history')) { + history_write($node->id()); + } + + return $nodes; + } + +} diff --git a/core/modules/node/node.module b/core/modules/node/node.module index fbdf07d..f4f9332 100644 --- a/core/modules/node/node.module +++ b/core/modules/node/node.module @@ -1676,10 +1676,7 @@ function node_menu() { ); $items['node/%node/revisions/%node_revision/view'] = array( 'title' => 'Revisions', - 'page callback' => 'node_show', - 'page arguments' => array(3, TRUE), - 'access callback' => '_node_revision_access', - 'access arguments' => array(3), + 'route_name' => 'node_revision_view', ); $items['node/%node/revisions/%node_revision/revert'] = array( 'title' => 'Revert to earlier revision', diff --git a/core/modules/node/node.routing.yml b/core/modules/node/node.routing.yml index ce897f5..30e8225 100644 --- a/core/modules/node/node.routing.yml +++ b/core/modules/node/node.routing.yml @@ -1,3 +1,13 @@ +node_revision_view: + pattern: /node/{node}/revisions/{node_revision}/view + defaults: + _content: '\Drupal\node\Controller\NodeController::nodeShow' + requirements: + _node_revision_access: 'view' + options: + defaults: + node: \d+ + node_revision: \d+ node_multiple_delete_confirm: pattern: '/admin/content/node/delete' defaults: diff --git a/core/modules/node/node.services.yml b/core/modules/node/node.services.yml new file mode 100644 index 0000000..c477fe1 --- /dev/null +++ b/core/modules/node/node.services.yml @@ -0,0 +1,6 @@ +services: + access_check.revisions: + class: Drupal\node\Access\NodeAccessCheck + arguments: ['@database'] + tags: + - { name: access_check }