]> scripts.mit.edu Git - autoinstalls/mediawiki.git/blob - includes/installer/WebInstaller.php
MediaWiki 1.17.0
[autoinstalls/mediawiki.git] / includes / installer / WebInstaller.php
1 <?php
2 /**
3  * Core installer web interface.
4  *
5  * @file
6  * @ingroup Deployment
7  */
8
9 /**
10  * Class for the core installer web interface.
11  *
12  * @ingroup Deployment
13  * @since 1.17
14  */
15 class WebInstaller extends Installer {
16
17         /**
18          * @var WebInstallerOutput
19          */
20         public $output;
21
22         /**
23          * WebRequest object.
24          *
25          * @var WebRequest
26          */
27         public $request;
28
29         /**
30          * Cached session array.
31          *
32          * @var array
33          */
34         protected $session;
35
36         /**
37          * Captured PHP error text. Temporary.
38          * @var array
39          */
40         protected $phpErrors;
41
42         /**
43          * The main sequence of page names. These will be displayed in turn.
44          * To add one:
45          *    * Add it here
46          *    * Add a config-page-<name> message
47          *    * Add a WebInstaller_<name> class
48          * @var array
49          */
50         public $pageSequence = array(
51                 'Language',
52                 'ExistingWiki',
53                 'Welcome',
54                 'DBConnect',
55                 'Upgrade',
56                 'DBSettings',
57                 'Name',
58                 'Options',
59                 'Install',
60                 'Complete',
61         );
62
63         /**
64          * Out of sequence pages, selectable by the user at any time.
65          * @var array
66          */
67         protected $otherPages = array(
68                 'Restart',
69                 'Readme',
70                 'ReleaseNotes',
71                 'Copying',
72                 'UpgradeDoc', // Can't use Upgrade due to Upgrade step
73         );
74
75         /**
76          * Array of pages which have declared that they have been submitted, have validated
77          * their input, and need no further processing.
78          * @var array
79          */
80         protected $happyPages;
81
82         /**
83          * List of "skipped" pages. These are pages that will automatically continue
84          * to the next page on any GET request. To avoid breaking the "back" button,
85          * they need to be skipped during a back operation.
86          * @var array
87          */
88         protected $skippedPages;
89
90         /**
91          * Flag indicating that session data may have been lost.
92          * @var bool
93          */
94         public $showSessionWarning = false;
95
96         /**
97          * Numeric index of the page we're on
98          * @var int
99          */
100         protected $tabIndex = 1;
101
102         /**
103          * Name of the page we're on
104          * @var string
105          */
106         protected $currentPageName;
107
108         /**
109          * Constructor.
110          *
111          * @param $request WebRequest
112          */
113         public function __construct( WebRequest $request ) {
114                 parent::__construct();
115                 $this->output = new WebInstallerOutput( $this );
116                 $this->request = $request;
117
118                 // Add parser hooks
119                 global $wgParser;
120                 $wgParser->setHook( 'downloadlink', array( $this, 'downloadLinkHook' ) );
121                 $wgParser->setHook( 'doclink', array( $this, 'docLink' ) );
122         }
123
124         /**
125          * Main entry point.
126          *
127          * @param $session Array: initial session array
128          *
129          * @return Array: new session array
130          */
131         public function execute( array $session ) {
132                 $this->session = $session;
133
134                 if ( isset( $session['settings'] ) ) {
135                         $this->settings = $session['settings'] + $this->settings;
136                 }
137
138                 $this->exportVars();
139                 $this->setupLanguage();
140
141                 if( ( $this->getVar( '_InstallDone' ) || $this->getVar( '_UpgradeDone' ) )
142                         && $this->request->getVal( 'localsettings' ) )
143                 {
144                         $this->request->response()->header( 'Content-type: application/x-httpd-php' );
145                         $this->request->response()->header(
146                                 'Content-Disposition: attachment; filename="LocalSettings.php"'
147                         );
148
149                         $ls = new LocalSettingsGenerator( $this );
150                         $rightsProfile = $this->rightsProfiles[$this->getVar( '_RightsProfile' )];
151                         foreach( $rightsProfile as $group => $rightsArr ) {
152                                 $ls->setGroupRights( $group, $rightsArr );
153                         }
154                         echo $ls->getText();
155                         return $this->session;
156                 }
157
158                 $cssDir = $this->request->getVal( 'css' );
159                 if( $cssDir ) {
160                         $cssDir = ( $cssDir == 'rtl' ? 'rtl' : 'ltr' );
161                         $this->request->response()->header( 'Content-type: text/css' );
162                         echo $this->output->getCSS( $cssDir );
163                         return $this->session;
164                 }
165
166                 if ( isset( $session['happyPages'] ) ) {
167                         $this->happyPages = $session['happyPages'];
168                 } else {
169                         $this->happyPages = array();
170                 }
171
172                 if ( isset( $session['skippedPages'] ) ) {
173                         $this->skippedPages = $session['skippedPages'];
174                 } else {
175                         $this->skippedPages = array();
176                 }
177
178                 $lowestUnhappy = $this->getLowestUnhappy();
179
180                 # Special case for Creative Commons partner chooser box.
181                 if ( $this->request->getVal( 'SubmitCC' ) ) {
182                         $page = $this->getPageByName( 'Options' );
183                         $this->output->useShortHeader();
184                         $this->output->allowFrames();
185                         $page->submitCC();
186                         return $this->finish();
187                 }
188
189                 if ( $this->request->getVal( 'ShowCC' ) ) {
190                         $page = $this->getPageByName( 'Options' );
191                         $this->output->useShortHeader();
192                         $this->output->allowFrames();
193                         $this->output->addHTML( $page->getCCDoneBox() );
194                         return $this->finish();
195                 }
196
197                 # Get the page name.
198                 $pageName = $this->request->getVal( 'page' );
199
200                 if ( in_array( $pageName, $this->otherPages ) ) {
201                         # Out of sequence
202                         $pageId = false;
203                         $page = $this->getPageByName( $pageName );
204                 } else {
205                         # Main sequence
206                         if ( !$pageName || !in_array( $pageName, $this->pageSequence ) ) {
207                                 $pageId = $lowestUnhappy;
208                         } else {
209                                 $pageId = array_search( $pageName, $this->pageSequence );
210                         }
211
212                         # If necessary, move back to the lowest-numbered unhappy page
213                         if ( $pageId > $lowestUnhappy ) {
214                                 $pageId = $lowestUnhappy;
215                                 if ( $lowestUnhappy == 0 ) {
216                                         # Knocked back to start, possible loss of session data.
217                                         $this->showSessionWarning = true;
218                                 }
219                         }
220
221                         $pageName = $this->pageSequence[$pageId];
222                         $page = $this->getPageByName( $pageName );
223                 }
224
225                 # If a back button was submitted, go back without submitting the form data.
226                 if ( $this->request->wasPosted() && $this->request->getBool( 'submit-back' ) ) {
227                         if ( $this->request->getVal( 'lastPage' ) ) {
228                                 $nextPage = $this->request->getVal( 'lastPage' );
229                         } elseif ( $pageId !== false ) {
230                                 # Main sequence page
231                                 # Skip the skipped pages
232                                 $nextPageId = $pageId;
233
234                                 do {
235                                         $nextPageId--;
236                                         $nextPage = $this->pageSequence[$nextPageId];
237                                 } while( isset( $this->skippedPages[$nextPage] ) );
238                         } else {
239                                 $nextPage = $this->pageSequence[$lowestUnhappy];
240                         }
241
242                         $this->output->redirect( $this->getUrl( array( 'page' => $nextPage ) ) );
243                         return $this->finish();
244                 }
245
246                 # Execute the page.
247                 $this->currentPageName = $page->getName();
248                 $this->startPageWrapper( $pageName );
249
250                 $result = $page->execute();
251
252                 $this->endPageWrapper();
253
254                 if ( $result == 'skip' ) {
255                         # Page skipped without explicit submission.
256                         # Skip it when we click "back" so that we don't just go forward again.
257                         $this->skippedPages[$pageName] = true;
258                         $result = 'continue';
259                 } else {
260                         unset( $this->skippedPages[$pageName] );
261                 }
262
263                 # If it was posted, the page can request a continue to the next page.
264                 if ( $result === 'continue' && !$this->output->headerDone() ) {
265                         if ( $pageId !== false ) {
266                                 $this->happyPages[$pageId] = true;
267                         }
268
269                         $lowestUnhappy = $this->getLowestUnhappy();
270
271                         if ( $this->request->getVal( 'lastPage' ) ) {
272                                 $nextPage = $this->request->getVal( 'lastPage' );
273                         } elseif ( $pageId !== false ) {
274                                 $nextPage = $this->pageSequence[$pageId + 1];
275                         } else {
276                                 $nextPage = $this->pageSequence[$lowestUnhappy];
277                         }
278
279                         if ( array_search( $nextPage, $this->pageSequence ) > $lowestUnhappy ) {
280                                 $nextPage = $this->pageSequence[$lowestUnhappy];
281                         }
282
283                         $this->output->redirect( $this->getUrl( array( 'page' => $nextPage ) ) );
284                 }
285
286                 return $this->finish();
287         }
288
289         /**
290          * Find the next page in sequence that hasn't been completed
291          * @return int
292          */
293         public function getLowestUnhappy() {
294                 if ( count( $this->happyPages ) == 0 ) {
295                         return 0;
296                 } else {
297                         return max( array_keys( $this->happyPages ) ) + 1;
298                 }
299         }
300
301         /**
302          * Start the PHP session. This may be called before execute() to start the PHP session.
303          */
304         public function startSession() {
305                 if( wfIniGetBool( 'session.auto_start' ) || session_id() ) {
306                         // Done already
307                         return true;
308                 }
309
310                 $this->phpErrors = array();
311                 set_error_handler( array( $this, 'errorHandler' ) );
312                 session_start();
313                 restore_error_handler();
314
315                 if ( $this->phpErrors ) {
316                         $this->showError( 'config-session-error', $this->phpErrors[0] );
317                         return false;
318                 }
319
320                 return true;
321         }
322
323         /**
324          * Get a hash of data identifying this MW installation.
325          *
326          * This is used by mw-config/index.php to prevent multiple installations of MW
327          * on the same cookie domain from interfering with each other.
328          */
329         public function getFingerprint() {
330                 // Get the base URL of the installation
331                 $url = $this->request->getFullRequestURL();
332                 if ( preg_match( '!^(.*\?)!', $url, $m) ) {
333                         // Trim query string
334                         $url = $m[1];
335                 }
336                 if ( preg_match( '!^(.*)/[^/]*/[^/]*$!', $url, $m ) ) {
337                         // This... seems to try to get the base path from
338                         // the /mw-config/index.php. Kinda scary though?
339                         $url = $m[1];
340                 }
341                 return md5( serialize( array(
342                         'local path' => dirname( dirname( __FILE__ ) ),
343                         'url' => $url,
344                         'version' => $GLOBALS['wgVersion']
345                 ) ) );
346         }
347
348         /**
349          * Show an error message in a box. Parameters are like wfMsg().
350          */
351         public function showError( $msg /*...*/ ) {
352                 $args = func_get_args();
353                 array_shift( $args );
354                 $args = array_map( 'htmlspecialchars', $args );
355                 $msg = wfMsgReal( $msg, $args, false, false, false );
356                 $this->output->addHTML( $this->getErrorBox( $msg ) );
357         }
358
359         /**
360          * Temporary error handler for session start debugging.
361          */
362         public function errorHandler( $errno, $errstr ) {
363                 $this->phpErrors[] = $errstr;
364         }
365
366         /**
367          * Clean up from execute()
368          *
369          * @return array
370          */
371         public function finish() {
372                 $this->output->output();
373
374                 $this->session['happyPages'] = $this->happyPages;
375                 $this->session['skippedPages'] = $this->skippedPages;
376                 $this->session['settings'] = $this->settings;
377
378                 return $this->session;
379         }
380
381         /**
382          * We're restarting the installation, reset the session, happyPages, etc
383          */
384         public function reset() {
385                 $this->session = array();
386                 $this->happyPages = array();
387                 $this->settings = array();
388         }
389
390         /**
391          * Get a URL for submission back to the same script.
392          *
393          * @param $query: Array
394          * @return string
395          */
396         public function getUrl( $query = array() ) {
397                 $url = $this->request->getRequestURL();
398                 # Remove existing query
399                 $url = preg_replace( '/\?.*$/', '', $url );
400
401                 if ( $query ) {
402                         $url .= '?' . wfArrayToCGI( $query );
403                 }
404
405                 return $url;
406         }
407
408         /**
409          * Get a WebInstallerPage by name.
410          *
411          * @param $pageName String
412          * @return WebInstallerPage
413          */
414         public function getPageByName( $pageName ) {
415                 // Totally lame way to force autoload of WebInstallerPage.php
416                 class_exists( 'WebInstallerPage' );
417
418                 $pageClass = 'WebInstaller_' . $pageName;
419
420                 return new $pageClass( $this );
421         }
422
423         /**
424          * Get a session variable.
425          *
426          * @param $name String
427          * @param $default
428          */
429         public function getSession( $name, $default = null ) {
430                 if ( !isset( $this->session[$name] ) ) {
431                         return $default;
432                 } else {
433                         return $this->session[$name];
434                 }
435         }
436
437         /**
438          * Set a session variable.
439          * @param $name String key for the variable
440          * @param $value Mixed
441          */
442         public function setSession( $name, $value ) {
443                 $this->session[$name] = $value;
444         }
445
446         /**
447          * Get the next tabindex attribute value.
448          * @return int
449          */
450         public function nextTabIndex() {
451                 return $this->tabIndex++;
452         }
453
454         /**
455          * Initializes language-related variables.
456          */
457         public function setupLanguage() {
458                 global $wgLang, $wgContLang, $wgLanguageCode;
459
460                 if ( $this->getSession( 'test' ) === null && !$this->request->wasPosted() ) {
461                         $wgLanguageCode = $this->getAcceptLanguage();
462                         $wgLang = $wgContLang = Language::factory( $wgLanguageCode );
463                         $this->setVar( 'wgLanguageCode', $wgLanguageCode );
464                         $this->setVar( '_UserLang', $wgLanguageCode );
465                 } else {
466                         $wgLanguageCode = $this->getVar( 'wgLanguageCode' );
467                         $wgLang = Language::factory( $this->getVar( '_UserLang' ) );
468                         $wgContLang = Language::factory( $wgLanguageCode );
469                 }
470         }
471
472         /**
473          * Retrieves MediaWiki language from Accept-Language HTTP header.
474          *
475          * @return string
476          */
477         public function getAcceptLanguage() {
478                 global $wgLanguageCode, $wgRequest;
479
480                 $mwLanguages = Language::getLanguageNames();
481                 $headerLanguages = array_keys( $wgRequest->getAcceptLang() );
482
483                 foreach ( $headerLanguages as $lang ) {
484                         if ( isset( $mwLanguages[$lang] ) ) {
485                                 return $lang;
486                         }
487                 }
488
489                 return $wgLanguageCode;
490         }
491
492         /**
493          * Called by execute() before page output starts, to show a page list.
494          *
495          * @param $currentPageName String
496          */
497         private function startPageWrapper( $currentPageName ) {
498                 $s = "<div class=\"config-page-wrapper\">\n";
499                 $s .= "<div class=\"config-page\">\n";
500                 $s .= "<div class=\"config-page-list\"><ul>\n";
501                 $lastHappy = -1;
502
503                 foreach ( $this->pageSequence as $id => $pageName ) {
504                         $happy = !empty( $this->happyPages[$id] );
505                         $s .= $this->getPageListItem(
506                                 $pageName,
507                                 $happy || $lastHappy == $id - 1,
508                                 $currentPageName
509                         );
510
511                         if ( $happy ) {
512                                 $lastHappy = $id;
513                         }
514                 }
515
516                 $s .= "</ul><br/><ul>\n";
517                 $s .= $this->getPageListItem( 'Restart', true, $currentPageName );
518                 $s .= "</ul></div>\n"; // end list pane
519                 $s .= Html::element( 'h2', array(),
520                                 wfMsg( 'config-page-' . strtolower( $currentPageName ) ) );
521
522                 $this->output->addHTMLNoFlush( $s );
523         }
524
525         /**
526          * Get a list item for the page list.
527          *
528          * @param $pageName String
529          * @param $enabled Boolean
530          * @param $currentPageName String
531          *
532          * @return string
533          */
534         private function getPageListItem( $pageName, $enabled, $currentPageName ) {
535                 $s = "<li class=\"config-page-list-item\">";
536                 $name = wfMsg( 'config-page-' . strtolower( $pageName ) );
537
538                 if ( $enabled ) {
539                         $query = array( 'page' => $pageName );
540
541                         if ( !in_array( $pageName, $this->pageSequence ) ) {
542                                 if ( in_array( $currentPageName, $this->pageSequence ) ) {
543                                         $query['lastPage'] = $currentPageName;
544                                 }
545
546                                 $link = Html::element( 'a',
547                                         array(
548                                                 'href' => $this->getUrl( $query )
549                                         ),
550                                         $name
551                                 );
552                         } else {
553                                 $link = htmlspecialchars( $name );
554                         }
555
556                         if ( $pageName == $currentPageName ) {
557                                 $s .= "<span class=\"config-page-current\">$link</span>";
558                         } else {
559                                 $s .= $link;
560                         }
561                 } else {
562                         $s .= Html::element( 'span',
563                                 array(
564                                         'class' => 'config-page-disabled'
565                                 ),
566                                 $name
567                         );
568                 }
569
570                 $s .= "</li>\n";
571
572                 return $s;
573         }
574
575         /**
576          * Output some stuff after a page is finished.
577          */
578         private function endPageWrapper() {
579                 $this->output->addHTMLNoFlush(
580                                         "<div class=\"visualClear\"></div>\n" .
581                                 "</div>\n" .
582                                 "<div class=\"visualClear\"></div>\n" .
583                         "</div>" );
584         }
585
586         /**
587          * Get HTML for an error box with an icon.
588          *
589          * @param $text String: wikitext, get this with wfMsgNoTrans()
590          */
591         public function getErrorBox( $text ) {
592                 return $this->getInfoBox( $text, 'critical-32.png', 'config-error-box' );
593         }
594
595         /**
596          * Get HTML for a warning box with an icon.
597          *
598          * @param $text String: wikitext, get this with wfMsgNoTrans()
599          */
600         public function getWarningBox( $text ) {
601                 return $this->getInfoBox( $text, 'warning-32.png', 'config-warning-box' );
602         }
603
604         /**
605          * Get HTML for an info box with an icon.
606          *
607          * @param $text String: wikitext, get this with wfMsgNoTrans()
608          * @param $icon String: icon name, file in skins/common/images
609          * @param $class String: additional class name to add to the wrapper div
610          */
611         public function getInfoBox( $text, $icon = 'info-32.png', $class = false ) {
612                 $s =
613                         "<div class=\"config-info $class\">\n" .
614                                 "<div class=\"config-info-left\">\n" .
615                                 Html::element( 'img',
616                                         array(
617                                                 'src' => '../skins/common/images/' . $icon,
618                                                 'alt' => wfMsg( 'config-information' ),
619                                         )
620                                 ) . "\n" .
621                                 "</div>\n" .
622                                 "<div class=\"config-info-right\">\n" .
623                                         $this->parse( $text, true ) . "\n" .
624                                 "</div>\n" .
625                                 "<div style=\"clear: left;\"></div>\n" .
626                         "</div>\n";
627                 return $s;
628         }
629
630         /**
631          * Get small text indented help for a preceding form field.
632          * Parameters like wfMsg().
633          */
634         public function getHelpBox( $msg /*, ... */ ) {
635                 $args = func_get_args();
636                 array_shift( $args );
637                 $args = array_map( 'htmlspecialchars', $args );
638                 $text = wfMsgReal( $msg, $args, false, false, false );
639                 $html = htmlspecialchars( $text );
640                 $html = $this->parse( $text, true );
641
642                 return "<div class=\"mw-help-field-container\">\n" .
643                            "<span class=\"mw-help-field-hint\">" . wfMsgHtml( 'config-help' ) . "</span>\n" .
644                            "<span class=\"mw-help-field-data\">" . $html . "</span>\n" .
645                            "</div>\n";
646         }
647
648         /**
649          * Output a help box.
650          * @param $msg String key for wfMsg()
651          */
652         public function showHelpBox( $msg /*, ... */ ) {
653                 $args = func_get_args();
654                 $html = call_user_func_array( array( $this, 'getHelpBox' ), $args );
655                 $this->output->addHTML( $html );
656         }
657
658         /**
659          * Show a short informational message.
660          * Output looks like a list.
661          *
662          * @param $msg string
663          */
664         public function showMessage( $msg /*, ... */ ) {
665                 $args = func_get_args();
666                 array_shift( $args );
667                 $html = '<div class="config-message">' .
668                         $this->parse( wfMsgReal( $msg, $args, false, false, false ) ) .
669                         "</div>\n";
670                 $this->output->addHTML( $html );
671         }
672
673         /**
674          * @param $status Status
675          */
676         public function showStatusMessage( Status $status ) {
677                 $text = $status->getWikiText();
678                 $this->output->addWikiText(
679                         "<div class=\"config-message\">\n" .
680                         $text .
681                         "</div>"
682                 );
683         }
684
685         /**
686          * Label a control by wrapping a config-input div around it and putting a
687          * label before it.
688          */
689         public function label( $msg, $forId, $contents, $helpData = "" ) {
690                 if ( strval( $msg ) == '' ) {
691                         $labelText = '&#160;';
692                 } else {
693                         $labelText = wfMsgHtml( $msg );
694                 }
695
696                 $attributes = array( 'class' => 'config-label' );
697
698                 if ( $forId ) {
699                         $attributes['for'] = $forId;
700                 }
701
702                 return
703                         "<div class=\"config-block\">\n" .
704                         "  <div class=\"config-block-label\">\n" .
705                         Xml::tags( 'label',
706                                 $attributes,
707                                 $labelText ) . "\n" .
708                                 $helpData .
709                         "  </div>\n" .
710                         "  <div class=\"config-block-elements\">\n" .
711                                 $contents .
712                         "  </div>\n" .
713                         "</div>\n";
714         }
715
716         /**
717          * Get a labelled text box to configure a variable.
718          *
719          * @param $params Array
720          *    Parameters are:
721          *      var:        The variable to be configured (required)
722          *      label:      The message name for the label (required)
723          *      attribs:    Additional attributes for the input element (optional)
724          *      controlName: The name for the input element (optional)
725          *      value:      The current value of the variable (optional)
726          *      help:           The html for the help text (optional)
727          */
728         public function getTextBox( $params ) {
729                 if ( !isset( $params['controlName'] ) ) {
730                         $params['controlName'] = 'config_' . $params['var'];
731                 }
732
733                 if ( !isset( $params['value'] ) ) {
734                         $params['value'] = $this->getVar( $params['var'] );
735                 }
736
737                 if ( !isset( $params['attribs'] ) ) {
738                         $params['attribs'] = array();
739                 }
740                 if ( !isset( $params['help'] ) ) {
741                         $params['help'] = "";
742                 }
743                 return
744                         $this->label(
745                                 $params['label'],
746                                 $params['controlName'],
747                                 Xml::input(
748                                         $params['controlName'],
749                                         30, // intended to be overridden by CSS
750                                         $params['value'],
751                                         $params['attribs'] + array(
752                                                 'id' => $params['controlName'],
753                                                 'class' => 'config-input-text',
754                                                 'tabindex' => $this->nextTabIndex()
755                                         )
756                                 ),
757                                 $params['help']
758                         );
759         }
760
761         /**
762          * Get a labelled textarea to configure a variable
763          *
764          * @param $params Array
765          *    Parameters are:
766          *      var:        The variable to be configured (required)
767          *      label:      The message name for the label (required)
768          *      attribs:    Additional attributes for the input element (optional)
769          *      controlName: The name for the input element (optional)
770          *      value:      The current value of the variable (optional)
771          *      help:           The html for the help text (optional)
772          */
773         public function getTextArea( $params ) {
774                 if ( !isset( $params['controlName'] ) ) {
775                         $params['controlName'] = 'config_' . $params['var'];
776                 }
777
778                 if ( !isset( $params['value'] ) ) {
779                         $params['value'] = $this->getVar( $params['var'] );
780                 }
781
782                 if ( !isset( $params['attribs'] ) ) {
783                         $params['attribs'] = array();
784                 }
785                 if ( !isset( $params['help'] ) ) {
786                         $params['help'] = "";
787                 }
788                 return
789                         $this->label(
790                                 $params['label'],
791                                 $params['controlName'],
792                                 Xml::textarea(
793                                         $params['controlName'],
794                                         $params['value'],
795                                         30,
796                                         5,
797                                         $params['attribs'] + array(
798                                                 'id' => $params['controlName'],
799                                                 'class' => 'config-input-text',
800                                                 'tabindex' => $this->nextTabIndex()
801                                         )
802                                 ),
803                                 $params['help']
804                         );
805         }
806
807         /**
808          * Get a labelled password box to configure a variable.
809          *
810          * Implements password hiding
811          * @param $params Array
812          *    Parameters are:
813          *      var:        The variable to be configured (required)
814          *      label:      The message name for the label (required)
815          *      attribs:    Additional attributes for the input element (optional)
816          *      controlName: The name for the input element (optional)
817          *      value:      The current value of the variable (optional)
818          *      help:           The html for the help text (optional)
819          */
820         public function getPasswordBox( $params ) {
821                 if ( !isset( $params['value'] ) ) {
822                         $params['value'] = $this->getVar( $params['var'] );
823                 }
824
825                 if ( !isset( $params['attribs'] ) ) {
826                         $params['attribs'] = array();
827                 }
828
829                 $params['value'] = $this->getFakePassword( $params['value'] );
830                 $params['attribs']['type'] = 'password';
831
832                 return $this->getTextBox( $params );
833         }
834
835         /**
836          * Get a labelled checkbox to configure a boolean variable.
837          *
838          * @param $params Array
839          *    Parameters are:
840          *      var:        The variable to be configured (required)
841          *      label:      The message name for the label (required)
842          *      attribs:    Additional attributes for the input element (optional)
843          *      controlName: The name for the input element (optional)
844          *      value:      The current value of the variable (optional)
845          *      help:           The html for the help text (optional)
846          */
847         public function getCheckBox( $params ) {
848                 if ( !isset( $params['controlName'] ) ) {
849                         $params['controlName'] = 'config_' . $params['var'];
850                 }
851
852                 if ( !isset( $params['value'] ) ) {
853                         $params['value'] = $this->getVar( $params['var'] );
854                 }
855
856                 if ( !isset( $params['attribs'] ) ) {
857                         $params['attribs'] = array();
858                 }
859                 if ( !isset( $params['help'] ) ) {
860                         $params['help'] = "";
861                 }
862                 if( isset( $params['rawtext'] ) ) {
863                         $labelText = $params['rawtext'];
864                 } else {
865                         $labelText = $this->parse( wfMsg( $params['label'] ) );
866                 }
867
868                 return
869                         "<div class=\"config-input-check\">\n" .
870                         $params['help'] .
871                         "<label>\n" .
872                         Xml::check(
873                                 $params['controlName'],
874                                 $params['value'],
875                                 $params['attribs'] + array(
876                                         'id' => $params['controlName'],
877                                         'tabindex' => $this->nextTabIndex(),
878                                 )
879                         ) .
880                         $labelText . "\n" .
881                         "</label>\n" .
882                         "</div>\n";
883         }
884
885         /**
886          * Get a set of labelled radio buttons.
887          *
888          * @param $params Array
889          *    Parameters are:
890          *      var:            The variable to be configured (required)
891          *      label:          The message name for the label (required)
892          *      itemLabelPrefix: The message name prefix for the item labels (required)
893          *      values:         List of allowed values (required)
894          *      itemAttribs     Array of attribute arrays, outer key is the value name (optional)
895          *      commonAttribs   Attribute array applied to all items
896          *      controlName:    The name for the input element (optional)
897          *      value:          The current value of the variable (optional)
898          *      help:           The html for the help text (optional)
899          */
900         public function getRadioSet( $params ) {
901                 if ( !isset( $params['controlName']  ) ) {
902                         $params['controlName'] = 'config_' . $params['var'];
903                 }
904
905                 if ( !isset( $params['value'] ) ) {
906                         $params['value'] = $this->getVar( $params['var'] );
907                 }
908
909                 if ( !isset( $params['label'] ) ) {
910                         $label = '';
911                 } else {
912                         $label = $params['label'];
913                 }
914                 if ( !isset( $params['help'] ) ) {
915                         $params['help'] = "";
916                 }
917                 $s = "<ul>\n";
918                 foreach ( $params['values'] as $value ) {
919                         $itemAttribs = array();
920
921                         if ( isset( $params['commonAttribs'] ) ) {
922                                 $itemAttribs = $params['commonAttribs'];
923                         }
924
925                         if ( isset( $params['itemAttribs'][$value] ) ) {
926                                 $itemAttribs = $params['itemAttribs'][$value] + $itemAttribs;
927                         }
928
929                         $checked = $value == $params['value'];
930                         $id = $params['controlName'] . '_' . $value;
931                         $itemAttribs['id'] = $id;
932                         $itemAttribs['tabindex'] = $this->nextTabIndex();
933
934                         $s .=
935                                 '<li>' .
936                                 Xml::radio( $params['controlName'], $value, $checked, $itemAttribs ) .
937                                 '&#160;' .
938                                 Xml::tags( 'label', array( 'for' => $id ), $this->parse(
939                                         wfMsgNoTrans( $params['itemLabelPrefix'] . strtolower( $value ) )
940                                 ) ) .
941                                 "</li>\n";
942                 }
943
944                 $s .= "</ul>\n";
945
946                 return $this->label( $label, $params['controlName'], $s, $params['help'] );
947         }
948
949         /**
950          * Output an error or warning box using a Status object.
951          */
952         public function showStatusBox( $status ) {
953                 if( !$status->isGood() ) {
954                         $text = $status->getWikiText();
955
956                         if( $status->isOk() ) {
957                                 $box = $this->getWarningBox( $text );
958                         } else {
959                                 $box = $this->getErrorBox( $text );
960                         }
961
962                         $this->output->addHTML( $box );
963                 }
964         }
965
966         /**
967          * Convenience function to set variables based on form data.
968          * Assumes that variables containing "password" in the name are (potentially
969          * fake) passwords.
970          *
971          * @param $varNames Array
972          * @param $prefix String: the prefix added to variables to obtain form names
973          */
974         public function setVarsFromRequest( $varNames, $prefix = 'config_' ) {
975                 $newValues = array();
976
977                 foreach ( $varNames as $name ) {
978                         $value = trim( $this->request->getVal( $prefix . $name ) );
979                         $newValues[$name] = $value;
980
981                         if ( $value === null ) {
982                                 // Checkbox?
983                                 $this->setVar( $name, false );
984                         } else {
985                                 if ( stripos( $name, 'password' ) !== false ) {
986                                         $this->setPassword( $name, $value );
987                                 } else {
988                                         $this->setVar( $name, $value );
989                                 }
990                         }
991                 }
992
993                 return $newValues;
994         }
995
996         /**
997          * Helper for Installer::docLink()
998          */
999         protected function getDocUrl( $page ) {
1000                 $url = "{$_SERVER['PHP_SELF']}?page=" . urlencode( $page );
1001
1002                 if ( in_array( $this->currentPageName, $this->pageSequence ) ) {
1003                         $url .= '&lastPage=' . urlencode( $this->currentPageName );
1004                 }
1005
1006                 return $url;
1007         }
1008
1009         /**
1010          * Extension tag hook for a documentation link.
1011          */
1012         public function docLink( $linkText, $attribs, $parser ) {
1013                 $url = $this->getDocUrl( $attribs['href'] );
1014                 return '<a href="' . htmlspecialchars( $url ) . '">' .
1015                         htmlspecialchars( $linkText ) .
1016                         '</a>';
1017         }
1018         
1019         /**
1020          * Helper for "Download LocalSettings" link on WebInstall_Complete
1021          * @return String Html for download link
1022          */
1023         public function downloadLinkHook( $text, $attribs, $parser  ) {
1024                 $img = Html::element( 'img', array(
1025                         'src' => '../skins/common/images/download-32.png',
1026                         'width' => '32',
1027                         'height' => '32',
1028                 ) );
1029                 $anchor = Html::rawElement( 'a',
1030                         array( 'href' => $this->getURL( array( 'localsettings' => 1 ) ) ),
1031                         $img . ' ' . wfMsgHtml( 'config-download-localsettings' ) );
1032                 return Html::rawElement( 'div', array( 'class' => 'config-download-link' ), $anchor );
1033         }
1034 }