Commit c677a96f authored by Dries's avatar Dries

- Patch #1589174 by sun, marcingy: Fixed Configuration upgrade path is broken.

parent 356aa34d
......@@ -9,6 +9,7 @@
*/
use Drupal\Component\Graph\Graph;
use Drupal\Core\Config\FileStorage;
/**
* Minimum schema version of Drupal 7 required for upgrade to Drupal 8.
......@@ -877,64 +878,66 @@ function update_retrieve_dependencies() {
* Provide a generalised method to migrate variables from Drupal 7 to Drupal 8's
* configuration management system.
*
* @param $config_name
* The name of the configuration object to retrieve. The name corresponds to
* an XML configuration file. For example, passing "book.admin" will return
* the config object containing the contents of book.admin.xml.
* @param $variable_map
* An array to map new to old configuration naming conventions. Example:
* @param string $config_name
* The configuration object name to retrieve.
* @param array $variable_map
* An associative array that maps old variables names to new configuration
* object keys; e.g.:
* @code
* array('new_config' => 'old_config')
* array('old_variable' => 'new_config.sub_key')
* @endcode
* This would update the value for new_config to the value old_config has in
* the variable table.
* This would migrate the value contained in variable name 'old_variable' into
* the data key 'new_config.sub_key' of the configuration object $config_name.
*/
function update_variables_to_config($config_name, array $variable_map = array()) {
function update_variables_to_config($config_name, array $variable_map) {
// Build the new configuration object.
// This potentially loads an existing configuration object, in case another
// update function migrated configuration values into $config_name already.
$config = config($config_name);
$config_data = array_keys($config->get());
if (!empty($config_data)) {
// Build a list of variables to select from the database and build a mapping
// of variable names to config keys.
foreach ($config_data as $config_key) {
if (isset($variable_map[$config_key])) {
$variables[] = $variable_map[$config_key];
$config_keys[$variable_map[$config_key]] = $config_key;
}
else {
$variables[] = $config_key;
$config_keys[$config_key] = $config_key;
}
}
$original_data = $config->get();
// Extract the module namespace/owner from the configuration object name.
$module = strtok($config_name, '.');
// Load and set default configuration values.
// Throws a FileStorageReadException if there is no default configuration
// file, which is required to exist.
$file = new FileStorage($config_name);
$file->setPath(drupal_get_path('module', $module) . '/config');
$default_data = $file->read();
// Merge any possibly existing original data into default values.
// Only relevant when being called repetitively on the same config object.
if (!empty($original_data)) {
$data = drupal_array_merge_deep($default_data, $original_data);
}
else {
$data = $default_data;
}
// Get any variables currently defined that match the new setting names in
// the config file.
$query = db_select('variable', 'v')
->fields('v')
->condition('name', $variables, 'IN');
$var_values = $query->execute()->fetchAllKeyed(0);
if (!empty($var_values)) {
// Update the config system settings to use the values previously stored in
// the variable table.
try {
foreach($var_values as $name => $val) {
$config->set($config_keys[$name], unserialize($val));
}
$config->save();
// Delete the old variables. The config system will throw an exception if a
// value cannot be saved, so this code will not run if there is a problem
// running the update.
$del = db_delete('variable')->condition('name', $variables, 'IN');
$del->execute();
}
// @TODO We may want to do different error handling for different
// exception types, but for now we'll just log the exception.
catch (Exception $e) {
watchdog_exception('update', $e);
}
// Apply the default values.
$config->setData($data);
// Fetch existing variables.
$variables = db_query('SELECT name, value FROM {variable} WHERE name IN (:variables)', array(':variables' => array_keys($variable_map)))->fetchAllKeyed();
// Set configuration values according to the provided variable mapping.
foreach ($variable_map as $variable_name => $config_key) {
// This function migrates variables regardless of their value, including
// NULL values. Any possibly required customizations need to be performed
// manually, either via variable_set() before calling this function or via
// config() after calling this function.
if (isset($variables[$variable_name])) {
$value = unserialize($variables[$variable_name]);
$config->set($config_key, $value);
}
}
// Save the configuration object.
$config->save();
// Delete the migrated variables.
db_delete('variable')->condition('name', array_keys($variable_map), 'IN')->execute();
}
/**
......
......@@ -105,13 +105,13 @@ public function write($data) {
*/
public function read() {
if (!$this->exists()) {
throw new FileStorageReadException('Configuration file does not exist.');
throw new FileStorageReadException("Configuration file '$this->name' does not exist.");
}
$data = file_get_contents($this->getFilePath());
$data = $this->decode($data);
if ($data === FALSE) {
throw new FileStorageReadException('Unable to decode configuration file.');
throw new FileStorageReadException("Failed to decode configuration file '$this->name'.");
}
return $data;
}
......
......@@ -224,6 +224,8 @@ function block_install() {
/**
* Block cache is always enabled in 8.x.
*
* @ingroup config_upgrade
*/
function block_update_8000() {
variable_del('block_cache');
......
......@@ -315,16 +315,15 @@ class ConfOverrideTestCase extends WebTestBase {
}
/**
* Tests function providing configuration upgrade from Drupal 7 to 8.
* Tests migration of variables into configuration objects.
*/
class ConfUpdate7to8TestCase extends WebTestBase {
class ConfigUpgradeTestCase extends WebTestBase {
protected $testContent = 'Olá, Sao Paulo!';
public static function getInfo() {
return array(
'name' => 'Configuration update from Drupal 7 to 8',
'description' => 'Tests the ability to update Drupal 7 variables to the
configuration management system.',
'name' => 'Variable migration',
'description' => 'Tests migration of variables into configuration objects.',
'group' => 'Configuration',
);
}
......@@ -335,18 +334,56 @@ class ConfUpdate7to8TestCase extends WebTestBase {
}
/**
* Test configuration update function.
* Tests update_variables_to_config().
*/
function testConfigurationUpdate() {
// Ensure that the variable table has the object. The variable table will
// remain in place for Drupal 8 to provide an upgrade path for overridden
// variables.
db_merge('variable')->key(array('name' => 'config_test_foo'))->fields(array('value' => serialize($this->testContent)))->execute();
db_merge('variable')->key(array('name' => 'config_bar'))->fields(array('value' => serialize($this->testContent)))->execute();
update_variables_to_config('config.test', array('config_test_bar' => 'config_bar'));
$config = config('config.test');
$this->assertEqual($config->get('config_test_foo'), $this->testContent);
$this->assertEqual($config->get('config_test_bar'), $this->testContent);
db_insert('variable')
->fields(array('name', 'value'))
->values(array('config_upgrade_foo', serialize($this->testContent)))
->values(array('config_upgrade_bar', serialize($this->testContent)))
->execute();
// Perform migration.
update_variables_to_config('config_upgrade.test', array(
'config_upgrade_bar' => 'parent.bar',
'config_upgrade_foo' => 'foo',
// A default configuration value for which no variable exists.
'config_upgrade_baz' => 'parent.baz',
));
// Verify that variables have been converted and default values exist.
$config = config('config_upgrade.test');
$this->assertIdentical($config->get('foo'), $this->testContent);
$this->assertIdentical($config->get('parent.bar'), $this->testContent);
$this->assertIdentical($config->get('parent.baz'), 'Baz');
// Verify that variables have been deleted.
$variables = db_query('SELECT name FROM {variable} WHERE name IN (:names)', array(':names' => array('config_upgrade_bar', 'config_upgrade_foo')))->fetchCol();
$this->assertFalse($variables);
// Add another variable to migrate into the same config object.
db_insert('variable')
->fields(array('name', 'value'))
->values(array('config_upgrade_additional', serialize($this->testContent)))
->execute();
// Perform migration into the exsting config object.
update_variables_to_config('config_upgrade.test', array(
'config_upgrade_additional' => 'parent.additional',
));
// Verify that new variables have been converted and existing still exist.
$config = config('config_upgrade.test');
$this->assertIdentical($config->get('foo'), $this->testContent);
$this->assertIdentical($config->get('parent.bar'), $this->testContent);
$this->assertIdentical($config->get('parent.baz'), 'Baz');
$this->assertIdentical($config->get('parent.additional'), $this->testContent);
// Verify that variables have been deleted.
$variables = db_query('SELECT name FROM {variable} WHERE name IN (:names)', array(':names' => array('config_upgrade_additional')))->fetchCol();
$this->assertFalse($variables);
}
}
......@@ -157,6 +157,8 @@ function locale_update_8000() {
/**
* Language type 'language' renamed to 'language_interface'.
*
* @ingroup config_upgrade
*/
function locale_update_8001() {
// Only change language_types if we had this setting saved. Keep order
......@@ -242,6 +244,8 @@ function locale_update_8002() {
/**
* Converts language domains to new format.
*
* @ingroup config_upgrade
*/
function locale_update_8003() {
$message = '';
......@@ -274,6 +278,8 @@ function locale_update_8003() {
/**
* Rename language providers to language negotiation methods.
*
* @ingroup config_upgrade
*/
function locale_update_8004() {
$types = variable_get('language_types', NULL);
......@@ -437,6 +443,8 @@ function locale_update_8006() {
/**
* Convert language_negotiation_* variables to use the new callbacks.
*
* @ingroup config_upgrade
*/
function locale_update_8007() {
$variable_names = array(
......@@ -510,6 +518,8 @@ function locale_update_8007() {
/**
* Rename the option variables of the locale language negotiation.
*
* @ingroup config_upgrade
*/
function locale_update_8008() {
$variable_name_map = array(
......
......@@ -510,27 +510,12 @@ function _update_7000_node_get_types() {
* @{
*/
/**
* Set 'node' as front page path if it implicitly was before.
*
* Node module became optional. The default front page path was changed to
* 'user'. Since 'node' was the implicit default front page path previously and
* may not have been explicitly configured as such, this update ensures that the
* old implicit default is still the default.
*
* @see http://drupal.org/node/375397
*/
function node_update_8000() {
$front_page = variable_get('site_frontpage');
if (!isset($front_page)) {
variable_set('site_frontpage', 'node');
}
}
/**
* Rename node type language variable names.
*
* @see http://drupal.org/node/540294
*
* @ingroup config_upgrade
*/
function node_update_8001() {
$types = db_query('SELECT type FROM {node_type}')->fetchCol();
......
......@@ -1687,6 +1687,17 @@ function system_update_last_removed() {
return 7069;
}
/**
* @defgroup config_upgrade Configuration system upgrade functions
* @{
* Module update functions that
* - update variables prior to configuration system conversions
* - convert variables to the new configuration system
* - update configuration system values after conversion
*
* @} End of "defgroup config_upgrade".
*/
/**
* @defgroup updates-7.x-to-8.x Updates from 7.x to 8.x
* @{
......@@ -1713,7 +1724,14 @@ function system_update_8001() {
}
/**
* Set Bartik as default theme if it implicitly was the default before.
* Set 'node' as front page path and Bartik as default theme if it implicitly was before.
*
* Node module became optional. The default front page path was changed to
* 'user'. Since 'node' was the implicit default front page path previously and
* may not have been explicitly configured as such, this update ensures that the
* old implicit default is still the default.
*
* @see http://drupal.org/node/375397
*
* The default theme for Drupal core was changed from Bartik to Stark.
* Installation profiles (including Standard and Minimal) were changed to
......@@ -1723,8 +1741,14 @@ function system_update_8001() {
* default.
*
* @see http://drupal.org/node/1181776
*
* @ingroup config_upgrade
*/
function system_update_8002() {
$front_page = variable_get('site_frontpage');
if (!isset($front_page)) {
variable_set('site_frontpage', 'node');
}
$theme = variable_get('theme_default');
if (!isset($theme)) {
variable_set('theme_default', 'bartik');
......@@ -1864,16 +1888,30 @@ function system_update_8008() {
/**
* Moves cron system settings from variable to config.
*
* @ingroup config_upgrade
*/
function system_update_8009() {
update_variables_to_config('system.cron');
update_variables_to_config('system.cron', array(
'cron_max_threshold' => 'cron_max_threshold',
'cron_safe_threshold' => 'cron_safe_threshold',
'cron_threshold_warning' => 'cron_threshold_warning',
'cron_threshold_error' => 'cron_threshold_error',
'cron_key' => 'cron_key',
));
}
/**
* Moves system settings from variable to config.
* Moves RSS system settings from variable to config.
*
* @ingroup config_upgrade
*/
function system_update_8010() {
update_variables_to_config('system.rss-publishing');
update_variables_to_config('system.rss-publishing', array(
'feed_description' => 'feed_description',
'feed_default_items' => 'feed_default_items',
'feed_item_length' => 'feed_item_length',
));
}
/**
......
......@@ -128,6 +128,7 @@ abstract class UpgradePathTestCase extends WebTestBase {
$this->prepareD8Session();
// Restore necessary variables.
// @todo Convert into config('system.site')->set('mail')?
$this->variable_set('site_mail', 'simpletest@example.com');
drupal_set_time_limit($this->timeLimit);
......@@ -142,6 +143,8 @@ abstract class UpgradePathTestCase extends WebTestBase {
* @param $value
* The value to set. This can be any PHP data type; these functions take care
* of serialization as necessary.
*
* @todo Update for D8 configuration system.
*/
protected function variable_set($name, $value) {
db_delete('variable')
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment