Issue #2788777 by alexpott, bircher, jribeiro, Eli-T, mpotter, douggreen, GoZ, DamienMcKenna, Dane Powell, jibran, szeidler, Alumei, andypost, dawehner, johndevman: Allow a site-specific profile to be installed from existing config
parent
34637de7aa
commit
de724027fe
|
@ -6,6 +6,11 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use Drupal\Component\Utility\UrlHelper;
|
use Drupal\Component\Utility\UrlHelper;
|
||||||
|
use Drupal\Core\Batch\BatchBuilder;
|
||||||
|
use Drupal\Core\Config\ConfigImporter;
|
||||||
|
use Drupal\Core\Config\ConfigImporterException;
|
||||||
|
use Drupal\Core\Config\Importer\ConfigImporterBatch;
|
||||||
|
use Drupal\Core\Config\StorageComparer;
|
||||||
use Drupal\Core\DrupalKernel;
|
use Drupal\Core\DrupalKernel;
|
||||||
use Drupal\Core\Database\Database;
|
use Drupal\Core\Database\Database;
|
||||||
use Drupal\Core\Database\DatabaseExceptionWrapper;
|
use Drupal\Core\Database\DatabaseExceptionWrapper;
|
||||||
|
@ -198,6 +203,10 @@ function install_state_defaults() {
|
||||||
// The last task that was completed during the previous installation
|
// The last task that was completed during the previous installation
|
||||||
// request.
|
// request.
|
||||||
'completed_task' => NULL,
|
'completed_task' => NULL,
|
||||||
|
// Partial configuration cached during an installation from existing config.
|
||||||
|
'config' => NULL,
|
||||||
|
// The path to the configuration to install when installing from config.
|
||||||
|
'config_install_path' => NULL,
|
||||||
// TRUE when there are valid config directories.
|
// TRUE when there are valid config directories.
|
||||||
'config_verified' => FALSE,
|
'config_verified' => FALSE,
|
||||||
// TRUE when there is a valid database connection.
|
// TRUE when there is a valid database connection.
|
||||||
|
@ -473,9 +482,13 @@ function install_begin_request($class_loader, &$install_state) {
|
||||||
// @todo Remove as part of https://www.drupal.org/node/2186491
|
// @todo Remove as part of https://www.drupal.org/node/2186491
|
||||||
drupal_get_filename('module', 'system', 'core/modules/system/system.info.yml');
|
drupal_get_filename('module', 'system', 'core/modules/system/system.info.yml');
|
||||||
|
|
||||||
// Use the language from the profile configuration, if available, to override
|
// Use the language from profile configuration if available.
|
||||||
// the language previously set in the parameters.
|
if (!empty($install_state['config_install_path']) && $install_state['config']['system.site']) {
|
||||||
if (isset($install_state['profile_info']['distribution']['langcode'])) {
|
$install_state['parameters']['langcode'] = $install_state['config']['system.site']['default_langcode'];
|
||||||
|
}
|
||||||
|
elseif (isset($install_state['profile_info']['distribution']['langcode'])) {
|
||||||
|
// Otherwise, Use the language from the profile configuration, if available,
|
||||||
|
// to override the language previously set in the parameters.
|
||||||
$install_state['parameters']['langcode'] = $install_state['profile_info']['distribution']['langcode'];
|
$install_state['parameters']['langcode'] = $install_state['profile_info']['distribution']['langcode'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -818,6 +831,30 @@ function install_tasks($install_state) {
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if (!empty($install_state['config_install_path'])) {
|
||||||
|
// The chosen profile indicates that rather than installing a new site, an
|
||||||
|
// instance of the same site should be installed from the given
|
||||||
|
// configuration.
|
||||||
|
// That means we need to remove the steps installing the extensions and
|
||||||
|
// replace them with a configuration synchronization step.
|
||||||
|
unset($tasks['install_download_translation']);
|
||||||
|
$key = array_search('install_profile_modules', array_keys($tasks), TRUE);
|
||||||
|
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',
|
||||||
|
],
|
||||||
|
'install_config_download_translations' => [],
|
||||||
|
'install_config_revert_install_changes' => [],
|
||||||
|
];
|
||||||
|
$tasks = array_slice($tasks, 0, $key, TRUE) +
|
||||||
|
$config_tasks +
|
||||||
|
array_slice($tasks, $key, NULL, TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
// Now add any tasks defined by the installation profile.
|
// Now add any tasks defined by the installation profile.
|
||||||
if (!empty($install_state['parameters']['profile'])) {
|
if (!empty($install_state['parameters']['profile'])) {
|
||||||
// Load the profile install file, because it is not always loaded when
|
// Load the profile install file, because it is not always loaded when
|
||||||
|
@ -1494,6 +1531,14 @@ function install_load_profile(&$install_state) {
|
||||||
$profile = $install_state['parameters']['profile'];
|
$profile = $install_state['parameters']['profile'];
|
||||||
$install_state['profiles'][$profile]->load();
|
$install_state['profiles'][$profile]->load();
|
||||||
$install_state['profile_info'] = install_profile_info($profile, isset($install_state['parameters']['langcode']) ? $install_state['parameters']['langcode'] : 'en');
|
$install_state['profile_info'] = install_profile_info($profile, isset($install_state['parameters']['langcode']) ? $install_state['parameters']['langcode'] : 'en');
|
||||||
|
// If the profile has a config/sync directory copy the information to the
|
||||||
|
// install_state global.
|
||||||
|
if (!empty($install_state['profile_info']['config_install_path'])) {
|
||||||
|
$install_state['config_install_path'] = $install_state['profile_info']['config_install_path'];
|
||||||
|
if (!empty($install_state['profile_info']['config'])) {
|
||||||
|
$install_state['config'] = $install_state['profile_info']['config'];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2260,3 +2305,134 @@ function install_write_profile($install_state) {
|
||||||
throw new InstallProfileMismatchException($install_state['parameters']['profile'], $settings_profile, $settings_path, \Drupal::translation());
|
throw new InstallProfileMismatchException($install_state['parameters']['profile'], $settings_profile, $settings_path, \Drupal::translation());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a batch for the config importer to process.
|
||||||
|
*
|
||||||
|
* @see install_tasks()
|
||||||
|
*/
|
||||||
|
function install_config_import_batch() {
|
||||||
|
// 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();
|
||||||
|
|
||||||
|
// Get the sync storage.
|
||||||
|
$sync = \Drupal::service('config.storage.sync');
|
||||||
|
// 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();
|
||||||
|
|
||||||
|
$batch_builder = new BatchBuilder();
|
||||||
|
$batch_builder
|
||||||
|
->setFinishCallback([ConfigImporterBatch::class, 'finish'])
|
||||||
|
->setTitle(t('Importing configuration'))
|
||||||
|
->setInitMessage(t('Starting configuration import.'))
|
||||||
|
->setErrorMessage(t('Configuration import has encountered an error.'));
|
||||||
|
|
||||||
|
foreach ($sync_steps as $sync_step) {
|
||||||
|
$batch_builder->addOperation([ConfigImporterBatch::class, 'process'], [$config_importer, $sync_step]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $batch_builder->toArray();
|
||||||
|
}
|
||||||
|
catch (ConfigImporterException $e) {
|
||||||
|
global $install_state;
|
||||||
|
// There are validation errors.
|
||||||
|
$messenger = \Drupal::messenger();
|
||||||
|
$messenger->addError(t('The configuration synchronization failed validation.'));
|
||||||
|
foreach ($config_importer->getErrors() as $message) {
|
||||||
|
$messenger->addError($message);
|
||||||
|
}
|
||||||
|
install_display_output(['#title' => t('Configuration validation')], $install_state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces install_download_translation() during configuration installs.
|
||||||
|
*
|
||||||
|
* @param array $install_state
|
||||||
|
* An array of information about the current installation state.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* A themed status report, or an exception if there are requirement errors.
|
||||||
|
* Upon successful download the page is reloaded and no output is returned.
|
||||||
|
*
|
||||||
|
* @see install_download_translation()
|
||||||
|
*/
|
||||||
|
function install_config_download_translations(&$install_state) {
|
||||||
|
$needs_download = isset($install_state['parameters']['langcode']) && !isset($install_state['translations'][$install_state['parameters']['langcode']]) && $install_state['parameters']['langcode'] !== 'en';
|
||||||
|
if ($needs_download) {
|
||||||
|
return install_download_translation($install_state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverts configuration if hook_install() implementations have made changes.
|
||||||
|
*
|
||||||
|
* This step ensures that the final configuration matches the configuration
|
||||||
|
* provided to the installer.
|
||||||
|
*/
|
||||||
|
function install_config_revert_install_changes() {
|
||||||
|
global $install_state;
|
||||||
|
|
||||||
|
$config_manager = \Drupal::service('config.manager');
|
||||||
|
$storage_comparer = new StorageComparer(\Drupal::service('config.storage.sync'), \Drupal::service('config.storage'), $config_manager);
|
||||||
|
$storage_comparer->createChangelist();
|
||||||
|
if ($storage_comparer->hasChanges()) {
|
||||||
|
$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 {
|
||||||
|
$config_importer->import();
|
||||||
|
}
|
||||||
|
catch (ConfigImporterException $e) {
|
||||||
|
global $install_state;
|
||||||
|
$messenger = \Drupal::messenger();
|
||||||
|
// There are validation errors.
|
||||||
|
$messenger->addError(t('The configuration synchronization failed validation.'));
|
||||||
|
foreach ($config_importer->getErrors() as $message) {
|
||||||
|
$messenger->addError($message);
|
||||||
|
}
|
||||||
|
install_display_output(['#title' => t('Configuration validation')], $install_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
// At this point the configuration should match completely.
|
||||||
|
if (\Drupal::moduleHandler()->moduleExists('language')) {
|
||||||
|
// If the English language exists at this point we need to ensure
|
||||||
|
// install_download_additional_translations_operations() does not delete
|
||||||
|
// it.
|
||||||
|
if (ConfigurableLanguage::load('en')) {
|
||||||
|
$install_state['profile_info']['keep_english'] = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||||
use Drupal\Component\Utility\Crypt;
|
use Drupal\Component\Utility\Crypt;
|
||||||
use Drupal\Component\Utility\OpCodeCache;
|
use Drupal\Component\Utility\OpCodeCache;
|
||||||
use Drupal\Component\Utility\UrlHelper;
|
use Drupal\Component\Utility\UrlHelper;
|
||||||
|
use Drupal\Core\Config\FileStorage;
|
||||||
use Drupal\Core\Extension\ExtensionDiscovery;
|
use Drupal\Core\Extension\ExtensionDiscovery;
|
||||||
use Drupal\Core\Site\Settings;
|
use Drupal\Core\Site\Settings;
|
||||||
|
|
||||||
|
@ -481,12 +482,20 @@ function _drupal_rewrite_settings_dump_one(\stdClass $variable, $prefix = '', $s
|
||||||
* @see update_prepare_d8_bootstrap()
|
* @see update_prepare_d8_bootstrap()
|
||||||
*/
|
*/
|
||||||
function drupal_install_config_directories() {
|
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
|
// If settings.php does not contain a config sync directory name we need to
|
||||||
// manually defined in the existing already.
|
// configure one.
|
||||||
if (empty($config_directories[CONFIG_SYNC_DIRECTORY])) {
|
if (empty($config_directories[CONFIG_SYNC_DIRECTORY])) {
|
||||||
$config_directories[CONFIG_SYNC_DIRECTORY] = \Drupal::service('site.path') . '/files/config_' . Crypt::randomBytesBase64(55) . '/sync';
|
if (empty($install_state['config_install_path'])) {
|
||||||
|
// Add a randomized config directory name to settings.php
|
||||||
|
$config_directories[CONFIG_SYNC_DIRECTORY] = \Drupal::service('site.path') . '/files/config_' . Crypt::randomBytesBase64(55) . '/sync';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Install profiles can contain a config sync directory. If they do,
|
||||||
|
// 'config_install_path' is a path to the directory.
|
||||||
|
$config_directories[CONFIG_SYNC_DIRECTORY] = $install_state['config_install_path'];
|
||||||
|
}
|
||||||
$settings['config_directories'][CONFIG_SYNC_DIRECTORY] = (object) [
|
$settings['config_directories'][CONFIG_SYNC_DIRECTORY] = (object) [
|
||||||
'value' => $config_directories[CONFIG_SYNC_DIRECTORY],
|
'value' => $config_directories[CONFIG_SYNC_DIRECTORY],
|
||||||
'required' => TRUE,
|
'required' => TRUE,
|
||||||
|
@ -1099,9 +1108,10 @@ function install_profile_info($profile, $langcode = 'en') {
|
||||||
'version' => NULL,
|
'version' => NULL,
|
||||||
'hidden' => FALSE,
|
'hidden' => FALSE,
|
||||||
'php' => DRUPAL_MINIMUM_PHP,
|
'php' => DRUPAL_MINIMUM_PHP,
|
||||||
|
'config_install_path' => NULL,
|
||||||
];
|
];
|
||||||
$profile_file = drupal_get_path('profile', $profile) . "/$profile.info.yml";
|
$profile_path = drupal_get_path('profile', $profile);
|
||||||
$info = \Drupal::service('info_parser')->parse($profile_file);
|
$info = \Drupal::service('info_parser')->parse("$profile_path/$profile.info.yml");
|
||||||
$info += $defaults;
|
$info += $defaults;
|
||||||
|
|
||||||
// drupal_required_modules() includes the current profile as a dependency.
|
// drupal_required_modules() includes the current profile as a dependency.
|
||||||
|
@ -1114,6 +1124,12 @@ function install_profile_info($profile, $langcode = 'en') {
|
||||||
// remove any duplicates.
|
// remove any duplicates.
|
||||||
$info['install'] = array_unique(array_merge($info['install'], $required, $info['dependencies'], $locale));
|
$info['install'] = array_unique(array_merge($info['install'], $required, $info['dependencies'], $locale));
|
||||||
|
|
||||||
|
// If the profile has a config/sync directory use that to install drupal.
|
||||||
|
if (is_dir($profile_path . '/config/sync')) {
|
||||||
|
$info['config_install_path'] = $profile_path . '/config/sync';
|
||||||
|
$sync = new FileStorage($profile_path . '/config/sync');
|
||||||
|
$info['config']['system.site'] = $sync->read('system.site');
|
||||||
|
}
|
||||||
$cache[$profile][$langcode] = $info;
|
$cache[$profile][$langcode] = $info;
|
||||||
}
|
}
|
||||||
return $cache[$profile][$langcode];
|
return $cache[$profile][$langcode];
|
||||||
|
|
|
@ -405,6 +405,14 @@ class ConfigImporter {
|
||||||
$module_list = array_reverse($module_list);
|
$module_list = array_reverse($module_list);
|
||||||
$this->extensionChangelist['module']['install'] = array_intersect(array_keys($module_list), $install);
|
$this->extensionChangelist['module']['install'] = array_intersect(array_keys($module_list), $install);
|
||||||
|
|
||||||
|
// If we're installing the install profile ensure it comes last. This will
|
||||||
|
// occur when installing a site from configuration.
|
||||||
|
$install_profile_key = array_search($new_extensions['profile'], $this->extensionChangelist['module']['install'], TRUE);
|
||||||
|
if ($install_profile_key !== FALSE) {
|
||||||
|
unset($this->extensionChangelist['module']['install'][$install_profile_key]);
|
||||||
|
$this->extensionChangelist['module']['install'][] = $new_extensions['profile'];
|
||||||
|
}
|
||||||
|
|
||||||
// Work out what themes to install and to uninstall.
|
// Work out what themes to install and to uninstall.
|
||||||
$this->extensionChangelist['theme']['install'] = array_keys(array_diff_key($new_extensions['theme'], $current_extensions['theme']));
|
$this->extensionChangelist['theme']['install'] = array_keys(array_diff_key($new_extensions['theme'], $current_extensions['theme']));
|
||||||
$this->extensionChangelist['theme']['uninstall'] = array_keys(array_diff_key($current_extensions['theme'], $new_extensions['theme']));
|
$this->extensionChangelist['theme']['uninstall'] = array_keys(array_diff_key($current_extensions['theme'], $new_extensions['theme']));
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\Core\Config\Importer;
|
||||||
|
|
||||||
|
use Drupal\Core\Config\ConfigImporter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Methods for running the ConfigImporter in a batch.
|
||||||
|
*
|
||||||
|
* @see \Drupal\Core\Config\ConfigImporter
|
||||||
|
*/
|
||||||
|
class ConfigImporterBatch {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 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'] = array_merge($errors, $context['results']['errors']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finish batch.
|
||||||
|
*
|
||||||
|
* This function is a static function to avoid serializing the ConfigSync
|
||||||
|
* object unnecessarily.
|
||||||
|
*
|
||||||
|
* @param bool $success
|
||||||
|
* Indicate that the batch API tasks were all completed successfully.
|
||||||
|
* @param array $results
|
||||||
|
* An array of all the results that were updated in update_do_one().
|
||||||
|
* @param array $operations
|
||||||
|
* A list of the operations that had not been completed by the batch API.
|
||||||
|
*/
|
||||||
|
public static function finish($success, $results, $operations) {
|
||||||
|
$messenger = \Drupal::messenger();
|
||||||
|
if ($success) {
|
||||||
|
if (!empty($results['errors'])) {
|
||||||
|
$logger = \Drupal::logger('config_sync');
|
||||||
|
foreach ($results['errors'] as $error) {
|
||||||
|
$messenger->addError($error);
|
||||||
|
$logger->error($error);
|
||||||
|
}
|
||||||
|
$messenger->addWarning(t('The configuration was imported with errors.'));
|
||||||
|
}
|
||||||
|
elseif (!drupal_installation_attempted()) {
|
||||||
|
// Display a success message when not installing Drupal.
|
||||||
|
$messenger->addStatus(t('The configuration was imported successfully.'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// An error occurred.
|
||||||
|
// $operations contains the operations that remained unprocessed.
|
||||||
|
$error_operation = reset($operations);
|
||||||
|
$message = t('An error occurred while processing %error_operation with arguments: @arguments', ['%error_operation' => $error_operation[0], '@arguments' => print_r($error_operation[1], TRUE)]);
|
||||||
|
$messenger->addError($message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -121,6 +121,7 @@ class SiteConfigureForm extends ConfigFormBase {
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||||
|
global $install_state;
|
||||||
$form['#title'] = $this->t('Configure site');
|
$form['#title'] = $this->t('Configure site');
|
||||||
|
|
||||||
// Warn about settings.php permissions risk
|
// Warn about settings.php permissions risk
|
||||||
|
@ -148,12 +149,14 @@ class SiteConfigureForm extends ConfigFormBase {
|
||||||
$form['site_information'] = [
|
$form['site_information'] = [
|
||||||
'#type' => 'fieldgroup',
|
'#type' => 'fieldgroup',
|
||||||
'#title' => $this->t('Site information'),
|
'#title' => $this->t('Site information'),
|
||||||
|
'#access' => empty($install_state['config_install_path']),
|
||||||
];
|
];
|
||||||
$form['site_information']['site_name'] = [
|
$form['site_information']['site_name'] = [
|
||||||
'#type' => 'textfield',
|
'#type' => 'textfield',
|
||||||
'#title' => $this->t('Site name'),
|
'#title' => $this->t('Site name'),
|
||||||
'#required' => TRUE,
|
'#required' => TRUE,
|
||||||
'#weight' => -20,
|
'#weight' => -20,
|
||||||
|
'#access' => empty($install_state['config_install_path']),
|
||||||
];
|
];
|
||||||
$form['site_information']['site_mail'] = [
|
$form['site_information']['site_mail'] = [
|
||||||
'#type' => 'email',
|
'#type' => 'email',
|
||||||
|
@ -162,6 +165,7 @@ class SiteConfigureForm extends ConfigFormBase {
|
||||||
'#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."),
|
'#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,
|
'#required' => TRUE,
|
||||||
'#weight' => -15,
|
'#weight' => -15,
|
||||||
|
'#access' => empty($install_state['config_install_path']),
|
||||||
];
|
];
|
||||||
|
|
||||||
$form['admin_account'] = [
|
$form['admin_account'] = [
|
||||||
|
@ -191,6 +195,7 @@ class SiteConfigureForm extends ConfigFormBase {
|
||||||
$form['regional_settings'] = [
|
$form['regional_settings'] = [
|
||||||
'#type' => 'fieldgroup',
|
'#type' => 'fieldgroup',
|
||||||
'#title' => $this->t('Regional settings'),
|
'#title' => $this->t('Regional settings'),
|
||||||
|
'#access' => empty($install_state['config_install_path']),
|
||||||
];
|
];
|
||||||
$countries = $this->countryManager->getList();
|
$countries = $this->countryManager->getList();
|
||||||
$form['regional_settings']['site_default_country'] = [
|
$form['regional_settings']['site_default_country'] = [
|
||||||
|
@ -201,6 +206,7 @@ class SiteConfigureForm extends ConfigFormBase {
|
||||||
'#options' => $countries,
|
'#options' => $countries,
|
||||||
'#description' => $this->t('Select the default country for the site.'),
|
'#description' => $this->t('Select the default country for the site.'),
|
||||||
'#weight' => 0,
|
'#weight' => 0,
|
||||||
|
'#access' => empty($install_state['config_install_path']),
|
||||||
];
|
];
|
||||||
$form['regional_settings']['date_default_timezone'] = [
|
$form['regional_settings']['date_default_timezone'] = [
|
||||||
'#type' => 'select',
|
'#type' => 'select',
|
||||||
|
@ -211,17 +217,20 @@ class SiteConfigureForm extends ConfigFormBase {
|
||||||
'#description' => $this->t('By default, dates in this site will be displayed in the chosen time zone.'),
|
'#description' => $this->t('By default, dates in this site will be displayed in the chosen time zone.'),
|
||||||
'#weight' => 5,
|
'#weight' => 5,
|
||||||
'#attributes' => ['class' => ['timezone-detect']],
|
'#attributes' => ['class' => ['timezone-detect']],
|
||||||
|
'#access' => empty($install_state['config_install_path']),
|
||||||
];
|
];
|
||||||
|
|
||||||
$form['update_notifications'] = [
|
$form['update_notifications'] = [
|
||||||
'#type' => 'fieldgroup',
|
'#type' => 'fieldgroup',
|
||||||
'#title' => $this->t('Update notifications'),
|
'#title' => $this->t('Update notifications'),
|
||||||
'#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 <a href=":drupal">Drupal.org</a>.', [':drupal' => 'https://www.drupal.org']),
|
'#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 <a href=":drupal">Drupal.org</a>.', [':drupal' => 'https://www.drupal.org']),
|
||||||
|
'#access' => empty($install_state['config_install_path']),
|
||||||
];
|
];
|
||||||
$form['update_notifications']['enable_update_status_module'] = [
|
$form['update_notifications']['enable_update_status_module'] = [
|
||||||
'#type' => 'checkbox',
|
'#type' => 'checkbox',
|
||||||
'#title' => $this->t('Check for updates automatically'),
|
'#title' => $this->t('Check for updates automatically'),
|
||||||
'#default_value' => 1,
|
'#default_value' => 1,
|
||||||
|
'#access' => empty($install_state['config_install_path']),
|
||||||
];
|
];
|
||||||
$form['update_notifications']['enable_update_status_emails'] = [
|
$form['update_notifications']['enable_update_status_emails'] = [
|
||||||
'#type' => 'checkbox',
|
'#type' => 'checkbox',
|
||||||
|
@ -232,6 +241,7 @@ class SiteConfigureForm extends ConfigFormBase {
|
||||||
'input[name="enable_update_status_module"]' => ['checked' => TRUE],
|
'input[name="enable_update_status_module"]' => ['checked' => TRUE],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
'#access' => empty($install_state['config_install_path']),
|
||||||
];
|
];
|
||||||
|
|
||||||
$form['actions'] = ['#type' => 'actions'];
|
$form['actions'] = ['#type' => 'actions'];
|
||||||
|
@ -258,21 +268,25 @@ class SiteConfigureForm extends ConfigFormBase {
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||||
$this->config('system.site')
|
global $install_state;
|
||||||
->set('name', (string) $form_state->getValue('site_name'))
|
|
||||||
->set('mail', (string) $form_state->getValue('site_mail'))
|
|
||||||
->save(TRUE);
|
|
||||||
|
|
||||||
$this->config('system.date')
|
if (empty($install_state['config_install_path'])) {
|
||||||
->set('timezone.default', (string) $form_state->getValue('date_default_timezone'))
|
$this->config('system.site')
|
||||||
->set('country.default', (string) $form_state->getValue('site_default_country'))
|
->set('name', (string) $form_state->getValue('site_name'))
|
||||||
->save(TRUE);
|
->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');
|
$account_values = $form_state->getValue('account');
|
||||||
|
|
||||||
// Enable update.module if this option was selected.
|
// Enable update.module if this option was selected.
|
||||||
$update_status_module = $form_state->getValue('enable_update_status_module');
|
$update_status_module = $form_state->getValue('enable_update_status_module');
|
||||||
if ($update_status_module) {
|
if (empty($install_state['config_install_path']) && $update_status_module) {
|
||||||
$this->moduleInstaller->install(['file', 'update'], FALSE);
|
$this->moduleInstaller->install(['file', 'update'], FALSE);
|
||||||
|
|
||||||
// Add the site maintenance account's email address to the list of
|
// Add the site maintenance account's email address to the list of
|
||||||
|
|
|
@ -4,6 +4,7 @@ namespace Drupal\config\Form;
|
||||||
|
|
||||||
use Drupal\Core\Config\ConfigImporterException;
|
use Drupal\Core\Config\ConfigImporterException;
|
||||||
use Drupal\Core\Config\ConfigImporter;
|
use Drupal\Core\Config\ConfigImporter;
|
||||||
|
use Drupal\Core\Config\Importer\ConfigImporterBatch;
|
||||||
use Drupal\Core\Config\TypedConfigManagerInterface;
|
use Drupal\Core\Config\TypedConfigManagerInterface;
|
||||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||||
use Drupal\Core\Extension\ModuleInstallerInterface;
|
use Drupal\Core\Extension\ModuleInstallerInterface;
|
||||||
|
@ -337,14 +338,14 @@ class ConfigSync extends FormBase {
|
||||||
$sync_steps = $config_importer->initialize();
|
$sync_steps = $config_importer->initialize();
|
||||||
$batch = [
|
$batch = [
|
||||||
'operations' => [],
|
'operations' => [],
|
||||||
'finished' => [get_class($this), 'finishBatch'],
|
'finished' => [ConfigImporterBatch::class, 'finish'],
|
||||||
'title' => t('Synchronizing configuration'),
|
'title' => t('Synchronizing configuration'),
|
||||||
'init_message' => t('Starting configuration synchronization.'),
|
'init_message' => t('Starting configuration synchronization.'),
|
||||||
'progress_message' => t('Completed step @current of @total.'),
|
'progress_message' => t('Completed step @current of @total.'),
|
||||||
'error_message' => t('Configuration synchronization has encountered an error.'),
|
'error_message' => t('Configuration synchronization has encountered an error.'),
|
||||||
];
|
];
|
||||||
foreach ($sync_steps as $sync_step) {
|
foreach ($sync_steps as $sync_step) {
|
||||||
$batch['operations'][] = [[get_class($this), 'processBatch'], [$config_importer, $sync_step]];
|
$batch['operations'][] = [[ConfigImporterBatch::class, 'process'], [$config_importer, $sync_step]];
|
||||||
}
|
}
|
||||||
|
|
||||||
batch_set($batch);
|
batch_set($batch);
|
||||||
|
@ -368,20 +369,15 @@ class ConfigSync extends FormBase {
|
||||||
* The synchronization step to do.
|
* The synchronization step to do.
|
||||||
* @param array $context
|
* @param array $context
|
||||||
* The batch context.
|
* The batch context.
|
||||||
|
*
|
||||||
|
* @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
|
||||||
*/
|
*/
|
||||||
public static function processBatch(ConfigImporter $config_importer, $sync_step, &$context) {
|
public static function processBatch(ConfigImporter $config_importer, $sync_step, &$context) {
|
||||||
if (!isset($context['sandbox']['config_importer'])) {
|
@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');
|
||||||
$context['sandbox']['config_importer'] = $config_importer;
|
ConfigImporterBatch::process($config_importer, $sync_step, $context);
|
||||||
}
|
|
||||||
|
|
||||||
$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'] = array_merge($context['results']['errors'], $errors);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -389,27 +385,15 @@ class ConfigSync extends FormBase {
|
||||||
*
|
*
|
||||||
* This function is a static function to avoid serializing the ConfigSync
|
* This function is a static function to avoid serializing the ConfigSync
|
||||||
* object unnecessarily.
|
* object unnecessarily.
|
||||||
|
*
|
||||||
|
* @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
|
||||||
*/
|
*/
|
||||||
public static function finishBatch($success, $results, $operations) {
|
public static function finishBatch($success, $results, $operations) {
|
||||||
if ($success) {
|
@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');
|
||||||
if (!empty($results['errors'])) {
|
ConfigImporterBatch::finish($success, $results, $operations);
|
||||||
foreach ($results['errors'] as $error) {
|
|
||||||
\Drupal::messenger()->addError($error);
|
|
||||||
\Drupal::logger('config_sync')->error($error);
|
|
||||||
}
|
|
||||||
\Drupal::messenger()->addWarning(\Drupal::translation()->translate('The configuration was imported with errors.'));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
\Drupal::messenger()->addStatus(\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', ['%error_operation' => $error_operation[0], '@arguments' => print_r($error_operation[1], TRUE)]);
|
|
||||||
\Drupal::messenger()->addError($message);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ class ConfigUninstallViaCliImportTest extends KernelTestBase {
|
||||||
$this->markTestSkipped('This test has to be run from the CLI');
|
$this->markTestSkipped('This test has to be run from the CLI');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->installConfig(['system']);
|
||||||
$this->copyConfig($this->container->get('config.storage'), $this->container->get('config.storage.sync'));
|
$this->copyConfig($this->container->get('config.storage'), $this->container->get('config.storage.sync'));
|
||||||
|
|
||||||
// Set up the ConfigImporter object for testing.
|
// Set up the ConfigImporter object for testing.
|
||||||
|
|
|
@ -59,7 +59,7 @@ class ContentModerationWorkflowConfigTest extends KernelTestBase {
|
||||||
$this->installEntitySchema('node');
|
$this->installEntitySchema('node');
|
||||||
$this->installEntitySchema('user');
|
$this->installEntitySchema('user');
|
||||||
$this->installEntitySchema('content_moderation_state');
|
$this->installEntitySchema('content_moderation_state');
|
||||||
$this->installConfig('content_moderation');
|
$this->installConfig(['system', 'content_moderation']);
|
||||||
|
|
||||||
NodeType::create([
|
NodeType::create([
|
||||||
'type' => 'example',
|
'type' => 'example',
|
||||||
|
|
|
@ -33,6 +33,7 @@ class ContentTranslationConfigImportTest extends KernelTestBase {
|
||||||
protected function setUp() {
|
protected function setUp() {
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->installConfig(['system']);
|
||||||
$this->installEntitySchema('entity_test_mul');
|
$this->installEntitySchema('entity_test_mul');
|
||||||
$this->copyConfig($this->container->get('config.storage'), $this->container->get('config.storage.sync'));
|
$this->copyConfig($this->container->get('config.storage'), $this->container->get('config.storage.sync'));
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ class NodeImportChangeTest extends KernelTestBase {
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
|
|
||||||
// Set default storage backend.
|
// Set default storage backend.
|
||||||
$this->installConfig(['field', 'node_test_config']);
|
$this->installConfig(['system', 'field', 'node_test_config']);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -28,7 +28,7 @@ class NodeImportCreateTest extends KernelTestBase {
|
||||||
$this->installEntitySchema('user');
|
$this->installEntitySchema('user');
|
||||||
|
|
||||||
// Set default storage backend.
|
// Set default storage backend.
|
||||||
$this->installConfig(['field']);
|
$this->installConfig(['system', 'field']);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -72,6 +72,9 @@ class SystemConfigSubscriber implements EventSubscriberInterface {
|
||||||
* The config import event.
|
* The config import event.
|
||||||
*/
|
*/
|
||||||
public function onConfigImporterValidateSiteUUID(ConfigImporterEvent $event) {
|
public function onConfigImporterValidateSiteUUID(ConfigImporterEvent $event) {
|
||||||
|
if (!$event->getConfigImporter()->getStorageComparer()->getSourceStorage()->exists('system.site')) {
|
||||||
|
$event->getConfigImporter()->logError($this->t('This import does not contain system.site configuration, so has been rejected.'));
|
||||||
|
}
|
||||||
if (!$event->getConfigImporter()->getStorageComparer()->validateSiteUuid()) {
|
if (!$event->getConfigImporter()->getStorageComparer()->validateSiteUuid()) {
|
||||||
$event->getConfigImporter()->logError($this->t('Site UUID in source storage does not match the target storage.'));
|
$event->getConfigImporter()->logError($this->t('Site UUID in source storage does not match the target storage.'));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1002,6 +1002,19 @@ function system_requirements($phase) {
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// During installs from configuration don't support install profiles that
|
||||||
|
// implement hook_install.
|
||||||
|
if ($phase == 'install' && !empty($install_state['config_install_path'])) {
|
||||||
|
$install_hook = $install_state['parameters']['profile'] . '_install';
|
||||||
|
if (function_exists($install_hook)) {
|
||||||
|
$requirements['config_install'] = [
|
||||||
|
'title' => t('Configuration install'),
|
||||||
|
'value' => $install_state['parameters']['profile'],
|
||||||
|
'description' => t('The selected profile has a hook_install() implementation and therefore can not be installed from configuration.'),
|
||||||
|
'severity' => REQUIREMENT_ERROR,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
return $requirements;
|
return $requirements;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\FunctionalTests\Installer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies that installing from existing configuration works.
|
||||||
|
*
|
||||||
|
* @group Installer
|
||||||
|
*/
|
||||||
|
class InstallerExistingConfigMultilingualTest extends InstallerExistingConfigTestBase {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected $profile = 'testing_config_install_multilingual';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function getConfigTarball() {
|
||||||
|
return __DIR__ . '/../../../fixtures/config_install/multilingual.tar.gz';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\FunctionalTests\Installer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies that profiles invalid config can not be installed.
|
||||||
|
*
|
||||||
|
* @group Installer
|
||||||
|
*/
|
||||||
|
class InstallerExistingConfigNoConfigTest extends InstallerExistingConfigTestBase {
|
||||||
|
|
||||||
|
protected $profile = 'no_config_profile';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Final installer step: Configure site.
|
||||||
|
*/
|
||||||
|
protected function setUpSite() {
|
||||||
|
// There are errors therefore there is nothing to do here.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function getConfigTarball() {
|
||||||
|
return __DIR__ . '/../../../fixtures/config_install/testing_config_install_no_config.tar.gz';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that profiles with an empty config/sync directory do not work.
|
||||||
|
*/
|
||||||
|
public function testConfigSync() {
|
||||||
|
$this->assertTitle('Configuration validation | Drupal');
|
||||||
|
$this->assertText('The configuration synchronization failed validation.');
|
||||||
|
$this->assertText('This import is empty and if applied would delete all of your configuration, so has been rejected.');
|
||||||
|
|
||||||
|
// Ensure there is no continuation button.
|
||||||
|
$this->assertNoText('Save and continue');
|
||||||
|
$this->assertNoFieldById('edit-submit');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\FunctionalTests\Installer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Testing installing from config without system.site.
|
||||||
|
*
|
||||||
|
* @group Installer
|
||||||
|
*/
|
||||||
|
class InstallerExistingConfigNoSystemSiteTest extends InstallerExistingConfigTestBase {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function prepareEnvironment() {
|
||||||
|
parent::prepareEnvironment();
|
||||||
|
// File API functions are not available yet.
|
||||||
|
unlink($this->siteDirectory . '/profiles/' . $this->profile . '/config/sync/system.site.yml');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function setUpSite() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that profiles with no system.site do not work.
|
||||||
|
*/
|
||||||
|
public function testConfigSync() {
|
||||||
|
$this->htmlOutput(NULL);
|
||||||
|
$this->assertTitle('Configuration validation | Drupal');
|
||||||
|
$this->assertText('The configuration synchronization failed validation.');
|
||||||
|
$this->assertText('This import does not contain system.site configuration, so has been rejected.');
|
||||||
|
|
||||||
|
// Ensure there is no continuation button.
|
||||||
|
$this->assertNoText('Save and continue');
|
||||||
|
$this->assertNoFieldById('edit-submit');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function getConfigTarball() {
|
||||||
|
return __DIR__ . '/../../../fixtures/config_install/testing_config_install.tar.gz';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\FunctionalTests\Installer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies that profiles with hook_install() can't be installed from config.
|
||||||
|
*
|
||||||
|
* @group Installer
|
||||||
|
*/
|
||||||
|
class InstallerExistingConfigProfileHookInstall extends InstallerExistingConfigTestBase {
|
||||||
|
|
||||||
|
protected $profile = 'config_profile_with_hook_install';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function visitInstaller() {
|
||||||
|
// Create an .install file with a hook_install() implementation.
|
||||||
|
$path = $this->siteDirectory . '/profiles/' . $this->profile;
|
||||||
|
$contents = <<<EOF
|
||||||
|
<?php
|
||||||
|
|
||||||
|
function config_profile_with_hook_install_install() {
|
||||||
|
}
|
||||||
|
EOF;
|
||||||
|
file_put_contents("$path/{$this->profile}.install", $contents);
|
||||||
|
parent::visitInstaller();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Installer step: Configure settings.
|
||||||
|
*/
|
||||||
|
protected function setUpSettings() {
|
||||||
|
// There are errors therefore there is nothing to do here.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Final installer step: Configure site.
|
||||||
|
*/
|
||||||
|
protected function setUpSite() {
|
||||||
|
// There are errors therefore there is nothing to do here.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function getConfigTarball() {
|
||||||
|
// We're not going to get to the config import stage so this does not
|
||||||
|
// matter.
|
||||||
|
return __DIR__ . '/../../../fixtures/config_install/testing_config_install_no_config.tar.gz';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Confirms the installation has failed and the expected error is displayed.
|
||||||
|
*/
|
||||||
|
public function testConfigSync() {
|
||||||
|
$this->assertTitle('Requirements problem | Drupal');
|
||||||
|
$this->assertText($this->profile);
|
||||||
|
$this->assertText('The selected profile has a hook_install() implementation and therefore can not be installed from configuration.');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\FunctionalTests\Installer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies that installing from existing configuration works.
|
||||||
|
*
|
||||||
|
* @group Installer
|
||||||
|
*/
|
||||||
|
class InstallerExistingConfigTest extends InstallerExistingConfigTestBase {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function setUpSite() {
|
||||||
|
// The configuration is from a site installed in French.
|
||||||
|
// So after selecting the profile the installer detects that the site must
|
||||||
|
// be installed in French, thus we change the button translation.
|
||||||
|
$this->translations['Save and continue'] = 'Enregistrer et continuer';
|
||||||
|
parent::setUpSite();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function getConfigTarball() {
|
||||||
|
return __DIR__ . '/../../../fixtures/config_install/testing_config_install.tar.gz';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,99 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\FunctionalTests\Installer;
|
||||||
|
|
||||||
|
use Drupal\Component\Serialization\Yaml;
|
||||||
|
use Drupal\Core\Archiver\ArchiveTar;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides a base class for testing installing from existing configuration.
|
||||||
|
*/
|
||||||
|
abstract class InstallerExistingConfigTestBase extends InstallerTestBase {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is set by the profile in the core.extension extracted.
|
||||||
|
*/
|
||||||
|
protected $profile = NULL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function prepareEnvironment() {
|
||||||
|
parent::prepareEnvironment();
|
||||||
|
$archiver = new ArchiveTar($this->getConfigTarball(), 'gz');
|
||||||
|
|
||||||
|
if ($this->profile === NULL) {
|
||||||
|
$core_extension = Yaml::decode($archiver->extractInString('core.extension.yml'));
|
||||||
|
$this->profile = $core_extension['profile'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a profile for testing.
|
||||||
|
$info = [
|
||||||
|
'type' => 'profile',
|
||||||
|
'core' => \Drupal::CORE_COMPATIBILITY,
|
||||||
|
'name' => 'Configuration installation test profile (' . $this->profile . ')',
|
||||||
|
];
|
||||||
|
// File API functions are not available yet.
|
||||||
|
$path = $this->siteDirectory . '/profiles/' . $this->profile;
|
||||||
|
|
||||||
|
mkdir($path, 0777, TRUE);
|
||||||
|
file_put_contents("$path/{$this->profile}.info.yml", Yaml::encode($info));
|
||||||
|
|
||||||
|
// Create config/sync directory and extract tarball contents to it.
|
||||||
|
$config_sync_directory = $path . '/config/sync';
|
||||||
|
mkdir($config_sync_directory, 0777, TRUE);
|
||||||
|
$files = [];
|
||||||
|
$list = $archiver->listContent();
|
||||||
|
if (is_array($list)) {
|
||||||
|
/** @var array $list */
|
||||||
|
foreach ($list as $file) {
|
||||||
|
$files[] = $file['filename'];
|
||||||
|
}
|
||||||
|
$archiver->extractList($files, $config_sync_directory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the filepath to the configuration tarball.
|
||||||
|
*
|
||||||
|
* The tarball will be extracted to the install profile's config/sync
|
||||||
|
* directory for testing.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* The filepath to the configuration tarball.
|
||||||
|
*/
|
||||||
|
abstract protected function getConfigTarball();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function installParameters() {
|
||||||
|
$parameters = parent::installParameters();
|
||||||
|
|
||||||
|
// The options that change configuration are disabled when installing from
|
||||||
|
// existing configuration.
|
||||||
|
unset($parameters['forms']['install_configure_form']['site_name']);
|
||||||
|
unset($parameters['forms']['install_configure_form']['site_mail']);
|
||||||
|
unset($parameters['forms']['install_configure_form']['update_status_module']);
|
||||||
|
|
||||||
|
return $parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Confirms that the installation installed the configuration correctly.
|
||||||
|
*/
|
||||||
|
public function testConfigSync() {
|
||||||
|
// After installation there is no snapshot and nothing to import.
|
||||||
|
$change_list = $this->configImporter()->getStorageComparer()->getChangelist();
|
||||||
|
$expected = [
|
||||||
|
'create' => [],
|
||||||
|
// The system.mail is changed configuration because the test system
|
||||||
|
// changes it to ensure that mails are not sent.
|
||||||
|
'update' => ['system.mail'],
|
||||||
|
'delete' => [],
|
||||||
|
'rename' => [],
|
||||||
|
];
|
||||||
|
$this->assertEqual($expected, $change_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -32,7 +32,7 @@ class ConfigImportRecreateTest extends KernelTestBase {
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
|
|
||||||
$this->installEntitySchema('node');
|
$this->installEntitySchema('node');
|
||||||
$this->installConfig(['field', 'node']);
|
$this->installConfig(['system', 'field', 'node']);
|
||||||
|
|
||||||
$this->copyConfig($this->container->get('config.storage'), $this->container->get('config.storage.sync'));
|
$this->copyConfig($this->container->get('config.storage'), $this->container->get('config.storage.sync'));
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,7 @@ class ConfigImportRenameValidationTest extends KernelTestBase {
|
||||||
|
|
||||||
$this->installEntitySchema('user');
|
$this->installEntitySchema('user');
|
||||||
$this->installEntitySchema('node');
|
$this->installEntitySchema('node');
|
||||||
$this->installConfig(['field']);
|
$this->installConfig(['system', 'field']);
|
||||||
|
|
||||||
// Set up the ConfigImporter object for testing.
|
// Set up the ConfigImporter object for testing.
|
||||||
$storage_comparer = new StorageComparer(
|
$storage_comparer = new StorageComparer(
|
||||||
|
|
|
@ -33,7 +33,7 @@ class ConfigImporterMissingContentTest extends KernelTestBase {
|
||||||
$this->installSchema('system', 'sequences');
|
$this->installSchema('system', 'sequences');
|
||||||
$this->installEntitySchema('entity_test');
|
$this->installEntitySchema('entity_test');
|
||||||
$this->installEntitySchema('user');
|
$this->installEntitySchema('user');
|
||||||
$this->installConfig(['config_test']);
|
$this->installConfig(['system', 'config_test']);
|
||||||
// Installing config_test's default configuration pollutes the global
|
// Installing config_test's default configuration pollutes the global
|
||||||
// variable being used for recording hook invocations by this test already,
|
// variable being used for recording hook invocations by this test already,
|
||||||
// so it has to be cleared out manually.
|
// so it has to be cleared out manually.
|
||||||
|
|
|
@ -33,7 +33,7 @@ class ConfigImporterTest extends KernelTestBase {
|
||||||
protected function setUp() {
|
protected function setUp() {
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
|
|
||||||
$this->installConfig(['config_test']);
|
$this->installConfig(['system', 'config_test']);
|
||||||
// Installing config_test's default configuration pollutes the global
|
// Installing config_test's default configuration pollutes the global
|
||||||
// variable being used for recording hook invocations by this test already,
|
// variable being used for recording hook invocations by this test already,
|
||||||
// so it has to be cleared out manually.
|
// so it has to be cleared out manually.
|
||||||
|
|
|
@ -20,6 +20,7 @@ class ConfigOverrideTest extends KernelTestBase {
|
||||||
|
|
||||||
protected function setUp() {
|
protected function setUp() {
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
|
$this->installConfig(['system']);
|
||||||
$this->copyConfig($this->container->get('config.storage'), $this->container->get('config.storage.sync'));
|
$this->copyConfig($this->container->get('config.storage'), $this->container->get('config.storage.sync'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ class ConfigSnapshotTest extends KernelTestBase {
|
||||||
*/
|
*/
|
||||||
protected function setUp() {
|
protected function setUp() {
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
|
$this->installConfig(['system']);
|
||||||
// Update the config snapshot. This allows the parent::setUp() to write
|
// Update the config snapshot. This allows the parent::setUp() to write
|
||||||
// configuration files.
|
// configuration files.
|
||||||
\Drupal::service('config.manager')->createSnapshot(\Drupal::service('config.storage'), \Drupal::service('config.storage.snapshot'));
|
\Drupal::service('config.manager')->createSnapshot(\Drupal::service('config.storage'), \Drupal::service('config.storage.snapshot'));
|
||||||
|
|
|
@ -43,6 +43,7 @@ class ContentEntityNullStorageTest extends KernelTestBase {
|
||||||
* @see \Drupal\Core\Entity\Event\BundleConfigImportValidate
|
* @see \Drupal\Core\Entity\Event\BundleConfigImportValidate
|
||||||
*/
|
*/
|
||||||
public function testDeleteThroughImport() {
|
public function testDeleteThroughImport() {
|
||||||
|
$this->installConfig(['system']);
|
||||||
$contact_form = ContactForm::create(['id' => 'test']);
|
$contact_form = ContactForm::create(['id' => 'test']);
|
||||||
$contact_form->save();
|
$contact_form->save();
|
||||||
|
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue