configManager; } /** * @return StorageInterface */ public function getConfigStorage() { return $this->configStorage; } /** * @return StorageInterface */ public function getConfigStorageSync() { return $this->configStorageSync; } public function getModuleHandler() { return $this->moduleHandler; } /** * @return \Symfony\Component\EventDispatcher\EventDispatcherInterface */ public function getEventDispatcher() { return $this->eventDispatcher; } /** * @return \Drupal\Core\Lock\LockBackendInterface */ public function getLock() { return $this->lock; } /** * @return \Drupal\Core\Config\TypedConfigManagerInterface */ public function getConfigTyped() { return $this->configTyped; } /** * @return \Drupal\Core\Extension\ModuleInstallerInterface */ public function getModuleInstaller() { return $this->moduleInstaller; } /** * @return \Drupal\Core\Extension\ThemeHandlerInterface */ public function getThemeHandler() { return $this->themeHandler; } /** * @return \Drupal\Core\StringTranslation\TranslationInterface */ public function getStringTranslation() { return $this->stringTranslation; } /** * @param ConfigManagerInterface $configManager * @param StorageInterface $configStorage * @param StorageInterface $configStorageSync */ public function __construct(ConfigManagerInterface $configManager, StorageInterface $configStorage, StorageInterface $configStorageSync, ModuleHandlerInterface $moduleHandler, EventDispatcherInterface $eventDispatcher, LockBackendInterface $lock, TypedConfigManagerInterface $configTyped, ModuleInstallerInterface $moduleInstaller, ThemeHandlerInterface $themeHandler, TranslationInterface $stringTranslation) { parent::__construct(); $this->configManager = $configManager; $this->configStorage = $configStorage; $this->configStorageSync = $configStorageSync; $this->moduleHandler = $moduleHandler; $this->eventDispatcher = $eventDispatcher; $this->lock = $lock; $this->configTyped = $configTyped; $this->moduleInstaller = $moduleInstaller; $this->themeHandler = $themeHandler; $this->stringTranslation = $stringTranslation; } /** * Import config from a config directory. * * @command config:import * @param $label A config directory label (i.e. a key in \$config_directories array in settings.php). * @interact-config-label * @option diff Show preview as a diff. * @option preview Deprecated. Format for displaying proposed changes. Recognized values: list, diff. * @option source An arbitrary directory that holds the configuration files. An alternative to label argument * @option partial Allows for partial config imports from the source directory. Only updates and new configs will be processed with this flag (missing configs will not be deleted). * @aliases cim,config-import */ public function import($label = null, $options = ['preview' => 'list', 'source' => self::REQ, 'partial' => false, 'diff' => false]) { // Determine source directory. $source_storage_dir = ConfigCommands::getDirectory($label, $options['source']); // Prepare the configuration storage for the import. if ($source_storage_dir == Path::canonicalize(\config_get_config_directory(CONFIG_SYNC_DIRECTORY))) { $source_storage = $this->getConfigStorageSync(); } else { $source_storage = new FileStorage($source_storage_dir); } // Determine $source_storage in partial case. $active_storage = $this->getConfigStorage(); if ($options['partial']) { $replacement_storage = new StorageReplaceDataWrapper($active_storage); foreach ($source_storage->listAll() as $name) { $data = $source_storage->read($name); $replacement_storage->replaceData($name, $data); } $source_storage = $replacement_storage; } $config_manager = $this->getConfigManager(); $storage_comparer = new StorageComparer($source_storage, $active_storage, $config_manager); if (!$storage_comparer->createChangelist()->hasChanges()) { $this->logger()->notice(('There are no changes to import.')); return; } if ($options['preview'] == 'list' && !$options['diff']) { $change_list = []; foreach ($storage_comparer->getAllCollectionNames() as $collection) { $change_list[$collection] = $storage_comparer->getChangelist(null, $collection); } $table = ConfigCommands::configChangesTable($change_list, $this->output()); $table->render(); } else { $output = ConfigCommands::getDiff($active_storage, $source_storage, $this->output()); $this->output()->writeln(implode("\n", $output)); } if ($this->io()->confirm(dt('Import the listed configuration changes?'))) { return drush_op([$this, 'doImport'], $storage_comparer); } } // Copied from submitForm() at /core/modules/config/src/Form/ConfigSync.php public function doImport($storage_comparer) { $config_importer = new ConfigImporter( $storage_comparer, $this->getEventDispatcher(), $this->getConfigManager(), $this->getLock(), $this->getConfigTyped(), $this->getModuleHandler(), $this->getModuleInstaller(), $this->getThemeHandler(), $this->getStringTranslation() ); if ($config_importer->alreadyImporting()) { $this->logger()->warning('Another request may be synchronizing configuration already.'); } else { try { // This is the contents of \Drupal\Core\Config\ConfigImporter::import. // Copied here so we can log progress. if ($config_importer->hasUnprocessedConfigurationChanges()) { $sync_steps = $config_importer->initialize(); foreach ($sync_steps as $step) { $context = []; do { $config_importer->doSyncStep($step, $context); if (isset($context['message'])) { $this->logger()->notice(str_replace('Synchronizing', 'Synchronized', (string)$context['message'])); } } while ($context['finished'] < 1); } } if ($config_importer->getErrors()) { throw new ConfigException('Errors occurred during import'); } else { $this->logger()->success('The configuration was imported successfully.'); } } catch (ConfigException $e) { // Return a negative result for UI purposes. We do not differentiate // between an actual synchronization error and a failed lock, because // concurrent synchronizations are an edge-case happening only when // multiple developers or site builders attempt to do it without // coordinating. $message = 'The import failed due to the following reasons:' . "\n"; $message .= implode("\n", $config_importer->getErrors()); watchdog_exception('config_import', $e); throw new \Exception($message); } } } /** * @hook validate config-import * @param \Consolidation\AnnotatedCommand\CommandData $commandData * @return \Consolidation\AnnotatedCommand\CommandError|null */ public function validate(CommandData $commandData) { $msgs = []; if ($commandData->input()->getOption('partial') && !$this->getModuleHandler()->moduleExists('config')) { $msgs[] = 'Enable the config module in order to use the --partial option.'; } if ($source = $commandData->input()->getOption('source')) { if (!file_exists($source)) { $msgs[] = 'The source directory does not exist.'; } if (!is_dir($source)) { $msgs[] = 'The source is not a directory.'; } } if ($msgs) { return new CommandError(implode(' ', $msgs)); } } }