61 'linktbl' =>
'pagelinks',
62 'helpurl' =>
'https://www.mediawiki.org/wiki/API:Backlinks',
64 'embeddedin' =>
array(
67 'linktbl' =>
'templatelinks',
68 'helpurl' =>
'https://www.mediawiki.org/wiki/API:Embeddedin',
70 'imageusage' =>
array(
73 'linktbl' =>
'imagelinks',
74 'helpurl' =>
'https://www.mediawiki.org/wiki/API:Imageusage',
79 $settings = $this->backlinksSettings[$moduleName];
80 $prefix = $settings[
'prefix'];
81 $code = $settings[
'code'];
82 $this->resultArr =
array();
84 parent::__construct(
$query, $moduleName, $code );
85 $this->bl_ns = $prefix .
'_namespace';
86 $this->bl_from = $prefix .
'_from';
87 $this->bl_table = $settings[
'linktbl'];
88 $this->bl_code = $code;
89 $this->helpUrl = $settings[
'helpurl'];
91 $this->hasNS = $moduleName !==
'imageusage';
93 $this->bl_title = $prefix .
'_title';
94 $this->bl_fields =
array(
99 $this->bl_title = $prefix .
'_to';
100 $this->bl_fields =
array(
115 $this->
run( $resultPageSet );
129 $this->
addWhere(
"{$this->bl_from}=page_id" );
130 if ( is_null( $resultPageSet ) ) {
131 $this->
addFields(
array(
'page_id',
'page_title',
'page_namespace' ) );
133 $this->
addFields( $resultPageSet->getPageTableFields() );
137 $this->
addWhereFld( $this->bl_title, $this->rootTitle->getDBkey() );
139 if ( $this->hasNS ) {
140 $this->
addWhereFld( $this->bl_ns, $this->rootTitle->getNamespace() );
142 $this->
addWhereFld(
'page_namespace', $this->params[
'namespace'] );
144 if ( !is_null( $this->contID ) ) {
145 $op = $this->params[
'dir'] ==
'descending' ?
'<' :
'>';
146 $this->
addWhere(
"{$this->bl_from}$op={$this->contID}" );
149 if ( $this->params[
'filterredir'] ==
'redirects' ) {
151 } elseif ( $this->params[
'filterredir'] ==
'nonredirects' && !$this->
redirect ) {
157 $this->
addOption(
'LIMIT', $this->params[
'limit'] + 1 );
158 $sort = ( $this->params[
'dir'] ==
'descending' ?
' DESC' :
'' );
173 $db = $this->
getDB();
175 $this->
addWhere(
"{$this->bl_from}=page_id" );
177 if ( is_null( $resultPageSet ) ) {
178 $this->
addFields(
array(
'page_id',
'page_title',
'page_namespace',
'page_is_redirect' ) );
180 $this->
addFields( $resultPageSet->getPageTableFields() );
184 if ( $this->hasNS ) {
189 $titleWhere =
array();
190 $allRedirNs =
array();
191 $allRedirDBkey =
array();
193 foreach ( $this->redirTitles
as $t ) {
194 $redirNs =
$t->getNamespace();
195 $redirDBkey =
$t->getDBkey();
196 $titleWhere[] =
"{$this->bl_title} = " . $db->addQuotes( $redirDBkey ) .
197 ( $this->hasNS ?
" AND {$this->bl_ns} = {$redirNs}" :
'' );
198 $allRedirNs[] = $redirNs;
199 $allRedirDBkey[] = $redirDBkey;
202 $this->
addWhereFld(
'page_namespace', $this->params[
'namespace'] );
204 if ( !is_null( $this->redirID ) ) {
205 $op = $this->params[
'dir'] ==
'descending' ?
'<' :
'>';
207 $first = $this->redirTitles[0];
208 $title = $db->addQuotes( $first->getDBkey() );
209 $ns = $first->getNamespace();
211 if ( $this->hasNS ) {
212 $this->
addWhere(
"{$this->bl_ns} $op $ns OR " .
213 "({$this->bl_ns} = $ns AND " .
214 "({$this->bl_title} $op $title OR " .
215 "({$this->bl_title} = $title AND " .
216 "{$this->bl_from} $op= $from)))" );
218 $this->
addWhere(
"{$this->bl_title} $op $title OR " .
219 "({$this->bl_title} = $title AND " .
220 "{$this->bl_from} $op= $from)" );
223 if ( $this->params[
'filterredir'] ==
'redirects' ) {
225 } elseif ( $this->params[
'filterredir'] ==
'nonredirects' ) {
229 $this->
addOption(
'LIMIT', $this->params[
'limit'] + 1 );
231 $sort = ( $this->params[
'dir'] ==
'descending' ?
' DESC' :
'' );
233 if ( $this->hasNS && count( array_unique( $allRedirNs ) ) != 1 ) {
234 $orderBy[] = $this->bl_ns .
$sort;
236 if ( count( array_unique( $allRedirDBkey ) ) != 1 ) {
237 $orderBy[] = $this->bl_title .
$sort;
239 $orderBy[] = $this->bl_from .
$sort;
240 $this->
addOption(
'ORDER BY', $orderBy );
248 private function run( $resultPageSet =
null ) {
250 $this->
redirect = isset( $this->params[
'redirect'] ) && $this->params[
'redirect'];
256 if ( $this->params[
'limit'] ==
'max' ) {
257 $this->params[
'limit'] = $this->
getMain()->canApiHighLimits() ? $botMax : $userMax;
260 $this->params[
'limit'] = intval( $this->params[
'limit'] );
261 $this->
validateLimit(
'limit', $this->params[
'limit'], 1, $userMax, $botMax );
267 $res = $this->
select( __METHOD__ .
'::firstQuery' );
271 foreach (
$res as $row ) {
272 if ( ++
$count > $this->params[
'limit'] ) {
280 if ( is_null( $resultPageSet ) ) {
283 $this->pageMap[$row->page_namespace][$row->page_title] = $row->page_id;
284 if ( $row->page_is_redirect ) {
285 $this->redirTitles[] =
Title::makeTitle( $row->page_namespace, $row->page_title );
288 $resultPageSet->processDbRow( $row );
292 if ( $this->
redirect && count( $this->redirTitles ) ) {
295 $res = $this->
select( __METHOD__ .
'::secondQuery' );
297 foreach (
$res as $row ) {
298 if ( ++
$count > $this->params[
'limit'] ) {
302 if ( $this->hasNS ) {
303 $parentID = $this->pageMap[$row->{$this->bl_ns}][$row->{$this->bl_title}];
305 $parentID = $this->pageMap[
NS_FILE][$row->{$this->bl_title}];
311 if ( is_null( $resultPageSet ) ) {
314 $resultPageSet->processDbRow( $row );
318 if ( is_null( $resultPageSet ) ) {
320 $fit =
$result->addValue(
'query', $this->getModuleName(), array_values( $this->resultArr ) );
324 foreach ( $this->resultArr
as $pageID => $arr ) {
327 array(
'query', $this->getModuleName() ),
328 null, array_diff_key( $arr,
array(
'redirlinks' =>
'' ) ) );
330 $this->continueStr = $this->getContinueStr( $pageID );
335 $redirLinks = isset( $arr[
'redirlinks'] ) ? $arr[
'redirlinks'] :
array();
336 foreach ( (
array)$redirLinks
as $key => $redir ) {
338 array(
'query', $this->getModuleName(), $pageID,
'redirlinks' ),
341 $this->continueStr = $this->getContinueRedirStr( $pageID, $redir[
'pageid'] );
347 $result->setIndexedTagName_internal(
348 array(
'query', $this->getModuleName(), $pageID,
'redirlinks' ),
357 $result->setIndexedTagName_internal(
358 array(
'query', $this->getModuleName() ),
362 if ( !is_null( $this->continueStr ) ) {
363 $this->setContinueEnumParameter(
'continue', $this->continueStr );
367 private function extractRowInfo( $row ) {
368 $this->pageMap[$row->page_namespace][$row->page_title] = $row->page_id;
370 $a =
array(
'pageid' => intval( $row->page_id ) );
372 if ( $row->page_is_redirect ) {
374 $this->redirTitles[] =
$t;
377 $this->resultArr[$a[
'pageid']] = $a;
380 private function extractRedirRowInfo( $row ) {
381 $a[
'pageid'] = intval( $row->page_id );
383 if ( $row->page_is_redirect ) {
386 $ns = $this->hasNS ? $row->{$this->bl_ns} :
NS_FILE;
387 $parentID = $this->pageMap[$ns][$row->{$this->bl_title}];
389 $this->resultArr[$parentID][
'redirlinks'][] = $a;
390 $this->getResult()->setIndexedTagName(
391 $this->resultArr[$parentID][
'redirlinks'],
396 protected function processContinue() {
397 if ( !is_null( $this->params[
'continue'] ) ) {
398 $this->parseContinueParam();
400 $this->rootTitle = $this->getTitleOrPageId( $this->params )->getTitle();
404 if ( !$this->hasNS && $this->rootTitle->getNamespace() !==
NS_FILE ) {
406 "The title for {$this->getModuleName()} query must be an image",
412 protected function parseContinueParam() {
413 $continueList = explode(
'|', $this->params[
'continue'] );
421 $this->rootTitle = $this->contID = $this->redirID =
null;
422 $rootNs = intval( $continueList[0] );
423 $this->dieContinueUsageIf( $rootNs === 0 && $continueList[0] !==
'0' );
426 $this->dieContinueUsageIf( !$this->rootTitle );
428 $contID = intval( $continueList[2] );
429 $this->dieContinueUsageIf( $contID === 0 && $continueList[2] !==
'0' );
431 $this->contID = $contID;
432 $id2 = isset( $continueList[3] ) ? $continueList[3] :
null;
433 $redirID = intval( $id2 );
435 if ( $redirID === 0 && $id2 !==
'0' ) {
439 $this->redirID = $redirID;
442 protected function getContinueStr( $lastPageID ) {
443 return $this->rootTitle->getNamespace() .
444 '|' . $this->rootTitle->getDBkey() .
448 protected function getContinueRedirStr( $lastPageID, $lastRedirID ) {
449 return $this->getContinueStr( $lastPageID ) .
'|' . $lastRedirID;
452 public function getAllowedParams() {
461 'namespace' =>
array(
472 'filterredir' =>
array(
488 if ( $this->getModuleName() ==
'embeddedin' ) {
496 public function getParamDescription() {
498 'title' =>
"Title to search. Cannot be used together with {$this->bl_code}pageid",
499 'pageid' =>
"Pageid to search. Cannot be used together with {$this->bl_code}title",
500 'continue' =>
'When more results are available, use this to continue',
501 'namespace' =>
'The namespace to enumerate',
502 'dir' =>
'The direction in which to list',
504 if ( $this->getModuleName() !=
'embeddedin' ) {
506 'redirect' =>
'If linking page is a redirect, find all pages ' .
507 'that link to that redirect as well. Maximum limit is halved.',
508 'filterredir' =>
'How to filter for redirects. If set to ' .
509 "nonredirects when {$this->bl_code}redirect is enabled, " .
510 'this is only applied to the second level',
511 'limit' =>
'How many total pages to return. If ' .
512 "{$this->bl_code}redirect is enabled, limit applies to each " .
513 'level separately (which means you may get up to 2 * limit results).'
518 'filterredir' =>
'How to filter for redirects',
519 'limit' =>
'How many total pages to return'
523 public function getResultProperties() {
526 'pageid' =>
'integer',
529 'redirect' =>
'boolean'
534 public function getDescription() {
535 switch ( $this->getModuleName() ) {
537 return 'Find all pages that link to the given page.';
539 return 'Find all pages that embed (transclude) the given title.';
541 return 'Find all pages that use the given image title.';
547 public function getPossibleErrors() {
548 return array_merge( parent::getPossibleErrors(),
549 $this->getTitleOrPageIdErrorMessage(),
552 'code' =>
'bad_image_title',
553 'info' =>
"The title for {$this->getModuleName()} query must be an image"
559 public function getExamples() {
560 static $examples =
array(
561 'backlinks' =>
array(
562 'api.php?action=query&list=backlinks&bltitle=Main%20Page',
563 'api.php?action=query&generator=backlinks&gbltitle=Main%20Page&prop=info'
565 'embeddedin' =>
array(
566 'api.php?action=query&list=embeddedin&eititle=Template:Stub',
567 'api.php?action=query&generator=embeddedin&geititle=Template:Stub&prop=info'
569 'imageusage' =>
array(
570 'api.php?action=query&list=imageusage&iutitle=File:Albert%20Einstein%20Head.jpg',
571 'api.php?action=query&generator=imageusage&giutitle=File:Albert%20Einstein%20Head.jpg&prop=info'
575 return $examples[$this->getModuleName()];
578 public function getHelpUrls() {
579 return $this->helpUrl;