MediaWiki REL1_37
WebInstallerOptions.php
Go to the documentation of this file.
1<?php
22use Wikimedia\IPUtils;
23
25
29 public function execute() {
30 global $wgLang;
31
32 if ( $this->getVar( '_SkipOptional' ) == 'skip' ) {
33 $this->submitSkins();
34 return 'skip';
35 }
36 if ( $this->parent->request->wasPosted() && $this->submit() ) {
37 return 'continue';
38 }
39
40 $emailwrapperStyle = $this->getVar( 'wgEnableEmail' ) ? '' : 'display: none';
41 $this->startForm();
42 $this->addHTML(
43 # User Rights
44 // getRadioSet() builds a set of labeled radio buttons.
45 // For grep: The following messages are used as the item labels:
46 // config-profile-wiki, config-profile-no-anon, config-profile-fishbowl, config-profile-private
47 $this->parent->getRadioSet( [
48 'var' => '_RightsProfile',
49 'label' => 'config-profile',
50 'itemLabelPrefix' => 'config-profile-',
51 'values' => array_keys( $this->parent->rightsProfiles ),
52 ] ) .
53 $this->parent->getInfoBox( wfMessage( 'config-profile-help' )->plain() ) .
54
55 # Licensing
56 // getRadioSet() builds a set of labeled radio buttons.
57 // For grep: The following messages are used as the item labels:
58 // config-license-cc-by, config-license-cc-by-sa, config-license-cc-by-nc-sa,
59 // config-license-cc-0, config-license-pd, config-license-gfdl,
60 // config-license-none, config-license-cc-choose
61 $this->parent->getRadioSet( [
62 'var' => '_LicenseCode',
63 'label' => 'config-license',
64 'itemLabelPrefix' => 'config-license-',
65 'values' => array_keys( $this->parent->licenses ),
66 'commonAttribs' => [ 'class' => 'licenseRadio' ],
67 ] ) .
68 $this->getCCChooser() .
69 $this->parent->getHelpBox( 'config-license-help' ) .
70
71 # E-mail
72 $this->getFieldsetStart( 'config-email-settings' ) .
73 $this->parent->getCheckBox( [
74 'var' => 'wgEnableEmail',
75 'label' => 'config-enable-email',
76 'attribs' => [ 'class' => 'showHideRadio', 'rel' => 'emailwrapper' ],
77 ] ) .
78 $this->parent->getHelpBox( 'config-enable-email-help' ) .
79 "<div id=\"emailwrapper\" style=\"$emailwrapperStyle\">" .
80 $this->parent->getTextBox( [
81 'var' => 'wgPasswordSender',
82 'label' => 'config-email-sender'
83 ] ) .
84 $this->parent->getHelpBox( 'config-email-sender-help' ) .
85 $this->parent->getCheckBox( [
86 'var' => 'wgEnableUserEmail',
87 'label' => 'config-email-user',
88 ] ) .
89 $this->parent->getHelpBox( 'config-email-user-help' ) .
90 $this->parent->getCheckBox( [
91 'var' => 'wgEnotifUserTalk',
92 'label' => 'config-email-usertalk',
93 ] ) .
94 $this->parent->getHelpBox( 'config-email-usertalk-help' ) .
95 $this->parent->getCheckBox( [
96 'var' => 'wgEnotifWatchlist',
97 'label' => 'config-email-watchlist',
98 ] ) .
99 $this->parent->getHelpBox( 'config-email-watchlist-help' ) .
100 $this->parent->getCheckBox( [
101 'var' => 'wgEmailAuthentication',
102 'label' => 'config-email-auth',
103 ] ) .
104 $this->parent->getHelpBox( 'config-email-auth-help' ) .
105 "</div>" .
106 $this->getFieldsetEnd()
107 );
108
109 $skins = $this->parent->findExtensions( 'skins' )->value;
110 '@phan-var array[] $skins';
111 $skinHtml = $this->getFieldsetStart( 'config-skins' );
112
113 $skinNames = array_map( 'strtolower', array_keys( $skins ) );
114 $chosenSkinName = $this->getVar( 'wgDefaultSkin', $this->parent->getDefaultSkin( $skinNames ) );
115
116 if ( $skins ) {
117 $radioButtons = $this->parent->getRadioElements( [
118 'var' => 'wgDefaultSkin',
119 'itemLabels' => array_fill_keys( $skinNames, 'config-skins-use-as-default' ),
120 'values' => $skinNames,
121 'value' => $chosenSkinName,
122 ] );
123
124 foreach ( $skins as $skin => $info ) {
125 if ( isset( $info['screenshots'] ) ) {
126 $screenshotText = $this->makeScreenshotsLink( $skin, $info['screenshots'] );
127 } else {
128 $screenshotText = htmlspecialchars( $skin );
129 }
130 $skinHtml .=
131 '<div class="config-skins-item">' .
132 $this->parent->getCheckBox( [
133 'var' => "skin-$skin",
134 'rawtext' => $screenshotText,
135 'value' => $this->getVar( "skin-$skin", true ), // all found skins enabled by default
136 ] ) .
137 '<div class="config-skins-use-as-default">' . $radioButtons[strtolower( $skin )] . '</div>' .
138 '</div>';
139 }
140 } else {
141 $skinHtml .=
142 Html::warningBox( wfMessage( 'config-skins-missing' )->plain(), 'config-warning-box' ) .
143 Html::hidden( 'config_wgDefaultSkin', $chosenSkinName );
144 }
145
146 $skinHtml .= $this->parent->getHelpBox( 'config-skins-help' ) .
147 $this->getFieldsetEnd();
148 $this->addHTML( $skinHtml );
149
150 $extensions = $this->parent->findExtensions()->value;
151 '@phan-var array[] $extensions';
152 $dependencyMap = [];
153
154 if ( $extensions ) {
155 $extHtml = $this->getFieldsetStart( 'config-extensions' );
156
157 $extByType = [];
159 // Sort by type first
160 foreach ( $extensions as $ext => $info ) {
161 if ( !isset( $info['type'] ) || !isset( $types[$info['type']] ) ) {
162 // We let extensions normally define custom types, but
163 // since we aren't loading extensions, we'll have to
164 // categorize them under other
165 $info['type'] = 'other';
166 }
167 $extByType[$info['type']][$ext] = $info;
168 }
169
170 foreach ( $types as $type => $message ) {
171 if ( !isset( $extByType[$type] ) ) {
172 continue;
173 }
174 $extHtml .= Html::element( 'h2', [], $message );
175 foreach ( $extByType[$type] as $ext => $info ) {
176 $urlText = '';
177 if ( isset( $info['url'] ) ) {
178 $urlText = ' ' . Html::element( 'a', [ 'href' => $info['url'] ], '(more information)' );
179 }
180 $attribs = [
181 'data-name' => $ext,
182 'class' => 'config-ext-input'
183 ];
184 $labelAttribs = [];
185 $fullDepList = [];
186 if ( isset( $info['requires']['extensions'] ) ) {
187 $dependencyMap[$ext]['extensions'] = $info['requires']['extensions'];
188 $labelAttribs['class'] = 'mw-ext-with-dependencies';
189 }
190 if ( isset( $info['requires']['skins'] ) ) {
191 $dependencyMap[$ext]['skins'] = $info['requires']['skins'];
192 $labelAttribs['class'] = 'mw-ext-with-dependencies';
193 }
194 if ( isset( $dependencyMap[$ext] ) ) {
195 $links = [];
196 // For each dependency, link to the checkbox for each
197 // extension/skin that is required
198 if ( isset( $dependencyMap[$ext]['extensions'] ) ) {
199 foreach ( $dependencyMap[$ext]['extensions'] as $name ) {
200 $links[] = Html::element(
201 'a',
202 [ 'href' => "#config_ext-$name" ],
203 $name
204 );
205 }
206 }
207 if ( isset( $dependencyMap[$ext]['skins'] ) ) {
208 // @phan-suppress-next-line PhanTypeMismatchForeach Phan internal bug
209 foreach ( $dependencyMap[$ext]['skins'] as $name ) {
210 $links[] = Html::element(
211 'a',
212 [ 'href' => "#config_skin-$name" ],
213 $name
214 );
215 }
216 }
217
218 // @phan-suppress-next-line SecurityCheck-XSS
219 $text = wfMessage( 'config-extensions-requires' )
220 ->rawParams( $ext, $wgLang->commaList( $links ) )
221 ->escaped();
222 } else {
223 $text = $ext;
224 }
225 $extHtml .= $this->parent->getCheckBox( [
226 'var' => "ext-$ext",
227 'rawtext' => $text,
228 'attribs' => $attribs,
229 'labelAttribs' => $labelAttribs,
230 ] );
231 }
232 }
233
234 $extHtml .= $this->parent->getHelpBox( 'config-extensions-help' ) .
235 $this->getFieldsetEnd();
236 $this->addHTML( $extHtml );
237 // Push the dependency map to the client side
238 $this->addHTML( Html::inlineScript(
239 'var extDependencyMap = ' . Xml::encodeJsVar( $dependencyMap )
240 ) );
241 }
242
243 // Having / in paths in Windows looks funny :)
244 $this->setVar( 'wgDeletedDirectory',
245 str_replace(
246 '/', DIRECTORY_SEPARATOR,
247 $this->getVar( 'wgDeletedDirectory' )
248 )
249 );
250
251 $uploadwrapperStyle = $this->getVar( 'wgEnableUploads' ) ? '' : 'display: none';
252 $this->addHTML(
253 # Uploading
254 $this->getFieldsetStart( 'config-upload-settings' ) .
255 $this->parent->getCheckBox( [
256 'var' => 'wgEnableUploads',
257 'label' => 'config-upload-enable',
258 'attribs' => [ 'class' => 'showHideRadio', 'rel' => 'uploadwrapper' ],
259 'help' => $this->parent->getHelpBox( 'config-upload-help' )
260 ] ) .
261 '<div id="uploadwrapper" style="' . $uploadwrapperStyle . '">' .
262 $this->parent->getTextBox( [
263 'var' => 'wgDeletedDirectory',
264 'label' => 'config-upload-deleted',
265 'attribs' => [ 'dir' => 'ltr' ],
266 'help' => $this->parent->getHelpBox( 'config-upload-deleted-help' )
267 ] ) .
268 '</div>' .
269 $this->parent->getTextBox( [
270 'var' => '_Logo',
271 'label' => 'config-logo',
272 'attribs' => [ 'dir' => 'ltr' ],
273 'help' => $this->parent->getHelpBox( 'config-logo-help' )
274 ] )
275 );
276 $this->addHTML(
277 $this->parent->getCheckBox( [
278 'var' => 'wgUseInstantCommons',
279 'label' => 'config-instantcommons',
280 'help' => $this->parent->getHelpBox( 'config-instantcommons-help' )
281 ] ) .
282 $this->getFieldsetEnd()
283 );
284
285 $caches = [ 'none' ];
286 $cachevalDefault = 'none';
287
288 if ( count( $this->getVar( '_Caches' ) ) ) {
289 // A CACHE_ACCEL implementation is available
290 $caches[] = 'accel';
291 $cachevalDefault = 'accel';
292 }
293 $caches[] = 'memcached';
294
295 // We'll hide/show this on demand when the value changes, see config.js.
296 $cacheval = $this->getVar( '_MainCacheType' );
297 if ( !$cacheval ) {
298 // We need to set a default here; but don't hardcode it
299 // or we lose it every time we reload the page for validation
300 // or going back!
301 $cacheval = $cachevalDefault;
302 }
303 $hidden = ( $cacheval == 'memcached' ) ? '' : 'display: none';
304 $this->addHTML(
305 # Advanced settings
306 $this->getFieldsetStart( 'config-advanced-settings' ) .
307 # Object cache settings
308 // getRadioSet() builds a set of labeled radio buttons.
309 // For grep: The following messages are used as the item labels:
310 // config-cache-none, config-cache-accel, config-cache-memcached
311 $this->parent->getRadioSet( [
312 'var' => '_MainCacheType',
313 'label' => 'config-cache-options',
314 'itemLabelPrefix' => 'config-cache-',
315 'values' => $caches,
316 'value' => $cacheval,
317 ] ) .
318 $this->parent->getHelpBox( 'config-cache-help' ) .
319 "<div id=\"config-memcachewrapper\" style=\"$hidden\">" .
320 $this->parent->getTextArea( [
321 'var' => '_MemCachedServers',
322 'label' => 'config-memcached-servers',
323 'help' => $this->parent->getHelpBox( 'config-memcached-help' )
324 ] ) .
325 '</div>' .
326 $this->getFieldsetEnd()
327 );
328 $this->endForm();
329
330 return null;
331 }
332
338 private function makeScreenshotsLink( $name, $screenshots ) {
339 global $wgLang;
340 if ( count( $screenshots ) > 1 ) {
341 $links = [];
342 $counter = 1;
343
344 foreach ( $screenshots as $shot ) {
345 $links[] = Html::element(
346 'a',
347 [ 'href' => $shot, 'target' => '_blank' ],
348 $wgLang->formatNum( $counter++ )
349 );
350 }
351 return wfMessage( 'config-skins-screenshots' )
352 ->rawParams( $name, $wgLang->commaList( $links ) )
353 ->escaped();
354 } else {
355 $link = Html::element(
356 'a',
357 [ 'href' => $screenshots[0], 'target' => '_blank' ],
358 wfMessage( 'config-screenshot' )->text()
359 );
360 return wfMessage( 'config-skins-screenshot', $name )->rawParams( $link )->escaped();
361 }
362 }
363
367 public function getCCPartnerUrl() {
368 $server = $this->getVar( 'wgServer' );
369 $exitUrl = $server . $this->parent->getUrl( [
370 'page' => 'Options',
371 'SubmitCC' => 'indeed',
372 'config__LicenseCode' => 'cc',
373 'config_wgRightsUrl' => '[license_url]',
374 'config_wgRightsText' => '[license_name]',
375 'config_wgRightsIcon' => '[license_button]',
376 ] );
377 $styleUrl = $server . dirname( dirname( $this->parent->getUrl() ) ) .
378 '/mw-config/config-cc.css';
379 $iframeUrl = 'https://creativecommons.org/license/?' .
380 wfArrayToCgi( [
381 'partner' => 'MediaWiki',
382 'exit_url' => $exitUrl,
383 'lang' => $this->getVar( '_UserLang' ),
384 'stylesheet' => $styleUrl,
385 ] );
386
387 return $iframeUrl;
388 }
389
393 public function getCCChooser() {
394 $iframeAttribs = [
395 'class' => 'config-cc-iframe',
396 'name' => 'config-cc-iframe',
397 'id' => 'config-cc-iframe',
398 'frameborder' => 0,
399 'width' => '100%',
400 'height' => '100%',
401 ];
402 if ( $this->getVar( '_CCDone' ) ) {
403 $iframeAttribs['src'] = $this->parent->getUrl( [ 'ShowCC' => 'yes' ] );
404 } else {
405 $iframeAttribs['src'] = $this->getCCPartnerUrl();
406 }
407 $wrapperStyle = ( $this->getVar( '_LicenseCode' ) == 'cc-choose' ) ? '' : 'display: none';
408
409 return "<div class=\"config-cc-wrapper\" id=\"config-cc-wrapper\" style=\"$wrapperStyle\">\n" .
410 Html::element( 'iframe', $iframeAttribs ) .
411 "</div>\n";
412 }
413
417 public function getCCDoneBox() {
418 $js = "parent.document.getElementById('config-cc-wrapper').style.height = '$1';";
419 // If you change this height, also change it in config.css
420 $expandJs = str_replace( '$1', '54em', $js );
421 $reduceJs = str_replace( '$1', '70px', $js );
422
423 return '<p>' .
424 Html::element( 'img', [ 'src' => $this->getVar( 'wgRightsIcon' ) ] ) .
425 "\u{00A0}\u{00A0}" .
426 htmlspecialchars( $this->getVar( 'wgRightsText' ) ) .
427 "</p>\n" .
428 "<p style=\"text-align: center;\">" .
429 Html::element( 'a',
430 [
431 'href' => $this->getCCPartnerUrl(),
432 'onclick' => $expandJs,
433 ],
434 wfMessage( 'config-cc-again' )->text()
435 ) .
436 "</p>\n" .
437 "<script>\n" .
438 # Reduce the wrapper div height
439 htmlspecialchars( $reduceJs ) .
440 "\n" .
441 "</script>\n";
442 }
443
444 public function submitCC() {
445 $newValues = $this->parent->setVarsFromRequest(
446 [ 'wgRightsUrl', 'wgRightsText', 'wgRightsIcon' ] );
447 if ( count( $newValues ) != 3 ) {
448 $this->parent->showError( 'config-cc-error' );
449
450 return;
451 }
452 $this->setVar( '_CCDone', true );
453 $this->addHTML( $this->getCCDoneBox() );
454 }
455
462 public function submitSkins() {
463 $skins = array_keys( $this->parent->findExtensions( 'skins' )->value );
464 $this->parent->setVar( '_Skins', $skins );
465
466 if ( $skins ) {
467 $skinNames = array_map( 'strtolower', $skins );
468 $this->parent->setVar( 'wgDefaultSkin', $this->parent->getDefaultSkin( $skinNames ) );
469 }
470
471 return true;
472 }
473
477 public function submit() {
478 $this->parent->setVarsFromRequest( [ '_RightsProfile', '_LicenseCode',
479 'wgEnableEmail', 'wgPasswordSender', 'wgEnableUploads', '_Logo',
480 'wgEnableUserEmail', 'wgEnotifUserTalk', 'wgEnotifWatchlist',
481 'wgEmailAuthentication', '_MainCacheType', '_MemCachedServers',
482 'wgUseInstantCommons', 'wgDefaultSkin' ] );
483
484 $retVal = true;
485
486 if ( !array_key_exists( $this->getVar( '_RightsProfile' ), $this->parent->rightsProfiles ) ) {
487 reset( $this->parent->rightsProfiles );
488 $this->setVar( '_RightsProfile', key( $this->parent->rightsProfiles ) );
489 }
490
491 // If this is empty, either the default got lost internally
492 // or the user blanked it
493 if ( strval( $this->getVar( '_Logo' ) ) === '' ) {
494 $this->parent->showError( 'config-install-logo-blank' );
495 $retVal = false;
496 }
497
498 $code = $this->getVar( '_LicenseCode' );
499 if ( $code == 'cc-choose' ) {
500 if ( !$this->getVar( '_CCDone' ) ) {
501 $this->parent->showError( 'config-cc-not-chosen' );
502 $retVal = false;
503 }
504 } elseif ( array_key_exists( $code, $this->parent->licenses ) ) {
505 // Messages:
506 // config-license-cc-by, config-license-cc-by-sa, config-license-cc-by-nc-sa,
507 // config-license-cc-0, config-license-pd, config-license-gfdl, config-license-none,
508 // config-license-cc-choose
509 $entry = $this->parent->licenses[$code];
510 $this->setVar( 'wgRightsText',
511 $entry['text'] ?? wfMessage( 'config-license-' . $code )->text() );
512 $this->setVar( 'wgRightsUrl', $entry['url'] );
513 $this->setVar( 'wgRightsIcon', $entry['icon'] );
514 } else {
515 $this->setVar( 'wgRightsText', '' );
516 $this->setVar( 'wgRightsUrl', '' );
517 $this->setVar( 'wgRightsIcon', '' );
518 }
519
520 $skinsAvailable = array_keys( $this->parent->findExtensions( 'skins' )->value );
521 $skinsToInstall = [];
522 foreach ( $skinsAvailable as $skin ) {
523 $this->parent->setVarsFromRequest( [ "skin-$skin" ] );
524 if ( $this->getVar( "skin-$skin" ) ) {
525 $skinsToInstall[] = $skin;
526 }
527 }
528 $this->parent->setVar( '_Skins', $skinsToInstall );
529
530 if ( !$skinsToInstall && $skinsAvailable ) {
531 $this->parent->showError( 'config-skins-must-enable-some' );
532 $retVal = false;
533 }
534 $defaultSkin = $this->getVar( 'wgDefaultSkin' );
535 $skinsToInstallLowercase = array_map( 'strtolower', $skinsToInstall );
536 if ( $skinsToInstall && array_search( $defaultSkin, $skinsToInstallLowercase ) === false ) {
537 $this->parent->showError( 'config-skins-must-enable-default' );
538 $retVal = false;
539 }
540
541 $extsAvailable = array_keys( $this->parent->findExtensions()->value );
542 $extsToInstall = [];
543 foreach ( $extsAvailable as $ext ) {
544 $this->parent->setVarsFromRequest( [ "ext-$ext" ] );
545 if ( $this->getVar( "ext-$ext" ) ) {
546 $extsToInstall[] = $ext;
547 }
548 }
549 $this->parent->setVar( '_Extensions', $extsToInstall );
550
551 if ( $this->getVar( '_MainCacheType' ) == 'memcached' ) {
552 $memcServers = explode( "\n", $this->getVar( '_MemCachedServers' ) );
553 if ( !$memcServers ) {
554 $this->parent->showError( 'config-memcache-needservers' );
555 $retVal = false;
556 }
557
558 foreach ( $memcServers as $server ) {
559 $memcParts = explode( ":", $server, 2 );
560 if ( !isset( $memcParts[0] )
561 || ( !IPUtils::isValid( $memcParts[0] )
562 && ( gethostbyname( $memcParts[0] ) == $memcParts[0] ) )
563 ) {
564 $this->parent->showError( 'config-memcache-badip', $memcParts[0] );
565 $retVal = false;
566 } elseif ( !isset( $memcParts[1] ) ) {
567 $this->parent->showError( 'config-memcache-noport', $memcParts[0] );
568 $retVal = false;
569 } elseif ( $memcParts[1] < 1 || $memcParts[1] > 65535 ) {
570 $this->parent->showError( 'config-memcache-badport', 1, 65535 );
571 $retVal = false;
572 }
573 }
574 }
575
576 return $retVal;
577 }
578
579}
wfArrayToCgi( $array1, $array2=null, $prefix='')
This function takes one or two arrays as input, and returns a CGI-style string, e....
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
$wgLang
Definition Setup.php:831
static getExtensionTypes()
Returns an array with the base extension types.
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition User.php:69
makeScreenshotsLink( $name, $screenshots)
submitSkins()
If the user skips this installer page, we still need to set up the default skins, but ignore everythi...
Abstract class to define pages for the web installer.
getFieldsetEnd()
Get the end tag of a fieldset.
setVar( $name, $value)
endForm( $continue='continue', $back='back')
getVar( $var, $default=null)
getFieldsetStart( $legend)
Get the starting tags of a fieldset.
if(!is_readable( $file)) $ext
Definition router.php:48