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