Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 134 |
|
0.00% |
0 / 22 |
CRAP | |
0.00% |
0 / 1 |
SiteMatrix | |
0.00% |
0 / 134 |
|
0.00% |
0 / 22 |
3080 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 59 |
|
0.00% |
0 / 1 |
306 | |||
getLangList | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getNames | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getSites | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getSpecials | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getCount | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getCountPerSite | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getSiteUrl | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getUrl | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
6 | |||
getCanonicalUrl | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getSitename | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getLanguageCode | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getSetting | |
0.00% |
0 / 12 |
|
0.00% |
0 / 1 |
6 | |||
getDBName | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
12 | |||
exist | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
isClosed | |
0.00% |
0 / 20 |
|
0.00% |
0 / 1 |
56 | |||
isPrivate | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
isFishbowl | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
isNonGlobal | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
isSpecial | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
extractDbList | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
12 | |||
extractFile | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
12 |
1 | <?php |
2 | |
3 | namespace MediaWiki\Extension\SiteMatrix; |
4 | |
5 | use InvalidArgumentException; |
6 | use LanguageCode; |
7 | use MediaWiki\MediaWikiServices; |
8 | |
9 | /** |
10 | * Service to access |
11 | */ |
12 | class SiteMatrix { |
13 | /** @var string[] Language codes used by this wikifarm, sorted alphabetically. */ |
14 | protected $langlist; |
15 | |
16 | /** |
17 | * Sites (aka project families) used by this wikifarm. These will be things like 'wiktionary'. |
18 | * |
19 | * @var string[] |
20 | * @see $wgSiteMatrixSites |
21 | */ |
22 | protected $sites; |
23 | |
24 | /** |
25 | * Human-readable site names. (Except they aren't...) |
26 | * |
27 | * @var string[] site => name<br/>iw-prefix |
28 | * @see $wgSiteMatrixSites |
29 | */ |
30 | protected $names; |
31 | |
32 | /** |
33 | * Wiki family domain names. |
34 | * |
35 | * @var string[] site => host |
36 | * @see $wgSiteMatrixSites |
37 | */ |
38 | protected $hosts; |
39 | |
40 | /** @var string[]|null Lazy-loaded dbname list of private wikis. */ |
41 | protected $private; |
42 | |
43 | /** @var string[]|null Lazy-loaded dbname list of fishbowl wikis. */ |
44 | protected $fishbowl; |
45 | |
46 | /** @var string[]|null Lazy-loaded dbname list of closed wikis. */ |
47 | protected $closed; |
48 | |
49 | /** @var string[]|null Lazy-loaded dbname list of non-SUL wikis. */ |
50 | protected $nonglobal; |
51 | |
52 | /** |
53 | * Special wikis (which are multilingual or otherwise not split by language), |
54 | * partially sorted by language. |
55 | * Language codes use _ instead of -. |
56 | * |
57 | * @var array[] [ <language code>, <family> ] |
58 | */ |
59 | protected $specials; |
60 | |
61 | /** |
62 | * A matrix of which wikis exist in which language. |
63 | * Language codes use - instead of _. |
64 | * |
65 | * @var array[] site => language => 1 |
66 | */ |
67 | protected $matrix; |
68 | |
69 | /** |
70 | * A matrix of which wikis' DB name in which language. |
71 | * Language codes use - instead of _. |
72 | * |
73 | * @var array[] site => language => DBname |
74 | */ |
75 | protected $matrixDBnames; |
76 | |
77 | /** @var int Total number of wikis. */ |
78 | protected $count; |
79 | |
80 | /** |
81 | * The number of wikis in each wiki family. |
82 | * |
83 | * @var int[] site => count |
84 | */ |
85 | protected $countPerSite; |
86 | |
87 | /** |
88 | * Create and load the site matrix. |
89 | * Goes through $wgLocalDatabases and uses a bunch of $wgSiteMatrix* settings to |
90 | * sort it into project families and special projects. |
91 | */ |
92 | public function __construct() { |
93 | global $wgSiteMatrixFile, $wgSiteMatrixSites; |
94 | global $wgLocalDatabases, $wgConf; |
95 | |
96 | $wgConf->loadFullData(); |
97 | |
98 | if ( $wgSiteMatrixFile !== null && file_exists( $wgSiteMatrixFile ) ) { |
99 | $this->langlist = $this->extractFile( $wgSiteMatrixFile ); |
100 | $hideEmpty = false; |
101 | } else { |
102 | $this->langlist = array_keys( |
103 | MediaWikiServices::getInstance()->getLanguageNameUtils()->getLanguageNames() |
104 | ); |
105 | $hideEmpty = true; |
106 | } |
107 | |
108 | sort( $this->langlist ); |
109 | $xLanglist = array_flip( $this->langlist ); |
110 | |
111 | $this->sites = []; |
112 | $this->names = []; |
113 | $this->hosts = []; |
114 | |
115 | foreach ( $wgSiteMatrixSites as $site => $conf ) { |
116 | $this->sites[] = $site; |
117 | $this->names[$site] = $conf['name'] . ( isset( $conf['prefix'] ) ? |
118 | '<br />' . $conf['prefix'] : '' ); |
119 | $this->hosts[$site] = $conf['host']; |
120 | } |
121 | |
122 | # Initialize $countPerSite |
123 | $this->countPerSite = []; |
124 | foreach ( $this->sites as $site ) { |
125 | $this->countPerSite[$site] = 0; |
126 | } |
127 | |
128 | # Tabulate the matrix |
129 | $this->specials = []; |
130 | $this->matrix = []; |
131 | $this->matrixDBnames = []; |
132 | foreach ( $wgLocalDatabases as $db ) { |
133 | # Find suffix |
134 | $found = false; |
135 | foreach ( $this->sites as $site ) { |
136 | $m = []; |
137 | if ( preg_match( "/(.*)$site\$/", $db, $m ) ) { |
138 | $langPrefix = $m[1]; |
139 | # The language code prefix might be deprecated language codes |
140 | $langCode = LanguageCode::replaceDeprecatedCodes( |
141 | str_replace( '_', '-', $langPrefix ) |
142 | ); |
143 | # Non-deprecated language codes were already added in the extractFile function |
144 | if ( isset( $xLanglist[$langCode] ) ) { |
145 | $this->matrix[$site][$langCode] = 1; |
146 | $this->matrixDBnames[$site][$langCode] = $db; |
147 | $this->countPerSite[$site]++; |
148 | } else { |
149 | $this->specials[] = [ $langPrefix, $site ]; |
150 | } |
151 | $found = true; |
152 | break; |
153 | } |
154 | } |
155 | if ( !$found ) { |
156 | [ $major, $minor ] = $wgConf->siteFromDB( $db ); |
157 | if ( $major !== null ) { |
158 | $this->specials[] = [ str_replace( '-', '_', $minor ), $major ]; |
159 | } |
160 | } |
161 | } |
162 | |
163 | uasort( $this->specials, static function ( $a1, $a2 ) { |
164 | return strcmp( $a1[0], $a2[0] ); |
165 | } ); |
166 | |
167 | if ( $hideEmpty ) { |
168 | foreach ( $xLanglist as $lang => $_ ) { |
169 | $empty = true; |
170 | foreach ( $this->sites as $site ) { |
171 | if ( !empty( $this->matrix[$site][$lang] ) ) { |
172 | $empty = false; |
173 | } |
174 | } |
175 | if ( $empty ) { |
176 | unset( $xLanglist[$lang] ); |
177 | } |
178 | } |
179 | $this->langlist = array_keys( $xLanglist ); |
180 | } |
181 | |
182 | $this->count = count( $wgLocalDatabases ); |
183 | } |
184 | |
185 | /** |
186 | * Get language codes used by this wikifarm (sorted alphabetically). |
187 | * |
188 | * @return string[] |
189 | */ |
190 | public function getLangList() { |
191 | return $this->langlist; |
192 | } |
193 | |
194 | /** |
195 | * Get family names in an almost-human-readable format (will be something like 'Wikipedia<br/>w'). |
196 | * |
197 | * @return string[] family => name |
198 | */ |
199 | public function getNames() { |
200 | return $this->names; |
201 | } |
202 | |
203 | /** |
204 | * Get the list of project families used by this wikifarm. |
205 | * |
206 | * @return string[] |
207 | */ |
208 | public function getSites() { |
209 | return $this->sites; |
210 | } |
211 | |
212 | /** |
213 | * Get list of special wikis (which are multilingual or otherwise not split by language), |
214 | * partially sorted by language. |
215 | * Language codes use _ instead of -. |
216 | * |
217 | * @return array[] [ <language code>, <family> ] |
218 | */ |
219 | public function getSpecials() { |
220 | return $this->specials; |
221 | } |
222 | |
223 | /** |
224 | * Get the total number of wikis. |
225 | * |
226 | * @return int |
227 | */ |
228 | public function getCount() { |
229 | return $this->count; |
230 | } |
231 | |
232 | /** |
233 | * Get the total number of wikis in a wiki family. |
234 | * |
235 | * @param string $site |
236 | * @return int |
237 | */ |
238 | public function getCountPerSite( $site ) { |
239 | return $this->countPerSite[$site]; |
240 | } |
241 | |
242 | /** |
243 | * Get the base URL of a wiki family (e.g. '//www.wikipedia.org/') with trailing /. |
244 | * |
245 | * @param string $site |
246 | * @return string |
247 | */ |
248 | public function getSiteUrl( $site ) { |
249 | return '//' . $this->hosts[$site] . '/'; |
250 | } |
251 | |
252 | /** |
253 | * Get the base URL of a wiki (as in $wgServer / $wgCanonicalServer). |
254 | * |
255 | * @param string $langCode Language code (may not equal to language subdomain) |
256 | * @param string $major Site group code |
257 | * @param bool $canonical use canonical url. |
258 | * @return mixed |
259 | */ |
260 | public function getUrl( $langCode, $major, $canonical = false ) { |
261 | return $this->getSetting( |
262 | $canonical ? 'wgCanonicalServer' : 'wgServer', |
263 | $langCode, |
264 | $major |
265 | ); |
266 | } |
267 | |
268 | /** |
269 | * Shortcut for getUrl( $langCode, $major, true ). |
270 | * |
271 | * @param string $langCode Language code (may not equal to language subdomain) |
272 | * @param string $major Site group code |
273 | * @return mixed |
274 | */ |
275 | public function getCanonicalUrl( $langCode, $major ) { |
276 | return $this->getSetting( 'wgCanonicalServer', $langCode, $major ); |
277 | } |
278 | |
279 | /** |
280 | * Get human-readable name of a wiki. |
281 | * |
282 | * @param string $langCode Language code (may not equal to language subdomain) |
283 | * @param string $major |
284 | * @return string |
285 | */ |
286 | public function getSitename( $langCode, $major ) { |
287 | return $this->getSetting( 'wgSitename', $langCode, $major ); |
288 | } |
289 | |
290 | /** |
291 | * Get the normalised IETF language tag. |
292 | * |
293 | * @param string $langCode |
294 | * @param string $major |
295 | * @return string |
296 | */ |
297 | public function getLanguageCode( $langCode, $major ) { |
298 | return LanguageCode::bcp47( $this->getSetting( 'wgLanguageCode', $langCode, $major ) ); |
299 | } |
300 | |
301 | /** |
302 | * @param string $setting Setting name |
303 | * @param string $langCode Language code (may not equal to language DB name prefix) |
304 | * @param string $dbSuffix e.g. 'wiki' for 'enwiki' or 'wikisource' for 'enwikisource' |
305 | * @return mixed |
306 | */ |
307 | private function getSetting( $setting, $langCode, $dbSuffix ) { |
308 | global $wgConf; |
309 | |
310 | $dbname = $this->getDBName( $langCode, $dbSuffix ); |
311 | |
312 | [ $major, $minor ] = $wgConf->siteFromDB( $dbname ); |
313 | if ( $major === null ) { |
314 | throw new InvalidArgumentException( "Invalid DB name \"$dbname\"" ); |
315 | } |
316 | $minor = str_replace( '_', '-', $minor ); |
317 | |
318 | return $wgConf->get( |
319 | $setting, |
320 | $dbname, |
321 | $major, |
322 | [ 'lang' => $minor, 'site' => $major ] |
323 | ); |
324 | } |
325 | |
326 | /** |
327 | * @param string $langCode Language code (may not equal to language DB name prefix) |
328 | * @param string $major |
329 | * @return string |
330 | */ |
331 | public function getDBName( $langCode, $major ) { |
332 | if ( isset( $this->matrix[$major] ) && isset( $this->matrix[$major][$langCode] ) ) { |
333 | return $this->matrixDBnames[$major][$langCode]; |
334 | } |
335 | return str_replace( '-', '_', $langCode ) . $major; |
336 | } |
337 | |
338 | /** |
339 | * Check whether a wiki exists. |
340 | * |
341 | * @param string $langCode Language code (may not equal to language DB name prefix) |
342 | * @param string $major Site group code |
343 | * @return bool |
344 | */ |
345 | public function exist( $langCode, $major ) { |
346 | return !empty( $this->matrix[$major][$langCode] ); |
347 | } |
348 | |
349 | /** |
350 | * Check whether a wiki is closed (not editable). |
351 | * |
352 | * @param string $langCode Language code (may not equal to language DB name prefix) |
353 | * @param string $major Site group code |
354 | * @return bool |
355 | */ |
356 | public function isClosed( $langCode, $major ) { |
357 | global $wgSiteMatrixClosedSites; |
358 | |
359 | $dbname = $this->getDBName( $langCode, $major ); |
360 | |
361 | if ( $wgSiteMatrixClosedSites === null ) { |
362 | // Fallback to old behavior checking read-only settings; |
363 | // not very reliable. |
364 | global $wgConf; |
365 | |
366 | [ $major, $langCode ] = $wgConf->siteFromDB( $dbname ); |
367 | if ( $major === null ) { |
368 | // No such suffix |
369 | return false; |
370 | } |
371 | |
372 | if ( $wgConf->get( 'wgReadOnly', $dbname, $major, [ 'site' => $major, 'lang' => $langCode ] ) ) { |
373 | return true; |
374 | } |
375 | $readOnlyFile = $wgConf->get( 'wgReadOnlyFile', |
376 | $dbname, |
377 | $major, |
378 | [ 'site' => $major, 'lang' => $langCode ] |
379 | ); |
380 | if ( $readOnlyFile && file_exists( $readOnlyFile ) ) { |
381 | return true; |
382 | } |
383 | return false; |
384 | } |
385 | |
386 | if ( $this->closed == null ) { |
387 | $this->closed = $this->extractDbList( $wgSiteMatrixClosedSites ); |
388 | } |
389 | return in_array( $dbname, $this->closed ); |
390 | } |
391 | |
392 | /** |
393 | * Check whether a wiki is private (not publicly readable). |
394 | * |
395 | * @param string $dbname Database name |
396 | * @return bool |
397 | */ |
398 | public function isPrivate( $dbname ) { |
399 | global $wgSiteMatrixPrivateSites; |
400 | |
401 | if ( $this->private == null ) { |
402 | $this->private = $this->extractDbList( $wgSiteMatrixPrivateSites ); |
403 | } |
404 | |
405 | return in_array( $dbname, $this->private ); |
406 | } |
407 | |
408 | /** |
409 | * Check whether a wiki is a fishbowl (publicly readable but not publicly editable). |
410 | * |
411 | * @param string $dbname Database name |
412 | * @return bool |
413 | */ |
414 | public function isFishbowl( $dbname ) { |
415 | global $wgSiteMatrixFishbowlSites; |
416 | |
417 | if ( $this->fishbowl == null ) { |
418 | $this->fishbowl = $this->extractDbList( $wgSiteMatrixFishbowlSites ); |
419 | } |
420 | |
421 | return in_array( $dbname, $this->fishbowl ); |
422 | } |
423 | |
424 | /** |
425 | * Check whether a wiki is non-global (not using single sign-on). |
426 | * |
427 | * @param string $dbname Database name |
428 | * @return bool |
429 | */ |
430 | public function isNonGlobal( $dbname ) { |
431 | global $wgSiteMatrixNonGlobalSites; |
432 | |
433 | if ( $this->nonglobal == null ) { |
434 | $this->nonglobal = $this->extractDbList( $wgSiteMatrixNonGlobalSites ); |
435 | } |
436 | |
437 | return in_array( $dbname, $this->nonglobal ); |
438 | } |
439 | |
440 | /** |
441 | * Check whether a wiki is special (the only wiki in its wiki family; typically this means |
442 | * a multilingual wiki). |
443 | * |
444 | * @param string $dbname Database name |
445 | * @return bool |
446 | */ |
447 | public function isSpecial( $dbname ) { |
448 | return in_array( $dbname, $this->specials ); |
449 | } |
450 | |
451 | /** |
452 | * Pull a list of dbnames from a given text file, or pass through an array. |
453 | * Used for the DB list configuration settings. |
454 | * |
455 | * @param string[]|string $listOrFilename Array of strings, or string with a file name |
456 | * @return string[] |
457 | */ |
458 | private function extractDbList( $listOrFilename ) { |
459 | if ( is_string( $listOrFilename ) ) { |
460 | return $this->extractFile( $listOrFilename ); |
461 | } elseif ( is_array( $listOrFilename ) ) { |
462 | return $listOrFilename; |
463 | } else { |
464 | return []; |
465 | } |
466 | } |
467 | |
468 | /** |
469 | * Pull a list of dbnames from a given text file. |
470 | * |
471 | * @param string $filename |
472 | * @return string[] |
473 | */ |
474 | private function extractFile( $filename ) { |
475 | $langCodes = array_map( 'trim', file( $filename ) ); |
476 | foreach ( $langCodes as $langCode ) { |
477 | $nonDeprecatedLangCode = LanguageCode::replaceDeprecatedCodes( $langCode ); |
478 | if ( $langCode !== $nonDeprecatedLangCode ) { |
479 | array_push( $langCodes, $nonDeprecatedLangCode ); |
480 | } |
481 | } |
482 | return array_unique( $langCodes ); |
483 | } |
484 | } |