Unverified Commit 81d8e2a7 authored by larowlan's avatar larowlan

Issue #2953331 by Sam152, Manuel Garcia, alexpott, azinck, anish.a, amateescu,...

Issue #2953331 by Sam152, Manuel Garcia, alexpott, azinck, anish.a, amateescu, jibran, Wim Leers, dawehner: Add a views sort handler for sorting content by moderation state
parent 51284616
......@@ -16,3 +16,11 @@ views.filter.moderation_state_filter:
sequence:
type: string
label: 'Workflow'
views.sort.moderation_state_sort:
type: views_sort
label: 'Moderation state sort'
views.field.moderation_state_field:
type: views.field.field
label: 'Moderation state field'
......@@ -170,3 +170,27 @@ function content_moderation_post_update_entity_display_dependencies(&$sandbox) {
return FALSE;
});
}
/**
* Update the moderation state views field plugin ID.
*/
function content_moderation_post_update_views_field_plugin_id(&$sandbox) {
\Drupal::classResolver(ConfigEntityUpdater::class)->update($sandbox, 'view', function ($view) {
/** @var \Drupal\views\ViewEntityInterface $view */
$updated = FALSE;
$displays = $view->get('display');
foreach ($displays as &$display) {
if (empty($display['display_options']['fields'])) {
continue;
}
foreach ($display['display_options']['fields'] as &$display_field) {
if ($display_field['id'] === 'moderation_state' && $display_field['plugin_id'] === 'field') {
$display_field['plugin_id'] = 'moderation_state_field';
$updated = TRUE;
}
}
}
$view->set('display', $displays);
return $updated;
});
}
<?php
namespace Drupal\content_moderation\Plugin\views;
use Drupal\views\Views;
/**
* Assist views handler plugins to join to the content_moderation_state entity.
*
* @internal
*/
trait ModerationStateJoinViewsHandlerTrait {
/**
* {@inheritdoc}
*/
public function ensureMyTable() {
if (!isset($this->tableAlias)) {
$table_alias = $this->query->ensureTable($this->table, $this->relationship);
// Join the moderation states of the content via the
// ContentModerationState field revision table, joining either the entity
// field data or revision table. This allows filtering states against
// either the default or latest revision, depending on the relationship of
// the filter.
$left_entity_type = $this->entityTypeManager->getDefinition($this->getEntityType());
$entity_type = $this->entityTypeManager->getDefinition('content_moderation_state');
$configuration = [
'table' => $entity_type->getRevisionDataTable(),
'field' => 'content_entity_revision_id',
'left_table' => $table_alias,
'left_field' => $left_entity_type->getKey('revision'),
'extra' => [
[
'field' => 'content_entity_type_id',
'value' => $left_entity_type->id(),
],
],
];
if ($left_entity_type->isTranslatable()) {
$configuration['extra'][] = [
'field' => $entity_type->getKey('langcode'),
'left_field' => $left_entity_type->getKey('langcode'),
];
}
$join = Views::pluginManager('join')->createInstance('standard', $configuration);
$this->tableAlias = $this->query->addRelationship('content_moderation_state', $join, 'content_moderation_state_field_revision');
}
return $this->tableAlias;
}
}
<?php
namespace Drupal\content_moderation\Plugin\views\field;
use Drupal\content_moderation\Plugin\views\ModerationStateJoinViewsHandlerTrait;
use Drupal\views\Plugin\views\field\EntityField;
/**
* A field handler for the computed moderation_state field.
*
* @ingroup views_field_handlers
*
* @ViewsField("moderation_state_field")
*/
class ModerationStateField extends EntityField {
use ModerationStateJoinViewsHandlerTrait;
/**
* {@inheritdoc}
*/
public function clickSort($order) {
$this->ensureMyTable();
// This could be derived from the content_moderation_state entity table
// mapping, however this is an internal entity type whose storage should
// remain constant.
$storage = $this->entityTypeManager->getStorage('content_moderation_state');
$storage_definition = $this->entityFieldManager->getActiveFieldStorageDefinitions('content_moderation_state')['moderation_state'];
$column_name = $storage->getTableMapping()->getFieldColumnName($storage_definition, 'value');
$this->aliases[$column_name] = $this->tableAlias . '.' . $column_name;
$this->query->addOrderBy(NULL, NULL, $order, $this->aliases[$column_name]);
}
}
......@@ -2,6 +2,7 @@
namespace Drupal\content_moderation\Plugin\views\filter;
use Drupal\content_moderation\Plugin\views\ModerationStateJoinViewsHandlerTrait;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Database\Query\Condition;
use Drupal\Core\Entity\EntityStorageInterface;
......@@ -21,6 +22,8 @@
*/
class ModerationStateFilter extends InOperator implements DependentWithRemovalPluginInterface {
use ModerationStateJoinViewsHandlerTrait;
/**
* {@inheritdoc}
*/
......@@ -109,45 +112,6 @@ public function getValueOptions() {
return $this->valueOptions;
}
/**
* {@inheritdoc}
*/
public function ensureMyTable() {
if (!isset($this->tableAlias)) {
$table_alias = $this->query->ensureTable($this->table, $this->relationship);
// Filter the moderation states of the content via the
// ContentModerationState field revision table, joining either the entity
// field data or revision table. This allows filtering states against
// either the default or latest revision, depending on the relationship of
// the filter.
$left_entity_type = $this->entityTypeManager->getDefinition($this->getEntityType());
$entity_type = $this->entityTypeManager->getDefinition('content_moderation_state');
$configuration = [
'table' => $entity_type->getRevisionDataTable(),
'field' => 'content_entity_revision_id',
'left_table' => $table_alias,
'left_field' => $left_entity_type->getKey('revision'),
'extra' => [
[
'field' => 'content_entity_type_id',
'value' => $left_entity_type->id(),
],
],
];
if ($left_entity_type->isTranslatable()) {
$configuration['extra'][] = [
'field' => $entity_type->getKey('langcode'),
'left_field' => $left_entity_type->getKey('langcode'),
];
}
$join = Views::pluginManager('join')->createInstance('standard', $configuration);
$this->tableAlias = $this->query->addRelationship('content_moderation_state', $join, 'content_moderation_state_field_revision');
}
return $this->tableAlias;
}
/**
* {@inheritdoc}
*/
......
<?php
namespace Drupal\content_moderation\Plugin\views\sort;
use Drupal\content_moderation\Plugin\views\ModerationStateJoinViewsHandlerTrait;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\views\Plugin\views\sort\SortPluginBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Enables sorting for the computed moderation_state field.
*
* @ingroup views_sort_handlers
*
* @ViewsSort("moderation_state_sort")
*/
class ModerationStateSort extends SortPluginBase {
use ModerationStateJoinViewsHandlerTrait;
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* Creates an instance of ModerationStateFilter.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->entityTypeManager = $entity_type_manager;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('entity_type.manager')
);
}
}
......@@ -79,11 +79,12 @@ public function getViewsData() {
],
],
'field' => [
'id' => 'field',
'id' => 'moderation_state_field',
'default_formatter' => 'content_moderation_state',
'field_name' => 'moderation_state',
],
'filter' => ['id' => 'moderation_state_filter', 'allow empty' => TRUE],
'sort' => ['id' => 'moderation_state_sort'],
];
$revision_table = $entity_type->getRevisionDataTable() ?: $entity_type->getRevisionTable();
......@@ -103,11 +104,12 @@ public function getViewsData() {
],
],
'field' => [
'id' => 'field',
'id' => 'moderation_state_field',
'default_formatter' => 'content_moderation_state',
'field_name' => 'moderation_state',
],
'filter' => ['id' => 'moderation_state_filter', 'allow empty' => TRUE],
'sort' => ['id' => 'moderation_state_sort'],
];
}
......
langcode: en
status: true
dependencies:
module:
- content_moderation
- node
- user
id: test_content_moderation_state_sort_base_table
label: test_content_moderation_state_sort_base_table
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: table
options:
grouping: { }
row_class: ''
default_row_class: true
override: true
sticky: false
caption: ''
summary: ''
description: ''
columns:
nid: nid
moderation_state: moderation_state
info:
nid:
sortable: false
default_sort_order: asc
align: ''
separator: ''
empty_column: false
responsive: ''
moderation_state:
sortable: true
default_sort_order: asc
align: ''
separator: ''
empty_column: false
responsive: ''
default: '-1'
empty_table: 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
moderation_state:
id: moderation_state
table: node_field_data
field: moderation_state
relationship: none
group_type: group
admin_label: ''
label: 'Moderation state'
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: true
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: content_moderation_state
settings: { }
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
plugin_id: moderation_state_field
filters: { }
sorts:
moderation_state:
id: moderation_state
table: node_field_data
field: moderation_state
relationship: none
group_type: group
admin_label: ''
order: ASC
exposed: true
expose:
label: 'Moderation state'
entity_type: node
plugin_id: moderation_state_sort
header: { }
footer: { }
empty: { }
relationships: { }
arguments: { }
display_extenders: { }
cache_metadata:
max-age: -1
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- url.query_args
- 'url.query_args:sort_by'
- 'url.query_args:sort_order'
- 'user.node_grants:view'
- user.permissions
tags: { }
langcode: en
status: true
dependencies:
module:
- content_moderation
- user
id: test_content_moderation_state_sort_revision_table
label: test_content_moderation_state_sort_revision_table
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: perm
options:
perm: 'view all revisions'
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: table
options:
grouping: { }
row_class: ''
default_row_class: true
override: true
sticky: false
caption: ''
summary: ''
description: ''
columns:
vid: vid
moderation_state: moderation_state
info:
vid:
sortable: false
default_sort_order: asc
align: ''
separator: ''
empty_column: false
responsive: ''
moderation_state:
sortable: true
default_sort_order: asc
align: ''
separator: ''
empty_column: false
responsive: ''
default: '-1'
empty_table: false
row:
type: fields
options:
inline: { }
separator: ''