4 * Give information about the version of MediaWiki, PHP, the DB and extensions
8 * @author Ævar Arnfjörð Bjarmason <avarab@gmail.com>
9 * @copyright Copyright © 2005, Ævar Arnfjörð Bjarmason
10 * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
12 class SpecialVersion
extends SpecialPage
{
13 private $firstExtOpened = true;
15 function __construct(){
16 parent
::__construct( 'Version' );
22 function execute( $par ) {
23 global $wgOut, $wgMessageCache, $wgSpecialVersionShowHooks, $wgContLang;
24 $wgMessageCache->loadAllMessages();
27 $this->outputHeader();
29 if( $wgContLang->isRTL() ) {
30 $wgOut->addHTML( '<div dir="rtl">' );
32 $wgOut->addHTML( '<div dir="ltr">' );
35 $this->MediaWikiCredits() .
36 $this->softwareInformation() .
37 $this->extensionCredits();
38 if ( $wgSpecialVersionShowHooks ) {
39 $text .= $this->wgHooks();
41 $wgOut->addWikiText( $text );
42 $wgOut->addHTML( $this->IPInfo() );
43 $wgOut->addHTML( '</div>' );
51 * @return wiki text showing the license information
53 static function MediaWikiCredits() {
56 $ret = Xml
::element( 'h2', array( 'id' => 'mw-version-license' ), wfMsg( 'version-license' ) );
58 // This text is always left-to-right.
59 $ret .= '<div dir="ltr">';
61 This wiki is powered by '''[http://www.mediawiki.org/ MediaWiki]''',
62 copyright © 2001-2009 Magnus Manske, Brion Vibber, Lee Daniel Crocker,
63 Tim Starling, Erik Möller, Gabriel Wicke, Ævar Arnfjörð Bjarmason,
64 Niklas Laxström, Domas Mituzas, Rob Church, Yuri Astrakhan, Aryeh Gregor,
65 Aaron Schulz and others.
67 MediaWiki is free software; you can redistribute it and/or modify
68 it under the terms of the GNU General Public License as published by
69 the Free Software Foundation; either version 2 of the License, or
70 (at your option) any later version.
72 MediaWiki is distributed in the hope that it will be useful,
73 but WITHOUT ANY WARRANTY; without even the implied warranty of
74 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
75 GNU General Public License for more details.
77 You should have received [{{SERVER}}{{SCRIPTPATH}}/COPYING a copy of the GNU General Public License]
78 along with this program; if not, write to the Free Software
79 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
80 or [http://www.gnu.org/licenses/old-licenses/gpl-2.0.html read it online].
84 return str_replace( "\t\t", '', $ret ) . "\n";
88 * @return wiki text showing the third party software versions (apache, php, mysql).
90 static function softwareInformation() {
91 $dbr = wfGetDB( DB_SLAVE
);
93 // Put the software in an array of form 'name' => 'version'. All messages should
94 // be loaded here, so feel free to use wfMsg*() in the 'name'. Raw HTML or wikimarkup
97 $software['[http://www.mediawiki.org/ MediaWiki]'] = self
::getVersionLinked();
98 $software['[http://www.php.net/ PHP]'] = phpversion() . " (" . php_sapi_name() . ")";
99 $software[$dbr->getSoftwareLink()] = $dbr->getServerVersion();
101 // Allow a hook to add/remove items
102 wfRunHooks( 'SoftwareInfo', array( &$software ) );
104 $out = Xml
::element( 'h2', array( 'id' => 'mw-version-software' ), wfMsg( 'version-software' ) ) .
105 Xml
::openElement( 'table', array( 'class' => 'wikitable', 'id' => 'sv-software' ) ) .
107 <th>" . wfMsg( 'version-software-product' ) . "</th>
108 <th>" . wfMsg( 'version-software-version' ) . "</th>
110 foreach( $software as $name => $version ) {
112 <td>" . $name . "</td>
113 <td>" . $version . "</td>
116 return $out . Xml
::closeElement( 'table' );
120 * Return a string of the MediaWiki version with SVN revision if available
124 public static function getVersion( $flags = '' ) {
125 global $wgVersion, $IP;
126 wfProfileIn( __METHOD__
);
127 $svn = self
::getSvnRevision( $IP, false, false , false );
128 $svnCo = self
::getSvnRevision( $IP, true, false , false );
130 $version = $wgVersion;
131 } elseif( $flags === 'nodb' ) {
132 $version = "$wgVersion (r$svnCo)";
134 $version = $wgVersion . wfMsg( 'version-svn-revision', $svn, $svnCo );
136 wfProfileOut( __METHOD__
);
141 * Return a string of the MediaWiki version with a link to SVN revision if
146 public static function getVersionLinked() {
147 global $wgVersion, $IP;
148 wfProfileIn( __METHOD__
);
149 $svn = self
::getSvnRevision( $IP, false, false, false );
150 $svnCo = self
::getSvnRevision( $IP, true, false, false );
151 $svnDir = self
::getSvnRevision( $IP, true, false, true );
152 $viewvcStart = 'http://svn.wikimedia.org/viewvc/mediawiki/';
153 $viewvcEnd = '/?pathrev=';
154 $viewvc = $viewvcStart . $svnDir . $viewvcEnd;
155 $version = $svn ?
$wgVersion . " [{$viewvc}{$svnCo} " . wfMsg( 'version-svn-revision', $svn, $svnCo ) . ']' : $wgVersion;
156 wfProfileOut( __METHOD__
);
160 /** Generate wikitext showing extensions name, URL, author and description */
161 function extensionCredits() {
162 global $wgExtensionCredits, $wgExtensionFunctions, $wgParser, $wgSkinExtensionFunctions;
164 if ( ! count( $wgExtensionCredits ) && ! count( $wgExtensionFunctions ) && ! count( $wgSkinExtensionFunctions ) )
167 $extensionTypes = array(
168 'specialpage' => wfMsg( 'version-specialpages' ),
169 'parserhook' => wfMsg( 'version-parserhooks' ),
170 'variable' => wfMsg( 'version-variables' ),
171 'media' => wfMsg( 'version-mediahandlers' ),
172 'other' => wfMsg( 'version-other' ),
174 wfRunHooks( 'SpecialVersionExtensionTypes', array( &$this, &$extensionTypes ) );
176 $out = Xml
::element( 'h2', array( 'id' => 'mw-version-ext' ), wfMsg( 'version-extensions' ) ) .
177 Xml
::openElement( 'table', array( 'class' => 'wikitable', 'id' => 'sv-ext' ) );
179 foreach ( $extensionTypes as $type => $text ) {
180 if ( isset ( $wgExtensionCredits[$type] ) && count ( $wgExtensionCredits[$type] ) ) {
181 $out .= $this->openExtType( $text );
183 usort( $wgExtensionCredits[$type], array( $this, 'compare' ) );
185 foreach ( $wgExtensionCredits[$type] as $extension ) {
188 $subVersionCo = null;
190 if ( isset( $extension['path'] ) ) {
191 $subVersion = self
::getSvnRevision(dirname($extension['path']), false, true, false);
192 $subVersionCo = self
::getSvnRevision(dirname($extension['path']), true, true, false);
193 $subVersionDir = self
::getSvnRevision(dirname($extension['path']), false, true, true);
195 $viewvc = $subVersionDir . $subVersionCo;
197 if ( isset( $extension['version'] ) ) {
198 $version = $extension['version'];
201 $out .= $this->formatCredits(
202 isset ( $extension['name'] ) ?
$extension['name'] : '',
207 isset ( $extension['author'] ) ?
$extension['author'] : '',
208 isset ( $extension['url'] ) ?
$extension['url'] : null,
209 isset ( $extension['description'] ) ?
$extension['description'] : '',
210 isset ( $extension['descriptionmsg'] ) ?
$extension['descriptionmsg'] : null
216 if ( count( $wgExtensionFunctions ) ) {
217 $out .= $this->openExtType( wfMsg( 'version-extension-functions' ) );
218 $out .= '<tr><td colspan="4">' . $this->listToText( $wgExtensionFunctions ) . "</td></tr>\n";
221 if ( $cnt = count( $tags = $wgParser->getTags() ) ) {
222 for ( $i = 0; $i < $cnt; ++
$i )
223 $tags[$i] = "<{$tags[$i]}>";
224 $out .= $this->openExtType( wfMsg( 'version-parser-extensiontags' ) );
225 $out .= '<tr><td colspan="4">' . $this->listToText( $tags ). "</td></tr>\n";
228 if( $cnt = count( $fhooks = $wgParser->getFunctionHooks() ) ) {
229 $out .= $this->openExtType( wfMsg( 'version-parser-function-hooks' ) );
230 $out .= '<tr><td colspan="4">' . $this->listToText( $fhooks ) . "</td></tr>\n";
233 if ( count( $wgSkinExtensionFunctions ) ) {
234 $out .= $this->openExtType( wfMsg( 'version-skin-extension-functions' ) );
235 $out .= '<tr><td colspan="4">' . $this->listToText( $wgSkinExtensionFunctions ) . "</td></tr>\n";
237 $out .= Xml
::closeElement( 'table' );
241 /** Callback to sort extensions by type */
242 function compare( $a, $b ) {
244 if( $a['name'] === $b['name'] ) {
247 return $wgLang->lc( $a['name'] ) > $wgLang->lc( $b['name'] )
253 function formatCredits( $name, $version = null, $subVersion = null, $subVersionCo = null, $subVersionURL = null, $author = null, $url = null, $description = null, $descriptionMsg = null ) {
254 $haveSubversion = $subVersion;
255 $extension = isset( $url ) ?
"[$url $name]" : $name;
256 $version = isset( $version ) ?
wfMsg( 'version-version', $version ) : '';
257 $subVersion = isset( $subVersion ) ?
wfMsg( 'version-svn-revision', $subVersion, $subVersionCo ) : '';
258 $subVersion = isset( $subVersionURL ) ?
"[$subVersionURL $subVersion]" : $subVersion;
260 # Look for a localized description
261 if( isset( $descriptionMsg ) ) {
262 if( is_array( $descriptionMsg ) ) {
263 $descriptionMsgKey = $descriptionMsg[0]; // Get the message key
264 array_shift( $descriptionMsg ); // Shift out the message key to get the parameters only
265 array_map( "htmlspecialchars", $descriptionMsg ); // For sanity
266 $msg = wfMsg( $descriptionMsgKey, $descriptionMsg );
268 $msg = wfMsg( $descriptionMsg );
270 if ( !wfEmptyMsg( $descriptionMsg, $msg ) && $msg != '' ) {
275 if ( $haveSubversion ) {
277 <td><em>$extension $version</em></td>
278 <td><em>$subVersion</em></td>";
281 <td colspan=\"2\"><em>$extension $version</em></td>";
283 $extDescAuthor = "<td>$description</td>
284 <td>" . $this->listToText( (array)$author ) . "</td>
286 return $ret = $extNameVer . $extDescAuthor;
296 if ( count( $wgHooks ) ) {
297 $myWgHooks = $wgHooks;
300 $ret = Xml
::element( 'h2', array( 'id' => 'mw-version-hooks' ), wfMsg( 'version-hooks' ) ) .
301 Xml
::openElement( 'table', array( 'class' => 'wikitable', 'id' => 'sv-hooks' ) ) .
303 <th>" . wfMsg( 'version-hook-name' ) . "</th>
304 <th>" . wfMsg( 'version-hook-subscribedby' ) . "</th>
307 foreach ( $myWgHooks as $hook => $hooks )
310 <td>" . $this->listToText( $hooks ) . "</td>
313 $ret .= Xml
::closeElement( 'table' );
319 private function openExtType($text, $name = null) {
320 $opt = array( 'colspan' => 4 );
323 if(!$this->firstExtOpened
) {
324 // Insert a spacing line
325 $out .= '<tr class="sv-space">' . Xml
::element( 'td', $opt ) . "</tr>\n";
327 $this->firstExtOpened
= false;
329 if($name) { $opt['id'] = "sv-$name"; }
331 $out .= "<tr>" . Xml
::element( 'th', $opt, $text) . "</tr>\n";
339 $ip = str_replace( '--', ' - ', htmlspecialchars( wfGetIP() ) );
340 return "<!-- visited from $ip -->\n" .
341 "<span style='display:none'>visited from $ip</span>";
348 function listToText( $list ) {
349 $cnt = count( $list );
352 // Enforce always returning a string
353 return (string)self
::arrayToString( $list[0] );
354 } elseif ( $cnt == 0 ) {
359 return $wgLang->listToText( array_map( array( __CLASS__
, 'arrayToString' ), $list ) );
364 * @param mixed $list Will convert an array to string if given and return
365 * the paramater unaltered otherwise
368 static function arrayToString( $list ) {
369 if( is_array( $list ) && count( $list ) == 1 )
371 if( is_object( $list ) ) {
372 $class = get_class( $list );
374 } elseif ( !is_array( $list ) ) {
377 if( is_object( $list[0] ) )
378 $class = get_class( $list[0] );
381 return "($class, {$list[1]})";
386 * Retrieve the revision number of a Subversion working directory.
388 * @param String $dir Directory of the svn checkout
389 * @param Boolean $coRev optional to return value whether is Last Modified
390 * or Checkout revision number
391 * @param Boolean $extension optional to check the path whether is from
392 * Wikimedia SVN server or not
393 * @param Boolean $relPath optional to get the end part of the checkout path
394 * @return mixed revision number as int, end part of the checkout path,
395 * or false if not a SVN checkout
397 public static function getSvnRevision( $dir, $coRev = false, $extension = false, $relPath = false) {
398 // http://svnbook.red-bean.com/nightly/en/svn.developer.insidewc.html
399 $entries = $dir . '/.svn/entries';
401 if( !file_exists( $entries ) ) {
405 $content = file( $entries );
407 // check if file is xml (subversion release <= 1.3) or not (subversion release = 1.4)
408 if( preg_match( '/^<\?xml/', $content[0] ) ) {
409 // subversion is release <= 1.3
410 if( !function_exists( 'simplexml_load_file' ) ) {
411 // We could fall back to expat... YUCK
415 // SimpleXml whines about the xmlns...
416 wfSuppressWarnings();
417 $xml = simplexml_load_file( $entries );
421 foreach( $xml->entry
as $entry ) {
422 if( $xml->entry
[0]['name'] == '' ) {
423 // The directory entry should always have a revision marker.
424 if( $entry['revision'] ) {
425 return intval( $entry['revision'] );
432 // subversion is release 1.4 or above
434 $endPath = strstr( $content[4], 'tags' );
436 $endPath = strstr( $content[4], 'branches' );
438 $endPath = strstr( $content[4], 'trunk' );
443 $endPath = trim ( $endPath );
445 $wmSvnPath = 'svn.wikimedia.org/svnroot/mediawiki';
446 $isWMSvn = strstr($content[5],$wmSvnPath);
447 if (!strcmp($isWMSvn,null)) {
450 $viewvcStart = 'http://svn.wikimedia.org/viewvc/mediawiki/';
451 if (strstr( $content[4], 'trunk' ))
452 $viewvcEnd = '/?pathrev=';
454 // Avoids 404 error using pathrev when it does not found
455 $viewvcEnd = '/?revision=';
456 $viewvc = $viewvcStart . $endPath . $viewvcEnd;
463 // get the directory checkout revsion number
464 return intval( $content[3]) ;
466 // get the directory last modified revision number
467 return intval( $content[10] );