Merge "Allow API results to wrap long lines"
[lhc/web/wiklou.git] / includes / api / ApiMain.php
index 5193ab5..fed515b 100644 (file)
@@ -131,8 +131,9 @@ class ApiMain extends ApiBase {
         */
        private $mPrinter;
 
-       private $mModules, $mModuleNames, $mFormats, $mFormatNames;
-       private $mResult, $mAction, $mShowVersions, $mEnableWrite;
+       private $mModuleMgr, $mResult;
+       private $mAction;
+       private $mEnableWrite;
        private $mInternalMode, $mSquidMaxage, $mModule;
 
        private $mCacheMode = 'private';
@@ -180,15 +181,13 @@ class ApiMain extends ApiBase {
                        }
                }
 
-               global $wgAPIModules; // extension modules
-               $this->mModules = $wgAPIModules + self::$Modules;
-
-               $this->mModuleNames = array_keys( $this->mModules );
-               $this->mFormats = self::$Formats;
-               $this->mFormatNames = array_keys( $this->mFormats );
+               global $wgAPIModules;
+               $this->mModuleMgr = new ApiModuleManager( $this );
+               $this->mModuleMgr->addModules( self::$Modules, 'action' );
+               $this->mModuleMgr->addModules( $wgAPIModules, 'action' );
+               $this->mModuleMgr->addModules( self::$Formats, 'format' );
 
                $this->mResult = new ApiResult( $this );
-               $this->mShowVersions = false;
                $this->mEnableWrite = $enableWrite;
 
                $this->mSquidMaxage = - 1; // flag for executeActionWithErrorHandling()
