diff --git a/core/modules/node/lib/Drupal/node/Plugin/views/field/Link.php b/core/modules/node/lib/Drupal/node/Plugin/views/field/Link.php index 266bedf..1258a39 100644 --- a/core/modules/node/lib/Drupal/node/Plugin/views/field/Link.php +++ b/core/modules/node/lib/Drupal/node/Plugin/views/field/Link.php @@ -42,6 +42,7 @@ public function buildOptionsForm(&$form, &$form_state) { * {@inheritdoc} */ public function query() { + $this->ensureMyTable(); $this->add_additional_fields(); } diff --git a/core/modules/node/lib/Drupal/node/Plugin/views/field/RevisionLink.php b/core/modules/node/lib/Drupal/node/Plugin/views/field/RevisionLink.php index 1f4bba2..07e1acd 100644 --- a/core/modules/node/lib/Drupal/node/Plugin/views/field/RevisionLink.php +++ b/core/modules/node/lib/Drupal/node/Plugin/views/field/RevisionLink.php @@ -20,6 +20,7 @@ * @PluginID("node_revision_link") */ class RevisionLink extends Link { + public $countRevisions; /** * Overrides Drupal\views\Plugin\views\field\FieldPluginBase::init(). @@ -27,22 +28,38 @@ class RevisionLink extends Link { public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) { parent::init($view, $display, $options); - $this->additional_fields['node_vid'] = array('table' => 'node_field_revision', 'field' => 'vid'); + $this->additional_fields[] = 'vid'; + $this->additional_fields[] = 'nid'; } - public function access() { - return user_access('view revisions') || user_access('administer nodes'); + /** + * Overrides \Drupal\views\Plugin\views\field::pre_render(). + */ + function pre_render(&$values) { + parent::pre_render($values); + + // Count the amount of revisions per node. + $nids = array(); + foreach ($values as $row) { + $nids[] = $this->get_value($row, 'nid'); + } + + if ($nids) { + $this->countRevisions = db_query("SELECT nid, COUNT(vid) as count FROM {node_field_revision} WHERE nid IN (:nids) GROUP BY nid", array(':nids' => array_unique($nids)))->fetchAllKeyed(); + } } function render_link($data, $values) { - list($node, $vid) = $this->get_revision_entity($values, 'view'); + list($node, $revision_node, $vid) = $this->get_revision_entity($values, 'view'); if (!isset($vid)) { return; } + // Current revision uses the node view path. - $path = 'node/' . $node->nid; - if (!$node->isDefaultRevision()) { + $uri = $node->uri(); + $path = $uri['path']; + if ($this->countRevisions[$node->id()] > 1 && $node->vid != $vid) { $path .= "/revisions/$vid/view"; } @@ -56,25 +73,21 @@ function render_link($data, $values) { /** * Returns the revision values of a node. * - * @param object $values + * @param \stdClass $values * An object containing all retrieved values. * @param string $op * The operation being performed. * * @return array - * A numerically indexed array containing the current node object and the - * revision ID for this row. + * A numerically indexed array containing the current node object the + * revision node object and the revision ID for this row. */ - function get_revision_entity($values, $op) { - $vid = $this->get_value($values, 'node_vid'); - $node = $this->get_entity($values); - // Unpublished nodes ignore access control. - $node->status = 1; - // Ensure user has access to perform the operation on this node. - if (!node_access($op, $node)) { - return array($node, NULL); - } - return array($node, $vid); + function get_revision_entity(\stdClass $values, $op) { + $vid = $this->get_value($values, 'vid'); + $revision_node = node_revision_load($vid); + $node = node_load($revision_node->id()); + + return array($node, $revision_node, $vid); } } diff --git a/core/modules/node/lib/Drupal/node/Plugin/views/field/RevisionLinkDelete.php b/core/modules/node/lib/Drupal/node/Plugin/views/field/RevisionLinkDelete.php index 2ffb49c..009ff2f 100644 --- a/core/modules/node/lib/Drupal/node/Plugin/views/field/RevisionLinkDelete.php +++ b/core/modules/node/lib/Drupal/node/Plugin/views/field/RevisionLinkDelete.php @@ -19,13 +19,11 @@ */ class RevisionLinkDelete extends RevisionLink { - public function access() { - return user_access('delete revisions') || user_access('administer nodes'); - } - function render_link($data, $values) { - list($node, $vid) = $this->get_revision_entity($values, 'delete'); - if (!isset($vid)) { + list($node, $revision_node, $vid) = $this->get_revision_entity($values, 'update'); + + $type = $node->bundle(); + if (!($access = (user_access("delete $type revisions") || user_access('delete all revisions') || user_access('administer nodes')) && node_access('delete', $revision_node))) { return; } diff --git a/core/modules/node/lib/Drupal/node/Plugin/views/field/RevisionLinkRevert.php b/core/modules/node/lib/Drupal/node/Plugin/views/field/RevisionLinkRevert.php index 8f9671c..52e1e8f 100644 --- a/core/modules/node/lib/Drupal/node/Plugin/views/field/RevisionLinkRevert.php +++ b/core/modules/node/lib/Drupal/node/Plugin/views/field/RevisionLinkRevert.php @@ -19,13 +19,11 @@ */ class RevisionLinkRevert extends RevisionLink { - public function access() { - return user_access('revert revisions') || user_access('administer nodes'); - } - function render_link($data, $values) { - list($node, $vid) = $this->get_revision_entity($values, 'update'); - if (!isset($vid)) { + list($node, $revision_node, $vid) = $this->get_revision_entity($values, 'update'); + + $type = $node->bundle(); + if (!($access = (user_access("revert $type revisions") || user_access('revert all revisions') || user_access('administer nodes')) && node_access('update', $node))) { return; } diff --git a/core/modules/node/lib/Drupal/node/Tests/Views/RevisionLinkTest.php b/core/modules/node/lib/Drupal/node/Tests/Views/RevisionLinkTest.php new file mode 100644 index 0000000..033a700 --- /dev/null +++ b/core/modules/node/lib/Drupal/node/Tests/Views/RevisionLinkTest.php @@ -0,0 +1,104 @@ + 'Node Revision: Links', + 'description' => 'Tests the different revision link handlers.', + 'group' => 'Views Modules', + ); + } + + /** + * Tests revision links. + */ + public function testRevisionLinks() { + // Create one user which can view/revert and delete and one which can only + // one of them. + $this->drupalCreateContentType(array('name' => 'page', 'type' => 'page')); + $account = $this->drupalCreateUser(array('revert all revisions', 'view all revisions', 'delete all revisions', 'edit any page content', 'delete any page content')); + $this->drupalLogin($account); + + // Create two nodes, one without an additional revision and one with a + // revision. + $this->nodes[] = $node = $this->drupalCreateNode(); + $this->revisions[] = $node; + + $this->nodes[] = $node = $this->drupalCreateNode(); + $this->revisions[] = $node; + $node = clone $node; + $node->setNewRevision(); + $this->revisions[] = $node; + $node->save(); + + $this->drupalGet('test-node-revision-links'); + + // The first node revision should link to the node directly as you get a + // access denied if you link to the revision. + $uri = $this->nodes[0]->uri(); + $this->assertLinkByHref($uri['path']); + $this->assertNoLinkByHref($uri['path'] . '/revision/' . $this->revisions[0]->vid . '/delete'); + $this->assertNoLinkByHref($uri['path'] . '/revision/' . $this->revisions[0]->vid . '/revert'); + + // For the second node the current revision got set to the last revision, so + // the first one should also link to the node page itself. + $uri = $this->revisions[1]->uri(); + $this->assertLinkByHref($uri['path'] . '/revision/' . $this->revisions[2]->vid . '/view'); + $this->assertLinkByHref($uri['path'] . '/revision/' . $this->revisions[1]->vid . '/delete'); + $this->assertLinkByHref($uri['path'] . '/revision/' . $this->revisions[1]->vid . '/revert'); + + $uri = $this->revisions[2]->uri(); + $this->assertLinkByHref($uri['path']); + $this->assertNoLinkByHref($uri['path'] . '/revision/' . $this->revisions[2]->vid . '/delete'); + $this->assertNoLinkByHref($uri['path'] . '/revision/' . $this->revisions[2]->vid . '/revert'); + + $accounts = array(); + $accounts['view'] = $this->drupalCreateUser(array('view all revisions')); + $accounts['revert'] = $this->drupalCreateUser(array('revert all revisions', 'edit any page content')); + $accounts['delete'] = $this->drupalCreateUser(array('delete all revisions', 'delete any page content')); + + $operations = array_keys($accounts); + + // Render the view with users which can only view/delete/revert revisions. + foreach ($accounts as $op => $account) { + $this->drupalLogin($account); + $this->drupalGet('test-node-revision-links'); + + // Check expected links. + foreach ($operations as $operation) { + if ($operation == $op) { + $this->assertLinkByHref($uri['path'] . '/revision/' . $this->revisions[2]->vid . '/' . $operation); + } + else { + $this->assertNoLinkByHref($uri['path'] . '/revision/' . $this->revisions[2]->vid . '/' . $operation); + } + } + } + } +} diff --git a/core/modules/node/tests/modules/node_test_views/test_views/views.view.test_node_revision_links.yml b/core/modules/node/tests/modules/node_test_views/test_views/views.view.test_node_revision_links.yml new file mode 100644 index 0000000..eff51d1 --- /dev/null +++ b/core/modules/node/tests/modules/node_test_views/test_views/views.view.test_node_revision_links.yml @@ -0,0 +1,63 @@ +api_version: '3.0' +base_field: vid +base_table: node_revision +core: 8.x +description: '' +disabled: '0' +display: + default: + display_plugin: default + id: default + display_title: Master + position: '' + display_options: + access: + type: perm + perm: 'view revisions' + cache: + type: none + query: + type: views_query + exposed_form: + type: basic + pager: + type: full + style: + type: default + row: + type: fields + fields: + link_to_revision: + id: link_to_revision + table: node_revision + field: link_to_revision + delete_revision: + id: delete_revision + table: node_revision + field: delete_revision + revert_revision: + id: revert_revision + table: node_revision + field: revert_revision + filters: + status: + value: '1' + table: node_revision + field: status + id: status + expose: + operator: '0' + group: '1' + sorts: { } + page_1: + display_plugin: page + id: page_1 + display_title: Page + position: '' + display_options: + path: test-node-revision-links +human_name: test_node_revision_links +module: views +name: test_node_revision_links +tag: '' +uuid: b1ab4547-dcf3-49c1-b900-d270ace37c53