mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/VisualEditor
synced 2024-11-12 09:09:25 +00:00
Use built-in mw.Api 'badtoken' handling, also 'assert'/'assertuser'
When the user is saving their edit, we want to ensure that they understand how it will be attributed. If the user gets logged out or logs in in another tab, we want to display a message about it before saving. Instead of manually managing tokens and handling the 'badtoken' error to detect this, use the 'assert'/'assertuser' parameters for the API to detect it for us. Thanks to this we can rely on automatic retrying for 'badtoken' errors in mw.Api#postWithToken. It will be possible to share some of this code with other extensions that already use ArticleTargetSaver, namely DiscussionTools, now that it doesn't need to manage tokens for VisualEditor. Bug: T245327 Depends-On: I485f99e1f5f493262b0c9af22370da01adf1e09c Change-Id: I37f8e89b6d92c419d1b6569891612256342f8139
This commit is contained in:
parent
6f7ef2fd76
commit
58757d4e3e
|
@ -155,7 +155,7 @@ ve.ui.MWExportWikitextDialog.prototype.export = function () {
|
|||
format: 'text/x-wiki',
|
||||
model: 'wikitext',
|
||||
wpTextbox1: wikitext,
|
||||
wpEditToken: ve.init.target.editToken,
|
||||
wpEditToken: mw.user.tokens.get( 'csrfToken' ),
|
||||
// MediaWiki function-verification parameters, mostly relevant to the
|
||||
// classic editpage, but still required here:
|
||||
wpUnicodeCheck: 'ℳ𝒲♥𝓊𝓃𝒾𝒸ℴ𝒹ℯ',
|
||||
|
|
|
@ -694,23 +694,13 @@ ve.init.mw.ArticleTarget.prototype.saveFail = function ( doc, saveData, wasRetry
|
|||
for ( i = 0; i < data.errors.length; i++ ) {
|
||||
error = data.errors[ i ];
|
||||
|
||||
// Handle token errors
|
||||
if ( error.code === 'badtoken' ) {
|
||||
if ( wasRetry ) {
|
||||
this.saveErrorBadToken( null, true );
|
||||
return;
|
||||
}
|
||||
this.refreshEditToken().done( function ( userChanged ) {
|
||||
// target.editToken has been refreshed
|
||||
if ( userChanged ) {
|
||||
target.saveErrorBadToken( mw.user.isAnon() ? null : mw.user.getName(), false );
|
||||
} else {
|
||||
// New session is the same user still; retry
|
||||
target.emit( 'saveErrorBadToken', true );
|
||||
target.save( doc, saveData, true );
|
||||
}
|
||||
} ).fail( function () {
|
||||
target.saveErrorBadToken( null, true );
|
||||
this.saveErrorBadTokenOrNewUser( null, true );
|
||||
} else if ( error.code === 'assertanonfailed' || error.code === 'assertuserfailed' || error.code === 'assertnameduserfailed' ) {
|
||||
this.refreshUser().then( function ( username ) {
|
||||
target.saveErrorBadTokenOrNewUser( username, false );
|
||||
}, function () {
|
||||
target.saveErrorUnknown( data );
|
||||
} );
|
||||
return;
|
||||
} else if ( error.code === 'editconflict' ) {
|
||||
|
@ -794,14 +784,14 @@ ve.init.mw.ArticleTarget.prototype.saveErrorHookAborted = function ( data ) {
|
|||
};
|
||||
|
||||
/**
|
||||
* Handle token fetch indicating another user is logged in, and token fetch errors.
|
||||
* Handle assert error indicating another user is logged in, and token fetch errors.
|
||||
*
|
||||
* @param {string|null} username Name of newly logged-in user, or null if anonymous
|
||||
* @param {boolean} [error=false] Whether there was an error trying to figure out who we're logged in as
|
||||
* @param {boolean} [error=false] Whether this is a token fetch error
|
||||
* @fires saveErrorBadToken
|
||||
* @fires saveErrorNewUser
|
||||
*/
|
||||
ve.init.mw.ArticleTarget.prototype.saveErrorBadToken = function ( username, error ) {
|
||||
ve.init.mw.ArticleTarget.prototype.saveErrorBadTokenOrNewUser = function ( username, error ) {
|
||||
var $msg = $( document.createTextNode( mw.msg( 'visualeditor-savedialog-error-badtoken' ) + ' ' ) );
|
||||
|
||||
if ( error ) {
|
||||
|
@ -1480,8 +1470,8 @@ ve.init.mw.ArticleTarget.prototype.save = function ( doc, options, isRetry ) {
|
|||
basetimestamp: this.baseTimeStamp,
|
||||
starttimestamp: this.startTimeStamp,
|
||||
etag: this.etag,
|
||||
// Pass in token to prevent automatic badtoken retries
|
||||
token: this.editToken
|
||||
assert: mw.user.isAnon() ? 'anon' : 'user',
|
||||
assertuser: mw.user.getName() || undefined
|
||||
} );
|
||||
|
||||
if ( mw.config.get( 'wgVisualEditorConfig' ).useChangeTagging && !data.vetags ) {
|
||||
|
@ -1588,7 +1578,7 @@ ve.init.mw.ArticleTarget.prototype.submit = function ( wikitext, fields ) {
|
|||
wpStarttime: this.startTimeStamp,
|
||||
wpEdittime: this.baseTimeStamp,
|
||||
wpTextbox1: wikitext,
|
||||
wpEditToken: this.editToken,
|
||||
wpEditToken: mw.user.tokens.get( 'csrfToken' ),
|
||||
// MediaWiki function-verification parameters, mostly relevant to the
|
||||
// classic editpage, but still required here:
|
||||
wpUnicodeCheck: 'ℳ𝒲♥𝓊𝓃𝒾𝒸ℴ𝒹ℯ',
|
||||
|
|
|
@ -20,7 +20,6 @@ ve.init.mw.Target = function VeInitMwTarget( config ) {
|
|||
|
||||
this.active = false;
|
||||
this.pageName = mw.config.get( 'wgRelevantPageName' );
|
||||
this.editToken = mw.user.tokens.get( 'csrfToken' );
|
||||
this.recovered = false;
|
||||
this.fromEditedState = false;
|
||||
this.originalHtml = null;
|
||||
|
@ -459,26 +458,21 @@ ve.init.mw.Target.prototype.teardown = function () {
|
|||
* the current user.
|
||||
*
|
||||
* @param {ve.dm.Document} [doc] Document to associate with the API request
|
||||
* @return {jQuery.Promise} Promise resolved with whether we switched users
|
||||
* @return {jQuery.Promise} Promise resolved with new username, or null if anonymous
|
||||
*/
|
||||
ve.init.mw.Target.prototype.refreshEditToken = function ( doc ) {
|
||||
ve.init.mw.Target.prototype.refreshUser = function ( doc ) {
|
||||
var api = this.getContentApi( doc ),
|
||||
deferred = ve.createDeferred(),
|
||||
target = this;
|
||||
deferred = ve.createDeferred();
|
||||
api.get( {
|
||||
action: 'query',
|
||||
meta: 'tokens|userinfo',
|
||||
type: 'csrf'
|
||||
meta: 'userinfo'
|
||||
} )
|
||||
.done( function ( data ) {
|
||||
var
|
||||
userInfo = data.query && data.query.userinfo,
|
||||
editToken = data.query && data.query.tokens && data.query.tokens.csrftoken,
|
||||
isAnon = mw.user.isAnon();
|
||||
|
||||
if ( userInfo && editToken ) {
|
||||
target.editToken = editToken;
|
||||
|
||||
if ( userInfo ) {
|
||||
if (
|
||||
( isAnon && userInfo.anon !== undefined ) ||
|
||||
// Comparing id instead of name to protect against possible
|
||||
|
@ -486,7 +480,7 @@ ve.init.mw.Target.prototype.refreshEditToken = function ( doc ) {
|
|||
mw.config.get( 'wgUserId' ) === userInfo.id
|
||||
) {
|
||||
// New session is the same user still
|
||||
deferred.resolve( false );
|
||||
deferred.resolve( mw.user.getName() );
|
||||
} else {
|
||||
// The now current session is a different user
|
||||
if ( userInfo.anon !== undefined ) {
|
||||
|
@ -502,7 +496,7 @@ ve.init.mw.Target.prototype.refreshEditToken = function ( doc ) {
|
|||
// New session is a different user
|
||||
mw.config.set( { wgUserId: userInfo.id, wgUserName: userInfo.name } );
|
||||
}
|
||||
deferred.resolve( true );
|
||||
deferred.resolve( mw.user.getName() );
|
||||
}
|
||||
} else {
|
||||
deferred.reject();
|
||||
|
@ -519,15 +513,12 @@ ve.init.mw.Target.prototype.refreshEditToken = function ( doc ) {
|
|||
*
|
||||
* @param {ve.dm.Document} doc Document
|
||||
* @param {boolean} [useRevision=true] Whether to use the revision ID + ETag
|
||||
* @param {boolean} [isRetry=false] Whether this call is retrying a prior call
|
||||
* @return {jQuery.Promise} Abortable promise which resolves with a wikitext string
|
||||
*/
|
||||
ve.init.mw.Target.prototype.getWikitextFragment = function ( doc, useRevision, isRetry ) {
|
||||
ve.init.mw.Target.prototype.getWikitextFragment = function ( doc, useRevision ) {
|
||||
var promise, xhr,
|
||||
target = this,
|
||||
params = {
|
||||
action: 'visualeditoredit',
|
||||
token: this.editToken,
|
||||
paction: 'serialize',
|
||||
html: ve.dm.converter.getDomFromModel( doc ).body.innerHTML,
|
||||
page: this.getPageName()
|
||||
|
@ -543,7 +534,7 @@ ve.init.mw.Target.prototype.getWikitextFragment = function ( doc, useRevision, i
|
|||
params.etag = this.etag;
|
||||
}
|
||||
|
||||
xhr = this.getContentApi( doc ).post(
|
||||
xhr = this.getContentApi( doc ).postWithToken( 'csrf',
|
||||
params,
|
||||
{ contentType: 'multipart/form-data' }
|
||||
);
|
||||
|
@ -553,12 +544,6 @@ ve.init.mw.Target.prototype.getWikitextFragment = function ( doc, useRevision, i
|
|||
return response.visualeditoredit.content;
|
||||
}
|
||||
return ve.createDeferred().reject();
|
||||
}, function ( error ) {
|
||||
if ( error === 'badtoken' && !isRetry ) {
|
||||
return target.refreshEditToken( doc ).then( function () {
|
||||
return target.getWikitextFragment( doc, useRevision, true );
|
||||
} );
|
||||
}
|
||||
} );
|
||||
|
||||
promise.abort = function () {
|
||||
|
|
Loading…
Reference in a new issue