27use GuzzleHttp\Psr7\Header;
44use Wikimedia\AtEase\AtEase;
72 private const MEDIAWIKI_ANNOUNCE_URL =
73 'https://lists.wikimedia.org/postorius/lists/mediawiki-announce.lists.wikimedia.org/';
147 'envCheckModSecurity',
153 'envCheckUploadsDirectory',
154 'envCheckUploadsServerResponse',
175 private const DEFAULT_VAR_NAMES = [
176 MainConfigNames::Sitename,
177 MainConfigNames::PasswordSender,
178 MainConfigNames::LanguageCode,
179 MainConfigNames::Localtimezone,
180 MainConfigNames::RightsIcon,
181 MainConfigNames::RightsText,
182 MainConfigNames::RightsUrl,
183 MainConfigNames::EnableEmail,
184 MainConfigNames::EnableUserEmail,
185 MainConfigNames::EnotifUserTalk,
186 MainConfigNames::EnotifWatchlist,
187 MainConfigNames::EmailAuthentication,
188 MainConfigNames::DBname,
189 MainConfigNames::DBtype,
190 MainConfigNames::Diff3,
191 MainConfigNames::ImageMagickConvertCommand,
192 MainConfigNames::GitBin,
193 MainConfigNames::ScriptPath,
194 MainConfigNames::MetaNamespace,
195 MainConfigNames::DeletedDirectory,
196 MainConfigNames::EnableUploads,
197 MainConfigNames::SecretKey,
198 MainConfigNames::UseInstantCommons,
199 MainConfigNames::UpgradeKey,
200 MainConfigNames::DefaultSkin,
201 MainConfigNames::Pingback,
213 '_Environment' =>
false,
214 '_RaiseMemory' =>
false,
215 '_UpgradeDone' =>
false,
216 '_InstallDone' =>
false,
218 '_InstallPassword' =>
'',
219 '_SameAccount' =>
true,
220 '_CreateDBAccount' =>
false,
221 '_NamespaceType' =>
'site-name',
223 '_AdminPassword' =>
'',
224 '_AdminPasswordConfirm' =>
'',
226 '_Subscribe' =>
false,
227 '_SkipOptional' =>
'continue',
228 '_RightsProfile' =>
'wiki',
229 '_LicenseCode' =>
'none',
233 '_MemCachedServers' =>
'',
234 '_UpgradeKeySupplied' =>
false,
235 '_ExistingDBSettings' =>
false,
236 '_LogoWordmark' =>
'',
237 '_LogoWordmarkWidth' => 119,
238 '_LogoWordmarkHeight' => 18,
240 '_Logo1x' =>
'$wgResourceBasePath/resources/assets/change-your-logo.svg',
241 '_LogoIcon' =>
'$wgResourceBasePath/resources/assets/change-your-logo-icon.svg',
242 '_LogoTagline' =>
'',
243 '_LogoTaglineWidth' => 117,
244 '_LogoTaglineHeight' => 13,
245 '_WithDevelopmentSettings' =>
false,
246 'wgAuthenticationTokenVersion' => 1,
255 private $installSteps = [];
270 'apcu' =>
'apcu_fetch',
271 'wincache' =>
'wincache_ucache_get'
282 '*' => [
'edit' => false ]
286 'createaccount' =>
false,
292 'createaccount' =>
false,
306 'url' =>
'https://creativecommons.org/licenses/by/4.0/',
307 'icon' =>
'$wgResourceBasePath/resources/assets/licenses/cc-by.png',
310 'url' =>
'https://creativecommons.org/licenses/by-sa/4.0/',
311 'icon' =>
'$wgResourceBasePath/resources/assets/licenses/cc-by-sa.png',
314 'url' =>
'https://creativecommons.org/licenses/by-nc-sa/4.0/',
315 'icon' =>
'$wgResourceBasePath/resources/assets/licenses/cc-by-nc-sa.png',
318 'url' =>
'https://creativecommons.org/publicdomain/zero/1.0/',
319 'icon' =>
'$wgResourceBasePath/resources/assets/licenses/cc-0.png',
322 'url' =>
'https://www.gnu.org/copyleft/fdl.html',
323 'icon' =>
'$wgResourceBasePath/resources/assets/licenses/gnu-fdl.png',
381 $emptyCache = [
'class' => EmptyBagOStuff::class ];
388 ] + $baseConfig->
get( MainConfigNames::ObjectCaches );
390 $configOverrides->set( MainConfigNames::ObjectCaches,
$objectCaches );
393 $messageDirs = $baseConfig->
get( MainConfigNames::MessagesDirs );
394 $messageDirs[
'MediawikiInstaller'] = __DIR__ .
'/i18n';
396 $configOverrides->set( MainConfigNames::MessagesDirs, $messageDirs );
398 $installerConfig =
new MultiConfig( [ $configOverrides, $baseConfig ] );
401 $configRegistry = $baseConfig->
get( MainConfigNames::ConfigRegistry );
402 $configRegistry[
'main'] =
static function () use ( $installerConfig ) {
403 return $installerConfig;
406 $configOverrides->set( MainConfigNames::ConfigRegistry, $configRegistry );
408 return $installerConfig;
421 $this->settings = $this->getDefaultSettings();
425 $this->compiledDBs = [];
426 foreach ( self::getDBTypes() as $type ) {
429 if ( !$installer->isCompiled() ) {
432 $this->compiledDBs[] = $type;
435 $this->parserTitle = Title::newFromText(
'Installer' );
441 private function getDefaultSettings(): array {
446 foreach ( self::DEFAULT_VAR_NAMES as $name ) {
448 $ret[$var] = MainConfigSchema::getDefaultValue( $name );
473 Config $installerConfig =
null,
474 $serviceOverrides = [],
475 bool $disableStorage =
false
481 MediaWikiServices::resetGlobalInstance( $installerConfig );
483 $mwServices = MediaWikiServices::getInstance();
485 if ( $disableStorage ) {
486 $mwServices->disableStorage();
490 $serviceOverrides += [
492 'InterwikiLookup' =>
static function () {
498 return $services->get(
'_DefaultOptionsLookup' );
503 return $services->getDBLoadBalancerFactory()->getMainLB();
508 $lang = $this->getVar(
'_UserLang',
'en' );
510 foreach ( $serviceOverrides as $name => $callback ) {
514 $mwServices->redefineService( $name, $callback );
519 $mwServices->getLocalisationCache()->disableBackend();
524 $user = User::newFromId( 0 );
525 StubGlobalUser::setUser( $user );
527 RequestContext::getMain()->setUser( $user );
533 RequestContext::getMain()->setLanguage( $lang );
534 $wgLang = RequestContext::getMain()->getLanguage();
538 $wgObjectCaches = $mwServices->getMainConfig()->get( MainConfigNames::ObjectCaches );
542 $this->setParserLanguage( $mwServices->getLanguageFactory()->getLanguage(
'en' ) );
553 return self::$dbTypes;
572 $this->showMessage(
'config-env-php', PHP_VERSION );
575 foreach ( $this->envChecks as $check ) {
576 $status = $this->$check();
577 if ( $status ===
false ) {
582 $this->setVar(
'_Environment', $good );
584 return $good ? Status::newGood() : Status::newFatal(
'config-env-bad' );
588 foreach ( $this->envPreps as $prep ) {
599 public function setVar( $name, $value ) {
600 $this->settings[$name] = $value;
613 public function getVar( $name, $default =
null ) {
614 return $this->settings[$name] ?? $default;
623 return $this->compiledDBs;
634 return ucfirst( $type ) .
'Installer';
646 $type = $this->getVar(
'wgDBtype' );
649 $type = strtolower( $type );
651 if ( !isset( $this->dbInstallers[$type] ) ) {
652 $class = self::getDBInstallerClass( $type );
653 $this->dbInstallers[$type] =
new $class( $this );
656 return $this->dbInstallers[$type];
683 $lsExists = @file_exists( $lsFile );
689 if ( !str_ends_with( $lsFile,
'.php' ) ) {
691 'The installer cannot yet handle non-php settings files: ' . $lsFile .
'. ' .
692 'Use `php maintenance/run.php update` to update an existing installation.'
698 foreach ( MainConfigSchema::listDefaultValues(
'wg' ) as $var => $value ) {
718 return get_defined_vars();
731 return str_repeat(
'*', strlen( $realPassword ) );
742 if ( !preg_match(
'/^\*+$/', $value ) ) {
743 $this->setVar( $name, $value );
759 if ( !function_exists(
'posix_getegid' ) || !function_exists(
'posix_getpwuid' ) ) {
760 # I don't know this, this isn't UNIX.
764 # posix_getegid() *not* getmygid() because we want the group of the webserver,
765 # not whoever owns the current script.
766 $gid = posix_getegid();
767 return posix_getpwuid( $gid )[
'name'] ??
null;
786 public function parse( $text, $lineStart =
false ) {
787 $parser = MediaWikiServices::getInstance()->getParser();
790 $out = $parser->parse( $text, $this->parserTitle, $this->parserOptions, $lineStart );
791 $html = $out->getText( [
792 'enableSectionEditLinks' =>
false,
795 $html = Parser::stripOuterParagraph( $html );
796 }
catch (
Wikimedia\Services\ServiceDisabledException $e ) {
797 $html =
'<!--DB access attempted during parse--> ' . htmlspecialchars( $text );
807 return $this->parserOptions;
813 $this->parserOptions->setExternalLinkTarget(
false );
833 if ( !$status->isOK() ) {
837 $status->value->insert(
841 'ss_total_edits' => 0,
842 'ss_good_articles' => 0,
843 'ss_total_pages' => 0,
845 'ss_active_users' => 0,
852 return Status::newGood();
862 $dbType = $this->getVar(
'wgDBtype' );
867 foreach ( self::getDBTypes() as $name ) {
868 $allNames[] =
wfMessage(
"config-type-$name" )->text();
871 $databases = $this->getCompiledDBs();
873 $databases = array_flip( $databases );
875 foreach ( $databases as $db => $_ ) {
876 $installer = $this->getDBInstaller( $db );
877 $status = $installer->checkPrerequisites();
878 if ( !$status->isGood() ) {
879 if ( !$this instanceof
WebInstaller && $db === $dbType ) {
885 $this->showStatusMessage( $status );
886 unset( $databases[$db] );
889 $databases = array_flip( $databases );
891 $this->showError(
'config-no-db',
$wgLang->commaList( $allNames ), count( $allNames ) );
910 if ( preg_match(
'/^b.*c$/',
'bÄ…c' ) === 0 ) {
911 $this->showError(
'config-pcre-invalid-newline' );
922 $limit = ini_get(
'memory_limit' );
924 if ( !$limit || $limit == -1 ) {
930 if ( $n < $this->minMemorySize * 1024 * 1024 ) {
931 $newLimit =
"{$this->minMemorySize}M";
933 if ( ini_set(
"memory_limit", $newLimit ) ===
false ) {
934 $this->showMessage(
'config-memory-bad', $limit );
936 $this->showMessage(
'config-memory-raised', $limit, $newLimit );
937 $this->setVar(
'_RaiseMemory',
true );
949 foreach ( $this->objectCaches as $name => $function ) {
950 if ( function_exists( $function ) ) {
951 $caches[$name] =
true;
956 $this->showMessage(
'config-no-cache-apcu' );
959 $this->setVar(
'_Caches', $caches );
967 if ( self::apacheModulePresent(
'mod_security' )
968 || self::apacheModulePresent(
'mod_security2' ) ) {
969 $this->showMessage(
'config-mod-security' );
980 $names = [
"gdiff3",
"diff3" ];
982 $names[] =
'diff3.exe';
984 $versionInfo = [
'--version',
'GNU diffutils' ];
986 $diff3 = ExecutableFinder::findInDefaultPaths( $names, $versionInfo );
989 $this->setVar(
'wgDiff3', $diff3 );
991 $this->setVar(
'wgDiff3',
false );
992 $this->showMessage(
'config-diff3-bad' );
1003 $names =
wfIsWindows() ?
'convert.exe' :
'convert';
1004 $versionInfo = [
'-version',
'ImageMagick' ];
1005 $convert = ExecutableFinder::findInDefaultPaths( $names, $versionInfo );
1007 $this->setVar(
'wgImageMagickConvertCommand',
'' );
1009 $this->setVar(
'wgImageMagickConvertCommand', $convert );
1010 $this->showMessage(
'config-imagemagick', $convert );
1011 } elseif ( function_exists(
'imagejpeg' ) ) {
1012 $this->showMessage(
'config-gd' );
1014 $this->showMessage(
'config-no-scaling' );
1028 $versionInfo = [
'--version',
'git version' ];
1030 $git = ExecutableFinder::findInDefaultPaths( $names, $versionInfo );
1033 $this->setVar(
'wgGitBin', $git );
1034 $this->showMessage(
'config-git', $git );
1036 $this->setVar(
'wgGitBin',
false );
1037 $this->showMessage(
'config-git-bad' );
1049 $server = $this->envGetDefaultServer();
1050 if ( $server !==
null ) {
1051 $this->showMessage(
'config-using-server', $server );
1064 $this->getVar(
'wgServer' ),
1065 $this->getVar(
'wgScriptPath' )
1077 $dir =
$IP .
'/images/';
1078 $url = $this->getVar(
'wgServer' ) . $this->getVar(
'wgScriptPath' ) .
'/images/';
1079 $safe = !$this->dirIsExecutable( $dir, $url );
1082 $this->showMessage(
'config-uploads-not-safe', $dir );
1089 $url = $this->getVar(
'wgServer' ) . $this->getVar(
'wgScriptPath' ) .
'/images/README';
1090 $httpRequestFactory = MediaWikiServices::getInstance()->getHttpRequestFactory();
1093 $req = $httpRequestFactory->create(
1098 'followRedirects' =>
true
1103 $status = $req->execute();
1104 }
catch ( Exception $e ) {
1109 if ( !$status || !$status->isGood() ) {
1110 $this->showMessage(
'config-uploads-security-requesterror',
'X-Content-Type-Options: nosniff' );
1114 $headerValue = $req->getResponseHeader(
'X-Content-Type-Options' ) ??
'';
1115 $responseList = Header::splitList( $headerValue );
1116 if ( !in_array(
'nosniff', $responseList,
true ) ) {
1117 $this->showMessage(
'config-uploads-security-headers',
'X-Content-Type-Options: nosniff' );
1130 if ( PHP_INT_SIZE == 4 ) {
1131 $this->showMessage(
'config-using-32bit' );
1141 $unicodeVersion = implode(
'.', array_slice( IntlChar::getUnicodeVersion(), 0, 3 ) );
1142 $this->showMessage(
'config-env-icu', INTL_ICU_VERSION, $unicodeVersion );
1149 $server = $this->envGetDefaultServer();
1150 if ( $server !==
null ) {
1151 $this->setVar(
'wgServer', $server );
1166 $IP = dirname( dirname( __DIR__ ) );
1167 $this->setVar(
'IP',
$IP );
1181 "<?php echo 'exec';",
1182 "#!/var/env php\n<?php echo 'exec';",
1189 $httpRequestFactory = MediaWikiServices::getInstance()->getHttpRequestFactory();
1191 AtEase::suppressWarnings();
1193 foreach ( $scriptTypes as
$ext => $contents ) {
1194 foreach ( $contents as
$source ) {
1202 $text = $httpRequestFactory->get(
1207 }
catch ( Exception $e ) {
1212 unlink( $dir .
$file );
1214 if ( $text ==
'exec' ) {
1215 AtEase::restoreWarnings();
1222 AtEase::restoreWarnings();
1234 if ( function_exists(
'apache_get_modules' ) && in_array( $moduleName, apache_get_modules() ) ) {
1239 phpinfo( INFO_MODULES );
1240 $info = ob_get_clean();
1242 return strpos( $info, $moduleName ) !==
false;
1251 $this->parserOptions->setTargetLanguage( $lang );
1252 $this->parserOptions->setUserLang( $lang );
1261 return "{$_SERVER['PHP_SELF']}?page=" . urlencode( $page );
1274 switch ( $directory ) {
1276 return $this->findExtensionsByType(
'extension',
'extensions' );
1278 return $this->findExtensionsByType(
'skin',
'skins' );
1280 throw new InvalidArgumentException(
"Invalid extension type" );
1294 if ( $this->getVar(
'IP' ) ===
null ) {
1295 return Status::newGood( [] );
1298 $extDir = $this->getVar(
'IP' ) .
'/' . $directory;
1299 if ( !is_readable( $extDir ) || !is_dir( $extDir ) ) {
1300 return Status::newGood( [] );
1303 $dh = opendir( $extDir );
1306 while ( (
$file = readdir( $dh ) ) !==
false ) {
1308 if ( !is_dir(
"$extDir/$file" ) ||
$file[0] ===
'.' ) {
1311 $extStatus = $this->getExtensionInfo( $type, $directory,
$file );
1312 if ( $extStatus->isOK() ) {
1313 $exts[
$file] = $extStatus->value;
1314 } elseif ( $extStatus->hasMessage(
'config-extension-not-found' ) ) {
1316 $status->warning(
'config-extension-not-found',
$file );
1318 $status->merge( $extStatus );
1322 uksort( $exts,
'strnatcasecmp' );
1324 $status->value = $exts;
1337 if ( $this->getVar(
'IP' ) ===
null ) {
1338 throw new Exception(
'Cannot find extensions since the IP variable is not yet set' );
1340 if ( $type !==
'extension' && $type !==
'skin' ) {
1341 throw new InvalidArgumentException(
"Invalid extension type" );
1343 $absDir = $this->getVar(
'IP' ) .
"/$parentRelPath/$name";
1344 $relDir =
"../$parentRelPath/$name";
1345 if ( !is_dir( $absDir ) ) {
1346 return Status::newFatal(
'config-extension-not-found', $name );
1348 $jsonFile = $type .
'.json';
1349 $fullJsonFile =
"$absDir/$jsonFile";
1350 $isJson = file_exists( $fullJsonFile );
1354 $fullPhpFile =
"$absDir/$name.php";
1355 $isPhp = file_exists( $fullPhpFile );
1357 if ( !$isJson && !$isPhp ) {
1358 return Status::newFatal(
'config-extension-not-found', $name );
1363 if ( is_dir(
"$absDir/screenshots" ) ) {
1364 $paths = glob(
"$absDir/screenshots/*.png" );
1365 foreach ( $paths as
$path ) {
1366 $info[
'screenshots'][] = str_replace( $absDir, $relDir,
$path );
1371 $jsonStatus = $this->readExtension( $fullJsonFile );
1372 if ( !$jsonStatus->isOK() ) {
1375 $info += $jsonStatus->value;
1378 return Status::newGood( $info );
1389 private function readExtension( $fullJsonFile, $extDeps = [], $skinDeps = [] ) {
1394 $extDir = $this->getVar(
'IP' ) .
'/extensions';
1395 foreach ( $extDeps as $dep ) {
1396 $fname =
"$extDir/$dep/extension.json";
1397 if ( !file_exists( $fname ) ) {
1398 return Status::newFatal(
'config-extension-not-found', $dep );
1404 $skinDir = $this->getVar(
'IP' ) .
'/skins';
1405 foreach ( $skinDeps as $dep ) {
1406 $fname =
"$skinDir/$dep/skin.json";
1407 if ( !file_exists( $fname ) ) {
1408 return Status::newFatal(
'config-extension-not-found', $dep );
1415 $info = $registry->readFromQueue( $load );
1417 if ( $e->incompatibleCore || $e->incompatibleSkins
1418 || $e->incompatibleExtensions
1422 return Status::newFatal(
'config-extension-dependency',
1423 basename( dirname( $fullJsonFile ) ), $e->getMessage() );
1424 } elseif ( $e->missingExtensions || $e->missingSkins ) {
1427 $status = $this->readExtension(
1429 array_merge( $extDeps, $e->missingExtensions ),
1430 array_merge( $skinDeps, $e->missingSkins )
1432 if ( !$status->isOK() && !$status->hasMessage(
'config-extension-dependency' ) ) {
1433 $status = Status::newFatal(
'config-extension-dependency',
1434 basename( dirname( $fullJsonFile ) ), $status->getMessage() );
1439 return Status::newFatal(
'config-extension-dependency',
1440 basename( dirname( $fullJsonFile ) ), $e->getMessage() );
1447 foreach ( $info[
'credits'] as $credit ) {
1453 $type = basename( $credit[
'path'] ) ===
'skin.json' ?
'skins' :
'extensions';
1454 $ret[
'requires'][$type][] = $credit[
'name'];
1456 $credits = array_values( $info[
'credits'] )[0];
1457 if ( isset( $credits[
'url'] ) ) {
1458 $ret[
'url'] = $credits[
'url'];
1460 $ret[
'type'] = $credits[
'type'];
1462 return Status::newGood( $ret );
1474 $defaultSkin = $GLOBALS[
'wgDefaultSkin'];
1476 if ( in_array(
'vector', $skinNames ) ) {
1477 $skinNames[] =
'vector-2022';
1481 if ( in_array(
'minervaneue', $skinNames ) ) {
1482 $minervaNeue = array_search(
'minervaneue', $skinNames );
1483 $skinNames[$minervaNeue] =
'minerva';
1486 if ( !$skinNames || in_array( $defaultSkin, $skinNames ) ) {
1487 return $defaultSkin;
1489 return $skinNames[0];
1501 define(
'MW_EXTENSIONS_LOADED',
true );
1503 $legacySchemaHooks = $this->getAutoExtensionLegacyHooks();
1504 $data = $this->getAutoExtensionData();
1505 if ( isset( $data[
'globals'][
'wgHooks'][
'LoadExtensionSchemaUpdates'] ) ) {
1506 $legacySchemaHooks = array_merge( $legacySchemaHooks,
1507 $data[
'globals'][
'wgHooks'][
'LoadExtensionSchemaUpdates'] );
1509 $extDeprecatedHooks = $data[
'attributes'][
'DeprecatedHooks'] ?? [];
1513 [
'LoadExtensionSchemaUpdates' => $legacySchemaHooks ],
1514 $data[
'attributes'][
'Hooks'] ?? [],
1517 MediaWikiServices::getInstance()->getObjectFactory()
1520 return Status::newGood();
1531 $exts = $this->getVar(
'_Extensions' );
1532 $installPath = $this->getVar(
'IP' );
1534 foreach ( $exts as $e ) {
1535 if ( file_exists(
"$installPath/extensions/$e/$e.php" ) ) {
1536 $files[] =
"$installPath/extensions/$e/$e.php";
1541 return $this->includeExtensionFiles( $files );
1556 $IP = $this->getVar(
'IP' );
1567 foreach ( MainConfigSchema::listDefaultValues(
'wg' ) as $var => $value ) {
1576 foreach ( $files as
$file ) {
1583 $hooksWeWant =
$wgHooks[
'LoadExtensionSchemaUpdates'] ?? [];
1588 return [
'LoadExtensionSchemaUpdates' => $hooksWeWant ];
1598 $exts = $this->getVar(
'_Extensions' );
1599 $installPath = $this->getVar(
'IP' );
1602 foreach ( $exts as $e ) {
1603 $jsonPath =
"$installPath/extensions/$e/extension.json";
1604 if ( file_exists( $jsonPath ) ) {
1605 $extensionProcessor->extractInfoFromFile( $jsonPath );
1609 $autoload = $extensionProcessor->getExtractedAutoloadInfo();
1614 return $extensionProcessor->getExtractedInfo();
1625 if ( !$this->autoExtensionHookContainer ) {
1626 throw new \Exception( __METHOD__ .
1627 ': includeExtensions() has not been called' );
1629 return $this->autoExtensionHookContainer;
1646 $coreInstallSteps = [
1647 [
'name' =>
'database',
'callback' => [ $installer,
'setupDatabase' ] ],
1648 [
'name' =>
'tables',
'callback' => [ $installer,
'createTables' ] ],
1649 [
'name' =>
'tables-manual',
'callback' => [ $installer,
'createManualTables' ] ],
1650 [
'name' =>
'interwiki',
'callback' => [ $installer,
'populateInterwikiTable' ] ],
1651 [
'name' =>
'stats',
'callback' => [ $this,
'populateSiteStats' ] ],
1652 [
'name' =>
'keys',
'callback' => [ $this,
'generateKeys' ] ],
1653 [
'name' =>
'updates',
'callback' => [ $installer,
'insertUpdateKeys' ] ],
1654 [
'name' =>
'restore-services',
'callback' => [ $this,
'restoreServices' ] ],
1655 [
'name' =>
'sysop',
'callback' => [ $this,
'createSysop' ] ],
1656 [
'name' =>
'mainpage',
'callback' => [ $this,
'createMainpage' ] ],
1661 foreach ( $coreInstallSteps as $step ) {
1662 $this->installSteps[] = $step;
1663 if ( isset( $this->extraInstallSteps[$step[
'name']] ) ) {
1664 $this->installSteps = array_merge(
1665 $this->installSteps,
1666 $this->extraInstallSteps[$step[
'name']]
1672 if ( isset( $this->extraInstallSteps[
'BEGINNING'] ) ) {
1673 $this->installSteps = array_merge(
1674 $this->extraInstallSteps[
'BEGINNING'],
1680 if ( count( $this->getVar(
'_Extensions' ) ) ) {
1681 array_unshift( $this->installSteps,
1682 [
'name' =>
'extensions',
'callback' => [ $this,
'includeExtensions' ] ]
1684 $this->installSteps[] = [
1685 'name' =>
'extension-tables',
1686 'callback' => [ $installer,
'createExtensionTables' ]
1690 return $this->installSteps;
1702 $installResults = [];
1703 $installer = $this->getDBInstaller();
1704 $installer->preInstall();
1705 $steps = $this->getInstallSteps( $installer );
1706 foreach ( $steps as $stepObj ) {
1707 $name = $stepObj[
'name'];
1708 call_user_func_array( $startCB, [ $name ] );
1711 $status = call_user_func( $stepObj[
'callback'], $installer );
1714 call_user_func( $endCB, $name, $status );
1715 $installResults[$name] = $status;
1719 if ( !$status->isOK() ) {
1725 if ( $status->isOK() ) {
1727 'config-install-db-success'
1729 $this->setVar(
'_InstallDone',
true );
1732 return $installResults;
1741 $keys = [
'wgSecretKey' => 64 ];
1742 if ( strval( $this->getVar(
'wgUpgradeKey' ) ) ===
'' ) {
1743 $keys[
'wgUpgradeKey'] = 16;
1746 return $this->doGenerateKeys( $keys );
1754 $this->resetMediaWikiServices(
null, [
1756 return $services->get(
'UserOptionsManager' );
1759 return Status::newGood();
1769 foreach ( $keys as $name => $length ) {
1770 $secretKey = MWCryptRand::generateHex( $length );
1771 $this->setVar( $name, $secretKey );
1773 return Status::newGood();
1782 $name = $this->getVar(
'_AdminName' );
1783 $user = User::newFromName( $name );
1787 return Status::newFatal(
'config-admin-error-user', $name );
1790 if ( $user->idForName() == 0 ) {
1791 $user->addToDatabase();
1793 $password = $this->getVar(
'_AdminPassword' );
1794 $status = $user->changeAuthenticationData( [
1795 'username' => $user->getName(),
1796 'password' => $password,
1797 'retype' => $password,
1799 if ( !$status->isGood() ) {
1800 return Status::newFatal(
'config-admin-error-password',
1801 $name, $status->getWikiText(
false,
false, $this->getVar(
'_UserLang' ) ) );
1804 $userGroupManager = MediaWikiServices::getInstance()->getUserGroupManager();
1805 $userGroupManager->addUserToGroup( $user,
'sysop' );
1806 $userGroupManager->addUserToGroup( $user,
'bureaucrat' );
1807 $userGroupManager->addUserToGroup( $user,
'interface-admin' );
1808 if ( $this->getVar(
'_AdminEmail' ) ) {
1809 $user->setEmail( $this->getVar(
'_AdminEmail' ) );
1811 $user->saveSettings();
1814 $ssUpdate = SiteStatsUpdate::factory( [
'users' => 1 ] );
1815 $ssUpdate->doUpdate();
1818 if ( $this->getVar(
'_Subscribe' ) && $this->getVar(
'_AdminEmail' ) ) {
1819 return $this->subscribeToMediaWikiAnnounce();
1821 return Status::newGood();
1827 private function subscribeToMediaWikiAnnounce() {
1828 $status = Status::newGood();
1829 $http = MediaWikiServices::getInstance()->getHttpRequestFactory();
1830 if ( !$http->canMakeRequests() ) {
1831 $status->warning(
'config-install-subscribe-fail',
1832 wfMessage(
'config-install-subscribe-notpossible' ) );
1837 $params = [
'email' => $this->getVar(
'_AdminEmail' ) ];
1838 $req = $http->create( self::MEDIAWIKI_ANNOUNCE_URL .
'anonymous_subscribe',
1839 [
'method' =>
'POST',
'postData' => $params ], __METHOD__ );
1842 $token = str_repeat(
'a', 64 );
1843 $req->setHeader(
'Referer', self::MEDIAWIKI_ANNOUNCE_URL );
1844 $req->setHeader(
'Cookie',
"csrftoken=$token" );
1845 $req->setHeader(
'X-CSRFToken', $token );
1848 $reqStatus = $req->execute();
1849 if ( !$reqStatus->isOK() ) {
1850 $status->warning(
'config-install-subscribe-fail',
1851 Status::wrap( $reqStatus )->getMessage() );
1860 $checkReq = $http->create( self::MEDIAWIKI_ANNOUNCE_URL, [], __METHOD__ );
1861 $checkReq->setCookieJar( $req->getCookieJar() );
1862 if ( !$checkReq->execute()->isOK() ) {
1863 $status->warning(
'config-install-subscribe-possiblefail' );
1866 $html = $checkReq->getContent();
1867 if ( strpos( $html,
'Please check your inbox for further instructions' ) !==
false ) {
1869 } elseif ( strpos( $html,
'Member already subscribed' ) !==
false ) {
1870 $status->warning(
'config-install-subscribe-alreadysubscribed' );
1871 } elseif ( strpos( $html,
'Subscription request already pending' ) !==
false ) {
1872 $status->warning(
'config-install-subscribe-alreadypending' );
1874 $status->warning(
'config-install-subscribe-possiblefail' );
1886 $status = Status::newGood();
1887 $title = Title::newMainPage();
1888 if ( $title->exists() ) {
1889 $status->warning(
'config-install-mainpage-exists' );
1893 $page = MediaWikiServices::getInstance()->getWikiPageFactory()->newFromTitle( $title );
1895 wfMessage(
'mainpagetext' )->inContentLanguage()->text() .
"\n\n" .
1896 wfMessage(
'mainpagedocfooter' )->inContentLanguage()->text()
1899 $status = $page->doUserEditContent(
1901 User::newSystemUser(
'MediaWiki default' ),
1905 }
catch ( Exception $e ) {
1907 $status->fatal(
'config-install-mainpage-failed', $e->getMessage() );
1921 if ( !defined(
'MW_NO_SESSION_HANDLER' ) ) {
1922 define(
'MW_NO_SESSION_HANDLER', 1 );
1928 MainConfigNames::UseDatabaseMessages =>
false,
1931 MainConfigNames::LanguageConverterCacheType =>
CACHE_NONE,
1934 MainConfigNames::ResourceLoaderUseObjectCacheForDeps =>
true,
1937 MainConfigNames::ShowExceptionDetails =>
true,
1938 MainConfigNames::ShowHostnames =>
true,
1941 MainConfigNames::ExternalLinkTarget =>
'_blank',
1944 MainConfigNames::DisableOutputCompression =>
true,
1947 MainConfigNames::CookiePrefix =>
'mw_installer',
1950 MainConfigNames::MaxShellMemory => 0,
1954 MainConfigNames::SessionProviders => [
1956 'class' => InstallerSessionProvider::class,
1967 MainConfigNames::SessionCacheType =>
CACHE_NONE,
1971 MainConfigNames::Server =>
'https://🌻.invalid',
1983 $this->extraInstallSteps[$findStep][] = $callback;
1991 AtEase::suppressWarnings();
1992 set_time_limit( 0 );
1993 AtEase::restoreWarnings();
wfDetectLocalSettingsFile(?string $installationPath=null)
Decide and remember where to load LocalSettings from.
wfIsWindows()
Check if the operating system is Windows.
wfDetectInstallPath()
Decide and remember where mediawiki is installed.
wfShorthandToInteger(?string $string='', int $default=-1)
Converts shorthand byte notation to integer form.
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
if(!defined( 'MEDIAWIKI')) if(ini_get('mbstring.func_overload')) if(!defined( 'MW_ENTRY_POINT')) global $IP
Environment checks.
if(!defined( 'MW_NO_SESSION') &&! $wgCommandLineMode) $wgLang
static registerClasses(array $files)
Register a file to load the given class from.
static loadFiles(array $files)
Batch version of loadFile()
static registerNamespaces(array $dirs)
Register a directory to load the classes of a given namespace from, per PSR4.
Base class for DBMS-specific installation helper classes.
getConnection()
Connect to the database using the administrative user/password currently defined in the session.
Load extension manifests and then aggregate their contents.
Load JSON files, and uses a Processor to extract information.
envPrepServer()
Environment prep for the server hostname.
parse( $text, $lineStart=false)
Convert wikitext $text to HTML.
array $compiledDBs
List of detected DBs, access using getCompiledDBs().
getExtensionInfo( $type, $parentRelPath, $name)
Title $parserTitle
Cached Title, used by parse().
includeExtensions()
Installs the auto-detected extensions.
createMainpage(DatabaseInstaller $installer)
Insert Main Page with default content.
getDefaultSkin(array $skinNames)
Returns a default value to be used for $wgDefaultSkin: normally the DefaultSkin from config-schema....
envCheckLibicu()
Check and display the libicu and Unicode versions.
getDBInstaller( $type=false)
Get an instance of DatabaseInstaller for the specified DB type.
envCheckDB()
Environment check for DB types.
envCheckUploadsServerResponse()
setVar( $name, $value)
Set a MW configuration variable, or internal installer configuration variable.
array $internalDefaults
Variables that are stored alongside globals, and are used for any configuration of the installation p...
envCheckModSecurity()
Scare user to death if they have mod_security or mod_security2.
getFakePassword( $realPassword)
Get a fake password for sending back to the user in HTML.
envCheckServer()
Environment check to inform user which server we've assumed.
disableTimeLimit()
Disable the time limit for execution.
static apacheModulePresent( $moduleName)
Checks for presence of an Apache module.
array $rightsProfiles
User rights profiles.
addInstallStep( $callback, $findStep='BEGINNING')
Add an installation step following the given step.
getCompiledDBs()
Get a list of DBs supported by current PHP setup.
ParserOptions $parserOptions
Cached ParserOptions, used by parse().
dirIsExecutable( $dir, $url)
Checks if scripts located in the given directory can be executed via the given URL.
envCheckGit()
Search for git.
getDocUrl( $page)
Overridden by WebInstaller to provide lastPage parameters.
static getExistingLocalSettings()
Determine if LocalSettings.php exists.
static getInstallerConfig(Config $baseConfig)
Constructs a Config object that contains configuration settings that should be overwritten for the in...
restoreServices()
Restore services that have been redefined in the early stage of installation.
includeExtensionFiles( $files)
Include the specified extension PHP files.
getAutoExtensionData()
Auto-detect extensions with an extension.json file.
envCheckMemory()
Environment check for available memory.
array $objectCaches
Known object cache types and the functions used to test for their existence.
array $licenses
License types.
static maybeGetWebserverPrimaryGroup()
On POSIX systems return the primary group of the webserver we're running under.
doEnvironmentChecks()
Do initial checks of the PHP environment.
performInstallation( $startCB, $endCB)
Actually perform the installation.
getAutoExtensionLegacyHooks()
Auto-detect extensions with an old style .php registration file, load the extensions,...
envCheck64Bit()
Checks if we're running on 64 bit or not.
generateKeys()
Generate $wgSecretKey.
populateSiteStats(DatabaseInstaller $installer)
Install step which adds a row to the site_stats table with appropriate initial values.
envCheckCache()
Environment check for compiled object cache types.
__construct()
Constructor, always call this from child classes.
int $minMemorySize
Minimum memory size in MiB.
findExtensionsByType( $type='extension', $directory='extensions')
Find extensions or skins, and return an array containing the value for 'Name' for each found extensio...
doGenerateKeys( $keys)
Generate a secret value for variables using a secure generator.
resetMediaWikiServices(Config $installerConfig=null, $serviceOverrides=[], bool $disableStorage=false)
Reset the global service container and associated global state to accommodate different stages of the...
showStatusMessage(Status $status)
Show a message to the installing user by using a Status object.
envCheckPath()
Environment check to inform user which paths we've assumed.
array $envPreps
A list of environment preparation methods called by doEnvironmentPreps().
static overrideConfig(SettingsBuilder $settings)
Override the necessary bits of the config to run an installation.
setPassword( $name, $value)
Set a variable which stores a password, except if the new value is a fake password in which case leav...
envPrepPath()
Environment prep for setting $IP and $wgScriptPath.
static getDBTypes()
Get a list of known DB types.
createSysop()
Create the first user account, grant it sysop, bureaucrat and interface-admin rights.
envCheckPCRE()
Check for known PCRE-related compatibility issues.
getVar( $name, $default=null)
Get an MW configuration variable, or internal installer configuration variable.
static getDBInstallerClass( $type)
Get the DatabaseInstaller class name for this type.
array $extraInstallSteps
Extra steps for installation, for things like DatabaseInstallers to modify.
HookContainer null $autoExtensionHookContainer
static array $dbTypes
Known database types.
getAutoExtensionHookContainer()
Get the hook container previously populated by includeExtensions().
envCheckDiff3()
Search for GNU diff3.
envGetDefaultServer()
Helper function to be called from envPrepServer()
getInstallSteps(DatabaseInstaller $installer)
Get an array of install steps.
array $dbInstallers
Cached DB installer instances, access using getDBInstaller().
array $envChecks
A list of environment check methods called by doEnvironmentChecks().
envCheckGraphics()
Environment check for ImageMagick and GD.
showMessage( $msg,... $params)
UI interface for displaying a short message The parameters are like parameters to wfMessage().
showError( $msg,... $params)
Same as showMessage(), but for displaying errors.
envCheckUploadsDirectory()
Environment check for the permissions of the uploads directory.
setParserLanguage( $lang)
ParserOptions are constructed before we determined the language, so fix it.
findExtensions( $directory='extensions')
Find extensions or skins in a subdirectory of $IP.
A class containing constants representing the names of configuration variables.
This class contains schema declarations for all configuration variables known to MediaWiki core.
Set options of the Parser.
Class for the core installer web interface.
Content object for wiki text pages.
$wgObjectCaches
Config variable stub for the ObjectCaches setting, for use by phpdoc and IDEs.
$wgStyleDirectory
Config variable stub for the StyleDirectory setting, for use by phpdoc and IDEs.
$wgHooks
Config variable stub for the Hooks setting, for use by phpdoc and IDEs.
$wgLocaltimezone
Config variable stub for the Localtimezone setting, for use by phpdoc and IDEs.
$wgExtensionDirectory
Config variable stub for the ExtensionDirectory setting, for use by phpdoc and IDEs.
$wgExternalLinkTarget
Config variable stub for the ExternalLinkTarget setting, for use by phpdoc and IDEs.
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Item class for a filearchive table row.
if(!is_readable( $file)) $ext