/** @var Article */
public $mArticle;
+ /** @var WikiPage */
+ private $page;
/** @var Title */
public $mTitle;
*/
public function __construct( Article $article ) {
$this->mArticle = $article;
+ $this->page = $article->getPage(); // model object
$this->mTitle = $article->getTitle();
$this->contentModel = $this->mTitle->getContentModel();
return;
}
+ $revision = $this->mArticle->getRevisionFetched();
+ // Disallow editing revisions with content models different from the current one
+ if ( $revision && $revision->getContentModel() !== $this->contentModel ) {
+ $this->displayViewSourcePage(
+ $this->getContentObject(),
+ wfMessage(
+ 'contentmodelediterror',
+ $revision->getContentModel(),
+ $this->contentModel
+ )->plain()
+ );
+ return;
+ }
+
$this->isConflict = false;
// css / js subpages of user pages get a special treatment
$this->isCssJsSubpage = $this->mTitle->isCssJsSubpage();
throw new PermissionsError( $action, $permErrors );
}
+ $this->displayViewSourcePage(
+ $content,
+ $wgOut->formatPermissionsErrorMessage( $permErrors, 'edit' )
+ );
+ }
+
+ /**
+ * Display a read-only View Source page
+ * @param Content $content content object
+ * @param string $errorMessage additional wikitext error message to display
+ */
+ protected function displayViewSourcePage( Content $content, $errorMessage = '' ) {
+ global $wgOut;
+
Hooks::run( 'EditPage::showReadOnlyForm:initial', array( $this, &$wgOut ) );
$wgOut->setRobotPolicy( 'noindex,nofollow' );
$wgOut->addHTML( $this->editFormPageTop );
$wgOut->addHTML( $this->editFormTextTop );
- $wgOut->addWikiText( $wgOut->formatPermissionsErrorMessage( $permErrors, 'edit' ) );
- $wgOut->addHTML( "<hr />\n" );
+ if ( $errorMessage !== '' ) {
+ $wgOut->addWikiText( $errorMessage );
+ $wgOut->addHTML( "<hr />\n" );
+ }
# If the user made changes, preserve them when showing the markup
# (This happens when a user is blocked during edit, for instance)
$text = $this->textbox1;
$wgOut->addWikiMsg( 'viewyourtext' );
} else {
- $text = $this->toEditText( $content );
+ try {
+ $text = $this->toEditText( $content );
+ } catch ( MWException $e ) {
+ # Serialize using the default format if the content model is not supported
+ # (e.g. for an old revision with a different model)
+ $text = $content->serialize();
+ }
$wgOut->addWikiMsg( 'viewsourcetext' );
}
* for saving, preview parsing and so on...
*
* @param WebRequest $request
+ * @return string|null
*/
protected function importContentFormData( &$request ) {
return; // Don't do anything, EditPage already extracted wpTextbox1
*/
function initialiseForm() {
global $wgUser;
- $this->edittime = $this->mArticle->getTimestamp();
+ $this->edittime = $this->page->getTimestamp();
$content = $this->getContentObject( false ); # TODO: track content object?!
if ( $content === false ) {
!$undorev->isDeleted( Revision::DELETED_TEXT ) &&
!$oldrev->isDeleted( Revision::DELETED_TEXT )
) {
- $content = $this->mArticle->getUndoContent( $undorev, $oldrev );
+ $content = $this->page->getUndoContent( $undorev, $oldrev );
if ( $content === false ) {
# Warn the user that something went wrong
$undoMsg = 'failure';
} else {
- $oldContent = $this->mArticle->getPage()->getContent( Revision::RAW );
+ $oldContent = $this->page->getContent( Revision::RAW );
$popts = ParserOptions::newFromUserAndLang( $wgUser, $wgContLang );
$newContent = $content->preSaveTransform( $this->mTitle, $wgUser, $popts );
return $content;
}
+ /**
+ * Get the edit's parent revision ID
+ *
+ * The "parent" revision is the ancestor that should be recorded in this
+ * page's revision history. It is either the revision ID of the in-memory
+ * article content, or in the case of a 3-way merge in order to rebase
+ * across a recoverable edit conflict, the ID of the newer revision to
+ * which we have rebased this page.
+ *
+ * @since 1.27
+ * @return int Revision ID
+ */
+ public function getParentRevId() {
+ if ( $this->parentRevId ) {
+ return $this->parentRevId;
+ } else {
+ return $this->mArticle->getRevIdFetched();
+ }
+ }
+
/**
* Get the current content of the page. This is basically similar to
* WikiPage::getContent( Revision::RAW ) except that when the page doesn't exist an empty
* @return Content
*/
protected function getCurrentContent() {
- $rev = $this->mArticle->getRevision();
+ $rev = $this->page->getRevision();
$content = $rev ? $rev->getContent( Revision::RAW ) : null;
if ( $content === false || $content === null ) {
* @param int $statusValue The status value (to check for new article status)
*/
protected function setPostEditCookie( $statusValue ) {
- $revisionId = $this->mArticle->getLatest();
+ $revisionId = $this->page->getLatest();
$postEditKey = self::POST_EDIT_COOKIE_KEY_PREFIX . $revisionId;
$val = 'saved';
# Load the page data from the master. If anything changes in the meantime,
# we detect it by using page_latest like a token in a 1 try compare-and-swap.
- $this->mArticle->loadPageData( 'fromdbmaster' );
- $new = !$this->mArticle->exists();
+ $this->page->loadPageData( 'fromdbmaster' );
+ $new = !$this->page->exists();
if ( $new ) {
// Late check for create permission, just in case *PARANOIA*
# Article exists. Check for edit conflict.
- $this->mArticle->clear(); # Force reload of dates, etc.
- $timestamp = $this->mArticle->getTimestamp();
+ $this->page->clear(); # Force reload of dates, etc.
+ $timestamp = $this->page->getTimestamp();
wfDebug( "timestamp: {$timestamp}, edittime: {$this->edittime}\n" );
if ( $timestamp != $this->edittime ) {
$this->isConflict = true;
if ( $this->section == 'new' ) {
- if ( $this->mArticle->getUserText() == $wgUser->getName() &&
- $this->mArticle->getComment() == $this->newSectionSummary()
+ if ( $this->page->getUserText() == $wgUser->getName() &&
+ $this->page->getComment() == $this->newSectionSummary()
) {
// Probably a duplicate submission of a new comment.
- // This can happen when squid resends a request after
+ // This can happen when CDN resends a request after
// a timeout but the first one actually went through.
wfDebug( __METHOD__
. ": duplicate new section submission; trigger edit conflict!\n" );
. ": conflict! getting section '{$this->section}' for time '{$this->edittime}'"
. " (article time '{$timestamp}')\n" );
- $content = $this->mArticle->replaceSectionContent(
+ $content = $this->page->replaceSectionContent(
$this->section,
$textbox_content,
$sectionTitle,
);
} else {
wfDebug( __METHOD__ . ": getting section '{$this->section}'\n" );
- $content = $this->mArticle->replaceSectionContent(
+ $content = $this->page->replaceSectionContent(
$this->section,
$textbox_content,
$sectionTitle
( ( $this->minoredit && !$this->isNew ) ? EDIT_MINOR : 0 ) |
( $bot ? EDIT_FORCE_BOT : 0 );
- $doEditStatus = $this->mArticle->doEditContent(
+ $doEditStatus = $this->page->doEditContent(
$content,
$this->summary,
$flags,
}
/**
- * @param Title $title
+ * @param User $user
* @param string $oldModel
* @param string $newModel
* @param string $reason
$log->publish( $logid );
}
-
/**
* Register the change of watch status
*/
protected function updateWatchlist() {
global $wgUser;
- if ( $wgUser->isLoggedIn()
- && $this->watchthis != $wgUser->isWatched( $this->mTitle, WatchedItem::IGNORE_USER_RIGHTS )
- ) {
- $fname = __METHOD__;
- $title = $this->mTitle;
- $watch = $this->watchthis;
-
- // Do this in its own transaction to reduce contention...
- $dbw = wfGetDB( DB_MASTER );
- $dbw->onTransactionIdle( function () use ( $dbw, $title, $watch, $wgUser, $fname ) {
- WatchAction::doWatchOrUnwatch( $watch, $title, $wgUser );
- } );
+ if ( !$wgUser->isLoggedIn() ) {
+ return;
}
+
+ $user = $wgUser;
+ $title = $this->mTitle;
+ $watch = $this->watchthis;
+ // Do this in its own transaction to reduce contention...
+ DeferredUpdates::addCallableUpdate( function () use ( $user, $title, $watch ) {
+ if ( $watch == $user->isWatched( $title, WatchedItem::IGNORE_USER_RIGHTS ) ) {
+ return; // nothing to change
+ }
+ WatchAction::doWatchOrUnwatch( $watch, $title, $user );
+ } );
}
/**
if ( $result ) {
$editContent = $result;
+ // Update parentRevId to what we just merged.
+ $this->parentRevId = $currentRevision->getId();
return true;
}
}
/**
- * @return Revision
+ * @note: 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.
+ * @return Revision Current version when the edit was started
*/
function getBaseRevision() {
if ( !$this->mBaseRevision ) {
}
# Use the title defined by DISPLAYTITLE magic word when present
+ # NOTE: getDisplayTitle() returns HTML while getPrefixedText() returns plain text.
+ # setPageTitle() treats the input as wikitext, which should be safe in either case.
$displayTitle = isset( $this->mParserOutput ) ? $this->mParserOutput->getDisplayTitle() : false;
if ( $displayTitle === false ) {
$displayTitle = $contextTitle->getPrefixedText();
$wgOut->addHTML( Html::hidden( 'wpAutoSummary', $autosumm ) );
$wgOut->addHTML( Html::hidden( 'oldid', $this->oldid ) );
- $wgOut->addHTML( Html::hidden( 'parentRevId',
- $this->parentRevId ?: $this->mArticle->getRevIdFetched() ) );
+ $wgOut->addHTML( Html::hidden( 'parentRevId', $this->getParentRevId() ) );
$wgOut->addHTML( Html::hidden( 'format', $this->contentFormat ) );
$wgOut->addHTML( Html::hidden( 'model', $this->contentModel ) );
Linker::formatTemplates( $this->getTemplates(), $this->preview, $this->section != '' ) ) );
$wgOut->addHTML( Html::rawElement( 'div', array( 'class' => 'hiddencats' ),
- Linker::formatHiddenCategories( $this->mArticle->getHiddenCategories() ) ) );
+ Linker::formatHiddenCategories( $this->page->getHiddenCategories() ) ) );
$wgOut->addHTML( Html::rawElement( 'div', array( 'class' => 'limitreport' ),
self::getPreviewLimitReport( $this->mParserOutput ) ) );
if ( $this->isConflict ) {
$wgOut->wrapWikiMsg( "<div class='mw-explainconflict'>\n$1\n</div>", 'explainconflict' );
- $this->edittime = $this->mArticle->getTimestamp();
+ $this->edittime = $this->page->getTimestamp();
} else {
if ( $this->section != '' && !$this->isSectionEditSupported() ) {
// We use $this->section to much before this and getVal('wgSection') directly in other places
}
if ( $this->mTitle->isCascadeProtected() ) {
# Is this page under cascading protection from some source pages?
+ /** @var Title[] $cascadeSources */
list( $cascadeSources, /* $restrictions */ ) = $this->mTitle->getCascadeProtectionSources();
$notice = "<div class='mw-cascadeprotectedwarning'>\n$1\n";
$cascadeSourcesCount = count( $cascadeSources );
$textboxContent = $this->toEditContent( $this->textbox1 );
- $newContent = $this->mArticle->replaceSectionContent(
+ $newContent = $this->page->replaceSectionContent(
$this->section, $textboxContent,
$this->summary, $this->edittime );
$note = wfMessage( 'previewnote' )->plain() . ' ' . $continueEditing;
}
- $parserOptions = $this->mArticle->makeParserOptions( $this->mArticle->getContext() );
+ $parserOptions = $this->page->makeParserOptions( $this->mArticle->getContext() );
$parserOptions->setIsPreview( true );
$parserOptions->setIsSectionPreview( !is_null( $this->section ) && $this->section !== '' );
if ( count( $parserOutput->getWarnings() ) ) {
$note .= "\n\n" . implode( "\n\n", $parserOutput->getWarnings() );
}
+
+ ScopedCallback::consume( $scopedCallback );
} catch ( MWContentSerializationException $ex ) {
$m = wfMessage(
'content-failed-to-parse',