From 742ea74200dc1711c1312eb184e3f9c59a8db252 Mon Sep 17 00:00:00 2001 From: Chad Horohoe Date: Mon, 31 Jan 2011 20:00:59 +0000 Subject: [PATCH] (bug 26857) Fatal error during installation enabling 'Vector' extension (or any extension, really). The reason we're including extensions during setup is so they have a chance to register with LoadExtensionSchemaUpdates. Before including extensions, we now include DefaultSettings.php, so any operations on those variables won't result in the fatals described in the bug. More importantly, this adds support for installing extension tables *at* install time. Previously, the best we could do was to add the require()s to LocalSettings. However, without a subsequent update, wikis will probably start having errors immediately after install (this is really bad!). So I've added an interface to the DatabaseUpdater passed to LoadExtensionSchemaUpdates. An extension can now call addNewExtension() which will allow the extension to be enabled on install and not just update. Example committed in CodeReview --- includes/installer/DatabaseInstaller.php | 105 +++++++++++++++++++++++ includes/installer/DatabaseUpdater.php | 29 ++++++- includes/installer/Installer.i18n.php | 1 + includes/installer/Installer.php | 19 +++- 4 files changed, 152 insertions(+), 2 deletions(-) diff --git a/includes/installer/DatabaseInstaller.php b/includes/installer/DatabaseInstaller.php index 38367c43b5..16f42b821e 100644 --- a/includes/installer/DatabaseInstaller.php +++ b/includes/installer/DatabaseInstaller.php @@ -170,6 +170,111 @@ abstract class DatabaseInstaller { return $status; } + /** + * Create the tables for each extension the user enabled + * @return Status + */ + public function createExtensionTables() { + $status = $this->getConnection(); + if ( !$status->isOK() ) { + return $status; + } + $this->db; + $updater = DatabaseUpdater::newForDB( $this->db ); + $extensionUpdates = $updater->getNewExtensions(); + + // No extensions need tables (or haven't updated to new installer support) + if( !count( $extensionUpdates ) ) { + return $status; + } + + $ourExtensions = array_map( 'strtolower', $this->getVar( '_Extensions' ) ); + + foreach( $ourExtensions as $ext ) { + if( isset( $extensionUpdates[$ext] ) ) { + $this->db->begin( __METHOD__ ); + $error = $this->db->sourceFile( $extensionUpdates[$ext] ); + if( $error !== true ) { + $this->db->rollback( __METHOD__ ); + $status->warning( 'config-install-tables-failed', $error ); + } else { + $this->db->commit( __METHOD__ ); + } + } + } + return $status; + } + + /** + * Create the tables for each extension the user enabled + * @return Status + */ + public function createExtensionTables() { + $status = $this->getConnection(); + if ( !$status->isOK() ) { + return $status; + } + $this->db; + $updater = DatabaseUpdater::newForDB( $this->db ); + $extensionUpdates = $updater->getNewExtensions(); + + // No extensions need tables (or haven't updated to new installer support) + if( !count( $extensionUpdates ) ) { + return $status; + } + + $ourExtensions = array_map( 'strtolower', $this->getVar( '_Extensions' ) ); + + foreach( $ourExtensions as $ext ) { + if( isset( $extensionUpdates[$ext] ) ) { + $this->db->begin( __METHOD__ ); + $error = $this->db->sourceFile( $extensionUpdates[$ext] ); + if( $error !== true ) { + $this->db->rollback( __METHOD__ ); + $status->warning( 'config-install-tables-failed', $error ); + } else { + $this->db->commit( __METHOD__ ); + } + } + } + return $status; + } + + /** + * Create the tables for each extension the user enabled + * @return Status + */ + public function createExtensionTables() { + $status = $this->getConnection(); + if ( !$status->isOK() ) { + return $status; + } + $this->db; + $updater = DatabaseUpdater::newForDB( $this->db ); + $extensionUpdates = $updater->getNewExtensions(); + + // No extensions need tables (or haven't updated to new installer support) + if( !count( $extensionUpdates ) ) { + return $status; + } + + $ourExtensions = array_map( 'strtolower', $this->getVar( '_Extensions' ) ); + + foreach( $ourExtensions as $ext ) { + if( isset( $extensionUpdates[$ext] ) ) { + $this->db->begin( __METHOD__ ); + $error = $this->db->sourceFile( $extensionUpdates[$ext] ); + if( $error !== true ) { + $this->db->rollback( __METHOD__ ); + $status->warning( 'config-install-tables-failed', $error ); + } else { + $this->db->commit( __METHOD__ ); + } + } + } + return $status; + } + /** * Get the DBMS-specific options for LocalSettings.php generation. * diff --git a/includes/installer/DatabaseUpdater.php b/includes/installer/DatabaseUpdater.php index 5bdfec5b0d..900dfebfc0 100644 --- a/includes/installer/DatabaseUpdater.php +++ b/includes/installer/DatabaseUpdater.php @@ -24,8 +24,18 @@ abstract class DatabaseUpdater { */ protected $updates = array(); + /** + * List of extension-provided database updates + * @var array + */ protected $extensionUpdates = array(); + /** + * Used to hold schema files during installation process + * @var array + */ + protected $newExtensions = array(); + /** * Handle to the database subclass * @@ -83,7 +93,7 @@ abstract class DatabaseUpdater { * @param DatabaseBase $db * @param bool $shared * @param null $maintenance - * @return + * @return DatabaseUpdater */ public static function newForDB( &$db, $shared = false, $maintenance = null ) { $type = $db->getType(); @@ -146,6 +156,23 @@ abstract class DatabaseUpdater { $this->extensionUpdates[] = array( 'addTable', $tableName, $sqlPath, true ); } + /** + * Add a brand new extension to MediaWiki. Used during the initial install + * @param $ext String Name of extension + * @param $sqlPath String Full path to the schema file + */ + public function addNewExtension( $ext, $sqlPath ) { + $this->newExtensions[ strtolower( $ext ) ] = $sqlPath; + } + + /** + * Get the list of extensions that registered a schema with our DB type + * @return array + */ + public function getNewExtensions() { + return $this->newExtensions; + } + /** * Get the list of extension-defined updates * diff --git a/includes/installer/Installer.i18n.php b/includes/installer/Installer.i18n.php index e013ec7db0..e27e0af37a 100644 --- a/includes/installer/Installer.i18n.php +++ b/includes/installer/Installer.i18n.php @@ -467,6 +467,7 @@ Consider changing it manually.", 'config-install-sysop' => 'Creating administrator user account', 'config-install-subscribe-fail' => 'Unable to subscribe to mediawiki-announce', 'config-install-mainpage' => 'Creating main page with default content', + 'config-install-extension-tables' => 'Creating tables for enabled extensions', 'config-install-mainpage-failed' => 'Could not insert main page: $1', 'config-install-done' => "'''Congratulations!''' You have successfully installed MediaWiki. diff --git a/includes/installer/Installer.php b/includes/installer/Installer.php index ceb8e98040..097d98bb93 100644 --- a/includes/installer/Installer.php +++ b/includes/installer/Installer.php @@ -1192,8 +1192,21 @@ abstract class Installer { * @return Status */ protected function includeExtensions() { + global $IP; $exts = $this->getVar( '_Extensions' ); - $path = $this->getVar( 'IP' ) . '/extensions'; + $IP = $this->getVar( 'IP' ); + $path = $IP . '/extensions'; + + /** + * We need to include DefaultSettings before including extensions to avoid + * warnings about unset variables. However, the only thing we really + * want here is $wgHooks['LoadExtensionSchemaUpdates']. This won't work + * if the extension has hidden hook registration in $wgExtensionFunctions, + * but we're not opening that can of worms + * @see https://bugzilla.wikimedia.org/show_bug.cgi?id=26857 + */ + global $wgHooks, $IP; + require( "$IP/includes/DefaultSettings.php" ); foreach( $exts as $e ) { require( "$path/$e/$e.php" ); @@ -1251,6 +1264,10 @@ abstract class Installer { array_unshift( $this->installSteps, array( 'name' => 'extensions', 'callback' => array( $this, 'includeExtensions' ) ) ); + $this->installSteps[] = array( + 'name' => 'extension-tables', + 'callback' => array( $installer, 'createExtensionTables' ) + ); } return $this->installSteps; } -- 2.20.1