API PageSet allows generator for non-query modules
[lhc/web/wiklou.git] / includes / api / ApiQuery.php
index d24745c..619d1ca 100644 (file)
  */
 class ApiQuery extends ApiBase {
 
-       private $mPropModuleNames, $mListModuleNames, $mMetaModuleNames;
-
-       /**
-        * @var ApiPageSet
-        */
-       private $mPageSet;
-
-       private $params, $redirects, $convertTitles, $iwUrl;
-
        /**
         * List of Api Query prop modules
         * @var array
         */
-       private $mQueryPropModules = array(
+       private static $QueryPropModules = array(
                'categories' => 'ApiQueryCategories',
                'categoryinfo' => 'ApiQueryCategoryInfo',
                'duplicatefiles' => 'ApiQueryDuplicateFiles',
@@ -71,11 +62,12 @@ class ApiQuery extends ApiBase {
         * List of Api Query list modules
         * @var array
         */
-       private $mQueryListModules = array(
+       private static $QueryListModules = array(
                'allcategories' => 'ApiQueryAllCategories',
                'allimages' => 'ApiQueryAllImages',
                'alllinks' => 'ApiQueryAllLinks',
                'allpages' => 'ApiQueryAllPages',
+               'alltransclusions' => 'ApiQueryAllLinks',
                'allusers' => 'ApiQueryAllUsers',
                'backlinks' => 'ApiQueryBacklinks',
                'blocks' => 'ApiQueryBlocks',
@@ -104,7 +96,7 @@ class ApiQuery extends ApiBase {
         * List of Api Query meta modules
         * @var array
         */
-       private $mQueryMetaModules = array(
+       private static $QueryMetaModules = array(
                'allmessages' => 'ApiQueryAllMessages',
                'siteinfo' => 'ApiQuerySiteinfo',
                'userinfo' => 'ApiQueryUserInfo',
@@ -121,6 +113,7 @@ class ApiQuery extends ApiBase {
                'allimages' => 'ApiQueryAllImages',
                'alllinks' => 'ApiQueryAllLinks',
                'allpages' => 'ApiQueryAllPages',
+               'alltransclusions' => 'ApiQueryAllLinks',
                'backlinks' => 'ApiQueryBacklinks',
                'categories' => 'ApiQueryCategories',
                'categorymembers' => 'ApiQueryCategoryMembers',
@@ -142,10 +135,15 @@ class ApiQuery extends ApiBase {
                'watchlistraw' => 'ApiQueryWatchlistRaw',
        );
 
-       private $mSlaveDB = null;
-       private $mNamedDB = array();
+       /**
+        * @var ApiPageSet
+        */
+       private $mPageSet;
 
-       protected $mAllowedGenerators;
+       private $params;
+       private $iwUrl;
+       private $mNamedDB = array();
+       private $mModuleMgr;
 
        /**
         * @param $main ApiMain
@@ -154,43 +152,34 @@ class ApiQuery extends ApiBase {
        public function __construct( $main, $action ) {
                parent::__construct( $main, $action );
 
-               // Allow custom modules to be added in LocalSettings.php
-               global $wgAPIPropModules, $wgAPIListModules, $wgAPIMetaModules, $wgAPIGeneratorModules;
-               self::appendUserModules( $this->mQueryPropModules, $wgAPIPropModules );
-               self::appendUserModules( $this->mQueryListModules, $wgAPIListModules );
-               self::appendUserModules( $this->mQueryMetaModules, $wgAPIMetaModules );
-               self::appendUserModules( $this->mQueryGenerators, $wgAPIGeneratorModules );
-
-               $this->mPropModuleNames = array_keys( $this->mQueryPropModules );
-               $this->mListModuleNames = array_keys( $this->mQueryListModules );
-               $this->mMetaModuleNames = array_keys( $this->mQueryMetaModules );
-               $this->mAllowedGenerators = array_keys( $this->mQueryGenerators );
-       }
+               $this->mModuleMgr = new ApiModuleManager( $this );
 
-       /**
-        * Helper function to append any add-in modules to the list
-        * @param $modules array Module array
-        * @param $newModules array Module array to add to $modules
-        */
-       private static function appendUserModules( &$modules, $newModules ) {
-               if ( is_array( $newModules ) ) {
-                       foreach ( $newModules as $moduleName => $moduleClass ) {
-                               $modules[$moduleName] = $moduleClass;
+               // Allow custom modules to be added in LocalSettings.php
+               global $wgAPIPropModules, $wgAPIListModules, $wgAPIMetaModules;
+               $this->mModuleMgr->addModules( self::$QueryPropModules, 'prop' );
+               $this->mModuleMgr->addModules( $wgAPIPropModules, 'prop' );
+               $this->mModuleMgr->addModules( self::$QueryListModules, 'list' );
+               $this->mModuleMgr->addModules( $wgAPIListModules, 'list' );
+               $this->mModuleMgr->addModules( self::$QueryMetaModules, 'meta' );
+               $this->mModuleMgr->addModules( $wgAPIMetaModules, 'meta' );
+
+               global $wgAPIGeneratorModules;
+               if ( is_array( $wgAPIGeneratorModules ) ) {
+                       foreach ( $wgAPIGeneratorModules as $moduleName => $moduleClass ) {
+                               $this->mQueryGenerators[$moduleName] = $moduleClass;
                        }
                }
+
+               // Create PageSet that will process titles/pageids/revids/generator
+               $this->mPageSet = new ApiPageSet( $this );
        }
 
        /**
-        * Gets a default slave database connection object
-        * @return DatabaseBase
+        * Overrides to return this instance's module manager.
+        * @return ApiModuleManager
         */
-       public function getDB() {
-               if ( !isset( $this->mSlaveDB ) ) {
-                       $this->profileDBIn();
-                       $this->mSlaveDB = wfGetDB( DB_SLAVE, 'api' );
-                       $this->profileDBOut();
-               }
-               return $this->mSlaveDB;
+       public function getModuleManager() {
+               return $this->mModuleMgr;
        }
 
        /**
@@ -222,10 +211,12 @@ class ApiQuery extends ApiBase {
 
        /**
         * Get the array mapping module names to class names
+        * @deprecated since 1.21, use getModuleManager()'s methods instead
         * @return array array(modulename => classname)
         */
        public function getModules() {
-               return array_merge( $this->mQueryPropModules, $this->mQueryListModules, $this->mQueryMetaModules );
+               wfDeprecated( __METHOD__, '1.21' );
+               return $this->getModuleManager()->getNamesWithClasses();
        }
 
        /**
@@ -238,23 +229,12 @@ class ApiQuery extends ApiBase {
 
        /**
         * Get whether the specified module is a prop, list or a meta query module
+        * @deprecated since 1.21, use getModuleManager()->getModuleGroup()
         * @param $moduleName string Name of the module to find type for
         * @return mixed string or null
         */
        function getModuleType( $moduleName ) {
-               if ( isset( $this->mQueryPropModules[$moduleName] ) ) {
-                       return 'prop';
-               }
-
-               if ( isset( $this->mQueryListModules[$moduleName] ) ) {
-                       return 'list';
-               }
-
-               if ( isset( $this->mQueryMetaModules[$moduleName] ) ) {
-                       return 'meta';
-               }
-
-               return null;
+               return $this->getModuleManager()->getModuleGroup( $moduleName );
        }
 
        /**
@@ -284,34 +264,25 @@ class ApiQuery extends ApiBase {
         */
        public function execute() {
                $this->params = $this->extractRequestParams();
-               $this->redirects = $this->params['redirects'];
-               $this->convertTitles = $this->params['converttitles'];
                $this->iwUrl = $this->params['iwurl'];
 
-               // Create PageSet
-               $this->mPageSet = new ApiPageSet( $this, $this->redirects, $this->convertTitles );
-
                // Instantiate requested modules
                $modules = array();
-               $this->instantiateModules( $modules, 'prop', $this->mQueryPropModules );
-               $this->instantiateModules( $modules, 'list', $this->mQueryListModules );
-               $this->instantiateModules( $modules, 'meta', $this->mQueryMetaModules );
-
-               $cacheMode = 'public';
-
-               // If given, execute generator to substitute user supplied data with generated data.
-               if ( isset( $this->params['generator'] ) ) {
-                       $generator = $this->newGenerator( $this->params['generator'] );
-                       $params = $generator->extractRequestParams();
-                       $cacheMode = $this->mergeCacheMode( $cacheMode,
-                               $generator->getCacheMode( $params ) );
-                       $this->executeGeneratorModule( $generator, $modules );
-               } else {
-                       // Append custom fields and populate page/revision information
-                       $this->addCustomFldsToPageSet( $modules, $this->mPageSet );
-                       $this->mPageSet->execute();
+               $this->instantiateModules( $modules, 'prop' );
+               $this->instantiateModules( $modules, 'list' );
+               $this->instantiateModules( $modules, 'meta' );
+
+               // Query modules may optimize data requests through the $this->getPageSet()
+               // object by adding extra fields from the page table.
+               // This function will gather all the extra request fields from the modules.
+               foreach ( $modules as $module ) {
+                       $module->requestExtraData( $this->mPageSet );
                }
 
+               // Populate page/revision information
+               $this->mPageSet->execute();
+               $cacheMode = $this->mPageSet->getCacheMode();
+
                // Record page information (title, namespace, if exists, etc)
                $this->outputGeneralPageInfo();
 
@@ -355,33 +326,15 @@ class ApiQuery extends ApiBase {
                return $cacheMode;
        }
 
-       /**
-        * Query modules may optimize data requests through the $this->getPageSet() object
-        * by adding extra fields from the page table.
-        * This function will gather all the extra request fields from the modules.
-        * @param $modules array of module objects
-        * @param $pageSet ApiPageSet
-        */
-       private function addCustomFldsToPageSet( $modules, $pageSet ) {
-               // Query all requested modules.
-               /**
-                * @var $module ApiQueryBase
-                */
-               foreach ( $modules as $module ) {
-                       $module->requestExtraData( $pageSet );
-               }
-       }
-
        /**
         * Create instances of all modules requested by the client
         * @param $modules Array to append instantiated modules to
         * @param $param string Parameter name to read modules from
-        * @param $moduleList Array array(modulename => classname)
         */
-       private function instantiateModules( &$modules, $param, $moduleList ) {
+       private function instantiateModules( &$modules, $param ) {
                if ( isset( $this->params[$param] ) ) {
                        foreach ( $this->params[$param] as $moduleName ) {
-                               $modules[] = new $moduleList[$moduleName] ( $this, $moduleName );
+                               $modules[] = $this->mModuleMgr->getModule( $moduleName );
                        }
                }
        }
@@ -399,85 +352,25 @@ class ApiQuery extends ApiBase {
                // more than 380K. The maximum revision size is in the megabyte range,
                // and the maximum result size must be even higher than that.
 
-               // Title normalizations
-               $normValues = array();
-               foreach ( $pageSet->getNormalizedTitles() as $rawTitleStr => $titleStr ) {
-                       $normValues[] = array(
-                               'from' => $rawTitleStr,
-                               'to' => $titleStr
-                       );
+               $values = $pageSet->getNormalizedTitlesAsResult( $result );
+               if ( $values ) {
+                       $result->addValue( 'query', 'normalized', $values );
                }
-
-               if ( count( $normValues ) ) {
-                       $result->setIndexedTagName( $normValues, 'n' );
-                       $result->addValue( 'query', 'normalized', $normValues );
+               $values = $pageSet->getConvertedTitlesAsResult( $result );
+               if ( $values ) {
+                       $result->addValue( 'query', 'converted', $values );
                }
-
-               // Title conversions
-               $convValues = array();
-               foreach ( $pageSet->getConvertedTitles() as $rawTitleStr => $titleStr ) {
-                       $convValues[] = array(
-                               'from' => $rawTitleStr,
-                               'to' => $titleStr
-                       );
+               $values = $pageSet->getInterwikiTitlesAsResult( $result, $this->iwUrl );
+               if ( $values ) {
+                       $result->addValue( 'query', 'interwiki', $values );
                }
-
-               if ( count( $convValues ) ) {
-                       $result->setIndexedTagName( $convValues, 'c' );
-                       $result->addValue( 'query', 'converted', $convValues );
+               $values = $pageSet->getRedirectTitlesAsResult( $result );
+               if ( $values ) {
+                       $result->addValue( 'query', 'redirects', $values );
                }
-
-               // Interwiki titles
-               $intrwValues = array();
-               foreach ( $pageSet->getInterwikiTitles() as $rawTitleStr => $interwikiStr ) {
-                       $item = array(
-                               'title' => $rawTitleStr,
-                               'iw' => $interwikiStr,
-                       );
-                       if ( $this->iwUrl ) {
-                               $title = Title::newFromText( $rawTitleStr );
-                               $item['url'] = wfExpandUrl( $title->getFullURL(), PROTO_CURRENT );
-                       }
-                       $intrwValues[] = $item;
-               }
-
-               if ( count( $intrwValues ) ) {
-                       $result->setIndexedTagName( $intrwValues, 'i' );
-                       $result->addValue( 'query', 'interwiki', $intrwValues );
-               }
-
-               // Show redirect information
-               $redirValues = array();
-               /**
-                * @var $titleTo Title
-                */
-               foreach ( $pageSet->getRedirectTitles() as $titleStrFrom => $titleTo ) {
-                       $r = array(
-                               'from' => strval( $titleStrFrom ),
-                               'to' => $titleTo->getPrefixedText(),
-                       );
-                       if ( $titleTo->getFragment() !== '' ) {
-                               $r['tofragment'] = $titleTo->getFragment();
-                       }
-                       $redirValues[] = $r;
-               }
-
-               if ( count( $redirValues ) ) {
-                       $result->setIndexedTagName( $redirValues, 'r' );
-                       $result->addValue( 'query', 'redirects', $redirValues );
-               }
-
-               // Missing revision elements
-               $missingRevIDs = $pageSet->getMissingRevisionIDs();
-               if ( count( $missingRevIDs ) ) {
-                       $revids = array();
-                       foreach ( $missingRevIDs as $revid ) {
-                               $revids[$revid] = array(
-                                       'revid' => $revid
-                               );
-                       }
-                       $result->setIndexedTagName( $revids, 'rev' );
-                       $result->addValue( 'query', 'badrevids', $revids );
+               $values = $pageSet->getMissingRevisionIDsAsResult( $result );
+               if ( $values ) {
+                       $result->addValue( 'query', 'badrevids', $values );
                }
 
                // Page elements
@@ -542,8 +435,8 @@ class ApiQuery extends ApiBase {
        }
 
        /**
-        * @param  $pageSet ApiPageSet Pages to be exported
-        * @param  $result ApiResult Result to output to
+        * @param $pageSet ApiPageSet Pages to be exported
+        * @param $result ApiResult Result to output to
         */
        private function doExport( $pageSet, $result ) {
                $exportTitles = array();
@@ -586,80 +479,29 @@ class ApiQuery extends ApiBase {
                $result->enableSizeCheck();
        }
 
-       /**
-        * Create a generator object of the given type and return it
-        * @param $generatorName string Module name
-        * @return ApiQueryGeneratorBase
-        */
-       public function newGenerator( $generatorName ) {
-               // Find class that implements requested generator
-               if ( isset( $this->mQueryListModules[$generatorName] ) ) {
-                       $className = $this->mQueryListModules[$generatorName];
-               } elseif ( isset( $this->mQueryPropModules[$generatorName] ) ) {
-                       $className = $this->mQueryPropModules[$generatorName];
-               } else {
-                       ApiBase::dieDebug( __METHOD__, "Unknown generator=$generatorName" );
-               }
-               $generator = new $className ( $this, $generatorName );
-               if ( !$generator instanceof ApiQueryGeneratorBase ) {
-                       $this->dieUsage( "Module $generatorName cannot be used as a generator", 'badgenerator' );
-               }
-               $generator->setGeneratorMode();
-               return $generator;
-       }
-
-       /**
-        * For generator mode, execute generator, and use its output as new
-        * ApiPageSet
-        * @param $generator ApiQueryGeneratorBase Generator Module
-        * @param $modules array of module objects
-        */
-       protected function executeGeneratorModule( $generator, $modules ) {
-               // Generator results
-               $resultPageSet = new ApiPageSet( $this, $this->redirects, $this->convertTitles );
-
-               // Add any additional fields modules may need
-               $generator->requestExtraData( $this->mPageSet );
-               $this->addCustomFldsToPageSet( $modules, $resultPageSet );
-
-               // Populate page information with the original user input
-               $this->mPageSet->execute();
-
-               // populate resultPageSet with the generator output
-               $generator->profileIn();
-               $generator->executeGenerator( $resultPageSet );
-               wfRunHooks( 'APIQueryGeneratorAfterExecute', array( &$generator, &$resultPageSet ) );
-               $resultPageSet->finishPageSetGeneration();
-               $generator->profileOut();
-
-               // Swap the resulting pageset back in
-               $this->mPageSet = $resultPageSet;
-       }
-
-       public function getAllowedParams() {
-               return array(
+       public function getAllowedParams( $flags = 0 ) {
+               $result = array(
                        'prop' => array(
                                ApiBase::PARAM_ISMULTI => true,
-                               ApiBase::PARAM_TYPE => $this->mPropModuleNames
+                               ApiBase::PARAM_TYPE => $this->mModuleMgr->getNames( 'prop' )
                        ),
                        'list' => array(
                                ApiBase::PARAM_ISMULTI => true,
-                               ApiBase::PARAM_TYPE => $this->mListModuleNames
+                               ApiBase::PARAM_TYPE => $this->mModuleMgr->getNames( 'list' )
                        ),
                        'meta' => array(
                                ApiBase::PARAM_ISMULTI => true,
-                               ApiBase::PARAM_TYPE => $this->mMetaModuleNames
+                               ApiBase::PARAM_TYPE => $this->mModuleMgr->getNames( 'meta' )
                        ),
-                       'generator' => array(
-                               ApiBase::PARAM_TYPE => $this->mAllowedGenerators
-                       ),
-                       'redirects' => false,
-                       'converttitles' => false,
                        'indexpageids' => false,
                        'export' => false,
                        'exportnowrap' => false,
                        'iwurl' => false,
                );
+               if( $flags ) {
+                       $result += $this->getPageSet()->getFinalParams( $flags );
+               }
+               return $result;
        }
 
        /**
@@ -667,42 +509,40 @@ class ApiQuery extends ApiBase {
         * @return string
         */
        public function makeHelpMsg() {
-               // Make sure the internal object is empty
-               // (just in case a sub-module decides to optimize during instantiation)
-               $this->mPageSet = null;
+
+               // Use parent to make default message for the query module
+               $msg = parent::makeHelpMsg();
 
                $querySeparator = str_repeat( '--- ', 12 );
                $moduleSeparator = str_repeat( '*** ', 14 );
-               $msg = "\n$querySeparator Query: Prop  $querySeparator\n\n";
-               $msg .= $this->makeHelpMsgHelper( $this->mQueryPropModules, 'prop' );
+               $msg .= "\n$querySeparator Query: Prop  $querySeparator\n\n";
+               $msg .= $this->makeHelpMsgHelper( 'prop' );
                $msg .= "\n$querySeparator Query: List  $querySeparator\n\n";
-               $msg .= $this->makeHelpMsgHelper( $this->mQueryListModules, 'list' );
+               $msg .= $this->makeHelpMsgHelper( 'list' );
                $msg .= "\n$querySeparator Query: Meta  $querySeparator\n\n";
-               $msg .= $this->makeHelpMsgHelper( $this->mQueryMetaModules, 'meta' );
+               $msg .= $this->makeHelpMsgHelper( 'meta' );
                $msg .= "\n\n$moduleSeparator Modules: continuation  $moduleSeparator\n\n";
 
-               // Use parent to make default message for the query module
-               $msg = parent::makeHelpMsg() . $msg;
-
                return $msg;
        }
 
        /**
-        * For all modules in $moduleList, generate help messages and join them together
-        * @param $moduleList Array array(modulename => classname)
-        * @param $paramName string Parameter name
+        * For all modules of a given group, generate help messages and join them together
+        * @param $group string Module group
         * @return string
         */
-       private function makeHelpMsgHelper( $moduleList, $paramName ) {
+       private function makeHelpMsgHelper( $group ) {
                $moduleDescriptions = array();
 
-               foreach ( $moduleList as $moduleName => $moduleClass ) {
+               $moduleNames = $this->mModuleMgr->getNames( $group );
+               sort( $moduleNames );
+               foreach ( $moduleNames as $name ) {
                        /**
                         * @var $module ApiQueryBase
                         */
-                       $module = new $moduleClass( $this, $moduleName, null );
+                       $module = $this->mModuleMgr->getModule( $name );
 
-                       $msg = ApiMain::makeHelpMsgHeader( $module, $paramName );
+                       $msg = ApiMain::makeHelpMsgHeader( $module, $group );
                        $msg2 = $module->makeHelpMsg();
                        if ( $msg2 !== false ) {
                                $msg .= $msg2;
@@ -716,29 +556,15 @@ class ApiQuery extends ApiBase {
                return implode( "\n", $moduleDescriptions );
        }
 
-       /**
-        * Override to add extra parameters from PageSet
-        * @return string
-        */
-       public function makeHelpMsgParameters() {
-               $psModule = new ApiPageSet( $this );
-               return $psModule->makeHelpMsgParameters() . parent::makeHelpMsgParameters();
-       }
-
        public function shouldCheckMaxlag() {
                return true;
        }
 
        public function getParamDescription() {
-               return array(
+               return $this->getPageSet()->getParamDescription() + array(
                        'prop' => 'Which properties to get for the titles/revisions/pageids. Module help is available below',
                        'list' => 'Which lists to get. Module help is available below',
                        'meta' => 'Which metadata to get about the site. Module help is available below',
-                       'generator' => array( 'Use the output of a list as the input for other prop/list/meta items',
-                                       'NOTE: generator parameter names must be prefixed with a \'g\', see examples' ),
-                       'redirects' => 'Automatically resolve redirects',
-                       'converttitles' => array( "Convert titles to other variants if necessary. Only works if the wiki's content language supports variant conversion.",
-                                       'Languages that support variant conversion include ' . implode( ', ', LanguageConverter::$languagesWithVariants ) ),
                        'indexpageids' => 'Include an additional pageids section listing all returned page IDs',
                        'export' => 'Export the current revisions of all given or generated pages',
                        'exportnowrap' => 'Return the export XML without wrapping it in an XML result (same format as Special:Export). Can only be used with export',
@@ -755,9 +581,10 @@ class ApiQuery extends ApiBase {
        }
 
        public function getPossibleErrors() {
-               return array_merge( parent::getPossibleErrors(), array(
-                       array( 'code' => 'badgenerator', 'info' => 'Module $generatorName cannot be used as a generator' ),
-               ) );
+               return array_merge(
+                       parent::getPossibleErrors(),
+                       $this->getPageSet()->getPossibleErrors()
+               );
        }
 
        public function getExamples() {
@@ -774,12 +601,4 @@ class ApiQuery extends ApiBase {
                        'https://www.mediawiki.org/wiki/API:Lists',
                );
        }
-
-       public function getVersion() {
-               $psModule = new ApiPageSet( $this );
-               $vers = array();
-               $vers[] = __CLASS__ . ': $Id$';
-               $vers[] = $psModule->getVersion();
-               return $vers;
-       }
 }