diff --git a/core/lib/Drupal/Core/Config/ConfigManagerInterface.php b/core/lib/Drupal/Core/Config/ConfigManagerInterface.php index d9b0908..7da86f7 100644 --- a/core/lib/Drupal/Core/Config/ConfigManagerInterface.php +++ b/core/lib/Drupal/Core/Config/ConfigManagerInterface.php @@ -181,8 +181,13 @@ public function supportsConfigurationEntities($collection); public function getConfigCollectionInfo(); /** + * Finds missing content dependencies declared in configuration entities. + * * @return array - * A list of missing dependencies() + * A list of missing content dependencies. The array is keyed by UUID. Each + * value is an array with the following keys: 'entity_type', 'bundle' and + * 'uuid'. */ public function findMissingContentDependencies(); + } diff --git a/core/lib/Drupal/Core/Config/Importer/MissingContentEvent.php b/core/lib/Drupal/Core/Config/Importer/MissingContentEvent.php index c77fb80..4a64c74 100644 --- a/core/lib/Drupal/Core/Config/Importer/MissingContentEvent.php +++ b/core/lib/Drupal/Core/Config/Importer/MissingContentEvent.php @@ -15,7 +15,7 @@ class MissingContentEvent extends Event { /** - * Missing content. + * A list of missing content dependencies. * * @var array */ @@ -35,7 +35,9 @@ public function __construct(array $missing_content) { * Gets missing content information. * * @return array - * The missing content. + * A list of missing content dependencies. The array is keyed by UUID. Each + * value is an array with the following keys: 'entity_type', 'bundle' and + * 'uuid'. */ public function getMissingContent() { return $this->missingContent; @@ -44,9 +46,6 @@ public function getMissingContent() { /** * Resolves the missing content by removing it from the list. * - * Calling this also stops event propagation so that if the event subscriber - * does actually creates content it will work nicely with the batch system. - * * @param string $uuid * The UUID of the content entity to mark resolved. * @@ -57,9 +56,6 @@ public function resolveMissingContent($uuid) { if (isset($this->missingContent[$uuid])) { unset($this->missingContent[$uuid]); } - else { - // @todo throw an exception? Atm I don't think so. - } return $this; } diff --git a/core/modules/config/src/Tests/ConfigDependencyTest.php b/core/modules/config/src/Tests/ConfigDependencyTest.php index bad5466..35b176c 100644 --- a/core/modules/config/src/Tests/ConfigDependencyTest.php +++ b/core/modules/config/src/Tests/ConfigDependencyTest.php @@ -43,7 +43,7 @@ public function testNonEntity() { /** * Tests creating dependencies on configuration entities. */ - public function testDependencyMangement() { + public function testDependencyManagement() { /** @var \Drupal\Core\Config\ConfigManagerInterface $config_manager */ $config_manager = \Drupal::service('config.manager'); $storage = $this->container->get('entity.manager')->getStorage('config_test'); diff --git a/core/modules/config/src/Tests/ConfigImporterMissingContentTest.php b/core/modules/config/src/Tests/ConfigImporterMissingContentTest.php new file mode 100644 index 0000000..d4220aa --- /dev/null +++ b/core/modules/config/src/Tests/ConfigImporterMissingContentTest.php @@ -0,0 +1,92 @@ +installSchema('system', 'sequences'); + $this->installEntitySchema('entity_test'); + $this->installEntitySchema('user'); + $this->installConfig(array('config_test')); + // Installing config_test's default configuration pollutes the global + // variable being used for recording hook invocations by this test already, + // so it has to be cleared out manually. + unset($GLOBALS['hook_config_test']); + + $this->copyConfig($this->container->get('config.storage'), $this->container->get('config.storage.staging')); + + // Set up the ConfigImporter object for testing. + $storage_comparer = new StorageComparer( + $this->container->get('config.storage.staging'), + $this->container->get('config.storage'), + $this->container->get('config.manager') + ); + $this->configImporter = new ConfigImporter( + $storage_comparer->createChangelist(), + $this->container->get('event_dispatcher'), + $this->container->get('config.manager'), + $this->container->get('lock'), + $this->container->get('config.typed'), + $this->container->get('module_handler'), + $this->container->get('module_installer'), + $this->container->get('theme_handler'), + $this->container->get('string_translation') + ); + } + + /** + * Tests the missing content event is fired. + */ + function testMissingContent() { + \Drupal::state()->set('config_import_test.config_import_missing_content', TRUE); + + // Update a configuration entity in the staging directory to have a + // dependency on a content entity that does not exist. + $storage = $this->container->get('config.storage'); + $staging = $this->container->get('config.storage.staging'); + $entity = entity_create('entity_test', array('name' => 'test')); + $dynamic_name = 'config_test.dynamic.dotted.default'; + $original_dynamic_data = $storage->read($dynamic_name); + $original_dynamic_data['dependencies']['content'][] = $entity->getConfigDependencyName(); + $staging->write($dynamic_name, $original_dynamic_data); + + // Import. + $this->configImporter->reset()->import(); + $this->assertEqual($entity->uuid(), \Drupal::state()->get('config_import_test.config_import_missing_content'), 'The missing content event is fired during configuration import.'); + $original_dynamic_data = $storage->read($dynamic_name); + $this->assertEqual([$entity->getConfigDependencyName()], $original_dynamic_data['dependencies']['content'], 'The imported configuration entity has the missing content entity dependency.'); + } + +} diff --git a/core/modules/config/tests/config_import_test/src/EventSubscriber.php b/core/modules/config/tests/config_import_test/src/EventSubscriber.php index 0be4f97..8fc2d6b 100644 --- a/core/modules/config/tests/config_import_test/src/EventSubscriber.php +++ b/core/modules/config/tests/config_import_test/src/EventSubscriber.php @@ -10,6 +10,7 @@ use Drupal\Core\Config\ConfigCrudEvent; use Drupal\Core\Config\ConfigEvents; use Drupal\Core\Config\ConfigImporterEvent; +use Drupal\Core\Config\Importer\MissingContentEvent; use Drupal\Core\State\StateInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; @@ -52,6 +53,19 @@ public function onConfigImporterValidate(ConfigImporterEvent $event) { } /** + * Handles the missing content event. + * + * @param \Drupal\Core\Config\Importer\MissingContentEvent $event + * The missing content event. + */ + public function onConfigImporterMissingContent(MissingContentEvent $event) { + if ($this->state->get('config_import_test.config_import_missing_content', FALSE)) { + $missing = $event->getMissingContent(); + $this->state->set('config_import_test.config_import_missing_content', key($missing)); + } + } + + /** * Reacts to a config save and records information in state for testing. * * @param \Drupal\Core\Config\ConfigCrudEvent $event @@ -106,6 +120,7 @@ static function getSubscribedEvents() { $events[ConfigEvents::SAVE][] = array('onConfigSave', 40); $events[ConfigEvents::DELETE][] = array('onConfigDelete', 40); $events[ConfigEvents::IMPORT_VALIDATE] = array('onConfigImporterValidate'); + $events[ConfigEvents::IMPORT_MISSING_CONTENT] = array('onConfigImporterMissingContent'); return $events; }