MediaWiki REL1_41
TablePager.php
Go to the documentation of this file.
1<?php
21namespace MediaWiki\Pager;
22
26use ParserOutput;
27use stdClass;
28use XmlSelect;
29
36abstract class TablePager extends IndexPager {
38 protected $mSort;
39
41 protected $mCurrentRow;
42
49 public function __construct( IContextSource $context = null, LinkRenderer $linkRenderer = null ) {
50 if ( $context ) {
51 $this->setContext( $context );
52 }
53
54 $this->mSort = $this->getRequest()->getText( 'sort' );
55 if ( !array_key_exists( $this->mSort, $this->getFieldNames() )
56 || !$this->isFieldSortable( $this->mSort )
57 ) {
58 $this->mSort = $this->getDefaultSort();
59 }
60 if ( $this->getRequest()->getBool( 'asc' ) ) {
61 $this->mDefaultDirection = IndexPager::DIR_ASCENDING;
62 } elseif ( $this->getRequest()->getBool( 'desc' ) ) {
63 $this->mDefaultDirection = IndexPager::DIR_DESCENDING;
64 } /* Else leave it at whatever the class default is */
65
66 // Parent constructor needs mSort set, so we call it last
67 parent::__construct( null, $linkRenderer );
68 }
69
85 final public function getBody() {
86 wfDeprecated( __METHOD__, '1.24' );
87 return parent::getBody();
88 }
89
99 public function getBodyOutput() {
100 $body = parent::getBody();
101
102 $pout = new ParserOutput;
103 $pout->setText( $body );
104 return $pout;
105 }
106
116 public function getFullOutput() {
117 $navigation = $this->getNavigationBar();
118 $body = parent::getBody();
119
120 $pout = new ParserOutput;
121 $pout->setText( $navigation . $body . $navigation );
122 $pout->addModuleStyles( $this->getModuleStyles() );
123 return $pout;
124 }
125
130 protected function getStartBody() {
131 $sortClass = $this->getSortHeaderClass();
132
133 $s = '';
134 $fields = $this->getFieldNames();
135
136 // Make table header
137 foreach ( $fields as $field => $name ) {
138 if ( strval( $name ) == '' ) {
139 $s .= Html::rawElement( 'th', [], "\u{00A0}" ) . "\n";
140 } elseif ( $this->isFieldSortable( $field ) ) {
141 $query = [ 'sort' => $field, 'limit' => $this->mLimit ];
142 $linkType = null;
143 $class = null;
144
145 if ( $this->mSort == $field ) {
146 // The table is sorted by this field already, make a link to sort in the other direction
147 // We don't actually know in which direction other fields will be sorted by default…
148 if ( $this->mDefaultDirection == IndexPager::DIR_DESCENDING ) {
149 $linkType = 'asc';
150 $class = "$sortClass mw-datatable-is-sorted mw-datatable-is-descending";
151 $query['asc'] = '1';
152 $query['desc'] = '';
153 } else {
154 $linkType = 'desc';
155 $class = "$sortClass mw-datatable-is-sorted mw-datatable-is-ascending";
156 $query['asc'] = '';
157 $query['desc'] = '1';
158 }
159 }
160
161 $link = $this->makeLink( htmlspecialchars( $name ), $query, $linkType );
162 $s .= Html::rawElement( 'th', [ 'class' => $class ], $link ) . "\n";
163 } else {
164 $s .= Html::element( 'th', [], $name ) . "\n";
165 }
166 }
167
168 $ret = Html::openElement( 'table', [
169 'class' => $this->getTableClass() ]
170 );
171 $ret .= Html::rawElement( 'thead', [], Html::rawElement( 'tr', [], "\n" . $s . "\n" ) );
172 $ret .= Html::openElement( 'tbody' ) . "\n";
173
174 return $ret;
175 }
176
181 protected function getEndBody() {
182 return "</tbody></table>\n";
183 }
184
188 protected function getEmptyBody() {
189 $colspan = count( $this->getFieldNames() );
190 $msgEmpty = $this->msg( 'table_pager_empty' )->text();
191 return Html::rawElement( 'tr', [],
192 Html::element( 'td', [ 'colspan' => $colspan ], $msgEmpty ) );
193 }
194
200 public function formatRow( $row ) {
201 $this->mCurrentRow = $row; // In case formatValue etc need to know
202 $s = Html::openElement( 'tr', $this->getRowAttrs( $row ) ) . "\n";
203 $fieldNames = $this->getFieldNames();
204
205 foreach ( $fieldNames as $field => $name ) {
206 $value = $row->$field ?? null;
207 $formatted = strval( $this->formatValue( $field, $value ) );
208
209 if ( $formatted == '' ) {
210 $formatted = "\u{00A0}";
211 }
212
213 $s .= Html::rawElement( 'td', $this->getCellAttrs( $field, $value ), $formatted ) . "\n";
214 }
215
216 $s .= Html::closeElement( 'tr' ) . "\n";
217
218 return $s;
219 }
220
229 protected function getRowClass( $row ) {
230 return '';
231 }
232
241 protected function getRowAttrs( $row ) {
242 return [ 'class' => $this->getRowClass( $row ) ];
243 }
244
248 protected function getCurrentRow() {
249 return $this->mCurrentRow;
250 }
251
263 protected function getCellAttrs( $field, $value ) {
264 return [ 'class' => 'TablePager_col_' . $field ];
265 }
266
271 public function getIndexField() {
272 return $this->mSort;
273 }
274
281 protected function getTableClass() {
282 return 'mw-datatable';
283 }
284
289 protected function getNavClass() {
290 return 'TablePager_nav';
291 }
292
297 protected function getSortHeaderClass() {
298 return 'TablePager_sort';
299 }
300
307 public function getNavigationBar() {
308 if ( !$this->isNavigationBarShown() ) {
309 return '';
310 }
311
312 $this->getOutput()->enableOOUI();
313
314 $types = [ 'first', 'prev', 'next', 'last' ];
315
316 $queries = $this->getPagingQueries();
317
318 $buttons = [];
319
320 $title = $this->getTitle();
321
322 foreach ( $types as $type ) {
323 $buttons[] = new \OOUI\ButtonWidget( [
324 // Messages used here:
325 // * table_pager_first
326 // * table_pager_prev
327 // * table_pager_next
328 // * table_pager_last
329 'classes' => [ 'TablePager-button-' . $type ],
330 'flags' => [ 'progressive' ],
331 'framed' => false,
332 'label' => $this->msg( 'table_pager_' . $type )->text(),
333 'href' => $queries[ $type ] ?
334 $title->getLinkURL( $queries[ $type ] + $this->getDefaultQuery() ) :
335 null,
336 'icon' => $type === 'prev' ? 'previous' : $type,
337 'disabled' => $queries[ $type ] === false
338 ] );
339 }
340 return new \OOUI\ButtonGroupWidget( [
341 'classes' => [ $this->getNavClass() ],
342 'items' => $buttons,
343 ] );
344 }
345
349 public function getModuleStyles() {
350 return array_merge(
351 parent::getModuleStyles(), [ 'oojs-ui.styles.icons-movement' ]
352 );
353 }
354
361 public function getLimitSelect( $attribs = [] ) {
362 $select = new XmlSelect( 'limit', false, $this->mLimit );
363 $select->addOptions( $this->getLimitSelectList() );
364 foreach ( $attribs as $name => $value ) {
365 $select->setAttribute( $name, $value );
366 }
367 return $select->getHTML();
368 }
369
377 public function getLimitSelectList() {
378 # Add the current limit from the query string
379 # to avoid that the limit is lost after clicking Go next time
380 if ( !in_array( $this->mLimit, $this->mLimitsShown ) ) {
381 $this->mLimitsShown[] = $this->mLimit;
382 sort( $this->mLimitsShown );
383 }
384 $ret = [];
385 foreach ( $this->mLimitsShown as $key => $value ) {
386 # The pair is either $index => $limit, in which case the $value
387 # will be numeric, or $limit => $text, in which case the $value
388 # will be a string.
389 if ( is_int( $value ) ) {
390 $limit = $value;
391 $text = $this->getLanguage()->formatNum( $limit );
392 } else {
393 $limit = $key;
394 $text = $value;
395 }
396 $ret[$text] = $limit;
397 }
398 return $ret;
399 }
400
410 public function getHiddenFields( $noResubmit = [] ) {
411 $noResubmit = (array)$noResubmit;
412 $query = $this->getRequest()->getQueryValues();
413 foreach ( $noResubmit as $name ) {
414 unset( $query[$name] );
415 }
416 $s = '';
417 foreach ( $query as $name => $value ) {
418 if ( is_array( $value ) ) {
419 // Per WebRequest::getVal: Array values are discarded for security reasons.
420 continue;
421 }
422 $s .= Html::hidden( $name, $value ) . "\n";
423 }
424 return $s;
425 }
426
432 public function getLimitForm() {
433 return Html::rawElement(
434 'form',
435 [
436 'method' => 'get',
437 'action' => wfScript(),
438 ],
439 "\n" . $this->getLimitDropdown()
440 ) . "\n";
441 }
442
448 private function getLimitDropdown() {
449 # Make the select with some explanatory text
450 $msgSubmit = $this->msg( 'table_pager_limit_submit' )->escaped();
451
452 return $this->msg( 'table_pager_limit' )
453 ->rawParams( $this->getLimitSelect() )->escaped() .
454 "\n<input type=\"submit\" value=\"$msgSubmit\"/>\n" .
455 $this->getHiddenFields( [ 'limit' ] );
456 }
457
465 abstract protected function isFieldSortable( $field );
466
478 abstract public function formatValue( $name, $value );
479
489 abstract public function getDefaultSort();
490
498 abstract protected function getFieldNames();
499}
500
505class_alias( TablePager::class, 'TablePager' );
wfScript( $script='index')
Get the URL path to a MediaWiki entry point.
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that a deprecated feature was used.
msg( $key,... $params)
Get a Message object with context set Parameters are the same as wfMessage()
setContext(IContextSource $context)
This class is a collection of static functions that serve two purposes:
Definition Html.php:57
Class that generates HTML for internal links.
Efficient paging for SQL queries that use a (roughly unique) index.
int $mLimit
The maximum number of entries to show.
const DIR_ASCENDING
Backwards-compatible constant for $mDefaultDirection field (do not change)
const DIR_DESCENDING
Backwards-compatible constant for $mDefaultDirection field (do not change)
isNavigationBarShown()
Returns whether to show the "navigation bar".
getPagingQueries()
Get a URL query array for the prev, next, first and last links.
makeLink( $text, array $query=null, $type=null)
Make a self-link.
Table-based display with a user-selectable sort order.
getModuleStyles()
ResourceLoader modules that must be loaded to provide correct styling for this pager....
getRowClass( $row)
Get a class name to be applied to the given row.
formatValue( $name, $value)
Format a table cell.
__construct(IContextSource $context=null, LinkRenderer $linkRenderer=null)
isFieldSortable( $field)
Return true if the named field should be sortable by the UI, false otherwise.
getTableClass()
TablePager relies on mw-datatable for styling, see T214208.
getNavigationBar()
A navigation bar with images.
getHiddenFields( $noResubmit=[])
Get <input type="hidden"> elements for use in a method="get" form.
getLimitSelectList()
Get a list of items to show in a "<select>" element of limits.
getFieldNames()
An array mapping database field names to a textual description of the field name, for use in the tabl...
getLimitForm()
Get a form containing a limit selection dropdown.
getBody()
Get the formatted result list.
getLimitSelect( $attribs=[])
Get a "<select>" element which has options for each of the allowed limits.
getRowAttrs( $row)
Get attributes to be applied to the given row.
getBodyOutput()
Get the formatted result list.
getDefaultSort()
The database field name used as a default sort order.
getFullOutput()
Get the formatted result list, with navigation bars.
getCellAttrs( $field, $value)
Get any extra attributes to be applied to the given cell.
getIndexField()
Returns the name of the index field.If the pager supports multiple orders, it may return an array of ...
Rendered output of a wiki page, as parsed from wikitext.
setText( $text)
Set the text of the ParserOutput.
Class for generating HTML <select> or <datalist> elements.
Definition XmlSelect.php:28
Interface for objects which can provide a MediaWiki context on request.