From 5a06233304a9965c8319ec5c1fcc4fd582393d02 Mon Sep 17 00:00:00 2001 From: brettp Date: Fri, 18 Feb 2011 00:46:54 +0000 Subject: Fixes #1433, #2183. Upgrades are now tracked and will be run if needed regardless of version. git-svn-id: http://code.elgg.org/elgg/trunk@8277 36083f99-b078-4883-b0ff-0f9b5a30f544 --- engine/lib/database.php | 64 ---------- engine/lib/deprecated-1.8.php | 70 ++++++++++- engine/lib/upgrades/create_upgrade.php | 117 ++++++++++++++++++ engine/lib/version.php | 211 ++++++++++++++++++++++++++------- install/ElggInstaller.php | 8 +- upgrade.php | 2 +- 6 files changed, 361 insertions(+), 111 deletions(-) create mode 100644 engine/lib/upgrades/create_upgrade.php diff --git a/engine/lib/database.php b/engine/lib/database.php index b2cc5ca29..fa5b4a894 100644 --- a/engine/lib/database.php +++ b/engine/lib/database.php @@ -628,70 +628,6 @@ function run_sql_script($scriptlocation) { } } -/** - * Upgrade the database schema in an ordered sequence. - * - * Executes all upgrade files in elgg/engine/schema/upgrades/ in sequential order. - * Upgrade files must be in the standard Elgg release format of YYYYMMDDII.sql - * where II is an incrementor starting from 01. - * - * Files that are < $version will be ignored. - * - * @warning Plugin authors should not call this function directly. - * - * @param int $version The version you are upgrading from in the format YYYYMMDDII. - * @param string $fromdir Optional directory to load upgrades from. default: engine/schema/upgrades/ - * @param bool $quiet If true, suppress all error messages. Only use for the upgrade from <=1.6. - * - * @return bool - * @see upgrade.php - * @see version.php - */ -function db_upgrade($version, $fromdir = "", $quiet = FALSE) { - global $CONFIG; - - $version = (int) $version; - - if (!$fromdir) { - $fromdir = $CONFIG->path . 'engine/schema/upgrades/'; - } - - if ($handle = opendir($fromdir)) { - $sqlupgrades = array(); - - while ($sqlfile = readdir($handle)) { - if (!is_dir($fromdir . $sqlfile)) { - if (preg_match('/^([0-9]{10})\.(sql)$/', $sqlfile, $matches)) { - $sql_version = (int) $matches[1]; - if ($sql_version > $version) { - $sqlupgrades[] = $sqlfile; - } - } - } - } - - asort($sqlupgrades); - - if (sizeof($sqlupgrades) > 0) { - foreach ($sqlupgrades as $sqlfile) { - - // hide all errors. - if ($quiet) { - try { - run_sql_script($fromdir . $sqlfile); - } catch (DatabaseException $e) { - error_log($e->getmessage()); - } - } else { - run_sql_script($fromdir . $sqlfile); - } - } - } - } - - return TRUE; -} - /** * Sanitise a string for database use, but with the option of escaping extra characters. * diff --git a/engine/lib/deprecated-1.8.php b/engine/lib/deprecated-1.8.php index 76abc5c95..3f65fb58e 100644 --- a/engine/lib/deprecated-1.8.php +++ b/engine/lib/deprecated-1.8.php @@ -3808,7 +3808,7 @@ function register_metadata_url_handler($function, $extender_name = "all") { } /** - * + * * @deprecated 1.8 Use {@link elgg_register_relationship_url_handler()} */ function register_relationship_url_handler($function_name, $relationship_type = "all") { @@ -4335,3 +4335,71 @@ function display_widget(ElggObject $widget) { elgg_deprecated_notice("display_widget() was been deprecated. Use elgg_view_entity().", 1.8); return elgg_view_entity($widget); } + + +/** + * Upgrade the database schema in an ordered sequence. + * + * Executes all upgrade files in elgg/engine/schema/upgrades/ in sequential order. + * Upgrade files must be in the standard Elgg release format of YYYYMMDDII.sql + * where II is an incrementor starting from 01. + * + * Files that are < $version will be ignored. + * + * @warning Plugin authors should not call this function directly. + * + * @param int $version The version you are upgrading from in the format YYYYMMDDII. + * @param string $fromdir Optional directory to load upgrades from. default: engine/schema/upgrades/ + * @param bool $quiet If true, suppress all error messages. Only use for the upgrade from <=1.6. + * + * @return bool + * @see upgrade.php + * @see version.php + * @deprecated 1.8 Use PHP upgrades for sql changes. + */ +function db_upgrade($version, $fromdir = "", $quiet = FALSE) { + global $CONFIG; + + elgg_deprecated_notice('db_upgrade() is deprecated by using PHP upgrades.', 1.8); + + $version = (int) $version; + + if (!$fromdir) { + $fromdir = $CONFIG->path . 'engine/schema/upgrades/'; + } + + if ($handle = opendir($fromdir)) { + $sqlupgrades = array(); + + while ($sqlfile = readdir($handle)) { + if (!is_dir($fromdir . $sqlfile)) { + if (preg_match('/^([0-9]{10})\.(sql)$/', $sqlfile, $matches)) { + $sql_version = (int) $matches[1]; + if ($sql_version > $version) { + $sqlupgrades[] = $sqlfile; + } + } + } + } + + asort($sqlupgrades); + + if (sizeof($sqlupgrades) > 0) { + foreach ($sqlupgrades as $sqlfile) { + + // hide all errors. + if ($quiet) { + try { + run_sql_script($fromdir . $sqlfile); + } catch (DatabaseException $e) { + error_log($e->getmessage()); + } + } else { + run_sql_script($fromdir . $sqlfile); + } + } + } + } + + return TRUE; +} \ No newline at end of file diff --git a/engine/lib/upgrades/create_upgrade.php b/engine/lib/upgrades/create_upgrade.php new file mode 100644 index 000000000..9575ada74 --- /dev/null +++ b/engine/lib/upgrades/create_upgrade.php @@ -0,0 +1,117 @@ + 24) { + elgg_create_upgrade_show_usage('Upgrade names cannot be longer than 24 characters.'); +} + +require_once '../../../version.php'; +require_once '../elgglib.php'; +$upgrade_path = dirname(__FILE__); + +$upgrade_name = strtolower($name); +$upgrade_name = str_replace(array(' ', '-'), '_', $upgrade_name); +$upgrade_release = str_replace(array(' ', '-'), '_', $release); +$time = time(); +$upgrade_rnd = substr(md5($time), 0, 16); +$upgrade_date = date('Ymd', $time); + +// determine the inc count +$upgrade_inc = 0; +$files = elgg_get_file_list($upgrade_path); +sort($files); + +foreach ($files as $filename) { + $filename = basename($filename); + $date = (int)substr($filename, 0, 8); + $inc = (int)substr($filename, 8, 2); + + if ($upgrade_date == $date) { + if ($inc >= $upgrade_inc) { + $upgrade_inc = $inc + 1; + } + } +} + +// zero-pad +// if there are more than 10 upgrades in a day, someone needs talking to. +if ($upgrade_inc < 10) { + $upgrade_inc = "0$upgrade_inc"; +} + +// make filename +if (substr($release, 0, 3) == '1.7') { + // 1.7 upgrades are YYYYMMDDXX + $upgrade_name = $upgrade_date . $upgrade_inc . '.php'; +} else { + // 1.8+ upgrades are YYYYMMDDXX-release-friendly_name-rnd + $upgrade_name = $upgrade_date . $upgrade_inc . "-$upgrade_release-$name-$upgrade_rnd.php"; +} + +$upgrade_file = $upgrade_path . '/' . $upgrade_name; + +if (is_file($upgrade_file)) { + elgg_create_upgrade_show_usage("Upgrade file $upgrade_file already exists. This script has failed you."); +} + +$upgrade_code = <<<___UPGRADE +path . 'engine/lib/upgrades/')) { - $upgrades = array(); - - while ($updatefile = readdir($handle)) { - // Look for upgrades and add to upgrades list - if (!is_dir($CONFIG->path . 'engine/lib/upgrades/' . $updatefile)) { - if (preg_match('/^([0-9]{10})\.(php)$/', $updatefile, $matches)) { - $core_version = (int) $matches[1]; - if ($core_version > $version) { - $upgrades[] = $updatefile; - } + if (!$processed_upgrades) { + $processed_upgrades = array(); + } + + $upgrades = array(); + + $upgrade_files = elgg_get_upgrade_files($upgrade_path); + + if ($upgrade_files === false) { + return false; + } + + // bootstrap into the new upgrade system. + // can't do this in an upgrade because we need to check for 2010050701, + // which would already have been run by then. + if ($version < $upgrade_epoch) { + foreach ($upgrade_files as $upgrade_file) { + $upgrade_version = elgg_get_upgrade_file_version($upgrade_file); + + // the upgrade that made life difficult + // the only way to test if we're upgrading from 1.7 to 1.8 or within 1.8 + // is to test for the the walled_garden config option, which + // 2010050701 explicitly sets + if ($upgrade_version == 2010050701) { + $db_prefix = elgg_get_config('dbprefix'); + $site_guid = elgg_get_config('site_guid'); + $q = "SELECT value FROM {$db_prefix}config + WHERE name = 'walled_garden' AND site_guid = {$site_guid}"; + $result = get_data_row($q); + if (!$result) { + $upgrades[] = $upgrade_file; } + + continue; + } elseif ($version < $upgrade_version) { + $upgrades[] = $upgrade_file; + } else { + // all of the upgrades before the epoch have been run except one... + $processed_upgrades[] = $upgrade_file; } } + } else { + // add any upgrades that haven't been run to the upgrades list + $upgrades = elgg_get_unprocessed_upgrades($upgrade_files, $processed_upgrades); + } + + // Sort and execute + ksort($upgrades); + + foreach ($upgrades as $upgrade) { + $upgrade_version = elgg_get_upgrade_file_version($upgrade); + $success = true; - // Sort and execute - asort($upgrades); - - if (sizeof($upgrades) > 0) { - foreach ($upgrades as $upgrade) { - // hide all errors. - if ($quiet) { - // hide include errors as well as any exceptions that might happen - try { - if (!@include($CONFIG->path . 'engine/lib/upgrades/' . $upgrade)) { - error_log($e->getmessage()); - } - } catch (Exception $e) { - error_log($e->getmessage()); - } - } else { - include($CONFIG->path . 'engine/lib/upgrades/' . $upgrade); + // hide all errors. + if ($quiet) { + // hide include errors as well as any exceptions that might happen + try { + if (!@include("$upgrade_path/$upgrade")) { + $success = false; + error_log($e->getmessage()); } + } catch (Exception $e) { + $success = false; + error_log($e->getmessage()); + } + } else { + if (!include("$upgrade_path/$upgrade")) { + $success = false; } } - return TRUE; + if ($success) { + // incrementally set upgrade so we know where to start if something fails. + $processed_upgrades[] = $upgrade; + + // don't set the version to a lower number in instances where an upgrade + // has been merged from a lower version + if ($upgrade_version > $version) { + datalist_set('version', $upgrade_version); + } + + $processed_upgrades = array_unique($processed_upgrades); + datalist_set('processed_upgrades', serialize($processed_upgrades)); + } else { + return false; + } } - return FALSE; + return true; +} + +/** + * Returns the version of the upgrade filename. + * + * @param string $filename The upgrade filename. No full path. + * @return int|false + * @since 1.8 + */ +function elgg_get_upgrade_file_version($filename) { + preg_match('/^([0-9]{10})([\.a-z0-9-_]+)?\.(php)$/i', $filename, $matches); + + if (isset($matches[1])) { + return (int) $matches[1]; + } + + return false; +} + +/** + * Returns a list of upgrade files relative to the $upgrade_path dir. + * + * @param string $upgrade_path The up + * @return array|false + */ +function elgg_get_upgrade_files($upgrade_path = null) { + if (!$upgrade_path) { + $upgrade_path = elgg_get_config('path') . 'engine/lib/upgrades/'; + } + $upgrade_path = sanitise_filepath($upgrade_path); + $handle = opendir($upgrade_path); + + if (!$handle) { + return false; + } + + $upgrade_files = array(); + + while ($upgrade_file = readdir($handle)) { + // make sure this is a wellformed upgrade. + if (is_dir($upgrade_path . '$upgrade_file')) { + continue; + } + $upgrade_version = elgg_get_upgrade_file_version($upgrade_file); + if (!$upgrade_version) { + continue; + } + $upgrade_files[] = $upgrade_file; + } + + return $upgrade_files; } /** @@ -81,6 +185,30 @@ function get_version($humanreadable = false) { return FALSE; } +/** + * Checks if any upgrades need to be run. + * + * @param null|array $upgrade_files Optional upgrade files + * @param null|array $processed_upgrades Optional processed upgrades + * + * @return array() + */ +function elgg_get_unprocessed_upgrades($upgrade_files = null, $processed_upgrades = null) { + if ($upgrade_files === null) { + $upgrade_files = elgg_get_upgrade_files(); + } + + if ($processed_upgrades === null) { + $processed_upgrades = unserialize(datalist_get('processed_upgrades')); + if (!is_array($processed_upgrades)) { + $processed_upgrades = array(); + } + } + + $unprocessed = array_diff($upgrade_files, $processed_upgrades); + return $unprocessed; +} + /** * Determines whether or not the database needs to be upgraded. * @@ -114,23 +242,20 @@ function version_upgrade() { // See http://trac.elgg.org/elgg/ticket/1432 for more. $quiet = !$dbversion; - // Upgrade database - if (db_upgrade($dbversion, '', $quiet)) { - system_message(elgg_echo('upgrade:db')); - } + // Note: Database upgrades are deprecated as of 1.8. Use code upgrades. See #1433 - // Upgrade core if (upgrade_code($dbversion, $quiet)) { system_message(elgg_echo('upgrade:core')); - } - // Now we trigger an event to give the option for plugins to do something - $upgrade_details = new stdClass; - $upgrade_details->from = $dbversion; - $upgrade_details->to = get_version(); + // Now we trigger an event to give the option for plugins to do something + $upgrade_details = new stdClass; + $upgrade_details->from = $dbversion; + $upgrade_details->to = get_version(); + + elgg_trigger_event('upgrade', 'upgrade', $upgrade_details); - elgg_trigger_event('upgrade', 'upgrade', $upgrade_details); + return true; + } - // Update the version - datalist_set('version', get_version()); + return false; } diff --git a/install/ElggInstaller.php b/install/ElggInstaller.php index 7b3fbb9ec..fc0eff0ab 100644 --- a/install/ElggInstaller.php +++ b/install/ElggInstaller.php @@ -551,7 +551,7 @@ class ElggInstaller { protected function getNextStepUrl($currentStep) { global $CONFIG; $nextStep = $this->getNextStep($currentStep); - return elgg_get_site_url()."install.php?step=$nextStep"; + return elgg_get_site_url() . "install.php?step=$nextStep"; } /** @@ -1030,7 +1030,7 @@ class ElggInstaller { require_once(dirname(__FILE__) . "/ElggRewriteTester.php"); $tester = new ElggRewriteTester(); - $url = elgg_get_site_url()."rewrite.php"; + $url = elgg_get_site_url() . "rewrite.php"; $report['rewrite'] = array($tester->run($url, $CONFIG->path)); } @@ -1304,6 +1304,10 @@ class ElggInstaller { datalist_set('default_site', $site->getGUID()); datalist_set('version', get_version()); + // new installations have run all the upgrades + $upgrades = elgg_get_upgrade_files($submissionVars['path'] . 'engine/lib/upgrades/'); + datalist_set('processed_upgrades', serialize($upgrades)); + set_config('view', 'default', $site->getGUID()); set_config('language', $submissionVars['language'], $site->getGUID()); set_config('default_access', $submissionVars['siteaccess'], $site->getGUID()); diff --git a/upgrade.php b/upgrade.php index 0af441b10..6589780b3 100644 --- a/upgrade.php +++ b/upgrade.php @@ -17,7 +17,7 @@ define('UPGRADING', 'upgrading'); require_once(dirname(__FILE__) . "/engine/start.php"); if (get_input('upgrade') == 'upgrade') { - if (version_upgrade_check()) { + if (elgg_get_unprocessed_upgrades()) { version_upgrade(); } elgg_invalidate_simplecache(); -- cgit v1.2.3