MediaWiki 1.44.0
ImageListPager.php
Go to the documentation of this file.
1<?php
22namespace MediaWiki\Pager;
23
40use UnexpectedValueException;
45
50
52 protected ?array $mFieldNames = null;
57 protected $mQueryConds = [];
58 protected ?string $mUserName = null;
60 protected ?User $mUser = null;
61 protected ?bool $mIncluding = false;
62 protected bool $mShowAll = false;
63 protected string $mTableName = 'image';
64
65 private CommentStore $commentStore;
66 private LocalRepo $localRepo;
67 private RowCommentFormatter $rowCommentFormatter;
68 private LinkBatchFactory $linkBatchFactory;
69
71 private array $formattedComments = [];
72
73 private int $migrationStage;
74
78 private const INDEX_FIELDS = [
79 'img_timestamp' => [ 'img_timestamp', 'img_name' ],
80 'img_name' => [ 'img_name' ],
81 'img_size' => [ 'img_size', 'img_name' ],
82 ];
83
84 public function __construct(
85 IContextSource $context,
86 CommentStore $commentStore,
87 LinkRenderer $linkRenderer,
88 IConnectionProvider $dbProvider,
89 RepoGroup $repoGroup,
90 UserNameUtils $userNameUtils,
91 RowCommentFormatter $rowCommentFormatter,
92 LinkBatchFactory $linkBatchFactory,
93 ?string $userName,
94 string $search,
95 ?bool $including,
96 bool $showAll
97 ) {
98 $this->setContext( $context );
99
100 $this->mIncluding = $including;
101 $this->mShowAll = $showAll;
102
103 if ( $userName !== null && $userName !== '' ) {
104 $nt = Title::makeTitleSafe( NS_USER, $userName );
105 if ( $nt === null ) {
106 $this->outputUserDoesNotExist( $userName );
107 } else {
108 $this->mUserName = $nt->getText();
109 $user = User::newFromName( $this->mUserName, false );
110 if ( $user ) {
111 $this->mUser = $user;
112 }
113 if ( !$user || ( $user->isAnon() && !$userNameUtils->isIP( $user->getName() ) ) ) {
114 $this->outputUserDoesNotExist( $userName );
115 }
116 }
117 }
118
119 if ( $including ||
120 $this->getRequest()->getText( 'sort', 'img_date' ) === 'img_date'
121 ) {
122 $this->mDefaultDirection = IndexPager::DIR_DESCENDING;
123 } else {
124 $this->mDefaultDirection = IndexPager::DIR_ASCENDING;
125 }
126 // Set database before parent constructor to avoid setting it there
127 $this->mDb = $dbProvider->getReplicaDatabase();
128
129 parent::__construct( $context, $linkRenderer );
130 $this->commentStore = $commentStore;
131 $this->localRepo = $repoGroup->getLocalRepo();
132 $this->rowCommentFormatter = $rowCommentFormatter;
133 $this->linkBatchFactory = $linkBatchFactory;
134 $this->migrationStage = $this->getConfig()->get(
136 );
137 }
138
144 public function getRelevantUser() {
145 return $this->mUser;
146 }
147
153 protected function outputUserDoesNotExist( $userName ) {
154 $out = $this->getOutput();
155 $out->addModuleStyles( 'mediawiki.codex.messagebox.styles' );
156 $out->addHTML(
157 Html::warningBox(
158 $out->msg(
159 'listfiles-userdoesnotexist', wfEscapeWikiText( $userName )
160 )->parse(),
161 'mw-userpage-userdoesnotexist'
162 )
163 );
164 }
165
173 protected function buildQueryCondsOld( $table ) {
174 $conds = [];
175
176 if ( $this->mUserName !== null ) {
177 // getQueryInfoReal() should have handled the tables and joins.
178 $conds['actor_name'] = $this->mUserName;
179 }
180
181 if ( $table === 'oldimage' ) {
182 // Don't want to deal with revdel.
183 // Future fixme: Show partial information as appropriate.
184 // Would have to be careful about filtering by username when username is deleted.
185 $conds['oi_deleted'] = 0;
186 }
187
188 // Add mQueryConds in case anyone was subclassing and using the old variable.
189 return $conds + $this->mQueryConds;
190 }
191
192 private function buildQueryConds(): array {
193 $conds = [
194 'file_deleted' => 0,
195 'fr_deleted' => 0,
196 ];
197
198 if ( $this->mUserName !== null ) {
199 // getQueryInfoReal() should have handled the tables and joins.
200 $conds['actor_name'] = $this->mUserName;
201 }
202
203 if ( !$this->mShowAll ) {
204 $conds[] = 'file_latest = fr_id';
205 }
206 return $conds;
207 }
208
209 protected function getFieldNames() {
210 if ( !$this->mFieldNames ) {
211 $this->mFieldNames = [
212 'img_timestamp' => $this->msg( 'listfiles_date' )->text(),
213 'img_name' => $this->msg( 'listfiles_name' )->text(),
214 'thumb' => $this->msg( 'listfiles_thumb' )->text(),
215 'img_size' => $this->msg( 'listfiles_size' )->text(),
216 ];
217 if ( $this->mUserName === null ) {
218 // Do not show username if filtering by username
219 $this->mFieldNames['img_actor'] = $this->msg( 'listfiles_user' )->text();
220 }
221 // img_description down here, in order so that its still after the username field.
222 $this->mFieldNames['img_description'] = $this->msg( 'listfiles_description' )->text();
223
224 if ( $this->mShowAll ) {
225 $this->mFieldNames['top'] = $this->msg( 'listfiles-latestversion' )->text();
226 } elseif ( !$this->getConfig()->get( MainConfigNames::MiserMode ) ) {
227 $this->mFieldNames['count'] = $this->msg( 'listfiles_count' )->text();
228 }
229 }
230
231 return $this->mFieldNames;
232 }
233
234 protected function isFieldSortable( $field ) {
235 if ( $this->mIncluding ) {
236 return false;
237 }
238 /* For reference, the indices we can use for sorting are:
239 * On the image table: img_actor_timestamp, img_size, img_timestamp
240 * On oldimage: oi_actor_timestamp, oi_name_timestamp
241 *
242 * In particular that means we cannot sort by timestamp when not filtering
243 * by user and including old images in the results. Which is sad. (T279982)
244 */
245 if ( $this->getConfig()->get( MainConfigNames::MiserMode ) ) {
246 if ( $this->mUserName !== null ) {
247 // If we're sorting by user, the index only supports sorting by time.
248 return $field === 'img_timestamp';
249 } elseif ( $this->mShowAll ) {
250 // no oi_timestamp index, so only alphabetical sorting in this case.
251 return $field === 'img_name';
252 }
253 }
254
255 return isset( self::INDEX_FIELDS[$field] );
256 }
257
258 public function getQueryInfo() {
259 if ( $this->migrationStage & SCHEMA_COMPAT_READ_OLD ) {
260 // Hacky Hacky Hacky - I want to get query info
261 // for two different tables, without reimplementing
262 // the pager class.
263 return $this->getQueryInfoReal( $this->mTableName );
264 }
265 $dbr = $this->getDatabase();
266 $tables = [ 'filerevision', 'file', 'actor' ];
267 $fields = [
268 'img_timestamp' => 'fr_timestamp',
269 'img_name' => 'file_name',
270 'img_size' => 'fr_size',
271 'top' => 'CASE WHEN file_latest = fr_id THEN \'yes\' ELSE \'no\' END',
272 ];
273 $join_conds = [
274 'filerevision' => [ 'JOIN', 'fr_file=file_id' ],
275 'actor' => [ 'JOIN', 'actor_id=fr_actor' ]
276 ];
277
278 # Description field
279 $commentQuery = $this->commentStore->getJoin( 'fr_description' );
280 $tables += $commentQuery['tables'];
281 $fields += $commentQuery['fields'];
282 $join_conds += $commentQuery['joins'];
283 $fields['description_field'] = $dbr->addQuotes( "fr_description" );
284
285 # Actor fields
286 $fields[] = 'actor_user';
287 $fields[] = 'actor_name';
288
289 # Depends on $wgMiserMode
290 # Will also not happen if mShowAll is true.
291 if ( array_key_exists( 'count', $this->getFieldNames() ) ) {
292 $fields['count'] = new Subquery( $dbr->newSelectQueryBuilder()
293 ->select( 'COUNT(fr_archive_name)' )
294 ->from( 'filerevision' )
295 ->where( 'fr_file = file_id' )
296 ->caller( __METHOD__ )
297 ->getSQL()
298 );
299 }
300
301 return [
302 'tables' => $tables,
303 'fields' => $fields,
304 'conds' => $this->buildQueryConds(),
305 'options' => [],
306 'join_conds' => $join_conds
307 ];
308 }
309
320 protected function getQueryInfoReal( $table ) {
321 $dbr = $this->getDatabase();
322 $prefix = $table === 'oldimage' ? 'oi' : 'img';
323
324 $tables = [ $table, 'actor' ];
325 $join_conds = [];
326
327 if ( $table === 'oldimage' ) {
328 $fields = [
329 'img_timestamp' => 'oi_timestamp',
330 'img_name' => 'oi_name',
331 'img_size' => 'oi_size',
332 'top' => $dbr->addQuotes( 'no' )
333 ];
334 $join_conds['actor'] = [ 'JOIN', 'actor_id=oi_actor' ];
335 } else {
336 $fields = [
337 'img_timestamp',
338 'img_name',
339 'img_size',
340 'top' => $dbr->addQuotes( 'yes' )
341 ];
342 $join_conds['actor'] = [ 'JOIN', 'actor_id=img_actor' ];
343 }
344
345 # Description field
346 $commentQuery = $this->commentStore->getJoin( $prefix . '_description' );
347 $tables += $commentQuery['tables'];
348 $fields += $commentQuery['fields'];
349 $join_conds += $commentQuery['joins'];
350 $fields['description_field'] = $dbr->addQuotes( "{$prefix}_description" );
351
352 # Actor fields
353 $fields[] = 'actor_user';
354 $fields[] = 'actor_name';
355
356 # Depends on $wgMiserMode
357 # Will also not happen if mShowAll is true.
358 if ( array_key_exists( 'count', $this->getFieldNames() ) ) {
359 $fields['count'] = new Subquery( $dbr->newSelectQueryBuilder()
360 ->select( 'COUNT(oi_archive_name)' )
361 ->from( 'oldimage' )
362 ->where( 'oi_name = img_name' )
363 ->caller( __METHOD__ )
364 ->getSQL()
365 );
366 }
367
368 return [
369 'tables' => $tables,
370 'fields' => $fields,
371 'conds' => $this->buildQueryCondsOld( $table ),
372 'options' => [],
373 'join_conds' => $join_conds
374 ];
375 }
376
377 public function reallyDoQuery( $offset, $limit, $order ) {
378 if ( $this->migrationStage & SCHEMA_COMPAT_READ_OLD ) {
379 return $this->reallyDoQueryOld( $offset, $limit, $order );
380 } else {
381 return parent::reallyDoQuery( $offset, $limit, $order );
382 }
383 }
384
393 public function reallyDoQueryOld( $offset, $limit, $order ) {
394 $dbr = $this->getDatabase();
395 $prevTableName = $this->mTableName;
396 $this->mTableName = 'image';
397 [ $tables, $fields, $conds, $fname, $options, $join_conds ] =
398 $this->buildQueryInfo( $offset, $limit, $order );
399 $imageRes = $dbr->newSelectQueryBuilder()
400 ->tables( is_array( $tables ) ? $tables : [ $tables ] )
401 ->fields( $fields )
402 ->conds( $conds )
403 ->caller( $fname )
404 ->options( $options )
405 ->joinConds( $join_conds )
406 ->fetchResultSet();
407 $this->mTableName = $prevTableName;
408
409 if ( !$this->mShowAll ) {
410 return $imageRes;
411 }
412
413 $this->mTableName = 'oldimage';
414
415 # Hacky...
416 $oldIndex = $this->mIndexField;
417 foreach ( $this->mIndexField as &$index ) {
418 if ( !str_starts_with( $index, 'img_' ) ) {
419 throw new UnexpectedValueException( "Expected to be sorting on an image table field" );
420 }
421 $index = 'oi_' . substr( $index, 4 );
422 }
423 unset( $index );
424
425 [ $tables, $fields, $conds, $fname, $options, $join_conds ] =
426 $this->buildQueryInfo( $offset, $limit, $order );
427 $oldimageRes = $dbr->newSelectQueryBuilder()
428 ->tables( is_array( $tables ) ? $tables : [ $tables ] )
429 ->fields( $fields )
430 ->conds( $conds )
431 ->caller( $fname )
432 ->options( $options )
433 ->joinConds( $join_conds )
434 ->fetchResultSet();
435
436 $this->mTableName = $prevTableName;
437 $this->mIndexField = $oldIndex;
438
439 return $this->combineResult( $imageRes, $oldimageRes, $limit, $order );
440 }
441
453 protected function combineResult( $res1, $res2, $limit, $order ) {
454 $res1->rewind();
455 $res2->rewind();
456 $topRes1 = $res1->fetchObject();
457 $topRes2 = $res2->fetchObject();
458 $resultArray = [];
459 for ( $i = 0; $i < $limit && $topRes1 && $topRes2; $i++ ) {
460 if ( strcmp( $topRes1->{$this->mIndexField[0]}, $topRes2->{$this->mIndexField[0]} ) > 0 ) {
461 if ( $order !== IndexPager::QUERY_ASCENDING ) {
462 $resultArray[] = $topRes1;
463 $topRes1 = $res1->fetchObject();
464 } else {
465 $resultArray[] = $topRes2;
466 $topRes2 = $res2->fetchObject();
467 }
468 } elseif ( $order !== IndexPager::QUERY_ASCENDING ) {
469 $resultArray[] = $topRes2;
470 $topRes2 = $res2->fetchObject();
471 } else {
472 $resultArray[] = $topRes1;
473 $topRes1 = $res1->fetchObject();
474 }
475 }
476
477 for ( ; $i < $limit && $topRes1; $i++ ) {
478 $resultArray[] = $topRes1;
479 $topRes1 = $res1->fetchObject();
480 }
481
482 for ( ; $i < $limit && $topRes2; $i++ ) {
483 $resultArray[] = $topRes2;
484 $topRes2 = $res2->fetchObject();
485 }
486
487 return new FakeResultWrapper( $resultArray );
488 }
489
490 public function getIndexField() {
491 return [ self::INDEX_FIELDS[$this->mSort] ];
492 }
493
494 public function getDefaultSort() {
495 if ( $this->mShowAll &&
496 $this->getConfig()->get( MainConfigNames::MiserMode ) &&
497 $this->mUserName === null
498 ) {
499 // Unfortunately no index on oi_timestamp.
500 return 'img_name';
501 } else {
502 return 'img_timestamp';
503 }
504 }
505
506 protected function doBatchLookups() {
507 $this->mResult->seek( 0 );
508 $batch = $this->linkBatchFactory->newLinkBatch()->setCaller( __METHOD__ );
509 $rowsWithComments = [ 'img_description' => [], 'oi_description' => [], 'fr_description' => [] ];
510 foreach ( $this->mResult as $i => $row ) {
511 $batch->addUser( new UserIdentityValue( $row->actor_user ?? 0, $row->actor_name ) );
512 $batch->add( NS_FILE, $row->img_name );
513 $rowsWithComments[$row->description_field][$i] = $row;
514 }
515 $batch->execute();
516
517 // Format the comments
518 if ( $rowsWithComments['img_description'] ) {
519 $this->formattedComments += $this->rowCommentFormatter->formatRows(
520 $rowsWithComments['img_description'],
521 'img_description'
522 );
523 }
524 if ( $rowsWithComments['oi_description'] ) {
525 $this->formattedComments += $this->rowCommentFormatter->formatRows(
526 $rowsWithComments['oi_description'],
527 'oi_description'
528 );
529 }
530 if ( $rowsWithComments['fr_description'] ) {
531 $this->formattedComments += $this->rowCommentFormatter->formatRows(
532 $rowsWithComments['fr_description'],
533 'fr_description'
534 );
535 }
536 }
537
543 public function formatValue( $field, $value ) {
544 $linkRenderer = $this->getLinkRenderer();
545 switch ( $field ) {
546 case 'thumb':
547 $opt = [ 'time' => wfTimestamp( TS_MW, $this->mCurrentRow->img_timestamp ) ];
548 $file = $this->localRepo->findFile( $this->getCurrentRow()->img_name, $opt );
549 // If statement for paranoia
550 if ( $file ) {
551 $thumb = $file->transform( [ 'width' => 180, 'height' => 360 ] );
552 if ( $thumb ) {
553 return $thumb->toHtml( [ 'desc-link' => true, 'loading' => 'lazy' ] );
554 }
555 return $this->msg( 'thumbnail_error', '' )->escaped();
556 } else {
557 return htmlspecialchars( $this->getCurrentRow()->img_name );
558 }
559 case 'img_timestamp':
560 // We may want to make this a link to the "old" version when displaying old files
561 return htmlspecialchars( $this->getLanguage()->userTimeAndDate( $value, $this->getUser() ) );
562 case 'img_name':
563 static $imgfile = null;
564 $imgfile ??= $this->msg( 'imgfile' )->text();
565
566 // Weird files can maybe exist? T24227
567 $filePage = Title::makeTitleSafe( NS_FILE, $value );
568 if ( $filePage ) {
569 $html = $linkRenderer->makeKnownLink(
570 $filePage,
571 $filePage->getText()
572 );
573 $opt = [ 'time' => wfTimestamp( TS_MW, $this->mCurrentRow->img_timestamp ) ];
574 $file = $this->localRepo->findFile( $value, $opt );
575 if ( $file ) {
576 $download = Html::element(
577 'a',
578 [ 'href' => $file->getUrl() ],
579 $imgfile
580 );
581 $html .= ' ' . $this->msg( 'parentheses' )->rawParams( $download )->escaped();
582 }
583
584 // Add delete links if allowed
585 // From https://github.com/Wikia/app/pull/3859
586 if ( $this->getAuthority()->probablyCan( 'delete', $filePage ) ) {
587 $deleteMsg = $this->msg( 'listfiles-delete' )->text();
588
589 $delete = $linkRenderer->makeKnownLink(
590 $filePage, $deleteMsg, [], [ 'action' => 'delete' ]
591 );
592 $html .= ' ' . $this->msg( 'parentheses' )->rawParams( $delete )->escaped();
593 }
594
595 return $html;
596 } else {
597 return htmlspecialchars( $value );
598 }
599 case 'img_actor':
600 $userId = (int)$this->mCurrentRow->actor_user;
601 $userName = $this->mCurrentRow->actor_name;
602 return Linker::userLink( $userId, $userName )
603 . Linker::userToolLinks( $userId, $userName );
604 case 'img_size':
605 return htmlspecialchars( $this->getLanguage()->formatSize( (int)$value ) );
606 case 'img_description':
607 return $this->formattedComments[$this->getResultOffset()];
608 case 'count':
609 if ( $this->migrationStage & SCHEMA_COMPAT_READ_OLD ) {
610 return htmlspecialchars( $this->getLanguage()->formatNum( intval( $value ) + 1 ) );
611 } else {
612 return htmlspecialchars( $this->getLanguage()->formatNum( intval( $value ) ) );
613 }
614 case 'top':
615 // Messages: listfiles-latestversion-yes, listfiles-latestversion-no
616 return $this->msg( 'listfiles-latestversion-' . $value )->escaped();
617 default:
618 throw new UnexpectedValueException( "Unknown field '$field'" );
619 }
620 }
621
625 private function getEscapedLimitSelectList(): array {
626 $list = $this->getLimitSelectList();
627 $result = [];
628 foreach ( $list as $key => $value ) {
629 $result[htmlspecialchars( $key )] = $value;
630 }
631 return $result;
632 }
633
634 public function getForm() {
635 $formDescriptor = [];
636 $formDescriptor['limit'] = [
637 'type' => 'radio',
638 'name' => 'limit',
639 'label-message' => 'table_pager_limit_label',
640 'options' => $this->getEscapedLimitSelectList(),
641 'flatlist' => true,
642 'default' => $this->mLimit
643 ];
644
645 $formDescriptor['user'] = [
646 'type' => 'user',
647 'name' => 'user',
648 'id' => 'mw-listfiles-user',
649 'label-message' => 'username',
650 'default' => $this->mUserName,
651 'size' => '40',
652 'maxlength' => '255',
653 ];
654
655 $formDescriptor['ilshowall'] = [
656 'type' => 'check',
657 'name' => 'ilshowall',
658 'id' => 'mw-listfiles-show-all',
659 'label-message' => 'listfiles-show-all',
660 'default' => $this->mShowAll,
661 ];
662
663 $query = $this->getRequest()->getQueryValues();
664 unset( $query['title'] );
665 unset( $query['limit'] );
666 unset( $query['ilsearch'] );
667 unset( $query['ilshowall'] );
668 unset( $query['user'] );
669
670 HTMLForm::factory( 'ooui', $formDescriptor, $this->getContext() )
671 ->setMethod( 'get' )
672 ->setId( 'mw-listfiles-form' )
673 ->setTitle( $this->getTitle() )
674 ->setSubmitTextMsg( 'listfiles-pager-submit' )
675 ->setWrapperLegendMsg( 'listfiles' )
676 ->addHiddenFields( $query )
677 ->prepareForm()
678 ->displayForm( '' );
679 }
680
681 protected function getTableClass() {
682 return parent::getTableClass() . ' listfiles';
683 }
684
685 protected function getNavClass() {
686 return parent::getNavClass() . ' listfiles_nav';
687 }
688
689 protected function getSortHeaderClass() {
690 return parent::getSortHeaderClass() . ' listfiles_sort';
691 }
692
693 public function getPagingQueries() {
694 $queries = parent::getPagingQueries();
695 if ( $this->mUserName !== null ) {
696 # Append the username to the query string
697 foreach ( $queries as &$query ) {
698 if ( $query !== false ) {
699 $query['user'] = $this->mUserName;
700 }
701 }
702 }
703
704 return $queries;
705 }
706
707 public function getDefaultQuery() {
708 $queries = parent::getDefaultQuery();
709 if ( !isset( $queries['user'] ) && $this->mUserName !== null ) {
710 $queries['user'] = $this->mUserName;
711 }
712
713 return $queries;
714 }
715
716 public function getTitle() {
717 return SpecialPage::getTitleFor( 'Listfiles' );
718 }
719}
720
725class_alias( ImageListPager::class, 'ImageListPager' );
const NS_USER
Definition Defines.php:67
const NS_FILE
Definition Defines.php:71
const SCHEMA_COMPAT_READ_OLD
Definition Defines.php:304
wfEscapeWikiText( $input)
Escapes the given text so that it may be output using addWikiText() without any linking,...
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
This is basically a CommentFormatter with a CommentStore dependency, allowing it to retrieve comment ...
Handle database storage of comments such as edit summaries and log reasons.
setContext(IContextSource $context)
Local repository that stores files in the local filesystem and registers them in the wiki's own datab...
Definition LocalRepo.php:57
Prioritized list of file repositories.
Definition RepoGroup.php:38
getLocalRepo()
Get the local repository, i.e.
Object handling generic submission, CSRF protection, layout and other logic for UI forms in a reusabl...
Definition HTMLForm.php:209
This class is a collection of static functions that serve two purposes:
Definition Html.php:57
Class that generates HTML for internal links.
Some internal bits split of from Skin.php.
Definition Linker.php:61
A class containing constants representing the names of configuration variables.
const MiserMode
Name constant for the MiserMode setting, for use with Config::get()
const FileSchemaMigrationStage
Name constant for the FileSchemaMigrationStage setting, for use with Config::get()
reallyDoQuery( $offset, $limit, $order)
Do a query with specified parameters, rather than using the object context.
__construct(IContextSource $context, CommentStore $commentStore, LinkRenderer $linkRenderer, IConnectionProvider $dbProvider, RepoGroup $repoGroup, UserNameUtils $userNameUtils, RowCommentFormatter $rowCommentFormatter, LinkBatchFactory $linkBatchFactory, ?string $userName, string $search, ?bool $including, bool $showAll)
array string[] null $mFieldNames
getDefaultSort()
The database field name used as a default sort order.
getDefaultQuery()
Get an array of query parameters that should be put into self-links.
getIndexField()
Returns the name of the index field.If the pager supports multiple orders, it may return an array of ...
combineResult( $res1, $res2, $limit, $order)
Combine results from 2 tables.
outputUserDoesNotExist( $userName)
Add a message to the output stating that the user doesn't exist.
buildQueryCondsOld( $table)
Build the where clause of the query.
getQueryInfoReal( $table)
Actually get the query info.
reallyDoQueryOld( $offset, $limit, $order)
Override reallyDoQuery to mix together two queries.
isFieldSortable( $field)
Return true if the named field should be sortable by the UI, false otherwise.
doBatchLookups()
Called from getBody(), before getStartBody() is called and after doQuery() was called.
User $mUser
The relevant user.
getFieldNames()
An array mapping database field names to a textual description of the field name, for use in the tabl...
getQueryInfo()
Provides all parameters needed for the main paged query.
getTableClass()
TablePager relies on mw-datatable for styling, see T214208.
getPagingQueries()
Get a URL query array for the prev, next, first and last links.
getRelevantUser()
Get the user relevant to the ImageList.
const QUERY_ASCENDING
Backwards-compatible constant for reallyDoQuery() (do not change)
const DIR_ASCENDING
Backwards-compatible constant for $mDefaultDirection field (do not change)
const DIR_DESCENDING
Backwards-compatible constant for $mDefaultDirection field (do not change)
Table-based display with a user-selectable sort order.
Parent class for all special pages.
Represents a title within MediaWiki.
Definition Title.php:78
Value object representing a user's identity.
UserNameUtils service.
isIP(string $name)
Does the string match an anonymous IP address?
User class for the MediaWiki software.
Definition User.php:121
Overloads the relevant methods of the real ResultWrapper so it doesn't go anywhere near an actual dat...
Interface for objects which can provide a MediaWiki context on request.
Provide primary and replica IDatabase connections.
getReplicaDatabase( $domain=false, $group=null)
Get connection to a replica database.
Result wrapper for grabbing data queried from an IDatabase object.