wikimedia /
mediawiki
This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include, or for example
via PHP's auto-loading mechanism.
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
Loading history...
|
|||
| 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 ) { |
||
|
0 ignored issues
–
show
The expression
$gitVersion 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
Loading history...
|
|||
| 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 = [ |
||
|
0 ignored issues
–
show
It seems like
array('specialpage' => w...ersion-other')->text()) of type array<string,string,{"sp...ing","other":"string"}> is incompatible with the declared type boolean of property $extensionTypes.
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property. Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property.. Loading history...
|
|||
| 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 ) { |
||
|
0 ignored issues
–
show
The expression
$extensionTypes of type array<string,string,{"sp...her":"string"}>|boolean is not guaranteed to be traversable. How about adding an additional type check?
There are different options of fixing this problem.
Loading history...
|
|||
| 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; |
||
|
0 ignored issues
–
show
It seems like
$coreHeadSHA1 can also be of type boolean. However, the property $coreId is declared as type string. Maybe add an additional type check?
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly. For example, imagine you have a variable Either this assignment is in error or a type check should be added for that assignment. class Id
{
public $id;
public function __construct($id)
{
$this->id = $id;
}
}
class Account
{
/** @var Id $id */
public $id;
}
$account_id = false;
if (starsAreRight()) {
$account_id = new Id(42);
}
$account = new Account();
if ($account instanceof Id)
{
$account->id = $account_id;
}
Loading history...
|
|||
| 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 ) ) { |
||
|
0 ignored issues
–
show
The expression
$this->getExtLicenseFileName($extensionPath) 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
Loading history...
|
|||
| 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 Loading history...
|
|||
| 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!=, orswitchconditions), values of different types might be equal.For
stringvalues, the empty string''is a special case, in particular the following results might be unexpected: