Merge "API edit: allow ConfirmEdit to use the merged parse"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Wed, 10 Dec 2014 20:22:05 +0000 (20:22 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Wed, 10 Dec 2014 20:22:05 +0000 (20:22 +0000)
1  2 
docs/hooks.txt
includes/EditPage.php
includes/api/ApiEditPage.php

diff --combined docs/hooks.txt
@@@ -776,10 -776,12 +776,10 @@@ $out: OutputPage objec
  'BeforeParserFetchFileAndTitle': Before an image is rendered by Parser.
  $parser: Parser object
  $nt: the image title
 -&$options: array of options to RepoGroup::findFile
 +&$options: array of options to RepoGroup::findFile. If it contains 'broken'
 +  as a key then the file will appear as a broken thumbnail.
  &$descQuery: query string to add to thumbnail URL
  
 -FIXME: Where does the below sentence fit in?
 -If 'broken' is a key in $options then the file will appear as a broken thumbnail.
 -
  'BeforeParserFetchTemplateAndtitle': Before a template is fetched by Parser.
  $parser: Parser object
  $title: title of the template
@@@ -1046,7 -1048,8 +1046,8 @@@ This may be triggered by the EditPage o
  Use the $status object to indicate whether the edit should be allowed, and to provide
  a reason for disallowing it. Return false to abort the edit, and true to continue.
  Returning true if $status->isOK() returns false means "don't save but continue user
- interaction", e.g. show the edit form.
+ interaction", e.g. show the edit form. $status->apiHookResult can be set to an array
+ to be returned by api.php action=edit. This is used to deliver captchas.
  $context: object implementing the IContextSource interface.
  $content: content of the edit box, as a Content object.
  $status: Status object to represent errors, etc.
@@@ -2422,7 -2425,7 +2423,7 @@@ after variants have been added
  'SkinTemplateOutputPageBeforeExec': Before SkinTemplate::outputPage() starts
  page output.
  &$sktemplate: SkinTemplate object
 -&$tpl: Template engine object
 +&$tpl: QuickTemplate engine object
  
  'SkinTemplatePreventOtherActiveTabs': Use this to prevent showing active tabs.
  $sktemplate: SkinTemplate object
diff --combined includes/EditPage.php
@@@ -150,12 -150,6 +150,12 @@@ class EditPage 
         */
        const AS_NO_CHANGE_CONTENT_MODEL = 235;
  
 +      /**
 +       * Status: user tried to create self-redirect (redirect to the same article) and
 +       * wpIgnoreSelfRedirect == false
 +       */
 +      const AS_SELF_REDIRECT = 236;
 +
        /**
         * Status: can't parse content
         */
        /** @var bool */
        protected $allowBlankArticle = false;
  
 +      /** @var bool */
 +      protected $selfRedirect = false;
 +
 +      /** @var bool */
 +      protected $allowSelfRedirect = false;
 +
        /** @var string */
        public $autoSumm = '';
  
                        $this->autoSumm = $request->getText( 'wpAutoSummary' );
  
                        $this->allowBlankArticle = $request->getBool( 'wpIgnoreBlankArticle' );
 +                      $this->allowSelfRedirect = $request->getBool( 'wpIgnoreSelfRedirect' );
                } else {
                        # Not a posted form? Start with nothing.
                        wfDebug( __METHOD__ . ": Not a posted form.\n" );
                        case self::AS_MAX_ARTICLE_SIZE_EXCEEDED:
                        case self::AS_END:
                        case self::AS_BLANK_ARTICLE:
 +                      case self::AS_SELF_REDIRECT:
                                return true;
  
                        case self::AS_HOOK_ERROR:
        protected function runPostMergeFilters( Content $content, Status $status, User $user ) {
                // Run old style post-section-merge edit filter
                if ( !ContentHandler::runLegacyHooks( 'EditFilterMerged',
-                       array( $this, $content, &$this->hookError, $this->summary ) ) ) {
+                       array( $this, $content, &$this->hookError, $this->summary ) ) )
+               {
                        # Error messages etc. could be handled within the hook...
                        $status->fatal( 'hookaborted' );
                        $status->value = self::AS_HOOK_ERROR;
  
                // Run new style post-section-merge edit filter
                if ( !wfRunHooks( 'EditFilterMergedContent',
-                       array( $this->mArticle->getContext(), $content, $status, $this->summary,
-                               $user, $this->minoredit ) ) ) {
+                               array( $this->mArticle->getContext(), $content, $status, $this->summary,
+                               $user, $this->minoredit ) ) )
+               {
                        # Error messages etc. could be handled within the hook...
-                       // XXX: $status->value may already be something informative...
-                       $this->hookError = $status->getWikiText();
-                       $status->fatal( 'hookaborted' );
-                       $status->value = self::AS_HOOK_ERROR;
+                       if ( $status->isGood() ) {
+                               $status->fatal( 'hookaborted' );
+                               // Not setting $this->hookError here is a hack to allow the hook
+                               // to cause a return to the edit page without $this->hookError
+                               // being set. This is used by ConfirmEdit to display a captcha
+                               // without any error message cruft.
+                       } else {
+                               $this->hookError = $status->getWikiText();
+                       }
+                       // Use the existing $status->value if the hook set it
+                       if ( !$status->value ) {
+                               $status->value = self::AS_HOOK_ERROR;
+                       }
                        return false;
                } elseif ( !$status->isOK() ) {
                        # ...or the hook could be expecting us to produce an error
                        $status->value = self::AS_SUCCESS_UPDATE;
                }
  
 +              if ( !$this->allowSelfRedirect
 +                      && $content->isRedirect()
 +                      && $content->getRedirectTarget()->equals( $this->getTitle() )
 +              ) {
 +                      $this->selfRedirect = true;
 +                      $status->fatal( 'selfredirect' );
 +                      $status->value = self::AS_SELF_REDIRECT;
 +                      wfProfileOut( __METHOD__ );
 +                      return $status;
 +              }
 +
                // Check for length errors again now that the section is merged in
                $this->kblength = (int)( strlen( $this->toEditText( $content ) ) / 1024 );
                if ( $this->kblength > $wgMaxArticleSize ) {
         * Send the edit form and related headers to $wgOut
         * @param callable|null $formCallback That takes an OutputPage parameter; will be called
         *     during form output near the top, for captchas and the like.
+        *
+        * The $formCallback parameter is deprecated since MediaWiki 1.25. Please
+        * use the EditPage::showEditForm:fields hook instead.
         */
        function showEditForm( $formCallback = null ) {
                global $wgOut, $wgUser;
                ) );
  
                if ( is_callable( $formCallback ) ) {
+                       wfWarn( 'The $formCallback parameter to ' . __METHOD__ . 'is deprecated' );
                        call_user_func_array( $formCallback, array( &$wgOut ) );
                }
  
                        $wgOut->addHTML( Html::hidden( 'wpUndidRevision', $this->undidRev ) );
                }
  
 +              if ( $this->selfRedirect ) {
 +                      $wgOut->addHTML( Html::hidden( 'wpIgnoreSelfRedirect', true ) );
 +              }
 +
                if ( $this->hasPresetSummary ) {
                        // If a summary has been preset using &summary= we don't want to prompt for
                        // a different summary. Only prompt for a summary if the summary is blanked.
                                $wgOut->wrapWikiMsg( "<div id='mw-blankarticle'>\n$1\n</div>", 'blankarticle' );
                        }
  
 +                      if ( $this->selfRedirect ) {
 +                              $wgOut->wrapWikiMsg( "<div id='mw-selfredirect'>\n$1\n</div>", 'selfredirect' );
 +                      }
 +
                        if ( $this->hookError !== '' ) {
                                $wgOut->addWikiText( $this->hookError );
                        }
