? update.inc.patch ? update.sh ? includes/update.inc Index: update.php =================================================================== RCS file: /cvs/drupal/drupal/update.php,v retrieving revision 1.279 diff -u -u -p -r1.279 update.php --- update.php 9 Mar 2009 20:58:19 -0000 1.279 +++ update.php 24 Mar 2009 20:16:56 -0000 @@ -26,173 +26,6 @@ define('DRUPAL_ROOT', getcwd()); */ define('MAINTENANCE_MODE', 'update'); -/** - * Add a column to a database using syntax appropriate for PostgreSQL. - * Save result of SQL commands in $ret array. - * - * Note: when you add a column with NOT NULL and you are not sure if there are - * already rows in the table, you MUST also add DEFAULT. Otherwise PostgreSQL - * won't work when the table is not empty, and db_add_column() will fail. - * To have an empty string as the default, you must use: 'default' => "''" - * in the $attributes array. If NOT NULL and DEFAULT are set the PostgreSQL - * version will set values of the added column in old rows to the - * DEFAULT value. - * - * @param $ret - * Array to which results will be added. - * @param $table - * Name of the table, without {} - * @param $column - * Name of the column - * @param $type - * Type of column - * @param $attributes - * Additional optional attributes. Recognized attributes: - * not null => TRUE|FALSE - * default => NULL|FALSE|value (the value must be enclosed in '' marks) - * @return - * nothing, but modifies $ret parameter. - */ -function db_add_column(&$ret, $table, $column, $type, $attributes = array()) { - if (array_key_exists('not null', $attributes) and $attributes['not null']) { - $not_null = 'NOT NULL'; - } - if (array_key_exists('default', $attributes)) { - if (is_null($attributes['default'])) { - $default_val = 'NULL'; - $default = 'default NULL'; - } - elseif ($attributes['default'] === FALSE) { - $default = ''; - } - else { - $default_val = "$attributes[default]"; - $default = "default $attributes[default]"; - } - } - - $ret[] = update_sql("ALTER TABLE {" . $table . "} ADD $column $type"); - if (!empty($default)) { - $ret[] = update_sql("ALTER TABLE {" . $table . "} ALTER $column SET $default"); - } - if (!empty($not_null)) { - if (!empty($default)) { - $ret[] = update_sql("UPDATE {" . $table . "} SET $column = $default_val"); - } - $ret[] = update_sql("ALTER TABLE {" . $table . "} ALTER $column SET NOT NULL"); - } -} - -/** - * Change a column definition using syntax appropriate for PostgreSQL. - * Save result of SQL commands in $ret array. - * - * Remember that changing a column definition involves adding a new column - * and dropping an old one. This means that any indices, primary keys and - * sequences from serial-type columns are dropped and might need to be - * recreated. - * - * @param $ret - * Array to which results will be added. - * @param $table - * Name of the table, without {} - * @param $column - * Name of the column to change - * @param $column_new - * New name for the column (set to the same as $column if you don't want to change the name) - * @param $type - * Type of column - * @param $attributes - * Additional optional attributes. Recognized attributes: - * not null => TRUE|FALSE - * default => NULL|FALSE|value (with or without '', it won't be added) - * @return - * nothing, but modifies $ret parameter. - */ -function db_change_column(&$ret, $table, $column, $column_new, $type, $attributes = array()) { - if (array_key_exists('not null', $attributes) and $attributes['not null']) { - $not_null = 'NOT NULL'; - } - if (array_key_exists('default', $attributes)) { - if (is_null($attributes['default'])) { - $default_val = 'NULL'; - $default = 'default NULL'; - } - elseif ($attributes['default'] === FALSE) { - $default = ''; - } - else { - $default_val = "$attributes[default]"; - $default = "default $attributes[default]"; - } - } - - $ret[] = update_sql("ALTER TABLE {" . $table . "} RENAME $column TO " . $column . "_old"); - $ret[] = update_sql("ALTER TABLE {" . $table . "} ADD $column_new $type"); - $ret[] = update_sql("UPDATE {" . $table . "} SET $column_new = " . $column . "_old"); - if ($default) { - $ret[] = update_sql("ALTER TABLE {" . $table . "} ALTER $column_new SET $default"); - } - if ($not_null) { - $ret[] = update_sql("ALTER TABLE {" . $table . "} ALTER $column_new SET NOT NULL"); - } - $ret[] = update_sql("ALTER TABLE {" . $table . "} DROP " . $column . "_old"); -} - -/** - * Perform one update and store the results which will later be displayed on - * the finished page. - * - * An update function can force the current and all later updates for this - * module to abort by returning a $ret array with an element like: - * $ret['#abort'] = array('success' => FALSE, 'query' => 'What went wrong'); - * The schema version will not be updated in this case, and all the - * aborted updates will continue to appear on update.php as updates that - * have not yet been run. - * - * @param $module - * The module whose update will be run. - * @param $number - * The update number to run. - * @param $context - * The batch context array - */ -function update_do_one($module, $number, &$context) { - // If updates for this module have been aborted - // in a previous step, go no further. - if (!empty($context['results'][$module]['#abort'])) { - return; - } - - $function = $module . '_update_' . $number; - if (function_exists($function)) { - $ret = $function($context['sandbox']); - } - - if (isset($ret['#finished'])) { - $context['finished'] = $ret['#finished']; - unset($ret['#finished']); - } - - if (!isset($context['results'][$module])) { - $context['results'][$module] = array(); - } - if (!isset($context['results'][$module][$number])) { - $context['results'][$module][$number] = array(); - } - $context['results'][$module][$number] = array_merge($context['results'][$module][$number], $ret); - - if (!empty($ret['#abort'])) { - $context['results'][$module]['#abort'] = TRUE; - } - // Record the schema update if it was completed successfully. - if ($context['finished'] == 1 && empty($context['results'][$module]['#abort'])) { - drupal_set_installed_schema_version($module, $number); - } - - $context['message'] = 'Updating ' . check_plain($module) . ' module'; -} - function update_selection_page() { drupal_set_title('Drupal database update'); $output = drupal_get_form('update_script_selection_form'); @@ -445,167 +278,6 @@ function update_access_denied_page() { } /** - * Create the batch table. - * - * This is part of the Drupal 5.x to 6.x migration. - */ -function update_create_batch_table() { - - // If batch table exists, update is not necessary - if (db_table_exists('batch')) { - return; - } - - $schema['batch'] = array( - 'fields' => array( - 'bid' => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE), - 'token' => array('type' => 'varchar', 'length' => 64, 'not null' => TRUE), - 'timestamp' => array('type' => 'int', 'not null' => TRUE), - 'batch' => array('type' => 'text', 'not null' => FALSE, 'size' => 'big') - ), - 'primary key' => array('bid'), - 'indexes' => array('token' => array('token')), - ); - - $ret = array(); - db_create_table($ret, 'batch', $schema['batch']); - return $ret; -} - -/** - * Disable anything in the {system} table that is not compatible with the - * current version of Drupal core. - */ -function update_fix_compatibility() { - $ret = array(); - $incompatible = array(); - $query = db_query("SELECT name, type, status FROM {system} WHERE status = 1 AND type IN ('module','theme')"); - while ($result = db_fetch_object($query)) { - if (update_check_incompatibility($result->name, $result->type)) { - $incompatible[] = $result->name; - } - } - if (!empty($incompatible)) { - $ret[] = update_sql("UPDATE {system} SET status = 0 WHERE name IN ('" . implode("','", $incompatible) . "')"); - } - return $ret; -} - -/** - * Helper function to test compatibility of a module or theme. - */ -function update_check_incompatibility($name, $type = 'module') { - static $themes, $modules; - - // Store values of expensive functions for future use. - if (empty($themes) || empty($modules)) { - $themes = _system_theme_data(); - $modules = module_rebuild_cache(); - } - - if ($type == 'module' && isset($modules[$name])) { - $file = $modules[$name]; - } - elseif ($type == 'theme' && isset($themes[$name])) { - $file = $themes[$name]; - } - if (!isset($file) - || !isset($file->info['core']) - || $file->info['core'] != DRUPAL_CORE_COMPATIBILITY - || version_compare(phpversion(), $file->info['php']) < 0 - || ($type == 'module' && empty($file->info['files']))) { - return TRUE; - } - return FALSE; -} - -/** - * Perform Drupal 5.x to 6.x updates that are required for update.php - * to function properly. - * - * This function runs when update.php is run the first time for 6.x, - * even before updates are selected or performed. It is important - * that if updates are not ultimately performed that no changes are - * made which make it impossible to continue using the prior version. - * Just adding columns is safe. However, renaming the - * system.description column to owner is not. Therefore, we add the - * system.owner column and leave it to system_update_6008() to copy - * the data from description and remove description. The same for - * renaming locales_target.locale to locales_target.language, which - * will be finished by locale_update_6002(). - */ -function update_fix_d6_requirements() { - $ret = array(); - - if (drupal_get_installed_schema_version('system') < 6000 && !variable_get('update_d6_requirements', FALSE)) { - $spec = array('type' => 'int', 'size' => 'small', 'default' => 0, 'not null' => TRUE); - db_add_field($ret, 'cache', 'serialized', $spec); - db_add_field($ret, 'cache_filter', 'serialized', $spec); - db_add_field($ret, 'cache_page', 'serialized', $spec); - db_add_field($ret, 'cache_menu', 'serialized', $spec); - - db_add_field($ret, 'system', 'info', array('type' => 'text')); - db_add_field($ret, 'system', 'owner', array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => '')); - if (db_table_exists('locales_target')) { - db_add_field($ret, 'locales_target', 'language', array('type' => 'varchar', 'length' => 12, 'not null' => TRUE, 'default' => '')); - } - if (db_table_exists('locales_source')) { - db_add_field($ret, 'locales_source', 'textgroup', array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => 'default')); - db_add_field($ret, 'locales_source', 'version', array('type' => 'varchar', 'length' => 20, 'not null' => TRUE, 'default' => 'none')); - } - variable_set('update_d6_requirements', TRUE); - - // Create the cache_block table. See system_update_6027() for more details. - $schema['cache_block'] = array( - 'fields' => array( - 'cid' => array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => ''), - 'data' => array('type' => 'blob', 'not null' => FALSE, 'size' => 'big'), - 'expire' => array('type' => 'int', 'not null' => TRUE, 'default' => 0), - 'created' => array('type' => 'int', 'not null' => TRUE, 'default' => 0), - 'headers' => array('type' => 'text', 'not null' => FALSE), - 'serialized' => array('type' => 'int', 'size' => 'small', 'not null' => TRUE, 'default' => 0) - ), - 'indexes' => array('expire' => array('expire')), - 'primary key' => array('cid'), - ); - db_create_table($ret, 'cache_block', $schema['cache_block']); - } - - return $ret; -} - -/** - * Users who still have a Drupal 6 database (and are in the process of - * updating to Drupal 7) need extra help before a full bootstrap can be - * achieved. This function does the necessary preliminary work that allows - * the bootstrap to be successful. - * - * No access check has been performed when this function is called, so no - * changes to the database should be made here. - */ -function update_prepare_d7_bootstrap() { - // Allow the database system to work even though the registry has not - // been created yet. - drupal_bootstrap(DRUPAL_BOOTSTRAP_DATABASE); - include_once DRUPAL_ROOT . '/includes/install.inc'; - drupal_install_init_database(); - spl_autoload_unregister('drupal_autoload_class'); - spl_autoload_unregister('drupal_autoload_interface'); - // The new {blocked_ips} table is used in Drupal 7 to store a list of - // banned IP addresses. If this table doesn't exist then we are still - // running on a Drupal 6 database, so suppress the unavoidable errors - // that occur. - try { - drupal_bootstrap(DRUPAL_BOOTSTRAP_ACCESS); - } - catch (Exception $e) { - if (db_table_exists('blocked_ips')) { - throw $e; - } - } -} - -/** * Add the update task list to the current page. */ function update_task_list($active = NULL) { @@ -620,62 +292,6 @@ function update_task_list($active = NULL drupal_set_content('left', theme('task_list', $tasks, $active)); } -/** - * Check update requirements and report any errors. - */ -function update_check_requirements() { - global $db_url, $databases; - $requirements = array(); - - // If we will rewrite the settings.php then we need to make sure it is - // writeable. - if (empty($databases) && !empty($db_url) && is_string($db_url)) { - $requirements = install_check_requirements('', FALSE); - } - $warnings = FALSE; - - // Check the system module requirements only. - $requirements += module_invoke('system', 'requirements', 'update'); - $severity = drupal_requirements_severity($requirements); - - // If there are issues, report them. - if ($severity != REQUIREMENT_OK) { - foreach ($requirements as $requirement) { - if (isset($requirement['severity']) && $requirement['severity'] != REQUIREMENT_OK) { - $message = isset($requirement['description']) ? $requirement['description'] : ''; - if (isset($requirement['value']) && $requirement['value']) { - $message .= ' (Currently using ' . $requirement['title'] . ' ' . $requirement['value'] . ')'; - } - $warnings = TRUE; - drupal_set_message($message, 'warning'); - } - } - } - return $warnings; -} - -/** - * Converts Drupal 6 $db_url to Drupal 7 $databases array. - */ -function update_check_d7_settings() { - global $db_url, $databases; - - if (empty($databases) && !empty($db_url) && is_string($db_url)) { - $url = parse_url($db_url); - $driver = substr($db_url, 0, strpos($db_url, '://')); - if ($driver == 'mysqli') { - $driver = 'mysql'; - } - $databases['default']['default']['driver'] = $driver; - $databases['default']['default']['database'] = substr($url['path'], 1); - foreach (array('user' => 'username', 'pass' => 'password', 'host' => 'host', 'port' => 'port') as $old_key => $new_key) { - $databases['default']['default'][$new_key] = isset($url[$old_key]) ? urldecode($url[$old_key]) : ''; - } - $conf_path = conf_path(); - file_put_contents($conf_path .'/settings.php', "\n" . '$databases = '. var_export($databases, TRUE) . ';', FILE_APPEND); - } -} - // Some unavoidable errors happen because the database is not yet up-to-date. // Our custom error handler is not yet installed, so we just suppress them. ini_set('display_errors', FALSE); @@ -690,6 +306,7 @@ if (empty($op)) { drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION); require_once DRUPAL_ROOT . '/includes/install.inc'; + require_once DRUPAL_ROOT . '/includes/update.inc'; require_once DRUPAL_ROOT . '/includes/file.inc'; require_once DRUPAL_ROOT . '/modules/system/system.install'; --- /dev/null 2009-03-23 21:07:08.553263372 -0400 +++ includes/update.inc 2009-03-24 16:07:05.000000000 -0400 @@ -0,0 +1,397 @@ + "''" + * in the $attributes array. If NOT NULL and DEFAULT are set the PostgreSQL + * version will set values of the added column in old rows to the + * DEFAULT value. + * + * @param $ret + * Array to which results will be added. + * @param $table + * Name of the table, without {} + * @param $column + * Name of the column + * @param $type + * Type of column + * @param $attributes + * Additional optional attributes. Recognized attributes: + * not null => TRUE|FALSE + * default => NULL|FALSE|value (the value must be enclosed in '' marks) + * @return + * nothing, but modifies $ret parameter. + */ +function db_add_column(&$ret, $table, $column, $type, $attributes = array()) { + if (array_key_exists('not null', $attributes) and $attributes['not null']) { + $not_null = 'NOT NULL'; + } + if (array_key_exists('default', $attributes)) { + if (is_null($attributes['default'])) { + $default_val = 'NULL'; + $default = 'default NULL'; + } + elseif ($attributes['default'] === FALSE) { + $default = ''; + } + else { + $default_val = "$attributes[default]"; + $default = "default $attributes[default]"; + } + } + + $ret[] = update_sql("ALTER TABLE {" . $table . "} ADD $column $type"); + if (!empty($default)) { + $ret[] = update_sql("ALTER TABLE {" . $table . "} ALTER $column SET $default"); + } + if (!empty($not_null)) { + if (!empty($default)) { + $ret[] = update_sql("UPDATE {" . $table . "} SET $column = $default_val"); + } + $ret[] = update_sql("ALTER TABLE {" . $table . "} ALTER $column SET NOT NULL"); + } +} + +/** + * Change a column definition using syntax appropriate for PostgreSQL. + * Save result of SQL commands in $ret array. + * + * Remember that changing a column definition involves adding a new column + * and dropping an old one. This means that any indices, primary keys and + * sequences from serial-type columns are dropped and might need to be + * recreated. + * + * @param $ret + * Array to which results will be added. + * @param $table + * Name of the table, without {} + * @param $column + * Name of the column to change + * @param $column_new + * New name for the column (set to the same as $column if you don't want to change the name) + * @param $type + * Type of column + * @param $attributes + * Additional optional attributes. Recognized attributes: + * not null => TRUE|FALSE + * default => NULL|FALSE|value (with or without '', it won't be added) + * @return + * nothing, but modifies $ret parameter. + */ +function db_change_column(&$ret, $table, $column, $column_new, $type, $attributes = array()) { + if (array_key_exists('not null', $attributes) and $attributes['not null']) { + $not_null = 'NOT NULL'; + } + if (array_key_exists('default', $attributes)) { + if (is_null($attributes['default'])) { + $default_val = 'NULL'; + $default = 'default NULL'; + } + elseif ($attributes['default'] === FALSE) { + $default = ''; + } + else { + $default_val = "$attributes[default]"; + $default = "default $attributes[default]"; + } + } + + $ret[] = update_sql("ALTER TABLE {" . $table . "} RENAME $column TO " . $column . "_old"); + $ret[] = update_sql("ALTER TABLE {" . $table . "} ADD $column_new $type"); + $ret[] = update_sql("UPDATE {" . $table . "} SET $column_new = " . $column . "_old"); + if ($default) { + $ret[] = update_sql("ALTER TABLE {" . $table . "} ALTER $column_new SET $default"); + } + if ($not_null) { + $ret[] = update_sql("ALTER TABLE {" . $table . "} ALTER $column_new SET NOT NULL"); + } + $ret[] = update_sql("ALTER TABLE {" . $table . "} DROP " . $column . "_old"); +} + +/** + * Disable anything in the {system} table that is not compatible with the + * current version of Drupal core. + */ +function update_fix_compatibility() { + $ret = array(); + $incompatible = array(); + $query = db_query("SELECT name, type, status FROM {system} WHERE status = 1 AND type IN ('module','theme')"); + while ($result = db_fetch_object($query)) { + if (update_check_incompatibility($result->name, $result->type)) { + $incompatible[] = $result->name; + } + } + if (!empty($incompatible)) { + $ret[] = update_sql("UPDATE {system} SET status = 0 WHERE name IN ('" . implode("','", $incompatible) . "')"); + } + return $ret; +} + +/** + * Helper function to test compatibility of a module or theme. + */ +function update_check_incompatibility($name, $type = 'module') { + static $themes, $modules; + + // Store values of expensive functions for future use. + if (empty($themes) || empty($modules)) { + $themes = _system_theme_data(); + $modules = module_rebuild_cache(); + } + + if ($type == 'module' && isset($modules[$name])) { + $file = $modules[$name]; + } + elseif ($type == 'theme' && isset($themes[$name])) { + $file = $themes[$name]; + } + if (!isset($file) + || !isset($file->info['core']) + || $file->info['core'] != DRUPAL_CORE_COMPATIBILITY + || version_compare(phpversion(), $file->info['php']) < 0 + || ($type == 'module' && empty($file->info['files']))) { + return TRUE; + } + return FALSE; +} + +/** + * Perform Drupal 5.x to 6.x updates that are required for update.php + * to function properly. + * + * This function runs when update.php is run the first time for 6.x, + * even before updates are selected or performed. It is important + * that if updates are not ultimately performed that no changes are + * made which make it impossible to continue using the prior version. + * Just adding columns is safe. However, renaming the + * system.description column to owner is not. Therefore, we add the + * system.owner column and leave it to system_update_6008() to copy + * the data from description and remove description. The same for + * renaming locales_target.locale to locales_target.language, which + * will be finished by locale_update_6002(). + */ +function update_fix_d6_requirements() { + $ret = array(); + + if (drupal_get_installed_schema_version('system') < 6000 && !variable_get('update_d6_requirements', FALSE)) { + $spec = array('type' => 'int', 'size' => 'small', 'default' => 0, 'not null' => TRUE); + db_add_field($ret, 'cache', 'serialized', $spec); + db_add_field($ret, 'cache_filter', 'serialized', $spec); + db_add_field($ret, 'cache_page', 'serialized', $spec); + db_add_field($ret, 'cache_menu', 'serialized', $spec); + + db_add_field($ret, 'system', 'info', array('type' => 'text')); + db_add_field($ret, 'system', 'owner', array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => '')); + if (db_table_exists('locales_target')) { + db_add_field($ret, 'locales_target', 'language', array('type' => 'varchar', 'length' => 12, 'not null' => TRUE, 'default' => '')); + } + if (db_table_exists('locales_source')) { + db_add_field($ret, 'locales_source', 'textgroup', array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => 'default')); + db_add_field($ret, 'locales_source', 'version', array('type' => 'varchar', 'length' => 20, 'not null' => TRUE, 'default' => 'none')); + } + variable_set('update_d6_requirements', TRUE); + + // Create the cache_block table. See system_update_6027() for more details. + $schema['cache_block'] = array( + 'fields' => array( + 'cid' => array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => ''), + 'data' => array('type' => 'blob', 'not null' => FALSE, 'size' => 'big'), + 'expire' => array('type' => 'int', 'not null' => TRUE, 'default' => 0), + 'created' => array('type' => 'int', 'not null' => TRUE, 'default' => 0), + 'headers' => array('type' => 'text', 'not null' => FALSE), + 'serialized' => array('type' => 'int', 'size' => 'small', 'not null' => TRUE, 'default' => 0) + ), + 'indexes' => array('expire' => array('expire')), + 'primary key' => array('cid'), + ); + db_create_table($ret, 'cache_block', $schema['cache_block']); + } + + return $ret; +} + +/** + * Users who still have a Drupal 6 database (and are in the process of + * updating to Drupal 7) need extra help before a full bootstrap can be + * achieved. This function does the necessary preliminary work that allows + * the bootstrap to be successful. + * + * No access check has been performed when this function is called, so no + * changes to the database should be made here. + */ +function update_prepare_d7_bootstrap() { + // Allow the database system to work even though the registry has not + // been created yet. + drupal_bootstrap(DRUPAL_BOOTSTRAP_DATABASE); + include_once DRUPAL_ROOT . '/includes/install.inc'; + drupal_install_init_database(); + spl_autoload_unregister('drupal_autoload_class'); + spl_autoload_unregister('drupal_autoload_interface'); + // The new {blocked_ips} table is used in Drupal 7 to store a list of + // banned IP addresses. If this table doesn't exist then we are still + // running on a Drupal 6 database, so suppress the unavoidable errors + // that occur. + try { + drupal_bootstrap(DRUPAL_BOOTSTRAP_ACCESS); + } + catch (Exception $e) { + if (db_table_exists('blocked_ips')) { + throw $e; + } + } +} + +/** + * Check update requirements and report any errors. + */ +function update_check_requirements() { + global $db_url, $databases; + $requirements = array(); + + // If we will rewrite the settings.php then we need to make sure it is + // writeable. + if (empty($databases) && !empty($db_url) && is_string($db_url)) { + $requirements = install_check_requirements('', FALSE); + } + $warnings = FALSE; + + // Check the system module requirements only. + $requirements += module_invoke('system', 'requirements', 'update'); + $severity = drupal_requirements_severity($requirements); + + // If there are issues, report them. + if ($severity != REQUIREMENT_OK) { + foreach ($requirements as $requirement) { + if (isset($requirement['severity']) && $requirement['severity'] != REQUIREMENT_OK) { + $message = isset($requirement['description']) ? $requirement['description'] : ''; + if (isset($requirement['value']) && $requirement['value']) { + $message .= ' (Currently using ' . $requirement['title'] . ' ' . $requirement['value'] . ')'; + } + $warnings = TRUE; + drupal_set_message($message, 'warning'); + } + } + } + return $warnings; +} + +/** + * Create the batch table. + * + * This is part of the Drupal 5.x to 6.x migration. + */ +function update_create_batch_table() { + + // If batch table exists, update is not necessary + if (db_table_exists('batch')) { + return; + } + + $schema['batch'] = array( + 'fields' => array( + 'bid' => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE), + 'token' => array('type' => 'varchar', 'length' => 64, 'not null' => TRUE), + 'timestamp' => array('type' => 'int', 'not null' => TRUE), + 'batch' => array('type' => 'text', 'not null' => FALSE, 'size' => 'big') + ), + 'primary key' => array('bid'), + 'indexes' => array('token' => array('token')), + ); + + $ret = array(); + db_create_table($ret, 'batch', $schema['batch']); + return $ret; +} + +/** + * Converts Drupal 6 $db_url to Drupal 7 $databases array. + */ +function update_check_d7_settings() { + global $db_url, $databases; + + if (empty($databases) && !empty($db_url) && is_string($db_url)) { + $url = parse_url($db_url); + $driver = substr($db_url, 0, strpos($db_url, '://')); + if ($driver == 'mysqli') { + $driver = 'mysql'; + } + $databases['default']['default']['driver'] = $driver; + $databases['default']['default']['database'] = substr($url['path'], 1); + foreach (array('user' => 'username', 'pass' => 'password', 'host' => 'host', 'port' => 'port') as $old_key => $new_key) { + $databases['default']['default'][$new_key] = isset($url[$old_key]) ? urldecode($url[$old_key]) : ''; + } + $conf_path = conf_path(); + file_put_contents($conf_path .'/settings.php', "\n" . '$databases = '. var_export($databases, TRUE) . ';', FILE_APPEND); + } +} + +/** + * Perform one update and store the results which will later be displayed on + * the finished page. + * + * An update function can force the current and all later updates for this + * module to abort by returning a $ret array with an element like: + * $ret['#abort'] = array('success' => FALSE, 'query' => 'What went wrong'); + * The schema version will not be updated in this case, and all the + * aborted updates will continue to appear on update.php as updates that + * have not yet been run. + * + * @param $module + * The module whose update will be run. + * @param $number + * The update number to run. + * @param $context + * The batch context array + */ +function update_do_one($module, $number, &$context) { + // If updates for this module have been aborted + // in a previous step, go no further. + if (!empty($context['results'][$module]['#abort'])) { + return; + } + + $function = $module . '_update_' . $number; + if (function_exists($function)) { + $ret = $function($context['sandbox']); + } + + if (isset($ret['#finished'])) { + $context['finished'] = $ret['#finished']; + unset($ret['#finished']); + } + + if (!isset($context['results'][$module])) { + $context['results'][$module] = array(); + } + if (!isset($context['results'][$module][$number])) { + $context['results'][$module][$number] = array(); + } + $context['results'][$module][$number] = array_merge($context['results'][$module][$number], $ret); + + if (!empty($ret['#abort'])) { + $context['results'][$module]['#abort'] = TRUE; + } + // Record the schema update if it was completed successfully. + if ($context['finished'] == 1 && empty($context['results'][$module]['#abort'])) { + drupal_set_installed_schema_version($module, $number); + } + + $context['message'] = 'Updating ' . check_plain($module) . ' module'; +} + --- /dev/null 2009-03-23 21:07:08.553263372 -0400 +++ update.sh 2009-03-24 16:14:57.000000000 -0400 @@ -0,0 +1,348 @@ +#!/usr/bin/env php + drupal_get_installed_schema_version($module); +} + +function update_show_updates($updates) { + echo t('Modules requiring updates:') . "\n\n"; + $files = module_rebuild_cache(); + foreach ($updates as $module => $update_list) { + echo "{$files[$module]->info['name']} ({$files[$module]->name})\n"; + echo " " . t('Pending updates:') . ' ' . implode(',', $update_list) . "\n\n"; + } +} + +function update_show_versions() { + + $files = module_rebuild_cache(); + + printf("%-20s %-20s %-19s %5s\n\n", 'Name', 'Module', 'Version', 'Schema'); + foreach ($files as $module => $file) { + printf("%-20s %-20s %-19s %5s\n", $file->info['name'], $module, $file->info['version'], $file->schema_version); + } +} + +function update_do_updates($updates) { + global $maintenance; + if ($maintenance) { + $site_status = variable_get('site_offline', 0); + variable_set('site_offline', 1); + if ($maintenance !== TRUE) { + $site_status_message = variable_get('site_offline_message', NULL); + variable_set('site_offine_message', $message); + } + } + $operations = array(); + foreach ($updates as $module => $update_list) { + foreach ($update_list as $update) { + $operations[] = array('update_sh_do_one', array($module, $update)); + } + } + + // Since this is running from the shell, we want to turn progressive off. + $_init_batch =& batch_get(); + if (empty($_init_batch)) { + $_init_batch = array( + 'sets' => array(), + 'progressive' => FALSE, + ); + } + + $batch = array( + 'operations' => $operations, + 'title' => 'Updating', + 'init_message' => 'Starting updates', + 'error_message' => 'An unrecoverable error has occurred. You can find the error message below. It is advised to copy it to the clipboard for reference.', + 'finished' => 'update_sh_finished', + 'progressive' => FALSE, + ); + + batch_set($batch); + batch_process(); + + if ($_SESSION['update_success']) { + $output = 'Updates were attempted. If you see no failures below, you may proceed happily to the administration pages. Otherwise, you may need to update your database manually.' . $log_message . "\n"; + } + else { + list($module, $version) = array_pop(reset($_SESSION['updates_remaining'])); + $output = 'The update process was aborted prematurely while running update #' . $version . ' in ' . $module . '.module.' . $log_message; + if (module_exists('dblog')) { + $output .= ' You may need to check the watchdog database table manually.'; + } + $output .= "\n"; + } + + // Output a list of queries executed + $update_ok = TRUE; + if (!empty($_SESSION['update_results'])) { + $output .= "The following queries were executed\n\n"; + foreach ($_SESSION['update_results'] as $module => $updates) { + $output .= $module . " module\n"; + foreach ($updates as $number => $queries) { + if ($number != '#abort') { + $output .= 'Update #' . $number . "\n"; + foreach ($queries as $query) { + if ($query['success']) { + $output .= '* ' . htmlspecialchars_decode(html_entity_decode($query['query'])) . "\n"; + } + else { + $update_ok = FALSE; + $output .= '* Failed: ' . htmlspecialchars_decode(html_entity_decode($query['query'])) . "\n"; + } + } + if (!count($queries)) { + $output .= "* No queries\n"; + } + } + } + } + echo $output; + } + unset($_SESSION['update_results']); + unset($_SESSION['update_success']); + + if ($maintenance && $update_ok) { + variable_set('site_offline', $site_status); + if (empty($site_status_message)) { + variable_del('site_offine_message'); + } + else { + variable_set('site_offine_message', $site_status_message); + } + } + return $update_ok; +} + +function update_sh_do_one($module, $number, &$context) { + // If updates for this module have been aborted + // in a previous step, go no further. + if (!empty($context['results'][$module]['#abort'])) { + return; + } + + $function = $module . '_update_' . $number; + if (function_exists($function)) { + $ret = $function($context['sandbox']); + } + + if (isset($ret['#finished'])) { + $context['finished'] = $ret['#finished']; + unset($ret['#finished']); + } + + if (!isset($context['results'][$module])) { + $context['results'][$module] = array(); + } + if (!isset($context['results'][$module][$number])) { + $context['results'][$module][$number] = array(); + } + $context['results'][$module][$number] = array_merge($context['results'][$module][$number], $ret); + + if (!empty($ret['#abort'])) { + $context['results'][$module]['#abort'] = TRUE; + } + // Record the schema update if it was completed successfully. + if ($context['finished'] == 1 && empty($context['results'][$module]['#abort'])) { + drupal_set_installed_schema_version($module, $number); + } + + $context['message'] = 'Updating ' . check_plain($module) . ' module'; +} + +function update_sh_finished($success, $results, $operations) { + // clear the caches in case the data has been updated. + drupal_flush_all_caches(); + + $_SESSION['update_results'] = $results; + $_SESSION['update_success'] = $success; + $_SESSION['updates_remaining'] = $operations; +} + +$script = basename(array_shift($_SERVER['argv'])); + +$shortopts = 'hr:s:lum::ve'; +$longopts = array('help', 'root:', 'site:', 'list', 'update', + 'maintenance::', 'verbose', 'versions'); + +$args = @getopt($shortopts, $longopts); + +if (isset($args['h']) || isset($args['help'])) { + echo <<" +Example: {$script} "http://mysite.org/node" + +All arguments are long options. + + -h, --help This page. + + -r, --root Set the working directory for the script to the specified path. + To execute Drupal this has to be the root directory of your + Drupal installation, f.e. /home/www/foo/drupal (assuming Drupal + running on Unix). Current directory is not required. + Use surrounding quotation marks on Windows. + + -s, --site Used to specify with site will be used for the upgrade. If no + site is selected then default will be used. + + -l, --list List all pending updates. If there are updates available then + the script with exit with a 1 return flag + + -u, --update Process all pending updates. If there are any updates are not + processed successfully then the script with exit with a return + flag of 1. + + -m, --maintenance Set the site into maintenance while the updates are being + run. If there are any failures, the site will remain in + maintenance mode. An optional message can be specified which + will set the message on the maintenance screen. + + -v, --verbose This option displays the options as they are set, but will + produce errors from setting the session. + + -e, --versions List the versions of all the modules in the system. + +To run this script without --root argument invoke it from the root directory +of your Drupal installation with + + ./scripts/{$script} + +\n +EOF; + if (version_compare(phpversion(), '5.3.0', 'le')) { + echo "Warning: This version of PHP doesn't support long options\n"; + } + exit; +} + +// define default settings +$_SERVER['HTTP_HOST'] = 'default'; +$_SERVER['PHP_SELF'] = '/update.php'; +$_SERVER['REMOTE_ADDR'] = '127.0.0.1'; +$_SERVER['SERVER_SOFTWARE'] = 'PHP CLI'; +$_SERVER['REQUEST_METHOD'] = 'GET'; +$_SERVER['QUERY_STRING'] = ''; +$_SERVER['PHP_SELF'] = $_SERVER['REQUEST_URI'] = '/'; +$_SERVER['SCRIPT_NAME'] = '/' . basename($_SERVER['SCRIPT_NAME']); +$_SERVER['HTTP_USER_AGENT'] = 'console'; + +// toggle verbose mode +$_verbose_mode = isset($args['h']) || isset($args['help']) ? TRUE : FALSE; + +$maintenance = TRUE; + +// parse invocation arguments +if (isset($args['r']) || isset($args['root'])) { + // change working directory + $path = isset($args['r']) ? $args['r'] : $args['root']; + if (is_dir($path)) { + chdir($path); + if ($_verbose_mode) { + echo "cwd changed to: {$path}\n"; + } + } + else { + echo "\nERROR: {$path} not found.\n\n"; + } +} + +if (isset($args['s']) || isset($args['site'])) { + $site = isset($args['s']) ? $args['s'] : $args['site']; + if (file_exists('./sites/' . $site)) { + $_SERVER['HTTP_HOST'] = $site; + $_SERVER['PHP_SELF'] = $_SERVER['REQUEST_URI'] = '/update.php'; + } + else { + echo "ERROR: Unable to locate site {$site}\n"; + exit(1); + } +} + +if (isset($args['m']) || isset($args['maintenance'])) { + $opt = isset($args['m']) ? $args['m'] : (isset($args['maintenance']) ? $args['maintenance'] : NULL); + if (is_string($opt)) { + $maintenance = $opt; + } +} + +define('DRUPAL_ROOT', realpath(getcwd())); + +include_once './includes/bootstrap.inc'; +drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL); +include_once './includes/install.inc'; +include_once './includes/update.inc'; +include_once './includes/form.inc'; +drupal_load_updates(); + + + +$user = user_load(array('uid' => 1)); +foreach ($args as $arg => $option) { + switch ($arg) { + case 'l': + case 'list': + if ($updates = drupal_get_updates()) { + update_show_updates($updates); + exit(1); + } + else { + echo t('No updates available') . "\n"; + } + break 2; + + case 'e': + case 'versions': + update_show_versions(); + break 2; + + case 'u': + case 'update': + if ($updates = drupal_get_updates()) { + if (!update_do_updates($updates)) { + exit(1); + } + } + break 2; + } +} +exit();