@@ -333,10 +332,11 @@ class ApiMain extends ApiBase {
         * @return ApiFormatBase
         */
        public function createPrinterByName( $format ) {
-               if ( !isset( $this->mFormats[$format] ) ) {
+               $printer = $this->mModuleMgr->getModule( $format, 'format' );
+               if ( $printer === null ) {
                        $this->dieUsage( "Unrecognized format: {$format}", 'unknown_format' );
                }
-               return new $this->mFormats[$format] ( $this, $format );
+               return $printer;
        }
 
        /**
@@ -385,7 +385,7 @@ class ApiMain extends ApiBase {
                                }
                        }
 
-                       // Handle any kind of exception by outputing properly formatted error message.
+                       // Handle any kind of exception by outputting properly formatted error message.
                        // If this fails, an unhandled exception should be thrown so that global error
                        // handler will process and log it.
 
@@ -605,7 +605,7 @@ class ApiMain extends ApiBase {
                if ( !isset ( $this->mPrinter ) ) {
                        // The printer has not been created yet. Try to manually get formatter value.
                        $value = $this->getRequest()->getVal( 'format', self::API_DEFAULT_FORMAT );
-                       if ( !in_array( $value, $this->mFormatNames ) ) {
+                       if ( !$this->mModuleMgr->isDefined( $value, 'format' ) ) {
                                $value = self::API_DEFAULT_FORMAT;
                        }
 
@@ -623,7 +623,6 @@ class ApiMain extends ApiBase {
                        if ( $this->mPrinter->getWantsHelp() || $this->mAction == 'help' ) {
                                ApiResult::setContent( $errMessage, $this->makeHelpMsg() );
                        }
-
                } else {
                        global $wgShowSQLErrors, $wgShowExceptionDetails;
                        // Something is seriously wrong
@@ -640,6 +639,10 @@ class ApiMain extends ApiBase {
                        ApiResult::setContent( $errMessage, $wgShowExceptionDetails ? "\n\n{$e->getTraceAsString()}\n\n" : '' );
                }
 
+               // Remember all the warnings to re-add them later
+               $oldResult = $result->getData();
+               $warnings = isset( $oldResult['warnings'] ) ? $oldResult['warnings'] : null;
+
                $result->reset();
                $result->disableSizeCheck();
                // Re-add the id
@@ -647,11 +650,13 @@ class ApiMain extends ApiBase {
                if ( !is_null( $requestid ) ) {
                        $result->addValue( null, 'requestid', $requestid );
                }
-
                if ( $wgShowHostnames ) {
                        // servedby is especially useful when debugging errors
                        $result->addValue( null, 'servedby', wfHostName() );
                }
+               if ( $warnings !== null ) {
+                       $result->addValue( null, 'warnings', $warnings );
+               }
 
                $result->addValue( null, 'error', $errMessage );
 
@@ -681,7 +686,6 @@ class ApiMain extends ApiBase {
 
                $params = $this->extractRequestParams();
 
-               $this->mShowVersions = $params['version'];
                $this->mAction = $params['action'];
 
                if ( !is_string( $this->mAction ) ) {
@@ -697,9 +701,10 @@ class ApiMain extends ApiBase {
         */
        protected function setupModule() {
                // Instantiate the module requested by the user
-               $module = new $this->mModules[$this->mAction] ( $this, $this->mAction );
-               $this->mModule = $module;
-
+               $module = $this->mModuleMgr->getModule( $this->mAction, 'action' );
+               if ( $module === null ) {
+                       $this->dieUsage( 'The API requires a valid action parameter', 'unknown_action' );
+               }
                $moduleParams = $module->extractRequestParams();
 
                // Die if token required, but not provided (unless there is a gettoken parameter)
@@ -787,9 +792,10 @@ class ApiMain extends ApiBase {
         * @param $params Array an array with the request parameters
         */
        protected function setupExternalResponse( $module, $params ) {
-               // Ignore mustBePosted() for internal calls
-               if ( $module->mustBePosted() && !$this->getRequest()->wasPosted() ) {
-                       $this->dieUsageMsg( array( 'mustbeposted', $this->mAction ) );
+               if ( !$this->getRequest()->wasPosted() && $module->mustBePosted() ) {
+                       // Module requires POST. GET request might still be allowed
+                       // if $wgDebugApi is true, otherwise fail.
+                       $this->dieUsageMsgOrDebug( array( 'mustbeposted', $this->mAction ) );
                }
 
                // See if custom printer is used
@@ -810,6 +816,7 @@ class ApiMain extends ApiBase {
        protected function executeAction() {
                $params = $this->setupExecuteAction();
                $module = $this->setupModule();
+               $this->mModule = $module;
 
                $this->checkExecutePermissions( $module );
 
@@ -927,6 +934,11 @@ class ApiMain extends ApiBase {
         * @param $isError bool
         */
        protected function printResult( $isError ) {
+               global $wgDebugAPI;
+               if( $wgDebugAPI !== false ) {
+                       $this->setWarning( 'SECURITY WARNING: $wgDebugAPI is enabled' );
+               }
+
                $this->getResult()->cleanUpUTF8();
                $printer = $this->mPrinter;
                $printer->profileIn();
@@ -962,13 +974,12 @@ class ApiMain extends ApiBase {
                return array(
                        'format' => array(
                                ApiBase::PARAM_DFLT => ApiMain::API_DEFAULT_FORMAT,
-                               ApiBase::PARAM_TYPE => $this->mFormatNames
+                               ApiBase::PARAM_TYPE => $this->mModuleMgr->getNames( 'format' )
                        ),
                        'action' => array(
                                ApiBase::PARAM_DFLT => 'help',
-                               ApiBase::PARAM_TYPE => $this->mModuleNames
+                               ApiBase::PARAM_TYPE => $this->mModuleMgr->getNames( 'action' )
                        ),
-                       'version' => false,
                        'maxlag'  => array(
                                ApiBase::PARAM_TYPE => 'integer'
                        ),
@@ -995,7 +1006,6 @@ class ApiMain extends ApiBase {
                return array(
                        'format' => 'The format of the output',
                        'action' => 'What action you would like to perform. See below for module help',
-                       'version' => 'When showing help, include version for each module',
                        'maxlag' => array(
                                'Maximum lag can be used when MediaWiki is installed on a database replicated cluster.',
                                'To save actions causing any more site replication lag, this parameter can make the client',
@@ -1085,7 +1095,7 @@ class ApiMain extends ApiBase {
                        '    Victor Vasiliev - vasilvv at gee mail dot com',
                        '    Bryan Tong Minh - bryan . tongminh @ gmail . com',
                        '    Sam Reed - sam @ reedyboy . net',
-                       '    Yuri Astrakhan "<Firstname><Lastname>@gmail.com" (creator, lead developer Sep 2006-Sep 2007)',
+                       '    Yuri Astrakhan "<Firstname><Lastname>@gmail.com" (creator, lead developer Sep 2006-Sep 2007, 2012)',
                        '',
                        'Please send your comments, suggestions and questions to mediawiki-api@lists.wikimedia.org',
                        'or file a bug report at https://bugzilla.wikimedia.org/'
@@ -1111,8 +1121,7 @@ class ApiMain extends ApiBase {
                $this->setHelp();
                // Get help text from cache if present
                $key = wfMemcKey( 'apihelp', $this->getModuleName(),
-                       SpecialVersion::getVersion( 'nodb' ) .
-                       $this->getShowVersions() );
+                       SpecialVersion::getVersion( 'nodb' ) );
                if ( $wgAPICacheHelpTimeout > 0 ) {
                        $cached = $wgMemc->get( $key );
                        if ( $cached ) {
@@ -1137,8 +1146,9 @@ class ApiMain extends ApiBase {
 
                $astriks = str_repeat( '*** ', 14 );
                $msg .= "\n\n$astriks Modules  $astriks\n\n";
-               foreach ( array_keys( $this->mModules ) as $moduleName ) {
-                       $module = new $this->mModules[$moduleName] ( $this, $moduleName );
+
+               foreach ( $this->mModuleMgr->getNames( 'action' ) as $name ) {
+                       $module = $this->mModuleMgr->getModule( $name );
                        $msg .= self::makeHelpMsgHeader( $module, 'action' );
 
                        $msg2 = $module->makeHelpMsg();
@@ -1156,8 +1166,8 @@ class ApiMain extends ApiBase {
                }
 
                $msg .= "\n$astriks Formats  $astriks\n\n";
-               foreach ( array_keys( $this->mFormats ) as $formatName ) {
-                       $module = $this->createPrinterByName( $formatName );
+               foreach ( $this->mModuleMgr->getNames( 'format' ) as $name ) {
+                       $module = $this->mModuleMgr->getModule( $name );
                        $msg .= self::makeHelpMsgHeader( $module, 'format' );
                        $msg2 = $module->makeHelpMsg();
                        if ( $msg2 !== false ) {
@@ -1202,25 +1212,19 @@ class ApiMain extends ApiBase {
        /**
         * Check whether the user wants us to show version information in the API help
         * @return bool
+        * @deprecated since 1.21, always returns false
         */
        public function getShowVersions() {
-               return $this->mShowVersions;
+               wfDeprecated( __METHOD__, '1.21' );
+               return false;
        }
 
        /**
-        * Returns the version information of this file, plus it includes
-        * the versions for all files that are not callable proper API modules
-        *
-        * @return array
+        * Overrides to return this instance's module manager.
+        * @return ApiModuleManager
         */
-       public function getVersion() {
-               $vers = array();
-               $vers[] = 'MediaWiki: ' . SpecialVersion::getVersion() . "\n    https://svn.wikimedia.org/viewvc/mediawiki/trunk/phase3/";
-               $vers[] = __CLASS__ . ': $Id$';
-               $vers[] = ApiBase::getBaseVersion();
-               $vers[] = ApiFormatBase::getBaseVersion();
-               $vers[] = ApiQueryBase::getBaseVersion();
-               return $vers;
+       public function getModuleManager() {
+               return $this->mModuleMgr;
        }
 
        /**
@@ -1228,40 +1232,44 @@ class ApiMain extends ApiBase {
         * classes who wish to add their own modules to their lexicon or override the
         * behavior of inherent ones.
         *
-        * @param $mdlName String The identifier for this module.
-        * @param $mdlClass String The class where this module is implemented.
+        * @deprecated since 1.21, Use getModuleManager()->addModule() instead.
+        * @param $name string The identifier for this module.
+        * @param $class ApiBase The class where this module is implemented.
         */
-       protected function addModule( $mdlName, $mdlClass ) {
-               $this->mModules[$mdlName] = $mdlClass;
+       protected function addModule( $name, $class ) {
+               $this->getModuleManager()->addModule( $name, 'action', $class );
        }
 
        /**
         * Add or overwrite an output format for this ApiMain. Intended for use by extending
         * classes who wish to add to or modify current formatters.
         *
-        * @param $fmtName string The identifier for this format.
-        * @param $fmtClass ApiFormatBase The class implementing this format.
+        * @deprecated since 1.21, Use getModuleManager()->addModule() instead.
+        * @param $name string The identifier for this format.
+        * @param $class ApiFormatBase The class implementing this format.
         */
-       protected function addFormat( $fmtName, $fmtClass ) {
-               $this->mFormats[$fmtName] = $fmtClass;
+       protected function addFormat( $name, $class ) {
+               $this->getModuleManager->addModule( $name, 'format', $class );
        }
 
        /**
         * Get the array mapping module names to class names
+        * @deprecated since 1.21, Use getModuleManager()'s methods instead.
         * @return array
         */
        function getModules() {
-               return $this->mModules;
+               return $this->getModuleManager()->getNamesWithClasses( 'action' );
        }
 
        /**
         * Returns the list of supported formats in form ( 'format' => 'ClassName' )
         *
         * @since 1.18
+        * @deprecated since 1.21, Use getModuleManager()'s methods instead.
         * @return array
         */
        public function getFormats() {
-               return $this->mFormats;
+               return $this->getModuleManager()->getNamesWithClasses( 'format' );
        }
 }