Update core 8.3.0
This commit is contained in:
parent
da7a7918f8
commit
cd7a898e66
6144 changed files with 132297 additions and 87747 deletions
|
@ -0,0 +1,9 @@
|
|||
name: 'Content moderation test local task'
|
||||
type: module
|
||||
description: 'Provides a local task for testing.'
|
||||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
dependencies:
|
||||
- content_moderation
|
||||
- node
|
|
@ -0,0 +1,4 @@
|
|||
entity.node.test_local_task_without_upcast_node:
|
||||
route_name: entity.node.test_local_task_without_upcast_node
|
||||
base_route: entity.node.canonical
|
||||
title: 'Task Without Upcast Node'
|
|
@ -0,0 +1,7 @@
|
|||
entity.node.test_local_task_without_upcast_node:
|
||||
path: '/node/{node}/task-without-upcast-node'
|
||||
defaults:
|
||||
_title: 'Page Without Upcast Node'
|
||||
_controller: '\Drupal\content_moderation_test_local_task\Controller\TestLocalTaskController::methodWithoutUpcastNode'
|
||||
requirements:
|
||||
_access: 'TRUE'
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\content_moderation_test_local_task\Controller;
|
||||
|
||||
/**
|
||||
* A test controller.
|
||||
*/
|
||||
class TestLocalTaskController {
|
||||
|
||||
/**
|
||||
* A method which does not hint the node parameter to avoid upcasting.
|
||||
*/
|
||||
public function methodWithoutUpcastNode($node) {
|
||||
return ['#markup' => 'It works!'];
|
||||
}
|
||||
|
||||
}
|
|
@ -300,9 +300,7 @@ display:
|
|||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
click_sort_column: target_id
|
||||
type: entity_reference_label
|
||||
settings:
|
||||
link: true
|
||||
type: content_moderation_state
|
||||
group_column: target_id
|
||||
group_columns: { }
|
||||
group_rows: true
|
||||
|
|
|
@ -193,9 +193,7 @@ display:
|
|||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
click_sort_column: target_id
|
||||
type: entity_reference_label
|
||||
settings:
|
||||
link: false
|
||||
type: content_moderation_state
|
||||
group_column: target_id
|
||||
group_columns: { }
|
||||
group_rows: true
|
||||
|
@ -258,9 +256,7 @@ display:
|
|||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
click_sort_column: target_id
|
||||
type: entity_reference_label
|
||||
settings:
|
||||
link: false
|
||||
type: content_moderation_state
|
||||
group_column: target_id
|
||||
group_columns: { }
|
||||
group_rows: true
|
||||
|
@ -323,8 +319,7 @@ display:
|
|||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
click_sort_column: target_id
|
||||
type: entity_reference_entity_id
|
||||
settings: { }
|
||||
type: string
|
||||
group_column: target_id
|
||||
group_columns: { }
|
||||
group_rows: true
|
||||
|
|
|
@ -306,7 +306,7 @@ display:
|
|||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
click_sort_column: target_id
|
||||
type: entity_reference_entity_id
|
||||
type: string
|
||||
settings: { }
|
||||
group_column: target_id
|
||||
group_columns: { }
|
||||
|
@ -370,7 +370,7 @@ display:
|
|||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
click_sort_column: target_id
|
||||
type: entity_reference_entity_id
|
||||
type: string
|
||||
settings: { }
|
||||
group_column: target_id
|
||||
group_columns: { }
|
||||
|
|
|
@ -191,7 +191,7 @@ display:
|
|||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
click_sort_column: target_id
|
||||
type: entity_reference_entity_id
|
||||
type: string
|
||||
settings: { }
|
||||
group_column: target_id
|
||||
group_columns: { }
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\content_moderation\Functional;
|
||||
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Test the workflow type plugin in the content_moderation module.
|
||||
*
|
||||
* @group content_moderation
|
||||
*/
|
||||
class ContentModerationWorkflowTypeTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = [
|
||||
'content_moderation',
|
||||
'node',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$admin = $this->drupalCreateUser([
|
||||
'administer workflows',
|
||||
]);
|
||||
$this->drupalLogin($admin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test creating a new workflow using the content moderation plugin.
|
||||
*/
|
||||
public function testNewWorkflow() {
|
||||
$entity_bundle_info = \Drupal::service('entity_type.bundle.info');
|
||||
|
||||
$this->drupalPostForm('admin/config/workflow/workflows/add', [
|
||||
'label' => 'Test Workflow',
|
||||
'id' => 'test_workflow',
|
||||
'workflow_type' => 'content_moderation',
|
||||
], 'Save');
|
||||
|
||||
// Make sure the test workflow includes the default states and transitions.
|
||||
$this->assertSession()->pageTextContains('Draft');
|
||||
$this->assertSession()->pageTextContains('Published');
|
||||
$this->assertSession()->pageTextContains('Create New Draft');
|
||||
$this->assertSession()->pageTextContains('Publish');
|
||||
|
||||
// Ensure after a workflow is created, the bundle information can be
|
||||
// refreshed.
|
||||
$entity_bundle_info->clearCachedBundles();
|
||||
$this->assertNotEmpty($entity_bundle_info->getAllBundleInfo());
|
||||
|
||||
$this->clickLink('Add a new state');
|
||||
$this->submitForm([
|
||||
'label' => 'Test State',
|
||||
'id' => 'test_state',
|
||||
'type_settings[content_moderation][published]' => TRUE,
|
||||
'type_settings[content_moderation][default_revision]' => FALSE,
|
||||
], 'Save');
|
||||
$this->assertSession()->pageTextContains('Created Test State state.');
|
||||
|
||||
// Ensure that the published settings cannot be changed.
|
||||
$this->drupalGet('admin/config/workflow/workflows/manage/test_workflow/state/published');
|
||||
$this->assertSession()->fieldDisabled('type_settings[content_moderation][published]');
|
||||
$this->assertSession()->fieldDisabled('type_settings[content_moderation][default_revision]');
|
||||
|
||||
// Ensure that the draft settings cannot be changed.
|
||||
$this->drupalGet('admin/config/workflow/workflows/manage/test_workflow/state/draft');
|
||||
$this->assertSession()->fieldDisabled('type_settings[content_moderation][published]');
|
||||
$this->assertSession()->fieldDisabled('type_settings[content_moderation][default_revision]');
|
||||
}
|
||||
|
||||
}
|
|
@ -5,6 +5,7 @@ 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.
|
||||
|
@ -25,7 +26,7 @@ class LatestRevisionViewsFilterTest extends BrowserTestBase {
|
|||
* Tests view shows the correct node IDs.
|
||||
*/
|
||||
public function testViewShowsCorrectNids() {
|
||||
$node_type = $this->createNodeType('Test', 'test');
|
||||
$this->createNodeType('Test', 'test');
|
||||
|
||||
$permissions = [
|
||||
'access content',
|
||||
|
@ -45,8 +46,9 @@ class LatestRevisionViewsFilterTest extends BrowserTestBase {
|
|||
$node_0->save();
|
||||
|
||||
// Now enable moderation for subsequent nodes.
|
||||
$node_type->setThirdPartySetting('content_moderation', 'enabled', TRUE);
|
||||
$node_type->save();
|
||||
$workflow = Workflow::load('editorial');
|
||||
$workflow->getTypePlugin()->addEntityTypeAndBundle('node', 'test');
|
||||
$workflow->save();
|
||||
|
||||
// Make a node that is only ever in Draft.
|
||||
/** @var Node $node_1 */
|
||||
|
@ -55,7 +57,7 @@ class LatestRevisionViewsFilterTest extends BrowserTestBase {
|
|||
'title' => 'Node 1 - Rev 1',
|
||||
'uid' => $editor1->id(),
|
||||
]);
|
||||
$node_1->moderation_state->target_id = 'draft';
|
||||
$node_1->moderation_state->value = 'draft';
|
||||
$node_1->save();
|
||||
|
||||
// Make a node that is in Draft, then Published.
|
||||
|
@ -65,11 +67,11 @@ class LatestRevisionViewsFilterTest extends BrowserTestBase {
|
|||
'title' => 'Node 2 - Rev 1',
|
||||
'uid' => $editor1->id(),
|
||||
]);
|
||||
$node_2->moderation_state->target_id = 'draft';
|
||||
$node_2->moderation_state->value = 'draft';
|
||||
$node_2->save();
|
||||
|
||||
$node_2->setTitle('Node 2 - Rev 2');
|
||||
$node_2->moderation_state->target_id = 'published';
|
||||
$node_2->moderation_state->value = 'published';
|
||||
$node_2->save();
|
||||
|
||||
// Make a node that is in Draft, then Published, then Draft.
|
||||
|
@ -79,15 +81,15 @@ class LatestRevisionViewsFilterTest extends BrowserTestBase {
|
|||
'title' => 'Node 3 - Rev 1',
|
||||
'uid' => $editor1->id(),
|
||||
]);
|
||||
$node_3->moderation_state->target_id = 'draft';
|
||||
$node_3->moderation_state->value = 'draft';
|
||||
$node_3->save();
|
||||
|
||||
$node_3->setTitle('Node 3 - Rev 2');
|
||||
$node_3->moderation_state->target_id = 'published';
|
||||
$node_3->moderation_state->value = 'published';
|
||||
$node_3->save();
|
||||
|
||||
$node_3->setTitle('Node 3 - Rev 3');
|
||||
$node_3->moderation_state->target_id = 'draft';
|
||||
$node_3->moderation_state->value = 'draft';
|
||||
$node_3->save();
|
||||
|
||||
// Now show the View, and confirm that only the correct titles are showing.
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\content_moderation\Functional;
|
||||
|
||||
use Drupal\simpletest\ContentTypeCreationTrait;
|
||||
use Drupal\simpletest\NodeCreationTrait;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\workflows\Entity\Workflow;
|
||||
|
||||
/**
|
||||
* Test the content moderation local task.
|
||||
*
|
||||
* @group content_moderation
|
||||
*/
|
||||
class LocalTaskTest extends BrowserTestBase {
|
||||
|
||||
use ContentTypeCreationTrait;
|
||||
use NodeCreationTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'content_moderation_test_local_task',
|
||||
'content_moderation',
|
||||
'block',
|
||||
];
|
||||
|
||||
/**
|
||||
* A test node.
|
||||
*
|
||||
* @var \Drupal\node\NodeInterface
|
||||
*/
|
||||
protected $testNode;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->drupalPlaceBlock('local_tasks_block', ['id' => 'tabs_block']);
|
||||
$this->drupalLogin($this->createUser(['bypass node access']));
|
||||
|
||||
$node_type = $this->createContentType([
|
||||
'type' => 'test_content_type',
|
||||
]);
|
||||
|
||||
// Now enable moderation for subsequent nodes.
|
||||
$workflow = Workflow::load('editorial');
|
||||
$workflow->getTypePlugin()->addEntityTypeAndBundle('node', $node_type->id());
|
||||
$workflow->save();
|
||||
|
||||
$this->testNode = $this->createNode([
|
||||
'type' => $node_type->id(),
|
||||
'moderation_state' => 'draft',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests local tasks behave with content_moderation enabled.
|
||||
*/
|
||||
public function testLocalTasks() {
|
||||
// The default state is a draft.
|
||||
$this->drupalGet(sprintf('node/%s', $this->testNode->id()));
|
||||
$this->assertTasks('Edit draft');
|
||||
|
||||
// When published as the live revision, the label changes.
|
||||
$this->testNode->moderation_state = 'published';
|
||||
$this->testNode->save();
|
||||
$this->drupalGet(sprintf('node/%s', $this->testNode->id()));
|
||||
$this->assertTasks('New draft');
|
||||
|
||||
$tags = $this->drupalGetHeader('X-Drupal-Cache-Tags');
|
||||
$this->assertContains('node:1', $tags);
|
||||
$this->assertContains('node_type:test_content_type', $tags);
|
||||
|
||||
// Without an upcast node, the state cannot be determined.
|
||||
$this->clickLink('Task Without Upcast Node');
|
||||
$this->assertTasks('Edit');
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert the correct tasks appear.
|
||||
*
|
||||
* @param string $edit_tab_label
|
||||
* The edit tab label to assert.
|
||||
*/
|
||||
protected function assertTasks($edit_tab_label) {
|
||||
$this->assertSession()->linkExists('View');
|
||||
$this->assertSession()->linkExists('Task Without Upcast Node');
|
||||
$this->assertSession()->linkExists($edit_tab_label);
|
||||
$this->assertSession()->linkExists('Delete');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,137 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\content_moderation\Functional;
|
||||
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\simpletest\ContentTypeCreationTrait;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\workflows\Entity\Workflow;
|
||||
|
||||
/**
|
||||
* Test the content moderation actions.
|
||||
*
|
||||
* @group content_moderation
|
||||
*/
|
||||
class ModerationActionsTest extends BrowserTestBase {
|
||||
|
||||
use ContentTypeCreationTrait;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = [
|
||||
'content_moderation',
|
||||
'node',
|
||||
'views',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$moderated_bundle = $this->createContentType(['type' => 'moderated_bundle']);
|
||||
$moderated_bundle->save();
|
||||
$standard_bundle = $this->createContentType(['type' => 'standard_bundle']);
|
||||
$standard_bundle->save();
|
||||
|
||||
$workflow = Workflow::load('editorial');
|
||||
$workflow->getTypePlugin()->addEntityTypeAndBundle('node', 'moderated_bundle');
|
||||
$workflow->save();
|
||||
|
||||
$admin = $this->drupalCreateUser([
|
||||
'access content overview',
|
||||
'administer nodes',
|
||||
'bypass node access',
|
||||
]);
|
||||
$this->drupalLogin($admin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the node status actions report moderation status to users correctly.
|
||||
*
|
||||
* @dataProvider nodeStatusActionsTestCases
|
||||
*/
|
||||
public function testNodeStatusActions($action, $bundle, $warning_appears, $starting_status, $final_status) {
|
||||
// Create and run an action on a node.
|
||||
$node = Node::create([
|
||||
'type' => $bundle,
|
||||
'title' => $this->randomString(),
|
||||
'status' => $starting_status,
|
||||
]);
|
||||
if ($bundle == 'moderated_bundle') {
|
||||
$node->moderation_state->value = $starting_status ? 'published' : 'draft';
|
||||
}
|
||||
$node->save();
|
||||
|
||||
$this->drupalPostForm('admin/content', [
|
||||
'node_bulk_form[0]' => TRUE,
|
||||
'action' => $action,
|
||||
], 'Apply to selected items');
|
||||
|
||||
if ($warning_appears) {
|
||||
if ($action == 'node_publish_action') {
|
||||
$this->assertSession()
|
||||
->elementContains('css', '.messages--warning', node_get_type_label($node) . ' content items were skipped as they are under moderation and may not be directly published.');
|
||||
}
|
||||
else {
|
||||
$this->assertSession()
|
||||
->elementContains('css', '.messages--warning', node_get_type_label($node) . ' content items were skipped as they are under moderation and may not be directly unpublished.');
|
||||
}
|
||||
}
|
||||
else {
|
||||
$this->assertSession()->elementNotExists('css', '.messages--warning');
|
||||
}
|
||||
|
||||
// Ensure after the action has run, the node matches the expected status.
|
||||
$node = Node::load($node->id());
|
||||
$this->assertEquals($node->isPublished(), $final_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test cases for ::testNodeStatusActions.
|
||||
*
|
||||
* @return array
|
||||
* An array of test cases.
|
||||
*/
|
||||
public function nodeStatusActionsTestCases() {
|
||||
return [
|
||||
'Moderated bundle shows warning (publish action)' => [
|
||||
'node_publish_action',
|
||||
'moderated_bundle',
|
||||
TRUE,
|
||||
// If the node starts out unpublished, the action should not work.
|
||||
FALSE,
|
||||
FALSE,
|
||||
],
|
||||
'Moderated bundle shows warning (unpublish action)' => [
|
||||
'node_unpublish_action',
|
||||
'moderated_bundle',
|
||||
TRUE,
|
||||
// If the node starts out published, the action should not work.
|
||||
TRUE,
|
||||
TRUE,
|
||||
],
|
||||
'Normal bundle works (publish action)' => [
|
||||
'node_publish_action',
|
||||
'standard_bundle',
|
||||
FALSE,
|
||||
// If the node starts out unpublished, the action should work.
|
||||
FALSE,
|
||||
TRUE,
|
||||
],
|
||||
'Normal bundle works (unpublish action)' => [
|
||||
'node_unpublish_action',
|
||||
'standard_bundle',
|
||||
FALSE,
|
||||
// If the node starts out published, the action should work.
|
||||
TRUE,
|
||||
FALSE,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,215 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\content_moderation\Functional;
|
||||
|
||||
/**
|
||||
* Test content_moderation functionality with localization and translation.
|
||||
*
|
||||
* @group content_moderation
|
||||
*/
|
||||
class ModerationLocaleTest extends ModerationStateTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = [
|
||||
'node',
|
||||
'content_moderation',
|
||||
'locale',
|
||||
'content_translation',
|
||||
];
|
||||
|
||||
/**
|
||||
* Tests article translations can be moderated separately.
|
||||
*/
|
||||
public function testTranslateModeratedContent() {
|
||||
$this->drupalLogin($this->rootUser);
|
||||
|
||||
// Enable moderation on Article node type.
|
||||
$this->createContentTypeFromUi('Article', 'article', TRUE);
|
||||
|
||||
// Add French language.
|
||||
$edit = [
|
||||
'predefined_langcode' => 'fr',
|
||||
];
|
||||
$this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add language'));
|
||||
|
||||
// Enable content translation on articles.
|
||||
$this->drupalGet('admin/config/regional/content-language');
|
||||
$edit = [
|
||||
'entity_types[node]' => TRUE,
|
||||
'settings[node][article][translatable]' => TRUE,
|
||||
'settings[node][article][settings][language][language_alterable]' => TRUE,
|
||||
];
|
||||
$this->drupalPostForm(NULL, $edit, t('Save configuration'));
|
||||
|
||||
// Adding languages requires a container rebuild in the test running
|
||||
// environment so that multilingual services are used.
|
||||
$this->rebuildContainer();
|
||||
|
||||
// Create a published article in English.
|
||||
$edit = [
|
||||
'title[0][value]' => 'Published English node',
|
||||
'langcode[0][value]' => 'en',
|
||||
];
|
||||
$this->drupalPostForm('node/add/article', $edit, t('Save and Publish'));
|
||||
$this->assertText(t('Article Published English node has been created.'));
|
||||
$english_node = $this->drupalGetNodeByTitle('Published English node');
|
||||
|
||||
// Add a French translation.
|
||||
$this->drupalGet('node/' . $english_node->id() . '/translations');
|
||||
$this->clickLink(t('Add'));
|
||||
$edit = [
|
||||
'title[0][value]' => 'French node Draft',
|
||||
];
|
||||
$this->drupalPostForm(NULL, $edit, t('Save and Create New Draft (this translation)'));
|
||||
// Here the error has occurred "The website encountered an unexpected error.
|
||||
// Please try again later."
|
||||
// If the translation has got lost.
|
||||
$this->assertText(t('Article French node Draft has been updated.'));
|
||||
|
||||
// Create an article in English.
|
||||
$edit = [
|
||||
'title[0][value]' => 'English node',
|
||||
'langcode[0][value]' => 'en',
|
||||
];
|
||||
$this->drupalPostForm('node/add/article', $edit, t('Save and Create New Draft'));
|
||||
$this->assertText(t('Article English node has been created.'));
|
||||
$english_node = $this->drupalGetNodeByTitle('English node');
|
||||
|
||||
// Add a French translation.
|
||||
$this->drupalGet('node/' . $english_node->id() . '/translations');
|
||||
$this->clickLink(t('Add'));
|
||||
$edit = [
|
||||
'title[0][value]' => 'French node',
|
||||
];
|
||||
$this->drupalPostForm(NULL, $edit, t('Save and Create New Draft (this translation)'));
|
||||
$this->assertText(t('Article French node has been updated.'));
|
||||
$english_node = $this->drupalGetNodeByTitle('English node', TRUE);
|
||||
|
||||
// Publish the English article and check that the translation stays
|
||||
// unpublished.
|
||||
$this->drupalPostForm('node/' . $english_node->id() . '/edit', [], t('Save and Publish (this translation)'));
|
||||
$this->assertText(t('Article English node has been updated.'));
|
||||
$english_node = $this->drupalGetNodeByTitle('English node', TRUE);
|
||||
$french_node = $english_node->getTranslation('fr');
|
||||
$this->assertEqual('French node', $french_node->label());
|
||||
|
||||
$this->assertEqual($english_node->moderation_state->value, 'published');
|
||||
$this->assertTrue($english_node->isPublished());
|
||||
$this->assertEqual($french_node->moderation_state->value, 'draft');
|
||||
$this->assertFalse($french_node->isPublished());
|
||||
|
||||
// Create another article with its translation. This time we will publish
|
||||
// the translation first.
|
||||
$edit = [
|
||||
'title[0][value]' => 'Another node',
|
||||
];
|
||||
$this->drupalPostForm('node/add/article', $edit, t('Save and Create New Draft'));
|
||||
$this->assertText(t('Article Another node has been created.'));
|
||||
$english_node = $this->drupalGetNodeByTitle('Another node');
|
||||
|
||||
// Add a French translation.
|
||||
$this->drupalGet('node/' . $english_node->id() . '/translations');
|
||||
$this->clickLink(t('Add'));
|
||||
$edit = [
|
||||
'title[0][value]' => 'Translated node',
|
||||
];
|
||||
$this->drupalPostForm(NULL, $edit, t('Save and Create New Draft (this translation)'));
|
||||
$this->assertText(t('Article Translated node has been updated.'));
|
||||
$english_node = $this->drupalGetNodeByTitle('Another node', TRUE);
|
||||
|
||||
// Publish the translation and check that the source language version stays
|
||||
// unpublished.
|
||||
$this->drupalPostForm('fr/node/' . $english_node->id() . '/edit', [], t('Save and Publish (this translation)'));
|
||||
$this->assertText(t('Article Translated node has been updated.'));
|
||||
$english_node = $this->drupalGetNodeByTitle('Another node', TRUE);
|
||||
$french_node = $english_node->getTranslation('fr');
|
||||
$this->assertEqual($french_node->moderation_state->value, 'published');
|
||||
$this->assertTrue($french_node->isPublished());
|
||||
$this->assertEqual($english_node->moderation_state->value, 'draft');
|
||||
$this->assertFalse($english_node->isPublished());
|
||||
|
||||
// Now check that we can create a new draft of the translation.
|
||||
$edit = [
|
||||
'title[0][value]' => 'New draft of translated node',
|
||||
];
|
||||
$this->drupalPostForm('fr/node/' . $english_node->id() . '/edit', $edit, t('Save and Create New Draft (this translation)'));
|
||||
$this->assertText(t('Article New draft of translated node has been updated.'));
|
||||
$english_node = $this->drupalGetNodeByTitle('Another node', TRUE);
|
||||
$french_node = $english_node->getTranslation('fr');
|
||||
$this->assertEqual($french_node->moderation_state->value, 'published');
|
||||
$this->assertTrue($french_node->isPublished());
|
||||
$this->assertEqual($french_node->getTitle(), 'Translated node', 'The default revision of the published translation remains the same.');
|
||||
|
||||
// Publish the draft.
|
||||
$edit = [
|
||||
'new_state' => 'published',
|
||||
];
|
||||
$this->drupalPostForm('fr/node/' . $english_node->id() . '/latest', $edit, t('Apply'));
|
||||
$this->assertText(t('The moderation state has been updated.'));
|
||||
$english_node = $this->drupalGetNodeByTitle('Another node', TRUE);
|
||||
$french_node = $english_node->getTranslation('fr');
|
||||
$this->assertEqual($french_node->moderation_state->value, 'published');
|
||||
$this->assertTrue($french_node->isPublished());
|
||||
$this->assertEqual($french_node->getTitle(), 'New draft of translated node', 'The draft has replaced the published revision.');
|
||||
|
||||
// Publish the English article before testing the archive transition.
|
||||
$this->drupalPostForm('node/' . $english_node->id() . '/edit', [], t('Save and Publish (this translation)'));
|
||||
$this->assertText(t('Article Another node has been updated.'));
|
||||
$english_node = $this->drupalGetNodeByTitle('Another node', TRUE);
|
||||
$this->assertEqual($english_node->moderation_state->value, 'published');
|
||||
|
||||
// Archive the node and its translation.
|
||||
$this->drupalPostForm('node/' . $english_node->id() . '/edit', [], t('Save and Archive (this translation)'));
|
||||
$this->assertText(t('Article Another node has been updated.'));
|
||||
$this->drupalPostForm('fr/node/' . $english_node->id() . '/edit', [], t('Save and Archive (this translation)'));
|
||||
$this->assertText(t('Article New draft of translated node has been updated.'));
|
||||
$english_node = $this->drupalGetNodeByTitle('Another node', TRUE);
|
||||
$french_node = $english_node->getTranslation('fr');
|
||||
$this->assertEqual($english_node->moderation_state->value, 'archived');
|
||||
$this->assertFalse($english_node->isPublished());
|
||||
$this->assertEqual($french_node->moderation_state->value, 'archived');
|
||||
$this->assertFalse($french_node->isPublished());
|
||||
|
||||
// Create another article with its translation. This time publishing english
|
||||
// after creating a forward french revision.
|
||||
$edit = [
|
||||
'title[0][value]' => 'An english node',
|
||||
];
|
||||
$this->drupalPostForm('node/add/article', $edit, t('Save and Create New Draft'));
|
||||
$this->assertText(t('Article An english node has been created.'));
|
||||
$english_node = $this->drupalGetNodeByTitle('An english node');
|
||||
$this->assertFalse($english_node->isPublished());
|
||||
|
||||
// Add a French translation.
|
||||
$this->drupalGet('node/' . $english_node->id() . '/translations');
|
||||
$this->clickLink(t('Add'));
|
||||
$edit = [
|
||||
'title[0][value]' => 'A french node',
|
||||
];
|
||||
$this->drupalPostForm(NULL, $edit, t('Save and Publish (this translation)'));
|
||||
$english_node = $this->drupalGetNodeByTitle('An english node', TRUE);
|
||||
$french_node = $english_node->getTranslation('fr');
|
||||
$this->assertTrue($french_node->isPublished());
|
||||
$this->assertFalse($english_node->isPublished());
|
||||
|
||||
// Create a forward revision
|
||||
$this->drupalPostForm('fr/node/' . $english_node->id() . '/edit', [], t('Save and Create New Draft (this translation)'));
|
||||
$english_node = $this->drupalGetNodeByTitle('An english node', TRUE);
|
||||
$french_node = $english_node->getTranslation('fr');
|
||||
$this->assertTrue($french_node->isPublished());
|
||||
$this->assertFalse($english_node->isPublished());
|
||||
|
||||
// Publish the english node and the default french node not the latest
|
||||
// french node should be used.
|
||||
$this->drupalPostForm('/node/' . $english_node->id() . '/edit', [], t('Save and Publish (this translation)'));
|
||||
$english_node = $this->drupalGetNodeByTitle('An english node', TRUE);
|
||||
$french_node = $english_node->getTranslation('fr');
|
||||
$this->assertTrue($french_node->isPublished());
|
||||
$this->assertTrue($english_node->isPublished());
|
||||
}
|
||||
|
||||
}
|
|
@ -5,6 +5,7 @@ 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 view access control handler for moderation state entities.
|
||||
|
@ -31,7 +32,7 @@ class ModerationStateAccessTest extends BrowserTestBase {
|
|||
$permissions = [
|
||||
'access content',
|
||||
'view all revisions',
|
||||
'view moderation states',
|
||||
'view content moderation',
|
||||
];
|
||||
$editor1 = $this->drupalCreateUser($permissions);
|
||||
$this->drupalLogin($editor1);
|
||||
|
@ -41,7 +42,7 @@ class ModerationStateAccessTest extends BrowserTestBase {
|
|||
'title' => 'Draft node',
|
||||
'uid' => $editor1->id(),
|
||||
]);
|
||||
$node_1->moderation_state->target_id = 'draft';
|
||||
$node_1->moderation_state->value = 'draft';
|
||||
$node_1->save();
|
||||
|
||||
$node_2 = Node::create([
|
||||
|
@ -49,26 +50,26 @@ class ModerationStateAccessTest extends BrowserTestBase {
|
|||
'title' => 'Published node',
|
||||
'uid' => $editor1->id(),
|
||||
]);
|
||||
$node_2->moderation_state->target_id = 'published';
|
||||
$node_2->moderation_state->value = 'published';
|
||||
$node_2->save();
|
||||
|
||||
// Resave the node with a new state.
|
||||
$node_2->setTitle('Archived node');
|
||||
$node_2->moderation_state->target_id = 'archived';
|
||||
$node_2->moderation_state->value = 'archived';
|
||||
$node_2->save();
|
||||
|
||||
// Now show the View, and confirm that the state labels are showing.
|
||||
$this->drupalGet('/latest');
|
||||
$page = $this->getSession()->getPage();
|
||||
$this->assertTrue($page->hasLink('Draft'));
|
||||
$this->assertTrue($page->hasLink('Archived'));
|
||||
$this->assertFalse($page->hasLink('Published'));
|
||||
$this->assertTrue($page->hasContent('Draft'));
|
||||
$this->assertTrue($page->hasContent('Archived'));
|
||||
$this->assertFalse($page->hasContent('Published'));
|
||||
|
||||
// Now log in as an admin and test the same thing.
|
||||
$permissions = [
|
||||
'access content',
|
||||
'view all revisions',
|
||||
'administer moderation states',
|
||||
'administer content moderation',
|
||||
];
|
||||
$admin1 = $this->drupalCreateUser($permissions);
|
||||
$this->drupalLogin($admin1);
|
||||
|
@ -76,9 +77,9 @@ class ModerationStateAccessTest extends BrowserTestBase {
|
|||
$this->drupalGet('/latest');
|
||||
$page = $this->getSession()->getPage();
|
||||
$this->assertEquals(200, $this->getSession()->getStatusCode());
|
||||
$this->assertTrue($page->hasLink('Draft'));
|
||||
$this->assertTrue($page->hasLink('Archived'));
|
||||
$this->assertFalse($page->hasLink('Published'));
|
||||
$this->assertTrue($page->hasContent('Draft'));
|
||||
$this->assertTrue($page->hasContent('Archived'));
|
||||
$this->assertFalse($page->hasContent('Published'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -98,9 +99,11 @@ class ModerationStateAccessTest extends BrowserTestBase {
|
|||
'type' => $machine_name,
|
||||
'label' => $label,
|
||||
]);
|
||||
$node_type->setThirdPartySetting('content_moderation', 'enabled', TRUE);
|
||||
$node_type->save();
|
||||
|
||||
$workflow = Workflow::load('editorial');
|
||||
$workflow->getTypePlugin()->addEntityTypeAndBundle('node', $machine_name);
|
||||
$workflow->save();
|
||||
return $node_type;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,143 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\content_moderation\Functional;
|
||||
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\user\Entity\Role;
|
||||
|
||||
/**
|
||||
* Defines a base class for moderation state tests.
|
||||
*/
|
||||
abstract class ModerationStateTestBase extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Profile to use.
|
||||
*/
|
||||
protected $profile = 'testing';
|
||||
|
||||
/**
|
||||
* Admin user.
|
||||
*
|
||||
* @var \Drupal\Core\Session\AccountInterface
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
/**
|
||||
* Permissions to grant admin user.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $permissions = [
|
||||
'administer content moderation',
|
||||
'access administration pages',
|
||||
'administer content types',
|
||||
'administer nodes',
|
||||
'view latest version',
|
||||
'view any unpublished content',
|
||||
'access content overview',
|
||||
'use editorial transition create_new_draft',
|
||||
'use editorial transition publish',
|
||||
];
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = [
|
||||
'content_moderation',
|
||||
'block',
|
||||
'block_content',
|
||||
'node',
|
||||
];
|
||||
|
||||
/**
|
||||
* Sets the test up.
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->adminUser = $this->drupalCreateUser($this->permissions);
|
||||
$this->drupalPlaceBlock('local_tasks_block', ['id' => 'tabs_block']);
|
||||
$this->drupalPlaceBlock('page_title_block');
|
||||
$this->drupalPlaceBlock('local_actions_block', ['id' => 'actions_block']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the permission machine name for a transition.
|
||||
*
|
||||
* @param string $workflow_id
|
||||
* The workflow ID.
|
||||
* @param string $transition_id
|
||||
* The transition ID.
|
||||
*
|
||||
* @return string
|
||||
* The permission machine name for a transition.
|
||||
*/
|
||||
protected function getWorkflowTransitionPermission($workflow_id, $transition_id) {
|
||||
return 'use ' . $workflow_id . ' transition ' . $transition_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a content-type from the UI.
|
||||
*
|
||||
* @param string $content_type_name
|
||||
* Content type human name.
|
||||
* @param string $content_type_id
|
||||
* Machine name.
|
||||
* @param bool $moderated
|
||||
* TRUE if should be moderated.
|
||||
* @param string $workflow_id
|
||||
* The workflow to attach to the bundle.
|
||||
*/
|
||||
protected function createContentTypeFromUi($content_type_name, $content_type_id, $moderated = FALSE, $workflow_id = 'editorial') {
|
||||
$this->drupalGet('admin/structure/types');
|
||||
$this->clickLink('Add content type');
|
||||
$edit = [
|
||||
'name' => $content_type_name,
|
||||
'type' => $content_type_id,
|
||||
];
|
||||
$this->drupalPostForm(NULL, $edit, t('Save content type'));
|
||||
|
||||
if ($moderated) {
|
||||
$this->enableModerationThroughUi($content_type_id, $workflow_id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable moderation for a specified content type, using the UI.
|
||||
*
|
||||
* @param string $content_type_id
|
||||
* Machine name.
|
||||
* @param string $workflow_id
|
||||
* The workflow to attach to the bundle.
|
||||
*/
|
||||
protected function enableModerationThroughUi($content_type_id, $workflow_id = 'editorial') {
|
||||
$edit['workflow'] = $workflow_id;
|
||||
$this->drupalPostForm('admin/structure/types/manage/' . $content_type_id . '/moderation', $edit, t('Save'));
|
||||
// Ensure the parent environment is up-to-date.
|
||||
// @see content_moderation_workflow_insert()
|
||||
\Drupal::service('entity_type.bundle.info')->clearCachedBundles();
|
||||
\Drupal::service('entity_field.manager')->clearCachedFieldDefinitions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Grants given user permission to create content of given type.
|
||||
*
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* User to grant permission to.
|
||||
* @param string $content_type_id
|
||||
* Content type ID.
|
||||
*/
|
||||
protected function grantUserPermissionToCreateContentOfType(AccountInterface $account, $content_type_id) {
|
||||
$role_ids = $account->getRoles(TRUE);
|
||||
/* @var \Drupal\user\RoleInterface $role */
|
||||
$role_id = reset($role_ids);
|
||||
$role = Role::load($role_id);
|
||||
$role->grantPermission(sprintf('create %s content', $content_type_id));
|
||||
$role->grantPermission(sprintf('edit any %s content', $content_type_id));
|
||||
$role->grantPermission(sprintf('delete any %s content', $content_type_id));
|
||||
$role->save();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\content_moderation\Functional;
|
||||
|
||||
/**
|
||||
* Tests permission access control around nodes.
|
||||
*
|
||||
* @group content_moderation
|
||||
*/
|
||||
class NodeAccessTest extends ModerationStateTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$this->createContentTypeFromUi('Moderated content', 'moderated_content', TRUE);
|
||||
$this->grantUserPermissionToCreateContentOfType($this->adminUser, 'moderated_content');
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that a non-admin user can still access the appropriate pages.
|
||||
*/
|
||||
public function testPageAccess() {
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
// Create a node to test with.
|
||||
$this->drupalPostForm('node/add/moderated_content', [
|
||||
'title[0][value]' => 'moderated content',
|
||||
], t('Save and Create New Draft'));
|
||||
$node = $this->getNodeByTitle('moderated content');
|
||||
if (!$node) {
|
||||
$this->fail('Test node was not saved correctly.');
|
||||
}
|
||||
|
||||
$view_path = 'node/' . $node->id();
|
||||
$edit_path = 'node/' . $node->id() . '/edit';
|
||||
$latest_path = 'node/' . $node->id() . '/latest';
|
||||
|
||||
// Publish the node.
|
||||
$this->drupalPostForm($edit_path, [], t('Save and Publish'));
|
||||
|
||||
// Ensure access works correctly for anonymous users.
|
||||
$this->drupalLogout();
|
||||
|
||||
$this->drupalGet($edit_path);
|
||||
$this->assertResponse(403);
|
||||
|
||||
$this->drupalGet($latest_path);
|
||||
$this->assertResponse(403);
|
||||
$this->drupalGet($view_path);
|
||||
$this->assertResponse(200);
|
||||
|
||||
// Create a forward revision for the 'Latest revision' tab.
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$this->drupalPostForm($edit_path, [
|
||||
'title[0][value]' => 'moderated content revised',
|
||||
], t('Save and Create New Draft'));
|
||||
|
||||
// Now make a new user and verify that the new user's access is correct.
|
||||
$user = $this->createUser([
|
||||
'use editorial transition create_new_draft',
|
||||
'view latest version',
|
||||
'view any unpublished content',
|
||||
]);
|
||||
$this->drupalLogin($user);
|
||||
|
||||
$this->drupalGet($edit_path);
|
||||
$this->assertResponse(403);
|
||||
|
||||
$this->drupalGet($latest_path);
|
||||
$this->assertResponse(200);
|
||||
$this->drupalGet($view_path);
|
||||
$this->assertResponse(200);
|
||||
|
||||
// Now make another user, who should not be able to see forward revisions.
|
||||
$user = $this->createUser([
|
||||
'use editorial transition create_new_draft',
|
||||
]);
|
||||
$this->drupalLogin($user);
|
||||
|
||||
$this->drupalGet($edit_path);
|
||||
$this->assertResponse(403);
|
||||
|
||||
$this->drupalGet($latest_path);
|
||||
$this->assertResponse(403);
|
||||
$this->drupalGet($view_path);
|
||||
$this->assertResponse(200);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\content_moderation\Kernel;
|
||||
|
||||
use Drupal\content_moderation\Permissions;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\workflows\Entity\Workflow;
|
||||
|
||||
/**
|
||||
* Test to ensure content moderation permissions are generated correctly.
|
||||
*
|
||||
* @group content_moderation
|
||||
*/
|
||||
class ContentModerationPermissionsTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = [
|
||||
'workflows',
|
||||
'content_moderation',
|
||||
'workflow_type_test',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installEntitySchema('workflow');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test permissions generated by content moderation.
|
||||
*
|
||||
* @dataProvider permissionsTestCases
|
||||
*/
|
||||
public function testPermissions($workflow, $permissions) {
|
||||
Workflow::create($workflow)->save();
|
||||
$this->assertEquals($permissions, (new Permissions())->transitionPermissions());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test cases for ::testPermissions
|
||||
*
|
||||
* @return array
|
||||
* Content moderation permissions based test cases.
|
||||
*/
|
||||
public function permissionsTestCases() {
|
||||
return [
|
||||
'Simple Content Moderation Workflow' => [
|
||||
[
|
||||
'id' => 'simple_workflow',
|
||||
'label' => 'Simple Workflow',
|
||||
'type' => 'content_moderation',
|
||||
'transitions' => [
|
||||
'publish' => [
|
||||
'label' => 'Publish',
|
||||
'from' => ['draft'],
|
||||
'to' => 'published',
|
||||
'weight' => 0,
|
||||
],
|
||||
'unpublish' => [
|
||||
'label' => 'Unpublish',
|
||||
'from' => ['published'],
|
||||
'to' => 'draft',
|
||||
'weight' => 0,
|
||||
],
|
||||
],
|
||||
'states' => [
|
||||
'draft' => [
|
||||
'label' => 'Draft',
|
||||
'weight' => -5,
|
||||
],
|
||||
'published' => [
|
||||
'label' => 'Published',
|
||||
'weight' => 0,
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'use simple_workflow transition publish' => [
|
||||
'title' => 'Use <em class="placeholder">Publish</em> transition from <em class="placeholder">Simple Workflow</em> workflow.',
|
||||
],
|
||||
'use simple_workflow transition unpublish' => [
|
||||
'title' => 'Use <em class="placeholder">Unpublish</em> transition from <em class="placeholder">Simple Workflow</em> workflow.',
|
||||
],
|
||||
],
|
||||
],
|
||||
'Non Content Moderation Workflow' => [
|
||||
[
|
||||
'id' => 'morning',
|
||||
'label' => 'Morning',
|
||||
'type' => 'workflow_type_test',
|
||||
'transitions' => [
|
||||
'drink_coffee' => [
|
||||
'label' => 'Drink Coffee',
|
||||
'from' => ['tired'],
|
||||
'to' => 'awake',
|
||||
'weight' => 0,
|
||||
],
|
||||
],
|
||||
'states' => [
|
||||
'awake' => [
|
||||
'label' => 'Awake',
|
||||
'weight' => -5,
|
||||
],
|
||||
'tired' => [
|
||||
'label' => 'Tired',
|
||||
'weight' => -0,
|
||||
],
|
||||
],
|
||||
],
|
||||
[]
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
|
@ -1,89 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\content_moderation\Kernel;
|
||||
|
||||
use Drupal\block_content\Entity\BlockContentType;
|
||||
use Drupal\config\Tests\SchemaCheckTestTrait;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
use Drupal\content_moderation\Entity\ModerationState;
|
||||
use Drupal\content_moderation\Entity\ModerationStateTransition;
|
||||
|
||||
/**
|
||||
* Ensures that content moderation schema is correct.
|
||||
*
|
||||
* @group content_moderation
|
||||
*/
|
||||
class ContentModerationSchemaTest extends KernelTestBase {
|
||||
|
||||
use SchemaCheckTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'content_moderation',
|
||||
'node',
|
||||
'user',
|
||||
'block_content',
|
||||
'system',
|
||||
];
|
||||
|
||||
/**
|
||||
* Tests content moderation default schema.
|
||||
*/
|
||||
public function testContentModerationDefaultConfig() {
|
||||
$this->installConfig(['content_moderation']);
|
||||
$typed_config = \Drupal::service('config.typed');
|
||||
$moderation_states = ModerationState::loadMultiple();
|
||||
foreach ($moderation_states as $moderation_state) {
|
||||
$this->assertConfigSchema($typed_config, $moderation_state->getEntityType()->getConfigPrefix() . '.' . $moderation_state->id(), $moderation_state->toArray());
|
||||
}
|
||||
$moderation_state_transitions = ModerationStateTransition::loadMultiple();
|
||||
foreach ($moderation_state_transitions as $moderation_state_transition) {
|
||||
$this->assertConfigSchema($typed_config, $moderation_state_transition->getEntityType()->getConfigPrefix() . '.' . $moderation_state_transition->id(), $moderation_state_transition->toArray());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests content moderation third party schema for node types.
|
||||
*/
|
||||
public function testContentModerationNodeTypeConfig() {
|
||||
$this->installEntitySchema('node');
|
||||
$this->installEntitySchema('user');
|
||||
$this->installConfig(['content_moderation']);
|
||||
$typed_config = \Drupal::service('config.typed');
|
||||
$moderation_states = ModerationState::loadMultiple();
|
||||
$node_type = NodeType::create([
|
||||
'type' => 'example',
|
||||
]);
|
||||
$node_type->setThirdPartySetting('content_moderation', 'enabled', TRUE);
|
||||
$node_type->setThirdPartySetting('content_moderation', 'allowed_moderation_states', array_keys($moderation_states));
|
||||
$node_type->setThirdPartySetting('content_moderation', 'default_moderation_state', '');
|
||||
$node_type->save();
|
||||
$this->assertConfigSchema($typed_config, $node_type->getEntityType()->getConfigPrefix() . '.' . $node_type->id(), $node_type->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests content moderation third party schema for block content types.
|
||||
*/
|
||||
public function testContentModerationBlockContentTypeConfig() {
|
||||
$this->installEntitySchema('block_content');
|
||||
$this->installEntitySchema('user');
|
||||
$this->installConfig(['content_moderation']);
|
||||
$typed_config = \Drupal::service('config.typed');
|
||||
$moderation_states = ModerationState::loadMultiple();
|
||||
$block_content_type = BlockContentType::create([
|
||||
'id' => 'basic',
|
||||
'label' => 'basic',
|
||||
'revision' => TRUE,
|
||||
]);
|
||||
$block_content_type->setThirdPartySetting('content_moderation', 'enabled', TRUE);
|
||||
$block_content_type->setThirdPartySetting('content_moderation', 'allowed_moderation_states', array_keys($moderation_states));
|
||||
$block_content_type->setThirdPartySetting('content_moderation', 'default_moderation_state', '');
|
||||
$block_content_type->save();
|
||||
$this->assertConfigSchema($typed_config, $block_content_type->getEntityType()->getConfigPrefix() . '.' . $block_content_type->id(), $block_content_type->toArray());
|
||||
}
|
||||
|
||||
}
|
|
@ -3,14 +3,16 @@
|
|||
namespace Drupal\Tests\content_moderation\Kernel;
|
||||
|
||||
use Drupal\content_moderation\Entity\ContentModerationState;
|
||||
use Drupal\content_moderation\Entity\ModerationState;
|
||||
use Drupal\Core\Entity\EntityPublishedInterface;
|
||||
use Drupal\Core\Entity\EntityStorageException;
|
||||
use Drupal\entity_test\Entity\EntityTestBundle;
|
||||
use Drupal\entity_test\Entity\EntityTestWithBundle;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
use Drupal\node\NodeInterface;
|
||||
use Drupal\workflows\Entity\Workflow;
|
||||
|
||||
/**
|
||||
* Tests links between a content entity and a content_moderation_state entity.
|
||||
|
@ -25,14 +27,21 @@ class ContentModerationStateTest extends KernelTestBase {
|
|||
public static $modules = [
|
||||
'entity_test',
|
||||
'node',
|
||||
'block_content',
|
||||
'content_moderation',
|
||||
'user',
|
||||
'system',
|
||||
'language',
|
||||
'content_translation',
|
||||
'text',
|
||||
'workflows',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Entity\EntityTypeManager
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -43,35 +52,57 @@ class ContentModerationStateTest extends KernelTestBase {
|
|||
$this->installEntitySchema('node');
|
||||
$this->installEntitySchema('user');
|
||||
$this->installEntitySchema('entity_test_with_bundle');
|
||||
$this->installEntitySchema('entity_test_rev');
|
||||
$this->installEntitySchema('entity_test_mulrevpub');
|
||||
$this->installEntitySchema('block_content');
|
||||
$this->installEntitySchema('content_moderation_state');
|
||||
$this->installConfig('content_moderation');
|
||||
|
||||
$this->entityTypeManager = $this->container->get('entity_type.manager');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests basic monolingual content moderation through the API.
|
||||
*
|
||||
* @dataProvider basicModerationTestCases
|
||||
*/
|
||||
public function testBasicModeration() {
|
||||
$node_type = NodeType::create([
|
||||
'type' => 'example',
|
||||
]);
|
||||
$node_type->setThirdPartySetting('content_moderation', 'enabled', TRUE);
|
||||
$node_type->setThirdPartySetting('content_moderation', 'allowed_moderation_states', ['draft', 'published']);
|
||||
$node_type->setThirdPartySetting('content_moderation', 'default_moderation_state', 'draft');
|
||||
$node_type->save();
|
||||
$node = Node::create([
|
||||
'type' => 'example',
|
||||
public function testBasicModeration($entity_type_id) {
|
||||
// Make the 'entity_test_with_bundle' entity type revisionable.
|
||||
if ($entity_type_id == 'entity_test_with_bundle') {
|
||||
$this->setEntityTestWithBundleKeys(['revision' => 'revision_id']);
|
||||
}
|
||||
|
||||
$entity_storage = $this->entityTypeManager->getStorage($entity_type_id);
|
||||
$bundle_id = $entity_type_id;
|
||||
$bundle_entity_type_id = $this->entityTypeManager->getDefinition($entity_type_id)->getBundleEntityType();
|
||||
if ($bundle_entity_type_id) {
|
||||
$bundle_entity_type_definition = $this->entityTypeManager->getDefinition($bundle_entity_type_id);
|
||||
$entity_type_storage = $this->entityTypeManager->getStorage($bundle_entity_type_id);
|
||||
|
||||
$entity_type = $entity_type_storage->create([
|
||||
$bundle_entity_type_definition->getKey('id') => 'example',
|
||||
]);
|
||||
$entity_type->save();
|
||||
$bundle_id = $entity_type->id();
|
||||
}
|
||||
|
||||
$workflow = Workflow::load('editorial');
|
||||
$workflow->getTypePlugin()->addEntityTypeAndBundle($entity_type_id, $bundle_id);
|
||||
$workflow->save();
|
||||
|
||||
$entity = $entity_storage->create([
|
||||
'title' => 'Test title',
|
||||
$this->entityTypeManager->getDefinition($entity_type_id)->getKey('bundle') => $bundle_id,
|
||||
]);
|
||||
$node->save();
|
||||
$node = $this->reloadNode($node);
|
||||
$this->assertEquals('draft', $node->moderation_state->entity->id());
|
||||
$entity->save();
|
||||
$entity = $this->reloadEntity($entity);
|
||||
$this->assertEquals('draft', $entity->moderation_state->value);
|
||||
|
||||
$published = ModerationState::load('published');
|
||||
$node->moderation_state->entity = $published;
|
||||
$node->save();
|
||||
$entity->moderation_state->value = 'published';
|
||||
$entity->save();
|
||||
|
||||
$node = $this->reloadNode($node);
|
||||
$this->assertEquals('published', $node->moderation_state->entity->id());
|
||||
$entity = $this->reloadEntity($entity);
|
||||
$this->assertEquals('published', $entity->moderation_state->value);
|
||||
|
||||
// Change the state without saving the node.
|
||||
$content_moderation_state = ContentModerationState::load(1);
|
||||
|
@ -79,26 +110,76 @@ class ContentModerationStateTest extends KernelTestBase {
|
|||
$content_moderation_state->setNewRevision(TRUE);
|
||||
$content_moderation_state->save();
|
||||
|
||||
$node = $this->reloadNode($node, 3);
|
||||
$this->assertEquals('draft', $node->moderation_state->entity->id());
|
||||
$this->assertFalse($node->isPublished());
|
||||
$entity = $this->reloadEntity($entity, 3);
|
||||
$this->assertEquals('draft', $entity->moderation_state->value);
|
||||
if ($entity instanceof EntityPublishedInterface) {
|
||||
$this->assertFalse($entity->isPublished());
|
||||
}
|
||||
|
||||
// Get the default revision.
|
||||
$node = $this->reloadNode($node);
|
||||
$this->assertTrue($node->isPublished());
|
||||
$this->assertEquals(2, $node->getRevisionId());
|
||||
$entity = $this->reloadEntity($entity);
|
||||
if ($entity instanceof EntityPublishedInterface) {
|
||||
$this->assertTrue((bool) $entity->isPublished());
|
||||
}
|
||||
$this->assertEquals(2, $entity->getRevisionId());
|
||||
|
||||
$node->moderation_state->target_id = 'published';
|
||||
$node->save();
|
||||
$entity->moderation_state->value = 'published';
|
||||
$entity->save();
|
||||
|
||||
$node = $this->reloadNode($node, 4);
|
||||
$this->assertEquals('published', $node->moderation_state->entity->id());
|
||||
$entity = $this->reloadEntity($entity, 4);
|
||||
$this->assertEquals('published', $entity->moderation_state->value);
|
||||
|
||||
// Get the default revision.
|
||||
$node = $this->reloadNode($node);
|
||||
$this->assertTrue($node->isPublished());
|
||||
$this->assertEquals(4, $node->getRevisionId());
|
||||
$entity = $this->reloadEntity($entity);
|
||||
if ($entity instanceof EntityPublishedInterface) {
|
||||
$this->assertTrue((bool) $entity->isPublished());
|
||||
}
|
||||
$this->assertEquals(4, $entity->getRevisionId());
|
||||
|
||||
// Update the node to archived which will then be the default revision.
|
||||
$entity->moderation_state->value = 'archived';
|
||||
$entity->save();
|
||||
|
||||
// Revert to the previous (published) revision.
|
||||
$previous_revision = $entity_storage->loadRevision(4);
|
||||
$previous_revision->isDefaultRevision(TRUE);
|
||||
$previous_revision->setNewRevision(TRUE);
|
||||
$previous_revision->save();
|
||||
|
||||
// Get the default revision.
|
||||
$entity = $this->reloadEntity($entity);
|
||||
$this->assertEquals('published', $entity->moderation_state->value);
|
||||
if ($entity instanceof EntityPublishedInterface) {
|
||||
$this->assertTrue($entity->isPublished());
|
||||
}
|
||||
|
||||
// Set an invalid moderation state.
|
||||
$this->setExpectedException(EntityStorageException::class);
|
||||
$entity->moderation_state->value = 'foobar';
|
||||
$entity->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test cases for basic moderation test.
|
||||
*/
|
||||
public function basicModerationTestCases() {
|
||||
return [
|
||||
'Nodes' => [
|
||||
'node',
|
||||
],
|
||||
'Block content' => [
|
||||
'block_content',
|
||||
],
|
||||
'Test Entity with Bundle' => [
|
||||
'entity_test_with_bundle',
|
||||
],
|
||||
'Test entity - revisions, data table, and published interface' => [
|
||||
'entity_test_mulrevpub',
|
||||
],
|
||||
'Entity Test with revisions' => [
|
||||
'entity_test_rev',
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -110,75 +191,77 @@ class ContentModerationStateTest extends KernelTestBase {
|
|||
$node_type = NodeType::create([
|
||||
'type' => 'example',
|
||||
]);
|
||||
$node_type->setThirdPartySetting('content_moderation', 'enabled', TRUE);
|
||||
$node_type->setThirdPartySetting('content_moderation', 'allowed_moderation_states', ['draft', 'published']);
|
||||
$node_type->setThirdPartySetting('content_moderation', 'default_moderation_state', 'draft');
|
||||
$node_type->save();
|
||||
|
||||
$workflow = Workflow::load('editorial');
|
||||
$workflow->getTypePlugin()->addEntityTypeAndBundle('node', 'example');
|
||||
$workflow->save();
|
||||
|
||||
$english_node = Node::create([
|
||||
'type' => 'example',
|
||||
'title' => 'Test title',
|
||||
]);
|
||||
// Revision 1 (en).
|
||||
$english_node
|
||||
->setPublished(FALSE)
|
||||
->setUnpublished()
|
||||
->save();
|
||||
$this->assertEquals('draft', $english_node->moderation_state->entity->id());
|
||||
$this->assertEquals('draft', $english_node->moderation_state->value);
|
||||
$this->assertFalse($english_node->isPublished());
|
||||
|
||||
// Create a French translation.
|
||||
$french_node = $english_node->addTranslation('fr', ['title' => 'French title']);
|
||||
$french_node->setPublished(FALSE);
|
||||
$french_node->setUnpublished();
|
||||
// Revision 1 (fr).
|
||||
$french_node->save();
|
||||
$french_node = $this->reloadNode($english_node)->getTranslation('fr');
|
||||
$this->assertEquals('draft', $french_node->moderation_state->entity->id());
|
||||
$french_node = $this->reloadEntity($english_node)->getTranslation('fr');
|
||||
$this->assertEquals('draft', $french_node->moderation_state->value);
|
||||
$this->assertFalse($french_node->isPublished());
|
||||
|
||||
// Move English node to create another draft.
|
||||
$english_node = $this->reloadNode($english_node);
|
||||
$english_node->moderation_state->target_id = 'draft';
|
||||
$english_node = $this->reloadEntity($english_node);
|
||||
$english_node->moderation_state->value = 'draft';
|
||||
// Revision 2 (en, fr).
|
||||
$english_node->save();
|
||||
$english_node = $this->reloadNode($english_node);
|
||||
$this->assertEquals('draft', $english_node->moderation_state->entity->id());
|
||||
$english_node = $this->reloadEntity($english_node);
|
||||
$this->assertEquals('draft', $english_node->moderation_state->value);
|
||||
|
||||
// French node should still be in draft.
|
||||
$french_node = $this->reloadNode($english_node)->getTranslation('fr');
|
||||
$this->assertEquals('draft', $french_node->moderation_state->entity->id());
|
||||
$french_node = $this->reloadEntity($english_node)->getTranslation('fr');
|
||||
$this->assertEquals('draft', $french_node->moderation_state->value);
|
||||
|
||||
// Publish the French node.
|
||||
$french_node->moderation_state->target_id = 'published';
|
||||
$french_node->moderation_state->value = 'published';
|
||||
// Revision 3 (en, fr).
|
||||
$french_node->save();
|
||||
$french_node = $this->reloadNode($french_node)->getTranslation('fr');
|
||||
$french_node = $this->reloadEntity($french_node)->getTranslation('fr');
|
||||
$this->assertTrue($french_node->isPublished());
|
||||
$this->assertEquals('published', $french_node->moderation_state->entity->id());
|
||||
$this->assertEquals('published', $french_node->moderation_state->value);
|
||||
$this->assertTrue($french_node->isPublished());
|
||||
$english_node = $french_node->getTranslation('en');
|
||||
$this->assertEquals('draft', $english_node->moderation_state->entity->id());
|
||||
$this->assertEquals('draft', $english_node->moderation_state->value);
|
||||
|
||||
// Publish the English node.
|
||||
$english_node->moderation_state->target_id = 'published';
|
||||
$english_node->moderation_state->value = 'published';
|
||||
// Revision 4 (en, fr).
|
||||
$english_node->save();
|
||||
$english_node = $this->reloadNode($english_node);
|
||||
$english_node = $this->reloadEntity($english_node);
|
||||
$this->assertTrue($english_node->isPublished());
|
||||
|
||||
// Move the French node back to draft.
|
||||
$french_node = $this->reloadNode($english_node)->getTranslation('fr');
|
||||
$french_node = $this->reloadEntity($english_node)->getTranslation('fr');
|
||||
$this->assertTrue($french_node->isPublished());
|
||||
$french_node->moderation_state->target_id = 'draft';
|
||||
$french_node->moderation_state->value = 'draft';
|
||||
// Revision 5 (en, fr).
|
||||
$french_node->save();
|
||||
$french_node = $this->reloadNode($english_node, 5)->getTranslation('fr');
|
||||
$french_node = $this->reloadEntity($english_node, 5)->getTranslation('fr');
|
||||
$this->assertFalse($french_node->isPublished());
|
||||
$this->assertTrue($french_node->getTranslation('en')->isPublished());
|
||||
|
||||
// Republish the French node.
|
||||
$french_node->moderation_state->target_id = 'published';
|
||||
$french_node->moderation_state->value = 'published';
|
||||
// Revision 6 (en, fr).
|
||||
$french_node->save();
|
||||
$french_node = $this->reloadNode($english_node)->getTranslation('fr');
|
||||
$french_node = $this->reloadEntity($english_node)->getTranslation('fr');
|
||||
$this->assertTrue($french_node->isPublished());
|
||||
|
||||
// Change the EN state without saving the node.
|
||||
|
@ -187,11 +270,11 @@ class ContentModerationStateTest extends KernelTestBase {
|
|||
$content_moderation_state->setNewRevision(TRUE);
|
||||
// Revision 7 (en, fr).
|
||||
$content_moderation_state->save();
|
||||
$english_node = $this->reloadNode($french_node, $french_node->getRevisionId() + 1);
|
||||
$english_node = $this->reloadEntity($french_node, $french_node->getRevisionId() + 1);
|
||||
|
||||
$this->assertEquals('draft', $english_node->moderation_state->entity->id());
|
||||
$french_node = $this->reloadNode($english_node)->getTranslation('fr');
|
||||
$this->assertEquals('published', $french_node->moderation_state->entity->id());
|
||||
$this->assertEquals('draft', $english_node->moderation_state->value);
|
||||
$french_node = $this->reloadEntity($english_node)->getTranslation('fr');
|
||||
$this->assertEquals('published', $french_node->moderation_state->value);
|
||||
|
||||
// This should unpublish the French node.
|
||||
$content_moderation_state = ContentModerationState::load(1);
|
||||
|
@ -201,16 +284,16 @@ class ContentModerationStateTest extends KernelTestBase {
|
|||
// Revision 8 (en, fr).
|
||||
$content_moderation_state->save();
|
||||
|
||||
$english_node = $this->reloadNode($english_node, $english_node->getRevisionId());
|
||||
$this->assertEquals('draft', $english_node->moderation_state->entity->id());
|
||||
$french_node = $this->reloadNode($english_node, '8')->getTranslation('fr');
|
||||
$this->assertEquals('draft', $french_node->moderation_state->entity->id());
|
||||
$english_node = $this->reloadEntity($english_node, $english_node->getRevisionId());
|
||||
$this->assertEquals('draft', $english_node->moderation_state->value);
|
||||
$french_node = $this->reloadEntity($english_node, '8')->getTranslation('fr');
|
||||
$this->assertEquals('draft', $french_node->moderation_state->value);
|
||||
// Switching the moderation state to an unpublished state should update the
|
||||
// entity.
|
||||
$this->assertFalse($french_node->isPublished());
|
||||
|
||||
// Get the default english node.
|
||||
$english_node = $this->reloadNode($english_node);
|
||||
$english_node = $this->reloadEntity($english_node);
|
||||
$this->assertTrue($english_node->isPublished());
|
||||
$this->assertEquals(6, $english_node->getRevisionId());
|
||||
}
|
||||
|
@ -220,25 +303,18 @@ class ContentModerationStateTest extends KernelTestBase {
|
|||
*/
|
||||
public function testNonTranslatableEntityTypeModeration() {
|
||||
// Make the 'entity_test_with_bundle' entity type revisionable.
|
||||
$entity_type = clone \Drupal::entityTypeManager()->getDefinition('entity_test_with_bundle');
|
||||
$keys = $entity_type->getKeys();
|
||||
$keys['revision'] = 'revision_id';
|
||||
$entity_type->set('entity_keys', $keys);
|
||||
\Drupal::state()->set('entity_test_with_bundle.entity_type', $entity_type);
|
||||
\Drupal::entityDefinitionUpdateManager()->applyUpdates();
|
||||
$this->setEntityTestWithBundleKeys(['revision' => 'revision_id']);
|
||||
|
||||
// Create a test bundle.
|
||||
$entity_test_bundle = EntityTestBundle::create([
|
||||
'id' => 'example',
|
||||
]);
|
||||
$entity_test_bundle->setThirdPartySetting('content_moderation', 'enabled', TRUE);
|
||||
$entity_test_bundle->setThirdPartySetting('content_moderation', 'allowed_moderation_states', [
|
||||
'draft',
|
||||
'published'
|
||||
]);
|
||||
$entity_test_bundle->setThirdPartySetting('content_moderation', 'default_moderation_state', 'draft');
|
||||
$entity_test_bundle->save();
|
||||
|
||||
$workflow = Workflow::load('editorial');
|
||||
$workflow->getTypePlugin()->addEntityTypeAndBundle('entity_test_with_bundle', 'example');
|
||||
$workflow->save();
|
||||
|
||||
// Check that the tested entity type is not translatable.
|
||||
$entity_type = \Drupal::entityTypeManager()->getDefinition('entity_test_with_bundle');
|
||||
$this->assertFalse($entity_type->isTranslatable(), 'The test entity type is not translatable.');
|
||||
|
@ -248,12 +324,12 @@ class ContentModerationStateTest extends KernelTestBase {
|
|||
'type' => 'example'
|
||||
]);
|
||||
$entity_test_with_bundle->save();
|
||||
$this->assertEquals('draft', $entity_test_with_bundle->moderation_state->entity->id());
|
||||
$this->assertEquals('draft', $entity_test_with_bundle->moderation_state->value);
|
||||
|
||||
$entity_test_with_bundle->moderation_state->target_id = 'published';
|
||||
$entity_test_with_bundle->moderation_state->value = 'published';
|
||||
$entity_test_with_bundle->save();
|
||||
|
||||
$this->assertEquals('published', EntityTestWithBundle::load($entity_test_with_bundle->id())->moderation_state->entity->id());
|
||||
$this->assertEquals('published', EntityTestWithBundle::load($entity_test_with_bundle->id())->moderation_state->value);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -263,26 +339,18 @@ class ContentModerationStateTest extends KernelTestBase {
|
|||
public function testNonLangcodeEntityTypeModeration() {
|
||||
// Make the 'entity_test_with_bundle' entity type revisionable and unset
|
||||
// the langcode entity key.
|
||||
$entity_type = clone \Drupal::entityTypeManager()->getDefinition('entity_test_with_bundle');
|
||||
$keys = $entity_type->getKeys();
|
||||
$keys['revision'] = 'revision_id';
|
||||
unset($keys['langcode']);
|
||||
$entity_type->set('entity_keys', $keys);
|
||||
\Drupal::state()->set('entity_test_with_bundle.entity_type', $entity_type);
|
||||
\Drupal::entityDefinitionUpdateManager()->applyUpdates();
|
||||
$this->setEntityTestWithBundleKeys(['revision' => 'revision_id'], ['langcode']);
|
||||
|
||||
// Create a test bundle.
|
||||
$entity_test_bundle = EntityTestBundle::create([
|
||||
'id' => 'example',
|
||||
]);
|
||||
$entity_test_bundle->setThirdPartySetting('content_moderation', 'enabled', TRUE);
|
||||
$entity_test_bundle->setThirdPartySetting('content_moderation', 'allowed_moderation_states', [
|
||||
'draft',
|
||||
'published'
|
||||
]);
|
||||
$entity_test_bundle->setThirdPartySetting('content_moderation', 'default_moderation_state', 'draft');
|
||||
$entity_test_bundle->save();
|
||||
|
||||
$workflow = Workflow::load('editorial');
|
||||
$workflow->getTypePlugin()->addEntityTypeAndBundle('entity_test_with_bundle', 'example');
|
||||
$workflow->save();
|
||||
|
||||
// Check that the tested entity type is not translatable.
|
||||
$entity_type = \Drupal::entityTypeManager()->getDefinition('entity_test_with_bundle');
|
||||
$this->assertFalse($entity_type->isTranslatable(), 'The test entity type is not translatable.');
|
||||
|
@ -292,33 +360,94 @@ class ContentModerationStateTest extends KernelTestBase {
|
|||
'type' => 'example'
|
||||
]);
|
||||
$entity_test_with_bundle->save();
|
||||
$this->assertEquals('draft', $entity_test_with_bundle->moderation_state->entity->id());
|
||||
$this->assertEquals('draft', $entity_test_with_bundle->moderation_state->value);
|
||||
|
||||
$entity_test_with_bundle->moderation_state->target_id = 'published';
|
||||
$entity_test_with_bundle->moderation_state->value = 'published';
|
||||
$entity_test_with_bundle->save();
|
||||
|
||||
$this->assertEquals('published', EntityTestWithBundle::load($entity_test_with_bundle->id())->moderation_state->entity->id());
|
||||
$this->assertEquals('published', EntityTestWithBundle::load($entity_test_with_bundle->id())->moderation_state->value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads the node after clearing the static cache.
|
||||
* Set the keys on the test entity type.
|
||||
*
|
||||
* @param \Drupal\node\NodeInterface $node
|
||||
* The node to reload.
|
||||
* @param int|false $revision_id
|
||||
* @param array $keys
|
||||
* The entity keys to override
|
||||
* @param array $remove_keys
|
||||
* Keys to remove.
|
||||
*/
|
||||
protected function setEntityTestWithBundleKeys($keys, $remove_keys = []) {
|
||||
$entity_type = clone \Drupal::entityTypeManager()->getDefinition('entity_test_with_bundle');
|
||||
$original_keys = $entity_type->getKeys();
|
||||
foreach ($remove_keys as $remove_key) {
|
||||
unset($original_keys[$remove_key]);
|
||||
}
|
||||
$entity_type->set('entity_keys', $keys + $original_keys);
|
||||
\Drupal::state()->set('entity_test_with_bundle.entity_type', $entity_type);
|
||||
\Drupal::entityDefinitionUpdateManager()->applyUpdates();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the dependencies of the workflow when using content moderation.
|
||||
*/
|
||||
public function testWorkflowDependencies() {
|
||||
$node_type = NodeType::create([
|
||||
'type' => 'example',
|
||||
]);
|
||||
$node_type->save();
|
||||
|
||||
$workflow = Workflow::load('editorial');
|
||||
// Test both a config and non-config based bundle and entity type.
|
||||
$workflow->getTypePlugin()->addEntityTypeAndBundle('node', 'example');
|
||||
$workflow->getTypePlugin()->addEntityTypeAndBundle('entity_test_rev', 'entity_test_rev');
|
||||
$workflow->save();
|
||||
|
||||
$this->assertEquals([
|
||||
'module' => [
|
||||
'content_moderation',
|
||||
'entity_test',
|
||||
],
|
||||
'config' => [
|
||||
'node.type.example',
|
||||
],
|
||||
], $workflow->getDependencies());
|
||||
|
||||
$entity_types = $workflow->getTypePlugin()->getEntityTypes();
|
||||
$this->assertTrue(in_array('node', $entity_types));
|
||||
$this->assertTrue(in_array('entity_test_rev', $entity_types));
|
||||
|
||||
// Delete the node type and ensure it is removed from the workflow.
|
||||
$node_type->delete();
|
||||
$workflow = Workflow::load('editorial');
|
||||
$entity_types = $workflow->getTypePlugin()->getEntityTypes();
|
||||
$this->assertFalse(in_array('node', $entity_types));
|
||||
|
||||
// Uninstall entity test and ensure it's removed from the workflow.
|
||||
$this->container->get('config.manager')->uninstall('module', 'entity_test');
|
||||
$workflow = Workflow::load('editorial');
|
||||
$entity_types = $workflow->getTypePlugin()->getEntityTypes();
|
||||
$this->assertFalse(in_array('entity_test_rev', $entity_types));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads the entity after clearing the static cache.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* The entity to reload.
|
||||
* @param int|bool $revision_id
|
||||
* The specific revision ID to load. Defaults FALSE and just loads the
|
||||
* default revision.
|
||||
*
|
||||
* @return \Drupal\node\NodeInterface
|
||||
* The reloaded node.
|
||||
* @return \Drupal\Core\Entity\EntityInterface
|
||||
* The reloaded entity.
|
||||
*/
|
||||
protected function reloadNode(NodeInterface $node, $revision_id = FALSE) {
|
||||
$storage = \Drupal::entityTypeManager()->getStorage('node');
|
||||
$storage->resetCache([$node->id()]);
|
||||
protected function reloadEntity(EntityInterface $entity, $revision_id = FALSE) {
|
||||
$storage = \Drupal::entityTypeManager()->getStorage($entity->getEntityTypeId());
|
||||
$storage->resetCache([$entity->id()]);
|
||||
if ($revision_id) {
|
||||
return $storage->loadRevision($revision_id);
|
||||
}
|
||||
return $storage->load($node->id());
|
||||
return $storage->load($entity->id());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\content_moderation\Kernel;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\workflows\Entity\Workflow;
|
||||
|
||||
/**
|
||||
* Tests the API of the ContentModeration workflow type plugin.
|
||||
*
|
||||
* @group content_moderation
|
||||
*
|
||||
* @coversDefaultClass \Drupal\content_moderation\Plugin\WorkflowType\ContentModeration
|
||||
*/
|
||||
class ContentModerationWorkflowTypeApiTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* A workflow for testing.
|
||||
*
|
||||
* @var \Drupal\workflows\Entity\Workflow;
|
||||
*/
|
||||
protected $workflow;
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = [
|
||||
'workflows',
|
||||
'content_moderation',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->workflow = Workflow::create(['id' => 'test', 'type' => 'content_moderation']);
|
||||
$this->workflow
|
||||
->addState('draft', 'Draft')
|
||||
->addState('published', 'Published');
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getBundlesForEntityType
|
||||
* @covers ::addEntityTypeAndBundle
|
||||
* @covers ::removeEntityTypeAndBundle
|
||||
*/
|
||||
public function testGetBundlesForEntityType() {
|
||||
/** @var \Drupal\content_moderation\Plugin\WorkflowType\ContentModeration $workflow_plugin */
|
||||
$workflow_plugin = $this->workflow->getTypePlugin();
|
||||
// The content moderation plugin does not validate the existence of the
|
||||
// entity type or bundle.
|
||||
$this->assertEquals([], $workflow_plugin->getBundlesForEntityType('fake_node'));
|
||||
$workflow_plugin->addEntityTypeAndBundle('fake_node', 'fake_page');
|
||||
$this->assertEquals(['fake_page'], $workflow_plugin->getBundlesForEntityType('fake_node'));
|
||||
$this->assertEquals([], $workflow_plugin->getBundlesForEntityType('fake_block'));
|
||||
$workflow_plugin->removeEntityTypeAndBundle('fake_node', 'fake_page');
|
||||
$this->assertEquals([], $workflow_plugin->getBundlesForEntityType('fake_node'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::appliesToEntityTypeAndBundle
|
||||
* @covers ::addEntityTypeAndBundle
|
||||
* @covers ::removeEntityTypeAndBundle
|
||||
*/
|
||||
public function testAppliesToEntityTypeAndBundle() {
|
||||
/** @var \Drupal\content_moderation\Plugin\WorkflowType\ContentModeration $workflow_plugin */
|
||||
$workflow_plugin = $this->workflow->getTypePlugin();
|
||||
// The content moderation plugin does not validate the existence of the
|
||||
// entity type or bundle.
|
||||
$this->assertFalse($workflow_plugin->appliesToEntityTypeAndBundle('fake_node', 'fake_page'));
|
||||
$workflow_plugin->addEntityTypeAndBundle('fake_node', 'fake_page');
|
||||
$this->assertTrue($workflow_plugin->appliesToEntityTypeAndBundle('fake_node', 'fake_page'));
|
||||
$this->assertFalse($workflow_plugin->appliesToEntityTypeAndBundle('fake_block', 'fake_custom'));
|
||||
$workflow_plugin->removeEntityTypeAndBundle('fake_node', 'fake_page');
|
||||
$this->assertFalse($workflow_plugin->appliesToEntityTypeAndBundle('fake_node', 'fake_page'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::addEntityTypeAndBundle
|
||||
*/
|
||||
public function testAddEntityTypeAndBundle() {
|
||||
/** @var \Drupal\content_moderation\Plugin\WorkflowType\ContentModeration $workflow_plugin */
|
||||
$workflow_plugin = $this->workflow->getTypePlugin();
|
||||
|
||||
// The bundles are intentionally added in reverse alphabetical order.
|
||||
$workflow_plugin->addEntityTypeAndBundle('fake_node', 'fake_page');
|
||||
$workflow_plugin->addEntityTypeAndBundle('fake_node', 'fake_article');
|
||||
|
||||
// Add another entity type that comes alphabetically before 'fake_node'.
|
||||
$workflow_plugin->addEntityTypeAndBundle('fake_block', 'fake_custom');
|
||||
|
||||
// The entity type keys and bundle values should be sorted alphabetically.
|
||||
// The bundle array index should not reflect the order in which they are
|
||||
// added.
|
||||
$this->assertSame(
|
||||
['fake_block' => ['fake_custom'], 'fake_node' => ['fake_article', 'fake_page']],
|
||||
$workflow_plugin->getConfiguration()['entity_types']
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -4,9 +4,9 @@ namespace Drupal\Tests\content_moderation\Kernel;
|
|||
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\content_moderation\Entity\ModerationState;
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
use Drupal\workflows\Entity\Workflow;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\content_moderation\EntityOperations
|
||||
|
@ -23,6 +23,7 @@ class EntityOperationsTest extends KernelTestBase {
|
|||
'node',
|
||||
'user',
|
||||
'system',
|
||||
'workflows',
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -47,8 +48,10 @@ class EntityOperationsTest extends KernelTestBase {
|
|||
'type' => 'page',
|
||||
'label' => 'Page',
|
||||
]);
|
||||
$node_type->setThirdPartySetting('content_moderation', 'enabled', TRUE);
|
||||
$node_type->save();
|
||||
$workflow = Workflow::load('editorial');
|
||||
$workflow->getTypePlugin()->addEntityTypeAndBundle('node', 'page');
|
||||
$workflow->save();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -60,7 +63,7 @@ class EntityOperationsTest extends KernelTestBase {
|
|||
'type' => 'page',
|
||||
'title' => 'A',
|
||||
]);
|
||||
$page->moderation_state->target_id = 'draft';
|
||||
$page->moderation_state->value = 'draft';
|
||||
$page->save();
|
||||
|
||||
$id = $page->id();
|
||||
|
@ -75,7 +78,7 @@ class EntityOperationsTest extends KernelTestBase {
|
|||
|
||||
// Moderate the entity to published.
|
||||
$page->setTitle('B');
|
||||
$page->moderation_state->target_id = 'published';
|
||||
$page->moderation_state->value = 'published';
|
||||
$page->save();
|
||||
|
||||
// Verify the entity is now published and public.
|
||||
|
@ -86,7 +89,7 @@ class EntityOperationsTest extends KernelTestBase {
|
|||
|
||||
// Make a new forward-revision in Draft.
|
||||
$page->setTitle('C');
|
||||
$page->moderation_state->target_id = 'draft';
|
||||
$page->moderation_state->value = 'draft';
|
||||
$page->save();
|
||||
|
||||
// Verify normal loads return the still-default previous version.
|
||||
|
@ -105,7 +108,7 @@ class EntityOperationsTest extends KernelTestBase {
|
|||
$this->assertEquals('C', $page->getTitle());
|
||||
|
||||
$page->setTitle('D');
|
||||
$page->moderation_state->target_id = 'published';
|
||||
$page->moderation_state->value = 'published';
|
||||
$page->save();
|
||||
|
||||
// Verify normal loads return the still-default previous version.
|
||||
|
@ -116,7 +119,7 @@ class EntityOperationsTest extends KernelTestBase {
|
|||
|
||||
// Now check that we can immediately add a new published revision over it.
|
||||
$page->setTitle('E');
|
||||
$page->moderation_state->target_id = 'published';
|
||||
$page->moderation_state->value = 'published';
|
||||
$page->save();
|
||||
|
||||
$page = Node::load($id);
|
||||
|
@ -134,7 +137,7 @@ class EntityOperationsTest extends KernelTestBase {
|
|||
'type' => 'page',
|
||||
'title' => 'A',
|
||||
]);
|
||||
$page->moderation_state->target_id = 'published';
|
||||
$page->moderation_state->value = 'published';
|
||||
$page->save();
|
||||
|
||||
$id = $page->id();
|
||||
|
@ -151,29 +154,12 @@ class EntityOperationsTest extends KernelTestBase {
|
|||
* Verifies that an unpublished state may be made the default revision.
|
||||
*/
|
||||
public function testArchive() {
|
||||
$published_id = $this->randomMachineName();
|
||||
$published_state = ModerationState::create([
|
||||
'id' => $published_id,
|
||||
'label' => $this->randomString(),
|
||||
'published' => TRUE,
|
||||
'default_revision' => TRUE,
|
||||
]);
|
||||
$published_state->save();
|
||||
|
||||
$archived_id = $this->randomMachineName();
|
||||
$archived_state = ModerationState::create([
|
||||
'id' => $archived_id,
|
||||
'label' => $this->randomString(),
|
||||
'published' => FALSE,
|
||||
'default_revision' => TRUE,
|
||||
]);
|
||||
$archived_state->save();
|
||||
|
||||
$page = Node::create([
|
||||
'type' => 'page',
|
||||
'title' => $this->randomString(),
|
||||
]);
|
||||
$page->moderation_state->target_id = $published_id;
|
||||
|
||||
$page->moderation_state->value = 'published';
|
||||
$page->save();
|
||||
|
||||
$id = $page->id();
|
||||
|
@ -184,7 +170,7 @@ class EntityOperationsTest extends KernelTestBase {
|
|||
|
||||
// When the page is moderated to the archived state, then the latest
|
||||
// revision should be the default revision, and it should be unpublished.
|
||||
$page->moderation_state->target_id = $archived_id;
|
||||
$page->moderation_state->value = 'archived';
|
||||
$page->save();
|
||||
$new_revision_id = $page->getRevisionId();
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ use Drupal\entity_test\Entity\EntityTest;
|
|||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
use Drupal\workflows\Entity\Workflow;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\content_moderation\ParamConverter\EntityRevisionConverter
|
||||
|
@ -19,6 +20,7 @@ class EntityRevisionConverterTest extends KernelTestBase {
|
|||
'system',
|
||||
'content_moderation',
|
||||
'node',
|
||||
'workflows',
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -59,17 +61,21 @@ class EntityRevisionConverterTest extends KernelTestBase {
|
|||
* @covers ::convert
|
||||
*/
|
||||
public function testConvertWithRevisionableEntityType() {
|
||||
$this->installConfig(['content_moderation']);
|
||||
$node_type = NodeType::create([
|
||||
'type' => 'article',
|
||||
]);
|
||||
$node_type->setThirdPartySetting('content_moderation', 'enabled', TRUE);
|
||||
$node_type->save();
|
||||
$workflow = Workflow::load('editorial');
|
||||
$workflow->getTypePlugin()->addEntityTypeAndBundle('node', 'article');
|
||||
$workflow->save();
|
||||
|
||||
$revision_ids = [];
|
||||
$node = Node::create([
|
||||
'title' => 'test',
|
||||
'type' => 'article',
|
||||
]);
|
||||
$node->moderation_state->value = 'published';
|
||||
$node->save();
|
||||
|
||||
$revision_ids[] = $node->getRevisionId();
|
||||
|
@ -79,7 +85,7 @@ class EntityRevisionConverterTest extends KernelTestBase {
|
|||
$revision_ids[] = $node->getRevisionId();
|
||||
|
||||
$node->setNewRevision(TRUE);
|
||||
$node->isDefaultRevision(FALSE);
|
||||
$node->moderation_state->value = 'draft';
|
||||
$node->save();
|
||||
$revision_ids[] = $node->getRevisionId();
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ use Drupal\KernelTests\KernelTestBase;
|
|||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
use Drupal\workflows\Entity\Workflow;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\content_moderation\Plugin\Validation\Constraint\ModerationStateConstraintValidator
|
||||
|
@ -23,6 +24,7 @@ class EntityStateChangeValidationTest extends KernelTestBase {
|
|||
'system',
|
||||
'language',
|
||||
'content_translation',
|
||||
'workflows',
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -47,20 +49,23 @@ class EntityStateChangeValidationTest extends KernelTestBase {
|
|||
$node_type = NodeType::create([
|
||||
'type' => 'example',
|
||||
]);
|
||||
$node_type->setThirdPartySetting('content_moderation', 'enabled', TRUE);
|
||||
$node_type->save();
|
||||
$workflow = Workflow::load('editorial');
|
||||
$workflow->getTypePlugin()->addEntityTypeAndBundle('node', 'example');
|
||||
$workflow->save();
|
||||
|
||||
$node = Node::create([
|
||||
'type' => 'example',
|
||||
'title' => 'Test title',
|
||||
]);
|
||||
$node->moderation_state->target_id = 'draft';
|
||||
$node->moderation_state->value = 'draft';
|
||||
$node->save();
|
||||
|
||||
$node->moderation_state->target_id = 'published';
|
||||
$node->moderation_state->value = 'published';
|
||||
$this->assertCount(0, $node->validate());
|
||||
$node->save();
|
||||
|
||||
$this->assertEquals('published', $node->moderation_state->entity->id());
|
||||
$this->assertEquals('published', $node->moderation_state->value);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -72,16 +77,19 @@ class EntityStateChangeValidationTest extends KernelTestBase {
|
|||
$node_type = NodeType::create([
|
||||
'type' => 'example',
|
||||
]);
|
||||
$node_type->setThirdPartySetting('content_moderation', 'enabled', TRUE);
|
||||
$node_type->save();
|
||||
$workflow = Workflow::load('editorial');
|
||||
$workflow->getTypePlugin()->addEntityTypeAndBundle('node', 'example');
|
||||
$workflow->save();
|
||||
|
||||
$node = Node::create([
|
||||
'type' => 'example',
|
||||
'title' => 'Test title',
|
||||
]);
|
||||
$node->moderation_state->target_id = 'draft';
|
||||
$node->moderation_state->value = 'draft';
|
||||
$node->save();
|
||||
|
||||
$node->moderation_state->target_id = 'archived';
|
||||
$node->moderation_state->value = 'archived';
|
||||
$violations = $node->validate();
|
||||
$this->assertCount(1, $violations);
|
||||
|
||||
|
@ -106,12 +114,9 @@ class EntityStateChangeValidationTest extends KernelTestBase {
|
|||
$nid = $node->id();
|
||||
|
||||
// Enable moderation for our node type.
|
||||
/** @var NodeType $node_type */
|
||||
$node_type = NodeType::load('example');
|
||||
$node_type->setThirdPartySetting('content_moderation', 'enabled', TRUE);
|
||||
$node_type->setThirdPartySetting('content_moderation', 'allowed_moderation_states', ['draft', 'published']);
|
||||
$node_type->setThirdPartySetting('content_moderation', 'default_moderation_state', 'draft');
|
||||
$node_type->save();
|
||||
$workflow = Workflow::load('editorial');
|
||||
$workflow->getTypePlugin()->addEntityTypeAndBundle('node', 'example');
|
||||
$workflow->save();
|
||||
|
||||
$node = Node::load($nid);
|
||||
|
||||
|
@ -155,12 +160,9 @@ class EntityStateChangeValidationTest extends KernelTestBase {
|
|||
$node_fr->save();
|
||||
|
||||
// Enable moderation for our node type.
|
||||
/** @var NodeType $node_type */
|
||||
$node_type = NodeType::load('example');
|
||||
$node_type->setThirdPartySetting('content_moderation', 'enabled', TRUE);
|
||||
$node_type->setThirdPartySetting('content_moderation', 'allowed_moderation_states', ['draft', 'published']);
|
||||
$node_type->setThirdPartySetting('content_moderation', 'default_moderation_state', 'draft');
|
||||
$node_type->save();
|
||||
$workflow = Workflow::load('editorial');
|
||||
$workflow->getTypePlugin()->addEntityTypeAndBundle('node', 'example');
|
||||
$workflow->save();
|
||||
|
||||
// Reload the French version of the node.
|
||||
$node = Node::load($nid);
|
||||
|
|
|
@ -1,69 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\content_moderation\Kernel;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\content_moderation\Entity\ModerationState;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\content_moderation\Entity\ModerationState
|
||||
*
|
||||
* @group content_moderation
|
||||
*/
|
||||
class ModerationStateEntityTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['content_moderation'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installEntitySchema('moderation_state');
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify moderation state methods based on entity properties.
|
||||
*
|
||||
* @covers ::isPublishedState
|
||||
* @covers ::isDefaultRevisionState
|
||||
*
|
||||
* @dataProvider moderationStateProvider
|
||||
*/
|
||||
public function testModerationStateProperties($published, $default_revision, $is_published, $is_default) {
|
||||
$moderation_state_id = $this->randomMachineName();
|
||||
$moderation_state = ModerationState::create([
|
||||
'id' => $moderation_state_id,
|
||||
'label' => $this->randomString(),
|
||||
'published' => $published,
|
||||
'default_revision' => $default_revision,
|
||||
]);
|
||||
$moderation_state->save();
|
||||
|
||||
$moderation_state = ModerationState::load($moderation_state_id);
|
||||
$this->assertEquals($is_published, $moderation_state->isPublishedState());
|
||||
$this->assertEquals($is_default, $moderation_state->isDefaultRevisionState());
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for ::testModerationStateProperties.
|
||||
*/
|
||||
public function moderationStateProvider() {
|
||||
return [
|
||||
// Draft, Needs review; should not touch the default revision.
|
||||
[FALSE, FALSE, FALSE, FALSE],
|
||||
// Published; this state should update and publish the default revision.
|
||||
[TRUE, TRUE, TRUE, TRUE],
|
||||
// Archive; this state should update but not publish the default revision.
|
||||
[FALSE, TRUE, FALSE, TRUE],
|
||||
// We try to prevent creating this state via the UI, but when a moderation
|
||||
// state is a published state, it should also become the default revision.
|
||||
[TRUE, FALSE, TRUE, TRUE],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
|
@ -5,6 +5,7 @@ namespace Drupal\Tests\content_moderation\Kernel;
|
|||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
use Drupal\workflows\Entity\Workflow;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\content_moderation\Plugin\Field\ModerationStateFieldItemList
|
||||
|
@ -22,6 +23,7 @@ class ModerationStateFieldItemListTest extends KernelTestBase {
|
|||
'user',
|
||||
'system',
|
||||
'language',
|
||||
'workflows',
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -44,10 +46,11 @@ class ModerationStateFieldItemListTest extends KernelTestBase {
|
|||
$node_type = NodeType::create([
|
||||
'type' => 'example',
|
||||
]);
|
||||
$node_type->setThirdPartySetting('content_moderation', 'enabled', TRUE);
|
||||
$node_type->setThirdPartySetting('content_moderation', 'allowed_moderation_states', ['draft']);
|
||||
$node_type->setThirdPartySetting('content_moderation', 'default_moderation_state', 'draft');
|
||||
$node_type->save();
|
||||
$workflow = Workflow::load('editorial');
|
||||
$workflow->getTypePlugin()->addEntityTypeAndBundle('node', 'example');
|
||||
$workflow->save();
|
||||
|
||||
$this->testNode = Node::create([
|
||||
'type' => 'example',
|
||||
'title' => 'Test title',
|
||||
|
@ -61,7 +64,7 @@ class ModerationStateFieldItemListTest extends KernelTestBase {
|
|||
* Test the field item list when accessing an index.
|
||||
*/
|
||||
public function testArrayIndex() {
|
||||
$this->assertEquals('draft', $this->testNode->moderation_state[0]->entity->id());
|
||||
$this->assertEquals('draft', $this->testNode->moderation_state[0]->value);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -70,7 +73,7 @@ class ModerationStateFieldItemListTest extends KernelTestBase {
|
|||
public function testArrayIteration() {
|
||||
$states = [];
|
||||
foreach ($this->testNode->moderation_state as $item) {
|
||||
$states[] = $item->entity->id();
|
||||
$states[] = $item->value;
|
||||
}
|
||||
$this->assertEquals(['draft'], $states);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\content_moderation\Kernel;
|
||||
|
||||
use Drupal\Core\Render\RenderContext;
|
||||
use Drupal\entity_test\Entity\EntityTestRev;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\workflows\Entity\Workflow;
|
||||
|
||||
/**
|
||||
* Test the state field formatter.
|
||||
*
|
||||
* @group content_moderation
|
||||
*/
|
||||
class StateFormatterTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = [
|
||||
'workflows',
|
||||
'content_moderation',
|
||||
'entity_test',
|
||||
'user',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installEntitySchema('entity_test_rev');
|
||||
$this->installEntitySchema('content_moderation_state');
|
||||
$this->installConfig('content_moderation');
|
||||
|
||||
$workflow = Workflow::load('editorial');
|
||||
$workflow->getTypePlugin()->addEntityTypeAndBundle('entity_test_rev', 'entity_test_rev');
|
||||
$workflow->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the embed field.
|
||||
*
|
||||
* @dataProvider formatterTestCases
|
||||
*/
|
||||
public function testStateFieldFormatter($field_value, $formatter_settings, $expected_output) {
|
||||
$entity = EntityTestRev::create([
|
||||
'moderation_state' => $field_value,
|
||||
]);
|
||||
$entity->save();
|
||||
|
||||
$field_output = $this->container->get('renderer')->executeInRenderContext(new RenderContext(), function() use ($entity, $formatter_settings) {
|
||||
return $entity->moderation_state->view($formatter_settings);
|
||||
});
|
||||
|
||||
$this->assertEquals($expected_output, $field_output[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test cases for ::
|
||||
*/
|
||||
public function formatterTestCases() {
|
||||
return [
|
||||
'Draft State' => [
|
||||
'draft',
|
||||
[
|
||||
'type' => 'content_moderation_state',
|
||||
'settings' => [],
|
||||
],
|
||||
[
|
||||
'#markup' => 'Draft',
|
||||
],
|
||||
],
|
||||
'Published State' => [
|
||||
'published',
|
||||
[
|
||||
'type' => 'content_moderation_state',
|
||||
'settings' => [],
|
||||
],
|
||||
[
|
||||
'#markup' => 'Published',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
|
@ -2,10 +2,12 @@
|
|||
|
||||
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;
|
||||
use Drupal\views\Views;
|
||||
use Drupal\workflows\Entity\Workflow;
|
||||
|
||||
/**
|
||||
* Tests the views integration of content_moderation.
|
||||
|
@ -21,6 +23,8 @@ class ViewsDataIntegrationTest extends ViewsKernelTestBase {
|
|||
'content_moderation_test_views',
|
||||
'node',
|
||||
'content_moderation',
|
||||
'workflows',
|
||||
'entity_test',
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -30,6 +34,7 @@ class ViewsDataIntegrationTest extends ViewsKernelTestBase {
|
|||
parent::setUp($import_test_views);
|
||||
|
||||
$this->installEntitySchema('node');
|
||||
$this->installEntitySchema('entity_test_mulrevpub');
|
||||
$this->installEntitySchema('user');
|
||||
$this->installEntitySchema('content_moderation_state');
|
||||
$this->installSchema('node', 'node_access');
|
||||
|
@ -39,8 +44,11 @@ class ViewsDataIntegrationTest extends ViewsKernelTestBase {
|
|||
$node_type = NodeType::create([
|
||||
'type' => 'page',
|
||||
]);
|
||||
$node_type->setThirdPartySetting('content_moderation', 'enabled', TRUE);
|
||||
$node_type->save();
|
||||
$workflow = Workflow::load('editorial');
|
||||
$workflow->getTypePlugin()->addEntityTypeAndBundle('node', 'page');
|
||||
$workflow->getTypePlugin()->addEntityTypeAndBundle('entity_test_mulrevpub', 'entity_test_mulrevpub');
|
||||
$workflow->save();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -53,14 +61,23 @@ class ViewsDataIntegrationTest extends ViewsKernelTestBase {
|
|||
'type' => 'page',
|
||||
'title' => 'Test title first revision',
|
||||
]);
|
||||
$node->moderation_state->target_id = 'published';
|
||||
$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->target_id = 'draft';
|
||||
$revision->moderation_state->value = 'draft';
|
||||
$revision->save();
|
||||
|
||||
$view = Views::getView('test_content_moderation_latest_revision');
|
||||
|
@ -90,14 +107,14 @@ class ViewsDataIntegrationTest extends ViewsKernelTestBase {
|
|||
'type' => 'page',
|
||||
'title' => 'Test title first revision',
|
||||
]);
|
||||
$node->moderation_state->target_id = 'published';
|
||||
$node->moderation_state->value = 'published';
|
||||
$node->save();
|
||||
|
||||
$revision = clone $node;
|
||||
$revision->setNewRevision(TRUE);
|
||||
$revision->isDefaultRevision(FALSE);
|
||||
$revision->title->value = 'Test title second revision';
|
||||
$revision->moderation_state->target_id = 'draft';
|
||||
$revision->moderation_state->value = 'draft';
|
||||
$revision->save();
|
||||
|
||||
$view = Views::getView('test_content_moderation_revision_test');
|
||||
|
@ -105,15 +122,15 @@ class ViewsDataIntegrationTest extends ViewsKernelTestBase {
|
|||
|
||||
$expected_result = [
|
||||
[
|
||||
'revision_id' => $node->getRevisionId(),
|
||||
'vid' => $node->getRevisionId(),
|
||||
'moderation_state' => 'published',
|
||||
],
|
||||
[
|
||||
'revision_id' => $revision->getRevisionId(),
|
||||
'vid' => $revision->getRevisionId(),
|
||||
'moderation_state' => 'draft',
|
||||
],
|
||||
];
|
||||
$this->assertIdenticalResultset($view, $expected_result, ['revision_id' => 'revision_id', 'moderation_state' => 'moderation_state']);
|
||||
$this->assertIdenticalResultset($view, $expected_result, ['vid' => 'vid', 'moderation_state' => 'moderation_state']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -124,14 +141,14 @@ class ViewsDataIntegrationTest extends ViewsKernelTestBase {
|
|||
'type' => 'page',
|
||||
'title' => 'Test title first revision',
|
||||
]);
|
||||
$node->moderation_state->target_id = 'published';
|
||||
$node->moderation_state->value = 'published';
|
||||
$node->save();
|
||||
|
||||
$revision = clone $node;
|
||||
$revision->setNewRevision(TRUE);
|
||||
$revision->isDefaultRevision(FALSE);
|
||||
$revision->title->value = 'Test title second revision';
|
||||
$revision->moderation_state->target_id = 'draft';
|
||||
$revision->moderation_state->value = 'draft';
|
||||
$revision->save();
|
||||
|
||||
$view = Views::getView('test_content_moderation_base_table_test');
|
||||
|
|
|
@ -3,13 +3,14 @@
|
|||
namespace Drupal\Tests\content_moderation\Unit;
|
||||
|
||||
use Drupal\content_moderation\Entity\Handler\ModerationHandler;
|
||||
use Drupal\Core\Config\Entity\ConfigEntityInterface;
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
use Drupal\Core\Entity\ContentEntityType;
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\content_moderation\ModerationInformation;
|
||||
use Drupal\workflows\WorkflowInterface;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\content_moderation\ModerationInformation
|
||||
|
@ -30,43 +31,42 @@ class ModerationInformationTest extends \PHPUnit_Framework_TestCase {
|
|||
/**
|
||||
* Returns a mock Entity Type Manager.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityStorageInterface $entity_bundle_storage
|
||||
* Entity bundle storage.
|
||||
*
|
||||
* @return EntityTypeManagerInterface
|
||||
* The mocked entity type manager.
|
||||
*/
|
||||
protected function getEntityTypeManager(EntityStorageInterface $entity_bundle_storage) {
|
||||
protected function getEntityTypeManager() {
|
||||
$entity_type_manager = $this->prophesize(EntityTypeManagerInterface::class);
|
||||
$entity_type_manager->getStorage('entity_test_bundle')->willReturn($entity_bundle_storage);
|
||||
return $entity_type_manager->reveal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up content moderation and entity manager mocking.
|
||||
*
|
||||
* @param bool $status
|
||||
* TRUE if content_moderation should be enabled, FALSE if not.
|
||||
* @param string $bundle
|
||||
* The bundle ID.
|
||||
* @param string|null $workflow
|
||||
* The workflow ID. If nul no workflow information is added to the bundle.
|
||||
*
|
||||
* @return \Drupal\Core\Entity\EntityTypeManagerInterface
|
||||
* The mocked entity type manager.
|
||||
*/
|
||||
public function setupModerationEntityManager($status) {
|
||||
$bundle = $this->prophesize(ConfigEntityInterface::class);
|
||||
$bundle->getThirdPartySetting('content_moderation', 'enabled', FALSE)->willReturn($status);
|
||||
public function setupModerationBundleInfo($bundle, $workflow = NULL) {
|
||||
$bundle_info_array = [];
|
||||
if ($workflow) {
|
||||
$bundle_info_array['workflow'] = $workflow;
|
||||
}
|
||||
$bundle_info = $this->prophesize(EntityTypeBundleInfoInterface::class);
|
||||
$bundle_info->getBundleInfo("test_entity_type")->willReturn([$bundle => $bundle_info_array]);
|
||||
|
||||
$entity_storage = $this->prophesize(EntityStorageInterface::class);
|
||||
$entity_storage->load('test_bundle')->willReturn($bundle->reveal());
|
||||
|
||||
return $this->getEntityTypeManager($entity_storage->reveal());
|
||||
return $bundle_info->reveal();
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providerBoolean
|
||||
* @dataProvider providerWorkflow
|
||||
* @covers ::isModeratedEntity
|
||||
*/
|
||||
public function testIsModeratedEntity($status) {
|
||||
$moderation_information = new ModerationInformation($this->setupModerationEntityManager($status), $this->getUser());
|
||||
public function testIsModeratedEntity($workflow, $expected) {
|
||||
$moderation_information = new ModerationInformation($this->getEntityTypeManager(), $this->setupModerationBundleInfo('test_bundle', $workflow));
|
||||
|
||||
$entity_type = new ContentEntityType([
|
||||
'id' => 'test_entity_type',
|
||||
|
@ -77,50 +77,55 @@ class ModerationInformationTest extends \PHPUnit_Framework_TestCase {
|
|||
$entity->getEntityType()->willReturn($entity_type);
|
||||
$entity->bundle()->willReturn('test_bundle');
|
||||
|
||||
$this->assertEquals($status, $moderation_information->isModeratedEntity($entity->reveal()));
|
||||
$this->assertEquals($expected, $moderation_information->isModeratedEntity($entity->reveal()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::isModeratedEntity
|
||||
* @dataProvider providerWorkflow
|
||||
* @covers ::getWorkflowForEntity
|
||||
*/
|
||||
public function testIsModeratedEntityForNonBundleEntityType() {
|
||||
$entity_type = new ContentEntityType([
|
||||
'id' => 'test_entity_type',
|
||||
]);
|
||||
public function testGetWorkflowForEntity($workflow) {
|
||||
$entity_type_manager = $this->prophesize(EntityTypeManagerInterface::class);
|
||||
if ($workflow) {
|
||||
$workflow_entity = $this->prophesize(WorkflowInterface::class)->reveal();
|
||||
$workflow_storage = $this->prophesize(EntityStorageInterface::class);
|
||||
$workflow_storage->load('workflow')->willReturn($workflow_entity)->shouldBeCalled();
|
||||
$entity_type_manager->getStorage('workflow')->willReturn($workflow_storage->reveal());
|
||||
}
|
||||
else {
|
||||
$workflow_entity = NULL;
|
||||
}
|
||||
$moderation_information = new ModerationInformation($entity_type_manager->reveal(), $this->setupModerationBundleInfo('test_bundle', $workflow));
|
||||
$entity = $this->prophesize(ContentEntityInterface::class);
|
||||
$entity->getEntityType()->willReturn($entity_type);
|
||||
$entity->bundle()->willReturn('test_entity_type');
|
||||
$entity->getEntityTypeId()->willReturn('test_entity_type');
|
||||
$entity->bundle()->willReturn('test_bundle');
|
||||
|
||||
$entity_storage = $this->prophesize(EntityStorageInterface::class);
|
||||
$entity_type_manager = $this->getEntityTypeManager($entity_storage->reveal());
|
||||
$moderation_information = new ModerationInformation($entity_type_manager, $this->getUser());
|
||||
|
||||
$this->assertEquals(FALSE, $moderation_information->isModeratedEntity($entity->reveal()));
|
||||
$this->assertEquals($workflow_entity, $moderation_information->getWorkflowForEntity($entity->reveal()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providerBoolean
|
||||
* @dataProvider providerWorkflow
|
||||
* @covers ::shouldModerateEntitiesOfBundle
|
||||
*/
|
||||
public function testShouldModerateEntities($status) {
|
||||
public function testShouldModerateEntities($workflow, $expected) {
|
||||
$entity_type = new ContentEntityType([
|
||||
'id' => 'test_entity_type',
|
||||
'bundle_entity_type' => 'entity_test_bundle',
|
||||
'handlers' => ['moderation' => ModerationHandler::class],
|
||||
]);
|
||||
|
||||
$moderation_information = new ModerationInformation($this->setupModerationEntityManager($status), $this->getUser());
|
||||
$moderation_information = new ModerationInformation($this->getEntityTypeManager(), $this->setupModerationBundleInfo('test_bundle', $workflow));
|
||||
|
||||
$this->assertEquals($status, $moderation_information->shouldModerateEntitiesOfBundle($entity_type, 'test_bundle'));
|
||||
$this->assertEquals($expected, $moderation_information->shouldModerateEntitiesOfBundle($entity_type, 'test_bundle'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for several tests.
|
||||
*/
|
||||
public function providerBoolean() {
|
||||
public function providerWorkflow() {
|
||||
return [
|
||||
[FALSE],
|
||||
[TRUE],
|
||||
[NULL, FALSE],
|
||||
['workflow', TRUE],
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
@ -2,13 +2,14 @@
|
|||
|
||||
namespace Drupal\Tests\content_moderation\Unit;
|
||||
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Entity\Query\QueryFactory;
|
||||
use Drupal\content_moderation\ModerationInformationInterface;
|
||||
use Drupal\Core\DependencyInjection\ContainerBuilder;
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\content_moderation\ModerationStateInterface;
|
||||
use Drupal\content_moderation\ModerationStateTransitionInterface;
|
||||
use Drupal\content_moderation\StateTransitionValidation;
|
||||
use Drupal\workflows\Entity\Workflow;
|
||||
use Drupal\workflows\WorkflowTypeInterface;
|
||||
use Drupal\workflows\WorkflowTypeManager;
|
||||
use Prophecy\Argument;
|
||||
|
||||
/**
|
||||
|
@ -17,216 +18,6 @@ use Prophecy\Argument;
|
|||
*/
|
||||
class StateTransitionValidationTest extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
/**
|
||||
* Builds a mock storage object for Transitions.
|
||||
*
|
||||
* @return EntityStorageInterface
|
||||
* The mocked storage object for Transitions.
|
||||
*/
|
||||
protected function setupTransitionStorage() {
|
||||
$entity_storage = $this->prophesize(EntityStorageInterface::class);
|
||||
|
||||
$list = $this->setupTransitionEntityList();
|
||||
$entity_storage->loadMultiple()->willReturn($list);
|
||||
$entity_storage->loadMultiple(Argument::type('array'))->will(function ($args) use ($list) {
|
||||
$keys = $args[0];
|
||||
if (empty($keys)) {
|
||||
return $list;
|
||||
}
|
||||
|
||||
$return = array_map(function($key) use ($list) {
|
||||
return $list[$key];
|
||||
}, $keys);
|
||||
|
||||
return $return;
|
||||
});
|
||||
return $entity_storage->reveal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an array of mocked Transition objects.
|
||||
*
|
||||
* @return ModerationStateTransitionInterface[]
|
||||
* An array of mocked Transition objects.
|
||||
*/
|
||||
protected function setupTransitionEntityList() {
|
||||
$transition = $this->prophesize(ModerationStateTransitionInterface::class);
|
||||
$transition->id()->willReturn('draft__needs_review');
|
||||
$transition->getFromState()->willReturn('draft');
|
||||
$transition->getToState()->willReturn('needs_review');
|
||||
$list[$transition->reveal()->id()] = $transition->reveal();
|
||||
|
||||
$transition = $this->prophesize(ModerationStateTransitionInterface::class);
|
||||
$transition->id()->willReturn('needs_review__staging');
|
||||
$transition->getFromState()->willReturn('needs_review');
|
||||
$transition->getToState()->willReturn('staging');
|
||||
$list[$transition->reveal()->id()] = $transition->reveal();
|
||||
|
||||
$transition = $this->prophesize(ModerationStateTransitionInterface::class);
|
||||
$transition->id()->willReturn('staging__published');
|
||||
$transition->getFromState()->willReturn('staging');
|
||||
$transition->getToState()->willReturn('published');
|
||||
$list[$transition->reveal()->id()] = $transition->reveal();
|
||||
|
||||
$transition = $this->prophesize(ModerationStateTransitionInterface::class);
|
||||
$transition->id()->willReturn('needs_review__draft');
|
||||
$transition->getFromState()->willReturn('needs_review');
|
||||
$transition->getToState()->willReturn('draft');
|
||||
$list[$transition->reveal()->id()] = $transition->reveal();
|
||||
|
||||
$transition = $this->prophesize(ModerationStateTransitionInterface::class);
|
||||
$transition->id()->willReturn('draft__draft');
|
||||
$transition->getFromState()->willReturn('draft');
|
||||
$transition->getToState()->willReturn('draft');
|
||||
$list[$transition->reveal()->id()] = $transition->reveal();
|
||||
|
||||
$transition = $this->prophesize(ModerationStateTransitionInterface::class);
|
||||
$transition->id()->willReturn('needs_review__needs_review');
|
||||
$transition->getFromState()->willReturn('needs_review');
|
||||
$transition->getToState()->willReturn('needs_review');
|
||||
$list[$transition->reveal()->id()] = $transition->reveal();
|
||||
|
||||
$transition = $this->prophesize(ModerationStateTransitionInterface::class);
|
||||
$transition->id()->willReturn('published__published');
|
||||
$transition->getFromState()->willReturn('published');
|
||||
$transition->getToState()->willReturn('published');
|
||||
$list[$transition->reveal()->id()] = $transition->reveal();
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a mock storage object for States.
|
||||
*
|
||||
* @return EntityStorageInterface
|
||||
* The mocked storage object for States.
|
||||
*/
|
||||
protected function setupStateStorage() {
|
||||
$entity_storage = $this->prophesize(EntityStorageInterface::class);
|
||||
|
||||
$state = $this->prophesize(ModerationStateInterface::class);
|
||||
$state->id()->willReturn('draft');
|
||||
$state->label()->willReturn('Draft');
|
||||
$state->isPublishedState()->willReturn(FALSE);
|
||||
$state->isDefaultRevisionState()->willReturn(FALSE);
|
||||
$states['draft'] = $state->reveal();
|
||||
|
||||
$state = $this->prophesize(ModerationStateInterface::class);
|
||||
$state->id()->willReturn('needs_review');
|
||||
$state->label()->willReturn('Needs Review');
|
||||
$state->isPublishedState()->willReturn(FALSE);
|
||||
$state->isDefaultRevisionState()->willReturn(FALSE);
|
||||
$states['needs_review'] = $state->reveal();
|
||||
|
||||
$state = $this->prophesize(ModerationStateInterface::class);
|
||||
$state->id()->willReturn('staging');
|
||||
$state->label()->willReturn('Staging');
|
||||
$state->isPublishedState()->willReturn(FALSE);
|
||||
$state->isDefaultRevisionState()->willReturn(FALSE);
|
||||
$states['staging'] = $state->reveal();
|
||||
|
||||
$state = $this->prophesize(ModerationStateInterface::class);
|
||||
$state->id()->willReturn('published');
|
||||
$state->label()->willReturn('Published');
|
||||
$state->isPublishedState()->willReturn(TRUE);
|
||||
$state->isDefaultRevisionState()->willReturn(TRUE);
|
||||
$states['published'] = $state->reveal();
|
||||
|
||||
$state = $this->prophesize(ModerationStateInterface::class);
|
||||
$state->id()->willReturn('archived');
|
||||
$state->label()->willReturn('Archived');
|
||||
$state->isPublishedState()->willReturn(TRUE);
|
||||
$state->isDefaultRevisionState()->willReturn(TRUE);
|
||||
$states['archived'] = $state->reveal();
|
||||
|
||||
$entity_storage->loadMultiple()->willReturn($states);
|
||||
|
||||
foreach ($states as $id => $state) {
|
||||
$entity_storage->load($id)->willReturn($state);
|
||||
}
|
||||
|
||||
return $entity_storage->reveal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a mocked Entity Type Manager.
|
||||
*
|
||||
* @return EntityTypeManagerInterface
|
||||
* The mocked Entity Type Manager.
|
||||
*/
|
||||
protected function setupEntityTypeManager(EntityStorageInterface $storage) {
|
||||
$entityTypeManager = $this->prophesize(EntityTypeManagerInterface::class);
|
||||
$entityTypeManager->getStorage('moderation_state')->willReturn($storage);
|
||||
$entityTypeManager->getStorage('moderation_state_transition')->willReturn($this->setupTransitionStorage());
|
||||
|
||||
return $entityTypeManager->reveal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a mocked query factory that does nothing.
|
||||
*
|
||||
* @return QueryFactory
|
||||
* The mocked query factory that does nothing.
|
||||
*/
|
||||
protected function setupQueryFactory() {
|
||||
$factory = $this->prophesize(QueryFactory::class);
|
||||
|
||||
return $factory->reveal();
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::isTransitionAllowed
|
||||
* @covers ::calculatePossibleTransitions
|
||||
*
|
||||
* @dataProvider providerIsTransitionAllowedWithValidTransition
|
||||
*/
|
||||
public function testIsTransitionAllowedWithValidTransition($from_id, $to_id) {
|
||||
$storage = $this->setupStateStorage();
|
||||
$state_transition_validation = new StateTransitionValidation($this->setupEntityTypeManager($storage), $this->setupQueryFactory());
|
||||
$this->assertTrue($state_transition_validation->isTransitionAllowed($storage->load($from_id), $storage->load($to_id)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for self::testIsTransitionAllowedWithValidTransition().
|
||||
*/
|
||||
public function providerIsTransitionAllowedWithValidTransition() {
|
||||
return [
|
||||
['draft', 'draft'],
|
||||
['draft', 'needs_review'],
|
||||
['needs_review', 'needs_review'],
|
||||
['needs_review', 'staging'],
|
||||
['staging', 'published'],
|
||||
['needs_review', 'draft'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::isTransitionAllowed
|
||||
* @covers ::calculatePossibleTransitions
|
||||
*
|
||||
* @dataProvider providerIsTransitionAllowedWithInValidTransition
|
||||
*/
|
||||
public function testIsTransitionAllowedWithInValidTransition($from_id, $to_id) {
|
||||
$storage = $this->setupStateStorage();
|
||||
$state_transition_validation = new StateTransitionValidation($this->setupEntityTypeManager($storage), $this->setupQueryFactory());
|
||||
$this->assertFalse($state_transition_validation->isTransitionAllowed($storage->load($from_id), $storage->load($to_id)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for self::testIsTransitionAllowedWithInValidTransition().
|
||||
*/
|
||||
public function providerIsTransitionAllowedWithInValidTransition() {
|
||||
return [
|
||||
['published', 'needs_review'],
|
||||
['published', 'staging'],
|
||||
['staging', 'needs_review'],
|
||||
['staging', 'staging'],
|
||||
['needs_review', 'published'],
|
||||
['published', 'archived'],
|
||||
['archived', 'published'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies user-aware transition validation.
|
||||
*
|
||||
|
@ -239,7 +30,7 @@ class StateTransitionValidationTest extends \PHPUnit_Framework_TestCase {
|
|||
* @param bool $allowed
|
||||
* Whether or not to grant a user this permission.
|
||||
* @param bool $result
|
||||
* Whether userMayTransition() is expected to return TRUE or FALSE.
|
||||
* Whether getValidTransitions() is expected to have the.
|
||||
*
|
||||
* @dataProvider userTransitionsProvider
|
||||
*/
|
||||
|
@ -250,10 +41,45 @@ class StateTransitionValidationTest extends \PHPUnit_Framework_TestCase {
|
|||
$user->hasPermission($permission)->willReturn($allowed);
|
||||
$user->hasPermission(Argument::type('string'))->willReturn(FALSE);
|
||||
|
||||
$storage = $this->setupStateStorage();
|
||||
$validator = new Validator($this->setupEntityTypeManager($storage), $this->setupQueryFactory());
|
||||
$entity = $this->prophesize(ContentEntityInterface::class);
|
||||
$entity = $entity->reveal();
|
||||
$entity->moderation_state = new \stdClass();
|
||||
$entity->moderation_state->value = $from_id;
|
||||
|
||||
$this->assertEquals($result, $validator->userMayTransition($storage->load($from_id), $storage->load($to_id), $user->reveal()));
|
||||
$validator = new StateTransitionValidation($this->setUpModerationInformation($entity));
|
||||
$has_transition = FALSE;
|
||||
foreach ($validator->getValidTransitions($entity, $user->reveal()) as $transition) {
|
||||
if ($transition->to()->id() === $to_id) {
|
||||
$has_transition = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$this->assertSame($result, $has_transition);
|
||||
}
|
||||
|
||||
protected function setUpModerationInformation(ContentEntityInterface $entity) {
|
||||
// Create a container so that the plugin manager and workflow type can be
|
||||
// mocked.
|
||||
$container = new ContainerBuilder();
|
||||
$workflow_type = $this->prophesize(WorkflowTypeInterface::class);
|
||||
$workflow_type->decorateState(Argument::any())->willReturnArgument(0);
|
||||
$workflow_type->decorateTransition(Argument::any())->willReturnArgument(0);
|
||||
$workflow_manager = $this->prophesize(WorkflowTypeManager::class);
|
||||
$workflow_manager->createInstance('content_moderation', Argument::any())->willReturn($workflow_type->reveal());
|
||||
$container->set('plugin.manager.workflows.type', $workflow_manager->reveal());
|
||||
\Drupal::setContainer($container);
|
||||
|
||||
$workflow = new Workflow(['id' => 'process', 'type' => 'content_moderation'], 'workflow');
|
||||
$workflow
|
||||
->addState('draft', 'draft')
|
||||
->addState('needs_review', 'needs_review')
|
||||
->addState('published', 'published')
|
||||
->addTransition('draft', 'draft', ['draft'], 'draft')
|
||||
->addTransition('review', 'review', ['draft'], 'needs_review')
|
||||
->addTransition('publish', 'publish', ['needs_review', 'published'], 'published');
|
||||
$moderation_info = $this->prophesize(ModerationInformationInterface::class);
|
||||
$moderation_info->getWorkflowForEntity($entity)->willReturn($workflow);
|
||||
return $moderation_info->reveal();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -261,37 +87,15 @@ class StateTransitionValidationTest extends \PHPUnit_Framework_TestCase {
|
|||
*/
|
||||
public function userTransitionsProvider() {
|
||||
// The user has the right permission, so let it through.
|
||||
$ret[] = ['draft', 'draft', 'use draft__draft transition', TRUE, TRUE];
|
||||
$ret[] = ['draft', 'draft', 'use process transition draft', TRUE, TRUE];
|
||||
|
||||
// The user doesn't have the right permission, block it.
|
||||
$ret[] = ['draft', 'draft', 'use draft__draft transition', FALSE, FALSE];
|
||||
$ret[] = ['draft', 'draft', 'use process transition draft', FALSE, FALSE];
|
||||
|
||||
// The user has some other permission that doesn't matter.
|
||||
$ret[] = ['draft', 'draft', 'use draft__needs_review transition', TRUE, FALSE];
|
||||
|
||||
// The user has permission, but the transition isn't allowed anyway.
|
||||
$ret[] = ['published', 'needs_review', 'use published__needs_review transition', TRUE, FALSE];
|
||||
$ret[] = ['draft', 'draft', 'use process transition review', TRUE, FALSE];
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Testable subclass for selected tests.
|
||||
*
|
||||
* EntityQuery is beyond untestable, so we have to subclass and override the
|
||||
* method that uses it.
|
||||
*/
|
||||
class Validator extends StateTransitionValidation {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getTransitionFromStates(ModerationStateInterface $from, ModerationStateInterface $to) {
|
||||
if ($from->id() === 'draft' && $to->id() === 'draft') {
|
||||
return $this->transitionStorage()->loadMultiple(['draft__draft'])[0];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Reference in a new issue