Merge "EditPage: Show a different label for the button on create vs. modify"
[lhc/web/wiklou.git] / includes / EditPage.php
index 7f94140..da35fb5 100644 (file)
@@ -20,6 +20,8 @@
  * @file
  */
 
+use MediaWiki\Logger\LoggerFactory;
+
 /**
  * The edit page/HTML interface (split from Article)
  * The actual database and text munging is still in Article,
@@ -258,9 +260,6 @@ class EditPage {
        /** @var bool */
        public $tooBig = false;
 
-       /** @var bool */
-       public $kblength = false;
-
        /** @var bool */
        public $missingComment = false;
 
@@ -394,6 +393,9 @@ class EditPage {
        /** @var bool */
        protected $edit;
 
+       /** @var bool|int */
+       protected $contentLength = false;
+
        /**
         * @var bool Set in ApiEditPage, based on ContentHandler::allowsDirectApiEditing
         */
@@ -1249,9 +1251,31 @@ class EditPage {
 
                        return $handler->makeEmptyContent();
                } else {
-                       # nasty side-effect, but needed for consistency
-                       $this->contentModel = $rev->getContentModel();
-                       $this->contentFormat = $rev->getContentFormat();
+                       // Content models should always be the same since we error
+                       // out if they are different before this point.
+                       $logger = LoggerFactory::getInstance( 'editpage' );
+                       if ( $this->contentModel !== $rev->getContentModel() ) {
+                               $logger->warning( "Overriding content model from current edit {prev} to {new}", [
+                                       'prev' => $this->contentModel,
+                                       'new' => $rev->getContentModel(),
+                                       'title' => $this->getTitle()->getPrefixedDBkey(),
+                                       'method' => __METHOD__
+                               ] );
+                               $this->contentModel = $rev->getContentModel();
+                       }
+
+                       // Given that the content models should match, the current selected
+                       // format should be supported.
+                       if ( !$content->isSupportedFormat( $this->contentFormat ) ) {
+                               $logger->warning( "Current revision content format unsupported. Overriding {prev} to {new}", [
+
+                                       'prev' => $this->contentFormat,
+                                       'new' => $rev->getContentFormat(),
+                                       'title' => $this->getTitle()->getPrefixedDBkey(),
+                                       'method' => __METHOD__
+                               ] );
+                               $this->contentFormat = $rev->getContentFormat();
+                       }
 
                        return $content;
                }
@@ -1286,7 +1310,7 @@ class EditPage {
                        return $this->mPreloadContent;
                }
 
-               $handler = ContentHandler::getForTitle( $this->getTitle() );
+               $handler = ContentHandler::getForModelID( $this->contentModel );
 
                if ( $preload === '' ) {
                        return $handler->makeEmptyContent();
@@ -1450,7 +1474,7 @@ class EditPage {
 
                        case self::AS_CANNOT_USE_CUSTOM_MODEL:
                        case self::AS_PARSE_ERROR:
-                               $wgOut->addWikiText( '<div class="error">' . $status->getWikiText() . '</div>' );
+                               $wgOut->addWikiText( '<div class="error">' . "\n" . $status->getWikiText() . '</div>' );
                                return true;
 
                        case self::AS_SUCCESS_NEW_ARTICLE:
@@ -1527,7 +1551,7 @@ class EditPage {
                                // is if an extension hook aborted from inside ArticleSave.
                                // Render the status object into $this->hookError
                                // FIXME this sucks, we should just use the Status object throughout
-                               $this->hookError = '<div class="error">' . $status->getWikiText() .
+                               $this->hookError = '<div class="error">' ."\n" . $status->getWikiText() .
                                        '</div>';
                                return true;
                }
@@ -1750,8 +1774,8 @@ class EditPage {
                        return $status;
                }
 
-               $this->kblength = (int)( strlen( $this->textbox1 ) / 1024 );
-               if ( $this->kblength > $wgMaxArticleSize ) {
+               $this->contentLength = strlen( $this->textbox1 );
+               if ( $this->contentLength > $wgMaxArticleSize * 1024 ) {
                        // Error will be displayed by showEditForm()
                        $this->tooBig = true;
                        $status->setResult( false, self::AS_CONTENT_TOO_BIG );
@@ -2038,8 +2062,8 @@ class EditPage {
                }
 
                // Check for length errors again now that the section is merged in
-               $this->kblength = (int)( strlen( $this->toEditText( $content ) ) / 1024 );
-               if ( $this->kblength > $wgMaxArticleSize ) {
+               $this->contentLength = strlen( $this->toEditText( $content ) );
+               if ( $this->contentLength > $wgMaxArticleSize * 1024 ) {
                        $this->tooBig = true;
                        $status->setResult( false, self::AS_MAX_ARTICLE_SIZE_EXCEEDED );
                        return $status;
@@ -2690,8 +2714,9 @@ class EditPage {
                $wgOut->addHTML( Html::rawElement( 'div', [ 'class' => 'hiddencats' ],
                        Linker::formatHiddenCategories( $this->page->getHiddenCategories() ) ) );
 
-               $wgOut->addHTML( Html::rawElement( 'div', [ 'class' => 'limitreport' ],
-                       self::getPreviewLimitReport( $this->mParserOutput ) ) );
+               if ( $this->mParserOutput ) {
+                       $wgOut->setLimitReportData( $this->mParserOutput->getLimitReportData() );
+               }
 
                $wgOut->addModules( 'mediawiki.action.edit.collapsibleFooter' );
 
@@ -2889,6 +2914,9 @@ class EditPage {
                                        );
                                }
                                if ( $this->getTitle()->isSubpageOf( $wgUser->getUserPage() ) ) {
+                                       $wgOut->wrapWikiMsg( '<div class="mw-usercssjspublic">$1</div>',
+                                               $this->isCssSubpage ? 'usercssispublic' : 'userjsispublic'
+                                       );
                                        if ( $this->formtype !== 'preview' ) {
                                                if ( $this->isCssSubpage && $wgAllowUserCss ) {
                                                        $wgOut->wrapWikiMsg(
@@ -2944,15 +2972,15 @@ class EditPage {
                                        'wrap' => "<div class=\"mw-titleprotectedwarning\">\n$1</div>" ] );
                }
 
-               if ( $this->kblength === false ) {
-                       $this->kblength = (int)( strlen( $this->textbox1 ) / 1024 );
+               if ( $this->contentLength === false ) {
+                       $this->contentLength = strlen( $this->textbox1 );
                }
 
-               if ( $this->tooBig || $this->kblength > $wgMaxArticleSize ) {
+               if ( $this->tooBig || $this->contentLength > $wgMaxArticleSize * 1024 ) {
                        $wgOut->wrapWikiMsg( "<div class='error' id='mw-edit-longpageerror'>\n$1\n</div>",
                                [
                                        'longpageerror',
-                                       $wgLang->formatNum( $this->kblength ),
+                                       $wgLang->formatNum( round( $this->contentLength / 1024, 3 ) ),
                                        $wgLang->formatNum( $wgMaxArticleSize )
                                ]
                        );
@@ -3026,7 +3054,7 @@ class EditPage {
         * @param string $summary The text of the summary to display
         */
        protected function showSummaryInput( $isSubjectPreview, $summary = "" ) {
-               global $wgOut, $wgContLang;
+               global $wgOut;
                # Add a class if 'missingsummary' is triggered to allow styling of the summary line
                $summaryClass = $this->missingSummary ? 'mw-summarymissed' : 'mw-summary';
                if ( $isSubjectPreview ) {
@@ -3038,7 +3066,6 @@ class EditPage {
                                return;
                        }
                }
-               $summary = $wgContLang->recodeForEdit( $summary );
                $labelText = wfMessage( $isSubjectPreview ? 'subject' : 'summary' )->parse();
                list( $label, $input ) = $this->getSummaryInput(
                        $summary,
@@ -3419,41 +3446,12 @@ HTML
                        return '';
                }
 
-               $limitReport = Html::rawElement( 'div', [ 'class' => 'mw-limitReportExplanation' ],
-                       wfMessage( 'limitreport-title' )->parseAsBlock()
+               return ResourceLoader::makeInlineScript(
+                       ResourceLoader::makeConfigSetScript(
+                               [ 'wgPageParseReport' => $output->getLimitReportData() ],
+                               true
+                       )
                );
-
-               // Show/hide animation doesn't work correctly on a table, so wrap it in a div.
-               $limitReport .= Html::openElement( 'div', [ 'class' => 'preview-limit-report-wrapper' ] );
-
-               $limitReport .= Html::openElement( 'table', [
-                       'class' => 'preview-limit-report wikitable'
-               ] ) .
-                       Html::openElement( 'tbody' );
-
-               foreach ( $output->getLimitReportData() as $key => $value ) {
-                       if ( Hooks::run( 'ParserLimitReportFormat',
-                               [ $key, &$value, &$limitReport, true, true ]
-                       ) ) {
-                               $keyMsg = wfMessage( $key );
-                               $valueMsg = wfMessage( [ "$key-value-html", "$key-value" ] );
-                               if ( !$valueMsg->exists() ) {
-                                       $valueMsg = new RawMessage( '$1' );
-                               }
-                               if ( !$keyMsg->isDisabled() && !$valueMsg->isDisabled() ) {
-                                       $limitReport .= Html::openElement( 'tr' ) .
-                                               Html::rawElement( 'th', null, $keyMsg->parse() ) .
-                                               Html::rawElement( 'td', null, $valueMsg->params( $value )->parse() ) .
-                                               Html::closeElement( 'tr' );
-                               }
-                       }
-               }
-
-               $limitReport .= Html::closeElement( 'tbody' ) .
-                       Html::closeElement( 'table' ) .
-                       Html::closeElement( 'div' );
-
-               return $limitReport;
        }
 
        protected function showStandardInputs( &$tabindex = 2 ) {
@@ -3513,13 +3511,12 @@ HTML
                if ( Hooks::run( 'EditPageBeforeConflictDiff', [ &$this, &$wgOut ] ) ) {
                        $stats = $wgOut->getContext()->getStats();
                        $stats->increment( 'edit.failures.conflict' );
-                       if ( $this->mTitle->isTalkPage() ) {
-                               $stats->increment( 'edit.failures.conflict.byType.talk' );
-                       } else {
-                               $stats->increment( 'edit.failures.conflict.byType.subject' );
-                       }
-                       if ( $this->mTitle->getNamespace() === NS_PROJECT ) {
-                               $stats->increment( 'edit.failures.conflict.byNamespace.project' );
+                       // Only include 'standard' namespaces to avoid creating unknown numbers of statsd metrics
+                       if (
+                               $this->mTitle->getNamespace() >= NS_MAIN &&
+                               $this->mTitle->getNamespace() <= NS_CATEGORY_TALK
+                       ) {
+                               $stats->increment( 'edit.failures.conflict.byNamespaceId.' . $this->mTitle->getNamespace() );
                        }
 
                        $wgOut->wrapWikiMsg( '<h2>$1</h2>', "yourdiff" );
@@ -3648,7 +3645,7 @@ HTML
         * @return string
         */
        function getPreviewText() {
-               global $wgOut, $wgUser, $wgRawHtml, $wgLang;
+               global $wgOut, $wgRawHtml, $wgLang;
                global $wgAllowUserCss, $wgAllowUserJs;
 
                $stats = $wgOut->getContext()->getStats();
@@ -3702,10 +3699,6 @@ HTML
                                $note = wfMessage( 'previewnote' )->plain() . ' ' . $continueEditing;
                        }
 
-                       $parserOptions = $this->page->makeParserOptions( $this->mArticle->getContext() );
-                       $parserOptions->setIsPreview( true );
-                       $parserOptions->setIsSectionPreview( !is_null( $this->section ) && $this->section !== '' );
-
                        # don't parse non-wikitext pages, show message about preview
                        if ( $this->mTitle->isCssJsSubpage() || $this->mTitle->isCssOrJsPage() ) {
                                if ( $this->mTitle->isCssJsSubpage() ) {
@@ -3749,18 +3742,9 @@ HTML
                        ContentHandler::runLegacyHooks( 'EditPageGetPreviewText', $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
-
-                       $pstContent = $content->preSaveTransform( $this->mTitle, $wgUser, $parserOptions );
-                       $scopedCallback = $parserOptions->setupFakeRevision(
-                               $this->mTitle, $pstContent, $wgUser );
-                       $parserOutput = $pstContent->getParserOutput( $this->mTitle, null, $parserOptions );
-
-                       $parserOutput->setEditSectionTokens( false ); // no section edit links
-                       $previewHTML = $parserOutput->getText();
+                       $parserResult = $this->doPreviewParse( $content );
+                       $parserOutput = $parserResult['parserOutput'];
+                       $previewHTML = $parserResult['html'];
                        $this->mParserOutput = $parserOutput;
                        $wgOut->addParserOutputMetadata( $parserOutput );
 
@@ -3768,7 +3752,6 @@ HTML
                                $note .= "\n\n" . implode( "\n\n", $parserOutput->getWarnings() );
                        }
 
-                       ScopedCallback::consume( $scopedCallback );
                } catch ( MWContentSerializationException $ex ) {
                        $m = wfMessage(
                                'content-failed-to-parse',
@@ -3799,6 +3782,41 @@ HTML
                return $previewhead . $previewHTML . $this->previewTextAfterContent;
        }
 
+       /**
+        * Get parser options for a preview
+        * @return ParserOptions
+        */
+       protected function getPreviewParserOptions() {
+               $parserOptions = $this->page->makeParserOptions( $this->mArticle->getContext() );
+               $parserOptions->setIsPreview( true );
+               $parserOptions->setIsSectionPreview( !is_null( $this->section ) && $this->section !== '' );
+               $parserOptions->enableLimitReport();
+               return $parserOptions;
+       }
+
+       /**
+        * Parse the page for a preview. Subclasses may override this class, in order
+        * to parse with different options, or to otherwise modify the preview HTML.
+        *
+        * @param Content @content The page content
+        * @return Associative array with keys:
+        *   - parserOutput: The ParserOutput object
+        *   - html: The HTML to be displayed
+        */
+       protected function doPreviewParse( Content $content ) {
+               global $wgUser;
+               $parserOptions = $this->getPreviewParserOptions();
+               $pstContent = $content->preSaveTransform( $this->mTitle, $wgUser, $parserOptions );
+               $scopedCallback = $parserOptions->setupFakeRevision(
+                       $this->mTitle, $pstContent, $wgUser );
+               $parserOutput = $pstContent->getParserOutput( $this->mTitle, null, $parserOptions );
+               ScopedCallback::consume( $scopedCallback );
+               $parserOutput->setEditSectionTokens( false ); // no section edit links
+               return [
+                       'parserOutput' => $parserOutput,
+                       'html' => $parserOutput->getText() ];
+       }
+
        /**
         * @return array
         */
@@ -4155,11 +4173,9 @@ HTML
         * @return string
         */
        protected function safeUnicodeOutput( $text ) {
-               global $wgContLang;
-               $codedText = $wgContLang->recodeForEdit( $text );
                return $this->checkUnicodeCompliantBrowser()
-                       ? $codedText
-                       : $this->makeSafe( $codedText );
+                       ? $text
+                       : $this->makesafe( $text );
        }
 
        /**