@@@ -3496,6 -3476,7 +3509,6 @@@ HTM
                        }
  
                        $parserOptions = $this->mArticle->makeParserOptions( $this->mArticle->getContext() );
 -                      $parserOptions->setEditSection( false );
                        $parserOptions->setIsPreview( true );
                        $parserOptions->setIsSectionPreview( !is_null( $this->section ) && $this->section !== '' );
  
                        # For CSS/JS pages, we should have called the ShowRawCssJs hook here.
                        # But it's now deprecated, so never mind
  
 -                      $content = $content->preSaveTransform( $this->mTitle, $wgUser, $parserOptions );
 -                      $parserOutput = $content->getParserOutput(
 -                              $this->getArticle()->getTitle(),
 -                              null,
 -                              $parserOptions
 +                      $pstContent = $content->preSaveTransform( $this->mTitle, $wgUser, $parserOptions );
 +                      $parserOutput = $pstContent->getParserOutput( $this->mTitle, null, $parserOptions );
 +
 +                      # Try to stash the edit for the final submission step
 +                      # @todo: different date format preferences cause cache misses
 +                      ApiStashEdit::stashEditFromPreview(
 +                              $this->getArticle(), $content, $pstContent,
 +                              $parserOutput, $parserOptions, $parserOptions, wfTimestampNow()
                        );
  
 +                      $parserOutput->setEditSectionTokens( false ); // no section edit links
                        $previewHTML = $parserOutput->getText();
                        $this->mParserOutput = $parserOutput;
                        $wgOut->addParserOutputMetadata( $parserOutput );
@@@ -195,9 -195,9 +195,9 @@@ class ApiEditPage extends ApiBase 
                                        list( $params['undo'], $params['undoafter'] ) =
                                                array( $params['undoafter'], $params['undo'] );
                                }
 -                              $undoafterRev = Revision::newFromID( $params['undoafter'] );
 +                              $undoafterRev = Revision::newFromId( $params['undoafter'] );
                        }
 -                      $undoRev = Revision::newFromID( $params['undo'] );
 +                      $undoRev = Revision::newFromId( $params['undo'] );
                        if ( is_null( $undoRev ) || $undoRev->isDeleted( Revision::DELETED_TEXT ) ) {
                                $this->dieUsageMsg( array( 'nosuchrevid', $params['undo'] ) );
                        }
                        'model' => $contentHandler->getModelID(),
                        'wpEditToken' => $params['token'],
                        'wpIgnoreBlankSummary' => '',
 -                      'wpIgnoreBlankArticle' => true
 +                      'wpIgnoreBlankArticle' => true,
 +                      'wpIgnoreSelfRedirect' => true,
                );
  
                if ( !is_null( $params['summary'] ) ) {
                switch ( $status->value ) {
                        case EditPage::AS_HOOK_ERROR:
                        case EditPage::AS_HOOK_ERROR_EXPECTED:
-                               $this->dieUsageMsg( 'hookaborted' );
+                               if ( isset( $status->apiHookResult ) ) {
+                                       $r = $status->apiHookResult;
+                                       $r['result'] = 'Failure';
+                                       $apiResult->addValue( null, $this->getModuleName(), $r );
+                                       return;
+                               } else {
+                                       $this->dieUsageMsg( 'hookaborted' );
+                               }
  
                        case EditPage::AS_PARSE_ERROR:
                                $this->dieUsage( $status->getMessage(), 'parseerror' );