2f56cdddd69497cf7fd35ac5df8ef3f2b0290862
[yaffs-website] / vendor / drush / drush / src / Drupal / Commands / config / ConfigImportCommands.php
1 <?php
2 namespace Drush\Drupal\Commands\config;
3
4 use Consolidation\AnnotatedCommand\CommandError;
5 use Consolidation\AnnotatedCommand\CommandData;
6 use Drupal\config\StorageReplaceDataWrapper;
7 use Drupal\Core\Config\ConfigManagerInterface;
8 use Drupal\Core\Config\StorageComparer;
9 use Drupal\Core\Config\ConfigImporter;
10 use Drupal\Core\Config\ConfigException;
11 use Drupal\Core\Config\FileStorage;
12 use Drupal\Core\Config\StorageInterface;
13 use Drupal\Core\Config\TypedConfigManagerInterface;
14 use Drupal\Core\Extension\ModuleHandlerInterface;
15 use Drupal\Core\Extension\ModuleInstallerInterface;
16 use Drupal\Core\Extension\ThemeHandlerInterface;
17 use Drupal\Core\Lock\LockBackendInterface;
18 use Drupal\Core\StringTranslation\TranslationInterface;
19 use Drush\Commands\DrushCommands;
20 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
21 use Webmozart\PathUtil\Path;
22
23 class ConfigImportCommands extends DrushCommands
24 {
25
26     /**
27      * @var ConfigManagerInterface
28      */
29     protected $configManager;
30
31     protected $configStorage;
32
33     protected $configStorageSync;
34
35     protected $eventDispatcher;
36
37     protected $lock;
38
39     protected $configTyped;
40
41     protected $moduleInstaller;
42
43     protected $themeHandler;
44
45     protected $stringTranslation;
46
47     /**
48      * @var \Drupal\Core\Extension\ModuleHandlerInterface
49      */
50     protected $moduleHandler;
51
52     /**
53      * @return ConfigManagerInterface
54      */
55     public function getConfigManager()
56     {
57         return $this->configManager;
58     }
59
60     /**
61      * @return StorageInterface
62      */
63     public function getConfigStorage()
64     {
65         return $this->configStorage;
66     }
67
68     /**
69      * @return StorageInterface
70      */
71     public function getConfigStorageSync()
72     {
73         return $this->configStorageSync;
74     }
75
76     public function getModuleHandler()
77     {
78         return $this->moduleHandler;
79     }
80
81     /**
82      * @return \Symfony\Component\EventDispatcher\EventDispatcherInterface
83      */
84     public function getEventDispatcher()
85     {
86         return $this->eventDispatcher;
87     }
88
89     /**
90      * @return \Drupal\Core\Lock\LockBackendInterface
91      */
92     public function getLock()
93     {
94         return $this->lock;
95     }
96
97     /**
98      * @return \Drupal\Core\Config\TypedConfigManagerInterface
99      */
100     public function getConfigTyped()
101     {
102         return $this->configTyped;
103     }
104
105     /**
106      * @return \Drupal\Core\Extension\ModuleInstallerInterface
107      */
108     public function getModuleInstaller()
109     {
110         return $this->moduleInstaller;
111     }
112
113     /**
114      * @return \Drupal\Core\Extension\ThemeHandlerInterface
115      */
116     public function getThemeHandler()
117     {
118         return $this->themeHandler;
119     }
120
121     /**
122      * @return \Drupal\Core\StringTranslation\TranslationInterface
123      */
124     public function getStringTranslation()
125     {
126         return $this->stringTranslation;
127     }
128
129     /**
130      * @param ConfigManagerInterface $configManager
131      * @param StorageInterface $configStorage
132      * @param StorageInterface $configStorageSync
133      */
134     public function __construct(ConfigManagerInterface $configManager, StorageInterface $configStorage, StorageInterface $configStorageSync, ModuleHandlerInterface $moduleHandler, EventDispatcherInterface $eventDispatcher, LockBackendInterface $lock, TypedConfigManagerInterface $configTyped, ModuleInstallerInterface $moduleInstaller, ThemeHandlerInterface $themeHandler, TranslationInterface $stringTranslation)
135     {
136         parent::__construct();
137         $this->configManager = $configManager;
138         $this->configStorage = $configStorage;
139         $this->configStorageSync = $configStorageSync;
140         $this->moduleHandler = $moduleHandler;
141         $this->eventDispatcher = $eventDispatcher;
142         $this->lock = $lock;
143         $this->configTyped = $configTyped;
144         $this->moduleInstaller = $moduleInstaller;
145         $this->themeHandler = $themeHandler;
146         $this->stringTranslation = $stringTranslation;
147     }
148
149     /**
150      * Import config from a config directory.
151      *
152      * @command config:import
153      * @param $label A config directory label (i.e. a key in \$config_directories array in settings.php).
154      * @interact-config-label
155      * @option diff Show preview as a diff.
156      * @option preview Deprecated. Format for displaying proposed changes. Recognized values: list, diff.
157      * @option source An arbitrary directory that holds the configuration files. An alternative to label argument
158      * @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).
159      * @aliases cim,config-import
160      */
161     public function import($label = null, $options = ['preview' => 'list', 'source' => self::REQ, 'partial' => false, 'diff' => false])
162     {
163         // Determine source directory.
164
165         $source_storage_dir = ConfigCommands::getDirectory($label, $options['source']);
166
167         // Prepare the configuration storage for the import.
168         if ($source_storage_dir == Path::canonicalize(\config_get_config_directory(CONFIG_SYNC_DIRECTORY))) {
169             $source_storage = $this->getConfigStorageSync();
170         } else {
171             $source_storage = new FileStorage($source_storage_dir);
172         }
173
174         // Determine $source_storage in partial case.
175         $active_storage = $this->getConfigStorage();
176         if ($options['partial']) {
177             $replacement_storage = new StorageReplaceDataWrapper($active_storage);
178             foreach ($source_storage->listAll() as $name) {
179                 $data = $source_storage->read($name);
180                 $replacement_storage->replaceData($name, $data);
181             }
182             $source_storage = $replacement_storage;
183         }
184
185         $config_manager = $this->getConfigManager();
186         $storage_comparer = new StorageComparer($source_storage, $active_storage, $config_manager);
187
188
189         if (!$storage_comparer->createChangelist()->hasChanges()) {
190             $this->logger()->notice(('There are no changes to import.'));
191             return;
192         }
193
194         if ($options['preview'] == 'list' && !$options['diff']) {
195             $change_list = [];
196             foreach ($storage_comparer->getAllCollectionNames() as $collection) {
197                 $change_list[$collection] = $storage_comparer->getChangelist(null, $collection);
198             }
199             $table = ConfigCommands::configChangesTable($change_list, $this->output());
200             $table->render();
201         } else {
202             $output = ConfigCommands::getDiff($active_storage, $source_storage, $this->output());
203
204             $this->output()->writeln(implode("\n", $output));
205         }
206
207         if ($this->io()->confirm(dt('Import the listed configuration changes?'))) {
208             return drush_op([$this, 'doImport'], $storage_comparer);
209         }
210     }
211
212     // Copied from submitForm() at /core/modules/config/src/Form/ConfigSync.php
213     public function doImport($storage_comparer)
214     {
215         $config_importer = new ConfigImporter(
216             $storage_comparer,
217             $this->getEventDispatcher(),
218             $this->getConfigManager(),
219             $this->getLock(),
220             $this->getConfigTyped(),
221             $this->getModuleHandler(),
222             $this->getModuleInstaller(),
223             $this->getThemeHandler(),
224             $this->getStringTranslation()
225         );
226         if ($config_importer->alreadyImporting()) {
227             $this->logger()->warning('Another request may be synchronizing configuration already.');
228         } else {
229             try {
230                 // This is the contents of \Drupal\Core\Config\ConfigImporter::import.
231                 // Copied here so we can log progress.
232                 if ($config_importer->hasUnprocessedConfigurationChanges()) {
233                     $sync_steps = $config_importer->initialize();
234                     foreach ($sync_steps as $step) {
235                         $context = [];
236                         do {
237                             $config_importer->doSyncStep($step, $context);
238                             if (isset($context['message'])) {
239                                 $this->logger()->notice(str_replace('Synchronizing', 'Synchronized', (string)$context['message']));
240                             }
241                         } while ($context['finished'] < 1);
242                     }
243                 }
244                 if ($config_importer->getErrors()) {
245                     throw new ConfigException('Errors occurred during import');
246                 } else {
247                     $this->logger()->success('The configuration was imported successfully.');
248                 }
249             } catch (ConfigException $e) {
250                 // Return a negative result for UI purposes. We do not differentiate
251                 // between an actual synchronization error and a failed lock, because
252                 // concurrent synchronizations are an edge-case happening only when
253                 // multiple developers or site builders attempt to do it without
254                 // coordinating.
255                 $message = 'The import failed due to the following reasons:' . "\n";
256                 $message .= implode("\n", $config_importer->getErrors());
257
258                 watchdog_exception('config_import', $e);
259                 throw new \Exception($message);
260             }
261         }
262     }
263
264     /**
265      * @hook validate config-import
266      * @param \Consolidation\AnnotatedCommand\CommandData $commandData
267      * @return \Consolidation\AnnotatedCommand\CommandError|null
268      */
269     public function validate(CommandData $commandData)
270     {
271         $msgs = [];
272         if ($commandData->input()->getOption('partial') && !$this->getModuleHandler()->moduleExists('config')) {
273             $msgs[] = 'Enable the config module in order to use the --partial option.';
274         }
275
276         if ($source = $commandData->input()->getOption('source')) {
277             if (!file_exists($source)) {
278                 $msgs[] = 'The source directory does not exist.';
279             }
280             if (!is_dir($source)) {
281                 $msgs[] = 'The source is not a directory.';
282             }
283         }
284
285         if ($msgs) {
286             return new CommandError(implode(' ', $msgs));
287         }
288     }
289 }