Commit ba726bed authored by catch's avatar catch
Browse files

Issue #2915383 by Sam152, amateescu, jibran: The moderation_state base field...

Issue #2915383 by Sam152, amateescu, jibran: The moderation_state base field is added to all revisionable entity types even if they do not have moderation enabled
parent 3ba6bc11
......@@ -21,6 +21,9 @@
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Url;
use Drupal\views\Plugin\views\filter\Broken;
use Drupal\views\ViewExecutable;
use Drupal\views\Views;
use Drupal\workflows\WorkflowInterface;
use Drupal\Core\Action\Plugin\Action\PublishAction;
use Drupal\Core\Action\Plugin\Action\UnpublishAction;
......@@ -64,6 +67,20 @@ function content_moderation_entity_base_field_info(EntityTypeInterface $entity_t
->entityBaseFieldInfo($entity_type);
}
/**
* Implements hook_entity_bundle_field_info().
*/
function content_moderation_entity_bundle_field_info(EntityTypeInterface $entity_type, $bundle, array $base_field_definitions) {
if (isset($base_field_definitions['moderation_state'])) {
// Add the target bundle to the moderation state field. Since each bundle
// can be attached to a different moderation workflow, adding this
// information to the field definition allows the associated workflow to be
// derived where a field definition is present.
$base_field_definitions['moderation_state']->setTargetBundle($bundle);
return $base_field_definitions;
}
}
/**
* Implements hook_entity_type_alter().
*/
......@@ -316,6 +333,10 @@ function content_moderation_workflow_insert(WorkflowInterface $entity) {
\Drupal::service('entity_type.bundle.info')->clearCachedBundles();
// Clear field cache so extra field is added or removed.
\Drupal::service('entity_field.manager')->clearCachedFieldDefinitions();
// Clear the views data cache so the extra field is available in views.
if (\Drupal::moduleHandler()->moduleExists('views')) {
Views::viewsData()->clear();
}
}
/**
......@@ -327,4 +348,22 @@ function content_moderation_workflow_update(WorkflowInterface $entity) {
\Drupal::service('entity_type.bundle.info')->clearCachedBundles();
// Clear field cache so extra field is added or removed.
\Drupal::service('entity_field.manager')->clearCachedFieldDefinitions();
// Clear the views data cache so the extra field is available in views.
if (\Drupal::moduleHandler()->moduleExists('views')) {
Views::viewsData()->clear();
}
}
/**
* Implements hook_views_post_execute().
*/
function content_moderation_views_post_execute(ViewExecutable $view) {
// @todo, remove this once broken handlers in views configuration result in
// a view no longer returning results. https://www.drupal.org/node/2907954.
foreach ($view->filter as $id => $filter) {
if (strpos($id, 'moderation_state') === 0 && $filter instanceof Broken) {
$view->result = [];
break;
}
}
}
......@@ -6,6 +6,7 @@
*/
use Drupal\Core\Config\Entity\ConfigEntityUpdater;
use Drupal\Core\Entity\Entity\EntityFormDisplay;
use Drupal\Core\Site\Settings;
use Drupal\views\Entity\View;
use Drupal\workflows\Entity\Workflow;
......@@ -138,3 +139,34 @@ function content_moderation_post_update_set_views_filter_latest_translation_affe
$view->save();
}
}
/**
* Update the dependencies of entity displays to include associated workflow.
*/
function content_moderation_post_update_entity_display_dependencies(&$sandbox) {
/** @var \Drupal\content_moderation\ModerationInformationInterface $moderation_info */
$moderation_info = \Drupal::service('content_moderation.moderation_information');
/** @var \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager */
$entity_type_manager = \Drupal::service('entity_type.manager');
\Drupal::classResolver(ConfigEntityUpdater::class)->update($sandbox, 'entity_form_display', function (EntityFormDisplay $entity_form_display) use ($moderation_info, $entity_type_manager) {
$associated_entity_type = $entity_type_manager->getDefinition($entity_form_display->getTargetEntityTypeId());
if ($moderation_info->isModeratedEntityType($associated_entity_type)) {
$entity_form_display->calculateDependencies();
return TRUE;
}
elseif ($moderation_state_component = $entity_form_display->getComponent('moderation_state')) {
// Remove the component from the entity form display, then manually delete
// it from the hidden components list, completely purging it.
$entity_form_display->removeComponent('moderation_state');
$hidden_components = $entity_form_display->get('hidden');
unset($hidden_components['moderation_state']);
$entity_form_display->set('hidden', $hidden_components);
$entity_form_display->calculateDependencies();
return TRUE;
}
return FALSE;
});
}
......@@ -240,7 +240,7 @@ protected function getModeratedBundles() {
* @see hook_entity_base_field_info()
*/
public function entityBaseFieldInfo(EntityTypeInterface $entity_type) {
if (!$this->moderationInfo->canModerateEntitiesOfEntityType($entity_type)) {
if (!$this->moderationInfo->isModeratedEntityType($entity_type)) {
return [];
}
......
......@@ -56,6 +56,14 @@ public function isModeratedEntity(EntityInterface $entity) {
return $this->shouldModerateEntitiesOfBundle($entity->getEntityType(), $entity->bundle());
}
/**
* {@inheritdoc}
*/
public function isModeratedEntityType(EntityTypeInterface $entity_type) {
$bundles = $this->bundleInfo->getBundleInfo($entity_type->id());
return !empty(array_column($bundles, 'workflow'));
}
/**
* {@inheritdoc}
*/
......@@ -206,10 +214,17 @@ public function isDefaultRevisionPublished(ContentEntityInterface $entity) {
* {@inheritdoc}
*/
public function getWorkflowForEntity(ContentEntityInterface $entity) {
$bundles = $this->bundleInfo->getBundleInfo($entity->getEntityTypeId());
if (isset($bundles[$entity->bundle()]['workflow'])) {
return $this->entityTypeManager->getStorage('workflow')->load($bundles[$entity->bundle()]['workflow']);
};
return $this->getWorkflowForEntityTypeAndBundle($entity->getEntityTypeId(), $entity->bundle());
}
/**
* {@inheritdoc}
*/
public function getWorkflowForEntityTypeAndBundle($entity_type_id, $bundle_id) {
$bundles = $this->bundleInfo->getBundleInfo($entity_type_id);
if (isset($bundles[$bundle_id]['workflow'])) {
return $this->entityTypeManager->getStorage('workflow')->load($bundles[$bundle_id]['workflow']);
}
return NULL;
}
......
......@@ -47,6 +47,17 @@ public function canModerateEntitiesOfEntityType(EntityTypeInterface $entity_type
*/
public function shouldModerateEntitiesOfBundle(EntityTypeInterface $entity_type, $bundle);
/**
* Determines if an entity type has at least one moderated bundle.
*
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
* The entity type definition to check.
*
* @return bool
* TRUE if an entity type has a moderated bundle, FALSE otherwise.
*/
public function isModeratedEntityType(EntityTypeInterface $entity_type);
/**
* Loads the latest revision of a specific entity.
*
......@@ -163,6 +174,19 @@ public function isDefaultRevisionPublished(ContentEntityInterface $entity);
*/
public function getWorkflowForEntity(ContentEntityInterface $entity);
/**
* Gets the workflow for the given entity type and bundle.
*
* @param string $entity_type_id
* The entity type ID.
* @param string $bundle_id
* The entity bundle ID.
*
* @return \Drupal\workflows\WorkflowInterface|null
* The associated workflow. NULL if there is no workflow.
*/
public function getWorkflowForEntityTypeAndBundle($entity_type_id, $bundle_id);
/**
* Gets unsupported features for a given entity type.
*
......
......@@ -178,4 +178,15 @@ public static function isApplicable(FieldDefinitionInterface $field_definition)
return is_a($field_definition->getClass(), ModerationStateFieldItemList::class, TRUE);
}
/**
* {@inheritdoc}
*/
public function calculateDependencies() {
$dependencies = parent::calculateDependencies();
if ($workflow = $this->moderationInformation->getWorkflowForEntityTypeAndBundle($this->fieldDefinition->getTargetEntityTypeId(), $this->fieldDefinition->getTargetBundle())) {
$dependencies[$workflow->getConfigDependencyKey()][] = $workflow->getConfigDependencyName();
}
return $dependencies;
}
}
......@@ -52,7 +52,7 @@ public function getViewsData() {
$data = [];
$entity_types_with_moderation = array_filter($this->entityTypeManager->getDefinitions(), function (EntityTypeInterface $type) {
return $this->moderationInformation->canModerateEntitiesOfEntityType($type);
return $this->moderationInformation->isModeratedEntityType($type);
});
// Provides a relationship from moderated entity to its moderation state
......
<?php
// @codingStandardsIgnoreFile
/**
* @file
* Content for the update path test in #2915383.
*
* @see \Drupal\Tests\content_moderation\Functional\EntityFormDisplayDependenciesUpdateTest
*/
use Drupal\Core\Database\Database;
$connection = Database::getConnection();
$connection->update('config')
->fields([
'data' => 'a:11:{s:4:"uuid";s:36:"16624d7d-0800-4ed7-9861-41f7e71394a8";s:8:"langcode";s:2:"en";s:6:"status";b:1;s:12:"dependencies";a:2:{s:6:"config";a:2:{i:0;s:24:"block_content.type.basic";i:1;s:36:"field.field.block_content.basic.body";}s:6:"module";a:2:{i:0;s:18:"content_moderation";i:1;s:4:"text";}}s:5:"_core";a:1:{s:19:"default_config_hash";s:43:"e1Nu5xXAuF_QplbBUhQBPLnYWvHtDX0MkZnpuCiY8uM";}s:2:"id";s:27:"block_content.basic.default";s:16:"targetEntityType";s:13:"block_content";s:6:"bundle";s:5:"basic";s:4:"mode";s:7:"default";s:7:"content";a:3:{s:4:"body";a:5:{s:4:"type";s:26:"text_textarea_with_summary";s:6:"weight";i:-4;s:6:"region";s:7:"content";s:8:"settings";a:3:{s:4:"rows";i:9;s:12:"summary_rows";i:3;s:11:"placeholder";s:0:"";}s:20:"third_party_settings";a:0:{}}s:4:"info";a:5:{s:4:"type";s:16:"string_textfield";s:6:"weight";i:-5;s:6:"region";s:7:"content";s:8:"settings";a:2:{s:4:"size";i:60;s:11:"placeholder";s:0:"";}s:20:"third_party_settings";a:0:{}}s:16:"moderation_state";a:5:{s:4:"type";s:24:"moderation_state_default";s:6:"weight";i:100;s:8:"settings";a:0:{}s:6:"region";s:7:"content";s:20:"third_party_settings";a:0:{}}}s:6:"hidden";a:0:{}}',
])
->condition('name', 'core.entity_form_display.block_content.basic.default')
->execute();
$connection->update('config')
->fields([
'data' => 'a:11:{s:4:"uuid";s:36:"af6ca931-0ecc-46c0-8097-ffb383db6287";s:8:"langcode";s:2:"en";s:6:"status";b:1;s:12:"dependencies";a:2:{s:6:"config";a:6:{i:0;s:29:"field.field.node.article.body";i:1;s:32:"field.field.node.article.comment";i:2;s:36:"field.field.node.article.field_image";i:3;s:35:"field.field.node.article.field_tags";i:4;s:21:"image.style.thumbnail";i:5;s:17:"node.type.article";}s:6:"module";a:5:{i:0;s:7:"comment";i:1;s:18:"content_moderation";i:2;s:5:"image";i:3;s:4:"path";i:4;s:4:"text";}}s:5:"_core";a:1:{s:19:"default_config_hash";s:43:"buc38w3gxCqFnjINJhMiJvPpj9jWflKvlKDyBVMPVvw";}s:2:"id";s:20:"node.article.default";s:16:"targetEntityType";s:4:"node";s:6:"bundle";s:7:"article";s:4:"mode";s:7:"default";s:7:"content";a:12:{s:4:"body";a:5:{s:4:"type";s:26:"text_textarea_with_summary";s:6:"weight";i:1;s:6:"region";s:7:"content";s:8:"settings";a:3:{s:4:"rows";i:9;s:12:"summary_rows";i:3;s:11:"placeholder";s:0:"";}s:20:"third_party_settings";a:0:{}}s:7:"comment";a:5:{s:4:"type";s:15:"comment_default";s:6:"weight";i:20;s:6:"region";s:7:"content";s:8:"settings";a:0:{}s:20:"third_party_settings";a:0:{}}s:7:"created";a:5:{s:4:"type";s:18:"datetime_timestamp";s:6:"weight";i:10;s:6:"region";s:7:"content";s:8:"settings";a:0:{}s:20:"third_party_settings";a:0:{}}s:11:"field_image";a:5:{s:4:"type";s:11:"image_image";s:6:"weight";i:4;s:6:"region";s:7:"content";s:8:"settings";a:2:{s:18:"progress_indicator";s:8:"throbber";s:19:"preview_image_style";s:9:"thumbnail";}s:20:"third_party_settings";a:0:{}}s:10:"field_tags";a:5:{s:4:"type";s:34:"entity_reference_autocomplete_tags";s:6:"weight";i:3;s:6:"region";s:7:"content";s:8:"settings";a:3:{s:14:"match_operator";s:8:"CONTAINS";s:4:"size";i:60;s:11:"placeholder";s:0:"";}s:20:"third_party_settings";a:0:{}}s:16:"moderation_state";a:5:{s:4:"type";s:24:"moderation_state_default";s:6:"weight";i:100;s:8:"settings";a:0:{}s:6:"region";s:7:"content";s:20:"third_party_settings";a:0:{}}s:4:"path";a:5:{s:4:"type";s:4:"path";s:6:"weight";i:30;s:6:"region";s:7:"content";s:8:"settings";a:0:{}s:20:"third_party_settings";a:0:{}}s:7:"promote";a:5:{s:4:"type";s:16:"boolean_checkbox";s:8:"settings";a:1:{s:13:"display_label";b:1;}s:6:"weight";i:15;s:6:"region";s:7:"content";s:20:"third_party_settings";a:0:{}}s:6:"status";a:5:{s:4:"type";s:16:"boolean_checkbox";s:8:"settings";a:1:{s:13:"display_label";b:1;}s:6:"weight";i:120;s:6:"region";s:7:"content";s:20:"third_party_settings";a:0:{}}s:6:"sticky";a:5:{s:4:"type";s:16:"boolean_checkbox";s:8:"settings";a:1:{s:13:"display_label";b:1;}s:6:"weight";i:16;s:6:"region";s:7:"content";s:20:"third_party_settings";a:0:{}}s:5:"title";a:5:{s:4:"type";s:16:"string_textfield";s:6:"weight";i:0;s:6:"region";s:7:"content";s:8:"settings";a:2:{s:4:"size";i:60;s:11:"placeholder";s:0:"";}s:20:"third_party_settings";a:0:{}}s:3:"uid";a:5:{s:4:"type";s:29:"entity_reference_autocomplete";s:6:"weight";i:5;s:6:"region";s:7:"content";s:8:"settings";a:3:{s:14:"match_operator";s:8:"CONTAINS";s:4:"size";i:60;s:11:"placeholder";s:0:"";}s:20:"third_party_settings";a:0:{}}}s:6:"hidden";a:0:{}}',
])
->condition('name', 'core.entity_form_display.node.article.default')
->execute();
$connection->update('config')
->fields([
'data' => 'a:9:{s:4:"uuid";s:36:"08b548c7-ff59-468b-9347-7d697680d035";s:8:"langcode";s:2:"en";s:6:"status";b:1;s:12:"dependencies";a:2:{s:6:"config";a:2:{i:0;s:17:"node.type.article";i:1;s:14:"node.type.page";}s:6:"module";a:1:{i:0;s:18:"content_moderation";}}s:5:"_core";a:1:{s:19:"default_config_hash";s:43:"T_JxNjYlfoRBi7Bj1zs5Xv9xv1btuBkKp5C1tNrjMhI";}s:2:"id";s:9:"editorial";s:5:"label";s:9:"Editorial";s:4:"type";s:18:"content_moderation";s:13:"type_settings";a:3:{s:6:"states";a:3:{s:8:"archived";a:4:{s:5:"label";s:8:"Archived";s:6:"weight";i:5;s:9:"published";b:0;s:16:"default_revision";b:1;}s:5:"draft";a:4:{s:5:"label";s:5:"Draft";s:9:"published";b:0;s:16:"default_revision";b:0;s:6:"weight";i:-5;}s:9:"published";a:4:{s:5:"label";s:9:"Published";s:9:"published";b:1;s:16:"default_revision";b:1;s:6:"weight";i:0;}}s:11:"transitions";a:5:{s:7:"archive";a:4:{s:5:"label";s:7:"Archive";s:4:"from";a:1:{i:0;s:9:"published";}s:2:"to";s:8:"archived";s:6:"weight";i:2;}s:14:"archived_draft";a:4:{s:5:"label";s:16:"Restore to Draft";s:4:"from";a:1:{i:0;s:8:"archived";}s:2:"to";s:5:"draft";s:6:"weight";i:3;}s:18:"archived_published";a:4:{s:5:"label";s:7:"Restore";s:4:"from";a:1:{i:0;s:8:"archived";}s:2:"to";s:9:"published";s:6:"weight";i:4;}s:16:"create_new_draft";a:4:{s:5:"label";s:16:"Create New Draft";s:2:"to";s:5:"draft";s:6:"weight";i:0;s:4:"from";a:2:{i:0;s:5:"draft";i:1;s:9:"published";}}s:7:"publish";a:4:{s:5:"label";s:7:"Publish";s:2:"to";s:9:"published";s:6:"weight";i:1;s:4:"from";a:2:{i:0;s:5:"draft";i:1;s:9:"published";}}}s:12:"entity_types";a:1:{s:4:"node";a:2:{i:0;s:7:"article";i:1;s:4:"page";}}}}',
])
->condition('name', 'workflows.workflow.editorial')
->execute();
<?php
namespace Drupal\Tests\content_moderation\Functional;
use Drupal\Core\Entity\Entity\EntityFormDisplay;
use Drupal\FunctionalTests\Update\UpdatePathTestBase;
/**
* Test updating the dependencies of entity form displays.
*
* @group Update
* @group legacy
*
* @see content_moderation_post_update_entity_display_dependencies()
*/
class EntityFormDisplayDependenciesUpdateTest extends UpdatePathTestBase {
/**
* {@inheritdoc}
*/
protected function setDatabaseDumpFiles() {
$this->databaseDumpFiles = [
__DIR__ . '/../../../../system/tests/fixtures/update/drupal-8.4.0.bare.standard.php.gz',
__DIR__ . '/../../fixtures/update/drupal-8.4.0-content_moderation_installed.php',
__DIR__ . '/../../fixtures/update/drupal-8.entity-form-display-dependencies-2915383.php',
];
}
/**
* Tests updating the dependencies of entity displays.
*/
public function testEntityDisplaysUpdated() {
$no_moderation_form_display = EntityFormDisplay::load('block_content.basic.default');
$has_moderation_form_display = EntityFormDisplay::load('node.article.default');
// Assert the moderation field and content_moderation dependency exists on
// an entity type that does not have moderation enabled, these will be
// removed.
$this->assertEquals('moderation_state_default', $no_moderation_form_display->getComponent('moderation_state')['type']);
$this->assertTrue(in_array('content_moderation', $no_moderation_form_display->getDependencies()['module']));
// Assert the editorial config dependency doesn't exist on the entity form
// with moderation, this will be added.
$this->assertFalse(in_array('workflows.workflow.editorial', $has_moderation_form_display->getDependencies()['config']));
$this->runUpdates();
$no_moderation_form_display = EntityFormDisplay::load('block_content.basic.default');
$has_moderation_form_display = EntityFormDisplay::load('node.article.default');
// The moderation_state field has been removed from the non-moderated block
// entity form display.
$this->assertEquals(NULL, $no_moderation_form_display->getComponent('moderation_state'));
$this->assertFalse(in_array('content_moderation', $no_moderation_form_display->getDependencies()['module']));
// The editorial workflow config dependency has been added to moderated
// form display.
$this->assertTrue(in_array('workflows.workflow.editorial', $has_moderation_form_display->getDependencies()['config']));
}
}
......@@ -20,17 +20,33 @@ class ModerationStateAccessTest extends BrowserTestBase {
* {@inheritdoc}
*/
public static $modules = [
'content_moderation_test_views',
'content_moderation',
'node',
];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$node_type = NodeType::create([
'type' => 'test',
'label' => 'Test',
]);
$node_type->save();
$workflow = $this->createEditorialWorkflow();
$workflow->getTypePlugin()->addEntityTypeAndBundle('node', 'test');
$workflow->save();
$this->container->get('module_installer')->install(['content_moderation_test_views']);
}
/**
* Test the view operation access handler with the view permission.
*/
public function testViewShowsCorrectStates() {
$node_type_id = 'test';
$this->createNodeType('Test', $node_type_id);
$permissions = [
'access content',
'view all revisions',
......@@ -39,7 +55,7 @@ public function testViewShowsCorrectStates() {
$this->drupalLogin($editor1);
$node_1 = Node::create([
'type' => $node_type_id,
'type' => 'test',
'title' => 'Draft node',
'uid' => $editor1->id(),
]);
......@@ -47,7 +63,7 @@ public function testViewShowsCorrectStates() {
$node_1->save();
$node_2 = Node::create([
'type' => $node_type_id,
'type' => 'test',
'title' => 'Published node',
'uid' => $editor1->id(),
]);
......@@ -82,29 +98,4 @@ public function testViewShowsCorrectStates() {
$this->assertFalse($page->hasContent('Published'));
}
/**
* 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 \Drupal\node\Entity\NodeType
* The node type just created.
*/
protected function createNodeType($label, $machine_name) {
/** @var \Drupal\node\Entity\NodeType $node_type */
$node_type = NodeType::create([
'type' => $machine_name,
'label' => $label,
]);
$node_type->save();
$workflow = $this->createEditorialWorkflow();
$workflow->getTypePlugin()->addEntityTypeAndBundle('node', $machine_name);
$workflow->save();
return $node_type;
}
}
......@@ -71,7 +71,7 @@ public function testCreatingContent() {
if (!$node) {
$this->fail('Non-moderated test node was not saved correctly.');
}
$this->assertEqual(NULL, $node->moderation_state->value);
$this->assertFalse($node->hasField('moderation_state'));
}
/**
......
......@@ -5,8 +5,8 @@
use Drupal\node\Entity\NodeType;
use Drupal\Tests\content_moderation\Traits\ContentModerationTestTrait;
use Drupal\Tests\views\Functional\ViewTestBase;
use Drupal\views\ViewExecutable;
use Drupal\views\Views;
use Drupal\views\Entity\View;
use Drupal\views\ViewEntityInterface;
use Drupal\workflows\Entity\Workflow;
/**
......@@ -24,7 +24,6 @@ class ViewsModerationStateFilterTest extends ViewTestBase {
* {@inheritdoc}
*/
public static $modules = [
'content_moderation_test_views',
'node',
'content_moderation',
'workflows',
......@@ -47,6 +46,9 @@ protected function setUp($import_test_views = TRUE) {
NodeType::create([
'type' => 'example_b',
])->save();
NodeType::create([
'type' => 'example_c',
])->save();
$this->createEditorialWorkflow();
......@@ -56,9 +58,15 @@ protected function setUp($import_test_views = TRUE) {
'label' => 'New workflow',
]);
$new_workflow->getTypePlugin()->addState('bar', 'Bar');
$new_workflow->getTypePlugin()->addEntityTypeAndBundle('node', 'example_c');
$new_workflow->save();
$this->drupalLogin($this->drupalCreateUser(['administer workflows', 'administer views']));
$this->container->get('module_installer')->install(['content_moderation_test_views']);
$new_workflow->getTypePlugin()->removeEntityTypeAndBundle('node', 'example_c');
$new_workflow->save();
}
/**
......@@ -71,10 +79,10 @@ public function testModerationStateFilterDependencyHandling() {
// First, check that the view doesn't have any config dependency when there
// are no states configured in the filter.
$view_id = 'test_content_moderation_state_filter_base_table';
$view = Views::getView($view_id);
$view = View::load($view_id);
$this->assertWorkflowDependencies([], $view);
$this->assertTrue($view->storage->status());
$this->assertTrue($view->status());
// Configure the Editorial workflow for a node bundle, set the filter value
// to use one of its states and check that the workflow is now a dependency
......@@ -87,9 +95,9 @@ public function testModerationStateFilterDependencyHandling() {
$this->drupalPostForm("admin/structure/views/nojs/handler/$view_id/default/filter/moderation_state", $edit, 'Apply');
$this->drupalPostForm("admin/structure/views/view/$view_id", [], 'Save');
$view = Views::getView($view_id);
$view = $this->loadViewUnchanged($view_id);
$this->assertWorkflowDependencies(['editorial'], $view);
$this->assertTrue($view->storage->status());
$this->assertTrue($view->status());
// Create another workflow and repeat the checks above.
$this->drupalPostForm('admin/config/workflow/workflows/add', [
......@@ -109,35 +117,44 @@ public function testModerationStateFilterDependencyHandling() {
$this->drupalPostForm("admin/structure/views/nojs/handler/$view_id/default/filter/moderation_state", $edit, 'Apply');
$this->drupalPostForm("admin/structure/views/view/$view_id", [], 'Save');
$view = Views::getView($view_id);
$view = $this->loadViewUnchanged($view_id);
$this->assertWorkflowDependencies(['editorial', 'translation'], $view);
$this->assertTrue(isset($view->storage->getDisplay('default')['display_options']['filters']['moderation_state']));
$this->assertTrue($view->storage->status());
$this->assertTrue(isset($view->getDisplay('default')['display_options']['filters']['moderation_state']));
$this->assertTrue($view->status());
// Remove the 'Translation' workflow.
$this->drupalPostForm('admin/config/workflow/workflows/manage/translation/delete', [], 'Delete');
// Check that the view has been disabled, the filter has been deleted, the
// view can be saved and there are no more config dependencies.
$view = Views::getView($view_id);
$this->assertFalse($view->storage->status());
$this->assertFalse(isset($view->storage->getDisplay('default')['display_options']['filters']['moderation_state']));
$view = $this->loadViewUnchanged($view_id);
$this->assertFalse($view->status());
$this->assertFalse(isset($view->getDisplay('default')['display_options']['filters']['moderation_state']));
$this->drupalPostForm("admin/structure/views/view/$view_id", [], 'Save');
$this->assertWorkflowDependencies([], $view);
}
/**
* Load a view from the database after it has been modified in a sub-request.
*
* @param string $view_id
* The view ID.
*
* @return \Drupal\views\ViewEntityInterface
* A loaded view, bypassing static caches.
*/
public function loadViewUnchanged($view_id) {
$this->container->get('cache.config')->deleteAll();
$this->container->get('config.factory')->reset();
return $this->container->get('entity_type.manager')->getStorage('view')->loadUnchanged($view_id);
}
/**
* Tests the moderation state filter when the configured workflow is changed.
*
* @dataProvider providerTestWorkflowChanges
*/
public function testWorkflowChanges($view_id, $filter_name) {
// Update the view and make the default filter not exposed anymore,
// otherwise all results will be shown when there are no more moderated
// bundles left.
$this->drupalPostForm("admin/structure/views/nojs/handler/$view_id/default/filter/moderation_state", [], 'Hide filter');
$this->drupalPostForm("admin/structure/views/view/$view_id", [], 'Save');
public function testWorkflowChanges($view_id) {
// First, apply the Editorial workflow to both of our content types.
$this->drupalPostForm('admin/config/workflow/workflows/manage/editorial/type/node', [
'bundles[example_a]' => TRUE,
......@@ -145,6 +162,12 @@ public function testWorkflowChanges($view_id, $filter_name) {
], 'Save');
\Drupal::service('entity_type.bundle.info')->clearCachedBundles();
// Update the view and make the default filter not exposed anymore,
// otherwise all results will be shown when there are no more moderated
// bundles left.
$this->drupalPostForm("admin/structure/views/nojs/handler/$view_id/default/filter/moderation_state", [], 'Hide filter');
$this->drupalPostForm("admin/structure/views/view/$view_id", [], 'Save');
// Add a few nodes in various moderation states.
$this->createNode(['type' => 'example_a', 'moderation_state' => 'published']);
$this->createNode(['type' => 'example_b', 'moderation_state' => 'published']);
......@@ -158,9 +181,8 @@ public function testWorkflowChanges($view_id, $filter_name) {
// Check that only the archived nodes from both bundles are displayed by the
// view.
$view = Views::getView($view_id);
$this->executeView($view);
$this->assertIdenticalResultset($view, [['nid' => $archived_node_a->id()], ['nid' => $archived_node_b->id()]], ['nid' => 'nid']);
$view = $this->loadViewUnchanged($view_id);
$this->executeAndAssertIdenticalResultset($view, [['nid' => $archived_node_a->id()], ['nid' => $archived_node_b->id()]], ['nid' => 'nid']);
// Remove the Editorial workflow from one of the bundles.
$this->drupalPostForm('admin/config/workflow/workflows/manage/editorial/type/node', [
......@@ -169,9 +191,8 @@ public function testWorkflowChanges($view_id, $filter_name) {
], 'Save');
\Drupal::service('entity_type.bundle.info')->clearCachedBundles();
$view = Views::getView($view_id);
$this->executeView($view);
$this->assertIdenticalResultset($view, [['nid' => $archived_node_a->id()]], ['nid' => 'nid']);
$view = $this->loadViewUnchanged($view_id);
$this->executeAndAssertIdenticalResultset($view, [['nid' => $archived_node_a->id()]], ['nid' => 'nid']);
// Check that the view can still be edited and saved without any
// intervention.
......@@ -184,16 +205,31 @@ public function testWorkflowChanges($view_id, $filter_name) {
], 'Save');