diff --git a/core/modules/node/lib/Drupal/node/Tests/Views/RevisionLinkTest.php b/core/modules/node/lib/Drupal/node/Tests/Views/RevisionLinkTest.php deleted file mode 100644 index 177d0bc..0000000 --- a/core/modules/node/lib/Drupal/node/Tests/Views/RevisionLinkTest.php +++ /dev/null @@ -1,106 +0,0 @@ - 'Node: Revision Links', - 'description' => 'Tests the different revision link handlers.', - 'group' => 'Views module integration', - ); - } - - /** - * Tests revision links. - */ - public function testRevisionLinks() { - // Create one user which can view/revert and delete and one which can only - // do 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'); - $this->assertResponse(200, 'Test view can be accessed in the path expected'); - - // The first node revision should link to the node directly as you get an - // access denied if you link to the revision. - $path = $this->nodes[0]->getSystemPath(); - $this->assertLinkByHref($path); - $this->assertNoLinkByHref($path . '/revisions/' . $this->revisions[0]->getRevisionId() . '/delete'); - $this->assertNoLinkByHref($path . '/revision/' . $this->revisions[0]->getRevisionId() . '/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. - $path = $this->revisions[1]->getSystemPath(); - $this->assertLinkByHref($path . '/revisions/' . $this->revisions[1]->getRevisionId() . '/view'); - $this->assertLinkByHref($path . '/revisions/' . $this->revisions[1]->getRevisionId() . '/delete'); - $this->assertLinkByHref($path . '/revisions/' . $this->revisions[1]->getRevisionId() . '/revert'); - - $path = $this->revisions[2]->getSystemPath(); - $this->assertLinkByHref($path); - $this->assertNoLinkByHref($path . '/revisions/' . $this->revisions[2]->getRevisionId() . '/delete'); - $this->assertNoLinkByHref($path . '/revisions/' . $this->revisions[2]->getRevisionId() . '/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); - array_shift($operations); - $path = $this->revisions[1]->getSystemPath(); - // Render the view with users which can only 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($path . '/revisions/' . $this->revisions[1]->getRevisionId() . '/' . $operation); - } - else { - $this->assertNoLinkByHref($path . '/revisions/' . $this->revisions[1]->getRevisionId() . '/' . $operation); - } - } - } - } -} diff --git a/core/modules/node/src/Plugin/views/field/Link.php b/core/modules/node/src/Plugin/views/field/Link.php index 397b252..54b51ae 100644 --- a/core/modules/node/src/Plugin/views/field/Link.php +++ b/core/modules/node/src/Plugin/views/field/Link.php @@ -8,7 +8,6 @@ namespace Drupal\node\Plugin\views\field; use Drupal\Core\Form\FormStateInterface; -use Drupal\node\NodeInterface; use Drupal\views\Plugin\views\field\FieldPluginBase; use Drupal\views\ResultRow; @@ -57,7 +56,6 @@ public function buildOptionsForm(&$form, FormStateInterface $form_state) { * {@inheritdoc} */ public function query() { - $this->ensureMyTable(); $this->addAdditionalFields(); } @@ -85,7 +83,7 @@ protected function renderLink($node, ResultRow $values) { if ($node->access('view')) { $this->options['alter']['make_link'] = TRUE; $this->options['alter']['url'] = $node->urlInfo(); - $text = !empty($this->options['text']) ? $this->sanitizeValue($this->options['text']) : $this->t('View'); + $text = !empty($this->options['text']) ? $this->options['text'] : $this->t('View'); return $text; } } diff --git a/core/modules/node/src/Plugin/views/field/RevisionLink.php b/core/modules/node/src/Plugin/views/field/RevisionLink.php index ba8b155..b510e50 100644 --- a/core/modules/node/src/Plugin/views/field/RevisionLink.php +++ b/core/modules/node/src/Plugin/views/field/RevisionLink.php @@ -7,16 +7,12 @@ namespace Drupal\node\Plugin\views\field; -use Drupal\Core\Database\Connection; -use Drupal\Core\Entity\EntityManager; -use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\Core\Session\AccountInterface; use Drupal\Core\Url; use Drupal\node\Plugin\views\field\Link; use Drupal\views\Plugin\views\display\DisplayPluginBase; use Drupal\views\ResultRow; use Drupal\views\ViewExecutable; -use Symfony\Component\DependencyInjection\ContainerInterface; /** * Field handler to present a link to a node revision. @@ -25,98 +21,22 @@ * * @ViewsField("node_revision_link") */ -class RevisionLink extends Link implements ContainerFactoryPluginInterface { +class RevisionLink extends Link { /** - * Database Service Object. - * - * @var \Drupal\Core\Database\Connection - */ - protected $database; - - /** - * The entity manager service. - * - * @var \Drupal\Core\Entity\EntityManager - */ - protected $entityManager; - - /** - * The current active user. - * - * @var \Drupal\Core\Session\AccountInterface - */ - protected $currentUser; - - /** - * An array of revision count of a node keyed by nids. - * - * @var array - */ - protected $countRevisions; - - /** - * Constructs a Drupal\Component\Plugin\PluginBase object. - * - * @param array $configuration - * A configuration array containing information about the plugin instance. - * @param string $plugin_id - * The plugin_id for the plugin instance. - * @param array $plugin_definition - * The plugin implementation definition. - * @param \Drupal\Core\Database\Connection $database - * The database connection. - * @param \Drupal\Core\Entity\EntityManager $entity_manger - * The entity manager service. - * @param \Drupal\Core\Session\AccountInterface $current_user - * The current active user. - */ - public function __construct(array $configuration, $plugin_id, array $plugin_definition, Connection $database, EntityManager $entity_manger, AccountInterface $current_user) { - parent::__construct($configuration, $plugin_id, $plugin_definition); - $this->database = $database; - $this->entityManager = $entity_manger; - $this->currentUser = $current_user; - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { - return new static( - $configuration, - $plugin_id, - $plugin_definition, - $container->get('database'), - $container->get('entity.manager'), - $container->get('current_user') - ); - } - - /** - * {@inheritdoc} + * Overrides Drupal\views\Plugin\views\field\FieldPluginBase::init(). */ public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) { parent::init($view, $display, $options); - $this->additional_fields[] = 'vid'; - $this->additional_fields[] = 'nid'; + $this->additional_fields['node_vid'] = array('table' => 'node_field_revision', 'field' => 'vid'); } /** * {@inheritdoc} */ - public function preRender(&$values) { - parent::preRender($values); - - // Count the amount of revisions per node. - $nids = array(); - foreach ($values as $row) { - $nids[] = $this->getValue($row, 'nid'); - } - - if (!empty($nids)) { - $this->countRevisions = $this->database->query("SELECT nid, COUNT(vid) as count FROM {node_field_revision} WHERE nid IN (:nids) GROUP BY nid", array(':nids' => array_unique($nids)))->fetchAllKeyed(); - } + public function access(AccountInterface $account) { + return $account->hasPermission('view revisions') || $account->hasPermission('administer nodes'); } /** @@ -131,12 +51,14 @@ public function preRender(&$values) { * Returns a string for the link text. */ protected function renderLink($data, ResultRow $values) { - list($node, , $vid) = $this->getRevisionEntity($values, 'view'); - /** @var \Drupal\node\NodeInterface $node */ + list($node, $vid) = $this->get_revision_entity($values, 'view'); + if (!isset($vid)) { + return; + } // Current revision uses the node view path. - if ($this->countRevisions[$node->id()] > 1 && $node->getRevisionId() != $vid) { - $url = Url::fromRoute('node.revision_show', ['node' => $node->id(), 'node_revision' => $vid]); + if (!$node->isDefaultRevision()) { + $url = Url::fromRoute('node.revision_show', ['node' => $node->nid, 'node_revision' => $vid]); } else { $url = $node->urlInfo(); @@ -152,21 +74,25 @@ protected function renderLink($data, ResultRow $values) { /** * Returns the revision values of a node. * - * @param \Drupal\views\ResultRow $values + * @param object $values * An object containing all retrieved values. * @param string $op * The operation being performed. * * @return array - * Containing current node, node revision object and revision ID. + * A numerically indexed array containing the current node object and the + * revision ID for this row. */ - public function getRevisionEntity(ResultRow $values, $op) { - $storage_controller = $this->entityManager->getStorage('node'); - $vid = $this->getValue($values, 'vid'); - $revision_node = $storage_controller->loadRevision($vid); - $node = $storage_controller->load($revision_node->id()); - - return array($node, $revision_node, $vid); + function get_revision_entity($values, $op) { + $vid = $this->getValue($values, 'node_vid'); + $node = $this->getEntity($values); + // Unpublished nodes ignore access control. + $node->setPublished(TRUE); + // Ensure user has access to perform the operation on this node. + if (!$node->access($op)) { + return array($node, NULL); + } + return array($node, $vid); } } diff --git a/core/modules/node/src/Plugin/views/field/RevisionLinkDelete.php b/core/modules/node/src/Plugin/views/field/RevisionLinkDelete.php index b64a4aa..1d5a14b 100644 --- a/core/modules/node/src/Plugin/views/field/RevisionLinkDelete.php +++ b/core/modules/node/src/Plugin/views/field/RevisionLinkDelete.php @@ -7,6 +7,7 @@ namespace Drupal\node\Plugin\views\field; +use Drupal\Core\Session\AccountInterface; use Drupal\Core\Url; use Drupal\node\Plugin\views\field\RevisionLink; use Drupal\views\ResultRow; @@ -23,6 +24,9 @@ class RevisionLinkDelete extends RevisionLink { /** * {@inheritdoc} */ + public function access(AccountInterface $account) { + return $account->hasPermission('delete revisions') || $account->hasPermission('administer nodes'); + } /** * Prepares the link to delete a node revision. @@ -36,16 +40,13 @@ class RevisionLinkDelete extends RevisionLink { * Returns a string for the link text. */ protected function renderLink($data, ResultRow $values) { - list($node, $revision_node, $vid) = $this->getRevisionEntity($values, 'update'); - - /** @var \Drupal\node\NodeInterface $node */ - $type = $node->getType(); - if (!(($this->currentUser->hasPermission("delete $type revisions") || $this->currentUser->hasPermission('delete all revisions') || $this->currentUser->hasPermission('administer nodes')) && $revision_node->access('delete'))) { + list($node, $vid) = $this->get_revision_entity($values, 'delete'); + if (!isset($vid)) { return; } // Current revision cannot be deleted. - if ($node->getRevisionId() == $vid) { + if ($node->isDefaultRevision()) { return; } @@ -53,7 +54,7 @@ protected function renderLink($data, ResultRow $values) { $this->options['alter']['url'] = Url::fromRoute('node.revision_delete_confirm', ['node' => $node->id(), 'node_revision' => $vid]); $this->options['alter']['query'] = drupal_get_destination(); - return !empty($this->options['text']) ? $this->sanitizeValue($this->options['text']) : $this->t('Delete'); + return !empty($this->options['text']) ? $this->options['text'] : $this->t('Delete'); } } diff --git a/core/modules/node/src/Plugin/views/field/RevisionLinkRevert.php b/core/modules/node/src/Plugin/views/field/RevisionLinkRevert.php index 8f21372..17db8a8 100644 --- a/core/modules/node/src/Plugin/views/field/RevisionLinkRevert.php +++ b/core/modules/node/src/Plugin/views/field/RevisionLinkRevert.php @@ -7,6 +7,7 @@ namespace Drupal\node\Plugin\views\field; +use Drupal\Core\Session\AccountInterface; use Drupal\Core\Url; use Drupal\node\Plugin\views\field\RevisionLink; use Drupal\views\ResultRow; @@ -23,6 +24,9 @@ class RevisionLinkRevert extends RevisionLink { /** * {@inheritdoc} */ + public function access(AccountInterface $account) { + return $account->hasPermission('revert revisions') || $account->hasPermission('administer nodes'); + } /** * Prepares the link to revert node to a revision. @@ -36,16 +40,13 @@ class RevisionLinkRevert extends RevisionLink { * Returns a string for the link text. */ protected function renderLink($data, ResultRow $values) { - list($node, , $vid) = $this->getRevisionEntity($values, 'update'); - - /** @var \Drupal\node\NodeInterface $node */ - $type = $node->bundle(); - if (!($access = ($this->currentUser->hasPermission("revert $type revisions") || $this->currentUser->hasPermission('revert all revisions') || $this->currentUser->hasPermission('administer nodes')) && $node->access('update'))) { + list($node, $vid) = $this->get_revision_entity($values, 'update'); + if (!isset($vid)) { return; } // Current revision cannot be reverted. - if ($node->getRevisionId() == $vid) { + if ($node->isDefaultRevision()) { return; } @@ -53,7 +54,7 @@ protected function renderLink($data, ResultRow $values) { $this->options['alter']['url'] = Url::fromRoute('node.revision_revert_confirm', ['node' => $node->id(), 'node_revision' => $vid]); $this->options['alter']['query'] = drupal_get_destination(); - return !empty($this->options['text']) ? $this->sanitizeValue($this->options['text']) : $this->t('Revert'); + return !empty($this->options['text']) ? $this->options['text'] : $this->t('Revert'); } } diff --git a/core/modules/node/src/Tests/Views/RevisionLinkTest.php b/core/modules/node/src/Tests/Views/RevisionLinkTest.php new file mode 100644 index 0000000..37e52ac --- /dev/null +++ b/core/modules/node/src/Tests/Views/RevisionLinkTest.php @@ -0,0 +1,101 @@ +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. + $node = $this->drupalCreateNode(); + $this->nodes[] = $node; + $node = $this->drupalCreateNode(); + $this->nodes[] = $node; + + $first_revision = $node->getRevisionId(); + // Create revision of the node. + $node->setNewRevision(); + $node->save(); + $second_revision = $node->getRevisionId(); + + $this->drupalGet('test-node-revision-links'); + $this->assertResponse(200, 'Test view can be accessed in the path expected'); + // The first node revision should link to the node directly as you get an + // access denied if you link to the revision. + $url = $this->nodes[0]->urlInfo()->toString(); + $this->assertLinkByHref($url); + $this->assertNoLinkByHref($url . '/revisions/' . $this->nodes[0]->getRevisionId() . '/delete'); + $this->assertNoLinkByHref($url . '/revisions/' . $this->nodes[0]->getRevisionId() . '/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. + $url = $this->nodes[1]->urlInfo()->toString(); + $this->assertLinkByHref($url . '/revisions/' . $first_revision . '/view'); + $this->assertLinkByHref($url . '/revisions/' . $first_revision . '/delete'); + $this->assertLinkByHref($url . '/revisions/' . $first_revision . '/revert'); + + $this->assertLinkByHref($url); + $this->assertNoLinkByHref($url . '/revisions/' . $second_revision . '/view'); + $this->assertNoLinkByHref($url . '/revisions/' . $second_revision . '/delete'); + $this->assertNoLinkByHref($url . '/revisions/' . $second_revision . '/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); + array_shift($operations); + $url = $this->nodes[1]->urlInfo()->toString(); + // Render the view with users which can only 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($url . '/revisions/' . $first_revision . '/' . $operation); + } + else { + $this->assertNoLinkByHref($url . '/revisions/' . $first_revision . '/' . $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 index fc5c06e..1313f01 100644 --- 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 @@ -1,4 +1,4 @@ -langcode: und +langcode: en status: true dependencies: module: @@ -16,8 +16,11 @@ display: display_plugin: default id: default display_title: Master - position: 1 + position: 0 display_options: + access: + type: none + options: { } cache: type: none options: { } @@ -26,6 +29,7 @@ display: options: disable_sql_rewrite: false distinct: false + replica: false query_comment: '' query_tags: { } exposed_form: @@ -50,12 +54,12 @@ display: fields: link_to_revision: id: link_to_revision - table: node_revision + table: node_field_revision field: link_to_revision relationship: none group_type: group admin_label: '' - label: 'Link to revision' + label: '' exclude: false alter: alter_text: false @@ -74,7 +78,7 @@ display: suffix: '' target: '' nl2br: false - max_length: 1 + max_length: 0 word_boundary: true ellipsis: true more_link: false @@ -88,7 +92,7 @@ display: element_class: '' element_label_type: '' element_label_class: '' - element_label_colon: true + element_label_colon: false element_wrapper_type: '' element_wrapper_class: '' element_default_classes: true @@ -96,16 +100,17 @@ display: hide_empty: false empty_zero: false hide_alter_empty: true - text: '' + text: 'Link to revision' + entity_type: node plugin_id: node_revision_link delete_revision: id: delete_revision - table: node_revision + table: node_field_revision field: delete_revision relationship: none group_type: group admin_label: '' - label: 'Link to delete revision' + label: '' exclude: false alter: alter_text: false @@ -124,7 +129,7 @@ display: suffix: '' target: '' nl2br: false - max_length: 1 + max_length: 0 word_boundary: true ellipsis: true more_link: false @@ -138,7 +143,7 @@ display: element_class: '' element_label_type: '' element_label_class: '' - element_label_colon: true + element_label_colon: false element_wrapper_type: '' element_wrapper_class: '' element_default_classes: true @@ -146,16 +151,17 @@ display: hide_empty: false empty_zero: false hide_alter_empty: true - text: '' + text: 'Link to delete revision' + entity_type: node plugin_id: node_revision_link_delete revert_revision: id: revert_revision - table: node_revision + table: node_field_revision field: revert_revision relationship: none group_type: group admin_label: '' - label: 'Link to revert revision' + label: '' exclude: false alter: alter_text: false @@ -174,7 +180,7 @@ display: suffix: '' target: '' nl2br: false - max_length: 1 + max_length: 0 word_boundary: true ellipsis: true more_link: false @@ -188,7 +194,7 @@ display: element_class: '' element_label_type: '' element_label_class: '' - element_label_colon: true + element_label_colon: false element_wrapper_type: '' element_wrapper_class: '' element_default_classes: true @@ -196,7 +202,8 @@ display: hide_empty: false empty_zero: false hide_alter_empty: true - text: '' + text: 'Link to delete revision' + entity_type: node plugin_id: node_revision_link_revert filters: { } sorts: { } @@ -207,14 +214,11 @@ display: relationships: { } arguments: { } display_extenders: { } - filter_groups: - operator: AND - groups: { } page_1: display_plugin: page id: page_1 display_title: Page position: 1 display_options: - path: test-node-revision-links display_extenders: { } + path: test-node-revision-links