MediaWiki master
TablePager.php
Go to the documentation of this file.
1<?php
7namespace MediaWiki\Pager;
8
14use OOUI\ButtonGroupWidget;
15use OOUI\ButtonWidget;
16use stdClass;
17
24abstract class TablePager extends IndexPager {
26 protected $mSort;
27
29 protected $mCurrentRow;
30
37 public function __construct( ?IContextSource $context = null, ?LinkRenderer $linkRenderer = null ) {
38 if ( $context ) {
39 $this->setContext( $context );
40 }
41
42 $this->mSort = $this->getRequest()->getText( 'sort' );
43 if ( !array_key_exists( $this->mSort, $this->getFieldNames() )
44 || !$this->isFieldSortable( $this->mSort )
45 ) {
46 $this->mSort = $this->getDefaultSort();
47 }
48 if ( $this->getRequest()->getBool( 'asc' ) ) {
49 $this->mDefaultDirection = IndexPager::DIR_ASCENDING;
50 } elseif ( $this->getRequest()->getBool( 'desc' ) ) {
51 $this->mDefaultDirection = IndexPager::DIR_DESCENDING;
52 } /* Else leave it at whatever the class default is */
53
54 // Parent constructor needs mSort set, so we call it last
55 parent::__construct( null, $linkRenderer );
56 }
57
67 public function getBodyOutput() {
68 $body = parent::getBody();
69
70 $pout = new ParserOutput;
71 $pout->setRawText( $body );
72 return $pout;
73 }
74
84 public function getFullOutput() {
85 $navigation = $this->getNavigationBar();
86 $body = parent::getBody();
87
88 $pout = new ParserOutput;
89 $pout->setRawText( $navigation . $body . $navigation );
90 $pout->addModuleStyles( $this->getModuleStyles() );
91 return $pout;
92 }
93
98 protected function getStartBody() {
99 $sortClass = $this->getSortHeaderClass();
100
101 $s = '';
102 $fields = $this->getFieldNames();
103
104 // Make table header
105 foreach ( $fields as $field => $name ) {
106 if ( strval( $name ) == '' ) {
107 $s .= Html::rawElement( 'th', [], "\u{00A0}" ) . "\n";
108 } elseif ( $this->isFieldSortable( $field ) ) {
109 $query = [ 'sort' => $field, 'limit' => $this->mLimit ];
110 $linkType = null;
111 $class = null;
112
113 if ( $this->mSort == $field ) {
114 // The table is sorted by this field already, make a link to sort in the other direction
115 // We don't actually know in which direction other fields will be sorted by default…
116 if ( $this->mDefaultDirection == IndexPager::DIR_DESCENDING ) {
117 $linkType = 'asc';
118 $class = "$sortClass mw-datatable-is-sorted mw-datatable-is-descending";
119 $query['asc'] = '1';
120 $query['desc'] = '';
121 } else {
122 $linkType = 'desc';
123 $class = "$sortClass mw-datatable-is-sorted mw-datatable-is-ascending";
124 $query['asc'] = '';
125 $query['desc'] = '1';
126 }
127 }
128
129 $link = $this->makeLink( htmlspecialchars( $name ), $query, $linkType );
130 $s .= Html::rawElement( 'th', [ 'class' => $class ], $link ) . "\n";
131 } else {
132 $s .= Html::element( 'th', [], $name ) . "\n";
133 }
134 }
135
136 $ret = Html::openElement( 'table', [
137 'class' => $this->getTableClass() ]
138 );
139 $ret .= Html::rawElement( 'thead', [], Html::rawElement( 'tr', [], "\n" . $s . "\n" ) );
140 $ret .= Html::openElement( 'tbody' ) . "\n";
141
142 return $ret;
143 }
144
149 protected function getEndBody() {
150 return "</tbody></table>\n";
151 }
152
156 protected function getEmptyBody() {
157 $colspan = count( $this->getFieldNames() );
158 $msgEmpty = $this->msg( 'table_pager_empty' )->text();
159 return Html::rawElement( 'tr', [],
160 Html::element( 'td', [ 'colspan' => $colspan ], $msgEmpty ) );
161 }
162
168 public function formatRow( $row ) {
169 $this->mCurrentRow = $row; // In case formatValue etc need to know
170 $s = Html::openElement( 'tr', $this->getRowAttrs( $row ) ) . "\n";
171 $fieldNames = $this->getFieldNames();
172
173 foreach ( $fieldNames as $field => $name ) {
174 $value = $row->$field ?? null;
175 $formatted = strval( $this->formatValue( $field, $value ) );
176
177 if ( $formatted == '' ) {
178 $formatted = "\u{00A0}";
179 }
180
181 $s .= Html::rawElement( 'td', $this->getCellAttrs( $field, $value ), $formatted ) . "\n";
182 }
183
184 $s .= Html::closeElement( 'tr' ) . "\n";
185
186 return $s;
187 }
188
197 protected function getRowClass( $row ) {
198 return '';
199 }
200
209 protected function getRowAttrs( $row ) {
210 return [ 'class' => $this->getRowClass( $row ) ];
211 }
212
216 protected function getCurrentRow() {
217 return $this->mCurrentRow;
218 }
219
231 protected function getCellAttrs( $field, $value ) {
232 return [ 'class' => 'TablePager_col_' . $field ];
233 }
234
239 public function getIndexField() {
240 return $this->mSort;
241 }
242
249 protected function getTableClass() {
250 return 'mw-datatable';
251 }
252
257 protected function getNavClass() {
258 return 'TablePager_nav';
259 }
260
265 protected function getSortHeaderClass() {
266 return 'TablePager_sort';
267 }
268
275 public function getNavigationBar() {
276 if ( !$this->isNavigationBarShown() ) {
277 return '';
278 }
279
280 $this->getOutput()->enableOOUI();
281
282 $types = [ 'first', 'prev', 'next', 'last' ];
283
284 $queries = $this->getPagingQueries();
285
286 $buttons = [];
287
288 $title = $this->getTitle();
289
290 foreach ( $types as $type ) {
291 $buttons[] = new ButtonWidget( [
292 // Messages used here:
293 // * table_pager_first
294 // * table_pager_prev
295 // * table_pager_next
296 // * table_pager_last
297 'classes' => [ 'TablePager-button-' . $type ],
298 'flags' => [ 'progressive' ],
299 'framed' => false,
300 'label' => $this->msg( 'table_pager_' . $type )->text(),
301 'href' => $queries[ $type ] ?
302 $title->getLinkURL( $queries[ $type ] + $this->getDefaultQuery() ) :
303 null,
304 'icon' => $type === 'prev' ? 'previous' : $type,
305 'disabled' => $queries[ $type ] === false
306 ] );
307 }
308 return ( new ButtonGroupWidget( [
309 'classes' => [ $this->getNavClass() ],
310 'items' => $buttons,
311 ] ) )->toString();
312 }
313
317 public function getModuleStyles() {
318 return array_merge(
319 parent::getModuleStyles(), [ 'oojs-ui.styles.icons-movement' ]
320 );
321 }
322
329 public function getLimitSelect( array $attribs = [] ): string {
330 $select = new XmlSelect( 'limit', false, $this->mLimit );
331 $select->addOptions( $this->getLimitSelectList() );
332 foreach ( $attribs as $name => $value ) {
333 $select->setAttribute( $name, $value );
334 }
335 return $select->getHTML();
336 }
337
345 public function getLimitSelectList() {
346 # Add the current limit from the query string
347 # to avoid that the limit is lost after clicking Go next time
348 if ( !in_array( $this->mLimit, $this->mLimitsShown ) ) {
349 $this->mLimitsShown[] = $this->mLimit;
350 sort( $this->mLimitsShown );
351 }
352 $ret = [];
353 foreach ( $this->mLimitsShown as $key => $value ) {
354 # The pair is either $index => $limit, in which case the $value
355 # will be numeric, or $limit => $text, in which case the $value
356 # will be a string.
357 if ( is_int( $value ) ) {
358 $limit = $value;
359 $text = $this->getLanguage()->formatNum( $limit );
360 } else {
361 $limit = $key;
362 $text = $value;
363 }
364 $ret[$text] = $limit;
365 }
366 return $ret;
367 }
368
378 public function getHiddenFields( $noResubmit = [] ) {
379 $noResubmit = (array)$noResubmit;
380 $query = $this->getRequest()->getQueryValues();
381 foreach ( $noResubmit as $name ) {
382 unset( $query[$name] );
383 }
384 $s = '';
385 foreach ( $query as $name => $value ) {
386 if ( is_array( $value ) ) {
387 // Per WebRequest::getVal: Array values are discarded for security reasons.
388 continue;
389 }
390 $s .= Html::hidden( $name, $value ) . "\n";
391 }
392 return $s;
393 }
394
400 public function getLimitForm() {
401 return Html::rawElement(
402 'form',
403 [
404 'method' => 'get',
405 'action' => wfScript(),
406 ],
407 "\n" . $this->getLimitDropdown()
408 ) . "\n";
409 }
410
416 private function getLimitDropdown() {
417 # Make the select with some explanatory text
418 $msgSubmit = $this->msg( 'table_pager_limit_submit' )->escaped();
419
420 return $this->msg( 'table_pager_limit' )
421 ->rawParams( $this->getLimitSelect() )->escaped() .
422 "\n<input type=\"submit\" value=\"$msgSubmit\"/>\n" .
423 $this->getHiddenFields( [ 'limit' ] );
424 }
425
433 abstract protected function isFieldSortable( $field );
434
446 abstract public function formatValue( $name, $value );
447
457 abstract public function getDefaultSort();
458
466 abstract protected function getFieldNames();
467}
468
470class_alias( TablePager::class, 'TablePager' );
wfScript( $script='index')
Get the URL path to a MediaWiki entry point.
setContext(IContextSource $context)
msg( $key,... $params)
Get a Message object with context set Parameters are the same as wfMessage()
This class is a collection of static functions that serve two purposes:
Definition Html.php:43
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)
makeLink( $text, ?array $query=null, $type=null)
Make a self-link.
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.
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.
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.
__construct(?IContextSource $context=null, ?LinkRenderer $linkRenderer=null)
getLimitSelect(array $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 ...
ParserOutput is a rendering of a Content object or a message.
setRawText(?string $text)
Set the raw text of the ParserOutput.
Class for generating HTML <select> or <datalist> elements.
Definition XmlSelect.php:16
addOptions(array $options)
This accepts an array of form label => value label => ( label => value, label => value )
Definition XmlSelect.php:89
Interface for objects which can provide a MediaWiki context on request.
element(SerializerNode $parent, SerializerNode $node, $contents)