MediaWiki REL1_40
WebInstallerOptions.php
Go to the documentation of this file.
1<?php
23use Wikimedia\IPUtils;
24
26
30 public function execute() {
31 if ( $this->getVar( '_SkipOptional' ) == 'skip' ) {
32 $this->submitSkins();
33 return 'skip';
34 }
35 if ( $this->parent->request->wasPosted() && $this->submit() ) {
36 return 'continue';
37 }
38
39 $this->startForm();
40 $this->addModeOptions();
41 $this->addEmailOptions();
42 $this->addSkinOptions();
43 $this->addExtensionOptions();
44 $this->addFileOptions();
45 $this->addPersonalizationOptions();
46 $this->addAdvancedOptions();
47 $this->endForm();
48
49 return null;
50 }
51
52 private function addPersonalizationOptions() {
54 $this->addHTML(
55 $this->getFieldsetStart( 'config-personalization-settings' ) .
56 Html::rawElement( 'div', [
57 'class' => 'config-drag-drop'
58 ], wfMessage( 'config-logo-summary' )->parse() ) .
59 Html::openElement( 'div', [
60 'class' => 'config-personalization-options'
61 ] ) .
62 Html::hidden( 'config_LogoSiteName', $this->getVar( 'wgSitename' ) ) .
64 'var' => '_LogoIcon',
65 // Single quotes are intentional, LocalSettingsGenerator must output this unescaped.
66 'value' => '$wgResourceBasePath/resources/assets/change-your-logo.svg',
67 'label' => 'config-logo-icon',
68 'attribs' => [ 'dir' => 'ltr' ],
69 'help' => $parent->getHelpBox( 'config-logo-icon-help' )
70 ] ) .
72 'var' => '_LogoWordmark',
73 'label' => 'config-logo-wordmark',
74 'attribs' => [ 'dir' => 'ltr' ],
75 'help' => $parent->getHelpBox( 'config-logo-wordmark-help' )
76 ] ) .
78 'var' => '_LogoTagline',
79 'label' => 'config-logo-tagline',
80 'attribs' => [ 'dir' => 'ltr' ],
81 'help' => $parent->getHelpBox( 'config-logo-tagline-help' )
82 ] ) .
84 'var' => '_Logo1x',
85 'label' => 'config-logo-sidebar',
86 'attribs' => [ 'dir' => 'ltr' ],
87 'help' => $parent->getHelpBox( 'config-logo-sidebar-help' )
88 ] ) .
89 Html::openElement( 'div', [
90 'class' => 'logo-preview-area',
91 'data-main-page' => wfMessage( 'config-logo-preview-main' ),
92 'data-filedrop' => wfMessage( 'config-logo-filedrop' )
93 ] ) .
94 Html::closeElement( 'div' ) .
95 Html::closeElement( 'div' ) .
96 $this->getFieldsetEnd()
97 );
98 }
99
104 private function addModeOptions(): void {
105 $this->addHTML(
106 # User Rights
107 // getRadioSet() builds a set of labeled radio buttons.
108 // For grep: The following messages are used as the item labels:
109 // config-profile-wiki, config-profile-no-anon, config-profile-fishbowl, config-profile-private
110 $this->parent->getRadioSet( [
111 'var' => '_RightsProfile',
112 'label' => 'config-profile',
113 'itemLabelPrefix' => 'config-profile-',
114 'values' => array_keys( $this->parent->rightsProfiles ),
115 ] ) .
116 $this->parent->getInfoBox( wfMessage( 'config-profile-help' )->plain() ) .
117
118 # Licensing
119 // getRadioSet() builds a set of labeled radio buttons.
120 // For grep: The following messages are used as the item labels:
121 // config-license-cc-by, config-license-cc-by-sa, config-license-cc-by-nc-sa,
122 // config-license-cc-0, config-license-pd, config-license-gfdl,
123 // config-license-none, config-license-cc-choose
124 $this->parent->getRadioSet( [
125 'var' => '_LicenseCode',
126 'label' => 'config-license',
127 'itemLabelPrefix' => 'config-license-',
128 'values' => array_keys( $this->parent->licenses ),
129 'commonAttribs' => [ 'class' => 'licenseRadio' ],
130 ] ) .
131 $this->getCCChooser() .
132 $this->parent->getHelpBox( 'config-license-help' )
133 );
134 }
135
140 private function addEmailOptions(): void {
141 $emailwrapperStyle = $this->getVar( 'wgEnableEmail' ) ? '' : 'display: none';
142 $this->addHTML(
143 $this->getFieldsetStart( 'config-email-settings' ) .
144 $this->parent->getCheckBox( [
145 'var' => 'wgEnableEmail',
146 'label' => 'config-enable-email',
147 'attribs' => [ 'class' => 'showHideRadio', 'rel' => 'emailwrapper' ],
148 ] ) .
149 $this->parent->getHelpBox( 'config-enable-email-help' ) .
150 "<div id=\"emailwrapper\" style=\"$emailwrapperStyle\">" .
151 $this->parent->getTextBox( [
152 'var' => 'wgPasswordSender',
153 'label' => 'config-email-sender'
154 ] ) .
155 $this->parent->getHelpBox( 'config-email-sender-help' ) .
156 $this->parent->getCheckBox( [
157 'var' => 'wgEnableUserEmail',
158 'label' => 'config-email-user',
159 ] ) .
160 $this->parent->getHelpBox( 'config-email-user-help' ) .
161 $this->parent->getCheckBox( [
162 'var' => 'wgEnotifUserTalk',
163 'label' => 'config-email-usertalk',
164 ] ) .
165 $this->parent->getHelpBox( 'config-email-usertalk-help' ) .
166 $this->parent->getCheckBox( [
167 'var' => 'wgEnotifWatchlist',
168 'label' => 'config-email-watchlist',
169 ] ) .
170 $this->parent->getHelpBox( 'config-email-watchlist-help' ) .
171 $this->parent->getCheckBox( [
172 'var' => 'wgEmailAuthentication',
173 'label' => 'config-email-auth',
174 ] ) .
175 $this->parent->getHelpBox( 'config-email-auth-help' ) .
176 "</div>" .
177 $this->getFieldsetEnd()
178 );
179 }
180
185 private function addSkinOptions(): void {
186 $skins = $this->parent->findExtensions( 'skins' )->value;
187 '@phan-var array[] $skins';
188 $skinHtml = $this->getFieldsetStart( 'config-skins' );
189
190 $skinNames = array_map( 'strtolower', array_keys( $skins ) );
191 $chosenSkinName = $this->getVar( 'wgDefaultSkin', $this->parent->getDefaultSkin( $skinNames ) );
192
193 if ( $skins ) {
194 $radioButtons = $this->parent->getRadioElements( [
195 'var' => 'wgDefaultSkin',
196 'itemLabels' => array_fill_keys( $skinNames, 'config-skins-use-as-default' ),
197 'values' => $skinNames,
198 'value' => $chosenSkinName,
199 ] );
200
201 foreach ( $skins as $skin => $info ) {
202 if ( isset( $info['screenshots'] ) ) {
203 $screenshotText = $this->makeScreenshotsLink( $skin, $info['screenshots'] );
204 } else {
205 $screenshotText = htmlspecialchars( $skin );
206 }
207 $skinHtml .=
208 '<div class="config-skins-item">' .
209 $this->parent->getCheckBox( [
210 'var' => "skin-$skin",
211 'rawtext' => $screenshotText . $this->makeMoreInfoLink( $info ),
212 'value' => $this->getVar( "skin-$skin", true ), // all found skins enabled by default
213 ] ) .
214 '<div class="config-skins-use-as-default">' . $radioButtons[strtolower( $skin )] . '</div>' .
215 '</div>';
216 }
217 } else {
218 $skinHtml .=
219 Html::warningBox( wfMessage( 'config-skins-missing' )->plain(), 'config-warning-box' ) .
220 Html::hidden( 'config_wgDefaultSkin', $chosenSkinName );
221 }
222
223 $skinHtml .= $this->parent->getHelpBox( 'config-skins-help' ) .
224 $this->getFieldsetEnd();
225 $this->addHTML( $skinHtml );
226 }
227
232 private function addExtensionOptions(): void {
233 global $wgLang;
234
235 $extensions = $this->parent->findExtensions()->value;
236 '@phan-var array[] $extensions';
237 $dependencyMap = [];
238
239 if ( $extensions ) {
240 $extHtml = $this->getFieldsetStart( 'config-extensions' );
241
242 $extByType = [];
244 // Sort by type first
245 foreach ( $extensions as $ext => $info ) {
246 if ( !isset( $info['type'] ) || !isset( $types[$info['type']] ) ) {
247 // We let extensions normally define custom types, but
248 // since we aren't loading extensions, we'll have to
249 // categorize them under other
250 $info['type'] = 'other';
251 }
252 $extByType[$info['type']][$ext] = $info;
253 }
254
255 foreach ( $types as $type => $message ) {
256 if ( !isset( $extByType[$type] ) ) {
257 continue;
258 }
259 $extHtml .= Html::element( 'h2', [], $message );
260 foreach ( $extByType[$type] as $ext => $info ) {
261 $attribs = [
262 'data-name' => $ext,
263 'class' => 'config-ext-input'
264 ];
265 $labelAttribs = [];
266 if ( isset( $info['requires']['extensions'] ) ) {
267 $dependencyMap[$ext]['extensions'] = $info['requires']['extensions'];
268 $labelAttribs['class'] = 'mw-ext-with-dependencies';
269 }
270 if ( isset( $info['requires']['skins'] ) ) {
271 $dependencyMap[$ext]['skins'] = $info['requires']['skins'];
272 $labelAttribs['class'] = 'mw-ext-with-dependencies';
273 }
274 if ( isset( $dependencyMap[$ext] ) ) {
275 $links = [];
276 // For each dependency, link to the checkbox for each
277 // extension/skin that is required
278 if ( isset( $dependencyMap[$ext]['extensions'] ) ) {
279 foreach ( $dependencyMap[$ext]['extensions'] as $name ) {
280 $links[] = Html::element(
281 'a',
282 [ 'href' => "#config_ext-$name" ],
283 $name
284 );
285 }
286 }
287 if ( isset( $dependencyMap[$ext]['skins'] ) ) {
288 // @phan-suppress-next-line PhanTypeMismatchForeach Phan internal bug
289 foreach ( $dependencyMap[$ext]['skins'] as $name ) {
290 $links[] = Html::element(
291 'a',
292 [ 'href' => "#config_skin-$name" ],
293 $name
294 );
295 }
296 }
297
298 $text = wfMessage( 'config-extensions-requires' )
299 ->rawParams( $ext, $wgLang->commaList( $links ) )
300 ->escaped();
301 } else {
302 $text = $ext;
303 }
304 $extHtml .= $this->parent->getCheckBox( [
305 'var' => "ext-$ext",
306 'rawtext' => $text . $this->makeMoreInfoLink( $info ),
307 'attribs' => $attribs,
308 'labelAttribs' => $labelAttribs,
309 ] );
310 }
311 }
312
313 $extHtml .= $this->parent->getHelpBox( 'config-extensions-help' ) .
314 $this->getFieldsetEnd();
315 $this->addHTML( $extHtml );
316 // Push the dependency map to the client side
317 $this->addHTML( Html::inlineScript(
318 'var extDependencyMap = ' . Xml::encodeJsVar( $dependencyMap )
319 ) );
320 }
321 }
322
327 private function addFileOptions(): void {
328 // Having / in paths in Windows looks funny :)
329 $this->setVar( 'wgDeletedDirectory',
330 str_replace(
331 '/', DIRECTORY_SEPARATOR,
332 $this->getVar( 'wgDeletedDirectory' )
333 )
334 );
335
336 $uploadwrapperStyle = $this->getVar( 'wgEnableUploads' ) ? '' : 'display: none';
337 $this->addHTML(
338 # Uploading
339 $this->getFieldsetStart( 'config-upload-settings' ) .
340 $this->parent->getCheckBox( [
341 'var' => 'wgEnableUploads',
342 'label' => 'config-upload-enable',
343 'attribs' => [ 'class' => 'showHideRadio', 'rel' => 'uploadwrapper' ],
344 'help' => $this->parent->getHelpBox( 'config-upload-help' )
345 ] ) .
346 '<div id="uploadwrapper" style="' . $uploadwrapperStyle . '">' .
347 $this->parent->getTextBox( [
348 'var' => 'wgDeletedDirectory',
349 'label' => 'config-upload-deleted',
350 'attribs' => [ 'dir' => 'ltr' ],
351 'help' => $this->parent->getHelpBox( 'config-upload-deleted-help' )
352 ] ) .
353 '</div>'
354 );
355 $this->addHTML(
356 $this->parent->getCheckBox( [
357 'var' => 'wgUseInstantCommons',
358 'label' => 'config-instantcommons',
359 'help' => $this->parent->getHelpBox( 'config-instantcommons-help' )
360 ] ) .
361 $this->getFieldsetEnd()
362 );
363 }
364
369 private function addAdvancedOptions(): void {
370 $caches = [ 'none' ];
371 $cachevalDefault = 'none';
372
373 if ( count( $this->getVar( '_Caches' ) ) ) {
374 // A CACHE_ACCEL implementation is available
375 $caches[] = 'accel';
376 $cachevalDefault = 'accel';
377 }
378 $caches[] = 'memcached';
379
380 // We'll hide/show this on demand when the value changes, see config.js.
381 $cacheval = $this->getVar( '_MainCacheType' );
382 if ( !$cacheval ) {
383 // We need to set a default here; but don't hardcode it
384 // or we lose it every time we reload the page for validation
385 // or going back!
386 $cacheval = $cachevalDefault;
387 }
388 $hidden = ( $cacheval == 'memcached' ) ? '' : 'display: none';
389 $this->addHTML(
390 # Advanced settings
391 $this->getFieldsetStart( 'config-advanced-settings' ) .
392 # Object cache settings
393 // getRadioSet() builds a set of labeled radio buttons.
394 // For grep: The following messages are used as the item labels:
395 // config-cache-none, config-cache-accel, config-cache-memcached
396 $this->parent->getRadioSet( [
397 'var' => '_MainCacheType',
398 'label' => 'config-cache-options',
399 'itemLabelPrefix' => 'config-cache-',
400 'values' => $caches,
401 'value' => $cacheval,
402 ] ) .
403 $this->parent->getHelpBox( 'config-cache-help' ) .
404 "<div id=\"config-memcachewrapper\" style=\"$hidden\">" .
405 $this->parent->getTextArea( [
406 'var' => '_MemCachedServers',
407 'label' => 'config-memcached-servers',
408 'help' => $this->parent->getHelpBox( 'config-memcached-help' )
409 ] ) .
410 '</div>' .
411 $this->getFieldsetEnd()
412 );
413 }
414
420 private function makeScreenshotsLink( $name, $screenshots ) {
421 global $wgLang;
422 if ( count( $screenshots ) > 1 ) {
423 $links = [];
424 $counter = 1;
425
426 foreach ( $screenshots as $shot ) {
427 $links[] = Html::element(
428 'a',
429 [ 'href' => $shot, 'target' => '_blank' ],
430 $wgLang->formatNum( $counter++ )
431 );
432 }
433 return wfMessage( 'config-skins-screenshots' )
434 ->rawParams( $name, $wgLang->commaList( $links ) )
435 ->escaped();
436 } else {
437 $link = Html::element(
438 'a',
439 [ 'href' => $screenshots[0], 'target' => '_blank' ],
440 wfMessage( 'config-screenshot' )->text()
441 );
442 return wfMessage( 'config-skins-screenshot', $name )->rawParams( $link )->escaped();
443 }
444 }
445
450 private function makeMoreInfoLink( $info ) {
451 if ( !isset( $info['url'] ) ) {
452 return '';
453 }
454 return ' ' . wfMessage( 'parentheses' )->rawParams(
455 Html::element(
456 'a',
457 [ 'href' => $info['url'] ],
458 wfMessage( 'config-ext-skins-more-info' )->text()
459 )
460 )->escaped();
461 }
462
466 public function getCCPartnerUrl() {
467 $server = $this->getVar( 'wgServer' );
468 $exitUrl = $server . $this->parent->getUrl( [
469 'page' => 'Options',
470 'SubmitCC' => 'indeed',
471 'config__LicenseCode' => 'cc',
472 'config_wgRightsUrl' => '[license_url]',
473 'config_wgRightsText' => '[license_name]',
474 'config_wgRightsIcon' => '[license_button]',
475 ] );
476 $styleUrl = $server . dirname( dirname( $this->parent->getUrl() ) ) .
477 '/mw-config/config-cc.css';
478 $iframeUrl = 'https://creativecommons.org/license/?' .
479 wfArrayToCgi( [
480 'partner' => 'MediaWiki',
481 'exit_url' => $exitUrl,
482 'lang' => $this->getVar( '_UserLang' ),
483 'stylesheet' => $styleUrl,
484 ] );
485
486 return $iframeUrl;
487 }
488
492 public function getCCChooser() {
493 $iframeAttribs = [
494 'class' => 'config-cc-iframe',
495 'name' => 'config-cc-iframe',
496 'id' => 'config-cc-iframe',
497 'frameborder' => 0,
498 'width' => '100%',
499 'height' => '100%',
500 ];
501 if ( $this->getVar( '_CCDone' ) ) {
502 $iframeAttribs['src'] = $this->parent->getUrl( [ 'ShowCC' => 'yes' ] );
503 } else {
504 $iframeAttribs['src'] = $this->getCCPartnerUrl();
505 }
506 $wrapperStyle = ( $this->getVar( '_LicenseCode' ) == 'cc-choose' ) ? '' : 'display: none';
507
508 return "<div class=\"config-cc-wrapper\" id=\"config-cc-wrapper\" style=\"$wrapperStyle\">\n" .
509 Html::element( 'iframe', $iframeAttribs ) .
510 "</div>\n";
511 }
512
516 public function getCCDoneBox() {
517 $js = "parent.document.getElementById('config-cc-wrapper').style.height = '$1';";
518 // If you change this height, also change it in config.css
519 $expandJs = str_replace( '$1', '54em', $js );
520 $reduceJs = str_replace( '$1', '70px', $js );
521
522 return '<p>' .
523 Html::element( 'img', [ 'src' => $this->getVar( 'wgRightsIcon' ) ] ) .
524 "\u{00A0}\u{00A0}" .
525 htmlspecialchars( $this->getVar( 'wgRightsText' ) ) .
526 "</p>\n" .
527 "<p style=\"text-align: center;\">" .
528 Html::element( 'a',
529 [
530 'href' => $this->getCCPartnerUrl(),
531 'onclick' => $expandJs,
532 ],
533 wfMessage( 'config-cc-again' )->text()
534 ) .
535 "</p>\n" .
536 "<script>\n" .
537 # Reduce the wrapper div height
538 htmlspecialchars( $reduceJs ) .
539 "\n" .
540 "</script>\n";
541 }
542
543 public function submitCC() {
544 $newValues = $this->parent->setVarsFromRequest(
545 [ 'wgRightsUrl', 'wgRightsText', 'wgRightsIcon' ] );
546 if ( count( $newValues ) != 3 ) {
547 $this->parent->showError( 'config-cc-error' );
548
549 return;
550 }
551 $this->setVar( '_CCDone', true );
552 $this->addHTML( $this->getCCDoneBox() );
553 }
554
561 public function submitSkins() {
562 $skins = array_keys( $this->parent->findExtensions( 'skins' )->value );
563 $this->parent->setVar( '_Skins', $skins );
564
565 if ( $skins ) {
566 $skinNames = array_map( 'strtolower', $skins );
567 $this->parent->setVar( 'wgDefaultSkin', $this->parent->getDefaultSkin( $skinNames ) );
568 }
569
570 return true;
571 }
572
576 public function submit() {
577 $this->parent->setVarsFromRequest( [ '_RightsProfile', '_LicenseCode',
578 'wgEnableEmail', 'wgPasswordSender', 'wgEnableUploads',
579 '_Logo1x', '_LogoWordmark', '_LogoTagline', '_LogoIcon',
580 'wgEnableUserEmail', 'wgEnotifUserTalk', 'wgEnotifWatchlist',
581 'wgEmailAuthentication', '_MainCacheType', '_MemCachedServers',
582 'wgUseInstantCommons', 'wgDefaultSkin' ] );
583
584 $retVal = true;
585
586 if ( !array_key_exists( $this->getVar( '_RightsProfile' ), $this->parent->rightsProfiles ) ) {
587 $this->setVar( '_RightsProfile', array_key_first( $this->parent->rightsProfiles ) );
588 }
589
590 $code = $this->getVar( '_LicenseCode' );
591 if ( $code == 'cc-choose' ) {
592 if ( !$this->getVar( '_CCDone' ) ) {
593 $this->parent->showError( 'config-cc-not-chosen' );
594 $retVal = false;
595 }
596 } elseif ( array_key_exists( $code, $this->parent->licenses ) ) {
597 // Messages:
598 // config-license-cc-by, config-license-cc-by-sa, config-license-cc-by-nc-sa,
599 // config-license-cc-0, config-license-pd, config-license-gfdl, config-license-none,
600 // config-license-cc-choose
601 $entry = $this->parent->licenses[$code];
602 $this->setVar( 'wgRightsText',
603 $entry['text'] ?? wfMessage( 'config-license-' . $code )->text() );
604 $this->setVar( 'wgRightsUrl', $entry['url'] );
605 $this->setVar( 'wgRightsIcon', $entry['icon'] );
606 } else {
607 $this->setVar( 'wgRightsText', '' );
608 $this->setVar( 'wgRightsUrl', '' );
609 $this->setVar( 'wgRightsIcon', '' );
610 }
611
612 $skinsAvailable = array_keys( $this->parent->findExtensions( 'skins' )->value );
613 $skinsToInstall = [];
614 foreach ( $skinsAvailable as $skin ) {
615 $this->parent->setVarsFromRequest( [ "skin-$skin" ] );
616 if ( $this->getVar( "skin-$skin" ) ) {
617 $skinsToInstall[] = $skin;
618 }
619 }
620 $this->parent->setVar( '_Skins', $skinsToInstall );
621
622 if ( !$skinsToInstall && $skinsAvailable ) {
623 $this->parent->showError( 'config-skins-must-enable-some' );
624 $retVal = false;
625 }
626 $defaultSkin = $this->getVar( 'wgDefaultSkin' );
627 $skinsToInstallLowercase = array_map( 'strtolower', $skinsToInstall );
628 if ( $skinsToInstall && !in_array( $defaultSkin, $skinsToInstallLowercase ) ) {
629 $this->parent->showError( 'config-skins-must-enable-default' );
630 $retVal = false;
631 }
632
633 $extsAvailable = array_keys( $this->parent->findExtensions()->value );
634 $extsToInstall = [];
635 foreach ( $extsAvailable as $ext ) {
636 $this->parent->setVarsFromRequest( [ "ext-$ext" ] );
637 if ( $this->getVar( "ext-$ext" ) ) {
638 $extsToInstall[] = $ext;
639 }
640 }
641 $this->parent->setVar( '_Extensions', $extsToInstall );
642
643 if ( $this->getVar( '_MainCacheType' ) == 'memcached' ) {
644 $memcServers = explode( "\n", $this->getVar( '_MemCachedServers' ) );
645 // FIXME: explode() will always result in an array of at least one string, even on null (when
646 // the string will be empty and you'll get a PHP warning), so this has never worked?
647 // @phan-suppress-next-line PhanImpossibleCondition
648 if ( !$memcServers ) {
649 $this->parent->showError( 'config-memcache-needservers' );
650 $retVal = false;
651 }
652
653 foreach ( $memcServers as $server ) {
654 $memcParts = explode( ":", $server, 2 );
655 if ( !isset( $memcParts[0] )
656 || ( !IPUtils::isValid( $memcParts[0] )
657 && ( gethostbyname( $memcParts[0] ) == $memcParts[0] ) )
658 ) {
659 $this->parent->showError( 'config-memcache-badip', $memcParts[0] );
660 $retVal = false;
661 } elseif ( !isset( $memcParts[1] ) ) {
662 $this->parent->showError( 'config-memcache-noport', $memcParts[0] );
663 $retVal = false;
664 } elseif ( $memcParts[1] < 1 || $memcParts[1] > 65535 ) {
665 $this->parent->showError( 'config-memcache-badport', 1, 65535 );
666 $retVal = false;
667 }
668 }
669 }
670
671 return $retVal;
672 }
673
674}
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.
if(!defined( 'MW_NO_SESSION') &&! $wgCommandLineMode $wgLang
Definition Setup.php:527
This class is a collection of static functions that serve two purposes:
Definition Html.php:55
static getExtensionTypes()
Returns an array with the base extension types.
internal since 1.36
Definition User.php:71
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.
WebInstaller $parent
The WebInstaller object this WebInstallerPage belongs to.
getFieldsetEnd()
Get the end tag of a fieldset.
endForm( $continue='continue', $back='back')
getVar( $var, $default=null)
getFieldsetStart( $legend)
Get the starting tags of a fieldset.
getTextBox( $params)
Get a labelled text box to configure a variable.
getHelpBox( $msg,... $args)
Get small text indented help for a preceding form field.
if(!is_readable( $file)) $ext
Definition router.php:48