(bug 26857) Fatal error during installation enabling 'Vector' extension (or any exten...
authorChad Horohoe <demon@users.mediawiki.org>
Mon, 31 Jan 2011 20:00:59 +0000 (20:00 +0000)
committerChad Horohoe <demon@users.mediawiki.org>
Mon, 31 Jan 2011 20:00:59 +0000 (20:00 +0000)
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
includes/installer/DatabaseUpdater.php
includes/installer/Installer.i18n.php
includes/installer/Installer.php

index 38367c4..16f42b8 100644 (file)
@@ -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.
         *
index 5bdfec5..900dfeb 100644 (file)
@@ -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
         *
index e013ec7..e27e0af 100644 (file)
@@ -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.
index ceb8e98..097d98b 100644 (file)
@@ -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;
        }