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