Merge "resources: Strip '$' and 'mw' from file closures"
[lhc/web/wiklou.git] / includes / EditPage.php
index 2ae941f..f1f0572 100644 (file)
@@ -236,7 +236,7 @@ class EditPage {
        public $action = 'submit';
 
        /** @var bool Whether an edit conflict needs to be resolved. Detected based on whether
-        * $editRevId is different from the current revision. When a conflict has successfully
+        * $editRevId is different than the latest revision. When a conflict has successfully
         * been resolved by a 3-way-merge, this field is set to false.
         */
        public $isConflict = false;
@@ -250,7 +250,10 @@ class EditPage {
        /** @var string */
        public $formtype;
 
-       /** @var bool */
+       /** @var bool
+        * True the first time the edit form is rendered, false after re-rendering
+        * with diff, save prompts, etc.
+        */
        public $firsttime;
 
        /** @var bool|stdClass */
@@ -330,7 +333,9 @@ class EditPage {
        /** @var bool */
        public $recreate = false;
 
-       /** @var string */
+       /** @var string
+        * Page content input field.
+        */
        public $textbox1 = '';
 
        /** @var string */
@@ -339,18 +344,24 @@ class EditPage {
        /** @var string */
        public $summary = '';
 
-       /** @var bool */
+       /** @var bool
+        * If true, hide the summary field.
+        */
        public $nosummary = false;
 
-       /** @var string */
+       /** @var string
+        * Timestamp of the latest revision of the page when editing was initiated
+        * on the client.
+        */
        public $edittime = '';
 
-       /** @var int ID of the current revision at the time editing was initiated on the client.
-        * This is used to detect and resolve edit conflicts.
+       /** @var int Revision ID of the latest revision of the page when editing
+        * was initiated on the client.  This is used to detect and resolve edit
+        * conflicts.
         *
         * @note 0 if the page did not exist at that time.
         * @note When starting an edit from an old revision, this still records the current
-        * revision at the time , not the one the edit is based on.
+        * revision at the time, not the one the edit is based on.
         *
         * @see $oldid
         * @see getBaseRevision()
@@ -363,10 +374,14 @@ class EditPage {
        /** @var string */
        public $sectiontitle = '';
 
-       /** @var string */
+       /** @var string
+        * Timestamp from the first time the edit form was rendered.
+        */
        public $starttime = '';
 
        /** @var int Revision ID the edit is based on, or 0 if it's the current revision.
+        * FIXME: This isn't used in conflict resolution--provide a better
+        * justification or merge with parentRevId.
         * @see $editRevId
         */
        public $oldid = 0;
@@ -648,7 +663,7 @@ class EditPage {
                $this->isConflict = false;
 
                # Show applicable editing introductions
-               if ( $this->formtype === 'initial' || $this->firsttime ) {
+               if ( $this->formtype == 'initial' || $this->firsttime ) {
                        $this->showIntro();
                }
 
@@ -657,7 +672,7 @@ class EditPage {
                # that edit() already checked just in case someone tries to sneak
                # in the back door with a hand-edited submission URL.
 
-               if ( 'save' === $this->formtype ) {
+               if ( 'save' == $this->formtype ) {
                        $resultDetails = null;
                        $status = $this->attemptSave( $resultDetails );
                        if ( !$this->handleStatus( $status, $resultDetails ) ) {
@@ -667,9 +682,12 @@ class EditPage {
 
                # First time through: get contents, set time for conflict
                # checking, etc.
-               if ( 'initial' === $this->formtype || $this->firsttime ) {
+               if ( 'initial' == $this->formtype || $this->firsttime ) {
                        if ( $this->initialiseForm() === false ) {
-                               $this->noSuchSectionPage();
+                               $out = $this->context->getOutput();
+                               if ( $out->getRedirect() === '' ) { // mcrundo hack redirects, don't override it
+                                       $this->noSuchSectionPage();
+                               }
                                return;
                        }
 
@@ -706,9 +724,9 @@ class EditPage {
                foreach ( $permErrors as $error ) {
                        if ( ( $this->preview || $this->diff )
                                && (
-                                       $error[0] === 'blockedtext' ||
-                                       $error[0] === 'autoblockedtext' ||
-                                       $error[0] === 'systemblockedtext'
+                                       $error[0] == 'blockedtext' ||
+                                       $error[0] == 'autoblockedtext' ||
+                                       $error[0] == 'systemblockedtext'
                                )
                        ) {
                                $remove[] = $error;
@@ -825,13 +843,13 @@ class EditPage {
                        // security reasons
                        return false;
                }
-               if ( $request->getVal( 'preview' ) === 'yes' ) {
+               if ( $request->getVal( 'preview' ) == 'yes' ) {
                        // Explicit override from request
                        return true;
-               } elseif ( $request->getVal( 'preview' ) === 'no' ) {
+               } elseif ( $request->getVal( 'preview' ) == 'no' ) {
                        // Explicit override from request
                        return false;
-               } elseif ( $this->section === 'new' ) {
+               } elseif ( $this->section == 'new' ) {
                        // Nothing *to* preview for new sections
                        return false;
                } elseif ( ( $request->getVal( 'preload' ) !== null || $this->mTitle->exists() )
@@ -895,7 +913,7 @@ class EditPage {
                        throw new ErrorPageError( 'sectioneditnotsupported-title', 'sectioneditnotsupported-text' );
                }
 
-               $this->isNew = !$this->mTitle->exists() || $this->section === 'new';
+               $this->isNew = !$this->mTitle->exists() || $this->section == 'new';
 
                if ( $request->wasPosted() ) {
                        # These fields need to be checked for encoding.
@@ -995,8 +1013,8 @@ class EditPage {
 
                        $user = $this->context->getUser();
                        # Don't force edit summaries when a user is editing their own user or talk page
-                       if ( ( $this->mTitle->mNamespace === NS_USER || $this->mTitle->mNamespace === NS_USER_TALK )
-                               && $this->mTitle->getText() === $user->getName()
+                       if ( ( $this->mTitle->mNamespace == NS_USER || $this->mTitle->mNamespace == NS_USER_TALK )
+                               && $this->mTitle->getText() == $user->getName()
                        ) {
                                $this->allowBlankSummary = true;
                        } else {
@@ -1036,11 +1054,11 @@ class EditPage {
 
                        // When creating a new section, we can preload a section title by passing it as the
                        // preloadtitle parameter in the URL (T15100)
-                       if ( $this->section === 'new' && $request->getVal( 'preloadtitle' ) ) {
+                       if ( $this->section == 'new' && $request->getVal( 'preloadtitle' ) ) {
                                $this->sectiontitle = $request->getVal( 'preloadtitle' );
                                // Once wpSummary isn't being use for setting section titles, we should delete this.
                                $this->summary = $request->getVal( 'preloadtitle' );
-                       } elseif ( $this->section !== 'new' && $request->getVal( 'summary' ) ) {
+                       } elseif ( $this->section != 'new' && $request->getVal( 'summary' ) !== '' ) {
                                $this->summary = $request->getText( 'summary' );
                                if ( $this->summary !== '' ) {
                                        $this->hasPresetSummary = true;
@@ -1156,16 +1174,14 @@ class EditPage {
         * @since 1.21
         */
        protected function getContentObject( $def_content = null ) {
-               global $wgContLang;
-
                $content = false;
 
                $user = $this->context->getUser();
                $request = $this->context->getRequest();
                // For message page not locally set, use the i18n message.
                // For other non-existent articles, use preload text if any.
-               if ( !$this->mTitle->exists() || $this->section === 'new' ) {
-                       if ( $this->mTitle->getNamespace() === NS_MEDIAWIKI && $this->section !== 'new' ) {
+               if ( !$this->mTitle->exists() || $this->section == 'new' ) {
+                       if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI && $this->section != 'new' ) {
                                # If this is a system message, get the default text.
                                $msg = $this->mTitle->getDefaultMessageText();
 
@@ -1207,8 +1223,13 @@ class EditPage {
                                                !$oldrev->isDeleted( Revision::DELETED_TEXT )
                                        ) {
                                                if ( WikiPage::hasDifferencesOutsideMainSlot( $undorev, $oldrev ) ) {
-                                                       // Cannot yet undo edits that involve anything other the main slot.
-                                                       $undoMsg = 'main-slot-only';
+                                                       // Hack for undo while EditPage can't handle multi-slot editing
+                                                       $this->context->getOutput()->redirect( $this->mTitle->getFullURL( [
+                                                               'action' => 'mcrundo',
+                                                               'undo' => $undo,
+                                                               'undoafter' => $undoafter,
+                                                       ] ) );
+                                                       return false;
                                                } else {
                                                        $content = $this->page->getUndoContent( $undorev, $oldrev );
 
@@ -1220,7 +1241,8 @@ class EditPage {
 
                                                if ( $undoMsg === null ) {
                                                        $oldContent = $this->page->getContent( Revision::RAW );
-                                                       $popts = ParserOptions::newFromUserAndLang( $user, $wgContLang );
+                                                       $popts = ParserOptions::newFromUserAndLang(
+                                                               $user, MediaWikiServices::getInstance()->getContentLanguage() );
                                                        $newContent = $content->preSaveTransform( $this->mTitle, $user, $popts );
                                                        if ( $newContent->getModel() !== $oldContent->getModel() ) {
                                                                // The undo may change content
@@ -1242,7 +1264,7 @@ class EditPage {
 
                                                                # If we just undid one rev, use an autosummary
                                                                $firstrev = $oldrev->getNext();
-                                                               if ( $firstrev && $firstrev->getId() === $undo ) {
+                                                               if ( $firstrev && $firstrev->getId() == $undo ) {
                                                                        $userText = $undorev->getUserText();
                                                                        if ( $userText === '' ) {
                                                                                $undoSummary = $this->context->msg(
@@ -1277,7 +1299,7 @@ class EditPage {
                                        $out = $this->context->getOutput();
                                        // Messages: undo-success, undo-failure, undo-main-slot-only, undo-norev,
                                        // undo-nochange.
-                                       $class = ( $undoMsg === 'success' ? '' : 'error ' ) . "mw-undo-{$undoMsg}";
+                                       $class = ( $undoMsg == 'success' ? '' : 'error ' ) . "mw-undo-{$undoMsg}";
                                        $this->editFormPageTop .= $out->parse( "<div class=\"{$class}\">" .
                                                $this->context->msg( 'undo-' . $undoMsg )->plain() . '</div>', true, /* interface */true );
                                }
@@ -1307,7 +1329,7 @@ class EditPage {
         * @return Content|null
         */
        private function getOriginalContent( User $user ) {
-               if ( $this->section === 'new' ) {
+               if ( $this->section == 'new' ) {
                        return $this->getCurrentContent();
                }
                $revision = $this->mArticle->getRevisionFetched();
@@ -1498,7 +1520,7 @@ class EditPage {
                $postEditKey = self::POST_EDIT_COOKIE_KEY_PREFIX . $revisionId;
 
                $val = 'saved';
-               if ( $statusValue === self::AS_SUCCESS_NEW_ARTICLE ) {
+               if ( $statusValue == self::AS_SUCCESS_NEW_ARTICLE ) {
                        $val = 'created';
                } elseif ( $this->oldid ) {
                        $val = 'restored';
@@ -1553,8 +1575,8 @@ class EditPage {
                 * @todo FIXME: once the interface for internalAttemptSave() is made
                 *   nicer, this should use the message in $status
                 */
-               if ( $status->value === self::AS_SUCCESS_UPDATE
-                       || $status->value === self::AS_SUCCESS_NEW_ARTICLE
+               if ( $status->value == self::AS_SUCCESS_UPDATE
+                       || $status->value == self::AS_SUCCESS_NEW_ARTICLE
                ) {
                        $this->incrementResolvedConflicts();
 
@@ -1617,7 +1639,7 @@ class EditPage {
                                );
 
                                if ( $resultDetails['redirect'] ) {
-                                       if ( $extraQuery === '' ) {
+                                       if ( $extraQuery == '' ) {
                                                $extraQuery = 'redirect=no';
                                        } else {
                                                $extraQuery = 'redirect=no&' . $extraQuery;
@@ -1667,7 +1689,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">' ."\n" . $status->getWikiText() .
+                               $this->hookError = '<div class="error">' . "\n" . $status->getWikiText() .
                                        '</div>';
                                return true;
                }
@@ -1684,7 +1706,7 @@ class EditPage {
         */
        protected function runPostMergeFilters( Content $content, Status $status, User $user ) {
                // Run old style post-section-merge edit filter
-               if ( $this->hookError !== '' ) {
+               if ( $this->hookError != '' ) {
                        # ...or the hook could be expecting us to produce an error
                        $status->fatal( 'hookaborted' );
                        $status->value = self::AS_HOOK_ERROR_EXPECTED;
@@ -1760,7 +1782,7 @@ ERROR;
                        if ( $this->summary === '' ) {
                                $cleanSectionTitle = $wgParser->stripSectionName( $this->sectiontitle );
                                return $this->context->msg( 'newsectionsummary' )
-                                       ->rawParams( $cleanSectionTitle )->inContentLanguage()->text();
+                                       ->plaintextParams( $cleanSectionTitle )->inContentLanguage()->text();
                        }
                } elseif ( $this->summary !== '' ) {
                        $sectionanchor = $this->guessSectionName( $this->summary );
@@ -1768,7 +1790,7 @@ ERROR;
                        # in the revision summary.
                        $cleanSummary = $wgParser->stripSectionName( $this->summary );
                        return $this->context->msg( 'newsectionsummary' )
-                               ->rawParams( $cleanSummary )->inContentLanguage()->text();
+                               ->plaintextParams( $cleanSummary )->inContentLanguage()->text();
                }
                return $this->summary;
        }
@@ -1846,7 +1868,7 @@ ERROR;
                }
 
                # Check image redirect
-               if ( $this->mTitle->getNamespace() === NS_FILE &&
+               if ( $this->mTitle->getNamespace() == NS_FILE &&
                        $textbox_content->isRedirect() &&
                        !$user->isAllowed( 'upload' )
                ) {
@@ -1858,7 +1880,7 @@ ERROR;
 
                # Check for spam
                $match = self::matchSummarySpamRegex( $this->summary );
-               if ( $match === false && $this->section === 'new' ) {
+               if ( $match === false && $this->section == 'new' ) {
                        # $wgSpamRegex is enforced on this new heading/summary because, unlike
                        # regular summaries, it is added to the actual wikitext.
                        if ( $this->sectiontitle !== '' ) {
@@ -1891,7 +1913,7 @@ ERROR;
                        $status->fatal( 'hookaborted' );
                        $status->value = self::AS_HOOK_ERROR;
                        return $status;
-               } elseif ( $this->hookError !== '' ) {
+               } elseif ( $this->hookError != '' ) {
                        # ...or the hook could be expecting us to produce an error
                        $status->fatal( 'hookaborted' );
                        $status->value = self::AS_HOOK_ERROR_EXPECTED;
@@ -2020,7 +2042,7 @@ ERROR;
                        $content = $textbox_content;
 
                        $result['sectionanchor'] = '';
-                       if ( $this->section === 'new' ) {
+                       if ( $this->section == 'new' ) {
                                if ( $this->sectiontitle !== '' ) {
                                        // Insert the section title above the content.
                                        $content = $content->addSectionHeader( $this->sectiontitle );
@@ -2047,13 +2069,13 @@ ERROR;
                        // revision that was current when editing was initiated on the client.
                        // This is checked based on the timestamp and revision ID.
                        // TODO: the timestamp based check can probably go away now.
-                       if ( $timestamp !== $this->edittime
-                               || ( $this->editRevId !== null && $this->editRevId !== $latest )
+                       if ( $timestamp != $this->edittime
+                               || ( $this->editRevId !== null && $this->editRevId != $latest )
                        ) {
                                $this->isConflict = true;
-                               if ( $this->section === 'new' ) {
-                                       if ( $this->page->getUserText() === $user->getName() &&
-                                               $this->page->getComment() === $this->newSectionSummary()
+                               if ( $this->section == 'new' ) {
+                                       if ( $this->page->getUserText() == $user->getName() &&
+                                               $this->page->getComment() == $this->newSectionSummary()
                                        ) {
                                                // Probably a duplicate submission of a new comment.
                                                // This can happen when CDN resends a request after
@@ -2142,9 +2164,9 @@ ERROR;
                                return $status;
                        }
 
-                       if ( $this->section === 'new' ) {
+                       if ( $this->section == 'new' ) {
                                // Handle the user preference to force summaries here
-                               if ( !$this->allowBlankSummary && trim( $this->summary ) === '' ) {
+                               if ( !$this->allowBlankSummary && trim( $this->summary ) == '' ) {
                                        $this->missingSummary = true;
                                        $status->fatal( 'missingsummary' ); // or 'missingcommentheader' if $section == 'new'. Blegh
                                        $status->value = self::AS_SUMMARY_NEEDED;
@@ -2152,7 +2174,7 @@ ERROR;
                                }
 
                                // Do not allow the user to post an empty comment
-                               if ( $this->textbox1 === '' ) {
+                               if ( $this->textbox1 == '' ) {
                                        $this->missingComment = true;
                                        $status->fatal( 'missingcommenttext' );
                                        $status->value = self::AS_TEXTBOX_EMPTY;
@@ -2161,7 +2183,7 @@ ERROR;
                        } elseif ( !$this->allowBlankSummary
                                && !$content->equals( $this->getOriginalContent( $user ) )
                                && !$content->isRedirect()
-                               && md5( $this->summary ) === $this->autoSumm
+                               && md5( $this->summary ) == $this->autoSumm
                        ) {
                                $this->missingSummary = true;
                                $status->fatal( 'missingsummary' );
@@ -2171,9 +2193,9 @@ ERROR;
 
                        # All's well
                        $sectionanchor = '';
-                       if ( $this->section === 'new' ) {
+                       if ( $this->section == 'new' ) {
                                $this->summary = $this->newSectionSummary( $sectionanchor );
-                       } elseif ( $this->section !== '' ) {
+                       } elseif ( $this->section != '' ) {
                                # Try to get a section anchor from the section source, redirect
                                # to edited section if header found.
                                # XXX: Might be better to integrate this into Article::replaceSectionAtRev
@@ -2305,7 +2327,7 @@ ERROR;
                $watch = $this->watchthis;
                // Do this in its own transaction to reduce contention...
                DeferredUpdates::addCallableUpdate( function () use ( $user, $title, $watch ) {
-                       if ( $watch === $user->isWatched( $title, User::IGNORE_USER_RIGHTS ) ) {
+                       if ( $watch == $user->isWatched( $title, User::IGNORE_USER_RIGHTS ) ) {
                                return; // nothing to change
                        }
                        WatchAction::doWatchOrUnwatch( $watch, $title, $user );
@@ -2361,7 +2383,7 @@ ERROR;
         * Returns the revision that was current at the time editing was initiated on the client,
         * even if the edit was based on an old revision.
         *
-        * @warning: this method is very poorly named. If the user opened the form with ?oldid=X,
+        * @warning this method is very poorly named. If the user opened the form with ?oldid=X,
         *        one might think of X as the "base revision", which is NOT what this returns,
         *        see oldid for that. One might further assume that this corresponds to the $baseRevId
         *        parameter of WikiPage::doEditContent, which is not the case either.
@@ -2451,11 +2473,11 @@ ERROR;
                $contextTitle = $this->getContextTitle();
                if ( $this->isConflict ) {
                        $msg = 'editconflict';
-               } elseif ( $contextTitle->exists() && $this->section !== '' ) {
-                       $msg = $this->section === 'new' ? 'editingcomment' : 'editingsection';
+               } elseif ( $contextTitle->exists() && $this->section != '' ) {
+                       $msg = $this->section == 'new' ? 'editingcomment' : 'editingsection';
                } else {
                        $msg = $contextTitle->exists()
-                               || ( $contextTitle->getNamespace() === NS_MEDIAWIKI
+                               || ( $contextTitle->getNamespace() == NS_MEDIAWIKI
                                        && $contextTitle->getDefaultMessageText() !== false
                                )
                                ? 'editing'
@@ -2468,6 +2490,8 @@ ERROR;
                $displayTitle = isset( $this->mParserOutput ) ? $this->mParserOutput->getDisplayTitle() : false;
                if ( $displayTitle === false ) {
                        $displayTitle = $contextTitle->getPrefixedText();
+               } else {
+                       $out->setDisplayTitle( $displayTitle );
                }
                $out->setPageTitle( $this->context->msg( $msg, $displayTitle ) );
 
@@ -2499,7 +2523,7 @@ ERROR;
                $out = $this->context->getOutput();
                $namespace = $this->mTitle->getNamespace();
 
-               if ( $namespace === NS_MEDIAWIKI ) {
+               if ( $namespace == NS_MEDIAWIKI ) {
                        # Show a warning if editing an interface message
                        $out->wrapWikiMsg( "<div class='mw-editinginterface'>\n$1\n</div>", 'editinginterface' );
                        # If this is a default message (but not css, json, or js),
@@ -2515,7 +2539,7 @@ ERROR;
                                                'translateinterface' );
                                }
                        }
-               } elseif ( $namespace === NS_FILE ) {
+               } elseif ( $namespace == NS_FILE ) {
                        # Show a hint to shared repo
                        $file = wfFindFile( $this->mTitle );
                        if ( $file && !$file->isLocal() ) {
@@ -2537,7 +2561,7 @@ ERROR;
 
                # Show a warning message when someone creates/edits a user (talk) page but the user does not exist
                # Show log extract when the user is currently blocked
-               if ( $namespace === NS_USER || $namespace === NS_USER_TALK ) {
+               if ( $namespace == NS_USER || $namespace == NS_USER_TALK ) {
                        $username = explode( '/', $this->mTitle->getText(), 2 )[0];
                        $user = User::newFromName( $username, false /* allow IP users */ );
                        $ip = User::isIP( $username );
@@ -2545,7 +2569,7 @@ ERROR;
                        if ( !( $user && $user->isLoggedIn() ) && !$ip ) { # User does not exist
                                $out->wrapWikiMsg( "<div class=\"mw-userpage-userdoesnotexist error\">\n$1\n</div>",
                                        [ 'userpage-userdoesnotexist', wfEscapeWikiText( $username ) ] );
-                       } elseif ( !is_null( $block ) && $block->getType() !== Block::TYPE_AUTO ) {
+                       } elseif ( !is_null( $block ) && $block->getType() != Block::TYPE_AUTO ) {
                                # Show log extract if the user is currently blocked
                                LogEventsList::showLogExtract(
                                        $out,
@@ -2699,7 +2723,7 @@ ERROR;
                # we parse this near the beginning so that setHeaders can do the title
                # setting work instead of leaving it in getPreviewText
                $previewOutput = '';
-               if ( $this->formtype === 'preview' ) {
+               if ( $this->formtype == 'preview' ) {
                        $previewOutput = $this->getPreviewText();
                }
 
@@ -2715,7 +2739,7 @@ ERROR;
                $this->addEditNotices();
 
                if ( !$this->isConflict &&
-                       $this->section !== '' &&
+                       $this->section != '' &&
                        !$this->isSectionEditSupported() ) {
                        // We use $this->section to much before this and getVal('wgSection') directly in other places
                        // at this point we can't reset $this->section to '' to fallback to non-section editing.
@@ -2737,7 +2761,7 @@ ERROR;
 
                $showToolbar = true;
                if ( $this->wasDeletedSinceLastEdit() ) {
-                       if ( $this->formtype === 'save' ) {
+                       if ( $this->formtype == 'save' ) {
                                // Hide the toolbar and edit area, user can click preview to get it back
                                // Add an confirmation checkbox and explanation.
                                $showToolbar = false;
@@ -2796,7 +2820,7 @@ ERROR;
                // Put these up at the top to ensure they aren't lost on early form submission
                $this->showFormBeforeText();
 
-               if ( $this->wasDeletedSinceLastEdit() && 'save' === $this->formtype ) {
+               if ( $this->wasDeletedSinceLastEdit() && 'save' == $this->formtype ) {
                        $username = $this->lastDelete->user_name;
                        $comment = CommentStore::getStore()
                                ->getComment( 'log_comment', $this->lastDelete )->text;
@@ -2828,7 +2852,7 @@ ERROR;
                # ####
                # For a bit more sophisticated detection of blank summaries, hash the
                # automatic one and pass that in the hidden field wpAutoSummary.
-               if ( $this->missingSummary || ( $this->section === 'new' && $this->nosummary ) ) {
+               if ( $this->missingSummary || ( $this->section == 'new' && $this->nosummary ) ) {
                        $out->addHTML( Html::hidden( 'wpIgnoreBlankSummary', true ) );
                }
 
@@ -2847,7 +2871,7 @@ ERROR;
                        $this->autoSumm = md5( '' );
                }
 
-               $autosumm = $this->autoSumm ?: md5( $this->summary );
+               $autosumm = $this->autoSumm !== '' ? $this->autoSumm : md5( $this->summary );
                $out->addHTML( Html::hidden( 'wpAutoSummary', $autosumm ) );
 
                $out->addHTML( Html::hidden( 'oldid', $this->oldid ) );
@@ -2858,7 +2882,7 @@ ERROR;
 
                $out->enableOOUI();
 
-               if ( $this->section === 'new' ) {
+               if ( $this->section == 'new' ) {
                        $this->showSummaryInput( true, $this->summary );
                        $out->addHTML( $this->getSummaryPreview( true, $this->summary ) );
                }
@@ -2980,7 +3004,7 @@ ERROR;
                $type = false;
                if ( $this->preview ) {
                        $type = 'preview';
-               } elseif ( $this->section !== '' ) {
+               } elseif ( $this->section != '' ) {
                        $type = 'section';
                }
 
@@ -3012,7 +3036,7 @@ ERROR;
                        $this->addExplainConflictHeader( $out );
                        $this->editRevId = $this->page->getLatest();
                } else {
-                       if ( $this->section !== '' && $this->section !== 'new' ) {
+                       if ( $this->section != '' && $this->section != 'new' ) {
                                if ( !$this->summary && !$this->preview && !$this->diff ) {
                                        $sectionTitle = self::extractSectionTitle( $this->textbox1 ); // FIXME: use Content object
                                        if ( $sectionTitle !== false ) {
@@ -3027,14 +3051,14 @@ ERROR;
                                $out->wrapWikiMsg( "<div id='mw-missingcommenttext'>\n$1\n</div>", 'missingcommenttext' );
                        }
 
-                       if ( $this->missingSummary && $this->section !== 'new' ) {
+                       if ( $this->missingSummary && $this->section != 'new' ) {
                                $out->wrapWikiMsg(
                                        "<div id='mw-missingsummary'>\n$1\n</div>",
                                        [ 'missingsummary', $buttonLabel ]
                                );
                        }
 
-                       if ( $this->missingSummary && $this->section === 'new' ) {
+                       if ( $this->missingSummary && $this->section == 'new' ) {
                                $out->wrapWikiMsg(
                                        "<div id='mw-missingcommentheader'>\n$1\n</div>",
                                        [ 'missingcommentheader', $buttonLabel ]
@@ -3059,7 +3083,7 @@ ERROR;
                                $out->addWikiText( $this->hookError );
                        }
 
-                       if ( $this->section !== 'new' ) {
+                       if ( $this->section != 'new' ) {
                                $revision = $this->mArticle->getRevisionFetched();
                                if ( $revision ) {
                                        // Let sysop know that this will make private content public if saved
@@ -3096,7 +3120,7 @@ ERROR;
                                [ 'readonlywarning', wfReadOnlyReason() ]
                        );
                } elseif ( $user->isAnon() ) {
-                       if ( $this->formtype !== 'preview' ) {
+                       if ( $this->formtype != 'preview' ) {
                                $out->wrapWikiMsg(
                                        "<div id='mw-anon-edit-warning' class='warningbox'>\n$1\n</div>",
                                        [ 'anoneditwarning',
@@ -3167,7 +3191,7 @@ ERROR;
        }
 
        /**
-        * Helper function for summary input functions, which returns the neccessary
+        * Helper function for summary input functions, which returns the necessary
         * attributes for the input.
         *
         * @param array|null $inputAttrs Array of attrs to use on the input
@@ -3331,7 +3355,7 @@ ERROR;
         * @param string|null $textoverride Optional text to override $this->textarea1 with
         */
        protected function showTextbox1( $customAttribs = null, $textoverride = null ) {
-               if ( $this->wasDeletedSinceLastEdit() && $this->formtype === 'save' ) {
+               if ( $this->wasDeletedSinceLastEdit() && $this->formtype == 'save' ) {
                        $attribs = [ 'style' => 'display:none;' ];
                } else {
                        $builder = new TextboxBuilder();
@@ -3384,14 +3408,14 @@ ERROR;
 
                $attribs = [ 'id' => 'wikiPreview', 'class' => implode( ' ', $classes ) ];
 
-               if ( $this->formtype !== 'preview' ) {
+               if ( $this->formtype != 'preview' ) {
                        $attribs['style'] = 'display: none;';
                }
 
                $out = $this->context->getOutput();
                $out->addHTML( Xml::openElement( 'div', $attribs ) );
 
-               if ( $this->formtype === 'preview' ) {
+               if ( $this->formtype == 'preview' ) {
                        $this->showPreview( $previewOutput );
                } else {
                        // Empty content container for LivePreview
@@ -3403,7 +3427,7 @@ ERROR;
 
                $out->addHTML( '</div>' );
 
-               if ( $this->formtype === 'diff' ) {
+               if ( $this->formtype == 'diff' ) {
                        try {
                                $this->showDiff();
                        } catch ( MWContentSerializationException $ex ) {
@@ -3446,11 +3470,9 @@ ERROR;
         * save and then make a comparison.
         */
        public function showDiff() {
-               global $wgContLang;
-
                $oldtitlemsg = 'currentrev';
                # if message does not exist, show diff against the preloaded default
-               if ( $this->mTitle->getNamespace() === NS_MEDIAWIKI && !$this->mTitle->exists() ) {
+               if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI && !$this->mTitle->exists() ) {
                        $oldtext = $this->mTitle->getDefaultMessageText();
                        if ( $oldtext !== false ) {
                                $oldtitlemsg = 'defaultmessagetext';
@@ -3477,7 +3499,8 @@ ERROR;
                        Hooks::run( 'EditPageGetDiffContent', [ $this, &$newContent ] );
 
                        $user = $this->context->getUser();
-                       $popts = ParserOptions::newFromUserAndLang( $user, $wgContLang );
+                       $popts = ParserOptions::newFromUserAndLang( $user,
+                               MediaWikiServices::getInstance()->getContentLanguage() );
                        $newContent = $newContent->preSaveTransform( $this->mTitle, $user, $popts );
                }
 
@@ -3588,10 +3611,10 @@ ERROR;
         * Get the Limit report for page previews
         *
         * @since 1.22
-        * @param ParserOutput $output ParserOutput object from the parse
+        * @param ParserOutput|null $output ParserOutput object from the parse
         * @return string HTML
         */
-       public static function getPreviewLimitReport( $output ) {
+       public static function getPreviewLimitReport( ParserOutput $output = null ) {
                global $wgLang;
 
                if ( !$output || !$output->getLimitReportData() ) {
@@ -3641,7 +3664,7 @@ ERROR;
                $out = $this->context->getOutput();
                $out->addHTML( "<div class='editOptions'>\n" );
 
-               if ( $this->section !== 'new' ) {
+               if ( $this->section != 'new' ) {
                        $this->showSummaryInput( false, $this->summary );
                        $out->addHTML( $this->getSummaryPreview( false, $this->summary ) );
                }
@@ -3764,6 +3787,8 @@ ERROR;
        }
 
        /**
+        * Get the last log record of this page being deleted, if ever.  This is
+        * used to detect whether a delete occurred during editing.
         * @return bool|stdClass
         */
        protected function getLastDelete() {
@@ -3879,17 +3904,17 @@ ERROR;
                                        $level = false;
                                }
 
-                               if ( $content->getModel() === CONTENT_MODEL_CSS ) {
+                               if ( $content->getModel() == CONTENT_MODEL_CSS ) {
                                        $format = 'css';
                                        if ( $level === 'user' && !$config->get( 'AllowUserCss' ) ) {
                                                $format = false;
                                        }
-                               } elseif ( $content->getModel() === CONTENT_MODEL_JSON ) {
+                               } elseif ( $content->getModel() == CONTENT_MODEL_JSON ) {
                                        $format = 'json';
                                        if ( $level === 'user' /* No comparable 'AllowUserJson' */ ) {
                                                $format = false;
                                        }
-                               } elseif ( $content->getModel() === CONTENT_MODEL_JAVASCRIPT ) {
+                               } elseif ( $content->getModel() == CONTENT_MODEL_JAVASCRIPT ) {
                                        $format = 'js';
                                        if ( $level === 'user' && !$config->get( 'AllowUserJs' ) ) {
                                                $format = false;
@@ -3974,6 +3999,12 @@ ERROR;
                $parserOptions->setIsPreview( true );
                $parserOptions->setIsSectionPreview( !is_null( $this->section ) && $this->section !== '' );
                $parserOptions->enableLimitReport();
+
+               // XXX: we could call $parserOptions->setCurrentRevisionCallback here to force the
+               // current revision to be null during PST, until setupFakeRevision is called on
+               // the ParserOptions. Currently, we rely on Parser::getRevisionObject() to ignore
+               // existing revisions in preview mode.
+
                return $parserOptions;
        }
 
@@ -3989,9 +4020,14 @@ ERROR;
        protected function doPreviewParse( Content $content ) {
                $user = $this->context->getUser();
                $parserOptions = $this->getPreviewParserOptions();
+
+               // NOTE: preSaveTransform doesn't have a fake revision to operate on.
+               // Parser::getRevisionObject() will return null in preview mode,
+               // causing the context user to be used for {{subst:REVISIONUSER}}.
+               // XXX: Alternatively, we could also call setupFakeRevision() a second time:
+               // once before PST with $content, and then after PST with $pstContent.
                $pstContent = $content->preSaveTransform( $this->mTitle, $user, $parserOptions );
-               $scopedCallback = $parserOptions->setupFakeRevision(
-                       $this->mTitle, $pstContent, $user );
+               $scopedCallback = $parserOptions->setupFakeRevision( $this->mTitle, $pstContent, $user );
                $parserOutput = $pstContent->getParserOutput( $this->mTitle, null, $parserOptions );
                ScopedCallback::consume( $scopedCallback );
                return [
@@ -4006,7 +4042,7 @@ ERROR;
         * @return array
         */
        public function getTemplates() {
-               if ( $this->preview || $this->section !== '' ) {
+               if ( $this->preview || $this->section != '' ) {
                        $templates = [];
                        if ( !isset( $this->mParserOutput ) ) {
                                return $templates;
@@ -4030,8 +4066,7 @@ ERROR;
         * @return string
         */
        public static function getEditToolbar( $title = null ) {
-               global $wgContLang, $wgOut;
-               global $wgEnableUploads, $wgForeignFileRepos;
+               global $wgOut, $wgEnableUploads, $wgForeignFileRepos;
 
                $imagesAvailable = $wgEnableUploads || count( $wgForeignFileRepos );
                $showSignature = true;
@@ -4039,6 +4074,8 @@ ERROR;
                        $showSignature = MWNamespace::wantSignatures( $title->getNamespace() );
                }
 
+               $contLang = MediaWikiServices::getInstance()->getContentLanguage();
+
                /**
                 * $toolarray is an array of arrays each of which includes the
                 * opening tag, the closing tag, optionally a sample text that is
@@ -4086,14 +4123,14 @@ ERROR;
                        ],
                        $imagesAvailable ? [
                                'id'     => 'mw-editbutton-image',
-                               'open'   => '[[' . $wgContLang->getNsText( NS_FILE ) . ':',
+                               'open'   => '[[' . $contLang->getNsText( NS_FILE ) . ':',
                                'close'  => ']]',
                                'sample' => wfMessage( 'image_sample' )->text(),
                                'tip'    => wfMessage( 'image_tip' )->text(),
                        ] : false,
                        $imagesAvailable ? [
                                'id'     => 'mw-editbutton-media',
-                               'open'   => '[[' . $wgContLang->getNsText( NS_MEDIA ) . ':',
+                               'open'   => '[[' . $contLang->getNsText( NS_MEDIA ) . ':',
                                'close'  => ']]',
                                'sample' => wfMessage( 'media_sample' )->text(),
                                'tip'    => wfMessage( 'media_tip' )->text(),
@@ -4121,7 +4158,7 @@ ERROR;
                        ]
                ];
 
-               $script = 'mw.loader.using("mediawiki.toolbar", function () {';
+               $script = '';
                foreach ( $toolarray as $tool ) {
                        if ( !$tool ) {
                                continue;
@@ -4148,15 +4185,16 @@ ERROR;
                        );
                }
 
-               $script .= '});';
-
                $toolbar = '<div id="toolbar"></div>';
 
                if ( Hooks::run( 'EditPageBeforeEditToolbar', [ &$toolbar ] ) ) {
                        // Only add the old toolbar cruft to the page payload if the toolbar has not
                        // been over-written by a hook caller
                        $nonce = $wgOut->getCSPNonce();
-                       $wgOut->addScript( ResourceLoader::makeInlineScript( $script, $nonce ) );
+                       $wgOut->addScript( Html::inlineScript(
+                               ResourceLoader::makeInlineCodeWithModule( 'mediawiki.toolbar', $script ),
+                               $nonce
+                       ) );
                };
 
                return $toolbar;