Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 111 |
|
0.00% |
0 / 6 |
CRAP | |
0.00% |
0 / 1 |
MergeMessageFileList | |
0.00% |
0 / 111 |
|
0.00% |
0 / 6 |
1260 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 12 |
|
0.00% |
0 / 1 |
2 | |||
execute | |
0.00% |
0 / 37 |
|
0.00% |
0 / 1 |
240 | |||
finalSetup | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
getDbType | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
readFile | |
0.00% |
0 / 17 |
|
0.00% |
0 / 1 |
42 | |||
generateMessageFileList | |
0.00% |
0 / 42 |
|
0.00% |
0 / 1 |
132 |
1 | <?php |
2 | /** |
3 | * Merge $wgExtensionMessagesFiles from various extensions to produce a |
4 | * single array containing all message files. |
5 | * |
6 | * This program is free software; you can redistribute it and/or modify |
7 | * it under the terms of the GNU General Public License as published by |
8 | * the Free Software Foundation; either version 2 of the License, or |
9 | * (at your option) any later version. |
10 | * |
11 | * This program is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | * GNU General Public License for more details. |
15 | * |
16 | * You should have received a copy of the GNU General Public License along |
17 | * with this program; if not, write to the Free Software Foundation, Inc., |
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
19 | * http://www.gnu.org/copyleft/gpl.html |
20 | * |
21 | * @file |
22 | * @ingroup Maintenance |
23 | */ |
24 | |
25 | # Start from scratch |
26 | use MediaWiki\MainConfigNames; |
27 | use MediaWiki\Registration\ExtensionRegistry; |
28 | use MediaWiki\Settings\SettingsBuilder; |
29 | |
30 | // @codeCoverageIgnoreStart |
31 | define( 'MW_NO_EXTENSION_MESSAGES', 1 ); |
32 | |
33 | require_once __DIR__ . '/Maintenance.php'; |
34 | // @codeCoverageIgnoreEnd |
35 | |
36 | /** |
37 | * Maintenance script that merges $wgExtensionMessagesFiles from various |
38 | * extensions to produce a single array containing all message files. |
39 | * |
40 | * @ingroup Maintenance |
41 | */ |
42 | class MergeMessageFileList extends Maintenance { |
43 | public function __construct() { |
44 | parent::__construct(); |
45 | $this->addOption( |
46 | 'list-file', |
47 | 'A file containing a list of extension setup files, one per line.', |
48 | false, |
49 | true |
50 | ); |
51 | $this->addOption( 'extensions-dir', 'Path where extensions can be found.', false, true ); |
52 | $this->addOption( 'output', 'Send output to this file (omit for stdout)', false, true ); |
53 | $this->addDescription( 'Merge $wgExtensionMessagesFiles and $wgMessagesDirs from ' . |
54 | ' various extensions to produce a single file listing all message files and dirs.' |
55 | ); |
56 | } |
57 | |
58 | public function execute() { |
59 | $config = $this->getConfig(); |
60 | $extensionEntryPointListFiles = $config->get( MainConfigNames::ExtensionEntryPointListFiles ); |
61 | |
62 | if ( !count( $extensionEntryPointListFiles ) |
63 | && !$this->hasOption( 'list-file' ) |
64 | && !$this->hasOption( 'extensions-dir' ) |
65 | ) { |
66 | $this->fatalError( "Either --list-file or --extensions-dir must be provided if " . |
67 | "\$wgExtensionEntryPointListFiles is not set" ); |
68 | } |
69 | |
70 | $setupFiles = []; |
71 | |
72 | # Add setup files contained in file passed to --list-file |
73 | if ( $this->hasOption( 'list-file' ) ) { |
74 | $extensionPaths = $this->readFile( $this->getOption( 'list-file' ) ); |
75 | $setupFiles = array_merge( $setupFiles, $extensionPaths ); |
76 | } |
77 | |
78 | # Now find out files in a directory |
79 | if ( $this->hasOption( 'extensions-dir' ) ) { |
80 | $extdir = $this->getOption( 'extensions-dir' ); |
81 | # Allow multiple directories to be passed with ":" as delimiter |
82 | $extdirs = explode( ':', $extdir ); |
83 | foreach ( $extdirs as $extdir ) { |
84 | $entries = scandir( $extdir ); |
85 | foreach ( $entries as $extname ) { |
86 | if ( $extname == '.' || $extname == '..' || !is_dir( "$extdir/$extname" ) ) { |
87 | continue; |
88 | } |
89 | $possibilities = [ |
90 | "$extdir/$extname/extension.json", |
91 | "$extdir/$extname/skin.json", |
92 | ]; |
93 | $found = false; |
94 | foreach ( $possibilities as $extfile ) { |
95 | if ( file_exists( $extfile ) ) { |
96 | $setupFiles[] = $extfile; |
97 | $found = true; |
98 | break; |
99 | } |
100 | } |
101 | |
102 | if ( !$found ) { |
103 | $this->error( "Extension {$extname} in {$extdir} lacks expected entry point: " . |
104 | "extension.json or skin.json " . |
105 | "(PHP entry points are no longer supported by this script)." ); |
106 | } |
107 | } |
108 | } |
109 | } |
110 | |
111 | # Add setup files defined via configuration |
112 | foreach ( $extensionEntryPointListFiles as $points ) { |
113 | $extensionPaths = $this->readFile( $points ); |
114 | $setupFiles = array_merge( $setupFiles, $extensionPaths ); |
115 | } |
116 | |
117 | $this->generateMessageFileList( $setupFiles ); |
118 | } |
119 | |
120 | public function finalSetup( SettingsBuilder $settingsBuilder ) { |
121 | # This script commonly needs to be run before the l10n cache. But if |
122 | # LanguageCode is not 'en', it won't be able to run because there is |
123 | # no l10n cache. Break the cycle by forcing the LanguageCode setting to 'en'. |
124 | $settingsBuilder->putConfigValue( MainConfigNames::LanguageCode, 'en' ); |
125 | parent::finalSetup( $settingsBuilder ); |
126 | } |
127 | |
128 | /** |
129 | * Database access is not needed. |
130 | * |
131 | * @return int DB constant |
132 | */ |
133 | public function getDbType() { |
134 | return Maintenance::DB_NONE; |
135 | } |
136 | |
137 | /** |
138 | * @param string $fileName |
139 | * @return array List of absolute extension paths |
140 | */ |
141 | private function readFile( $fileName ) { |
142 | $IP = MW_INSTALL_PATH; |
143 | |
144 | $files = []; |
145 | $fileLines = file( $fileName ); |
146 | if ( $fileLines === false ) { |
147 | $this->error( "Unable to open list file $fileName." ); |
148 | |
149 | return $files; |
150 | } |
151 | # Strip comments, discard empty lines, and trim leading and trailing |
152 | # whitespace. Comments start with '#' and extend to the end of the line. |
153 | foreach ( $fileLines as $extension ) { |
154 | $extension = trim( preg_replace( '/#.*/', '', $extension ) ); |
155 | if ( $extension !== '' ) { |
156 | # Paths may use the string $IP to be substituted by the actual value |
157 | $extension = str_replace( '$IP', $IP, $extension ); |
158 | if ( !str_ends_with( $extension, '.json' ) ) { |
159 | $this->error( "Extension {$extension} does not end with .json " . |
160 | "(PHP entry points are no longer supported by this script)" ); |
161 | } elseif ( file_exists( $extension ) ) { |
162 | $files[] = $extension; |
163 | } else { |
164 | $this->error( "Extension {$extension} doesn't exist" ); |
165 | } |
166 | } |
167 | } |
168 | |
169 | return $files; |
170 | } |
171 | |
172 | private function generateMessageFileList( array $setupFiles ) { |
173 | $IP = MW_INSTALL_PATH; |
174 | |
175 | $outputFile = $this->getOption( 'output' ); |
176 | $quiet = $this->hasOption( 'quiet' ); |
177 | |
178 | $queue = []; |
179 | |
180 | foreach ( $setupFiles as $fileName ) { |
181 | if ( strval( $fileName ) === '' ) { |
182 | continue; |
183 | } |
184 | if ( !$quiet ) { |
185 | fwrite( STDERR, "Loading data from $fileName\n" ); |
186 | } |
187 | $queue[$fileName] = 1; |
188 | } |
189 | |
190 | $config = $this->getConfig(); |
191 | $vars = [ |
192 | 'wgExtensionMessagesFiles' => $config->get( MainConfigNames::ExtensionMessagesFiles ), |
193 | 'wgMessagesDirs' => $config->get( MainConfigNames::MessagesDirs ), |
194 | ]; |
195 | |
196 | if ( $queue ) { |
197 | $registry = new ExtensionRegistry(); |
198 | $data = $registry->readFromQueue( $queue ); |
199 | foreach ( [ 'wgExtensionMessagesFiles', 'wgMessagesDirs' ] as $var ) { |
200 | if ( isset( $data['globals'][$var] ) ) { |
201 | $vars[$var] = array_merge( $data['globals'][$var], $vars[$var] ); |
202 | } |
203 | } |
204 | } |
205 | |
206 | if ( !$quiet ) { |
207 | fwrite( STDERR, "\n" ); |
208 | } |
209 | $s = |
210 | "<?php\n" . |
211 | "## This file is generated by mergeMessageFileList.php. Do not edit it directly.\n\n" . |
212 | "if ( defined( 'MW_NO_EXTENSION_MESSAGES' ) ) return;\n\n" . |
213 | '$wgExtensionMessagesFiles = ' . var_export( $vars['wgExtensionMessagesFiles'], true ) . ";\n\n" . |
214 | '$wgMessagesDirs = ' . var_export( $vars['wgMessagesDirs'], true ) . ";\n\n"; |
215 | |
216 | $dirs = [ |
217 | $IP, |
218 | dirname( __DIR__ ), |
219 | realpath( $IP ) |
220 | ]; |
221 | |
222 | foreach ( $dirs as $dir ) { |
223 | $s = preg_replace( "/'" . preg_quote( $dir, '/' ) . "([^']*)'/", '"$IP\1"', $s ); |
224 | } |
225 | |
226 | if ( $outputFile !== null ) { |
227 | $res = file_put_contents( $outputFile, $s ); |
228 | if ( $res === false ) { |
229 | fwrite( STDERR, "Failed to write to $outputFile\n" ); |
230 | exit( 1 ); |
231 | } |
232 | } else { |
233 | echo $s; |
234 | } |
235 | } |
236 | } |
237 | |
238 | // @codeCoverageIgnoreStart |
239 | $maintClass = MergeMessageFileList::class; |
240 | require_once RUN_MAINTENANCE_IF_MAIN; |
241 | // @codeCoverageIgnoreEnd |