MediaWiki  master
AllMessagesTablePager.php
Go to the documentation of this file.
1 <?php
25 
33 
37  protected $langcode;
38 
42  protected $foreign;
43 
47  protected $prefix;
48 
52  protected $suffix;
53 
57  public $lang;
58 
62  public $custom;
63 
69  public function __construct( ?IContextSource $context, FormOptions $opts,
71  ) {
72  parent::__construct( $context, $linkRenderer );
73 
74  $this->mIndexField = 'am_title';
75  // FIXME: Why does this need to be set to DIR_DESCENDING to produce ascending ordering?
76  $this->mDefaultDirection = IndexPager::DIR_DESCENDING;
77 
78  $contLang = MediaWikiServices::getInstance()->getContentLanguage();
79  $this->lang = wfGetLangObj( $opts->getValue( 'lang' ) );
80 
81  $this->langcode = $this->lang->getCode();
82  $this->foreign = !$this->lang->equals( $contLang );
83 
84  $filter = $opts->getValue( 'filter' );
85  if ( $filter === 'all' ) {
86  $this->custom = null; // So won't match in either case
87  } else {
88  $this->custom = ( $filter === 'unmodified' );
89  }
90 
91  $prefix = $this->getLanguage()->ucfirst( $opts->getValue( 'prefix' ) );
92  $prefix = $prefix !== '' ?
93  Title::makeTitleSafe( NS_MEDIAWIKI, $opts->getValue( 'prefix' ) ) :
94  null;
95 
96  if ( $prefix !== null ) {
97  $displayPrefix = $prefix->getDBkey();
98  $this->prefix = '/^' . preg_quote( $displayPrefix, '/' ) . '/i';
99  } else {
100  $this->prefix = false;
101  }
102 
103  // The suffix that may be needed for message names if we're in a
104  // different language (eg [[MediaWiki:Foo/fr]]: $suffix = '/fr'
105  if ( $this->foreign ) {
106  $this->suffix = '/' . $this->langcode;
107  } else {
108  $this->suffix = '';
109  }
110  }
111 
112  function getAllMessages( $descending ) {
113  $messageNames = Language::getLocalisationCache()->getSubitemList( 'en', 'messages' );
114 
115  // Normalise message names so they look like page titles and sort correctly - T86139
116  $messageNames = array_map( [ $this->lang, 'ucfirst' ], $messageNames );
117 
118  if ( $descending ) {
119  rsort( $messageNames );
120  } else {
121  asort( $messageNames );
122  }
123 
124  return $messageNames;
125  }
126 
138  public static function getCustomisedStatuses( $messageNames, $langcode = 'en', $foreign = false ) {
139  // FIXME: This function should be moved to Language:: or something.
140 
141  $dbr = wfGetDB( DB_REPLICA );
142  $res = $dbr->select( 'page',
143  [ 'page_namespace', 'page_title' ],
144  [ 'page_namespace' => [ NS_MEDIAWIKI, NS_MEDIAWIKI_TALK ] ],
145  __METHOD__,
146  [ 'USE INDEX' => 'name_title' ]
147  );
148  $xNames = array_flip( $messageNames );
149 
150  $pageFlags = $talkFlags = [];
151 
152  foreach ( $res as $s ) {
153  $exists = false;
154 
155  if ( $foreign ) {
156  $titleParts = explode( '/', $s->page_title );
157  if ( count( $titleParts ) === 2 &&
158  $langcode === $titleParts[1] &&
159  isset( $xNames[$titleParts[0]] )
160  ) {
161  $exists = $titleParts[0];
162  }
163  } elseif ( isset( $xNames[$s->page_title] ) ) {
164  $exists = $s->page_title;
165  }
166 
167  $title = Title::newFromRow( $s );
168  if ( $exists && $title->inNamespace( NS_MEDIAWIKI ) ) {
169  $pageFlags[$exists] = true;
170  } elseif ( $exists && $title->inNamespace( NS_MEDIAWIKI_TALK ) ) {
171  $talkFlags[$exists] = true;
172  }
173  }
174 
175  return [ 'pages' => $pageFlags, 'talks' => $talkFlags ];
176  }
177 
186  function reallyDoQuery( $offset, $limit, $order ) {
187  $asc = ( $order === self::QUERY_ASCENDING );
188 
189  $messageNames = $this->getAllMessages( $order );
190  $statuses = self::getCustomisedStatuses( $messageNames, $this->langcode, $this->foreign );
191 
192  $rows = [];
193  $count = 0;
194  foreach ( $messageNames as $key ) {
195  $customised = isset( $statuses['pages'][$key] );
196  if ( $customised !== $this->custom &&
197  ( $asc && ( $key < $offset || !$offset ) || !$asc && $key > $offset ) &&
198  ( ( $this->prefix && preg_match( $this->prefix, $key ) ) || $this->prefix === false )
199  ) {
200  $actual = $this->msg( $key )->inLanguage( $this->lang )->plain();
201  $default = $this->msg( $key )->inLanguage( $this->lang )->useDatabase( false )->plain();
202  $rows[] = [
203  'am_title' => $key,
204  'am_actual' => $actual,
205  'am_default' => $default,
206  'am_customised' => $customised,
207  'am_talk_exists' => isset( $statuses['talks'][$key] )
208  ];
209  $count++;
210  }
211 
212  if ( $count === $limit ) {
213  break;
214  }
215  }
216 
217  return new FakeResultWrapper( $rows );
218  }
219 
220  protected function getStartBody() {
221  $tableClass = $this->getTableClass();
222  return Xml::openElement( 'table', [
223  'class' => "mw-datatable $tableClass",
224  'id' => 'mw-allmessagestable'
225  ] ) .
226  "\n" .
227  "<thead><tr>
228  <th rowspan=\"2\">" .
229  $this->msg( 'allmessagesname' )->escaped() . "
230  </th>
231  <th>" .
232  $this->msg( 'allmessagesdefault' )->escaped() .
233  "</th>
234  </tr>\n
235  <tr>
236  <th>" .
237  $this->msg( 'allmessagescurrent' )->escaped() .
238  "</th>
239  </tr></thead>\n";
240  }
241 
242  function getEndBody() {
243  return Html::closeElement( 'table' );
244  }
245 
246  function formatValue( $field, $value ) {
247  $linkRenderer = $this->getLinkRenderer();
248  switch ( $field ) {
249  case 'am_title' :
250  $title = Title::makeTitle( NS_MEDIAWIKI, $value . $this->suffix );
251  $talk = Title::makeTitle( NS_MEDIAWIKI_TALK, $value . $this->suffix );
252  $translation = Linker::makeExternalLink(
253  'https://translatewiki.net/w/i.php?' . wfArrayToCgi( [
254  'title' => 'Special:SearchTranslations',
255  'group' => 'mediawiki',
256  'grouppath' => 'mediawiki',
257  'language' => $this->getLanguage()->getCode(),
258  'query' => $value . ' ' . $this->msg( $value )->plain()
259  ] ),
260  $this->msg( 'allmessages-filter-translate' )->text()
261  );
262  $talkLink = $this->msg( 'talkpagelinktext' )->escaped();
263 
264  if ( $this->mCurrentRow->am_customised ) {
265  $title = $linkRenderer->makeKnownLink( $title, $this->getLanguage()->lcfirst( $value ) );
266  } else {
268  $title, $this->getLanguage()->lcfirst( $value )
269  );
270  }
271  if ( $this->mCurrentRow->am_talk_exists ) {
272  $talk = $linkRenderer->makeKnownLink( $talk, $talkLink );
273  } else {
274  $talk = $linkRenderer->makeBrokenLink(
275  $talk,
276  $talkLink
277  );
278  }
279 
280  return $title . ' ' .
281  $this->msg( 'parentheses' )->rawParams( $talk )->escaped() .
282  ' ' .
283  $this->msg( 'parentheses' )->rawParams( $translation )->escaped();
284 
285  case 'am_default' :
286  case 'am_actual' :
287  return Sanitizer::escapeHtmlAllowEntities( $value );
288  }
289 
290  return '';
291  }
292 
297  function formatRow( $row ) {
298  // Do all the normal stuff
299  $s = parent::formatRow( $row );
300 
301  // But if there's a customised message, add that too.
302  if ( $row->am_customised ) {
303  $s .= Html::openElement( 'tr', $this->getRowAttrs( $row ) );
304  $formatted = strval( $this->formatValue( 'am_actual', $row->am_actual ) );
305 
306  if ( $formatted === '' ) {
307  $formatted = "\u{00A0}";
308  }
309 
310  $s .= Html::element( 'td', $this->getCellAttrs( 'am_actual', $row->am_actual ), $formatted )
311  . Html::closeElement( 'tr' );
312  }
313 
314  return Html::rawElement( 'tbody', [], $s );
315  }
316 
317  function getRowAttrs( $row ) {
318  return [];
319  }
320 
326  function getCellAttrs( $field, $value ) {
327  $attr = [];
328  if ( $field === 'am_title' ) {
329  if ( $this->mCurrentRow->am_customised ) {
330  $attr += [ 'rowspan' => '2' ];
331  }
332  } else {
333  $attr += [
334  'lang' => $this->lang->getHtmlCode(),
335  'dir' => $this->lang->getDir(),
336  ];
337  if ( $this->mCurrentRow->am_customised ) {
338  // CSS class: am_default, am_actual
339  $attr += [ 'class' => $field ];
340  }
341  }
342  return $attr;
343  }
344 
345  // This is not actually used, as getStartBody is overridden above
346  function getFieldNames() {
347  return [
348  'am_title' => $this->msg( 'allmessagesname' )->text(),
349  'am_default' => $this->msg( 'allmessagesdefault' )->text()
350  ];
351  }
352 
353  function getTitle() {
354  return SpecialPage::getTitleFor( 'Allmessages', false );
355  }
356 
357  function isFieldSortable( $x ) {
358  return false;
359  }
360 
361  function getDefaultSort() {
362  return '';
363  }
364 
365  function getQueryInfo() {
366  return [];
367  }
368 
369 }
const DIR_DESCENDING
Backwards-compatible constant for $mDefaultDirection field (do not change)
Definition: IndexPager.php:76
Helper class to keep track of options when mixing links and form elements.
Definition: FormOptions.php:35
Table-based display with a user-selectable sort order.
Definition: TablePager.php:30
static getLocalisationCache()
Get the LocalisationCache instance.
Definition: Language.php:420
static element( $element, $attribs=[], $contents='')
Identical to rawElement(), but HTML-escapes $contents (like Xml::element()).
Definition: Html.php:231
makeBrokenLink(LinkTarget $target, $text=null, array $extraAttribs=[], array $query=[])
static openElement( $element, $attribs=[])
Identical to rawElement(), but has no third parameter and omits the end tag (and the self-closing &#39;/&#39;...
Definition: Html.php:251
static rawElement( $element, $attribs=[], $contents='')
Returns an HTML element in a string.
Definition: Html.php:209
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
LinkRenderer $linkRenderer
Definition: IndexPager.php:162
static newFromRow( $row)
Make a Title object from a DB row.
Definition: Title.php:518
IContextSource $context
static getCustomisedStatuses( $messageNames, $langcode='en', $foreign=false)
Determine which of the MediaWiki and MediaWiki_talk namespace pages exist.
wfGetLangObj( $langcode=false)
Return a Language object from $langcode.
wfArrayToCgi( $array1, $array2=null, $prefix='')
This function takes one or two arrays as input, and returns a CGI-style string, e.g.
Class that generates HTML links for pages.
static openElement( $element, $attribs=null)
This opens an XML element.
Definition: Xml.php:108
__construct(?IContextSource $context, FormOptions $opts, LinkRenderer $linkRenderer)
const NS_MEDIAWIKI_TALK
Definition: Defines.php:69
reallyDoQuery( $offset, $limit, $order)
This function normally does a database query to get the results; we need to make a pretend result usi...
static closeElement( $element)
Returns "</$element>".
Definition: Html.php:315
static makeExternalLink( $url, $text, $escape=true, $linktype='', $attribs=[], $title=null)
Make an external link.
Definition: Linker.php:848
makeKnownLink(LinkTarget $target, $text=null, array $extraAttribs=[], array $query=[])
$filter
static getTitleFor( $name, $subpage=false, $fragment='')
Get a localised Title object for a specified special page name If you don&#39;t need a full Title object...
Definition: SpecialPage.php:83
const NS_MEDIAWIKI
Definition: Defines.php:68
getTableClass()
TablePager relies on mw-datatable for styling, see T214208.
Definition: TablePager.php:275
static makeTitleSafe( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:612
static makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:586
msg( $key,... $params)
Get a Message object with context set Parameters are the same as wfMessage()
Overloads the relevant methods of the real ResultsWrapper so it doesn&#39;t go anywhere near an actual da...
static escapeHtmlAllowEntities( $html)
Given HTML input, escape with htmlspecialchars but un-escape entities.
Definition: Sanitizer.php:1433
const DB_REPLICA
Definition: defines.php:25
getValue( $name)
Get the value for the given option name.
Use TablePager for prettified output.