WARNING: HUGE COMMIT
[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 * @file
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
13 /**
14 * constructor
15 */
16 function wfSpecialVersion() {
17 $version = new SpecialVersion;
18 $version->execute();
19 }
20
21 /**
22 * @ingroup SpecialPage
23 */
24 class SpecialVersion {
25 private $firstExtOpened = true;
26
27 /**
28 * main()
29 */
30 function execute() {
31 global $wgOut, $wgMessageCache;
32 $wgMessageCache->loadAllMessages();
33
34 $wgOut->addHTML( '<div dir="ltr">' );
35 $wgOut->addWikiText(
36 $this->MediaWikiCredits() .
37 $this->softwareInformation() .
38 $this->extensionCredits() .
39 $this->wgHooks()
40 );
41 $wgOut->addHTML( $this->IPInfo() );
42 $wgOut->addHTML( '</div>' );
43 }
44
45 /**#@+
46 * @private
47 */
48
49 /**
50 * @return wiki text showing the license information
51 */
52 static function MediaWikiCredits() {
53 $ret = Xml::element( 'h2', array( 'id' => 'mw-version-license' ), wfMsg( 'version-license' ) ) .
54 "__NOTOC__
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.
59
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.
64
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.
69
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].
74 ";
75
76 return str_replace( "\t\t", '', $ret ) . "\n";
77 }
78
79 /**
80 * @return wiki text showing the third party software versions (apache, php, mysql).
81 */
82 static function softwareInformation() {
83 $dbr = wfGetDB( DB_SLAVE );
84
85 return Xml::element( 'h2', array( 'id' => 'mw-version-software' ), wfMsg( 'version-software' ) ) .
86 Xml::openElement( 'table', array( 'id' => 'sv-software' ) ) .
87 "<tr>
88 <th>" . wfMsg( 'version-software-product' ) . "</th>
89 <th>" . wfMsg( 'version-software-version' ) . "</th>
90 </tr>\n
91 <tr>
92 <td>[http://www.mediawiki.org/ MediaWiki]</td>
93 <td>" . self::getVersionLinked() . "</td>
94 </tr>\n
95 <tr>
96 <td>[http://www.php.net/ PHP]</td>
97 <td>" . phpversion() . " (" . php_sapi_name() . ")</td>
98 </tr>\n
99 <tr>
100 <td>" . $dbr->getSoftwareLink() . "</td>
101 <td>" . $dbr->getServerVersion() . "</td>
102 </tr>\n" .
103 Xml::closeElement( 'table' );
104 }
105
106 /**
107 * Return a string of the MediaWiki version with SVN revision if available
108 *
109 * @return mixed
110 */
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__ );
117 return $version;
118 }
119
120 /**
121 * Return a string of the MediaWiki version with a link to SVN revision if
122 * available
123 *
124 * @return mixed
125 */
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__ );
133 return $version;
134 }
135
136 /** Generate wikitext showing extensions name, URL, author and description */
137 function extensionCredits() {
138 global $wgExtensionCredits, $wgExtensionFunctions, $wgParser, $wgSkinExtensionFunctions;
139
140 if ( ! count( $wgExtensionCredits ) && ! count( $wgExtensionFunctions ) && ! count( $wgSkinExtensionFunctions ) )
141 return '';
142
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' ),
149 );
150 wfRunHooks( 'SpecialVersionExtensionTypes', array( &$this, &$extensionTypes ) );
151
152 $out = Xml::element( 'h2', array( 'id' => 'mw-version-ext' ), wfMsg( 'version-extensions' ) ) .
153 Xml::openElement( 'table', array( 'id' => 'sv-ext' ) );
154
155 foreach ( $extensionTypes as $type => $text ) {
156 if ( isset ( $wgExtensionCredits[$type] ) && count ( $wgExtensionCredits[$type] ) ) {
157 $out .= $this->openExtType( $text );
158
159 usort( $wgExtensionCredits[$type], array( $this, 'compare' ) );
160
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 ) )
167 {
168 $version = 'r' . $m[1];
169 } else {
170 $version = null;
171 }
172
173 $out .= $this->formatCredits(
174 isset ( $extension['name'] ) ? $extension['name'] : '',
175 $version,
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'] : ''
180 );
181 }
182 }
183 }
184
185 if ( count( $wgExtensionFunctions ) ) {
186 $out .= $this->openExtType( wfMsg( 'version-extension-functions' ) );
187 $out .= '<tr><td colspan="3">' . $this->listToText( $wgExtensionFunctions ) . "</td></tr>\n";
188 }
189
190 if ( $cnt = count( $tags = $wgParser->getTags() ) ) {
191 for ( $i = 0; $i < $cnt; ++$i )
192 $tags[$i] = "&lt;{$tags[$i]}&gt;";
193 $out .= $this->openExtType( wfMsg( 'version-parser-extensiontags' ) );
194 $out .= '<tr><td colspan="3">' . $this->listToText( $tags ). "</td></tr>\n";
195 }
196
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";
200 }
201
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";
205 }
206 $out .= Xml::closeElement( 'table' );
207 return $out;
208 }
209
210 /** Callback to sort extensions by type */
211 function compare( $a, $b ) {
212 global $wgLang;
213 if( $a['name'] === $b['name'] ) {
214 return 0;
215 } else {
216 return $wgLang->lc( $a['name'] ) > $wgLang->lc( $b['name'] )
217 ? 1
218 : -1;
219 }
220 }
221
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)" : '';
225
226 # Look for a localized description
227 if( isset( $descriptionMsg ) ) {
228 $msg = wfMsg( $descriptionMsg );
229 if ( !wfEmptyMsg( $descriptionMsg, $msg ) && $msg != '' ) {
230 $description = $msg;
231 }
232 }
233
234 return "<tr>
235 <td><em>$extension $version</em></td>
236 <td>$description</td>
237 <td>" . $this->listToText( (array)$author ) . "</td>
238 </tr>\n";
239 }
240
241 /**
242 * @return string
243 */
244 function wgHooks() {
245 global $wgHooks;
246
247 if ( count( $wgHooks ) ) {
248 $myWgHooks = $wgHooks;
249 ksort( $myWgHooks );
250
251 $ret = Xml::element( 'h2', array( 'id' => 'mw-version-hooks' ), wfMsg( 'version-hooks' ) ) .
252 Xml::openElement( 'table', array( 'id' => 'sv-hooks' ) ) .
253 "<tr>
254 <th>" . wfMsg( 'version-hook-name' ) . "</th>
255 <th>" . wfMsg( 'version-hook-subscribedby' ) . "</th>
256 </tr>\n";
257
258 foreach ( $myWgHooks as $hook => $hooks )
259 $ret .= "<tr>
260 <td>$hook</td>
261 <td>" . $this->listToText( $hooks ) . "</td>
262 </tr>\n";
263
264 $ret .= Xml::closeElement( 'table' );
265 return $ret;
266 } else
267 return '';
268 }
269
270 private function openExtType($text, $name = null) {
271 $opt = array( 'colspan' => 3 );
272 $out = '';
273
274 if(!$this->firstExtOpened) {
275 // Insert a spacing line
276 $out .= '<tr class="sv-space">' . Xml::element( 'td', $opt ) . "</tr>\n";
277 }
278 $this->firstExtOpened = false;
279
280 if($name) { $opt['id'] = "sv-$name"; }
281
282 $out .= "<tr>" . Xml::element( 'th', $opt, $text) . "</tr>\n";
283 return $out;
284 }
285
286 /**
287 * @static
288 *
289 * @return string
290 */
291 function IPInfo() {
292 $ip = str_replace( '--', ' - ', htmlspecialchars( wfGetIP() ) );
293 return "<!-- visited from $ip -->\n" .
294 "<span style='display:none'>visited from $ip</span>";
295 }
296
297 /**
298 * @param array $list
299 * @return string
300 */
301 function listToText( $list ) {
302 $cnt = count( $list );
303
304 if ( $cnt == 1 ) {
305 // Enforce always returning a string
306 return (string)$this->arrayToString( $list[0] );
307 } elseif ( $cnt == 0 ) {
308 return '';
309 } else {
310 sort( $list );
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' );
315
316 return implode( ', ', $one ) . " $and $two";
317 }
318 }
319
320 /**
321 * @static
322 *
323 * @param mixed $list Will convert an array to string if given and return
324 * the paramater unaltered otherwise
325 * @return mixed
326 */
327 function arrayToString( $list ) {
328 if( is_object( $list ) ) {
329 $class = get_class( $list );
330 return "($class)";
331 } elseif ( ! is_array( $list ) ) {
332 return $list;
333 } else {
334 $class = get_class( $list[0] );
335 return "($class, {$list[1]})";
336 }
337 }
338
339 /**
340 * Retrieve the revision number of a Subversion working directory.
341 *
342 * @param string $dir
343 * @return mixed revision number as int, or false if not a SVN checkout
344 */
345 public static function getSvnRevision( $dir ) {
346 // http://svnbook.red-bean.com/nightly/en/svn.developer.insidewc.html
347 $entries = $dir . '/.svn/entries';
348
349 if( !file_exists( $entries ) ) {
350 return false;
351 }
352
353 $content = file( $entries );
354
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
360 return false;
361 }
362
363 // SimpleXml whines about the xmlns...
364 wfSuppressWarnings();
365 $xml = simplexml_load_file( $entries );
366 wfRestoreWarnings();
367
368 if( $xml ) {
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'] );
374 }
375 }
376 }
377 }
378 return false;
379 } else {
380 // subversion is release 1.4
381 return intval( $content[3] );
382 }
383 }
384
385 /**#@-*/
386 }
387
388 /**#@-*/