SECURITY: Fix CORS origin matching in the API
[lhc/web/wiklou.git] / includes / EditPage.php
index db2e442..d05b996 100644 (file)
@@ -463,7 +463,7 @@ class EditPage {
        function edit() {
                global $wgOut, $wgRequest, $wgUser;
                // Allow extensions to modify/prevent this form or submission
-               if ( !wfRunHooks( 'AlternateEdit', array( $this ) ) ) {
+               if ( !Hooks::run( 'AlternateEdit', array( $this ) ) ) {
                        return;
                }
 
@@ -558,9 +558,9 @@ class EditPage {
                        }
 
                        if ( !$this->mTitle->getArticleID() ) {
-                               wfRunHooks( 'EditFormPreloadText', array( &$this->textbox1, &$this->mTitle ) );
+                               Hooks::run( 'EditFormPreloadText', array( &$this->textbox1, &$this->mTitle ) );
                        } else {
-                               wfRunHooks( 'EditFormInitialText', array( $this ) );
+                               Hooks::run( 'EditFormInitialText', array( $this ) );
                        }
 
                }
@@ -627,7 +627,7 @@ class EditPage {
                        throw new PermissionsError( $action, $permErrors );
                }
 
-               wfRunHooks( 'EditPage::showReadOnlyForm:initial', array( $this, &$wgOut ) );
+               Hooks::run( 'EditPage::showReadOnlyForm:initial', array( $this, &$wgOut ) );
 
                $wgOut->setRobotPolicy( 'noindex,nofollow' );
                $wgOut->setPageTitle( wfMessage(
@@ -929,7 +929,7 @@ class EditPage {
                        $this->section === 'new' ? 'MediaWiki:addsection-editintro' : '' );
 
                // Allow extensions to modify form data
-               wfRunHooks( 'EditPage::importFormData', array( $this, $request ) );
+               Hooks::run( 'EditPage::importFormData', array( $this, $request ) );
 
                wfProfileOut( __METHOD__ );
        }
@@ -1368,7 +1368,7 @@ class EditPage {
                                $sectionanchor = $resultDetails['sectionanchor'];
 
                                // Give extensions a chance to modify URL query on update
-                               wfRunHooks(
+                               Hooks::run(
                                        'ArticleUpdateBeforeRedirect',
                                        array( $this->mArticle, &$sectionanchor, &$extraQuery )
                                );
@@ -1434,8 +1434,8 @@ class EditPage {
        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;
@@ -1448,15 +1448,24 @@ class EditPage {
                }
 
                // Run new style post-section-merge edit filter
-               if ( !wfRunHooks( 'EditFilterMergedContent',
-                       array( $this->mArticle->getContext(), $content, $status, $this->summary,
-                               $user, $this->minoredit ) ) ) {
-
+               if ( !Hooks::run( 'EditFilterMergedContent',
+                               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
@@ -1532,7 +1541,7 @@ class EditPage {
                wfProfileIn( __METHOD__ );
                wfProfileIn( __METHOD__ . '-checks' );
 
-               if ( !wfRunHooks( 'EditPage::attemptSave', array( $this ) ) ) {
+               if ( !Hooks::run( 'EditPage::attemptSave', array( $this ) ) ) {
                        wfDebug( "Hook 'EditPage::attemptSave' aborted article saving\n" );
                        $status->fatal( 'hookaborted' );
                        $status->value = self::AS_HOOK_ERROR;
@@ -1618,7 +1627,7 @@ class EditPage {
                        wfProfileOut( __METHOD__ );
                        return $status;
                }
-               if ( !wfRunHooks(
+               if ( !Hooks::run(
                        'EditFilter',
                        array( $this, $this->textbox1, $this->section, &$this->hookError, $this->summary ) )
                ) {
@@ -1919,11 +1928,15 @@ class EditPage {
                        && $content->isRedirect()
                        && $content->getRedirectTarget()->equals( $this->getTitle() )
                ) {
-                       $this->selfRedirect = true;
-                       $status->fatal( 'selfredirect' );
-                       $status->value = self::AS_SELF_REDIRECT;
-                       wfProfileOut( __METHOD__ );
-                       return $status;
+                       // If the page already redirects to itself, don't warn.
+                       $currentTarget = $this->getCurrentContent()->getRedirectTarget();
+                       if ( !$currentTarget || !$currentTarget->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
@@ -2345,6 +2358,9 @@ class EditPage {
         * 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;
@@ -2360,7 +2376,7 @@ class EditPage {
                        $previewOutput = $this->getPreviewText();
                }
 
-               wfRunHooks( 'EditPage::showEditForm:initial', array( &$this, &$wgOut ) );
+               Hooks::run( 'EditPage::showEditForm:initial', array( &$this, &$wgOut ) );
 
                $this->setHeaders();
 
@@ -2403,6 +2419,7 @@ class EditPage {
                ) );
 
                if ( is_callable( $formCallback ) ) {
+                       wfWarn( 'The $formCallback parameter to ' . __METHOD__ . 'is deprecated' );
                        call_user_func_array( $formCallback, array( &$wgOut ) );
                }
 
@@ -2426,7 +2443,7 @@ class EditPage {
                        . Xml::closeElement( 'div' )
                );
 
-               wfRunHooks( 'EditPage::showEditForm:fields', array( &$this, &$wgOut ) );
+               Hooks::run( 'EditPage::showEditForm:fields', array( &$this, &$wgOut ) );
 
                // Put these up at the top to ensure they aren't lost on early form submission
                $this->showFormBeforeText();
@@ -3072,7 +3089,7 @@ HTML
                }
                # This hook seems slightly odd here, but makes things more
                # consistent for extensions.
-               wfRunHooks( 'OutputPageBeforeHTML', array( &$wgOut, &$text ) );
+               Hooks::run( 'OutputPageBeforeHTML', array( &$wgOut, &$text ) );
                $wgOut->addHTML( $text );
                if ( $this->mTitle->getNamespace() == NS_CATEGORY ) {
                        $this->mArticle->closeShowCategory();
@@ -3111,7 +3128,7 @@ HTML
 
                if ( $newContent ) {
                        ContentHandler::runLegacyHooks( 'EditPageGetDiffText', array( $this, &$newContent ) );
-                       wfRunHooks( 'EditPageGetDiffContent', array( $this, &$newContent ) );
+                       Hooks::run( 'EditPageGetDiffContent', array( $this, &$newContent ) );
 
                        $popts = ParserOptions::newFromUserAndLang( $wgUser, $wgContLang );
                        $newContent = $newContent->preSaveTransform( $this->mTitle, $wgUser, $popts );
@@ -3163,7 +3180,7 @@ HTML
         */
        protected function showTosSummary() {
                $msg = 'editpage-tos-summary';
-               wfRunHooks( 'EditPageTosSummary', array( $this->mTitle, &$msg ) );
+               Hooks::run( 'EditPageTosSummary', array( $this->mTitle, &$msg ) );
                if ( !wfMessage( $msg )->isDisabled() ) {
                        global $wgOut;
                        $wgOut->addHTML( '<div class="mw-tos-summary">' );
@@ -3207,7 +3224,7 @@ HTML
                                '[[' . wfMessage( 'copyrightpage' )->inContentLanguage()->text() . ']]' );
                }
                // Allow for site and per-namespace customization of contribution/copyright notice.
-               wfRunHooks( 'EditPageCopyrightWarning', array( $title, &$copywarnMsg ) );
+               Hooks::run( 'EditPageCopyrightWarning', array( $title, &$copywarnMsg ) );
 
                return "<div id=\"editpage-copywarn\">\n" .
                        call_user_func_array( 'wfMessage', $copywarnMsg )->$format() . "\n</div>";
@@ -3240,7 +3257,7 @@ HTML
                        Html::openElement( 'tbody' );
 
                foreach ( $output->getLimitReportData() as $key => $value ) {
-                       if ( wfRunHooks( 'ParserLimitReportFormat',
+                       if ( Hooks::run( 'ParserLimitReportFormat',
                                array( $key, &$value, &$limitReport, true, true )
                        ) ) {
                                $keyMsg = wfMessage( $key );
@@ -3308,7 +3325,7 @@ HTML
                $wgOut->addHTML( "      <span class='editHelp'>{$edithelp}</span>\n" );
                $wgOut->addHTML( "</div><!-- editButtons -->\n" );
 
-               wfRunHooks( 'EditPage::showStandardInputs:options', array( $this, $wgOut, &$tabindex ) );
+               Hooks::run( 'EditPage::showStandardInputs:options', array( $this, $wgOut, &$tabindex ) );
 
                $wgOut->addHTML( "</div><!-- editOptions -->\n" );
        }
@@ -3320,7 +3337,7 @@ HTML
        protected function showConflict() {
                global $wgOut;
 
-               if ( wfRunHooks( 'EditPageBeforeConflictDiff', array( &$this, &$wgOut ) ) ) {
+               if ( Hooks::run( 'EditPageBeforeConflictDiff', array( &$this, &$wgOut ) ) ) {
                        $wgOut->wrapWikiMsg( '<h2>$1</h2>', "yourdiff" );
 
                        $content1 = $this->toEditContent( $this->textbox1 );
@@ -3471,7 +3488,7 @@ HTML
                        $content = $this->toEditContent( $this->textbox1 );
 
                        $previewHTML = '';
-                       if ( !wfRunHooks(
+                       if ( !Hooks::run(
                                'AlternateEditPreview',
                                array( $this, &$content, &$previewHTML, &$this->mParserOutput ) )
                        ) {
@@ -3496,7 +3513,6 @@ HTML
                        }
 
                        $parserOptions = $this->mArticle->makeParserOptions( $this->mArticle->getContext() );
-                       $parserOptions->setEditSection( false );
                        $parserOptions->setIsPreview( true );
                        $parserOptions->setIsSectionPreview( !is_null( $this->section ) && $this->section !== '' );
 
@@ -3541,20 +3557,24 @@ HTML
 
                        $hook_args = array( $this, &$content );
                        ContentHandler::runLegacyHooks( 'EditPageGetPreviewText', $hook_args );
-                       wfRunHooks( 'EditPageGetPreviewContent', $hook_args );
+                       Hooks::run( 'EditPageGetPreviewContent', $hook_args );
 
                        $parserOptions->enableLimitReport();
 
                        # 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 );
@@ -3735,7 +3755,7 @@ HTML
 
                $toolbar = '<div id="toolbar"></div>';
 
-               wfRunHooks( 'EditPageBeforeEditToolbar', array( &$toolbar ) );
+               Hooks::run( 'EditPageBeforeEditToolbar', array( &$toolbar ) );
 
                return $toolbar;
        }
@@ -3802,7 +3822,7 @@ HTML
                                $checkboxes['watch'] = $watchThisHtml;
                        }
                }
-               wfRunHooks( 'EditPageBeforeEditChecks', array( &$this, &$checkboxes, &$tabindex ) );
+               Hooks::run( 'EditPageBeforeEditChecks', array( &$this, &$checkboxes, &$tabindex ) );
                return $checkboxes;
        }
 
@@ -3843,7 +3863,7 @@ HTML
                $buttons['diff'] = Html::submitButton( wfMessage( 'showdiff' )->text(),
                        $attribs );
 
-               wfRunHooks( 'EditPageBeforeEditButtons', array( &$this, &$buttons, &$tabindex ) );
+               Hooks::run( 'EditPageBeforeEditButtons', array( &$this, &$buttons, &$tabindex ) );
                return $buttons;
        }
 
@@ -3887,7 +3907,7 @@ HTML
                $wgOut->prepareErrorPage( wfMessage( 'nosuchsectiontitle' ) );
 
                $res = wfMessage( 'nosuchsectiontext', $this->section )->parseAsBlock();
-               wfRunHooks( 'EditPageNoSuchSection', array( &$this, &$res ) );
+               Hooks::run( 'EditPageNoSuchSection', array( &$this, &$res ) );
                $wgOut->addHTML( $res );
 
                $wgOut->returnToMain( false, $this->mTitle );