// Keyed by ajax url and symbolic name for the individual request
promises = {};
+ function mapLegacyToken( action ) {
+ // Legacy types for backward-compatibility with API action=tokens.
+ var csrfActions = [
+ 'edit',
+ 'delete',
+ 'protect',
+ 'move',
+ 'block',
+ 'unblock',
+ 'email',
+ 'import',
+ 'options'
+ ];
+ if ( $.inArray( action, csrfActions ) !== -1 ) {
+ mw.track( 'mw.deprecate', 'apitoken_' + action );
+ mw.log.warn( 'Use of the "' + action + '" token is deprecated. Use "csrf" instead.' );
+ return 'csrf';
+ }
+ return action;
+ }
+
// Pre-populate with fake ajax promises to save http requests for tokens
// we already have on the page via the user.tokens module (bug 34733).
promises[ defaultOptions.ajax.url ] = {};
$.each( mw.user.tokens.get(), function ( key, value ) {
// This requires #getToken to use the same key as user.tokens.
- // Format: token-type + "Token" (eg. editToken, patrolToken, watchToken).
+ // Format: token-type + "Token" (eg. csrfToken, patrolToken, watchToken).
promises[ defaultOptions.ajax.url ][ key ] = $.Deferred()
.resolve( value )
.promise( { abort: function () {} } );
.done( function ( result, textStatus, jqXHR ) {
if ( result === undefined || result === null || result === '' ) {
apiDeferred.reject( 'ok-but-empty',
- 'OK response but empty result (check HTTP headers?)'
+ 'OK response but empty result (check HTTP headers?)',
+ result,
+ jqXHR
);
} else if ( result.error ) {
var code = result.error.code === undefined ? 'unknown' : result.error.code;
- apiDeferred.reject( code, result );
+ apiDeferred.reject( code, result, result, jqXHR );
} else {
apiDeferred.resolve( result, jqXHR );
}
* If we have a cached token try using that, and if it fails, blank out the
* cached token and start over. For example to change an user option you could do:
*
- * new mw.Api().postWithToken( 'options', {
+ * new mw.Api().postWithToken( 'csrf', {
* action: 'options',
* optionname: 'gender',
* optionvalue: 'female'
/**
* Get a token for a certain action from the API.
*
- * The assert parameter is only for internal use by postWithToken.
+ * The assert parameter is only for internal use by #postWithToken.
*
- * @param {string} type Token type
- * @return {jQuery.Promise}
- * @return {Function} return.done
- * @return {string} return.done.token Received token.
* @since 1.22
+ * @param {string} type Token type
+ * @return {jQuery.Promise} Received token.
*/
getToken: function ( type, assert ) {
- var apiPromise,
- promiseGroup = promises[ this.defaults.ajax.url ],
- d = promiseGroup && promiseGroup[ type + 'Token' ];
+ var apiPromise, promiseGroup, d;
+ type = mapLegacyToken( type );
+ promiseGroup = promises[ this.defaults.ajax.url ];
+ d = promiseGroup && promiseGroup[ type + 'Token' ];
if ( !d ) {
- apiPromise = this.get( { action: 'tokens', type: type, assert: assert } );
-
+ apiPromise = this.get( {
+ action: 'query',
+ meta: 'tokens',
+ type: type,
+ assert: assert
+ } );
d = apiPromise
- .then( function ( data ) {
- if ( data.tokens && data.tokens[ type + 'token' ] ) {
- return data.tokens[ type + 'token' ];
+ .then( function ( res ) {
+ // If token type is unknown, it is omitted from the response
+ if ( !res.query.tokens[ type + 'token' ] ) {
+ return $.Deferred().reject( 'token-missing', res );
}
- // If token type is not available for this user,
- // key '...token' is either missing or set to boolean false
- return $.Deferred().reject( 'token-missing', data );
+ return res.query.tokens[ type + 'token' ];
}, function () {
// Clear promise. Do not cache errors.
delete promiseGroup[ type + 'Token' ];
+
// Pass on to allow the caller to handle the error
return this;
} )
*/
badToken: function ( type ) {
var promiseGroup = promises[ this.defaults.ajax.url ];
+
+ type = mapLegacyToken( type );
if ( promiseGroup ) {
delete promiseGroup[ type + 'Token' ];
}