00001 <?php
00002
00012 class SpecialVersion extends SpecialPage {
00013 private $firstExtOpened = true;
00014
00015 static $viewvcUrls = array(
00016 'svn+ssh://svn.wikimedia.org/svnroot/mediawiki' => 'http://svn.wikimedia.org/viewvc/mediawiki',
00017 'http://svn.wikimedia.org/svnroot/mediawiki' => 'http://svn.wikimedia.org/viewvc/mediawiki',
00018 # Doesn't work at the time of writing but maybe some day:
00019 'https:
00020 );
00021
00022 function __construct(){
00023 parent::__construct( 'Version' );
00024 }
00025
00029 function execute( $par ) {
00030 global $wgOut, $wgMessageCache, $wgSpecialVersionShowHooks, $wgContLang;
00031 $wgMessageCache->loadAllMessages();
00032
00033 $this->setHeaders();
00034 $this->outputHeader();
00035 $wgOut->allowClickjacking();
00036
00037 $wgOut->addHTML( Xml::openElement( 'div',
00038 array( 'dir' => $wgContLang->getDir() ) ) );
00039 $text =
00040 $this->MediaWikiCredits() .
00041 $this->softwareInformation() .
00042 $this->extensionCredits();
00043 if ( $wgSpecialVersionShowHooks ) {
00044 $text .= $this->wgHooks();
00045 }
00046 $wgOut->addWikiText( $text );
00047 $wgOut->addHTML( $this->IPInfo() );
00048 $wgOut->addHTML( '</div>' );
00049 }
00050
00058 static function MediaWikiCredits() {
00059 global $wgContLang;
00060
00061 $ret = Xml::element( 'h2', array( 'id' => 'mw-version-license' ), wfMsg( 'version-license' ) );
00062
00063
00064 $ret .= '<div dir="ltr">';
00065 $ret .= "__NOTOC__
00066 This wiki is powered by '''[http://www.mediawiki.org/ MediaWiki]''',
00067 copyright © 2001-2010 Magnus Manske, Brion Vibber, Lee Daniel Crocker,
00068 Tim Starling, Erik Möller, Gabriel Wicke, Ævar Arnfjörð Bjarmason,
00069 Niklas Laxström, Domas Mituzas, Rob Church, Yuri Astrakhan, Aryeh Gregor,
00070 Aaron Schulz, Andrew Garrett, Raimond Spekking, Alexandre Emsenhuber,
00071 Siebrand Mazeland, Chad Horohoe and others.
00072
00073 MediaWiki is free software; you can redistribute it and/or modify
00074 it under the terms of the GNU General Public License as published by
00075 the Free Software Foundation; either version 2 of the License, or
00076 (at your option) any later version.
00077
00078 MediaWiki is distributed in the hope that it will be useful,
00079 but WITHOUT ANY WARRANTY; without even the implied warranty of
00080 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00081 GNU General Public License for more details.
00082
00083 You should have received [{{SERVER}}{{SCRIPTPATH}}/COPYING a copy of the GNU General Public License]
00084 along with this program; if not, write to the Free Software
00085 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
00086 or [http://www.gnu.org/licenses/old-licenses/gpl-2.0.html read it online].
00087 ";
00088 $ret .= '</div>';
00089
00090 return str_replace( "\t\t", '', $ret ) . "\n";
00091 }
00092
00096 static function softwareInformation() {
00097 $dbr = wfGetDB( DB_SLAVE );
00098
00099
00100
00101
00102 $software = array();
00103 $software['[http://www.mediawiki.org/ MediaWiki]'] = self::getVersionLinked();
00104 $software['[http://www.php.net/ PHP]'] = phpversion() . " (" . php_sapi_name() . ")";
00105 $software[$dbr->getSoftwareLink()] = $dbr->getServerVersion();
00106
00107
00108 wfRunHooks( 'SoftwareInfo', array( &$software ) );
00109
00110 $out = Xml::element( 'h2', array( 'id' => 'mw-version-software' ), wfMsg( 'version-software' ) ) .
00111 Xml::openElement( 'table', array( 'class' => 'wikitable', 'id' => 'sv-software' ) ) .
00112 "<tr>
00113 <th>" . wfMsg( 'version-software-product' ) . "</th>
00114 <th>" . wfMsg( 'version-software-version' ) . "</th>
00115 </tr>\n";
00116 foreach( $software as $name => $version ) {
00117 $out .= "<tr>
00118 <td>" . $name . "</td>
00119 <td>" . $version . "</td>
00120 </tr>\n";
00121 }
00122 return $out . Xml::closeElement( 'table' );
00123 }
00124
00130 public static function getVersion( $flags = '' ) {
00131 global $wgVersion, $IP;
00132 wfProfileIn( __METHOD__ );
00133
00134 $info = self::getSvnInfo( $IP );
00135 if ( !$info ) {
00136 $version = $wgVersion;
00137 } elseif( $flags === 'nodb' ) {
00138 $version = "$wgVersion (r{$info['checkout-rev']})";
00139 } else {
00140 $version = $wgVersion . ' ' .
00141 wfMsg(
00142 'version-svn-revision',
00143 isset( $info['directory-rev'] ) ? $info['directory-rev'] : '',
00144 $info['checkout-rev']
00145 );
00146 }
00147
00148 wfProfileOut( __METHOD__ );
00149 return $version;
00150 }
00151
00158 public static function getVersionLinked() {
00159 global $wgVersion, $IP;
00160 wfProfileIn( __METHOD__ );
00161 $info = self::getSvnInfo( $IP );
00162 if ( isset( $info['checkout-rev'] ) ) {
00163 $linkText = wfMsg(
00164 'version-svn-revision',
00165 isset( $info['directory-rev'] ) ? $info['directory-rev'] : '',
00166 $info['checkout-rev']
00167 );
00168 if ( isset( $info['viewvc-url'] ) ) {
00169 $version = "$wgVersion [{$info['viewvc-url']} $linkText]";
00170 } else {
00171 $version = "$wgVersion $linkText";
00172 }
00173 } else {
00174 $version = $wgVersion;
00175 }
00176 wfProfileOut( __METHOD__ );
00177 return $version;
00178 }
00179
00181 function extensionCredits() {
00182 global $wgExtensionCredits, $wgExtensionFunctions, $wgParser, $wgSkinExtensionFunctions;
00183
00184 if ( ! count( $wgExtensionCredits ) && ! count( $wgExtensionFunctions ) && ! count( $wgSkinExtensionFunctions ) )
00185 return '';
00186
00187 $extensionTypes = array(
00188 'specialpage' => wfMsg( 'version-specialpages' ),
00189 'parserhook' => wfMsg( 'version-parserhooks' ),
00190 'variable' => wfMsg( 'version-variables' ),
00191 'media' => wfMsg( 'version-mediahandlers' ),
00192 'other' => wfMsg( 'version-other' ),
00193 );
00194 wfRunHooks( 'SpecialVersionExtensionTypes', array( &$this, &$extensionTypes ) );
00195
00196 $out = Xml::element( 'h2', array( 'id' => 'mw-version-ext' ), wfMsg( 'version-extensions' ) ) .
00197 Xml::openElement( 'table', array( 'class' => 'wikitable', 'id' => 'sv-ext' ) );
00198
00199 foreach ( $extensionTypes as $type => $text ) {
00200 if ( isset ( $wgExtensionCredits[$type] ) && count ( $wgExtensionCredits[$type] ) ) {
00201 $out .= $this->openExtType( $text, 'credits-' . $type );
00202
00203 usort( $wgExtensionCredits[$type], array( $this, 'compare' ) );
00204
00205 foreach ( $wgExtensionCredits[$type] as $extension ) {
00206 $out .= $this->formatCredits( $extension );
00207 }
00208 }
00209 }
00210
00211 if ( count( $wgExtensionFunctions ) ) {
00212 $out .= $this->openExtType( wfMsg( 'version-extension-functions' ), 'extension-functions' );
00213 $out .= '<tr><td colspan="4">' . $this->listToText( $wgExtensionFunctions ) . "</td></tr>\n";
00214 }
00215
00216 if ( $cnt = count( $tags = $wgParser->getTags() ) ) {
00217 for ( $i = 0; $i < $cnt; ++$i )
00218 $tags[$i] = "<{$tags[$i]}>";
00219 $out .= $this->openExtType( wfMsg( 'version-parser-extensiontags' ), 'parser-tags' );
00220 $out .= '<tr><td colspan="4">' . $this->listToText( $tags ). "</td></tr>\n";
00221 }
00222
00223 if( $cnt = count( $fhooks = $wgParser->getFunctionHooks() ) ) {
00224 $out .= $this->openExtType( wfMsg( 'version-parser-function-hooks' ), 'parser-function-hooks' );
00225 $out .= '<tr><td colspan="4">' . $this->listToText( $fhooks ) . "</td></tr>\n";
00226 }
00227
00228 if ( count( $wgSkinExtensionFunctions ) ) {
00229 $out .= $this->openExtType( wfMsg( 'version-skin-extension-functions' ), 'skin-extension-functions' );
00230 $out .= '<tr><td colspan="4">' . $this->listToText( $wgSkinExtensionFunctions ) . "</td></tr>\n";
00231 }
00232 $out .= Xml::closeElement( 'table' );
00233 return $out;
00234 }
00235
00237 function compare( $a, $b ) {
00238 global $wgLang;
00239 if( $a['name'] === $b['name'] ) {
00240 return 0;
00241 } else {
00242 return $wgLang->lc( $a['name'] ) > $wgLang->lc( $b['name'] )
00243 ? 1
00244 : -1;
00245 }
00246 }
00247
00248 function formatCredits( $extension ) {
00249 $name = isset( $extension['name'] ) ? $extension['name'] : '[no name]';
00250 if ( isset( $extension['path'] ) ) {
00251 $svnInfo = self::getSvnInfo( dirname($extension['path']) );
00252 $directoryRev = isset( $svnInfo['directory-rev'] ) ? $svnInfo['directory-rev'] : null;
00253 $checkoutRev = isset( $svnInfo['checkout-rev'] ) ? $svnInfo['checkout-rev'] : null;
00254 $viewvcUrl = isset( $svnInfo['viewvc-url'] ) ? $svnInfo['viewvc-url'] : null;
00255 } else {
00256 $directoryRev = null;
00257 $checkoutRev = null;
00258 $viewvcUrl = null;
00259 }
00260
00261 # Make main link (or just the name if there is no URL)
00262 if ( isset( $extension['url'] ) ) {
00263 $mainLink = "[{$extension['url']} $name]";
00264 } else {
00265 $mainLink = $name;
00266 }
00267 if ( isset( $extension['version'] ) ) {
00268 $versionText = '<span class="mw-version-ext-version">' .
00269 wfMsg( 'version-version', $extension['version'] ) .
00270 '</span>';
00271 } else {
00272 $versionText = '';
00273 }
00274
00275 # Make subversion text/link
00276 if ( $checkoutRev ) {
00277 $svnText = wfMsg( 'version-svn-revision', $directoryRev, $checkoutRev );
00278 $svnText = isset( $viewvcUrl ) ? "[$viewvcUrl $svnText]" : $svnText;
00279 } else {
00280 $svnText = false;
00281 }
00282
00283 # Make description text
00284 $description = isset ( $extension['description'] ) ? $extension['description'] : '';
00285 if( isset ( $extension['descriptionmsg'] ) ) {
00286 # Look for a localized description
00287 $descriptionMsg = $extension['descriptionmsg'];
00288 if( is_array( $descriptionMsg ) ) {
00289 $descriptionMsgKey = $descriptionMsg[0];
00290 array_shift( $descriptionMsg );
00291 array_map( "htmlspecialchars", $descriptionMsg );
00292 $msg = wfMsg( $descriptionMsgKey, $descriptionMsg );
00293 } else {
00294 $msg = wfMsg( $descriptionMsg );
00295 }
00296 if ( !wfEmptyMsg( $descriptionMsg, $msg ) && $msg != '' ) {
00297 $description = $msg;
00298 }
00299 }
00300
00301 if ( $svnText !== false ) {
00302 $extNameVer = "<tr>
00303 <td><em>$mainLink $versionText</em></td>
00304 <td><em>$svnText</em></td>";
00305 } else {
00306 $extNameVer = "<tr>
00307 <td colspan=\"2\"><em>$mainLink $versionText</em></td>";
00308 }
00309 $author = isset ( $extension['author'] ) ? $extension['author'] : array();
00310 $extDescAuthor = "<td>$description</td>
00311 <td>" . $this->listToText( (array)$author, false ) . "</td>
00312 </tr>\n";
00313 return $extNameVer . $extDescAuthor;
00314 }
00315
00319 function wgHooks() {
00320 global $wgHooks;
00321
00322 if ( count( $wgHooks ) ) {
00323 $myWgHooks = $wgHooks;
00324 ksort( $myWgHooks );
00325
00326 $ret = Xml::element( 'h2', array( 'id' => 'mw-version-hooks' ), wfMsg( 'version-hooks' ) ) .
00327 Xml::openElement( 'table', array( 'class' => 'wikitable', 'id' => 'sv-hooks' ) ) .
00328 "<tr>
00329 <th>" . wfMsg( 'version-hook-name' ) . "</th>
00330 <th>" . wfMsg( 'version-hook-subscribedby' ) . "</th>
00331 </tr>\n";
00332
00333 foreach ( $myWgHooks as $hook => $hooks )
00334 $ret .= "<tr>
00335 <td>$hook</td>
00336 <td>" . $this->listToText( $hooks ) . "</td>
00337 </tr>\n";
00338
00339 $ret .= Xml::closeElement( 'table' );
00340 return $ret;
00341 } else
00342 return '';
00343 }
00344
00345 private function openExtType( $text, $name = null ) {
00346 $opt = array( 'colspan' => 4 );
00347 $out = '';
00348
00349 if( !$this->firstExtOpened ) {
00350
00351 $out .= '<tr class="sv-space">' . Xml::element( 'td', $opt ) . "</tr>\n";
00352 }
00353 $this->firstExtOpened = false;
00354
00355 if( $name )
00356 $opt['id'] = "sv-$name";
00357
00358 $out .= "<tr>" . Xml::element( 'th', $opt, $text ) . "</tr>\n";
00359 return $out;
00360 }
00361
00365 function IPInfo() {
00366 $ip = str_replace( '--', ' - ', htmlspecialchars( wfGetIP() ) );
00367 return "<!-- visited from $ip -->\n" .
00368 "<span style='display:none'>visited from $ip</span>";
00369 }
00370
00376 function listToText( $list, $sort = true ) {
00377 $cnt = count( $list );
00378
00379 if ( $cnt == 1 ) {
00380
00381 return (string)self::arrayToString( $list[0] );
00382 } elseif ( $cnt == 0 ) {
00383 return '';
00384 } else {
00385 global $wgLang;
00386 if ( $sort ) {
00387 sort( $list );
00388 }
00389 return $wgLang->listToText( array_map( array( __CLASS__, 'arrayToString' ), $list ) );
00390 }
00391 }
00392
00398 static function arrayToString( $list ) {
00399 if( is_array( $list ) && count( $list ) == 1 )
00400 $list = $list[0];
00401 if( is_object( $list ) ) {
00402 $class = get_class( $list );
00403 return "($class)";
00404 } elseif ( !is_array( $list ) ) {
00405 return $list;
00406 } else {
00407 if( is_object( $list[0] ) )
00408 $class = get_class( $list[0] );
00409 else
00410 $class = $list[0];
00411 return "($class, {$list[1]})";
00412 }
00413 }
00414
00429 public static function getSvnInfo( $dir ) {
00430
00431 $entries = $dir . '/.svn/entries';
00432
00433 if( !file_exists( $entries ) ) {
00434 return false;
00435 }
00436
00437 $lines = file( $entries );
00438 if ( !count( $lines ) ) {
00439 return false;
00440 }
00441
00442
00443 if( preg_match( '/^<\?xml/', $lines[0] ) ) {
00444
00445 if( !function_exists( 'simplexml_load_file' ) ) {
00446
00447 return false;
00448 }
00449
00450
00451 wfSuppressWarnings();
00452 $xml = simplexml_load_file( $entries );
00453 wfRestoreWarnings();
00454
00455 if( $xml ) {
00456 foreach( $xml->entry as $entry ) {
00457 if( $xml->entry[0]['name'] == '' ) {
00458
00459 if( $entry['revision'] ) {
00460 return array( 'checkout-rev' => intval( $entry['revision'] ) );
00461 }
00462 }
00463 }
00464 }
00465 return false;
00466 }
00467
00468
00469 if ( count( $lines ) < 11 ) {
00470 return false;
00471 }
00472 $info = array(
00473 'checkout-rev' => intval( trim( $lines[3] ) ),
00474 'url' => trim( $lines[4] ),
00475 'repo-url' => trim( $lines[5] ),
00476 'directory-rev' => intval( trim( $lines[10] ) )
00477 );
00478 if ( isset( self::$viewvcUrls[$info['repo-url']] ) ) {
00479 $viewvc = str_replace(
00480 $info['repo-url'],
00481 self::$viewvcUrls[$info['repo-url']],
00482 $info['url']
00483 );
00484 $pathRelativeToRepo = substr( $info['url'], strlen( $info['repo-url'] ) );
00485 $viewvc .= '/?pathrev=';
00486 $viewvc .= urlencode( $info['checkout-rev'] );
00487 $info['viewvc-url'] = $viewvc;
00488 }
00489 return $info;
00490 }
00491
00498 public static function getSvnRevision( $dir ) {
00499 $info = self::getSvnInfo( $dir );
00500 if ( $info === false ) {
00501 return false;
00502 } elseif ( isset( $info['checkout-rev'] ) ) {
00503 return $info['checkout-rev'];
00504 } else {
00505 return false;
00506 }
00507 }
00508
00510 }