r80687 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r80686‎ | r80687 | r80688 >
Date:15:27, 21 January 2011
Author:demon
Status:ok
Tags:
Comment:
Partially revert r69738 (splitting Installer/CoreInstaller). As discussed on CR, this probably wasn't the best route to go with this code.

We can figure that out sometime in 1.18
Modified paths:
  • /trunk/phase3/config/index.php (modified) (history)
  • /trunk/phase3/includes/AutoLoader.php (modified) (history)
  • /trunk/phase3/includes/installer/CliInstaller.php (modified) (history)
  • /trunk/phase3/includes/installer/CoreInstaller.php (deleted) (history)
  • /trunk/phase3/includes/installer/DatabaseInstaller.php (modified) (history)
  • /trunk/phase3/includes/installer/Installer.php (modified) (history)
  • /trunk/phase3/includes/installer/WebInstaller.php (modified) (history)
  • /trunk/phase3/maintenance/install.php (modified) (history)

Diff [purge]

Index: trunk/phase3/maintenance/install.php
@@ -20,7 +20,7 @@
2121 * @see wfWaitForSlaves()
2222 */
2323
24 -define( 'MW_CONFIG_CALLBACK', 'CoreInstaller::overrideConfig' );
 24+define( 'MW_CONFIG_CALLBACK', 'Installer::overrideConfig' );
2525
2626 require_once( dirname( dirname( __FILE__ ) )."/maintenance/Maintenance.php" );
2727
Index: trunk/phase3/includes/installer/CoreInstaller.php
@@ -1,591 +0,0 @@
2 -<?php
3 -/**
4 - * Base core installer.
5 - *
6 - * @file
7 - * @ingroup Deployment
8 - */
9 -
10 -/**
11 - * Base core installer class.
12 - * Handles everything that is independent of user interface.
13 - *
14 - * @ingroup Deployment
15 - * @since 1.17
16 - */
17 -abstract class CoreInstaller extends Installer {
18 -
19 - /**
20 - * MediaWiki configuration globals that will eventually be passed through
21 - * to LocalSettings.php. The names only are given here, the defaults
22 - * typically come from DefaultSettings.php.
23 - *
24 - * @var array
25 - */
26 - protected $defaultVarNames = array(
27 - 'wgSitename',
28 - 'wgPasswordSender',
29 - 'wgLanguageCode',
30 - 'wgRightsIcon',
31 - 'wgRightsText',
32 - 'wgRightsUrl',
33 - 'wgMainCacheType',
34 - 'wgEnableEmail',
35 - 'wgEnableUserEmail',
36 - 'wgEnotifUserTalk',
37 - 'wgEnotifWatchlist',
38 - 'wgEmailAuthentication',
39 - 'wgDBtype',
40 - 'wgDiff3',
41 - 'wgImageMagickConvertCommand',
42 - 'IP',
43 - 'wgScriptPath',
44 - 'wgScriptExtension',
45 - 'wgMetaNamespace',
46 - 'wgDeletedDirectory',
47 - 'wgEnableUploads',
48 - 'wgLogo',
49 - 'wgShellLocale',
50 - 'wgSecretKey',
51 - 'wgUseInstantCommons',
52 - 'wgUpgradeKey',
53 - 'wgDefaultSkin',
54 - );
55 -
56 - /**
57 - * Variables that are stored alongside globals, and are used for any
58 - * configuration of the installation process aside from the MediaWiki
59 - * configuration. Map of names to defaults.
60 - *
61 - * @var array
62 - */
63 - protected $internalDefaults = array(
64 - '_UserLang' => 'en',
65 - '_Environment' => false,
66 - '_CompiledDBs' => array(),
67 - '_SafeMode' => false,
68 - '_RaiseMemory' => false,
69 - '_UpgradeDone' => false,
70 - '_InstallDone' => false,
71 - '_Caches' => array(),
72 - '_InstallUser' => 'root',
73 - '_InstallPassword' => '',
74 - '_SameAccount' => true,
75 - '_CreateDBAccount' => false,
76 - '_NamespaceType' => 'site-name',
77 - '_AdminName' => '', // will be set later, when the user selects language
78 - '_AdminPassword' => '',
79 - '_AdminPassword2' => '',
80 - '_AdminEmail' => '',
81 - '_Subscribe' => false,
82 - '_SkipOptional' => 'continue',
83 - '_RightsProfile' => 'wiki',
84 - '_LicenseCode' => 'none',
85 - '_CCDone' => false,
86 - '_Extensions' => array(),
87 - '_MemCachedServers' => '',
88 - '_UpgradeKeySupplied' => false,
89 - '_ExistingDBSettings' => false,
90 - );
91 -
92 - /**
93 - * The actual list of installation steps. This will be initialized by getInstallSteps()
94 - *
95 - * @var array
96 - */
97 - private $installSteps = array();
98 -
99 - /**
100 - * Extra steps for installation, for things like DatabaseInstallers to modify
101 - *
102 - * @var array
103 - */
104 - protected $extraInstallSteps = array();
105 -
106 - /**
107 - * Known object cache types and the functions used to test for their existence.
108 - *
109 - * @var array
110 - */
111 - protected $objectCaches = array(
112 - 'xcache' => 'xcache_get',
113 - 'apc' => 'apc_fetch',
114 - 'eaccel' => 'eaccelerator_get',
115 - 'wincache' => 'wincache_ucache_get'
116 - );
117 -
118 - /**
119 - * User rights profiles.
120 - *
121 - * @var array
122 - */
123 - public $rightsProfiles = array(
124 - 'wiki' => array(),
125 - 'no-anon' => array(
126 - '*' => array( 'edit' => false )
127 - ),
128 - 'fishbowl' => array(
129 - '*' => array(
130 - 'createaccount' => false,
131 - 'edit' => false,
132 - ),
133 - ),
134 - 'private' => array(
135 - '*' => array(
136 - 'createaccount' => false,
137 - 'edit' => false,
138 - 'read' => false,
139 - ),
140 - ),
141 - );
142 -
143 - /**
144 - * License types.
145 - *
146 - * @var array
147 - */
148 - public $licenses = array(
149 - 'cc-by-sa' => array(
150 - 'url' => 'http://creativecommons.org/licenses/by-sa/3.0/',
151 - 'icon' => '{$wgStylePath}/common/images/cc-by-sa.png',
152 - ),
153 - 'cc-by-nc-sa' => array(
154 - 'url' => 'http://creativecommons.org/licenses/by-nc-sa/3.0/',
155 - 'icon' => '{$wgStylePath}/common/images/cc-by-nc-sa.png',
156 - ),
157 - 'pd' => array(
158 - 'url' => 'http://creativecommons.org/licenses/publicdomain/',
159 - 'icon' => '{$wgStylePath}/common/images/public-domain.png',
160 - ),
161 - 'gfdl-old' => array(
162 - 'url' => 'http://www.gnu.org/licenses/old-licenses/fdl-1.2.html',
163 - 'icon' => '{$wgStylePath}/common/images/gnu-fdl.png',
164 - ),
165 - 'gfdl-current' => array(
166 - 'url' => 'http://www.gnu.org/copyleft/fdl.html',
167 - 'icon' => '{$wgStylePath}/common/images/gnu-fdl.png',
168 - ),
169 - 'none' => array(
170 - 'url' => '',
171 - 'icon' => '',
172 - 'text' => ''
173 - ),
174 - 'cc-choose' => array(
175 - // Details will be filled in by the selector.
176 - 'url' => '',
177 - 'icon' => '',
178 - 'text' => '',
179 - ),
180 - );
181 -
182 - /**
183 - * URL to mediawiki-announce subscription
184 - */
185 - protected $mediaWikiAnnounceUrl = 'https://lists.wikimedia.org/mailman/subscribe/mediawiki-announce';
186 -
187 - /**
188 - * Supported language codes for Mailman
189 - */
190 - protected $mediaWikiAnnounceLanguages = array(
191 - 'ca', 'cs', 'da', 'de', 'en', 'es', 'et', 'eu', 'fi', 'fr', 'hr', 'hu',
192 - 'it', 'ja', 'ko', 'lt', 'nl', 'no', 'pl', 'pt', 'pt-br', 'ro', 'ru',
193 - 'sl', 'sr', 'sv', 'tr', 'uk'
194 - );
195 -
196 - /**
197 - * TODO: document
198 - *
199 - * @param $status Status
200 - */
201 - public abstract function showStatusMessage( Status $status );
202 -
203 - /**
204 - * Constructor, always call this from child classes.
205 - */
206 - public function __construct() {
207 - parent::__construct();
208 -
209 - global $wgExtensionMessagesFiles, $wgUser, $wgHooks;
210 -
211 - // Load the installer's i18n file.
212 - $wgExtensionMessagesFiles['MediawikiInstaller'] =
213 - dirname( __FILE__ ) . '/Installer.i18n.php';
214 -
215 - // Having a user with id = 0 safeguards us from DB access via User::loadOptions().
216 - $wgUser = User::newFromId( 0 );
217 -
218 - // Set our custom <doclink> hook.
219 - $wgHooks['ParserFirstCallInit'][] = array( $this, 'registerDocLink' );
220 -
221 - $this->settings = $this->internalDefaults;
222 -
223 - foreach ( $this->defaultVarNames as $var ) {
224 - $this->settings[$var] = $GLOBALS[$var];
225 - }
226 -
227 - foreach ( self::getDBTypes() as $type ) {
228 - $installer = $this->getDBInstaller( $type );
229 -
230 - if ( !$installer->isCompiled() ) {
231 - continue;
232 - }
233 -
234 - $defaults = $installer->getGlobalDefaults();
235 -
236 - foreach ( $installer->getGlobalNames() as $var ) {
237 - if ( isset( $defaults[$var] ) ) {
238 - $this->settings[$var] = $defaults[$var];
239 - } else {
240 - $this->settings[$var] = $GLOBALS[$var];
241 - }
242 - }
243 - }
244 -
245 - $this->parserTitle = Title::newFromText( 'Installer' );
246 - $this->parserOptions = new ParserOptions; // language will be wrong :(
247 - $this->parserOptions->setEditSection( false );
248 - }
249 -
250 - /**
251 - * Register tag hook below.
252 - *
253 - * @todo Move this to WebInstaller with the two things below?
254 - *
255 - * @param $parser Parser
256 - */
257 - public function registerDocLink( Parser &$parser ) {
258 - $parser->setHook( 'doclink', array( $this, 'docLink' ) );
259 - return true;
260 - }
261 -
262 - /**
263 - * ParserOptions are constructed before we determined the language, so fix it
264 - */
265 - public function setParserLanguage( $lang ) {
266 - $this->parserOptions->setTargetLanguage( $lang );
267 - $this->parserOptions->setUserLang( $lang->getCode() );
268 - }
269 -
270 - /**
271 - * Extension tag hook for a documentation link.
272 - */
273 - public function docLink( $linkText, $attribs, $parser ) {
274 - $url = $this->getDocUrl( $attribs['href'] );
275 - return '<a href="' . htmlspecialchars( $url ) . '">' .
276 - htmlspecialchars( $linkText ) .
277 - '</a>';
278 - }
279 -
280 - /**
281 - * Overridden by WebInstaller to provide lastPage parameters.
282 - */
283 - protected function getDocUrl( $page ) {
284 - return "{$_SERVER['PHP_SELF']}?page=" . urlencode( $page );
285 - }
286 -
287 - /**
288 - * Finds extensions that follow the format /extensions/Name/Name.php,
289 - * and returns an array containing the value for 'Name' for each found extension.
290 - *
291 - * @return array
292 - */
293 - public function findExtensions() {
294 - if( $this->getVar( 'IP' ) === null ) {
295 - return false;
296 - }
297 -
298 - $exts = array();
299 - $dir = $this->getVar( 'IP' ) . '/extensions';
300 - $dh = opendir( $dir );
301 -
302 - while ( ( $file = readdir( $dh ) ) !== false ) {
303 - if( file_exists( "$dir/$file/$file.php" ) ) {
304 - $exts[] = $file;
305 - }
306 - }
307 -
308 - return $exts;
309 - }
310 -
311 - /**
312 - * Installs the auto-detected extensions.
313 - *
314 - * @return Status
315 - */
316 - protected function includeExtensions() {
317 - $exts = $this->getVar( '_Extensions' );
318 - $path = $this->getVar( 'IP' ) . '/extensions';
319 -
320 - foreach( $exts as $e ) {
321 - require( "$path/$e/$e.php" );
322 - }
323 -
324 - return Status::newGood();
325 - }
326 -
327 - /**
328 - * Get an array of install steps. Should always be in the format of
329 - * array(
330 - * 'name' => 'someuniquename',
331 - * 'callback' => array( $obj, 'method' ),
332 - * )
333 - * There must be a config-install-$name message defined per step, which will
334 - * be shown on install.
335 - *
336 - * @param $installer DatabaseInstaller so we can make callbacks
337 - * @return array
338 - */
339 - protected function getInstallSteps( DatabaseInstaller &$installer ) {
340 - $coreInstallSteps = array(
341 - array( 'name' => 'database', 'callback' => array( $installer, 'setupDatabase' ) ),
342 - array( 'name' => 'tables', 'callback' => array( $this, 'installTables' ) ),
343 - array( 'name' => 'interwiki', 'callback' => array( $installer, 'populateInterwikiTable' ) ),
344 - array( 'name' => 'secretkey', 'callback' => array( $this, 'generateSecretKey' ) ),
345 - array( 'name' => 'upgradekey', 'callback' => array( $this, 'generateUpgradeKey' ) ),
346 - array( 'name' => 'sysop', 'callback' => array( $this, 'createSysop' ) ),
347 - array( 'name' => 'mainpage', 'callback' => array( $this, 'createMainpage' ) ),
348 - );
349 -
350 - // Build the array of install steps starting from the core install list,
351 - // then adding any callbacks that wanted to attach after a given step
352 - foreach( $coreInstallSteps as $step ) {
353 - $this->installSteps[] = $step;
354 - if( isset( $this->extraInstallSteps[ $step['name'] ] ) ) {
355 - $this->installSteps = array_merge(
356 - $this->installSteps,
357 - $this->extraInstallSteps[ $step['name'] ]
358 - );
359 - }
360 - }
361 -
362 - // Prepend any steps that want to be at the beginning
363 - if( isset( $this->extraInstallSteps['BEGINNING'] ) ) {
364 - $this->installSteps = array_merge(
365 - $this->extraInstallSteps['BEGINNING'],
366 - $this->installSteps
367 - );
368 - }
369 -
370 - // Extensions should always go first, chance to tie into hooks and such
371 - if( count( $this->getVar( '_Extensions' ) ) ) {
372 - array_unshift( $this->installSteps,
373 - array( 'name' => 'extensions', 'callback' => array( $this, 'includeExtensions' ) )
374 - );
375 - }
376 - return $this->installSteps;
377 - }
378 -
379 - /**
380 - * Actually perform the installation.
381 - *
382 - * @param $startCB A callback array for the beginning of each step
383 - * @param $endCB A callback array for the end of each step
384 - *
385 - * @return Array of Status objects
386 - */
387 - public function performInstallation( $startCB, $endCB ) {
388 - $installResults = array();
389 - $installer = $this->getDBInstaller();
390 - $installer->preInstall();
391 - $steps = $this->getInstallSteps( $installer );
392 - foreach( $steps as $stepObj ) {
393 - $name = $stepObj['name'];
394 - call_user_func_array( $startCB, array( $name ) );
395 -
396 - // Perform the callback step
397 - $status = call_user_func_array( $stepObj['callback'], array( &$installer ) );
398 -
399 - // Output and save the results
400 - call_user_func_array( $endCB, array( $name, $status ) );
401 - $installResults[$name] = $status;
402 -
403 - // If we've hit some sort of fatal, we need to bail.
404 - // Callback already had a chance to do output above.
405 - if( !$status->isOk() ) {
406 - break;
407 - }
408 - }
409 - if( $status->isOk() ) {
410 - $this->setVar( '_InstallDone', true );
411 - }
412 - return $installResults;
413 - }
414 -
415 - /**
416 - * Generate $wgSecretKey. Will warn if we had to use mt_rand() instead of
417 - * /dev/urandom
418 - *
419 - * @return Status
420 - */
421 - protected function generateSecretKey() {
422 - return $this->generateSecret( 'wgSecretKey' );
423 - }
424 -
425 - /**
426 - * Generate a secret value for a variable using either
427 - * /dev/urandom or mt_rand() Produce a warning in the later case.
428 - *
429 - * @return Status
430 - */
431 - protected function generateSecret( $secretName, $length = 64 ) {
432 - if ( wfIsWindows() ) {
433 - $file = null;
434 - } else {
435 - wfSuppressWarnings();
436 - $file = fopen( "/dev/urandom", "r" );
437 - wfRestoreWarnings();
438 - }
439 -
440 - $status = Status::newGood();
441 -
442 - if ( $file ) {
443 - $secretKey = bin2hex( fread( $file, $length / 2 ) );
444 - fclose( $file );
445 - } else {
446 - $secretKey = '';
447 -
448 - for ( $i = 0; $i < $length / 8; $i++ ) {
449 - $secretKey .= dechex( mt_rand( 0, 0x7fffffff ) );
450 - }
451 -
452 - $status->warning( 'config-insecure-secret', '$' . $secretName );
453 - }
454 -
455 - $this->setVar( $secretName, $secretKey );
456 -
457 - return $status;
458 - }
459 -
460 - /**
461 - * Generate a default $wgUpgradeKey. Will warn if we had to use
462 - * mt_rand() instead of /dev/urandom
463 - *
464 - * @return Status
465 - */
466 - public function generateUpgradeKey() {
467 - if ( strval( $this->getVar( 'wgUpgradeKey' ) ) === '' ) {
468 - return $this->generateSecret( 'wgUpgradeKey', 16 );
469 - }
470 - }
471 -
472 - /**
473 - * Create the first user account, grant it sysop and bureaucrat rights
474 - *
475 - * @return Status
476 - */
477 - protected function createSysop() {
478 - $name = $this->getVar( '_AdminName' );
479 - $user = User::newFromName( $name );
480 -
481 - if ( !$user ) {
482 - // We should've validated this earlier anyway!
483 - return Status::newFatal( 'config-admin-error-user', $name );
484 - }
485 -
486 - if ( $user->idForName() == 0 ) {
487 - $user->addToDatabase();
488 -
489 - try {
490 - $user->setPassword( $this->getVar( '_AdminPassword' ) );
491 - } catch( PasswordError $pwe ) {
492 - return Status::newFatal( 'config-admin-error-password', $name, $pwe->getMessage() );
493 - }
494 -
495 - $user->addGroup( 'sysop' );
496 - $user->addGroup( 'bureaucrat' );
497 - if( $this->getVar( '_AdminEmail' ) ) {
498 - $user->setEmail( $this->getVar( '_AdminEmail' ) );
499 - }
500 - $user->saveSettings();
501 - }
502 - $status = Status::newGood();
503 -
504 - if( $this->getVar( '_Subscribe' ) && $this->getVar( '_AdminEmail' ) ) {
505 - $this->subscribeToMediaWikiAnnounce( $status );
506 - }
507 -
508 - return $status;
509 - }
510 -
511 - private function subscribeToMediaWikiAnnounce( Status $s ) {
512 - $params = array(
513 - 'email' => $this->getVar( '_AdminEmail' ),
514 - 'language' => 'en',
515 - 'digest' => 0
516 - );
517 -
518 - // Mailman doesn't support as many languages as we do, so check to make
519 - // sure their selected language is available
520 - $myLang = $this->getVar( '_UserLang' );
521 - if( in_array( $myLang, $this->mediaWikiAnnounceLanguages ) ) {
522 - $myLang = $myLang == 'pt-br' ? 'pt_BR' : $myLang; // rewrite to Mailman's pt_BR
523 - $params['language'] = $myLang;
524 - }
525 -
526 - $res = Http::post( $this->mediaWikiAnnounceUrl, array( 'postData' => $params ) );
527 - if( !$res ) {
528 - $s->warning( 'config-install-subscribe-fail' );
529 - }
530 - }
531 -
532 - /**
533 - * Insert Main Page with default content.
534 - *
535 - * @return Status
536 - */
537 - protected function createMainpage( DatabaseInstaller &$installer ) {
538 - $status = Status::newGood();
539 - try {
540 - $article = new Article( Title::newMainPage() );
541 - $article->doEdit( wfMsgForContent( 'mainpagetext' ) . "\n\n" .
542 - wfMsgForContent( 'mainpagedocfooter' ),
543 - '',
544 - EDIT_NEW,
545 - false,
546 - User::newFromName( 'MediaWiki Default' ) );
547 - } catch (MWException $e) {
548 - //using raw, because $wgShowExceptionDetails can not be set yet
549 - $status->fatal( 'config-install-mainpage-failed', $e->getMessage() );
550 - }
551 -
552 - return $status;
553 - }
554 -
555 - /**
556 - * Override the necessary bits of the config to run an installation.
557 - */
558 - public static function overrideConfig() {
559 - define( 'MW_NO_SESSION', 1 );
560 -
561 - // Don't access the database
562 - $GLOBALS['wgUseDatabaseMessages'] = false;
563 - // Debug-friendly
564 - $GLOBALS['wgShowExceptionDetails'] = true;
565 - // Don't break forms
566 - $GLOBALS['wgExternalLinkTarget'] = '_blank';
567 -
568 - // Extended debugging
569 - $GLOBALS['wgShowSQLErrors'] = true;
570 - $GLOBALS['wgShowDBErrorBacktrace'] = true;
571 -
572 - // Allow multiple ob_flush() calls
573 - $GLOBALS['wgDisableOutputCompression'] = true;
574 -
575 - // Use a sensible cookie prefix (not my_wiki)
576 - $GLOBALS['wgCookiePrefix'] = 'mw_installer';
577 -
578 - // Some of the environment checks make shell requests, remove limits
579 - $GLOBALS['wgMaxShellMemory'] = 0;
580 - }
581 -
582 - /**
583 - * Add an installation step following the given step.
584 - *
585 - * @param $callback Array A valid installation callback array, in this form:
586 - * array( 'name' => 'some-unique-name', 'callback' => array( $obj, 'function' ) );
587 - * @param $findStep String the step to find. Omit to put the step at the beginning
588 - */
589 - public function addInstallStep( $callback, $findStep = 'BEGINNING' ) {
590 - $this->extraInstallSteps[$findStep][] = $callback;
591 - }
592 -}
Index: trunk/phase3/includes/installer/WebInstaller.php
@@ -12,7 +12,7 @@
1313 * @ingroup Deployment
1414 * @since 1.17
1515 */
16 -class WebInstaller extends CoreInstaller {
 16+class WebInstaller extends Installer {
1717
1818 /**
1919 * @var WebInstallerOutput
Index: trunk/phase3/includes/installer/Installer.php
@@ -104,7 +104,184 @@
105105 'envCheckLibicu'
106106 );
107107
 108+ /**
 109+ * MediaWiki configuration globals that will eventually be passed through
 110+ * to LocalSettings.php. The names only are given here, the defaults
 111+ * typically come from DefaultSettings.php.
 112+ *
 113+ * @var array
 114+ */
 115+ protected $defaultVarNames = array(
 116+ 'wgSitename',
 117+ 'wgPasswordSender',
 118+ 'wgLanguageCode',
 119+ 'wgRightsIcon',
 120+ 'wgRightsText',
 121+ 'wgRightsUrl',
 122+ 'wgMainCacheType',
 123+ 'wgEnableEmail',
 124+ 'wgEnableUserEmail',
 125+ 'wgEnotifUserTalk',
 126+ 'wgEnotifWatchlist',
 127+ 'wgEmailAuthentication',
 128+ 'wgDBtype',
 129+ 'wgDiff3',
 130+ 'wgImageMagickConvertCommand',
 131+ 'IP',
 132+ 'wgScriptPath',
 133+ 'wgScriptExtension',
 134+ 'wgMetaNamespace',
 135+ 'wgDeletedDirectory',
 136+ 'wgEnableUploads',
 137+ 'wgLogo',
 138+ 'wgShellLocale',
 139+ 'wgSecretKey',
 140+ 'wgUseInstantCommons',
 141+ 'wgUpgradeKey',
 142+ 'wgDefaultSkin',
 143+ );
 144+
108145 /**
 146+ * Variables that are stored alongside globals, and are used for any
 147+ * configuration of the installation process aside from the MediaWiki
 148+ * configuration. Map of names to defaults.
 149+ *
 150+ * @var array
 151+ */
 152+ protected $internalDefaults = array(
 153+ '_UserLang' => 'en',
 154+ '_Environment' => false,
 155+ '_CompiledDBs' => array(),
 156+ '_SafeMode' => false,
 157+ '_RaiseMemory' => false,
 158+ '_UpgradeDone' => false,
 159+ '_InstallDone' => false,
 160+ '_Caches' => array(),
 161+ '_InstallUser' => 'root',
 162+ '_InstallPassword' => '',
 163+ '_SameAccount' => true,
 164+ '_CreateDBAccount' => false,
 165+ '_NamespaceType' => 'site-name',
 166+ '_AdminName' => '', // will be set later, when the user selects language
 167+ '_AdminPassword' => '',
 168+ '_AdminPassword2' => '',
 169+ '_AdminEmail' => '',
 170+ '_Subscribe' => false,
 171+ '_SkipOptional' => 'continue',
 172+ '_RightsProfile' => 'wiki',
 173+ '_LicenseCode' => 'none',
 174+ '_CCDone' => false,
 175+ '_Extensions' => array(),
 176+ '_MemCachedServers' => '',
 177+ '_UpgradeKeySupplied' => false,
 178+ '_ExistingDBSettings' => false,
 179+ );
 180+
 181+ /**
 182+ * The actual list of installation steps. This will be initialized by getInstallSteps()
 183+ *
 184+ * @var array
 185+ */
 186+ private $installSteps = array();
 187+
 188+ /**
 189+ * Extra steps for installation, for things like DatabaseInstallers to modify
 190+ *
 191+ * @var array
 192+ */
 193+ protected $extraInstallSteps = array();
 194+
 195+ /**
 196+ * Known object cache types and the functions used to test for their existence.
 197+ *
 198+ * @var array
 199+ */
 200+ protected $objectCaches = array(
 201+ 'xcache' => 'xcache_get',
 202+ 'apc' => 'apc_fetch',
 203+ 'eaccel' => 'eaccelerator_get',
 204+ 'wincache' => 'wincache_ucache_get'
 205+ );
 206+
 207+ /**
 208+ * User rights profiles.
 209+ *
 210+ * @var array
 211+ */
 212+ public $rightsProfiles = array(
 213+ 'wiki' => array(),
 214+ 'no-anon' => array(
 215+ '*' => array( 'edit' => false )
 216+ ),
 217+ 'fishbowl' => array(
 218+ '*' => array(
 219+ 'createaccount' => false,
 220+ 'edit' => false,
 221+ ),
 222+ ),
 223+ 'private' => array(
 224+ '*' => array(
 225+ 'createaccount' => false,
 226+ 'edit' => false,
 227+ 'read' => false,
 228+ ),
 229+ ),
 230+ );
 231+
 232+ /**
 233+ * License types.
 234+ *
 235+ * @var array
 236+ */
 237+ public $licenses = array(
 238+ 'cc-by-sa' => array(
 239+ 'url' => 'http://creativecommons.org/licenses/by-sa/3.0/',
 240+ 'icon' => '{$wgStylePath}/common/images/cc-by-sa.png',
 241+ ),
 242+ 'cc-by-nc-sa' => array(
 243+ 'url' => 'http://creativecommons.org/licenses/by-nc-sa/3.0/',
 244+ 'icon' => '{$wgStylePath}/common/images/cc-by-nc-sa.png',
 245+ ),
 246+ 'pd' => array(
 247+ 'url' => 'http://creativecommons.org/licenses/publicdomain/',
 248+ 'icon' => '{$wgStylePath}/common/images/public-domain.png',
 249+ ),
 250+ 'gfdl-old' => array(
 251+ 'url' => 'http://www.gnu.org/licenses/old-licenses/fdl-1.2.html',
 252+ 'icon' => '{$wgStylePath}/common/images/gnu-fdl.png',
 253+ ),
 254+ 'gfdl-current' => array(
 255+ 'url' => 'http://www.gnu.org/copyleft/fdl.html',
 256+ 'icon' => '{$wgStylePath}/common/images/gnu-fdl.png',
 257+ ),
 258+ 'none' => array(
 259+ 'url' => '',
 260+ 'icon' => '',
 261+ 'text' => ''
 262+ ),
 263+ 'cc-choose' => array(
 264+ // Details will be filled in by the selector.
 265+ 'url' => '',
 266+ 'icon' => '',
 267+ 'text' => '',
 268+ ),
 269+ );
 270+
 271+ /**
 272+ * URL to mediawiki-announce subscription
 273+ */
 274+ protected $mediaWikiAnnounceUrl = 'https://lists.wikimedia.org/mailman/subscribe/mediawiki-announce';
 275+
 276+ /**
 277+ * Supported language codes for Mailman
 278+ */
 279+ protected $mediaWikiAnnounceLanguages = array(
 280+ 'ca', 'cs', 'da', 'de', 'en', 'es', 'et', 'eu', 'fi', 'fr', 'hr', 'hu',
 281+ 'it', 'ja', 'ko', 'lt', 'nl', 'no', 'pl', 'pt', 'pt-br', 'ro', 'ru',
 282+ 'sl', 'sr', 'sv', 'tr', 'uk'
 283+ );
 284+
 285+ /**
109286 * UI interface for displaying a short message
110287 * The parameters are like parameters to wfMsg().
111288 * The messages will be in wikitext format, which will be converted to an
@@ -113,12 +290,58 @@
114291 public abstract function showMessage( $msg /*, ... */ );
115292
116293 /**
 294+ * Show a message to the installing user by using a Status object
 295+ * @param $status Status
 296+ */
 297+ public abstract function showStatusMessage( Status $status );
 298+
 299+ /**
117300 * Constructor, always call this from child classes.
118301 */
119302 public function __construct() {
 303+ global $wgExtensionMessagesFiles, $wgUser, $wgHooks;
 304+
120305 // Disable the i18n cache and LoadBalancer
121306 Language::getLocalisationCache()->disableBackend();
122307 LBFactory::disableBackend();
 308+
 309+ // Load the installer's i18n file.
 310+ $wgExtensionMessagesFiles['MediawikiInstaller'] =
 311+ dirname( __FILE__ ) . '/Installer.i18n.php';
 312+
 313+ // Having a user with id = 0 safeguards us from DB access via User::loadOptions().
 314+ $wgUser = User::newFromId( 0 );
 315+
 316+ // Set our custom <doclink> hook.
 317+ $wgHooks['ParserFirstCallInit'][] = array( $this, 'registerDocLink' );
 318+
 319+ $this->settings = $this->internalDefaults;
 320+
 321+ foreach ( $this->defaultVarNames as $var ) {
 322+ $this->settings[$var] = $GLOBALS[$var];
 323+ }
 324+
 325+ foreach ( self::getDBTypes() as $type ) {
 326+ $installer = $this->getDBInstaller( $type );
 327+
 328+ if ( !$installer->isCompiled() ) {
 329+ continue;
 330+ }
 331+
 332+ $defaults = $installer->getGlobalDefaults();
 333+
 334+ foreach ( $installer->getGlobalNames() as $var ) {
 335+ if ( isset( $defaults[$var] ) ) {
 336+ $this->settings[$var] = $defaults[$var];
 337+ } else {
 338+ $this->settings[$var] = $GLOBALS[$var];
 339+ }
 340+ }
 341+ }
 342+
 343+ $this->parserTitle = Title::newFromText( 'Installer' );
 344+ $this->parserOptions = new ParserOptions; // language will be wrong :(
 345+ $this->parserOptions->setEditSection( false );
123346 }
124347
125348 /**
@@ -907,4 +1130,346 @@
9081131 return false;
9091132 }
9101133
 1134+ /**
 1135+ * Register tag hook below.
 1136+ *
 1137+ * @todo Move this to WebInstaller with the two things below?
 1138+ *
 1139+ * @param $parser Parser
 1140+ */
 1141+ public function registerDocLink( Parser &$parser ) {
 1142+ $parser->setHook( 'doclink', array( $this, 'docLink' ) );
 1143+ return true;
 1144+ }
 1145+
 1146+ /**
 1147+ * ParserOptions are constructed before we determined the language, so fix it
 1148+ */
 1149+ public function setParserLanguage( $lang ) {
 1150+ $this->parserOptions->setTargetLanguage( $lang );
 1151+ $this->parserOptions->setUserLang( $lang->getCode() );
 1152+ }
 1153+
 1154+ /**
 1155+ * Extension tag hook for a documentation link.
 1156+ */
 1157+ public function docLink( $linkText, $attribs, $parser ) {
 1158+ $url = $this->getDocUrl( $attribs['href'] );
 1159+ return '<a href="' . htmlspecialchars( $url ) . '">' .
 1160+ htmlspecialchars( $linkText ) .
 1161+ '</a>';
 1162+ }
 1163+
 1164+ /**
 1165+ * Overridden by WebInstaller to provide lastPage parameters.
 1166+ */
 1167+ protected function getDocUrl( $page ) {
 1168+ return "{$_SERVER['PHP_SELF']}?page=" . urlencode( $page );
 1169+ }
 1170+
 1171+ /**
 1172+ * Finds extensions that follow the format /extensions/Name/Name.php,
 1173+ * and returns an array containing the value for 'Name' for each found extension.
 1174+ *
 1175+ * @return array
 1176+ */
 1177+ public function findExtensions() {
 1178+ if( $this->getVar( 'IP' ) === null ) {
 1179+ return false;
 1180+ }
 1181+
 1182+ $exts = array();
 1183+ $dir = $this->getVar( 'IP' ) . '/extensions';
 1184+ $dh = opendir( $dir );
 1185+
 1186+ while ( ( $file = readdir( $dh ) ) !== false ) {
 1187+ if( file_exists( "$dir/$file/$file.php" ) ) {
 1188+ $exts[] = $file;
 1189+ }
 1190+ }
 1191+
 1192+ return $exts;
 1193+ }
 1194+
 1195+ /**
 1196+ * Installs the auto-detected extensions.
 1197+ *
 1198+ * @return Status
 1199+ */
 1200+ protected function includeExtensions() {
 1201+ $exts = $this->getVar( '_Extensions' );
 1202+ $path = $this->getVar( 'IP' ) . '/extensions';
 1203+
 1204+ foreach( $exts as $e ) {
 1205+ require( "$path/$e/$e.php" );
 1206+ }
 1207+
 1208+ return Status::newGood();
 1209+ }
 1210+
 1211+ /**
 1212+ * Get an array of install steps. Should always be in the format of
 1213+ * array(
 1214+ * 'name' => 'someuniquename',
 1215+ * 'callback' => array( $obj, 'method' ),
 1216+ * )
 1217+ * There must be a config-install-$name message defined per step, which will
 1218+ * be shown on install.
 1219+ *
 1220+ * @param $installer DatabaseInstaller so we can make callbacks
 1221+ * @return array
 1222+ */
 1223+ protected function getInstallSteps( DatabaseInstaller &$installer ) {
 1224+ $coreInstallSteps = array(
 1225+ array( 'name' => 'database', 'callback' => array( $installer, 'setupDatabase' ) ),
 1226+ array( 'name' => 'tables', 'callback' => array( $this, 'installTables' ) ),
 1227+ array( 'name' => 'interwiki', 'callback' => array( $installer, 'populateInterwikiTable' ) ),
 1228+ array( 'name' => 'secretkey', 'callback' => array( $this, 'generateSecretKey' ) ),
 1229+ array( 'name' => 'upgradekey', 'callback' => array( $this, 'generateUpgradeKey' ) ),
 1230+ array( 'name' => 'sysop', 'callback' => array( $this, 'createSysop' ) ),
 1231+ array( 'name' => 'mainpage', 'callback' => array( $this, 'createMainpage' ) ),
 1232+ );
 1233+
 1234+ // Build the array of install steps starting from the core install list,
 1235+ // then adding any callbacks that wanted to attach after a given step
 1236+ foreach( $coreInstallSteps as $step ) {
 1237+ $this->installSteps[] = $step;
 1238+ if( isset( $this->extraInstallSteps[ $step['name'] ] ) ) {
 1239+ $this->installSteps = array_merge(
 1240+ $this->installSteps,
 1241+ $this->extraInstallSteps[ $step['name'] ]
 1242+ );
 1243+ }
 1244+ }
 1245+
 1246+ // Prepend any steps that want to be at the beginning
 1247+ if( isset( $this->extraInstallSteps['BEGINNING'] ) ) {
 1248+ $this->installSteps = array_merge(
 1249+ $this->extraInstallSteps['BEGINNING'],
 1250+ $this->installSteps
 1251+ );
 1252+ }
 1253+
 1254+ // Extensions should always go first, chance to tie into hooks and such
 1255+ if( count( $this->getVar( '_Extensions' ) ) ) {
 1256+ array_unshift( $this->installSteps,
 1257+ array( 'name' => 'extensions', 'callback' => array( $this, 'includeExtensions' ) )
 1258+ );
 1259+ }
 1260+ return $this->installSteps;
 1261+ }
 1262+
 1263+ /**
 1264+ * Actually perform the installation.
 1265+ *
 1266+ * @param $startCB A callback array for the beginning of each step
 1267+ * @param $endCB A callback array for the end of each step
 1268+ *
 1269+ * @return Array of Status objects
 1270+ */
 1271+ public function performInstallation( $startCB, $endCB ) {
 1272+ $installResults = array();
 1273+ $installer = $this->getDBInstaller();
 1274+ $installer->preInstall();
 1275+ $steps = $this->getInstallSteps( $installer );
 1276+ foreach( $steps as $stepObj ) {
 1277+ $name = $stepObj['name'];
 1278+ call_user_func_array( $startCB, array( $name ) );
 1279+
 1280+ // Perform the callback step
 1281+ $status = call_user_func_array( $stepObj['callback'], array( &$installer ) );
 1282+
 1283+ // Output and save the results
 1284+ call_user_func_array( $endCB, array( $name, $status ) );
 1285+ $installResults[$name] = $status;
 1286+
 1287+ // If we've hit some sort of fatal, we need to bail.
 1288+ // Callback already had a chance to do output above.
 1289+ if( !$status->isOk() ) {
 1290+ break;
 1291+ }
 1292+ }
 1293+ if( $status->isOk() ) {
 1294+ $this->setVar( '_InstallDone', true );
 1295+ }
 1296+ return $installResults;
 1297+ }
 1298+
 1299+ /**
 1300+ * Generate $wgSecretKey. Will warn if we had to use mt_rand() instead of
 1301+ * /dev/urandom
 1302+ *
 1303+ * @return Status
 1304+ */
 1305+ protected function generateSecretKey() {
 1306+ return $this->generateSecret( 'wgSecretKey' );
 1307+ }
 1308+
 1309+ /**
 1310+ * Generate a secret value for a variable using either
 1311+ * /dev/urandom or mt_rand() Produce a warning in the later case.
 1312+ *
 1313+ * @return Status
 1314+ */
 1315+ protected function generateSecret( $secretName, $length = 64 ) {
 1316+ if ( wfIsWindows() ) {
 1317+ $file = null;
 1318+ } else {
 1319+ wfSuppressWarnings();
 1320+ $file = fopen( "/dev/urandom", "r" );
 1321+ wfRestoreWarnings();
 1322+ }
 1323+
 1324+ $status = Status::newGood();
 1325+
 1326+ if ( $file ) {
 1327+ $secretKey = bin2hex( fread( $file, $length / 2 ) );
 1328+ fclose( $file );
 1329+ } else {
 1330+ $secretKey = '';
 1331+
 1332+ for ( $i = 0; $i < $length / 8; $i++ ) {
 1333+ $secretKey .= dechex( mt_rand( 0, 0x7fffffff ) );
 1334+ }
 1335+
 1336+ $status->warning( 'config-insecure-secret', '$' . $secretName );
 1337+ }
 1338+
 1339+ $this->setVar( $secretName, $secretKey );
 1340+
 1341+ return $status;
 1342+ }
 1343+
 1344+ /**
 1345+ * Generate a default $wgUpgradeKey. Will warn if we had to use
 1346+ * mt_rand() instead of /dev/urandom
 1347+ *
 1348+ * @return Status
 1349+ */
 1350+ public function generateUpgradeKey() {
 1351+ if ( strval( $this->getVar( 'wgUpgradeKey' ) ) === '' ) {
 1352+ return $this->generateSecret( 'wgUpgradeKey', 16 );
 1353+ }
 1354+ }
 1355+
 1356+ /**
 1357+ * Create the first user account, grant it sysop and bureaucrat rights
 1358+ *
 1359+ * @return Status
 1360+ */
 1361+ protected function createSysop() {
 1362+ $name = $this->getVar( '_AdminName' );
 1363+ $user = User::newFromName( $name );
 1364+
 1365+ if ( !$user ) {
 1366+ // We should've validated this earlier anyway!
 1367+ return Status::newFatal( 'config-admin-error-user', $name );
 1368+ }
 1369+
 1370+ if ( $user->idForName() == 0 ) {
 1371+ $user->addToDatabase();
 1372+
 1373+ try {
 1374+ $user->setPassword( $this->getVar( '_AdminPassword' ) );
 1375+ } catch( PasswordError $pwe ) {
 1376+ return Status::newFatal( 'config-admin-error-password', $name, $pwe->getMessage() );
 1377+ }
 1378+
 1379+ $user->addGroup( 'sysop' );
 1380+ $user->addGroup( 'bureaucrat' );
 1381+ if( $this->getVar( '_AdminEmail' ) ) {
 1382+ $user->setEmail( $this->getVar( '_AdminEmail' ) );
 1383+ }
 1384+ $user->saveSettings();
 1385+ }
 1386+ $status = Status::newGood();
 1387+
 1388+ if( $this->getVar( '_Subscribe' ) && $this->getVar( '_AdminEmail' ) ) {
 1389+ $this->subscribeToMediaWikiAnnounce( $status );
 1390+ }
 1391+
 1392+ return $status;
 1393+ }
 1394+
 1395+ private function subscribeToMediaWikiAnnounce( Status $s ) {
 1396+ $params = array(
 1397+ 'email' => $this->getVar( '_AdminEmail' ),
 1398+ 'language' => 'en',
 1399+ 'digest' => 0
 1400+ );
 1401+
 1402+ // Mailman doesn't support as many languages as we do, so check to make
 1403+ // sure their selected language is available
 1404+ $myLang = $this->getVar( '_UserLang' );
 1405+ if( in_array( $myLang, $this->mediaWikiAnnounceLanguages ) ) {
 1406+ $myLang = $myLang == 'pt-br' ? 'pt_BR' : $myLang; // rewrite to Mailman's pt_BR
 1407+ $params['language'] = $myLang;
 1408+ }
 1409+
 1410+ $res = Http::post( $this->mediaWikiAnnounceUrl, array( 'postData' => $params ) );
 1411+ if( !$res ) {
 1412+ $s->warning( 'config-install-subscribe-fail' );
 1413+ }
 1414+ }
 1415+
 1416+ /**
 1417+ * Insert Main Page with default content.
 1418+ *
 1419+ * @return Status
 1420+ */
 1421+ protected function createMainpage( DatabaseInstaller &$installer ) {
 1422+ $status = Status::newGood();
 1423+ try {
 1424+ $article = new Article( Title::newMainPage() );
 1425+ $article->doEdit( wfMsgForContent( 'mainpagetext' ) . "\n\n" .
 1426+ wfMsgForContent( 'mainpagedocfooter' ),
 1427+ '',
 1428+ EDIT_NEW,
 1429+ false,
 1430+ User::newFromName( 'MediaWiki Default' ) );
 1431+ } catch (MWException $e) {
 1432+ //using raw, because $wgShowExceptionDetails can not be set yet
 1433+ $status->fatal( 'config-install-mainpage-failed', $e->getMessage() );
 1434+ }
 1435+
 1436+ return $status;
 1437+ }
 1438+
 1439+ /**
 1440+ * Override the necessary bits of the config to run an installation.
 1441+ */
 1442+ public static function overrideConfig() {
 1443+ define( 'MW_NO_SESSION', 1 );
 1444+
 1445+ // Don't access the database
 1446+ $GLOBALS['wgUseDatabaseMessages'] = false;
 1447+ // Debug-friendly
 1448+ $GLOBALS['wgShowExceptionDetails'] = true;
 1449+ // Don't break forms
 1450+ $GLOBALS['wgExternalLinkTarget'] = '_blank';
 1451+
 1452+ // Extended debugging
 1453+ $GLOBALS['wgShowSQLErrors'] = true;
 1454+ $GLOBALS['wgShowDBErrorBacktrace'] = true;
 1455+
 1456+ // Allow multiple ob_flush() calls
 1457+ $GLOBALS['wgDisableOutputCompression'] = true;
 1458+
 1459+ // Use a sensible cookie prefix (not my_wiki)
 1460+ $GLOBALS['wgCookiePrefix'] = 'mw_installer';
 1461+
 1462+ // Some of the environment checks make shell requests, remove limits
 1463+ $GLOBALS['wgMaxShellMemory'] = 0;
 1464+ }
 1465+
 1466+ /**
 1467+ * Add an installation step following the given step.
 1468+ *
 1469+ * @param $callback Array A valid installation callback array, in this form:
 1470+ * array( 'name' => 'some-unique-name', 'callback' => array( $obj, 'function' ) );
 1471+ * @param $findStep String the step to find. Omit to put the step at the beginning
 1472+ */
 1473+ public function addInstallStep( $callback, $findStep = 'BEGINNING' ) {
 1474+ $this->extraInstallSteps[$findStep][] = $callback;
 1475+ }
9111476 }
Index: trunk/phase3/includes/installer/DatabaseInstaller.php
@@ -19,7 +19,7 @@
2020 *
2121 * TODO: naming this parent is confusing, 'installer' would be clearer.
2222 *
23 - * @var CoreInstaller
 23+ * @var Installer
