MediaWiki
REL1_22
|
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 }