Mark SpecialVersion::execOutput as static to avoid E_STRICT
[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, $wgContLang;
24 $wgMessageCache->loadAllMessages();
25
26 $this->setHeaders();
27 $this->outputHeader();
28
29 if( $wgContLang->isRTL() ) {
30 $wgOut->addHTML( '<div dir="rtl">' );
31 } else {
32 $wgOut->addHTML( '<div dir="ltr">' );
33 }
34 $text =
35 $this->MediaWikiCredits() .
36 $this->softwareInformation() .
37 $this->extensionCredits();
38 if ( $wgSpecialVersionShowHooks ) {
39 $text .= $this->wgHooks();
40 }
41 $wgOut->addWikiText( $text );
42 $wgOut->addHTML( $this->IPInfo() );
43 $wgOut->addHTML( '</div>' );
44 }
45
46 /**
47 * execuate command for output
48 * @param string command
49 * @return string output
50 */
51 static function execOutput( $cmd ) {
52 $out = array( $cmd );
53 exec( $cmd.' 2>&1', $out );
54 unset($out[0]);
55 return implode("\n", $out );
56 }
57
58 /**#@+
59 * @private
60 */
61
62 /**
63 * @return wiki text showing the license information
64 */
65 static function MediaWikiCredits() {
66 global $wgContLang;
67
68 $ret = Xml::element( 'h2', array( 'id' => 'mw-version-license' ), wfMsg( 'version-license' ) );
69
70 // This text is always left-to-right.
71 $ret .= '<div dir="ltr">';
72 $ret .= "__NOTOC__
73 This wiki is powered by '''[http://www.mediawiki.org/ MediaWiki]''',
74 copyright © 2001-2009 Magnus Manske, Brion Vibber, Lee Daniel Crocker,
75 Tim Starling, Erik Möller, Gabriel Wicke, Ævar Arnfjörð Bjarmason,
76 Niklas Laxström, Domas Mituzas, Rob Church, Yuri Astrakhan, Aryeh Gregor,
77 Aaron Schulz and others.
78
79 MediaWiki is free software; you can redistribute it and/or modify
80 it under the terms of the GNU General Public License as published by
81 the Free Software Foundation; either version 2 of the License, or
82 (at your option) any later version.
83
84 MediaWiki is distributed in the hope that it will be useful,
85 but WITHOUT ANY WARRANTY; without even the implied warranty of
86 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
87 GNU General Public License for more details.
88
89 You should have received [{{SERVER}}{{SCRIPTPATH}}/COPYING a copy of the GNU General Public License]
90 along with this program; if not, write to the Free Software
91 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
92 or [http://www.gnu.org/licenses/old-licenses/gpl-2.0.html read it online].
93 ";
94 $ret .= '</div>';
95
96 return str_replace( "\t\t", '', $ret ) . "\n";
97 }
98
99 /**
100 * @return wiki text showing the third party software versions (apache, php, mysql).
101 */
102 static function softwareInformation() {
103 global $wgUseImageMagick, $wgImageMagickConvertCommand, $wgDiff3, $wgDiff;
104 $dbr = wfGetDB( DB_SLAVE );
105
106 // Put the software in an array of form 'name' => 'version'. All messages should
107 // be loaded here, so feel free to use wfMsg*() in the 'name'. Raw HTML or wikimarkup
108 // can be used
109 $software = array();
110 $software['[http://www.mediawiki.org/ MediaWiki]'] = self::getVersionLinked();
111 $software['[http://www.php.net/ PHP]'] = phpversion() . " (" . php_sapi_name() . ")";
112 $software[$dbr->getSoftwareLink()] = $dbr->getServerVersion();
113
114 // Version information for diff3
115 if ( file_exists( trim( $wgDiff3, '"' ) ) ) {
116 $swDiff3Info = self::execOutput( $wgDiff3 . ' -v' );
117 $swDiff3Line = explode("\n",$swDiff3Info ,2);
118 $swDiff3Ver = $swDiff3Line[0];
119 $software['[http://www.gnu.org/software/diffutils/diffutils.html diff3]'] = $swDiff3Ver;
120 }
121
122 // Version information for diff
123 if ( file_exists( trim( $wgDiff, '"' ) ) ) {
124 $swDiffInfo = self::execOutput( $wgDiff . ' -v' );
125 $swDiffLine = explode("\n",$swDiffInfo ,2);
126 $swDiffVer = $swDiffLine[0];
127 $software['[http://www.gnu.org/software/diffutils/diffutils.html diff]'] = $swDiffVer;
128 }
129
130 // Look for ImageMagick's version, if did not found, try to find the GD library version
131 if ( $wgUseImageMagick === true ) {
132 if ( file_exists( trim( $wgImageMagickConvertCommand, '"' ) ) ) {
133 $swImageMagickInfo = self::execOutput( $wgImageMagickConvertCommand . ' -version' );
134 list( $head, $tail ) = explode( 'ImageMagick', $swImageMagickInfo );
135 list( $swImageMagickVer ) = explode('http://www.imagemagick.org', $tail );
136 $software['[http://www.imagemagick.org/ ImageMagick]'] = $swImageMagickVer;
137 }
138 } else {
139 if( function_exists( 'gd_info' ) ) {
140 $gdInfo = gd_info();
141 if ( strstr( $gdInfo['GD Version'], 'bundled' ) != false ) {
142 $gd_URL = 'http://www.php.net/gd';
143 }
144 else {
145 $gd_URL = 'http://www.libgd.org';
146 }
147 $software['[' . $gd_URL . ' GD library]'] = $gdInfo['GD Version'];
148 }
149 }
150
151 // Allow a hook to add/remove items
152 wfRunHooks( 'SoftwareInfo', array( &$software ) );
153
154 $out = Xml::element( 'h2', array( 'id' => 'mw-version-software' ), wfMsg( 'version-software' ) ) .
155 Xml::openElement( 'table', array( 'class' => 'wikitable', 'id' => 'sv-software' ) ) .
156 "<tr>
157 <th>" . wfMsg( 'version-software-product' ) . "</th>
158 <th>" . wfMsg( 'version-software-version' ) . "</th>
159 </tr>\n";
160 foreach( $software as $name => $version ) {
161 $out .= "<tr>
162 <td>" . $name . "</td>
163 <td>" . $version . "</td>
164 </tr>\n";
165 }
166 return $out . Xml::closeElement( 'table' );
167 }
168
169 /**
170 * Return a string of the MediaWiki version with SVN revision if available
171 *
172 * @return mixed
173 */
174 public static function getVersion( $flags = '' ) {
175 global $wgVersion, $IP;
176 wfProfileIn( __METHOD__ );
177 $svn = self::getSvnRevision( $IP, false, false , false );
178 $svnCo = self::getSvnRevision( $IP, true, false , false );
179 if ( !$svn ) {
180 $version = $wgVersion;
181 } elseif( $flags === 'nodb' ) {
182 $version = "$wgVersion (r$svnCo)";
183 } else {
184 $version = $wgVersion . wfMsg( 'version-svn-revision', $svn, $svnCo );
185 }
186 wfProfileOut( __METHOD__ );
187 return $version;
188 }
189
190 /**
191 * Return a string of the MediaWiki version with a link to SVN revision if
192 * available
193 *
194 * @return mixed
195 */
196 public static function getVersionLinked() {
197 global $wgVersion, $IP;
198 wfProfileIn( __METHOD__ );
199 $svn = self::getSvnRevision( $IP, false, false, false );
200 $svnCo = self::getSvnRevision( $IP, true, false, false );
201 $svnDir = self::getSvnRevision( $IP, true, false, true );
202 $viewvcStart = 'http://svn.wikimedia.org/viewvc/mediawiki/';
203 $viewvcEnd = '/?pathrev=';
204 $viewvc = $viewvcStart . $svnDir . $viewvcEnd;
205 $version = $svn ? $wgVersion . " [{$viewvc}{$svnCo} " . wfMsg( 'version-svn-revision', $svn, $svnCo ) . ']' : $wgVersion;
206 wfProfileOut( __METHOD__ );
207 return $version;
208 }
209
210 /** Generate wikitext showing extensions name, URL, author and description */
211 function extensionCredits() {
212 global $wgExtensionCredits, $wgExtensionFunctions, $wgParser, $wgSkinExtensionFunctions;
213
214 if ( ! count( $wgExtensionCredits ) && ! count( $wgExtensionFunctions ) && ! count( $wgSkinExtensionFunctions ) )
215 return '';
216
217 $extensionTypes = array(
218 'specialpage' => wfMsg( 'version-specialpages' ),
219 'parserhook' => wfMsg( 'version-parserhooks' ),
220 'variable' => wfMsg( 'version-variables' ),
221 'media' => wfMsg( 'version-mediahandlers' ),
222 'other' => wfMsg( 'version-other' ),
223 );
224 wfRunHooks( 'SpecialVersionExtensionTypes', array( &$this, &$extensionTypes ) );
225
226 $out = Xml::element( 'h2', array( 'id' => 'mw-version-ext' ), wfMsg( 'version-extensions' ) ) .
227 Xml::openElement( 'table', array( 'class' => 'wikitable', 'id' => 'sv-ext' ) );
228
229 foreach ( $extensionTypes as $type => $text ) {
230 if ( isset ( $wgExtensionCredits[$type] ) && count ( $wgExtensionCredits[$type] ) ) {
231 $out .= $this->openExtType( $text );
232
233 usort( $wgExtensionCredits[$type], array( $this, 'compare' ) );
234
235 foreach ( $wgExtensionCredits[$type] as $extension ) {
236 $version = null;
237 $subVersion = null;
238 $subVersionCo = null;
239 $viewvc = null;
240 if ( isset( $extension['path'] ) ) {
241 $subVersion = self::getSvnRevision(dirname($extension['path']), false, true, false);
242 $subVersionCo = self::getSvnRevision(dirname($extension['path']), true, true, false);
243 $subVersionDir = self::getSvnRevision(dirname($extension['path']), false, true, true);
244 if ($subVersionDir)
245 $viewvc = $subVersionDir . $subVersionCo;
246 }
247 if ( isset( $extension['version'] ) ) {
248 $version = $extension['version'];
249 }
250
251 $out .= $this->formatCredits(
252 isset ( $extension['name'] ) ? $extension['name'] : '',
253 $version,
254 $subVersion,
255 $subVersionCo,
256 $viewvc,
257 isset ( $extension['author'] ) ? $extension['author'] : '',
258 isset ( $extension['url'] ) ? $extension['url'] : null,
259 isset ( $extension['description'] ) ? $extension['description'] : '',
260 isset ( $extension['descriptionmsg'] ) ? $extension['descriptionmsg'] : ''
261 );
262 }
263 }
264 }
265
266 if ( count( $wgExtensionFunctions ) ) {
267 $out .= $this->openExtType( wfMsg( 'version-extension-functions' ) );
268 $out .= '<tr><td colspan="4">' . $this->listToText( $wgExtensionFunctions ) . "</td></tr>\n";
269 }
270
271 if ( $cnt = count( $tags = $wgParser->getTags() ) ) {
272 for ( $i = 0; $i < $cnt; ++$i )
273 $tags[$i] = "&lt;{$tags[$i]}&gt;";
274 $out .= $this->openExtType( wfMsg( 'version-parser-extensiontags' ) );
275 $out .= '<tr><td colspan="4">' . $this->listToText( $tags ). "</td></tr>\n";
276 }
277
278 if( $cnt = count( $fhooks = $wgParser->getFunctionHooks() ) ) {
279 $out .= $this->openExtType( wfMsg( 'version-parser-function-hooks' ) );
280 $out .= '<tr><td colspan="4">' . $this->listToText( $fhooks ) . "</td></tr>\n";
281 }
282
283 if ( count( $wgSkinExtensionFunctions ) ) {
284 $out .= $this->openExtType( wfMsg( 'version-skin-extension-functions' ) );
285 $out .= '<tr><td colspan="4">' . $this->listToText( $wgSkinExtensionFunctions ) . "</td></tr>\n";
286 }
287 $out .= Xml::closeElement( 'table' );
288 return $out;
289 }
290
291 /** Callback to sort extensions by type */
292 function compare( $a, $b ) {
293 global $wgLang;
294 if( $a['name'] === $b['name'] ) {
295 return 0;
296 } else {
297 return $wgLang->lc( $a['name'] ) > $wgLang->lc( $b['name'] )
298 ? 1
299 : -1;
300 }
301 }
302
303 function formatCredits( $name, $version = null, $subVersion = null, $subVersionCo = null, $subVersionURL = null, $author = null, $url = null, $description = null, $descriptionMsg = null ) {
304 $haveSubversion = $subVersion;
305 $extension = isset( $url ) ? "[$url $name]" : $name;
306 $version = isset( $version ) ? wfMsg( 'version-version', $version ) : '';
307 $subVersion = isset( $subVersion ) ? wfMsg( 'version-svn-revision', $subVersion, $subVersionCo ) : '';
308 $subVersion = isset( $subVersionURL ) ? "[$subVersionURL $subVersion]" : $subVersion;
309
310 # Look for a localized description
311 if( isset( $descriptionMsg ) ) {
312 $msg = wfMsg( $descriptionMsg );
313 if ( !wfEmptyMsg( $descriptionMsg, $msg ) && $msg != '' ) {
314 $description = $msg;
315 }
316 }
317
318 if ( $haveSubversion ) {
319 $extNameVer = "<tr>
320 <td><em>$extension $version</em></td>
321 <td><em>$subVersion</em></td>";
322 } else {
323 $extNameVer = "<tr>
324 <td colspan=\"2\"><em>$extension $version</em></td>";
325 }
326 $extDescAuthor = "<td>$description</td>
327 <td>" . $this->listToText( (array)$author ) . "</td>
328 </tr>\n";
329 return $ret = $extNameVer . $extDescAuthor;
330 return $ret;
331 }
332
333 /**
334 * @return string
335 */
336 function wgHooks() {
337 global $wgHooks;
338
339 if ( count( $wgHooks ) ) {
340 $myWgHooks = $wgHooks;
341 ksort( $myWgHooks );
342
343 $ret = Xml::element( 'h2', array( 'id' => 'mw-version-hooks' ), wfMsg( 'version-hooks' ) ) .
344 Xml::openElement( 'table', array( 'class' => 'wikitable', 'id' => 'sv-hooks' ) ) .
345 "<tr>
346 <th>" . wfMsg( 'version-hook-name' ) . "</th>
347 <th>" . wfMsg( 'version-hook-subscribedby' ) . "</th>
348 </tr>\n";
349
350 foreach ( $myWgHooks as $hook => $hooks )
351 $ret .= "<tr>
352 <td>$hook</td>
353 <td>" . $this->listToText( $hooks ) . "</td>
354 </tr>\n";
355
356 $ret .= Xml::closeElement( 'table' );
357 return $ret;
358 } else
359 return '';
360 }
361
362 private function openExtType($text, $name = null) {
363 $opt = array( 'colspan' => 4 );
364 $out = '';
365
366 if(!$this->firstExtOpened) {
367 // Insert a spacing line
368 $out .= '<tr class="sv-space">' . Xml::element( 'td', $opt ) . "</tr>\n";
369 }
370 $this->firstExtOpened = false;
371
372 if($name) { $opt['id'] = "sv-$name"; }
373
374 $out .= "<tr>" . Xml::element( 'th', $opt, $text) . "</tr>\n";
375 return $out;
376 }
377
378 /**
379 * @return string
380 */
381 function IPInfo() {
382 $ip = str_replace( '--', ' - ', htmlspecialchars( wfGetIP() ) );
383 return "<!-- visited from $ip -->\n" .
384 "<span style='display:none'>visited from $ip</span>";
385 }
386
387 /**
388 * @param array $list
389 * @return string
390 */
391 function listToText( $list ) {
392 $cnt = count( $list );
393
394 if ( $cnt == 1 ) {
395 // Enforce always returning a string
396 return (string)self::arrayToString( $list[0] );
397 } elseif ( $cnt == 0 ) {
398 return '';
399 } else {
400 global $wgLang;
401 sort( $list );
402 return $wgLang->listToText( array_map( array( __CLASS__, 'arrayToString' ), $list ) );
403 }
404 }
405
406 /**
407 * @param mixed $list Will convert an array to string if given and return
408 * the paramater unaltered otherwise
409 * @return mixed
410 */
411 static function arrayToString( $list ) {
412 if( is_array( $list ) && count( $list ) == 1 )
413 $list = $list[0];
414 if( is_object( $list ) ) {
415 $class = get_class( $list );
416 return "($class)";
417 } elseif ( !is_array( $list ) ) {
418 return $list;
419 } else {
420 if( is_object( $list[0] ) )
421 $class = get_class( $list[0] );
422 else
423 $class = $list[0];
424 return "($class, {$list[1]})";
425 }
426 }
427
428 /**
429 * Retrieve the revision number of a Subversion working directory.
430 *
431 * @param String $dir Directory of the svn checkout
432 * @param Boolean $coRev optional to return value whether is Last Modified
433 * or Checkout revision number
434 * @param Boolean $extension optional to check the path whether is from
435 * Wikimedia SVN server or not
436 * @param Boolean $relPath optional to get the end part of the checkout path
437 * @return mixed revision number as int, end part of the checkout path,
438 * or false if not a SVN checkout
439 */
440 public static function getSvnRevision( $dir, $coRev = false, $extension = false, $relPath = false) {
441 // http://svnbook.red-bean.com/nightly/en/svn.developer.insidewc.html
442 $entries = $dir . '/.svn/entries';
443
444 if( !file_exists( $entries ) ) {
445 return false;
446 }
447
448 $content = file( $entries );
449
450 // check if file is xml (subversion release <= 1.3) or not (subversion release = 1.4)
451 if( preg_match( '/^<\?xml/', $content[0] ) ) {
452 // subversion is release <= 1.3
453 if( !function_exists( 'simplexml_load_file' ) ) {
454 // We could fall back to expat... YUCK
455 return false;
456 }
457
458 // SimpleXml whines about the xmlns...
459 wfSuppressWarnings();
460 $xml = simplexml_load_file( $entries );
461 wfRestoreWarnings();
462
463 if( $xml ) {
464 foreach( $xml->entry as $entry ) {
465 if( $xml->entry[0]['name'] == '' ) {
466 // The directory entry should always have a revision marker.
467 if( $entry['revision'] ) {
468 return intval( $entry['revision'] );
469 }
470 }
471 }
472 }
473 return false;
474 } else {
475 // subversion is release 1.4 or above
476 if ($relPath) {
477 $endPath = strstr( $content[4], 'tags' );
478 if (!$endPath) {
479 $endPath = strstr( $content[4], 'branches' );
480 if (!$endPath) {
481 $endPath = strstr( $content[4], 'trunk' );
482 if (!$endPath)
483 return false;
484 }
485 }
486 $endPath = trim ( $endPath );
487 if ($extension) {
488 $wmSvnPath = 'svn.wikimedia.org/svnroot/mediawiki';
489 $isWMSvn = strstr($content[5],$wmSvnPath);
490 if (!strcmp($isWMSvn,null)) {
491 return false;
492 } else {
493 $viewvcStart = 'http://svn.wikimedia.org/viewvc/mediawiki/';
494 if (strstr( $content[4], 'trunk' ))
495 $viewvcEnd = '/?pathrev=';
496 else
497 // Avoids 404 error using pathrev when it does not found
498 $viewvcEnd = '/?revision=';
499 $viewvc = $viewvcStart . $endPath . $viewvcEnd;
500 return $viewvc;
501 }
502 }
503 return $endPath;
504 }
505 if ($coRev)
506 // get the directory checkout revsion number
507 return intval( $content[3]) ;
508 else
509 // get the directory last modified revision number
510 return intval( $content[10] );
511 }
512 }
513
514 /**#@-*/
515 }