diff --git a/core/core.services.yml b/core/core.services.yml index c4c7593..7139cd4 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -839,6 +839,7 @@ services: class: Drupal\Core\EventSubscriber\ConfigImportSubscriber tags: - { name: event_subscriber } + arguments: ['@module_handler'] config_snapshot_subscriber: class: Drupal\Core\EventSubscriber\ConfigSnapshotSubscriber tags: diff --git a/core/lib/Drupal/Core/Config/Schema/Element.php b/core/lib/Drupal/Core/Config/Schema/Element.php index c492d42..4993de8 100644 --- a/core/lib/Drupal/Core/Config/Schema/Element.php +++ b/core/lib/Drupal/Core/Config/Schema/Element.php @@ -7,6 +7,7 @@ namespace Drupal\Core\Config\Schema; +use Drupal\Core\Config\TypedConfigManagerInterface; use Drupal\Core\TypedData\TypedData; /** @@ -15,6 +16,13 @@ abstract class Element extends TypedData { /** + * The typed config manager. + * + * @var \Drupal\Core\Config\TypedConfigManagerInterface + */ + protected $typedConfig; + + /** * The configuration value. * * @var mixed @@ -25,7 +33,7 @@ * Create typed config object. */ protected function parseElement($key, $data, $definition) { - return \Drupal::service('config.typed')->create($definition, $data, $key, $this); + return $this->typedConfig->create($definition, $data, $key, $this); } /** @@ -34,7 +42,19 @@ protected function parseElement($key, $data, $definition) { * @return \Drupal\Core\TypedData\DataDefinitionInterface */ protected function buildDataDefinition($definition, $value, $key) { - return \Drupal::service('config.typed')->buildDataDefinition($definition, $value, $key, $this); + return $this->typedConfig->buildDataDefinition($definition, $value, $key, $this); + } + + /** + * Sets the typed config manager on the instance. + * + * This must be called immediately after construction to enable + * self::parseElement() and self::buildDataDefinition() to work. + * + * @param \Drupal\Core\Config\TypedConfigManagerInterface $typed_config + */ + public function setTypedConfig(TypedConfigManagerInterface $typed_config) { + $this->typedConfig = $typed_config; } } diff --git a/core/lib/Drupal/Core/Config/TypedConfigManager.php b/core/lib/Drupal/Core/Config/TypedConfigManager.php index fb1c198..a784364 100644 --- a/core/lib/Drupal/Core/Config/TypedConfigManager.php +++ b/core/lib/Drupal/Core/Config/TypedConfigManager.php @@ -10,6 +10,7 @@ use Drupal\Component\Utility\NestedArray; use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Config\Schema\ConfigSchemaDiscovery; +use Drupal\Core\Config\Schema\Element; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\TypedData\TypedDataManager; @@ -295,4 +296,17 @@ public function hasConfigSchema($name) { return is_array($definition) && ($definition['class'] != '\Drupal\Core\Config\Schema\Undefined'); } + /** + * {@inheritdoc} + */ + public function createInstance($data_type, array $configuration = array()) { + $instance = parent::createInstance($data_type, $configuration); + // Enable elements to construct their own definitions using the typed config + // manager. + if ($instance instanceof Element) { + $instance->setTypedConfig($this); + } + return $instance; + } + } diff --git a/core/lib/Drupal/Core/EventSubscriber/ConfigImportSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/ConfigImportSubscriber.php index f17d5bd..f2386c6 100644 --- a/core/lib/Drupal/Core/EventSubscriber/ConfigImportSubscriber.php +++ b/core/lib/Drupal/Core/EventSubscriber/ConfigImportSubscriber.php @@ -7,15 +7,49 @@ namespace Drupal\Core\EventSubscriber; +use Drupal\Core\Cache\MemoryBackend; use Drupal\Core\Config\Config; use Drupal\Core\Config\ConfigImporterEvent; use Drupal\Core\Config\ConfigImportValidateEventSubscriberBase; use Drupal\Core\Config\ConfigNameException; +use Drupal\Core\Config\Schema\SchemaCheckTrait; +use Drupal\Core\Config\TypedConfigManager; +use Drupal\Core\Config\InstallStorage; +use Drupal\Core\Extension\ModuleHandlerInterface; /** * Config import subscriber for config import events. */ class ConfigImportSubscriber extends ConfigImportValidateEventSubscriberBase { + use SchemaCheckTrait; + + /** + * The module handler. + * + * @var \Drupal\Core\Extension\ModuleHandlerInterface + */ + protected $moduleHandler; + + /** + * @var \Drupal\Core\Config\TypedConfigManagerInterface + */ + protected $typedConfig; + + /** + * The config importer. + * + * @var \Drupal\Core\Config\ConfigImporter + */ + protected $configImporter; + + /** + * Constructs the ConfigImportSubscriber. + * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler + * The module handler. + */ + public function __construct(ModuleHandlerInterface $module_handler) { + $this->moduleHandler = $module_handler; + } /** * Validates the configuration to be imported. @@ -26,17 +60,78 @@ class ConfigImportSubscriber extends ConfigImportValidateEventSubscriberBase { * @throws \Drupal\Core\Config\ConfigNameException */ public function onConfigImporterValidate(ConfigImporterEvent $event) { + $this->typedConfig = NULL; + $this->configImporter = $event->getConfigImporter(); foreach (array('delete', 'create', 'update') as $op) { - foreach ($event->getConfigImporter()->getUnprocessedConfiguration($op) as $name) { - try { - Config::validateName($name); - } - catch (ConfigNameException $e) { - $message = $this->t('The config name @config_name is invalid.', array('@config_name' => $name)); - $event->getConfigImporter()->logError($message); + foreach ($this->configImporter->getUnprocessedConfiguration($op) as $name) { + $this->validateName($name); + + if ($op != 'delete') { + $this->validateSchema($name); } } } } + /** + * Validates configuration object names. + * + * @param $name + * The configuration name. + */ + protected function validateName($name) { + try { + Config::validateName($name); + } + catch (ConfigNameException $e) { + $message = $this->t('The config name @config_name is invalid.', array('@config_name' => $name)); + $this->configImporter->logError($message); + } + } + + /** + * Validates configuration data using its schema. + * + * @param $name + * The configuration name. + * @param \Drupal\Core\Config\ConfigImporter $config_importer + * The configuration importer. + */ + protected function validateSchema($name) { + $config_data = $this->configImporter + ->getStorageComparer() + ->getSourceStorage() + ->read($name); + $errors = $this->checkConfigSchema($this->getTypedConfig(), $name, $config_data); + if (is_array($errors)) { + foreach ($errors as $key => $error) { + $this->configImporter->logError($this->t('Schema key @key failed with: @error', array( + '@key' => $key, + '@error' => $error + ))); + } + } + } + + /** + * Creates a typed config manager that reads all available schema. + * + * @return \Drupal\Core\Config\TypedConfigManagerInterface + * The typed config manager. + */ + protected function getTypedConfig () { + if (!isset($this->typedConfig)) { + // Construct a TypedConfigManager will access to all schema even if the + // module or theme that provides it is uninstalled. + $schema_storage = new InstallStorage(InstallStorage::CONFIG_SCHEMA_DIRECTORY); + $this->typedConfig = new TypedConfigManager( + $this->configImporter->getStorageComparer()->getSourceStorage(), + $schema_storage, + new MemoryBackend('ConfigImportSubscriber'), + $this->moduleHandler + ); + } + return $this->typedConfig; + } + } diff --git a/core/modules/field/tests/modules/field_test_config/staging/field.field.entity_test.entity_test.field_test_import_staging.yml b/core/modules/field/tests/modules/field_test_config/staging/field.field.entity_test.entity_test.field_test_import_staging.yml index 30768c0..2d97dc0 100644 --- a/core/modules/field/tests/modules/field_test_config/staging/field.field.entity_test.entity_test.field_test_import_staging.yml +++ b/core/modules/field/tests/modules/field_test_config/staging/field.field.entity_test.entity_test.field_test_import_staging.yml @@ -6,7 +6,7 @@ entity_type: entity_test bundle: entity_test label: 'Import from staging' description: '' -required: '0' +required: false default_value: { } default_value_callback: '' settings: { } diff --git a/core/modules/field/tests/modules/field_test_config/staging/field.field.entity_test.test_bundle.field_test_import_staging_2.yml b/core/modules/field/tests/modules/field_test_config/staging/field.field.entity_test.test_bundle.field_test_import_staging_2.yml index 447205d..8980f3a 100644 --- a/core/modules/field/tests/modules/field_test_config/staging/field.field.entity_test.test_bundle.field_test_import_staging_2.yml +++ b/core/modules/field/tests/modules/field_test_config/staging/field.field.entity_test.test_bundle.field_test_import_staging_2.yml @@ -6,7 +6,7 @@ entity_type: entity_test bundle: test_bundle label: 'Test import field 2 on test bundle' description: '' -required: '0' +required: false default_value: { } default_value_callback: '' settings: { } diff --git a/core/modules/field/tests/modules/field_test_config/staging/field.field.entity_test.test_bundle_2.field_test_import_staging_2.yml b/core/modules/field/tests/modules/field_test_config/staging/field.field.entity_test.test_bundle_2.field_test_import_staging_2.yml index 282fc83..0748e35 100644 --- a/core/modules/field/tests/modules/field_test_config/staging/field.field.entity_test.test_bundle_2.field_test_import_staging_2.yml +++ b/core/modules/field/tests/modules/field_test_config/staging/field.field.entity_test.test_bundle_2.field_test_import_staging_2.yml @@ -6,7 +6,7 @@ entity_type: entity_test bundle: test_bundle_2 label: 'Test import field 2 on test bundle 2' description: '' -required: '0' +required: false default_value: { } default_value_callback: '' settings: { } diff --git a/core/modules/field/tests/modules/field_test_config/staging/field.storage.entity_test.field_test_import_staging.yml b/core/modules/field/tests/modules/field_test_config/staging/field.storage.entity_test.field_test_import_staging.yml index 4477f26..523a688 100644 --- a/core/modules/field/tests/modules/field_test_config/staging/field.storage.entity_test.field_test_import_staging.yml +++ b/core/modules/field/tests/modules/field_test_config/staging/field.storage.entity_test.field_test_import_staging.yml @@ -5,11 +5,11 @@ field_name: field_test_import_staging entity_type: entity_test type: text settings: - max_length: '255' + max_length: 255 module: text -locked: '0' -cardinality: '1' -translatable: '0' +locked: false +cardinality: 1 +translatable: false indexes: format: - format diff --git a/core/modules/field/tests/modules/field_test_config/staging/field.storage.entity_test.field_test_import_staging_2.yml b/core/modules/field/tests/modules/field_test_config/staging/field.storage.entity_test.field_test_import_staging_2.yml index 5460ecc..1416dde 100644 --- a/core/modules/field/tests/modules/field_test_config/staging/field.storage.entity_test.field_test_import_staging_2.yml +++ b/core/modules/field/tests/modules/field_test_config/staging/field.storage.entity_test.field_test_import_staging_2.yml @@ -5,11 +5,11 @@ field_name: field_test_import_staging_2 entity_type: entity_test type: text settings: - max_length: '255' + max_length: 255 module: text -locked: '0' -cardinality: '1' -translatable: '0' +locked: false +cardinality: 1 +translatable: false indexes: format: - format