*/
const AS_SELF_REDIRECT = 236;
+ /**
+ * Status: an error relating to change tagging. Look at the message key for
+ * more details
+ */
+ const AS_CHANGE_TAG_ERROR = 237;
+
/**
* Status: can't parse content
*/
const AS_PARSE_ERROR = 240;
+ /**
+ * Status: when changing the content model is disallowed due to
+ * $wgContentHandlerUseDB being false
+ */
+ const AS_CANNOT_USE_CUSTOM_MODEL = 241;
+
/**
* HTML id and name for the beginning of the edit form.
*/
/** @var null|string */
public $contentFormat = null;
+ /** @var null|array */
+ public $changeTags = null;
+
# Placeholders for text injection by hooks (must be HTML)
# extensions should take care to _append_ to the present value
public $suppressIntro = false;
- /** @var bool Set to true to allow editing of non-text content types. */
- public $allowNonTextContent = false;
-
/** @var bool */
protected $edit;
+ /**
+ * @var bool Set in ApiEditPage, based on ContentHandler::allowsDirectApiEditing
+ */
+ private $enableApiEditOverride = false;
+
/**
* @param Article $article
*/
* @throws MWException If $modelId has no known handler
*/
public function isSupportedContentModel( $modelId ) {
- return $this->allowNonTextContent ||
- ContentHandler::getForModelID( $modelId ) instanceof TextContentHandler;
+ return $this->enableApiEditOverride === true ||
+ ContentHandler::getForModelID( $modelId )->supportsDirectEditing();
+ }
+
+ /**
+ * Allow editing of content that supports API direct editing, but not general
+ * direct editing. Set to false by default.
+ *
+ * @param bool $enableOverride
+ */
+ public function setApiEditOverride( $enableOverride ) {
+ $this->enableApiEditOverride = $enableOverride;
}
function submit() {
$this->allowBlankArticle = $request->getBool( 'wpIgnoreBlankArticle' );
$this->allowSelfRedirect = $request->getBool( 'wpIgnoreSelfRedirect' );
+
+ $changeTags = $request->getVal( 'wpChangeTags' );
+ if ( is_null( $changeTags ) || $changeTags === '' ) {
+ $this->changeTags = array();
+ } else {
+ $this->changeTags = array_filter( array_map( 'trim', explode( ',',
+ $changeTags ) ) );
+ }
} else {
# Not a posted form? Start with nothing.
wfDebug( __METHOD__ . ": Not a posted form.\n" );
$undo = $wgRequest->getInt( 'undo' );
if ( $undo > 0 && $undoafter > 0 ) {
-
$undorev = Revision::newFromId( $undo );
$oldrev = Revision::newFromId( $undoafter );
# Otherwise, $content will be left as-is.
if ( !is_null( $undorev ) && !is_null( $oldrev ) &&
!$undorev->isDeleted( Revision::DELETED_TEXT ) &&
- !$oldrev->isDeleted( Revision::DELETED_TEXT ) ) {
-
+ !$oldrev->isDeleted( Revision::DELETED_TEXT )
+ ) {
$content = $this->mArticle->getUndoContent( $undorev, $oldrev );
if ( $content === false ) {
if ( !$converted ) {
//TODO: somehow show a warning to the user!
- wfDebug( "Attempt to preload incompatible content: "
- . "can't convert " . $content->getModel()
- . " to " . $handler->getModelID() );
+ wfDebug( "Attempt to preload incompatible content: " .
+ "can't convert " . $content->getModel() .
+ " to " . $handler->getModelID() );
return $handler->makeEmptyContent();
}
case self::AS_HOOK_ERROR:
return false;
+ case self::AS_CANNOT_USE_CUSTOM_MODEL:
case self::AS_PARSE_ERROR:
$wgOut->addWikiText( '<div class="error">' . $status->getWikiText() . '</div>' );
return true;
*/
function internalAttemptSave( &$result, $bot = false ) {
global $wgUser, $wgRequest, $wgParser, $wgMaxArticleSize;
+ global $wgContentHandlerUseDB;
$status = Status::newGood();
}
}
- if ( $this->contentModel !== $this->mTitle->getContentModel()
- && !$wgUser->isAllowed( 'editcontentmodel' )
- ) {
- $status->setResult( false, self::AS_NO_CHANGE_CONTENT_MODEL );
- return $status;
+ if ( $this->contentModel !== $this->mTitle->getContentModel() ) {
+ if ( !$wgContentHandlerUseDB ) {
+ $status->fatal( 'editpage-cannot-use-custom-model' );
+ $status->value = self::AS_CANNOT_USE_CUSTOM_MODEL;
+ return $status;
+ } elseif ( !$wgUser->isAllowed( 'editcontentmodel' ) ) {
+ $status->setResult( false, self::AS_NO_CHANGE_CONTENT_MODEL );
+ return $status;
+ }
+ }
+
+ if ( $this->changeTags ) {
+ $changeTagsStatus = ChangeTags::canAddTagsAccompanyingChange(
+ $this->changeTags, $wgUser );
+ if ( !$changeTagsStatus->isOK() ) {
+ $changeTagsStatus->value = self::AS_CHANGE_TAG_ERROR;
+ return $changeTagsStatus;
+ }
}
if ( wfReadOnly() ) {
$wgUser->pingLimiter( 'linkpurge' );
}
$result['redirect'] = $content->isRedirect();
+
$this->updateWatchlist();
+
+ if ( $this->changeTags && isset( $doEditStatus->value['revision'] ) ) {
+ // If a revision was created, apply any change tags that were requested
+ $addTags = $this->changeTags;
+ $revId = $doEditStatus->value['revision']->getId();
+ // Defer this both for performance and so that addTags() sees the rc_id
+ // since the recentchange entry addition is deferred first (bug T100248)
+ DeferredUpdates::addCallableUpdate( function() use ( $addTags, $revId ) {
+ ChangeTags::addTags( $addTags, null, $revId );
+ } );
+ }
+
return $status;
}
global $wgOut, $wgUser, $wgRawHtml, $wgLang;
global $wgAllowUserCss, $wgAllowUserJs;
+ $stats = $wgOut->getContext()->getStats();
+
if ( $wgRawHtml && !$this->mTokenOk ) {
// Could be an offsite preview attempt. This is very unsafe if
// HTML is enabled, as it could be an attack.
$parsedNote = $wgOut->parse( "<div class='previewnote'>" .
wfMessage( 'session_fail_preview_html' )->text() . "</div>", true, /* interface */true );
}
+ $stats->increment( 'edit.failures.session_loss' );
return $parsedNote;
}
if ( $this->mTriedSave && !$this->mTokenOk ) {
if ( $this->mTokenOkExceptSuffix ) {
$note = wfMessage( 'token_suffix_mismatch' )->plain();
+ $stats->increment( 'edit.failures.bad_token' );
} else {
$note = wfMessage( 'session_fail_preview' )->plain();
+ $stats->increment( 'edit.failures.session_loss' );
}
} elseif ( $this->incompleteForm ) {
$note = wfMessage( 'edit_form_incomplete' )->plain();
+ if ( $this->mTriedSave ) {
+ $stats->increment( 'edit.failures.incomplete_form' );
+ }
} else {
$note = wfMessage( 'previewnote' )->plain() . ' ' . $continueEditing;
}
}
$script .= '});';
- $wgOut->addScript( Html::inlineScript( ResourceLoader::makeLoaderConditionalScript( $script ) ) );
+ $wgOut->addScript( ResourceLoader::makeInlineScript( $script ) );
$toolbar = '<div id="toolbar"></div>';