461 lines
10 KiB
PHP
461 lines
10 KiB
PHP
<?php
|
|
/**
|
|
* Base core installer.
|
|
*
|
|
* @file
|
|
* @ingroup Deployment
|
|
*/
|
|
|
|
/**
|
|
* Base core installer class.
|
|
* Handles everything that is independent of user interface.
|
|
*
|
|
* @ingroup Deployment
|
|
* @since 1.17
|
|
*/
|
|
abstract class CoreInstaller extends Installer {
|
|
|
|
/**
|
|
* MediaWiki configuration globals that will eventually be passed through
|
|
* to LocalSettings.php. The names only are given here, the defaults
|
|
* typically come from DefaultSettings.php.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $defaultVarNames = array(
|
|
'wgSitename',
|
|
'wgPasswordSender',
|
|
'wgLanguageCode',
|
|
'wgRightsIcon',
|
|
'wgRightsText',
|
|
'wgRightsUrl',
|
|
'wgMainCacheType',
|
|
'wgEnableEmail',
|
|
'wgEnableUserEmail',
|
|
'wgEnotifUserTalk',
|
|
'wgEnotifWatchlist',
|
|
'wgEmailAuthentication',
|
|
'wgDBtype',
|
|
'wgDiff3',
|
|
'wgImageMagickConvertCommand',
|
|
'IP',
|
|
'wgScriptPath',
|
|
'wgScriptExtension',
|
|
'wgMetaNamespace',
|
|
'wgDeletedDirectory',
|
|
'wgEnableUploads',
|
|
'wgLogo',
|
|
'wgShellLocale',
|
|
'wgSecretKey',
|
|
'wgUseInstantCommons',
|
|
);
|
|
|
|
/**
|
|
* Variables that are stored alongside globals, and are used for any
|
|
* configuration of the installation process aside from the MediaWiki
|
|
* configuration. Map of names to defaults.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $internalDefaults = array(
|
|
'_UserLang' => 'en',
|
|
'_Environment' => false,
|
|
'_CompiledDBs' => array(),
|
|
'_SafeMode' => false,
|
|
'_RaiseMemory' => false,
|
|
'_UpgradeDone' => false,
|
|
'_InstallDone' => false,
|
|
'_Caches' => array(),
|
|
'_InstallUser' => 'root',
|
|
'_InstallPassword' => '',
|
|
'_SameAccount' => true,
|
|
'_CreateDBAccount' => false,
|
|
'_NamespaceType' => 'site-name',
|
|
'_AdminName' => '', // will be set later, when the user selects language
|
|
'_AdminPassword' => '',
|
|
'_AdminPassword2' => '',
|
|
'_AdminEmail' => '',
|
|
'_Subscribe' => false,
|
|
'_SkipOptional' => 'continue',
|
|
'_RightsProfile' => 'wiki',
|
|
'_LicenseCode' => 'none',
|
|
'_CCDone' => false,
|
|
'_Extensions' => array(),
|
|
'_MemCachedServers' => '',
|
|
'_ExternalHTTP' => false,
|
|
);
|
|
|
|
/**
|
|
* Steps for installation.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $installSteps = array(
|
|
'database',
|
|
'tables',
|
|
'interwiki',
|
|
'secretkey',
|
|
'sysop',
|
|
);
|
|
|
|
/**
|
|
* Known object cache types and the functions used to test for their existence.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $objectCaches = array(
|
|
'xcache' => 'xcache_get',
|
|
'apc' => 'apc_fetch',
|
|
'eaccel' => 'eaccelerator_get',
|
|
'wincache' => 'wincache_ucache_get'
|
|
);
|
|
|
|
/**
|
|
* User rights profiles.
|
|
*
|
|
* @var array
|
|
*/
|
|
public $rightsProfiles = array(
|
|
'wiki' => array(),
|
|
'no-anon' => array(
|
|
'*' => array( 'edit' => false )
|
|
),
|
|
'fishbowl' => array(
|
|
'*' => array(
|
|
'createaccount' => false,
|
|
'edit' => false,
|
|
),
|
|
),
|
|
'private' => array(
|
|
'*' => array(
|
|
'createaccount' => false,
|
|
'edit' => false,
|
|
'read' => false,
|
|
),
|
|
),
|
|
);
|
|
|
|
/**
|
|
* License types.
|
|
*
|
|
* @var array
|
|
*/
|
|
public $licenses = array(
|
|
'none' => array(
|
|
'url' => '',
|
|
'icon' => '',
|
|
'text' => ''
|
|
),
|
|
'cc-by-sa' => array(
|
|
'url' => 'http://creativecommons.org/licenses/by-sa/3.0/',
|
|
'icon' => '{$wgStylePath}/common/images/cc-by-sa.png',
|
|
),
|
|
'cc-by-nc-sa' => array(
|
|
'url' => 'http://creativecommons.org/licenses/by-nc-sa/3.0/',
|
|
'icon' => '{$wgStylePath}/common/images/cc-by-nc-sa.png',
|
|
),
|
|
'pd' => array(
|
|
'url' => 'http://creativecommons.org/licenses/publicdomain/',
|
|
'icon' => '{$wgStylePath}/common/images/public-domain.png',
|
|
),
|
|
'gfdl-old' => array(
|
|
'url' => 'http://www.gnu.org/licenses/old-licenses/fdl-1.2.html',
|
|
'icon' => '{$wgStylePath}/common/images/gnu-fdl.png',
|
|
),
|
|
'gfdl-current' => array(
|
|
'url' => 'http://www.gnu.org/copyleft/fdl.html',
|
|
'icon' => '{$wgStylePath}/common/images/gnu-fdl.png',
|
|
),
|
|
'cc-choose' => array(
|
|
// Details will be filled in by the selector.
|
|
'url' => '',
|
|
'icon' => '',
|
|
'text' => '',
|
|
),
|
|
);
|
|
|
|
/**
|
|
* TODO: document
|
|
*
|
|
* @param $status Status
|
|
*/
|
|
public abstract function showStatusMessage( Status $status );
|
|
|
|
|
|
/**
|
|
* Constructor, always call this from child classes.
|
|
*/
|
|
public function __construct() {
|
|
parent::__construct();
|
|
|
|
global $wgExtensionMessagesFiles, $wgUser, $wgHooks;
|
|
|
|
// Load the installer's i18n file.
|
|
$wgExtensionMessagesFiles['MediawikiInstaller'] =
|
|
dirname( __FILE__ ) . '/Installer.i18n.php';
|
|
|
|
// Having a user with id = 0 safeguards us from DB access via User::loadOptions().
|
|
$wgUser = User::newFromId( 0 );
|
|
|
|
// Set our custom <doclink> hook.
|
|
$wgHooks['ParserFirstCallInit'][] = array( $this, 'registerDocLink' );
|
|
|
|
$this->settings = $this->internalDefaults;
|
|
|
|
foreach ( $this->defaultVarNames as $var ) {
|
|
$this->settings[$var] = $GLOBALS[$var];
|
|
}
|
|
|
|
foreach ( self::getDBTypes() as $type ) {
|
|
$installer = $this->getDBInstaller( $type );
|
|
|
|
if ( !$installer->isCompiled() ) {
|
|
continue;
|
|
}
|
|
|
|
$defaults = $installer->getGlobalDefaults();
|
|
|
|
foreach ( $installer->getGlobalNames() as $var ) {
|
|
if ( isset( $defaults[$var] ) ) {
|
|
$this->settings[$var] = $defaults[$var];
|
|
} else {
|
|
$this->settings[$var] = $GLOBALS[$var];
|
|
}
|
|
}
|
|
}
|
|
|
|
$this->parserTitle = Title::newFromText( 'Installer' );
|
|
$this->parserOptions = new ParserOptions;
|
|
$this->parserOptions->setEditSection( false );
|
|
}
|
|
|
|
/**
|
|
* Register tag hook below.
|
|
*
|
|
* @param $parser Parser
|
|
*/
|
|
public function registerDocLink( Parser &$parser ) {
|
|
$parser->setHook( 'doclink', array( $this, 'docLink' ) );
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Extension tag hook for a documentation link.
|
|
*/
|
|
public function docLink( $linkText, $attribs, $parser ) {
|
|
$url = $this->getDocUrl( $attribs['href'] );
|
|
return '<a href="' . htmlspecialchars( $url ) . '">' .
|
|
htmlspecialchars( $linkText ) .
|
|
'</a>';
|
|
}
|
|
|
|
/**
|
|
* Overridden by WebInstaller to provide lastPage parameters.
|
|
*/
|
|
protected function getDocUrl( $page ) {
|
|
return "{$_SERVER['PHP_SELF']}?page=" . urlencode( $attribs['href'] );
|
|
}
|
|
|
|
/**
|
|
* Finds extensions that follow the format /extensions/Name/Name.php,
|
|
* and returns an array containing the value for 'Name' for each found extension.
|
|
*
|
|
* @return array
|
|
*/
|
|
public function findExtensions() {
|
|
if( $this->getVar( 'IP' ) === null ) {
|
|
return false;
|
|
}
|
|
|
|
$exts = array();
|
|
$dir = $this->getVar( 'IP' ) . '/extensions';
|
|
$dh = opendir( $dir );
|
|
|
|
while ( ( $file = readdir( $dh ) ) !== false ) {
|
|
if( file_exists( "$dir/$file/$file.php" ) ) {
|
|
$exts[] = $file;
|
|
}
|
|
}
|
|
|
|
return $exts;
|
|
}
|
|
|
|
/**
|
|
* Installs the auto-detected extensions.
|
|
*
|
|
* TODO: this only requires them?
|
|
*
|
|
* @return Status
|
|
*/
|
|
public function installExtensions() {
|
|
$exts = $this->getVar( '_Extensions' );
|
|
$path = $this->getVar( 'IP' ) . '/extensions';
|
|
|
|
foreach( $exts as $e ) {
|
|
require( "$path/$e/$e.php" );
|
|
}
|
|
|
|
return Status::newGood();
|
|
}
|
|
|
|
public function getInstallSteps() {
|
|
if( $this->getVar( '_UpgradeDone' ) ) {
|
|
$this->installSteps = array( 'localsettings' );
|
|
}
|
|
|
|
if( count( $this->getVar( '_Extensions' ) ) ) {
|
|
array_unshift( $this->installSteps, 'extensions' );
|
|
}
|
|
|
|
return $this->installSteps;
|
|
}
|
|
|
|
/**
|
|
* Actually perform the installation.
|
|
*
|
|
* @param $startCB A callback array for the beginning of each step
|
|
* @param $endCB A callback array for the end of each step
|
|
*
|
|
* @return Array of Status objects
|
|
*/
|
|
public function performInstallation( $startCB, $endCB ) {
|
|
$installResults = array();
|
|
$installer = $this->getDBInstaller();
|
|
|
|
foreach( $this->getInstallSteps() as $stepObj ) {
|
|
$step = is_array( $stepObj ) ? $stepObj['name'] : $stepObj;
|
|
call_user_func_array( $startCB, array( $step ) );
|
|
$status = null;
|
|
|
|
# Call our working function
|
|
if ( is_array( $stepObj ) ) {
|
|
# A custom callaback
|
|
$callback = $stepObj['callback'];
|
|
$status = call_user_func_array( $callback, array( $installer ) );
|
|
} else {
|
|
# Boring implicitly named callback
|
|
$func = 'install' . ucfirst( $step );
|
|
$status = $this->{$func}( $installer );
|
|
}
|
|
|
|
call_user_func_array( $endCB, array( $step, $status ) );
|
|
$installResults[$step] = $status;
|
|
|
|
// If we've hit some sort of fatal, we need to bail.
|
|
// Callback already had a chance to do output above.
|
|
if( !$status->isOk() ) {
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
if( $status->isOk() ) {
|
|
$this->setVar( '_InstallDone', true );
|
|
}
|
|
|
|
return $installResults;
|
|
}
|
|
|
|
/**
|
|
* TODO: document
|
|
*
|
|
* @return Status
|
|
*/
|
|
public function installSecretKey() {
|
|
if ( wfIsWindows() ) {
|
|
$file = null;
|
|
} else {
|
|
wfSuppressWarnings();
|
|
$file = fopen( "/dev/urandom", "r" );
|
|
wfRestoreWarnings();
|
|
}
|
|
|
|
$status = Status::newGood();
|
|
|
|
if ( $file ) {
|
|
$secretKey = bin2hex( fread( $file, 32 ) );
|
|
fclose( $file );
|
|
} else {
|
|
$secretKey = '';
|
|
|
|
for ( $i=0; $i<8; $i++ ) {
|
|
$secretKey .= dechex( mt_rand( 0, 0x7fffffff ) );
|
|
}
|
|
|
|
$status->warning( 'config-insecure-secretkey' );
|
|
}
|
|
|
|
$this->setVar( 'wgSecretKey', $secretKey );
|
|
|
|
return $status;
|
|
}
|
|
|
|
/**
|
|
* TODO: document
|
|
*
|
|
* @return Status
|
|
*/
|
|
public function installSysop() {
|
|
$name = $this->getVar( '_AdminName' );
|
|
$user = User::newFromName( $name );
|
|
|
|
if ( !$user ) {
|
|
// We should've validated this earlier anyway!
|
|
return Status::newFatal( 'config-admin-error-user', $name );
|
|
}
|
|
|
|
if ( $user->idForName() == 0 ) {
|
|
$user->addToDatabase();
|
|
|
|
try {
|
|
$user->setPassword( $this->getVar( '_AdminPassword' ) );
|
|
} catch( PasswordError $pwe ) {
|
|
return Status::newFatal( 'config-admin-error-password', $name, $pwe->getMessage() );
|
|
}
|
|
|
|
$user->addGroup( 'sysop' );
|
|
$user->addGroup( 'bureaucrat' );
|
|
$user->saveSettings();
|
|
}
|
|
|
|
return Status::newGood();
|
|
}
|
|
|
|
/**
|
|
* Override the necessary bits of the config to run an installation.
|
|
*/
|
|
public static function overrideConfig() {
|
|
define( 'MW_NO_SESSION', 1 );
|
|
|
|
// Don't access the database
|
|
$GLOBALS['wgUseDatabaseMessages'] = false;
|
|
// Debug-friendly
|
|
$GLOBALS['wgShowExceptionDetails'] = true;
|
|
// Don't break forms
|
|
$GLOBALS['wgExternalLinkTarget'] = '_blank';
|
|
|
|
// Extended debugging. Maybe disable before release?
|
|
$GLOBALS['wgShowSQLErrors'] = true;
|
|
$GLOBALS['wgShowDBErrorBacktrace'] = true;
|
|
|
|
// Allow multiple ob_flush() calls
|
|
$GLOBALS['wgDisableOutputCompression'] = true;
|
|
}
|
|
|
|
/**
|
|
* Add an installation step following the given step.
|
|
*
|
|
* @param $findStep String the step to find. Use NULL to put the step at the beginning.
|
|
* @param $callback array
|
|
*/
|
|
public function addInstallStepFollowing( $findStep, $callback ) {
|
|
$where = 0;
|
|
|
|
if( $findStep !== null ) {
|
|
$where = array_search( $findStep, $this->installSteps );
|
|
}
|
|
|
|
array_splice( $this->installSteps, $where, 0, $callback );
|
|
}
|
|
|
|
}
|