From a19acb219e249693357daeb037165eaf19a70b33 Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Tue, 6 Dec 2005 09:25:22 +0000 Subject: - 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 a5ecd1de4..7be8d9ecd 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 59ce317f3..5dfee4889 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 e1523b949..0c06471c2 100644 --- a/database/updates.inc +++ b/database/updates.inc @@ -1,114 +1,17 @@ 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) ."\nOK\n"); - } - else { - return array('0', check_plain($sql) ."\nFAILED\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 a58c9f1d5..04af5ea0a 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 ed80b70ca..8645b7378 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 3c2f8f710..28d5efd7a 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 c7f585853..279015b71 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 = '
'; + $output .= '
'. $percent .'%
'; + $output .= '
'. $message .'
'; + $output .= '
'; + $output .= '
'; + + return $output; +} + /** * @} End of "defgroup themeable". */ diff --git a/misc/drupal.js b/misc/drupal.js index c2ee52332..f1af13be1 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 8c6a5bbe6..2292550ce 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 a2de44e80..b19cc08db 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 000000000..e4358d269 --- /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 9f9269b4a..a76587ce7 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 .= '

'. $date .'

';
-    $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 .= "
\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 = '

'. 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.') .'

'; + $output .= '

'. t('Click Update to start the update process.') .'

'; + + $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 = '
'; + $output .= '

Updating your site will take a few seconds.

'; + 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(''); + drupal_set_title('Updating'); + $output = theme('progress_bar', $percent, $message); + $output .= '

Updating your site will take a few seconds.

'; + + 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[] = "main page"; - $links[] = "administration pages"; - $output = theme('item_list', $links); - $output .= update_data($edit['start']); - $output .= '

Updates were attempted. If you see no failures above, you may proceed happily to the administration pages. Otherwise, you may need to update your database manually.

'; + $links[] = 'main page'; + $links[] = 'administration pages'; + $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. All errors have been logged.

'; if ($GLOBALS['access_check'] == FALSE) { $output .= "

Reminder: don't forget to set the \$access_check value at the top of update.php back to TRUE."; } + $output .= theme('item_list', $links); + + // Output a list of queries executed + if ($_SESSION['update_results']) { + $output .= '

'; + $output .= '

The following queries were executed

'; + foreach ($_SESSION['update_results'] as $module => $updates) { + $output .= '

'. $module .' module

'; + foreach ($updates as $number => $queries) { + $output .= '

Update #'. $number .'

'; + $output .= ''; + } + } + $output .= '
'; + unset($_SESSION['update_results']); + } + return $output; } @@ -90,7 +517,7 @@ function update_info_page() { $output = "
    \n"; $output .= "
  1. Use this script to upgrade an existing Drupal installation. You don't need this script when installing Drupal from scratch.
  2. "; $output .= "
  3. Before doing anything, backup your database. This process will change your database and its values, and some things might get lost.
  4. \n"; - $output .= "
  5. Update your Drupal sources, check the notes below and run the database upgrade script. Don't upgrade your database twice as it may cause problems.
  6. \n"; + $output .= "
  7. Update your Drupal sources, check the notes below and run the database upgrade script. Don't upgrade your database twice as it may cause problems.
  8. \n"; $output .= "
  9. Go through the various administration pages to change the existing and new settings to your liking.
  10. \n"; $output .= "
"; $output .= '

For more help, see the Installation and upgrading handbook. If you are unsure what these terms mean you should probably contact your hosting provider.

'; @@ -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); } - -- cgit v1.2.3