3 * Give information about the version of MediaWiki, PHP, the DB and extensions
5 * @addtogroup SpecialPage
7 * @author Ævar Arnfjörð Bjarmason <avarab@gmail.com>
8 * @copyright Copyright © 2005, Ævar Arnfjörð Bjarmason
9 * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
15 function wfSpecialVersion() {
16 $version = new SpecialVersion
;
20 class SpecialVersion
{
21 private $firstExtOpened = true;
29 $wgOut->addHTML( '<div dir="ltr">' );
31 $this->MediaWikiCredits() .
32 $this->extensionCredits() .
35 $wgOut->addHTML( $this->IPInfo() );
36 $wgOut->addHTML( '</div>' );
44 * Return wiki text showing the licence information and third party
45 * software versions (apache, php, mysql).
48 function MediaWikiCredits() {
49 $version = self
::getVersion();
50 $dbr = wfGetDB( DB_SLAVE
);
54 This wiki is powered by '''[http://www.mediawiki.org/ MediaWiki]''',
55 copyright (C) 2001-2008 Magnus Manske, Brion Vibber, Lee Daniel Crocker,
56 Tim Starling, Erik Möller, Gabriel Wicke, Ævar Arnfjörð Bjarmason,
57 Niklas Laxström, Domas Mituzas, Rob Church and others.
59 MediaWiki is free software; you can redistribute it and/or modify
60 it under the terms of the GNU General Public License as published by
61 the Free Software Foundation; either version 2 of the License, or
62 (at your option) any later version.
64 MediaWiki is distributed in the hope that it will be useful,
65 but WITHOUT ANY WARRANTY; without even the implied warranty of
66 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
67 GNU General Public License for more details.
69 You should have received [{{SERVER}}{{SCRIPTPATH}}/COPYING a copy of the GNU General Public License]
70 along with this program; if not, write to the Free Software
71 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
72 or [http://www.gnu.org/copyleft/gpl.html read it online]
74 * [http://www.mediawiki.org/ MediaWiki]: $version
75 * [http://www.php.net/ PHP]: " . phpversion() . " (" . php_sapi_name() . ")
76 * " . $dbr->getSoftwareLink() . ": " . $dbr->getServerVersion();
78 return str_replace( "\t\t", '', $ret ) . "\n";
81 /** Return a string of the MediaWiki version with SVN revision if available */
82 public static function getVersion() {
83 global $wgVersion, $IP;
84 wfProfileIn( __METHOD__
);
85 $svn = self
::getSvnRevision( $IP );
86 $version = $svn ?
"$wgVersion (r$svn)" : $wgVersion;
87 wfProfileOut( __METHOD__
);
91 /** Generate wikitext showing extensions name, URL, author and description */
92 function extensionCredits() {
93 global $wgExtensionCredits, $wgExtensionFunctions, $wgParser, $wgSkinExtensionFunction;
95 if ( ! count( $wgExtensionCredits ) && ! count( $wgExtensionFunctions ) && ! count( $wgSkinExtensionFunction ) )
98 $extensionTypes = array(
99 'specialpage' => 'Special pages',
100 'parserhook' => 'Parser hooks',
101 'variable' => 'Variables',
102 'media' => 'Media handlers',
105 wfRunHooks( 'SpecialVersionExtensionTypes', array( &$this, &$extensionTypes ) );
107 $out = "<h2>Extensions</h2>\n";
108 $out .= Xml
::openElement('table', array('id' => 'sv-ext') );
110 foreach ( $extensionTypes as $type => $text ) {
111 if ( isset ( $wgExtensionCredits[$type] ) && count ( $wgExtensionCredits[$type] ) ) {
112 $out .= $this->openExtType( $text );
114 usort( $wgExtensionCredits[$type], array( $this, 'compare' ) );
116 foreach ( $wgExtensionCredits[$type] as $extension ) {
117 $out .= $this->formatCredits(
118 isset ( $extension['name'] ) ?
$extension['name'] : '',
119 isset ( $extension['version'] ) ?
$extension['version'] : null,
120 isset ( $extension['author'] ) ?
$extension['author'] : '',
121 isset ( $extension['url'] ) ?
$extension['url'] : null,
122 isset ( $extension['description'] ) ?
$extension['description'] : ''
128 if ( count( $wgExtensionFunctions ) ) {
129 $out .= $this->openExtType('Extension functions');
130 $out .= '<tr><td colspan="3">' . $this->listToText( $wgExtensionFunctions ) . "</td></tr>\n";
133 if ( $cnt = count( $tags = $wgParser->getTags() ) ) {
134 for ( $i = 0; $i < $cnt; ++
$i )
135 $tags[$i] = "<{$tags[$i]}>";
136 $out .= $this->openExtType('Parser extension tags');
137 $out .= '<tr><td colspan="3">' . $this->listToText( $tags ). "</td></tr>\n";
140 if( $cnt = count( $fhooks = $wgParser->getFunctionHooks() ) ) {
141 $out .= $this->openExtType('Parser function hooks');
142 $out .= '<tr><td colspan="3">' . $this->listToText( $fhooks ) . "</td></tr>\n";
145 if ( count( $wgSkinExtensionFunction ) ) {
146 $out .= $this->openExtType('Skin extension functions');
147 $out .= '<tr><td colspan="3">' . $this->listToText( $wgSkinExtensionFunction ) . "</td></tr>\n";
149 $out .= Xml
::closeElement( 'table' );
153 /** Callback to sort extensions by type */
154 function compare( $a, $b ) {
156 if( $a['name'] === $b['name'] ) {
159 return $wgLang->lc( $a['name'] ) > $wgLang->lc( $b['name'] )
165 function formatCredits( $name, $version = null, $author = null, $url = null, $description = null) {
170 if ( isset( $version ) )
171 $ret .= " (version $version)";
176 $ret .= "<td>$description</td>";
177 $ret .= "<td>" . $this->listToText( (array)$author ) . "</td>";
188 if ( count( $wgHooks ) ) {
189 $myWgHooks = $wgHooks;
192 $ret = "<h2>Hooks</h2>\n"
193 . Xml
::openElement('table', array('id' => 'sv-hooks') )
194 . "<tr><th>Hook name</th><th>Subscribed by</th></tr>\n";
196 foreach ($myWgHooks as $hook => $hooks)
197 $ret .= "<tr><td>$hook</td><td>" . $this->listToText( $hooks ) . "</td></tr>\n";
199 $ret .= Xml
::closeElement( 'table' );
205 private function openExtType($text, $name = null) {
206 $opt = array( 'colspan' => 3 );
209 if(!$this->firstExtOpened
) {
210 // Insert a spacing line
211 $out .= '<tr class="sv-space">' . Xml
::element( 'td', $opt ) . "</tr>\n";
213 $this->firstExtOpened
= false;
215 if($name) { $opt['id'] = "sv-$name"; }
217 $out .= "<tr>" . Xml
::element( 'th', $opt, $text) . "</tr>\n";
227 $ip = str_replace( '--', ' - ', htmlspecialchars( wfGetIP() ) );
228 return "<!-- visited from $ip -->\n" .
229 "<span style='display:none'>visited from $ip</span>";
236 function listToText( $list ) {
237 $cnt = count( $list );
241 // Enforce always returning a string
242 return (string)$this->arrayToString( $list[0] );
243 } elseif ( $cnt == 0 ) {
246 $t = array_slice( $list, 0, $cnt - 1 );
247 $one = array_map( array( &$this, 'arrayToString' ), $t );
248 $two = $this->arrayToString( $list[$cnt - 1] );
250 return implode( ', ', $one ) . " and $two";
257 * @param mixed $list Will convert an array to string if given and return
258 * the paramater unaltered otherwise
261 function arrayToString( $list ) {
262 if( is_object( $list ) ) {
263 $class = get_class( $list );
265 } elseif ( ! is_array( $list ) ) {
268 $class = get_class( $list[0] );
269 return "($class, {$list[1]})";
274 * Retrieve the revision number of a Subversion working directory.
277 * @return mixed revision number as int, or false if not a SVN checkout
279 public static function getSvnRevision( $dir ) {
280 // http://svnbook.red-bean.com/nightly/en/svn.developer.insidewc.html
281 $entries = $dir . '/.svn/entries';
283 if( !file_exists( $entries ) ) {
287 $content = file( $entries );
289 // check if file is xml (subversion release <= 1.3) or not (subversion release = 1.4)
290 if( preg_match( '/^<\?xml/', $content[0] ) ) {
291 // subversion is release <= 1.3
292 if( !function_exists( 'simplexml_load_file' ) ) {
293 // We could fall back to expat... YUCK
297 // SimpleXml whines about the xmlns...
298 wfSuppressWarnings();
299 $xml = simplexml_load_file( $entries );
303 foreach( $xml->entry
as $entry ) {
304 if( $xml->entry
[0]['name'] == '' ) {
305 // The directory entry should always have a revision marker.
306 if( $entry['revision'] ) {
307 return intval( $entry['revision'] );
314 // subversion is release 1.4
315 return intval( $content[3] );