From 3cce3dd5991ca94a0507c456e759c1bb63f7eb9e Mon Sep 17 00:00:00 2001 From: Santhosh Thottingal Date: Thu, 7 Mar 2013 14:49:39 +0530 Subject: [PATCH] Cache the options token for preferences system Change-Id: I2e9a4326119db390aba36cc5bc6da20c5c16eed1 --- resources/js/ext.uls.preferences.js | 114 ++++++++++++++++++++++------ 1 file changed, 92 insertions(+), 22 deletions(-) diff --git a/resources/js/ext.uls.preferences.js b/resources/js/ext.uls.preferences.js index 8f7f0769..913d539b 100644 --- a/resources/js/ext.uls.preferences.js +++ b/resources/js/ext.uls.preferences.js @@ -21,7 +21,90 @@ ( function ( $, mw, undefined ) { 'use strict'; - var ULSPreferences = function () { + var ULSPreferences, + cachedOptionsToken = null; + + /** + * Post to options API with correct 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. + * + * @param params {Object} API parameters + * @param ok {Function} callback for success + * @param err {Function} [optional] error callback + * @return {jqXHR} + */ + function saveOptionsWithToken ( params, ok, err ) { + var useTokenToPost, getTokenIfBad; + if ( cachedOptionsToken === null ) { + // We don't have a valid cached token, so get a fresh one and try posting. + // We do not trap any 'badtoken' or 'notoken' errors, because we don't want + // an infinite loop. If this fresh token is bad, something else is very wrong. + useTokenToPost = function ( token ) { + params.token = token; + new mw.Api().post( params, ok, err ); + }; + return getOptionToken( useTokenToPost, err ); + } else { + // We do have a token, but it might be expired. So if it is 'bad' then + // start over with a new token. + params.token = cachedOptionsToken; + getTokenIfBad = function ( code, result ) { + if ( code === 'badtoken' ) { + // force a new token, clear any old one + cachedOptionsToken = null; + saveOptionsWithToken( params, ok, err ); + } else { + err( code, result ); + } + }; + return new mw.Api().post( params, { ok : ok, err : getTokenIfBad }); + } + } + + /** + * Api helper to grab an options token + * + * token callback has signature ( String token ) + * error callback has signature ( String code, Object results, XmlHttpRequest xhr, Exception exception ) + * Note that xhr and exception are only available for 'http_*' errors + * code may be any http_* error code (see mw.Api), or 'token_missing' + * + * @param tokenCallback {Function} received token callback + * @param err {Function} error callback + * @return {jqXHR} + */ + function getOptionToken ( tokenCallback, err ) { + var parameters = { + action: 'tokens', + type: 'options' + }, + ok = function ( data ) { + var token; + // If token type is not available for this user, + // key 'translationreviewtoken' is missing or can contain Boolean false + if ( data.tokens && data.tokens.optionstoken ) { + token = data.tokens.optionstoken; + cachedOptionsToken = token; + tokenCallback( token ); + } else { + err( 'token-missing', data ); + } + }, + ajaxOptions = { + ok: ok, + err: err, + // Due to the API assuming we're logged out if we pass the callback-parameter, + // we have to disable jQuery's callback system, and instead parse JSON string, + // by setting 'jsonp' to false. + jsonp: false + }; + + return new mw.Api().get( parameters, ajaxOptions ); + } + + ULSPreferences = function () { this.preferenceName = 'uls-preferences'; this.username = mw.user.getName(); this.isAnon = mw.user.isAnon(); @@ -68,8 +151,7 @@ * @param callback */ save: function ( callback ) { - var ulsPreferences = this, - api; + var ulsPreferences = this; callback = callback || $.noop; if ( this.isAnon ) { @@ -78,25 +160,13 @@ callback.call( this, true ); } else { // Logged in user. Use MW apis to change preferences - api = new mw.Api(); - - api.post( { - action: 'tokens', - type: 'options' - } ).done( function ( tokenresult ) { - var token = tokenresult.tokens.optionstoken; - - api.post( { - action: 'options', - optionname: ulsPreferences.preferenceName, - optionvalue: $.toJSON( ulsPreferences.preferences ), - token: token - } ).done( function ( data ) { - callback.call( this, true ); - } ).fail( function ( data ) { - callback.call( this, false ); - } ); - } ).fail( function () { + saveOptionsWithToken( { + action: 'options', + optionname: ulsPreferences.preferenceName, + optionvalue: $.toJSON( ulsPreferences.preferences ) + }, function () { + callback.call( this, true ); + }, function () { callback.call( this, false ); } ); }