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
00044 class ApiPageSet extends ApiQueryBase {
00045
00046 private $mAllPages;
00047 private $mTitles, $mGoodTitles, $mMissingTitles, $mInvalidTitles;
00048 private $mMissingPageIDs, $mRedirectTitles;
00049 private $mNormalizedTitles, $mInterwikiTitles;
00050 private $mResolveRedirects, $mPendingRedirectIDs;
00051 private $mGoodRevIDs, $mMissingRevIDs;
00052 private $mFakePageId;
00053
00054 private $mRequestedPageFields;
00055
00061 public function __construct( $query, $resolveRedirects = false ) {
00062 parent :: __construct( $query, 'query' );
00063
00064 $this->mAllPages = array ();
00065 $this->mTitles = array();
00066 $this->mGoodTitles = array ();
00067 $this->mMissingTitles = array ();
00068 $this->mInvalidTitles = array ();
00069 $this->mMissingPageIDs = array ();
00070 $this->mRedirectTitles = array ();
00071 $this->mNormalizedTitles = array ();
00072 $this->mInterwikiTitles = array ();
00073 $this->mGoodRevIDs = array();
00074 $this->mMissingRevIDs = array();
00075
00076 $this->mRequestedPageFields = array ();
00077 $this->mResolveRedirects = $resolveRedirects;
00078 if ( $resolveRedirects )
00079 $this->mPendingRedirectIDs = array();
00080
00081 $this->mFakePageId = - 1;
00082 }
00083
00088 public function isResolvingRedirects() {
00089 return $this->mResolveRedirects;
00090 }
00091
00097 public function requestField( $fieldName ) {
00098 $this->mRequestedPageFields[$fieldName] = null;
00099 }
00100
00107 public function getCustomField( $fieldName ) {
00108 return $this->mRequestedPageFields[$fieldName];
00109 }
00110
00117 public function getPageTableFields() {
00118
00119
00120 $pageFlds = array (
00121 'page_namespace' => null,
00122 'page_title' => null,
00123 'page_id' => null,
00124 );
00125
00126 if ( $this->mResolveRedirects )
00127 $pageFlds['page_is_redirect'] = null;
00128
00129
00130 $this->mRequestedPageFields = array_diff_key( $this->mRequestedPageFields, $pageFlds );
00131
00132 $pageFlds = array_merge( $pageFlds, $this->mRequestedPageFields );
00133 return array_keys( $pageFlds );
00134 }
00135
00142 public function getAllTitlesByNamespace() {
00143 return $this->mAllPages;
00144 }
00145
00150 public function getTitles() {
00151 return $this->mTitles;
00152 }
00153
00158 public function getTitleCount() {
00159 return count( $this->mTitles );
00160 }
00161
00166 public function getGoodTitles() {
00167 return $this->mGoodTitles;
00168 }
00169
00174 public function getGoodTitleCount() {
00175 return count( $this->mGoodTitles );
00176 }
00177
00183 public function getMissingTitles() {
00184 return $this->mMissingTitles;
00185 }
00186
00192 public function getInvalidTitles() {
00193 return $this->mInvalidTitles;
00194 }
00195
00200 public function getMissingPageIDs() {
00201 return $this->mMissingPageIDs;
00202 }
00203
00209 public function getRedirectTitles() {
00210 return $this->mRedirectTitles;
00211 }
00212
00218 public function getNormalizedTitles() {
00219 return $this->mNormalizedTitles;
00220 }
00221
00227 public function getInterwikiTitles() {
00228 return $this->mInterwikiTitles;
00229 }
00230
00235 public function getRevisionIDs() {
00236 return $this->mGoodRevIDs;
00237 }
00238
00243 public function getMissingRevisionIDs() {
00244 return $this->mMissingRevIDs;
00245 }
00246
00251 public function getRevisionCount() {
00252 return count( $this->getRevisionIDs() );
00253 }
00254
00258 public function execute() {
00259 $this->profileIn();
00260 $params = $this->extractRequestParams();
00261
00262
00263 $dataSource = null;
00264 if ( isset ( $params['titles'] ) )
00265 $dataSource = 'titles';
00266 if ( isset ( $params['pageids'] ) ) {
00267 if ( isset ( $dataSource ) )
00268 $this->dieUsage( "Cannot use 'pageids' at the same time as '$dataSource'", 'multisource' );
00269 $dataSource = 'pageids';
00270 }
00271 if ( isset ( $params['revids'] ) ) {
00272 if ( isset ( $dataSource ) )
00273 $this->dieUsage( "Cannot use 'revids' at the same time as '$dataSource'", 'multisource' );
00274 $dataSource = 'revids';
00275 }
00276
00277 switch ( $dataSource ) {
00278 case 'titles' :
00279 $this->initFromTitles( $params['titles'] );
00280 break;
00281 case 'pageids' :
00282 $this->initFromPageIds( $params['pageids'] );
00283 break;
00284 case 'revids' :
00285 if ( $this->mResolveRedirects )
00286 $this->setWarning( 'Redirect resolution cannot be used together with the revids= parameter. ' .
00287 'Any redirects the revids= point to have not been resolved.' );
00288 $this->mResolveRedirects = false;
00289 $this->initFromRevIDs( $params['revids'] );
00290 break;
00291 default :
00292
00293 break;
00294 }
00295 $this->profileOut();
00296 }
00297
00302 public function populateFromTitles( $titles ) {
00303 $this->profileIn();
00304 $this->initFromTitles( $titles );
00305 $this->profileOut();
00306 }
00307
00312 public function populateFromPageIDs( $pageIDs ) {
00313 $this->profileIn();
00314 $this->initFromPageIds( $pageIDs );
00315 $this->profileOut();
00316 }
00317
00323 public function populateFromQueryResult( $db, $queryResult ) {
00324 $this->profileIn();
00325 $this->initFromQueryResult( $db, $queryResult );
00326 $this->profileOut();
00327 }
00328
00333 public function populateFromRevisionIDs( $revIDs ) {
00334 $this->profileIn();
00335 $this->initFromRevIDs( $revIDs );
00336 $this->profileOut();
00337 }
00338
00343 public function processDbRow( $row ) {
00344
00345
00346 $title = Title :: makeTitle( $row->page_namespace, $row->page_title );
00347
00348 $pageId = intval( $row->page_id );
00349 $this->mAllPages[$row->page_namespace][$row->page_title] = $pageId;
00350 $this->mTitles[] = $title;
00351
00352 if ( $this->mResolveRedirects && $row->page_is_redirect == '1' ) {
00353 $this->mPendingRedirectIDs[$pageId] = $title;
00354 } else {
00355 $this->mGoodTitles[$pageId] = $title;
00356 }
00357
00358 foreach ( $this->mRequestedPageFields as $fieldName => & $fieldValues )
00359 $fieldValues[$pageId] = $row-> $fieldName;
00360 }
00361
00365 public function finishPageSetGeneration() {
00366 $this->profileIn();
00367 $this->resolvePendingRedirects();
00368 $this->profileOut();
00369 }
00370
00387 private function initFromTitles( $titles ) {
00388
00389
00390 $linkBatch = $this->processTitlesArray( $titles );
00391 if ( $linkBatch->isEmpty() )
00392 return;
00393
00394 $db = $this->getDB();
00395 $set = $linkBatch->constructSet( 'page', $db );
00396
00397
00398 $this->profileDBIn();
00399 $res = $db->select( 'page', $this->getPageTableFields(), $set,
00400 __METHOD__ );
00401 $this->profileDBOut();
00402
00403
00404 $this->initFromQueryResult( $db, $res, $linkBatch->data, true );
00405
00406
00407 $this->resolvePendingRedirects();
00408 }
00409
00414 private function initFromPageIds( $pageids ) {
00415 if ( !count( $pageids ) )
00416 return;
00417
00418 $pageids = array_map( 'intval', $pageids );
00419 $set = array (
00420 'page_id' => $pageids
00421 );
00422 $db = $this->getDB();
00423
00424
00425 $this->profileDBIn();
00426 $res = $db->select( 'page', $this->getPageTableFields(), $set,
00427 __METHOD__ );
00428 $this->profileDBOut();
00429
00430 $remaining = array_flip( $pageids );
00431 $this->initFromQueryResult( $db, $res, $remaining, false );
00432
00433
00434 $this->resolvePendingRedirects();
00435 }
00436
00448 private function initFromQueryResult( $db, $res, &$remaining = null, $processTitles = null ) {
00449 if ( !is_null( $remaining ) && is_null( $processTitles ) )
00450 ApiBase :: dieDebug( __METHOD__, 'Missing $processTitles parameter when $remaining is provided' );
00451
00452 while ( $row = $db->fetchObject( $res ) ) {
00453
00454 $pageId = intval( $row->page_id );
00455
00456
00457 if ( isset( $remaining ) ) {
00458 if ( $processTitles )
00459 unset ( $remaining[$row->page_namespace][$row->page_title] );
00460 else
00461 unset ( $remaining[$pageId] );
00462 }
00463
00464
00465 $this->processDbRow( $row );
00466 }
00467 $db->freeResult( $res );
00468
00469 if ( isset( $remaining ) ) {
00470
00471 if ( $processTitles ) {
00472
00473 foreach ( $remaining as $ns => $dbkeys ) {
00474 foreach ( $dbkeys as $dbkey => $unused ) {
00475 $title = Title :: makeTitle( $ns, $dbkey );
00476 $this->mAllPages[$ns][$dbkey] = $this->mFakePageId;
00477 $this->mMissingTitles[$this->mFakePageId] = $title;
00478 $this->mFakePageId--;
00479 $this->mTitles[] = $title;
00480 }
00481 }
00482 }
00483 else
00484 {
00485
00486 if ( !$this->mMissingPageIDs )
00487 $this->mMissingPageIDs = array_keys( $remaining );
00488 else
00489 $this->mMissingPageIDs = array_merge( $this->mMissingPageIDs, array_keys( $remaining ) );
00490 }
00491 }
00492 }
00493
00499 private function initFromRevIDs( $revids ) {
00500
00501 if ( !count( $revids ) )
00502 return;
00503
00504 $revids = array_map( 'intval', $revids );
00505 $db = $this->getDB();
00506 $pageids = array();
00507 $remaining = array_flip( $revids );
00508
00509 $tables = array( 'revision', 'page' );
00510 $fields = array( 'rev_id', 'rev_page' );
00511 $where = array( 'rev_id' => $revids, 'rev_page = page_id' );
00512
00513
00514 $this->profileDBIn();
00515 $res = $db->select( $tables, $fields, $where, __METHOD__ );
00516 while ( $row = $db->fetchObject( $res ) ) {
00517 $revid = intval( $row->rev_id );
00518 $pageid = intval( $row->rev_page );
00519 $this->mGoodRevIDs[$revid] = $pageid;
00520 $pageids[$pageid] = '';
00521 unset( $remaining[$revid] );
00522 }
00523 $db->freeResult( $res );
00524 $this->profileDBOut();
00525
00526 $this->mMissingRevIDs = array_keys( $remaining );
00527
00528
00529 $this->initFromPageIds( array_keys( $pageids ) );
00530 }
00531
00537 private function resolvePendingRedirects() {
00538
00539 if ( $this->mResolveRedirects ) {
00540 $db = $this->getDB();
00541 $pageFlds = $this->getPageTableFields();
00542
00543
00544
00545 while ( $this->mPendingRedirectIDs ) {
00546
00547
00548
00549 $linkBatch = $this->getRedirectTargets();
00550
00551 if ( $linkBatch->isEmpty() )
00552 break;
00553
00554 $set = $linkBatch->constructSet( 'page', $db );
00555 if ( $set === false )
00556 break;
00557
00558
00559 $this->profileDBIn();
00560 $res = $db->select( 'page', $pageFlds, $set, __METHOD__ );
00561 $this->profileDBOut();
00562
00563
00564 $this->initFromQueryResult( $db, $res, $linkBatch->data, true );
00565 }
00566 }
00567 }
00568
00576 private function getRedirectTargets() {
00577 $lb = new LinkBatch();
00578 $db = $this->getDB();
00579
00580 $this->profileDBIn();
00581 $res = $db->select( 'redirect', array(
00582 'rd_from',
00583 'rd_namespace',
00584 'rd_title'
00585 ), array( 'rd_from' => array_keys( $this->mPendingRedirectIDs ) ),
00586 __METHOD__
00587 );
00588 $this->profileDBOut();
00589
00590 while ( $row = $db->fetchObject( $res ) )
00591 {
00592 $rdfrom = intval( $row->rd_from );
00593 $from = $this->mPendingRedirectIDs[$rdfrom]->getPrefixedText();
00594 $to = Title::makeTitle( $row->rd_namespace, $row->rd_title )->getPrefixedText();
00595 unset( $this->mPendingRedirectIDs[$rdfrom] );
00596 if ( !isset( $this->mAllPages[$row->rd_namespace][$row->rd_title] ) )
00597 $lb->add( $row->rd_namespace, $row->rd_title );
00598 $this->mRedirectTitles[$from] = $to;
00599 }
00600 $db->freeResult( $res );
00601 if ( $this->mPendingRedirectIDs )
00602 {
00603
00604
00605 foreach ( $this->mPendingRedirectIDs as $id => $title )
00606 {
00607 $article = new Article( $title );
00608 $rt = $article->insertRedirect();
00609 if ( !$rt )
00610
00611 continue;
00612 $lb->addObj( $rt );
00613 $this->mRedirectTitles[$title->getPrefixedText()] = $rt->getPrefixedText();
00614 unset( $this->mPendingRedirectIDs[$id] );
00615 }
00616 }
00617 return $lb;
00618 }
00619
00629 private function processTitlesArray( $titles ) {
00630
00631 $linkBatch = new LinkBatch();
00632
00633 foreach ( $titles as $title ) {
00634
00635 $titleObj = is_string( $title ) ? Title :: newFromText( $title ) : $title;
00636 if ( !$titleObj )
00637 {
00638
00639 $this->mAllpages[0][$title] = $this->mFakePageId;
00640 $this->mInvalidTitles[$this->mFakePageId] = $title;
00641 $this->mFakePageId--;
00642 continue;
00643 }
00644 $iw = $titleObj->getInterwiki();
00645 if ( strval( $iw ) !== '' ) {
00646
00647 $this->mInterwikiTitles[$titleObj->getPrefixedText()] = $iw;
00648 } else {
00649
00650
00651 if ( $titleObj->getNamespace() < 0 )
00652 $this->setWarning( "No support for special pages has been implemented" );
00653 else
00654 $linkBatch->addObj( $titleObj );
00655 }
00656
00657
00658
00659
00660
00661
00662 if ( is_string( $title ) && $title !== $titleObj->getPrefixedText() ) {
00663 $this->mNormalizedTitles[$title] = $titleObj->getPrefixedText();
00664 }
00665 }
00666
00667 return $linkBatch;
00668 }
00669
00670 protected function getAllowedParams() {
00671 return array (
00672 'titles' => array (
00673 ApiBase :: PARAM_ISMULTI => true
00674 ),
00675 'pageids' => array (
00676 ApiBase :: PARAM_TYPE => 'integer',
00677 ApiBase :: PARAM_ISMULTI => true
00678 ),
00679 'revids' => array (
00680 ApiBase :: PARAM_TYPE => 'integer',
00681 ApiBase :: PARAM_ISMULTI => true
00682 )
00683 );
00684 }
00685
00686 protected function getParamDescription() {
00687 return array (
00688 'titles' => 'A list of titles to work on',
00689 'pageids' => 'A list of page IDs to work on',
00690 'revids' => 'A list of revision IDs to work on'
00691 );
00692 }
00693
00694 public function getPossibleErrors() {
00695 return array_merge( parent::getPossibleErrors(), array(
00696 array( 'code' => 'multisource', 'info' => "Cannot use 'pageids' at the same time as 'dataSource'" ),
00697 array( 'code' => 'multisource', 'info' => "Cannot use 'revids' at the same time as 'dataSource'" ),
00698 ) );
00699 }
00700
00701 public function getVersion() {
00702 return __CLASS__ . ': $Id: ApiPageSet.php 62410 2010-02-13 01:21:52Z reedy $';
00703 }
00704 }