Merge "Avoid muliple cache calls to explicitly defined tags"
[lhc/web/wiklou.git] / includes / api / ApiMain.php
index ee1cfa6..a8db20c 100644 (file)
@@ -140,7 +140,7 @@ class ApiMain extends ApiBase {
         */
        private $mPrinter;
 
-       private $mModuleMgr, $mResult;
+       private $mModuleMgr, $mResult, $mErrorFormatter, $mContinuationManager;
        private $mAction;
        private $mEnableWrite;
        private $mInternalMode, $mSquidMaxage, $mModule;
@@ -218,7 +218,11 @@ class ApiMain extends ApiBase {
 
                Hooks::run( 'ApiMain::moduleManager', array( $this->mModuleMgr ) );
 
-               $this->mResult = new ApiResult( $this );
+               $this->mResult = new ApiResult( $this->getConfig()->get( 'APIMaxResultSize' ) );
+               $this->mErrorFormatter = new ApiErrorFormatter_BackCompat( $this->mResult );
+               $this->mResult->setErrorFormatter( $this->mErrorFormatter );
+               $this->mResult->setMainForContinuation( $this );
+               $this->mContinuationManager = null;
                $this->mEnableWrite = $enableWrite;
 
                $this->mSquidMaxage = -1; // flag for executeActionWithErrorHandling()
@@ -242,6 +246,43 @@ class ApiMain extends ApiBase {
                return $this->mResult;
        }
 
+       /**
+        * Get the ApiErrorFormatter object associated with current request
+        * @return ApiErrorFormatter
+        */
+       public function getErrorFormatter() {
+               return $this->mErrorFormatter;
+       }
+
+       /**
+        * Get the continuation manager
+        * @return ApiContinuationManager|null
+        */
+       public function getContinuationManager() {
+               return $this->mContinuationManager;
+       }
+
+       /**
+        * Set the continuation manager
+        * @param ApiContinuationManager|null
+        */
+       public function setContinuationManager( $manager ) {
+               if ( $manager !== null ) {
+                       if ( !$manager instanceof ApiContinuationManager ) {
+                               throw new InvalidArgumentException( __METHOD__ . ': Was passed ' .
+                                       is_object( $manager ) ? get_class( $manager ) : gettype( $manager )
+                               );
+                       }
+                       if ( $this->mContinuationManager !== null ) {
+                               throw new UnexpectedValueException(
+                                       __METHOD__ . ': tried to set manager from ' . $manager->getSource() .
+                                       ' when a manager is already set from ' . $this->mContinuationManager->getSource()
+                               );
+                       }
+               }
+               $this->mContinuationManager = $manager;
+       }
+
        /**
         * Get the API module object. Only works after executeAction()
         *
@@ -454,7 +495,22 @@ class ApiMain extends ApiBase {
                // Reset and print just the error message
                ob_clean();
 
-               $this->printResult( true );
+               // Printer may not be initialized if the extractRequestParams() fails for the main module
+               $this->createErrorPrinter();
+
+               try {
+                       $this->printResult( true );
+               } catch ( UsageException $ex ) {
+                       // The error printer itself is failing. Try suppressing its request
+                       // parameters and redo.
+                       $this->setWarning(
+                               'Error printer failed (will retry without params): ' . $ex->getMessage()
+                       );
+                       $this->mPrinter = null;
+                       $this->createErrorPrinter();
+                       $this->mPrinter->forceDefaultParams();
+                       $this->printResult( true );
+               }
        }
 
        /**
@@ -521,8 +577,7 @@ class ApiMain extends ApiBase {
                if ( !in_array( $originParam, $origins ) ) {
                        // origin parameter set but incorrect
                        // Send a 403 response
-                       $message = HttpStatus::getMessage( 403 );
-                       $response->header( "HTTP/1.1 403 $message", true, 403 );
+                       $response->statusHeader( 403 );
                        $response->header( 'Cache-Control: no-cache' );
                        echo "'origin' parameter does not match Origin header\n";
 
@@ -751,22 +806,14 @@ class ApiMain extends ApiBase {
        }
 
        /**
-        * Replace the result data with the information about an exception.
-        * Returns the error code
-        * @param Exception $e
-        * @return string
+        * Create the printer for error output
         */
-       protected function substituteResultWithError( $e ) {
-               $result = $this->getResult();
-
-               // Printer may not be initialized if the extractRequestParams() fails for the main module
+       private function createErrorPrinter() {
                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 ( !$this->mModuleMgr->isDefined( $value, 'format' ) ) {
                                $value = self::API_DEFAULT_FORMAT;
                        }
-
                        $this->mPrinter = $this->createPrinterByName( $value );
                }
 
@@ -775,17 +822,23 @@ class ApiMain extends ApiBase {
                if ( !$this->mPrinter->canPrintErrors() ) {
                        $this->mPrinter = $this->createPrinterByName( self::API_DEFAULT_FORMAT );
                }
+       }
 
-               // Update raw mode flag for the selected printer.
-               $result->setRawMode( $this->mPrinter->getNeedsRawData() );
-
+       /**
+        * Replace the result data with the information about an exception.
+        * Returns the error code
+        * @param Exception $e
+        * @return string
+        */
+       protected function substituteResultWithError( $e ) {
+               $result = $this->getResult();
                $config = $this->getConfig();
 
                if ( $e instanceof UsageException ) {
                        // User entered incorrect parameters - generate error response
                        $errMessage = $e->getMessageArray();
                        $link = wfExpandUrl( wfScript( 'api' ) );
-                       ApiResult::setContent( $errMessage, "See $link for API usage" );
+                       ApiResult::setContentValue( $errMessage, 'docref', "See $link for API usage" );
                } else {
                        // Something is seriously wrong
                        if ( ( $e instanceof DBQueryError ) && !$config->get( 'ShowSQLErrors' ) ) {
@@ -799,16 +852,16 @@ class ApiMain extends ApiBase {
                                'info' => '[' . MWExceptionHandler::getLogId( $e ) . '] ' . $info,
                        );
                        if ( $config->get( 'ShowExceptionDetails' ) ) {
-                               ApiResult::setContent(
+                               ApiResult::setContentValue(
                                        $errMessage,
+                                       'trace',
                                        MWExceptionHandler::getRedactedTraceAsString( $e )
                                );
                        }
                }
 
                // Remember all the warnings to re-add them later
-               $oldResult = $result->getData();
-               $warnings = isset( $oldResult['warnings'] ) ? $oldResult['warnings'] : null;
+               $warnings = $result->getResultData( array( 'warnings' ) );
 
                $result->reset();
                // Re-add the id
@@ -1191,9 +1244,7 @@ class ApiMain extends ApiBase {
                        $this->setWarning( 'SECURITY WARNING: $wgDebugAPI is enabled' );
                }
 
-               $this->getResult()->cleanUpUTF8();
                $printer = $this->mPrinter;
-
                $printer->initPrinter( false );
                $printer->execute();
                $printer->closePrinter();
@@ -1255,7 +1306,7 @@ class ApiMain extends ApiBase {
                );
        }
 
-       public function modifyHelp( array &$help, array $options ) {
+       public function modifyHelp( array &$help, array $options, array &$tocData ) {
                // Wish PHP had an "array_insert_before". Instead, we have to manually
                // reindex the array to get 'permissions' in the right place.
                $oldHelp = $help;
@@ -1266,6 +1317,7 @@ class ApiMain extends ApiBase {
                        }
                        $help[$k] = $v;
                }
+               $help['datatypes'] = '';
                $help['credits'] = '';
 
                // Fill 'permissions'
@@ -1298,13 +1350,48 @@ class ApiMain extends ApiBase {
                $help['permissions'] .= Html::closeElement( 'dl' );
                $help['permissions'] .= Html::closeElement( 'div' );
 
-               // Fill 'credits', if applicable
+               // Fill 'datatypes' and 'credits', if applicable
                if ( empty( $options['nolead'] ) ) {
-                       $help['credits'] .= Html::element( 'h' . min( 6, $options['headerlevel'] + 1 ),
-                               array( 'id' => '+credits', 'class' => 'apihelp-header' ),
-                               $this->msg( 'api-credits-header' )->parse()
+                       $level = $options['headerlevel'];
+                       $tocnumber = &$options['tocnumber'];
+
+                       $header = $this->msg( 'api-help-datatypes-header' )->parse();
+                       $help['datatypes'] .= Html::rawelement( 'h' . min( 6, $level ),
+                               array( 'id' => 'main/datatypes', 'class' => 'apihelp-header' ),
+                               Html::element( 'span', array( 'id' => Sanitizer::escapeId( 'main/datatypes' ) ) ) .
+                               $header
+                       );
+                       $help['datatypes'] .= $this->msg( 'api-help-datatypes' )->parseAsBlock();
+                       if ( !isset( $tocData['main/datatypes'] ) ) {
+                               $tocnumber[$level]++;
+                               $tocData['main/datatypes'] = array(
+                                       'toclevel' => count( $tocnumber ),
+                                       'level' => $level,
+                                       'anchor' => 'main/datatypes',
+                                       'line' => $header,
+                                       'number' => join( '.', $tocnumber ),
+                                       'index' => false,
+                               );
+                       }
+
+                       $header = $this->msg( 'api-credits-header' )->parse();
+                       $help['credits'] .= Html::rawelement( 'h' . min( 6, $level ),
+                               array( 'id' => 'main/credits', 'class' => 'apihelp-header' ),
+                               Html::element( 'span', array( 'id' => Sanitizer::escapeId( 'main/credits' ) ) ) .
+                               $header
                        );
                        $help['credits'] .= $this->msg( 'api-credits' )->useDatabase( false )->parseAsBlock();
+                       if ( !isset( $tocData['main/credits'] ) ) {
+                               $tocnumber[$level]++;
+                               $tocData['main/credits'] = array(
+                                       'toclevel' => count( $tocnumber ),
+                                       'level' => $level,
+                                       'anchor' => 'main/credits',
+                                       'line' => $header,
+                                       'number' => join( '.', $tocnumber ),
+                                       'index' => false,
+                               );
+                       }
                }
        }