MediaWiki  REL1_21
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();
00072         private $mDefaultNamespace = NS_MAIN;
00073 
00082         public function __construct( ApiBase $dbSource, $flags = 0, $defaultNamespace = NS_MAIN ) {
00083                 parent::__construct( $dbSource->getMain(), $dbSource->getModuleName() );
00084                 $this->mDbSource = $dbSource;
00085                 $this->mAllowGenerator = ( $flags & ApiPageSet::DISABLE_GENERATORS ) == 0;
00086                 $this->mDefaultNamespace = $defaultNamespace;
00087 
00088                 $this->profileIn();
00089                 $this->mParams = $this->extractRequestParams();
00090                 $this->mResolveRedirects = $this->mParams['redirects'];
00091                 $this->mConvertTitles = $this->mParams['converttitles'];
00092                 $this->profileOut();
00093         }
00094 
00099         public function executeDryRun() {
00100                 $this->executeInternal( true );
00101         }
00102 
00106         public function execute() {
00107                 $this->executeInternal( false );
00108         }
00109 
00114         private function executeInternal( $isDryRun ) {
00115                 $this->profileIn();
00116 
00117                 $generatorName = $this->mAllowGenerator ? $this->mParams['generator'] : null;
00118                 if ( isset( $generatorName ) ) {
00119                         $dbSource = $this->mDbSource;
00120                         $isQuery = $dbSource instanceof ApiQuery;
00121                         if ( !$isQuery ) {
00122                                 // If the parent container of this pageset is not ApiQuery, we must create it to run generator
00123                                 $dbSource = $this->getMain()->getModuleManager()->getModule( 'query' );
00124                                 // Enable profiling for query module because it will be used for db sql profiling
00125                                 $dbSource->profileIn();
00126                         }
00127                         $generator = $dbSource->getModuleManager()->getModule( $generatorName, null, true );
00128                         if ( $generator === null ) {
00129                                 $this->dieUsage( 'Unknown generator=' . $generatorName, 'badgenerator' );
00130                         }
00131                         if ( !$generator instanceof ApiQueryGeneratorBase ) {
00132                                 $this->dieUsage( "Module $generatorName cannot be used as a generator", 'badgenerator' );
00133                         }
00134                         // Create a temporary pageset to store generator's output,
00135                         // add any additional fields generator may need, and execute pageset to populate titles/pageids
00136                         $tmpPageSet = new ApiPageSet( $dbSource, ApiPageSet::DISABLE_GENERATORS );
00137                         $generator->setGeneratorMode( $tmpPageSet );
00138                         $this->mCacheMode = $generator->getCacheMode( $generator->extractRequestParams() );
00139 
00140                         if ( !$isDryRun ) {
00141                                 $generator->requestExtraData( $tmpPageSet );
00142                         }
00143                         $tmpPageSet->executeInternal( $isDryRun );
00144 
00145                         // populate this pageset with the generator output
00146                         $this->profileOut();
00147                         $generator->profileIn();
00148 
00149                         if ( !$isDryRun ) {
00150                                 $generator->executeGenerator( $this );
00151                                 wfRunHooks( 'APIQueryGeneratorAfterExecute', array( &$generator, &$this ) );
00152                                 $this->resolvePendingRedirects();
00153                         } else {
00154                                 // Prevent warnings from being reported on these parameters
00155                                 $main = $this->getMain();
00156                                 foreach ( $generator->extractRequestParams() as $paramName => $param ) {
00157                                         $main->getVal( $generator->encodeParamName( $paramName ) );
00158                                 }
00159                         }
00160                         $generator->profileOut();
00161                         $this->profileIn();
00162 
00163                         if ( !$isQuery ) {
00164                                 // If this pageset is not part of the query, we called profileIn() above
00165                                 $dbSource->profileOut();
00166                         }
00167                 } else {
00168                         // Only one of the titles/pageids/revids is allowed at the same time
00169                         $dataSource = null;
00170                         if ( isset( $this->mParams['titles'] ) ) {
00171                                 $dataSource = 'titles';
00172                         }
00173                         if ( isset( $this->mParams['pageids'] ) ) {
00174                                 if ( isset( $dataSource ) ) {
00175                                         $this->dieUsage( "Cannot use 'pageids' at the same time as '$dataSource'", 'multisource' );
00176                                 }
00177                                 $dataSource = 'pageids';
00178                         }
00179                         if ( isset( $this->mParams['revids'] ) ) {
00180                                 if ( isset( $dataSource ) ) {
00181                                         $this->dieUsage( "Cannot use 'revids' at the same time as '$dataSource'", 'multisource' );
00182                                 }
00183                                 $dataSource = 'revids';
00184                         }
00185 
00186                         if ( !$isDryRun ) {
00187                                 // Populate page information with the original user input
00188                                 switch( $dataSource ) {
00189                                         case 'titles':
00190                                                 $this->initFromTitles( $this->mParams['titles'] );
00191                                                 break;
00192                                         case 'pageids':
00193                                                 $this->initFromPageIds( $this->mParams['pageids'] );
00194                                                 break;
00195                                         case 'revids':
00196                                                 if ( $this->mResolveRedirects ) {
00197                                                         $this->setWarning( 'Redirect resolution cannot be used together with the revids= parameter. ' .
00198                                                                 'Any redirects the revids= point to have not been resolved.' );
00199                                                 }
00200                                                 $this->mResolveRedirects = false;
00201                                                 $this->initFromRevIDs( $this->mParams['revids'] );
00202                                                 break;
00203                                         default:
00204                                                 // Do nothing - some queries do not need any of the data sources.
00205                                                 break;
00206                                 }
00207                         }
00208                 }
00209                 $this->profileOut();
00210         }
00211 
00216         public function isResolvingRedirects() {
00217                 return $this->mResolveRedirects;
00218         }
00219 
00228         public function getDataSource() {
00229                 if ( $this->mAllowGenerator && isset( $this->mParams['generator'] ) ) {
00230                         return 'generator';
00231                 }
00232                 if ( isset( $this->mParams['titles'] ) ) {
00233                         return 'titles';
00234                 }
00235                 if ( isset( $this->mParams['pageids'] ) ) {
00236                         return 'pageids';
00237                 }
00238                 if ( isset( $this->mParams['revids'] ) ) {
00239                         return 'revids';
00240                 }
00241                 return null;
00242         }
00243 
00249         public function requestField( $fieldName ) {
00250                 $this->mRequestedPageFields[$fieldName] = null;
00251         }
00252 
00259         public function getCustomField( $fieldName ) {
00260                 return $this->mRequestedPageFields[$fieldName];
00261         }
00262 
00269         public function getPageTableFields() {
00270                 // Ensure we get minimum required fields
00271                 // DON'T change this order
00272                 $pageFlds = array(
00273                         'page_namespace' => null,
00274                         'page_title' => null,
00275                         'page_id' => null,
00276                 );
00277 
00278                 if ( $this->mResolveRedirects ) {
00279                         $pageFlds['page_is_redirect'] = null;
00280                 }
00281 
00282                 // only store non-default fields
00283                 $this->mRequestedPageFields = array_diff_key( $this->mRequestedPageFields, $pageFlds );
00284 
00285                 $pageFlds = array_merge( $pageFlds, $this->mRequestedPageFields );
00286                 return array_keys( $pageFlds );
00287         }
00288 
00295         public function getAllTitlesByNamespace() {
00296                 return $this->mAllPages;
00297         }
00298 
00303         public function getTitles() {
00304                 return $this->mTitles;
00305         }
00306 
00311         public function getTitleCount() {
00312                 return count( $this->mTitles );
00313         }
00314 
00319         public function getGoodTitles() {
00320                 return $this->mGoodTitles;
00321         }
00322 
00327         public function getGoodTitleCount() {
00328                 return count( $this->mGoodTitles );
00329         }
00330 
00336         public function getMissingTitles() {
00337                 return $this->mMissingTitles;
00338         }
00339 
00345         public function getInvalidTitles() {
00346                 return $this->mInvalidTitles;
00347         }
00348 
00353         public function getMissingPageIDs() {
00354                 return $this->mMissingPageIDs;
00355         }
00356 
00362         public function getRedirectTitles() {
00363                 return $this->mRedirectTitles;
00364         }
00365 
00373         public function getRedirectTitlesAsResult( $result = null ) {
00374                 $values = array();
00375                 foreach ( $this->getRedirectTitles() as $titleStrFrom => $titleTo ) {
00376                         $r = array(
00377                                 'from' => strval( $titleStrFrom ),
00378                                 'to' => $titleTo->getPrefixedText(),
00379                         );
00380                         if ( $titleTo->getFragment() !== '' ) {
00381                                 $r['tofragment'] = $titleTo->getFragment();
00382                         }
00383                         $values[] = $r;
00384                 }
00385                 if ( !empty( $values ) && $result ) {
00386                         $result->setIndexedTagName( $values, 'r' );
00387                 }
00388                 return $values;
00389         }
00390 
00396         public function getNormalizedTitles() {
00397                 return $this->mNormalizedTitles;
00398         }
00399 
00407         public function getNormalizedTitlesAsResult( $result = null  ) {
00408                 $values = array();
00409                 foreach ( $this->getNormalizedTitles() as $rawTitleStr => $titleStr ) {
00410                         $values[] = array(
00411                                 'from' => $rawTitleStr,
00412                                 'to' => $titleStr
00413                         );
00414                 }
00415                 if ( !empty( $values ) && $result ) {
00416                         $result->setIndexedTagName( $values, 'n' );
00417                 }
00418                 return $values;
00419         }
00420 
00426         public function getConvertedTitles() {
00427                 return $this->mConvertedTitles;
00428         }
00429 
00437         public function getConvertedTitlesAsResult( $result = null ) {
00438                 $values = array();
00439                 foreach ( $this->getConvertedTitles() as $rawTitleStr => $titleStr ) {
00440                         $values[] = array(
00441                                 'from' => $rawTitleStr,
00442                                 'to' => $titleStr
00443                         );
00444                 }
00445                 if ( !empty( $values ) && $result ) {
00446                         $result->setIndexedTagName( $values, 'c' );
00447                 }
00448                 return $values;
00449         }
00450 
00456         public function getInterwikiTitles() {
00457                 return $this->mInterwikiTitles;
00458         }
00459 
00468         public function getInterwikiTitlesAsResult( $result = null, $iwUrl = false ) {
00469                 $values = array();
00470                 foreach ( $this->getInterwikiTitles() as $rawTitleStr => $interwikiStr ) {
00471                         $item = array(
00472                                 'title' => $rawTitleStr,
00473                                 'iw' => $interwikiStr,
00474                         );
00475                         if ( $iwUrl ) {
00476                                 $title = Title::newFromText( $rawTitleStr );
00477                                 $item['url'] = $title->getFullURL( '', false, PROTO_CURRENT );
00478                         }
00479                         $values[] = $item;
00480                 }
00481                 if ( !empty( $values ) && $result ) {
00482                         $result->setIndexedTagName( $values, 'i' );
00483                 }
00484                 return $values;
00485         }
00486 
00491         public function getRevisionIDs() {
00492                 return $this->mGoodRevIDs;
00493         }
00494 
00499         public function getMissingRevisionIDs() {
00500                 return $this->mMissingRevIDs;
00501         }
00502 
00509         public function getMissingRevisionIDsAsResult( $result = null ) {
00510                 $values = array();
00511                 foreach ( $this->getMissingRevisionIDs() as $revid ) {
00512                         $values[$revid] = array(
00513                                 'revid' => $revid
00514                         );
00515                 }
00516                 if ( !empty( $values ) && $result ) {
00517                         $result->setIndexedTagName( $values, 'rev' );
00518                 }
00519                 return $values;
00520         }
00521 
00526         public function getSpecialTitles() {
00527                 return $this->mSpecialTitles;
00528         }
00529 
00534         public function getRevisionCount() {
00535                 return count( $this->getRevisionIDs() );
00536         }
00537 
00542         public function populateFromTitles( $titles ) {
00543                 $this->profileIn();
00544                 $this->initFromTitles( $titles );
00545                 $this->profileOut();
00546         }
00547 
00552         public function populateFromPageIDs( $pageIDs ) {
00553                 $this->profileIn();
00554                 $this->initFromPageIds( $pageIDs );
00555                 $this->profileOut();
00556         }
00557 
00563         public function populateFromQueryResult( $db, $queryResult ) {
00564                 $this->profileIn();
00565                 $this->initFromQueryResult( $queryResult );
00566                 $this->profileOut();
00567         }
00568 
00573         public function populateFromRevisionIDs( $revIDs ) {
00574                 $this->profileIn();
00575                 $this->initFromRevIDs( $revIDs );
00576                 $this->profileOut();
00577         }
00578 
00583         public function processDbRow( $row ) {
00584                 // Store Title object in various data structures
00585                 $title = Title::newFromRow( $row );
00586 
00587                 $pageId = intval( $row->page_id );
00588                 $this->mAllPages[$row->page_namespace][$row->page_title] = $pageId;
00589                 $this->mTitles[] = $title;
00590 
00591                 if ( $this->mResolveRedirects && $row->page_is_redirect == '1' ) {
00592                         $this->mPendingRedirectIDs[$pageId] = $title;
00593                 } else {
00594                         $this->mGoodTitles[$pageId] = $title;
00595                 }
00596 
00597                 foreach ( $this->mRequestedPageFields as $fieldName => &$fieldValues ) {
00598                         $fieldValues[$pageId] = $row-> $fieldName;
00599                 }
00600         }
00601 
00606         public function finishPageSetGeneration() {
00607                 wfDeprecated( __METHOD__, '1.21' );
00608         }
00609 
00626         private function initFromTitles( $titles ) {
00627                 // Get validated and normalized title objects
00628                 $linkBatch = $this->processTitlesArray( $titles );
00629                 if ( $linkBatch->isEmpty() ) {
00630                         return;
00631                 }
00632 
00633                 $db = $this->getDB();
00634                 $set = $linkBatch->constructSet( 'page', $db );
00635 
00636                 // Get pageIDs data from the `page` table
00637                 $this->profileDBIn();
00638                 $res = $db->select( 'page', $this->getPageTableFields(), $set,
00639                                         __METHOD__ );
00640                 $this->profileDBOut();
00641 
00642                 // Hack: get the ns:titles stored in array(ns => array(titles)) format
00643                 $this->initFromQueryResult( $res, $linkBatch->data, true ); // process Titles
00644 
00645                 // Resolve any found redirects
00646                 $this->resolvePendingRedirects();
00647         }
00648 
00653         private function initFromPageIds( $pageids ) {
00654                 if ( !$pageids ) {
00655                         return;
00656                 }
00657 
00658                 $pageids = array_map( 'intval', $pageids ); // paranoia
00659                 $remaining = array_flip( $pageids );
00660 
00661                 $pageids = self::getPositiveIntegers( $pageids );
00662 
00663                 $res = null;
00664                 if ( !empty( $pageids ) ) {
00665                         $set = array(
00666                                 'page_id' => $pageids
00667                         );
00668                         $db = $this->getDB();
00669 
00670                         // Get pageIDs data from the `page` table
00671                         $this->profileDBIn();
00672                         $res = $db->select( 'page', $this->getPageTableFields(), $set,
00673                                                 __METHOD__ );
00674                         $this->profileDBOut();
00675                 }
00676 
00677                 $this->initFromQueryResult( $res, $remaining, false ); // process PageIDs
00678 
00679                 // Resolve any found redirects
00680                 $this->resolvePendingRedirects();
00681         }
00682 
00693         private function initFromQueryResult( $res, &$remaining = null, $processTitles = null ) {
00694                 if ( !is_null( $remaining ) && is_null( $processTitles ) ) {
00695                         ApiBase::dieDebug( __METHOD__, 'Missing $processTitles parameter when $remaining is provided' );
00696                 }
00697 
00698                 $usernames = array();
00699                 if ( $res ) {
00700                         foreach ( $res as $row ) {
00701                                 $pageId = intval( $row->page_id );
00702 
00703                                 // Remove found page from the list of remaining items
00704                                 if ( isset( $remaining ) ) {
00705                                         if ( $processTitles ) {
00706                                                 unset( $remaining[$row->page_namespace][$row->page_title] );
00707                                         } else {
00708                                                 unset( $remaining[$pageId] );
00709                                         }
00710                                 }
00711 
00712                                 // Store any extra fields requested by modules
00713                                 $this->processDbRow( $row );
00714 
00715                                 // Need gender information
00716                                 if ( MWNamespace::hasGenderDistinction( $row->page_namespace ) ) {
00717                                         $usernames[] = $row->page_title;
00718                                 }
00719                         }
00720                 }
00721 
00722                 if ( isset( $remaining ) ) {
00723                         // Any items left in the $remaining list are added as missing
00724                         if ( $processTitles ) {
00725                                 // The remaining titles in $remaining are non-existent pages
00726                                 foreach ( $remaining as $ns => $dbkeys ) {
00727                                         foreach ( array_keys( $dbkeys ) as $dbkey ) {
00728                                                 $title = Title::makeTitle( $ns, $dbkey );
00729                                                 $this->mAllPages[$ns][$dbkey] = $this->mFakePageId;
00730                                                 $this->mMissingTitles[$this->mFakePageId] = $title;
00731                                                 $this->mFakePageId--;
00732                                                 $this->mTitles[] = $title;
00733 
00734                                                 // need gender information
00735                                                 if ( MWNamespace::hasGenderDistinction( $ns ) ) {
00736                                                         $usernames[] = $dbkey;
00737                                                 }
00738                                         }
00739                                 }
00740                         } else {
00741                                 // The remaining pageids do not exist
00742                                 if ( !$this->mMissingPageIDs ) {
00743                                         $this->mMissingPageIDs = array_keys( $remaining );
00744                                 } else {
00745                                         $this->mMissingPageIDs = array_merge( $this->mMissingPageIDs, array_keys( $remaining ) );
00746                                 }
00747                         }
00748                 }
00749 
00750                 // Get gender information
00751                 $genderCache = GenderCache::singleton();
00752                 $genderCache->doQuery( $usernames, __METHOD__ );
00753         }
00754 
00760         private function initFromRevIDs( $revids ) {
00761                 if ( !$revids ) {
00762                         return;
00763                 }
00764 
00765                 $revids = array_map( 'intval', $revids ); // paranoia
00766                 $db = $this->getDB();
00767                 $pageids = array();
00768                 $remaining = array_flip( $revids );
00769 
00770                 $revids = self::getPositiveIntegers( $revids );
00771 
00772                 if ( !empty( $revids ) ) {
00773                         $tables = array( 'revision', 'page' );
00774                         $fields = array( 'rev_id', 'rev_page' );
00775                         $where = array( 'rev_id' => $revids, 'rev_page = page_id' );
00776 
00777                         // Get pageIDs data from the `page` table
00778                         $this->profileDBIn();
00779                         $res = $db->select( $tables, $fields, $where, __METHOD__ );
00780                         foreach ( $res as $row ) {
00781                                 $revid = intval( $row->rev_id );
00782                                 $pageid = intval( $row->rev_page );
00783                                 $this->mGoodRevIDs[$revid] = $pageid;
00784                                 $pageids[$pageid] = '';
00785                                 unset( $remaining[$revid] );
00786                         }
00787                         $this->profileDBOut();
00788                 }
00789 
00790                 $this->mMissingRevIDs = array_keys( $remaining );
00791 
00792                 // Populate all the page information
00793                 $this->initFromPageIds( array_keys( $pageids ) );
00794         }
00795 
00801         private function resolvePendingRedirects() {
00802                 if ( $this->mResolveRedirects ) {
00803                         $db = $this->getDB();
00804                         $pageFlds = $this->getPageTableFields();
00805 
00806                         // Repeat until all redirects have been resolved
00807                         // The infinite loop is prevented by keeping all known pages in $this->mAllPages
00808                         while ( $this->mPendingRedirectIDs ) {
00809                                 // Resolve redirects by querying the pagelinks table, and repeat the process
00810                                 // Create a new linkBatch object for the next pass
00811                                 $linkBatch = $this->getRedirectTargets();
00812 
00813                                 if ( $linkBatch->isEmpty() ) {
00814                                         break;
00815                                 }
00816 
00817                                 $set = $linkBatch->constructSet( 'page', $db );
00818                                 if ( $set === false ) {
00819                                         break;
00820                                 }
00821 
00822                                 // Get pageIDs data from the `page` table
00823                                 $this->profileDBIn();
00824                                 $res = $db->select( 'page', $pageFlds, $set, __METHOD__ );
00825                                 $this->profileDBOut();
00826 
00827                                 // Hack: get the ns:titles stored in array(ns => array(titles)) format
00828                                 $this->initFromQueryResult( $res, $linkBatch->data, true );
00829                         }
00830                 }
00831         }
00832 
00840         private function getRedirectTargets() {
00841                 $lb = new LinkBatch();
00842                 $db = $this->getDB();
00843 
00844                 $this->profileDBIn();
00845                 $res = $db->select(
00846                         'redirect',
00847                         array(
00848                                 'rd_from',
00849                                 'rd_namespace',
00850                                 'rd_fragment',
00851                                 'rd_interwiki',
00852                                 'rd_title'
00853                         ), array( 'rd_from' => array_keys( $this->mPendingRedirectIDs ) ),
00854                         __METHOD__
00855                 );
00856                 $this->profileDBOut();
00857                 foreach ( $res as $row ) {
00858                         $rdfrom = intval( $row->rd_from );
00859                         $from = $this->mPendingRedirectIDs[$rdfrom]->getPrefixedText();
00860                         $to = Title::makeTitle( $row->rd_namespace, $row->rd_title, $row->rd_fragment, $row->rd_interwiki );
00861                         unset( $this->mPendingRedirectIDs[$rdfrom] );
00862                         if ( !isset( $this->mAllPages[$row->rd_namespace][$row->rd_title] ) ) {
00863                                 $lb->add( $row->rd_namespace, $row->rd_title );
00864                         }
00865                         $this->mRedirectTitles[$from] = $to;
00866                 }
00867 
00868                 if ( $this->mPendingRedirectIDs ) {
00869                         // We found pages that aren't in the redirect table
00870                         // Add them
00871                         foreach ( $this->mPendingRedirectIDs as $id => $title ) {
00872                                 $page = WikiPage::factory( $title );
00873                                 $rt = $page->insertRedirect();
00874                                 if ( !$rt ) {
00875                                         // What the hell. Let's just ignore this
00876                                         continue;
00877                                 }
00878                                 $lb->addObj( $rt );
00879                                 $this->mRedirectTitles[$title->getPrefixedText()] = $rt;
00880                                 unset( $this->mPendingRedirectIDs[$id] );
00881                         }
00882                 }
00883                 return $lb;
00884         }
00885 
00899         public function getCacheMode( $params = null ) {
00900                 return $this->mCacheMode;
00901         }
00902 
00912         private function processTitlesArray( $titles ) {
00913                 $usernames = array();
00914                 $linkBatch = new LinkBatch();
00915 
00916                 foreach ( $titles as $title ) {
00917                         if ( is_string( $title ) ) {
00918                                 $titleObj = Title::newFromText( $title, $this->mDefaultNamespace );
00919                         } else {
00920                                 $titleObj = $title;
00921                         }
00922                         if ( !$titleObj ) {
00923                                 // Handle invalid titles gracefully
00924                                 $this->mAllPages[0][$title] = $this->mFakePageId;
00925                                 $this->mInvalidTitles[$this->mFakePageId] = $title;
00926                                 $this->mFakePageId--;
00927                                 continue; // There's nothing else we can do
00928                         }
00929                         $unconvertedTitle = $titleObj->getPrefixedText();
00930                         $titleWasConverted = false;
00931                         if ( $titleObj->isExternal() ) {
00932                                 // This title is an interwiki link.
00933                                 $this->mInterwikiTitles[$unconvertedTitle] = $titleObj->getInterwiki();
00934                         } else {
00935                                 // Variants checking
00936                                 global $wgContLang;
00937                                 if ( $this->mConvertTitles &&
00938                                                 count( $wgContLang->getVariants() ) > 1 &&
00939                                                 !$titleObj->exists() ) {
00940                                         // Language::findVariantLink will modify titleText and titleObj into
00941                                         // the canonical variant if possible
00942                                         $titleText = is_string( $title ) ? $title : $titleObj->getPrefixedText();
00943                                         $wgContLang->findVariantLink( $titleText, $titleObj );
00944                                         $titleWasConverted = $unconvertedTitle !== $titleObj->getPrefixedText();
00945                                 }
00946 
00947                                 if ( $titleObj->getNamespace() < 0 ) {
00948                                         // Handle Special and Media pages
00949                                         $titleObj = $titleObj->fixSpecialName();
00950                                         $this->mSpecialTitles[$this->mFakePageId] = $titleObj;
00951                                         $this->mFakePageId--;
00952                                 } else {
00953                                         // Regular page
00954                                         $linkBatch->addObj( $titleObj );
00955                                 }
00956                         }
00957 
00958                         // Make sure we remember the original title that was
00959                         // given to us. This way the caller can correlate new
00960                         // titles with the originally requested when e.g. the
00961                         // namespace is localized or the capitalization is
00962                         // different
00963                         if ( $titleWasConverted ) {
00964                                 $this->mConvertedTitles[$unconvertedTitle] = $titleObj->getPrefixedText();
00965                                 // In this case the page can't be Special.
00966                                 if ( is_string( $title ) && $title !== $unconvertedTitle ) {
00967                                         $this->mNormalizedTitles[$title] = $unconvertedTitle;
00968                                 }
00969                         } elseif ( is_string( $title ) && $title !== $titleObj->getPrefixedText() ) {
00970                                 $this->mNormalizedTitles[$title] = $titleObj->getPrefixedText();
00971                         }
00972 
00973                         // Need gender information
00974                         if ( MWNamespace::hasGenderDistinction( $titleObj->getNamespace() ) ) {
00975                                 $usernames[] = $titleObj->getText();
00976                         }
00977                 }
00978                 // Get gender information
00979                 $genderCache = GenderCache::singleton();
00980                 $genderCache->doQuery( $usernames, __METHOD__ );
00981 
00982                 return $linkBatch;
00983         }
00984 
00989         protected function getDB() {
00990                 return $this->mDbSource->getDB();
00991         }
00992 
00999         private static function getPositiveIntegers( $array ) {
01000                 // bug 25734 API: possible issue with revids validation
01001                 // It seems with a load of revision rows, MySQL gets upset
01002                 // Remove any < 0 integers, as they can't be valid
01003                 foreach ( $array as $i => $int ) {
01004                         if ( $int < 0 ) {
01005                                 unset( $array[$i] );
01006                         }
01007                 }
01008 
01009                 return $array;
01010         }
01011 
01012         public function getAllowedParams( $flags = 0 ) {
01013                 $result = array(
01014                         'titles' => array(
01015                                 ApiBase::PARAM_ISMULTI => true
01016                         ),
01017                         'pageids' => array(
01018                                 ApiBase::PARAM_TYPE => 'integer',
01019                                 ApiBase::PARAM_ISMULTI => true
01020                         ),
01021                         'revids' => array(
01022                                 ApiBase::PARAM_TYPE => 'integer',
01023                                 ApiBase::PARAM_ISMULTI => true
01024                         ),
01025                         'redirects' => false,
01026                         'converttitles' => false,
01027                 );
01028                 if ( $this->mAllowGenerator ) {
01029                         if ( $flags & ApiBase::GET_VALUES_FOR_HELP ) {
01030                                 $result['generator'] = array(
01031                                         ApiBase::PARAM_TYPE => $this->getGenerators()
01032                                 );
01033                         } else {
01034                                 $result['generator'] = null;
01035                         }
01036                 }
01037                 return $result;
01038         }
01039 
01040         private static $generators = null;
01041 
01046         private function getGenerators() {
01047                 if ( self::$generators === null ) {
01048                         $query = $this->mDbSource;
01049                         if ( !( $query instanceof ApiQuery ) ) {
01050                                 // If the parent container of this pageset is not ApiQuery,
01051                                 // we must create it to get module manager
01052                                 $query = $this->getMain()->getModuleManager()->getModule( 'query' );
01053                         }
01054                         $gens = array();
01055                         $mgr = $query->getModuleManager();
01056                         foreach ( $mgr->getNamesWithClasses() as $name => $class ) {
01057                                 if ( is_subclass_of( $class, 'ApiQueryGeneratorBase' ) ) {
01058                                         $gens[] = $name;
01059                                 }
01060                         }
01061                         sort( $gens );
01062                         self::$generators = $gens;
01063                 }
01064                 return self::$generators;
01065         }
01066 
01067         public function getParamDescription() {
01068                 return array(
01069                         'titles' => 'A list of titles to work on',
01070                         'pageids' => 'A list of page IDs to work on',
01071                         'revids' => 'A list of revision IDs to work on',
01072                         'generator' => array( 'Get the list of pages to work on by executing the specified query module.',
01073                                 'NOTE: generator parameter names must be prefixed with a \'g\', see examples' ),
01074                         'redirects' => 'Automatically resolve redirects',
01075                         'converttitles' => array( 'Convert titles to other variants if necessary. Only works if the wiki\'s content language supports variant conversion.',
01076                                 'Languages that support variant conversion include ' . implode( ', ', LanguageConverter::$languagesWithVariants ) ),
01077                 );
01078         }
01079 
01080         public function getPossibleErrors() {
01081                 return array_merge( parent::getPossibleErrors(), array(
01082                         array( 'code' => 'multisource', 'info' => "Cannot use 'pageids' at the same time as 'dataSource'" ),
01083                         array( 'code' => 'multisource', 'info' => "Cannot use 'revids' at the same time as 'dataSource'" ),
01084                         array( 'code' => 'badgenerator', 'info' => 'Module $generatorName cannot be used as a generator' ),
01085                 ) );
01086         }
01087 }