Mercurial > hg > cmmr2012-drupal-site
comparison core/modules/config/src/Form/ConfigSync.php @ 0:c75dbcec494b
Initial commit from drush-created site
author | Chris Cannam |
---|---|
date | Thu, 05 Jul 2018 14:24:15 +0000 |
parents | |
children | a9cd425dd02b |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:c75dbcec494b |
---|---|
1 <?php | |
2 | |
3 namespace Drupal\config\Form; | |
4 | |
5 use Drupal\Core\Config\ConfigImporterException; | |
6 use Drupal\Core\Config\ConfigImporter; | |
7 use Drupal\Core\Config\TypedConfigManagerInterface; | |
8 use Drupal\Core\Extension\ModuleHandlerInterface; | |
9 use Drupal\Core\Extension\ModuleInstallerInterface; | |
10 use Drupal\Core\Extension\ThemeHandlerInterface; | |
11 use Drupal\Core\Config\ConfigManagerInterface; | |
12 use Drupal\Core\Form\FormBase; | |
13 use Drupal\Core\Config\StorageInterface; | |
14 use Drupal\Core\Form\FormStateInterface; | |
15 use Drupal\Core\Lock\LockBackendInterface; | |
16 use Drupal\Core\Config\StorageComparer; | |
17 use Drupal\Core\Render\RendererInterface; | |
18 use Drupal\Core\Url; | |
19 use Symfony\Component\EventDispatcher\EventDispatcherInterface; | |
20 use Symfony\Component\DependencyInjection\ContainerInterface; | |
21 | |
22 /** | |
23 * Construct the storage changes in a configuration synchronization form. | |
24 * | |
25 * @internal | |
26 */ | |
27 class ConfigSync extends FormBase { | |
28 | |
29 /** | |
30 * The database lock object. | |
31 * | |
32 * @var \Drupal\Core\Lock\LockBackendInterface | |
33 */ | |
34 protected $lock; | |
35 | |
36 /** | |
37 * The sync configuration object. | |
38 * | |
39 * @var \Drupal\Core\Config\StorageInterface | |
40 */ | |
41 protected $syncStorage; | |
42 | |
43 /** | |
44 * The active configuration object. | |
45 * | |
46 * @var \Drupal\Core\Config\StorageInterface | |
47 */ | |
48 protected $activeStorage; | |
49 | |
50 /** | |
51 * The snapshot configuration object. | |
52 * | |
53 * @var \Drupal\Core\Config\StorageInterface | |
54 */ | |
55 protected $snapshotStorage; | |
56 | |
57 /** | |
58 * Event dispatcher. | |
59 * | |
60 * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface | |
61 */ | |
62 protected $eventDispatcher; | |
63 | |
64 /** | |
65 * The configuration manager. | |
66 * | |
67 * @var \Drupal\Core\Config\ConfigManagerInterface; | |
68 */ | |
69 protected $configManager; | |
70 | |
71 /** | |
72 * The typed config manager. | |
73 * | |
74 * @var \Drupal\Core\Config\TypedConfigManagerInterface | |
75 */ | |
76 protected $typedConfigManager; | |
77 | |
78 /** | |
79 * The module handler. | |
80 * | |
81 * @var \Drupal\Core\Extension\ModuleHandlerInterface | |
82 */ | |
83 protected $moduleHandler; | |
84 | |
85 /** | |
86 * The theme handler. | |
87 * | |
88 * @var \Drupal\Core\Extension\ThemeHandlerInterface | |
89 */ | |
90 protected $themeHandler; | |
91 | |
92 /** | |
93 * The module installer. | |
94 * | |
95 * @var \Drupal\Core\Extension\ModuleInstallerInterface | |
96 */ | |
97 protected $moduleInstaller; | |
98 | |
99 /** | |
100 * The renderer. | |
101 * | |
102 * @var \Drupal\Core\Render\RendererInterface | |
103 */ | |
104 protected $renderer; | |
105 | |
106 /** | |
107 * Constructs the object. | |
108 * | |
109 * @param \Drupal\Core\Config\StorageInterface $sync_storage | |
110 * The source storage. | |
111 * @param \Drupal\Core\Config\StorageInterface $active_storage | |
112 * The target storage. | |
113 * @param \Drupal\Core\Config\StorageInterface $snapshot_storage | |
114 * The snapshot storage. | |
115 * @param \Drupal\Core\Lock\LockBackendInterface $lock | |
116 * The lock object. | |
117 * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher | |
118 * Event dispatcher. | |
119 * @param \Drupal\Core\Config\ConfigManagerInterface $config_manager | |
120 * Configuration manager. | |
121 * @param \Drupal\Core\Config\TypedConfigManagerInterface $typed_config | |
122 * The typed configuration manager. | |
123 * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler | |
124 * The module handler. | |
125 * @param \Drupal\Core\Extension\ModuleInstallerInterface $module_installer | |
126 * The module installer. | |
127 * @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler | |
128 * The theme handler. | |
129 * @param \Drupal\Core\Render\RendererInterface $renderer | |
130 * The renderer. | |
131 */ | |
132 public function __construct(StorageInterface $sync_storage, StorageInterface $active_storage, StorageInterface $snapshot_storage, LockBackendInterface $lock, EventDispatcherInterface $event_dispatcher, ConfigManagerInterface $config_manager, TypedConfigManagerInterface $typed_config, ModuleHandlerInterface $module_handler, ModuleInstallerInterface $module_installer, ThemeHandlerInterface $theme_handler, RendererInterface $renderer) { | |
133 $this->syncStorage = $sync_storage; | |
134 $this->activeStorage = $active_storage; | |
135 $this->snapshotStorage = $snapshot_storage; | |
136 $this->lock = $lock; | |
137 $this->eventDispatcher = $event_dispatcher; | |
138 $this->configManager = $config_manager; | |
139 $this->typedConfigManager = $typed_config; | |
140 $this->moduleHandler = $module_handler; | |
141 $this->moduleInstaller = $module_installer; | |
142 $this->themeHandler = $theme_handler; | |
143 $this->renderer = $renderer; | |
144 } | |
145 | |
146 /** | |
147 * {@inheritdoc} | |
148 */ | |
149 public static function create(ContainerInterface $container) { | |
150 return new static( | |
151 $container->get('config.storage.sync'), | |
152 $container->get('config.storage'), | |
153 $container->get('config.storage.snapshot'), | |
154 $container->get('lock.persistent'), | |
155 $container->get('event_dispatcher'), | |
156 $container->get('config.manager'), | |
157 $container->get('config.typed'), | |
158 $container->get('module_handler'), | |
159 $container->get('module_installer'), | |
160 $container->get('theme_handler'), | |
161 $container->get('renderer') | |
162 ); | |
163 } | |
164 | |
165 /** | |
166 * {@inheritdoc} | |
167 */ | |
168 public function getFormId() { | |
169 return 'config_admin_import_form'; | |
170 } | |
171 | |
172 /** | |
173 * {@inheritdoc} | |
174 */ | |
175 public function buildForm(array $form, FormStateInterface $form_state) { | |
176 $form['actions'] = ['#type' => 'actions']; | |
177 $form['actions']['submit'] = [ | |
178 '#type' => 'submit', | |
179 '#value' => $this->t('Import all'), | |
180 ]; | |
181 $source_list = $this->syncStorage->listAll(); | |
182 $storage_comparer = new StorageComparer($this->syncStorage, $this->activeStorage, $this->configManager); | |
183 if (empty($source_list) || !$storage_comparer->createChangelist()->hasChanges()) { | |
184 $form['no_changes'] = [ | |
185 '#type' => 'table', | |
186 '#header' => [$this->t('Name'), $this->t('Operations')], | |
187 '#rows' => [], | |
188 '#empty' => $this->t('There are no configuration changes to import.'), | |
189 ]; | |
190 $form['actions']['#access'] = FALSE; | |
191 return $form; | |
192 } | |
193 elseif (!$storage_comparer->validateSiteUuid()) { | |
194 drupal_set_message($this->t('The staged configuration cannot be imported, because it originates from a different site than this site. You can only synchronize configuration between cloned instances of this site.'), 'error'); | |
195 $form['actions']['#access'] = FALSE; | |
196 return $form; | |
197 } | |
198 // A list of changes will be displayed, so check if the user should be | |
199 // warned of potential losses to configuration. | |
200 if ($this->snapshotStorage->exists('core.extension')) { | |
201 $snapshot_comparer = new StorageComparer($this->activeStorage, $this->snapshotStorage, $this->configManager); | |
202 if (!$form_state->getUserInput() && $snapshot_comparer->createChangelist()->hasChanges()) { | |
203 $change_list = []; | |
204 foreach ($snapshot_comparer->getAllCollectionNames() as $collection) { | |
205 foreach ($snapshot_comparer->getChangelist(NULL, $collection) as $config_names) { | |
206 if (empty($config_names)) { | |
207 continue; | |
208 } | |
209 foreach ($config_names as $config_name) { | |
210 $change_list[] = $config_name; | |
211 } | |
212 } | |
213 } | |
214 sort($change_list); | |
215 $message = [ | |
216 [ | |
217 '#markup' => $this->t('The following items in your active configuration have changes since the last import that may be lost on the next import.') | |
218 ], | |
219 [ | |
220 '#theme' => 'item_list', | |
221 '#items' => $change_list, | |
222 ] | |
223 ]; | |
224 drupal_set_message($this->renderer->renderPlain($message), 'warning'); | |
225 } | |
226 } | |
227 | |
228 // Store the comparer for use in the submit. | |
229 $form_state->set('storage_comparer', $storage_comparer); | |
230 | |
231 // Add the AJAX library to the form for dialog support. | |
232 $form['#attached']['library'][] = 'core/drupal.dialog.ajax'; | |
233 | |
234 foreach ($storage_comparer->getAllCollectionNames() as $collection) { | |
235 if ($collection != StorageInterface::DEFAULT_COLLECTION) { | |
236 $form[$collection]['collection_heading'] = [ | |
237 '#type' => 'html_tag', | |
238 '#tag' => 'h2', | |
239 '#value' => $this->t('@collection configuration collection', ['@collection' => $collection]), | |
240 ]; | |
241 } | |
242 foreach ($storage_comparer->getChangelist(NULL, $collection) as $config_change_type => $config_names) { | |
243 if (empty($config_names)) { | |
244 continue; | |
245 } | |
246 | |
247 // @todo A table caption would be more appropriate, but does not have the | |
248 // visual importance of a heading. | |
249 $form[$collection][$config_change_type]['heading'] = [ | |
250 '#type' => 'html_tag', | |
251 '#tag' => 'h3', | |
252 ]; | |
253 switch ($config_change_type) { | |
254 case 'create': | |
255 $form[$collection][$config_change_type]['heading']['#value'] = $this->formatPlural(count($config_names), '@count new', '@count new'); | |
256 break; | |
257 | |
258 case 'update': | |
259 $form[$collection][$config_change_type]['heading']['#value'] = $this->formatPlural(count($config_names), '@count changed', '@count changed'); | |
260 break; | |
261 | |
262 case 'delete': | |
263 $form[$collection][$config_change_type]['heading']['#value'] = $this->formatPlural(count($config_names), '@count removed', '@count removed'); | |
264 break; | |
265 | |
266 case 'rename': | |
267 $form[$collection][$config_change_type]['heading']['#value'] = $this->formatPlural(count($config_names), '@count renamed', '@count renamed'); | |
268 break; | |
269 } | |
270 $form[$collection][$config_change_type]['list'] = [ | |
271 '#type' => 'table', | |
272 '#header' => [$this->t('Name'), $this->t('Operations')], | |
273 ]; | |
274 | |
275 foreach ($config_names as $config_name) { | |
276 if ($config_change_type == 'rename') { | |
277 $names = $storage_comparer->extractRenameNames($config_name); | |
278 $route_options = ['source_name' => $names['old_name'], 'target_name' => $names['new_name']]; | |
279 $config_name = $this->t('@source_name to @target_name', ['@source_name' => $names['old_name'], '@target_name' => $names['new_name']]); | |
280 } | |
281 else { | |
282 $route_options = ['source_name' => $config_name]; | |
283 } | |
284 if ($collection != StorageInterface::DEFAULT_COLLECTION) { | |
285 $route_name = 'config.diff_collection'; | |
286 $route_options['collection'] = $collection; | |
287 } | |
288 else { | |
289 $route_name = 'config.diff'; | |
290 } | |
291 $links['view_diff'] = [ | |
292 'title' => $this->t('View differences'), | |
293 'url' => Url::fromRoute($route_name, $route_options), | |
294 'attributes' => [ | |
295 'class' => ['use-ajax'], | |
296 'data-dialog-type' => 'modal', | |
297 'data-dialog-options' => json_encode([ | |
298 'width' => 700 | |
299 ]), | |
300 ], | |
301 ]; | |
302 $form[$collection][$config_change_type]['list']['#rows'][] = [ | |
303 'name' => $config_name, | |
304 'operations' => [ | |
305 'data' => [ | |
306 '#type' => 'operations', | |
307 '#links' => $links, | |
308 ], | |
309 ], | |
310 ]; | |
311 } | |
312 } | |
313 } | |
314 return $form; | |
315 } | |
316 | |
317 /** | |
318 * {@inheritdoc} | |
319 */ | |
320 public function submitForm(array &$form, FormStateInterface $form_state) { | |
321 $config_importer = new ConfigImporter( | |
322 $form_state->get('storage_comparer'), | |
323 $this->eventDispatcher, | |
324 $this->configManager, | |
325 $this->lock, | |
326 $this->typedConfigManager, | |
327 $this->moduleHandler, | |
328 $this->moduleInstaller, | |
329 $this->themeHandler, | |
330 $this->getStringTranslation() | |
331 ); | |
332 if ($config_importer->alreadyImporting()) { | |
333 drupal_set_message($this->t('Another request may be synchronizing configuration already.')); | |
334 } | |
335 else { | |
336 try { | |
337 $sync_steps = $config_importer->initialize(); | |
338 $batch = [ | |
339 'operations' => [], | |
340 'finished' => [get_class($this), 'finishBatch'], | |
341 'title' => t('Synchronizing configuration'), | |
342 'init_message' => t('Starting configuration synchronization.'), | |
343 'progress_message' => t('Completed step @current of @total.'), | |
344 'error_message' => t('Configuration synchronization has encountered an error.'), | |
345 ]; | |
346 foreach ($sync_steps as $sync_step) { | |
347 $batch['operations'][] = [[get_class($this), 'processBatch'], [$config_importer, $sync_step]]; | |
348 } | |
349 | |
350 batch_set($batch); | |
351 } | |
352 catch (ConfigImporterException $e) { | |
353 // There are validation errors. | |
354 drupal_set_message($this->t('The configuration cannot be imported because it failed validation for the following reasons:'), 'error'); | |
355 foreach ($config_importer->getErrors() as $message) { | |
356 drupal_set_message($message, 'error'); | |
357 } | |
358 } | |
359 } | |
360 } | |
361 | |
362 /** | |
363 * Processes the config import batch and persists the importer. | |
364 * | |
365 * @param \Drupal\Core\Config\ConfigImporter $config_importer | |
366 * The batch config importer object to persist. | |
367 * @param string $sync_step | |
368 * The synchronization step to do. | |
369 * @param array $context | |
370 * The batch context. | |
371 */ | |
372 public static function processBatch(ConfigImporter $config_importer, $sync_step, &$context) { | |
373 if (!isset($context['sandbox']['config_importer'])) { | |
374 $context['sandbox']['config_importer'] = $config_importer; | |
375 } | |
376 | |
377 $config_importer = $context['sandbox']['config_importer']; | |
378 $config_importer->doSyncStep($sync_step, $context); | |
379 if ($errors = $config_importer->getErrors()) { | |
380 if (!isset($context['results']['errors'])) { | |
381 $context['results']['errors'] = []; | |
382 } | |
383 $context['results']['errors'] += $errors; | |
384 } | |
385 } | |
386 | |
387 /** | |
388 * Finish batch. | |
389 * | |
390 * This function is a static function to avoid serializing the ConfigSync | |
391 * object unnecessarily. | |
392 */ | |
393 public static function finishBatch($success, $results, $operations) { | |
394 if ($success) { | |
395 if (!empty($results['errors'])) { | |
396 foreach ($results['errors'] as $error) { | |
397 drupal_set_message($error, 'error'); | |
398 \Drupal::logger('config_sync')->error($error); | |
399 } | |
400 drupal_set_message(\Drupal::translation()->translate('The configuration was imported with errors.'), 'warning'); | |
401 } | |
402 else { | |
403 drupal_set_message(\Drupal::translation()->translate('The configuration was imported successfully.')); | |
404 } | |
405 } | |
406 else { | |
407 // An error occurred. | |
408 // $operations contains the operations that remained unprocessed. | |
409 $error_operation = reset($operations); | |
410 $message = \Drupal::translation()->translate('An error occurred while processing %error_operation with arguments: @arguments', ['%error_operation' => $error_operation[0], '@arguments' => print_r($error_operation[1], TRUE)]); | |
411 drupal_set_message($message, 'error'); | |
412 } | |
413 } | |
414 | |
415 } |