MediaWiki master
AllMessagesTablePager.php
Go to the documentation of this file.
1<?php
9
23use stdClass;
28
36
40 protected $foreign;
41
45 protected $prefix;
46
50 protected $suffix;
51
55 public $lang;
56
60 public $custom;
61
62 public function __construct(
63 IContextSource $context,
64 Language $contentLanguage,
65 LanguageFactory $languageFactory,
66 LinkRenderer $linkRenderer,
67 IConnectionProvider $dbProvider,
68 private readonly LocalisationCache $localisationCache,
69 FormOptions $opts,
70 ) {
71 // Set database before parent constructor to avoid setting it there
72 $this->mDb = $dbProvider->getReplicaDatabase();
73 parent::__construct( $context, $linkRenderer );
74
75 $this->mIndexField = 'am_title';
76 // FIXME: Why does this need to be set to DIR_DESCENDING to produce ascending ordering?
77 $this->mDefaultDirection = IndexPager::DIR_DESCENDING;
78
79 $this->lang = $languageFactory->getRawLanguage( $opts->getValue( 'lang' ) );
80
81 $this->foreign = !$this->lang->equals( $contentLanguage );
82
83 $filter = $opts->getValue( 'filter' );
84 if ( $filter === 'all' ) {
85 $this->custom = null; // So won't match in either case
86 } else {
87 $this->custom = ( $filter === 'unmodified' );
88 }
89
90 $prefix = $this->getLanguage()->ucfirst( $opts->getValue( 'prefix' ) );
91 $prefix = $prefix !== '' ?
92 Title::makeTitleSafe( NS_MEDIAWIKI, $opts->getValue( 'prefix' ) ) :
93 null;
94
95 if ( $prefix !== null ) {
96 $displayPrefix = $prefix->getDBkey();
97 $this->prefix = '/^' . preg_quote( $displayPrefix, '/' ) . '/i';
98 } else {
99 $this->prefix = false;
100 }
101
102 // The suffix that may be needed for message names if we're in a
103 // different language (eg [[MediaWiki:Foo/fr]]: $suffix = '/fr'
104 if ( $this->foreign ) {
105 $this->suffix = '/' . $this->lang->getCode();
106 } else {
107 $this->suffix = '';
108 }
109 }
110
111 private function getAllMessages( bool $descending ): array {
112 $messageNames = $this->localisationCache->getSubitemList( 'en', 'messages' );
113
114 // Normalise message names so they look like page titles and sort correctly - T86139
115 $messageNames = array_map( $this->lang->ucfirst( ... ), $messageNames );
116
117 if ( $descending ) {
118 rsort( $messageNames );
119 } else {
120 asort( $messageNames );
121 }
122
123 return $messageNames;
124 }
125
140 public static function getCustomisedStatuses(
141 $messageNames,
142 $langcode = 'en',
143 $foreign = false,
144 ?IReadableDatabase $dbr = null
145 ) {
146 // FIXME: This function should be moved to Language:: or something.
147 // Fallback to global state, if not provided
148 $dbr ??= MediaWikiServices::getInstance()->getConnectionProvider()->getReplicaDatabase();
149 $res = $dbr->newSelectQueryBuilder()
150 ->select( [ 'page_namespace', 'page_title' ] )
151 ->from( 'page' )
152 ->where( [ 'page_namespace' => [ NS_MEDIAWIKI, NS_MEDIAWIKI_TALK ] ] )
153 ->useIndex( 'page_name_title' )
154 ->caller( __METHOD__ )->fetchResultSet();
155 $xNames = array_fill_keys( $messageNames, true );
156
157 $pageFlags = $talkFlags = [];
158
159 foreach ( $res as $s ) {
160 $exists = false;
161
162 if ( $foreign ) {
163 $titleParts = explode( '/', $s->page_title );
164 if ( count( $titleParts ) === 2 &&
165 $langcode === $titleParts[1] &&
166 isset( $xNames[$titleParts[0]] )
167 ) {
168 $exists = $titleParts[0];
169 }
170 } elseif ( isset( $xNames[$s->page_title] ) ) {
171 $exists = $s->page_title;
172 }
173
174 $title = Title::newFromRow( $s );
175 if ( $exists && $title->inNamespace( NS_MEDIAWIKI ) ) {
176 $pageFlags[$exists] = true;
177 } elseif ( $exists && $title->inNamespace( NS_MEDIAWIKI_TALK ) ) {
178 $talkFlags[$exists] = true;
179 }
180 }
181
182 return [ 'pages' => $pageFlags, 'talks' => $talkFlags ];
183 }
184
193 public function reallyDoQuery( $offset, $limit, $order ) {
194 $asc = ( $order === self::QUERY_ASCENDING );
195
196 $messageNames = $this->getAllMessages( $order );
197 $statuses = self::getCustomisedStatuses(
198 $messageNames,
199 $this->lang->getCode(),
200 $this->foreign,
201 $this->getDatabase()
202 );
203
204 $rows = [];
205 $count = 0;
206 foreach ( $messageNames as $key ) {
207 $customised = isset( $statuses['pages'][$key] );
208 if ( $customised !== $this->custom &&
209 ( ( $asc && ( $key < $offset || !$offset ) ) || ( !$asc && $key > $offset ) ) &&
210 ( ( $this->prefix && preg_match( $this->prefix, $key ) ) || $this->prefix === false )
211 ) {
212 $actual = $this->msg( $key )->inLanguage( $this->lang )->plain();
213 $default = $this->msg( $key )->inLanguage( $this->lang )->useDatabase( false )->plain();
214 $rows[] = [
215 'am_title' => $key,
216 'am_actual' => $actual,
217 'am_default' => $default,
218 'am_customised' => $customised,
219 'am_talk_exists' => isset( $statuses['talks'][$key] )
220 ];
221 $count++;
222 }
223
224 if ( $count === $limit ) {
225 break;
226 }
227 }
228
229 return new FakeResultWrapper( $rows );
230 }
231
233 protected function getStartBody() {
234 return Html::openElement( 'table', [
235 'class' => $this->getTableClass(),
236 'id' => 'mw-allmessagestable'
237 ] ) .
238 "\n" .
239 "<thead><tr>
240 <th rowspan=\"2\">" .
241 $this->msg( 'allmessagesname' )->escaped() . "
242 </th>
243 <th>" .
244 $this->msg( 'allmessagesdefault' )->escaped() .
245 "</th>
246 </tr>\n
247 <tr>
248 <th>" .
249 $this->msg( 'allmessagescurrent' )->escaped() .
250 "</th>
251 </tr></thead>\n";
252 }
253
255 protected function getEndBody() {
256 return Html::closeElement( 'table' );
257 }
258
264 public function formatValue( $field, $value ) {
265 $linkRenderer = $this->getLinkRenderer();
266 switch ( $field ) {
267 case 'am_title':
268 $title = Title::makeTitle( NS_MEDIAWIKI, $value . $this->suffix );
269 $talk = Title::makeTitle( NS_MEDIAWIKI_TALK, $value . $this->suffix );
270 $message = $this->msg( $value )->inLanguage( $this->lang )->useDatabase( false )->plain();
271 $translation = $linkRenderer->makeExternalLink(
272 'https://translatewiki.net/w/i.php?' . wfArrayToCgi( [
273 'title' => 'Special:SearchTranslations',
274 'group' => 'mediawiki',
275 'grouppath' => 'mediawiki',
276 'language' => $this->lang->getCode(),
277 'query' => $value . ' ' . $message
278 ] ),
279 $this->msg( 'allmessages-filter-translate' ),
280 $this->getTitle()
281 );
282 $talkLink = $this->msg( 'talkpagelinktext' )->text();
283
284 if ( $this->mCurrentRow->am_customised ) {
285 $title = $linkRenderer->makeKnownLink( $title, $this->getLanguage()->lcfirst( $value ) );
286 } else {
287 $title = $linkRenderer->makeBrokenLink(
288 $title, $this->getLanguage()->lcfirst( $value )
289 );
290 }
291 if ( $this->mCurrentRow->am_talk_exists ) {
292 $talk = $linkRenderer->makeKnownLink( $talk, $talkLink );
293 } else {
294 $talk = $linkRenderer->makeBrokenLink(
295 $talk,
296 $talkLink
297 );
298 }
299
300 return $title . ' ' .
301 $this->msg( 'parentheses' )->rawParams( $talk )->escaped() .
302 ' ' .
303 $this->msg( 'parentheses' )->rawParams( $translation )->escaped();
304
305 case 'am_default':
306 case 'am_actual':
307 return Sanitizer::escapeHtmlAllowEntities( $value );
308 }
309
310 return '';
311 }
312
317 public function formatRow( $row ) {
318 // Do all the normal stuff
319 $s = parent::formatRow( $row );
320
321 // But if there's a customised message, add that too.
322 if ( $row->am_customised ) {
323 $s .= Html::openElement( 'tr', $this->getRowAttrs( $row ) );
324 $formatted = strval( $this->formatValue( 'am_actual', $row->am_actual ) );
325
326 if ( $formatted === '' ) {
327 $formatted = "\u{00A0}";
328 }
329
330 $s .= Html::rawElement( 'td', $this->getCellAttrs( 'am_actual', $row->am_actual ), $formatted )
331 . Html::closeElement( 'tr' );
332 }
333
334 return Html::rawElement( 'tbody', [], $s );
335 }
336
338 protected function getRowAttrs( $row ) {
339 return [];
340 }
341
347 protected function getCellAttrs( $field, $value ) {
348 $attr = [];
349 if ( $field === 'am_title' ) {
350 if ( $this->mCurrentRow->am_customised ) {
351 $attr += [ 'rowspan' => '2' ];
352 }
353 } else {
354 $attr += [
355 'lang' => $this->lang->getHtmlCode(),
356 'dir' => $this->lang->getDir(),
357 ];
358 if ( $this->mCurrentRow->am_customised ) {
359 // CSS class: am_default, am_actual
360 $attr += [ 'class' => $field ];
361 }
362 }
363 return $attr;
364 }
365
367 protected function getFieldNames() {
368 // This is not actually used, as getStartBody is overridden above
369 return [
370 'am_title' => $this->msg( 'allmessagesname' )->text(),
371 'am_default' => $this->msg( 'allmessagesdefault' )->text()
372 ];
373 }
374
376 public function getTitle() {
377 return SpecialPage::getTitleFor( 'Allmessages', false );
378 }
379
381 protected function isFieldSortable( $x ) {
382 return false;
383 }
384
386 public function getDefaultSort() {
387 return '';
388 }
389
391 public function getQueryInfo() {
392 return [];
393 }
394
395}
396
401class_alias( AllMessagesTablePager::class, 'AllMessagesTablePager' );
402
404class_alias( AllMessagesTablePager::class, 'MediaWiki\\Pager\\AllMessagesTablePager' );
const NS_MEDIAWIKI_TALK
Definition Defines.php:60
const NS_MEDIAWIKI
Definition Defines.php:59
wfArrayToCgi( $array1, $array2=null, $prefix='')
This function takes one or two arrays as input, and returns a CGI-style string, e....
makeTitle( $linkId)
Convert a link ID to a Title.to override Title
Helper class to keep track of options when mixing links and form elements.
getValue( $name)
Get the value for the given option name.
This class is a collection of static functions that serve two purposes:
Definition Html.php:44
Internationalisation code See https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation for more...
getRawLanguage( $code)
Get a cached or new language object for a given language code without normalization of the language c...
Base class for language-specific code.
Definition Language.php:65
Caching for the contents of localisation files.
Class that generates HTML for internal links.
Service locator for MediaWiki core services.
static getInstance()
Returns the global default instance of the top level service locator.
Efficient paging for SQL queries that use a (roughly unique) index.
Table-based display with a user-selectable sort order.
HTML sanitizer for MediaWiki.
Definition Sanitizer.php:34
Parent class for all special pages.
static getTitleFor( $name, $subpage=false, $fragment='')
Get a localised Title object for a specified special page name If you don't need a full Title object,...
getRowAttrs( $row)
Get attributes to be applied to the given row.to overridearray Array of attribute => value
getQueryInfo()
Provides all parameters needed for the main paged query.It returns an associative array with the foll...
getDefaultSort()
The database field name used as a default sort order.Note that this field will only be sorted on if i...
static getCustomisedStatuses( $messageNames, $langcode='en', $foreign=false, ?IReadableDatabase $dbr=null)
Determine which of the MediaWiki and MediaWiki_talk namespace pages exist.
isFieldSortable( $x)
Return true if the named field should be sortable by the UI, false otherwise.bool
reallyDoQuery( $offset, $limit, $order)
This function normally does a database query to get the results; we need to make a pretend result usi...
getFieldNames()
An array mapping database field names to a textual description of the field name, for use in the tabl...
__construct(IContextSource $context, Language $contentLanguage, LanguageFactory $languageFactory, LinkRenderer $linkRenderer, IConnectionProvider $dbProvider, private readonly LocalisationCache $localisationCache, FormOptions $opts,)
Represents a title within MediaWiki.
Definition Title.php:69
Overloads the relevant methods of the real ResultWrapper so it doesn't go anywhere near an actual dat...
Interface for objects which can provide a MediaWiki context on request.
Provide primary and replica IDatabase connections.
getReplicaDatabase(string|false $domain=false, $group=null)
Get connection to a replica database.
A database connection without write operations.
Result wrapper for grabbing data queried from an IDatabase object.