2424 */
2525 public $parent;
2626
Index: trunk/phase3/includes/installer/CliInstaller.php
@@ -12,7 +12,7 @@
1313 * @ingroup Deployment
1414 * @since 1.17
1515 */
16 -class CliInstaller extends CoreInstaller {
 16+class CliInstaller extends Installer {
1717
1818 private $optionMap = array(
1919 'dbtype' => 'wgDBtype',
Index: trunk/phase3/includes/AutoLoader.php
@@ -453,7 +453,6 @@
454454 # includes/installer
455455 'CliInstaller' => 'includes/installer/CliInstaller.php',
456456 'Installer' => 'includes/installer/Installer.php',
457 - 'CoreInstaller' => 'includes/installer/CoreInstaller.php',
458457 'DatabaseInstaller' => 'includes/installer/DatabaseInstaller.php',
459458 'DatabaseUpdater' => 'includes/installer/DatabaseUpdater.php',
460459 'LBFactory_InstallerFake' => 'includes/installer/Installer.php',
Index: trunk/phase3/config/index.php
@@ -5,7 +5,7 @@
66 * @file
77 */
88
9 -define( 'MW_CONFIG_CALLBACK', 'CoreInstaller::overrideConfig' );
 9+define( 'MW_CONFIG_CALLBACK', 'Installer::overrideConfig' );
1010 define( 'MEDIAWIKI_INSTALL', true );
1111
1212 chdir( dirname( dirname( __FILE__ ) ) );

Follow-up revisions

RevisionCommit summaryAuthorDate
r814021.17: MFT r79915, r79957, r79964, r79990, r80687, r80999, r81006, r81011, r81...catrope16:18, 2 February 2011

Past revisions this follows-up on

RevisionCommit summaryAuthorDate
r69738Split Installer into Installer and CoreInstaller + made misc style and doc im...jeroendedauw17:58, 22 July 2010

Status & tagging log