Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 196
0.00% covered (danger)
0.00%
0 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
LocalSettingsGenerator
0.00% covered (danger)
0.00%
0 / 196
0.00% covered (danger)
0.00%
0 / 8
2352
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 39
0.00% covered (danger)
0.00%
0 / 1
30
 setGroupRights
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 escapePhpString
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
12
 getText
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
30
 generateExtEnableLine
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
20
 writeFile
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 buildMemcachedServerList
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
12
 getDefaultText
0.00% covered (danger)
0.00%
0 / 106
0.00% covered (danger)
0.00%
0 / 1
702
1<?php
2/**
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 * http://www.gnu.org/copyleft/gpl.html
17 *
18 * @file
19 */
20
21namespace MediaWiki\Installer;
22
23use InvalidArgumentException;
24
25/**
26 * Generate the LocalSettings.php file.
27 *
28 * @ingroup Installer
29 * @since 1.17
30 */
31class LocalSettingsGenerator {
32
33    protected $extensions = [];
34    protected $skins = [];
35    protected $values = [];
36    protected $groupPermissions = [];
37    protected $dbSettings = '';
38    protected $IP;
39
40    /**
41     * @var Installer
42     */
43    protected $installer;
44
45    /**
46     * @param Installer $installer
47     */
48    public function __construct( Installer $installer ) {
49        $this->installer = $installer;
50
51        $this->extensions = $installer->getVar( '_Extensions' );
52        $this->skins = $installer->getVar( '_Skins' );
53        $this->IP = $installer->getVar( 'IP' );
54
55        $db = $installer->getDBInstaller( $installer->getVar( 'wgDBtype' ) );
56
57        $confItems = array_merge(
58            [
59                'wgServer', 'wgScriptPath',
60                'wgPasswordSender', 'wgImageMagickConvertCommand',
61                'wgLanguageCode', 'wgLocaltimezone', 'wgEnableEmail', 'wgEnableUserEmail',
62                'wgDiff3', 'wgEnotifUserTalk', 'wgEnotifWatchlist', 'wgEmailAuthentication',
63                'wgDBtype', 'wgSecretKey', 'wgRightsUrl', 'wgSitename', 'wgRightsIcon',
64                'wgRightsText', '_MainCacheType', 'wgEnableUploads',
65                '_MemCachedServers', 'wgDBserver', 'wgDBuser',
66                'wgDBpassword', 'wgUseInstantCommons', 'wgUpgradeKey', 'wgDefaultSkin',
67                'wgMetaNamespace', 'wgAuthenticationTokenVersion', 'wgPingback',
68                '_Logo1x', '_LogoTagline', '_LogoWordmark', '_LogoIcon',
69                '_LogoWordmarkWidth', '_LogoWordmarkHeight',
70                '_LogoTaglineWidth', '_LogoTaglineHeight', '_WithDevelopmentSettings'
71            ],
72            $db->getGlobalNames()
73        );
74
75        // The WebInstaller form field for "Logo" contains a literal "$wgResourceBasePath",
76        // and site admins are told in the help text that they can use $wgStylePath and $wgScriptPath
77        // within their input, such treat this as raw PHP for now.
78        $unescaped = [ 'wgRightsIcon', '_Caches',
79            '_Logo1x', '_LogoWordmark', '_LogoTagline', '_LogoIcon',
80        ];
81        $boolItems = [
82            'wgEnableEmail', 'wgEnableUserEmail', 'wgEnotifUserTalk',
83            'wgEnotifWatchlist', 'wgEmailAuthentication', 'wgEnableUploads', 'wgUseInstantCommons',
84            'wgPingback',
85        ];
86
87        foreach ( $confItems as $c ) {
88            $val = $installer->getVar( $c );
89
90            if ( in_array( $c, $boolItems ) ) {
91                $val = wfBoolToStr( $val );
92            }
93
94            if ( !in_array( $c, $unescaped ) && $val !== null ) {
95                $val = self::escapePhpString( $val );
96            }
97
98            $this->values[$c] = $val;
99        }
100
101        $this->dbSettings = $db->getLocalSettings();
102        $this->values['wgEmergencyContact'] = $this->values['wgPasswordSender'];
103    }
104
105    /**
106     * For $wgGroupPermissions, set a given ['group']['permission'] value.
107     * @param string $group Group name
108     * @param array $rightsArr An array of permissions, in the form of:
109     *   [ 'right' => true, 'right2' => false ]
110     */
111    public function setGroupRights( $group, $rightsArr ) {
112        $this->groupPermissions[$group] = $rightsArr;
113    }
114
115    /**
116     * Returns the escaped version of a string of php code.
117     *
118     * @param string $string
119     *
120     * @return string|false
121     */
122    public static function escapePhpString( $string ) {
123        if ( is_array( $string ) || is_object( $string ) ) {
124            return false;
125        }
126
127        return strtr(
128            $string,
129            [
130                "\n" => "\\n",
131                "\r" => "\\r",
132                "\t" => "\\t",
133                "\\" => "\\\\",
134                "\$" => "\\\$",
135                "\"" => "\\\""
136            ]
137        );
138    }
139
140    /**
141     * Return the full text of the generated LocalSettings.php file,
142     * including the extensions and skins.
143     *
144     * @return string
145     */
146    public function getText() {
147        $localSettings = $this->getDefaultText();
148
149        if ( count( $this->skins ) ) {
150            $localSettings .= "
151# Enabled skins.
152# The following skins were automatically enabled:\n";
153
154            foreach ( $this->skins as $skinName ) {
155                $localSettings .= $this->generateExtEnableLine( 'skins', $skinName );
156            }
157
158            $localSettings .= "\n";
159        }
160
161        if ( count( $this->extensions ) ) {
162            $localSettings .= "
163# Enabled extensions. Most of the extensions are enabled by adding
164# wfLoadExtension( 'ExtensionName' );
165# to LocalSettings.php. Check specific extension documentation for more details.
166# The following extensions were automatically enabled:\n";
167
168            foreach ( $this->extensions as $extName ) {
169                $localSettings .= $this->generateExtEnableLine( 'extensions', $extName );
170            }
171
172            $localSettings .= "\n";
173        }
174
175        $localSettings .= "
176# End of automatically generated settings.
177# Add more configuration options below.\n\n";
178
179        return $localSettings;
180    }
181
182    /**
183     * Generate the appropriate line to enable the given extension or skin
184     *
185     * @param string $dir Either "extensions" or "skins"
186     * @param string $name Name of extension/skin
187     * @throws InvalidArgumentException
188     * @return string
189     */
190    private function generateExtEnableLine( $dir, $name ) {
191        if ( $dir === 'extensions' ) {
192            $jsonFile = 'extension.json';
193            $function = 'wfLoadExtension';
194        } elseif ( $dir === 'skins' ) {
195            $jsonFile = 'skin.json';
196            $function = 'wfLoadSkin';
197        } else {
198            throw new InvalidArgumentException( '$dir was not "extensions" or "skins"' );
199        }
200
201        $encName = self::escapePhpString( $name );
202
203        if ( file_exists( "{$this->IP}/$dir/$encName/$jsonFile" ) ) {
204            return "$function( '$encName' );\n";
205        } else {
206            return "require_once \"\$IP/$dir/$encName/$encName.php\";\n";
207        }
208    }
209
210    /**
211     * Write the generated LocalSettings to a file
212     *
213     * @param string $fileName Full path to filename to write to
214     */
215    public function writeFile( $fileName ) {
216        file_put_contents( $fileName, $this->getText() );
217    }
218
219    /**
220     * @return string
221     */
222    protected function buildMemcachedServerList() {
223        $servers = $this->values['_MemCachedServers'];
224
225        if ( !$servers ) {
226            return '[]';
227        } else {
228            $ret = '[ ';
229            $servers = explode( ',', $servers );
230
231            foreach ( $servers as $srv ) {
232                $srv = trim( $srv );
233                $ret .= "'$srv', ";
234            }
235
236            return rtrim( $ret, ', ' ) . ' ]';
237        }
238    }
239
240    /**
241     * @return string
242     */
243    protected function getDefaultText() {
244        if ( !$this->values['wgImageMagickConvertCommand'] ) {
245            $this->values['wgImageMagickConvertCommand'] = '/usr/bin/convert';
246            $magic = '#';
247        } else {
248            $magic = '';
249        }
250
251        $metaNamespace = '';
252        if ( $this->values['wgMetaNamespace'] !== $this->values['wgSitename'] ) {
253            $metaNamespace = "\$wgMetaNamespace = '{$this->values['wgMetaNamespace']}';\n";
254        }
255
256        $groupRights = '';
257        $noFollow = '';
258        if ( $this->groupPermissions ) {
259            $groupRights .= "# The following permissions were set based on your choice in the installer\n";
260            foreach ( $this->groupPermissions as $group => $rightArr ) {
261                $group = self::escapePhpString( $group );
262                foreach ( $rightArr as $right => $perm ) {
263                    $right = self::escapePhpString( $right );
264                    $groupRights .= "\$wgGroupPermissions['$group']['$right'] = " .
265                        wfBoolToStr( $perm ) . ";\n";
266                }
267            }
268            $groupRights .= "\n";
269
270            if ( ( isset( $this->groupPermissions['*']['edit'] ) &&
271                    $this->groupPermissions['*']['edit'] === false )
272                && ( isset( $this->groupPermissions['*']['createaccount'] ) &&
273                    $this->groupPermissions['*']['createaccount'] === false )
274                && ( isset( $this->groupPermissions['*']['read'] ) &&
275                    $this->groupPermissions['*']['read'] !== false )
276            ) {
277                $noFollow = "# Set \$wgNoFollowLinks to true if you open up your wiki to editing by\n"
278                    . "# the general public and wish to apply nofollow to external links as a\n"
279                    . "# deterrent to spammers. Nofollow is not a comprehensive anti-spam solution\n"
280                    . "# and open wikis will generally require other anti-spam measures; for more\n"
281                    . "# information, see https://www.mediawiki.org/wiki/Manual:Combating_spam\n"
282                    . "\$wgNoFollowLinks = false;\n\n";
283            }
284        }
285
286        $serverSetting = "";
287        if ( array_key_exists( 'wgServer', $this->values ) && $this->values['wgServer'] !== null ) {
288            $serverSetting = "\n## The protocol and server name to use in fully-qualified URLs\n";
289            $serverSetting .= "\$wgServer = '{$this->values['wgServer']}';";
290        }
291
292        switch ( $this->values['_MainCacheType'] ) {
293            case 'anything':
294            case 'db':
295            case 'memcached':
296            case 'accel':
297                $cacheType = 'CACHE_' . strtoupper( $this->values['_MainCacheType'] );
298                break;
299            case 'none':
300            default:
301                $cacheType = 'CACHE_NONE';
302        }
303
304        $mcservers = $this->buildMemcachedServerList();
305        if ( file_exists( dirname( __DIR__ ) . '/PlatformSettings.php' ) ) {
306            $platformSettings = "\n## Include platform/distribution defaults";
307            $platformSettings .= "\nrequire_once \"\$IP/includes/PlatformSettings.php\";";
308        } else {
309            $platformSettings = '';
310        }
311
312        $developmentSettings = '';
313        if ( isset( $this->values['_WithDevelopmentSettings'] ) && $this->values['_WithDevelopmentSettings'] ) {
314            $developmentSettings = "\n## Include DevelopmentSettings.php";
315            $developmentSettings .= "\nrequire_once \"\$IP/includes/DevelopmentSettings.php\";";
316        }
317
318        $this->values['taglineConfig'] = $this->values['_LogoTagline'] ? "\n\t'tagline' => [
319        \"src\" => \"{$this->values['_LogoTagline']}\",
320        \"width\" => {$this->values['_LogoTaglineWidth']},
321        \"height\" => {$this->values['_LogoTaglineHeight']}
322    ]," : "";
323
324        $this->values['wordmarkConfig'] = $this->values['_LogoWordmark'] ? "\n\t'wordmark' => [
325        \"src\" => \"{$this->values['_LogoWordmark']}\",
326        \"width\" => {$this->values['_LogoWordmarkWidth']},
327        \"height\" => {$this->values['_LogoWordmarkHeight']},
328    ]," : "";
329
330        $this->values['sidebarLogo'] = $this->values['_Logo1x'] ?: $this->values['_LogoIcon'];
331
332        $version = MW_VERSION;
333        return "<?php
334# This file was automatically generated by the MediaWiki {$version}
335# installer. If you make manual changes, please keep track in case you
336# need to recreate them later.
337#
338# See includes/MainConfigSchema.php for all configurable settings
339# and their default values, but don't forget to make changes in _this_
340# file, not there.
341#
342# Further documentation for configuration settings may be found at:
343# https://www.mediawiki.org/wiki/Manual:Configuration_settings
344
345# Protect against web entry
346if ( !defined( 'MEDIAWIKI' ) ) {
347    exit;
348}
349{$developmentSettings}
350
351{$platformSettings}
352
353## Uncomment this to disable output compression
354# \$wgDisableOutputCompression = true;
355
356\$wgSitename = '{$this->values['wgSitename']}';
357{$metaNamespace}
358## The URL base path to the directory containing the wiki;
359## defaults for all runtime URL paths are based off of this.
360## For more information on customizing the URLs
361## (like /w/index.php/Page_title to /wiki/Page_title) please see:
362## https://www.mediawiki.org/wiki/Manual:Short_URL
363\$wgScriptPath = '{$this->values['wgScriptPath']}';
364{$serverSetting}
365
366## The URL path to static resources (images, scripts, etc.)
367\$wgResourceBasePath = \$wgScriptPath;
368
369## The URL paths to the logo.  Make sure you change this from the default,
370## or else you'll overwrite your logo when you upgrade!
371\$wgLogos = [
372    '1x' => \"{$this->values['sidebarLogo']}\",{$this->values['wordmarkConfig']}{$this->values['taglineConfig']}
373    'icon' => \"{$this->values['_LogoIcon']}\",
374];
375
376## UPO means: this is also a user preference option
377
378\$wgEnableEmail = {$this->values['wgEnableEmail']};
379\$wgEnableUserEmail = {$this->values['wgEnableUserEmail']}; # UPO
380
381\$wgEmergencyContact = '{$this->values['wgEmergencyContact']}';
382\$wgPasswordSender = '{$this->values['wgPasswordSender']}';
383
384\$wgEnotifUserTalk = {$this->values['wgEnotifUserTalk']}; # UPO
385\$wgEnotifWatchlist = {$this->values['wgEnotifWatchlist']}; # UPO
386\$wgEmailAuthentication = {$this->values['wgEmailAuthentication']};
387
388## Database settings
389\$wgDBtype = '{$this->values['wgDBtype']}';
390\$wgDBserver = '{$this->values['wgDBserver']}';
391\$wgDBname = '{$this->values['wgDBname']}';
392\$wgDBuser = '{$this->values['wgDBuser']}';
393\$wgDBpassword = '{$this->values['wgDBpassword']}';
394
395{$this->dbSettings}
396
397# Shared database table
398# This has no effect unless \$wgSharedDB is also set.
399\$wgSharedTables[] = \"actor\";
400
401## Shared memory settings
402\$wgMainCacheType = $cacheType;
403\$wgMemCachedServers = $mcservers;
404
405## To enable image uploads, make sure the 'images' directory
406## is writable, then set this to true:
407\$wgEnableUploads = {$this->values['wgEnableUploads']};
408{$magic}\$wgUseImageMagick = true;
409{$magic}\$wgImageMagickConvertCommand = '{$this->values['wgImageMagickConvertCommand']}';
410
411# InstantCommons allows wiki to use images from https://commons.wikimedia.org
412\$wgUseInstantCommons = {$this->values['wgUseInstantCommons']};
413
414# Periodically send a pingback to https://www.mediawiki.org/ with basic data
415# about this MediaWiki instance. The Wikimedia Foundation shares this data
416# with MediaWiki developers to help guide future development efforts.
417\$wgPingback = {$this->values['wgPingback']};
418
419# Site language code, should be one of the list in ./includes/languages/data/Names.php
420\$wgLanguageCode = '{$this->values['wgLanguageCode']}';
421
422# Time zone
423\$wgLocaltimezone = '{$this->values['wgLocaltimezone']}';
424
425## Set \$wgCacheDirectory to a writable directory on the web server
426## to make your wiki go slightly faster. The directory should not
427## be publicly accessible from the web.
428#\$wgCacheDirectory = \"\$IP/cache\";
429
430\$wgSecretKey = '{$this->values['wgSecretKey']}';
431
432# Changing this will log out all existing sessions.
433\$wgAuthenticationTokenVersion = '{$this->values['wgAuthenticationTokenVersion']}';
434
435# Site upgrade key. Must be set to a string (default provided) to turn on the
436# web installer while LocalSettings.php is in place
437\$wgUpgradeKey = '{$this->values['wgUpgradeKey']}';
438
439## For attaching licensing metadata to pages, and displaying an
440## appropriate copyright notice / icon. GNU Free Documentation
441## License and Creative Commons licenses are supported so far.
442\$wgRightsPage = \"\"; # Set to the title of a wiki page that describes your license/copyright
443\$wgRightsUrl = '{$this->values['wgRightsUrl']}';
444\$wgRightsText = '{$this->values['wgRightsText']}';
445\$wgRightsIcon = \"{$this->values['wgRightsIcon']}\";
446
447# Path to the GNU diff3 utility. Used for conflict resolution.
448\$wgDiff3 = '{$this->values['wgDiff3']}';
449
450{$groupRights}{$noFollow}## Default skin: you can change the default skin. Use the internal symbolic
451## names, e.g. 'vector' or 'monobook':
452\$wgDefaultSkin = '{$this->values['wgDefaultSkin']}';
453";
454    }
455}