MediaWiki master
TablePager.php
Go to the documentation of this file.
1<?php
21namespace MediaWiki\Pager;
22
28use OOUI\ButtonGroupWidget;
29use OOUI\ButtonWidget;
30use stdClass;
31
38abstract class TablePager extends IndexPager {
40 protected $mSort;
41
43 protected $mCurrentRow;
44
51 public function __construct( ?IContextSource $context = null, ?LinkRenderer $linkRenderer = null ) {
52 if ( $context ) {
53 $this->setContext( $context );
54 }
55
56 $this->mSort = $this->getRequest()->getText( 'sort' );
57 if ( !array_key_exists( $this->mSort, $this->getFieldNames() )
58 || !$this->isFieldSortable( $this->mSort )
59 ) {
60 $this->mSort = $this->getDefaultSort();
61 }
62 if ( $this->getRequest()->getBool( 'asc' ) ) {
63 $this->mDefaultDirection = IndexPager::DIR_ASCENDING;
64 } elseif ( $this->getRequest()->getBool( 'desc' ) ) {
65 $this->mDefaultDirection = IndexPager::DIR_DESCENDING;
66 } /* Else leave it at whatever the class default is */
67
68 // Parent constructor needs mSort set, so we call it last
69 parent::__construct( null, $linkRenderer );
70 }
71
81 public function getBodyOutput() {
82 $body = parent::getBody();
83
84 $pout = new ParserOutput;
85 $pout->setRawText( $body );
86 return $pout;
87 }
88
98 public function getFullOutput() {
99 $navigation = $this->getNavigationBar();
100 $body = parent::getBody();
101
102 $pout = new ParserOutput;
103 $pout->setRawText( $navigation . $body . $navigation );
104 $pout->addModuleStyles( $this->getModuleStyles() );
105 return $pout;
106 }
107
112 protected function getStartBody() {
113 $sortClass = $this->getSortHeaderClass();
114
115 $s = '';
116 $fields = $this->getFieldNames();
117
118 // Make table header
119 foreach ( $fields as $field => $name ) {
120 if ( strval( $name ) == '' ) {
121 $s .= Html::rawElement( 'th', [], "\u{00A0}" ) . "\n";
122 } elseif ( $this->isFieldSortable( $field ) ) {
123 $query = [ 'sort' => $field, 'limit' => $this->mLimit ];
124 $linkType = null;
125 $class = null;
126
127 if ( $this->mSort == $field ) {
128 // The table is sorted by this field already, make a link to sort in the other direction
129 // We don't actually know in which direction other fields will be sorted by default…
130 if ( $this->mDefaultDirection == IndexPager::DIR_DESCENDING ) {
131 $linkType = 'asc';
132 $class = "$sortClass mw-datatable-is-sorted mw-datatable-is-descending";
133 $query['asc'] = '1';
134 $query['desc'] = '';
135 } else {
136 $linkType = 'desc';
137 $class = "$sortClass mw-datatable-is-sorted mw-datatable-is-ascending";
138 $query['asc'] = '';
139 $query['desc'] = '1';
140 }
141 }
142
143 $link = $this->makeLink( htmlspecialchars( $name ), $query, $linkType );
144 $s .= Html::rawElement( 'th', [ 'class' => $class ], $link ) . "\n";
145 } else {
146 $s .= Html::element( 'th', [], $name ) . "\n";
147 }
148 }
149
150 $ret = Html::openElement( 'table', [
151 'class' => $this->getTableClass() ]
152 );
153 $ret .= Html::rawElement( 'thead', [], Html::rawElement( 'tr', [], "\n" . $s . "\n" ) );
154 $ret .= Html::openElement( 'tbody' ) . "\n";
155
156 return $ret;
157 }
158
163 protected function getEndBody() {
164 return "</tbody></table>\n";
165 }
166
170 protected function getEmptyBody() {
171 $colspan = count( $this->getFieldNames() );
172 $msgEmpty = $this->msg( 'table_pager_empty' )->text();
173 return Html::rawElement( 'tr', [],
174 Html::element( 'td', [ 'colspan' => $colspan ], $msgEmpty ) );
175 }
176
182 public function formatRow( $row ) {
183 $this->mCurrentRow = $row; // In case formatValue etc need to know
184 $s = Html::openElement( 'tr', $this->getRowAttrs( $row ) ) . "\n";
185 $fieldNames = $this->getFieldNames();
186
187 foreach ( $fieldNames as $field => $name ) {
188 $value = $row->$field ?? null;
189 $formatted = strval( $this->formatValue( $field, $value ) );
190
191 if ( $formatted == '' ) {
192 $formatted = "\u{00A0}";
193 }
194
195 $s .= Html::rawElement( 'td', $this->getCellAttrs( $field, $value ), $formatted ) . "\n";
196 }
197
198 $s .= Html::closeElement( 'tr' ) . "\n";
199
200 return $s;
201 }
202
211 protected function getRowClass( $row ) {
212 return '';
213 }
214
223 protected function getRowAttrs( $row ) {
224 return [ 'class' => $this->getRowClass( $row ) ];
225 }
226
230 protected function getCurrentRow() {
231 return $this->mCurrentRow;
232 }
233
245 protected function getCellAttrs( $field, $value ) {
246 return [ 'class' => 'TablePager_col_' . $field ];
247 }
248
253 public function getIndexField() {
254 return $this->mSort;
255 }
256
263 protected function getTableClass() {
264 return 'mw-datatable';
265 }
266
271 protected function getNavClass() {
272 return 'TablePager_nav';
273 }
274
279 protected function getSortHeaderClass() {
280 return 'TablePager_sort';
281 }
282
289 public function getNavigationBar() {
290 if ( !$this->isNavigationBarShown() ) {
291 return '';
292 }
293
294 $this->getOutput()->enableOOUI();
295
296 $types = [ 'first', 'prev', 'next', 'last' ];
297
298 $queries = $this->getPagingQueries();
299
300 $buttons = [];
301
302 $title = $this->getTitle();
303
304 foreach ( $types as $type ) {
305 $buttons[] = new ButtonWidget( [
306 // Messages used here:
307 // * table_pager_first
308 // * table_pager_prev
309 // * table_pager_next
310 // * table_pager_last
311 'classes' => [ 'TablePager-button-' . $type ],
312 'flags' => [ 'progressive' ],
313 'framed' => false,
314 'label' => $this->msg( 'table_pager_' . $type )->text(),
315 'href' => $queries[ $type ] ?
316 $title->getLinkURL( $queries[ $type ] + $this->getDefaultQuery() ) :
317 null,
318 'icon' => $type === 'prev' ? 'previous' : $type,
319 'disabled' => $queries[ $type ] === false
320 ] );
321 }
322 return new ButtonGroupWidget( [
323 'classes' => [ $this->getNavClass() ],
324 'items' => $buttons,
325 ] );
326 }
327
331 public function getModuleStyles() {
332 return array_merge(
333 parent::getModuleStyles(), [ 'oojs-ui.styles.icons-movement' ]
334 );
335 }
336
343 public function getLimitSelect( array $attribs = [] ): string {
344 $select = new XmlSelect( 'limit', false, $this->mLimit );
345 $select->addOptions( $this->getLimitSelectList() );
346 foreach ( $attribs as $name => $value ) {
347 $select->setAttribute( $name, $value );
348 }
349 return $select->getHTML();
350 }
351
359 public function getLimitSelectList() {
360 # Add the current limit from the query string
361 # to avoid that the limit is lost after clicking Go next time
362 if ( !in_array( $this->mLimit, $this->mLimitsShown ) ) {
363 $this->mLimitsShown[] = $this->mLimit;
364 sort( $this->mLimitsShown );
365 }
366 $ret = [];
367 foreach ( $this->mLimitsShown as $key => $value ) {
368 # The pair is either $index => $limit, in which case the $value
369 # will be numeric, or $limit => $text, in which case the $value
370 # will be a string.
371 if ( is_int( $value ) ) {
372 $limit = $value;
373 $text = $this->getLanguage()->formatNum( $limit );
374 } else {
375 $limit = $key;
376 $text = $value;
377 }
378 $ret[$text] = $limit;
379 }
380 return $ret;
381 }
382
392 public function getHiddenFields( $noResubmit = [] ) {
393 $noResubmit = (array)$noResubmit;
394 $query = $this->getRequest()->getQueryValues();
395 foreach ( $noResubmit as $name ) {
396 unset( $query[$name] );
397 }
398 $s = '';
399 foreach ( $query as $name => $value ) {
400 if ( is_array( $value ) ) {
401 // Per WebRequest::getVal: Array values are discarded for security reasons.
402 continue;
403 }
404 $s .= Html::hidden( $name, $value ) . "\n";
405 }
406 return $s;
407 }
408
414 public function getLimitForm() {
415 return Html::rawElement(
416 'form',
417 [
418 'method' => 'get',
419 'action' => wfScript(),
420 ],
421 "\n" . $this->getLimitDropdown()
422 ) . "\n";
423 }
424
430 private function getLimitDropdown() {
431 # Make the select with some explanatory text
432 $msgSubmit = $this->msg( 'table_pager_limit_submit' )->escaped();
433
434 return $this->msg( 'table_pager_limit' )
435 ->rawParams( $this->getLimitSelect() )->escaped() .
436 "\n<input type=\"submit\" value=\"$msgSubmit\"/>\n" .
437 $this->getHiddenFields( [ 'limit' ] );
438 }
439
447 abstract protected function isFieldSortable( $field );
448
460 abstract public function formatValue( $name, $value );
461
471 abstract public function getDefaultSort();
472
480 abstract protected function getFieldNames();
481}
482
484class_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:56
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:30
addOptions( $options)
This accepts an array of form label => value label => ( label => value, label => value )
Interface for objects which can provide a MediaWiki context on request.
element(SerializerNode $parent, SerializerNode $node, $contents)