Clean up code related to token and bad token handling

Things I noticed while writing I37f8e89b6d92c419d1b6569891612256342f8139,
but which felt too messy to include in that commit.

* Use promise chaining
* Update documentation
* Remove redundant code
* Split a method that now handles two different errors
* Grumble about localisation messages

Change-Id: I81e28a03af4f6c3452679ef6bbcaa89bb1235122
This commit is contained in:
Bartosz Dziewoński 2020-02-15 03:50:16 +01:00
parent 58757d4e3e
commit 8065fdf2b9
3 changed files with 69 additions and 80 deletions

View file

@ -784,22 +784,20 @@ ve.init.mw.ArticleTarget.prototype.saveErrorHookAborted = function ( data ) {
}; };
/** /**
* Handle assert error indicating another user is logged in, and token fetch errors. * Handle assert error indicating another user is logged in.
* *
* @param {string|null} username Name of newly logged-in user, or null if anonymous * @param {string|null} username Name of newly logged-in user, or null if anonymous
* @param {boolean} [error=false] Whether this is a token fetch error
* @fires saveErrorBadToken
* @fires saveErrorNewUser * @fires saveErrorNewUser
*/ */
ve.init.mw.ArticleTarget.prototype.saveErrorBadTokenOrNewUser = function ( username, error ) { ve.init.mw.ArticleTarget.prototype.saveErrorNewUser = function ( username ) {
var $msg = $( document.createTextNode( mw.msg( 'visualeditor-savedialog-error-badtoken' ) + ' ' ) ); var $msg;
if ( error ) {
this.emit( 'saveErrorBadToken', false );
$msg = $msg.add( document.createTextNode( mw.msg( 'visualeditor-savedialog-identify-trylogin' ) ) );
} else {
this.emit( 'saveErrorNewUser' ); this.emit( 'saveErrorNewUser' );
$msg = $msg.add(
// TODO: Improve this message, concatenating it this way is a bad practice.
// This should read more like 'session_fail_preview' in MediaWiki core
// (but with the caveat that we know already whether you're logged in or not).
$msg = $( document.createTextNode( mw.msg( 'visualeditor-savedialog-error-badtoken' ) + ' ' ) ).add(
mw.message( mw.message(
username === null ? username === null ?
'visualeditor-savedialog-identify-anon' : 'visualeditor-savedialog-identify-anon' :
@ -807,10 +805,27 @@ ve.init.mw.ArticleTarget.prototype.saveErrorBadTokenOrNewUser = function ( usern
username username
).parseDom() ).parseDom()
); );
}
this.showSaveError( $msg ); this.showSaveError( $msg );
}; };
/**
* Handle token fetch errors.
*
* @fires saveErrorBadToken
*/
ve.init.mw.ArticleTarget.prototype.saveErrorBadToken = function () {
this.emit( 'saveErrorBadToken', false );
// TODO: Improve this message, concatenating it this way is a bad practice.
// Also, it's not always true that you're "no longer logged in".
// This should read more like 'session_fail_preview' in MediaWiki core.
this.showSaveError(
mw.msg( 'visualeditor-savedialog-error-badtoken' ) + ' ' +
mw.msg( 'visualeditor-savedialog-identify-trylogin' )
);
};
/** /**
* Handle unknown save error * Handle unknown save error
* *
@ -1264,10 +1279,8 @@ ve.init.mw.ArticleTarget.prototype.clearPreparedCacheKey = function () {
* HTML directly if there is no cache key present or pending, or if the request for the cache key * HTML directly if there is no cache key present or pending, or if the request for the cache key
* fails, or if using the cache key fails with a badcachekey error. * fails, or if using the cache key fails with a badcachekey error.
* *
* If extraData.token is set, this function will use mw.Api#post and let the caller handle badtoken * This function will use mw.Api#postWithToken to retry automatically when encountering a 'badtoken'
* errors. If extraData.token is not set, this function will use mw.Api#postWithToken which retries * error.
* automatically when encountering a badtoken error. If you do not want the automatic retry behavior
* and want to control badtoken retries, you have to set extradata.token.
* *
* @param {HTMLDocument|string} doc Document to submit or string in source mode * @param {HTMLDocument|string} doc Document to submit or string in source mode
* @param {Object} extraData POST parameters to send. Do not include 'html', 'cachekey' or 'format'. * @param {Object} extraData POST parameters to send. Do not include 'html', 'cachekey' or 'format'.

View file

@ -461,28 +461,16 @@ ve.init.mw.Target.prototype.teardown = function () {
* @return {jQuery.Promise} Promise resolved with new username, or null if anonymous * @return {jQuery.Promise} Promise resolved with new username, or null if anonymous
*/ */
ve.init.mw.Target.prototype.refreshUser = function ( doc ) { ve.init.mw.Target.prototype.refreshUser = function ( doc ) {
var api = this.getContentApi( doc ), return this.getContentApi( doc ).get( {
deferred = ve.createDeferred();
api.get( {
action: 'query', action: 'query',
meta: 'userinfo' meta: 'userinfo'
} ) } ).then( function ( data ) {
.done( function ( data ) { var userInfo = data.query && data.query.userinfo;
var
userInfo = data.query && data.query.userinfo, if ( !userInfo ) {
isAnon = mw.user.isAnon(); return ve.createDeferred().reject();
}
if ( userInfo ) {
if (
( isAnon && userInfo.anon !== undefined ) ||
// Comparing id instead of name to protect against possible
// normalisation and against case where the user got renamed.
mw.config.get( 'wgUserId' ) === userInfo.id
) {
// New session is the same user still
deferred.resolve( mw.user.getName() );
} else {
// The now current session is a different user
if ( userInfo.anon !== undefined ) { if ( userInfo.anon !== undefined ) {
// New session is an anonymous user // New session is an anonymous user
mw.config.set( { mw.config.set( {
@ -493,19 +481,15 @@ ve.init.mw.Target.prototype.refreshUser = function ( doc ) {
wgUserName: null wgUserName: null
} ); } );
} else { } else {
// New session is a different user // New session is a logged in user
mw.config.set( { wgUserId: userInfo.id, wgUserName: userInfo.name } ); mw.config.set( {
} wgUserId: userInfo.id,
deferred.resolve( mw.user.getName() ); wgUserName: userInfo.name
} } );
} else { }
deferred.reject();
} return mw.user.getName();
} )
.fail( function () {
deferred.reject();
} ); } );
return deferred.promise();
}; };
/** /**
@ -516,7 +500,7 @@ ve.init.mw.Target.prototype.refreshUser = function ( doc ) {
* @return {jQuery.Promise} Abortable promise which resolves with a wikitext string * @return {jQuery.Promise} Abortable promise which resolves with a wikitext string
*/ */
ve.init.mw.Target.prototype.getWikitextFragment = function ( doc, useRevision ) { ve.init.mw.Target.prototype.getWikitextFragment = function ( doc, useRevision ) {
var promise, xhr, var xhr,
params = { params = {
action: 'visualeditoredit', action: 'visualeditoredit',
paction: 'serialize', paction: 'serialize',
@ -539,18 +523,12 @@ ve.init.mw.Target.prototype.getWikitextFragment = function ( doc, useRevision )
{ contentType: 'multipart/form-data' } { contentType: 'multipart/form-data' }
); );
promise = xhr.then( function ( response ) { return xhr.then( function ( response ) {
if ( response.visualeditoredit ) { if ( response.visualeditoredit ) {
return response.visualeditoredit.content; return response.visualeditoredit.content;
} }
return ve.createDeferred().reject(); return ve.createDeferred().reject();
} ); } ).promise( { abort: xhr.abort } );
promise.abort = function () {
xhr.abort();
};
return promise;
}; };
/** /**

View file

@ -195,7 +195,8 @@
}, },
/** /**
* Post content to the API. * Post content to the API, using mw.Api#postWithToken to retry automatically when encountering
* a 'badtoken' error.
* *
* By default uses action=visualeditoredit, paction=save. * By default uses action=visualeditoredit, paction=save.
* *
@ -230,11 +231,8 @@
data data
); );
if ( data.token ) {
request = api.post( data, { contentType: 'multipart/form-data' } );
} else {
request = api.postWithToken( 'csrf', data, { contentType: 'multipart/form-data' } ); request = api.postWithToken( 'csrf', data, { contentType: 'multipart/form-data' } );
}
return request.then( return request.then(
function ( response, jqxhr ) { function ( response, jqxhr ) {
var eventData, fullEventName, error, var eventData, fullEventName, error,