Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
1.50% |
7 / 467 |
|
0.00% |
0 / 48 |
CRAP | |
0.00% |
0 / 1 |
WebInstaller | |
1.50% |
7 / 467 |
|
0.00% |
0 / 48 |
18338.40 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
execute | |
0.00% |
0 / 64 |
|
0.00% |
0 / 1 |
600 | |||
getLowestUnhappy | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
startSession | |
0.00% |
0 / 16 |
|
0.00% |
0 / 1 |
42 | |||
getFingerprint | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
12 | |||
showError | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
6 | |||
errorHandler | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
finish | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
2 | |||
reset | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
getUrl | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
6 | |||
getPageByName | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
getSession | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
setSession | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
nextTabIndex | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
setupLanguage | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
12 | |||
getAcceptLanguage | |
87.50% |
7 / 8 |
|
0.00% |
0 / 1 |
3.02 | |||
startPageWrapper | |
0.00% |
0 / 20 |
|
0.00% |
0 / 1 |
20 | |||
getPageListItem | |
0.00% |
0 / 25 |
|
0.00% |
0 / 1 |
30 | |||
endPageWrapper | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
2 | |||
getHelpBox | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
2 | |||
getInfoBox | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
6 | |||
showSuccess | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
2 | |||
showMessage | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
2 | |||
showWarning | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
2 | |||
showStatusMessage | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
12 | |||
label | |
0.00% |
0 / 18 |
|
0.00% |
0 / 1 |
12 | |||
getTextBox | |
0.00% |
0 / 24 |
|
0.00% |
0 / 1 |
30 | |||
getTextArea | |
0.00% |
0 / 24 |
|
0.00% |
0 / 1 |
30 | |||
getPasswordBox | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
12 | |||
addClassAttrib | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
getCheckBox | |
0.00% |
0 / 34 |
|
0.00% |
0 / 1 |
42 | |||
getRadioSet | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
20 | |||
getRadioElements | |
0.00% |
0 / 33 |
|
0.00% |
0 / 1 |
56 | |||
showStatusBox | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
12 | |||
setVarsFromRequest | |
0.00% |
0 / 12 |
|
0.00% |
0 / 1 |
42 | |||
getDocUrl | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
makeLinkItem | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
getLocalSettingsLocation | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
envCheckPath | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
20 | |||
detectWebPaths | |
0.00% |
0 / 18 |
|
0.00% |
0 / 1 |
20 | |||
envGetDefaultServer | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
getDefaultServer | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
outputLS | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
6 | |||
outputCss | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
getPhpErrors | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
needsUpgrade | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
doUpgrade | |
0.00% |
0 / 16 |
|
0.00% |
0 / 1 |
6 | |||
outputHandler | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | /** |
3 | * Core installer web interface. |
4 | * |
5 | * This program is free software; you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License as published by |
7 | * the Free Software Foundation; either version 2 of the License, or |
8 | * (at your option) any later version. |
9 | * |
10 | * This program is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | * GNU General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU General Public License along |
16 | * with this program; if not, write to the Free Software Foundation, Inc., |
17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
18 | * http://www.gnu.org/copyleft/gpl.html |
19 | * |
20 | * @file |
21 | * @ingroup Installer |
22 | */ |
23 | |
24 | namespace MediaWiki\Installer; |
25 | |
26 | use Exception; |
27 | use HtmlArmor; |
28 | use MediaWiki\Context\RequestContext; |
29 | use MediaWiki\Html\Html; |
30 | use MediaWiki\Installer\Task\TaskFactory; |
31 | use MediaWiki\Installer\Task\TaskList; |
32 | use MediaWiki\Installer\Task\TaskRunner; |
33 | use MediaWiki\Languages\LanguageNameUtils; |
34 | use MediaWiki\MediaWikiServices; |
35 | use MediaWiki\Message\Message; |
36 | use MediaWiki\Request\WebRequest; |
37 | use MediaWiki\Status\Status; |
38 | use MediaWiki\Xml\Xml; |
39 | |
40 | /** |
41 | * Class for the core installer web interface. |
42 | * |
43 | * @ingroup Installer |
44 | * @since 1.17 |
45 | */ |
46 | class WebInstaller extends Installer { |
47 | |
48 | /** |
49 | * @var WebInstallerOutput |
50 | */ |
51 | public $output; |
52 | |
53 | /** |
54 | * WebRequest object. |
55 | * |
56 | * @var WebRequest |
57 | */ |
58 | public $request; |
59 | |
60 | /** |
61 | * Cached session array. |
62 | * |
63 | * @var array[] |
64 | */ |
65 | protected $session; |
66 | |
67 | /** |
68 | * Captured PHP error text. Temporary. |
69 | * |
70 | * @var string[] |
71 | */ |
72 | protected $phpErrors; |
73 | |
74 | /** |
75 | * The main sequence of page names. These will be displayed in turn. |
76 | * |
77 | * To add a new installer page: |
78 | * * Add it to this WebInstaller::$pageSequence property |
79 | * * Add a "config-page-<name>" message |
80 | * * Add a "WebInstaller<name>" class |
81 | * |
82 | * @var string[] |
83 | */ |
84 | public $pageSequence = [ |
85 | 'Language', |
86 | 'ExistingWiki', |
87 | 'Welcome', |
88 | 'DBConnect', |
89 | 'Upgrade', |
90 | 'DBSettings', |
91 | 'Name', |
92 | 'Options', |
93 | 'Install', |
94 | 'Complete', |
95 | ]; |
96 | |
97 | /** |
98 | * Out of sequence pages, selectable by the user at any time. |
99 | * |
100 | * @var string[] |
101 | */ |
102 | protected $otherPages = [ |
103 | 'Restart', |
104 | 'ReleaseNotes', |
105 | 'Copying', |
106 | 'UpgradeDoc', // Can't use Upgrade due to Upgrade step |
107 | ]; |
108 | |
109 | /** |
110 | * Array of pages which have declared that they have been submitted, have validated |
111 | * their input, and need no further processing. |
112 | * |
113 | * @var bool[] |
114 | */ |
115 | protected $happyPages; |
116 | |
117 | /** |
118 | * List of "skipped" pages. These are pages that will automatically continue |
119 | * to the next page on any GET request. To avoid breaking the "back" button, |
120 | * they need to be skipped during a back operation. |
121 | * |
122 | * @var bool[] |
123 | */ |
124 | protected $skippedPages; |
125 | |
126 | /** |
127 | * Flag indicating that session data may have been lost. |
128 | * |
129 | * @var bool |
130 | */ |
131 | public $showSessionWarning = false; |
132 | |
133 | /** |
134 | * Numeric index of the page we're on |
135 | * |
136 | * @var int |
137 | */ |
138 | protected $tabIndex = 1; |
139 | |
140 | /** |
141 | * Numeric index of the help box |
142 | * |
143 | * @var int |
144 | */ |
145 | protected $helpBoxId = 1; |
146 | |
147 | /** |
148 | * Name of the page we're on |
149 | * |
150 | * @var string |
151 | */ |
152 | protected $currentPageName; |
153 | |
154 | public function __construct( WebRequest $request ) { |
155 | parent::__construct(); |
156 | $this->output = new WebInstallerOutput( $this ); |
157 | $this->request = $request; |
158 | } |
159 | |
160 | /** |
161 | * Main entry point. |
162 | * |
163 | * @param array[] $session Initial session array |
164 | * |
165 | * @return array[] New session array |
166 | */ |
167 | public function execute( array $session ) { |
168 | $this->session = $session; |
169 | |
170 | if ( isset( $session['settings'] ) ) { |
171 | $this->settings = $session['settings'] + $this->settings; |
172 | // T187586 MediaWikiServices works with globals |
173 | foreach ( $this->settings as $key => $val ) { |
174 | $GLOBALS[$key] = $val; |
175 | } |
176 | } |
177 | |
178 | $this->setupLanguage(); |
179 | |
180 | if ( ( $this->getVar( '_InstallDone' ) || $this->getVar( '_UpgradeDone' ) ) |
181 | && $this->request->getVal( 'localsettings' ) |
182 | ) { |
183 | $this->outputLS(); |
184 | return $this->session; |
185 | } |
186 | |
187 | $isCSS = $this->request->getCheck( 'css' ); |
188 | if ( $isCSS ) { |
189 | $this->outputCss(); |
190 | return $this->session; |
191 | } |
192 | |
193 | $this->happyPages = $session['happyPages'] ?? []; |
194 | |
195 | $this->skippedPages = $session['skippedPages'] ?? []; |
196 | |
197 | $lowestUnhappy = $this->getLowestUnhappy(); |
198 | |
199 | # Get the page name. |
200 | $pageName = $this->request->getVal( 'page', '' ); |
201 | |
202 | if ( in_array( $pageName, $this->otherPages ) ) { |
203 | # Out of sequence |
204 | $pageId = false; |
205 | $page = $this->getPageByName( $pageName ); |
206 | } else { |
207 | # Main sequence |
208 | if ( !$pageName || !in_array( $pageName, $this->pageSequence ) ) { |
209 | $pageId = $lowestUnhappy; |
210 | } else { |
211 | $pageId = array_search( $pageName, $this->pageSequence ); |
212 | } |
213 | |
214 | # If necessary, move back to the lowest-numbered unhappy page |
215 | if ( $pageId > $lowestUnhappy ) { |
216 | $pageId = $lowestUnhappy; |
217 | if ( $lowestUnhappy == 0 ) { |
218 | # Knocked back to start, possible loss of session data. |
219 | $this->showSessionWarning = true; |
220 | } |
221 | } |
222 | |
223 | $pageName = $this->pageSequence[$pageId]; |
224 | $page = $this->getPageByName( $pageName ); |
225 | } |
226 | |
227 | # If a back button was submitted, go back without submitting the form data. |
228 | if ( $this->request->wasPosted() && $this->request->getBool( 'submit-back' ) ) { |
229 | if ( $this->request->getVal( 'lastPage' ) ) { |
230 | $nextPage = $this->request->getVal( 'lastPage' ); |
231 | } elseif ( $pageId !== false ) { |
232 | # Main sequence page |
233 | # Skip the skipped pages |
234 | $nextPageId = $pageId; |
235 | |
236 | do { |
237 | $nextPageId--; |
238 | $nextPage = $this->pageSequence[$nextPageId]; |
239 | } while ( isset( $this->skippedPages[$nextPage] ) ); |
240 | } else { |
241 | $nextPage = $this->pageSequence[$lowestUnhappy]; |
242 | } |
243 | |
244 | $this->output->redirect( $this->getUrl( [ 'page' => $nextPage ] ) ); |
245 | |
246 | return $this->finish(); |
247 | } |
248 | |
249 | # Execute the page. |
250 | $this->currentPageName = $page->getName(); |
251 | $this->startPageWrapper( $pageName ); |
252 | |
253 | if ( $page->isSlow() ) { |
254 | $this->disableTimeLimit(); |
255 | } |
256 | |
257 | $result = $page->execute(); |
258 | |
259 | $this->endPageWrapper(); |
260 | |
261 | if ( $result == 'skip' ) { |
262 | # Page skipped without explicit submission. |
263 | # Skip it when we click "back" so that we don't just go forward again. |
264 | $this->skippedPages[$pageName] = true; |
265 | $result = 'continue'; |
266 | } else { |
267 | unset( $this->skippedPages[$pageName] ); |
268 | } |
269 | |
270 | # If it was posted, the page can request a continue to the next page. |
271 | if ( $result === 'continue' && !$this->output->headerDone() ) { |
272 | if ( $pageId !== false ) { |
273 | $this->happyPages[$pageId] = true; |
274 | } |
275 | |
276 | $lowestUnhappy = $this->getLowestUnhappy(); |
277 | |
278 | if ( $this->request->getVal( 'lastPage' ) ) { |
279 | $nextPage = $this->request->getVal( 'lastPage' ); |
280 | } elseif ( $pageId !== false ) { |
281 | $nextPage = $this->pageSequence[$pageId + 1]; |
282 | } else { |
283 | $nextPage = $this->pageSequence[$lowestUnhappy]; |
284 | } |
285 | |
286 | if ( array_search( $nextPage, $this->pageSequence ) > $lowestUnhappy ) { |
287 | $nextPage = $this->pageSequence[$lowestUnhappy]; |
288 | } |
289 | |
290 | $this->output->redirect( $this->getUrl( [ 'page' => $nextPage ] ) ); |
291 | } |
292 | |
293 | return $this->finish(); |
294 | } |
295 | |
296 | /** |
297 | * Find the next page in sequence that hasn't been completed |
298 | * @return int |
299 | */ |
300 | public function getLowestUnhappy() { |
301 | if ( count( $this->happyPages ) == 0 ) { |
302 | return 0; |
303 | } else { |
304 | return max( array_keys( $this->happyPages ) ) + 1; |
305 | } |
306 | } |
307 | |
308 | /** |
309 | * Start the PHP session. This may be called before execute() to start the PHP session. |
310 | * |
311 | * @throws Exception |
312 | * @return bool |
313 | */ |
314 | public function startSession() { |
315 | if ( wfIniGetBool( 'session.auto_start' ) || session_id() ) { |
316 | // Done already |
317 | return true; |
318 | } |
319 | |
320 | // Use secure cookies if we are on HTTPS |
321 | $options = []; |
322 | if ( $this->request->getProtocol() === 'https' ) { |
323 | $options['cookie_secure'] = '1'; |
324 | } |
325 | |
326 | $this->phpErrors = []; |
327 | set_error_handler( [ $this, 'errorHandler' ] ); |
328 | try { |
329 | session_name( 'mw_installer_session' ); |
330 | session_start( $options ); |
331 | } catch ( Exception $e ) { |
332 | restore_error_handler(); |
333 | throw $e; |
334 | } |
335 | restore_error_handler(); |
336 | |
337 | if ( $this->phpErrors ) { |
338 | return false; |
339 | } |
340 | |
341 | return true; |
342 | } |
343 | |
344 | /** |
345 | * Get a hash of data identifying this MW installation. |
346 | * |
347 | * This is used by mw-config/index.php to prevent multiple installations of MW |
348 | * on the same cookie domain from interfering with each other. |
349 | * |
350 | * @return string |
351 | */ |
352 | public function getFingerprint() { |
353 | // Get the base URL of the installation |
354 | $url = $this->request->getFullRequestURL(); |
355 | if ( preg_match( '!^(.*\?)!', $url, $m ) ) { |
356 | // Trim query string |
357 | $url = $m[1]; |
358 | } |
359 | if ( preg_match( '!^(.*)/[^/]*/[^/]*$!', $url, $m ) ) { |
360 | // This... seems to try to get the base path from |
361 | // the /mw-config/index.php. Kinda scary though? |
362 | $url = $m[1]; |
363 | } |
364 | |
365 | return md5( serialize( [ |
366 | 'local path' => dirname( __DIR__ ), |
367 | 'url' => $url, |
368 | 'version' => MW_VERSION |
369 | ] ) ); |
370 | } |
371 | |
372 | public function showError( $msg, ...$params ) { |
373 | if ( !( $msg instanceof Message ) ) { |
374 | $msg = wfMessage( |
375 | $msg, |
376 | array_map( 'htmlspecialchars', $params ) |
377 | ); |
378 | } |
379 | $text = $msg->useDatabase( false )->parse(); |
380 | $box = Html::errorBox( $text, '', 'config-error-box' ); |
381 | $this->output->addHTML( $box ); |
382 | } |
383 | |
384 | /** |
385 | * Temporary error handler for session start debugging. |
386 | * |
387 | * @param int $errno Unused |
388 | * @param string $errstr |
389 | */ |
390 | public function errorHandler( $errno, $errstr ) { |
391 | $this->phpErrors[] = $errstr; |
392 | } |
393 | |
394 | /** |
395 | * Clean up from execute() |
396 | * |
397 | * @return array[] |
398 | */ |
399 | public function finish() { |
400 | $this->output->output(); |
401 | |
402 | $this->session['happyPages'] = $this->happyPages; |
403 | $this->session['skippedPages'] = $this->skippedPages; |
404 | $this->session['settings'] = $this->settings; |
405 | |
406 | return $this->session; |
407 | } |
408 | |
409 | /** |
410 | * We're restarting the installation, reset the session, happyPages, etc |
411 | */ |
412 | public function reset() { |
413 | $this->session = []; |
414 | $this->happyPages = []; |
415 | $this->settings = []; |
416 | } |
417 | |
418 | /** |
419 | * Get a URL for submission back to the same script. |
420 | * |
421 | * @param string[] $query |
422 | * |
423 | * @return string |
424 | */ |
425 | public function getUrl( $query = [] ) { |
426 | $url = $this->request->getRequestURL(); |
427 | # Remove existing query |
428 | $url = preg_replace( '/\?.*$/', '', $url ); |
429 | |
430 | if ( $query ) { |
431 | $url .= '?' . wfArrayToCgi( $query ); |
432 | } |
433 | |
434 | return $url; |
435 | } |
436 | |
437 | /** |
438 | * Get a WebInstallerPage by name. |
439 | * |
440 | * @param string $pageName |
441 | * @return WebInstallerPage |
442 | */ |
443 | public function getPageByName( $pageName ) { |
444 | $pageClass = 'MediaWiki\\Installer\\WebInstaller' . $pageName; |
445 | |
446 | return new $pageClass( $this ); |
447 | } |
448 | |
449 | /** |
450 | * Get a session variable. |
451 | * |
452 | * @param string $name |
453 | * @param array|null $default |
454 | * |
455 | * @return array|null |
456 | */ |
457 | public function getSession( $name, $default = null ) { |
458 | return $this->session[$name] ?? $default; |
459 | } |
460 | |
461 | /** |
462 | * Set a session variable. |
463 | * |
464 | * @param string $name Key for the variable |
465 | * @param mixed $value |
466 | */ |
467 | public function setSession( $name, $value ) { |
468 | $this->session[$name] = $value; |
469 | } |
470 | |
471 | /** |
472 | * Get the next tabindex attribute value. |
473 | * |
474 | * @return int |
475 | */ |
476 | public function nextTabIndex() { |
477 | return $this->tabIndex++; |
478 | } |
479 | |
480 | /** |
481 | * Initializes language-related variables. |
482 | */ |
483 | public function setupLanguage() { |
484 | global $wgLang, $wgLanguageCode; |
485 | |
486 | if ( $this->getSession( 'test' ) === null && !$this->request->wasPosted() ) { |
487 | $wgLanguageCode = $this->getAcceptLanguage(); |
488 | $wgLang = MediaWikiServices::getInstance()->getLanguageFactory() |
489 | ->getLanguage( $wgLanguageCode ); |
490 | RequestContext::getMain()->setLanguage( $wgLang ); |
491 | $this->setVar( 'wgLanguageCode', $wgLanguageCode ); |
492 | $this->setVar( '_UserLang', $wgLanguageCode ); |
493 | } else { |
494 | $wgLanguageCode = $this->getVar( 'wgLanguageCode' ); |
495 | } |
496 | } |
497 | |
498 | /** |
499 | * Retrieves MediaWiki language from Accept-Language HTTP header. |
500 | * |
501 | * @return string |
502 | * @return-taint none It can only return a known-good code. |
503 | */ |
504 | public function getAcceptLanguage() { |
505 | global $wgLanguageCode; |
506 | |
507 | $mwLanguages = MediaWikiServices::getInstance() |
508 | ->getLanguageNameUtils() |
509 | ->getLanguageNames( LanguageNameUtils::AUTONYMS, LanguageNameUtils::SUPPORTED ); |
510 | $headerLanguages = array_keys( $this->request->getAcceptLang() ); |
511 | |
512 | foreach ( $headerLanguages as $lang ) { |
513 | if ( isset( $mwLanguages[$lang] ) ) { |
514 | return $lang; |
515 | } |
516 | } |
517 | |
518 | return $wgLanguageCode; |
519 | } |
520 | |
521 | /** |
522 | * Called by execute() before page output starts, to show a page list. |
523 | * |
524 | * @param string $currentPageName |
525 | */ |
526 | private function startPageWrapper( $currentPageName ) { |
527 | $s = "<div class=\"config-page-wrapper\">\n"; |
528 | $s .= "<div class=\"config-page\">\n"; |
529 | $s .= "<div class=\"config-page-list cdx-card\"><span class=\"cdx-card__text\">"; |
530 | $s .= "<span class=\"cdx-card__text__description\"><ul>\n"; |
531 | $lastHappy = -1; |
532 | |
533 | foreach ( $this->pageSequence as $id => $pageName ) { |
534 | $happy = !empty( $this->happyPages[$id] ); |
535 | $s .= $this->getPageListItem( |
536 | $pageName, |
537 | $happy || $lastHappy == $id - 1, |
538 | $currentPageName |
539 | ); |
540 | |
541 | if ( $happy ) { |
542 | $lastHappy = $id; |
543 | } |
544 | } |
545 | |
546 | $s .= "</ul><br/><ul>\n"; |
547 | $s .= $this->getPageListItem( 'Restart', true, $currentPageName ); |
548 | // End list pane |
549 | $s .= "</ul></span></span></div>\n"; |
550 | |
551 | // Messages: |
552 | // config-page-language, config-page-welcome, config-page-dbconnect, config-page-upgrade, |
553 | // config-page-dbsettings, config-page-name, config-page-options, config-page-install, |
554 | // config-page-complete, config-page-restart, config-page-releasenotes, |
555 | // config-page-copying, config-page-upgradedoc, config-page-existingwiki |
556 | $s .= Html::element( 'h2', [], |
557 | wfMessage( 'config-page-' . strtolower( $currentPageName ) )->text() ); |
558 | |
559 | $this->output->addHTMLNoFlush( $s ); |
560 | } |
561 | |
562 | /** |
563 | * Get a list item for the page list. |
564 | * |
565 | * @param string $pageName |
566 | * @param bool $enabled |
567 | * @param string $currentPageName |
568 | * |
569 | * @return string |
570 | */ |
571 | private function getPageListItem( $pageName, $enabled, $currentPageName ) { |
572 | $s = "<li class=\"config-page-list-item\">"; |
573 | |
574 | // Messages: |
575 | // config-page-language, config-page-welcome, config-page-dbconnect, config-page-upgrade, |
576 | // config-page-dbsettings, config-page-name, config-page-options, config-page-install, |
577 | // config-page-complete, config-page-restart, config-page-releasenotes, |
578 | // config-page-copying, config-page-upgradedoc, config-page-existingwiki |
579 | $name = wfMessage( 'config-page-' . strtolower( $pageName ) )->text(); |
580 | |
581 | if ( $enabled ) { |
582 | $query = [ 'page' => $pageName ]; |
583 | |
584 | if ( !in_array( $pageName, $this->pageSequence ) ) { |
585 | if ( in_array( $currentPageName, $this->pageSequence ) ) { |
586 | $query['lastPage'] = $currentPageName; |
587 | } |
588 | |
589 | $link = Html::element( 'a', |
590 | [ |
591 | 'href' => $this->getUrl( $query ) |
592 | ], |
593 | $name |
594 | ); |
595 | } else { |
596 | $link = htmlspecialchars( $name ); |
597 | } |
598 | |
599 | if ( $pageName == $currentPageName ) { |
600 | $s .= "<span class=\"config-page-current\">$link</span>"; |
601 | } else { |
602 | $s .= $link; |
603 | } |
604 | } else { |
605 | $s .= Html::element( 'span', |
606 | [ |
607 | 'class' => 'config-page-disabled' |
608 | ], |
609 | $name |
610 | ); |
611 | } |
612 | |
613 | $s .= "</li>\n"; |
614 | |
615 | return $s; |
616 | } |
617 | |
618 | /** |
619 | * Output some stuff after a page is finished. |
620 | */ |
621 | private function endPageWrapper() { |
622 | $this->output->addHTMLNoFlush( |
623 | "<div class=\"visualClear\"></div>\n" . |
624 | "</div>\n" . |
625 | "<div class=\"visualClear\"></div>\n" . |
626 | "</div>" ); |
627 | } |
628 | |
629 | /** |
630 | * Get small text indented help for a preceding form field. |
631 | * Parameters like wfMessage(). |
632 | * |
633 | * @param string $msg Message key |
634 | * @param string|int|float ...$params Message parameters |
635 | * @return string HTML |
636 | * @return-taint escaped |
637 | */ |
638 | public function getHelpBox( $msg, ...$params ) { |
639 | $params = array_map( 'htmlspecialchars', $params ); |
640 | $text = wfMessage( $msg, $params )->useDatabase( false )->plain(); |
641 | $html = $this->parse( $text, true ); |
642 | |
643 | return "<div class=\"config-help-field-container\">\n" . |
644 | "<a class=\"config-help-field-hint\" title=\"" . |
645 | wfMessage( 'config-help-tooltip' )->escaped() . "\">ℹ️ " . |
646 | wfMessage( 'config-help' )->escaped() . "</a>\n" . |
647 | "<div class=\"config-help-field-content config-help-field-content-hidden " . |
648 | "cdx-message cdx-message--block cdx-message--notice\" style=\"margin: 10px\">" . |
649 | "<div class=\"cdx-message__content\">" . $html . "</div></div>\n" . |
650 | "</div>\n"; |
651 | } |
652 | |
653 | /** |
654 | * Get HTML for an information message box. |
655 | * |
656 | * @param string|HtmlArmor $text Wikitext to be parsed (from Message::plain) or raw HTML. |
657 | * @return string HTML |
658 | */ |
659 | public function getInfoBox( $text ) { |
660 | $html = ( $text instanceof HtmlArmor ) ? |
661 | HtmlArmor::getHtml( $text ) : |
662 | $this->parse( $text, true ); |
663 | return '<div class="cdx-message cdx-message--block cdx-message--notice">' . |
664 | '<span class="cdx-message__icon"></span><div class="cdx-message__content">' . |
665 | '<p><strong>' . wfMessage( 'config-information' )->escaped() . '</strong></p>' . |
666 | $html . |
667 | "</div></div>\n"; |
668 | } |
669 | |
670 | public function showSuccess( $msg, ...$params ) { |
671 | $html = '<div class="cdx-message cdx-message--block cdx-message--success">' . |
672 | '<span class="cdx-message__icon"></span><div class="cdx-message__content">' . |
673 | $this->parse( wfMessage( $msg, $params )->useDatabase( false )->plain() ) . |
674 | "</div></div>\n"; |
675 | $this->output->addHTML( $html ); |
676 | } |
677 | |
678 | public function showMessage( $msg, ...$params ) { |
679 | $html = '<div class="cdx-message cdx-message--block cdx-message--notice">' . |
680 | '<span class="cdx-message__icon"></span><div class="cdx-message__content">' . |
681 | $this->parse( wfMessage( $msg, $params )->useDatabase( false )->plain() ) . |
682 | "</div></div>\n"; |
683 | $this->output->addHTML( $html ); |
684 | } |
685 | |
686 | public function showWarning( $msg, ...$params ) { |
687 | $html = '<div class="cdx-message cdx-message--block cdx-message--warning">' . |
688 | '<span class="cdx-message__icon"></span><div class="cdx-message__content">' . |
689 | $this->parse( wfMessage( $msg, $params )->useDatabase( false )->plain() ) . |
690 | "</div></div>\n"; |
691 | $this->output->addHTML( $html ); |
692 | } |
693 | |
694 | public function showStatusMessage( Status $status ) { |
695 | // Show errors at the top in web installer to make them easier to notice |
696 | foreach ( $status->getMessages( 'error' ) as $msg ) { |
697 | $this->showWarning( $msg ); |
698 | } |
699 | foreach ( $status->getMessages( 'warning' ) as $msg ) { |
700 | $this->showWarning( $msg ); |
701 | } |
702 | } |
703 | |
704 | /** |
705 | * Label a control by wrapping a config-input div around it and putting a |
706 | * label before it. |
707 | * |
708 | * @param string $msg |
709 | * @param string|false $forId |
710 | * @param string $contents HTML |
711 | * @param string $helpData |
712 | * @return string HTML |
713 | * @return-taint escaped |
714 | */ |
715 | public function label( $msg, $forId, $contents, $helpData = "" ) { |
716 | if ( strval( $msg ) == '' ) { |
717 | $labelText = "\u{00A0}"; |
718 | } else { |
719 | $labelText = wfMessage( $msg )->escaped(); |
720 | } |
721 | |
722 | $attributes = [ 'class' => 'config-label' ]; |
723 | |
724 | if ( $forId ) { |
725 | $attributes['for'] = $forId; |
726 | } |
727 | |
728 | return "<div class=\"config-block\">\n" . |
729 | " <div class=\"config-block-label\">\n" . |
730 | Xml::tags( 'label', |
731 | $attributes, |
732 | $labelText |
733 | ) . "\n" . |
734 | $helpData . |
735 | " </div>\n" . |
736 | " <div class=\"config-block-elements\">\n" . |
737 | $contents . |
738 | " </div>\n" . |
739 | "</div>\n"; |
740 | } |
741 | |
742 | /** |
743 | * Get a labelled text box to configure a variable. |
744 | * |
745 | * @param mixed[] $params |
746 | * Parameters are: |
747 | * var: The variable to be configured (required) |
748 | * label: The message name for the label (required) |
749 | * attribs: Additional attributes for the input element (optional) |
750 | * controlName: The name for the input element (optional) |
751 | * value: The current value of the variable (optional) |
752 | * help: The html for the help text (optional) |
753 | * |
754 | * @return string HTML |
755 | * @return-taint escaped |
756 | */ |
757 | public function getTextBox( $params ) { |
758 | if ( !isset( $params['controlName'] ) ) { |
759 | $params['controlName'] = 'config_' . $params['var']; |
760 | } |
761 | |
762 | if ( !isset( $params['value'] ) ) { |
763 | $params['value'] = $this->getVar( $params['var'] ); |
764 | } |
765 | |
766 | if ( !isset( $params['attribs'] ) ) { |
767 | $params['attribs'] = []; |
768 | } |
769 | if ( !isset( $params['help'] ) ) { |
770 | $params['help'] = ""; |
771 | } |
772 | |
773 | return $this->label( |
774 | $params['label'], |
775 | $params['controlName'], |
776 | "<div class=\"cdx-text-input\">" . |
777 | Xml::input( |
778 | $params['controlName'], |
779 | 30, // intended to be overridden by CSS |
780 | $params['value'], |
781 | $params['attribs'] + [ |
782 | 'id' => $params['controlName'], |
783 | 'class' => 'cdx-text-input__input', |
784 | 'tabindex' => $this->nextTabIndex() |
785 | ] |
786 | ) . "</div>", |
787 | $params['help'] |
788 | ); |
789 | } |
790 | |
791 | /** |
792 | * Get a labelled textarea to configure a variable |
793 | * |
794 | * @param mixed[] $params |
795 | * Parameters are: |
796 | * var: The variable to be configured (required) |
797 | * label: The message name for the label (required) |
798 | * attribs: Additional attributes for the input element (optional) |
799 | * controlName: The name for the input element (optional) |
800 | * value: The current value of the variable (optional) |
801 | * help: The html for the help text (optional) |
802 | * |
803 | * @return string |
804 | */ |
805 | public function getTextArea( $params ) { |
806 | if ( !isset( $params['controlName'] ) ) { |
807 | $params['controlName'] = 'config_' . $params['var']; |
808 | } |
809 | |
810 | if ( !isset( $params['value'] ) ) { |
811 | $params['value'] = $this->getVar( $params['var'] ); |
812 | } |
813 | |
814 | if ( !isset( $params['attribs'] ) ) { |
815 | $params['attribs'] = []; |
816 | } |
817 | if ( !isset( $params['help'] ) ) { |
818 | $params['help'] = ""; |
819 | } |
820 | |
821 | return $this->label( |
822 | $params['label'], |
823 | $params['controlName'], |
824 | Xml::textarea( |
825 | $params['controlName'], |
826 | $params['value'], |
827 | 30, |
828 | 5, |
829 | $params['attribs'] + [ |
830 | 'id' => $params['controlName'], |
831 | 'class' => 'config-input-text', |
832 | 'tabindex' => $this->nextTabIndex() |
833 | ] |
834 | ), |
835 | $params['help'] |
836 | ); |
837 | } |
838 | |
839 | /** |
840 | * Get a labelled password box to configure a variable. |
841 | * |
842 | * Implements password hiding |
843 | * @param mixed[] $params |
844 | * Parameters are: |
845 | * var: The variable to be configured (required) |
846 | * label: The message name for the label (required) |
847 | * attribs: Additional attributes for the input element (optional) |
848 | * controlName: The name for the input element (optional) |
849 | * value: The current value of the variable (optional) |
850 | * help: The html for the help text (optional) |
851 | * |
852 | * @return string HTML |
853 | * @return-taint escaped |
854 | */ |
855 | public function getPasswordBox( $params ) { |
856 | if ( !isset( $params['value'] ) ) { |
857 | $params['value'] = $this->getVar( $params['var'] ); |
858 | } |
859 | |
860 | if ( !isset( $params['attribs'] ) ) { |
861 | $params['attribs'] = []; |
862 | } |
863 | |
864 | $params['value'] = $this->getFakePassword( $params['value'] ); |
865 | $params['attribs']['type'] = 'password'; |
866 | |
867 | return $this->getTextBox( $params ); |
868 | } |
869 | |
870 | /** |
871 | * Add a class to an array of attributes. If the array already has a class, |
872 | * append the new class to the list. |
873 | * |
874 | * @param array &$attribs |
875 | * @param string $class |
876 | */ |
877 | private static function addClassAttrib( &$attribs, $class ) { |
878 | if ( isset( $attribs['class'] ) ) { |
879 | $attribs['class'] .= ' ' . $class; |
880 | } else { |
881 | $attribs['class'] = $class; |
882 | } |
883 | } |
884 | |
885 | /** |
886 | * Get a labelled checkbox to configure a boolean variable. |
887 | * |
888 | * @param mixed[] $params |
889 | * Parameters are: |
890 | * var: The variable to be configured (required) |
891 | * label: The message name for the label (required) |
892 | * labelAttribs:Additional attributes for the label element (optional) |
893 | * attribs: Additional attributes for the input element (optional) |
894 | * controlName: The name for the input element (optional) |
895 | * value: The current value of the variable (optional) |
896 | * help: The html for the help text (optional) |
897 | * |
898 | * @return string HTML |
899 | * @return-taint escaped |
900 | */ |
901 | public function getCheckBox( $params ) { |
902 | if ( !isset( $params['controlName'] ) ) { |
903 | $params['controlName'] = 'config_' . $params['var']; |
904 | } |
905 | |
906 | if ( !isset( $params['value'] ) ) { |
907 | $params['value'] = $this->getVar( $params['var'] ); |
908 | } |
909 | |
910 | if ( !isset( $params['attribs'] ) ) { |
911 | $params['attribs'] = []; |
912 | } |
913 | if ( !isset( $params['help'] ) ) { |
914 | $params['help'] = ""; |
915 | } |
916 | if ( !isset( $params['labelAttribs'] ) ) { |
917 | $params['labelAttribs'] = []; |
918 | } |
919 | $labelText = $params['rawtext'] ?? $this->parse( wfMessage( $params['label'] )->plain() ); |
920 | $labelText = '<span class="cdx-label__label__text"> ' . $labelText . '</span>'; |
921 | self::addClassAttrib( $params['attribs'], 'cdx-checkbox__input' ); |
922 | self::addClassAttrib( $params['labelAttribs'], 'cdx-label__label' ); |
923 | |
924 | return "<div class=\"cdx-checkbox\" style=\"margin-top: 12px; margin-bottom: 2px;\">" . |
925 | "<div class=\"cdx-checkbox__wrapper\">\n" . |
926 | Xml::check( |
927 | $params['controlName'], |
928 | $params['value'], |
929 | $params['attribs'] + [ |
930 | 'id' => $params['controlName'], |
931 | 'tabindex' => $this->nextTabIndex() |
932 | ] |
933 | ) . |
934 | "<span class=\"cdx-checkbox__icon\"></span>" . |
935 | "<div class=\"cdx-checkbox__label cdx-label\">" . |
936 | Html::rawElement( |
937 | 'label', |
938 | $params['labelAttribs'] + [ |
939 | 'for' => $params['controlName'] |
940 | ], |
941 | $labelText |
942 | ) . |
943 | "</div></div></div>\n" . $params['help']; |
944 | } |
945 | |
946 | /** |
947 | * Get a set of labelled radio buttons. |
948 | * |
949 | * @param mixed[] $params |
950 | * Parameters are: |
951 | * var: The variable to be configured (required) |
952 | * label: The message name for the label (required) |
953 | * itemLabelPrefix: The message name prefix for the item labels (required) |
954 | * itemLabels: List of message names to use for the item labels instead |
955 | * of itemLabelPrefix, keyed by values |
956 | * values: List of allowed values (required) |
957 | * itemAttribs: Array of attribute arrays, outer key is the value name (optional) |
958 | * commonAttribs: Attribute array applied to all items |
959 | * controlName: The name for the input element (optional) |
960 | * value: The current value of the variable (optional) |
961 | * help: The html for the help text (optional) |
962 | * |
963 | * @return string HTML |
964 | * @return-taint escaped |
965 | */ |
966 | public function getRadioSet( $params ) { |
967 | $items = $this->getRadioElements( $params ); |
968 | |
969 | $label = $params['label'] ?? ''; |
970 | |
971 | if ( !isset( $params['controlName'] ) ) { |
972 | $params['controlName'] = 'config_' . $params['var']; |
973 | } |
974 | |
975 | if ( !isset( $params['help'] ) ) { |
976 | $params['help'] = ""; |
977 | } |
978 | |
979 | $s = ""; |
980 | foreach ( $items as $item ) { |
981 | $s .= "$item\n"; |
982 | } |
983 | |
984 | return $this->label( $label, $params['controlName'], $s, $params['help'] ); |
985 | } |
986 | |
987 | /** |
988 | * Get a set of labelled radio buttons. You probably want to use getRadioSet(), not this. |
989 | * |
990 | * @see getRadioSet |
991 | * |
992 | * @param mixed[] $params |
993 | * @return string[] HTML |
994 | * @return-taint escaped |
995 | */ |
996 | public function getRadioElements( $params ) { |
997 | if ( !isset( $params['controlName'] ) ) { |
998 | $params['controlName'] = 'config_' . $params['var']; |
999 | } |
1000 | |
1001 | if ( !isset( $params['value'] ) ) { |
1002 | $params['value'] = $this->getVar( $params['var'] ); |
1003 | } |
1004 | |
1005 | $items = []; |
1006 | |
1007 | foreach ( $params['values'] as $value ) { |
1008 | $itemAttribs = []; |
1009 | |
1010 | if ( isset( $params['commonAttribs'] ) ) { |
1011 | $itemAttribs = $params['commonAttribs']; |
1012 | } |
1013 | |
1014 | if ( isset( $params['itemAttribs'][$value] ) ) { |
1015 | $itemAttribs = $params['itemAttribs'][$value] + $itemAttribs; |
1016 | } |
1017 | |
1018 | $checked = $value == $params['value']; |
1019 | $id = $params['controlName'] . '_' . $value; |
1020 | $itemAttribs['id'] = $id; |
1021 | $itemAttribs['tabindex'] = $this->nextTabIndex(); |
1022 | self::addClassAttrib( $itemAttribs, 'cdx-radio__input' ); |
1023 | |
1024 | $radioText = $this->parse( |
1025 | isset( $params['itemLabels'] ) ? |
1026 | wfMessage( $params['itemLabels'][$value] )->plain() : |
1027 | wfMessage( $params['itemLabelPrefix'] . strtolower( $value ) )->plain() |
1028 | ); |
1029 | $items[$value] = |
1030 | '<span class="cdx-radio">' . |
1031 | '<span class="cdx-radio__wrapper">' . |
1032 | Xml::radio( $params['controlName'], $value, $checked, $itemAttribs ) . |
1033 | '<span class="cdx-radio__icon"></span>' . |
1034 | '<span class="cdx-radio__label cdx-label">' . |
1035 | Xml::tags( |
1036 | 'label', |
1037 | [ 'for' => $id, 'class' => 'cdx-label__label' ], |
1038 | '<span class="cdx-label__label__text">' . $radioText . '</span>' |
1039 | ) . '</span></span></span>'; |
1040 | } |
1041 | |
1042 | return $items; |
1043 | } |
1044 | |
1045 | /** |
1046 | * Output an error or warning box using a Status object. |
1047 | * |
1048 | * @param Status $status |
1049 | */ |
1050 | public function showStatusBox( $status ) { |
1051 | if ( !$status->isGood() ) { |
1052 | $html = $status->getHTML(); |
1053 | |
1054 | if ( $status->isOK() ) { |
1055 | $box = Html::warningBox( $html, 'config-warning-box' ); |
1056 | } else { |
1057 | $box = Html::errorBox( $html, '', 'config-error-box' ); |
1058 | } |
1059 | |
1060 | $this->output->addHTML( $box ); |
1061 | } |
1062 | } |
1063 | |
1064 | /** |
1065 | * Convenience function to set variables based on form data. |
1066 | * Assumes that variables containing "password" in the name are (potentially |
1067 | * fake) passwords. |
1068 | * |
1069 | * @param string[] $varNames |
1070 | * @param string $prefix The prefix added to variables to obtain form names |
1071 | * |
1072 | * @return string[] |
1073 | */ |
1074 | public function setVarsFromRequest( $varNames, $prefix = 'config_' ) { |
1075 | $newValues = []; |
1076 | |
1077 | foreach ( $varNames as $name ) { |
1078 | $value = $this->request->getVal( $prefix . $name ); |
1079 | // T32524, do not trim passwords |
1080 | if ( $value !== null && stripos( $name, 'password' ) === false ) { |
1081 | $value = trim( $value ); |
1082 | } |
1083 | $newValues[$name] = $value; |
1084 | |
1085 | if ( $value === null ) { |
1086 | // Checkbox? |
1087 | $this->setVar( $name, false ); |
1088 | } elseif ( stripos( $name, 'password' ) !== false ) { |
1089 | $this->setPassword( $name, $value ); |
1090 | } else { |
1091 | $this->setVar( $name, $value ); |
1092 | } |
1093 | } |
1094 | |
1095 | return $newValues; |
1096 | } |
1097 | |
1098 | /** |
1099 | * Helper for WebInstallerOutput |
1100 | * |
1101 | * @internal For use by WebInstallerOutput |
1102 | * @param string $page |
1103 | * @return string |
1104 | */ |
1105 | public function getDocUrl( $page ) { |
1106 | $query = [ 'page' => $page ]; |
1107 | |
1108 | if ( in_array( $this->currentPageName, $this->pageSequence ) ) { |
1109 | $query['lastPage'] = $this->currentPageName; |
1110 | } |
1111 | |
1112 | return $this->getUrl( $query ); |
1113 | } |
1114 | |
1115 | /** |
1116 | * Helper for sidebar links. |
1117 | * |
1118 | * @internal For use in WebInstallerOutput class |
1119 | * @param string $url |
1120 | * @param string $linkText |
1121 | * @return string HTML |
1122 | */ |
1123 | public function makeLinkItem( $url, $linkText ) { |
1124 | return Html::rawElement( 'li', [], |
1125 | Html::element( 'a', [ 'href' => $url ], $linkText ) |
1126 | ); |
1127 | } |
1128 | |
1129 | /** |
1130 | * If the software package wants the LocalSettings.php file |
1131 | * to be placed in a specific location, override this function |
1132 | * (see mw-config/overrides/README) to return the path of |
1133 | * where the file should be saved, or false for a generic |
1134 | * "in the base of your install" |
1135 | * |
1136 | * @since 1.27 |
1137 | * @return string|bool |
1138 | */ |
1139 | public function getLocalSettingsLocation() { |
1140 | return false; |
1141 | } |
1142 | |
1143 | /** |
1144 | * @return bool |
1145 | */ |
1146 | public function envCheckPath() { |
1147 | // PHP_SELF isn't available sometimes, such as when PHP is CGI but |
1148 | // cgi.fix_pathinfo is disabled. In that case, fall back to SCRIPT_NAME |
1149 | // to get the path to the current script... hopefully it's reliable. SIGH |
1150 | $path = false; |
1151 | if ( !empty( $_SERVER['PHP_SELF'] ) ) { |
1152 | $path = $_SERVER['PHP_SELF']; |
1153 | } elseif ( !empty( $_SERVER['SCRIPT_NAME'] ) ) { |
1154 | $path = $_SERVER['SCRIPT_NAME']; |
1155 | } |
1156 | if ( $path === false ) { |
1157 | $this->showError( 'config-no-uri' ); |
1158 | return false; |
1159 | } |
1160 | |
1161 | return parent::envCheckPath(); |
1162 | } |
1163 | |
1164 | protected function detectWebPaths() { |
1165 | // PHP_SELF isn't available sometimes, such as when PHP is CGI but |
1166 | // cgi.fix_pathinfo is disabled. In that case, fall back to SCRIPT_NAME |
1167 | // to get the path to the current script... hopefully it's reliable. SIGH |
1168 | $path = false; |
1169 | if ( !empty( $_SERVER['PHP_SELF'] ) ) { |
1170 | $path = $_SERVER['PHP_SELF']; |
1171 | } elseif ( !empty( $_SERVER['SCRIPT_NAME'] ) ) { |
1172 | $path = $_SERVER['SCRIPT_NAME']; |
1173 | } |
1174 | if ( $path !== false ) { |
1175 | $scriptPath = preg_replace( '{^(.*)/(mw-)?config.*$}', '$1', $path ); |
1176 | |
1177 | return [ |
1178 | 'wgScriptPath' => "$scriptPath", |
1179 | // Update variables set from Setup.php that are derived from wgScriptPath |
1180 | 'wgScript' => "$scriptPath/index.php", |
1181 | 'wgLoadScript' => "$scriptPath/load.php", |
1182 | 'wgStylePath' => "$scriptPath/skins", |
1183 | 'wgLocalStylePath' => "$scriptPath/skins", |
1184 | 'wgExtensionAssetsPath' => "$scriptPath/extensions", |
1185 | 'wgUploadPath' => "$scriptPath/images", |
1186 | 'wgResourceBasePath' => "$scriptPath", |
1187 | ]; |
1188 | } |
1189 | return []; |
1190 | } |
1191 | |
1192 | /** |
1193 | * @return string |
1194 | */ |
1195 | protected function envGetDefaultServer() { |
1196 | $assumeProxiesUseDefaultProtocolPorts = |
1197 | $this->getVar( 'wgAssumeProxiesUseDefaultProtocolPorts' ); |
1198 | |
1199 | return WebRequest::detectServer( $assumeProxiesUseDefaultProtocolPorts ); |
1200 | } |
1201 | |
1202 | /** |
1203 | * @return string |
1204 | */ |
1205 | public function getDefaultServer() { |
1206 | return $this->envGetDefaultServer(); |
1207 | } |
1208 | |
1209 | /** |
1210 | * Actually output LocalSettings.php for download |
1211 | */ |
1212 | private function outputLS() { |
1213 | $this->request->response()->header( 'Content-type: application/x-httpd-php' ); |
1214 | $this->request->response()->header( |
1215 | 'Content-Disposition: attachment; filename="LocalSettings.php"' |
1216 | ); |
1217 | |
1218 | $ls = InstallerOverrides::getLocalSettingsGenerator( $this ); |
1219 | $rightsProfile = $this->rightsProfiles[$this->getVar( '_RightsProfile' )]; |
1220 | foreach ( $rightsProfile as $group => $rightsArr ) { |
1221 | $ls->setGroupRights( $group, $rightsArr ); |
1222 | } |
1223 | echo $ls->getText(); |
1224 | } |
1225 | |
1226 | /** |
1227 | * Output stylesheet for web installer pages |
1228 | */ |
1229 | public function outputCss() { |
1230 | $this->request->response()->header( 'Content-type: text/css' ); |
1231 | echo $this->output->getCSS(); |
1232 | } |
1233 | |
1234 | /** |
1235 | * @return string[] |
1236 | */ |
1237 | public function getPhpErrors() { |
1238 | return $this->phpErrors; |
1239 | } |
1240 | |
1241 | /** |
1242 | * Determine whether the current database needs to be upgraded, i.e. whether |
1243 | * it already has MediaWiki tables. |
1244 | * |
1245 | * @return bool |
1246 | */ |
1247 | public function needsUpgrade() { |
1248 | return $this->getDBInstaller()->needsUpgrade(); |
1249 | } |
1250 | |
1251 | /** |
1252 | * Perform database upgrades |
1253 | * |
1254 | * @return bool |
1255 | */ |
1256 | public function doUpgrade() { |
1257 | $dbInstaller = $this->getDBInstaller(); |
1258 | $dbInstaller->preUpgrade(); |
1259 | |
1260 | $taskList = new TaskList; |
1261 | $taskFactory = $this->getTaskFactory(); |
1262 | $taskFactory->registerWebUpgradeTasks( $taskList ); |
1263 | $taskRunner = new TaskRunner( $taskList, $taskFactory, TaskFactory::PROFILE_WEB_UPGRADE ); |
1264 | |
1265 | ob_start( [ $this, 'outputHandler' ] ); |
1266 | try { |
1267 | $status = $taskRunner->execute(); |
1268 | $ret = $status->isOK(); |
1269 | |
1270 | $this->setVar( '_UpgradeDone', true ); |
1271 | } catch ( Exception $e ) { |
1272 | // TODO: Should this use MWExceptionRenderer? |
1273 | echo "\nAn error occurred:\n"; |
1274 | echo $e->getMessage(); |
1275 | $ret = false; |
1276 | } |
1277 | ob_end_flush(); |
1278 | |
1279 | return $ret; |
1280 | } |
1281 | |
1282 | public function outputHandler( $string ) { |
1283 | return htmlspecialchars( $string ); |
1284 | } |
1285 | |
1286 | } |