MediaWiki REL1_35
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 foreach ( $dependencyMap[$ext]['skins'] as $name ) {
209 $links[] = Html::element(
210 'a',
211 [ 'href' => "#config_skin-$name" ],
212 $name
213 );
214 }
215 }
216
217 $text = wfMessage( 'config-extensions-requires' )
218 ->rawParams( $ext, $wgLang->commaList( $links ) )
219 ->escaped();
220 } else {
221 $text = $ext;
222 }
223 $extHtml .= $this->parent->getCheckBox( [
224 'var' => "ext-$ext",
225 'rawtext' => $text,
226 'attribs' => $attribs,
227 'labelAttribs' => $labelAttribs,
228 ] );
229 }
230 }
231
232 $extHtml .= $this->parent->getHelpBox( 'config-extensions-help' ) .
233 $this->getFieldsetEnd();
234 $this->addHTML( $extHtml );
235 // Push the dependency map to the client side
236 $this->addHTML( Html::inlineScript(
237 'var extDependencyMap = ' . Xml::encodeJsVar( $dependencyMap )
238 ) );
239 }
240
241 // Having / in paths in Windows looks funny :)
242 $this->setVar( 'wgDeletedDirectory',
243 str_replace(
244 '/', DIRECTORY_SEPARATOR,
245 $this->getVar( 'wgDeletedDirectory' )
246 )
247 );
248
249 $uploadwrapperStyle = $this->getVar( 'wgEnableUploads' ) ? '' : 'display: none';
250 $this->addHTML(
251 # Uploading
252 $this->getFieldsetStart( 'config-upload-settings' ) .
253 $this->parent->getCheckBox( [
254 'var' => 'wgEnableUploads',
255 'label' => 'config-upload-enable',
256 'attribs' => [ 'class' => 'showHideRadio', 'rel' => 'uploadwrapper' ],
257 'help' => $this->parent->getHelpBox( 'config-upload-help' )
258 ] ) .
259 '<div id="uploadwrapper" style="' . $uploadwrapperStyle . '">' .
260 $this->parent->getTextBox( [
261 'var' => 'wgDeletedDirectory',
262 'label' => 'config-upload-deleted',
263 'attribs' => [ 'dir' => 'ltr' ],
264 'help' => $this->parent->getHelpBox( 'config-upload-deleted-help' )
265 ] ) .
266 '</div>' .
267 $this->parent->getTextBox( [
268 'var' => '_Logo',
269 'label' => 'config-logo',
270 'attribs' => [ 'dir' => 'ltr' ],
271 'help' => $this->parent->getHelpBox( 'config-logo-help' )
272 ] )
273 );
274 $this->addHTML(
275 $this->parent->getCheckBox( [
276 'var' => 'wgUseInstantCommons',
277 'label' => 'config-instantcommons',
278 'help' => $this->parent->getHelpBox( 'config-instantcommons-help' )
279 ] ) .
280 $this->getFieldsetEnd()
281 );
282
283 $caches = [ 'none' ];
284 $cachevalDefault = 'none';
285
286 if ( count( $this->getVar( '_Caches' ) ) ) {
287 // A CACHE_ACCEL implementation is available
288 $caches[] = 'accel';
289 $cachevalDefault = 'accel';
290 }
291 $caches[] = 'memcached';
292
293 // We'll hide/show this on demand when the value changes, see config.js.
294 $cacheval = $this->getVar( '_MainCacheType' );
295 if ( !$cacheval ) {
296 // We need to set a default here; but don't hardcode it
297 // or we lose it every time we reload the page for validation
298 // or going back!
299 $cacheval = $cachevalDefault;
300 }
301 $hidden = ( $cacheval == 'memcached' ) ? '' : 'display: none';
302 $this->addHTML(
303 # Advanced settings
304 $this->getFieldsetStart( 'config-advanced-settings' ) .
305 # Object cache settings
306 // getRadioSet() builds a set of labeled radio buttons.
307 // For grep: The following messages are used as the item labels:
308 // config-cache-none, config-cache-accel, config-cache-memcached
309 $this->parent->getRadioSet( [
310 'var' => '_MainCacheType',
311 'label' => 'config-cache-options',
312 'itemLabelPrefix' => 'config-cache-',
313 'values' => $caches,
314 'value' => $cacheval,
315 ] ) .
316 $this->parent->getHelpBox( 'config-cache-help' ) .
317 "<div id=\"config-memcachewrapper\" style=\"$hidden\">" .
318 $this->parent->getTextArea( [
319 'var' => '_MemCachedServers',
320 'label' => 'config-memcached-servers',
321 'help' => $this->parent->getHelpBox( 'config-memcached-help' )
322 ] ) .
323 '</div>' .
324 $this->getFieldsetEnd()
325 );
326 $this->endForm();
327
328 return null;
329 }
330
336 private function makeScreenshotsLink( $name, $screenshots ) {
337 global $wgLang;
338 if ( count( $screenshots ) > 1 ) {
339 $links = [];
340 $counter = 1;
341
342 foreach ( $screenshots as $shot ) {
343 $links[] = Html::element(
344 'a',
345 [ 'href' => $shot, 'target' => '_blank' ],
346 $wgLang->formatNum( $counter++ )
347 );
348 }
349 return wfMessage( 'config-skins-screenshots' )
350 ->rawParams( $name, $wgLang->commaList( $links ) )
351 ->escaped();
352 } else {
353 $link = Html::element(
354 'a',
355 [ 'href' => $screenshots[0], 'target' => '_blank' ],
356 wfMessage( 'config-screenshot' )->text()
357 );
358 return wfMessage( 'config-skins-screenshot', $name )->rawParams( $link )->escaped();
359 }
360 }
361
365 public function getCCPartnerUrl() {
366 $server = $this->getVar( 'wgServer' );
367 $exitUrl = $server . $this->parent->getUrl( [
368 'page' => 'Options',
369 'SubmitCC' => 'indeed',
370 'config__LicenseCode' => 'cc',
371 'config_wgRightsUrl' => '[license_url]',
372 'config_wgRightsText' => '[license_name]',
373 'config_wgRightsIcon' => '[license_button]',
374 ] );
375 $styleUrl = $server . dirname( dirname( $this->parent->getUrl() ) ) .
376 '/mw-config/config-cc.css';
377 $iframeUrl = 'https://creativecommons.org/license/?' .
378 wfArrayToCgi( [
379 'partner' => 'MediaWiki',
380 'exit_url' => $exitUrl,
381 'lang' => $this->getVar( '_UserLang' ),
382 'stylesheet' => $styleUrl,
383 ] );
384
385 return $iframeUrl;
386 }
387
391 public function getCCChooser() {
392 $iframeAttribs = [
393 'class' => 'config-cc-iframe',
394 'name' => 'config-cc-iframe',
395 'id' => 'config-cc-iframe',
396 'frameborder' => 0,
397 'width' => '100%',
398 'height' => '100%',
399 ];
400 if ( $this->getVar( '_CCDone' ) ) {
401 $iframeAttribs['src'] = $this->parent->getUrl( [ 'ShowCC' => 'yes' ] );
402 } else {
403 $iframeAttribs['src'] = $this->getCCPartnerUrl();
404 }
405 $wrapperStyle = ( $this->getVar( '_LicenseCode' ) == 'cc-choose' ) ? '' : 'display: none';
406
407 return "<div class=\"config-cc-wrapper\" id=\"config-cc-wrapper\" style=\"$wrapperStyle\">\n" .
408 Html::element( 'iframe', $iframeAttribs ) .
409 "</div>\n";
410 }
411
415 public function getCCDoneBox() {
416 $js = "parent.document.getElementById('config-cc-wrapper').style.height = '$1';";
417 // If you change this height, also change it in config.css
418 $expandJs = str_replace( '$1', '54em', $js );
419 $reduceJs = str_replace( '$1', '70px', $js );
420
421 return '<p>' .
422 Html::element( 'img', [ 'src' => $this->getVar( 'wgRightsIcon' ) ] ) .
423 "\u{00A0}\u{00A0}" .
424 htmlspecialchars( $this->getVar( 'wgRightsText' ) ) .
425 "</p>\n" .
426 "<p style=\"text-align: center;\">" .
427 Html::element( 'a',
428 [
429 'href' => $this->getCCPartnerUrl(),
430 'onclick' => $expandJs,
431 ],
432 wfMessage( 'config-cc-again' )->text()
433 ) .
434 "</p>\n" .
435 "<script>\n" .
436 # Reduce the wrapper div height
437 htmlspecialchars( $reduceJs ) .
438 "\n" .
439 "</script>\n";
440 }
441
442 public function submitCC() {
443 $newValues = $this->parent->setVarsFromRequest(
444 [ 'wgRightsUrl', 'wgRightsText', 'wgRightsIcon' ] );
445 if ( count( $newValues ) != 3 ) {
446 $this->parent->showError( 'config-cc-error' );
447
448 return;
449 }
450 $this->setVar( '_CCDone', true );
451 $this->addHTML( $this->getCCDoneBox() );
452 }
453
460 public function submitSkins() {
461 $skins = array_keys( $this->parent->findExtensions( 'skins' )->value );
462 $this->parent->setVar( '_Skins', $skins );
463
464 if ( $skins ) {
465 $skinNames = array_map( 'strtolower', $skins );
466 $this->parent->setVar( 'wgDefaultSkin', $this->parent->getDefaultSkin( $skinNames ) );
467 }
468
469 return true;
470 }
471
475 public function submit() {
476 $this->parent->setVarsFromRequest( [ '_RightsProfile', '_LicenseCode',
477 'wgEnableEmail', 'wgPasswordSender', 'wgEnableUploads', '_Logo',
478 'wgEnableUserEmail', 'wgEnotifUserTalk', 'wgEnotifWatchlist',
479 'wgEmailAuthentication', '_MainCacheType', '_MemCachedServers',
480 'wgUseInstantCommons', 'wgDefaultSkin' ] );
481
482 $retVal = true;
483
484 if ( !array_key_exists( $this->getVar( '_RightsProfile' ), $this->parent->rightsProfiles ) ) {
485 reset( $this->parent->rightsProfiles );
486 $this->setVar( '_RightsProfile', key( $this->parent->rightsProfiles ) );
487 }
488
489 // If this is empty, either the default got lost internally
490 // or the user blanked it
491 if ( strval( $this->getVar( '_Logo' ) ) === '' ) {
492 $this->parent->showError( 'config-install-logo-blank' );
493 $retVal = false;
494 }
495
496 $code = $this->getVar( '_LicenseCode' );
497 if ( $code == 'cc-choose' ) {
498 if ( !$this->getVar( '_CCDone' ) ) {
499 $this->parent->showError( 'config-cc-not-chosen' );
500 $retVal = false;
501 }
502 } elseif ( array_key_exists( $code, $this->parent->licenses ) ) {
503 // Messages:
504 // config-license-cc-by, config-license-cc-by-sa, config-license-cc-by-nc-sa,
505 // config-license-cc-0, config-license-pd, config-license-gfdl, config-license-none,
506 // config-license-cc-choose
507 $entry = $this->parent->licenses[$code];
508 $this->setVar( 'wgRightsText',
509 $entry['text'] ?? wfMessage( 'config-license-' . $code )->text() );
510 $this->setVar( 'wgRightsUrl', $entry['url'] );
511 $this->setVar( 'wgRightsIcon', $entry['icon'] );
512 } else {
513 $this->setVar( 'wgRightsText', '' );
514 $this->setVar( 'wgRightsUrl', '' );
515 $this->setVar( 'wgRightsIcon', '' );
516 }
517
518 $skinsAvailable = array_keys( $this->parent->findExtensions( 'skins' )->value );
519 $skinsToInstall = [];
520 foreach ( $skinsAvailable as $skin ) {
521 $this->parent->setVarsFromRequest( [ "skin-$skin" ] );
522 if ( $this->getVar( "skin-$skin" ) ) {
523 $skinsToInstall[] = $skin;
524 }
525 }
526 $this->parent->setVar( '_Skins', $skinsToInstall );
527
528 if ( !$skinsToInstall && $skinsAvailable ) {
529 $this->parent->showError( 'config-skins-must-enable-some' );
530 $retVal = false;
531 }
532 $defaultSkin = $this->getVar( 'wgDefaultSkin' );
533 $skinsToInstallLowercase = array_map( 'strtolower', $skinsToInstall );
534 if ( $skinsToInstall && array_search( $defaultSkin, $skinsToInstallLowercase ) === false ) {
535 $this->parent->showError( 'config-skins-must-enable-default' );
536 $retVal = false;
537 }
538
539 $extsAvailable = array_keys( $this->parent->findExtensions()->value );
540 $extsToInstall = [];
541 foreach ( $extsAvailable as $ext ) {
542 $this->parent->setVarsFromRequest( [ "ext-$ext" ] );
543 if ( $this->getVar( "ext-$ext" ) ) {
544 $extsToInstall[] = $ext;
545 }
546 }
547 $this->parent->setVar( '_Extensions', $extsToInstall );
548
549 if ( $this->getVar( '_MainCacheType' ) == 'memcached' ) {
550 $memcServers = explode( "\n", $this->getVar( '_MemCachedServers' ) );
551 if ( !$memcServers ) {
552 $this->parent->showError( 'config-memcache-needservers' );
553 $retVal = false;
554 }
555
556 foreach ( $memcServers as $server ) {
557 $memcParts = explode( ":", $server, 2 );
558 if ( !isset( $memcParts[0] )
559 || ( !IPUtils::isValid( $memcParts[0] )
560 && ( gethostbyname( $memcParts[0] ) == $memcParts[0] ) )
561 ) {
562 $this->parent->showError( 'config-memcache-badip', $memcParts[0] );
563 $retVal = false;
564 } elseif ( !isset( $memcParts[1] ) ) {
565 $this->parent->showError( 'config-memcache-noport', $memcParts[0] );
566 $retVal = false;
567 } elseif ( $memcParts[1] < 1 || $memcParts[1] > 65535 ) {
568 $this->parent->showError( 'config-memcache-badport', 1, 65535 );
569 $retVal = false;
570 }
571 }
572 }
573
574 return $retVal;
575 }
576
577}
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:781
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:60
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