Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 443 |
|
0.00% |
0 / 45 |
CRAP | |
0.00% |
0 / 1 |
WebInstaller | |
0.00% |
0 / 443 |
|
0.00% |
0 / 45 |
17822 | |
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 | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
12 | |||
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 | |||
getInfoBox | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
6 | |||
getHelpBox | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
2 | |||
showHelpBox | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
showMessage | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
2 | |||
showStatusMessage | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
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 / 31 |
|
0.00% |
0 / 1 |
42 | |||
getRadioSet | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
20 | |||
getRadioElements | |
0.00% |
0 / 26 |
|
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 | |||
makeDownloadLinkHtml | |
0.00% |
0 / 5 |
|
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 | |||
envPrepPath | |
0.00% |
0 / 16 |
|
0.00% |
0 / 1 |
20 | |||
envGetDefaultServer | |
0.00% |
0 / 3 |
|
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 | |||
infoBox | |
0.00% |
0 / 14 |
|
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\Languages\LanguageNameUtils; |
31 | use MediaWiki\MediaWikiServices; |
32 | use MediaWiki\Message\Message; |
33 | use MediaWiki\Request\WebRequest; |
34 | use MediaWiki\Status\Status; |
35 | use Xml; |
36 | |
37 | /** |
38 | * Class for the core installer web interface. |
39 | * |
40 | * @ingroup Installer |
41 | * @since 1.17 |
42 | */ |
43 | class WebInstaller extends Installer { |
44 | |
45 | /** |
46 | * @var WebInstallerOutput |
47 | */ |
48 | public $output; |
49 | |
50 | /** |
51 | * WebRequest object. |
52 | * |
53 | * @var WebRequest |
54 | */ |
55 | public $request; |
56 | |
57 | /** |
58 | * Cached session array. |
59 | * |
60 | * @var array[] |
61 | */ |
62 | protected $session; |
63 | |
64 | /** |
65 | * Captured PHP error text. Temporary. |
66 | * |
67 | * @var string[] |
68 | */ |
69 | protected $phpErrors; |
70 | |
71 | /** |
72 | * The main sequence of page names. These will be displayed in turn. |
73 | * |
74 | * To add a new installer page: |
75 | * * Add it to this WebInstaller::$pageSequence property |
76 | * * Add a "config-page-<name>" message |
77 | * * Add a "WebInstaller<name>" class |
78 | * |
79 | * @var string[] |
80 | */ |
81 | public $pageSequence = [ |
82 | 'Language', |
83 | 'ExistingWiki', |
84 | 'Welcome', |
85 | 'DBConnect', |
86 | 'Upgrade', |
87 | 'DBSettings', |
88 | 'Name', |
89 | 'Options', |
90 | 'Install', |
91 | 'Complete', |
92 | ]; |
93 | |
94 | /** |
95 | * Out of sequence pages, selectable by the user at any time. |
96 | * |
97 | * @var string[] |
98 | */ |
99 | protected $otherPages = [ |
100 | 'Restart', |
101 | 'ReleaseNotes', |
102 | 'Copying', |
103 | 'UpgradeDoc', // Can't use Upgrade due to Upgrade step |
104 | ]; |
105 | |
106 | /** |
107 | * Array of pages which have declared that they have been submitted, have validated |
108 | * their input, and need no further processing. |
109 | * |
110 | * @var bool[] |
111 | */ |
112 | protected $happyPages; |
113 | |
114 | /** |
115 | * List of "skipped" pages. These are pages that will automatically continue |
116 | * to the next page on any GET request. To avoid breaking the "back" button, |
117 | * they need to be skipped during a back operation. |
118 | * |
119 | * @var bool[] |
120 | */ |
121 | protected $skippedPages; |
122 | |
123 | /** |
124 | * Flag indicating that session data may have been lost. |
125 | * |
126 | * @var bool |
127 | */ |
128 | public $showSessionWarning = false; |
129 | |
130 | /** |
131 | * Numeric index of the page we're on |
132 | * |
133 | * @var int |
134 | */ |
135 | protected $tabIndex = 1; |
136 | |
137 | /** |
138 | * Numeric index of the help box |
139 | * |
140 | * @var int |
141 | */ |
142 | protected $helpBoxId = 1; |
143 | |
144 | /** |
145 | * Name of the page we're on |
146 | * |
147 | * @var string |
148 | */ |
149 | protected $currentPageName; |
150 | |
151 | /** |
152 | * @param WebRequest $request |
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->getVal( '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, $wgRequest; |
506 | |
507 | $mwLanguages = MediaWikiServices::getInstance() |
508 | ->getLanguageNameUtils() |
509 | ->getLanguageNames( LanguageNameUtils::AUTONYMS, LanguageNameUtils::SUPPORTED ); |
510 | $headerLanguages = array_keys( $wgRequest->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 HTML for an information message box with an icon. |
631 | * |
632 | * @param string|HtmlArmor $text Wikitext to be parsed (from Message::plain) or raw HTML. |
633 | * @param string|false $icon Icon name, file in mw-config/images. Default: false |
634 | * @param string $class Additional class name to add to the wrapper div. Default: Empty string. |
635 | * @return string HTML |
636 | */ |
637 | public function getInfoBox( $text, $icon = false, $class = '' ) { |
638 | $html = ( $text instanceof HtmlArmor ) ? |
639 | HtmlArmor::getHtml( $text ) : |
640 | $this->parse( $text, true ); |
641 | $alt = wfMessage( 'config-information' )->text(); |
642 | |
643 | return self::infoBox( $html, '', $alt, $class ); |
644 | } |
645 | |
646 | /** |
647 | * Get small text indented help for a preceding form field. |
648 | * Parameters like wfMessage(). |
649 | * |
650 | * @param string $msg |
651 | * @param mixed ...$args |
652 | * @return string HTML |
653 | * @return-taint escaped |
654 | */ |
655 | public function getHelpBox( $msg, ...$args ) { |
656 | $args = array_map( 'htmlspecialchars', $args ); |
657 | $text = wfMessage( $msg, $args )->useDatabase( false )->plain(); |
658 | $html = $this->parse( $text, true ); |
659 | |
660 | return "<div class=\"config-help-field-container\">\n" . |
661 | "<a class=\"config-help-field-hint\" title=\"" . |
662 | wfMessage( 'config-help-tooltip' )->escaped() . "\">ℹ️ " . |
663 | wfMessage( 'config-help' )->escaped() . "</a>\n" . |
664 | "<div class=\"config-help-field-content config-help-field-content-hidden " . |
665 | "cdx-message cdx-message--block cdx-message--notice\" style=\"margin: 10px\">" . |
666 | "<div class=\"cdx-message__content\">" . $html . "</div></div>\n" . |
667 | "</div>\n"; |
668 | } |
669 | |
670 | /** |
671 | * Output a help box. |
672 | * @param string $msg Key for wfMessage() |
673 | * @param mixed ...$params |
674 | */ |
675 | public function showHelpBox( $msg, ...$params ) { |
676 | $html = $this->getHelpBox( $msg, ...$params ); |
677 | $this->output->addHTML( $html ); |
678 | } |
679 | |
680 | public function showMessage( $msg, ...$params ) { |
681 | $html = '<div class="cdx-message cdx-message--block cdx-message--notice">' . |
682 | '<span class="cdx-message__icon"></span><div class="cdx-message__content">' . |
683 | $this->parse( wfMessage( $msg, $params )->useDatabase( false )->plain() ) . |
684 | "</div></div>\n"; |
685 | $this->output->addHTML( $html ); |
686 | } |
687 | |
688 | public function showStatusMessage( Status $status ) { |
689 | $errors = array_merge( $status->getErrorsArray(), $status->getWarningsArray() ); |
690 | foreach ( $errors as $error ) { |
691 | $this->showMessage( ...$error ); |
692 | } |
693 | } |
694 | |
695 | /** |
696 | * Label a control by wrapping a config-input div around it and putting a |
697 | * label before it. |
698 | * |
699 | * @param string $msg |
700 | * @param string|false $forId |
701 | * @param string $contents HTML |
702 | * @param string $helpData |
703 | * @return string HTML |
704 | * @return-taint escaped |
705 | */ |
706 | public function label( $msg, $forId, $contents, $helpData = "" ) { |
707 | if ( strval( $msg ) == '' ) { |
708 | $labelText = "\u{00A0}"; |
709 | } else { |
710 | $labelText = wfMessage( $msg )->escaped(); |
711 | } |
712 | |
713 | $attributes = [ 'class' => 'config-label' ]; |
714 | |
715 | if ( $forId ) { |
716 | $attributes['for'] = $forId; |
717 | } |
718 | |
719 | return "<div class=\"config-block\">\n" . |
720 | " <div class=\"config-block-label\">\n" . |
721 | Xml::tags( 'label', |
722 | $attributes, |
723 | $labelText |
724 | ) . "\n" . |
725 | $helpData . |
726 | " </div>\n" . |
727 | " <div class=\"config-block-elements\">\n" . |
728 | $contents . |
729 | " </div>\n" . |
730 | "</div>\n"; |
731 | } |
732 | |
733 | /** |
734 | * Get a labelled text box to configure a variable. |
735 | * |
736 | * @param mixed[] $params |
737 | * Parameters are: |
738 | * var: The variable to be configured (required) |
739 | * label: The message name for the label (required) |
740 | * attribs: Additional attributes for the input element (optional) |
741 | * controlName: The name for the input element (optional) |
742 | * value: The current value of the variable (optional) |
743 | * help: The html for the help text (optional) |
744 | * |
745 | * @return string HTML |
746 | * @return-taint escaped |
747 | */ |
748 | public function getTextBox( $params ) { |
749 | if ( !isset( $params['controlName'] ) ) { |
750 | $params['controlName'] = 'config_' . $params['var']; |
751 | } |
752 | |
753 | if ( !isset( $params['value'] ) ) { |
754 | $params['value'] = $this->getVar( $params['var'] ); |
755 | } |
756 | |
757 | if ( !isset( $params['attribs'] ) ) { |
758 | $params['attribs'] = []; |
759 | } |
760 | if ( !isset( $params['help'] ) ) { |
761 | $params['help'] = ""; |
762 | } |
763 | |
764 | return $this->label( |
765 | $params['label'], |
766 | $params['controlName'], |
767 | "<div class=\"cdx-text-input\">" . |
768 | Xml::input( |
769 | $params['controlName'], |
770 | 30, // intended to be overridden by CSS |
771 | $params['value'], |
772 | $params['attribs'] + [ |
773 | 'id' => $params['controlName'], |
774 | 'class' => 'cdx-text-input__input', |
775 | 'tabindex' => $this->nextTabIndex() |
776 | ] |
777 | ) . "</div>", |
778 | $params['help'] |
779 | ); |
780 | } |
781 | |
782 | /** |
783 | * Get a labelled textarea to configure a variable |
784 | * |
785 | * @param mixed[] $params |
786 | * Parameters are: |
787 | * var: The variable to be configured (required) |
788 | * label: The message name for the label (required) |
789 | * attribs: Additional attributes for the input element (optional) |
790 | * controlName: The name for the input element (optional) |
791 | * value: The current value of the variable (optional) |
792 | * help: The html for the help text (optional) |
793 | * |
794 | * @return string |
795 | */ |
796 | public function getTextArea( $params ) { |
797 | if ( !isset( $params['controlName'] ) ) { |
798 | $params['controlName'] = 'config_' . $params['var']; |
799 | } |
800 | |
801 | if ( !isset( $params['value'] ) ) { |
802 | $params['value'] = $this->getVar( $params['var'] ); |
803 | } |
804 | |
805 | if ( !isset( $params['attribs'] ) ) { |
806 | $params['attribs'] = []; |
807 | } |
808 | if ( !isset( $params['help'] ) ) { |
809 | $params['help'] = ""; |
810 | } |
811 | |
812 | return $this->label( |
813 | $params['label'], |
814 | $params['controlName'], |
815 | Xml::textarea( |
816 | $params['controlName'], |
817 | $params['value'], |
818 | 30, |
819 | 5, |
820 | $params['attribs'] + [ |
821 | 'id' => $params['controlName'], |
822 | 'class' => 'config-input-text', |
823 | 'tabindex' => $this->nextTabIndex() |
824 | ] |
825 | ), |
826 | $params['help'] |
827 | ); |
828 | } |
829 | |
830 | /** |
831 | * Get a labelled password box to configure a variable. |
832 | * |
833 | * Implements password hiding |
834 | * @param mixed[] $params |
835 | * Parameters are: |
836 | * var: The variable to be configured (required) |
837 | * label: The message name for the label (required) |
838 | * attribs: Additional attributes for the input element (optional) |
839 | * controlName: The name for the input element (optional) |
840 | * value: The current value of the variable (optional) |
841 | * help: The html for the help text (optional) |
842 | * |
843 | * @return string HTML |
844 | * @return-taint escaped |
845 | */ |
846 | public function getPasswordBox( $params ) { |
847 | if ( !isset( $params['value'] ) ) { |
848 | $params['value'] = $this->getVar( $params['var'] ); |
849 | } |
850 | |
851 | if ( !isset( $params['attribs'] ) ) { |
852 | $params['attribs'] = []; |
853 | } |
854 | |
855 | $params['value'] = $this->getFakePassword( $params['value'] ); |
856 | $params['attribs']['type'] = 'password'; |
857 | |
858 | return $this->getTextBox( $params ); |
859 | } |
860 | |
861 | /** |
862 | * Add a class to an array of attributes. If the array already has a class, |
863 | * append the new class to the list. |
864 | * |
865 | * @param array &$attribs |
866 | * @param string $class |
867 | */ |
868 | private static function addClassAttrib( &$attribs, $class ) { |
869 | if ( isset( $attribs['class'] ) ) { |
870 | $attribs['class'] .= ' ' . $class; |
871 | } else { |
872 | $attribs['class'] = $class; |
873 | } |
874 | } |
875 | |
876 | /** |
877 | * Get a labelled checkbox to configure a boolean variable. |
878 | * |
879 | * @param mixed[] $params |
880 | * Parameters are: |
881 | * var: The variable to be configured (required) |
882 | * label: The message name for the label (required) |
883 | * labelAttribs:Additional attributes for the label element (optional) |
884 | * attribs: Additional attributes for the input element (optional) |
885 | * controlName: The name for the input element (optional) |
886 | * value: The current value of the variable (optional) |
887 | * help: The html for the help text (optional) |
888 | * |
889 | * @return string HTML |
890 | * @return-taint escaped |
891 | */ |
892 | public function getCheckBox( $params ) { |
893 | if ( !isset( $params['controlName'] ) ) { |
894 | $params['controlName'] = 'config_' . $params['var']; |
895 | } |
896 | |
897 | if ( !isset( $params['value'] ) ) { |
898 | $params['value'] = $this->getVar( $params['var'] ); |
899 | } |
900 | |
901 | if ( !isset( $params['attribs'] ) ) { |
902 | $params['attribs'] = []; |
903 | } |
904 | if ( !isset( $params['help'] ) ) { |
905 | $params['help'] = ""; |
906 | } |
907 | if ( !isset( $params['labelAttribs'] ) ) { |
908 | $params['labelAttribs'] = []; |
909 | } |
910 | $labelText = $params['rawtext'] ?? $this->parse( wfMessage( $params['label'] )->plain() ); |
911 | self::addClassAttrib( $params['attribs'], 'cdx-checkbox__input' ); |
912 | self::addClassAttrib( $params['labelAttribs'], 'cdx-checkbox__label' ); |
913 | |
914 | return "<div class=\"cdx-checkbox\" style=\"margin-top: 12px; margin-bottom: 2px;\">\n" . |
915 | Xml::check( |
916 | $params['controlName'], |
917 | $params['value'], |
918 | $params['attribs'] + [ |
919 | 'id' => $params['controlName'], |
920 | 'tabindex' => $this->nextTabIndex() |
921 | ] |
922 | ) . |
923 | "<span class=\"cdx-checkbox__icon\"></span>" . |
924 | Html::rawElement( |
925 | 'label', |
926 | $params['labelAttribs'] + [ |
927 | 'for' => $params['controlName'] |
928 | ], |
929 | $labelText |
930 | ) . |
931 | "</div>\n" . $params['help']; |
932 | } |
933 | |
934 | /** |
935 | * Get a set of labelled radio buttons. |
936 | * |
937 | * @param mixed[] $params |
938 | * Parameters are: |
939 | * var: The variable to be configured (required) |
940 | * label: The message name for the label (required) |
941 | * itemLabelPrefix: The message name prefix for the item labels (required) |
942 | * itemLabels: List of message names to use for the item labels instead |
943 | * of itemLabelPrefix, keyed by values |
944 | * values: List of allowed values (required) |
945 | * itemAttribs: Array of attribute arrays, outer key is the value name (optional) |
946 | * commonAttribs: Attribute array applied to all items |
947 | * controlName: The name for the input element (optional) |
948 | * value: The current value of the variable (optional) |
949 | * help: The html for the help text (optional) |
950 | * |
951 | * @return string HTML |
952 | * @return-taint escaped |
953 | */ |
954 | public function getRadioSet( $params ) { |
955 | $items = $this->getRadioElements( $params ); |
956 | |
957 | $label = $params['label'] ?? ''; |
958 | |
959 | if ( !isset( $params['controlName'] ) ) { |
960 | $params['controlName'] = 'config_' . $params['var']; |
961 | } |
962 | |
963 | if ( !isset( $params['help'] ) ) { |
964 | $params['help'] = ""; |
965 | } |
966 | |
967 | $s = ""; |
968 | foreach ( $items as $item ) { |
969 | $s .= "$item\n"; |
970 | } |
971 | |
972 | return $this->label( $label, $params['controlName'], $s, $params['help'] ); |
973 | } |
974 | |
975 | /** |
976 | * Get a set of labelled radio buttons. You probably want to use getRadioSet(), not this. |
977 | * |
978 | * @see getRadioSet |
979 | * |
980 | * @param mixed[] $params |
981 | * @return string[] HTML |
982 | * @return-taint escaped |
983 | */ |
984 | public function getRadioElements( $params ) { |
985 | if ( !isset( $params['controlName'] ) ) { |
986 | $params['controlName'] = 'config_' . $params['var']; |
987 | } |
988 | |
989 | if ( !isset( $params['value'] ) ) { |
990 | $params['value'] = $this->getVar( $params['var'] ); |
991 | } |
992 | |
993 | $items = []; |
994 | |
995 | foreach ( $params['values'] as $value ) { |
996 | $itemAttribs = []; |
997 | |
998 | if ( isset( $params['commonAttribs'] ) ) { |
999 | $itemAttribs = $params['commonAttribs']; |
1000 | } |
1001 | |
1002 | if ( isset( $params['itemAttribs'][$value] ) ) { |
1003 | $itemAttribs = $params['itemAttribs'][$value] + $itemAttribs; |
1004 | } |
1005 | |
1006 | $checked = $value == $params['value']; |
1007 | $id = $params['controlName'] . '_' . $value; |
1008 | $itemAttribs['id'] = $id; |
1009 | $itemAttribs['tabindex'] = $this->nextTabIndex(); |
1010 | self::addClassAttrib( $itemAttribs, 'cdx-radio__input' ); |
1011 | |
1012 | $items[$value] = |
1013 | '<span class="cdx-radio">' . |
1014 | Xml::radio( $params['controlName'], $value, $checked, $itemAttribs ) . |
1015 | "<span class=\"cdx-radio__icon\"></span>\u{00A0}" . |
1016 | Xml::tags( 'label', [ 'for' => $id, 'class' => 'cdx-radio__label' ], $this->parse( |
1017 | isset( $params['itemLabels'] ) ? |
1018 | wfMessage( $params['itemLabels'][$value] )->plain() : |
1019 | wfMessage( $params['itemLabelPrefix'] . strtolower( $value ) )->plain() |
1020 | ) ) . '</span>'; |
1021 | } |
1022 | |
1023 | return $items; |
1024 | } |
1025 | |
1026 | /** |
1027 | * Output an error or warning box using a Status object. |
1028 | * |
1029 | * @param Status $status |
1030 | */ |
1031 | public function showStatusBox( $status ) { |
1032 | if ( !$status->isGood() ) { |
1033 | $html = $status->getHTML(); |
1034 | |
1035 | if ( $status->isOK() ) { |
1036 | $box = Html::warningBox( $html, 'config-warning-box' ); |
1037 | } else { |
1038 | $box = Html::errorBox( $html, '', 'config-error-box' ); |
1039 | } |
1040 | |
1041 | $this->output->addHTML( $box ); |
1042 | } |
1043 | } |
1044 | |
1045 | /** |
1046 | * Convenience function to set variables based on form data. |
1047 | * Assumes that variables containing "password" in the name are (potentially |
1048 | * fake) passwords. |
1049 | * |
1050 | * @param string[] $varNames |
1051 | * @param string $prefix The prefix added to variables to obtain form names |
1052 | * |
1053 | * @return string[] |
1054 | */ |
1055 | public function setVarsFromRequest( $varNames, $prefix = 'config_' ) { |
1056 | $newValues = []; |
1057 | |
1058 | foreach ( $varNames as $name ) { |
1059 | $value = $this->request->getVal( $prefix . $name ); |
1060 | // T32524, do not trim passwords |
1061 | if ( $value !== null && stripos( $name, 'password' ) === false ) { |
1062 | $value = trim( $value ); |
1063 | } |
1064 | $newValues[$name] = $value; |
1065 | |
1066 | if ( $value === null ) { |
1067 | // Checkbox? |
1068 | $this->setVar( $name, false ); |
1069 | } elseif ( stripos( $name, 'password' ) !== false ) { |
1070 | $this->setPassword( $name, $value ); |
1071 | } else { |
1072 | $this->setVar( $name, $value ); |
1073 | } |
1074 | } |
1075 | |
1076 | return $newValues; |
1077 | } |
1078 | |
1079 | /** |
1080 | * Helper for WebInstallerOutput |
1081 | * |
1082 | * @internal For use by WebInstallerOutput |
1083 | * @param string $page |
1084 | * @return string |
1085 | */ |
1086 | public function getDocUrl( $page ) { |
1087 | $query = [ 'page' => $page ]; |
1088 | |
1089 | if ( in_array( $this->currentPageName, $this->pageSequence ) ) { |
1090 | $query['lastPage'] = $this->currentPageName; |
1091 | } |
1092 | |
1093 | return $this->getUrl( $query ); |
1094 | } |
1095 | |
1096 | /** |
1097 | * Helper for sidebar links. |
1098 | * |
1099 | * @internal For use in WebInstallerOutput class |
1100 | * @param string $url |
1101 | * @param string $linkText |
1102 | * @return string HTML |
1103 | */ |
1104 | public function makeLinkItem( $url, $linkText ) { |
1105 | return Html::rawElement( 'li', [], |
1106 | Html::element( 'a', [ 'href' => $url ], $linkText ) |
1107 | ); |
1108 | } |
1109 | |
1110 | /** |
1111 | * Helper for "Download LocalSettings" link. |
1112 | * |
1113 | * @internal For use in WebInstallerComplete class |
1114 | * @return string Html for download link |
1115 | */ |
1116 | public function makeDownloadLinkHtml() { |
1117 | $anchor = Html::rawElement( 'a', |
1118 | [ 'href' => $this->getUrl( [ 'localsettings' => 1 ] ) ], |
1119 | wfMessage( 'config-download-localsettings' )->parse() |
1120 | ); |
1121 | |
1122 | return Html::rawElement( 'div', [ 'class' => 'config-download-link' ], $anchor ); |
1123 | } |
1124 | |
1125 | /** |
1126 | * If the software package wants the LocalSettings.php file |
1127 | * to be placed in a specific location, override this function |
1128 | * (see mw-config/overrides/README) to return the path of |
1129 | * where the file should be saved, or false for a generic |
1130 | * "in the base of your install" |
1131 | * |
1132 | * @since 1.27 |
1133 | * @return string|bool |
1134 | */ |
1135 | public function getLocalSettingsLocation() { |
1136 | return false; |
1137 | } |
1138 | |
1139 | /** |
1140 | * @return bool |
1141 | */ |
1142 | public function envCheckPath() { |
1143 | // PHP_SELF isn't available sometimes, such as when PHP is CGI but |
1144 | // cgi.fix_pathinfo is disabled. In that case, fall back to SCRIPT_NAME |
1145 | // to get the path to the current script... hopefully it's reliable. SIGH |
1146 | $path = false; |
1147 | if ( !empty( $_SERVER['PHP_SELF'] ) ) { |
1148 | $path = $_SERVER['PHP_SELF']; |
1149 | } elseif ( !empty( $_SERVER['SCRIPT_NAME'] ) ) { |
1150 | $path = $_SERVER['SCRIPT_NAME']; |
1151 | } |
1152 | if ( $path === false ) { |
1153 | $this->showError( 'config-no-uri' ); |
1154 | return false; |
1155 | } |
1156 | |
1157 | return parent::envCheckPath(); |
1158 | } |
1159 | |
1160 | public function envPrepPath() { |
1161 | parent::envPrepPath(); |
1162 | // PHP_SELF isn't available sometimes, such as when PHP is CGI but |
1163 | // cgi.fix_pathinfo is disabled. In that case, fall back to SCRIPT_NAME |
1164 | // to get the path to the current script... hopefully it's reliable. SIGH |
1165 | $path = false; |
1166 | if ( !empty( $_SERVER['PHP_SELF'] ) ) { |
1167 | $path = $_SERVER['PHP_SELF']; |
1168 | } elseif ( !empty( $_SERVER['SCRIPT_NAME'] ) ) { |
1169 | $path = $_SERVER['SCRIPT_NAME']; |
1170 | } |
1171 | if ( $path !== false ) { |
1172 | $scriptPath = preg_replace( '{^(.*)/(mw-)?config.*$}', '$1', $path ); |
1173 | |
1174 | $this->setVar( 'wgScriptPath', "$scriptPath" ); |
1175 | // Update variables set from Setup.php that are derived from wgScriptPath |
1176 | $this->setVar( 'wgScript', "$scriptPath/index.php" ); |
1177 | $this->setVar( 'wgLoadScript', "$scriptPath/load.php" ); |
1178 | $this->setVar( 'wgStylePath', "$scriptPath/skins" ); |
1179 | $this->setVar( 'wgLocalStylePath', "$scriptPath/skins" ); |
1180 | $this->setVar( 'wgExtensionAssetsPath', "$scriptPath/extensions" ); |
1181 | $this->setVar( 'wgUploadPath', "$scriptPath/images" ); |
1182 | $this->setVar( 'wgResourceBasePath', "$scriptPath" ); |
1183 | } |
1184 | } |
1185 | |
1186 | /** |
1187 | * @return string |
1188 | */ |
1189 | protected function envGetDefaultServer() { |
1190 | $assumeProxiesUseDefaultProtocolPorts = |
1191 | $this->getVar( 'wgAssumeProxiesUseDefaultProtocolPorts' ); |
1192 | |
1193 | return WebRequest::detectServer( $assumeProxiesUseDefaultProtocolPorts ); |
1194 | } |
1195 | |
1196 | /** |
1197 | * Actually output LocalSettings.php for download |
1198 | */ |
1199 | private function outputLS() { |
1200 | $this->request->response()->header( 'Content-type: application/x-httpd-php' ); |
1201 | $this->request->response()->header( |
1202 | 'Content-Disposition: attachment; filename="LocalSettings.php"' |
1203 | ); |
1204 | |
1205 | $ls = InstallerOverrides::getLocalSettingsGenerator( $this ); |
1206 | $rightsProfile = $this->rightsProfiles[$this->getVar( '_RightsProfile' )]; |
1207 | foreach ( $rightsProfile as $group => $rightsArr ) { |
1208 | $ls->setGroupRights( $group, $rightsArr ); |
1209 | } |
1210 | echo $ls->getText(); |
1211 | } |
1212 | |
1213 | /** |
1214 | * Output stylesheet for web installer pages |
1215 | */ |
1216 | public function outputCss() { |
1217 | $this->request->response()->header( 'Content-type: text/css' ); |
1218 | echo $this->output->getCSS(); |
1219 | } |
1220 | |
1221 | /** |
1222 | * @return string[] |
1223 | */ |
1224 | public function getPhpErrors() { |
1225 | return $this->phpErrors; |
1226 | } |
1227 | |
1228 | /** |
1229 | * Get HTML for an information message box with an icon. |
1230 | * |
1231 | * @since 1.36 |
1232 | * @param string $rawHtml HTML |
1233 | * @param string $icon Path to icon file (used as 'src' attribute) |
1234 | * @param string $alt Alternate text for the icon |
1235 | * @param string $class Additional class name to add to the wrapper div |
1236 | * @return string HTML |
1237 | */ |
1238 | protected static function infoBox( $rawHtml, $icon, $alt, $class = '' ) { |
1239 | $s = Html::openElement( 'div', [ 'class' => 'mw-installer-box-left' ] ) . |
1240 | Html::element( 'img', |
1241 | [ |
1242 | 'src' => $icon, |
1243 | 'alt' => $alt, |
1244 | ] |
1245 | ) . |
1246 | Html::closeElement( 'div' ) . |
1247 | Html::openElement( 'div', [ 'class' => 'mw-installer-box-right' ] ) . |
1248 | $rawHtml . |
1249 | Html::closeElement( 'div' ) . |
1250 | Html::element( 'div', [ 'style' => 'clear: left;' ], ' ' ); |
1251 | |
1252 | return Html::warningBox( $s, $class ) |
1253 | . Html::element( 'div', [ 'style' => 'clear: left;' ], ' ' ); |
1254 | } |
1255 | |
1256 | } |