Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
| Total | |
0.00% |
0 / 1 |
|
0.00% |
0 / 7 |
CRAP | |
0.00% |
0 / 223 |
| ConfigSync | |
0.00% |
0 / 1 |
|
0.00% |
0 / 7 |
1482 | |
0.00% |
0 / 223 |
| __construct | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 12 |
|||
| create | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 14 |
|||
| getFormId | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
| buildForm | |
0.00% |
0 / 1 |
506 | |
0.00% |
0 / 126 |
|||
| submitForm | |
0.00% |
0 / 1 |
30 | |
0.00% |
0 / 39 |
|||
| processBatch | |
0.00% |
0 / 1 |
20 | |
0.00% |
0 / 12 |
|||
| finishBatch | |
0.00% |
0 / 1 |
20 | |
0.00% |
0 / 18 |
|||
| <?php | |
| /** | |
| * @file | |
| * Contains \Drupal\config\Form\ConfigSync. | |
| */ | |
| namespace Drupal\config\Form; | |
| use Drupal\Core\Config\ConfigImporterException; | |
| use Drupal\Core\Config\ConfigImporter; | |
| use Drupal\Core\Config\TypedConfigManagerInterface; | |
| use Drupal\Core\Extension\ModuleHandlerInterface; | |
| use Drupal\Core\Extension\ModuleInstallerInterface; | |
| use Drupal\Core\Extension\ThemeHandlerInterface; | |
| use Drupal\Core\Config\ConfigManagerInterface; | |
| use Drupal\Core\Form\FormBase; | |
| use Drupal\Core\Config\StorageInterface; | |
| use Drupal\Core\Form\FormStateInterface; | |
| use Drupal\Core\Lock\LockBackendInterface; | |
| use Drupal\Core\Config\StorageComparer; | |
| use Drupal\Core\Render\RendererInterface; | |
| use Drupal\Core\Url; | |
| use Symfony\Component\EventDispatcher\EventDispatcherInterface; | |
| use Symfony\Component\DependencyInjection\ContainerInterface; | |
| /** | |
| * Construct the storage changes in a configuration synchronization form. | |
| */ | |
| class ConfigSync extends FormBase { | |
| /** | |
| * The database lock object. | |
| * | |
| * @var \Drupal\Core\Lock\LockBackendInterface | |
| */ | |
| protected $lock; | |
| /** | |
| * The sync configuration object. | |
| * | |
| * @var \Drupal\Core\Config\StorageInterface | |
| */ | |
| protected $syncStorage; | |
| /** | |
| * The active configuration object. | |
| * | |
| * @var \Drupal\Core\Config\StorageInterface | |
| */ | |
| protected $activeStorage; | |
| /** | |
| * The snapshot configuration object. | |
| * | |
| * @var \Drupal\Core\Config\StorageInterface | |
| */ | |
| protected $snapshotStorage; | |
| /** | |
| * Event dispatcher. | |
| * | |
| * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface | |
| */ | |
| protected $eventDispatcher; | |
| /** | |
| * The configuration manager. | |
| * | |
| * @var \Drupal\Core\Config\ConfigManagerInterface; | |
| */ | |
| protected $configManager; | |
| /** | |
| * The typed config manager. | |
| * | |
| * @var \Drupal\Core\Config\TypedConfigManagerInterface | |
| */ | |
| protected $typedConfigManager; | |
| /** | |
| * The module handler. | |
| * | |
| * @var \Drupal\Core\Extension\ModuleHandlerInterface | |
| */ | |
| protected $moduleHandler; | |
| /** | |
| * The theme handler. | |
| * | |
| * @var \Drupal\Core\Extension\ThemeHandlerInterface | |
| */ | |
| protected $themeHandler; | |
| /** | |
| * The module installer. | |
| * | |
| * @var \Drupal\Core\Extension\ModuleInstallerInterface | |
| */ | |
| protected $moduleInstaller; | |
| /** | |
| * The renderer. | |
| * | |
| * @var \Drupal\Core\Render\RendererInterface | |
| */ | |
| protected $renderer; | |
| /** | |
| * Constructs the object. | |
| * | |
| * @param \Drupal\Core\Config\StorageInterface $sync_storage | |
| * The source storage. | |
| * @param \Drupal\Core\Config\StorageInterface $active_storage | |
| * The target storage. | |
| * @param \Drupal\Core\Config\StorageInterface $snapshot_storage | |
| * The snapshot storage. | |
| * @param \Drupal\Core\Lock\LockBackendInterface $lock | |
| * The lock object. | |
| * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher | |
| * Event dispatcher. | |
| * @param \Drupal\Core\Config\ConfigManagerInterface $config_manager | |
| * Configuration manager. | |
| * @param \Drupal\Core\Config\TypedConfigManagerInterface $typed_config | |
| * The typed configuration manager. | |
| * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler | |
| * The module handler. | |
| * @param \Drupal\Core\Extension\ModuleInstallerInterface $module_installer | |
| * The module installer. | |
| * @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler | |
| * The theme handler. | |
| * @param \Drupal\Core\Render\RendererInterface | |
| * The renderer. | |
| */ | |
| 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) { | |
| $this->syncStorage = $sync_storage; | |
| $this->activeStorage = $active_storage; | |
| $this->snapshotStorage = $snapshot_storage; | |
| $this->lock = $lock; | |
| $this->eventDispatcher = $event_dispatcher; | |
| $this->configManager = $config_manager; | |
| $this->typedConfigManager = $typed_config; | |
| $this->moduleHandler = $module_handler; | |
| $this->moduleInstaller = $module_installer; | |
| $this->themeHandler = $theme_handler; | |
| $this->renderer = $renderer; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public static function create(ContainerInterface $container) { | |
| return new static( | |
| $container->get('config.storage.sync'), | |
| $container->get('config.storage'), | |
| $container->get('config.storage.snapshot'), | |
| $container->get('lock.persistent'), | |
| $container->get('event_dispatcher'), | |
| $container->get('config.manager'), | |
| $container->get('config.typed'), | |
| $container->get('module_handler'), | |
| $container->get('module_installer'), | |
| $container->get('theme_handler'), | |
| $container->get('renderer') | |
| ); | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function getFormId() { | |
| return 'config_admin_import_form'; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function buildForm(array $form, FormStateInterface $form_state) { | |
| $form['actions'] = array('#type' => 'actions'); | |
| $form['actions']['submit'] = array( | |
| '#type' => 'submit', | |
| '#value' => $this->t('Import all'), | |
| ); | |
| $source_list = $this->syncStorage->listAll(); | |
| $storage_comparer = new StorageComparer($this->syncStorage, $this->activeStorage, $this->configManager); | |
| if (empty($source_list) || !$storage_comparer->createChangelist()->hasChanges()) { | |
| $form['no_changes'] = array( | |
| '#type' => 'table', | |
| '#header' => array('Name', 'Operations'), | |
| '#rows' => array(), | |
| '#empty' => $this->t('There are no configuration changes to import.'), | |
| ); | |
| $form['actions']['#access'] = FALSE; | |
| return $form; | |
| } | |
| elseif (!$storage_comparer->validateSiteUuid()) { | |
| 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'); | |
| $form['actions']['#access'] = FALSE; | |
| return $form; | |
| } | |
| // A list of changes will be displayed, so check if the user should be | |
| // warned of potential losses to configuration. | |
| if ($this->snapshotStorage->exists('core.extension')) { | |
| $snapshot_comparer = new StorageComparer($this->activeStorage, $this->snapshotStorage, $this->configManager); | |
| if (!$form_state->getUserInput() && $snapshot_comparer->createChangelist()->hasChanges()) { | |
| $change_list = array(); | |
| foreach ($snapshot_comparer->getAllCollectionNames() as $collection) { | |
| foreach ($snapshot_comparer->getChangelist(NULL, $collection) as $config_names) { | |
| if (empty($config_names)) { | |
| continue; | |
| } | |
| foreach ($config_names as $config_name) { | |
| $change_list[] = $config_name; | |
| } | |
| } | |
| } | |
| sort($change_list); | |
| $message = [ | |
| [ | |
| '#markup' => $this->t('The following items in your active configuration have changes since the last import that may be lost on the next import.') | |
| ], | |
| [ | |
| '#theme' => 'item_list', | |
| '#items' => $change_list, | |
| ] | |
| ]; | |
| drupal_set_message($this->renderer->renderPlain($message), 'warning'); | |
| } | |
| } | |
| // Store the comparer for use in the submit. | |
| $form_state->set('storage_comparer', $storage_comparer); | |
| // Add the AJAX library to the form for dialog support. | |
| $form['#attached']['library'][] = 'core/drupal.ajax'; | |
| foreach ($storage_comparer->getAllCollectionNames() as $collection) { | |
| if ($collection != StorageInterface::DEFAULT_COLLECTION) { | |
| $form[$collection]['collection_heading'] = array( | |
| '#type' => 'html_tag', | |
| '#tag' => 'h2', | |
| '#value' => $this->t('@collection configuration collection', array('@collection' => $collection)), | |
| ); | |
| } | |
| foreach ($storage_comparer->getChangelist(NULL, $collection) as $config_change_type => $config_names) { | |
| if (empty($config_names)) { | |
| continue; | |
| } | |
| // @todo A table caption would be more appropriate, but does not have the | |
| // visual importance of a heading. | |
| $form[$collection][$config_change_type]['heading'] = array( | |
| '#type' => 'html_tag', | |
| '#tag' => 'h3', | |
| ); | |
| switch ($config_change_type) { | |
| case 'create': | |
| $form[$collection][$config_change_type]['heading']['#value'] = $this->formatPlural(count($config_names), '@count new', '@count new'); | |
| break; | |
| case 'update': | |
| $form[$collection][$config_change_type]['heading']['#value'] = $this->formatPlural(count($config_names), '@count changed', '@count changed'); | |
| break; | |
| case 'delete': | |
| $form[$collection][$config_change_type]['heading']['#value'] = $this->formatPlural(count($config_names), '@count removed', '@count removed'); | |
| break; | |
| case 'rename': | |
| $form[$collection][$config_change_type]['heading']['#value'] = $this->formatPlural(count($config_names), '@count renamed', '@count renamed'); | |
| break; | |
| } | |
| $form[$collection][$config_change_type]['list'] = array( | |
| '#type' => 'table', | |
| '#header' => array('Name', 'Operations'), | |
| ); | |
| foreach ($config_names as $config_name) { | |
| if ($config_change_type == 'rename') { | |
| $names = $storage_comparer->extractRenameNames($config_name); | |
| $route_options = array('source_name' => $names['old_name'], 'target_name' => $names['new_name']); | |
| $config_name = $this->t('@source_name to @target_name', array('@source_name' => $names['old_name'], '@target_name' => $names['new_name'])); | |
| } | |
| else { | |
| $route_options = array('source_name' => $config_name); | |
| } | |
| if ($collection != StorageInterface::DEFAULT_COLLECTION) { | |
| $route_name = 'config.diff_collection'; | |
| $route_options['collection'] = $collection; | |
| } | |
| else { | |
| $route_name = 'config.diff'; | |
| } | |
| $links['view_diff'] = array( | |
| 'title' => $this->t('View differences'), | |
| 'url' => Url::fromRoute($route_name, $route_options), | |
| 'attributes' => array( | |
| 'class' => array('use-ajax'), | |
| 'data-dialog-type' => 'modal', | |
| 'data-dialog-options' => json_encode(array( | |
| 'width' => 700 | |
| )), | |
| ), | |
| ); | |
| $form[$collection][$config_change_type]['list']['#rows'][] = array( | |
| 'name' => $config_name, | |
| 'operations' => array( | |
| 'data' => array( | |
| '#type' => 'operations', | |
| '#links' => $links, | |
| ), | |
| ), | |
| ); | |
| } | |
| } | |
| } | |
| return $form; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function submitForm(array &$form, FormStateInterface $form_state) { | |
| $config_importer = new ConfigImporter( | |
| $form_state->get('storage_comparer'), | |
| $this->eventDispatcher, | |
| $this->configManager, | |
| $this->lock, | |
| $this->typedConfigManager, | |
| $this->moduleHandler, | |
| $this->moduleInstaller, | |
| $this->themeHandler, | |
| $this->getStringTranslation() | |
| ); | |
| if ($config_importer->alreadyImporting()) { | |
| drupal_set_message($this->t('Another request may be synchronizing configuration already.')); | |
| } | |
| else{ | |
| try { | |
| $sync_steps = $config_importer->initialize(); | |
| $batch = array( | |
| 'operations' => array(), | |
| 'finished' => array(get_class($this), 'finishBatch'), | |
| 'title' => t('Synchronizing configuration'), | |
| 'init_message' => t('Starting configuration synchronization.'), | |
| 'progress_message' => t('Completed @current step of @total.'), | |
| 'error_message' => t('Configuration synchronization has encountered an error.'), | |
| 'file' => drupal_get_path('module', 'config') . '/config.admin.inc', | |
| ); | |
| foreach ($sync_steps as $sync_step) { | |
| $batch['operations'][] = array(array(get_class($this), 'processBatch'), array($config_importer, $sync_step)); | |
| } | |
| batch_set($batch); | |
| } | |
| catch (ConfigImporterException $e) { | |
| // There are validation errors. | |
| drupal_set_message($this->t('The configuration cannot be imported because it failed validation for the following reasons:'), 'error'); | |
| foreach ($config_importer->getErrors() as $message) { | |
| drupal_set_message($message, 'error'); | |
| } | |
| } | |
| } | |
| } | |
| /** | |
| * Processes the config import batch and persists the importer. | |
| * | |
| * @param \Drupal\Core\Config\ConfigImporter $config_importer | |
| * The batch config importer object to persist. | |
| * @param string $sync_step | |
| * The synchronization step to do. | |
| * @param array $context | |
| * The batch context. | |
| */ | |
| public static function processBatch(ConfigImporter $config_importer, $sync_step, &$context) { | |
| if (!isset($context['sandbox']['config_importer'])) { | |
| $context['sandbox']['config_importer'] = $config_importer; | |
| } | |
| $config_importer = $context['sandbox']['config_importer']; | |
| $config_importer->doSyncStep($sync_step, $context); | |
| if ($errors = $config_importer->getErrors()) { | |
| if (!isset($context['results']['errors'])) { | |
| $context['results']['errors'] = array(); | |
| } | |
| $context['results']['errors'] += $errors; | |
| } | |
| } | |
| /** | |
| * Finish batch. | |
| * | |
| * This function is a static function to avoid serializing the ConfigSync | |
| * object unnecessarily. | |
| */ | |
| public static function finishBatch($success, $results, $operations) { | |
| if ($success) { | |
| if (!empty($results['errors'])) { | |
| foreach ($results['errors'] as $error) { | |
| drupal_set_message($error, 'error'); | |
| \Drupal::logger('config_sync')->error($error); | |
| } | |
| drupal_set_message(\Drupal::translation()->translate('The configuration was imported with errors.'), 'warning'); | |
| } | |
| else { | |
| drupal_set_message(\Drupal::translation()->translate('The configuration was imported successfully.')); | |
| } | |
| } | |
| else { | |
| // An error occurred. | |
| // $operations contains the operations that remained unprocessed. | |
| $error_operation = reset($operations); | |
| $message = \Drupal::translation()->translate('An error occurred while processing %error_operation with arguments: @arguments', array('%error_operation' => $error_operation[0], '@arguments' => print_r($error_operation[1], TRUE))); | |
| drupal_set_message($message, 'error'); | |
| } | |
| } | |
| } |