Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 164
0.00% covered (danger)
0.00%
0 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
WebInstallerName
0.00% covered (danger)
0.00%
0 / 164
0.00% covered (danger)
0.00%
0 / 2
650
0.00% covered (danger)
0.00%
0 / 1
 execute
0.00% covered (danger)
0.00%
0 / 87
0.00% covered (danger)
0.00%
0 / 1
20
 submit
0.00% covered (danger)
0.00%
0 / 77
0.00% covered (danger)
0.00%
0 / 1
462
1<?php
2
3/**
4 * @license GPL-2.0-or-later
5 * @file
6 * @ingroup Installer
7 */
8
9namespace MediaWiki\Installer;
10
11use MediaWiki\Config\HashConfig;
12use MediaWiki\Json\FormatJson;
13use MediaWiki\MainConfigNames;
14use MediaWiki\MediaWikiServices;
15use MediaWiki\Parser\Sanitizer;
16use MediaWiki\Password\UserPasswordPolicy;
17use MediaWiki\Title\Title;
18use MediaWiki\User\User;
19use MediaWiki\User\UserRigorOptions;
20
21class WebInstallerName extends WebInstallerPage {
22
23    /**
24     * @return string
25     */
26    public function execute() {
27        $r = $this->parent->request;
28        if ( $r->wasPosted() && $this->submit() ) {
29            return 'continue';
30        }
31
32        $this->startForm();
33
34        // Encourage people to not name their site 'MediaWiki' by blanking the
35        // field. I think that was the intent with the original $GLOBALS['wgSitename']
36        // but these two always were the same so had the effect of making the
37        // installer forget $wgSitename when navigating back to this page.
38        if ( $this->getVar( 'wgSitename' ) == 'MediaWiki' ) {
39            $this->setVar( 'wgSitename', '' );
40        }
41
42        // Set wgMetaNamespace to something valid before we show the form.
43        // $wgMetaNamespace defaults to $wgSiteName which is 'MediaWiki'
44        $metaNS = $this->getVar( 'wgMetaNamespace' );
45        $this->setVar(
46            'wgMetaNamespace',
47            wfMessage( 'config-ns-other-default' )->inContentLanguage()->text()
48        );
49
50        // Database isn't available in config yet, so take it
51        // from the installer
52        $pingbackConf = new HashConfig( [
53            MainConfigNames::DBtype => $this->getVar( 'wgDBtype' ),
54        ] );
55        $pingbackInfo = Pingback::getSystemInfo( $pingbackConf );
56
57        $this->addHTML(
58            // TODO: validate wgServer on the client side
59            $this->parent->getTextBox( [
60                'var' => 'wgServer',
61                'label' => 'config-server',
62                'help' => $this->parent->getHelpBox( 'config-server-help' )
63            ] ) .
64            $this->parent->getTextBox( [
65                'var' => 'wgSitename',
66                'label' => 'config-site-name',
67                'help' => $this->parent->getHelpBox( 'config-site-name-help' )
68            ] ) .
69            // getRadioSet() builds a set of labeled radio buttons.
70            // For grep: The following messages are used as the item labels:
71            // config-ns-site-name, config-ns-generic, config-ns-other
72            $this->parent->getRadioSet( [
73                'var' => '_NamespaceType',
74                'label' => 'config-project-namespace',
75                'itemLabelPrefix' => 'config-ns-',
76                'values' => [ 'site-name', 'generic', 'other' ],
77                'commonAttribs' => [ 'class' => 'enableForOther',
78                    'rel' => 'config_wgMetaNamespace' ],
79                'help' => $this->parent->getHelpBox( 'config-project-namespace-help' )
80            ] ) .
81            $this->parent->getTextBox( [
82                'var' => 'wgMetaNamespace',
83                'label' => '', // @todo Needs a label?
84                'attribs' => [ 'class' => 'enabledByOther' ]
85            ] ) .
86            $this->getFieldsetStart( 'config-admin-box' ) .
87            $this->parent->getTextBox( [
88                'var' => '_AdminName',
89                'label' => 'config-admin-name',
90                'help' => $this->parent->getHelpBox( 'config-admin-help' )
91            ] ) .
92            $this->parent->getPasswordBox( [
93                'var' => '_AdminPassword',
94                'label' => 'config-admin-password',
95            ] ) .
96            $this->parent->getPasswordBox( [
97                'var' => '_AdminPasswordConfirm',
98                'label' => 'config-admin-password-confirm'
99            ] ) .
100            $this->parent->getTextBox( [
101                'var' => '_AdminEmail',
102                'attribs' => [
103                    'dir' => 'ltr',
104                ],
105                'label' => 'config-admin-email',
106                'help' => $this->parent->getHelpBox( 'config-admin-email-help' )
107            ] ) .
108            $this->parent->getCheckBox( [
109                'var' => '_Subscribe',
110                'label' => 'config-subscribe',
111                'help' => $this->parent->getHelpBox( 'config-subscribe-help' )
112            ] ) .
113            $this->parent->getCheckBox( [
114                'var' => 'wgPingback',
115                'label' => 'config-pingback',
116                'help' => $this->parent->getHelpBox(
117                    'config-pingback-help',
118                    FormatJson::encode( $pingbackInfo, true )
119                ),
120                'value' => true,
121            ] ) .
122            $this->getFieldsetEnd() .
123            $this->parent->getInfoBox( wfMessage( 'config-almost-done' )->plain() ) .
124            // getRadioSet() builds a set of labeled radio buttons.
125            // For grep: The following messages are used as the item labels:
126            // config-optional-continue, config-optional-skip
127            $this->parent->getRadioSet( [
128                'var' => '_SkipOptional',
129                'itemLabelPrefix' => 'config-optional-',
130                'values' => [ 'continue', 'skip' ]
131            ] )
132        );
133
134        // Restore the default value
135        $this->setVar( 'wgMetaNamespace', $metaNS );
136
137        $this->endForm();
138
139        return 'output';
140    }
141
142    /**
143     * @return bool
144     */
145    public function submit() {
146        global $wgPasswordPolicy;
147
148        $retVal = true;
149        $this->parent->setVarsFromRequest( [ 'wgServer', 'wgSitename', '_NamespaceType',
150            '_AdminName', '_AdminPassword', '_AdminPasswordConfirm', '_AdminEmail',
151            '_Subscribe', '_SkipOptional', 'wgMetaNamespace', 'wgPingback' ] );
152
153        // Validate site name
154        if ( strval( $this->getVar( 'wgSitename' ) ) === '' ) {
155            $this->parent->showError( 'config-site-name-blank' );
156            $retVal = false;
157        }
158
159        // Fetch namespace
160        $nsType = $this->getVar( '_NamespaceType' );
161        if ( $nsType == 'site-name' ) {
162            $name = $this->getVar( 'wgSitename' );
163            // Sanitize for namespace
164            // This algorithm should match the JS one in WebInstallerOutput.php
165            $name = preg_replace( '/[\[\]\{\}|#<>%+? ]/', '_', $name );
166            $name = str_replace( '&', '&amp;', $name );
167            $name = preg_replace( '/__+/', '_', $name );
168            $name = ucfirst( trim( $name, '_' ) );
169        } elseif ( $nsType == 'generic' ) {
170            $name = wfMessage( 'config-ns-generic' )->text();
171        } else { // other
172            $name = $this->getVar( 'wgMetaNamespace' );
173        }
174
175        // Validate namespace
176        if ( str_contains( $name, ':' ) ) {
177            $good = false;
178        } else {
179            // Title-style validation
180            $title = Title::newFromText( $name );
181            if ( !$title ) {
182                $good = $nsType == 'site-name';
183            } else {
184                $name = $title->getDBkey();
185                $good = true;
186            }
187        }
188        if ( !$good ) {
189            $this->parent->showError( 'config-ns-invalid', $name );
190            $retVal = false;
191        }
192
193        // Make sure it won't conflict with any existing namespaces
194        $nsIndex = MediaWikiServices::getInstance()->getContentLanguage()->getNsIndex( $name );
195        if ( $nsIndex !== false && $nsIndex !== NS_PROJECT ) {
196            $this->parent->showError( 'config-ns-conflict', $name );
197            $retVal = false;
198        }
199
200        $this->setVar( 'wgMetaNamespace', $name );
201
202        // Validate username for creation
203        $name = $this->getVar( '_AdminName' );
204        if ( strval( $name ) === '' ) {
205            $this->parent->showError( 'config-admin-name-blank' );
206            $cname = $name;
207            $retVal = false;
208        } else {
209            $userNameUtils = MediaWikiServices::getInstance()->getUserNameUtils();
210            $cname = $userNameUtils->getCanonical( $name, UserRigorOptions::RIGOR_CREATABLE );
211            if ( $cname === false ) {
212                $this->parent->showError( 'config-admin-name-invalid', $name );
213                $retVal = false;
214            } else {
215                $this->setVar( '_AdminName', $cname );
216            }
217        }
218
219        // Validate password
220        $msg = false;
221        $pwd = $this->getVar( '_AdminPassword' );
222        $user = User::newFromName( $cname );
223        if ( $user ) {
224            $upp = new UserPasswordPolicy(
225                $wgPasswordPolicy['policies'],
226                $wgPasswordPolicy['checks']
227            );
228            $status = $upp->checkUserPasswordForGroups(
229                $user,
230                $pwd,
231                [ 'bureaucrat', 'sysop', 'interface-admin' ]  // per Installer::createSysop()
232            );
233            $valid = $status->isGood() ? true : $status->getMessage();
234        } else {
235            $valid = 'config-admin-name-invalid';
236        }
237        if ( strval( $pwd ) === '' ) {
238            // Provide a more specific and helpful message if password field is left blank
239            $msg = 'config-admin-password-blank';
240        } elseif ( $pwd !== $this->getVar( '_AdminPasswordConfirm' ) ) {
241            $msg = 'config-admin-password-mismatch';
242        } elseif ( $valid !== true ) {
243            $msg = $valid;
244        }
245        if ( $msg !== false ) {
246            $this->parent->showError( $msg );
247            $this->setVar( '_AdminPassword', '' );
248            $this->setVar( '_AdminPasswordConfirm', '' );
249            $retVal = false;
250        }
251
252        // Validate e-mail if provided
253        $email = $this->getVar( '_AdminEmail' );
254        if ( $email && !Sanitizer::validateEmail( $email ) ) {
255            $this->parent->showError( 'config-admin-error-bademail' );
256            $retVal = false;
257        }
258        // If they asked to subscribe to mediawiki-announce but didn't give
259        // an e-mail, show an error. T31332
260        if ( !$email && $this->getVar( '_Subscribe' ) ) {
261            $this->parent->showError( 'config-subscribe-noemail' );
262            $retVal = false;
263        }
264
265        return $retVal;
266    }
267
268}