diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc index ceb9982..d48c0f0 100644 --- a/core/includes/install.core.inc +++ b/core/includes/install.core.inc @@ -6,6 +6,10 @@ */ use Drupal\Component\Utility\UrlHelper; +use Drupal\Core\Config\ConfigImporter; +use Drupal\Core\Config\ConfigImporterException; +use Drupal\Core\Config\FileStorage; +use Drupal\Core\Config\StorageComparer; use Drupal\Core\DrupalKernel; use Drupal\Core\Database\Database; use Drupal\Core\Database\DatabaseExceptionWrapper; @@ -787,6 +791,19 @@ function install_tasks($install_state) { // Now add any tasks defined by the installation profile. if (!empty($install_state['parameters']['profile'])) { + if ($install_state['profile_info']['config_install']) { + $key = array_search('install_profile_modules', array_keys($tasks)); + unset($tasks['install_profile_modules']); + unset($tasks['install_profile_themes']); + unset($tasks['install_install_profile']); + $config_tasks = [ + 'install_config_import_batch' => [ + 'display_name' => t('Install configuration'), + 'type' => 'batch', + ], + ]; + $tasks = array_slice($tasks, 0, $key, TRUE) + $config_tasks + array_slice($tasks, $key, NULL, TRUE); + } // Load the profile install file, because it is not always loaded when // hook_install_tasks() is invoked (e.g. batch processing). $profile = $install_state['parameters']['profile']; @@ -2187,3 +2204,132 @@ function install_write_profile($install_state) { drupal_rewrite_settings($settings); } } + +/** + * Creates a batch for the config importer to process. + * + * @see config_installer_install_tasks_alter() + */ +function install_config_import_batch() { + global $config_directories; + // We need to manually trigger the installation of core-provided entity types, + // as those will not be handled by the module installer. + // @see install_profile_modules() + install_core_entity_type_definitions(); + + // Create a source storage that reads from sync. + $sync = new FileStorage($config_directories[CONFIG_SYNC_DIRECTORY]); + // Match up the site uuids, the install_base_system install task will have + // installed the system module and created a new UUID. + $system_site = $sync->read('system.site'); + \Drupal::configFactory()->getEditable('system.site')->set('uuid', $system_site['uuid'])->save(); + + // Create the storage comparer and the config importer. + $config_manager = \Drupal::service('config.manager'); + $storage_comparer = new StorageComparer($sync, \Drupal::service('config.storage'), $config_manager); + $storage_comparer->createChangelist(); + $config_importer = new ConfigImporter( + $storage_comparer, + \Drupal::service('event_dispatcher'), + $config_manager, + \Drupal::service('lock.persistent'), + \Drupal::service('config.typed'), + \Drupal::service('module_handler'), + \Drupal::service('module_installer'), + \Drupal::service('theme_handler'), + \Drupal::service('string_translation') + ); + + try { + $sync_steps = $config_importer->initialize(); + + // Implementing hook_config_import_steps_alter() in this file does not work + // if using the 'drush site-install' command. Add the step to fix the import + // profile before the last step of the configuration import. + // $last = array_pop($sync_steps); + // $sync_steps[] = 'config_installer_install_uninstalled_profile_dependencies'; + // $sync_steps[] = 'config_installer_config_import_profile'; + // $sync_steps[] = $last; + + $batch = [ + 'operations' => [], + 'finished' => 'install_config_import_batch_finish', + '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'][] = ['install_config_import_batch_process', [$config_importer, $sync_step]]; + } + + return $batch; + } + catch (ConfigImporterException $e) { + // There are validation errors. + drupal_set_message(\Drupal::translation()->translate('The configuration synchronization failed validation.')); + 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 synchronisation step to do. + * @param $context + * The batch context. + * + * @see install_config_import_batch() + */ +function install_config_import_batch_process(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'] = []; + } + $context['results']['errors'] += $errors; + } +} + +/** + * Finish config importer batch. + * + * @see install_config_import_batch() + */ +function install_config_import_batch_finish($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 { + // Configuration sync needs a complete cache flush. + drupal_flush_all_caches(); + } + } + 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', [ + '%error_operation' => $error_operation[0], + '@arguments' => print_r($error_operation[1], TRUE) + ]); + drupal_set_message($message, 'error'); + } +} diff --git a/core/includes/install.inc b/core/includes/install.inc index 3a9c2bc..f17694f 100644 --- a/core/includes/install.inc +++ b/core/includes/install.inc @@ -482,12 +482,20 @@ function _drupal_rewrite_settings_dump_one(\stdClass $variable, $prefix = '', $s * @see update_prepare_d8_bootstrap() */ function drupal_install_config_directories() { - global $config_directories; + global $config_directories, $install_state; // Add a randomized config directory name to settings.php, unless it was // manually defined in the existing already. if (empty($config_directories[CONFIG_SYNC_DIRECTORY])) { - $config_directories[CONFIG_SYNC_DIRECTORY] = \Drupal::service('site.path') . '/files/config_' . Crypt::randomBytesBase64(55) . '/sync'; + // @todo Should the info key be config_install or config_sync? The directory + // can't be config/install because that would clash with module install. + if ($install_state['profile_info']['config_install']) { + $profile = $install_state['parameters']['profile']; + $config_directories[CONFIG_SYNC_DIRECTORY] = $install_state['profiles'][$profile]->getPath() . '/config/sync'; + } + else { + $config_directories[CONFIG_SYNC_DIRECTORY] = \Drupal::service('site.path') . '/files/config_' . Crypt::randomBytesBase64(55) . '/sync'; + } $settings['config_directories'][CONFIG_SYNC_DIRECTORY] = (object) [ 'value' => $config_directories[CONFIG_SYNC_DIRECTORY], 'required' => TRUE, @@ -1062,6 +1070,7 @@ function install_profile_info($profile, $langcode = 'en') { 'version' => NULL, 'hidden' => FALSE, 'php' => DRUPAL_MINIMUM_PHP, + 'config_install' => FALSE, ); $profile_file = drupal_get_path('profile', $profile) . "/$profile.info.yml"; $info = \Drupal::service('info_parser')->parse($profile_file); diff --git a/core/lib/Drupal/Core/Installer/Form/SiteConfigureForm.php b/core/lib/Drupal/Core/Installer/Form/SiteConfigureForm.php index 6d5fe65..468a53b 100644 --- a/core/lib/Drupal/Core/Installer/Form/SiteConfigureForm.php +++ b/core/lib/Drupal/Core/Installer/Form/SiteConfigureForm.php @@ -118,6 +118,7 @@ protected function getEditableConfigNames() { * {@inheritdoc} */ public function buildForm(array $form, FormStateInterface $form_state) { + global $install_state; $form['#title'] = $this->t('Configure site'); // Warn about settings.php permissions risk @@ -145,12 +146,14 @@ public function buildForm(array $form, FormStateInterface $form_state) { $form['site_information'] = array( '#type' => 'fieldgroup', '#title' => $this->t('Site information'), + '#access' => !$install_state['profile_info']['config_install'], ); $form['site_information']['site_name'] = array( '#type' => 'textfield', '#title' => $this->t('Site name'), '#required' => TRUE, '#weight' => -20, + '#access' => !$install_state['profile_info']['config_install'], ); $form['site_information']['site_mail'] = array( '#type' => 'email', @@ -159,6 +162,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { '#description' => $this->t("Automated emails, such as registration information, will be sent from this address. Use an address ending in your site's domain to help prevent these emails from being flagged as spam."), '#required' => TRUE, '#weight' => -15, + '#access' => !$install_state['profile_info']['config_install'], ); $form['admin_account'] = array( @@ -188,6 +192,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { $form['regional_settings'] = array( '#type' => 'fieldgroup', '#title' => $this->t('Regional settings'), + '#access' => !$install_state['profile_info']['config_install'], ); $countries = $this->countryManager->getList(); $form['regional_settings']['site_default_country'] = array( @@ -198,6 +203,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { '#options' => $countries, '#description' => $this->t('Select the default country for the site.'), '#weight' => 0, + '#access' => !$install_state['profile_info']['config_install'], ); $form['regional_settings']['date_default_timezone'] = array( '#type' => 'select', @@ -208,11 +214,13 @@ public function buildForm(array $form, FormStateInterface $form_state) { '#description' => $this->t('By default, dates in this site will be displayed in the chosen time zone.'), '#weight' => 5, '#attributes' => array('class' => array('timezone-detect')), + '#access' => !$install_state['profile_info']['config_install'], ); $form['update_notifications'] = array( '#type' => 'fieldgroup', '#title' => $this->t('Update notifications'), + '#access' => !$install_state['profile_info']['config_install'], ); $form['update_notifications']['update_status_module'] = array( '#type' => 'checkboxes', @@ -224,6 +232,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { '#default_value' => array(1, 2), '#description' => $this->t('The system will notify you when updates and important security releases are available for installed components. Anonymous information about your site is sent to Drupal.org.', array(':drupal' => 'https://www.drupal.org')), '#weight' => 15, + '#access' => !$install_state['profile_info']['config_install'], ); $form['update_notifications']['update_status_module'][2] = array( '#states' => array( @@ -257,21 +266,25 @@ public function validateForm(array &$form, FormStateInterface $form_state) { * {@inheritdoc} */ public function submitForm(array &$form, FormStateInterface $form_state) { - $this->config('system.site') - ->set('name', (string) $form_state->getValue('site_name')) - ->set('mail', (string) $form_state->getValue('site_mail')) - ->save(TRUE); + global $install_state; - $this->config('system.date') - ->set('timezone.default', (string) $form_state->getValue('date_default_timezone')) - ->set('country.default', (string) $form_state->getValue('site_default_country')) - ->save(TRUE); + if (!$install_state['profile_info']['config_install']) { + $this->config('system.site') + ->set('name', (string) $form_state->getValue('site_name')) + ->set('mail', (string) $form_state->getValue('site_mail')) + ->save(TRUE); + + $this->config('system.date') + ->set('timezone.default', (string) $form_state->getValue('date_default_timezone')) + ->set('country.default', (string) $form_state->getValue('site_default_country')) + ->save(TRUE); + } $account_values = $form_state->getValue('account'); // Enable update.module if this option was selected. $update_status_module = $form_state->getValue('update_status_module'); - if ($update_status_module[1]) { + if (!$install_state['profile_info']['config_install'] && $update_status_module[1]) { $this->moduleInstaller->install(array('file', 'update'), FALSE); // Add the site maintenance account's email address to the list of