summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNathaniel Catchpole2017-08-25 03:51:01 (GMT)
committerNathaniel Catchpole2017-08-25 03:51:01 (GMT)
commit30a133b0f4c16eb1db13238e8e6c572e09791f6a (patch)
tree0dbc682389f7196f1d369f284201e93f009e9926
parent1a71b1a949c26ed1cb2e7ac1ba7815b9be4ee99d (diff)
Issue #2865579 by amateescu, Sam152: Rewrite the 'Latest revision' views filter and remove the revision_tracker table
-rw-r--r--core/modules/content_moderation/config/schema/content_moderation.schema.yml8
-rw-r--r--core/modules/content_moderation/content_moderation.install16
-rw-r--r--core/modules/content_moderation/content_moderation.services.yml5
-rw-r--r--core/modules/content_moderation/content_moderation.views.inc7
-rw-r--r--core/modules/content_moderation/src/EntityOperations.php31
-rw-r--r--core/modules/content_moderation/src/RevisionTracker.php154
-rw-r--r--core/modules/content_moderation/src/RevisionTrackerInterface.php30
-rw-r--r--core/modules/content_moderation/src/ViewsData.php161
-rw-r--r--core/modules/content_moderation/tests/modules/content_moderation_test_views/config/install/views.view.test_content_moderation_latest_revision.yml447
-rw-r--r--core/modules/content_moderation/tests/src/Functional/LatestRevisionViewsFilterTest.php130
-rw-r--r--core/modules/content_moderation/tests/src/Kernel/ViewsDataIntegrationTest.php49
-rw-r--r--core/modules/views/config/schema/views.filter.schema.yml4
-rw-r--r--core/modules/views/src/EntityViewsData.php7
-rw-r--r--core/modules/views/src/Plugin/views/filter/LatestRevision.php (renamed from core/modules/content_moderation/src/Plugin/views/filter/LatestRevision.php)51
-rw-r--r--core/modules/views/tests/modules/views_test_config/test_views/views.view.test_latest_revision_filter.yml163
-rw-r--r--core/modules/views/tests/src/Functional/Entity/LatestRevisionFilterTest.php160
16 files changed, 366 insertions, 1057 deletions
diff --git a/core/modules/content_moderation/config/schema/content_moderation.schema.yml b/core/modules/content_moderation/config/schema/content_moderation.schema.yml
index 5a10d85..d48ffe4 100644
--- a/core/modules/content_moderation/config/schema/content_moderation.schema.yml
+++ b/core/modules/content_moderation/config/schema/content_moderation.schema.yml
@@ -1,11 +1,3 @@
-views.filter.latest_revision:
- type: views_filter
- label: 'Latest revision'
- mapping:
- value:
- type: string
- label: 'Value'
-
content_moderation.state:
type: workflows.state
mapping:
diff --git a/core/modules/content_moderation/content_moderation.install b/core/modules/content_moderation/content_moderation.install
new file mode 100644
index 0000000..e33a359
--- /dev/null
+++ b/core/modules/content_moderation/content_moderation.install
@@ -0,0 +1,16 @@
+<?php
+
+/**
+ * @file
+ * Install, update and uninstall functions for the Content Moderation module.
+ */
+
+/**
+ * Remove the 'content_revision_tracker' table.
+ */
+function content_moderation_update_8401() {
+ $database_schema = \Drupal::database()->schema();
+ if ($database_schema->tableExists('content_revision_tracker')) {
+ $database_schema->dropTable('content_revision_tracker');
+ }
+}
diff --git a/core/modules/content_moderation/content_moderation.services.yml b/core/modules/content_moderation/content_moderation.services.yml
index 256095a..5035b67 100644
--- a/core/modules/content_moderation/content_moderation.services.yml
+++ b/core/modules/content_moderation/content_moderation.services.yml
@@ -15,11 +15,6 @@ services:
arguments: ['@content_moderation.moderation_information']
tags:
- { name: access_check, applies_to: _content_moderation_latest_version }
- content_moderation.revision_tracker:
- class: Drupal\content_moderation\RevisionTracker
- arguments: ['@database']
- tags:
- - { name: backend_overridable }
content_moderation.config_import_subscriber:
class: Drupal\content_moderation\EventSubscriber\ConfigImportSubscriber
arguments: ['@config.manager', '@entity_type.manager']
diff --git a/core/modules/content_moderation/content_moderation.views.inc b/core/modules/content_moderation/content_moderation.views.inc
index faabc6a..799af94 100644
--- a/core/modules/content_moderation/content_moderation.views.inc
+++ b/core/modules/content_moderation/content_moderation.views.inc
@@ -17,13 +17,6 @@ function content_moderation_views_data() {
}
/**
- * Implements hook_views_data_alter().
- */
-function content_moderation_views_data_alter(array &$data) {
- _content_moderation_views_data_object()->alterViewsData($data);
-}
-
-/**
* Creates a ViewsData object to respond to views hooks.
*
* @return \Drupal\content_moderation\ViewsData
diff --git a/core/modules/content_moderation/src/EntityOperations.php b/core/modules/content_moderation/src/EntityOperations.php
index 580c21e..a7fcf7e 100644
--- a/core/modules/content_moderation/src/EntityOperations.php
+++ b/core/modules/content_moderation/src/EntityOperations.php
@@ -42,13 +42,6 @@ class EntityOperations implements ContainerInjectionInterface {
protected $formBuilder;
/**
- * The Revision Tracker service.
- *
- * @var \Drupal\content_moderation\RevisionTrackerInterface
- */
- protected $tracker;
-
- /**
* The entity bundle information service.
*
* @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface
@@ -64,16 +57,13 @@ class EntityOperations implements ContainerInjectionInterface {
* Entity type manager service.
* @param \Drupal\Core\Form\FormBuilderInterface $form_builder
* The form builder.
- * @param \Drupal\content_moderation\RevisionTrackerInterface $tracker
- * The revision tracker.
* @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $bundle_info
* The entity bundle information service.
*/
- public function __construct(ModerationInformationInterface $moderation_info, EntityTypeManagerInterface $entity_type_manager, FormBuilderInterface $form_builder, RevisionTrackerInterface $tracker, EntityTypeBundleInfoInterface $bundle_info) {
+ public function __construct(ModerationInformationInterface $moderation_info, EntityTypeManagerInterface $entity_type_manager, FormBuilderInterface $form_builder, EntityTypeBundleInfoInterface $bundle_info) {
$this->moderationInfo = $moderation_info;
$this->entityTypeManager = $entity_type_manager;
$this->formBuilder = $form_builder;
- $this->tracker = $tracker;
$this->bundleInfo = $bundle_info;
}
@@ -85,7 +75,6 @@ class EntityOperations implements ContainerInjectionInterface {
$container->get('content_moderation.moderation_information'),
$container->get('entity_type.manager'),
$container->get('form_builder'),
- $container->get('content_moderation.revision_tracker'),
$container->get('entity_type.bundle.info')
);
}
@@ -132,7 +121,6 @@ class EntityOperations implements ContainerInjectionInterface {
public function entityInsert(EntityInterface $entity) {
if ($this->moderationInfo->isModeratedEntity($entity)) {
$this->updateOrCreateFromEntity($entity);
- $this->setLatestRevision($entity);
}
}
@@ -145,7 +133,6 @@ class EntityOperations implements ContainerInjectionInterface {
public function entityUpdate(EntityInterface $entity) {
if ($this->moderationInfo->isModeratedEntity($entity)) {
$this->updateOrCreateFromEntity($entity);
- $this->setLatestRevision($entity);
}
}
@@ -203,22 +190,6 @@ class EntityOperations implements ContainerInjectionInterface {
}
/**
- * Set the latest revision.
- *
- * @param \Drupal\Core\Entity\EntityInterface $entity
- * The content entity to create content_moderation_state entity for.
- */
- protected function setLatestRevision(EntityInterface $entity) {
- /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
- $this->tracker->setLatestRevision(
- $entity->getEntityTypeId(),
- $entity->id(),
- $entity->language()->getId(),
- $entity->getRevisionId()
- );
- }
-
- /**
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity being deleted.
*
diff --git a/core/modules/content_moderation/src/RevisionTracker.php b/core/modules/content_moderation/src/RevisionTracker.php
deleted file mode 100644
index 3f82fdd..0000000
--- a/core/modules/content_moderation/src/RevisionTracker.php
+++ /dev/null
@@ -1,154 +0,0 @@
-<?php
-
-namespace Drupal\content_moderation;
-
-use Drupal\Core\Database\Connection;
-use Drupal\Core\Database\DatabaseExceptionWrapper;
-use Drupal\Core\Database\SchemaObjectExistsException;
-
-/**
- * Tracks metadata about revisions across entities.
- *
- * @internal
- */
-class RevisionTracker implements RevisionTrackerInterface {
-
- /**
- * The name of the SQL table we use for tracking.
- *
- * @var string
- */
- protected $tableName;
-
- /**
- * The database connection.
- *
- * @var \Drupal\Core\Database\Connection
- */
- protected $connection;
-
- /**
- * Constructs a new RevisionTracker.
- *
- * @param \Drupal\Core\Database\Connection $connection
- * The database connection.
- * @param string $table
- * The table that should be used for tracking.
- */
- public function __construct(Connection $connection, $table = 'content_revision_tracker') {
- $this->connection = $connection;
- $this->tableName = $table;
- }
-
- /**
- * {@inheritdoc}
- */
- public function setLatestRevision($entity_type_id, $entity_id, $langcode, $revision_id) {
- try {
- $this->recordLatestRevision($entity_type_id, $entity_id, $langcode, $revision_id);
- }
- catch (DatabaseExceptionWrapper $e) {
- $this->ensureTableExists();
- $this->recordLatestRevision($entity_type_id, $entity_id, $langcode, $revision_id);
- }
-
- return $this;
- }
-
- /**
- * Records the latest revision of a given entity.
- *
- * @param string $entity_type_id
- * The machine name of the type of entity.
- * @param string $entity_id
- * The Entity ID in question.
- * @param string $langcode
- * The langcode of the revision we're saving. Each language has its own
- * effective tree of entity revisions, so in different languages
- * different revisions will be "latest".
- * @param int $revision_id
- * The revision ID that is now the latest revision.
- *
- * @return int
- * One of the valid returns from a merge query's execute method.
- */
- protected function recordLatestRevision($entity_type_id, $entity_id, $langcode, $revision_id) {
- return $this->connection->merge($this->tableName)
- ->keys([
- 'entity_type' => $entity_type_id,
- 'entity_id' => $entity_id,
- 'langcode' => $langcode,
- ])
- ->fields([
- 'revision_id' => $revision_id,
- ])
- ->execute();
- }
-
- /**
- * Checks if the table exists and create it if not.
- *
- * @return bool
- * TRUE if the table was created, FALSE otherwise.
- */
- protected function ensureTableExists() {
- try {
- if (!$this->connection->schema()->tableExists($this->tableName)) {
- $this->connection->schema()->createTable($this->tableName, $this->schemaDefinition());
- return TRUE;
- }
- }
- catch (SchemaObjectExistsException $e) {
- // If another process has already created the table, attempting to
- // recreate it will throw an exception. In this case just catch the
- // exception and do nothing.
- return TRUE;
- }
- return FALSE;
- }
-
- /**
- * Defines the schema for the tracker table.
- *
- * @return array
- * The schema API definition for the SQL storage table.
- */
- protected function schemaDefinition() {
- $schema = [
- 'description' => 'Tracks the latest revision for any entity',
- 'fields' => [
- 'entity_type' => [
- 'description' => 'The entity type',
- 'type' => 'varchar_ascii',
- 'length' => 255,
- 'not null' => TRUE,
- 'default' => '',
- ],
- 'entity_id' => [
- 'description' => 'The entity ID',
- 'type' => 'int',
- 'length' => 255,
- 'not null' => TRUE,
- 'default' => 0,
- ],
- 'langcode' => [
- 'description' => 'The language of the entity revision',
- 'type' => 'varchar',
- 'length' => 12,
- 'not null' => TRUE,
- 'default' => '',
- ],
- 'revision_id' => [
- 'description' => 'The latest revision ID for this entity',
- 'type' => 'int',
- 'not null' => TRUE,
- 'default' => 0,
- ],
- ],
- 'primary key' => ['entity_type', 'entity_id', 'langcode'],
- ];
-
- return $schema;
- }
-
-}
diff --git a/core/modules/content_moderation/src/RevisionTrackerInterface.php b/core/modules/content_moderation/src/RevisionTrackerInterface.php
deleted file mode 100644
index 5079151..0000000
--- a/core/modules/content_moderation/src/RevisionTrackerInterface.php
+++ /dev/null
@@ -1,30 +0,0 @@
-<?php
-
-namespace Drupal\content_moderation;
-
-/**
- * Tracks metadata about revisions across content entities.
- *
- * @internal
- */
-interface RevisionTrackerInterface {
-
- /**
- * Sets the latest revision of a given entity.
- *
- * @param string $entity_type_id
- * The machine name of the type of entity.
- * @param string $entity_id
- * The Entity ID in question.
- * @param string $langcode
- * The langcode of the revision we're saving. Each language has its own
- * effective tree of entity revisions, so in different languages
- * different revisions will be "latest".
- * @param int $revision_id
- * The revision ID that is now the latest revision.
- *
- * @return static
- */
- public function setLatestRevision($entity_type_id, $entity_id, $langcode, $revision_id);
-
-}
diff --git a/core/modules/content_moderation/src/ViewsData.php b/core/modules/content_moderation/src/ViewsData.php
index 189562d..57d6d76 100644
--- a/core/modules/content_moderation/src/ViewsData.php
+++ b/core/modules/content_moderation/src/ViewsData.php
@@ -51,137 +51,13 @@ class ViewsData {
public function getViewsData() {
$data = [];
- $data['content_revision_tracker']['table']['group'] = $this->t('Content moderation (tracker)');
-
- $data['content_revision_tracker']['entity_type'] = [
- 'title' => $this->t('Entity type'),
- 'field' => [
- 'id' => 'standard',
- ],
- 'filter' => [
- 'id' => 'string',
- ],
- 'argument' => [
- 'id' => 'string',
- ],
- 'sort' => [
- 'id' => 'standard',
- ],
- ];
-
- $data['content_revision_tracker']['entity_id'] = [
- 'title' => $this->t('Entity ID'),
- 'field' => [
- 'id' => 'standard',
- ],
- 'filter' => [
- 'id' => 'numeric',
- ],
- 'argument' => [
- 'id' => 'numeric',
- ],
- 'sort' => [
- 'id' => 'standard',
- ],
- ];
-
- $data['content_revision_tracker']['langcode'] = [
- 'title' => $this->t('Entity language'),
- 'field' => [
- 'id' => 'standard',
- ],
- 'filter' => [
- 'id' => 'language',
- ],
- 'argument' => [
- 'id' => 'language',
- ],
- 'sort' => [
- 'id' => 'standard',
- ],
- ];
-
- $data['content_revision_tracker']['revision_id'] = [
- 'title' => $this->t('Latest revision ID'),
- 'field' => [
- 'id' => 'standard',
- ],
- 'filter' => [
- 'id' => 'numeric',
- ],
- 'argument' => [
- 'id' => 'numeric',
- ],
- 'sort' => [
- 'id' => 'standard',
- ],
- ];
-
$entity_types_with_moderation = array_filter($this->entityTypeManager->getDefinitions(), function (EntityTypeInterface $type) {
return $this->moderationInformation->canModerateEntitiesOfEntityType($type);
});
- // Add a join for each entity type to the content_revision_tracker table.
- foreach ($entity_types_with_moderation as $entity_type_id => $entity_type) {
- /** @var \Drupal\views\EntityViewsDataInterface $views_data */
- // We need the views_data handler in order to get the table name later.
- if ($this->entityTypeManager->hasHandler($entity_type_id, 'views_data') && $views_data = $this->entityTypeManager->getHandler($entity_type_id, 'views_data')) {
- // Add a join from the entity base table to the revision tracker table.
- $base_table = $views_data->getViewsTableForEntityType($entity_type);
- $data['content_revision_tracker']['table']['join'][$base_table] = [
- 'left_field' => $entity_type->getKey('id'),
- 'field' => 'entity_id',
- 'extra' => [
- [
- 'field' => 'entity_type',
- 'value' => $entity_type_id,
- ],
- ],
- ];
-
- // Some entity types might not be translatable.
- if ($entity_type->hasKey('langcode')) {
- $data['content_revision_tracker']['table']['join'][$base_table]['extra'][] = [
- 'field' => 'langcode',
- 'left_field' => $entity_type->getKey('langcode'),
- 'operation' => '=',
- ];
- }
-
- // Add a relationship between the revision tracker table to the latest
- // revision on the entity revision table.
- $data['content_revision_tracker']['latest_revision__' . $entity_type_id] = [
- 'title' => $this->t('@label latest revision', ['@label' => $entity_type->getLabel()]),
- 'group' => $this->t('@label revision', ['@label' => $entity_type->getLabel()]),
- 'relationship' => [
- 'id' => 'standard',
- 'label' => $this->t('@label latest revision', ['@label' => $entity_type->getLabel()]),
- 'base' => $this->getRevisionViewsTableForEntityType($entity_type),
- 'base field' => $entity_type->getKey('revision'),
- 'relationship field' => 'revision_id',
- 'extra' => [
- [
- 'left_field' => 'entity_type',
- 'value' => $entity_type_id,
- ],
- ],
- ],
- ];
-
- // Some entity types might not be translatable.
- if ($entity_type->hasKey('langcode')) {
- $data['content_revision_tracker']['latest_revision__' . $entity_type_id]['relationship']['extra'][] = [
- 'left_field' => 'langcode',
- 'field' => $entity_type->getKey('langcode'),
- 'operation' => '=',
- ];
- }
- }
- }
-
// Provides a relationship from moderated entity to its moderation state
// entity.
- $content_moderation_state_entity_type = \Drupal::entityTypeManager()->getDefinition('content_moderation_state');
+ $content_moderation_state_entity_type = $this->entityTypeManager->getDefinition('content_moderation_state');
$content_moderation_state_entity_base_table = $content_moderation_state_entity_type->getDataTable() ?: $content_moderation_state_entity_type->getBaseTable();
$content_moderation_state_entity_revision_base_table = $content_moderation_state_entity_type->getRevisionDataTable() ?: $content_moderation_state_entity_type->getRevisionTable();
foreach ($entity_types_with_moderation as $entity_type_id => $entity_type) {
@@ -228,39 +104,4 @@ class ViewsData {
return $data;
}
- /**
- * Alters the table and field information from hook_views_data().
- *
- * @param array $data
- * An array of all information about Views tables and fields, collected from
- * hook_views_data(), passed by reference.
- *
- * @see hook_views_data()
- */
- public function alterViewsData(array &$data) {
- $entity_types_with_moderation = array_filter($this->entityTypeManager->getDefinitions(), function (EntityTypeInterface $type) {
- return $this->moderationInformation->canModerateEntitiesOfEntityType($type);
- });
- foreach ($entity_types_with_moderation as $type) {
- $data[$type->getRevisionTable()]['latest_revision'] = [
- 'title' => t('Is Latest Revision'),
- 'help' => t('Restrict the view to only revisions that are the latest revision of their entity.'),
- 'filter' => ['id' => 'latest_revision'],
- ];
- }
- }
-
- /**
- * Gets the table of an entity type to be used as revision table in views.
- *
- * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
- * The entity type.
- *
- * @return string
- * The revision base table.
- */
- protected function getRevisionViewsTableForEntityType(EntityTypeInterface $entity_type) {
- return $entity_type->getRevisionDataTable() ?: $entity_type->getRevisionTable();
- }
-
}
diff --git a/core/modules/content_moderation/tests/modules/content_moderation_test_views/config/install/views.view.test_content_moderation_latest_revision.yml b/core/modules/content_moderation/tests/modules/content_moderation_test_views/config/install/views.view.test_content_moderation_latest_revision.yml
deleted file mode 100644
index 4727efa..0000000
--- a/core/modules/content_moderation/tests/modules/content_moderation_test_views/config/install/views.view.test_content_moderation_latest_revision.yml
+++ /dev/null
@@ -1,447 +0,0 @@
-langcode: en
-status: true
-dependencies:
- module:
- - node
- - user
-id: test_content_moderation_latest_revision
-label: test_content_moderation_latest_revision
-module: views
-description: ''
-tag: ''
-base_table: node_field_data
-base_field: nid
-core: 8.x
-display:
- default:
- display_plugin: default
- id: default
- display_title: Master
- position: 0
- display_options:
- access:
- type: perm
- options:
- perm: 'access content'
- cache:
- type: tag
- options: { }
- query:
- type: views_query
- options:
- disable_sql_rewrite: false
- distinct: false
- replica: false
- query_comment: ''
- query_tags: { }
- exposed_form:
- type: basic
- options:
- submit_button: Apply
- reset_button: false
- reset_button_label: Reset
- exposed_sorts_label: 'Sort by'
- expose_sort_order: true
- sort_asc_label: Asc
- sort_desc_label: Desc
- pager:
- type: mini
- options:
- items_per_page: 10
- offset: 0
- id: 0
- total_pages: null
- expose:
- items_per_page: false
- items_per_page_label: 'Items per page'
- items_per_page_options: '5, 10, 25, 50'
- items_per_page_options_all: false
- items_per_page_options_all_label: '- All -'
- offset: false
- offset_label: Offset
- tags:
- previous: ‹‹
- next: ››
- style:
- type: default
- options:
- grouping: { }
- row_class: ''
- default_row_class: true
- uses_fields: false
- row:
- type: fields
- options:
- inline: { }
- separator: ''
- hide_empty: false
- default_field_elements: true
- fields:
- nid:
- id: nid
- table: node_field_data
- field: nid
- relationship: none
- group_type: group
- admin_label: ''
- label: ''
- exclude: false
- alter:
- alter_text: false
- text: ''
- make_link: false
- path: ''
- absolute: false
- external: false
- replace_spaces: false
- path_case: none
- trim_whitespace: false
- alt: ''
- rel: ''
- link_class: ''
- prefix: ''
- suffix: ''
- target: ''
- nl2br: false
- max_length: 0
- word_boundary: true
- ellipsis: true
- more_link: false
- more_link_text: ''
- more_link_path: ''
- strip_tags: false
- trim: false
- preserve_tags: ''
- html: false
- element_type: ''
- element_class: ''
- element_label_type: ''
- element_label_class: ''
- element_label_colon: false
- element_wrapper_type: ''
- element_wrapper_class: ''
- element_default_classes: true
- empty: ''
- hide_empty: false
- empty_zero: false
- hide_alter_empty: true
- click_sort_column: value
- type: number_integer
- settings:
- thousand_separator: ''
- prefix_suffix: true
- group_column: value
- group_columns: { }
- group_rows: true
- delta_limit: 0
- delta_offset: 0
- delta_reversed: false
- delta_first_last: false
- multi_type: separator
- separator: ', '
- field_api_classes: false
- entity_type: node
- entity_field: nid
- plugin_id: field
- revision_id:
- id: revision_id
- table: content_revision_tracker
- field: revision_id
- relationship: none
- group_type: group
- admin_label: ''
- label: ''
- exclude: false
- alter:
- alter_text: false
- text: ''
- make_link: false
- path: ''
- absolute: false
- external: false
- replace_spaces: false
- path_case: none
- trim_whitespace: false
- alt: ''
- rel: ''
- link_class: ''
- prefix: ''
- suffix: ''
- target: ''
- nl2br: false
- max_length: 0
- word_boundary: true
- ellipsis: true
- more_link: false
- more_link_text: ''
- more_link_path: ''
- strip_tags: false
- trim: false
- preserve_tags: ''
- html: false
- element_type: ''
- element_class: ''
- element_label_type: ''
- element_label_class: ''
- element_label_colon: false
- element_wrapper_type: ''
- element_wrapper_class: ''
- element_default_classes: true
- empty: ''
- hide_empty: false
- empty_zero: false
- hide_alter_empty: true
- plugin_id: standard
- title:
- id: title
- table: node_field_revision
- field: title
- relationship: latest_revision__node
- group_type: group
- admin_label: ''
- label: ''
- exclude: false
- alter:
- alter_text: false
- text: ''
- make_link: false
- path: ''
- absolute: false
- external: false
- replace_spaces: false
- path_case: none
- trim_whitespace: false
- alt: ''
- rel: ''
- link_class: ''
- prefix: ''
- suffix: ''
- target: ''
- nl2br: false
- max_length: 0
- word_boundary: true
- ellipsis: true
- more_link: false
- more_link_text: ''
- more_link_path: ''
- strip_tags: false
- trim: false
- preserve_tags: ''
- html: false
- element_type: ''
- element_class: ''
- element_label_type: ''
- element_label_class: ''
- element_label_colon: false
- element_wrapper_type: ''
- element_wrapper_class: ''
- element_default_classes: true
- empty: ''
- hide_empty: false
- empty_zero: false
- hide_alter_empty: true
- click_sort_column: value
- type: string
- settings:
- link_to_entity: false
- group_column: value
- group_columns: { }
- group_rows: true
- delta_limit: 0
- delta_offset: 0
- delta_reversed: false
- delta_first_last: false
- multi_type: separator
- separator: ', '
- field_api_classes: false
- entity_type: node
- entity_field: title
- plugin_id: field
- moderation_state:
- id: moderation_state
- table: content_moderation_state_field_revision
- field: moderation_state
- relationship: moderation_state
- group_type: group
- admin_label: ''
- label: ''
- exclude: false
- alter:
- alter_text: false
- text: ''
- make_link: false
- path: ''
- absolute: false
- external: false
- replace_spaces: false
- path_case: none
- trim_whitespace: false
- alt: ''
- rel: ''
- link_class: ''
- prefix: ''
- suffix: ''
- target: ''
- nl2br: false
- max_length: 0
- word_boundary: true
- ellipsis: true
- more_link: false
- more_link_text: ''
- more_link_path: ''
- strip_tags: false
- trim: false
- preserve_tags: ''
- html: false
- element_type: ''
- element_class: ''
- element_label_type: ''
- element_label_class: ''
- element_label_colon: false
- element_wrapper_type: ''
- element_wrapper_class: ''
- element_default_classes: true
- empty: ''
- hide_empty: false
- empty_zero: false
- hide_alter_empty: true
- click_sort_column: target_id
- type: string
- settings: { }
- group_column: target_id
- group_columns: { }
- group_rows: true
- delta_limit: 0
- delta_offset: 0
- delta_reversed: false
- delta_first_last: false
- multi_type: separator
- separator: ', '
- field_api_classes: false
- entity_type: content_moderation_state
- entity_field: moderation_state
- plugin_id: field
- moderation_state_1:
- id: moderation_state_1
- table: content_moderation_state_field_revision
- field: moderation_state
- relationship: moderation_state_1
- group_type: group
- admin_label: ''
- label: ''
- exclude: false
- alter:
- alter_text: false
- text: ''
- make_link: false
- path: ''
- absolute: false
- external: false
- replace_spaces: false
- path_case: none
- trim_whitespace: false
- alt: ''
- rel: ''
- link_class: ''
- prefix: ''
- suffix: ''
- target: ''
- nl2br: false
- max_length: 0
- word_boundary: true
- ellipsis: true
- more_link: false
- more_link_text: ''
- more_link_path: ''
- strip_tags: false
- trim: false
- preserve_tags: ''
- html: false
- element_type: ''
- element_class: ''
- element_label_type: ''
- element_label_class: ''
- element_label_colon: false
- element_wrapper_type: ''
- element_wrapper_class: ''
- element_default_classes: true
- empty: ''
- hide_empty: false
- empty_zero: false
- hide_alter_empty: true
- click_sort_column: target_id
- type: string
- settings: { }
- group_column: target_id
- group_columns: { }
- group_rows: true
- delta_limit: 0
- delta_offset: 0
- delta_reversed: false
- delta_first_last: false
- multi_type: separator
- separator: ', '
- field_api_classes: false
- entity_type: content_moderation_state
- entity_field: moderation_state
- plugin_id: field
- filters: { }
- sorts:
- nid:
- id: nid
- table: node_field_data
- field: nid
- relationship: none
- group_type: group
- admin_label: ''
- order: ASC
- exposed: false
- expose:
- label: ''
- entity_type: node
- entity_field: nid
- plugin_id: standard
- header: { }
- footer: { }
- empty: { }
- relationships:
- latest_revision__node:
- id: latest_revision__node
- table: content_revision_tracker
- field: latest_revision__node
- relationship: none
- group_type: group
- admin_label: 'Content latest revision'
- required: false
- plugin_id: standard
- moderation_state_1:
- id: moderation_state_1
- table: node_field_revision
- field: moderation_state
- relationship: latest_revision__node
- group_type: group
- admin_label: 'Content moderation state (latest revision)'
- required: false
- entity_type: node
- plugin_id: standard
- moderation_state:
- id: moderation_state
- table: node_field_revision
- field: moderation_state
- relationship: none
- group_type: group
- admin_label: 'Content moderation state'
- required: false
- entity_type: node
- plugin_id: standard
- arguments: { }
- display_extenders: { }
- rendering_language: '***LANGUAGE_entity_default***'
- cache_metadata:
- max-age: -1
- contexts:
- - 'languages:language_interface'
- - url.query_args
- - 'user.node_grants:view'
- - user.permissions
- tags: { }
diff --git a/core/modules/content_moderation/tests/src/Functional/LatestRevisionViewsFilterTest.php b/core/modules/content_moderation/tests/src/Functional/LatestRevisionViewsFilterTest.php
deleted file mode 100644
index cf14b23..0000000
--- a/core/modules/content_moderation/tests/src/Functional/LatestRevisionViewsFilterTest.php
+++ /dev/null
@@ -1,130 +0,0 @@
-<?php
-
-namespace Drupal\Tests\content_moderation\Functional;
-
-use Drupal\node\Entity\Node;
-use Drupal\node\Entity\NodeType;
-use Drupal\Tests\BrowserTestBase;
-use Drupal\workflows\Entity\Workflow;
-
-/**
- * Tests the "Latest Revision" views filter.
- *
- * @group content_moderation
- */
-class LatestRevisionViewsFilterTest extends BrowserTestBase {
-
- /**
- * {@inheritdoc}
- */
- public static $modules = [
- 'content_moderation_test_views',
- 'content_moderation',
- ];
-
- /**
- * Tests view shows the correct node IDs.
- */
- public function testViewShowsCorrectNids() {
- $this->createNodeType('Test', 'test');
-
- $permissions = [
- 'access content',
- 'view all revisions',
- ];
- $editor1 = $this->drupalCreateUser($permissions);
-
- $this->drupalLogin($editor1);
-
- // Make a pre-moderation node.
- /** @var Node $node_0 */
- $node_0 = Node::create([
- 'type' => 'test',
- 'title' => 'Node 0 - Rev 1',
- 'uid' => $editor1->id(),
- ]);
- $node_0->save();
-
- // Now enable moderation for subsequent nodes.
- $workflow = Workflow::load('editorial');
- $workflow->getTypePlugin()->addEntityTypeAndBundle('node', 'test');
- $workflow->save();
-
- // Make a node that is only ever in Draft.
- /** @var Node $node_1 */
- $node_1 = Node::create([
- 'type' => 'test',
- 'title' => 'Node 1 - Rev 1',
- 'uid' => $editor1->id(),
- ]);
- $node_1->moderation_state->value = 'draft';
- $node_1->save();
-
- // Make a node that is in Draft, then Published.
- /** @var Node $node_2 */
- $node_2 = Node::create([
- 'type' => 'test',
- 'title' => 'Node 2 - Rev 1',
- 'uid' => $editor1->id(),
- ]);
- $node_2->moderation_state->value = 'draft';
- $node_2->save();
-
- $node_2->setTitle('Node 2 - Rev 2');
- $node_2->moderation_state->value = 'published';
- $node_2->save();
-
- // Make a node that is in Draft, then Published, then Draft.
- /** @var Node $node_3 */
- $node_3 = Node::create([
- 'type' => 'test',
- 'title' => 'Node 3 - Rev 1',
- 'uid' => $editor1->id(),
- ]);
- $node_3->moderation_state->value = 'draft';
- $node_3->save();
-
- $node_3->setTitle('Node 3 - Rev 2');
- $node_3->moderation_state->value = 'published';
- $node_3->save();
-
- $node_3->setTitle('Node 3 - Rev 3');
- $node_3->moderation_state->value = 'draft';
- $node_3->save();
-
- // Now show the View, and confirm that only the correct titles are showing.
- $this->drupalGet('/latest');
- $page = $this->getSession()->getPage();
- $this->assertEquals(200, $this->getSession()->getStatusCode());
- $this->assertTrue($page->hasContent('Node 1 - Rev 1'));
- $this->assertTrue($page->hasContent('Node 2 - Rev 2'));
- $this->assertTrue($page->hasContent('Node 3 - Rev 3'));
- $this->assertFalse($page->hasContent('Node 2 - Rev 1'));
- $this->assertFalse($page->hasContent('Node 3 - Rev 1'));
- $this->assertFalse($page->hasContent('Node 3 - Rev 2'));
- $this->assertFalse($page->hasContent('Node 0 - Rev 1'));
- }
-
- /**
- * Creates a new node type.
- *
- * @param string $label
- * The human-readable label of the type to create.
- * @param string $machine_name
- * The machine name of the type to create.
- *
- * @return NodeType
- * The node type just created.
- */
- protected function createNodeType($label, $machine_name) {
- /** @var NodeType $node_type */
- $node_type = NodeType::create([
- 'type' => $machine_name,
- 'label' => $label,
- ]);
- $node_type->save();
-
- return $node_type;
- }
-
-}
diff --git a/core/modules/content_moderation/tests/src/Kernel/ViewsDataIntegrationTest.php b/core/modules/content_moderation/tests/src/Kernel/ViewsDataIntegrationTest.php
index d4c4aa3..125d68f 100644
--- a/core/modules/content_moderation/tests/src/Kernel/ViewsDataIntegrationTest.php
+++ b/core/modules/content_moderation/tests/src/Kernel/ViewsDataIntegrationTest.php
@@ -2,7 +2,6 @@
namespace Drupal\Tests\content_moderation\Kernel;
-use Drupal\entity_test\Entity\EntityTestMulRevPub;
use Drupal\node\Entity\Node;
use Drupal\node\Entity\NodeType;
use Drupal\Tests\views\Kernel\ViewsKernelTestBase;
@@ -52,54 +51,6 @@ class ViewsDataIntegrationTest extends ViewsKernelTestBase {
}
/**
- * Tests content_moderation_views_data().
- *
- * @see content_moderation_views_data()
- */
- public function testViewsData() {
- $node = Node::create([
- 'type' => 'page',
- 'title' => 'Test title first revision',
- ]);
- $node->moderation_state->value = 'published';
- $node->save();
-
- // Create a totally unrelated entity to ensure the extra join information
- // joins by the correct entity type.
- $unrelated_entity = EntityTestMulRevPub::create([
- 'id' => $node->id(),
- ]);
- $unrelated_entity->save();
-
- $this->assertEquals($unrelated_entity->id(), $node->id());
-
- $revision = clone $node;
- $revision->setNewRevision(TRUE);
- $revision->isDefaultRevision(FALSE);
- $revision->title->value = 'Test title second revision';
- $revision->moderation_state->value = 'draft';
- $revision->save();
-
- $view = Views::getView('test_content_moderation_latest_revision');
- $view->execute();
-
- // Ensure that the content_revision_tracker contains the right latest
- // revision ID.
- // Also ensure that the relationship back to the revision table contains the
- // right latest revision.
- $expected_result = [
- [
- 'nid' => $node->id(),
- 'revision_id' => $revision->getRevisionId(),
- 'title' => $revision->label(),
- 'moderation_state_1' => 'draft',
- 'moderation_state' => 'published',
- ],
- ];
- $this->assertIdenticalResultset($view, $expected_result, ['nid' => 'nid', 'content_revision_tracker_revision_id' => 'revision_id', 'moderation_state' => 'moderation_state', 'moderation_state_1' => 'moderation_state_1']);
- }
-
- /**
* Tests the join from the revision data table to the moderation state table.
*/
public function testContentModerationStateRevisionJoin() {
diff --git a/core/modules/views/config/schema/views.filter.schema.yml b/core/modules/views/config/schema/views.filter.schema.yml
index 00eb11a..18c13b6 100644
--- a/core/modules/views/config/schema/views.filter.schema.yml
+++ b/core/modules/views/config/schema/views.filter.schema.yml
@@ -142,6 +142,10 @@ views.filter.language:
type: views.filter.in_operator
label: 'Language'
+views.filter.latest_revision:
+ type: views_filter
+ label: 'Latest revision'
+
views.filter_value.date:
type: views.filter_value.numeric
label: 'Date'
diff --git a/core/modules/views/src/EntityViewsData.php b/core/modules/views/src/EntityViewsData.php
index 2ba2086..49b0af8 100644
--- a/core/modules/views/src/EntityViewsData.php
+++ b/core/modules/views/src/EntityViewsData.php
@@ -236,6 +236,13 @@ class EntityViewsData implements EntityHandlerInterface, EntityViewsDataInterfac
'type' => 'INNER',
];
}
+
+ // Add a filter for showing only the latest revisions of an entity.
+ $data[$revision_table]['latest_revision'] = [
+ 'title' => $this->t('Is Latest Revision'),
+ 'help' => $this->t('Restrict the view to only revisions that are the latest revision of their entity.'),
+ 'filter' => ['id' => 'latest_revision'],
+ ];
}
$this->addEntityLinks($data[$base_table]);
diff --git a/core/modules/content_moderation/src/Plugin/views/filter/LatestRevision.php b/core/modules/views/src/Plugin/views/filter/LatestRevision.php
index 6440019..7930a7f 100644
--- a/core/modules/content_moderation/src/Plugin/views/filter/LatestRevision.php
+++ b/core/modules/views/src/Plugin/views/filter/LatestRevision.php
@@ -1,12 +1,10 @@
<?php
-namespace Drupal\content_moderation\Plugin\views\filter;
+namespace Drupal\views\Plugin\views\filter;
-use Drupal\Core\Database\Connection;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
-use Drupal\views\Plugin\views\filter\FilterPluginBase;
use Drupal\views\Plugin\ViewsHandlerManager;
use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -34,13 +32,6 @@ class LatestRevision extends FilterPluginBase implements ContainerFactoryPluginI
protected $joinHandler;
/**
- * Database Connection.
- *
- * @var \Drupal\Core\Database\Connection
- */
- protected $connection;
-
- /**
* Constructs a new LatestRevision.
*
* @param array $configuration
@@ -53,14 +44,12 @@ class LatestRevision extends FilterPluginBase implements ContainerFactoryPluginI
* Entity Type Manager Service.
* @param \Drupal\views\Plugin\ViewsHandlerManager $join_handler
* Views Handler Plugin Manager.
- * @param \Drupal\Core\Database\Connection $connection
- * Database Connection.
*/
- public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, ViewsHandlerManager $join_handler, Connection $connection) {
+ public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, ViewsHandlerManager $join_handler) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
+
$this->entityTypeManager = $entity_type_manager;
$this->joinHandler = $join_handler;
- $this->connection = $connection;
}
/**
@@ -70,8 +59,7 @@ class LatestRevision extends FilterPluginBase implements ContainerFactoryPluginI
return new static(
$configuration, $plugin_id, $plugin_definition,
$container->get('entity_type.manager'),
- $container->get('plugin.manager.views.join'),
- $container->get('database')
+ $container->get('plugin.manager.views.join')
);
}
@@ -98,39 +86,28 @@ class LatestRevision extends FilterPluginBase implements ContainerFactoryPluginI
* {@inheritdoc}
*/
public function query() {
- // The table doesn't exist until a moderated node has been saved at least
- // once. Just in case, disable this filter until then. Note that this means
- // the view will still show all revisions, not just latest, but this is
- // sufficiently edge-case-y that it's probably not worth the time to
- // handle more robustly.
- if (!$this->connection->schema()->tableExists('content_revision_tracker')) {
- return;
- }
-
- $table = $this->ensureMyTable();
-
/** @var \Drupal\views\Plugin\views\query\Sql $query */
$query = $this->query;
+ $query_base_table = $this->relationship ?: $this->view->storage->get('base_table');
- $definition = $this->entityTypeManager->getDefinition($this->getEntityType());
- $keys = $definition->getKeys();
+ $entity_type = $this->entityTypeManager->getDefinition($this->getEntityType());
+ $keys = $entity_type->getKeys();
$definition = [
- 'table' => 'content_revision_tracker',
- 'type' => 'INNER',
- 'field' => 'entity_id',
- 'left_table' => $table,
+ 'table' => $query_base_table,
+ 'type' => 'LEFT',
+ 'field' => $keys['id'],
+ 'left_table' => $query_base_table,
'left_field' => $keys['id'],
'extra' => [
- ['left_field' => $keys['langcode'], 'field' => 'langcode'],
- ['left_field' => $keys['revision'], 'field' => 'revision_id'],
- ['field' => 'entity_type', 'value' => $this->getEntityType()],
+ ['left_field' => $keys['revision'], 'field' => $keys['revision'], 'operator' => '>'],
],
];
$join = $this->joinHandler->createInstance('standard', $definition);
- $query->ensureTable('content_revision_tracker', $this->relationship, $join);
+ $join_table_alias = $query->addTable($query_base_table, $this->relationship, $join);
+ $query->addWhere($this->options['group'], "$join_table_alias.{$keys['id']}", NULL, 'IS NULL');
}
}
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_latest_revision_filter.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_latest_revision_filter.yml
new file mode 100644
index 0000000..53f0f72
--- /dev/null
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_latest_revision_filter.yml
@@ -0,0 +1,163 @@
+langcode: en
+status: true
+dependencies:
+ module:
+ - node
+id: test_latest_revision_filter
+label: ''
+module: views
+description: ''
+tag: ''
+base_table: node_field_revision
+base_field: vid
+core: 8.x
+display:
+ default:
+ display_plugin: default
+ id: default
+ display_title: Master
+ position: 0
+ display_options:
+ access:
+ type: none
+ options: { }
+ cache:
+ type: tag
+ options: { }
+ query:
+ type: views_query
+ options:
+ disable_sql_rewrite: false
+ distinct: false
+ replica: false
+ query_comment: ''
+ query_tags: { }
+ exposed_form:
+ type: basic
+ options:
+ submit_button: Apply
+ reset_button: false
+ reset_button_label: Reset
+ exposed_sorts_label: 'Sort by'
+ expose_sort_order: true
+ sort_asc_label: Asc
+ sort_desc_label: Desc
+ pager:
+ type: none
+ options:
+ offset: 0
+ style:
+ type: default
+ options:
+ grouping: { }
+ row_class: ''
+ default_row_class: true
+ uses_fields: false
+ row:
+ type: fields
+ options:
+ inline: { }
+ separator: ''
+ hide_empty: false
+ default_field_elements: true
+ fields:
+ title:
+ id: title
+ table: node_field_revision
+ field: title
+ entity_type: node
+ entity_field: title
+ label: ''
+ alter:
+ alter_text: false
+ make_link: false
+ absolute: false
+ trim: false
+ word_boundary: false
+ ellipsis: false
+ strip_tags: false
+ html: false
+ hide_empty: false
+ empty_zero: false
+ settings:
+ link_to_entity: false
+ plugin_id: field
+ relationship: none
+ group_type: group
+ admin_label: ''
+ exclude: false
+ element_type: ''
+ element_class: ''
+ element_label_type: ''
+ element_label_class: ''
+ element_label_colon: true
+ element_wrapper_type: ''
+ element_wrapper_class: ''
+ element_default_classes: true
+ empty: ''
+ hide_alter_empty: true
+ click_sort_column: value
+ type: string
+ group_column: value
+ group_columns: { }
+ group_rows: true
+ delta_limit: 0
+ delta_offset: 0
+ delta_reversed: false
+ delta_first_last: false
+ multi_type: separator
+ separator: ', '
+ field_api_classes: false
+ filters:
+ latest_revision:
+ id: latest_revision
+ table: node_revision
+ field: latest_revision
+ relationship: none
+ group_type: group
+ admin_label: ''
+ operator: '='
+ value: ''
+ group: 1
+ exposed: false
+ expose:
+ operator_id: ''
+ label: ''
+ description: ''
+ use_operator: false
+ operator: ''
+ identifier: ''
+ required: false
+ remember: false
+ multiple: false
+ remember_roles:
+ authenticated: authenticated
+ is_grouped: false
+ group_info:
+ label: ''
+ description: ''
+ identifier: ''
+ optional: true
+ widget: select
+ multiple: false
+ remember: false
+ default_group: All
+ default_group_multiple: { }
+ group_items: { }
+ entity_type: node
+ plugin_id: latest_revision
+ sorts: { }
+ header: { }
+ footer: { }
+ empty: { }
+ relationships: { }
+ arguments: { }
+ display_extenders: { }
+ show_admin_links: false
+ cache_metadata:
+ max-age: -1
+ contexts:
+ - 'languages:language_content'
+ - 'languages:language_interface'
+ - 'user.node_grants:view'
+ tags: { }
diff --git a/core/modules/views/tests/src/Functional/Entity/LatestRevisionFilterTest.php b/core/modules/views/tests/src/Functional/Entity/LatestRevisionFilterTest.php
new file mode 100644
index 0000000..d73a117
--- /dev/null
+++ b/core/modules/views/tests/src/Functional/Entity/LatestRevisionFilterTest.php
@@ -0,0 +1,160 @@
+<?php
+
+namespace Drupal\Tests\views\Functional\Entity;
+
+use Drupal\node\Entity\Node;
+use Drupal\Tests\views\Functional\ViewTestBase;
+use Drupal\views\ViewExecutable;
+use Drupal\views\Views;
+
+/**
+ * Tests the 'Latest revision' filter.
+ *
+ * @group views
+ */
+class LatestRevisionFilterTest extends ViewTestBase {
+
+ /**
+ * An array of node revisions.
+ *
+ * @var \Drupal\node\NodeInterface[]
+ */
+ protected $allRevisions = [];
+
+ /**
+ * An array of node revisions.
+ *
+ * @var \Drupal\node\NodeInterface[]
+ */
+ protected $latestRevisions = [];
+
+ /**
+ * Views used by this test.
+ *
+ * @var array
+ */
+ public static $testViews = ['test_latest_revision_filter'];
+
+ /**
+ * Modules to enable.
+ *
+ * @var array
+ */
+ public static $modules = ['node'];
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function setUp($import_test_views = TRUE) {
+ parent::setUp();
+
+ $this->drupalCreateContentType(['type' => 'article']);
+
+ // Create a node that goes through various default/pending revision stages.
+ $node = Node::create([
+ 'title' => 'First node - v1 - default',
+ 'type' => 'article',
+ ]);
+ $node->save();
+ $this->allRevisions[$node->getRevisionId()] = $node;
+
+ $node->setTitle('First node - v2 - pending');
+ $node->setNewRevision(TRUE);
+ $node->isDefaultRevision(FALSE);
+ $node->save();
+ $this->allRevisions[$node->getRevisionId()] = $node;
+
+ $node->setTitle('First node - v3 - default');
+ $node->setNewRevision(TRUE);
+ $node->isDefaultRevision(TRUE);
+ $node->save();
+ $this->allRevisions[$node->getRevisionId()] = $node;
+
+ $node->setTitle('First node - v4 - pending');
+ $node->setNewRevision(TRUE);
+ $node->isDefaultRevision(TRUE);
+ $node->save();
+ $this->allRevisions[$node->getRevisionId()] = $node;
+ $this->latestRevisions[$node->getRevisionId()] = $node;
+
+ // Create a node that has a default and a pending revision.
+ $node = Node::create([
+ 'title' => 'Second node - v1 - default',
+ 'type' => 'article',
+ ]);
+ $node->save();
+ $this->allRevisions[$node->getRevisionId()] = $node;
+
+ $node->setTitle('Second node - v2 - pending');
+ $node->setNewRevision(TRUE);
+ $node->isDefaultRevision(FALSE);
+ $node->save();
+ $this->allRevisions[$node->getRevisionId()] = $node;
+ $this->latestRevisions[$node->getRevisionId()] = $node;
+
+ // Create a node that only has a default revision.
+ $node = Node::create([
+ 'title' => 'Third node - v1 - default',
+ 'type' => 'article',
+ ]);
+ $node->save();
+ $this->allRevisions[$node->getRevisionId()] = $node;
+ $this->latestRevisions[$node->getRevisionId()] = $node;
+
+ // Create a node that only has a pending revision.
+ $node = Node::create([
+ 'title' => 'Fourth node - v1 - pending',
+ 'type' => 'article',
+ ]);
+ $node->isDefaultRevision(FALSE);
+ $node->save();
+ $this->allRevisions[$node->getRevisionId()] = $node;
+ $this->latestRevisions[$node->getRevisionId()] = $node;
+ }
+
+ /**
+ * Tests the 'Latest revision' filter.
+ */
+ public function testLatestRevisionFilter() {
+ $view = Views::getView('test_latest_revision_filter');
+
+ $this->executeView($view);
+
+ // Check that we have all the results.
+ $this->assertCount(count($this->latestRevisions), $view->result);
+
+ $expected = $not_expected = [];
+ foreach ($this->allRevisions as $revision_id => $revision) {
+ if (isset($this->latestRevisions[$revision_id])) {
+ $expected[] = [
+ 'vid' => $revision_id,
+ 'title' => $revision->label(),
+ ];
+ }
+ else {
+ $not_expected[] = $revision_id;
+ }
+ }
+ $this->assertIdenticalResultset($view, $expected, ['vid' => 'vid', 'title' => 'title'], 'The test view only shows the latest revisions.');
+ $this->assertNotInResultSet($view, $not_expected, 'Non-latest revisions are not shown by the view.');
+ $view->destroy();
+ }
+
+ /**
+ * Verifies that a list of revision IDs are not in the result.
+ *
+ * @param \Drupal\views\ViewExecutable $view
+ * An executed View.
+ * @param array $not_expected_revision_ids
+ * An array of revision IDs which should not be part of the result set.
+ * @param string $message
+ * (optional) A custom message to display with the assertion.
+ */
+ protected function assertNotInResultSet(ViewExecutable $view, array $not_expected_revision_ids, $message = '') {
+ $found_revision_ids = array_filter($view->result, function ($row) use ($not_expected_revision_ids) {
+ return in_array($row->vid, $not_expected_revision_ids);
+ });
+ $this->assertFalse($found_revision_ids, $message);
+ }
+
+}