Commit ec407ec9 authored by webchick's avatar webchick

#211182 by Damien Tournoud, David_Rothstein, clemens.tolboom, scor, hunmonk,...

#211182 by Damien Tournoud, David_Rothstein, clemens.tolboom, scor, hunmonk, et al: Allow updates to specify dependencies to ensure they run in a predictable order.
parent 59c9219f
......@@ -131,7 +131,8 @@ function drupal_get_schema_versions($module) {
* Set to TRUE if you want to get information about all modules in the
* system.
* @return
* The currently installed schema version.
* The currently installed schema version, or SCHEMA_UNINSTALLED if the
* module is not installed.
*/
function drupal_get_installed_schema_version($module, $reset = FALSE, $array = FALSE) {
static $versions = array();
......@@ -148,7 +149,12 @@ function drupal_get_installed_schema_version($module, $reset = FALSE, $array = F
}
}
return $array ? $versions : $versions[$module];
if ($array) {
return $versions;
}
else {
return isset($versions[$module]) ? $versions[$module] : SCHEMA_UNINSTALLED;
}
}
/**
......
This diff is collapsed.
......@@ -267,9 +267,19 @@ function block_update_7001() {
}
/**
* Change the weight column to normal int.
* Rename {blocks} table to {block}, {blocks_roles} to {block_role} and
* {boxes} to {block_custom}.
*/
function block_update_7002() {
db_rename_table('blocks', 'block');
db_rename_table('blocks_roles', 'block_role');
db_rename_table('boxes', 'block_custom');
}
/**
* Change the weight column to normal int.
*/
function block_update_7003() {
db_drop_index('block', 'list');
db_change_field('block', 'weight', 'weight', array(
'type' => 'int',
......@@ -282,3 +292,162 @@ function block_update_7002() {
),
));
}
/**
* Add new blocks to new regions, migrate custom variables to blocks.
*/
function block_update_7004() {
// Collect a list of themes with blocks.
$themes_with_blocks = array();
$result = db_query("SELECT s.name FROM {system} s INNER JOIN {block} b ON s.name = b.theme WHERE s.type = 'theme' GROUP by s.name");
$insert = db_insert('block')->fields(array('module', 'delta', 'theme', 'status', 'weight', 'region', 'pages', 'cache'));
foreach ($result as $theme) {
$themes_with_blocks[] = $theme->name;
// Add new system generated help block.
$insert->values(array(
'module' => 'system',
'delta' => 'help',
'theme' => $theme->name,
'status' => 1,
'weight' => 0,
'region' => 'help',
'pages' => '',
'cache' => 1,
));
// Add new system generated main page content block.
$insert->values(array(
'module' => 'system',
'delta' => 'main',
'theme' => $theme->name,
'status' => 1,
'weight' => 0,
'region' => 'content',
'pages' => '',
'cache' => -1,
));
}
$insert->execute();
// Migrate blocks from left/right regions to first/second regions.
db_update('block')
->fields(array('region' => 'sidebar_first'))
->condition('region', 'left')
->execute();
db_update('block')
->fields(array('region' => 'sidebar_second'))
->condition('region', 'right')
->execute();
// Migrate contact form information.
$default_format = variable_get('filter_default_format', 1);
if ($contact_help = variable_get('contact_form_information', '')) {
$bid = db_insert('block_custom')
->fields(array(
'body' => $contact_help,
'info' => 'Contact page help',
'format' => $default_format,
))
->execute();
$insert = db_insert('block')->fields(array('module', 'delta', 'theme', 'status', 'weight', 'region', 'visibility', 'pages', 'cache'));
foreach ($themes_with_blocks as $theme) {
// Add contact help block for themes, which had blocks.
$insert->values(array(
'module' => 'block',
'delta' => $bid,
'theme' => $theme,
'status' => 1,
'weight' => 5,
'region' => 'help',
'visibility' => 1,
'pages' => 'contact',
'cache' => -1,
));
}
drupal_set_message('The contact form information setting was migrated to <a href="' . url('admin/structure/block/manage/block/' . $bid . '/configure') . '">a custom block</a> and set up to only show on the site-wide contact page. The block was set to use the default text format, which might differ from the HTML based format used before. Check the block and ensure that the output is right.');
}
$insert->execute();
// Migrate user help setting.
if ($user_help = variable_get('user_registration_help', '')) {
$bid = db_insert('block_custom')->fields(array('body' => $user_help, 'info' => 'User registration guidelines', 'format' => $default_format))->execute();
$insert = db_insert('block')->fields(array('module', 'delta', 'theme', 'status', 'weight', 'region', 'visibility', 'pages', 'cache'));
foreach ($themes_with_blocks as $theme) {
// Add user registration help block for themes, which had blocks.
$insert->values(array(
'module' => 'block',
'delta' => $bid,
'theme' => $theme,
'status' => 1,
'weight' => 5,
'region' => 'help',
'visibility' => 1,
'pages' => 'user/register',
'cache' => -1,
));
}
drupal_set_message('The user registration guidelines were migrated to <a href="' . url('admin/structure/block/manage/block/' . $bid . '/configure') . '">a custom block</a> and set up to only show on the user registration page. The block was set to use the default text format, which might differ from the HTML based format used before. Check the block and ensure that the output is right.');
$insert->execute();
}
// Migrate site mission setting.
if ($mission = variable_get('site_mission')) {
$bid = db_insert('block_custom')->fields(array('body' => $mission, 'info' => 'Site mission', 'format' => $default_format))->execute();
$insert = db_insert('block')->fields(array('module', 'delta', 'theme', 'status', 'weight', 'region', 'visibility', 'pages', 'cache'));
foreach ($themes_with_blocks as $theme) {
// Add mission block for themes, which had blocks.
$insert->values(array(
'module' => 'block',
'delta' => $bid,
'theme' => $theme,
'status' => 1,
'weight' => 0,
'region' => 'highlight',
'visibility' => 1,
'pages' => '<front>',
'cache' => -1,
));
}
drupal_set_message('The site mission was migrated to <a href="' . url('admin/structure/block/manage/block/' . $bid . '/configure') . '">a custom block</a> and set up to only show on the front page in the highlighted content region. The block was set to use the default text format, which might differ from the HTML based format used before. Check the block and ensure that the output is right. If your theme does not have a highlighted content region, you might need to <a href="' . url('admin/structure/block') . '">relocate the block</a>.');
$insert->execute();
// Migrate mission to RSS site description.
variable_set('feed_description', $mission);
}
// Migrate site footer message to a custom block.
if ($footer_message = variable_get('site_footer', '')) {
$bid = db_insert('block_custom')->fields(array('body' => $footer_message, 'info' => 'Footer message', 'format' => $default_format))->execute();
$insert = db_insert('block')->fields(array('module', 'delta', 'theme', 'status', 'weight', 'region', 'pages', 'cache'));
foreach ($themes_with_blocks as $theme) {
// Add site footer block for themes, which had blocks.
// Set low weight, so the block comes early (it used to be
// before the other blocks).
$insert->values(array(
'module' => 'block',
'delta' => $bid,
'theme' => $theme,
'status' => 1,
'weight' => -10,
'region' => 'footer',
'pages' => '',
'cache' => -1,
));
}
drupal_set_message('The footer message was migrated to <a href="' . url('admin/structure/block/manage/block/' . $bid . '/configure') . '">a custom block</a> and set up to appear in the footer. The block was set to use the default text format, which might differ from the HTML based format used before. Check the block and ensure that the output is right. If your theme does not have a footer region, you might need to <a href="' . url('admin/structure/block') . '">relocate the block</a>.');
$insert->execute();
}
// Remove the variables (even if they were saved empty on the admin interface),
// to avoid keeping clutter in the variables table.
variable_del('contact_form_information');
variable_del('user_registration_help');
variable_del('site_mission');
variable_del('site_footer');
// Rebuild theme data, so the new 'help' region is identified.
system_rebuild_theme_data();
}
......@@ -75,6 +75,29 @@ function comment_enable() {
}
}
/**
* Implements hook_update_dependencies().
*/
function comment_update_dependencies() {
// Comment update 7005 creates comment Field API bundles and therefore must
// run after the Field module has been enabled, but before upgrading field
// data.
$dependencies['comment'][7005] = array(
'system' => 7049,
);
$dependencies['system'][7050] = array(
'comment' => 7005,
);
// Comment update 7012 creates the comment body field and therefore must run
// after all entities have been updated.
$dependencies['comment'][7012] = array(
'system' => 7021,
);
return $dependencies;
}
/**
* @defgroup updates-6.x-to-7.x Comment updates from 6.x to 7.x
* @{
......@@ -248,9 +271,6 @@ function comment_update_7011() {
* Create the comment_body field.
*/
function comment_update_7012() {
// @todo Remove when http://drupal.org/node/211182 is fixed.
taxonomy_update_7002();
// Create comment body field.
$field = array(
'field_name' => 'comment_body',
......
......@@ -106,6 +106,18 @@ function filter_schema() {
return $schema;
}
/**
* Implements hook_update_dependencies().
*/
function filter_update_dependencies() {
// Filter update 7005 migrates block data and therefore needs to run after
// the {block_custom} table is properly set up.
$dependencies['filter'][7005] = array(
'system' => 7037,
);
return $dependencies;
}
/**
* @defgroup updates-6.x-to-7.x Filter updates from 6.x to 7.x
* @{
......
......@@ -356,6 +356,21 @@ function node_schema() {
return $schema;
}
/**
* Implements hook_update_dependencies().
*/
function node_update_dependencies() {
// Node update 7006 migrates node data to fields and therefore must run after
// the Field module has been enabled, but before upgrading field data.
$dependencies['node'][7006] = array(
'system' => 7049,
);
$dependencies['system'][7050] = array(
'node' => 7006,
);
return $dependencies;
}
/**
* @defgroup updates-6.x-to-7.x System updates from 6.x to 7.x
* @{
......@@ -437,9 +452,6 @@ function node_update_7005() {
* Convert body and teaser from node properties to fields, and migrate status/comment/promote and sticky columns to the {node_revision} table.
*/
function node_update_7006(&$context) {
// @todo Remove when http://drupal.org/node/211182 is fixed.
taxonomy_update_7002();
$context['#finished'] = 0;
// Get node type info for every invocation.
......
......@@ -158,7 +158,7 @@ function profile_update_7001() {
* Change the weight column to normal int.
*/
function profile_update_7002() {
db_change_field('profile', 'weight', 'weight', array(
db_change_field('profile_field', 'weight', 'weight', array(
'type' => 'int',
'not null' => TRUE,
'default' => 0,
......
......@@ -35,4 +35,5 @@ files[] = tests/schema.test
files[] = tests/session.test
files[] = tests/theme.test
files[] = tests/unicode.test
files[] = tests/update.test
files[] = tests/xmlrpc.test
<?php
// $Id$
/**
* @file
* Tests for the update system.
*/
/**
* Tests for the update dependency ordering system.
*/
class UpdateDependencyOrderingTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'Update dependency ordering',
'description' => 'Test that update functions are run in the proper order.',
'group' => 'Update API',
);
}
function setUp() {
parent::setUp('update_test_1', 'update_test_2', 'update_test_3');
require_once DRUPAL_ROOT . '/includes/update.inc';
}
/**
* Test that updates within a single module run in the correct order.
*/
function testUpdateOrderingSingleModule() {
$starting_updates = array(
'update_test_1' => 7000,
);
$expected_updates = array(
'update_test_1_update_7000',
'update_test_1_update_7001',
'update_test_1_update_7002',
);
$actual_updates = array_keys(update_resolve_dependencies($starting_updates));
$this->assertEqual($expected_updates, $actual_updates, t('Updates within a single module run in the correct order.'));
}
/**
* Test that dependencies between modules are resolved correctly.
*/
function testUpdateOrderingModuleInterdependency() {
$starting_updates = array(
'update_test_2' => 7000,
'update_test_3' => 7000,
);
$update_order = array_keys(update_resolve_dependencies($starting_updates));
// Make sure that each dependency is satisfied.
$first_dependency_satisfied = array_search('update_test_2_update_7000', $update_order) < array_search('update_test_3_update_7000', $update_order);
$this->assertTrue($first_dependency_satisfied, t('The dependency of the second module on the first module is respected by the update function order.'));
$second_dependency_satisfied = array_search('update_test_3_update_7000', $update_order) < array_search('update_test_2_update_7001', $update_order);
$this->assertTrue($second_dependency_satisfied, t('The dependency of the first module on the second module is respected by the update function order.'));
}
}
/**
* Tests for missing update dependencies.
*/
class UpdateDependencyMissingTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'Missing update dependencies',
'description' => 'Test that missing update dependencies are correctly flagged.',
'group' => 'Update API',
);
}
function setUp() {
// Only install update_test_2.module, even though its updates have a
// dependency on update_test_3.module.
parent::setUp('update_test_2');
require_once DRUPAL_ROOT . '/includes/update.inc';
}
function testMissingUpdate() {
$starting_updates = array(
'update_test_2' => 7000,
);
$update_graph = update_resolve_dependencies($starting_updates);
$this->assertTrue($update_graph['update_test_2_update_7000']['allowed'], t("The module's first update function is allowed to run, since it does not have any missing dependencies."));
$this->assertFalse($update_graph['update_test_2_update_7001']['allowed'], t("The module's second update function is not allowed to run, since it has a direct dependency on a missing update."));
$this->assertFalse($update_graph['update_test_2_update_7002']['allowed'], t("The module's third update function is not allowed to run, since it has an indirect dependency on a missing update."));
}
}
; $Id$
name = "Update test"
description = "Support module for update testing."
package = Testing
version = VERSION
core = 7.x
files[] = update_test_1.module
files[] = update_test_1.install
hidden = TRUE
<?php
// $Id$
/**
* @file
* Install, update and uninstall functions for the update_test_1 module.
*/
/**
* Dummy update_test_1 update 7000.
*/
function update_test_1_update_7000() {
}
/**
* Dummy update_test_1 update 7001.
*/
function update_test_1_update_7001() {
}
/**
* Dummy update_test_1 update 7002.
*/
function update_test_1_update_7002() {
}
; $Id$
name = "Update test"
description = "Support module for update testing."
package = Testing
version = VERSION
core = 7.x
files[] = update_test_2.module
files[] = update_test_2.install
hidden = TRUE
<?php
// $Id$
/**
* @file
* Install, update and uninstall functions for the update_test_2 module.
*/
/**
* Implements hook_update_dependencies().
*/
function update_test_2_update_dependencies() {
$dependencies['update_test_2'][7001] = array(
'update_test_3' => 7000,
);
return $dependencies;
}
/**
* Dummy update_test_2 update 7000.
*/
function update_test_2_update_7000() {
}
/**
* Dummy update_test_2 update 7001.
*/
function update_test_2_update_7001() {
}
/**
* Dummy update_test_2 update 7002.
*/
function update_test_2_update_7002() {
}
; $Id$
name = "Update test"
description = "Support module for update testing."
package = Testing
version = VERSION
core = 7.x
files[] = update_test_3.module
files[] = update_test_3.install
hidden = TRUE
<?php
// $Id$
/**
* @file
* Install, update and uninstall functions for the update_test_3 module.
*/
/**
* Implements hook_update_dependencies().
*/
function update_test_3_update_dependencies() {
$dependencies['update_test_3'][7000] = array(
'update_test_2' => 7000,
);
return $dependencies;
}
/**
* Dummy update_test_3 update 7000.
*/
function update_test_3_update_7000() {
}
......@@ -2232,6 +2232,51 @@ function hook_update_N(&$sandbox) {
throw new DrupalUpdateException('Something went wrong; here is what you should do.');
}
/**
* Return an array of information about module update dependencies.
*
* This can be used to indicate update functions from other modules that your
* module's update functions depend on, or vice versa. It is used by the update
* system to determine the appropriate order in which updates should be run, as
* well as to search for missing dependencies.
*
* Implementations of this hook should be placed in a mymodule.install file in
* the same directory as mymodule.module.
*
* @return
* A multidimensional array containing information about the module update
* dependencies. The first two levels of keys represent the module and update
* number (respectively) for which information is being returned, and the
* value is an array of information about that update's dependencies. Within
* this array, each key represents a module, and each value represents the
* number of an update function within that module. In the event that your
* update function depends on more than one update from a particular module,
* you should always list the highest numbered one here (since updates within
* a given module always run in numerical order).
*
* @see update_resolve_dependencies()
* @see hook_update_N()
*/
function hook_update_dependencies() {
// Indicate that the mymodule_update_7000() function provided by this module
// must run after the another_module_update_7002() function provided by the
// 'another_module' module.
$dependencies['mymodule'][7000] = array(
'another_module' => 7002,
);
// Indicate that the mymodule_update_7001() function provided by this module
// must run before the yet_another_module_update_7004() function provided by
// the 'yet_another_module' module. (Note that declaring dependencies in this
// direction should be done only in rare situations, since it can lead to the
// following problem: If a site has already run the yet_another_module
// module's database updates before it updates its codebase to pick up the
// newest mymodule code, then the dependency declared here will be ignored.)
$dependencies['yet_another_module'][7004] = array(
'mymodule' => 7001,
);
return $dependencies;
}
/**
* Return a number which is no longer available as hook_update_N().
*
......
......@@ -1882,15 +1882,7 @@ function system_update_7011() {
$insert->execute();
}
/**
* Rename {blocks} table to {block}, {blocks_roles} to {block_role} and
* {boxes} to {box}.
*/
function system_update_7012() {
db_rename_table('blocks', 'block');
db_rename_table('blocks_roles', 'block_role');
db_rename_table('boxes', 'box');
}
// system_update_7012() moved to block_update_7002().
/**
* Convert default time zone offset to default time zone name.
......@@ -2027,176 +2019,10 @@ function system_update_7020() {
module_enable($module_list, FALSE);
}
/**
* Add new blocks to new regions, migrate custom variables to blocks.
*/
function system_update_7021() {
// Collect a list of themes with blocks.
$themes_with_blocks = array();
$result = db_query("SELECT s.name FROM {system} s INNER JOIN {block} b ON s.name = b.theme WHERE s.type = 'theme' GROUP by s.name");
$insert = db_insert('block')->fields(array('module', 'delta', 'theme', 'status', 'weight', 'region', 'pages', 'cache'));
foreach ($result as $theme) {
$themes_with_blocks[] = $theme->name;
// Add new system generated help block.
$insert->values(array(
'module' => 'system',
'delta' => 'help',
'theme' => $theme->name,
'status' => 1,
'weight' => 0,
'region' => 'help',
'pages' => '',
'cache' => 1,
));
// Add new system generated main page content block.
$insert->values(array(
'module' => 'system',
'delta' => 'main',
'theme' => $theme->name,
'status' => 1,
'weight' => 0,