diff --git a/core/modules/content_moderation/content_moderation.services.yml b/core/modules/content_moderation/content_moderation.services.yml index 904bc0dc33..256095a03a 100644 --- a/core/modules/content_moderation/content_moderation.services.yml +++ b/core/modules/content_moderation/content_moderation.services.yml @@ -19,4 +19,9 @@ services: class: Drupal\content_moderation\RevisionTracker arguments: ['@database'] tags: - - { name: backend_overridable } + - { name: backend_overridable } + content_moderation.config_import_subscriber: + class: Drupal\content_moderation\EventSubscriber\ConfigImportSubscriber + arguments: ['@config.manager', '@entity_type.manager'] + tags: + - { name: event_subscriber } diff --git a/core/modules/content_moderation/src/EventSubscriber/ConfigImportSubscriber.php b/core/modules/content_moderation/src/EventSubscriber/ConfigImportSubscriber.php index e69de29bb2..3555d736aa 100644 --- a/core/modules/content_moderation/src/EventSubscriber/ConfigImportSubscriber.php +++ b/core/modules/content_moderation/src/EventSubscriber/ConfigImportSubscriber.php @@ -0,0 +1,72 @@ +configManager = $config_manager; + $this->entityTypeManager = $entity_type_manager; + } + + /** + * {@inheritdoc} + */ + public function onConfigImporterValidate(ConfigImporterEvent $event) { + $unprocessed_configurations = $event->getConfigImporter()->getUnprocessedConfiguration('update'); + foreach ($unprocessed_configurations as $unprocessed_configuration) { + $entity_type_id = $this->configManager->getEntityTypeIdByName($unprocessed_configuration); + if ($entity_type_id == 'workflow') { + /** @var \Drupal\Core\Config\Entity\ConfigEntityTypeInterface $entity_type */ + $entity_type = $this->entityTypeManager->getDefinition($entity_type_id); + $importer = $event->getConfigImporter(); + $source_storage = $importer->getStorageComparer() + ->getSourceStorage() + ->read($unprocessed_configuration); + $target_storage = $importer->getStorageComparer() + ->getTargetStorage() + ->read($unprocessed_configuration); + $diff = array_diff_key($target_storage['states'], $source_storage['states']); + $entity_id = ConfigEntityStorage::getIDFromConfigName($unprocessed_configuration, $entity_type->getConfigPrefix()); + /** @var \Drupal\workflows\WorkflowInterface $workflow */ + $workflow = $this->entityTypeManager->getStorage($entity_type_id)->load($entity_id); + $state = $workflow->getState(key($diff)); + if ($workflow->getTypePlugin()->isWorkflowStateUsed($workflow, $state)) { + $importer->logError($this->t('The moderation state @state_label is being used, but is not in the source storage.', ['@state_label' => $state->label()])); + } + } + } + } + +} diff --git a/core/modules/content_moderation/tests/src/Kernel/ContentModerationWorkflowConfigTest.php b/core/modules/content_moderation/tests/src/Kernel/ContentModerationWorkflowConfigTest.php index e69de29bb2..97d9596d2b 100644 --- a/core/modules/content_moderation/tests/src/Kernel/ContentModerationWorkflowConfigTest.php +++ b/core/modules/content_moderation/tests/src/Kernel/ContentModerationWorkflowConfigTest.php @@ -0,0 +1,125 @@ +installSchema('node', 'node_access'); + $this->installEntitySchema('node'); + $this->installEntitySchema('user'); + $this->installEntitySchema('content_moderation_state'); + $this->installConfig('content_moderation'); + + NodeType::create([ + 'type' => 'example', + ])->save(); + + $workflow = Workflow::load('editorial'); + $workflow->addState('test1', 'Test one'); + $workflow->addState('test2', 'Test two'); + $workflow->getTypePlugin()->setConfiguration([ + 'states' => [ + 'test1' => [ + 'published' => TRUE, + 'default_revision' => TRUE, + ], + 'test2' => [ + 'published' => TRUE, + 'default_revision' => TRUE, + ], + ], + ]); + $workflow->getTypePlugin()->addEntityTypeAndBundle('node', 'example'); + $workflow->save(); + $this->workflow = $workflow; + + $this->copyConfig($this->container->get('config.storage'), $this->container->get('config.storage.sync')); + + } + + /** + * Test deleting a state via config import. + */ + public function testDeletingStateViaConfiguration() { + $config_data = $this->config('workflows.workflow.editorial')->get(); + unset($config_data['states']['test1']); + \Drupal::service('config.storage.sync')->write('workflows.workflow.editorial', $config_data); + + // There are no Nodes with the moderation state test, so this should run + // with no errors. + $this->configImporter()->reset()->import(); + + $node = Node::create([ + 'type' => 'example', + 'title' => 'Test title', + 'moderation_state' => 'test2', + ]); + $node->save(); + + $config_data = $this->config('workflows.workflow.editorial')->get(); + unset($config_data['states']['test2']); + \Drupal::service('config.storage.sync')->write('workflows.workflow.editorial', $config_data); + + // Now there is a Node with the moderation state unpub_test, this will fail. + try { + $this->configImporter()->reset()->import(); + $this->fail('ConfigImporterException not thrown, invalid import was not stopped due to deleted state.'); + } + catch (ConfigImporterException $e) { + $this->assertEqual($e->getMessage(), 'There were errors validating the config synchronization.'); + $error_log = $this->configImporter->getErrors(); + $expected = ['The moderation state Test two is being used, but is not in the source storage.']; + $this->assertEqual($expected, $error_log); + } + } + +}