diff --git a/core/modules/node/lib/Drupal/node/Access/NodeRevisionAccessCheck.php b/core/modules/node/lib/Drupal/node/Access/NodeRevisionAccessCheck.php index aca491d..6e31c31 100644 --- a/core/modules/node/lib/Drupal/node/Access/NodeRevisionAccessCheck.php +++ b/core/modules/node/lib/Drupal/node/Access/NodeRevisionAccessCheck.php @@ -66,7 +66,7 @@ public function __construct(EntityManager $entity_manager, Connection $connectio * {@inheritdoc} */ public function applies(Route $route) { - return array_key_exists('_access_node_revision', $route->getRequirements()); + return array_key_exists('_node_revision_access', $route->getRequirements()); } /** @@ -74,7 +74,7 @@ public function applies(Route $route) { */ 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; + return $this->checkAccess($revision, $route->getRequirement('_node_revision_access'), $revision->language()->id, $request->attributes->get('account')) ? static::ALLOW : static::DENY; } /** @@ -96,7 +96,7 @@ public function access(Route $route, Request $request) { * @return bool * TRUE if the operation may be performed, FALSE otherwise. */ - public function checkAccess(NodeInterface $node, $op = 'view', AccountInterface $account = NULL, $langcode = NULL) { + public function checkAccess(NodeInterface $node, $op = 'view', $langcode = NULL, AccountInterface $account = NULL) { $map = array( 'view' => 'view all revisions', 'update' => 'revert all revisions', @@ -115,6 +115,8 @@ public function checkAccess(NodeInterface $node, $op = 'view', AccountInterface return FALSE; } + // @todo, remove this check and the reference to $GLOBALS['user'] once + // https://drupal.org/node/2040065 is committed. if (!isset($account)) { $account = $GLOBALS['user']; } @@ -148,7 +150,10 @@ public function checkAccess(NodeInterface $node, $op = 'view', AccountInterface 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)); + $accessDefaultRevision = $this->nodeAccess->access($this->nodeStorage->load($node->id()), $op, $langcode, $account); + $isDefaultRevision = $node->isDefaultRevision(); + $accessSpecificRevision = $this->nodeAccess->access($node, $op, $langcode, $account); + $this->access[$cid] = $accessDefaultRevision && ($isDefaultRevision || $accessSpecificRevision); } } 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..465975d --- /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 nodeRevisionView(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/lib/Drupal/node/Tests/NodeRevisionPermissionsTest.php b/core/modules/node/lib/Drupal/node/Tests/NodeRevisionPermissionsTest.php index 3bcf362..368bdca 100644 --- a/core/modules/node/lib/Drupal/node/Tests/NodeRevisionPermissionsTest.php +++ b/core/modules/node/lib/Drupal/node/Tests/NodeRevisionPermissionsTest.php @@ -7,6 +7,8 @@ namespace Drupal\node\Tests; +use Drupal\Core\Language\Language; + /** * Tests user permissions for node revisions. */ @@ -100,10 +102,10 @@ function testNodeRevisionAccessAnyType() { // Skip this test if there are no revisions for the node. if (!($revision->isDefaultRevision() && (db_query('SELECT COUNT(vid) FROM {node_field_revision} WHERE nid = :nid', array(':nid' => $revision->id()))->fetchField() == 1 || $case['op'] == 'update' || $case['op'] == 'delete'))) { if (!empty($case['account']->is_admin) || user_access($this->map[$case['op']], $case['account'])) { - $this->assertTrue(_node_revision_access($revision, $case['op'], $case['account']), "{$this->map[$case['op']]} granted."); + $this->assertTrue(_node_revision_access($revision, $case['op'], $revision->language()->id, $case['account']), "{$this->map[$case['op']]} granted."); } else { - $this->assertFalse(_node_revision_access($revision, $case['op'], $case['account']), "{$this->map[$case['op']]} not granted."); + $this->assertFalse(_node_revision_access($revision, $case['op'], $revision->language()->id, $case['account']), "{$this->map[$case['op']]} not granted."); } } } @@ -146,10 +148,10 @@ function testNodeRevisionAccessPerType() { // Skip this test if there are no revisions for the node. if (!($revision->isDefaultRevision() && (db_query('SELECT COUNT(vid) FROM {node_field_revision} WHERE nid = :nid', array(':nid' => $revision->id()))->fetchField() == 1 || $case['op'] == 'update' || $case['op'] == 'delete'))) { if (!empty($case['account']->is_admin) || user_access($this->type_map[$case['op']], $case['account'])) { - $this->assertTrue(_node_revision_access($revision, $case['op'], $case['account']), "{$this->type_map[$case['op']]} granted."); + $this->assertTrue(_node_revision_access($revision, $case['op'], $revision->language()->id, $case['account']), "{$this->type_map[$case['op']]} granted."); } else { - $this->assertFalse(_node_revision_access($revision, $case['op'], $case['account']), "{$this->type_map[$case['op']]} not granted."); + $this->assertFalse(_node_revision_access($revision, $case['op'], $revision->language()->id, $case['account']), "{$this->type_map[$case['op']]} not granted."); } } } @@ -158,7 +160,7 @@ function testNodeRevisionAccessPerType() { $revision = $this->node_revisions['article'][1]; foreach ($permutations as $case) { - $this->assertFalse(_node_revision_access($revision, $case['op'], $case['account']), "{$this->type_map[$case['op']]} did not grant revision permission for articles."); + $this->assertFalse(_node_revision_access($revision, $case['op'], $revision->language()->id, $case['account']), "{$this->type_map[$case['op']]} did not grant revision permission for articles."); } } } diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeRevisionsAllTestCase.php b/core/modules/node/lib/Drupal/node/Tests/NodeRevisionsAllTestCase.php index 3906c94..9fb4157 100644 --- a/core/modules/node/lib/Drupal/node/Tests/NodeRevisionsAllTestCase.php +++ b/core/modules/node/lib/Drupal/node/Tests/NodeRevisionsAllTestCase.php @@ -15,7 +15,6 @@ class NodeRevisionsAllTestCase extends NodeTestBase { protected $nodes; protected $logs; - protected $profile = "standard"; public static function getInfo() { return array( diff --git a/core/modules/node/node.module b/core/modules/node/node.module index 5a6d53a..27bbdec 100644 --- a/core/modules/node/node.module +++ b/core/modules/node/node.module @@ -1244,10 +1244,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 dfdc0a6..0e72b55 100644 --- a/core/modules/node/node.routing.yml +++ b/core/modules/node/node.routing.yml @@ -19,19 +19,30 @@ node_delete_confirm: requirements: _entity_access: 'node.delete' +node_revision_view: + pattern: /node/{node}/revisions/{node_revision}/view + defaults: + _content: '\Drupal\node\Controller\NodeController::nodeRevisionView' + requirements: + _node_revision_access: 'view' + options: + defaults: + node: \d+ + node_revision: \d+ + node_revision_revert_confirm: pattern: '/node/{node}/revisions/{node_revision}/revert' defaults: _form: '\Drupal\node\Form\NodeRevisionRevertForm' requirements: - _access_node_revision: 'update' + _node_revision_access: 'update' node_revision_delete_confirm: pattern: '/node/{node}/revisions/{node_revision}/delete' defaults: _form: '\Drupal\node\Form\NodeRevisionDeleteForm' requirements: - _access_node_revision: 'delete' + _node_revision_access: 'delete' node_overview_types: pattern: '/admin/structure/types'