MediaWiki  master
WebInstallerOptions.php
Go to the documentation of this file.
1 <?php
22 use Wikimedia\IPUtils;
23 
24 class WebInstallerOptions extends WebInstallerPage {
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' => 'wgLogo',
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', 'wgLogo',
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  $code = $this->getVar( '_LicenseCode' );
490  if ( $code == 'cc-choose' ) {
491  if ( !$this->getVar( '_CCDone' ) ) {
492  $this->parent->showError( 'config-cc-not-chosen' );
493  $retVal = false;
494  }
495  } elseif ( array_key_exists( $code, $this->parent->licenses ) ) {
496  // Messages:
497  // config-license-cc-by, config-license-cc-by-sa, config-license-cc-by-nc-sa,
498  // config-license-cc-0, config-license-pd, config-license-gfdl, config-license-none,
499  // config-license-cc-choose
500  $entry = $this->parent->licenses[$code];
501  $this->setVar( 'wgRightsText',
502  $entry['text'] ?? wfMessage( 'config-license-' . $code )->text() );
503  $this->setVar( 'wgRightsUrl', $entry['url'] );
504  $this->setVar( 'wgRightsIcon', $entry['icon'] );
505  } else {
506  $this->setVar( 'wgRightsText', '' );
507  $this->setVar( 'wgRightsUrl', '' );
508  $this->setVar( 'wgRightsIcon', '' );
509  }
510 
511  $skinsAvailable = array_keys( $this->parent->findExtensions( 'skins' )->value );
512  $skinsToInstall = [];
513  foreach ( $skinsAvailable as $skin ) {
514  $this->parent->setVarsFromRequest( [ "skin-$skin" ] );
515  if ( $this->getVar( "skin-$skin" ) ) {
516  $skinsToInstall[] = $skin;
517  }
518  }
519  $this->parent->setVar( '_Skins', $skinsToInstall );
520 
521  if ( !$skinsToInstall && $skinsAvailable ) {
522  $this->parent->showError( 'config-skins-must-enable-some' );
523  $retVal = false;
524  }
525  $defaultSkin = $this->getVar( 'wgDefaultSkin' );
526  $skinsToInstallLowercase = array_map( 'strtolower', $skinsToInstall );
527  if ( $skinsToInstall && array_search( $defaultSkin, $skinsToInstallLowercase ) === false ) {
528  $this->parent->showError( 'config-skins-must-enable-default' );
529  $retVal = false;
530  }
531 
532  $extsAvailable = array_keys( $this->parent->findExtensions()->value );
533  $extsToInstall = [];
534  foreach ( $extsAvailable as $ext ) {
535  $this->parent->setVarsFromRequest( [ "ext-$ext" ] );
536  if ( $this->getVar( "ext-$ext" ) ) {
537  $extsToInstall[] = $ext;
538  }
539  }
540  $this->parent->setVar( '_Extensions', $extsToInstall );
541 
542  if ( $this->getVar( '_MainCacheType' ) == 'memcached' ) {
543  $memcServers = explode( "\n", $this->getVar( '_MemCachedServers' ) );
544  if ( !$memcServers ) {
545  $this->parent->showError( 'config-memcache-needservers' );
546  $retVal = false;
547  }
548 
549  foreach ( $memcServers as $server ) {
550  $memcParts = explode( ":", $server, 2 );
551  if ( !isset( $memcParts[0] )
552  || ( !IPUtils::isValid( $memcParts[0] )
553  && ( gethostbyname( $memcParts[0] ) == $memcParts[0] ) )
554  ) {
555  $this->parent->showError( 'config-memcache-badip', $memcParts[0] );
556  $retVal = false;
557  } elseif ( !isset( $memcParts[1] ) ) {
558  $this->parent->showError( 'config-memcache-noport', $memcParts[0] );
559  $retVal = false;
560  } elseif ( $memcParts[1] < 1 || $memcParts[1] > 65535 ) {
561  $this->parent->showError( 'config-memcache-badport', 1, 65535 );
562  $retVal = false;
563  }
564  }
565  }
566 
567  return $retVal;
568  }
569 
570 }
wfMessage
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
Definition: GlobalFunctions.php:1265
Xml\encodeJsVar
static encodeJsVar( $value, $pretty=false)
Encode a variable of arbitrary type to JavaScript.
Definition: Xml.php:661
$wgLang
$wgLang
Definition: Setup.php:852
SpecialVersion\getExtensionTypes
static getExtensionTypes()
Returns an array with the base extension types.
Definition: SpecialVersion.php:385
$ext
if(!is_readable( $file)) $ext
Definition: router.php:48
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:52
wfArrayToCgi
wfArrayToCgi( $array1, $array2=null, $prefix='')
This function takes one or two arrays as input, and returns a CGI-style string, e....
Definition: GlobalFunctions.php:347
$type
$type
Definition: testCompression.php:50