Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 331
0.00% covered (danger)
0.00%
0 / 10
CRAP
0.00% covered (danger)
0.00%
0 / 1
ApiQueryBacklinks
0.00% covered (danger)
0.00%
0 / 330
0.00% covered (danger)
0.00%
0 / 10
8372
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
12
 execute
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getCacheMode
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 executeGenerator
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 runFirstQuery
0.00% covered (danger)
0.00%
0 / 56
0.00% covered (danger)
0.00%
0 / 1
462
 runSecondQuery
0.00% covered (danger)
0.00%
0 / 80
0.00% covered (danger)
0.00%
0 / 1
812
 run
0.00% covered (danger)
0.00%
0 / 109
0.00% covered (danger)
0.00%
0 / 1
1056
 getAllowedParams
0.00% covered (danger)
0.00%
0 / 40
0.00% covered (danger)
0.00%
0 / 1
6
 getExamplesMessages
0.00% covered (danger)
0.00%
0 / 23
0.00% covered (danger)
0.00%
0 / 1
2
 getHelpUrls
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * http://www.gnu.org/copyleft/gpl.html
19 *
20 * @file
21 */
22
23namespace MediaWiki\Api;
24
25use MediaWiki\Linker\LinksMigration;
26use MediaWiki\Title\Title;
27use Wikimedia\ParamValidator\ParamValidator;
28use Wikimedia\ParamValidator\TypeDef\IntegerDef;
29
30/**
31 * This is a three-in-one module to query:
32 *   * backlinks  - links pointing to the given page,
33 *   * embeddedin - what pages transclude the given page within themselves,
34 *   * imageusage - what pages use the given image
35 *
36 * @ingroup API
37 */
38class ApiQueryBacklinks extends ApiQueryGeneratorBase {
39
40    /**
41     * @var Title
42     */
43    private $rootTitle;
44
45    /**
46     * @var LinksMigration
47     */
48    private $linksMigration;
49
50    /** @var array */
51    private $params;
52    /** @var array */
53    private $cont;
54    /** @var bool */
55    private $redirect;
56
57    private string $bl_ns;
58    private string $bl_from;
59    private string $bl_from_ns;
60    private string $bl_table;
61    private string $bl_code;
62    private string $bl_title;
63    private bool $hasNS;
64
65    /** @var string */
66    private $helpUrl;
67
68    /**
69     * Maps ns and title to pageid
70     *
71     * @var array
72     */
73    private $pageMap = [];
74    /** @var array */
75    private $resultArr;
76
77    /** @var array */
78    private $redirTitles = [];
79    /** @var string|null */
80    private $continueStr = null;
81
82    /** @var string[][] output element name, database column field prefix, database table */
83    private $backlinksSettings = [
84        'backlinks' => [
85            'code' => 'bl',
86            'prefix' => 'pl',
87            'linktbl' => 'pagelinks',
88            'helpurl' => 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Backlinks',
89        ],
90        'embeddedin' => [
91            'code' => 'ei',
92            'prefix' => 'tl',
93            'linktbl' => 'templatelinks',
94            'helpurl' => 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Embeddedin',
95        ],
96        'imageusage' => [
97            'code' => 'iu',
98            'prefix' => 'il',
99            'linktbl' => 'imagelinks',
100            'helpurl' => 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Imageusage',
101        ]
102    ];
103
104    public function __construct( ApiQuery $query, string $moduleName, LinksMigration $linksMigration ) {
105        $settings = $this->backlinksSettings[$moduleName];
106        $prefix = $settings['prefix'];
107        $code = $settings['code'];
108        $this->resultArr = [];
109
110        parent::__construct( $query, $moduleName, $code );
111        $this->bl_table = $settings['linktbl'];
112        $this->hasNS = $moduleName !== 'imageusage';
113        $this->linksMigration = $linksMigration;
114        if ( isset( $this->linksMigration::$mapping[$this->bl_table] ) ) {
115            [ $this->bl_ns, $this->bl_title ] = $this->linksMigration->getTitleFields( $this->bl_table );
116        } else {
117            $this->bl_ns = $prefix . '_namespace';
118            if ( $this->hasNS ) {
119                $this->bl_title = $prefix . '_title';
120            } else {
121                $this->bl_title = $prefix . '_to';
122            }
123        }
124        $this->bl_from = $prefix . '_from';
125        $this->bl_from_ns = $prefix . '_from_namespace';
126        $this->bl_code = $code;
127        $this->helpUrl = $settings['helpurl'];
128    }
129
130    public function execute() {
131        $this->run();
132    }
133
134    public function getCacheMode( $params ) {
135        return 'public';
136    }
137
138    public function executeGenerator( $resultPageSet ) {
139        $this->run( $resultPageSet );
140    }
141
142    /**
143     * @param ApiPageSet|null $resultPageSet
144     * @return void
145     */
146    private function runFirstQuery( $resultPageSet = null ) {
147        $this->addTables( [ $this->bl_table, 'page' ] );
148        $this->addWhere( "{$this->bl_from}=page_id" );
149        if ( $resultPageSet === null ) {
150            $this->addFields( [ 'page_id', 'page_title', 'page_namespace' ] );
151        } else {
152            $this->addFields( $resultPageSet->getPageTableFields() );
153        }
154        $this->addFields( [ 'page_is_redirect', 'from_ns' => 'page_namespace' ] );
155
156        if ( isset( $this->linksMigration::$mapping[$this->bl_table] ) ) {
157            $conds = $this->linksMigration->getLinksConditions( $this->bl_table, $this->rootTitle );
158            $this->addWhere( $conds );
159        } else {
160            $this->addWhereFld( $this->bl_title, $this->rootTitle->getDBkey() );
161            if ( $this->hasNS ) {
162                $this->addWhereFld( $this->bl_ns, $this->rootTitle->getNamespace() );
163            }
164        }
165
166        $this->addWhereFld( $this->bl_from_ns, $this->params['namespace'] );
167
168        if ( count( $this->cont ) >= 2 ) {
169            $db = $this->getDB();
170            $op = $this->params['dir'] == 'descending' ? '<=' : '>=';
171            if ( $this->params['namespace'] !== null && count( $this->params['namespace'] ) > 1 ) {
172                $this->addWhere( $db->buildComparison( $op, [
173                    $this->bl_from_ns => $this->cont[0],
174                    $this->bl_from => $this->cont[1],
175                ] ) );
176            } else {
177                $this->addWhere( $db->buildComparison( $op, [ $this->bl_from => $this->cont[1] ] ) );
178            }
179        }
180
181        if ( $this->params['filterredir'] == 'redirects' ) {
182            $this->addWhereFld( 'page_is_redirect', 1 );
183        } elseif ( $this->params['filterredir'] == 'nonredirects' && !$this->redirect ) {
184            // T24245 - Check for !redirect, as filtering nonredirects, when
185            // getting what links to them is contradictory
186            $this->addWhereFld( 'page_is_redirect', 0 );
187        }
188
189        $this->addOption( 'LIMIT', $this->params['limit'] + 1 );
190        $sort = ( $this->params['dir'] == 'descending' ? ' DESC' : '' );
191        $orderBy = [];
192        if ( $this->params['namespace'] !== null && count( $this->params['namespace'] ) > 1 ) {
193            $orderBy[] = $this->bl_from_ns . $sort;
194        }
195        $orderBy[] = $this->bl_from . $sort;
196        $this->addOption( 'ORDER BY', $orderBy );
197        $this->addOption( 'STRAIGHT_JOIN' );
198
199        $res = $this->select( __METHOD__ );
200
201        if ( $resultPageSet === null ) {
202            $this->executeGenderCacheFromResultWrapper( $res, __METHOD__ );
203        }
204
205        $count = 0;
206        foreach ( $res as $row ) {
207            if ( ++$count > $this->params['limit'] ) {
208                // We've reached the one extra which shows that there are
209                // additional pages to be had. Stop here...
210                // Continue string may be overridden at a later step
211                $this->continueStr = "{$row->from_ns}|{$row->page_id}";
212                break;
213            }
214
215            // Fill in continuation fields for later steps
216            if ( count( $this->cont ) < 2 ) {
217                $this->cont[] = $row->from_ns;
218                $this->cont[] = $row->page_id;
219            }
220
221            $this->pageMap[$row->page_namespace][$row->page_title] = $row->page_id;
222            $t = Title::makeTitle( $row->page_namespace, $row->page_title );
223            if ( $row->page_is_redirect ) {
224                $this->redirTitles[] = $t;
225            }
226
227            if ( $resultPageSet === null ) {
228                $a = [ 'pageid' => (int)$row->page_id ];
229                ApiQueryBase::addTitleInfo( $a, $t );
230                if ( $row->page_is_redirect ) {
231                    $a['redirect'] = true;
232                }
233                // Put all the results in an array first
234                $this->resultArr[$a['pageid']] = $a;
235            } else {
236                $resultPageSet->processDbRow( $row );
237            }
238        }
239    }
240
241    /**
242     * @param ApiPageSet|null $resultPageSet
243     * @return void
244     */
245    private function runSecondQuery( $resultPageSet = null ) {
246        $db = $this->getDB();
247        if ( isset( $this->linksMigration::$mapping[$this->bl_table] ) ) {
248            $queryInfo = $this->linksMigration->getQueryInfo( $this->bl_table, $this->bl_table );
249            $this->addTables( $queryInfo['tables'] );
250            $this->addJoinConds( $queryInfo['joins'] );
251        } else {
252            $this->addTables( [ $this->bl_table ] );
253        }
254        $this->addTables( [ 'page' ] );
255        $this->addJoinConds( [ 'page' => [ 'JOIN', "{$this->bl_from}=page_id" ] ] );
256
257        if ( $resultPageSet === null ) {
258            $this->addFields( [ 'page_id', 'page_title', 'page_namespace', 'page_is_redirect' ] );
259        } else {
260            $this->addFields( $resultPageSet->getPageTableFields() );
261        }
262
263        $this->addFields( [ $this->bl_title, 'from_ns' => 'page_namespace' ] );
264        if ( $this->hasNS ) {
265            $this->addFields( $this->bl_ns );
266        }
267
268        // We can't use LinkBatch here because $this->hasNS may be false
269        $titleWhere = [];
270        $allRedirNs = [];
271        $allRedirDBkey = [];
272        /** @var Title $t */
273        foreach ( $this->redirTitles as $t ) {
274            $redirNs = $t->getNamespace();
275            $redirDBkey = $t->getDBkey();
276            $expr = $db->expr( $this->bl_title, '=', $redirDBkey );
277            if ( $this->hasNS ) {
278                $expr = $expr->and( $this->bl_ns, '=', $redirNs );
279            }
280            $titleWhere[] = $expr;
281            $allRedirNs[$redirNs] = true;
282            $allRedirDBkey[$redirDBkey] = true;
283        }
284        $this->addWhere( $db->orExpr( $titleWhere ) );
285        $this->addWhereFld( 'page_namespace', $this->params['namespace'] );
286
287        if ( count( $this->cont ) >= 6 ) {
288            $op = $this->params['dir'] == 'descending' ? '<=' : '>=';
289
290            $conds = [];
291            if ( $this->hasNS && count( $allRedirNs ) > 1 ) {
292                $conds[ $this->bl_ns ] = $this->cont[2];
293            }
294            if ( count( $allRedirDBkey ) > 1 ) {
295                $conds[ $this->bl_title ] = $this->cont[3];
296            }
297            // Don't bother with namespace, title, or from_namespace if it's
298            // otherwise constant in the where clause.
299            if ( $this->params['namespace'] !== null && count( $this->params['namespace'] ) > 1 ) {
300                $conds[ $this->bl_from_ns ] = $this->cont[4];
301            }
302            $conds[ $this->bl_from ] = $this->cont[5];
303
304            $this->addWhere( $db->buildComparison( $op, $conds ) );
305        }
306        if ( $this->params['filterredir'] == 'redirects' ) {
307            $this->addWhereFld( 'page_is_redirect', 1 );
308        } elseif ( $this->params['filterredir'] == 'nonredirects' ) {
309            $this->addWhereFld( 'page_is_redirect', 0 );
310        }
311
312        $this->addOption( 'LIMIT', $this->params['limit'] + 1 );
313        $orderBy = [];
314        $sort = ( $this->params['dir'] == 'descending' ? ' DESC' : '' );
315        // Don't order by namespace/title/from_namespace if it's constant in the WHERE clause
316        if ( $this->hasNS && count( $allRedirNs ) > 1 ) {
317            $orderBy[] = $this->bl_ns . $sort;
318        }
319        if ( count( $allRedirDBkey ) > 1 ) {
320            $orderBy[] = $this->bl_title . $sort;
321        }
322        if ( $this->params['namespace'] !== null && count( $this->params['namespace'] ) > 1 ) {
323            $orderBy[] = $this->bl_from_ns . $sort;
324        }
325        $orderBy[] = $this->bl_from . $sort;
326        $this->addOption( 'ORDER BY', $orderBy );
327        $this->addOption( 'USE INDEX', [ 'page' => 'PRIMARY' ] );
328        // T290379: Avoid MariaDB deciding to scan all of `page`.
329        $this->addOption( 'STRAIGHT_JOIN' );
330
331        $res = $this->select( __METHOD__ );
332
333        if ( $resultPageSet === null ) {
334            $this->executeGenderCacheFromResultWrapper( $res, __METHOD__ );
335        }
336
337        $count = 0;
338        foreach ( $res as $row ) {
339            $ns = $this->hasNS ? $row->{$this->bl_ns} : NS_FILE;
340
341            if ( ++$count > $this->params['limit'] ) {
342                // We've reached the one extra which shows that there are
343                // additional pages to be had. Stop here...
344                // Note we must keep the parameters for the first query constant
345                // This may be overridden at a later step
346                $title = $row->{$this->bl_title};
347                $this->continueStr = implode( '|', array_slice( $this->cont, 0, 2 ) ) .
348                    "|$ns|$title|{$row->from_ns}|{$row->page_id}";
349                break;
350            }
351
352            // Fill in continuation fields for later steps
353            if ( count( $this->cont ) < 6 ) {
354                $this->cont[] = $ns;
355                $this->cont[] = $row->{$this->bl_title};
356                $this->cont[] = $row->from_ns;
357                $this->cont[] = $row->page_id;
358            }
359
360            if ( $resultPageSet === null ) {
361                $a = [ 'pageid' => (int)$row->page_id ];
362                ApiQueryBase::addTitleInfo( $a, Title::makeTitle( $row->page_namespace, $row->page_title ) );
363                if ( $row->page_is_redirect ) {
364                    $a['redirect'] = true;
365                }
366                $parentID = $this->pageMap[$ns][$row->{$this->bl_title}];
367                // Put all the results in an array first
368                $this->resultArr[$parentID]['redirlinks'][$row->page_id] = $a;
369            } else {
370                $resultPageSet->processDbRow( $row );
371            }
372        }
373    }
374
375    /**
376     * @param ApiPageSet|null $resultPageSet
377     * @return void
378     */
379    private function run( $resultPageSet = null ) {
380        $this->params = $this->extractRequestParams( false );
381        $this->redirect = isset( $this->params['redirect'] ) && $this->params['redirect'];
382        $userMax = ( $this->redirect ? ApiBase::LIMIT_BIG1 / 2 : ApiBase::LIMIT_BIG1 );
383        $botMax = ( $this->redirect ? ApiBase::LIMIT_BIG2 / 2 : ApiBase::LIMIT_BIG2 );
384
385        $result = $this->getResult();
386
387        if ( $this->params['limit'] == 'max' ) {
388            $this->params['limit'] = $this->getMain()->canApiHighLimits() ? $botMax : $userMax;
389            $result->addParsedLimit( $this->getModuleName(), $this->params['limit'] );
390        } else {
391            $this->params['limit'] = $this->getMain()->getParamValidator()->validateValue(
392                $this, 'limit', (int)$this->params['limit'], [
393                    ParamValidator::PARAM_TYPE => 'limit',
394                    IntegerDef::PARAM_MIN => 1,
395                    IntegerDef::PARAM_MAX => $userMax,
396                    IntegerDef::PARAM_MAX2 => $botMax,
397                    IntegerDef::PARAM_IGNORE_RANGE => true,
398                ]
399            );
400        }
401
402        $this->rootTitle = $this->getTitleFromTitleOrPageId( $this->params );
403
404        // only image titles are allowed for the root in imageinfo mode
405        if ( !$this->hasNS && $this->rootTitle->getNamespace() !== NS_FILE ) {
406            $this->dieWithError(
407                [ 'apierror-imageusage-badtitle', $this->getModuleName() ],
408                'bad_image_title'
409            );
410        }
411
412        // Parse and validate continuation parameter
413        // (Can't use parseContinueParamOrDie(), because the length is variable)
414        $this->cont = [];
415        if ( $this->params['continue'] !== null ) {
416            $cont = explode( '|', $this->params['continue'] );
417
418            switch ( count( $cont ) ) {
419                case 8:
420                    // redirect page ID for result adding
421                    $this->cont[7] = (int)$cont[7];
422                    $this->dieContinueUsageIf( $cont[7] !== (string)$this->cont[7] );
423
424                    /* Fall through */
425
426                case 7:
427                    // top-level page ID for result adding
428                    $this->cont[6] = (int)$cont[6];
429                    $this->dieContinueUsageIf( $cont[6] !== (string)$this->cont[6] );
430
431                    /* Fall through */
432
433                case 6:
434                    // ns for 2nd query (even for imageusage)
435                    $this->cont[2] = (int)$cont[2];
436                    $this->dieContinueUsageIf( $cont[2] !== (string)$this->cont[2] );
437
438                    // title for 2nd query
439                    $this->cont[3] = $cont[3];
440
441                    // from_ns for 2nd query
442                    $this->cont[4] = (int)$cont[4];
443                    $this->dieContinueUsageIf( $cont[4] !== (string)$this->cont[4] );
444
445                    // from_id for 1st query
446                    $this->cont[5] = (int)$cont[5];
447                    $this->dieContinueUsageIf( $cont[5] !== (string)$this->cont[5] );
448
449                    /* Fall through */
450
451                case 2:
452                    // from_ns for 1st query
453                    $this->cont[0] = (int)$cont[0];
454                    $this->dieContinueUsageIf( $cont[0] !== (string)$this->cont[0] );
455
456                    // from_id for 1st query
457                    $this->cont[1] = (int)$cont[1];
458                    $this->dieContinueUsageIf( $cont[1] !== (string)$this->cont[1] );
459
460                    break;
461
462                default:
463                    // @phan-suppress-next-line PhanImpossibleCondition
464                    $this->dieContinueUsageIf( true );
465            }
466
467            ksort( $this->cont );
468        }
469
470        $this->runFirstQuery( $resultPageSet );
471        if ( $this->redirect && count( $this->redirTitles ) ) {
472            $this->resetQueryParams();
473            $this->runSecondQuery( $resultPageSet );
474        }
475
476        // Fill in any missing fields in case it's needed below
477        $this->cont += [ 0, 0, 0, '', 0, 0, 0 ];
478
479        if ( $resultPageSet === null ) {
480            // Try to add the result data in one go and pray that it fits
481            $code = $this->bl_code;
482            $data = array_map( static function ( $arr ) use ( $code ) {
483                if ( isset( $arr['redirlinks'] ) ) {
484                    $arr['redirlinks'] = array_values( $arr['redirlinks'] );
485                    ApiResult::setIndexedTagName( $arr['redirlinks'], $code );
486                }
487                return $arr;
488            }, array_values( $this->resultArr ) );
489            $fit = $result->addValue( 'query', $this->getModuleName(), $data );
490            if ( !$fit ) {
491                // It didn't fit. Add elements one by one until the
492                // result is full.
493                ksort( $this->resultArr );
494                // @phan-suppress-next-line PhanSuspiciousValueComparison
495                if ( count( $this->cont ) >= 7 ) {
496                    $startAt = $this->cont[6];
497                } else {
498                    $startAt = array_key_first( $this->resultArr );
499                }
500                $idx = 0;
501                foreach ( $this->resultArr as $pageID => $arr ) {
502                    if ( $pageID < $startAt ) {
503                        continue;
504                    }
505
506                    // Add the basic entry without redirlinks first
507                    $fit = $result->addValue(
508                        [ 'query', $this->getModuleName() ],
509                        $idx, array_diff_key( $arr, [ 'redirlinks' => '' ] ) );
510                    if ( !$fit ) {
511                        $this->continueStr = implode( '|', array_slice( $this->cont, 0, 6 ) ) .
512                            "|$pageID";
513                        break;
514                    }
515
516                    $hasRedirs = false;
517                    $redirLinks = isset( $arr['redirlinks'] ) ? (array)$arr['redirlinks'] : [];
518                    ksort( $redirLinks );
519                    // @phan-suppress-next-line PhanSuspiciousValueComparisonInLoop
520                    if ( count( $this->cont ) >= 8 && $pageID == $startAt ) {
521                        $redirStartAt = $this->cont[7];
522                    } else {
523                        $redirStartAt = array_key_first( $redirLinks );
524                    }
525                    foreach ( $redirLinks as $key => $redir ) {
526                        if ( $key < $redirStartAt ) {
527                            continue;
528                        }
529
530                        $fit = $result->addValue(
531                            [ 'query', $this->getModuleName(), $idx, 'redirlinks' ],
532                            null, $redir );
533                        if ( !$fit ) {
534                            $this->continueStr = implode( '|', array_slice( $this->cont, 0, 6 ) ) .
535                                "|$pageID|$key";
536                            break;
537                        }
538                        $hasRedirs = true;
539                    }
540                    if ( $hasRedirs ) {
541                        $result->addIndexedTagName(
542                            [ 'query', $this->getModuleName(), $idx, 'redirlinks' ],
543                            $this->bl_code );
544                    }
545                    if ( !$fit ) {
546                        break;
547                    }
548
549                    $idx++;
550                }
551            }
552
553            $result->addIndexedTagName(
554                [ 'query', $this->getModuleName() ],
555                $this->bl_code
556            );
557        }
558        if ( $this->continueStr !== null ) {
559            $this->setContinueEnumParameter( 'continue', $this->continueStr );
560        }
561    }
562
563    public function getAllowedParams() {
564        $retval = [
565            'title' => [
566                ParamValidator::PARAM_TYPE => 'string',
567            ],
568            'pageid' => [
569                ParamValidator::PARAM_TYPE => 'integer',
570            ],
571            'continue' => [
572                ApiBase::PARAM_HELP_MSG => 'api-help-param-continue',
573            ],
574            'namespace' => [
575                ParamValidator::PARAM_ISMULTI => true,
576                ParamValidator::PARAM_TYPE => 'namespace'
577            ],
578            'dir' => [
579                ParamValidator::PARAM_DEFAULT => 'ascending',
580                ParamValidator::PARAM_TYPE => [
581                    'ascending',
582                    'descending'
583                ]
584            ],
585            'filterredir' => [
586                ParamValidator::PARAM_DEFAULT => 'all',
587                ParamValidator::PARAM_TYPE => [
588                    'all',
589                    'redirects',
590                    'nonredirects'
591                ]
592            ],
593            'limit' => [
594                ParamValidator::PARAM_DEFAULT => 10,
595                ParamValidator::PARAM_TYPE => 'limit',
596                IntegerDef::PARAM_MIN => 1,
597                IntegerDef::PARAM_MAX => ApiBase::LIMIT_BIG1,
598                IntegerDef::PARAM_MAX2 => ApiBase::LIMIT_BIG2
599            ]
600        ];
601        if ( $this->getModuleName() !== 'embeddedin' ) {
602            $retval['redirect'] = false;
603        }
604
605        return $retval;
606    }
607
608    protected function getExamplesMessages() {
609        $title = Title::newMainPage()->getPrefixedText();
610        $mp = rawurlencode( $title );
611        $examples = [
612            'backlinks' => [
613                "action=query&list=backlinks&bltitle={$mp}"
614                    => 'apihelp-query+backlinks-example-simple',
615                "action=query&generator=backlinks&gbltitle={$mp}&prop=info"
616                    => 'apihelp-query+backlinks-example-generator',
617            ],
618            'embeddedin' => [
619                'action=query&list=embeddedin&eititle=Template:Stub'
620                    => 'apihelp-query+embeddedin-example-simple',
621                'action=query&generator=embeddedin&geititle=Template:Stub&prop=info'
622                    => 'apihelp-query+embeddedin-example-generator',
623            ],
624            'imageusage' => [
625                'action=query&list=imageusage&iutitle=File:Albert%20Einstein%20Head.jpg'
626                    => 'apihelp-query+imageusage-example-simple',
627                'action=query&generator=imageusage&giutitle=File:Albert%20Einstein%20Head.jpg&prop=info'
628                    => 'apihelp-query+imageusage-example-generator',
629            ]
630        ];
631
632        return $examples[$this->getModuleName()];
633    }
634
635    public function getHelpUrls() {
636        return $this->helpUrl;
637    }
638}
639
640/** @deprecated class alias since 1.43 */
641class_alias( ApiQueryBacklinks::class, 'ApiQueryBacklinks' );