Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 331 |
|
0.00% |
0 / 10 |
CRAP | |
0.00% |
0 / 1 |
ApiQueryBacklinks | |
0.00% |
0 / 330 |
|
0.00% |
0 / 10 |
8372 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 18 |
|
0.00% |
0 / 1 |
12 | |||
execute | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getCacheMode | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
executeGenerator | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
runFirstQuery | |
0.00% |
0 / 56 |
|
0.00% |
0 / 1 |
462 | |||
runSecondQuery | |
0.00% |
0 / 80 |
|
0.00% |
0 / 1 |
812 | |||
run | |
0.00% |
0 / 109 |
|
0.00% |
0 / 1 |
1056 | |||
getAllowedParams | |
0.00% |
0 / 40 |
|
0.00% |
0 / 1 |
6 | |||
getExamplesMessages | |
0.00% |
0 / 23 |
|
0.00% |
0 / 1 |
2 | |||
getHelpUrls | |
0.00% |
0 / 1 |
|
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 | |
23 | namespace MediaWiki\Api; |
24 | |
25 | use MediaWiki\Linker\LinksMigration; |
26 | use MediaWiki\Title\Title; |
27 | use Wikimedia\ParamValidator\ParamValidator; |
28 | use 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 | */ |
38 | class 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 */ |
641 | class_alias( ApiQueryBacklinks::class, 'ApiQueryBacklinks' ); |