MediaWiki  REL1_22
ApiPageSet.php
Go to the documentation of this file.
00001 <?php
00041 class ApiPageSet extends ApiBase {
00042 
00047     const DISABLE_GENERATORS = 1;
00048 
00049     private $mDbSource;
00050     private $mParams;
00051     private $mResolveRedirects;
00052     private $mConvertTitles;
00053     private $mAllowGenerator;
00054 
00055     private $mAllPages = array(); // [ns][dbkey] => page_id or negative when missing
00056     private $mTitles = array();
00057     private $mGoodTitles = array();
00058     private $mMissingTitles = array();
00059     private $mInvalidTitles = array();
00060     private $mMissingPageIDs = array();
00061     private $mRedirectTitles = array();
00062     private $mSpecialTitles = array();
00063     private $mNormalizedTitles = array();
00064     private $mInterwikiTitles = array();
00065     private $mPendingRedirectIDs = array();
00066     private $mConvertedTitles = array();
00067     private $mGoodRevIDs = array();
00068     private $mMissingRevIDs = array();
00069     private $mFakePageId = -1;
00070     private $mCacheMode = 'public';
00071     private $mRequestedPageFields = array();
00075     private $mDefaultNamespace = NS_MAIN;
00076 
00085     public function __construct( ApiBase $dbSource, $flags = 0, $defaultNamespace = NS_MAIN ) {
00086         parent::__construct( $dbSource->getMain(), $dbSource->getModuleName() );
00087         $this->mDbSource = $dbSource;
00088         $this->mAllowGenerator = ( $flags & ApiPageSet::DISABLE_GENERATORS ) == 0;
00089         $this->mDefaultNamespace = $defaultNamespace;
00090 
00091         $this->profileIn();
00092         $this->mParams = $this->extractRequestParams();
00093         $this->mResolveRedirects = $this->mParams['redirects'];
00094         $this->mConvertTitles = $this->mParams['converttitles'];
00095         $this->profileOut();
00096     }
00097 
00102     public function executeDryRun() {
00103         $this->executeInternal( true );
00104     }
00105 
00109     public function execute() {
00110         $this->executeInternal( false );
00111     }
00112 
00117     private function executeInternal( $isDryRun ) {
00118         $this->profileIn();
00119 
00120         $generatorName = $this->mAllowGenerator ? $this->mParams['generator'] : null;
00121         if ( isset( $generatorName ) ) {
00122             $dbSource = $this->mDbSource;
00123             $isQuery = $dbSource instanceof ApiQuery;
00124             if ( !$isQuery ) {
00125                 // If the parent container of this pageset is not ApiQuery, we must create it to run generator
00126                 $dbSource = $this->getMain()->getModuleManager()->getModule( 'query' );
00127                 // Enable profiling for query module because it will be used for db sql profiling
00128                 $dbSource->profileIn();
00129             }
00130             $generator = $dbSource->getModuleManager()->getModule( $generatorName, null, true );
00131             if ( $generator === null ) {
00132                 $this->dieUsage( 'Unknown generator=' . $generatorName, 'badgenerator' );
00133             }
00134             if ( !$generator instanceof ApiQueryGeneratorBase ) {
00135                 $this->dieUsage( "Module $generatorName cannot be used as a generator", 'badgenerator' );
00136             }
00137             // Create a temporary pageset to store generator's output,
00138             // add any additional fields generator may need, and execute pageset to populate titles/pageids
00139             $tmpPageSet = new ApiPageSet( $dbSource, ApiPageSet::DISABLE_GENERATORS );
00140             $generator->setGeneratorMode( $tmpPageSet );
00141             $this->mCacheMode = $generator->getCacheMode( $generator->extractRequestParams() );
00142 
00143             if ( !$isDryRun ) {
00144                 $generator->requestExtraData( $tmpPageSet );
00145             }
00146             $tmpPageSet->executeInternal( $isDryRun );
00147 
00148             // populate this pageset with the generator output
00149             $this->profileOut();
00150             $generator->profileIn();
00151 
00152             if ( !$isDryRun ) {
00153                 $generator->executeGenerator( $this );
00154                 wfRunHooks( 'APIQueryGeneratorAfterExecute', array( &$generator, &$this ) );
00155             } else {
00156                 // Prevent warnings from being reported on these parameters
00157                 $main = $this->getMain();
00158                 foreach ( $generator->extractRequestParams() as $paramName => $param ) {
00159                     $main->getVal( $generator->encodeParamName( $paramName ) );
00160                 }
00161             }
00162             $generator->profileOut();
00163             $this->profileIn();
00164 
00165             if ( !$isDryRun ) {
00166                 $this->resolvePendingRedirects();
00167             }
00168 
00169             if ( !$isQuery ) {
00170                 // If this pageset is not part of the query, we called profileIn() above
00171                 $dbSource->profileOut();
00172             }
00173         } else {
00174             // Only one of the titles/pageids/revids is allowed at the same time
00175             $dataSource = null;
00176             if ( isset( $this->mParams['titles'] ) ) {
00177                 $dataSource = 'titles';
00178             }
00179             if ( isset( $this->mParams['pageids'] ) ) {
00180                 if ( isset( $dataSource ) ) {
00181                     $this->dieUsage( "Cannot use 'pageids' at the same time as '$dataSource'", 'multisource' );
00182                 }
00183                 $dataSource = 'pageids';
00184             }
00185             if ( isset( $this->mParams['revids'] ) ) {
00186                 if ( isset( $dataSource ) ) {
00187                     $this->dieUsage( "Cannot use 'revids' at the same time as '$dataSource'", 'multisource' );
00188                 }
00189                 $dataSource = 'revids';
00190             }
00191 
00192             if ( !$isDryRun ) {
00193                 // Populate page information with the original user input
00194                 switch ( $dataSource ) {
00195                     case 'titles':
00196                         $this->initFromTitles( $this->mParams['titles'] );
00197                         break;
00198                     case 'pageids':
00199                         $this->initFromPageIds( $this->mParams['pageids'] );
00200                         break;
00201                     case 'revids':
00202                         if ( $this->mResolveRedirects ) {
00203                             $this->setWarning( 'Redirect resolution cannot be used together with the revids= parameter. ' .
00204                                 'Any redirects the revids= point to have not been resolved.' );
00205                         }
00206                         $this->mResolveRedirects = false;
00207                         $this->initFromRevIDs( $this->mParams['revids'] );
00208                         break;
00209                     default:
00210                         // Do nothing - some queries do not need any of the data sources.
00211                         break;
00212                 }
00213             }
00214         }
00215         $this->profileOut();
00216     }
00217 
00222     public function isResolvingRedirects() {
00223         return $this->mResolveRedirects;
00224     }
00225 
00234     public function getDataSource() {
00235         if ( $this->mAllowGenerator && isset( $this->mParams['generator'] ) ) {
00236             return 'generator';
00237         }
00238         if ( isset( $this->mParams['titles'] ) ) {
00239             return 'titles';
00240         }
00241         if ( isset( $this->mParams['pageids'] ) ) {
00242             return 'pageids';
00243         }
00244         if ( isset( $this->mParams['revids'] ) ) {
00245             return 'revids';
00246         }
00247         return null;
00248     }
00249 
00255     public function requestField( $fieldName ) {
00256         $this->mRequestedPageFields[$fieldName] = null;
00257     }
00258 
00265     public function getCustomField( $fieldName ) {
00266         return $this->mRequestedPageFields[$fieldName];
00267     }
00268 
00275     public function getPageTableFields() {
00276         // Ensure we get minimum required fields
00277         // DON'T change this order
00278         $pageFlds = array(
00279             'page_namespace' => null,
00280             'page_title' => null,
00281             'page_id' => null,
00282         );
00283 
00284         if ( $this->mResolveRedirects ) {
00285             $pageFlds['page_is_redirect'] = null;
00286         }
00287 
00288         // only store non-default fields
00289         $this->mRequestedPageFields = array_diff_key( $this->mRequestedPageFields, $pageFlds );
00290 
00291         $pageFlds = array_merge( $pageFlds, $this->mRequestedPageFields );
00292         return array_keys( $pageFlds );
00293     }
00294 
00301     public function getAllTitlesByNamespace() {
00302         return $this->mAllPages;
00303     }
00304 
00309     public function getTitles() {
00310         return $this->mTitles;
00311     }
00312 
00317     public function getTitleCount() {
00318         return count( $this->mTitles );
00319     }
00320 
00325     public function getGoodTitles() {
00326         return $this->mGoodTitles;
00327     }
00328 
00333     public function getGoodTitleCount() {
00334         return count( $this->mGoodTitles );
00335     }
00336 
00342     public function getMissingTitles() {
00343         return $this->mMissingTitles;
00344     }
00345 
00351     public function getInvalidTitles() {
00352         return $this->mInvalidTitles;
00353     }
00354 
00359     public function getMissingPageIDs() {
00360         return $this->mMissingPageIDs;
00361     }
00362 
00368     public function getRedirectTitles() {
00369         return $this->mRedirectTitles;
00370     }
00371 
00379     public function getRedirectTitlesAsResult( $result = null ) {
00380         $values = array();
00381         foreach ( $this->getRedirectTitles() as $titleStrFrom => $titleTo ) {
00382             $r = array(
00383                 'from' => strval( $titleStrFrom ),
00384                 'to' => $titleTo->getPrefixedText(),
00385             );
00386             if ( $titleTo->getFragment() !== '' ) {
00387                 $r['tofragment'] = $titleTo->getFragment();
00388             }
00389             $values[] = $r;
00390         }
00391         if ( !empty( $values ) && $result ) {
00392             $result->setIndexedTagName( $values, 'r' );
00393         }
00394         return $values;
00395     }
00396 
00402     public function getNormalizedTitles() {
00403         return $this->mNormalizedTitles;
00404     }
00405 
00413     public function getNormalizedTitlesAsResult( $result = null ) {
00414         $values = array();
00415         foreach ( $this->getNormalizedTitles() as $rawTitleStr => $titleStr ) {
00416             $values[] = array(
00417                 'from' => $rawTitleStr,
00418                 'to' => $titleStr
00419             );
00420         }
00421         if ( !empty( $values ) && $result ) {
00422             $result->setIndexedTagName( $values, 'n' );
00423         }
00424         return $values;
00425     }
00426 
00432     public function getConvertedTitles() {
00433         return $this->mConvertedTitles;
00434     }
00435 
00443     public function getConvertedTitlesAsResult( $result = null ) {
00444         $values = array();
00445         foreach ( $this->getConvertedTitles() as $rawTitleStr => $titleStr ) {
00446             $values[] = array(
00447                 'from' => $rawTitleStr,
00448                 'to' => $titleStr
00449             );
00450         }
00451         if ( !empty( $values ) && $result ) {
00452             $result->setIndexedTagName( $values, 'c' );
00453         }
00454         return $values;
00455     }
00456 
00462     public function getInterwikiTitles() {
00463         return $this->mInterwikiTitles;
00464     }
00465 
00474     public function getInterwikiTitlesAsResult( $result = null, $iwUrl = false ) {
00475         $values = array();
00476         foreach ( $this->getInterwikiTitles() as $rawTitleStr => $interwikiStr ) {
00477             $item = array(
00478                 'title' => $rawTitleStr,
00479                 'iw' => $interwikiStr,
00480             );
00481             if ( $iwUrl ) {
00482                 $title = Title::newFromText( $rawTitleStr );
00483                 $item['url'] = $title->getFullURL( '', false, PROTO_CURRENT );
00484             }
00485             $values[] = $item;
00486         }
00487         if ( !empty( $values ) && $result ) {
00488             $result->setIndexedTagName( $values, 'i' );
00489         }
00490         return $values;
00491     }
00492 
00497     public function getRevisionIDs() {
00498         return $this->mGoodRevIDs;
00499     }
00500 
00505     public function getMissingRevisionIDs() {
00506         return $this->mMissingRevIDs;
00507     }
00508 
00515     public function getMissingRevisionIDsAsResult( $result = null ) {
00516         $values = array();
00517         foreach ( $this->getMissingRevisionIDs() as $revid ) {
00518             $values[$revid] = array(
00519                 'revid' => $revid
00520             );
00521         }
00522         if ( !empty( $values ) && $result ) {
00523             $result->setIndexedTagName( $values, 'rev' );
00524         }
00525         return $values;
00526     }
00527 
00532     public function getSpecialTitles() {
00533         return $this->mSpecialTitles;
00534     }
00535 
00540     public function getRevisionCount() {
00541         return count( $this->getRevisionIDs() );
00542     }
00543 
00548     public function populateFromTitles( $titles ) {
00549         $this->profileIn();
00550         $this->initFromTitles( $titles );
00551         $this->profileOut();
00552     }
00553 
00558     public function populateFromPageIDs( $pageIDs ) {
00559         $this->profileIn();
00560         $this->initFromPageIds( $pageIDs );
00561         $this->profileOut();
00562     }
00563 
00569     public function populateFromQueryResult( $db, $queryResult ) {
00570         $this->profileIn();
00571         $this->initFromQueryResult( $queryResult );
00572         $this->profileOut();
00573     }
00574 
00579     public function populateFromRevisionIDs( $revIDs ) {
00580         $this->profileIn();
00581         $this->initFromRevIDs( $revIDs );
00582         $this->profileOut();
00583     }
00584 
00589     public function processDbRow( $row ) {
00590         // Store Title object in various data structures
00591         $title = Title::newFromRow( $row );
00592 
00593         $pageId = intval( $row->page_id );
00594         $this->mAllPages[$row->page_namespace][$row->page_title] = $pageId;
00595         $this->mTitles[] = $title;
00596 
00597         if ( $this->mResolveRedirects && $row->page_is_redirect == '1' ) {
00598             $this->mPendingRedirectIDs[$pageId] = $title;
00599         } else {
00600             $this->mGoodTitles[$pageId] = $title;
00601         }
00602 
00603         foreach ( $this->mRequestedPageFields as $fieldName => &$fieldValues ) {
00604             $fieldValues[$pageId] = $row->$fieldName;
00605         }
00606     }
00607 
00612     public function finishPageSetGeneration() {
00613         wfDeprecated( __METHOD__, '1.21' );
00614     }
00615 
00632     private function initFromTitles( $titles ) {
00633         // Get validated and normalized title objects
00634         $linkBatch = $this->processTitlesArray( $titles );
00635         if ( $linkBatch->isEmpty() ) {
00636             return;
00637         }
00638 
00639         $db = $this->getDB();
00640         $set = $linkBatch->constructSet( 'page', $db );
00641 
00642         // Get pageIDs data from the `page` table
00643         $this->profileDBIn();
00644         $res = $db->select( 'page', $this->getPageTableFields(), $set,
00645                     __METHOD__ );
00646         $this->profileDBOut();
00647 
00648         // Hack: get the ns:titles stored in array(ns => array(titles)) format
00649         $this->initFromQueryResult( $res, $linkBatch->data, true ); // process Titles
00650 
00651         // Resolve any found redirects
00652         $this->resolvePendingRedirects();
00653     }
00654 
00659     private function initFromPageIds( $pageids ) {
00660         if ( !$pageids ) {
00661             return;
00662         }
00663 
00664         $pageids = array_map( 'intval', $pageids ); // paranoia
00665         $remaining = array_flip( $pageids );
00666 
00667         $pageids = self::getPositiveIntegers( $pageids );
00668 
00669         $res = null;
00670         if ( !empty( $pageids ) ) {
00671             $set = array(
00672                 'page_id' => $pageids
00673             );
00674             $db = $this->getDB();
00675 
00676             // Get pageIDs data from the `page` table
00677             $this->profileDBIn();
00678             $res = $db->select( 'page', $this->getPageTableFields(), $set,
00679                         __METHOD__ );
00680             $this->profileDBOut();
00681         }
00682 
00683         $this->initFromQueryResult( $res, $remaining, false ); // process PageIDs
00684 
00685         // Resolve any found redirects
00686         $this->resolvePendingRedirects();
00687     }
00688 
00699     private function initFromQueryResult( $res, &$remaining = null, $processTitles = null ) {
00700         if ( !is_null( $remaining ) && is_null( $processTitles ) ) {
00701             ApiBase::dieDebug( __METHOD__, 'Missing $processTitles parameter when $remaining is provided' );
00702         }
00703 
00704         $usernames = array();
00705         if ( $res ) {
00706             foreach ( $res as $row ) {
00707                 $pageId = intval( $row->page_id );
00708 
00709                 // Remove found page from the list of remaining items
00710                 if ( isset( $remaining ) ) {
00711                     if ( $processTitles ) {
00712                         unset( $remaining[$row->page_namespace][$row->page_title] );
00713                     } else {
00714                         unset( $remaining[$pageId] );
00715                     }
00716                 }
00717 
00718                 // Store any extra fields requested by modules
00719                 $this->processDbRow( $row );
00720 
00721                 // Need gender information
00722                 if ( MWNamespace::hasGenderDistinction( $row->page_namespace ) ) {
00723                     $usernames[] = $row->page_title;
00724                 }
00725             }
00726         }
00727 
00728         if ( isset( $remaining ) ) {
00729             // Any items left in the $remaining list are added as missing
00730             if ( $processTitles ) {
00731                 // The remaining titles in $remaining are non-existent pages
00732                 foreach ( $remaining as $ns => $dbkeys ) {
00733                     foreach ( array_keys( $dbkeys ) as $dbkey ) {
00734                         $title = Title::makeTitle( $ns, $dbkey );
00735                         $this->mAllPages[$ns][$dbkey] = $this->mFakePageId;
00736                         $this->mMissingTitles[$this->mFakePageId] = $title;
00737                         $this->mFakePageId--;
00738                         $this->mTitles[] = $title;
00739 
00740                         // need gender information
00741                         if ( MWNamespace::hasGenderDistinction( $ns ) ) {
00742                             $usernames[] = $dbkey;
00743                         }
00744                     }
00745                 }
00746             } else {
00747                 // The remaining pageids do not exist
00748                 if ( !$this->mMissingPageIDs ) {
00749                     $this->mMissingPageIDs = array_keys( $remaining );
00750                 } else {
00751                     $this->mMissingPageIDs = array_merge( $this->mMissingPageIDs, array_keys( $remaining ) );
00752                 }
00753             }
00754         }
00755 
00756         // Get gender information
00757         $genderCache = GenderCache::singleton();
00758         $genderCache->doQuery( $usernames, __METHOD__ );
00759     }
00760 
00766     private function initFromRevIDs( $revids ) {
00767         if ( !$revids ) {
00768             return;
00769         }
00770 
00771         $revids = array_map( 'intval', $revids ); // paranoia
00772         $db = $this->getDB();
00773         $pageids = array();
00774         $remaining = array_flip( $revids );
00775 
00776         $revids = self::getPositiveIntegers( $revids );
00777 
00778         if ( !empty( $revids ) ) {
00779             $tables = array( 'revision', 'page' );
00780             $fields = array( 'rev_id', 'rev_page' );
00781             $where = array( 'rev_id' => $revids, 'rev_page = page_id' );
00782 
00783             // Get pageIDs data from the `page` table
00784             $this->profileDBIn();
00785             $res = $db->select( $tables, $fields, $where, __METHOD__ );
00786             foreach ( $res as $row ) {
00787                 $revid = intval( $row->rev_id );
00788                 $pageid = intval( $row->rev_page );
00789                 $this->mGoodRevIDs[$revid] = $pageid;
00790                 $pageids[$pageid] = '';
00791                 unset( $remaining[$revid] );
00792             }
00793             $this->profileDBOut();
00794         }
00795 
00796         $this->mMissingRevIDs = array_keys( $remaining );
00797 
00798         // Populate all the page information
00799         $this->initFromPageIds( array_keys( $pageids ) );
00800     }
00801 
00807     private function resolvePendingRedirects() {
00808         if ( $this->mResolveRedirects ) {
00809             $db = $this->getDB();
00810             $pageFlds = $this->getPageTableFields();
00811 
00812             // Repeat until all redirects have been resolved
00813             // The infinite loop is prevented by keeping all known pages in $this->mAllPages
00814             while ( $this->mPendingRedirectIDs ) {
00815                 // Resolve redirects by querying the pagelinks table, and repeat the process
00816                 // Create a new linkBatch object for the next pass
00817                 $linkBatch = $this->getRedirectTargets();
00818 
00819                 if ( $linkBatch->isEmpty() ) {
00820                     break;
00821                 }
00822 
00823                 $set = $linkBatch->constructSet( 'page', $db );
00824                 if ( $set === false ) {
00825                     break;
00826                 }
00827 
00828                 // Get pageIDs data from the `page` table
00829                 $this->profileDBIn();
00830                 $res = $db->select( 'page', $pageFlds, $set, __METHOD__ );
00831                 $this->profileDBOut();
00832 
00833                 // Hack: get the ns:titles stored in array(ns => array(titles)) format
00834                 $this->initFromQueryResult( $res, $linkBatch->data, true );
00835             }
00836         }
00837     }
00838 
00846     private function getRedirectTargets() {
00847         $lb = new LinkBatch();
00848         $db = $this->getDB();
00849 
00850         $this->profileDBIn();
00851         $res = $db->select(
00852             'redirect',
00853             array(
00854                 'rd_from',
00855                 'rd_namespace',
00856                 'rd_fragment',
00857                 'rd_interwiki',
00858                 'rd_title'
00859             ), array( 'rd_from' => array_keys( $this->mPendingRedirectIDs ) ),
00860             __METHOD__
00861         );
00862         $this->profileDBOut();
00863         foreach ( $res as $row ) {
00864             $rdfrom = intval( $row->rd_from );
00865             $from = $this->mPendingRedirectIDs[$rdfrom]->getPrefixedText();
00866             $to = Title::makeTitle( $row->rd_namespace, $row->rd_title, $row->rd_fragment, $row->rd_interwiki );
00867             unset( $this->mPendingRedirectIDs[$rdfrom] );
00868             if ( !$to->isExternal() && !isset( $this->mAllPages[$row->rd_namespace][$row->rd_title] ) ) {
00869                 $lb->add( $row->rd_namespace, $row->rd_title );
00870             }
00871             $this->mRedirectTitles[$from] = $to;
00872         }
00873 
00874         if ( $this->mPendingRedirectIDs ) {
00875             // We found pages that aren't in the redirect table
00876             // Add them
00877             foreach ( $this->mPendingRedirectIDs as $id => $title ) {
00878                 $page = WikiPage::factory( $title );
00879                 $rt = $page->insertRedirect();
00880                 if ( !$rt ) {
00881                     // What the hell. Let's just ignore this
00882                     continue;
00883                 }
00884                 $lb->addObj( $rt );
00885                 $this->mRedirectTitles[$title->getPrefixedText()] = $rt;
00886                 unset( $this->mPendingRedirectIDs[$id] );
00887             }
00888         }
00889         return $lb;
00890     }
00891 
00905     public function getCacheMode( $params = null ) {
00906         return $this->mCacheMode;
00907     }
00908 
00918     private function processTitlesArray( $titles ) {
00919         $usernames = array();
00920         $linkBatch = new LinkBatch();
00921 
00922         foreach ( $titles as $title ) {
00923             if ( is_string( $title ) ) {
00924                 $titleObj = Title::newFromText( $title, $this->mDefaultNamespace );
00925             } else {
00926                 $titleObj = $title;
00927             }
00928             if ( !$titleObj ) {
00929                 // Handle invalid titles gracefully
00930                 $this->mAllPages[0][$title] = $this->mFakePageId;
00931                 $this->mInvalidTitles[$this->mFakePageId] = $title;
00932                 $this->mFakePageId--;
00933                 continue; // There's nothing else we can do
00934             }
00935             $unconvertedTitle = $titleObj->getPrefixedText();
00936             $titleWasConverted = false;
00937             if ( $titleObj->isExternal() ) {
00938                 // This title is an interwiki link.
00939                 $this->mInterwikiTitles[$unconvertedTitle] = $titleObj->getInterwiki();
00940             } else {
00941                 // Variants checking
00942                 global $wgContLang;
00943                 if ( $this->mConvertTitles &&
00944                         count( $wgContLang->getVariants() ) > 1 &&
00945                         !$titleObj->exists() ) {
00946                     // Language::findVariantLink will modify titleText and titleObj into
00947                     // the canonical variant if possible
00948                     $titleText = is_string( $title ) ? $title : $titleObj->getPrefixedText();
00949                     $wgContLang->findVariantLink( $titleText, $titleObj );
00950                     $titleWasConverted = $unconvertedTitle !== $titleObj->getPrefixedText();
00951                 }
00952 
00953                 if ( $titleObj->getNamespace() < 0 ) {
00954                     // Handle Special and Media pages
00955                     $titleObj = $titleObj->fixSpecialName();
00956                     $this->mSpecialTitles[$this->mFakePageId] = $titleObj;
00957                     $this->mFakePageId--;
00958                 } else {
00959                     // Regular page
00960                     $linkBatch->addObj( $titleObj );
00961                 }
00962             }
00963 
00964             // Make sure we remember the original title that was
00965             // given to us. This way the caller can correlate new
00966             // titles with the originally requested when e.g. the
00967             // namespace is localized or the capitalization is
00968             // different
00969             if ( $titleWasConverted ) {
00970                 $this->mConvertedTitles[$unconvertedTitle] = $titleObj->getPrefixedText();
00971                 // In this case the page can't be Special.
00972                 if ( is_string( $title ) && $title !== $unconvertedTitle ) {
00973                     $this->mNormalizedTitles[$title] = $unconvertedTitle;
00974                 }
00975             } elseif ( is_string( $title ) && $title !== $titleObj->getPrefixedText() ) {
00976                 $this->mNormalizedTitles[$title] = $titleObj->getPrefixedText();
00977             }
00978 
00979             // Need gender information
00980             if ( MWNamespace::hasGenderDistinction( $titleObj->getNamespace() ) ) {
00981                 $usernames[] = $titleObj->getText();
00982             }
00983         }
00984         // Get gender information
00985         $genderCache = GenderCache::singleton();
00986         $genderCache->doQuery( $usernames, __METHOD__ );
00987 
00988         return $linkBatch;
00989     }
00990 
00995     protected function getDB() {
00996         return $this->mDbSource->getDB();
00997     }
00998 
01005     private static function getPositiveIntegers( $array ) {
01006         // bug 25734 API: possible issue with revids validation
01007         // It seems with a load of revision rows, MySQL gets upset
01008         // Remove any < 0 integers, as they can't be valid
01009         foreach ( $array as $i => $int ) {
01010             if ( $int < 0 ) {
01011                 unset( $array[$i] );
01012             }
01013         }
01014 
01015         return $array;
01016     }
01017 
01018     public function getAllowedParams( $flags = 0 ) {
01019         $result = array(
01020             'titles' => array(
01021                 ApiBase::PARAM_ISMULTI => true
01022             ),
01023             'pageids' => array(
01024                 ApiBase::PARAM_TYPE => 'integer',
01025                 ApiBase::PARAM_ISMULTI => true
01026             ),
01027             'revids' => array(
01028                 ApiBase::PARAM_TYPE => 'integer',
01029                 ApiBase::PARAM_ISMULTI => true
01030             ),
01031             'redirects' => false,
01032             'converttitles' => false,
01033         );
01034         if ( $this->mAllowGenerator ) {
01035             if ( $flags & ApiBase::GET_VALUES_FOR_HELP ) {
01036                 $result['generator'] = array(
01037                     ApiBase::PARAM_TYPE => $this->getGenerators()
01038                 );
01039             } else {
01040                 $result['generator'] = null;
01041             }
01042         }
01043         return $result;
01044     }
01045 
01046     private static $generators = null;
01047 
01052     private function getGenerators() {
01053         if ( self::$generators === null ) {
01054             $query = $this->mDbSource;
01055             if ( !( $query instanceof ApiQuery ) ) {
01056                 // If the parent container of this pageset is not ApiQuery,
01057                 // we must create it to get module manager
01058                 $query = $this->getMain()->getModuleManager()->getModule( 'query' );
01059             }
01060             $gens = array();
01061             $mgr = $query->getModuleManager();
01062             foreach ( $mgr->getNamesWithClasses() as $name => $class ) {
01063                 if ( is_subclass_of( $class, 'ApiQueryGeneratorBase' ) ) {
01064                     $gens[] = $name;
01065                 }
01066             }
01067             sort( $gens );
01068             self::$generators = $gens;
01069         }
01070         return self::$generators;
01071     }
01072 
01073     public function getParamDescription() {
01074         return array(
01075             'titles' => 'A list of titles to work on',
01076             'pageids' => 'A list of page IDs to work on',
01077             'revids' => 'A list of revision IDs to work on',
01078             'generator' => array( 'Get the list of pages to work on by executing the specified query module.',
01079                 'NOTE: generator parameter names must be prefixed with a \'g\', see examples' ),
01080             'redirects' => 'Automatically resolve redirects',
01081             'converttitles' => array( 'Convert titles to other variants if necessary. Only works if the wiki\'s content language supports variant conversion.',
01082                 'Languages that support variant conversion include ' . implode( ', ', LanguageConverter::$languagesWithVariants ) ),
01083         );
01084     }
01085 
01086     public function getPossibleErrors() {
01087         return array_merge( parent::getPossibleErrors(), array(
01088             array( 'code' => 'multisource', 'info' => "Cannot use 'pageids' at the same time as 'dataSource'" ),
01089             array( 'code' => 'multisource', 'info' => "Cannot use 'revids' at the same time as 'dataSource'" ),
01090             array( 'code' => 'badgenerator', 'info' => 'Module $generatorName cannot be used as a generator' ),
01091         ) );
01092     }
01093 }