* Retrieves the object matching the provided key string.
*
* @param {String} key the key to fetch the object for
+ * @param {Boolean} [silent_error=false] returns undefined if the key or object is not found, rather than throwing an exception
* @returns {Class} the stored class, to initialize
*
* @throws {openerp.web.KeyNotFound} if the object was not in the mapping
* @throws {openerp.web.ObjectNotFound} if the object path was invalid
*/
- get_object: function (key) {
+ get_object: function (key, silent_error) {
var path_string = this.map[key];
if (path_string === undefined) {
+ if (silent_error) { return void 'nooo'; }
throw new openerp.web.KeyNotFound(key);
}
object_match = object_match[path[i]];
if (object_match === undefined) {
+ if (silent_error) { return void 'noooooo'; }
throw new openerp.web.ObjectNotFound(path_string);
}
}
*/
init: function() {
this._super();
- // TODO: session should have an optional name indicating that they'll
- // be saved to (and revived from) cookies
+ this.server = null;
+ this.debug = ($.deparam($.param.querystring()).debug != undefined);
+ // TODO: session store in cookie should be optional
this.name = openerp._session_id;
+ this.qweb_mutex = new $.Mutex();
},
- bind: function(host, protocol) {
- this.host = (host == undefined) ? location.host : host;
- this.protocol = (protocol == undefined) ? location.protocol : protocol;
- this.prefix = this.protocol + '//' + this.host;
- openerp.web.qweb.default_dict['_s'] = this.prefix
- this.rpc_mode = (host == location.host) ? "json" : "jsonp";
- this.debug = (window.location.search.indexOf('?debug') !== -1);
+ bind: function(origin) {
+ var window_origin = location.protocol+"//"+location.host;
+ this.origin = origin ? _.str.rtrim(origin,'/') : window_origin;
+ this.prefix = this.origin;
+ this.server = this.origin; // keep chs happy
+ openerp.web.qweb.default_dict['_s'] = this.origin;
+ this.rpc_function = (this.origin == window_origin) ? this.rpc_json : this.rpc_jsonp;
this.session_id = false;
this.uid = false;
this.username = false;
this.user_context= {};
this.db = false;
- this.module_loading = $.Deferred();
this.module_list = [];
this.module_loaded = {"web": true};
this.context = {};
this.shortcuts = [];
this.active_id = null;
- this.do_load_qweb(['/web/webclient/qweb'], continuation);
+ this.ready = $.Deferred();
+ return this.session_init();
},
/**
* Executes an RPC call, registering the provided callbacks.
jsonrpc: '2.0',
method: 'call',
params: params,
- id: _.uniqueId('oe-')
+ id: _.uniqueId('r')
};
- // Call using the rpc_mode
var deferred = $.Deferred();
this.on_rpc_request();
- this.rpc_json(url, payload).then(
+ this.rpc_function(url, payload).then(
function (response, textStatus, jqXHR) {
self.on_rpc_response();
if (!response.error) {
deferred.resolve(response["result"], textStatus, jqXHR);
} else if (response.error.data.type === "session_invalid") {
self.uid = false;
+ // TODO deprecate or use a deferred on login.do_ask_login()
self.on_session_invalid(function() {
self.rpc(url, payload.params,
function() { deferred.resolve.apply(deferred, arguments); },
data: JSON.stringify(payload),
processData: false,
}, url);
+ if (this.synch)
+ ajax.async = false;
return $.ajax(ajax);
},
rpc_jsonp: function(url, payload) {
var self = this;
+ // extracted from payload to set on the url
+ var data = {
+ session_id: this.session_id,
+ id: payload.id,
+ };
+ url.url = this.get_url(url.url);
var ajax = _.extend({
type: "GET",
dataType: 'jsonp',
jsonp: 'jsonp',
cache: false,
- data: {
- session_id: this.session_id,
- id: payload.id,
- }
+ data: data
}, url);
+ if (this.synch)
+ ajax.async = false;
var payload_str = JSON.stringify(payload);
var payload_url = $.param({r:payload_str});
- if(playload_url.length < 2000) {
- // Direct json request
+ if(payload_url.length < 2000) {
+ // Direct jsonp request
ajax.data.r = payload_str;
return $.ajax(ajax);
} else {
- // Indirect json request
- var ifid = _.uniqueId('oe_rpc_iframe_');
+ // Indirect jsonp request
+ var ifid = _.uniqueId('oe_rpc_iframe');
var display = options.openerp.debug ? 'block' : 'none';
var $iframe = $(_.str.sprintf("<iframe src='javascript:false;' name='%s' id='%s' style='display:%s'></iframe>", ifid, ifid, display));
var $form = $('<form>')
.attr('method', 'POST')
.attr('target', ifid)
.attr('enctype', "multipart/form-data")
- .attr('action', ajax.url)
+ .attr('action', ajax.url + '?' + $.param(data))
.append($('<input type="hidden" name="r" />').attr('value', payload_str))
.hide()
.appendTo($('body'));
});
// append the iframe to the DOM (will trigger the first load)
$form.after($iframe);
- return deffered;
+ return deferred;
}
},
on_rpc_request: function() {
on_rpc_error: function(error) {
},
/**
- * The session is validated either by login or by restoration of a previous session
+ * Init a session, reloads from cookie, if it exists
*/
- on_session_valid: function(continuation) {
- this.load_modules().then(function() { continuation() } );
- },
- on_session_invalid: function(continuation) {
- },
- session_is_valid: function() {
- return this.uid;
- },
- session_authenticate: function(db, login, password, success_callback) {
+ session_init: function () {
var self = this;
- var base_location = document.location.protocol + '//' + document.location.host;
- var params = { db: db, login: login, password: password, base_location: base_location };
- return this.rpc("/web/session/authenticate", params, function(result) {
+ // TODO: session store in cookie should be optional
+ this.session_id = this.get_cookie('session_id');
+ return this.rpc("/web/session/get_session_info", {}).pipe(function(result) {
+ // If immediately follows a login (triggered by trying to restore
+ // an invalid session or no session at all), refresh session data
+ // (should not change, but just in case...)
_.extend(self, {
- session_id: result.session_id,
- uid: result.uid,
- user_context: result.context,
db: result.db,
- username: result.login
+ username: result.login,
+ uid: result.uid,
+ user_context: result.context
});
- self.session_save();
- self.on_session_valid(success_callback);
- return true;
+ var deferred = self.do_load_qweb(['/web/webclient/qweb']);
+ if(self.session_is_valid()) {
+ return deferred.pipe(_.bind(function() { this.load_modules(); }, self));
+ }
+ return deferred;
});
},
+ session_is_valid: function() {
+ return !!this.uid;
+ },
/**
- * Reloads uid and session_id from local storage, if they exist
+ * The session is validated either by login or by restoration of a previous session
*/
- session_restore: function (continuation) {
+ session_authenticate: function(db, login, password, volatile) {
var self = this;
- this.session_id = this.get_cookie('session_id');
- return this.rpc("/web/session/get_session_info", {}).then(function(result) {
- // If immediately follows a login (triggered by trying to restore
- // an invalid session or no session at all), refresh session data
- // (should not change, but just in case...) but should not call
- // on_session_valid again as it triggers reloading the menu
- var already_logged = self.uid;
+ var base_location = document.location.protocol + '//' + document.location.host;
+ var params = { db: db, login: login, password: password, base_location: base_location };
+ return this.rpc("/web/session/authenticate", params).pipe(function(result) {
_.extend(self, {
- uid: result.uid,
- user_context: result.context,
+ session_id: result.session_id,
db: result.db,
- username: result.login
+ username: result.login,
+ uid: result.uid,
+ user_context: result.context
});
- if (!already_logged) {
- if (self.uid) {
- self.on_session_valid(continuation);
- } else {
- self.on_session_invalid(continuation);
- }
+ if (!volatile) {
+ self.set_cookie('session_id', self.session_id);
}
- }, function() {
- self.on_session_invalid(continuation);
+ return self.load_modules();
});
},
- /**
- * Saves the session id and uid locally
- */
- session_save: function () {
- this.set_cookie('session_id', this.session_id);
- },
- logout: function() {
+ session_logout: function() {
this.set_cookie('session_id', '');
- this.reload_client();
- },
- reload_client: function() {
window.location.reload();
},
/**
+ * Called when a rpc call fail due to an invalid session.
+ * By default, it's a noop
+ */
+ on_session_invalid: function(retry_callback) {
+ },
+ /**
* Fetches a cookie stored by an openerp session
*
* @private
if(openerp._modules_loaded) {
return $.when();
}
- this.rpc('/web/session/modules', {}, function(result) {
+ return this.rpc('/web/session/modules', {}).pipe(function(result) {
self.module_list = result;
var lang = self.user_context.lang;
var params = { mods: ["web"].concat(result), lang: lang};
- self.rpc('/web/webclient/translations',params).then(function(transs) {
- openerp.web._t.database.set_bundle(transs);
- var modules = self.module_list.join(',');
- var file_list = ["/web/static/lib/datejs/globalization/" +
- self.user_context.lang.replace("_", "-") + ".js"
- ];
- return $.when(
- self.rpc('/web/webclient/qweblist', {mods: modules}, self.do_load_qweb),
- self.rpc('/web/webclient/csslist', {mods: modules}, self.do_load_css),
- self.rpc('/web/webclient/jslist', {mods: modules}, function(files) {
- self.do_load_js(file_list.concat(files));
- })
- );
+ var modules = self.module_list.join(',');
+ return $.when(
+ self.rpc('/web/webclient/csslist', {mods: modules}, self.do_load_css),
+ self.rpc('/web/webclient/qweblist', {mods: modules}).pipe(self.do_load_qweb),
+ self.rpc('/web/webclient/translations', params).pipe(function(trans) {
+ openerp.web._t.database.set_bundle(trans);
+ var file_list = ["/web/static/lib/datejs/globalization/" + lang.replace("_", "-") + ".js"];
+ return self.rpc('/web/webclient/jslist', {mods: modules}).pipe(function(files) {
+ return self.do_load_js(file_list.concat(files));
+ });
+ })
+ ).then(function() {
+ self.ready.resolve();
});
});
},
var self = this;
_.each(files, function (file) {
$('head').append($('<link>', {
- 'href': self.get_absolute_url(file),
+ 'href': self.get_url(file),
'rel': 'stylesheet',
'type': 'text/css'
}));
},
do_load_js: function(files) {
var self = this;
+ var d = $.Deferred();
if(files.length != 0) {
var file = files.shift();
var tag = document.createElement('script');
tag.type = 'text/javascript';
- tag.src = self.get_absolute_url(file);
+ tag.src = self.get_url(file);
tag.onload = tag.onreadystatechange = function() {
if ( (tag.readyState && tag.readyState != "loaded" && tag.readyState != "complete") || tag.onload_done )
return;
tag.onload_done = true;
- self.do_load_js(files, callback);
+ self.do_load_js(files).then(function () {
+ d.resolve();
+ });
};
var head = document.head || document.getElementsByTagName('head')[0];
head.appendChild(tag);
} else {
self.on_modules_loaded();
+ d.resolve();
}
+ return d;
},
do_load_qweb: function(files) {
var self = this;
- if (files.length != 0) {
- var file = files.shift();
- self.rpc('/web/proxy/load', {path: file}, function(xml) {
- openerp.web.qweb.add_template(_.str.trim(xml));
- self.do_load_qweb(files, callback);
+ _.each(files, function(file) {
+ self.qweb_mutex.exec(function() {
+ return self.rpc('/web/proxy/load', {path: file}).pipe(function(xml) {
+ openerp.web.qweb.add_template(_.str.trim(xml));
+ });
});
- }
+ });
+ return self.qweb_mutex.def;
},
on_modules_loaded: function() {
for(var j=0; j<this.module_list.length; j++) {
}
}
},
+ get_url: function (file) {
+ return this.prefix + file;
+ },
/**
* Cooperative file download implementation, for ajaxy APIs.
*
}
};
timer = setTimeout(waitLoop, CHECK_INTERVAL);
- }
+ },
+ synchronized_mode: function(to_execute) {
+ var synch = this.synch;
+ this.synch = true;
+ try {
+ return to_execute();
+ } finally {
+ this.synch = synch;
+ }
+ },
});
/**
/** Configure default qweb */
openerp.web._t = new openerp.web.TranslationDataBase().build_translation_function();
+/**
+ * Lazy translation function, only performs the translation when actually
+ * printed (e.g. inserted into a template)
+ *
+ * Useful when defining translatable strings in code evaluated before the
+ * translation database is loaded, as class attributes or at the top-level of
+ * an OpenERP Web module
+ *
+ * @param {String} s string to translate
+ * @returns {Object} lazy translation object
+ */
+openerp.web._lt = function (s) {
+ return {toString: function () { return openerp.web._t(s); }}
+};
openerp.web.qweb = new QWeb2.Engine();
openerp.web.qweb.debug = (window.location.search.indexOf('?debug') !== -1);
openerp.web.qweb.default_dict = {
'_' : _,
'_t' : openerp.web._t
-}
+};
openerp.web.qweb.format_text_node = function(s) {
// Note that 'this' is the Qweb Node of the text
var translation = this.node.parentNode.attributes['t-translation'];
return tr === ts ? s : tr;
}
-/** Setup default connection */
-openerp.connection = new openerp.web.Connection();
-openerp.web.qweb.default_dict['__debug__'] = openerp.connection.debug;
-
/** Jquery extentions */
$.Mutex = (function() {
function Mutex() {
return Mutex;
})();
+/** Setup default connection */
+openerp.connection = new openerp.web.Connection();
+openerp.web.qweb.default_dict['__debug__'] = openerp.connection.debug;
+
+
$.async_when = function() {
var async = false;
var def = $.Deferred();
return def;
};
+// special tweak for the web client
+var old_async_when = $.async_when;
+$.async_when = function() {
+ if (openerp.connection.synch)
+ return $.when.apply(this, arguments);
+ else
+ return old_async_when.apply(this, arguments);
+};
+
};
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax: