Increment years
[lhc/web/wiklou.git] / includes / specials / SpecialVersion.php
1 <?php
2
3 /**
4 * Give information about the version of MediaWiki, PHP, the DB and extensions
5 *
6 * @ingroup SpecialPage
7 *
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
11 */
12 class SpecialVersion extends SpecialPage {
13 private $firstExtOpened = true;
14
15 function __construct(){
16 parent::__construct( 'Version' );
17 }
18
19 /**
20 * main()
21 */
22 function execute( $par ) {
23 global $wgOut, $wgMessageCache, $wgSpecialVersionShowHooks;
24 $wgMessageCache->loadAllMessages();
25
26 $this->setHeaders();
27 $this->outputHeader();
28
29 $wgOut->addHTML( '<div dir="ltr">' );
30 $text =
31 $this->MediaWikiCredits() .
32 $this->softwareInformation() .
33 $this->extensionCredits();
34 if ( $wgSpecialVersionShowHooks ) {
35 $text .= $this->wgHooks();
36 }
37 $wgOut->addWikiText( $text );
38 $wgOut->addHTML( $this->IPInfo() );
39 $wgOut->addHTML( '</div>' );
40 }
41
42 /**#@+
43 * @private
44 */
45
46 /**
47 * @return wiki text showing the license information
48 */
49 static function MediaWikiCredits() {
50 $ret = Xml::element( 'h2', array( 'id' => 'mw-version-license' ), wfMsg( 'version-license' ) ) .
51 "__NOTOC__
52 This wiki is powered by '''[http://www.mediawiki.org/ MediaWiki]''',
53 copyright (C) 2001-2009 Magnus Manske, Brion Vibber, Lee Daniel Crocker,
54 Tim Starling, Erik Möller, Gabriel Wicke, Ævar Arnfjörð Bjarmason,
55 Niklas Laxström, Domas Mituzas, Rob Church, Yuri Astrakhan, Aryeh Gregor,
56 Aaron Schulz and others.
57
58 MediaWiki is free software; you can redistribute it and/or modify
59 it under the terms of the GNU General Public License as published by
60 the Free Software Foundation; either version 2 of the License, or
61 (at your option) any later version.
62
63 MediaWiki is distributed in the hope that it will be useful,
64 but WITHOUT ANY WARRANTY; without even the implied warranty of
65 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
66 GNU General Public License for more details.
67
68 You should have received [{{SERVER}}{{SCRIPTPATH}}/COPYING a copy of the GNU General Public License]
69 along with this program; if not, write to the Free Software
70 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
71 or [http://www.gnu.org/licenses/old-licenses/gpl-2.0.html read it online].
72 ";
73
74 return str_replace( "\t\t", '', $ret ) . "\n";
75 }
76
77 /**
78 * @return wiki text showing the third party software versions (apache, php, mysql).
79 */
80 static function softwareInformation() {
81 $dbr = wfGetDB( DB_SLAVE );
82
83 return Xml::element( 'h2', array( 'id' => 'mw-version-software' ), wfMsg( 'version-software' ) ) .
84 Xml::openElement( 'table', array( 'id' => 'sv-software' ) ) .
85 "<tr>
86 <th>" . wfMsg( 'version-software-product' ) . "</th>
87 <th>" . wfMsg( 'version-software-version' ) . "</th>
88 </tr>\n
89 <tr>
90 <td>[http://www.mediawiki.org/ MediaWiki]</td>
91 <td>" . self::getVersionLinked() . "</td>
92 </tr>\n
93 <tr>
94 <td>[http://www.php.net/ PHP]</td>
95 <td>" . phpversion() . " (" . php_sapi_name() . ")</td>
96 </tr>\n
97 <tr>
98 <td>" . $dbr->getSoftwareLink() . "</td>
99 <td>" . $dbr->getServerVersion() . "</td>
100 </tr>\n" .
101 Xml::closeElement( 'table' );
102 }
103
104 /**
105 * Return a string of the MediaWiki version with SVN revision if available
106 *
107 * @return mixed
108 */
109 public static function getVersion() {
110 global $wgVersion, $IP;
111 wfProfileIn( __METHOD__ );
112 $svn = self::getSvnRevision( $IP );
113 $version = $svn ? "$wgVersion (r$svn)" : $wgVersion;
114 wfProfileOut( __METHOD__ );
115 return $version;
116 }
117
118 /**
119 * Return a string of the MediaWiki version with a link to SVN revision if
120 * available
121 *
122 * @return mixed
123 */
124 public static function getVersionLinked() {
125 global $wgVersion, $IP;
126 wfProfileIn( __METHOD__ );
127 $svn = self::getSvnRevision( $IP );
128 $viewvc = 'http://svn.wikimedia.org/viewvc/mediawiki/trunk/phase3/?pathrev=';
129 $version = $svn ? "$wgVersion ([{$viewvc}{$svn} r$svn])" : $wgVersion;
130 wfProfileOut( __METHOD__ );
131 return $version;
132 }
133
134 /** Generate wikitext showing extensions name, URL, author and description */
135 function extensionCredits() {
136 global $wgExtensionCredits, $wgExtensionFunctions, $wgParser, $wgSkinExtensionFunctions;
137
138 if ( ! count( $wgExtensionCredits ) && ! count( $wgExtensionFunctions ) && ! count( $wgSkinExtensionFunctions ) )
139 return '';
140
141 $extensionTypes = array(
142 'specialpage' => wfMsg( 'version-specialpages' ),
143 'parserhook' => wfMsg( 'version-parserhooks' ),
144 'variable' => wfMsg( 'version-variables' ),
145 'media' => wfMsg( 'version-mediahandlers' ),
146 'other' => wfMsg( 'version-other' ),
147 );
148 wfRunHooks( 'SpecialVersionExtensionTypes', array( &$this, &$extensionTypes ) );
149
150 $out = Xml::element( 'h2', array( 'id' => 'mw-version-ext' ), wfMsg( 'version-extensions' ) ) .
151 Xml::openElement( 'table', array( 'id' => 'sv-ext' ) );
152
153 foreach ( $extensionTypes as $type => $text ) {
154 if ( isset ( $wgExtensionCredits[$type] ) && count ( $wgExtensionCredits[$type] ) ) {
155 $out .= $this->openExtType( $text );
156
157 usort( $wgExtensionCredits[$type], array( $this, 'compare' ) );
158
159 foreach ( $wgExtensionCredits[$type] as $extension ) {
160 $version = null;
161 $subVersion = '';
162 if ( isset( $extension['version'] ) ) {
163 $version = $extension['version'];
164 }
165 if ( isset( $extension['svn-revision'] ) &&
166 preg_match( '/\$(?:Rev|LastChangedRevision|Revision): *(\d+)/',
167 $extension['svn-revision'], $m ) ) {
168 $subVersion = 'r' . $m[1];
169 }
170
171 if( $version && $subVersion ) {
172 $version = $version . ' [' . $subVersion . ']';
173 } elseif ( !$version && $subVersion ) {
174 $version = $subVersion;
175 }
176
177 $out .= $this->formatCredits(
178 isset ( $extension['name'] ) ? $extension['name'] : '',
179 $version,
180 isset ( $extension['author'] ) ? $extension['author'] : '',
181 isset ( $extension['url'] ) ? $extension['url'] : null,
182 isset ( $extension['description'] ) ? $extension['description'] : '',
183 isset ( $extension['descriptionmsg'] ) ? $extension['descriptionmsg'] : ''
184 );
185 }
186 }
187 }
188
189 if ( count( $wgExtensionFunctions ) ) {
190 $out .= $this->openExtType( wfMsg( 'version-extension-functions' ) );
191 $out .= '<tr><td colspan="3">' . $this->listToText( $wgExtensionFunctions ) . "</td></tr>\n";
192 }
193
194 if ( $cnt = count( $tags = $wgParser->getTags() ) ) {
195 for ( $i = 0; $i < $cnt; ++$i )
196 $tags[$i] = "&lt;{$tags[$i]}&gt;";
197 $out .= $this->openExtType( wfMsg( 'version-parser-extensiontags' ) );
198 $out .= '<tr><td colspan="3">' . $this->listToText( $tags ). "</td></tr>\n";
199 }
200
201 if( $cnt = count( $fhooks = $wgParser->getFunctionHooks() ) ) {
202 $out .= $this->openExtType( wfMsg( 'version-parser-function-hooks' ) );
203 $out .= '<tr><td colspan="3">' . $this->listToText( $fhooks ) . "</td></tr>\n";
204 }
205
206 if ( count( $wgSkinExtensionFunctions ) ) {
207 $out .= $this->openExtType( wfMsg( 'version-skin-extension-functions' ) );
208 $out .= '<tr><td colspan="3">' . $this->listToText( $wgSkinExtensionFunctions ) . "</td></tr>\n";
209 }
210 $out .= Xml::closeElement( 'table' );
211 return $out;
212 }
213
214 /** Callback to sort extensions by type */
215 function compare( $a, $b ) {
216 global $wgLang;
217 if( $a['name'] === $b['name'] ) {
218 return 0;
219 } else {
220 return $wgLang->lc( $a['name'] ) > $wgLang->lc( $b['name'] )
221 ? 1
222 : -1;
223 }
224 }
225
226 function formatCredits( $name, $version = null, $author = null, $url = null, $description = null, $descriptionMsg = null ) {
227 $extension = isset( $url ) ? "[$url $name]" : $name;
228 $version = isset( $version ) ? "(" . wfMsg( 'version-version' ) . " $version)" : '';
229
230 # Look for a localized description
231 if( isset( $descriptionMsg ) ) {
232 $msg = wfMsg( $descriptionMsg );
233 if ( !wfEmptyMsg( $descriptionMsg, $msg ) && $msg != '' ) {
234 $description = $msg;
235 }
236 }
237
238 return "<tr>
239 <td><em>$extension $version</em></td>
240 <td>$description</td>
241 <td>" . $this->listToText( (array)$author ) . "</td>
242 </tr>\n";
243 }
244
245 /**
246 * @return string
247 */
248 function wgHooks() {
249 global $wgHooks;
250
251 if ( count( $wgHooks ) ) {
252 $myWgHooks = $wgHooks;
253 ksort( $myWgHooks );
254
255 $ret = Xml::element( 'h2', array( 'id' => 'mw-version-hooks' ), wfMsg( 'version-hooks' ) ) .
256 Xml::openElement( 'table', array( 'id' => 'sv-hooks' ) ) .
257 "<tr>
258 <th>" . wfMsg( 'version-hook-name' ) . "</th>
259 <th>" . wfMsg( 'version-hook-subscribedby' ) . "</th>
260 </tr>\n";
261
262 foreach ( $myWgHooks as $hook => $hooks )
263 $ret .= "<tr>
264 <td>$hook</td>
265 <td>" . $this->listToText( $hooks ) . "</td>
266 </tr>\n";
267
268 $ret .= Xml::closeElement( 'table' );
269 return $ret;
270 } else
271 return '';
272 }
273
274 private function openExtType($text, $name = null) {
275 $opt = array( 'colspan' => 3 );
276 $out = '';
277
278 if(!$this->firstExtOpened) {
279 // Insert a spacing line
280 $out .= '<tr class="sv-space">' . Xml::element( 'td', $opt ) . "</tr>\n";
281 }
282 $this->firstExtOpened = false;
283
284 if($name) { $opt['id'] = "sv-$name"; }
285
286 $out .= "<tr>" . Xml::element( 'th', $opt, $text) . "</tr>\n";
287 return $out;
288 }
289
290 /**
291 * @return string
292 */
293 function IPInfo() {
294 $ip = str_replace( '--', ' - ', htmlspecialchars( wfGetIP() ) );
295 return "<!-- visited from $ip -->\n" .
296 "<span style='display:none'>visited from $ip</span>";
297 }
298
299 /**
300 * @param array $list
301 * @return string
302 */
303 function listToText( $list ) {
304 $cnt = count( $list );
305
306 if ( $cnt == 1 ) {
307 // Enforce always returning a string
308 return (string)self::arrayToString( $list[0] );
309 } elseif ( $cnt == 0 ) {
310 return '';
311 } else {
312 global $wgLang;
313 sort( $list );
314 return $wgLang->listToText( array_map( array( __CLASS__, 'arrayToString' ), $list ) );
315 }
316 }
317
318 /**
319 * @param mixed $list Will convert an array to string if given and return
320 * the paramater unaltered otherwise
321 * @return mixed
322 */
323 static function arrayToString( $list ) {
324 if( is_array( $list ) && count( $list ) == 1 )
325 $list = $list[0];
326 if( is_object( $list ) ) {
327 $class = get_class( $list );
328 return "($class)";
329 } elseif ( !is_array( $list ) ) {
330 return $list;
331 } else {
332 if( is_object( $list[0] ) )
333 $class = get_class( $list[0] );
334 else
335 $class = $list[0];
336 return "($class, {$list[1]})";
337 }
338 }
339
340 /**
341 * Retrieve the revision number of a Subversion working directory.
342 *
343 * @param string $dir
344 * @return mixed revision number as int, or false if not a SVN checkout
345 */
346 public static function getSvnRevision( $dir ) {
347 // http://svnbook.red-bean.com/nightly/en/svn.developer.insidewc.html
348 $entries = $dir . '/.svn/entries';
349
350 if( !file_exists( $entries ) ) {
351 return false;
352 }
353
354 $content = file( $entries );
355
356 // check if file is xml (subversion release <= 1.3) or not (subversion release = 1.4)
357 if( preg_match( '/^<\?xml/', $content[0] ) ) {
358 // subversion is release <= 1.3
359 if( !function_exists( 'simplexml_load_file' ) ) {
360 // We could fall back to expat... YUCK
361 return false;
362 }
363
364 // SimpleXml whines about the xmlns...
365 wfSuppressWarnings();
366 $xml = simplexml_load_file( $entries );
367 wfRestoreWarnings();
368
369 if( $xml ) {
370 foreach( $xml->entry as $entry ) {
371 if( $xml->entry[0]['name'] == '' ) {
372 // The directory entry should always have a revision marker.
373 if( $entry['revision'] ) {
374 return intval( $entry['revision'] );
375 }
376 }
377 }
378 }
379 return false;
380 } else {
381 // subversion is release 1.4
382 return intval( $content[3] );
383 }
384 }
385
386 /**#@-*/
387 }