Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
4.73% covered (danger)
4.73%
13 / 275
0.00% covered (danger)
0.00%
0 / 22
CRAP
0.00% covered (danger)
0.00%
0 / 1
ImageListPager
4.74% covered (danger)
4.74%
13 / 274
0.00% covered (danger)
0.00%
0 / 22
6329.64
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 23
0.00% covered (danger)
0.00%
0 / 1
110
 getRelevantUser
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 outputUserDoesNotExist
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 buildQueryConds
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
 getFieldNames
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
30
 isFieldSortable
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
30
 getQueryInfo
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getQueryInfoReal
0.00% covered (danger)
0.00%
0 / 40
0.00% covered (danger)
0.00%
0 / 1
20
 reallyDoQuery
0.00% covered (danger)
0.00%
0 / 22
0.00% covered (danger)
0.00%
0 / 1
20
 combineResult
0.00% covered (danger)
0.00%
0 / 24
0.00% covered (danger)
0.00%
0 / 1
132
 getIndexField
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getDefaultSort
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
20
 doBatchLookups
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
 formatValue
22.03% covered (danger)
22.03%
13 / 59
0.00% covered (danger)
0.00%
0 / 1
171.55
 getEscapedLimitSelectList
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 getForm
0.00% covered (danger)
0.00%
0 / 40
0.00% covered (danger)
0.00%
0 / 1
2
 getTableClass
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getNavClass
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getSortHeaderClass
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getPagingQueries
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
20
 getDefaultQuery
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
12
 getTitle
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 * http://www.gnu.org/copyleft/gpl.html
17 *
18 * @file
19 * @ingroup Pager
20 */
21
22namespace MediaWiki\Pager;
23
24use LocalRepo;
25use MediaWiki\Cache\UserCache;
26use MediaWiki\CommentFormatter\CommentFormatter;
27use MediaWiki\CommentStore\CommentStore;
28use MediaWiki\Context\IContextSource;
29use MediaWiki\Html\Html;
30use MediaWiki\HTMLForm\HTMLForm;
31use MediaWiki\Linker\LinkRenderer;
32use MediaWiki\MainConfigNames;
33use MediaWiki\SpecialPage\SpecialPage;
34use MediaWiki\Title\Title;
35use MediaWiki\User\User;
36use MediaWiki\User\UserNameUtils;
37use RepoGroup;
38use UnexpectedValueException;
39use Wikimedia\Rdbms\FakeResultWrapper;
40use Wikimedia\Rdbms\IConnectionProvider;
41use Wikimedia\Rdbms\IResultWrapper;
42use Xml;
43
44/**
45 * @ingroup Pager
46 */
47class ImageListPager extends TablePager {
48
49    /** @var string[]|null */
50    protected $mFieldNames = null;
51    /**
52     * @deprecated Subclasses should override {@see buildQueryConds} instead
53     * @var array
54     */
55    protected $mQueryConds = [];
56    /** @var string|null */
57    protected $mUserName = null;
58    /** @var User|null The relevant user */
59    protected $mUser = null;
60    /** @var bool */
61    protected $mIncluding = false;
62    /** @var bool */
63    protected $mShowAll = false;
64    /** @var string */
65    protected $mTableName = 'image';
66
67    private CommentStore $commentStore;
68    private LocalRepo $localRepo;
69    private UserCache $userCache;
70    private CommentFormatter $commentFormatter;
71
72    /**
73     * The unique sort fields for the sort options for unique paginate
74     */
75    private const INDEX_FIELDS = [
76        'img_timestamp' => [ 'img_timestamp', 'img_name' ],
77        'img_name' => [ 'img_name' ],
78        'img_size' => [ 'img_size', 'img_name' ],
79    ];
80
81    /**
82     * @param IContextSource $context
83     * @param CommentStore $commentStore
84     * @param LinkRenderer $linkRenderer
85     * @param IConnectionProvider $dbProvider
86     * @param RepoGroup $repoGroup
87     * @param UserCache $userCache
88     * @param UserNameUtils $userNameUtils
89     * @param CommentFormatter $commentFormatter
90     * @param string $userName
91     * @param string $search
92     * @param bool $including
93     * @param bool $showAll
94     */
95    public function __construct(
96        IContextSource $context,
97        CommentStore $commentStore,
98        LinkRenderer $linkRenderer,
99        IConnectionProvider $dbProvider,
100        RepoGroup $repoGroup,
101        UserCache $userCache,
102        UserNameUtils $userNameUtils,
103        CommentFormatter $commentFormatter,
104        $userName,
105        $search,
106        $including,
107        $showAll
108    ) {
109        $this->setContext( $context );
110
111        $this->mIncluding = $including;
112        $this->mShowAll = $showAll;
113
114        if ( $userName !== null && $userName !== '' ) {
115            $nt = Title::makeTitleSafe( NS_USER, $userName );
116            if ( $nt === null ) {
117                $this->outputUserDoesNotExist( $userName );
118            } else {
119                $this->mUserName = $nt->getText();
120                $user = User::newFromName( $this->mUserName, false );
121                if ( $user ) {
122                    $this->mUser = $user;
123                }
124                if ( !$user || ( $user->isAnon() && !$userNameUtils->isIP( $user->getName() ) ) ) {
125                    $this->outputUserDoesNotExist( $userName );
126                }
127            }
128        }
129
130        if ( $including ||
131            $this->getRequest()->getText( 'sort', 'img_date' ) === 'img_date'
132        ) {
133            $this->mDefaultDirection = IndexPager::DIR_DESCENDING;
134        } else {
135            $this->mDefaultDirection = IndexPager::DIR_ASCENDING;
136        }
137        // Set database before parent constructor to avoid setting it there
138        $this->mDb = $dbProvider->getReplicaDatabase();
139
140        parent::__construct( $context, $linkRenderer );
141        $this->commentStore = $commentStore;
142        $this->localRepo = $repoGroup->getLocalRepo();
143        $this->userCache = $userCache;
144        $this->commentFormatter = $commentFormatter;
145    }
146
147    /**
148     * Get the user relevant to the ImageList
149     *
150     * @return User|null
151     */
152    public function getRelevantUser() {
153        return $this->mUser;
154    }
155
156    /**
157     * Add a message to the output stating that the user doesn't exist
158     *
159     * @param string $userName Unescaped user name
160     */
161    protected function outputUserDoesNotExist( $userName ) {
162        $this->getOutput()->addHTML( Html::warningBox(
163            $this->getOutput()->msg( 'listfiles-userdoesnotexist', wfEscapeWikiText( $userName ) )->parse(),
164            'mw-userpage-userdoesnotexist'
165        ) );
166    }
167
168    /**
169     * Build the where clause of the query.
170     *
171     * Replaces the older mQueryConds member variable.
172     * @param string $table Either "image" or "oldimage"
173     * @return array The query conditions.
174     */
175    protected function buildQueryConds( $table ) {
176        $conds = [];
177
178        if ( $this->mUserName !== null ) {
179            // getQueryInfoReal() should have handled the tables and joins.
180            $conds['actor_name'] = $this->mUserName;
181        }
182
183        if ( $table === 'oldimage' ) {
184            // Don't want to deal with revdel.
185            // Future fixme: Show partial information as appropriate.
186            // Would have to be careful about filtering by username when username is deleted.
187            $conds['oi_deleted'] = 0;
188        }
189
190        // Add mQueryConds in case anyone was subclassing and using the old variable.
191        return $conds + $this->mQueryConds;
192    }
193
194    protected function getFieldNames() {
195        if ( !$this->mFieldNames ) {
196            $this->mFieldNames = [
197                'img_timestamp' => $this->msg( 'listfiles_date' )->text(),
198                'img_name' => $this->msg( 'listfiles_name' )->text(),
199                'thumb' => $this->msg( 'listfiles_thumb' )->text(),
200                'img_size' => $this->msg( 'listfiles_size' )->text(),
201            ];
202            if ( $this->mUserName === null ) {
203                // Do not show username if filtering by username
204                $this->mFieldNames['img_actor'] = $this->msg( 'listfiles_user' )->text();
205            }
206            // img_description down here, in order so that its still after the username field.
207            $this->mFieldNames['img_description'] = $this->msg( 'listfiles_description' )->text();
208
209            if ( $this->mShowAll ) {
210                $this->mFieldNames['top'] = $this->msg( 'listfiles-latestversion' )->text();
211            } elseif ( !$this->getConfig()->get( MainConfigNames::MiserMode ) ) {
212                $this->mFieldNames['count'] = $this->msg( 'listfiles_count' )->text();
213            }
214        }
215
216        return $this->mFieldNames;
217    }
218
219    protected function isFieldSortable( $field ) {
220        if ( $this->mIncluding ) {
221            return false;
222        }
223        /* For reference, the indices we can use for sorting are:
224         * On the image table: img_actor_timestamp, img_size, img_timestamp
225         * On oldimage: oi_actor_timestamp, oi_name_timestamp
226         *
227         * In particular that means we cannot sort by timestamp when not filtering
228         * by user and including old images in the results. Which is sad. (T279982)
229         */
230        if ( $this->getConfig()->get( MainConfigNames::MiserMode ) ) {
231            if ( $this->mUserName !== null ) {
232                // If we're sorting by user, the index only supports sorting by time.
233                return $field === 'img_timestamp';
234            } elseif ( $this->mShowAll ) {
235                // no oi_timestamp index, so only alphabetical sorting in this case.
236                return $field === 'img_name';
237            }
238        }
239
240        return isset( self::INDEX_FIELDS[$field] );
241    }
242
243    public function getQueryInfo() {
244        // Hacky Hacky Hacky - I want to get query info
245        // for two different tables, without reimplementing
246        // the pager class.
247        return $this->getQueryInfoReal( $this->mTableName );
248    }
249
250    /**
251     * Actually get the query info.
252     *
253     * This is to allow displaying both stuff from image and oldimage table.
254     *
255     * This is a bit hacky.
256     *
257     * @param string $table Either 'image' or 'oldimage'
258     * @return array Query info
259     */
260    protected function getQueryInfoReal( $table ) {
261        $dbr = $this->getDatabase();
262        $prefix = $table === 'oldimage' ? 'oi' : 'img';
263
264        $tables = [ $table, 'actor' ];
265        $join_conds = [];
266
267        if ( $table === 'oldimage' ) {
268            $fields = [
269                'img_timestamp' => 'oi_timestamp',
270                'img_name' => 'oi_name',
271                'img_size' => 'oi_size',
272                'top' => $dbr->addQuotes( 'no' )
273            ];
274            $join_conds['actor'] = [ 'JOIN', 'actor_id=oi_actor' ];
275        } else {
276            $fields = [
277                'img_timestamp',
278                'img_name',
279                'img_size',
280                'top' => $dbr->addQuotes( 'yes' )
281            ];
282            $join_conds['actor'] = [ 'JOIN', 'actor_id=img_actor' ];
283        }
284
285        # Description field
286        $commentQuery = $this->commentStore->getJoin( $prefix . '_description' );
287        $tables += $commentQuery['tables'];
288        $fields += $commentQuery['fields'];
289        $join_conds += $commentQuery['joins'];
290        $fields['description_field'] = $dbr->addQuotes( "{$prefix}_description" );
291
292        # Actor fields
293        $fields[] = 'actor_user';
294        $fields[] = 'actor_name';
295
296        # Depends on $wgMiserMode
297        # Will also not happen if mShowAll is true.
298        if ( array_key_exists( 'count', $this->getFieldNames() ) ) {
299            $fields['count'] = $dbr->buildSelectSubquery(
300                'oldimage',
301                'COUNT(oi_archive_name)',
302                'oi_name = img_name',
303                __METHOD__
304            );
305        }
306
307        return [
308            'tables' => $tables,
309            'fields' => $fields,
310            'conds' => $this->buildQueryConds( $table ),
311            'options' => [],
312            'join_conds' => $join_conds
313        ];
314    }
315
316    /**
317     * Override reallyDoQuery to mix together two queries.
318     *
319     * @param string $offset
320     * @param int $limit
321     * @param bool $order IndexPager::QUERY_ASCENDING or IndexPager::QUERY_DESCENDING
322     * @return IResultWrapper
323     */
324    public function reallyDoQuery( $offset, $limit, $order ) {
325        $dbr = $this->getDatabase();
326        $prevTableName = $this->mTableName;
327        $this->mTableName = 'image';
328        [ $tables, $fields, $conds, $fname, $options, $join_conds ] =
329            $this->buildQueryInfo( $offset, $limit, $order );
330        $imageRes = $dbr->select( $tables, $fields, $conds, $fname, $options, $join_conds );
331        $this->mTableName = $prevTableName;
332
333        if ( !$this->mShowAll ) {
334            return $imageRes;
335        }
336
337        $this->mTableName = 'oldimage';
338
339        # Hacky...
340        $oldIndex = $this->mIndexField;
341        foreach ( $this->mIndexField as &$index ) {
342            if ( !str_starts_with( $index, 'img_' ) ) {
343                throw new UnexpectedValueException( "Expected to be sorting on an image table field" );
344            }
345            $index = 'oi_' . substr( $index, 4 );
346        }
347        unset( $index );
348
349        [ $tables, $fields, $conds, $fname, $options, $join_conds ] =
350            $this->buildQueryInfo( $offset, $limit, $order );
351        $oldimageRes = $dbr->select( $tables, $fields, $conds, $fname, $options, $join_conds );
352
353        $this->mTableName = $prevTableName;
354        $this->mIndexField = $oldIndex;
355
356        return $this->combineResult( $imageRes, $oldimageRes, $limit, $order );
357    }
358
359    /**
360     * Combine results from 2 tables.
361     *
362     * Note: This will throw away some results
363     *
364     * @param IResultWrapper $res1
365     * @param IResultWrapper $res2
366     * @param int $limit
367     * @param bool $order IndexPager::QUERY_ASCENDING or IndexPager::QUERY_DESCENDING
368     * @return IResultWrapper $res1 and $res2 combined
369     */
370    protected function combineResult( $res1, $res2, $limit, $order ) {
371        $res1->rewind();
372        $res2->rewind();
373        $topRes1 = $res1->fetchObject();
374        $topRes2 = $res2->fetchObject();
375        $resultArray = [];
376        for ( $i = 0; $i < $limit && $topRes1 && $topRes2; $i++ ) {
377            if ( strcmp( $topRes1->{$this->mIndexField[0]}, $topRes2->{$this->mIndexField[0]} ) > 0 ) {
378                if ( $order !== IndexPager::QUERY_ASCENDING ) {
379                    $resultArray[] = $topRes1;
380                    $topRes1 = $res1->fetchObject();
381                } else {
382                    $resultArray[] = $topRes2;
383                    $topRes2 = $res2->fetchObject();
384                }
385            } elseif ( $order !== IndexPager::QUERY_ASCENDING ) {
386                $resultArray[] = $topRes2;
387                $topRes2 = $res2->fetchObject();
388            } else {
389                $resultArray[] = $topRes1;
390                $topRes1 = $res1->fetchObject();
391            }
392        }
393
394        for ( ; $i < $limit && $topRes1; $i++ ) {
395            $resultArray[] = $topRes1;
396            $topRes1 = $res1->fetchObject();
397        }
398
399        for ( ; $i < $limit && $topRes2; $i++ ) {
400            $resultArray[] = $topRes2;
401            $topRes2 = $res2->fetchObject();
402        }
403
404        return new FakeResultWrapper( $resultArray );
405    }
406
407    public function getIndexField() {
408        return [ self::INDEX_FIELDS[$this->mSort] ];
409    }
410
411    public function getDefaultSort() {
412        if ( $this->mShowAll &&
413            $this->getConfig()->get( MainConfigNames::MiserMode ) &&
414            $this->mUserName === null
415        ) {
416            // Unfortunately no index on oi_timestamp.
417            return 'img_name';
418        } else {
419            return 'img_timestamp';
420        }
421    }
422
423    protected function doBatchLookups() {
424        $userIds = [];
425        $this->mResult->seek( 0 );
426        foreach ( $this->mResult as $row ) {
427            if ( $row->actor_user ) {
428                $userIds[] = $row->actor_user;
429            }
430        }
431        # Do a link batch query for names and userpages
432        $this->userCache->doQuery( $userIds, [ 'userpage' ], __METHOD__ );
433    }
434
435    /**
436     * @param string $field
437     * @param string|null $value
438     * @return string
439     */
440    public function formatValue( $field, $value ) {
441        $linkRenderer = $this->getLinkRenderer();
442        switch ( $field ) {
443            case 'thumb':
444                $opt = [ 'time' => wfTimestamp( TS_MW, $this->mCurrentRow->img_timestamp ) ];
445                $file = $this->localRepo->findFile( $this->getCurrentRow()->img_name, $opt );
446                // If statement for paranoia
447                if ( $file ) {
448                    $thumb = $file->transform( [ 'width' => 180, 'height' => 360 ] );
449                    if ( $thumb ) {
450                        return $thumb->toHtml( [ 'desc-link' => true, 'loading' => 'lazy' ] );
451                    }
452                    return $this->msg( 'thumbnail_error', '' )->escaped();
453                } else {
454                    return htmlspecialchars( $this->getCurrentRow()->img_name );
455                }
456            case 'img_timestamp':
457                // We may want to make this a link to the "old" version when displaying old files
458                return htmlspecialchars( $this->getLanguage()->userTimeAndDate( $value, $this->getUser() ) );
459            case 'img_name':
460                static $imgfile = null;
461                if ( $imgfile === null ) {
462                    $imgfile = $this->msg( 'imgfile' )->text();
463                }
464
465                // Weird files can maybe exist? T24227
466                $filePage = Title::makeTitleSafe( NS_FILE, $value );
467                if ( $filePage ) {
468                    $html = $linkRenderer->makeKnownLink(
469                        $filePage,
470                        $filePage->getText()
471                    );
472                    $opt = [ 'time' => wfTimestamp( TS_MW, $this->mCurrentRow->img_timestamp ) ];
473                    $file = $this->localRepo->findFile( $value, $opt );
474                    if ( $file ) {
475                        $download = Xml::element(
476                            'a',
477                            [ 'href' => $file->getUrl() ],
478                            $imgfile
479                        );
480                        $html .= ' ' . $this->msg( 'parentheses' )->rawParams( $download )->escaped();
481                    }
482
483                    // Add delete links if allowed
484                    // From https://github.com/Wikia/app/pull/3859
485                    if ( $this->getAuthority()->probablyCan( 'delete', $filePage ) ) {
486                        $deleteMsg = $this->msg( 'listfiles-delete' )->text();
487
488                        $delete = $linkRenderer->makeKnownLink(
489                            $filePage, $deleteMsg, [], [ 'action' => 'delete' ]
490                        );
491                        $html .= ' ' . $this->msg( 'parentheses' )->rawParams( $delete )->escaped();
492                    }
493
494                    return $html;
495                } else {
496                    return htmlspecialchars( $value );
497                }
498            case 'img_actor':
499                if ( $this->mCurrentRow->actor_user ) {
500                    $name = $this->mCurrentRow->actor_name;
501                    $link = $linkRenderer->makeLink(
502                        Title::makeTitle( NS_USER, $name ),
503                        $name
504                    );
505                } else {
506                    $link = $value !== null ? htmlspecialchars( $value ) : '';
507                }
508
509                return $link;
510            case 'img_size':
511                return htmlspecialchars( $this->getLanguage()->formatSize( (int)$value ) );
512            case 'img_description':
513                $field = $this->mCurrentRow->description_field;
514                $value = $this->commentStore->getComment( $field, $this->mCurrentRow )->text;
515                return $this->commentFormatter->format( $value );
516            case 'count':
517                return htmlspecialchars( $this->getLanguage()->formatNum( intval( $value ) + 1 ) );
518            case 'top':
519                // Messages: listfiles-latestversion-yes, listfiles-latestversion-no
520                return $this->msg( 'listfiles-latestversion-' . $value )->escaped();
521            default:
522                throw new UnexpectedValueException( "Unknown field '$field'" );
523        }
524    }
525
526    /**
527     * Escape the options list
528     * @return array
529     */
530    private function getEscapedLimitSelectList(): array {
531        $list = $this->getLimitSelectList();
532        $result = [];
533        foreach ( $list as $key => $value ) {
534            $result[htmlspecialchars( $key )] = $value;
535        }
536        return $result;
537    }
538
539    public function getForm() {
540        $formDescriptor = [];
541        $formDescriptor['limit'] = [
542            'type' => 'radio',
543            'name' => 'limit',
544            'label-message' => 'table_pager_limit_label',
545            'options' => $this->getEscapedLimitSelectList(),
546            'flatlist' => true,
547            'default' => $this->mLimit
548        ];
549
550        $formDescriptor['user'] = [
551            'type' => 'user',
552            'name' => 'user',
553            'id' => 'mw-listfiles-user',
554            'label-message' => 'username',
555            'default' => $this->mUserName,
556            'size' => '40',
557            'maxlength' => '255',
558        ];
559
560        $formDescriptor['ilshowall'] = [
561            'type' => 'check',
562            'name' => 'ilshowall',
563            'id' => 'mw-listfiles-show-all',
564            'label-message' => 'listfiles-show-all',
565            'default' => $this->mShowAll,
566        ];
567
568        $query = $this->getRequest()->getQueryValues();
569        unset( $query['title'] );
570        unset( $query['limit'] );
571        unset( $query['ilsearch'] );
572        unset( $query['ilshowall'] );
573        unset( $query['user'] );
574
575        HTMLForm::factory( 'ooui', $formDescriptor, $this->getContext() )
576            ->setMethod( 'get' )
577            ->setId( 'mw-listfiles-form' )
578            ->setTitle( $this->getTitle() )
579            ->setSubmitTextMsg( 'listfiles-pager-submit' )
580            ->setWrapperLegendMsg( 'listfiles' )
581            ->addHiddenFields( $query )
582            ->prepareForm()
583            ->displayForm( '' );
584    }
585
586    protected function getTableClass() {
587        return parent::getTableClass() . ' listfiles';
588    }
589
590    protected function getNavClass() {
591        return parent::getNavClass() . ' listfiles_nav';
592    }
593
594    protected function getSortHeaderClass() {
595        return parent::getSortHeaderClass() . ' listfiles_sort';
596    }
597
598    public function getPagingQueries() {
599        $queries = parent::getPagingQueries();
600        if ( $this->mUserName !== null ) {
601            # Append the username to the query string
602            foreach ( $queries as &$query ) {
603                if ( $query !== false ) {
604                    $query['user'] = $this->mUserName;
605                }
606            }
607        }
608
609        return $queries;
610    }
611
612    public function getDefaultQuery() {
613        $queries = parent::getDefaultQuery();
614        if ( !isset( $queries['user'] ) && $this->mUserName !== null ) {
615            $queries['user'] = $this->mUserName;
616        }
617
618        return $queries;
619    }
620
621    public function getTitle() {
622        return SpecialPage::getTitleFor( 'Listfiles' );
623    }
624}
625
626/**
627 * Retain the old class name for backwards compatibility.
628 * @deprecated since 1.41
629 */
630class_alias( ImageListPager::class, 'ImageListPager' );