PageRenderTime 43ms CodeModel.GetById 28ms RepoModel.GetById 1ms app.codeStats 0ms

/includes/installer/Installer.php

https://bitbucket.org/kgrashad/thawrapedia
PHP | 1511 lines | 871 code | 187 blank | 453 comment | 112 complexity | 483e85cc1dab343b1e9a34c25376b139 MD5 | raw file
Possible License(s): GPL-2.0, Apache-2.0, LGPL-3.0
  1. <?php
  2. /**
  3. * Base code for MediaWiki installer.
  4. *
  5. * @file
  6. * @ingroup Deployment
  7. */
  8. /**
  9. * This documentation group collects source code files with deployment functionality.
  10. *
  11. * @defgroup Deployment Deployment
  12. */
  13. /**
  14. * Base installer class.
  15. *
  16. * This class provides the base for installation and update functionality
  17. * for both MediaWiki core and extensions.
  18. *
  19. * @ingroup Deployment
  20. * @since 1.17
  21. */
  22. abstract class Installer {
  23. // This is the absolute minimum PHP version we can support
  24. const MINIMUM_PHP_VERSION = '5.2.3';
  25. /**
  26. * @var array
  27. */
  28. protected $settings;
  29. /**
  30. * Cached DB installer instances, access using getDBInstaller().
  31. *
  32. * @var array
  33. */
  34. protected $dbInstallers = array();
  35. /**
  36. * Minimum memory size in MB.
  37. *
  38. * @var integer
  39. */
  40. protected $minMemorySize = 50;
  41. /**
  42. * Cached Title, used by parse().
  43. *
  44. * @var Title
  45. */
  46. protected $parserTitle;
  47. /**
  48. * Cached ParserOptions, used by parse().
  49. *
  50. * @var ParserOptions
  51. */
  52. protected $parserOptions;
  53. /**
  54. * Known database types. These correspond to the class names <type>Installer,
  55. * and are also MediaWiki database types valid for $wgDBtype.
  56. *
  57. * To add a new type, create a <type>Installer class and a Database<type>
  58. * class, and add a config-type-<type> message to MessagesEn.php.
  59. *
  60. * @var array
  61. */
  62. protected static $dbTypes = array(
  63. 'mysql',
  64. 'postgres',
  65. 'oracle',
  66. 'sqlite',
  67. );
  68. /**
  69. * A list of environment check methods called by doEnvironmentChecks().
  70. * These may output warnings using showMessage(), and/or abort the
  71. * installation process by returning false.
  72. *
  73. * @var array
  74. */
  75. protected $envChecks = array(
  76. 'envCheckDB',
  77. 'envCheckRegisterGlobals',
  78. 'envCheckBrokenXML',
  79. 'envCheckPHP531',
  80. 'envCheckMagicQuotes',
  81. 'envCheckMagicSybase',
  82. 'envCheckMbstring',
  83. 'envCheckZE1',
  84. 'envCheckSafeMode',
  85. 'envCheckXML',
  86. 'envCheckPCRE',
  87. 'envCheckMemory',
  88. 'envCheckCache',
  89. 'envCheckDiff3',
  90. 'envCheckGraphics',
  91. 'envCheckPath',
  92. 'envCheckExtension',
  93. 'envCheckShellLocale',
  94. 'envCheckUploadsDirectory',
  95. 'envCheckLibicu',
  96. 'envCheckSuhosinMaxValueLength',
  97. );
  98. /**
  99. * MediaWiki configuration globals that will eventually be passed through
  100. * to LocalSettings.php. The names only are given here, the defaults
  101. * typically come from DefaultSettings.php.
  102. *
  103. * @var array
  104. */
  105. protected $defaultVarNames = array(
  106. 'wgSitename',
  107. 'wgPasswordSender',
  108. 'wgLanguageCode',
  109. 'wgRightsIcon',
  110. 'wgRightsText',
  111. 'wgRightsUrl',
  112. 'wgMainCacheType',
  113. 'wgEnableEmail',
  114. 'wgEnableUserEmail',
  115. 'wgEnotifUserTalk',
  116. 'wgEnotifWatchlist',
  117. 'wgEmailAuthentication',
  118. 'wgDBtype',
  119. 'wgDiff3',
  120. 'wgImageMagickConvertCommand',
  121. 'IP',
  122. 'wgScriptPath',
  123. 'wgScriptExtension',
  124. 'wgMetaNamespace',
  125. 'wgDeletedDirectory',
  126. 'wgEnableUploads',
  127. 'wgLogo',
  128. 'wgShellLocale',
  129. 'wgSecretKey',
  130. 'wgUseInstantCommons',
  131. 'wgUpgradeKey',
  132. 'wgDefaultSkin',
  133. 'wgResourceLoaderMaxQueryLength',
  134. );
  135. /**
  136. * Variables that are stored alongside globals, and are used for any
  137. * configuration of the installation process aside from the MediaWiki
  138. * configuration. Map of names to defaults.
  139. *
  140. * @var array
  141. */
  142. protected $internalDefaults = array(
  143. '_UserLang' => 'en',
  144. '_Environment' => false,
  145. '_CompiledDBs' => array(),
  146. '_SafeMode' => false,
  147. '_RaiseMemory' => false,
  148. '_UpgradeDone' => false,
  149. '_InstallDone' => false,
  150. '_Caches' => array(),
  151. '_InstallPassword' => '',
  152. '_SameAccount' => true,
  153. '_CreateDBAccount' => false,
  154. '_NamespaceType' => 'site-name',
  155. '_AdminName' => '', // will be set later, when the user selects language
  156. '_AdminPassword' => '',
  157. '_AdminPassword2' => '',
  158. '_AdminEmail' => '',
  159. '_Subscribe' => false,
  160. '_SkipOptional' => 'continue',
  161. '_RightsProfile' => 'wiki',
  162. '_LicenseCode' => 'none',
  163. '_CCDone' => false,
  164. '_Extensions' => array(),
  165. '_MemCachedServers' => '',
  166. '_UpgradeKeySupplied' => false,
  167. '_ExistingDBSettings' => false,
  168. );
  169. /**
  170. * The actual list of installation steps. This will be initialized by getInstallSteps()
  171. *
  172. * @var array
  173. */
  174. private $installSteps = array();
  175. /**
  176. * Extra steps for installation, for things like DatabaseInstallers to modify
  177. *
  178. * @var array
  179. */
  180. protected $extraInstallSteps = array();
  181. /**
  182. * Known object cache types and the functions used to test for their existence.
  183. *
  184. * @var array
  185. */
  186. protected $objectCaches = array(
  187. 'xcache' => 'xcache_get',
  188. 'apc' => 'apc_fetch',
  189. 'eaccel' => 'eaccelerator_get',
  190. 'wincache' => 'wincache_ucache_get'
  191. );
  192. /**
  193. * User rights profiles.
  194. *
  195. * @var array
  196. */
  197. public $rightsProfiles = array(
  198. 'wiki' => array(),
  199. 'no-anon' => array(
  200. '*' => array( 'edit' => false )
  201. ),
  202. 'fishbowl' => array(
  203. '*' => array(
  204. 'createaccount' => false,
  205. 'edit' => false,
  206. ),
  207. ),
  208. 'private' => array(
  209. '*' => array(
  210. 'createaccount' => false,
  211. 'edit' => false,
  212. 'read' => false,
  213. ),
  214. ),
  215. );
  216. /**
  217. * License types.
  218. *
  219. * @var array
  220. */
  221. public $licenses = array(
  222. 'cc-by-sa' => array(
  223. 'url' => 'http://creativecommons.org/licenses/by-sa/3.0/',
  224. 'icon' => '{$wgStylePath}/common/images/cc-by-sa.png',
  225. ),
  226. 'cc-by-nc-sa' => array(
  227. 'url' => 'http://creativecommons.org/licenses/by-nc-sa/3.0/',
  228. 'icon' => '{$wgStylePath}/common/images/cc-by-nc-sa.png',
  229. ),
  230. 'cc-0' => array(
  231. 'url' => 'https://creativecommons.org/publicdomain/zero/1.0/',
  232. 'icon' => '{$wgStylePath}/common/images/cc-0.png',
  233. ),
  234. 'pd' => array(
  235. 'url' => 'http://creativecommons.org/licenses/publicdomain/',
  236. 'icon' => '{$wgStylePath}/common/images/public-domain.png',
  237. ),
  238. 'gfdl-old' => array(
  239. 'url' => 'http://www.gnu.org/licenses/old-licenses/fdl-1.2.html',
  240. 'icon' => '{$wgStylePath}/common/images/gnu-fdl.png',
  241. ),
  242. 'gfdl-current' => array(
  243. 'url' => 'http://www.gnu.org/copyleft/fdl.html',
  244. 'icon' => '{$wgStylePath}/common/images/gnu-fdl.png',
  245. ),
  246. 'none' => array(
  247. 'url' => '',
  248. 'icon' => '',
  249. 'text' => ''
  250. ),
  251. 'cc-choose' => array(
  252. // Details will be filled in by the selector.
  253. 'url' => '',
  254. 'icon' => '',
  255. 'text' => '',
  256. ),
  257. );
  258. /**
  259. * URL to mediawiki-announce subscription
  260. */
  261. protected $mediaWikiAnnounceUrl = 'https://lists.wikimedia.org/mailman/subscribe/mediawiki-announce';
  262. /**
  263. * Supported language codes for Mailman
  264. */
  265. protected $mediaWikiAnnounceLanguages = array(
  266. 'ca', 'cs', 'da', 'de', 'en', 'es', 'et', 'eu', 'fi', 'fr', 'hr', 'hu',
  267. 'it', 'ja', 'ko', 'lt', 'nl', 'no', 'pl', 'pt', 'pt-br', 'ro', 'ru',
  268. 'sl', 'sr', 'sv', 'tr', 'uk'
  269. );
  270. /**
  271. * UI interface for displaying a short message
  272. * The parameters are like parameters to wfMsg().
  273. * The messages will be in wikitext format, which will be converted to an
  274. * output format such as HTML or text before being sent to the user.
  275. */
  276. public abstract function showMessage( $msg /*, ... */ );
  277. /**
  278. * Same as showMessage(), but for displaying errors
  279. */
  280. public abstract function showError( $msg /*, ... */ );
  281. /**
  282. * Show a message to the installing user by using a Status object
  283. * @param $status Status
  284. */
  285. public abstract function showStatusMessage( Status $status );
  286. /**
  287. * Constructor, always call this from child classes.
  288. */
  289. public function __construct() {
  290. global $wgExtensionMessagesFiles, $wgUser, $wgHooks;
  291. // Disable the i18n cache and LoadBalancer
  292. Language::getLocalisationCache()->disableBackend();
  293. LBFactory::disableBackend();
  294. // Load the installer's i18n file.
  295. $wgExtensionMessagesFiles['MediawikiInstaller'] =
  296. dirname( __FILE__ ) . '/Installer.i18n.php';
  297. // Having a user with id = 0 safeguards us from DB access via User::loadOptions().
  298. $wgUser = User::newFromId( 0 );
  299. $this->settings = $this->internalDefaults;
  300. foreach ( $this->defaultVarNames as $var ) {
  301. $this->settings[$var] = $GLOBALS[$var];
  302. }
  303. foreach ( self::getDBTypes() as $type ) {
  304. $installer = $this->getDBInstaller( $type );
  305. if ( !$installer->isCompiled() ) {
  306. continue;
  307. }
  308. $defaults = $installer->getGlobalDefaults();
  309. foreach ( $installer->getGlobalNames() as $var ) {
  310. if ( isset( $defaults[$var] ) ) {
  311. $this->settings[$var] = $defaults[$var];
  312. } else {
  313. $this->settings[$var] = $GLOBALS[$var];
  314. }
  315. }
  316. }
  317. $this->parserTitle = Title::newFromText( 'Installer' );
  318. $this->parserOptions = new ParserOptions; // language will be wrong :(
  319. $this->parserOptions->setEditSection( false );
  320. }
  321. /**
  322. * Get a list of known DB types.
  323. */
  324. public static function getDBTypes() {
  325. return self::$dbTypes;
  326. }
  327. /**
  328. * Do initial checks of the PHP environment. Set variables according to
  329. * the observed environment.
  330. *
  331. * It's possible that this may be called under the CLI SAPI, not the SAPI
  332. * that the wiki will primarily run under. In that case, the subclass should
  333. * initialise variables such as wgScriptPath, before calling this function.
  334. *
  335. * Under the web subclass, it can already be assumed that PHP 5+ is in use
  336. * and that sessions are working.
  337. *
  338. * @return Status
  339. */
  340. public function doEnvironmentChecks() {
  341. $phpVersion = phpversion();
  342. if( version_compare( $phpVersion, self::MINIMUM_PHP_VERSION, '>=' ) ) {
  343. $this->showMessage( 'config-env-php', $phpVersion );
  344. $good = true;
  345. } else {
  346. $this->showMessage( 'config-env-php-toolow', $phpVersion, self::MINIMUM_PHP_VERSION );
  347. $good = false;
  348. }
  349. if( $good ) {
  350. foreach ( $this->envChecks as $check ) {
  351. $status = $this->$check();
  352. if ( $status === false ) {
  353. $good = false;
  354. }
  355. }
  356. }
  357. $this->setVar( '_Environment', $good );
  358. return $good ? Status::newGood() : Status::newFatal( 'config-env-bad' );
  359. }
  360. /**
  361. * Set a MW configuration variable, or internal installer configuration variable.
  362. *
  363. * @param $name String
  364. * @param $value Mixed
  365. */
  366. public function setVar( $name, $value ) {
  367. $this->settings[$name] = $value;
  368. }
  369. /**
  370. * Get an MW configuration variable, or internal installer configuration variable.
  371. * The defaults come from $GLOBALS (ultimately DefaultSettings.php).
  372. * Installer variables are typically prefixed by an underscore.
  373. *
  374. * @param $name String
  375. * @param $default Mixed
  376. *
  377. * @return mixed
  378. */
  379. public function getVar( $name, $default = null ) {
  380. if ( !isset( $this->settings[$name] ) ) {
  381. return $default;
  382. } else {
  383. return $this->settings[$name];
  384. }
  385. }
  386. /**
  387. * Get an instance of DatabaseInstaller for the specified DB type.
  388. *
  389. * @param $type Mixed: DB installer for which is needed, false to use default.
  390. *
  391. * @return DatabaseInstaller
  392. */
  393. public function getDBInstaller( $type = false ) {
  394. if ( !$type ) {
  395. $type = $this->getVar( 'wgDBtype' );
  396. }
  397. $type = strtolower( $type );
  398. if ( !isset( $this->dbInstallers[$type] ) ) {
  399. $class = ucfirst( $type ). 'Installer';
  400. $this->dbInstallers[$type] = new $class( $this );
  401. }
  402. return $this->dbInstallers[$type];
  403. }
  404. /**
  405. * Determine if LocalSettings.php exists. If it does, return its variables,
  406. * merged with those from AdminSettings.php, as an array.
  407. *
  408. * @return Array
  409. */
  410. public static function getExistingLocalSettings() {
  411. global $IP;
  412. wfSuppressWarnings();
  413. $_lsExists = file_exists( "$IP/LocalSettings.php" );
  414. wfRestoreWarnings();
  415. if( !$_lsExists ) {
  416. return false;
  417. }
  418. unset($_lsExists);
  419. require( "$IP/includes/DefaultSettings.php" );
  420. require( "$IP/LocalSettings.php" );
  421. if ( file_exists( "$IP/AdminSettings.php" ) ) {
  422. require( "$IP/AdminSettings.php" );
  423. }
  424. return get_defined_vars();
  425. }
  426. /**
  427. * Get a fake password for sending back to the user in HTML.
  428. * This is a security mechanism to avoid compromise of the password in the
  429. * event of session ID compromise.
  430. *
  431. * @param $realPassword String
  432. *
  433. * @return string
  434. */
  435. public function getFakePassword( $realPassword ) {
  436. return str_repeat( '*', strlen( $realPassword ) );
  437. }
  438. /**
  439. * Set a variable which stores a password, except if the new value is a
  440. * fake password in which case leave it as it is.
  441. *
  442. * @param $name String
  443. * @param $value Mixed
  444. */
  445. public function setPassword( $name, $value ) {
  446. if ( !preg_match( '/^\*+$/', $value ) ) {
  447. $this->setVar( $name, $value );
  448. }
  449. }
  450. /**
  451. * On POSIX systems return the primary group of the webserver we're running under.
  452. * On other systems just returns null.
  453. *
  454. * This is used to advice the user that he should chgrp his mw-config/data/images directory as the
  455. * webserver user before he can install.
  456. *
  457. * Public because SqliteInstaller needs it, and doesn't subclass Installer.
  458. *
  459. * @return mixed
  460. */
  461. public static function maybeGetWebserverPrimaryGroup() {
  462. if ( !function_exists( 'posix_getegid' ) || !function_exists( 'posix_getpwuid' ) ) {
  463. # I don't know this, this isn't UNIX.
  464. return null;
  465. }
  466. # posix_getegid() *not* getmygid() because we want the group of the webserver,
  467. # not whoever owns the current script.
  468. $gid = posix_getegid();
  469. $getpwuid = posix_getpwuid( $gid );
  470. $group = $getpwuid['name'];
  471. return $group;
  472. }
  473. /**
  474. * Convert wikitext $text to HTML.
  475. *
  476. * This is potentially error prone since many parser features require a complete
  477. * installed MW database. The solution is to just not use those features when you
  478. * write your messages. This appears to work well enough. Basic formatting and
  479. * external links work just fine.
  480. *
  481. * But in case a translator decides to throw in a #ifexist or internal link or
  482. * whatever, this function is guarded to catch the attempted DB access and to present
  483. * some fallback text.
  484. *
  485. * @param $text String
  486. * @param $lineStart Boolean
  487. * @return String
  488. */
  489. public function parse( $text, $lineStart = false ) {
  490. global $wgParser;
  491. try {
  492. $out = $wgParser->parse( $text, $this->parserTitle, $this->parserOptions, $lineStart );
  493. $html = $out->getText();
  494. } catch ( DBAccessError $e ) {
  495. $html = '<!--DB access attempted during parse--> ' . htmlspecialchars( $text );
  496. if ( !empty( $this->debug ) ) {
  497. $html .= "<!--\n" . $e->getTraceAsString() . "\n-->";
  498. }
  499. }
  500. return $html;
  501. }
  502. public function getParserOptions() {
  503. return $this->parserOptions;
  504. }
  505. public function disableLinkPopups() {
  506. $this->parserOptions->setExternalLinkTarget( false );
  507. }
  508. public function restoreLinkPopups() {
  509. global $wgExternalLinkTarget;
  510. $this->parserOptions->setExternalLinkTarget( $wgExternalLinkTarget );
  511. }
  512. /**
  513. * Install step which adds a row to the site_stats table with appropriate
  514. * initial values.
  515. */
  516. public function populateSiteStats( DatabaseInstaller $installer ) {
  517. $status = $installer->getConnection();
  518. if ( !$status->isOK() ) {
  519. return $status;
  520. }
  521. $status->value->insert( 'site_stats', array(
  522. 'ss_row_id' => 1,
  523. 'ss_total_views' => 0,
  524. 'ss_total_edits' => 0,
  525. 'ss_good_articles' => 0,
  526. 'ss_total_pages' => 0,
  527. 'ss_users' => 0,
  528. 'ss_admins' => 0,
  529. 'ss_images' => 0 ),
  530. __METHOD__, 'IGNORE' );
  531. return Status::newGood();
  532. }
  533. /**
  534. * Exports all wg* variables stored by the installer into global scope.
  535. */
  536. public function exportVars() {
  537. foreach ( $this->settings as $name => $value ) {
  538. if ( substr( $name, 0, 2 ) == 'wg' ) {
  539. $GLOBALS[$name] = $value;
  540. }
  541. }
  542. }
  543. /**
  544. * Environment check for DB types.
  545. */
  546. protected function envCheckDB() {
  547. global $wgLang;
  548. $compiledDBs = array();
  549. $allNames = array();
  550. foreach ( self::getDBTypes() as $name ) {
  551. $db = $this->getDBInstaller( $name );
  552. $readableName = wfMsg( 'config-type-' . $name );
  553. if ( $db->isCompiled() ) {
  554. $compiledDBs[] = $name;
  555. }
  556. $allNames[] = $readableName;
  557. }
  558. $this->setVar( '_CompiledDBs', $compiledDBs );
  559. if ( !$compiledDBs ) {
  560. $this->showError( 'config-no-db', $wgLang->commaList( $allNames ) );
  561. // FIXME: this only works for the web installer!
  562. return false;
  563. }
  564. // Check for FTS3 full-text search module
  565. $sqlite = $this->getDBInstaller( 'sqlite' );
  566. if ( $sqlite->isCompiled() ) {
  567. if( DatabaseSqlite::getFulltextSearchModule() != 'FTS3' ) {
  568. $this->showMessage( 'config-no-fts3' );
  569. }
  570. }
  571. }
  572. /**
  573. * Environment check for register_globals.
  574. */
  575. protected function envCheckRegisterGlobals() {
  576. if( wfIniGetBool( "magic_quotes_runtime" ) ) {
  577. $this->showMessage( 'config-register-globals' );
  578. }
  579. }
  580. /**
  581. * Some versions of libxml+PHP break < and > encoding horribly
  582. */
  583. protected function envCheckBrokenXML() {
  584. $test = new PhpXmlBugTester();
  585. if ( !$test->ok ) {
  586. $this->showError( 'config-brokenlibxml' );
  587. return false;
  588. }
  589. }
  590. /**
  591. * Test PHP (probably 5.3.1, but it could regress again) to make sure that
  592. * reference parameters to __call() are not converted to null
  593. */
  594. protected function envCheckPHP531() {
  595. $test = new PhpRefCallBugTester;
  596. $test->execute();
  597. if ( !$test->ok ) {
  598. $this->showError( 'config-using531', phpversion() );
  599. return false;
  600. }
  601. }
  602. /**
  603. * Environment check for magic_quotes_runtime.
  604. */
  605. protected function envCheckMagicQuotes() {
  606. if( wfIniGetBool( "magic_quotes_runtime" ) ) {
  607. $this->showError( 'config-magic-quotes-runtime' );
  608. return false;
  609. }
  610. }
  611. /**
  612. * Environment check for magic_quotes_sybase.
  613. */
  614. protected function envCheckMagicSybase() {
  615. if ( wfIniGetBool( 'magic_quotes_sybase' ) ) {
  616. $this->showError( 'config-magic-quotes-sybase' );
  617. return false;
  618. }
  619. }
  620. /**
  621. * Environment check for mbstring.func_overload.
  622. */
  623. protected function envCheckMbstring() {
  624. if ( wfIniGetBool( 'mbstring.func_overload' ) ) {
  625. $this->showError( 'config-mbstring' );
  626. return false;
  627. }
  628. }
  629. /**
  630. * Environment check for zend.ze1_compatibility_mode.
  631. */
  632. protected function envCheckZE1() {
  633. if ( wfIniGetBool( 'zend.ze1_compatibility_mode' ) ) {
  634. $this->showError( 'config-ze1' );
  635. return false;
  636. }
  637. }
  638. /**
  639. * Environment check for safe_mode.
  640. */
  641. protected function envCheckSafeMode() {
  642. if ( wfIniGetBool( 'safe_mode' ) ) {
  643. $this->setVar( '_SafeMode', true );
  644. $this->showMessage( 'config-safe-mode' );
  645. }
  646. }
  647. /**
  648. * Environment check for the XML module.
  649. */
  650. protected function envCheckXML() {
  651. if ( !function_exists( "utf8_encode" ) ) {
  652. $this->showError( 'config-xml-bad' );
  653. return false;
  654. }
  655. }
  656. /**
  657. * Environment check for the PCRE module.
  658. */
  659. protected function envCheckPCRE() {
  660. if ( !function_exists( 'preg_match' ) ) {
  661. $this->showError( 'config-pcre' );
  662. return false;
  663. }
  664. wfSuppressWarnings();
  665. $regexd = preg_replace( '/[\x{0430}-\x{04FF}]/iu', '', '-АБВГД-' );
  666. wfRestoreWarnings();
  667. if ( $regexd != '--' ) {
  668. $this->showError( 'config-pcre-no-utf8' );
  669. return false;
  670. }
  671. }
  672. /**
  673. * Environment check for available memory.
  674. */
  675. protected function envCheckMemory() {
  676. $limit = ini_get( 'memory_limit' );
  677. if ( !$limit || $limit == -1 ) {
  678. return true;
  679. }
  680. $n = wfShorthandToInteger( $limit );
  681. if( $n < $this->minMemorySize * 1024 * 1024 ) {
  682. $newLimit = "{$this->minMemorySize}M";
  683. if( ini_set( "memory_limit", $newLimit ) === false ) {
  684. $this->showMessage( 'config-memory-bad', $limit );
  685. } else {
  686. $this->showMessage( 'config-memory-raised', $limit, $newLimit );
  687. $this->setVar( '_RaiseMemory', true );
  688. }
  689. } else {
  690. return true;
  691. }
  692. }
  693. /**
  694. * Environment check for compiled object cache types.
  695. */
  696. protected function envCheckCache() {
  697. $caches = array();
  698. foreach ( $this->objectCaches as $name => $function ) {
  699. if ( function_exists( $function ) ) {
  700. $caches[$name] = true;
  701. }
  702. }
  703. if ( !$caches ) {
  704. $this->showMessage( 'config-no-cache' );
  705. }
  706. $this->setVar( '_Caches', $caches );
  707. }
  708. /**
  709. * Search for GNU diff3.
  710. */
  711. protected function envCheckDiff3() {
  712. $names = array( "gdiff3", "diff3", "diff3.exe" );
  713. $versionInfo = array( '$1 --version 2>&1', 'GNU diffutils' );
  714. $diff3 = self::locateExecutableInDefaultPaths( $names, $versionInfo );
  715. if ( $diff3 ) {
  716. $this->setVar( 'wgDiff3', $diff3 );
  717. } else {
  718. $this->setVar( 'wgDiff3', false );
  719. $this->showMessage( 'config-diff3-bad' );
  720. }
  721. }
  722. /**
  723. * Environment check for ImageMagick and GD.
  724. */
  725. protected function envCheckGraphics() {
  726. $names = array( wfIsWindows() ? 'convert.exe' : 'convert' );
  727. $convert = self::locateExecutableInDefaultPaths( $names, array( '$1 -version', 'ImageMagick' ) );
  728. $this->setVar( 'wgImageMagickConvertCommand', '' );
  729. if ( $convert ) {
  730. $this->setVar( 'wgImageMagickConvertCommand', $convert );
  731. $this->showMessage( 'config-imagemagick', $convert );
  732. return true;
  733. } elseif ( function_exists( 'imagejpeg' ) ) {
  734. $this->showMessage( 'config-gd' );
  735. return true;
  736. } else {
  737. $this->showMessage( 'config-no-scaling' );
  738. }
  739. }
  740. /**
  741. * Environment check for setting $IP and $wgScriptPath.
  742. */
  743. protected function envCheckPath() {
  744. global $IP;
  745. $IP = dirname( dirname( dirname( __FILE__ ) ) );
  746. $this->setVar( 'IP', $IP );
  747. // PHP_SELF isn't available sometimes, such as when PHP is CGI but
  748. // cgi.fix_pathinfo is disabled. In that case, fall back to SCRIPT_NAME
  749. // to get the path to the current script... hopefully it's reliable. SIGH
  750. if ( !empty( $_SERVER['PHP_SELF'] ) ) {
  751. $path = $_SERVER['PHP_SELF'];
  752. } elseif ( !empty( $_SERVER['SCRIPT_NAME'] ) ) {
  753. $path = $_SERVER['SCRIPT_NAME'];
  754. } elseif ( $this->getVar( 'wgScriptPath' ) ) {
  755. // Some kind soul has set it for us already (e.g. debconf)
  756. return true;
  757. } else {
  758. $this->showError( 'config-no-uri' );
  759. return false;
  760. }
  761. $uri = preg_replace( '{^(.*)/(mw-)?config.*$}', '$1', $path );
  762. $this->setVar( 'wgScriptPath', $uri );
  763. }
  764. /**
  765. * Environment check for setting the preferred PHP file extension.
  766. */
  767. protected function envCheckExtension() {
  768. // FIXME: detect this properly
  769. if ( defined( 'MW_INSTALL_PHP5_EXT' ) ) {
  770. $ext = 'php5';
  771. } else {
  772. $ext = 'php';
  773. }
  774. $this->setVar( 'wgScriptExtension', ".$ext" );
  775. }
  776. /**
  777. * TODO: document
  778. */
  779. protected function envCheckShellLocale() {
  780. $os = php_uname( 's' );
  781. $supported = array( 'Linux', 'SunOS', 'HP-UX', 'Darwin' ); # Tested these
  782. if ( !in_array( $os, $supported ) ) {
  783. return true;
  784. }
  785. # Get a list of available locales.
  786. $ret = false;
  787. $lines = wfShellExec( '/usr/bin/locale -a', $ret );
  788. if ( $ret ) {
  789. return true;
  790. }
  791. $lines = wfArrayMap( 'trim', explode( "\n", $lines ) );
  792. $candidatesByLocale = array();
  793. $candidatesByLang = array();
  794. foreach ( $lines as $line ) {
  795. if ( $line === '' ) {
  796. continue;
  797. }
  798. if ( !preg_match( '/^([a-zA-Z]+)(_[a-zA-Z]+|)\.(utf8|UTF-8)(@[a-zA-Z_]*|)$/i', $line, $m ) ) {
  799. continue;
  800. }
  801. list( $all, $lang, $territory, $charset, $modifier ) = $m;
  802. $candidatesByLocale[$m[0]] = $m;
  803. $candidatesByLang[$lang][] = $m;
  804. }
  805. # Try the current value of LANG.
  806. if ( isset( $candidatesByLocale[ getenv( 'LANG' ) ] ) ) {
  807. $this->setVar( 'wgShellLocale', getenv( 'LANG' ) );
  808. return true;
  809. }
  810. # Try the most common ones.
  811. $commonLocales = array( 'en_US.UTF-8', 'en_US.utf8', 'de_DE.UTF-8', 'de_DE.utf8' );
  812. foreach ( $commonLocales as $commonLocale ) {
  813. if ( isset( $candidatesByLocale[$commonLocale] ) ) {
  814. $this->setVar( 'wgShellLocale', $commonLocale );
  815. return true;
  816. }
  817. }
  818. # Is there an available locale in the Wiki's language?
  819. $wikiLang = $this->getVar( 'wgLanguageCode' );
  820. if ( isset( $candidatesByLang[$wikiLang] ) ) {
  821. $m = reset( $candidatesByLang[$wikiLang] );
  822. $this->setVar( 'wgShellLocale', $m[0] );
  823. return true;
  824. }
  825. # Are there any at all?
  826. if ( count( $candidatesByLocale ) ) {
  827. $m = reset( $candidatesByLocale );
  828. $this->setVar( 'wgShellLocale', $m[0] );
  829. return true;
  830. }
  831. # Give up.
  832. return true;
  833. }
  834. /**
  835. * TODO: document
  836. */
  837. protected function envCheckUploadsDirectory() {
  838. global $IP, $wgServer;
  839. $dir = $IP . '/images/';
  840. $url = $wgServer . $this->getVar( 'wgScriptPath' ) . '/images/';
  841. $safe = !$this->dirIsExecutable( $dir, $url );
  842. if ( $safe ) {
  843. return true;
  844. } else {
  845. $this->showMessage( 'config-uploads-not-safe', $dir );
  846. }
  847. }
  848. /**
  849. * Checks if suhosin.get.max_value_length is set, and if so, sets
  850. * $wgResourceLoaderMaxQueryLength to that value in the generated
  851. * LocalSettings file
  852. */
  853. protected function envCheckSuhosinMaxValueLength() {
  854. $maxValueLength = ini_get( 'suhosin.get.max_value_length' );
  855. if ( $maxValueLength > 0 ) {
  856. $this->showMessage( 'config-suhosin-max-value-length', $maxValueLength );
  857. } else {
  858. $maxValueLength = -1;
  859. }
  860. $this->setVar( 'wgResourceLoaderMaxQueryLength', $maxValueLength );
  861. }
  862. /**
  863. * Convert a hex string representing a Unicode code point to that code point.
  864. * @param $c String
  865. * @return string
  866. */
  867. protected function unicodeChar( $c ) {
  868. $c = hexdec($c);
  869. if ($c <= 0x7F) {
  870. return chr($c);
  871. } else if ($c <= 0x7FF) {
  872. return chr(0xC0 | $c >> 6) . chr(0x80 | $c & 0x3F);
  873. } else if ($c <= 0xFFFF) {
  874. return chr(0xE0 | $c >> 12) . chr(0x80 | $c >> 6 & 0x3F)
  875. . chr(0x80 | $c & 0x3F);
  876. } else if ($c <= 0x10FFFF) {
  877. return chr(0xF0 | $c >> 18) . chr(0x80 | $c >> 12 & 0x3F)
  878. . chr(0x80 | $c >> 6 & 0x3F)
  879. . chr(0x80 | $c & 0x3F);
  880. } else {
  881. return false;
  882. }
  883. }
  884. /**
  885. * Check the libicu version
  886. */
  887. protected function envCheckLibicu() {
  888. $utf8 = function_exists( 'utf8_normalize' );
  889. $intl = function_exists( 'normalizer_normalize' );
  890. /**
  891. * This needs to be updated something that the latest libicu
  892. * will properly normalize. This normalization was found at
  893. * http://www.unicode.org/versions/Unicode5.2.0/#Character_Additions
  894. * Note that we use the hex representation to create the code
  895. * points in order to avoid any Unicode-destroying during transit.
  896. */
  897. $not_normal_c = $this->unicodeChar("FA6C");
  898. $normal_c = $this->unicodeChar("242EE");
  899. $useNormalizer = 'php';
  900. $needsUpdate = false;
  901. /**
  902. * We're going to prefer the pecl extension here unless
  903. * utf8_normalize is more up to date.
  904. */
  905. if( $utf8 ) {
  906. $useNormalizer = 'utf8';
  907. $utf8 = utf8_normalize( $not_normal_c, UNORM_NFC );
  908. if ( $utf8 !== $normal_c ) $needsUpdate = true;
  909. }
  910. if( $intl ) {
  911. $useNormalizer = 'intl';
  912. $intl = normalizer_normalize( $not_normal_c, Normalizer::FORM_C );
  913. if ( $intl !== $normal_c ) $needsUpdate = true;
  914. }
  915. // Uses messages 'config-unicode-using-php', 'config-unicode-using-utf8', 'config-unicode-using-intl'
  916. if( $useNormalizer === 'php' ) {
  917. $this->showMessage( 'config-unicode-pure-php-warning' );
  918. } else {
  919. $this->showMessage( 'config-unicode-using-' . $useNormalizer );
  920. if( $needsUpdate ) {
  921. $this->showMessage( 'config-unicode-update-warning' );
  922. }
  923. }
  924. }
  925. /**
  926. * Get an array of likely places we can find executables. Check a bunch
  927. * of known Unix-like defaults, as well as the PATH environment variable
  928. * (which should maybe make it work for Windows?)
  929. *
  930. * @return Array
  931. */
  932. protected static function getPossibleBinPaths() {
  933. return array_merge(
  934. array( '/usr/bin', '/usr/local/bin', '/opt/csw/bin',
  935. '/usr/gnu/bin', '/usr/sfw/bin', '/sw/bin', '/opt/local/bin' ),
  936. explode( PATH_SEPARATOR, getenv( 'PATH' ) )
  937. );
  938. }
  939. /**
  940. * Search a path for any of the given executable names. Returns the
  941. * executable name if found. Also checks the version string returned
  942. * by each executable.
  943. *
  944. * Used only by environment checks.
  945. *
  946. * @param $path String: path to search
  947. * @param $names Array of executable names
  948. * @param $versionInfo Boolean false or array with two members:
  949. * 0 => Command to run for version check, with $1 for the full executable name
  950. * 1 => String to compare the output with
  951. *
  952. * If $versionInfo is not false, only executables with a version
  953. * matching $versionInfo[1] will be returned.
  954. */
  955. public static function locateExecutable( $path, $names, $versionInfo = false ) {
  956. if ( !is_array( $names ) ) {
  957. $names = array( $names );
  958. }
  959. foreach ( $names as $name ) {
  960. $command = $path . DIRECTORY_SEPARATOR . $name;
  961. wfSuppressWarnings();
  962. $file_exists = file_exists( $command );
  963. wfRestoreWarnings();
  964. if ( $file_exists ) {
  965. if ( !$versionInfo ) {
  966. return $command;
  967. }
  968. $file = str_replace( '$1', wfEscapeShellArg( $command ), $versionInfo[0] );
  969. if ( strstr( wfShellExec( $file ), $versionInfo[1] ) !== false ) {
  970. return $command;
  971. }
  972. }
  973. }
  974. return false;
  975. }
  976. /**
  977. * Same as locateExecutable(), but checks in getPossibleBinPaths() by default
  978. * @see locateExecutable()
  979. */
  980. public static function locateExecutableInDefaultPaths( $names, $versionInfo = false ) {
  981. foreach( self::getPossibleBinPaths() as $path ) {
  982. $exe = self::locateExecutable( $path, $names, $versionInfo );
  983. if( $exe !== false ) {
  984. return $exe;
  985. }
  986. }
  987. return false;
  988. }
  989. /**
  990. * Checks if scripts located in the given directory can be executed via the given URL.
  991. *
  992. * Used only by environment checks.
  993. */
  994. public function dirIsExecutable( $dir, $url ) {
  995. $scriptTypes = array(
  996. 'php' => array(
  997. "<?php echo 'ex' . 'ec';",
  998. "#!/var/env php5\n<?php echo 'ex' . 'ec';",
  999. ),
  1000. );
  1001. // it would be good to check other popular languages here, but it'll be slow.
  1002. wfSuppressWarnings();
  1003. foreach ( $scriptTypes as $ext => $contents ) {
  1004. foreach ( $contents as $source ) {
  1005. $file = 'exectest.' . $ext;
  1006. if ( !file_put_contents( $dir . $file, $source ) ) {
  1007. break;
  1008. }
  1009. try {
  1010. $text = Http::get( $url . $file, array( 'timeout' => 3 ) );
  1011. }
  1012. catch( MWException $e ) {
  1013. // Http::get throws with allow_url_fopen = false and no curl extension.
  1014. $text = null;
  1015. }
  1016. unlink( $dir . $file );
  1017. if ( $text == 'exec' ) {
  1018. wfRestoreWarnings();
  1019. return $ext;
  1020. }
  1021. }
  1022. }
  1023. wfRestoreWarnings();
  1024. return false;
  1025. }
  1026. /**
  1027. * ParserOptions are constructed before we determined the language, so fix it
  1028. */
  1029. public function setParserLanguage( $lang ) {
  1030. $this->parserOptions->setTargetLanguage( $lang );
  1031. $this->parserOptions->setUserLang( $lang->getCode() );
  1032. }
  1033. /**
  1034. * Overridden by WebInstaller to provide lastPage parameters.
  1035. */
  1036. protected function getDocUrl( $page ) {
  1037. return "{$_SERVER['PHP_SELF']}?page=" . urlencode( $page );
  1038. }
  1039. /**
  1040. * Finds extensions that follow the format /extensions/Name/Name.php,
  1041. * and returns an array containing the value for 'Name' for each found extension.
  1042. *
  1043. * @return array
  1044. */
  1045. public function findExtensions() {
  1046. if( $this->getVar( 'IP' ) === null ) {
  1047. return false;
  1048. }
  1049. $exts = array();
  1050. $dir = $this->getVar( 'IP' ) . '/extensions';
  1051. $dh = opendir( $dir );
  1052. while ( ( $file = readdir( $dh ) ) !== false ) {
  1053. if( file_exists( "$dir/$file/$file.php" ) ) {
  1054. $exts[] = $file;
  1055. }
  1056. }
  1057. return $exts;
  1058. }
  1059. /**
  1060. * Installs the auto-detected extensions.
  1061. *
  1062. * @return Status
  1063. */
  1064. protected function includeExtensions() {
  1065. global $IP;
  1066. $exts = $this->getVar( '_Extensions' );
  1067. $IP = $this->getVar( 'IP' );
  1068. /**
  1069. * We need to include DefaultSettings before including extensions to avoid
  1070. * warnings about unset variables. However, the only thing we really
  1071. * want here is $wgHooks['LoadExtensionSchemaUpdates']. This won't work
  1072. * if the extension has hidden hook registration in $wgExtensionFunctions,
  1073. * but we're not opening that can of worms
  1074. * @see https://bugzilla.wikimedia.org/show_bug.cgi?id=26857
  1075. */
  1076. global $wgAutoloadClasses;
  1077. require( "$IP/includes/DefaultSettings.php" );
  1078. foreach( $exts as $e ) {
  1079. require_once( $IP . '/extensions' . "/$e/$e.php" );
  1080. }
  1081. $hooksWeWant = isset( $wgHooks['LoadExtensionSchemaUpdates'] ) ?
  1082. $wgHooks['LoadExtensionSchemaUpdates'] : array();
  1083. // Unset everyone else's hooks. Lord knows what someone might be doing
  1084. // in ParserFirstCallInit (see bug 27171)
  1085. $GLOBALS['wgHooks'] = array( 'LoadExtensionSchemaUpdates' => $hooksWeWant );
  1086. return Status::newGood();
  1087. }
  1088. /**
  1089. * Get an array of install steps. Should always be in the format of
  1090. * array(
  1091. * 'name' => 'someuniquename',
  1092. * 'callback' => array( $obj, 'method' ),
  1093. * )
  1094. * There must be a config-install-$name message defined per step, which will
  1095. * be shown on install.
  1096. *
  1097. * @param $installer DatabaseInstaller so we can make callbacks
  1098. * @return array
  1099. */
  1100. protected function getInstallSteps( DatabaseInstaller $installer ) {
  1101. $coreInstallSteps = array(
  1102. array( 'name' => 'database', 'callback' => array( $installer, 'setupDatabase' ) ),
  1103. array( 'name' => 'tables', 'callback' => array( $installer, 'createTables' ) ),
  1104. array( 'name' => 'interwiki', 'callback' => array( $installer, 'populateInterwikiTable' ) ),
  1105. array( 'name' => 'stats', 'callback' => array( $this, 'populateSiteStats' ) ),
  1106. array( 'name' => 'keys', 'callback' => array( $this, 'generateKeys' ) ),
  1107. array( 'name' => 'sysop', 'callback' => array( $this, 'createSysop' ) ),
  1108. array( 'name' => 'mainpage', 'callback' => array( $this, 'createMainpage' ) ),
  1109. );
  1110. // Build the array of install steps starting from the core install list,
  1111. // then adding any callbacks that wanted to attach after a given step
  1112. foreach( $coreInstallSteps as $step ) {
  1113. $this->installSteps[] = $step;
  1114. if( isset( $this->extraInstallSteps[ $step['name'] ] ) ) {
  1115. $this->installSteps = array_merge(
  1116. $this->installSteps,
  1117. $this->extraInstallSteps[ $step['name'] ]
  1118. );
  1119. }
  1120. }
  1121. // Prepend any steps that want to be at the beginning
  1122. if( isset( $this->extraInstallSteps['BEGINNING'] ) ) {
  1123. $this->installSteps = array_merge(
  1124. $this->extraInstallSteps['BEGINNING'],
  1125. $this->installSteps
  1126. );
  1127. }
  1128. // Extensions should always go first, chance to tie into hooks and such
  1129. if( count( $this->getVar( '_Extensions' ) ) ) {
  1130. array_unshift( $this->installSteps,
  1131. array( 'name' => 'extensions', 'callback' => array( $this, 'includeExtensions' ) )
  1132. );
  1133. $this->installSteps[] = array(
  1134. 'name' => 'extension-tables',
  1135. 'callback' => array( $installer, 'createExtensionTables' )
  1136. );
  1137. }
  1138. return $this->installSteps;
  1139. }
  1140. /**
  1141. * Actually perform the installation.
  1142. *
  1143. * @param $startCB Array A callback array for the beginning of each step
  1144. * @param $endCB Array A callback array for the end of each step
  1145. *
  1146. * @return Array of Status objects
  1147. */
  1148. public function performInstallation( $startCB, $endCB ) {
  1149. $installResults = array();
  1150. $installer = $this->getDBInstaller();
  1151. $installer->preInstall();
  1152. $steps = $this->getInstallSteps( $installer );
  1153. foreach( $steps as $stepObj ) {
  1154. $name = $stepObj['name'];
  1155. call_user_func_array( $startCB, array( $name ) );
  1156. // Perform the callback step
  1157. $status = call_user_func( $stepObj['callback'], $installer );
  1158. // Output and save the results
  1159. call_user_func( $endCB, $name, $status );
  1160. $installResults[$name] = $status;
  1161. // If we've hit some sort of fatal, we need to bail.
  1162. // Callback already had a chance to do output above.
  1163. if( !$status->isOk() ) {
  1164. break;
  1165. }
  1166. }
  1167. if( $status->isOk() ) {
  1168. $this->setVar( '_InstallDone', true );
  1169. }
  1170. return $installResults;
  1171. }
  1172. /**
  1173. * Generate $wgSecretKey. Will warn if we had to use mt_rand() instead of
  1174. * /dev/urandom
  1175. *
  1176. * @return Status
  1177. */
  1178. public function generateKeys() {
  1179. $keys = array( 'wgSecretKey' => 64 );
  1180. if ( strval( $this->getVar( 'wgUpgradeKey' ) ) === '' ) {
  1181. $keys['wgUpgradeKey'] = 16;
  1182. }
  1183. return $this->doGenerateKeys( $keys );
  1184. }
  1185. /**
  1186. * Generate a secret value for variables using either
  1187. * /dev/urandom or mt_rand(). Produce a warning in the later case.
  1188. *
  1189. * @param $keys Array
  1190. * @return Status
  1191. */
  1192. protected function doGenerateKeys( $keys ) {
  1193. $status = Status::newGood();
  1194. wfSuppressWarnings();
  1195. $file = fopen( "/dev/urandom", "r" );
  1196. wfRestoreWarnings();
  1197. foreach ( $keys as $name => $length ) {
  1198. if ( $file ) {
  1199. $secretKey = bin2hex( fread( $file, $length / 2 ) );
  1200. } else {
  1201. $secretKey = '';
  1202. for ( $i = 0; $i < $length / 8; $i++ ) {
  1203. $secretKey .= dechex( mt_rand( 0, 0x7fffffff ) );
  1204. }
  1205. }
  1206. $this->setVar( $name, $secretKey );
  1207. }
  1208. if ( $file ) {
  1209. fclose( $file );
  1210. } else {
  1211. $names = array_keys ( $keys );
  1212. $names = preg_replace( '/^(.*)$/', '\$$1', $names );
  1213. global $wgLang;
  1214. $status->warning( 'config-insecure-keys', $wgLang->listToText( $names ), count( $names ) );
  1215. }
  1216. return $status;
  1217. }
  1218. /**
  1219. * Create the first user account, grant it sysop and bureaucrat rights
  1220. *
  1221. * @return Status
  1222. */
  1223. protected function createSysop() {
  1224. $name = $this->getVar( '_AdminName' );
  1225. $user = User::newFromName( $name );
  1226. if ( !$user ) {
  1227. // We should've validated this earlier anyway!
  1228. return Status::newFatal( 'config-admin-error-user', $name );
  1229. }
  1230. if ( $user->idForName() == 0 ) {
  1231. $user->addToDatabase();
  1232. try {
  1233. $user->setPassword( $this->getVar( '_AdminPassword' ) );
  1234. } catch( PasswordError $pwe ) {
  1235. return Status::newFatal( 'config-admin-error-password', $name, $pwe->getMessage() );
  1236. }
  1237. $user->addGroup( 'sysop' );
  1238. $user->addGroup( 'bureaucrat' );
  1239. if( $this->getVar( '_AdminEmail' ) ) {
  1240. $user->setEmail( $this->getVar( '_AdminEmail' ) );
  1241. }
  1242. $user->saveSettings();
  1243. // Update user count
  1244. $ssUpdate = new SiteStatsUpdate( 0, 0, 0, 0, 1 );
  1245. $ssUpdate->doUpdate();
  1246. }
  1247. $status = Status::newGood();
  1248. if( $this->getVar( '_Subscribe' ) && $this->getVar( '_AdminEmail' ) ) {
  1249. $this->subscribeToMediaWikiAnnounce( $status );
  1250. }
  1251. return $status;
  1252. }
  1253. private function subscribeToMediaWikiAnnounce( Status $s ) {
  1254. $params = array(
  1255. 'email' => $this->getVar( '_AdminEmail' ),
  1256. 'language' => 'en',
  1257. 'digest' => 0
  1258. );
  1259. // Mailman doesn't support as many languages as we do, so check to make
  1260. // sure their selected language is available
  1261. $myLang = $this->getVar( '_UserLang' );
  1262. if( in_array( $myLang, $this->mediaWikiAnnounceLanguages ) ) {
  1263. $myLang = $myLang == 'pt-br' ? 'pt_BR' : $myLang; // rewrite to Mailman's pt_BR
  1264. $params['language'] = $myLang;
  1265. }
  1266. $res = MWHttpRequest::factory( $this->mediaWikiAnnounceUrl,
  1267. array( 'method' => 'POST', 'postData' => $params ) )->execute();
  1268. if( !$res->isOK() ) {
  1269. $s->warning( 'config-install-subscribe-fail', $res->getMessage() );
  1270. }
  1271. }
  1272. /**
  1273. * Insert Main Page with default content.
  1274. *
  1275. * @return Status
  1276. */
  1277. protected function createMainpage( DatabaseInstaller $installer ) {
  1278. $status = Status::newGood();
  1279. try {
  1280. $article = new Article( Title::newMainPage() );
  1281. $article->doEdit( wfMsgForContent( 'mainpagetext' ) . "\n\n" .
  1282. wfMsgForContent( 'mainpagedocfooter' ),
  1283. '',
  1284. EDIT_NEW,
  1285. false,
  1286. User::newFromName( 'MediaWiki default' ) );
  1287. } catch (MWException $e) {
  1288. //using raw, because $wgShowExceptionDetails can not be set yet
  1289. $status->fatal( 'config-install-mainpage-failed', $e->getMessage() );
  1290. }
  1291. return $status;
  1292. }
  1293. /**
  1294. * Override the necessary bits of the config to run an installation.
  1295. */
  1296. public static function overrideConfig() {
  1297. define( 'MW_NO_SESSION', 1 );
  1298. // Don't access the database
  1299. $GLOBALS['wgUseDatabaseMessages'] = false;
  1300. // Debug-friendly
  1301. $GLOBALS['wgShowExceptionDetails'] = true;
  1302. // Don't break forms
  1303. $GLOBALS['wgExternalLinkTarget'] = '_blank';
  1304. // Extended debugging
  1305. $GLOBALS['wgShowSQLErrors'] = true;
  1306. $GLOBALS['wgShowDBErrorBacktrace'] = true;
  1307. // Allow multiple ob_flush() calls
  1308. $GLOBALS['wgDisableOutputCompression'] = true;
  1309. // Use a sensible cookie prefix (not my_wiki)
  1310. $GLOBALS['wgCookiePrefix'] = 'mw_installer';
  1311. // Some of the environment checks make shell requests, remove limits
  1312. $GLOBALS['wgMaxShellMemory'] = 0;
  1313. }
  1314. /**
  1315. * Add an installation step following the given step.
  1316. *
  1317. * @param $callback Array A valid installation callback array, in this form:
  1318. * array( 'name' => 'some-unique-name', 'callback' => array( $obj, 'function' ) );
  1319. * @param $findStep String the step to find. Omit to put the step at the beginning
  1320. */
  1321. public function addInstallStep( $callback, $findStep = 'BEGINNING' ) {
  1322. $this->extraInstallSteps[$findStep][] = $callback;
  1323. }
  1324. }