dataType: 'json'
}
- };
+ },
+ tokenCache = {};
/**
* Constructor to create an object to interact with the API of a particular MediaWiki server.
*
* @param {Object} parameters
* @param {Object} [ajaxOptions]
- * @return {jQuery.Promise} Done: API response data. Fail: Error code
+ * @return {jQuery.Promise} Done: API response data and the jqXHR object.
+ * Fail: Error code
*/
ajax: function ( parameters, ajaxOptions ) {
var token,
apiDeferred = $.Deferred(),
+ msg = 'Use of mediawiki.api callback params is deprecated. Use the Promise instead.',
xhr;
parameters = $.extend( {}, this.defaults.parameters, parameters );
// Backwards compatibility: Before MediaWiki 1.20,
// callbacks were done with the 'ok' and 'err' property in ajaxOptions.
if ( ajaxOptions.ok ) {
+ mw.track( 'mw.deprecate', 'api.cbParam' );
+ mw.log.warn( msg );
apiDeferred.done( ajaxOptions.ok );
delete ajaxOptions.ok;
}
if ( ajaxOptions.err ) {
+ mw.track( 'mw.deprecate', 'api.cbParam' );
+ mw.log.warn( msg );
apiDeferred.fail( ajaxOptions.err );
delete ajaxOptions.err;
}
} );
} )
// AJAX success just means "200 OK" response, also check API error codes
- .done( function ( result ) {
+ .done( function ( result, textStatus, jqXHR ) {
if ( result === undefined || result === null || result === '' ) {
apiDeferred.reject( 'ok-but-empty',
'OK response but empty result (check HTTP headers?)'
var code = result.error.code === undefined ? 'unknown' : result.error.code;
apiDeferred.reject( code, result );
} else {
- apiDeferred.resolve( result );
+ apiDeferred.resolve( result, jqXHR );
}
} );
return apiDeferred.promise( { abort: xhr.abort } ).fail( function ( code, details ) {
mw.log( 'mw.Api error: ', code, details );
} );
- }
+ },
+
+ /**
+ * Post to API with specified type of token. If we have no token, get one and try to post.
+ * 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', {
+ * action: 'options',
+ * optionname: 'gender',
+ * optionvalue: 'female'
+ * } );
+ *
+ * @param {string} tokenType The name of the token, like options or edit.
+ * @param {Object} params API parameters
+ * @return {jQuery.Promise} See #post
+ */
+ postWithToken: function ( tokenType, params ) {
+ var api = this, hasOwn = tokenCache.hasOwnProperty;
+ if ( hasOwn.call( tokenCache, tokenType ) && tokenCache[tokenType] !== undefined ) {
+ params.token = tokenCache[tokenType];
+ return api.post( params ).then(
+ null,
+ function ( code ) {
+ if ( code === 'badtoken' ) {
+ // force a new token, clear any old one
+ tokenCache[tokenType] = params.token = undefined;
+ return api.post( params );
+ }
+ // Pass the promise forward, so the caller gets error codes
+ return this;
+ }
+ );
+ } else {
+ return api.getToken( tokenType ).then( function ( token ) {
+ tokenCache[tokenType] = params.token = token;
+ return api.post( params );
+ } );
+ }
+ },
+ /**
+ * Api helper to grab any token.
+ *
+ * @param {string} type Token type.
+ * @return {jQuery.Promise}
+ * @return {Function} return.done
+ * @return {string} return.done.token Received token.
+ */
+ getToken: function ( type ) {
+ var apiPromise,
+ d = $.Deferred();
+
+ apiPromise = this.get( {
+ action: 'tokens',
+ type: type
+ } )
+ .done( function ( data ) {
+ // If token type is not available for this user,
+ // key '...token' is missing or can contain Boolean false
+ if ( data.tokens && data.tokens[type + 'token'] ) {
+ d.resolve( data.tokens[type + 'token'] );
+ } else {
+ d.reject( 'token-missing', data );
+ }
+ } )
+ .fail( d.reject );
+
+ return d.promise( { abort: apiPromise.abort } );
+ }
};
/**