Tweak comment format
[lhc/web/wiklou.git] / includes / specials / SpecialAllmessages.php
1 <?php
2 /**
3 * Implements Special:Allmessages
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * http://www.gnu.org/copyleft/gpl.html
19 *
20 * @file
21 * @ingroup SpecialPage
22 */
23
24 /**
25 * Use this special page to get a list of the MediaWiki system messages.
26 *
27 * @file
28 * @ingroup SpecialPage
29 */
30 class SpecialAllmessages extends SpecialPage {
31
32 /**
33 * @var AllmessagesTablePager
34 */
35 protected $table;
36
37 protected $filter, $prefix, $langCode;
38
39 /**
40 * Constructor
41 */
42 public function __construct() {
43 parent::__construct( 'Allmessages' );
44 }
45
46 /**
47 * Show the special page
48 *
49 * @param $par Mixed: parameter passed to the page or null
50 */
51 public function execute( $par ) {
52 global $wgOut, $wgRequest;
53
54 $this->setHeaders();
55
56 global $wgUseDatabaseMessages;
57 if( !$wgUseDatabaseMessages ) {
58 $wgOut->addWikiMsg( 'allmessagesnotsupportedDB' );
59 return;
60 } else {
61 $this->outputHeader( 'allmessagestext' );
62 }
63
64 $wgOut->addModuleStyles( 'mediawiki.special' );
65
66 $this->filter = $wgRequest->getVal( 'filter', 'all' );
67 $this->prefix = $wgRequest->getVal( 'prefix', '' );
68
69 $this->table = new AllmessagesTablePager(
70 $this,
71 array(),
72 wfGetLangObj( $wgRequest->getVal( 'lang', $par ) )
73 );
74
75 $this->langCode = $this->table->lang->getCode();
76
77 $wgOut->addHTML( $this->buildForm() .
78 $this->table->getNavigationBar() .
79 $this->table->getLimitForm() .
80 $this->table->getBody() .
81 $this->table->getNavigationBar() );
82
83 }
84
85 function buildForm() {
86 global $wgScript;
87
88 $languages = Language::getLanguageNames( false );
89 ksort( $languages );
90
91 $out = Xml::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript, 'id' => 'mw-allmessages-form' ) ) .
92 Xml::fieldset( wfMsg( 'allmessages-filter-legend' ) ) .
93 Html::hidden( 'title', $this->getTitle()->getPrefixedText() ) .
94 Xml::openElement( 'table', array( 'class' => 'mw-allmessages-table' ) ) . "\n" .
95 '<tr>
96 <td class="mw-label">' .
97 Xml::label( wfMsg( 'allmessages-prefix' ), 'mw-allmessages-form-prefix' ) .
98 "</td>\n
99 <td class=\"mw-input\">" .
100 Xml::input( 'prefix', 20, str_replace( '_', ' ', $this->prefix ), array( 'id' => 'mw-allmessages-form-prefix' ) ) .
101 "</td>\n
102 </tr>
103 <tr>\n
104 <td class='mw-label'>" .
105 wfMsg( 'allmessages-filter' ) .
106 "</td>\n
107 <td class='mw-input'>" .
108 Xml::radioLabel( wfMsg( 'allmessages-filter-unmodified' ),
109 'filter',
110 'unmodified',
111 'mw-allmessages-form-filter-unmodified',
112 ( $this->filter == 'unmodified' )
113 ) .
114 Xml::radioLabel( wfMsg( 'allmessages-filter-all' ),
115 'filter',
116 'all',
117 'mw-allmessages-form-filter-all',
118 ( $this->filter == 'all' )
119 ) .
120 Xml::radioLabel( wfMsg( 'allmessages-filter-modified' ),
121 'filter',
122 'modified',
123 'mw-allmessages-form-filter-modified',
124 ( $this->filter == 'modified' )
125 ) .
126 "</td>\n
127 </tr>
128 <tr>\n
129 <td class=\"mw-label\">" .
130 Xml::label( wfMsg( 'allmessages-language' ), 'mw-allmessages-form-lang' ) .
131 "</td>\n
132 <td class=\"mw-input\">" .
133 Xml::openElement( 'select', array( 'id' => 'mw-allmessages-form-lang', 'name' => 'lang' ) );
134
135 foreach( $languages as $lang => $name ) {
136 $selected = $lang == $this->langCode;
137 $out .= Xml::option( $lang . ' - ' . $name, $lang, $selected ) . "\n";
138 }
139 $out .= Xml::closeElement( 'select' ) .
140 "</td>\n
141 </tr>
142 <tr>\n
143 <td></td>
144 <td>" .
145 Xml::submitButton( wfMsg( 'allmessages-filter-submit' ) ) .
146 "</td>\n
147 </tr>" .
148 Xml::closeElement( 'table' ) .
149 $this->table->getHiddenFields( array( 'title', 'prefix', 'filter', 'lang' ) ) .
150 Xml::closeElement( 'fieldset' ) .
151 Xml::closeElement( 'form' );
152 return $out;
153 }
154 }
155
156 /**
157 * Use TablePager for prettified output. We have to pretend that we're
158 * getting data from a table when in fact not all of it comes from the database.
159 */
160 class AllmessagesTablePager extends TablePager {
161
162 public $mLimitsShown;
163
164 /**
165 * @var Skin
166 */
167 protected $mSkin;
168
169 /**
170 * @var Language
171 */
172 public $lang;
173
174 /**
175 * @var null|bool
176 */
177 public $custom;
178
179 function __construct( $page, $conds, $langObj = null ) {
180 parent::__construct();
181 $this->mIndexField = 'am_title';
182 $this->mPage = $page;
183 $this->mConds = $conds;
184 $this->mDefaultDirection = true; // always sort ascending
185 $this->mLimitsShown = array( 20, 50, 100, 250, 500, 5000 );
186
187 global $wgLang, $wgContLang, $wgRequest;
188
189 $this->talk = htmlspecialchars( wfMsg( 'talkpagelinktext' ) );
190
191 $this->lang = ( $langObj ? $langObj : $wgContLang );
192 $this->langcode = $this->lang->getCode();
193 $this->foreign = $this->langcode != $wgContLang->getCode();
194
195 if( $wgRequest->getVal( 'filter', 'all' ) === 'all' ){
196 $this->custom = null; // So won't match in either case
197 } else {
198 $this->custom = ($wgRequest->getVal( 'filter' ) == 'unmodified');
199 }
200
201 $prefix = $wgLang->ucfirst( $wgRequest->getVal( 'prefix', '' ) );
202 $prefix = $prefix != '' ? Title::makeTitleSafe( NS_MEDIAWIKI, $wgRequest->getVal( 'prefix', null ) ) : null;
203 if( $prefix !== null ){
204 $this->prefix = '/^' . preg_quote( $prefix->getDBkey() ) . '/i';
205 } else {
206 $this->prefix = false;
207 }
208 $this->getSkin();
209
210 // The suffix that may be needed for message names if we're in a
211 // different language (eg [[MediaWiki:Foo/fr]]: $suffix = '/fr'
212 if( $this->foreign ) {
213 $this->suffix = '/' . $this->langcode;
214 } else {
215 $this->suffix = '';
216 }
217 }
218
219 function getAllMessages( $descending ) {
220 wfProfileIn( __METHOD__ );
221 $messageNames = Language::getLocalisationCache()->getSubitemList( 'en', 'messages' );
222 if( $descending ){
223 rsort( $messageNames );
224 } else {
225 asort( $messageNames );
226 }
227
228 // Normalise message names so they look like page titles
229 $messageNames = array_map( array( $this->lang, 'ucfirst' ), $messageNames );
230
231 wfProfileOut( __METHOD__ );
232 return $messageNames;
233 }
234
235 /**
236 * Determine which of the MediaWiki and MediaWiki_talk namespace pages exist.
237 * Returns array( 'pages' => ..., 'talks' => ... ), where the subarrays have
238 * an entry for each existing page, with the key being the message name and
239 * value arbitrary.
240 */
241 function getCustomisedStatuses( $messageNames ) {
242 wfProfileIn( __METHOD__ . '-db' );
243
244 $dbr = wfGetDB( DB_SLAVE );
245 $res = $dbr->select( 'page',
246 array( 'page_namespace', 'page_title' ),
247 array( 'page_namespace' => array( NS_MEDIAWIKI, NS_MEDIAWIKI_TALK ) ),
248 __METHOD__,
249 array( 'USE INDEX' => 'name_title' )
250 );
251 $xNames = array_flip( $messageNames );
252
253 $pageFlags = $talkFlags = array();
254
255 foreach ( $res as $s ) {
256 if( $s->page_namespace == NS_MEDIAWIKI ) {
257 if( $this->foreign ) {
258 $title = explode( '/', $s->page_title );
259 if( count( $title ) === 2 && $this->langcode == $title[1]
260 && isset( $xNames[$title[0]] ) ) {
261 $pageFlags["{$title[0]}"] = true;
262 }
263 } elseif( isset( $xNames[$s->page_title] ) ) {
264 $pageFlags[$s->page_title] = true;
265 }
266 } else if( $s->page_namespace == NS_MEDIAWIKI_TALK ){
267 $talkFlags[$s->page_title] = true;
268 }
269 }
270
271 wfProfileOut( __METHOD__ . '-db' );
272
273 return array( 'pages' => $pageFlags, 'talks' => $talkFlags );
274 }
275
276 /**
277 * This function normally does a database query to get the results; we need
278 * to make a pretend result using a FakeResultWrapper.
279 */
280 function reallyDoQuery( $offset, $limit, $descending ) {
281 $result = new FakeResultWrapper( array() );
282
283 $messageNames = $this->getAllMessages( $descending );
284 $statuses = $this->getCustomisedStatuses( $messageNames );
285
286 $count = 0;
287 foreach( $messageNames as $key ) {
288 $customised = isset( $statuses['pages'][$key] );
289 if( $customised !== $this->custom &&
290 ( $descending && ( $key < $offset || !$offset ) || !$descending && $key > $offset ) &&
291 ( ( $this->prefix && preg_match( $this->prefix, $key ) ) || $this->prefix === false )
292 ) {
293 $actual = wfMessage( $key )->inLanguage( $this->langcode )->plain();
294 $default = wfMessage( $key )->inLanguage( $this->langcode )->useDatabase( false )->plain();
295 $result->result[] = array(
296 'am_title' => $key,
297 'am_actual' => $actual,
298 'am_default' => $default,
299 'am_customised' => $customised,
300 'am_talk_exists' => isset( $statuses['talks'][$key] )
301 );
302 $count++;
303 }
304 if( $count == $limit ) {
305 break;
306 }
307 }
308 return $result;
309 }
310
311 function getStartBody() {
312 return Xml::openElement( 'table', array( 'class' => 'TablePager', 'id' => 'mw-allmessagestable' ) ) . "\n" .
313 "<thead><tr>
314 <th rowspan=\"2\">" .
315 wfMsg( 'allmessagesname' ) . "
316 </th>
317 <th>" .
318 wfMsg( 'allmessagesdefault' ) .
319 "</th>
320 </tr>\n
321 <tr>
322 <th>" .
323 wfMsg( 'allmessagescurrent' ) .
324 "</th>
325 </tr></thead><tbody>\n";
326 }
327
328 function formatValue( $field, $value ){
329 global $wgLang;
330 switch( $field ){
331
332 case 'am_title' :
333
334 $title = Title::makeTitle( NS_MEDIAWIKI, $value . $this->suffix );
335 $talk = Title::makeTitle( NS_MEDIAWIKI_TALK, $value . $this->suffix );
336
337 if( $this->mCurrentRow->am_customised ){
338 $title = $this->mSkin->linkKnown( $title, $wgLang->lcfirst( $value ) );
339 } else {
340 $title = $this->mSkin->link(
341 $title,
342 $wgLang->lcfirst( $value ),
343 array(),
344 array(),
345 array( 'broken' )
346 );
347 }
348 if ( $this->mCurrentRow->am_talk_exists ) {
349 $talk = $this->mSkin->linkKnown( $talk , $this->talk );
350 } else {
351 $talk = $this->mSkin->link(
352 $talk,
353 $this->talk,
354 array(),
355 array(),
356 array( 'broken' )
357 );
358 }
359 return $title . ' (' . $talk . ')';
360
361 case 'am_default' :
362 case 'am_actual' :
363 return Sanitizer::escapeHtmlAllowEntities( $value, ENT_QUOTES );
364 }
365 return '';
366 }
367
368 function formatRow( $row ){
369 // Do all the normal stuff
370 $s = parent::formatRow( $row );
371
372 // But if there's a customised message, add that too.
373 if( $row->am_customised ){
374 $s .= Xml::openElement( 'tr', $this->getRowAttrs( $row, true ) );
375 $formatted = strval( $this->formatValue( 'am_actual', $row->am_actual ) );
376 if ( $formatted == '' ) {
377 $formatted = '&#160;';
378 }
379 $s .= Xml::tags( 'td', $this->getCellAttrs( 'am_actual', $row->am_actual ), $formatted )
380 . "</tr>\n";
381 }
382 return $s;
383 }
384
385 function getRowAttrs( $row, $isSecond = false ){
386 $arr = array();
387 global $wgLang;
388 if( $row->am_customised ){
389 $arr['class'] = 'allmessages-customised';
390 }
391 if( !$isSecond ){
392 $arr['id'] = Sanitizer::escapeId( 'msg_' . $wgLang->lcfirst( $row->am_title ) );
393 }
394 return $arr;
395 }
396
397 function getCellAttrs( $field, $value ){
398 if( $this->mCurrentRow->am_customised && $field == 'am_title' ){
399 return array( 'rowspan' => '2', 'class' => $field );
400 } else {
401 return array( 'class' => $field );
402 }
403 }
404
405 // This is not actually used, as getStartBody is overridden above
406 function getFieldNames() {
407 return array(
408 'am_title' => wfMsg( 'allmessagesname' ),
409 'am_default' => wfMsg( 'allmessagesdefault' )
410 );
411 }
412
413 function getTitle() {
414 return SpecialPage::getTitleFor( 'Allmessages', false );
415 }
416
417 function isFieldSortable( $x ){
418 return false;
419 }
420
421 function getDefaultSort(){
422 return '';
423 }
424
425 function getQueryInfo(){
426 return '';
427 }
428 }
429