3 * 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
16 function wfSpecialVersion() {
17 $version = new SpecialVersion
;
22 * @ingroup SpecialPage
24 class SpecialVersion
{
25 private $firstExtOpened = true;
31 global $wgOut, $wgMessageCache;
32 $wgMessageCache->loadAllMessages();
34 $wgOut->addHTML( '<div dir="ltr">' );
36 $this->MediaWikiCredits() .
37 $this->softwareInformation() .
38 $this->extensionCredits() .
41 $wgOut->addHTML( $this->IPInfo() );
42 $wgOut->addHTML( '</div>' );
50 * @return wiki text showing the license information
52 static function MediaWikiCredits() {
53 $ret = Xml
::element( 'h2', array( 'id' => 'mw-version-license' ), wfMsg( 'version-license' ) ) .
55 This wiki is powered by '''[http://www.mediawiki.org/ MediaWiki]''',
56 copyright (C) 2001-2008 Magnus Manske, Brion Vibber, Lee Daniel Crocker,
57 Tim Starling, Erik Möller, Gabriel Wicke, Ævar Arnfjörð Bjarmason,
58 Niklas Laxström, Domas Mituzas, Rob Church, Yuri Astrakhan and others.
60 MediaWiki is free software; you can redistribute it and/or modify
61 it under the terms of the GNU General Public License as published by
62 the Free Software Foundation; either version 2 of the License, or
63 (at your option) any later version.
65 MediaWiki is distributed in the hope that it will be useful,
66 but WITHOUT ANY WARRANTY; without even the implied warranty of
67 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
68 GNU General Public License for more details.
70 You should have received [{{SERVER}}{{SCRIPTPATH}}/COPYING a copy of the GNU General Public License]
71 along with this program; if not, write to the Free Software
72 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
73 or [http://www.gnu.org/licenses/old-licenses/gpl-2.0.html read it online].
76 return str_replace( "\t\t", '', $ret ) . "\n";
80 * @return wiki text showing the third party software versions (apache, php, mysql).
82 static function softwareInformation() {
83 $dbr = wfGetDB( DB_SLAVE
);
85 return Xml
::element( 'h2', array( 'id' => 'mw-version-software' ), wfMsg( 'version-software' ) ) .
86 Xml
::openElement( 'table', array( 'id' => 'sv-software' ) ) .
88 <th>" . wfMsg( 'version-software-product' ) . "</th>
89 <th>" . wfMsg( 'version-software-version' ) . "</th>
92 <td>[http://www.mediawiki.org/ MediaWiki]</td>
93 <td>" . self
::getVersionLinked() . "</td>
96 <td>[http://www.php.net/ PHP]</td>
97 <td>" . phpversion() . " (" . php_sapi_name() . ")</td>
100 <td>" . $dbr->getSoftwareLink() . "</td>
101 <td>" . $dbr->getServerVersion() . "</td>
103 Xml
::closeElement( 'table' );
107 * Return a string of the MediaWiki version with SVN revision if available
111 public static function getVersion() {
112 global $wgVersion, $IP;
113 wfProfileIn( __METHOD__
);
114 $svn = self
::getSvnRevision( $IP );
115 $version = $svn ?
"$wgVersion (r$svn)" : $wgVersion;
116 wfProfileOut( __METHOD__
);
121 * Return a string of the MediaWiki version with a link to SVN revision if
126 public static function getVersionLinked() {
127 global $wgVersion, $IP;
128 wfProfileIn( __METHOD__
);
129 $svn = self
::getSvnRevision( $IP );
130 $viewvc = 'http://svn.wikimedia.org/viewvc/mediawiki/trunk/phase3/?pathrev=';
131 $version = $svn ?
"$wgVersion ([{$viewvc}{$svn} r$svn])" : $wgVersion;
132 wfProfileOut( __METHOD__
);
136 /** Generate wikitext showing extensions name, URL, author and description */
137 function extensionCredits() {
138 global $wgExtensionCredits, $wgExtensionFunctions, $wgParser, $wgSkinExtensionFunctions;
140 if ( ! count( $wgExtensionCredits ) && ! count( $wgExtensionFunctions ) && ! count( $wgSkinExtensionFunctions ) )
143 $extensionTypes = array(
144 'specialpage' => wfMsg( 'version-specialpages' ),
145 'parserhook' => wfMsg( 'version-parserhooks' ),
146 'variable' => wfMsg( 'version-variables' ),
147 'media' => wfMsg( 'version-mediahandlers' ),
148 'other' => wfMsg( 'version-other' ),
150 wfRunHooks( 'SpecialVersionExtensionTypes', array( &$this, &$extensionTypes ) );
152 $out = Xml
::element( 'h2', array( 'id' => 'mw-version-ext' ), wfMsg( 'version-extensions' ) ) .
153 Xml
::openElement( 'table', array( 'id' => 'sv-ext' ) );
155 foreach ( $extensionTypes as $type => $text ) {
156 if ( isset ( $wgExtensionCredits[$type] ) && count ( $wgExtensionCredits[$type] ) ) {
157 $out .= $this->openExtType( $text );
159 usort( $wgExtensionCredits[$type], array( $this, 'compare' ) );
161 foreach ( $wgExtensionCredits[$type] as $extension ) {
162 if ( isset( $extension['version'] ) ) {
163 $version = $extension['version'];
164 } elseif ( isset( $extension['svn-revision'] ) &&
165 preg_match( '/\$(?:Rev|LastChangedRevision|Revision): *(\d+)/',
166 $extension['svn-revision'], $m ) )
168 $version = 'r' . $m[1];
173 $out .= $this->formatCredits(
174 isset ( $extension['name'] ) ?
$extension['name'] : '',
176 isset ( $extension['author'] ) ?
$extension['author'] : '',
177 isset ( $extension['url'] ) ?
$extension['url'] : null,
178 isset ( $extension['description'] ) ?
$extension['description'] : '',
179 isset ( $extension['descriptionmsg'] ) ?
$extension['descriptionmsg'] : ''
185 if ( count( $wgExtensionFunctions ) ) {
186 $out .= $this->openExtType( wfMsg( 'version-extension-functions' ) );
187 $out .= '<tr><td colspan="3">' . $this->listToText( $wgExtensionFunctions ) . "</td></tr>\n";
190 if ( $cnt = count( $tags = $wgParser->getTags() ) ) {
191 for ( $i = 0; $i < $cnt; ++
$i )
192 $tags[$i] = "<{$tags[$i]}>";
193 $out .= $this->openExtType( wfMsg( 'version-parser-extensiontags' ) );
194 $out .= '<tr><td colspan="3">' . $this->listToText( $tags ). "</td></tr>\n";
197 if( $cnt = count( $fhooks = $wgParser->getFunctionHooks() ) ) {
198 $out .= $this->openExtType( wfMsg( 'version-parser-function-hooks' ) );
199 $out .= '<tr><td colspan="3">' . $this->listToText( $fhooks ) . "</td></tr>\n";
202 if ( count( $wgSkinExtensionFunctions ) ) {
203 $out .= $this->openExtType( wfMsg( 'version-skin-extension-functions' ) );
204 $out .= '<tr><td colspan="3">' . $this->listToText( $wgSkinExtensionFunctions ) . "</td></tr>\n";
206 $out .= Xml
::closeElement( 'table' );
210 /** Callback to sort extensions by type */
211 function compare( $a, $b ) {
213 if( $a['name'] === $b['name'] ) {
216 return $wgLang->lc( $a['name'] ) > $wgLang->lc( $b['name'] )
222 function formatCredits( $name, $version = null, $author = null, $url = null, $description = null, $descriptionMsg = null ) {
223 $extension = isset( $url ) ?
"[$url $name]" : $name;
224 $version = isset( $version ) ?
"(" . wfMsg( 'version-version' ) . " $version)" : '';
226 # Look for a localized description
227 if( isset( $descriptionMsg ) ) {
228 $msg = wfMsg( $descriptionMsg );
229 if ( !wfEmptyMsg( $descriptionMsg, $msg ) && $msg != '' ) {
235 <td><em>$extension $version</em></td>
236 <td>$description</td>
237 <td>" . $this->listToText( (array)$author ) . "</td>
247 if ( count( $wgHooks ) ) {
248 $myWgHooks = $wgHooks;
251 $ret = Xml
::element( 'h2', array( 'id' => 'mw-version-hooks' ), wfMsg( 'version-hooks' ) ) .
252 Xml
::openElement( 'table', array( 'id' => 'sv-hooks' ) ) .
254 <th>" . wfMsg( 'version-hook-name' ) . "</th>
255 <th>" . wfMsg( 'version-hook-subscribedby' ) . "</th>
258 foreach ( $myWgHooks as $hook => $hooks )
261 <td>" . $this->listToText( $hooks ) . "</td>
264 $ret .= Xml
::closeElement( 'table' );
270 private function openExtType($text, $name = null) {
271 $opt = array( 'colspan' => 3 );
274 if(!$this->firstExtOpened
) {
275 // Insert a spacing line
276 $out .= '<tr class="sv-space">' . Xml
::element( 'td', $opt ) . "</tr>\n";
278 $this->firstExtOpened
= false;
280 if($name) { $opt['id'] = "sv-$name"; }
282 $out .= "<tr>" . Xml
::element( 'th', $opt, $text) . "</tr>\n";
292 $ip = str_replace( '--', ' - ', htmlspecialchars( wfGetIP() ) );
293 return "<!-- visited from $ip -->\n" .
294 "<span style='display:none'>visited from $ip</span>";
301 function listToText( $list ) {
302 $cnt = count( $list );
305 // Enforce always returning a string
306 return (string)$this->arrayToString( $list[0] );
307 } elseif ( $cnt == 0 ) {
311 $t = array_slice( $list, 0, $cnt - 1 );
312 $one = array_map( array( &$this, 'arrayToString' ), $t );
313 $two = $this->arrayToString( $list[$cnt - 1] );
314 $and = wfMsg( 'and' );
316 return implode( ', ', $one ) . " $and $two";
323 * @param mixed $list Will convert an array to string if given and return
324 * the paramater unaltered otherwise
327 function arrayToString( $list ) {
328 if( is_object( $list ) ) {
329 $class = get_class( $list );
331 } elseif ( ! is_array( $list ) ) {
334 $class = get_class( $list[0] );
335 return "($class, {$list[1]})";
340 * Retrieve the revision number of a Subversion working directory.
343 * @return mixed revision number as int, or false if not a SVN checkout
345 public static function getSvnRevision( $dir ) {
346 // http://svnbook.red-bean.com/nightly/en/svn.developer.insidewc.html
347 $entries = $dir . '/.svn/entries';
349 if( !file_exists( $entries ) ) {
353 $content = file( $entries );
355 // check if file is xml (subversion release <= 1.3) or not (subversion release = 1.4)
356 if( preg_match( '/^<\?xml/', $content[0] ) ) {
357 // subversion is release <= 1.3
358 if( !function_exists( 'simplexml_load_file' ) ) {
359 // We could fall back to expat... YUCK
363 // SimpleXml whines about the xmlns...
364 wfSuppressWarnings();
365 $xml = simplexml_load_file( $entries );
369 foreach( $xml->entry
as $entry ) {
370 if( $xml->entry
[0]['name'] == '' ) {
371 // The directory entry should always have a revision marker.
372 if( $entry['revision'] ) {
373 return intval( $entry['revision'] );
380 // subversion is release 1.4
381 return intval( $content[3] );