Unverified Commit eadf1d65 authored by larowlan's avatar larowlan

Merge tag '8.x-1.4' into 8.x-1.x

parents 236d66c3 f4053490
......@@ -17,5 +17,6 @@ use Symfony\Component\Validator\Constraint;
class ModerationState extends Constraint {
public $message = 'Invalid state transition from %from to %to';
public $accessDeniedMessage = 'You do not have access to transition from %from to %to';
}
......@@ -5,6 +5,8 @@ namespace Drupal\workbench_moderation\Plugin\Validation\Constraint;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\workbench_moderation\Entity\ModerationState;
use Drupal\workbench_moderation\ModerationInformationInterface;
use Drupal\workbench_moderation\StateTransitionValidation;
use Symfony\Component\DependencyInjection\ContainerInterface;
......@@ -32,6 +34,13 @@ class ModerationStateValidator extends ConstraintValidator implements ContainerI
*/
protected $moderationInformation;
/**
* The current account.
*
* @var \Drupal\Core\Session\AccountInterface
*/
protected $currentUser;
/**
* Creates a new ModerationStateValidator instance.
*
......@@ -41,18 +50,22 @@ class ModerationStateValidator extends ConstraintValidator implements ContainerI
* The state transition validation.
* @param \Drupal\workbench_moderation\ModerationInformationInterface $moderation_information
* The moderation information.
* @param \Drupal\Core\Session\AccountInterface $account
* The moderation information.
*/
public function __construct(EntityTypeManagerInterface $entity_type_manager, StateTransitionValidation $validation, ModerationInformationInterface $moderation_information) {
public function __construct(EntityTypeManagerInterface $entity_type_manager, StateTransitionValidation $validation, ModerationInformationInterface $moderation_information, AccountInterface $account) {
$this->validation = $validation;
$this->entityTypeManager = $entity_type_manager;
$this->moderationInformation = $moderation_information;
$this->currentUser = $account;
}
public static function create(ContainerInterface $container) {
return new static(
$container->get('entity_type.manager'),
$container->get('workbench_moderation.state_transition_validation'),
$container->get('workbench_moderation.moderation_information')
$container->get('workbench_moderation.moderation_information'),
$container->get('current_user')
);
}
......@@ -68,26 +81,23 @@ class ModerationStateValidator extends ConstraintValidator implements ContainerI
return;
}
// Ignore entities that are being created for the first time.
if ($entity->isNew()) {
return;
}
// Ignore entities that are being moderated for the first time, such as
// when they existed before moderation was enabled for this entity type.
if ($this->isFirstTimeModeration($entity)) {
return;
}
$original_entity = $this->moderationInformation->getLatestRevision($entity->getEntityTypeId(), $entity->id());
if (!$entity->isDefaultTranslation() && $original_entity->hasTranslation($entity->language()->getId())) {
$original_entity = $original_entity->getTranslation($entity->language()->getId());
}
$next_moderation_state_id = $entity->moderation_state->target_id;
$original_moderation_state_id = $original_entity->moderation_state->target_id;
if (!$this->validation->isTransitionAllowed($original_moderation_state_id, $next_moderation_state_id)) {
$this->context->addViolation($constraint->message, ['%from' => $original_entity->moderation_state->entity->label(), '%to' => $entity->moderation_state->entity->label()]);
/** @var \Drupal\Core\Config\Entity\ConfigEntityInterface $bundle */
$bundle = $this->entityTypeManager->getStorage($entity->getEntityType()->getBundleEntityType())->load($entity->bundle());
$default_state = $bundle->getThirdPartySetting('workbench_moderation', 'default_moderation_state');
$next_moderation_state = ModerationState::load(!$entity->moderation_state->isEmpty() ? $entity->moderation_state->target_id : $default_state);
$original_moderation_state = ModerationState::load($original_entity && !$original_entity->moderation_state->isEmpty() ? $original_entity->moderation_state->target_id : $default_state);
if (!$this->validation->isTransitionAllowed($original_moderation_state->id(), $next_moderation_state->id())) {
$this->context->addViolation($constraint->message, ['%from' => $original_moderation_state->label(), '%to' => $next_moderation_state->label()]);
}
elseif (!$this->validation->userMayTransition($original_moderation_state->id(), $next_moderation_state->id(), $this->currentUser)) {
$this->context->addViolation($constraint->accessDeniedMessage, ['%from' => $original_moderation_state->label(), '%to' => $next_moderation_state->label()]);
}
}
......
......@@ -7,6 +7,7 @@ use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\node\Entity\Node;
use Drupal\node\Entity\NodeType;
use Drupal\node\NodeInterface;
use Drupal\Tests\user\Traits\UserCreationTrait;
/**
* @coversDefaultClass \Drupal\workbench_moderation\Plugin\Validation\Constraint\ModerationStateValidator
......@@ -14,11 +15,20 @@ use Drupal\node\NodeInterface;
*/
class EntityStateChangeValidationTest extends KernelTestBase {
use UserCreationTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['node', 'workbench_moderation', 'user', 'system', 'language', 'content_translation'];
/**
* An admin user account.
*
* @var \Drupal\Core\Session\AccountInterface
*/
protected $adminUser;
/**
* {@inheritdoc}
*/
......@@ -26,9 +36,12 @@ class EntityStateChangeValidationTest extends KernelTestBase {
parent::setUp();
$this->installSchema('node', 'node_access');
$this->installSchema('system', ['sequences']);
$this->installEntitySchema('node');
$this->installEntitySchema('user');
$this->installConfig('workbench_moderation');
$this->adminUser = $this->createUser(array_keys($this->container->get('user.permissions')->getPermissions()));
}
/**
......@@ -37,6 +50,8 @@ class EntityStateChangeValidationTest extends KernelTestBase {
* @covers ::validate
*/
public function testValidTransition() {
$this->setCurrentUser($this->adminUser);
$node_type = NodeType::create([
'type' => 'example',
]);
......@@ -58,6 +73,8 @@ class EntityStateChangeValidationTest extends KernelTestBase {
* @covers ::validate
*/
public function testInvalidTransition() {
$this->setCurrentUser($this->adminUser);
$node_type = NodeType::create([
'type' => 'example',
]);
......@@ -79,8 +96,12 @@ class EntityStateChangeValidationTest extends KernelTestBase {
/**
* Verifies that content without prior moderation information can be moderated.
*
* @legacy
*/
public function testLegacyContent() {
public function testContent() {
$this->setCurrentUser($this->adminUser);
$node_type = NodeType::create([
'type' => 'example',
]);
......@@ -116,8 +137,10 @@ class EntityStateChangeValidationTest extends KernelTestBase {
/**
* Verifies that content without prior moderation information can be translated.
*
* @legacy
*/
public function testLegacyMultilingualContent() {
public function testMultilingualContent() {
// Enable French
ConfigurableLanguage::createFromLangcode('fr')->save();
......@@ -159,4 +182,126 @@ class EntityStateChangeValidationTest extends KernelTestBase {
$node_fr->setTitle('Nouveau');
$node_fr->save();
}
/**
* @dataProvider transitionAccessValidationTestCases
*/
public function testTransitionAccessValidationNewEntity($permissions, $default_state, $target_state, $messages) {
$this->setCurrentUser($this->createUser($permissions));
$this->createExampleModeratedContentType($default_state, ['draft', 'needs_review', 'published', 'archived']);
$node = Node::create([
'type' => 'example',
'title' => 'Test content',
'moderation_state' => $target_state,
]);
$this->assertTrue($node->isNew());
$violations = $node->validate();
$this->assertCount(count($messages), $violations);
foreach ($messages as $i => $message) {
$this->assertEquals($message, $violations->get($i)->getMessage());
}
}
/**
* @dataProvider transitionAccessValidationTestCases
*/
public function testTransitionAccessValidationSavedEntity($permissions, $default_state, $target_state, $messages) {
$this->setCurrentUser($this->createUser($permissions));
$this->createExampleModeratedContentType($default_state, ['draft', 'needs_review', 'published', 'archived']);
$node = Node::create([
'type' => 'example',
'title' => 'Test content',
'moderation_state' => $default_state,
]);
$node->save();
$node->moderation_state = $target_state;
$violations = $node->validate();
$this->assertCount(count($messages), $violations);
foreach ($messages as $i => $message) {
$this->assertEquals($message, $violations->get($i)->getMessage());
}
}
/**
* Test cases for access validation.
*/
public function transitionAccessValidationTestCases() {
return [
'Invalid transition, no permissions validated' => [
[],
'draft',
'archived',
['Invalid state transition from <em class="placeholder">Draft</em> to <em class="placeholder">Archived</em>'],
],
'Valid transition, missing permission' => [
[],
'draft',
'published',
['You do not have access to transition from <em class="placeholder">Draft</em> to <em class="placeholder">Published</em>'],
],
'Valid transition, granted published permission' => [
['use draft_published transition'],
'draft',
'published',
[],
],
'Valid transition, granted draft permission' => [
['use draft_draft transition'],
'draft',
'draft',
[],
],
'Valid transition, incorrect permission granted' => [
['use draft_draft transition'],
'draft',
'published',
['You do not have access to transition from <em class="placeholder">Draft</em> to <em class="placeholder">Published</em>'],
],
'Non-draft default state, incorrect permission granted' => [
['use draft_draft transition'],
'archived',
'published',
['You do not have access to transition from <em class="placeholder">Archived</em> to <em class="placeholder">Published</em>'],
],
'Non-draft default state, correct permission granted' => [
['use archived_published transition'],
'archived',
'published',
[],
],
'Non-draft default state, invalid transition' => [
['use published_archived transition'],
'archived',
'draft',
['Invalid state transition from <em class="placeholder">Archived</em> to <em class="placeholder">Draft</em>'],
],
];
}
/**
* Create an example content type.
*
* @param string $default_state
* The default state.
* @param array $allowed_states
* The allowed states.
*/
protected function createExampleModeratedContentType($default_state, $allowed_states) {
$node_type = NodeType::create([
'type' => 'example',
]);
$node_type->save();
$node_type = NodeType::load('example');
$node_type->setThirdPartySetting('workbench_moderation', 'enabled', TRUE);
$node_type->setThirdPartySetting('workbench_moderation', 'allowed_moderation_states', $allowed_states);
$node_type->setThirdPartySetting('workbench_moderation', 'default_moderation_state', $default_state);
$node_type->save();
}
}
<?php
/**
* @file
* Contains install/update hooks for moderation_state.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment