Core and composer updates
This commit is contained in:
parent
a82634bb98
commit
62cac30480
1118 changed files with 21770 additions and 6306 deletions
|
@ -1,96 +0,0 @@
|
|||
<?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,208 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\content_moderation\Functional;
|
||||
|
||||
use Drupal\workflows\Entity\Workflow;
|
||||
|
||||
/**
|
||||
* Tests the moderation form, specifically on nodes.
|
||||
*
|
||||
* @group content_moderation
|
||||
*/
|
||||
class ModerationFormTest 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');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the moderation form that shows on the latest version page.
|
||||
*
|
||||
* The latest version page only shows if there is a forward revision. There
|
||||
* is only a forward revision if a draft revision is created on a node where
|
||||
* the default revision is not a published moderation state.
|
||||
*
|
||||
* @see \Drupal\content_moderation\EntityOperations
|
||||
* @see \Drupal\Tests\content_moderation\Functional\ModerationStateBlockTest::testCustomBlockModeration
|
||||
*/
|
||||
public function testModerationForm() {
|
||||
// Create new moderated content in draft.
|
||||
$this->drupalPostForm('node/add/moderated_content', [
|
||||
'title[0][value]' => 'Some moderated content',
|
||||
'body[0][value]' => 'First version of the content.',
|
||||
], t('Save and Create New Draft'));
|
||||
|
||||
$node = $this->drupalGetNodeByTitle('Some moderated content');
|
||||
$canonical_path = sprintf('node/%d', $node->id());
|
||||
$edit_path = sprintf('node/%d/edit', $node->id());
|
||||
$latest_version_path = sprintf('node/%d/latest', $node->id());
|
||||
|
||||
$this->assertTrue($this->adminUser->hasPermission('edit any moderated_content content'));
|
||||
|
||||
// The canonical view should have a moderation form, because it is not the
|
||||
// live revision.
|
||||
$this->drupalGet($canonical_path);
|
||||
$this->assertResponse(200);
|
||||
$this->assertField('edit-new-state', 'The node view page has a moderation form.');
|
||||
|
||||
// The latest version page should not show, because there is no forward
|
||||
// revision.
|
||||
$this->drupalGet($latest_version_path);
|
||||
$this->assertResponse(403);
|
||||
|
||||
// Update the draft.
|
||||
$this->drupalPostForm($edit_path, [
|
||||
'body[0][value]' => 'Second version of the content.',
|
||||
], t('Save and Create New Draft'));
|
||||
|
||||
// The canonical view should have a moderation form, because it is not the
|
||||
// live revision.
|
||||
$this->drupalGet($canonical_path);
|
||||
$this->assertResponse(200);
|
||||
$this->assertField('edit-new-state', 'The node view page has a moderation form.');
|
||||
|
||||
// The latest version page should not show, because there is still no
|
||||
// forward revision.
|
||||
$this->drupalGet($latest_version_path);
|
||||
$this->assertResponse(403);
|
||||
|
||||
// Publish the draft.
|
||||
$this->drupalPostForm($edit_path, [
|
||||
'body[0][value]' => 'Third version of the content.',
|
||||
], t('Save and Publish'));
|
||||
|
||||
// The published view should not have a moderation form, because it is the
|
||||
// live revision.
|
||||
$this->drupalGet($canonical_path);
|
||||
$this->assertResponse(200);
|
||||
$this->assertNoField('edit-new-state', 'The node view page has no moderation form.');
|
||||
|
||||
// The latest version page should not show, because there is still no
|
||||
// forward revision.
|
||||
$this->drupalGet($latest_version_path);
|
||||
$this->assertResponse(403);
|
||||
|
||||
// Make a forward revision.
|
||||
$this->drupalPostForm($edit_path, [
|
||||
'body[0][value]' => 'Fourth version of the content.',
|
||||
], t('Save and Create New Draft'));
|
||||
|
||||
// The published view should not have a moderation form, because it is the
|
||||
// live revision.
|
||||
$this->drupalGet($canonical_path);
|
||||
$this->assertResponse(200);
|
||||
$this->assertNoField('edit-new-state', 'The node view page has no moderation form.');
|
||||
|
||||
// The latest version page should show the moderation form and have "Draft"
|
||||
// status, because the forward revision is in "Draft".
|
||||
$this->drupalGet($latest_version_path);
|
||||
$this->assertResponse(200);
|
||||
$this->assertField('edit-new-state', 'The latest-version page has a moderation form.');
|
||||
$this->assertText('Draft', 'Correct status found on the latest-version page.');
|
||||
|
||||
// Submit the moderation form to change status to published.
|
||||
$this->drupalPostForm($latest_version_path, [
|
||||
'new_state' => 'published',
|
||||
], t('Apply'));
|
||||
|
||||
// The latest version page should not show, because there is no
|
||||
// forward revision.
|
||||
$this->drupalGet($latest_version_path);
|
||||
$this->assertResponse(403);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test moderation non-bundle entity type.
|
||||
*/
|
||||
public function testNonBundleModerationForm() {
|
||||
$this->drupalLogin($this->rootUser);
|
||||
$workflow = Workflow::load('editorial');
|
||||
$workflow->getTypePlugin()->addEntityTypeAndBundle('entity_test_mulrevpub', 'entity_test_mulrevpub');
|
||||
$workflow->save();
|
||||
|
||||
// Create new moderated content in draft.
|
||||
$this->drupalPostForm('entity_test_mulrevpub/add', [], t('Save and Create New Draft'));
|
||||
|
||||
// The latest version page should not show, because there is no forward
|
||||
// revision.
|
||||
$this->drupalGet('/entity_test_mulrevpub/manage/1/latest');
|
||||
$this->assertResponse(403);
|
||||
|
||||
// Update the draft.
|
||||
$this->drupalPostForm('entity_test_mulrevpub/manage/1/edit', [], t('Save and Create New Draft'));
|
||||
|
||||
// The latest version page should not show, because there is still no
|
||||
// forward revision.
|
||||
$this->drupalGet('/entity_test_mulrevpub/manage/1/latest');
|
||||
$this->assertResponse(403);
|
||||
|
||||
// Publish the draft.
|
||||
$this->drupalPostForm('entity_test_mulrevpub/manage/1/edit', [], t('Save and Publish'));
|
||||
|
||||
// The published view should not have a moderation form, because it is the
|
||||
// default revision.
|
||||
$this->drupalGet('entity_test_mulrevpub/manage/1');
|
||||
$this->assertResponse(200);
|
||||
$this->assertNoText('Status', 'The node view page has no moderation form.');
|
||||
|
||||
// The latest version page should not show, because there is still no
|
||||
// forward revision.
|
||||
$this->drupalGet('entity_test_mulrevpub/manage/1/latest');
|
||||
$this->assertResponse(403);
|
||||
|
||||
// Make a forward revision.
|
||||
$this->drupalPostForm('entity_test_mulrevpub/manage/1/edit', [], t('Save and Create New Draft'));
|
||||
|
||||
// The published view should not have a moderation form, because it is the
|
||||
// default revision.
|
||||
$this->drupalGet('entity_test_mulrevpub/manage/1');
|
||||
$this->assertResponse(200);
|
||||
$this->assertNoText('Status', 'The node view page has no moderation form.');
|
||||
|
||||
// The latest version page should show the moderation form and have "Draft"
|
||||
// status, because the forward revision is in "Draft".
|
||||
$this->drupalGet('entity_test_mulrevpub/manage/1/latest');
|
||||
$this->assertResponse(200);
|
||||
$this->assertText('Status', 'Form text found on the latest-version page.');
|
||||
$this->assertText('Draft', 'Correct status found on the latest-version page.');
|
||||
|
||||
// Submit the moderation form to change status to published.
|
||||
$this->drupalPostForm('entity_test_mulrevpub/manage/1/latest', [
|
||||
'new_state' => 'published',
|
||||
], t('Apply'));
|
||||
|
||||
// The latest version page should not show, because there is no
|
||||
// forward revision.
|
||||
$this->drupalGet('entity_test_mulrevpub/manage/1/latest');
|
||||
$this->assertResponse(403);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the revision author is updated when the moderation form is used.
|
||||
*/
|
||||
public function testModerationFormSetsRevisionAuthor() {
|
||||
// Create new moderated content in published.
|
||||
$node = $this->createNode(['type' => 'moderated_content', 'moderation_state' => 'published']);
|
||||
// Make a forward revision.
|
||||
$node->title = $this->randomMachineName();
|
||||
$node->moderation_state->value = 'draft';
|
||||
$node->save();
|
||||
|
||||
$another_user = $this->drupalCreateUser($this->permissions);
|
||||
$this->grantUserPermissionToCreateContentOfType($another_user, 'moderated_content');
|
||||
$this->drupalLogin($another_user);
|
||||
$this->drupalPostForm(sprintf('node/%d/latest', $node->id()), [
|
||||
'new_state' => 'published',
|
||||
], t('Apply'));
|
||||
|
||||
$this->drupalGet(sprintf('node/%d/revisions', $node->id()));
|
||||
$this->assertText('by ' . $another_user->getAccountName());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\content_moderation\Functional;
|
||||
|
||||
use Drupal\simpletest\ContentTypeCreationTrait;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\workflows\Entity\Workflow;
|
||||
|
||||
/**
|
||||
* Test revision revert.
|
||||
*
|
||||
* @group content_moderation
|
||||
*/
|
||||
class ModerationRevisionRevertTest extends BrowserTestBase {
|
||||
|
||||
use ContentTypeCreationTrait;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = [
|
||||
'content_moderation',
|
||||
'node',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$moderated_bundle = $this->createContentType(['type' => 'moderated_bundle']);
|
||||
$moderated_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',
|
||||
'view all revisions',
|
||||
'view content moderation',
|
||||
'use editorial transition create_new_draft',
|
||||
'use editorial transition publish',
|
||||
]);
|
||||
$this->drupalLogin($admin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that reverting a revision works.
|
||||
*/
|
||||
public function testEditingAfterRevertRevision() {
|
||||
// Create a draft.
|
||||
$this->drupalPostForm('node/add/moderated_bundle', ['title[0][value]' => 'First draft node'], t('Save and Create New Draft'));
|
||||
|
||||
// Now make it published.
|
||||
$this->drupalPostForm('node/1/edit', ['title[0][value]' => 'Published node'], t('Save and Publish'));
|
||||
|
||||
// Check the editing form that show the published title.
|
||||
$this->drupalGet('node/1/edit');
|
||||
$this->assertSession()
|
||||
->pageTextContains('Published node');
|
||||
|
||||
// Revert the first revision.
|
||||
$revision_url = 'node/1/revisions/1/revert';
|
||||
$this->drupalGet($revision_url);
|
||||
$this->assertSession()->elementExists('css', '.form-submit');
|
||||
$this->click('.form-submit');
|
||||
|
||||
// Check that it reverted.
|
||||
$this->drupalGet('node/1/edit');
|
||||
$this->assertSession()
|
||||
->pageTextContains('First draft node');
|
||||
// Try to save the node.
|
||||
$this->click('.moderation-state-draft > input');
|
||||
|
||||
// Check if the submission passed the EntityChangedConstraintValidator.
|
||||
$this->assertSession()
|
||||
->pageTextNotContains('The content has either been modified by another user, or you have already submitted modifications. As a result, your changes cannot be saved.');
|
||||
|
||||
// Check the node has been saved.
|
||||
$this->assertSession()
|
||||
->pageTextContains('moderated_bundle First draft node has been updated');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,133 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\content_moderation\Functional;
|
||||
|
||||
use Drupal\block_content\Entity\BlockContent;
|
||||
use Drupal\block_content\Entity\BlockContentType;
|
||||
|
||||
/**
|
||||
* Tests general content moderation workflow for blocks.
|
||||
*
|
||||
* @group content_moderation
|
||||
*/
|
||||
class ModerationStateBlockTest extends ModerationStateTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create the "basic" block type.
|
||||
$bundle = BlockContentType::create([
|
||||
'id' => 'basic',
|
||||
'label' => 'basic',
|
||||
'revision' => FALSE,
|
||||
]);
|
||||
$bundle->save();
|
||||
|
||||
// Add the body field to it.
|
||||
block_content_add_body_field($bundle->id());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests moderating custom blocks.
|
||||
*
|
||||
* Blocks and any non-node-type-entities do not have a concept of
|
||||
* "published". As such, we must use the "default revision" to know what is
|
||||
* going to be "published", i.e. visible to the user.
|
||||
*
|
||||
* The one exception is a block that has never been "published". When a block
|
||||
* is first created, it becomes the "default revision". For each edit of the
|
||||
* block after that, Content Moderation checks the "default revision" to
|
||||
* see if it is set to a published moderation state. If it is not, the entity
|
||||
* being saved will become the "default revision".
|
||||
*
|
||||
* The test below is intended, in part, to make this behavior clear.
|
||||
*
|
||||
* @see \Drupal\content_moderation\EntityOperations::entityPresave
|
||||
* @see \Drupal\content_moderation\Tests\ModerationFormTest::testModerationForm
|
||||
*/
|
||||
public function testCustomBlockModeration() {
|
||||
$this->drupalLogin($this->rootUser);
|
||||
|
||||
$this->drupalGet('admin/structure/block/block-content/types');
|
||||
$this->assertLinkByHref('admin/structure/block/block-content/manage/basic/moderation');
|
||||
$this->drupalGet('admin/structure/block/block-content/manage/basic');
|
||||
$this->assertLinkByHref('admin/structure/block/block-content/manage/basic/moderation');
|
||||
$this->drupalGet('admin/structure/block/block-content/manage/basic/moderation');
|
||||
|
||||
// Enable moderation for custom blocks at
|
||||
// admin/structure/block/block-content/manage/basic/moderation.
|
||||
$edit = ['workflow' => 'editorial'];
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->assertText(t('Your settings have been saved.'));
|
||||
|
||||
// Create a custom block at block/add and save it as draft.
|
||||
$body = 'Body of moderated block';
|
||||
$edit = [
|
||||
'info[0][value]' => 'Moderated block',
|
||||
'body[0][value]' => $body,
|
||||
];
|
||||
$this->drupalPostForm('block/add', $edit, t('Save and Create New Draft'));
|
||||
$this->assertText(t('basic Moderated block has been created.'));
|
||||
|
||||
// Place the block in the Sidebar First region.
|
||||
$instance = [
|
||||
'id' => 'moderated_block',
|
||||
'settings[label]' => $edit['info[0][value]'],
|
||||
'region' => 'sidebar_first',
|
||||
];
|
||||
$block = BlockContent::load(1);
|
||||
$url = 'admin/structure/block/add/block_content:' . $block->uuid() . '/' . $this->config('system.theme')->get('default');
|
||||
$this->drupalPostForm($url, $instance, t('Save block'));
|
||||
|
||||
// Navigate to home page and check that the block is visible. It should be
|
||||
// visible because it is the default revision.
|
||||
$this->drupalGet('');
|
||||
$this->assertText($body);
|
||||
|
||||
// Update the block.
|
||||
$updated_body = 'This is the new body value';
|
||||
$edit = [
|
||||
'body[0][value]' => $updated_body,
|
||||
];
|
||||
$this->drupalPostForm('block/' . $block->id(), $edit, t('Save and Create New Draft'));
|
||||
$this->assertText(t('basic Moderated block has been updated.'));
|
||||
|
||||
// Navigate to the home page and check that the block shows the updated
|
||||
// content. It should show the updated content because the block's default
|
||||
// revision is not a published moderation state.
|
||||
$this->drupalGet('');
|
||||
$this->assertText($updated_body);
|
||||
|
||||
// Publish the block so we can create a forward revision.
|
||||
$this->drupalPostForm('block/' . $block->id(), [], t('Save and Publish'));
|
||||
|
||||
// Create a forward revision.
|
||||
$forward_revision_body = 'This is the forward revision body value';
|
||||
$edit = [
|
||||
'body[0][value]' => $forward_revision_body,
|
||||
];
|
||||
$this->drupalPostForm('block/' . $block->id(), $edit, t('Save and Create New Draft'));
|
||||
$this->assertText(t('basic Moderated block has been updated.'));
|
||||
|
||||
// Navigate to home page and check that the forward revision doesn't show,
|
||||
// since it should not be set as the default revision.
|
||||
$this->drupalGet('');
|
||||
$this->assertText($updated_body);
|
||||
|
||||
// Open the latest tab and publish the new draft.
|
||||
$edit = [
|
||||
'new_state' => 'published',
|
||||
];
|
||||
$this->drupalPostForm('block/' . $block->id() . '/latest', $edit, t('Apply'));
|
||||
$this->assertText(t('The moderation state has been updated.'));
|
||||
|
||||
// Navigate to home page and check that the forward revision is now the
|
||||
// default revision and therefore visible.
|
||||
$this->drupalGet('');
|
||||
$this->assertText($forward_revision_body);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,141 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\content_moderation\Functional;
|
||||
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\node\Entity\Node;
|
||||
|
||||
/**
|
||||
* Tests general content moderation workflow for nodes.
|
||||
*
|
||||
* @group content_moderation
|
||||
*/
|
||||
class ModerationStateNodeTest 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');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests creating and deleting content.
|
||||
*/
|
||||
public function testCreatingContent() {
|
||||
$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.');
|
||||
}
|
||||
$this->assertEqual('draft', $node->moderation_state->value);
|
||||
|
||||
$path = 'node/' . $node->id() . '/edit';
|
||||
// Set up published revision.
|
||||
$this->drupalPostForm($path, [], t('Save and Publish'));
|
||||
\Drupal::entityTypeManager()->getStorage('node')->resetCache([$node->id()]);
|
||||
/* @var \Drupal\node\NodeInterface $node */
|
||||
$node = \Drupal::entityTypeManager()->getStorage('node')->load($node->id());
|
||||
$this->assertTrue($node->isPublished());
|
||||
$this->assertEqual('published', $node->moderation_state->value);
|
||||
|
||||
// Verify that the state field is not shown.
|
||||
$this->assertNoText('Published');
|
||||
|
||||
// Delete the node.
|
||||
$this->drupalPostForm('node/' . $node->id() . '/delete', [], t('Delete'));
|
||||
$this->assertText(t('The Moderated content moderated content has been deleted.'));
|
||||
|
||||
// Disable content moderation.
|
||||
$this->drupalPostForm('admin/structure/types/manage/moderated_content/moderation', ['workflow' => ''], t('Save'));
|
||||
$this->drupalGet('admin/structure/types/manage/moderated_content/moderation');
|
||||
$this->assertOptionSelected('edit-workflow', '');
|
||||
// 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();
|
||||
|
||||
// Create a new node.
|
||||
$this->drupalPostForm('node/add/moderated_content', [
|
||||
'title[0][value]' => 'non-moderated content',
|
||||
], t('Save and publish'));
|
||||
|
||||
$node = $this->getNodeByTitle('non-moderated content');
|
||||
if (!$node) {
|
||||
$this->fail('Non-moderated test node was not saved correctly.');
|
||||
}
|
||||
$this->assertEqual(NULL, $node->moderation_state->value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests edit form destinations.
|
||||
*/
|
||||
public function testFormSaveDestination() {
|
||||
// Create new moderated content in draft.
|
||||
$this->drupalPostForm('node/add/moderated_content', [
|
||||
'title[0][value]' => 'Some moderated content',
|
||||
'body[0][value]' => 'First version of the content.',
|
||||
], t('Save and Create New Draft'));
|
||||
|
||||
$node = $this->drupalGetNodeByTitle('Some moderated content');
|
||||
$edit_path = sprintf('node/%d/edit', $node->id());
|
||||
|
||||
// After saving, we should be at the canonical URL and viewing the first
|
||||
// revision.
|
||||
$this->assertUrl(Url::fromRoute('entity.node.canonical', ['node' => $node->id()]));
|
||||
$this->assertText('First version of the content.');
|
||||
|
||||
// Create a new draft; after saving, we should still be on the canonical
|
||||
// URL, but viewing the second revision.
|
||||
$this->drupalPostForm($edit_path, [
|
||||
'body[0][value]' => 'Second version of the content.',
|
||||
], t('Save and Create New Draft'));
|
||||
$this->assertUrl(Url::fromRoute('entity.node.canonical', ['node' => $node->id()]));
|
||||
$this->assertText('Second version of the content.');
|
||||
|
||||
// Make a new published revision; after saving, we should be at the
|
||||
// canonical URL.
|
||||
$this->drupalPostForm($edit_path, [
|
||||
'body[0][value]' => 'Third version of the content.',
|
||||
], t('Save and Publish'));
|
||||
$this->assertUrl(Url::fromRoute('entity.node.canonical', ['node' => $node->id()]));
|
||||
$this->assertText('Third version of the content.');
|
||||
|
||||
// Make a new forward revision; after saving, we should be on the "Latest
|
||||
// version" tab.
|
||||
$this->drupalPostForm($edit_path, [
|
||||
'body[0][value]' => 'Fourth version of the content.',
|
||||
], t('Save and Create New Draft'));
|
||||
$this->assertUrl(Url::fromRoute('entity.node.latest_version', ['node' => $node->id()]));
|
||||
$this->assertText('Fourth version of the content.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests pagers aren't broken by content_moderation.
|
||||
*/
|
||||
public function testPagers() {
|
||||
// Create 51 nodes to force the pager.
|
||||
foreach (range(1, 51) as $delta) {
|
||||
Node::create([
|
||||
'type' => 'moderated_content',
|
||||
'uid' => $this->adminUser->id(),
|
||||
'title' => 'Node ' . $delta,
|
||||
'status' => 1,
|
||||
'moderation_state' => 'published',
|
||||
])->save();
|
||||
}
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$this->drupalGet('admin/content');
|
||||
$element = $this->cssSelect('nav.pager li.is-active a');
|
||||
$url = $element[0]->getAttribute('href');
|
||||
$query = [];
|
||||
parse_str(parse_url($url, PHP_URL_QUERY), $query);
|
||||
$this->assertEqual(0, $query['page']);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\content_moderation\Functional;
|
||||
|
||||
/**
|
||||
* Tests moderation state node type integration.
|
||||
*
|
||||
* @group content_moderation
|
||||
*/
|
||||
class ModerationStateNodeTypeTest extends ModerationStateTestBase {
|
||||
|
||||
/**
|
||||
* A node type without moderation state disabled.
|
||||
*/
|
||||
public function testNotModerated() {
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$this->createContentTypeFromUi('Not moderated', 'not_moderated');
|
||||
$this->assertText('The content type Not moderated has been added.');
|
||||
$this->grantUserPermissionToCreateContentOfType($this->adminUser, 'not_moderated');
|
||||
$this->drupalGet('node/add/not_moderated');
|
||||
$this->assertRaw('Save as unpublished');
|
||||
$this->drupalPostForm(NULL, [
|
||||
'title[0][value]' => 'Test',
|
||||
], t('Save and publish'));
|
||||
$this->assertText('Not moderated Test has been created.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests enabling moderation on an existing node-type, with content.
|
||||
*/
|
||||
public function testEnablingOnExistingContent() {
|
||||
$editor_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',
|
||||
];
|
||||
$publish_permissions = array_merge($editor_permissions, ['use editorial transition publish']);
|
||||
$editor = $this->drupalCreateUser($editor_permissions);
|
||||
$editor_with_publish = $this->drupalCreateUser($publish_permissions);
|
||||
|
||||
// Create a node type that is not moderated.
|
||||
$this->drupalLogin($editor);
|
||||
$this->createContentTypeFromUi('Not moderated', 'not_moderated');
|
||||
$this->grantUserPermissionToCreateContentOfType($editor, 'not_moderated');
|
||||
$this->grantUserPermissionToCreateContentOfType($editor_with_publish, 'not_moderated');
|
||||
|
||||
// Create content.
|
||||
$this->drupalGet('node/add/not_moderated');
|
||||
$this->drupalPostForm(NULL, [
|
||||
'title[0][value]' => 'Test',
|
||||
], t('Save and publish'));
|
||||
$this->assertText('Not moderated Test has been created.');
|
||||
|
||||
// Now enable moderation state, ensuring all the expected links and tabs are
|
||||
// present.
|
||||
$this->drupalGet('admin/structure/types');
|
||||
$this->assertLinkByHref('admin/structure/types/manage/not_moderated/moderation');
|
||||
$this->drupalGet('admin/structure/types/manage/not_moderated');
|
||||
$this->assertLinkByHref('admin/structure/types/manage/not_moderated/moderation');
|
||||
$this->drupalGet('admin/structure/types/manage/not_moderated/moderation');
|
||||
$this->assertOptionSelected('edit-workflow', '');
|
||||
$this->assertNoLink('Delete');
|
||||
$edit['workflow'] = 'editorial';
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
|
||||
// And make sure it works.
|
||||
$nodes = \Drupal::entityTypeManager()->getStorage('node')
|
||||
->loadByProperties(['title' => 'Test']);
|
||||
if (empty($nodes)) {
|
||||
$this->fail('Could not load node with title Test');
|
||||
return;
|
||||
}
|
||||
$node = reset($nodes);
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
$this->assertResponse(200);
|
||||
$this->assertLinkByHref('node/' . $node->id() . '/edit');
|
||||
$this->drupalGet('node/' . $node->id() . '/edit');
|
||||
$this->assertResponse(200);
|
||||
$this->assertRaw('Save and Create New Draft');
|
||||
$this->assertNoRaw('Save and Publish');
|
||||
|
||||
$this->drupalLogin($editor_with_publish);
|
||||
$this->drupalGet('node/' . $node->id() . '/edit');
|
||||
$this->assertResponse(200);
|
||||
$this->assertRaw('Save and Create New Draft');
|
||||
$this->assertRaw('Save and Publish');
|
||||
}
|
||||
|
||||
}
|
|
@ -50,6 +50,7 @@ abstract class ModerationStateTestBase extends BrowserTestBase {
|
|||
'block',
|
||||
'block_content',
|
||||
'node',
|
||||
'entity_test',
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
|
@ -9,6 +9,37 @@ namespace Drupal\Tests\content_moderation\Functional;
|
|||
*/
|
||||
class NodeAccessTest extends ModerationStateTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = [
|
||||
'content_moderation',
|
||||
'block',
|
||||
'block_content',
|
||||
'node',
|
||||
'node_access_test_empty',
|
||||
];
|
||||
|
||||
/**
|
||||
* 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',
|
||||
'bypass node access',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -17,6 +48,10 @@ class NodeAccessTest extends ModerationStateTestBase {
|
|||
$this->drupalLogin($this->adminUser);
|
||||
$this->createContentTypeFromUi('Moderated content', 'moderated_content', TRUE);
|
||||
$this->grantUserPermissionToCreateContentOfType($this->adminUser, 'moderated_content');
|
||||
|
||||
// Rebuild permissions because hook_node_grants() is implemented by the
|
||||
// node_access_test_empty module.
|
||||
node_access_rebuild();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -38,7 +73,24 @@ class NodeAccessTest extends ModerationStateTestBase {
|
|||
$edit_path = 'node/' . $node->id() . '/edit';
|
||||
$latest_path = 'node/' . $node->id() . '/latest';
|
||||
|
||||
// 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(403);
|
||||
$this->drupalGet($view_path);
|
||||
$this->assertResponse(200);
|
||||
|
||||
// Publish the node.
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$this->drupalPostForm($edit_path, [], t('Save and Publish'));
|
||||
|
||||
// Ensure access works correctly for anonymous users.
|
||||
|
@ -58,12 +110,6 @@ class NodeAccessTest extends ModerationStateTestBase {
|
|||
'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);
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\content_moderation\Kernel;
|
||||
|
||||
use Drupal\content_moderation\Entity\ContentModerationState;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\content_moderation\ContentModerationStateAccessControlHandler
|
||||
* @group content_moderation
|
||||
*/
|
||||
class ContentModerationStateAccessControlHandlerTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = [
|
||||
'content_moderation',
|
||||
'workflows',
|
||||
'user',
|
||||
];
|
||||
|
||||
/**
|
||||
* The content_moderation_state access control handler.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityAccessControlHandlerInterface
|
||||
*/
|
||||
protected $accessControlHandler;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installEntitySchema('content_moderation_state');
|
||||
$this->installEntitySchema('user');
|
||||
$this->accessControlHandler = $this->container->get('entity_type.manager')->getAccessControlHandler('content_moderation_state');
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::checkAccess
|
||||
* @covers ::checkCreateAccess
|
||||
*/
|
||||
public function testHandler() {
|
||||
$entity = ContentModerationState::create([]);
|
||||
$this->assertFalse($this->accessControlHandler->access($entity, 'view'));
|
||||
$this->assertFalse($this->accessControlHandler->access($entity, 'update'));
|
||||
$this->assertFalse($this->accessControlHandler->access($entity, 'delete'));
|
||||
$this->assertFalse($this->accessControlHandler->createAccess());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,142 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\content_moderation\Kernel;
|
||||
|
||||
use Drupal\content_moderation\Entity\ContentModerationState;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
use Drupal\workflows\Entity\Workflow;
|
||||
|
||||
/**
|
||||
* Test the ContentModerationState storage schema.
|
||||
*
|
||||
* @coversDefaultClass \Drupal\content_moderation\ContentModerationStateStorageSchema
|
||||
* @group content_moderation
|
||||
*/
|
||||
class ContentModerationStateStorageSchemaTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'node',
|
||||
'content_moderation',
|
||||
'user',
|
||||
'system',
|
||||
'text',
|
||||
'workflows',
|
||||
'entity_test',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installSchema('node', 'node_access');
|
||||
$this->installEntitySchema('node');
|
||||
$this->installEntitySchema('entity_test');
|
||||
$this->installEntitySchema('user');
|
||||
$this->installEntitySchema('content_moderation_state');
|
||||
$this->installConfig('content_moderation');
|
||||
|
||||
NodeType::create([
|
||||
'type' => 'example',
|
||||
])->save();
|
||||
$workflow = Workflow::load('editorial');
|
||||
$workflow->getTypePlugin()->addEntityTypeAndBundle('node', 'example');
|
||||
$workflow->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the ContentModerationState unique keys.
|
||||
*
|
||||
* @covers ::getEntitySchema
|
||||
*/
|
||||
public function testUniqueKeys() {
|
||||
// Create a node which will create a new ContentModerationState entity.
|
||||
$node = Node::create([
|
||||
'title' => 'Test title',
|
||||
'type' => 'example',
|
||||
'moderation_state' => 'draft',
|
||||
]);
|
||||
$node->save();
|
||||
|
||||
// Ensure an exception when all values match.
|
||||
$this->assertStorageException([
|
||||
'content_entity_type_id' => $node->getEntityTypeId(),
|
||||
'content_entity_id' => $node->id(),
|
||||
'content_entity_revision_id' => $node->getRevisionId(),
|
||||
], TRUE);
|
||||
|
||||
// No exception for the same values, with a different langcode.
|
||||
$this->assertStorageException([
|
||||
'content_entity_type_id' => $node->getEntityTypeId(),
|
||||
'content_entity_id' => $node->id(),
|
||||
'content_entity_revision_id' => $node->getRevisionId(),
|
||||
'langcode' => 'de',
|
||||
], FALSE);
|
||||
|
||||
// A different workflow should not trigger an exception.
|
||||
$this->assertStorageException([
|
||||
'content_entity_type_id' => $node->getEntityTypeId(),
|
||||
'content_entity_id' => $node->id(),
|
||||
'content_entity_revision_id' => $node->getRevisionId(),
|
||||
'workflow' => 'foo',
|
||||
], FALSE);
|
||||
|
||||
// Different entity types should not trigger an exception.
|
||||
$this->assertStorageException([
|
||||
'content_entity_type_id' => 'entity_test',
|
||||
'content_entity_id' => $node->id(),
|
||||
'content_entity_revision_id' => $node->getRevisionId(),
|
||||
], FALSE);
|
||||
|
||||
// Different entity and revision IDs should not trigger an exception.
|
||||
$this->assertStorageException([
|
||||
'content_entity_type_id' => $node->getEntityTypeId(),
|
||||
'content_entity_id' => 9999,
|
||||
'content_entity_revision_id' => 9999,
|
||||
], FALSE);
|
||||
|
||||
// Creating a version of the entity with a previously used, but not current
|
||||
// revision ID should trigger an exception.
|
||||
$old_revision_id = $node->getRevisionId();
|
||||
$node->setNewRevision(TRUE);
|
||||
$node->title = 'Updated title';
|
||||
$node->moderation_state = 'published';
|
||||
$node->save();
|
||||
$this->assertStorageException([
|
||||
'content_entity_type_id' => $node->getEntityTypeId(),
|
||||
'content_entity_id' => $node->id(),
|
||||
'content_entity_revision_id' => $old_revision_id,
|
||||
], TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert if a storage exception is triggered when saving a given entity.
|
||||
*
|
||||
* @param array $values
|
||||
* An array of entity values.
|
||||
* @param bool $has_exception
|
||||
* If an exception should be triggered when saving the entity.
|
||||
*/
|
||||
protected function assertStorageException(array $values, $has_exception) {
|
||||
$defaults = [
|
||||
'moderation_state' => 'draft',
|
||||
'workflow' => 'editorial',
|
||||
];
|
||||
$entity = ContentModerationState::create($values + $defaults);
|
||||
$exception_triggered = FALSE;
|
||||
try {
|
||||
ContentModerationState::updateOrCreateFromEntity($entity);
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$exception_triggered = TRUE;
|
||||
}
|
||||
$this->assertEquals($has_exception, $exception_triggered);
|
||||
}
|
||||
|
||||
}
|
|
@ -53,6 +53,7 @@ class ContentModerationStateTest extends KernelTestBase {
|
|||
$this->installEntitySchema('user');
|
||||
$this->installEntitySchema('entity_test_with_bundle');
|
||||
$this->installEntitySchema('entity_test_rev');
|
||||
$this->installEntitySchema('entity_test_no_bundle');
|
||||
$this->installEntitySchema('entity_test_mulrevpub');
|
||||
$this->installEntitySchema('block_content');
|
||||
$this->installEntitySchema('content_moderation_state');
|
||||
|
@ -94,6 +95,9 @@ class ContentModerationStateTest extends KernelTestBase {
|
|||
'title' => 'Test title',
|
||||
$this->entityTypeManager->getDefinition($entity_type_id)->getKey('bundle') => $bundle_id,
|
||||
]);
|
||||
if ($entity instanceof EntityPublishedInterface) {
|
||||
$entity->setUnpublished();
|
||||
}
|
||||
$entity->save();
|
||||
$entity = $this->reloadEntity($entity);
|
||||
$this->assertEquals('draft', $entity->moderation_state->value);
|
||||
|
@ -178,7 +182,10 @@ class ContentModerationStateTest extends KernelTestBase {
|
|||
],
|
||||
'Entity Test with revisions' => [
|
||||
'entity_test_rev',
|
||||
]
|
||||
],
|
||||
'Entity without bundle' => [
|
||||
'entity_test_no_bundle',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -400,6 +407,7 @@ class ContentModerationStateTest extends KernelTestBase {
|
|||
// 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->getTypePlugin()->addEntityTypeAndBundle('entity_test_no_bundle', 'entity_test_no_bundle');
|
||||
$workflow->save();
|
||||
|
||||
$this->assertEquals([
|
||||
|
@ -412,9 +420,11 @@ class ContentModerationStateTest extends KernelTestBase {
|
|||
],
|
||||
], $workflow->getDependencies());
|
||||
|
||||
$entity_types = $workflow->getTypePlugin()->getEntityTypes();
|
||||
$this->assertTrue(in_array('node', $entity_types));
|
||||
$this->assertTrue(in_array('entity_test_rev', $entity_types));
|
||||
$this->assertEquals([
|
||||
'entity_test_no_bundle',
|
||||
'entity_test_rev',
|
||||
'node'
|
||||
], $workflow->getTypePlugin()->getEntityTypes());
|
||||
|
||||
// Delete the node type and ensure it is removed from the workflow.
|
||||
$node_type->delete();
|
||||
|
@ -426,7 +436,7 @@ class ContentModerationStateTest extends KernelTestBase {
|
|||
$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));
|
||||
$this->assertEquals([], $entity_types);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\content_moderation\Kernel;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
use Drupal\workflows\Entity\Workflow;
|
||||
|
||||
/**
|
||||
* Tests the correct default revision is set.
|
||||
*
|
||||
* @group content_moderation
|
||||
*/
|
||||
class DefaultRevisionStateTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
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}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installSchema('node', 'node_access');
|
||||
$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 a translatable Node.
|
||||
*/
|
||||
public function testMultilingual() {
|
||||
// Enable French.
|
||||
ConfigurableLanguage::createFromLangcode('fr')->save();
|
||||
$node_type = NodeType::create([
|
||||
'type' => 'example',
|
||||
]);
|
||||
$node_type->save();
|
||||
|
||||
$this->container->get('content_translation.manager')->setEnabled('node', 'example', TRUE);
|
||||
|
||||
$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
|
||||
->setUnpublished()
|
||||
->save();
|
||||
$this->assertEquals('draft', $english_node->moderation_state->value);
|
||||
$this->assertFalse($english_node->isPublished());
|
||||
$this->assertTrue($english_node->isDefaultRevision());
|
||||
|
||||
// Revision 2 (fr)
|
||||
$french_node = $english_node->addTranslation('fr', ['title' => 'French title']);
|
||||
$french_node->moderation_state->value = 'published';
|
||||
$french_node->save();
|
||||
$this->assertTrue($french_node->isPublished());
|
||||
$this->assertTrue($french_node->isDefaultRevision());
|
||||
|
||||
// Revision 3 (fr)
|
||||
$node = Node::load($english_node->id())->getTranslation('fr');
|
||||
$node->moderation_state->value = 'draft';
|
||||
$node->save();
|
||||
$this->assertFalse($node->isPublished());
|
||||
$this->assertFalse($node->isDefaultRevision());
|
||||
|
||||
// Revision 4 (en)
|
||||
$latest_revision = $this->entityTypeManager->getStorage('node')->loadRevision(3);
|
||||
$latest_revision->moderation_state->value = 'draft';
|
||||
$latest_revision->save();
|
||||
$this->assertFalse($latest_revision->isPublished());
|
||||
$this->assertFalse($latest_revision->isDefaultRevision());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\content_moderation\Kernel;
|
||||
|
||||
use Drupal\entity_test\Entity\EntityTestRev;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
use Drupal\workflows\Entity\Workflow;
|
||||
|
||||
/**
|
||||
* Tests the correct initial states are set on install.
|
||||
*
|
||||
* @group content_moderation
|
||||
*/
|
||||
class InitialStateTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'entity_test',
|
||||
'node',
|
||||
'user',
|
||||
'system',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installSchema('node', 'node_access');
|
||||
$this->installEntitySchema('node');
|
||||
$this->installEntitySchema('user');
|
||||
$this->installEntitySchema('entity_test_rev');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the correct initial state.
|
||||
*/
|
||||
public function testInitialState() {
|
||||
$node_type = NodeType::create([
|
||||
'type' => 'example',
|
||||
]);
|
||||
$node_type->save();
|
||||
|
||||
// Test with an entity type that implements EntityPublishedInterface.
|
||||
$unpublished_node = Node::create([
|
||||
'type' => 'example',
|
||||
'title' => 'Unpublished node',
|
||||
'status' => 0,
|
||||
]);
|
||||
$unpublished_node->save();
|
||||
|
||||
$published_node = Node::create([
|
||||
'type' => 'example',
|
||||
'title' => 'Published node',
|
||||
'status' => 1,
|
||||
]);
|
||||
$published_node->save();
|
||||
|
||||
// Test with an entity type that doesn't implement EntityPublishedInterface.
|
||||
$entity_test = EntityTestRev::create();
|
||||
$entity_test->save();
|
||||
|
||||
\Drupal::service('module_installer')->install(['content_moderation'], TRUE);
|
||||
$workflow = Workflow::load('editorial');
|
||||
$workflow->getTypePlugin()->addEntityTypeAndBundle('node', 'example');
|
||||
$workflow->getTypePlugin()->addEntityTypeAndBundle('entity_test_rev', 'entity_test_rev');
|
||||
$workflow->save();
|
||||
|
||||
$loaded_unpublished_node = Node::load($unpublished_node->id());
|
||||
$loaded_published_node = Node::load($published_node->id());
|
||||
$loaded_entity_test = EntityTestRev::load($entity_test->id());
|
||||
$this->assertEquals('draft', $loaded_unpublished_node->moderation_state->value);
|
||||
$this->assertEquals('published', $loaded_published_node->moderation_state->value);
|
||||
$this->assertEquals('draft', $loaded_entity_test->moderation_state->value);
|
||||
}
|
||||
|
||||
}
|
|
@ -64,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]->value);
|
||||
$this->assertEquals('published', $this->testNode->moderation_state[0]->value);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -75,7 +75,7 @@ class ModerationStateFieldItemListTest extends KernelTestBase {
|
|||
foreach ($this->testNode->moderation_state as $item) {
|
||||
$states[] = $item->value;
|
||||
}
|
||||
$this->assertEquals(['draft'], $states);
|
||||
$this->assertEquals(['published'], $states);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -5,10 +5,16 @@ namespace Drupal\Tests\content_moderation\Unit;
|
|||
use Drupal\block_content\Entity\BlockContent;
|
||||
use Drupal\Core\Access\AccessResultAllowed;
|
||||
use Drupal\Core\Access\AccessResultForbidden;
|
||||
use Drupal\Core\Access\AccessResultNeutral;
|
||||
use Drupal\Core\Cache\Context\CacheContextsManager;
|
||||
use Drupal\Core\Routing\RouteMatch;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\content_moderation\Access\LatestRevisionCheck;
|
||||
use Drupal\content_moderation\ModerationInformation;
|
||||
use Drupal\user\EntityOwnerInterface;
|
||||
use Prophecy\Argument;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\Routing\Route;
|
||||
|
||||
/**
|
||||
|
@ -17,6 +23,20 @@ use Symfony\Component\Routing\Route;
|
|||
*/
|
||||
class LatestRevisionCheckTest extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Initialize Drupal container since the cache context manager is needed.
|
||||
$contexts_manager = $this->prophesize(CacheContextsManager::class);
|
||||
$contexts_manager->assertValidTokens(Argument::any())->willReturn(TRUE);
|
||||
$builder = new ContainerBuilder();
|
||||
$builder->set('cache_contexts_manager', $contexts_manager->reveal());
|
||||
\Drupal::setContainer($builder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the access check of the LatestRevisionCheck service.
|
||||
*
|
||||
|
@ -26,19 +46,38 @@ class LatestRevisionCheckTest extends \PHPUnit_Framework_TestCase {
|
|||
* The machine name of the entity to mock.
|
||||
* @param bool $has_forward
|
||||
* Whether this entity should have a forward revision in the system.
|
||||
* @param array $account_permissions
|
||||
* An array of permissions the account has.
|
||||
* @param bool $is_owner
|
||||
* Indicates if the user should be the owner of the entity.
|
||||
* @param string $result_class
|
||||
* The AccessResult class that should result. One of AccessResultAllowed,
|
||||
* AccessResultForbidden, AccessResultNeutral.
|
||||
*
|
||||
* @dataProvider accessSituationProvider
|
||||
*/
|
||||
public function testLatestAccessPermissions($entity_class, $entity_type, $has_forward, $result_class) {
|
||||
public function testLatestAccessPermissions($entity_class, $entity_type, $has_forward, array $account_permissions, $is_owner, $result_class) {
|
||||
|
||||
/** @var \Drupal\Core\Session\AccountInterface $account */
|
||||
$account = $this->prophesize(AccountInterface::class);
|
||||
$possible_permissions = [
|
||||
'view latest version',
|
||||
'view any unpublished content',
|
||||
'view own unpublished content',
|
||||
];
|
||||
foreach ($possible_permissions as $permission) {
|
||||
$account->hasPermission($permission)->willReturn(in_array($permission, $account_permissions));
|
||||
}
|
||||
$account->id()->willReturn(42);
|
||||
|
||||
/** @var \Drupal\Core\Entity\EntityInterface $entity */
|
||||
$entity = $this->prophesize($entity_class);
|
||||
$entity->getCacheContexts()->willReturn([]);
|
||||
$entity->getCacheTags()->willReturn([]);
|
||||
$entity->getCacheMaxAge()->willReturn(0);
|
||||
if (is_subclass_of($entity_class, EntityOwnerInterface::class)) {
|
||||
$entity->getOwnerId()->willReturn($is_owner ? 42 : 3);
|
||||
}
|
||||
|
||||
/** @var \Drupal\content_moderation\ModerationInformation $mod_info */
|
||||
$mod_info = $this->prophesize(ModerationInformation::class);
|
||||
|
@ -54,7 +93,7 @@ class LatestRevisionCheckTest extends \PHPUnit_Framework_TestCase {
|
|||
$lrc = new LatestRevisionCheck($mod_info->reveal());
|
||||
|
||||
/** @var \Drupal\Core\Access\AccessResult $result */
|
||||
$result = $lrc->access($route->reveal(), $route_match->reveal());
|
||||
$result = $lrc->access($route->reveal(), $route_match->reveal(), $account->reveal());
|
||||
|
||||
$this->assertInstanceOf($result_class, $result);
|
||||
|
||||
|
@ -65,10 +104,28 @@ class LatestRevisionCheckTest extends \PHPUnit_Framework_TestCase {
|
|||
*/
|
||||
public function accessSituationProvider() {
|
||||
return [
|
||||
[Node::class, 'node', TRUE, AccessResultAllowed::class],
|
||||
[Node::class, 'node', FALSE, AccessResultForbidden::class],
|
||||
[BlockContent::class, 'block_content', TRUE, AccessResultAllowed::class],
|
||||
[BlockContent::class, 'block_content', FALSE, AccessResultForbidden::class],
|
||||
// Node with global permissions and latest version.
|
||||
[Node::class, 'node', TRUE, ['view latest version', 'view any unpublished content'], FALSE, AccessResultAllowed::class],
|
||||
// Node with global permissions and no latest version.
|
||||
[Node::class, 'node', FALSE, ['view latest version', 'view any unpublished content'], FALSE, AccessResultForbidden::class],
|
||||
// Node with own content permissions and latest version.
|
||||
[Node::class, 'node', TRUE, ['view latest version', 'view own unpublished content'], TRUE, AccessResultAllowed::class],
|
||||
// Node with own content permissions and no latest version.
|
||||
[Node::class, 'node', FALSE, ['view latest version', 'view own unpublished content'], FALSE, AccessResultForbidden::class],
|
||||
// Node with own content permissions and latest version, but no perms to
|
||||
// view latest version.
|
||||
[Node::class, 'node', TRUE, ['view own unpublished content'], TRUE, AccessResultNeutral::class],
|
||||
// Node with own content permissions and no latest version, but no perms
|
||||
// to view latest version.
|
||||
[Node::class, 'node', TRUE, ['view own unpublished content'], FALSE, AccessResultNeutral::class],
|
||||
// Block with forward revision, and permissions to view any.
|
||||
[BlockContent::class, 'block_content', TRUE, ['view latest version', 'view any unpublished content'], FALSE, AccessResultAllowed::class],
|
||||
// Block with no forward revision.
|
||||
[BlockContent::class, 'block_content', FALSE, ['view latest version', 'view any unpublished content'], FALSE, AccessResultForbidden::class],
|
||||
// Block with forward revision, but no permission to view any.
|
||||
[BlockContent::class, 'block_content', TRUE, ['view latest version', 'view own unpublished content'], FALSE, AccessResultNeutral::class],
|
||||
// Block with no forward revision.
|
||||
[BlockContent::class, 'block_content', FALSE, ['view latest version', 'view own unpublished content'], FALSE, AccessResultForbidden::class],
|
||||
];
|
||||
}
|
||||
|
||||
|
|
Reference in a new issue