Chris@0: syncStorage = $sync_storage; Chris@0: $this->activeStorage = $active_storage; Chris@0: $this->snapshotStorage = $snapshot_storage; Chris@0: $this->lock = $lock; Chris@0: $this->eventDispatcher = $event_dispatcher; Chris@0: $this->configManager = $config_manager; Chris@0: $this->typedConfigManager = $typed_config; Chris@0: $this->moduleHandler = $module_handler; Chris@0: $this->moduleInstaller = $module_installer; Chris@0: $this->themeHandler = $theme_handler; Chris@0: $this->renderer = $renderer; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public static function create(ContainerInterface $container) { Chris@0: return new static( Chris@0: $container->get('config.storage.sync'), Chris@0: $container->get('config.storage'), Chris@0: $container->get('config.storage.snapshot'), Chris@0: $container->get('lock.persistent'), Chris@0: $container->get('event_dispatcher'), Chris@0: $container->get('config.manager'), Chris@0: $container->get('config.typed'), Chris@0: $container->get('module_handler'), Chris@0: $container->get('module_installer'), Chris@0: $container->get('theme_handler'), Chris@0: $container->get('renderer') Chris@0: ); Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function getFormId() { Chris@0: return 'config_admin_import_form'; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function buildForm(array $form, FormStateInterface $form_state) { Chris@0: $form['actions'] = ['#type' => 'actions']; Chris@0: $form['actions']['submit'] = [ Chris@0: '#type' => 'submit', Chris@0: '#value' => $this->t('Import all'), Chris@0: ]; Chris@0: $source_list = $this->syncStorage->listAll(); Chris@18: $storage_comparer = new StorageComparer($this->syncStorage, $this->activeStorage); Chris@0: if (empty($source_list) || !$storage_comparer->createChangelist()->hasChanges()) { Chris@0: $form['no_changes'] = [ Chris@0: '#type' => 'table', Chris@0: '#header' => [$this->t('Name'), $this->t('Operations')], Chris@0: '#rows' => [], Chris@0: '#empty' => $this->t('There are no configuration changes to import.'), Chris@0: ]; Chris@0: $form['actions']['#access'] = FALSE; Chris@0: return $form; Chris@0: } Chris@0: elseif (!$storage_comparer->validateSiteUuid()) { Chris@17: $this->messenger()->addError($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.')); Chris@0: $form['actions']['#access'] = FALSE; Chris@0: return $form; Chris@0: } Chris@0: // A list of changes will be displayed, so check if the user should be Chris@0: // warned of potential losses to configuration. Chris@0: if ($this->snapshotStorage->exists('core.extension')) { Chris@18: $snapshot_comparer = new StorageComparer($this->activeStorage, $this->snapshotStorage); Chris@0: if (!$form_state->getUserInput() && $snapshot_comparer->createChangelist()->hasChanges()) { Chris@0: $change_list = []; Chris@0: foreach ($snapshot_comparer->getAllCollectionNames() as $collection) { Chris@0: foreach ($snapshot_comparer->getChangelist(NULL, $collection) as $config_names) { Chris@0: if (empty($config_names)) { Chris@0: continue; Chris@0: } Chris@0: foreach ($config_names as $config_name) { Chris@0: $change_list[] = $config_name; Chris@0: } Chris@0: } Chris@0: } Chris@0: sort($change_list); Chris@0: $message = [ Chris@0: [ Chris@17: '#markup' => $this->t('The following items in your active configuration have changes since the last import that may be lost on the next import.'), Chris@0: ], Chris@0: [ Chris@0: '#theme' => 'item_list', Chris@0: '#items' => $change_list, Chris@17: ], Chris@0: ]; Chris@17: $this->messenger()->addWarning($this->renderer->renderPlain($message)); Chris@0: } Chris@0: } Chris@0: Chris@0: // Store the comparer for use in the submit. Chris@0: $form_state->set('storage_comparer', $storage_comparer); Chris@0: Chris@0: // Add the AJAX library to the form for dialog support. Chris@0: $form['#attached']['library'][] = 'core/drupal.dialog.ajax'; Chris@0: Chris@0: foreach ($storage_comparer->getAllCollectionNames() as $collection) { Chris@0: if ($collection != StorageInterface::DEFAULT_COLLECTION) { Chris@0: $form[$collection]['collection_heading'] = [ Chris@0: '#type' => 'html_tag', Chris@0: '#tag' => 'h2', Chris@0: '#value' => $this->t('@collection configuration collection', ['@collection' => $collection]), Chris@0: ]; Chris@0: } Chris@0: foreach ($storage_comparer->getChangelist(NULL, $collection) as $config_change_type => $config_names) { Chris@0: if (empty($config_names)) { Chris@0: continue; Chris@0: } Chris@0: Chris@0: // @todo A table caption would be more appropriate, but does not have the Chris@0: // visual importance of a heading. Chris@0: $form[$collection][$config_change_type]['heading'] = [ Chris@0: '#type' => 'html_tag', Chris@0: '#tag' => 'h3', Chris@0: ]; Chris@0: switch ($config_change_type) { Chris@0: case 'create': Chris@0: $form[$collection][$config_change_type]['heading']['#value'] = $this->formatPlural(count($config_names), '@count new', '@count new'); Chris@0: break; Chris@0: Chris@0: case 'update': Chris@0: $form[$collection][$config_change_type]['heading']['#value'] = $this->formatPlural(count($config_names), '@count changed', '@count changed'); Chris@0: break; Chris@0: Chris@0: case 'delete': Chris@0: $form[$collection][$config_change_type]['heading']['#value'] = $this->formatPlural(count($config_names), '@count removed', '@count removed'); Chris@0: break; Chris@0: Chris@0: case 'rename': Chris@0: $form[$collection][$config_change_type]['heading']['#value'] = $this->formatPlural(count($config_names), '@count renamed', '@count renamed'); Chris@0: break; Chris@0: } Chris@0: $form[$collection][$config_change_type]['list'] = [ Chris@0: '#type' => 'table', Chris@0: '#header' => [$this->t('Name'), $this->t('Operations')], Chris@0: ]; Chris@0: Chris@0: foreach ($config_names as $config_name) { Chris@0: if ($config_change_type == 'rename') { Chris@0: $names = $storage_comparer->extractRenameNames($config_name); Chris@0: $route_options = ['source_name' => $names['old_name'], 'target_name' => $names['new_name']]; Chris@0: $config_name = $this->t('@source_name to @target_name', ['@source_name' => $names['old_name'], '@target_name' => $names['new_name']]); Chris@0: } Chris@0: else { Chris@0: $route_options = ['source_name' => $config_name]; Chris@0: } Chris@0: if ($collection != StorageInterface::DEFAULT_COLLECTION) { Chris@0: $route_name = 'config.diff_collection'; Chris@0: $route_options['collection'] = $collection; Chris@0: } Chris@0: else { Chris@0: $route_name = 'config.diff'; Chris@0: } Chris@0: $links['view_diff'] = [ Chris@0: 'title' => $this->t('View differences'), Chris@0: 'url' => Url::fromRoute($route_name, $route_options), Chris@0: 'attributes' => [ Chris@0: 'class' => ['use-ajax'], Chris@0: 'data-dialog-type' => 'modal', Chris@0: 'data-dialog-options' => json_encode([ Chris@17: 'width' => 700, Chris@0: ]), Chris@0: ], Chris@0: ]; Chris@0: $form[$collection][$config_change_type]['list']['#rows'][] = [ Chris@0: 'name' => $config_name, Chris@0: 'operations' => [ Chris@0: 'data' => [ Chris@0: '#type' => 'operations', Chris@0: '#links' => $links, Chris@0: ], Chris@0: ], Chris@0: ]; Chris@0: } Chris@0: } Chris@0: } Chris@0: return $form; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function submitForm(array &$form, FormStateInterface $form_state) { Chris@0: $config_importer = new ConfigImporter( Chris@0: $form_state->get('storage_comparer'), Chris@0: $this->eventDispatcher, Chris@0: $this->configManager, Chris@0: $this->lock, Chris@0: $this->typedConfigManager, Chris@0: $this->moduleHandler, Chris@0: $this->moduleInstaller, Chris@0: $this->themeHandler, Chris@0: $this->getStringTranslation() Chris@0: ); Chris@0: if ($config_importer->alreadyImporting()) { Chris@17: $this->messenger()->addStatus($this->t('Another request may be synchronizing configuration already.')); Chris@0: } Chris@0: else { Chris@0: try { Chris@0: $sync_steps = $config_importer->initialize(); Chris@0: $batch = [ Chris@0: 'operations' => [], Chris@17: 'finished' => [ConfigImporterBatch::class, 'finish'], Chris@0: 'title' => t('Synchronizing configuration'), Chris@0: 'init_message' => t('Starting configuration synchronization.'), Chris@0: 'progress_message' => t('Completed step @current of @total.'), Chris@0: 'error_message' => t('Configuration synchronization has encountered an error.'), Chris@0: ]; Chris@0: foreach ($sync_steps as $sync_step) { Chris@17: $batch['operations'][] = [[ConfigImporterBatch::class, 'process'], [$config_importer, $sync_step]]; Chris@0: } Chris@0: Chris@0: batch_set($batch); Chris@0: } Chris@0: catch (ConfigImporterException $e) { Chris@0: // There are validation errors. Chris@17: $this->messenger()->addError($this->t('The configuration cannot be imported because it failed validation for the following reasons:')); Chris@0: foreach ($config_importer->getErrors() as $message) { Chris@17: $this->messenger()->addError($message); Chris@0: } Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * Processes the config import batch and persists the importer. Chris@0: * Chris@0: * @param \Drupal\Core\Config\ConfigImporter $config_importer Chris@0: * The batch config importer object to persist. Chris@0: * @param string $sync_step Chris@0: * The synchronization step to do. Chris@0: * @param array $context Chris@0: * The batch context. Chris@17: * Chris@17: * @deprecated in Drupal 8.6.0 and will be removed before 9.0.0. Use Chris@17: * \Drupal\Core\Config\Importer\ConfigImporterBatch::process() instead. Chris@17: * Chris@17: * @see https://www.drupal.org/node/2897299 Chris@0: */ Chris@0: public static function processBatch(ConfigImporter $config_importer, $sync_step, &$context) { Chris@17: @trigger_error('\Drupal\config\Form\ConfigSync::processBatch() deprecated in Drupal 8.6.0 and will be removed before 9.0.0. Use \Drupal\Core\Config\Importer\ConfigImporterBatch::process() instead. See https://www.drupal.org/node/2897299'); Chris@17: ConfigImporterBatch::process($config_importer, $sync_step, $context); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Finish batch. Chris@0: * Chris@0: * This function is a static function to avoid serializing the ConfigSync Chris@0: * object unnecessarily. Chris@17: * Chris@17: * @deprecated in Drupal 8.6.0 and will be removed before 9.0.0. Use Chris@17: * \Drupal\Core\Config\Importer\ConfigImporterBatch::finish() instead. Chris@17: * Chris@17: * @see https://www.drupal.org/node/2897299 Chris@0: */ Chris@0: public static function finishBatch($success, $results, $operations) { Chris@17: @trigger_error('\Drupal\config\Form\ConfigSync::finishBatch() deprecated in Drupal 8.6.0 and will be removed before 9.0.0. Use \Drupal\Core\Config\Importer\ConfigImporterBatch::finish() instead. See https://www.drupal.org/node/2897299'); Chris@17: ConfigImporterBatch::finish($success, $results, $operations); Chris@0: } Chris@0: Chris@0: }