Merge "Acquire a temporary user username before previewing"

This commit is contained in:
jenkins-bot 2023-08-30 13:20:46 +00:00 committed by Gerrit Code Review
commit 4e9baea69a
5 changed files with 148 additions and 71 deletions

View file

@ -194,6 +194,7 @@
"services": [
"RevisionLookup",
"TempUserCreator",
"UserFactory",
"UserOptionsLookup",
"WatchlistManager",
"ContentTransformer",

View file

@ -35,10 +35,12 @@ use MediaWiki\Revision\RevisionLookup;
use MediaWiki\SpecialPage\SpecialPageFactory;
use MediaWiki\Title\Title;
use MediaWiki\User\TempUser\TempUserCreator;
use MediaWiki\User\UserFactory;
use MediaWiki\User\UserOptionsLookup;
use MediaWiki\Watchlist\WatchlistManager;
use MessageLocalizer;
use RequestContext;
use User;
use Wikimedia\ParamValidator\ParamValidator;
use WikitextContent;
@ -48,6 +50,7 @@ class ApiVisualEditor extends ApiBase {
private RevisionLookup $revisionLookup;
private TempUserCreator $tempUserCreator;
private UserFactory $userFactory;
private UserOptionsLookup $userOptionsLookup;
private WatchlistManager $watchlistManager;
private ContentTransformer $contentTransformer;
@ -62,6 +65,7 @@ class ApiVisualEditor extends ApiBase {
string $name,
RevisionLookup $revisionLookup,
TempUserCreator $tempUserCreator,
UserFactory $userFactory,
UserOptionsLookup $userOptionsLookup,
WatchlistManager $watchlistManager,
ContentTransformer $contentTransformer,
@ -77,6 +81,7 @@ class ApiVisualEditor extends ApiBase {
$this->setStats( $statsdDataFactory );
$this->revisionLookup = $revisionLookup;
$this->tempUserCreator = $tempUserCreator;
$this->userFactory = $userFactory;
$this->userOptionsLookup = $userOptionsLookup;
$this->watchlistManager = $watchlistManager;
$this->contentTransformer = $contentTransformer;
@ -96,6 +101,20 @@ class ApiVisualEditor extends ApiBase {
);
}
/**
* @see ApiParse::getUserForPreview
* @return User
*/
private function getUserForPreview() {
$user = $this->getUser();
if ( $this->tempUserCreator->shouldAutoCreate( $user, 'edit' ) ) {
return $this->userFactory->newUnsavedTempUser(
$this->tempUserCreator->getStashedName( $this->getRequest()->getSession() )
);
}
return $user;
}
/**
* Run wikitext through the parser's Pre-Save-Transform
*
@ -108,7 +127,7 @@ class ApiVisualEditor extends ApiBase {
return $this->contentTransformer->preSaveTransform(
$content,
$title,
$this->getUser(),
$this->getUserForPreview(),
$this->wikiPageFactory->newFromTitle( $title )->makeParserOptions( $this->getContext() )
)
->serialize( 'text/x-wiki' );

View file

@ -106,45 +106,62 @@ ve.ce.MWSignatureNode.prototype.onTeardown = function () {
* @inheritdoc ve.ce.GeneratedContentNode
*/
ve.ce.MWSignatureNode.prototype.generateContents = function () {
// Parsoid doesn't support pre-save transforms. PHP parser doesn't support Parsoid's
// meta attributes (that may or may not be required).
// We could try hacking up one (or even both) of these, but just calling the two parsers
// in order seems slightly saner.
// We must have only one top-level node, this is the easiest way.
var wikitext = '<span>~~~~</span>';
var doc = this.getModel().getDocument();
var abortable, aborted;
var abortedPromise = ve.createDeferred().reject( 'http',
{ textStatus: 'abort', exception: 'abort' } ).promise();
var deferred = ve.createDeferred();
var xhr = ve.init.target.getContentApi( doc ).post( {
action: 'parse',
text: wikitext,
contentmodel: 'wikitext',
prop: 'text',
onlypst: true
} )
.done( function ( resp ) {
var wt = ve.getProp( resp, 'parse', 'text' );
if ( wt ) {
ve.init.target.parseWikitextFragment( wt, true, doc ).then( function ( response ) {
if ( ve.getProp( response, 'visualeditor', 'result' ) !== 'success' ) {
deferred.reject();
return;
}
function abort() {
aborted = true;
if ( abortable && abortable.abort ) {
abortable.abort();
}
}
// Simplified case of template rendering, don't need to worry about filtering etc
deferred.resolve( $( response.visualeditor.content ).contents().toArray() );
} );
} else {
deferred.reject();
// Acquire a temporary user username before previewing, so that signatures
// display the temp user instead of IP user. (T331397)
return mw.user.acquireTempUserName()
.then( function () {
if ( aborted ) {
return abortedPromise;
}
} )
.fail( function () {
deferred.reject();
} );
return deferred.promise( { abort: xhr.abort } );
// We must have only one top-level node, this is the easiest way.
var wikitext = '<span>~~~~</span>';
// Parsoid doesn't support pre-save transforms. PHP parser doesn't support Parsoid's
// meta attributes (that may or may not be required).
// We could try hacking up one (or even both) of these, but just calling the two parsers
// in order seems slightly saner.
return ( abortable = ve.init.target.getContentApi( doc ).post( {
action: 'parse',
text: wikitext,
contentmodel: 'wikitext',
prop: 'text',
onlypst: true
} ) );
} )
.then( function ( pstResponse ) {
if ( aborted ) {
return abortedPromise;
}
var wikitext = ve.getProp( pstResponse, 'parse', 'text' );
if ( !wikitext ) {
return ve.createDeferred().reject();
}
return ( abortable = ve.init.target.parseWikitextFragment( wikitext, true, doc ) );
} )
.then( function ( parseResponse ) {
if ( aborted ) {
return abortedPromise;
}
if ( ve.getProp( parseResponse, 'visualeditor', 'result' ) !== 'success' ) {
return ve.createDeferred().reject();
}
// Simplified case of template rendering, don't need to worry about filtering etc
return $( parseResponse.visualeditor.content ).contents().toArray();
} )
.promise( { abort: abort } );
};
/* Registration */

View file

@ -915,14 +915,18 @@ ve.init.mw.ArticleTarget.prototype.onSaveDialogReview = function () {
if ( !this.saveDialog.hasDiff ) {
this.emit( 'saveReview' );
this.saveDialog.pushPending();
if ( this.pageExists ) {
// Has no callback, handled via target.showChangesDiff
this.showChanges( this.getDocToSave() );
} else {
this.serialize( this.getDocToSave() ).then( function ( data ) {
target.onSaveDialogReviewComplete( data.content );
} );
}
// Acquire a temporary user username before diffing, so that signatures and
// user-related magic words display the temp user instead of IP user in the diff. (T331397)
mw.user.acquireTempUserName().then( function () {
if ( target.pageExists ) {
// Has no callback, handled via target.showChangesDiff
target.showChanges( target.getDocToSave() );
} else {
target.serialize( target.getDocToSave() ).then( function ( data ) {
target.onSaveDialogReviewComplete( data.content );
} );
}
} );
} else {
this.saveDialog.swapPanel( 'review' );
}
@ -952,19 +956,23 @@ ve.init.mw.ArticleTarget.prototype.onSaveDialogPreview = function () {
params.variant = mw.config.get( 'wgUserVariant' );
}
api.post( ve.extendObject( params, {
action: 'parse',
title: this.getPageName(),
text: this.getDocToSave(),
pst: true,
preview: true,
sectionpreview: this.section !== null,
disableeditsection: true,
uselang: mw.config.get( 'wgUserLanguage' ),
useskin: mw.config.get( 'skin' ),
mobileformat: OO.ui.isMobile(),
prop: [ 'text', 'categorieshtml', 'displaytitle', 'subtitle', 'modules', 'jsconfigvars' ]
} ) ).then( function ( response ) {
// Acquire a temporary user username before previewing, so that signatures and
// user-related magic words display the temp user instead of IP user in the preview. (T331397)
mw.user.acquireTempUserName().then( function () {
return api.post( ve.extendObject( params, {
action: 'parse',
title: target.getPageName(),
text: target.getDocToSave(),
pst: true,
preview: true,
sectionpreview: target.section !== null,
disableeditsection: true,
uselang: mw.config.get( 'wgUserLanguage' ),
useskin: mw.config.get( 'skin' ),
mobileformat: OO.ui.isMobile(),
prop: [ 'text', 'categorieshtml', 'displaytitle', 'subtitle', 'modules', 'jsconfigvars' ]
} ) );
} ).then( function ( response ) {
target.saveDialog.showPreview( response );
}, function ( errorCode, details ) {
target.saveDialog.showPreview( target.extractErrorMessages( details ) );
@ -1035,14 +1043,18 @@ ve.init.mw.ArticleTarget.prototype.getVisualDiffGeneratorPromise = function () {
}
if ( mode === 'source' ) {
var newRevPromise = target.getContentApi().post( {
action: 'visualeditor',
paction: 'parse',
page: target.getPageName(),
wikitext: target.getSurface().getDom(),
section: target.section,
stash: 0,
pst: true
// Acquire a temporary user username before diffing, so that signatures and
// user-related magic words display the temp user instead of IP user in the diff. (T331397)
var newRevPromise = mw.user.acquireTempUserName().then( function () {
return target.getContentApi().post( {
action: 'visualeditor',
paction: 'parse',
page: target.getPageName(),
wikitext: target.getSurface().getDom(),
section: target.section,
stash: 0,
pst: true
} );
} ).then( function ( response ) {
// Source mode always fetches the whole document, so set section=null to unwrap sections
return mw.libs.ve.diffLoader.getModelFromResponse( response, null );

View file

@ -615,13 +615,41 @@ ve.init.mw.Target.prototype.getWikitextFragment = function ( doc, useRevision )
* @return {jQuery.Promise} Abortable promise
*/
ve.init.mw.Target.prototype.parseWikitextFragment = function ( wikitext, pst, doc ) {
return this.getContentApi( doc ).post( {
action: 'visualeditor',
paction: 'parsefragment',
page: this.getPageName( doc ),
wikitext: wikitext,
pst: pst
} );
var target = this;
var abortable, aborted;
var abortedPromise = ve.createDeferred().reject( 'http',
{ textStatus: 'abort', exception: 'abort' } ).promise();
function abort() {
aborted = true;
if ( abortable && abortable.abort ) {
abortable.abort();
}
}
// Acquire a temporary user username before previewing or diffing, so that signatures and
// user-related magic words display the temp user instead of IP user in the preview. (T331397)
var tempUserNamePromise;
if ( pst ) {
tempUserNamePromise = mw.user.acquireTempUserName();
} else {
tempUserNamePromise = ve.createDeferred().resolve( null );
}
return tempUserNamePromise
.then( function () {
if ( aborted ) {
return abortedPromise;
}
return ( abortable = target.getContentApi( doc ).post( {
action: 'visualeditor',
paction: 'parsefragment',
page: target.getPageName( doc ),
wikitext: wikitext,
pst: pst
} ) );
} )
.promise( { abort: abort } );
};
/**