diff --git a/core/lib/Drupal/Core/Config/ConfigEvents.php b/core/lib/Drupal/Core/Config/ConfigEvents.php index 794fbc5..8589565 100644 --- a/core/lib/Drupal/Core/Config/ConfigEvents.php +++ b/core/lib/Drupal/Core/Config/ConfigEvents.php @@ -10,6 +10,7 @@ /** * Defines events for the configuration system. * + * * @see \Drupal\Core\Config\ConfigCrudEvent */ final class ConfigEvents { @@ -21,11 +22,20 @@ * object is saved. The event listener method receives a * \Drupal\Core\Config\ConfigCrudEvent instance. * + * This event will be fired when configuration is saved by hook_update_N() + * implementations. Subscribers to this event can not make any assumption that + * the config data is valid and should ensure that their actions are safe + * regardless of the structure of the underlying configuration. + * Implementations must use the $trusted_data argument for + * \Drupal\Core\Config\Config::save() and pass schema correct data so that + * configuration schema are not used whilst saving configuration. + * * @Event * * @see \Drupal\Core\Config\ConfigCrudEvent * @see \Drupal\Core\Config\Config::save() * @see \Drupal\Core\Config\ConfigFactory::onConfigSave() + * @see hook_update_N() * * @var string */ @@ -38,11 +48,20 @@ * object is deleted. The event listener method receives a * \Drupal\Core\Config\ConfigCrudEvent instance. * + * This event will be fired when configuration is deleted by hook_update_N() + * implementations. Subscribers to this event can not make any assumption that + * the config data is valid and should ensure that their actions are safe + * regardless of the structure of the underlying configuration. + * Implementations must use the $trusted_data argument for + * \Drupal\Core\Config\Config::save() and pass schema correct data so that + * configuration schema are not used whilst saving configuration. + * * @Event * * @see \Drupal\Core\Config\ConfigCrudEvent * @see \Drupal\Core\Config\Config::delete() * @see \Drupal\Core\Config\ConfigFactory::onConfigDelete() + * @see hook_update_N() * * @var string */ @@ -55,10 +74,19 @@ * object's name is changed. The event listener method receives a * \Drupal\Core\Config\ConfigRenameEvent instance. * + * This event will be fired when configuration is renamed by hook_update_N() + * implementations. Subscribers to this event can not make any assumption that + * the config data is valid and should ensure that their actions are safe + * regardless of the structure of the underlying configuration. + * Implementations must use the $trusted_data argument for + * \Drupal\Core\Config\Config::save() and pass schema correct data so that + * configuration schema are not used whilst saving configuration. + * * @Event * * @see \Drupal\Core\Config\ConfigRenameEvent - * @see \Drupal\Core\Config\ConfigFactoryInterface::rename(). + * @see \Drupal\Core\Config\ConfigFactoryInterface::rename() + * @see hook_update_N() * * @var string */ diff --git a/core/lib/Drupal/Core/Config/ConfigFactoryOverrideBase.php b/core/lib/Drupal/Core/Config/ConfigFactoryOverrideBase.php index d7bc941..2dc6caa 100644 --- a/core/lib/Drupal/Core/Config/ConfigFactoryOverrideBase.php +++ b/core/lib/Drupal/Core/Config/ConfigFactoryOverrideBase.php @@ -74,7 +74,7 @@ protected function filterOverride(Config $config, StorableConfigBase $override) } elseif ($changed) { // Otherwise set the filtered override values back. - $override->setData($override_data)->save(); + $override->setData($override_data)->save(TRUE); } } diff --git a/core/lib/Drupal/Core/Extension/module.api.php b/core/lib/Drupal/Core/Extension/module.api.php index 63f81b2..55c2741 100644 --- a/core/lib/Drupal/Core/Extension/module.api.php +++ b/core/lib/Drupal/Core/Extension/module.api.php @@ -464,11 +464,25 @@ function hook_install_tasks_alter(&$tasks, $install_state) { * In order to call a function from your mymodule.module or an include file, * you need to explicitly load that file first. * - * During database updates the schema of any module could be out of date. For - * this reason, caution is needed when using any API function within an update + * Implementations must ensure that APIs used are safe during updates. During + * database updates the schema of any module could be out of date. For this + * reason, caution is needed when using any API function within an update * function - particularly CRUD functions, functions that depend on the schema - * (for example by using drupal_write_record()), and any functions that invoke - * hooks. + * (for example by using \Drupal\Core\Entity\Entity::save()), and any functions + * that invoke hooks. + * + * The following actions are examples that are safe: + * - Cache invalidation. + * - Using \Drupal::configFactory()->getEditable() and \Drupal::config(). + * Implementations can not make any assumption that the config data is valid. + * Implementations must use the $trusted_data argument for + * \Drupal\Core\Config\Config::save() and pass schema correct data so that + * configuration schema are not used whilst saving configuration. + * - Marking a container for rebuild. + * + * The following actions are examples that are unsafe: + * - Calling Node::save(). + * - Rebuilding the router using \Drupal::service('router.builder')->rebuild(). * * The $sandbox parameter should be used when a multipass update is needed, in * circumstances where running the whole update at once could cause PHP to diff --git a/core/modules/language/language.services.yml b/core/modules/language/language.services.yml index b9de79a..fc9bba1 100644 --- a/core/modules/language/language.services.yml +++ b/core/modules/language/language.services.yml @@ -9,7 +9,7 @@ services: - [initLanguageManager] language.config_subscriber: class: Drupal\language\EventSubscriber\ConfigSubscriber - arguments: ['@language_manager', '@language.default'] + arguments: ['@language_manager', '@language.default', '@config.factory'] tags: - { name: event_subscriber } language.config_factory_override: diff --git a/core/modules/language/src/EventSubscriber/ConfigSubscriber.php b/core/modules/language/src/EventSubscriber/ConfigSubscriber.php index d3486aa..4f087e3 100644 --- a/core/modules/language/src/EventSubscriber/ConfigSubscriber.php +++ b/core/modules/language/src/EventSubscriber/ConfigSubscriber.php @@ -7,6 +7,8 @@ namespace Drupal\language\EventSubscriber; +use Drupal\Core\Config\ConfigFactoryInterface; +use Drupal\Core\Language\Language; use Drupal\Core\Language\LanguageDefault; use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\Config\ConfigCrudEvent; @@ -34,16 +36,26 @@ class ConfigSubscriber implements EventSubscriberInterface { protected $languageDefault; /** + * The configuration factory. + * + * @var \Drupal\Core\Config\ConfigFactoryInterface + */ + protected $configFactory; + + /** * Constructs a new class object. * * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager * The language manager. * @param \Drupal\Core\Language\LanguageDefault $language_default * The default language. + * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory + * The configuration factory. */ - public function __construct(LanguageManagerInterface $language_manager, LanguageDefault $language_default) { + public function __construct(LanguageManagerInterface $language_manager, LanguageDefault $language_default, ConfigFactoryInterface $config_factory) { $this->languageManager = $language_manager; $this->languageDefault = $language_default; + $this->configFactory = $config_factory; } /** @@ -55,12 +67,31 @@ public function __construct(LanguageManagerInterface $language_manager, Language public function onConfigSave(ConfigCrudEvent $event) { $saved_config = $event->getConfig(); if ($saved_config->getName() == 'system.site' && $event->isChanged('default_langcode')) { - $language = $this->languageManager->getLanguage($saved_config->get('default_langcode')); + $new_default_langcode = $saved_config->get('default_langcode'); + + $default_language = $this->configFactory->get('language.entity.' . $new_default_langcode); // During an import the language might not exist yet. - if ($language) { - $this->languageDefault->set($language); + if (!$default_language->isNew()) { + $old_default_langcode = $saved_config->getOriginal('default_langcode'); + $this->languageDefault->set(new Language($default_language->get())); $this->languageManager->reset(); - language_negotiation_url_prefixes_update(); + + // Directly update language negotiation settings instead of calling + // language_negotiation_url_prefixes_update(). + $negotiation_config = $this->configFactory->getEditable('language.negotiation'); + $negotiation_changed = FALSE; + $url_prefixes = $negotiation_config->get('url.prefixes'); + if (empty($url_prefixes[$old_default_langcode])) { + $negotiation_config->set('url.prefixes.' . $old_default_langcode, $old_default_langcode); + $negotiation_changed = TRUE; + } + if (empty($url_prefixes[$new_default_langcode])) { + $negotiation_config->set('url.prefixes.' . $new_default_langcode, ''); + $negotiation_changed = TRUE; + } + if ($negotiation_changed) { + $negotiation_config->save(TRUE); + } } // Trigger a container rebuild on the next request by invalidating it. ConfigurableLanguageManager::rebuildServices();