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