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