00001 <?php
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 if ( !defined( 'MEDIAWIKI' ) ) {
00027
00028 require_once ( "ApiQueryBase.php" );
00029 }
00030
00039 class ApiQueryBacklinks extends ApiQueryGeneratorBase {
00040
00041 private $params, $rootTitle, $contRedirs, $contLevel, $contTitle, $contID, $redirID, $redirect;
00042 private $bl_ns, $bl_from, $bl_table, $bl_code, $bl_title, $bl_sort, $bl_fields, $hasNS;
00043 private $pageMap, $resultArr;
00044
00045
00046 private $backlinksSettings = array (
00047 'backlinks' => array (
00048 'code' => 'bl',
00049 'prefix' => 'pl',
00050 'linktbl' => 'pagelinks'
00051 ),
00052 'embeddedin' => array (
00053 'code' => 'ei',
00054 'prefix' => 'tl',
00055 'linktbl' => 'templatelinks'
00056 ),
00057 'imageusage' => array (
00058 'code' => 'iu',
00059 'prefix' => 'il',
00060 'linktbl' => 'imagelinks'
00061 )
00062 );
00063
00064 public function __construct( $query, $moduleName ) {
00065 extract( $this->backlinksSettings[$moduleName] );
00066 $this->resultArr = array();
00067
00068 parent :: __construct( $query, $moduleName, $code );
00069 $this->bl_ns = $prefix . '_namespace';
00070 $this->bl_from = $prefix . '_from';
00071 $this->bl_table = $linktbl;
00072 $this->bl_code = $code;
00073
00074 $this->hasNS = $moduleName !== 'imageusage';
00075 if ( $this->hasNS ) {
00076 $this->bl_title = $prefix . '_title';
00077 $this->bl_sort = "{$this->bl_ns}, {$this->bl_title}, {$this->bl_from}";
00078 $this->bl_fields = array (
00079 $this->bl_ns,
00080 $this->bl_title
00081 );
00082 } else {
00083 $this->bl_title = $prefix . '_to';
00084 $this->bl_sort = "{$this->bl_title}, {$this->bl_from}";
00085 $this->bl_fields = array (
00086 $this->bl_title
00087 );
00088 }
00089 }
00090
00091 public function execute() {
00092 $this->run();
00093 }
00094
00095 public function getCacheMode( $params ) {
00096 return 'public';
00097 }
00098
00099 public function executeGenerator( $resultPageSet ) {
00100 $this->run( $resultPageSet );
00101 }
00102
00103 private function prepareFirstQuery( $resultPageSet = null ) {
00104
00105
00106
00107
00108
00109 $db = $this->getDB();
00110 $this->addTables( array( $this->bl_table, 'page' ) );
00111 $this->addWhere( "{$this->bl_from}=page_id" );
00112 if ( is_null( $resultPageSet ) )
00113 $this->addFields( array( 'page_id', 'page_title', 'page_namespace' ) );
00114 else
00115 $this->addFields( $resultPageSet->getPageTableFields() );
00116
00117 $this->addFields( 'page_is_redirect' );
00118 $this->addWhereFld( $this->bl_title, $this->rootTitle->getDBkey() );
00119
00120 if ( $this->hasNS )
00121 $this->addWhereFld( $this->bl_ns, $this->rootTitle->getNamespace() );
00122 $this->addWhereFld( 'page_namespace', $this->params['namespace'] );
00123
00124 if ( !is_null( $this->contID ) )
00125 $this->addWhere( "{$this->bl_from}>={$this->contID}" );
00126
00127 if ( $this->params['filterredir'] == 'redirects' )
00128 $this->addWhereFld( 'page_is_redirect', 1 );
00129 else if ( $this->params['filterredir'] == 'nonredirects' && !$this->redirect )
00130
00131 $this->addWhereFld( 'page_is_redirect', 0 );
00132
00133 $this->addOption( 'LIMIT', $this->params['limit'] + 1 );
00134 $this->addOption( 'ORDER BY', $this->bl_from );
00135 $this->addOption( 'STRAIGHT_JOIN' );
00136 }
00137
00138 private function prepareSecondQuery( $resultPageSet = null ) {
00139
00140
00141
00142
00143
00144 $db = $this->getDB();
00145 $this->addTables( array( 'page', $this->bl_table ) );
00146 $this->addWhere( "{$this->bl_from}=page_id" );
00147
00148 if ( is_null( $resultPageSet ) )
00149 $this->addFields( array( 'page_id', 'page_title', 'page_namespace', 'page_is_redirect' ) );
00150 else
00151 $this->addFields( $resultPageSet->getPageTableFields() );
00152
00153 $this->addFields( $this->bl_title );
00154 if ( $this->hasNS )
00155 $this->addFields( $this->bl_ns );
00156
00157
00158 $titleWhere = array();
00159 foreach ( $this->redirTitles as $t )
00160 $titleWhere[] = "{$this->bl_title} = " . $db->addQuotes( $t->getDBkey() ) .
00161 ( $this->hasNS ? " AND {$this->bl_ns} = '{$t->getNamespace()}'" : "" );
00162 $this->addWhere( $db->makeList( $titleWhere, LIST_OR ) );
00163 $this->addWhereFld( 'page_namespace', $this->params['namespace'] );
00164
00165 if ( !is_null( $this->redirID ) )
00166 {
00167 $first = $this->redirTitles[0];
00168 $title = $db->strencode( $first->getDBkey() );
00169 $ns = $first->getNamespace();
00170 $from = $this->redirID;
00171 if ( $this->hasNS )
00172 $this->addWhere( "{$this->bl_ns} > $ns OR " .
00173 "({$this->bl_ns} = $ns AND " .
00174 "({$this->bl_title} > '$title' OR " .
00175 "({$this->bl_title} = '$title' AND " .
00176 "{$this->bl_from} >= $from)))" );
00177 else
00178 $this->addWhere( "{$this->bl_title} > '$title' OR " .
00179 "({$this->bl_title} = '$title' AND " .
00180 "{$this->bl_from} >= $from)" );
00181
00182 }
00183 if ( $this->params['filterredir'] == 'redirects' )
00184 $this->addWhereFld( 'page_is_redirect', 1 );
00185 else if ( $this->params['filterredir'] == 'nonredirects' )
00186 $this->addWhereFld( 'page_is_redirect', 0 );
00187
00188 $this->addOption( 'LIMIT', $this->params['limit'] + 1 );
00189 $this->addOption( 'ORDER BY', $this->bl_sort );
00190 $this->addOption( 'USE INDEX', array( 'page' => 'PRIMARY' ) );
00191 }
00192
00193 private function run( $resultPageSet = null ) {
00194 $this->params = $this->extractRequestParams( false );
00195 $this->redirect = isset( $this->params['redirect'] ) && $this->params['redirect'];
00196 $userMax = ( $this->redirect ? ApiBase::LIMIT_BIG1 / 2 : ApiBase::LIMIT_BIG1 );
00197 $botMax = ( $this->redirect ? ApiBase::LIMIT_BIG2 / 2 : ApiBase::LIMIT_BIG2 );
00198 if ( $this->params['limit'] == 'max' ) {
00199 $this->params['limit'] = $this->getMain()->canApiHighLimits() ? $botMax : $userMax;
00200 $this->getResult()->addValue( 'limits', $this->getModuleName(), $this->params['limit'] );
00201 }
00202
00203 $this->processContinue();
00204 $this->prepareFirstQuery( $resultPageSet );
00205
00206 $db = $this->getDB();
00207 $res = $this->select( __METHOD__ . '::firstQuery' );
00208
00209 $count = 0;
00210 $this->pageMap = array();
00211 $this->continueStr = null;
00212 $this->redirTitles = array();
00213 while ( $row = $db->fetchObject( $res ) ) {
00214 if ( ++ $count > $this->params['limit'] ) {
00215
00216
00217 $this->continueStr = $this->getContinueStr( $row->page_id );
00218 break;
00219 }
00220
00221 if ( is_null( $resultPageSet ) )
00222 $this->extractRowInfo( $row );
00223 else
00224 {
00225 $this->pageMap[$row->page_namespace][$row->page_title] = $row->page_id;
00226 if ( $row->page_is_redirect )
00227 $this->redirTitles[] = Title::makeTitle( $row->page_namespace, $row->page_title );
00228
00229 $resultPageSet->processDbRow( $row );
00230 }
00231 }
00232 $db->freeResult( $res );
00233
00234 if ( $this->redirect && count( $this->redirTitles ) )
00235 {
00236 $this->resetQueryParams();
00237 $this->prepareSecondQuery( $resultPageSet );
00238 $res = $this->select( __METHOD__ . '::secondQuery' );
00239 $count = 0;
00240 while ( $row = $db->fetchObject( $res ) )
00241 {
00242 if ( ++$count > $this->params['limit'] )
00243 {
00244
00245
00246 if ( $this->hasNS )
00247 $parentID = $this->pageMap[$row-> { $this->bl_ns } ][$row-> { $this->bl_title } ];
00248 else
00249 $parentID = $this->pageMap[NS_IMAGE][$row-> { $this->bl_title } ];
00250 $this->continueStr = $this->getContinueRedirStr( $parentID, $row->page_id );
00251 break;
00252 }
00253
00254 if ( is_null( $resultPageSet ) )
00255 $this->extractRedirRowInfo( $row );
00256 else
00257 $resultPageSet->processDbRow( $row );
00258 }
00259 $db->freeResult( $res );
00260 }
00261 if ( is_null( $resultPageSet ) ) {
00262
00263 $fit = $this->getResult()->addValue( 'query', $this->getModuleName(), array_values( $this->resultArr ) );
00264 if ( !$fit )
00265 {
00266
00267
00268 foreach ( $this->resultArr as $pageID => $arr )
00269 {
00270
00271 $fit = $this->getResult()->addValue(
00272 array( 'query', $this->getModuleName() ),
00273 null, array_diff_key( $arr, array( 'redirlinks' => '' ) ) );
00274 if ( !$fit )
00275 {
00276 $this->continueStr = $this->getContinueStr( $pageID );
00277 break;
00278 }
00279
00280 $hasRedirs = false;
00281 foreach ( (array)@$arr['redirlinks'] as $key => $redir )
00282 {
00283 $fit = $this->getResult()->addValue(
00284 array( 'query', $this->getModuleName(), $pageID, 'redirlinks' ),
00285 $key, $redir );
00286 if ( !$fit )
00287 {
00288 $this->continueStr = $this->getContinueRedirStr( $pageID, $redir['pageid'] );
00289 break;
00290 }
00291 $hasRedirs = true;
00292 }
00293 if ( $hasRedirs )
00294 $this->getResult()->setIndexedTagName_internal(
00295 array( 'query', $this->getModuleName(), $pageID, 'redirlinks' ),
00296 $this->bl_code );
00297 if ( !$fit )
00298 break;
00299 }
00300 }
00301
00302 $this->getResult()->setIndexedTagName_internal(
00303 array( 'query', $this->getModuleName() ),
00304 $this->bl_code );
00305 }
00306 if ( !is_null( $this->continueStr ) )
00307 $this->setContinueEnumParameter( 'continue', $this->continueStr );
00308 }
00309
00310 private function extractRowInfo( $row ) {
00311 $this->pageMap[$row->page_namespace][$row->page_title] = $row->page_id;
00312 $t = Title::makeTitle( $row->page_namespace, $row->page_title );
00313 $a = array( 'pageid' => intval( $row->page_id ) );
00314 ApiQueryBase::addTitleInfo( $a, $t );
00315 if ( $row->page_is_redirect )
00316 {
00317 $a['redirect'] = '';
00318 $this->redirTitles[] = $t;
00319 }
00320
00321 $this->resultArr[$a['pageid']] = $a;
00322 }
00323
00324 private function extractRedirRowInfo( $row )
00325 {
00326 $a['pageid'] = intval( $row->page_id );
00327 ApiQueryBase::addTitleInfo( $a, Title::makeTitle( $row->page_namespace, $row->page_title ) );
00328 if ( $row->page_is_redirect )
00329 $a['redirect'] = '';
00330 $ns = $this->hasNS ? $row-> { $this->bl_ns } : NS_FILE;
00331 $parentID = $this->pageMap[$ns][$row-> { $this->bl_title } ];
00332
00333 $this->resultArr[$parentID]['redirlinks'][] = $a;
00334 $this->getResult()->setIndexedTagName( $this->resultArr[$parentID]['redirlinks'], $this->bl_code );
00335 }
00336
00337 protected function processContinue() {
00338 if ( !is_null( $this->params['continue'] ) )
00339 $this->parseContinueParam();
00340 else {
00341 if ( $this->params['title'] !== "" ) {
00342 $title = Title::newFromText( $this->params['title'] );
00343 if ( !$title ) {
00344 $this->dieUsageMsg( array( 'invalidtitle', $this->params['title'] ) );
00345 } else {
00346 $this->rootTitle = $title;
00347 }
00348 } else {
00349 $this->dieUsageMsg( array( 'missingparam', 'title' ) );
00350 }
00351 }
00352
00353
00354 if ( !$this->hasNS && $this->rootTitle->getNamespace() !== NS_FILE )
00355 $this->dieUsage( "The title for {$this->getModuleName()} query must be an image", 'bad_image_title' );
00356 }
00357
00358 protected function parseContinueParam() {
00359 $continueList = explode( '|', $this->params['continue'] );
00360
00361
00362
00363
00364
00365
00366
00367 $this->rootTitle = $this->contID = $this->redirID = null;
00368 $rootNs = intval( $continueList[0] );
00369 if ( $rootNs === 0 && $continueList[0] !== '0' )
00370
00371 $this->dieUsage( "Invalid continue param. You should pass the original value returned by the previous query", "_badcontinue" );
00372 $this->rootTitle = Title::makeTitleSafe( $rootNs, $continueList[1] );
00373
00374 if ( !$this->rootTitle )
00375 $this->dieUsage( "Invalid continue param. You should pass the original value returned by the previous query", "_badcontinue" );
00376 $contID = intval( $continueList[2] );
00377
00378 if ( $contID === 0 && $continueList[2] !== '0' )
00379 $this->dieUsage( "Invalid continue param. You should pass the original value returned by the previous query", "_badcontinue" );
00380 $this->contID = $contID;
00381 $redirID = intval( @$continueList[3] );
00382
00383 if ( $redirID === 0 && @$continueList[3] !== '0' )
00384
00385 return;
00386 $this->redirID = $redirID;
00387
00388 }
00389
00390 protected function getContinueStr( $lastPageID ) {
00391 return $this->rootTitle->getNamespace() .
00392 '|' . $this->rootTitle->getDBkey() .
00393 '|' . $lastPageID;
00394 }
00395
00396 protected function getContinueRedirStr( $lastPageID, $lastRedirID ) {
00397 return $this->getContinueStr( $lastPageID ) . '|' . $lastRedirID;
00398 }
00399
00400 public function getAllowedParams() {
00401 $retval = array (
00402 'title' => null,
00403 'continue' => null,
00404 'namespace' => array (
00405 ApiBase :: PARAM_ISMULTI => true,
00406 ApiBase :: PARAM_TYPE => 'namespace'
00407 ),
00408 'filterredir' => array(
00409 ApiBase :: PARAM_DFLT => 'all',
00410 ApiBase :: PARAM_TYPE => array(
00411 'all',
00412 'redirects',
00413 'nonredirects'
00414 )
00415 ),
00416 'limit' => array (
00417 ApiBase :: PARAM_DFLT => 10,
00418 ApiBase :: PARAM_TYPE => 'limit',
00419 ApiBase :: PARAM_MIN => 1,
00420 ApiBase :: PARAM_MAX => ApiBase :: LIMIT_BIG1,
00421 ApiBase :: PARAM_MAX2 => ApiBase :: LIMIT_BIG2
00422 )
00423 );
00424 if ( $this->getModuleName() == 'embeddedin' )
00425 return $retval;
00426 $retval['redirect'] = false;
00427 return $retval;
00428 }
00429
00430 public function getParamDescription() {
00431 $retval = array (
00432 'title' => 'Title to search.',
00433 'continue' => 'When more results are available, use this to continue.',
00434 'namespace' => 'The namespace to enumerate.',
00435 );
00436 if ( $this->getModuleName() != 'embeddedin' )
00437 return array_merge( $retval, array(
00438 'redirect' => 'If linking page is a redirect, find all pages that link to that redirect as well. Maximum limit is halved.',
00439 'filterredir' => "How to filter for redirects. If set to nonredirects when {$this->bl_code}redirect is enabled, this is only applied to the second level",
00440 'limit' => "How many total pages to return. If {$this->bl_code}redirect is enabled, limit applies to each level separately (which means you may get up to 2 * limit results)."
00441 ) );
00442 return array_merge( $retval, array(
00443 'filterredir' => 'How to filter for redirects',
00444 'limit' => 'How many total pages to return.'
00445 ) );
00446 }
00447
00448 public function getDescription() {
00449 switch ( $this->getModuleName() ) {
00450 case 'backlinks' :
00451 return 'Find all pages that link to the given page';
00452 case 'embeddedin' :
00453 return 'Find all pages that embed (transclude) the given title';
00454 case 'imageusage' :
00455 return 'Find all pages that use the given image title.';
00456 default :
00457 ApiBase :: dieDebug( __METHOD__, 'Unknown module name' );
00458 }
00459 }
00460
00461 public function getPossibleErrors() {
00462 return array_merge( parent::getPossibleErrors(), array(
00463 array( 'invalidtitle', 'title' ),
00464 array( 'missingparam', 'title' ),
00465 array( 'code' => 'bad_image_title', 'info' => "The title for {$this->getModuleName()} query must be an image" ),
00466 array( 'code' => '_badcontinue', 'info' => 'Invalid continue param. You should pass the original value returned by the previous query' ),
00467 ) );
00468 }
00469
00470 protected function getExamples() {
00471 static $examples = array (
00472 'backlinks' => array (
00473 "api.php?action=query&list=backlinks&bltitle=Main%20Page",
00474 "api.php?action=query&generator=backlinks&gbltitle=Main%20Page&prop=info"
00475 ),
00476 'embeddedin' => array (
00477 "api.php?action=query&list=embeddedin&eititle=Template:Stub",
00478 "api.php?action=query&generator=embeddedin&geititle=Template:Stub&prop=info"
00479 ),
00480 'imageusage' => array (
00481 "api.php?action=query&list=imageusage&iutitle=File:Albert%20Einstein%20Head.jpg",
00482 "api.php?action=query&generator=imageusage&giutitle=File:Albert%20Einstein%20Head.jpg&prop=info"
00483 )
00484 );
00485
00486 return $examples[$this->getModuleName()];
00487 }
00488
00489 public function getVersion() {
00490 return __CLASS__ . ': $Id: ApiQueryBacklinks.php 69932 2010-07-26 08:03:21Z tstarling $';
00491 }
00492 }