summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNathaniel Catchpole2018-11-01 14:23:04 (GMT)
committerNathaniel Catchpole2018-11-01 14:23:24 (GMT)
commitbabf1e9949fce14fbc72882de5b8e7faea246f31 (patch)
tree1d44ef9f1651eb861ea3e7a3c34ba7049926aa92
parente3829e1fae72eeeed50c68df8a975d73269ee8ef (diff)
Issue #2943899 by Sam152, amateescu, tstoeckler: Moderation state field cannot be updated via REST, because special handling in ModerationStateFieldItemList
(cherry picked from commit 1e5459ebf2b84a15bdce3ae7af4f1f001072f3d0)
-rw-r--r--core/modules/content_moderation/src/Plugin/Field/FieldWidget/ModerationStateWidget.php2
-rw-r--r--core/modules/content_moderation/src/Plugin/Field/ModerationStateFieldItemList.php18
-rw-r--r--core/modules/content_moderation/src/Plugin/Validation/Constraint/ModerationStateConstraintValidator.php8
-rw-r--r--core/modules/content_moderation/tests/src/Kernel/ModerationStateFieldItemListTest.php63
-rw-r--r--core/modules/rest/tests/src/Functional/EntityResource/ModeratedNode/ModeratedNodeJsonAnonTest.php24
-rw-r--r--core/modules/rest/tests/src/Functional/EntityResource/ModeratedNode/ModeratedNodeJsonBasicAuthTest.php34
-rw-r--r--core/modules/rest/tests/src/Functional/EntityResource/ModeratedNode/ModeratedNodeJsonCookieTest.php29
-rw-r--r--core/modules/rest/tests/src/Functional/EntityResource/ModeratedNode/ModeratedNodeResourceTestBase.php74
-rw-r--r--core/modules/rest/tests/src/Functional/EntityResource/ModeratedNode/ModeratedNodeXmlAnonTest.php34
-rw-r--r--core/modules/rest/tests/src/Functional/EntityResource/ModeratedNode/ModeratedNodeXmlBasicAuthTest.php44
-rw-r--r--core/modules/rest/tests/src/Functional/EntityResource/ModeratedNode/ModeratedNodeXmlCookieTest.php39
11 files changed, 345 insertions, 24 deletions
diff --git a/core/modules/content_moderation/src/Plugin/Field/FieldWidget/ModerationStateWidget.php b/core/modules/content_moderation/src/Plugin/Field/FieldWidget/ModerationStateWidget.php
index 8a33c76..0d4c43a 100644
--- a/core/modules/content_moderation/src/Plugin/Field/FieldWidget/ModerationStateWidget.php
+++ b/core/modules/content_moderation/src/Plugin/Field/FieldWidget/ModerationStateWidget.php
@@ -127,7 +127,7 @@ class ModerationStateWidget extends OptionsSelectWidget implements ContainerFact
$transitions = $this->validator->getValidTransitions($entity, $this->currentUser);
$transition_labels = [];
- $default_value = NULL;
+ $default_value = $items->value;
foreach ($transitions as $transition) {
$transition_to_state = $transition->to();
$transition_labels[$transition_to_state->id()] = $transition_to_state->label();
diff --git a/core/modules/content_moderation/src/Plugin/Field/ModerationStateFieldItemList.php b/core/modules/content_moderation/src/Plugin/Field/ModerationStateFieldItemList.php
index c33ccac..64c5ad8 100644
--- a/core/modules/content_moderation/src/Plugin/Field/ModerationStateFieldItemList.php
+++ b/core/modules/content_moderation/src/Plugin/Field/ModerationStateFieldItemList.php
@@ -16,7 +16,6 @@ use Drupal\Core\TypedData\ComputedItemListTrait;
class ModerationStateFieldItemList extends FieldItemList {
use ComputedItemListTrait {
- ensureComputedValue as traitEnsureComputedValue;
get as traitGet;
}
@@ -35,19 +34,6 @@ class ModerationStateFieldItemList extends FieldItemList {
}
/**
- * {@inheritdoc}
- */
- protected function ensureComputedValue() {
- // If the moderation state field is set to an empty value, always recompute
- // the state. Empty is not a valid moderation state value, when none is
- // present the default state is used.
- if (!isset($this->list[0]) || $this->list[0]->isEmpty()) {
- $this->valueComputed = FALSE;
- }
- $this->traitEnsureComputedValue();
- }
-
- /**
* Gets the moderation state ID linked to a content entity revision.
*
* @return string|null
@@ -140,10 +126,8 @@ class ModerationStateFieldItemList extends FieldItemList {
*/
public function setValue($values, $notify = TRUE) {
parent::setValue($values, $notify);
+ $this->valueComputed = TRUE;
- if (isset($this->list[0])) {
- $this->valueComputed = TRUE;
- }
// If the parent created a field item and if the parent should be notified
// about the change (e.g. this is not initialized with the current value),
// update the moderated entity.
diff --git a/core/modules/content_moderation/src/Plugin/Validation/Constraint/ModerationStateConstraintValidator.php b/core/modules/content_moderation/src/Plugin/Validation/Constraint/ModerationStateConstraintValidator.php
index c3b9c81..7894cad 100644
--- a/core/modules/content_moderation/src/Plugin/Validation/Constraint/ModerationStateConstraintValidator.php
+++ b/core/modules/content_moderation/src/Plugin/Validation/Constraint/ModerationStateConstraintValidator.php
@@ -9,6 +9,7 @@ use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\content_moderation\ModerationInformationInterface;
use Drupal\Core\Session\AccountInterface;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\NotNullConstraint;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
@@ -89,6 +90,13 @@ class ModerationStateConstraintValidator extends ConstraintValidator implements
return;
}
+ // If the entity is moderated and the item list is empty, ensure users see
+ // the same required message as typical NotNull constraints.
+ if ($value->isEmpty()) {
+ $this->context->addViolation((new NotNullConstraint())->message);
+ return;
+ }
+
$workflow = $this->moderationInformation->getWorkflowForEntity($entity);
if (!$workflow->getTypePlugin()->hasState($entity->moderation_state->value)) {
diff --git a/core/modules/content_moderation/tests/src/Kernel/ModerationStateFieldItemListTest.php b/core/modules/content_moderation/tests/src/Kernel/ModerationStateFieldItemListTest.php
index af0bb4f..7b57bd7 100644
--- a/core/modules/content_moderation/tests/src/Kernel/ModerationStateFieldItemListTest.php
+++ b/core/modules/content_moderation/tests/src/Kernel/ModerationStateFieldItemListTest.php
@@ -102,20 +102,70 @@ class ModerationStateFieldItemListTest extends KernelTestBase {
}
/**
- * Tests the computed field when it is unset or set to an empty value.
+ * Tests the item list when it is emptied and appended to.
*/
- public function testSetEmptyState() {
+ public function testEmptyStateAndAppend() {
+ // This test case mimics the lifecycle of an entity that is being patched in
+ // a rest resource.
+ $this->testNode->moderation_state->setValue([]);
+ $this->assertTrue($this->testNode->moderation_state->isEmpty());
+ $this->assertEmptiedModerationFieldItemList();
+
+ $this->testNode->moderation_state->appendItem();
+ $this->assertEquals(1, $this->testNode->moderation_state->count());
+ $this->assertEquals(NULL, $this->testNode->moderation_state->value);
+ $this->assertEmptiedModerationFieldItemList();
+ }
+
+ /**
+ * Test an empty value assigned to the field item.
+ */
+ public function testEmptyFieldItem() {
$this->testNode->moderation_state->value = '';
- $this->assertEquals('draft', $this->testNode->moderation_state->value);
+ $this->assertEquals('', $this->testNode->moderation_state->value);
+ $this->assertEmptiedModerationFieldItemList();
+ }
+ /**
+ * Test an empty value assigned to the field item list.
+ */
+ public function testEmptyFieldItemList() {
$this->testNode->moderation_state = '';
- $this->assertEquals('draft', $this->testNode->moderation_state->value);
+ $this->assertEquals('', $this->testNode->moderation_state->value);
+ $this->assertEmptiedModerationFieldItemList();
+ }
+ /**
+ * Test the field item when it is unset.
+ */
+ public function testUnsetItemList() {
unset($this->testNode->moderation_state);
- $this->assertEquals('draft', $this->testNode->moderation_state->value);
+ $this->assertEquals(NULL, $this->testNode->moderation_state->value);
+ $this->assertEmptiedModerationFieldItemList();
+ }
+ /**
+ * Test the field item when it is assigned NULL.
+ */
+ public function testAssignNullItemList() {
$this->testNode->moderation_state = NULL;
- $this->assertEquals('draft', $this->testNode->moderation_state->value);
+ $this->assertEquals(NULL, $this->testNode->moderation_state->value);
+ $this->assertEmptiedModerationFieldItemList();
+ }
+
+ /**
+ * Assert the set of expectations when the moderation state field is emptied.
+ */
+ protected function assertEmptiedModerationFieldItemList() {
+ $this->assertTrue($this->testNode->moderation_state->isEmpty());
+ // Test the empty value causes a violation in the entity.
+ $violations = $this->testNode->validate();
+ $this->assertCount(1, $violations);
+ $this->assertEquals('This value should not be null.', $violations->get(0)->getMessage());
+ // Test that incorrectly saving the entity regardless will not produce a
+ // change in the moderation state.
+ $this->testNode->save();
+ $this->assertEquals('draft', Node::load($this->testNode->id())->moderation_state->value);
}
/**
@@ -131,6 +181,7 @@ class ModerationStateFieldItemListTest extends KernelTestBase {
$unmoderated_node->moderation_state = NULL;
$this->assertEquals(0, $unmoderated_node->moderation_state->count());
+ $this->assertCount(0, $unmoderated_node->validate());
}
/**
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/ModeratedNode/ModeratedNodeJsonAnonTest.php b/core/modules/rest/tests/src/Functional/EntityResource/ModeratedNode/ModeratedNodeJsonAnonTest.php
new file mode 100644
index 0000000..f28788a
--- /dev/null
+++ b/core/modules/rest/tests/src/Functional/EntityResource/ModeratedNode/ModeratedNodeJsonAnonTest.php
@@ -0,0 +1,24 @@
+<?php
+
+namespace Drupal\Tests\rest\Functional\EntityResource\ModeratedNode;
+
+use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
+
+/**
+ * @group rest
+ */
+class ModeratedNodeJsonAnonTest extends ModeratedNodeResourceTestBase {
+
+ use AnonResourceTestTrait;
+
+ /**
+ * {@inheritdoc}
+ */
+ protected static $format = 'json';
+
+ /**
+ * {@inheritdoc}
+ */
+ protected static $mimeType = 'application/json';
+
+}
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/ModeratedNode/ModeratedNodeJsonBasicAuthTest.php b/core/modules/rest/tests/src/Functional/EntityResource/ModeratedNode/ModeratedNodeJsonBasicAuthTest.php
new file mode 100644
index 0000000..b47a86f
--- /dev/null
+++ b/core/modules/rest/tests/src/Functional/EntityResource/ModeratedNode/ModeratedNodeJsonBasicAuthTest.php
@@ -0,0 +1,34 @@
+<?php
+
+namespace Drupal\Tests\rest\Functional\EntityResource\ModeratedNode;
+
+use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
+
+/**
+ * @group rest
+ */
+class ModeratedNodeJsonBasicAuthTest extends ModeratedNodeResourceTestBase {
+
+ use BasicAuthResourceTestTrait;
+
+ /**
+ * {@inheritdoc}
+ */
+ public static $modules = ['basic_auth'];
+
+ /**
+ * {@inheritdoc}
+ */
+ protected static $format = 'json';
+
+ /**
+ * {@inheritdoc}
+ */
+ protected static $mimeType = 'application/json';
+
+ /**
+ * {@inheritdoc}
+ */
+ protected static $auth = 'basic_auth';
+
+}
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/ModeratedNode/ModeratedNodeJsonCookieTest.php b/core/modules/rest/tests/src/Functional/EntityResource/ModeratedNode/ModeratedNodeJsonCookieTest.php
new file mode 100644
index 0000000..08fc4b5
--- /dev/null
+++ b/core/modules/rest/tests/src/Functional/EntityResource/ModeratedNode/ModeratedNodeJsonCookieTest.php
@@ -0,0 +1,29 @@
+<?php
+
+namespace Drupal\Tests\rest\Functional\EntityResource\ModeratedNode;
+
+use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
+
+/**
+ * @group rest
+ */
+class ModeratedNodeJsonCookieTest extends ModeratedNodeResourceTestBase {
+
+ use CookieResourceTestTrait;
+
+ /**
+ * {@inheritdoc}
+ */
+ protected static $format = 'json';
+
+ /**
+ * {@inheritdoc}
+ */
+ protected static $mimeType = 'application/json';
+
+ /**
+ * {@inheritdoc}
+ */
+ protected static $auth = 'cookie';
+
+}
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/ModeratedNode/ModeratedNodeResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/ModeratedNode/ModeratedNodeResourceTestBase.php
new file mode 100644
index 0000000..054259d
--- /dev/null
+++ b/core/modules/rest/tests/src/Functional/EntityResource/ModeratedNode/ModeratedNodeResourceTestBase.php
@@ -0,0 +1,74 @@
+<?php
+
+namespace Drupal\Tests\rest\Functional\EntityResource\ModeratedNode;
+
+use Drupal\Tests\content_moderation\Traits\ContentModerationTestTrait;
+use Drupal\Tests\node\Functional\Rest\NodeResourceTestBase;
+
+/**
+ * Extend the Node resource test base and apply moderation to the entity.
+ */
+abstract class ModeratedNodeResourceTestBase extends NodeResourceTestBase {
+
+ use ContentModerationTestTrait;
+
+ /**
+ * {@inheritdoc}
+ */
+ public static $modules = ['content_moderation'];
+
+ /**
+ * The test editorial workflow.
+ *
+ * @var \Drupal\workflows\WorkflowInterface
+ */
+ protected $workflow;
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function setUpAuthorization($method) {
+ parent::setUpAuthorization($method);
+
+ switch ($method) {
+ case 'POST':
+ case 'PATCH':
+ case 'DELETE':
+ $this->grantPermissionsToTestedRole(['use editorial transition publish', 'use editorial transition create_new_draft']);
+ break;
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function createEntity() {
+ $entity = parent::createEntity();
+ if (!$this->workflow) {
+ $this->workflow = $this->createEditorialWorkflow();
+ }
+ $this->workflow->getTypePlugin()->addEntityTypeAndBundle($entity->getEntityTypeId(), $entity->bundle());
+ $this->workflow->save();
+
+ return $entity;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExpectedNormalizedEntity() {
+ return array_merge(parent::getExpectedNormalizedEntity(), [
+ 'moderation_state' => [
+ [
+ 'value' => 'published',
+ ],
+ ],
+ 'vid' => [
+ [
+ 'value' => (int) $this->entity->getRevisionId(),
+ ],
+ ],
+ ]);
+ }
+
+}
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/ModeratedNode/ModeratedNodeXmlAnonTest.php b/core/modules/rest/tests/src/Functional/EntityResource/ModeratedNode/ModeratedNodeXmlAnonTest.php
new file mode 100644
index 0000000..4b91d76
--- /dev/null
+++ b/core/modules/rest/tests/src/Functional/EntityResource/ModeratedNode/ModeratedNodeXmlAnonTest.php
@@ -0,0 +1,34 @@
+<?php
+
+namespace Drupal\Tests\rest\Functional\EntityResource\ModeratedNode;
+
+use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
+use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
+
+/**
+ * @group rest
+ */
+class ModeratedNodeXmlAnonTest extends ModeratedNodeResourceTestBase {
+
+ use AnonResourceTestTrait;
+ use XmlEntityNormalizationQuirksTrait;
+
+ /**
+ * {@inheritdoc}
+ */
+ protected static $format = 'xml';
+
+ /**
+ * {@inheritdoc}
+ */
+ protected static $mimeType = 'text/xml; charset=UTF-8';
+
+ /**
+ * {@inheritdoc}
+ */
+ public function testPatchPath() {
+ // Deserialization of the XML format is not supported.
+ $this->markTestSkipped();
+ }
+
+}
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/ModeratedNode/ModeratedNodeXmlBasicAuthTest.php b/core/modules/rest/tests/src/Functional/EntityResource/ModeratedNode/ModeratedNodeXmlBasicAuthTest.php
new file mode 100644
index 0000000..a321e03
--- /dev/null
+++ b/core/modules/rest/tests/src/Functional/EntityResource/ModeratedNode/ModeratedNodeXmlBasicAuthTest.php
@@ -0,0 +1,44 @@
+<?php
+
+namespace Drupal\Tests\rest\Functional\EntityResource\ModeratedNode;
+
+use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
+use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
+
+/**
+ * @group rest
+ */
+class ModeratedNodeXmlBasicAuthTest extends ModeratedNodeResourceTestBase {
+
+ use BasicAuthResourceTestTrait;
+ use XmlEntityNormalizationQuirksTrait;
+
+ /**
+ * {@inheritdoc}
+ */
+ public static $modules = ['basic_auth'];
+
+ /**
+ * {@inheritdoc}
+ */
+ protected static $format = 'xml';
+
+ /**
+ * {@inheritdoc}
+ */
+ protected static $mimeType = 'text/xml; charset=UTF-8';
+
+ /**
+ * {@inheritdoc}
+ */
+ protected static $auth = 'basic_auth';
+
+ /**
+ * {@inheritdoc}
+ */
+ public function testPatchPath() {
+ // Deserialization of the XML format is not supported.
+ $this->markTestSkipped();
+ }
+
+}
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/ModeratedNode/ModeratedNodeXmlCookieTest.php b/core/modules/rest/tests/src/Functional/EntityResource/ModeratedNode/ModeratedNodeXmlCookieTest.php
new file mode 100644
index 0000000..2014a56
--- /dev/null
+++ b/core/modules/rest/tests/src/Functional/EntityResource/ModeratedNode/ModeratedNodeXmlCookieTest.php
@@ -0,0 +1,39 @@
+<?php
+
+namespace Drupal\Tests\rest\Functional\EntityResource\ModeratedNode;
+
+use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
+use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
+
+/**
+ * @group rest
+ */
+class ModeratedNodeXmlCookieTest extends ModeratedNodeResourceTestBase {
+
+ use CookieResourceTestTrait;
+ use XmlEntityNormalizationQuirksTrait;
+
+ /**
+ * {@inheritdoc}
+ */
+ protected static $format = 'xml';
+
+ /**
+ * {@inheritdoc}
+ */
+ protected static $mimeType = 'text/xml; charset=UTF-8';
+
+ /**
+ * {@inheritdoc}
+ */
+ protected static $auth = 'cookie';
+
+ /**
+ * {@inheritdoc}
+ */
+ public function testPatchPath() {
+ // Deserialization of the XML format is not supported.
+ $this->markTestSkipped();
+ }
+
+}