From a19acb219e249693357daeb037165eaf19a70b33 Mon Sep 17 00:00:00 2001 From: Dries Buytaert <dries@buytaert.net> Date: Tue, 6 Dec 2005 09:25:22 +0000 Subject: [PATCH] - Patch #35924 by Neil: improved the update system. --- database/database.mysql | 1 + database/database.pgsql | 1 + database/updates.inc | 401 +++++++------------------ includes/database.mysql.inc | 2 +- includes/database.mysqli.inc | 2 +- includes/database.pgsql.inc | 2 +- includes/theme.inc | 10 + misc/drupal.js | 2 +- misc/maintenance.css | 36 ++- misc/progress.js | 14 +- misc/update.js | 20 ++ update.php | 557 +++++++++++++++++++++++++++++++---- 12 files changed, 679 insertions(+), 369 deletions(-) create mode 100644 misc/update.js diff --git a/database/database.mysql b/database/database.mysql index a5ecd1de4795..7be8d9ecd402 100644 --- a/database/database.mysql +++ b/database/database.mysql @@ -656,6 +656,7 @@ CREATE TABLE system ( status int(2) NOT NULL default '0', throttle tinyint(1) DEFAULT '0' NOT NULL, bootstrap int(2) NOT NULL default '0', + schema_version smallint(2) unsigned NOT NULL, PRIMARY KEY (filename) ) TYPE=MyISAM; diff --git a/database/database.pgsql b/database/database.pgsql index 59ce317f30d4..5dfee4889fb6 100644 --- a/database/database.pgsql +++ b/database/database.pgsql @@ -650,6 +650,7 @@ CREATE TABLE system ( status integer NOT NULL default '0', throttle smallint NOT NULL default '0', bootstrap integer NOT NULL default '0', + schema_version int2 NOT NULL CHECK (schema_version > 0), PRIMARY KEY (filename) ); diff --git a/database/updates.inc b/database/updates.inc index e1523b94949c..0c06471c29e7 100644 --- a/database/updates.inc +++ b/database/updates.inc @@ -1,114 +1,17 @@ <?php // $Id$ -/** - * @file - * All incremental database updates performed between Drupal releases. - * - * For all updates after 147, please use the following syntax: - * - * function update_N() { - * $ret = array(); - * - * switch ($GLOBALS['db_type']) { - * case 'pgsql': - * // PostgreSQL code goes here - * break; - * case 'mysql': - * case 'mysqli': - * // MySQL code goes here - * break; - * } - * - * return $ret; - * } - * - * - * A quick guide to mysql2postgres conversion. Usually (but not allways!) you will use following sql statements: - * - * - Adding a key (an index): - * mysql: ALTER TABLE {$table} ADD KEY $column ($column) - * pgsql: CREATE INDEX {$table}_$column_idx ON {$table}($column) // Please note the _idx "extension" - * - * - Adding a primary key: - * mysql: ALTER TABLE {$table} ADD PRIMARY KEY $column ($column) - * pgsql: ALTER TABLE {$table} ADD PRIMARY KEY ($column) - * - * - Dropping a primary key: - * mysql: ALTER TABLE {$table} DROP PRIMARY KEY - * pgsql: ALTER TABLE {$table} DROP CONSTRAINT {$table}_pkey - * - * - Dropping a column: - * mysql: ALTER TABLE {$table} DROP $column - * pgsql: ALTER TABLE {$table} RENAME $column TO $column_old // For compatibility reasons we don't drop columns but rename them - * - * - Dropping an index: - * mysql: ALTER TABLE {$table} DROP INDEX $index - * pgsql: DROP INDEX {$table}_$column_idx // When index was defined by CREATE INDEX - * pgsql: ALTER TABLE {$table} DROP CONSTRAINT {$table}_$column_key // In case of UNIQUE($column) - * - * - Adding a column: (an example) - * mysql: $ret = update_sql("ALTER TABLE {vocabulary} ADD tags tinyint(3) unsigned default '0' NOT NULL"); - * pgsql: db_add_column($ret, 'vocabulary', 'tags', 'smallint', array('default' => 0, 'not null' => TRUE)); - * - * - Changing a column: (an example): - * mysql: $ret[] = update_sql("ALTER TABLE {locales_source} CHANGE location location varchar(255) NOT NULL default ''"); - * pgsql: db_change_column($ret, 'locales_source', 'location', 'location', 'varchar(255)', array('not null' => TRUE, 'default' => "''")); - * - */ - - -// Define the various updates in an array("date : comment" => "function"); -$sql_updates = array( - "2004-10-31: first update since Drupal 4.5.0 release" => "update_110", - "2004-11-07" => "update_111", - "2004-11-15" => "update_112", - "2004-11-28" => "update_113", - "2004-12-05" => "update_114", - "2005-01-07" => "update_115", - "2005-01-14" => "update_116", - "2005-01-18" => "update_117", - "2005-01-19" => "update_118", - "2005-01-20" => "update_119", - "2005-01-25" => "update_120", - "2005-01-26" => "update_121", - "2005-01-27" => "update_122", - "2005-01-28" => "update_123", - "2005-02-11" => "update_124", - "2005-02-23" => "update_125", - "2005-03-03" => "update_126", - "2005-03-18" => "update_127", - "2005-03-21" => "update_128", - "2005-04-08: first update since Drupal 4.6.0 release" => "update_129", - "2005-04-10" => "update_130", - "2005-04-11" => "update_131", - "2005-04-14" => "update_132", - "2005-04-24" => "update_133", - "2005-04-30" => "update_134", - "2005-05-06" => "update_135", - "2005-05-08" => "update_136", - "2005-05-09" => "update_137", - "2005-05-10" => "update_138", - "2005-05-11" => "update_139", - "2005-05-12" => "update_140", - "2005-05-22" => "update_141", - "2005-07-29" => "update_142", - "2005-07-30" => "update_143", - "2005-08-08" => "update_144", - "2005-08-15" => "update_145", - "2005-08-25" => "update_146", - "2005-09-07" => "update_147", - "2005-09-18" => "update_148", - "2005-09-27" => "update_149", - "2005-10-15" => "update_150", - "2005-10-23" => "update_151", - "2005-10-28" => "update_152", - "2005-11-03" => "update_153", - "2005-11-14" => "update_154", - "2005-11-27" => "update_155", -); // Please leave trailing , in the last line - -function update_110() { +function system_version($type) { + switch ($type) { + case SCHEMA: + return 155; + + case SCHEMA_MIN: + return 110; + } +} + +function system_update_110() { $ret = array(); // TODO: needs PGSQL version @@ -192,7 +95,7 @@ function update_110() { return $ret; } -function update_111() { +function system_update_111() { $ret = array(); $ret[] = update_sql("DELETE FROM {variable} WHERE name LIKE 'throttle_%'"); @@ -207,7 +110,7 @@ function update_111() { return $ret; } -function update_112() { +function system_update_112() { $ret = array(); if ($GLOBALS['db_type'] == 'mysql') { @@ -228,7 +131,7 @@ function update_112() { return $ret; } -function update_113() { +function system_update_113() { $ret = array(); if ($GLOBALS['db_type'] == 'mysql') { @@ -259,7 +162,7 @@ function update_113() { return $ret; } -function update_114() { +function system_update_114() { $ret = array(); if ($GLOBALS['db_type'] == 'mysql') { $ret[] = update_sql("CREATE TABLE {queue} ( @@ -313,7 +216,7 @@ function update_114() { return $ret; } -function update_115() { +function system_update_115() { $ret = array(); if ($GLOBALS['db_type'] == 'mysql') { $ret[] = update_sql("ALTER TABLE {watchdog} ADD severity tinyint(3) unsigned NOT NULL default '0'"); @@ -327,11 +230,11 @@ function update_115() { return $ret; } -function update_116() { +function system_update_116() { return array(update_sql("DELETE FROM {system} WHERE name = 'admin'")); } -function update_117() { +function system_update_117() { $ret = array(); if ($GLOBALS['db_type'] == 'mysql') { $ret[] = update_sql("CREATE TABLE {vocabulary_node_types} ( @@ -348,7 +251,7 @@ function update_117() { return $ret; } -function update_118() { +function system_update_118() { $ret = array(); $node_types = array(); $result = db_query('SELECT vid, nodes FROM {vocabulary}'); @@ -366,7 +269,7 @@ function update_118() { return $ret; } -function update_119() { +function system_update_119() { $ret = array(); foreach (node_get_types() as $type => $name) { @@ -397,7 +300,7 @@ function update_119() { return $ret; } -function update_120() { +function system_update_120() { $ret = array(); // Rewrite old URL aliases. Works for both PostgreSQL and MySQL @@ -413,7 +316,7 @@ function update_120() { return $ret; } -function update_121() { +function system_update_121() { $ret = array(); // Remove the unused page table. @@ -422,7 +325,7 @@ function update_121() { return $ret; } -function update_122() { +function system_update_122() { $ret = array(); $ret[] = update_sql("ALTER TABLE {blocks} ADD types text"); @@ -430,7 +333,7 @@ function update_122() { } -function update_123() { +function system_update_123() { $ret = array(); if ($GLOBALS['db_type'] == 'mysql') { @@ -452,7 +355,7 @@ function update_123() { return $ret; } -function update_124() { +function system_update_124() { $ret = array(); if ($GLOBALS['db_type'] == 'mysql') { @@ -500,7 +403,7 @@ function update_124() { return $ret; } -function update_125() { +function system_update_125() { // Postgres only update. $ret = array(); @@ -519,7 +422,7 @@ function update_125() { return $ret; } -function update_126() { +function system_update_126() { variable_set('forum_block_num_0', variable_get('forum_block_num', 5)); variable_set('forum_block_num_1', variable_get('forum_block_num', 5)); variable_del('forum_block_num'); @@ -527,7 +430,7 @@ function update_126() { return array(); } -function update_127() { +function system_update_127() { $ret = array(); if ($GLOBALS['db_type'] == 'pgsql') { $ret[] = update_sql("ALTER TABLE {poll} RENAME voters TO polled"); @@ -538,7 +441,7 @@ function update_127() { return $ret; } -function update_128() { +function system_update_128() { $ret = array(); if ($GLOBALS['db_type'] == 'mysql') { @@ -551,7 +454,7 @@ function update_128() { return $ret; } -function update_129() { +function system_update_129() { $ret = array(); if ($GLOBALS['db_type'] == 'mysql') { @@ -564,18 +467,16 @@ function update_129() { return $ret; } -function update_130() { +function system_update_130() { $ret = array(); - if ($GLOBALS['db_type'] == 'mysql') { - $ret[] = update_sql("ALTER TABLE {sessions} ADD cache int(11) NOT NULL default '0'"); - } - elseif ($GLOBALS['db_type'] == 'pgsql') { - db_add_column($ret, 'sessions', 'cache', 'int', array('default' => 0, 'not null' => TRUE)); - } + + // This update has been moved to update_fix_sessions in update.php because it + // is needed for the basic functioning of the update script. + return $ret; } -function update_131() { +function system_update_131() { $ret = array(); if ($GLOBALS['db_type'] == 'mysql') { @@ -589,30 +490,34 @@ function update_131() { return $ret; } -function update_132() { +function system_update_132() { /** * PostgreSQL only update. */ $ret = array(); - if ($GLOBALS['db_type'] == 'pgsql') { - $ret[] = update_sql('DROP TABLE {search_total}'); - $ret[] = update_sql("CREATE TABLE {search_total} ( - word varchar(50) NOT NULL default '', - count float default NULL)"); - $ret[] = update_sql('CREATE INDEX {search_total}_word_idx ON {search_total}(word)'); + if (variable_get('update_132_done', FALSE)) { + if ($GLOBALS['db_type'] == 'pgsql') { + $ret[] = update_sql('DROP TABLE {search_total}'); + $ret[] = update_sql("CREATE TABLE {search_total} ( + word varchar(50) NOT NULL default '', + count float default NULL)"); + $ret[] = update_sql('CREATE INDEX {search_total}_word_idx ON {search_total}(word)'); + + /** + * Wipe the search index + */ + include_once './modules/search.module'; + search_wipe(); + } - /** - * Wipe the search index - */ - include_once './modules/search.module'; - search_wipe(); + variable_del('update_132_done'); } return $ret; } -function update_133() { +function system_update_133() { $ret = array(); if ($GLOBALS['db_type'] == 'mysql') { @@ -633,7 +538,7 @@ function update_133() { return $ret; } -function update_134() { +function system_update_134() { $ret = array(); if ($GLOBALS['db_type'] == 'mysql') { $ret[] = update_sql('ALTER TABLE {blocks} DROP types'); @@ -644,16 +549,20 @@ function update_134() { return $ret; } -function update_135() { - $result = db_query("SELECT delta FROM {blocks} WHERE module = 'aggregator'"); - while ($block = db_fetch_object($result)) { - list($type, $id) = explode(':', $block->delta); - db_query("UPDATE {blocks} SET delta = '%s' WHERE module = 'aggregator' AND delta = '%s'", $type .'-'. $id, $block->delta); +function system_update_135() { + if (variable_get('update_135_done', FALSE)) { + $result = db_query("SELECT delta FROM {blocks} WHERE module = 'aggregator'"); + while ($block = db_fetch_object($result)) { + list($type, $id) = explode(':', $block->delta); + db_query("UPDATE {blocks} SET delta = '%s' WHERE module = 'aggregator' AND delta = '%s'", $type .'-'. $id, $block->delta); + } + + variable_del('update_135_done'); } return array(); } -function update_136() { +function system_update_136() { $ret = array(); switch ($GLOBALS['db_type']) { @@ -673,26 +582,30 @@ function update_136() { return $ret; } -function update_137() { +function system_update_137() { $ret = array(); - if ($GLOBALS['db_type'] == 'mysql') { - $ret[] = update_sql("ALTER TABLE {locales_source} CHANGE location location varchar(255) NOT NULL default ''"); - } - elseif ($GLOBALS['db_type'] == 'pgsql') { - db_change_column($ret, 'locales_source', 'location', 'location', 'varchar(255)', array('not null' => TRUE, 'default' => "''")); + if (variable_get('update_137_done', FALSE)) { + if ($GLOBALS['db_type'] == 'mysql') { + $ret[] = update_sql("ALTER TABLE {locales_source} CHANGE location location varchar(255) NOT NULL default ''"); + } + elseif ($GLOBALS['db_type'] == 'pgsql') { + db_change_column($ret, 'locales_source', 'location', 'location', 'varchar(255)', array('not null' => TRUE, 'default' => "''")); + } + variable_del('update_137_done'); } + return $ret; } -function update_138() { +function system_update_138() { $ret = array(); // duplicate of update_97 which never got into the default database.* files. $ret[] = update_sql("INSERT INTO {url_alias} (src, dst) VALUES ('node/feed', 'rss.xml')"); return $ret; } -function update_139() { +function system_update_139() { $ret = array(); switch ($GLOBALS['db_type']) { case 'pgsql': @@ -707,7 +620,7 @@ function update_139() { return $ret; } -function update_140() { +function system_update_140() { $ret = array(); if ($GLOBALS['db_type'] == 'mysql') { @@ -719,7 +632,7 @@ function update_140() { return $ret; } -function update_141() { +function system_update_141() { $ret = array(); variable_del('upload_maxsize_total'); @@ -727,21 +640,16 @@ function update_141() { return $ret; } -function update_142() { +function system_update_142() { $ret = array(); - switch ($GLOBALS['db_type']) { - case 'pgsql': - db_add_column($ret, 'watchdog', 'referer', 'varchar(128)', array('not null' => TRUE, 'default' => "''")); - break; - case 'mysql': - case 'mysqli': - $ret[] = update_sql("ALTER TABLE {watchdog} ADD COLUMN referer varchar(128) NOT NULL"); - break; - } + + // This update has been moved to update_fix_sessions in update.php because it + // is needed for the basic functioning of the update script. + return $ret; } -function update_143() { +function system_update_143() { $ret = array(); if ($GLOBALS['db_type'] == 'mysql') { @@ -760,7 +668,7 @@ function update_143() { return $ret; } -function update_144() { +function system_update_144() { $ret = array(); if ($GLOBALS['db_type'] == 'mysql') { $ret[] = update_sql("ALTER TABLE {node} CHANGE type type VARCHAR(32) NOT NULL"); @@ -777,7 +685,7 @@ function update_144() { return $ret; } -function update_145() { +function system_update_145() { $default_theme = variable_get('theme_default', 'bluemarine'); $ret = array(); @@ -809,7 +717,7 @@ function update_145() { return $ret; } -function update_146() { +function system_update_146() { $ret = array(); if ($GLOBALS['db_type'] == 'mysql') { @@ -917,7 +825,7 @@ function update_146() { return $ret; } -function update_147() { +function system_update_147() { $ret = array(); // this update is mysql only, pgsql should get it right in the first try. @@ -928,7 +836,7 @@ function update_147() { return $ret; } -function update_148() { +function system_update_148() { $ret = array(); // Add support for tracking users' session ids (useful for tracking anon users) @@ -940,12 +848,12 @@ function update_148() { case 'mysqli': $ret[] = update_sql("ALTER TABLE {accesslog} ADD sid varchar(32) NOT NULL default ''"); break; - } + } return $ret; } -function update_149() { +function system_update_149() { $ret = array(); switch ($GLOBALS['db_type']) { @@ -963,7 +871,7 @@ function update_149() { return $ret; } -function update_150() { +function system_update_150() { $ret = array(); $ret[] = update_sql("DELETE FROM {variable} WHERE name = 'node_cron_last'"); @@ -1032,7 +940,7 @@ function update_150() { return $ret; } -function update_151() { +function system_update_151() { $ret = array(); $ts = variable_get('theme_settings', null); @@ -1109,7 +1017,7 @@ function update_151() { return $ret; } -function update_152() { +function system_update_152() { $ret = array(); // Postgresql only update @@ -1125,7 +1033,7 @@ function update_152() { return $ret; } -function update_153(){ +function system_update_153(){ $ret = array(); switch ($GLOBALS['db_type']) { case 'pgsql': @@ -1145,7 +1053,7 @@ function update_153(){ return $ret; } -function update_154() { +function system_update_154() { $ret = array(); switch ($GLOBALS['db_type']) { case 'pgsql': @@ -1161,7 +1069,7 @@ function update_154() { return $ret; } -function update_155() { +function system_update_155() { $ret = array(); // Postgresql only update @@ -1186,114 +1094,9 @@ function update_155() { return $ret; } - -/** - * Adds a column to a database. Uses syntax appropriate for PostgreSQL. - * Saves result of SQL commands in $ret array. - * - * Note: when you add a column with NOT NULL and you are not sure if there are rows in table already, - * you MUST also add DEFAULT. Otherwise PostgreSQL won't work if the table is not empty. If NOT NULL and - * DEFAULT is 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 atributes: - * - not null => TRUE/FALSE - * - default => NULL/FALSE/value (with or without '', it wont' be added) - * @return - * nothing, but modifies $ret parametr. - */ -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 ($default) { $ret[] = update_sql("ALTER TABLE {". $table ."} ALTER $column SET $default"); } - if ($not_null) { - if ($default) { $ret[] = update_sql("UPDATE {". $table ."} SET $column = $default_val"); } - $ret[] = update_sql("ALTER TABLE {". $table ."} ALTER $column SET NOT NULL"); - } -} - -/** - * Changes a column definition. Uses syntax appropriate for PostgreSQL. - * Saves result of SQL commands in $ret array. - * - * @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 atributes: - * - not null => TRUE/FALSE - * - default => NULL/FALSE/value (with or without '', it wont' be added) - * @return - * nothing, but modifies $ret parametr. - */ -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"); } - // We don't drop columns for now - // $ret[] = update_sql("ALTER TABLE {". $table ."} DROP ". $column ."_old"); -} - - -function update_sql($sql) { - $edit = $_POST["edit"]; - $result = db_query($sql); - if ($result) { - return array('1', check_plain($sql) ."\n<span class=\"success\">OK</span>\n"); - } - else { - return array('0', check_plain($sql) ."\n<span class=\"failure\">FAILED</span>\n"); - } +function system_update_156() { + $ret = array(); + $ret[] = update_sql("DELETE FROM {cache}"); + system_themes(); + return array(); } - -?> diff --git a/includes/database.mysql.inc b/includes/database.mysql.inc index a58c9f1d5bc6..04af5ea0ac9a 100644 --- a/includes/database.mysql.inc +++ b/includes/database.mysql.inc @@ -105,7 +105,7 @@ function _db_query($query, $debug = 0) { return $result; } else { - trigger_error(check_plain(mysql_error() ."\nquery: ". $query), E_USER_ERROR); + trigger_error(check_plain(mysql_error() ."\nquery: ". $query), E_USER_WARNING); return FALSE; } } diff --git a/includes/database.mysqli.inc b/includes/database.mysqli.inc index ed80b70caf20..8645b7378ebd 100644 --- a/includes/database.mysqli.inc +++ b/includes/database.mysqli.inc @@ -113,7 +113,7 @@ function _db_query($query, $debug = 0) { return $result; } else { - trigger_error(check_plain(mysqli_error($active_db) ."\nquery: ". $query), E_USER_ERROR); + trigger_error(check_plain(mysqli_error($active_db) ."\nquery: ". $query), E_USER_WARNING); return FALSE; } } diff --git a/includes/database.pgsql.inc b/includes/database.pgsql.inc index 3c2f8f710d15..28d5efd7a426 100644 --- a/includes/database.pgsql.inc +++ b/includes/database.pgsql.inc @@ -92,7 +92,7 @@ function _db_query($query, $debug = 0) { return $last_result; } else { - trigger_error(check_plain(pg_last_error() ."\nquery: ". $query), E_USER_ERROR); + trigger_error(check_plain(pg_last_error() ."\nquery: ". $query), E_USER_WARNING); return FALSE; } } diff --git a/includes/theme.inc b/includes/theme.inc index c7f58585344b..279015b7118d 100644 --- a/includes/theme.inc +++ b/includes/theme.inc @@ -974,6 +974,16 @@ function theme_username($object) { return $output; } +function theme_progress_bar($percent, $message) { + $output = '<div id="progress" class="progress">'; + $output .= '<div class="percentage">'. $percent .'%</div>'; + $output .= '<div class="status">'. $message .'</div>'; + $output .= '<div class="bar"><div class="filled" style="width: '. $percent .'%"></div></div>'; + $output .= '</div>'; + + return $output; +} + /** * @} End of "defgroup themeable". */ diff --git a/misc/drupal.js b/misc/drupal.js index c2ee5233218a..f1af13be1849 100644 --- a/misc/drupal.js +++ b/misc/drupal.js @@ -67,7 +67,7 @@ function HTTPGet(uri, callbackFunction, callbackParameter) { /** * Creates an HTTP POST request and sends the response to the callback function */ -function HTTPPost(uri, object, callbackFunction, callbackParameter) { +function HTTPPost(uri, callbackFunction, callbackParameter, object) { var xmlHttp = new XMLHttpRequest(); var bAsync = true; if (!callbackFunction) { diff --git a/misc/maintenance.css b/misc/maintenance.css index 8c6a5bbe6fe2..2292550ce507 100644 --- a/misc/maintenance.css +++ b/misc/maintenance.css @@ -9,8 +9,13 @@ body { } h1 { margin: 1.6em 0 1.1em 0; +} +h1, h2, h3, h4, h5, h6 { font-family: sans-serif; } +ul { + margin: 0; +} :link { color: #0073ba; font-weight: bold; @@ -20,13 +25,34 @@ h1 { font-weight: bold; } +div.messages { + border: 1px solid #ddd; + padding: 0.4em; + margin-top: 1em; +} + +div.error { + border: 1px solid #daa; +} + /* Update styles */ -h3.update { - font-size: 1em; +#update-results { + margin-top: 3em; + padding: 0.25em; + border: 1px solid #ccc; + background: #eee; + font-size: smaller; +} +#update-results h2 { + margin-top: 0.25em; +} +#update-results h4 { + margin-bottom: 0.25em; } -pre.update span.success { - color: #6bb521; +#update-results li.none { + color: #888; + font-style: italic; } -pre.update span.failure { +#update-results li.failure strong { color: #b63300; } diff --git a/misc/progress.js b/misc/progress.js index a2de44e80711..b19cc08dbc8f 100644 --- a/misc/progress.js +++ b/misc/progress.js @@ -2,12 +2,17 @@ * A progressbar object. Initialized with the given id. Must be inserted into * the DOM afterwards through progressBar.element. * + * method is the function which will perform the HTTP request to get the + * progress bar status. Either HTTPGet or HTTPPost. + * * e.g. pb = new progressBar('myProgressBar'); * some_element.appendChild(pb.element); */ -function progressBar(id) { +function progressBar(id, callback, method) { var pb = this; this.id = id; + this.method = method ? method : HTTPGet; + this.callback = callback; this.element = document.createElement('div'); this.element.id = id; @@ -36,6 +41,9 @@ progressBar.prototype.setProgress = function (percentage, status) { divs[i].innerHTML = status; } } + if (this.callback) { + this.callback(percentage, status); + } } /** @@ -61,14 +69,14 @@ progressBar.prototype.sendPing = function () { if (this.timer) { clearTimeout(this.timer); } - HTTPGet(this.uri, this.receivePing, this); + this.method(this.uri, this.receivePing, this); } /** * HTTP callback function. Passes data back to the progressbar and sets a new * timer for the next ping. */ -progressBar.prototype.receivePing = function(string, xmlhttp, pb) { +progressBar.prototype.receivePing = function (string, xmlhttp, pb) { if (xmlhttp.status != 200) { return alert('An HTTP error '+ xmlhttp.status +' occured.\n'+ pb.uri); } diff --git a/misc/update.js b/misc/update.js new file mode 100644 index 000000000000..e4358d269172 --- /dev/null +++ b/misc/update.js @@ -0,0 +1,20 @@ +if (isJsEnabled()) { + addLoadEvent(function() { + if ($('edit-has_js')) { + $('edit-has_js').value = 1; + } + + if ($('progress')) { + updateCallback = function (progress, status) { + if (progress == 100) { + window.location = window.location.href.split('op=')[0] +'op=finished'; + } + } + + this.progress = new progressBar('updateprogress', updateCallback, HTTPPost); + this.progress.setProgress(-1, 'Starting updates...'); + $('progress').appendChild(this.progress.element); + this.progress.startMonitoring('update.php?op=do_update', 0); + } + }); +} diff --git a/update.php b/update.php index 9f9269b4a978..a76587ce79d2 100644 --- a/update.php +++ b/update.php @@ -17,49 +17,347 @@ // Enforce access checking? $access_check = TRUE; -if (!ini_get("safe_mode")) { - set_time_limit(180); + +define('SCHEMA', 0); +define('SCHEMA_MIN', 1); + +/** + * Includes install files. + */ +function update_include_install_files() { + // The system module (Drupal core) is currently a special case + include_once './database/updates.inc'; + + foreach (module_list() as $module) { + $install_file = './'. drupal_get_path('module', $module) .'/'. $module .'.install'; + if (is_file($install_file)) { + include_once $install_file; + } + } } -include_once './database/updates.inc'; +function update_sql($sql) { + $result = db_query($sql); + return array('success' => $result !== FALSE, 'query' => check_plain($sql)); +} -function update_data($start) { - global $sql_updates; - $output = ''; - $sql_updates = array_slice($sql_updates, ($start-- ? $start : 0)); - foreach ($sql_updates as $date => $func) { - $output .= '<h3 class="update">'. $date .'</h3><pre class="update">'; - $ret = $func(); - foreach ($ret as $return) { - $output .= $return[1]; +/** + * Adds a column to a database. Uses syntax appropriate for PostgreSQL. + * Saves result of SQL commands in $ret array. + * + * Note: when you add a column with NOT NULL and you are not sure if there are + * rows in table already, you MUST also add DEFAULT. Otherwise PostgreSQL won't + * work if the table is not empty. If NOT NULL and DEFAULT is 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 atributes: + * - not null => TRUE/FALSE + * - default => NULL/FALSE/value (with or without '', it wont' be added) + * @return + * nothing, but modifies $ret parametr. + */ +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]"; } - variable_set("update_start", $date); - $output .= "</pre>\n"; } - db_query('DELETE FROM {cache}'); - return $output; + + $ret[] = update_sql("ALTER TABLE {". $table ."} ADD $column $type"); + if ($default) { $ret[] = update_sql("ALTER TABLE {". $table ."} ALTER $column SET $default"); } + if ($not_null) { + if ($default) { $ret[] = update_sql("UPDATE {". $table ."} SET $column = $default_val"); } + $ret[] = update_sql("ALTER TABLE {". $table ."} ALTER $column SET NOT NULL"); + } } -function update_selection_page() { - global $sql_updates; +/** + * Changes a column definition. Uses syntax appropriate for PostgreSQL. + * Saves result of SQL commands in $ret array. + * + * @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 atributes: + * - not null => TRUE/FALSE + * - default => NULL/FALSE/value (with or without '', it wont' be added) + * @return + * nothing, but modifies $ret parametr. + */ +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"); } + // We don't drop columns for now + // $ret[] = update_sql("ALTER TABLE {". $table ."} DROP ". $column ."_old"); +} + +/** + * If the schema version for Drupal core is stored in the the variables table + * (4.6.x and earlier) move it to the schema_version column of the system + * table. + * + * This function may be removed when update 156 is removed, which is the last + * update in the 4.6 to 4.7 migration. + */ +function update_fix_schema_version() { + if ($update_start = variable_get('update_start', FALSE)) { + // Some updates were made to the 4.6 branch and 4.7 branch. This sets + // temporary variables to provent the updates from being executed twice and + // throwing errors. + switch ($update_start) { + case '2005-04-14': + variable_set('update_132_done', TRUE); + break; + + case '2005-05-06': + variable_set('update_132_done', TRUE); + variable_set('update_135_done', TRUE); + break; + + case '2005-05-07': + variable_set('update_132_done', TRUE); + variable_set('update_135_done', TRUE); + variable_set('update_137_done', TRUE); + break; + + } + + $sql_updates = array( + '2004-10-31: first update since Drupal 4.5.0 release' => 110, + '2004-11-07' => 111, '2004-11-15' => 112, '2004-11-28' => 113, + '2004-12-05' => 114, '2005-01-07' => 115, '2005-01-14' => 116, + '2005-01-18' => 117, '2005-01-19' => 118, '2005-01-20' => 119, + '2005-01-25' => 120, '2005-01-26' => 121, '2005-01-27' => 122, + '2005-01-28' => 123, '2005-02-11' => 124, '2005-02-23' => 125, + '2005-03-03' => 126, '2005-03-18' => 127, '2005-03-21' => 128, + // The following three updates were made on the 4.6 branch + '2005-04-14' => 129, '2005-05-06' => 129, '2005-05-07' => 129, + '2005-04-08: first update since Drupal 4.6.0 release' => 129, + '2005-04-10' => 130, '2005-04-11' => 131, '2005-04-14' => 132, + '2005-04-24' => 133, '2005-04-30' => 134, '2005-05-06' => 135, + '2005-05-08' => 136, '2005-05-09' => 137, '2005-05-10' => 138, + '2005-05-11' => 139, '2005-05-12' => 140, '2005-05-22' => 141, + '2005-07-29' => 142, '2005-07-30' => 143, '2005-08-08' => 144, + '2005-08-15' => 145, '2005-08-25' => 146, '2005-09-07' => 147, + '2005-09-18' => 148, '2005-09-27' => 149, '2005-10-15' => 150, + '2005-10-23' => 151, '2005-10-28' => 152, '2005-11-03' => 153, + '2005-11-14' => 154, '2005-11-27' => 155, '2005-12-03' => 156, + ); + + switch ($GLOBALS['db_type']) { + case 'pgsql': + $ret = array(); + db_add_column($ret, 'system', 'schema_version', 'int2', array('not null' => TRUE)); + break; + + case 'mysql': + case 'mysqli': + db_query('ALTER TABLE {system} ADD schema_version smallint(2) unsigned not null'); + break; + } + + update_set_installed_version('system', $sql_updates[$update_start]); + variable_del('update_start'); + } +} + +/** + * System update 130 changes the sessions table, which breaks the update + * script's ability to use session variables. This changes the table + * appropriately. + * + * This code, including the 'update_sessions_fixed' variable, may be removed + * when update 130 is removed. It is part of the Drupal 4.6 to 4.7 migration. + */ +function update_fix_sessions() { + $ret = array(); + + if (update_get_installed_version('system') < 130 && !variable_get('update_sessions_fixed', FALSE)) { + if ($GLOBALS['db_type'] == 'mysql') { + db_query("ALTER TABLE {sessions} ADD cache int(11) NOT NULL default '0' AFTER timestamp"); + } + elseif ($GLOBALS['db_type'] == 'pgsql') { + db_add_column($ret, 'sessions', 'cache', 'int', array('default' => 0, 'not null' => TRUE)); + } + + variable_set('update_sessions_fixed', TRUE); + } +} + +/** + * System update 142 changes the watchdog table, which breaks the update + * script's ability to use logging. This changes the table appropriately. + * + * This code, including the 'update_watchdog_fixed' variable, may be removed + * when update 142 is removed. It is part of the Drupal 4.6 to 4.7 migration. + */ +function update_fix_watchdog() { + if (update_get_installed_version('system') < 142 && !variable_get('update_watchdog_fixed', FALSE)) { + switch ($GLOBALS['db_type']) { + case 'pgsql': + db_add_column($ret, 'watchdog', 'referer', 'varchar(128)', array('not null' => TRUE, 'default' => "''")); + break; + case 'mysql': + case 'mysqli': + $ret[] = db_query("ALTER TABLE {watchdog} ADD COLUMN referer varchar(128) NOT NULL"); + break; + } + + variable_set('update_watchdog_fixed', TRUE); + } +} + +function update_data($module, $number) { + $ret = module_invoke($module, 'update_'. $number); + + // Save the query and results for display by update_finished_page(). + if (!isset($_SESSION['update_results'])) { + $_SESSION['update_results'] = array(); + } + else if (!isset($_SESSION['update_results'][$module])) { + $_SESSION['update_results'][$module] = array(); + } + $_SESSION['update_results'][$module][$number] = $ret; + + // Update the installed version + update_set_installed_version($module, $number); +} + +/** + * Returns an array of availiable schema versions for a module. + * + * @param $module + * A module name. + * @return + * If the module has updates, an array of available updates. Otherwise, + * FALSE. + */ +function update_get_versions($module) { + if (!($max = module_invoke($module, 'version', SCHEMA))) { + return FALSE; + } + if (!($min = module_invoke($module, 'version', SCHEMA_MIN))) { + $min = 1; + } + return range($min, $max); +} + +/** + * Returns the currently installed schema version for a module. + * + * @param $module + * A module name. + * @return + * The currently installed schema version. + */ +function update_get_installed_version($module, $reset = FALSE) { + static $versions; - $start = variable_get("update_start", 0); - $i = 1; - foreach ($sql_updates as $date => $sql) { - $dates[$i++] = $date; - if ($date == $start) { - $selected = $i; + if ($reset) { + unset($versions); + } + + if (!$versions) { + $versions = array(); + $result = db_query("SELECT name, schema_version FROM {system} WHERE type = 'module'"); + while ($row = db_fetch_object($result)) { + $versions[$row->name] = $row->schema_version; } } - $dates[$i] = "No updates available"; - // make update form and output it. + return $versions[$module]; +} + +/** + * Update the installed version information for a module. + * + * @param $module + * A module name. + * @param $version + * The new schema version. + */ +function update_set_installed_version($module, $version) { + db_query("UPDATE {system} SET schema_version = %d WHERE name = '%s'", $version, $module); +} + +function update_selection_page() { + $output = '<p>'. t('The version of Drupal you are updating from has been automatically detected. You can select a different version, but you should not need to.') .'</p>'; + $output .= '<p>'. t('Click Update to start the update process.') .'</p>'; + + $form = array(); $form['start'] = array( - '#type' => 'select', - '#title' => t('Perform updates from'), - '#default_value' => (isset($selected) ? $selected : -1), - '#options' => $dates, - '#description' => t('This defaults to the first available update since the last update you performed.') + '#tree' => TRUE, + '#type' => 'fieldset', + '#title' => 'Select versions', + '#collapsible' => TRUE, + '#collapsed' => TRUE, + ); + foreach (module_list() as $module) { + if (module_hook($module, 'version')) { + $updates = drupal_map_assoc(update_get_versions($module)); + $updates[] = 'No updates available'; + + $form['start'][$module] = array( + '#type' => 'select', + '#title' => t('%name module', array('%name' => $module)), + '#default_value' => array_search(update_get_installed_version($module), $updates) + 1, + '#options' => $updates, + ); + } + } + + $form['has_js'] = array( + '#type' => 'hidden', + '#default_value' => FALSE ); $form['submit'] = array( '#type' => 'submit', @@ -67,21 +365,150 @@ function update_selection_page() { ); drupal_set_title('Drupal database update'); - return drupal_get_form('update_script_selection_form', $form); + drupal_add_js('misc/update.js'); + $output .= drupal_get_form('update_script_selection_form', $form); + + return $output; +} + +function update_update_page() { + // Set the installed version so updates start at the correct place. + $_SESSION['update_remaining'] = array(); + foreach ($_POST['edit']['start'] as $module => $version) { + update_set_installed_version($module, $version - 1); + $max_version = max(update_get_versions($module)); + if ($version <= $max_version) { + foreach (range($version, $max_version) as $update) { + $_SESSION['update_remaining'][] = array('module' => $module, 'version' => $update); + } + } + } + // Keep track of total number of updates + $_SESSION['update_total'] = count($_SESSION['update_remaining']); + + if ($_POST['edit']['has_js']) { + return update_progress_page(); + } + else { + return update_progress_page_nojs(); + } +} + +function update_progress_page() { + drupal_add_js('misc/progress.js'); + drupal_add_js('misc/update.js'); + + drupal_set_title('Updating'); + $output = '<div id="progress"></div>'; + $output .= '<p>Updating your site will take a few seconds.</p>'; + return $output; } +/** + * Perform updates for one second or until finished. + * + * @return + * An array indicating the status after doing updates. The first element is + * the overall percent finished. The second element is a status message. + */ function update_do_updates() { - $edit = $_POST['edit']; + foreach ($_SESSION['update_remaining'] as $key => $update) { + update_data($update['module'], $update['version']); + unset($_SESSION['update_remaining'][$key]); + if (timer_read('page') > 1000) { + break; + } + } + + if ($_SESSION['update_total']) { + $percent = floor(($_SESSION['update_total'] - count($_SESSION['update_remaining'])) / $_SESSION['update_total'] * 100); + } + else { + $percent = 100; + } + return array($percent, 'Updating '. $update['module'] .' module'); +} + +function update_do_update_page() { + global $conf; + + // HTTP Post required + if ($_SERVER['REQUEST_METHOD'] != 'POST') { + drupal_set_message('HTTP Post is required.', 'error'); + drupal_set_title('Error'); + return ''; + } + + // Any errors which happen would cause the result to not be parsed properly, + // so we need to supporess them. All errors are still logged. + if (!isset($conf['error_level'])) { + $conf['error_level'] = 0; + } + ini_set('display_errors', FALSE); + + print implode('|', update_do_updates()); +} + +function update_progress_page_nojs() { + $new_op = 'do_update_nojs'; + if ($_SERVER['REQUEST_METHOD'] == 'GET') { + list($percent, $message) = update_do_updates(); + if ($percent == 100) { + $new_op = 'finished'; + } + } + else { + // This is the first page so return some output immediately. + $percent = 0; + $message = 'Starting updates...'; + } + + drupal_set_html_head('<meta http-equiv="Refresh" content="0; URL=update.php?op='. $new_op .'">'); + drupal_set_title('Updating'); + $output = theme('progress_bar', $percent, $message); + $output .= '<p>Updating your site will take a few seconds.</p>'; + + return $output; +} + +function update_finished_page() { drupal_set_title('Drupal database update'); // NOTE: we can't use l() here because the URL would point to 'update.php?q=admin'. - $links[] = "<a href=\"index.php\">main page</a>"; - $links[] = "<a href=\"index.php?q=admin\">administration pages</a>"; - $output = theme('item_list', $links); - $output .= update_data($edit['start']); - $output .= '<p>Updates were attempted. If you see no failures above, you may proceed happily to the <a href="index.php?q=admin">administration pages</a>. Otherwise, you may need to update your database manually.</p>'; + $links[] = '<a href="">main page</a>'; + $links[] = '<a href="?q=admin">administration pages</a>'; + $output = '<p>Updates were attempted. If you see no failures below, you may proceed happily to the <a href="?q=admin">administration pages</a>. Otherwise, you may need to update your database manually. All errors have been <a href="?q=admin/logs">logged</a>.</p>'; if ($GLOBALS['access_check'] == FALSE) { $output .= "<p><strong>Reminder: don't forget to set the <code>\$access_check</code> value at the top of <code>update.php</code> back to <code>TRUE</code>.</strong>"; } + $output .= theme('item_list', $links); + + // Output a list of queries executed + if ($_SESSION['update_results']) { + $output .= '<div id="update-results">'; + $output .= '<h2>The following queries were executed</h2>'; + foreach ($_SESSION['update_results'] as $module => $updates) { + $output .= '<h3>'. $module .' module</h3>'; + foreach ($updates as $number => $queries) { + $output .= '<h4>Update #'. $number .'</h4>'; + $output .= '<ul>'; + foreach ($queries as $query) { + if ($query['success']) { + $output .= '<li class="success">'. $query['query'] .'</li>'; + } + else { + $output .= '<li class="failure"><strong>Failed:</strong> '. $query['query'] .'</li>'; + } + } + if (!count($queries)) { + $output .= '<li class="none">No queries</li>'; + } + $output .= '</ul>'; + } + } + $output .= '</div>'; + unset($_SESSION['update_results']); + } + return $output; } @@ -90,7 +517,7 @@ function update_info_page() { $output = "<ol>\n"; $output .= "<li>Use this script to <strong>upgrade an existing Drupal installation</strong>. You don't need this script when installing Drupal from scratch.</li>"; $output .= "<li>Before doing anything, backup your database. This process will change your database and its values, and some things might get lost.</li>\n"; - $output .= "<li>Update your Drupal sources, check the notes below and <a href=\"update.php?op=update\">run the database upgrade script</a>. Don't upgrade your database twice as it may cause problems.</li>\n"; + $output .= "<li>Update your Drupal sources, check the notes below and <a href=\"update.php?op=selection\">run the database upgrade script</a>. Don't upgrade your database twice as it may cause problems.</li>\n"; $output .= "<li>Go through the various administration pages to change the existing and new settings to your liking.</li>\n"; $output .= "</ol>"; $output .= '<p>For more help, see the <a href="http://drupal.org/node/258">Installation and upgrading handbook</a>. If you are unsure what these terms mean you should probably contact your hosting provider.</p>'; @@ -109,34 +536,48 @@ function update_access_denied_page() { } include_once './includes/bootstrap.inc'; +drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL); drupal_maintenance_theme(); -if (isset($_GET["op"])) { - drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL); +// Access check: +if (($access_check == FALSE) || ($user->uid == 1)) { + update_include_install_files(); - // Access check: - if (($access_check == 0) || ($user->uid == 1)) { - $op = isset($_POST['op']) ? $_POST['op'] : ''; - switch ($op) { - case t('Update'): - $output = update_do_updates(); - break; + update_fix_schema_version(); + update_fix_sessions(); + update_fix_watchdog(); - default: - $output = update_selection_page(); - break; - } - } - else { - $output = update_access_denied_page(); + $op = isset($_REQUEST['op']) ? $_REQUEST['op'] : ''; + switch ($op) { + case 'Update': + $output = update_update_page(); + break; + + case 'finished': + $output = update_finished_page(); + break; + + case 'do_update': + $output = update_do_update_page(); + break; + + case 'do_update_nojs': + $output = update_progress_page_nojs(); + break; + + case 'selection': + $output = update_selection_page(); + break; + + default: + $output = update_info_page(); + break; } } else { - drupal_bootstrap(DRUPAL_BOOTSTRAP_DATABASE); - $output = update_info_page(); + $output = update_access_denied_page(); } if (isset($output)) { print theme('maintenance_page', $output); } - -- GitLab