diff --git a/src/EntityOperations.php b/src/EntityOperations.php index e41cf7f..ed608be 100644 --- a/src/EntityOperations.php +++ b/src/EntityOperations.php @@ -70,16 +70,26 @@ class EntityOperations { * The type of entity being loaded, such as "node" or "user". */ public function entityStorageLoad(array $entities, $entity_type_id) { - $needs_default = array_filter($entities, function(EntityInterface $entity) { - return $this->moderationInfo->isModeratableEntity($entity) - && $entity->moderation_state->target_id == NULL; - }); - - // Because objects pass by handle, this will modify each in place - // appropriately. - array_map(function(ContentEntityInterface $entity) { - $entity->moderation_state->target_id = $this->getDefaultLoadStateId($entity); - }, $needs_default); + + // Ensure that all moderatable entities always have a moderation_state field + // with data, in all translations. That avoids us needing to have a thousand + // NULL checks elsewhere in the code. + + // Quickly exclude any non-moderatable entities. + $to_check = array_filter($entities, [$this->moderationInfo, 'isModeratableEntity']); + if (!$to_check) { + return; + } + + // @todo make this more functional, less iterative. + foreach ($to_check as $entity) { + foreach ($entity->getTranslationLanguages() as $language) { + $translation = $entity->getTranslation($language->getId()); + if ($translation->moderation_state->target_id == NULL) { + $translation->moderation_state->target_id = $this->getDefaultLoadStateId($translation); + } + } + } } /** diff --git a/tests/src/Kernel/EntityStateChangeValidationTest.php b/tests/src/Kernel/EntityStateChangeValidationTest.php index 0f013c1..538f2b9 100644 --- a/tests/src/Kernel/EntityStateChangeValidationTest.php +++ b/tests/src/Kernel/EntityStateChangeValidationTest.php @@ -3,6 +3,7 @@ namespace Drupal\Tests\workbench_moderation\Kernel; use Drupal\KernelTests\KernelTestBase; +use Drupal\language\Entity\ConfigurableLanguage; use Drupal\node\Entity\Node; use Drupal\node\Entity\NodeType; use Drupal\node\NodeInterface; @@ -16,7 +17,7 @@ class EntityStateChangeValidationTest extends KernelTestBase { /** * {@inheritdoc} */ - public static $modules = ['node', 'workbench_moderation', 'user', 'system']; + public static $modules = ['node', 'workbench_moderation', 'user', 'system', 'language', 'content_translation']; /** * {@inheritdoc} @@ -93,7 +94,7 @@ class EntityStateChangeValidationTest extends KernelTestBase { $nid = $node->id(); - // Enable moderation for Articles. + // Enable moderation for our node type. /** @var NodeType $node_type */ $node_type = NodeType::load('example'); $node_type->setThirdPartySetting('workbench_moderation', 'enabled', TRUE); @@ -113,4 +114,49 @@ class EntityStateChangeValidationTest extends KernelTestBase { $node->save(); } + /** + * Verifies that content without prior moderation information can be translated. + */ + public function testLegacyMultilingualContent() { + // Enable French + ConfigurableLanguage::createFromLangcode('fr')->save(); + + $node_type = NodeType::create([ + 'type' => 'example', + ]); + $node_type->save(); + /** @var NodeInterface $node */ + $node = Node::create([ + 'type' => 'example', + 'title' => 'Test title', + 'langcode' => 'en', + ]); + $node->save(); + + $nid = $node->id(); + + $node = Node::load($nid); + + // Creating a translation shouldn't break, even though there's no previous + // moderated revision for the new language. + $node_fr = $node->addTranslation('fr'); + $node_fr->setTitle('Francais'); + $node_fr->save(); + + // Enable moderation for our node type. + /** @var NodeType $node_type */ + $node_type = NodeType::load('example'); + $node_type->setThirdPartySetting('workbench_moderation', 'enabled', TRUE); + $node_type->setThirdPartySetting('workbench_moderation', 'allowed_moderation_states', ['draft', 'needs_review', 'published']); + $node_type->setThirdPartySetting('workbench_moderation', 'default_moderation_state', 'draft'); + $node_type->save(); + + // Reload the French version of the node. + $node = Node::load($nid); + $node_fr = $node->getTranslation('fr'); + + /** @var NodeInterface $node_fr */ + $node_fr->setTitle('Nouveau'); + $node_fr->save(); + } }