These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /** |
||
3 | * Implements Special:Version |
||
4 | * |
||
5 | * Copyright © 2005 Ævar Arnfjörð Bjarmason |
||
6 | * |
||
7 | * This program is free software; you can redistribute it and/or modify |
||
8 | * it under the terms of the GNU General Public License as published by |
||
9 | * the Free Software Foundation; either version 2 of the License, or |
||
10 | * (at your option) any later version. |
||
11 | * |
||
12 | * This program is distributed in the hope that it will be useful, |
||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
15 | * GNU General Public License for more details. |
||
16 | * |
||
17 | * You should have received a copy of the GNU General Public License along |
||
18 | * with this program; if not, write to the Free Software Foundation, Inc., |
||
19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||
20 | * http://www.gnu.org/copyleft/gpl.html |
||
21 | * |
||
22 | * @file |
||
23 | * @ingroup SpecialPage |
||
24 | */ |
||
25 | |||
26 | /** |
||
27 | * Give information about the version of MediaWiki, PHP, the DB and extensions |
||
28 | * |
||
29 | * @ingroup SpecialPage |
||
30 | */ |
||
31 | class SpecialVersion extends SpecialPage { |
||
32 | protected $firstExtOpened = false; |
||
33 | |||
34 | /** |
||
35 | * Stores the current rev id/SHA hash of MediaWiki core |
||
36 | */ |
||
37 | protected $coreId = ''; |
||
38 | |||
39 | protected static $extensionTypes = false; |
||
40 | |||
41 | public function __construct() { |
||
42 | parent::__construct( 'Version' ); |
||
43 | } |
||
44 | |||
45 | /** |
||
46 | * main() |
||
47 | * @param string|null $par |
||
48 | */ |
||
49 | public function execute( $par ) { |
||
50 | global $IP, $wgExtensionCredits; |
||
51 | |||
52 | $this->setHeaders(); |
||
53 | $this->outputHeader(); |
||
54 | $out = $this->getOutput(); |
||
55 | $out->allowClickjacking(); |
||
56 | |||
57 | // Explode the sub page information into useful bits |
||
58 | $parts = explode( '/', (string)$par ); |
||
59 | $extNode = null; |
||
60 | if ( isset( $parts[1] ) ) { |
||
61 | $extName = str_replace( '_', ' ', $parts[1] ); |
||
62 | // Find it! |
||
63 | foreach ( $wgExtensionCredits as $group => $extensions ) { |
||
64 | foreach ( $extensions as $ext ) { |
||
65 | if ( isset( $ext['name'] ) && ( $ext['name'] === $extName ) ) { |
||
66 | $extNode = &$ext; |
||
67 | break 2; |
||
68 | } |
||
69 | } |
||
70 | } |
||
71 | if ( !$extNode ) { |
||
72 | $out->setStatusCode( 404 ); |
||
73 | } |
||
74 | } else { |
||
75 | $extName = 'MediaWiki'; |
||
76 | } |
||
77 | |||
78 | // Now figure out what to do |
||
79 | switch ( strtolower( $parts[0] ) ) { |
||
80 | case 'credits': |
||
81 | $wikiText = '{{int:version-credits-not-found}}'; |
||
82 | if ( $extName === 'MediaWiki' ) { |
||
83 | $wikiText = file_get_contents( $IP . '/CREDITS' ); |
||
84 | View Code Duplication | } elseif ( ( $extNode !== null ) && isset( $extNode['path'] ) ) { |
|
85 | $file = $this->getExtAuthorsFileName( dirname( $extNode['path'] ) ); |
||
86 | if ( $file ) { |
||
0 ignored issues
–
show
|
|||
87 | $wikiText = file_get_contents( $file ); |
||
88 | if ( substr( $file, -4 ) === '.txt' ) { |
||
89 | $wikiText = Html::element( |
||
90 | 'pre', |
||
91 | [ |
||
92 | 'lang' => 'en', |
||
93 | 'dir' => 'ltr', |
||
94 | ], |
||
95 | $wikiText |
||
96 | ); |
||
97 | } |
||
98 | } |
||
99 | } |
||
100 | |||
101 | $out->setPageTitle( $this->msg( 'version-credits-title', $extName ) ); |
||
102 | $out->addWikiText( $wikiText ); |
||
103 | break; |
||
104 | |||
105 | case 'license': |
||
106 | $wikiText = '{{int:version-license-not-found}}'; |
||
107 | View Code Duplication | if ( $extName === 'MediaWiki' ) { |
|
108 | $wikiText = file_get_contents( $IP . '/COPYING' ); |
||
109 | } elseif ( ( $extNode !== null ) && isset( $extNode['path'] ) ) { |
||
110 | $file = $this->getExtLicenseFileName( dirname( $extNode['path'] ) ); |
||
111 | if ( $file ) { |
||
0 ignored issues
–
show
The expression
$file of type false|string is loosely compared to true ; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.
In PHP, under loose comparison (like For '' == false // true
'' == null // true
'ab' == false // false
'ab' == null // false
// It is often better to use strict comparison
'' === false // false
'' === null // false
![]() |
|||
112 | $wikiText = file_get_contents( $file ); |
||
113 | $wikiText = Html::element( |
||
114 | 'pre', |
||
115 | [ |
||
116 | 'lang' => 'en', |
||
117 | 'dir' => 'ltr', |
||
118 | ], |
||
119 | $wikiText |
||
120 | ); |
||
121 | } |
||
122 | } |
||
123 | |||
124 | $out->setPageTitle( $this->msg( 'version-license-title', $extName ) ); |
||
125 | $out->addWikiText( $wikiText ); |
||
126 | break; |
||
127 | |||
128 | default: |
||
129 | $out->addModuleStyles( 'mediawiki.special.version' ); |
||
130 | $out->addWikiText( |
||
131 | $this->getMediaWikiCredits() . |
||
132 | $this->softwareInformation() . |
||
133 | $this->getEntryPointInfo() |
||
134 | ); |
||
135 | $out->addHTML( |
||
136 | $this->getSkinCredits() . |
||
137 | $this->getExtensionCredits() . |
||
138 | $this->getExternalLibraries() . |
||
139 | $this->getParserTags() . |
||
140 | $this->getParserFunctionHooks() |
||
141 | ); |
||
142 | $out->addWikiText( $this->getWgHooks() ); |
||
143 | $out->addHTML( $this->IPInfo() ); |
||
144 | |||
145 | break; |
||
146 | } |
||
147 | } |
||
148 | |||
149 | /** |
||
150 | * Returns wiki text showing the license information. |
||
151 | * |
||
152 | * @return string |
||
153 | */ |
||
154 | private static function getMediaWikiCredits() { |
||
155 | $ret = Xml::element( |
||
156 | 'h2', |
||
157 | [ 'id' => 'mw-version-license' ], |
||
158 | wfMessage( 'version-license' )->text() |
||
159 | ); |
||
160 | |||
161 | // This text is always left-to-right. |
||
162 | $ret .= '<div class="plainlinks">'; |
||
163 | $ret .= "__NOTOC__ |
||
164 | " . self::getCopyrightAndAuthorList() . "\n |
||
165 | " . wfMessage( 'version-license-info' )->text(); |
||
166 | $ret .= '</div>'; |
||
167 | |||
168 | return str_replace( "\t\t", '', $ret ) . "\n"; |
||
169 | } |
||
170 | |||
171 | /** |
||
172 | * Get the "MediaWiki is copyright 2001-20xx by lots of cool guys" text |
||
173 | * |
||
174 | * @return string |
||
175 | */ |
||
176 | public static function getCopyrightAndAuthorList() { |
||
177 | global $wgLang; |
||
178 | |||
179 | if ( defined( 'MEDIAWIKI_INSTALL' ) ) { |
||
180 | $othersLink = '[https://www.mediawiki.org/wiki/Special:Version/Credits ' . |
||
181 | wfMessage( 'version-poweredby-others' )->text() . ']'; |
||
182 | } else { |
||
183 | $othersLink = '[[Special:Version/Credits|' . |
||
184 | wfMessage( 'version-poweredby-others' )->text() . ']]'; |
||
185 | } |
||
186 | |||
187 | $translatorsLink = '[https://translatewiki.net/wiki/Translating:MediaWiki/Credits ' . |
||
188 | wfMessage( 'version-poweredby-translators' )->text() . ']'; |
||
189 | |||
190 | $authorList = [ |
||
191 | 'Magnus Manske', 'Brion Vibber', 'Lee Daniel Crocker', |
||
192 | 'Tim Starling', 'Erik Möller', 'Gabriel Wicke', 'Ævar Arnfjörð Bjarmason', |
||
193 | 'Niklas Laxström', 'Domas Mituzas', 'Rob Church', 'Yuri Astrakhan', |
||
194 | 'Aryeh Gregor', 'Aaron Schulz', 'Andrew Garrett', 'Raimond Spekking', |
||
195 | 'Alexandre Emsenhuber', 'Siebrand Mazeland', 'Chad Horohoe', |
||
196 | 'Roan Kattouw', 'Trevor Parscal', 'Bryan Tong Minh', 'Sam Reed', |
||
197 | 'Victor Vasiliev', 'Rotem Liss', 'Platonides', 'Antoine Musso', |
||
198 | 'Timo Tijhof', 'Daniel Kinzler', 'Jeroen De Dauw', 'Brad Jorsch', |
||
199 | $othersLink, $translatorsLink |
||
200 | ]; |
||
201 | |||
202 | return wfMessage( 'version-poweredby-credits', MWTimestamp::getLocalInstance()->format( 'Y' ), |
||
203 | $wgLang->listToText( $authorList ) )->text(); |
||
204 | } |
||
205 | |||
206 | /** |
||
207 | * Returns wiki text showing the third party software versions (apache, php, mysql). |
||
208 | * |
||
209 | * @return string |
||
210 | */ |
||
211 | public static function softwareInformation() { |
||
212 | $dbr = wfGetDB( DB_REPLICA ); |
||
213 | |||
214 | // Put the software in an array of form 'name' => 'version'. All messages should |
||
215 | // be loaded here, so feel free to use wfMessage in the 'name'. Raw HTML or |
||
216 | // wikimarkup can be used. |
||
217 | $software = []; |
||
218 | $software['[https://www.mediawiki.org/ MediaWiki]'] = self::getVersionLinked(); |
||
219 | if ( wfIsHHVM() ) { |
||
220 | $software['[http://hhvm.com/ HHVM]'] = HHVM_VERSION . " (" . PHP_SAPI . ")"; |
||
221 | } else { |
||
222 | $software['[https://php.net/ PHP]'] = PHP_VERSION . " (" . PHP_SAPI . ")"; |
||
223 | } |
||
224 | $software[$dbr->getSoftwareLink()] = $dbr->getServerInfo(); |
||
225 | |||
226 | if ( IcuCollation::getICUVersion() ) { |
||
227 | $software['[http://site.icu-project.org/ ICU]'] = IcuCollation::getICUVersion(); |
||
228 | } |
||
229 | |||
230 | // Allow a hook to add/remove items. |
||
231 | Hooks::run( 'SoftwareInfo', [ &$software ] ); |
||
232 | |||
233 | $out = Xml::element( |
||
234 | 'h2', |
||
235 | [ 'id' => 'mw-version-software' ], |
||
236 | wfMessage( 'version-software' )->text() |
||
237 | ) . |
||
238 | Xml::openElement( 'table', [ 'class' => 'wikitable plainlinks', 'id' => 'sv-software' ] ) . |
||
239 | "<tr> |
||
240 | <th>" . wfMessage( 'version-software-product' )->text() . "</th> |
||
241 | <th>" . wfMessage( 'version-software-version' )->text() . "</th> |
||
242 | </tr>\n"; |
||
243 | |||
244 | foreach ( $software as $name => $version ) { |
||
245 | $out .= "<tr> |
||
246 | <td>" . $name . "</td> |
||
247 | <td dir=\"ltr\">" . $version . "</td> |
||
248 | </tr>\n"; |
||
249 | } |
||
250 | |||
251 | return $out . Xml::closeElement( 'table' ); |
||
252 | } |
||
253 | |||
254 | /** |
||
255 | * Return a string of the MediaWiki version with Git revision if available. |
||
256 | * |
||
257 | * @param string $flags |
||
258 | * @param Language|string|null $lang |
||
259 | * @return mixed |
||
260 | */ |
||
261 | public static function getVersion( $flags = '', $lang = null ) { |
||
262 | global $wgVersion, $IP; |
||
263 | |||
264 | $gitInfo = self::getGitHeadSha1( $IP ); |
||
265 | if ( !$gitInfo ) { |
||
266 | $version = $wgVersion; |
||
267 | } elseif ( $flags === 'nodb' ) { |
||
268 | $shortSha1 = substr( $gitInfo, 0, 7 ); |
||
269 | $version = "$wgVersion ($shortSha1)"; |
||
270 | } else { |
||
271 | $shortSha1 = substr( $gitInfo, 0, 7 ); |
||
272 | $msg = wfMessage( 'parentheses' ); |
||
273 | if ( $lang !== null ) { |
||
274 | $msg->inLanguage( $lang ); |
||
275 | } |
||
276 | $shortSha1 = $msg->params( $shortSha1 )->escaped(); |
||
277 | $version = "$wgVersion $shortSha1"; |
||
278 | } |
||
279 | |||
280 | return $version; |
||
281 | } |
||
282 | |||
283 | /** |
||
284 | * Return a wikitext-formatted string of the MediaWiki version with a link to |
||
285 | * the Git SHA1 of head if available. |
||
286 | * The fallback is just $wgVersion |
||
287 | * |
||
288 | * @return mixed |
||
289 | */ |
||
290 | public static function getVersionLinked() { |
||
291 | global $wgVersion; |
||
292 | |||
293 | $gitVersion = self::getVersionLinkedGit(); |
||
294 | if ( $gitVersion ) { |
||
295 | $v = $gitVersion; |
||
296 | } else { |
||
297 | $v = $wgVersion; // fallback |
||
298 | } |
||
299 | |||
300 | return $v; |
||
301 | } |
||
302 | |||
303 | /** |
||
304 | * @return string |
||
305 | */ |
||
306 | private static function getwgVersionLinked() { |
||
307 | global $wgVersion; |
||
308 | $versionUrl = ""; |
||
309 | if ( Hooks::run( 'SpecialVersionVersionUrl', [ $wgVersion, &$versionUrl ] ) ) { |
||
310 | $versionParts = []; |
||
311 | preg_match( "/^(\d+\.\d+)/", $wgVersion, $versionParts ); |
||
312 | $versionUrl = "https://www.mediawiki.org/wiki/MediaWiki_{$versionParts[1]}"; |
||
313 | } |
||
314 | |||
315 | return "[$versionUrl $wgVersion]"; |
||
316 | } |
||
317 | |||
318 | /** |
||
319 | * @since 1.22 Returns the HEAD date in addition to the sha1 and link |
||
320 | * @return bool|string Global wgVersion + HEAD sha1 stripped to the first 7 chars |
||
321 | * with link and date, or false on failure |
||
322 | */ |
||
323 | private static function getVersionLinkedGit() { |
||
324 | global $IP, $wgLang; |
||
325 | |||
326 | $gitInfo = new GitInfo( $IP ); |
||
327 | $headSHA1 = $gitInfo->getHeadSHA1(); |
||
328 | if ( !$headSHA1 ) { |
||
329 | return false; |
||
330 | } |
||
331 | |||
332 | $shortSHA1 = '(' . substr( $headSHA1, 0, 7 ) . ')'; |
||
333 | |||
334 | $gitHeadUrl = $gitInfo->getHeadViewUrl(); |
||
335 | if ( $gitHeadUrl !== false ) { |
||
336 | $shortSHA1 = "[$gitHeadUrl $shortSHA1]"; |
||
337 | } |
||
338 | |||
339 | $gitHeadCommitDate = $gitInfo->getHeadCommitDate(); |
||
340 | if ( $gitHeadCommitDate ) { |
||
341 | $shortSHA1 .= Html::element( 'br' ) . $wgLang->timeanddate( $gitHeadCommitDate, true ); |
||
342 | } |
||
343 | |||
344 | return self::getwgVersionLinked() . " $shortSHA1"; |
||
345 | } |
||
346 | |||
347 | /** |
||
348 | * Returns an array with the base extension types. |
||
349 | * Type is stored as array key, the message as array value. |
||
350 | * |
||
351 | * TODO: ideally this would return all extension types. |
||
352 | * |
||
353 | * @since 1.17 |
||
354 | * |
||
355 | * @return array |
||
356 | */ |
||
357 | public static function getExtensionTypes() { |
||
358 | if ( self::$extensionTypes === false ) { |
||
359 | self::$extensionTypes = [ |
||
360 | 'specialpage' => wfMessage( 'version-specialpages' )->text(), |
||
361 | 'parserhook' => wfMessage( 'version-parserhooks' )->text(), |
||
362 | 'variable' => wfMessage( 'version-variables' )->text(), |
||
363 | 'media' => wfMessage( 'version-mediahandlers' )->text(), |
||
364 | 'antispam' => wfMessage( 'version-antispam' )->text(), |
||
365 | 'skin' => wfMessage( 'version-skins' )->text(), |
||
366 | 'api' => wfMessage( 'version-api' )->text(), |
||
367 | 'other' => wfMessage( 'version-other' )->text(), |
||
368 | ]; |
||
369 | |||
370 | Hooks::run( 'ExtensionTypes', [ &self::$extensionTypes ] ); |
||
371 | } |
||
372 | |||
373 | return self::$extensionTypes; |
||
374 | } |
||
375 | |||
376 | /** |
||
377 | * Returns the internationalized name for an extension type. |
||
378 | * |
||
379 | * @since 1.17 |
||
380 | * |
||
381 | * @param string $type |
||
382 | * |
||
383 | * @return string |
||
384 | */ |
||
385 | public static function getExtensionTypeName( $type ) { |
||
386 | $types = self::getExtensionTypes(); |
||
387 | |||
388 | return isset( $types[$type] ) ? $types[$type] : $types['other']; |
||
389 | } |
||
390 | |||
391 | /** |
||
392 | * Generate wikitext showing the name, URL, author and description of each extension. |
||
393 | * |
||
394 | * @return string Wikitext |
||
395 | */ |
||
396 | public function getExtensionCredits() { |
||
397 | global $wgExtensionCredits; |
||
398 | |||
399 | View Code Duplication | if ( |
|
400 | count( $wgExtensionCredits ) === 0 || |
||
401 | // Skins are displayed separately, see getSkinCredits() |
||
402 | ( count( $wgExtensionCredits ) === 1 && isset( $wgExtensionCredits['skin'] ) ) |
||
403 | ) { |
||
404 | return ''; |
||
405 | } |
||
406 | |||
407 | $extensionTypes = self::getExtensionTypes(); |
||
408 | |||
409 | $out = Xml::element( |
||
410 | 'h2', |
||
411 | [ 'id' => 'mw-version-ext' ], |
||
412 | $this->msg( 'version-extensions' )->text() |
||
413 | ) . |
||
414 | Xml::openElement( 'table', [ 'class' => 'wikitable plainlinks', 'id' => 'sv-ext' ] ); |
||
415 | |||
416 | // Make sure the 'other' type is set to an array. |
||
417 | if ( !array_key_exists( 'other', $wgExtensionCredits ) ) { |
||
418 | $wgExtensionCredits['other'] = []; |
||
419 | } |
||
420 | |||
421 | // Find all extensions that do not have a valid type and give them the type 'other'. |
||
422 | foreach ( $wgExtensionCredits as $type => $extensions ) { |
||
423 | if ( !array_key_exists( $type, $extensionTypes ) ) { |
||
424 | $wgExtensionCredits['other'] = array_merge( $wgExtensionCredits['other'], $extensions ); |
||
425 | } |
||
426 | } |
||
427 | |||
428 | $this->firstExtOpened = false; |
||
429 | // Loop through the extension categories to display their extensions in the list. |
||
430 | foreach ( $extensionTypes as $type => $message ) { |
||
431 | // Skins have a separate section |
||
432 | if ( $type !== 'other' && $type !== 'skin' ) { |
||
433 | $out .= $this->getExtensionCategory( $type, $message ); |
||
434 | } |
||
435 | } |
||
436 | |||
437 | // We want the 'other' type to be last in the list. |
||
438 | $out .= $this->getExtensionCategory( 'other', $extensionTypes['other'] ); |
||
439 | |||
440 | $out .= Xml::closeElement( 'table' ); |
||
441 | |||
442 | return $out; |
||
443 | } |
||
444 | |||
445 | /** |
||
446 | * Generate wikitext showing the name, URL, author and description of each skin. |
||
447 | * |
||
448 | * @return string Wikitext |
||
449 | */ |
||
450 | public function getSkinCredits() { |
||
451 | global $wgExtensionCredits; |
||
452 | View Code Duplication | if ( !isset( $wgExtensionCredits['skin'] ) || count( $wgExtensionCredits['skin'] ) === 0 ) { |
|
453 | return ''; |
||
454 | } |
||
455 | |||
456 | $out = Xml::element( |
||
457 | 'h2', |
||
458 | [ 'id' => 'mw-version-skin' ], |
||
459 | $this->msg( 'version-skins' )->text() |
||
460 | ) . |
||
461 | Xml::openElement( 'table', [ 'class' => 'wikitable plainlinks', 'id' => 'sv-skin' ] ); |
||
462 | |||
463 | $this->firstExtOpened = false; |
||
464 | $out .= $this->getExtensionCategory( 'skin', null ); |
||
465 | |||
466 | $out .= Xml::closeElement( 'table' ); |
||
467 | |||
468 | return $out; |
||
469 | } |
||
470 | |||
471 | /** |
||
472 | * Generate an HTML table for external libraries that are installed |
||
473 | * |
||
474 | * @return string |
||
475 | */ |
||
476 | protected function getExternalLibraries() { |
||
477 | global $IP; |
||
478 | $path = "$IP/vendor/composer/installed.json"; |
||
479 | if ( !file_exists( $path ) ) { |
||
480 | return ''; |
||
481 | } |
||
482 | |||
483 | $installed = new ComposerInstalled( $path ); |
||
484 | $out = Html::element( |
||
485 | 'h2', |
||
486 | [ 'id' => 'mw-version-libraries' ], |
||
487 | $this->msg( 'version-libraries' )->text() |
||
488 | ); |
||
489 | $out .= Html::openElement( |
||
490 | 'table', |
||
491 | [ 'class' => 'wikitable plainlinks', 'id' => 'sv-libraries' ] |
||
492 | ); |
||
493 | $out .= Html::openElement( 'tr' ) |
||
494 | . Html::element( 'th', [], $this->msg( 'version-libraries-library' )->text() ) |
||
495 | . Html::element( 'th', [], $this->msg( 'version-libraries-version' )->text() ) |
||
496 | . Html::element( 'th', [], $this->msg( 'version-libraries-license' )->text() ) |
||
497 | . Html::element( 'th', [], $this->msg( 'version-libraries-description' )->text() ) |
||
498 | . Html::element( 'th', [], $this->msg( 'version-libraries-authors' )->text() ) |
||
499 | . Html::closeElement( 'tr' ); |
||
500 | |||
501 | foreach ( $installed->getInstalledDependencies() as $name => $info ) { |
||
502 | if ( strpos( $info['type'], 'mediawiki-' ) === 0 ) { |
||
503 | // Skip any extensions or skins since they'll be listed |
||
504 | // in their proper section |
||
505 | continue; |
||
506 | } |
||
507 | $authors = array_map( function( $arr ) { |
||
508 | // If a homepage is set, link to it |
||
509 | if ( isset( $arr['homepage'] ) ) { |
||
510 | return "[{$arr['homepage']} {$arr['name']}]"; |
||
511 | } |
||
512 | return $arr['name']; |
||
513 | }, $info['authors'] ); |
||
514 | $authors = $this->listAuthors( $authors, false, "$IP/vendor/$name" ); |
||
515 | |||
516 | // We can safely assume that the libraries' names and descriptions |
||
517 | // are written in English and aren't going to be translated, |
||
518 | // so set appropriate lang and dir attributes |
||
519 | $out .= Html::openElement( 'tr' ) |
||
520 | . Html::rawElement( |
||
521 | 'td', |
||
522 | [], |
||
523 | Linker::makeExternalLink( |
||
524 | "https://packagist.org/packages/$name", $name, |
||
525 | true, '', |
||
526 | [ 'class' => 'mw-version-library-name' ] |
||
527 | ) |
||
528 | ) |
||
529 | . Html::element( 'td', [ 'dir' => 'auto' ], $info['version'] ) |
||
530 | . Html::element( 'td', [ 'dir' => 'auto' ], $this->listToText( $info['licenses'] ) ) |
||
531 | . Html::element( 'td', [ 'lang' => 'en', 'dir' => 'ltr' ], $info['description'] ) |
||
532 | . Html::rawElement( 'td', [], $authors ) |
||
533 | . Html::closeElement( 'tr' ); |
||
534 | } |
||
535 | $out .= Html::closeElement( 'table' ); |
||
536 | |||
537 | return $out; |
||
538 | } |
||
539 | |||
540 | /** |
||
541 | * Obtains a list of installed parser tags and the associated H2 header |
||
542 | * |
||
543 | * @return string HTML output |
||
544 | */ |
||
545 | protected function getParserTags() { |
||
546 | global $wgParser; |
||
547 | |||
548 | $tags = $wgParser->getTags(); |
||
549 | |||
550 | if ( count( $tags ) ) { |
||
551 | $out = Html::rawElement( |
||
552 | 'h2', |
||
553 | [ |
||
554 | 'class' => 'mw-headline plainlinks', |
||
555 | 'id' => 'mw-version-parser-extensiontags', |
||
556 | ], |
||
557 | Linker::makeExternalLink( |
||
558 | 'https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Tag_extensions', |
||
559 | $this->msg( 'version-parser-extensiontags' )->parse(), |
||
560 | false /* msg()->parse() already escapes */ |
||
561 | ) |
||
562 | ); |
||
563 | |||
564 | array_walk( $tags, function ( &$value ) { |
||
565 | // Bidirectional isolation improves readability in RTL wikis |
||
566 | $value = Html::element( |
||
567 | 'bdi', |
||
568 | // Prevent < and > from slipping to another line |
||
569 | [ |
||
570 | 'style' => 'white-space: nowrap;', |
||
571 | ], |
||
572 | "<$value>" |
||
573 | ); |
||
574 | } ); |
||
575 | |||
576 | $out .= $this->listToText( $tags ); |
||
577 | } else { |
||
578 | $out = ''; |
||
579 | } |
||
580 | |||
581 | return $out; |
||
582 | } |
||
583 | |||
584 | /** |
||
585 | * Obtains a list of installed parser function hooks and the associated H2 header |
||
586 | * |
||
587 | * @return string HTML output |
||
588 | */ |
||
589 | protected function getParserFunctionHooks() { |
||
590 | global $wgParser; |
||
591 | |||
592 | $fhooks = $wgParser->getFunctionHooks(); |
||
593 | if ( count( $fhooks ) ) { |
||
594 | $out = Html::rawElement( |
||
595 | 'h2', |
||
596 | [ |
||
597 | 'class' => 'mw-headline plainlinks', |
||
598 | 'id' => 'mw-version-parser-function-hooks', |
||
599 | ], |
||
600 | Linker::makeExternalLink( |
||
601 | 'https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Parser_functions', |
||
602 | $this->msg( 'version-parser-function-hooks' )->parse(), |
||
603 | false /* msg()->parse() already escapes */ |
||
604 | ) |
||
605 | ); |
||
606 | |||
607 | $out .= $this->listToText( $fhooks ); |
||
608 | } else { |
||
609 | $out = ''; |
||
610 | } |
||
611 | |||
612 | return $out; |
||
613 | } |
||
614 | |||
615 | /** |
||
616 | * Creates and returns the HTML for a single extension category. |
||
617 | * |
||
618 | * @since 1.17 |
||
619 | * |
||
620 | * @param string $type |
||
621 | * @param string $message |
||
622 | * |
||
623 | * @return string |
||
624 | */ |
||
625 | protected function getExtensionCategory( $type, $message ) { |
||
626 | global $wgExtensionCredits; |
||
627 | |||
628 | $out = ''; |
||
629 | |||
630 | if ( array_key_exists( $type, $wgExtensionCredits ) && count( $wgExtensionCredits[$type] ) > 0 ) { |
||
631 | $out .= $this->openExtType( $message, 'credits-' . $type ); |
||
632 | |||
633 | usort( $wgExtensionCredits[$type], [ $this, 'compare' ] ); |
||
634 | |||
635 | foreach ( $wgExtensionCredits[$type] as $extension ) { |
||
636 | $out .= $this->getCreditsForExtension( $type, $extension ); |
||
637 | } |
||
638 | } |
||
639 | |||
640 | return $out; |
||
641 | } |
||
642 | |||
643 | /** |
||
644 | * Callback to sort extensions by type. |
||
645 | * @param array $a |
||
646 | * @param array $b |
||
647 | * @return int |
||
648 | */ |
||
649 | public function compare( $a, $b ) { |
||
650 | if ( $a['name'] === $b['name'] ) { |
||
651 | return 0; |
||
652 | } else { |
||
653 | return $this->getLanguage()->lc( $a['name'] ) > $this->getLanguage()->lc( $b['name'] ) |
||
654 | ? 1 |
||
655 | : -1; |
||
656 | } |
||
657 | } |
||
658 | |||
659 | /** |
||
660 | * Creates and formats a version line for a single extension. |
||
661 | * |
||
662 | * Information for five columns will be created. Parameters required in the |
||
663 | * $extension array for part rendering are indicated in () |
||
664 | * - The name of (name), and URL link to (url), the extension |
||
665 | * - Official version number (version) and if available version control system |
||
666 | * revision (path), link, and date |
||
667 | * - If available the short name of the license (license-name) and a link |
||
668 | * to ((LICENSE)|(COPYING))(\.txt)? if it exists. |
||
669 | * - Description of extension (descriptionmsg or description) |
||
670 | * - List of authors (author) and link to a ((AUTHORS)|(CREDITS))(\.txt)? file if it exists |
||
671 | * |
||
672 | * @param string $type Category name of the extension |
||
673 | * @param array $extension |
||
674 | * |
||
675 | * @return string Raw HTML |
||
676 | */ |
||
677 | public function getCreditsForExtension( $type, array $extension ) { |
||
678 | $out = $this->getOutput(); |
||
679 | |||
680 | // We must obtain the information for all the bits and pieces! |
||
681 | // ... such as extension names and links |
||
682 | if ( isset( $extension['namemsg'] ) ) { |
||
683 | // Localized name of extension |
||
684 | $extensionName = $this->msg( $extension['namemsg'] )->text(); |
||
685 | } elseif ( isset( $extension['name'] ) ) { |
||
686 | // Non localized version |
||
687 | $extensionName = $extension['name']; |
||
688 | } else { |
||
689 | $extensionName = $this->msg( 'version-no-ext-name' )->text(); |
||
690 | } |
||
691 | |||
692 | if ( isset( $extension['url'] ) ) { |
||
693 | $extensionNameLink = Linker::makeExternalLink( |
||
694 | $extension['url'], |
||
695 | $extensionName, |
||
696 | true, |
||
697 | '', |
||
698 | [ 'class' => 'mw-version-ext-name' ] |
||
699 | ); |
||
700 | } else { |
||
701 | $extensionNameLink = $extensionName; |
||
702 | } |
||
703 | |||
704 | // ... and the version information |
||
705 | // If the extension path is set we will check that directory for GIT |
||
706 | // metadata in an attempt to extract date and vcs commit metadata. |
||
707 | $canonicalVersion = '–'; |
||
708 | $extensionPath = null; |
||
709 | $vcsVersion = null; |
||
710 | $vcsLink = null; |
||
711 | $vcsDate = null; |
||
712 | |||
713 | if ( isset( $extension['version'] ) ) { |
||
714 | $canonicalVersion = $out->parseInline( $extension['version'] ); |
||
715 | } |
||
716 | |||
717 | if ( isset( $extension['path'] ) ) { |
||
718 | global $IP; |
||
719 | $extensionPath = dirname( $extension['path'] ); |
||
720 | if ( $this->coreId == '' ) { |
||
721 | wfDebug( 'Looking up core head id' ); |
||
722 | $coreHeadSHA1 = self::getGitHeadSha1( $IP ); |
||
723 | if ( $coreHeadSHA1 ) { |
||
724 | $this->coreId = $coreHeadSHA1; |
||
725 | } |
||
726 | } |
||
727 | $cache = wfGetCache( CACHE_ANYTHING ); |
||
728 | $memcKey = wfMemcKey( 'specialversion-ext-version-text', $extension['path'], $this->coreId ); |
||
729 | list( $vcsVersion, $vcsLink, $vcsDate ) = $cache->get( $memcKey ); |
||
730 | |||
731 | if ( !$vcsVersion ) { |
||
732 | wfDebug( "Getting VCS info for extension {$extension['name']}" ); |
||
733 | $gitInfo = new GitInfo( $extensionPath ); |
||
734 | $vcsVersion = $gitInfo->getHeadSHA1(); |
||
735 | if ( $vcsVersion !== false ) { |
||
736 | $vcsVersion = substr( $vcsVersion, 0, 7 ); |
||
737 | $vcsLink = $gitInfo->getHeadViewUrl(); |
||
738 | $vcsDate = $gitInfo->getHeadCommitDate(); |
||
739 | } |
||
740 | $cache->set( $memcKey, [ $vcsVersion, $vcsLink, $vcsDate ], 60 * 60 * 24 ); |
||
741 | } else { |
||
742 | wfDebug( "Pulled VCS info for extension {$extension['name']} from cache" ); |
||
743 | } |
||
744 | } |
||
745 | |||
746 | $versionString = Html::rawElement( |
||
747 | 'span', |
||
748 | [ 'class' => 'mw-version-ext-version' ], |
||
749 | $canonicalVersion |
||
750 | ); |
||
751 | |||
752 | if ( $vcsVersion ) { |
||
753 | if ( $vcsLink ) { |
||
754 | $vcsVerString = Linker::makeExternalLink( |
||
755 | $vcsLink, |
||
756 | $this->msg( 'version-version', $vcsVersion ), |
||
757 | true, |
||
758 | '', |
||
759 | [ 'class' => 'mw-version-ext-vcs-version' ] |
||
760 | ); |
||
761 | } else { |
||
762 | $vcsVerString = Html::element( 'span', |
||
763 | [ 'class' => 'mw-version-ext-vcs-version' ], |
||
764 | "({$vcsVersion})" |
||
765 | ); |
||
766 | } |
||
767 | $versionString .= " {$vcsVerString}"; |
||
768 | |||
769 | if ( $vcsDate ) { |
||
770 | $vcsTimeString = Html::element( 'span', |
||
771 | [ 'class' => 'mw-version-ext-vcs-timestamp' ], |
||
772 | $this->getLanguage()->timeanddate( $vcsDate, true ) |
||
773 | ); |
||
774 | $versionString .= " {$vcsTimeString}"; |
||
775 | } |
||
776 | $versionString = Html::rawElement( 'span', |
||
777 | [ 'class' => 'mw-version-ext-meta-version' ], |
||
778 | $versionString |
||
779 | ); |
||
780 | } |
||
781 | |||
782 | // ... and license information; if a license file exists we |
||
783 | // will link to it |
||
784 | $licenseLink = ''; |
||
785 | if ( isset( $extension['name'] ) ) { |
||
786 | $licenseName = null; |
||
787 | if ( isset( $extension['license-name'] ) ) { |
||
788 | $licenseName = $out->parseInline( $extension['license-name'] ); |
||
789 | } elseif ( $this->getExtLicenseFileName( $extensionPath ) ) { |
||
790 | $licenseName = $this->msg( 'version-ext-license' )->escaped(); |
||
791 | } |
||
792 | if ( $licenseName !== null ) { |
||
793 | $licenseLink = Linker::link( |
||
794 | $this->getPageTitle( 'License/' . $extension['name'] ), |
||
795 | $licenseName, |
||
796 | [ |
||
797 | 'class' => 'mw-version-ext-license', |
||
798 | 'dir' => 'auto', |
||
799 | ] |
||
800 | ); |
||
801 | } |
||
802 | } |
||
803 | |||
804 | // ... and generate the description; which can be a parameterized l10n message |
||
805 | // in the form array( <msgname>, <parameter>, <parameter>... ) or just a straight |
||
806 | // up string |
||
807 | if ( isset( $extension['descriptionmsg'] ) ) { |
||
808 | // Localized description of extension |
||
809 | $descriptionMsg = $extension['descriptionmsg']; |
||
810 | |||
811 | if ( is_array( $descriptionMsg ) ) { |
||
812 | $descriptionMsgKey = $descriptionMsg[0]; // Get the message key |
||
813 | array_shift( $descriptionMsg ); // Shift out the message key to get the parameters only |
||
814 | array_map( "htmlspecialchars", $descriptionMsg ); // For sanity |
||
815 | $description = $this->msg( $descriptionMsgKey, $descriptionMsg )->text(); |
||
816 | } else { |
||
817 | $description = $this->msg( $descriptionMsg )->text(); |
||
818 | } |
||
819 | } elseif ( isset( $extension['description'] ) ) { |
||
820 | // Non localized version |
||
821 | $description = $extension['description']; |
||
822 | } else { |
||
823 | $description = ''; |
||
824 | } |
||
825 | $description = $out->parseInline( $description ); |
||
826 | |||
827 | // ... now get the authors for this extension |
||
828 | $authors = isset( $extension['author'] ) ? $extension['author'] : []; |
||
829 | $authors = $this->listAuthors( $authors, $extension['name'], $extensionPath ); |
||
830 | |||
831 | // Finally! Create the table |
||
832 | $html = Html::openElement( 'tr', [ |
||
833 | 'class' => 'mw-version-ext', |
||
834 | 'id' => Sanitizer::escapeId( 'mw-version-ext-' . $type . '-' . $extension['name'] ) |
||
835 | ] |
||
836 | ); |
||
837 | |||
838 | $html .= Html::rawElement( 'td', [], $extensionNameLink ); |
||
839 | $html .= Html::rawElement( 'td', [], $versionString ); |
||
840 | $html .= Html::rawElement( 'td', [], $licenseLink ); |
||
841 | $html .= Html::rawElement( 'td', [ 'class' => 'mw-version-ext-description' ], $description ); |
||
842 | $html .= Html::rawElement( 'td', [ 'class' => 'mw-version-ext-authors' ], $authors ); |
||
843 | |||
844 | $html .= Html::closeElement( 'tr' ); |
||
845 | |||
846 | return $html; |
||
847 | } |
||
848 | |||
849 | /** |
||
850 | * Generate wikitext showing hooks in $wgHooks. |
||
851 | * |
||
852 | * @return string Wikitext |
||
853 | */ |
||
854 | private function getWgHooks() { |
||
855 | global $wgSpecialVersionShowHooks, $wgHooks; |
||
856 | |||
857 | if ( $wgSpecialVersionShowHooks && count( $wgHooks ) ) { |
||
858 | $myWgHooks = $wgHooks; |
||
859 | ksort( $myWgHooks ); |
||
860 | |||
861 | $ret = []; |
||
862 | $ret[] = '== {{int:version-hooks}} =='; |
||
863 | $ret[] = Html::openElement( 'table', [ 'class' => 'wikitable', 'id' => 'sv-hooks' ] ); |
||
864 | $ret[] = Html::openElement( 'tr' ); |
||
865 | $ret[] = Html::element( 'th', [], $this->msg( 'version-hook-name' )->text() ); |
||
866 | $ret[] = Html::element( 'th', [], $this->msg( 'version-hook-subscribedby' )->text() ); |
||
867 | $ret[] = Html::closeElement( 'tr' ); |
||
868 | |||
869 | foreach ( $myWgHooks as $hook => $hooks ) { |
||
870 | $ret[] = Html::openElement( 'tr' ); |
||
871 | $ret[] = Html::element( 'td', [], $hook ); |
||
872 | $ret[] = Html::element( 'td', [], $this->listToText( $hooks ) ); |
||
873 | $ret[] = Html::closeElement( 'tr' ); |
||
874 | } |
||
875 | |||
876 | $ret[] = Html::closeElement( 'table' ); |
||
877 | |||
878 | return implode( "\n", $ret ); |
||
879 | } else { |
||
880 | return ''; |
||
881 | } |
||
882 | } |
||
883 | |||
884 | private function openExtType( $text = null, $name = null ) { |
||
885 | $out = ''; |
||
886 | |||
887 | $opt = [ 'colspan' => 5 ]; |
||
888 | if ( $this->firstExtOpened ) { |
||
889 | // Insert a spacing line |
||
890 | $out .= Html::rawElement( 'tr', [ 'class' => 'sv-space' ], |
||
891 | Html::element( 'td', $opt ) |
||
892 | ); |
||
893 | } |
||
894 | $this->firstExtOpened = true; |
||
895 | |||
896 | if ( $name ) { |
||
897 | $opt['id'] = "sv-$name"; |
||
898 | } |
||
899 | |||
900 | if ( $text !== null ) { |
||
901 | $out .= Html::rawElement( 'tr', [], |
||
902 | Html::element( 'th', $opt, $text ) |
||
903 | ); |
||
904 | } |
||
905 | |||
906 | $firstHeadingMsg = ( $name === 'credits-skin' ) |
||
907 | ? 'version-skin-colheader-name' |
||
908 | : 'version-ext-colheader-name'; |
||
909 | $out .= Html::openElement( 'tr' ); |
||
910 | $out .= Html::element( 'th', [ 'class' => 'mw-version-ext-col-label' ], |
||
911 | $this->msg( $firstHeadingMsg )->text() ); |
||
912 | $out .= Html::element( 'th', [ 'class' => 'mw-version-ext-col-label' ], |
||
913 | $this->msg( 'version-ext-colheader-version' )->text() ); |
||
914 | $out .= Html::element( 'th', [ 'class' => 'mw-version-ext-col-label' ], |
||
915 | $this->msg( 'version-ext-colheader-license' )->text() ); |
||
916 | $out .= Html::element( 'th', [ 'class' => 'mw-version-ext-col-label' ], |
||
917 | $this->msg( 'version-ext-colheader-description' )->text() ); |
||
918 | $out .= Html::element( 'th', [ 'class' => 'mw-version-ext-col-label' ], |
||
919 | $this->msg( 'version-ext-colheader-credits' )->text() ); |
||
920 | $out .= Html::closeElement( 'tr' ); |
||
921 | |||
922 | return $out; |
||
923 | } |
||
924 | |||
925 | /** |
||
926 | * Get information about client's IP address. |
||
927 | * |
||
928 | * @return string HTML fragment |
||
929 | */ |
||
930 | private function IPInfo() { |
||
931 | $ip = str_replace( '--', ' - ', htmlspecialchars( $this->getRequest()->getIP() ) ); |
||
932 | |||
933 | return "<!-- visited from $ip -->\n<span style='display:none'>visited from $ip</span>"; |
||
934 | } |
||
935 | |||
936 | /** |
||
937 | * Return a formatted unsorted list of authors |
||
938 | * |
||
939 | * 'And Others' |
||
940 | * If an item in the $authors array is '...' it is assumed to indicate an |
||
941 | * 'and others' string which will then be linked to an ((AUTHORS)|(CREDITS))(\.txt)? |
||
942 | * file if it exists in $dir. |
||
943 | * |
||
944 | * Similarly an entry ending with ' ...]' is assumed to be a link to an |
||
945 | * 'and others' page. |
||
946 | * |
||
947 | * If no '...' string variant is found, but an authors file is found an |
||
948 | * 'and others' will be added to the end of the credits. |
||
949 | * |
||
950 | * @param string|array $authors |
||
951 | * @param string|bool $extName Name of the extension for link creation, |
||
952 | * false if no links should be created |
||
953 | * @param string $extDir Path to the extension root directory |
||
954 | * |
||
955 | * @return string HTML fragment |
||
956 | */ |
||
957 | public function listAuthors( $authors, $extName, $extDir ) { |
||
958 | $hasOthers = false; |
||
959 | |||
960 | $list = []; |
||
961 | foreach ( (array)$authors as $item ) { |
||
962 | if ( $item == '...' ) { |
||
963 | $hasOthers = true; |
||
964 | |||
965 | if ( $extName && $this->getExtAuthorsFileName( $extDir ) ) { |
||
966 | $text = Linker::link( |
||
967 | $this->getPageTitle( "Credits/$extName" ), |
||
968 | $this->msg( 'version-poweredby-others' )->escaped() |
||
969 | ); |
||
970 | } else { |
||
971 | $text = $this->msg( 'version-poweredby-others' )->escaped(); |
||
972 | } |
||
973 | $list[] = $text; |
||
974 | } elseif ( substr( $item, -5 ) == ' ...]' ) { |
||
975 | $hasOthers = true; |
||
976 | $list[] = $this->getOutput()->parseInline( |
||
977 | substr( $item, 0, -4 ) . $this->msg( 'version-poweredby-others' )->text() . "]" |
||
978 | ); |
||
979 | } else { |
||
980 | $list[] = $this->getOutput()->parseInline( $item ); |
||
981 | } |
||
982 | } |
||
983 | |||
984 | if ( $extName && !$hasOthers && $this->getExtAuthorsFileName( $extDir ) ) { |
||
985 | $list[] = $text = Linker::link( |
||
0 ignored issues
–
show
$text is not used, you could remove the assignment.
This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently. $myVar = 'Value';
$higher = false;
if (rand(1, 6) > 3) {
$higher = true;
} else {
$higher = false;
}
Both the ![]() |
|||
986 | $this->getPageTitle( "Credits/$extName" ), |
||
987 | $this->msg( 'version-poweredby-others' )->escaped() |
||
988 | ); |
||
989 | } |
||
990 | |||
991 | return $this->listToText( $list, false ); |
||
992 | } |
||
993 | |||
994 | /** |
||
995 | * Obtains the full path of an extensions authors or credits file if |
||
996 | * one exists. |
||
997 | * |
||
998 | * @param string $extDir Path to the extensions root directory |
||
999 | * |
||
1000 | * @since 1.23 |
||
1001 | * |
||
1002 | * @return bool|string False if no such file exists, otherwise returns |
||
1003 | * a path to it. |
||
1004 | */ |
||
1005 | View Code Duplication | public static function getExtAuthorsFileName( $extDir ) { |
|
1006 | if ( !$extDir ) { |
||
1007 | return false; |
||
1008 | } |
||
1009 | |||
1010 | foreach ( scandir( $extDir ) as $file ) { |
||
1011 | $fullPath = $extDir . DIRECTORY_SEPARATOR . $file; |
||
1012 | if ( preg_match( '/^((AUTHORS)|(CREDITS))(\.txt|\.wiki|\.mediawiki)?$/', $file ) && |
||
1013 | is_readable( $fullPath ) && |
||
1014 | is_file( $fullPath ) |
||
1015 | ) { |
||
1016 | return $fullPath; |
||
1017 | } |
||
1018 | } |
||
1019 | |||
1020 | return false; |
||
1021 | } |
||
1022 | |||
1023 | /** |
||
1024 | * Obtains the full path of an extensions copying or license file if |
||
1025 | * one exists. |
||
1026 | * |
||
1027 | * @param string $extDir Path to the extensions root directory |
||
1028 | * |
||
1029 | * @since 1.23 |
||
1030 | * |
||
1031 | * @return bool|string False if no such file exists, otherwise returns |
||
1032 | * a path to it. |
||
1033 | */ |
||
1034 | View Code Duplication | public static function getExtLicenseFileName( $extDir ) { |
|
1035 | if ( !$extDir ) { |
||
1036 | return false; |
||
1037 | } |
||
1038 | |||
1039 | foreach ( scandir( $extDir ) as $file ) { |
||
1040 | $fullPath = $extDir . DIRECTORY_SEPARATOR . $file; |
||
1041 | if ( preg_match( '/^((COPYING)|(LICENSE))(\.txt)?$/', $file ) && |
||
1042 | is_readable( $fullPath ) && |
||
1043 | is_file( $fullPath ) |
||
1044 | ) { |
||
1045 | return $fullPath; |
||
1046 | } |
||
1047 | } |
||
1048 | |||
1049 | return false; |
||
1050 | } |
||
1051 | |||
1052 | /** |
||
1053 | * Convert an array of items into a list for display. |
||
1054 | * |
||
1055 | * @param array $list List of elements to display |
||
1056 | * @param bool $sort Whether to sort the items in $list |
||
1057 | * |
||
1058 | * @return string |
||
1059 | */ |
||
1060 | public function listToText( $list, $sort = true ) { |
||
1061 | if ( !count( $list ) ) { |
||
1062 | return ''; |
||
1063 | } |
||
1064 | if ( $sort ) { |
||
1065 | sort( $list ); |
||
1066 | } |
||
1067 | |||
1068 | return $this->getLanguage() |
||
1069 | ->listToText( array_map( [ __CLASS__, 'arrayToString' ], $list ) ); |
||
1070 | } |
||
1071 | |||
1072 | /** |
||
1073 | * Convert an array or object to a string for display. |
||
1074 | * |
||
1075 | * @param mixed $list Will convert an array to string if given and return |
||
1076 | * the parameter unaltered otherwise |
||
1077 | * |
||
1078 | * @return mixed |
||
1079 | */ |
||
1080 | public static function arrayToString( $list ) { |
||
1081 | if ( is_array( $list ) && count( $list ) == 1 ) { |
||
1082 | $list = $list[0]; |
||
1083 | } |
||
1084 | if ( $list instanceof Closure ) { |
||
1085 | // Don't output stuff like "Closure$;1028376090#8$48499d94fe0147f7c633b365be39952b$" |
||
1086 | return 'Closure'; |
||
1087 | } elseif ( is_object( $list ) ) { |
||
1088 | $class = wfMessage( 'parentheses' )->params( get_class( $list ) )->escaped(); |
||
1089 | |||
1090 | return $class; |
||
1091 | } elseif ( !is_array( $list ) ) { |
||
1092 | return $list; |
||
1093 | } else { |
||
1094 | if ( is_object( $list[0] ) ) { |
||
1095 | $class = get_class( $list[0] ); |
||
1096 | } else { |
||
1097 | $class = $list[0]; |
||
1098 | } |
||
1099 | |||
1100 | return wfMessage( 'parentheses' )->params( "$class, {$list[1]}" )->escaped(); |
||
1101 | } |
||
1102 | } |
||
1103 | |||
1104 | /** |
||
1105 | * @param string $dir Directory of the git checkout |
||
1106 | * @return bool|string Sha1 of commit HEAD points to |
||
1107 | */ |
||
1108 | public static function getGitHeadSha1( $dir ) { |
||
1109 | $repo = new GitInfo( $dir ); |
||
1110 | |||
1111 | return $repo->getHeadSHA1(); |
||
1112 | } |
||
1113 | |||
1114 | /** |
||
1115 | * @param string $dir Directory of the git checkout |
||
1116 | * @return bool|string Branch currently checked out |
||
1117 | */ |
||
1118 | public static function getGitCurrentBranch( $dir ) { |
||
1119 | $repo = new GitInfo( $dir ); |
||
1120 | return $repo->getCurrentBranch(); |
||
1121 | } |
||
1122 | |||
1123 | /** |
||
1124 | * Get the list of entry points and their URLs |
||
1125 | * @return string Wikitext |
||
1126 | */ |
||
1127 | public function getEntryPointInfo() { |
||
1128 | global $wgArticlePath, $wgScriptPath; |
||
1129 | $scriptPath = $wgScriptPath ? $wgScriptPath : "/"; |
||
1130 | $entryPoints = [ |
||
1131 | 'version-entrypoints-articlepath' => $wgArticlePath, |
||
1132 | 'version-entrypoints-scriptpath' => $scriptPath, |
||
1133 | 'version-entrypoints-index-php' => wfScript( 'index' ), |
||
1134 | 'version-entrypoints-api-php' => wfScript( 'api' ), |
||
1135 | 'version-entrypoints-load-php' => wfScript( 'load' ), |
||
1136 | ]; |
||
1137 | |||
1138 | $language = $this->getLanguage(); |
||
1139 | $thAttribures = [ |
||
1140 | 'dir' => $language->getDir(), |
||
1141 | 'lang' => $language->getHtmlCode() |
||
1142 | ]; |
||
1143 | $out = Html::element( |
||
1144 | 'h2', |
||
1145 | [ 'id' => 'mw-version-entrypoints' ], |
||
1146 | $this->msg( 'version-entrypoints' )->text() |
||
1147 | ) . |
||
1148 | Html::openElement( 'table', |
||
1149 | [ |
||
1150 | 'class' => 'wikitable plainlinks', |
||
1151 | 'id' => 'mw-version-entrypoints-table', |
||
1152 | 'dir' => 'ltr', |
||
1153 | 'lang' => 'en' |
||
1154 | ] |
||
1155 | ) . |
||
1156 | Html::openElement( 'tr' ) . |
||
1157 | Html::element( |
||
1158 | 'th', |
||
1159 | $thAttribures, |
||
1160 | $this->msg( 'version-entrypoints-header-entrypoint' )->text() |
||
1161 | ) . |
||
1162 | Html::element( |
||
1163 | 'th', |
||
1164 | $thAttribures, |
||
1165 | $this->msg( 'version-entrypoints-header-url' )->text() |
||
1166 | ) . |
||
1167 | Html::closeElement( 'tr' ); |
||
1168 | |||
1169 | foreach ( $entryPoints as $message => $value ) { |
||
1170 | $url = wfExpandUrl( $value, PROTO_RELATIVE ); |
||
1171 | $out .= Html::openElement( 'tr' ) . |
||
1172 | // ->text() looks like it should be ->parse(), but this function |
||
1173 | // returns wikitext, not HTML, boo |
||
1174 | Html::rawElement( 'td', [], $this->msg( $message )->text() ) . |
||
1175 | Html::rawElement( 'td', [], Html::rawElement( 'code', [], "[$url $value]" ) ) . |
||
1176 | Html::closeElement( 'tr' ); |
||
1177 | } |
||
1178 | |||
1179 | $out .= Html::closeElement( 'table' ); |
||
1180 | |||
1181 | return $out; |
||
1182 | } |
||
1183 | |||
1184 | protected function getGroupName() { |
||
1185 | return 'wiki'; |
||
1186 | } |
||
1187 | } |
||
1188 |
In PHP, under loose comparison (like
==
, or!=
, orswitch
conditions), values of different types might be equal.For
string
values, the empty string''
is a special case, in particular the following results might be unexpected: