summaryrefslogtreecommitdiff
path: root/addons/web/static/src/js/framework/session.js
diff options
context:
space:
mode:
Diffstat (limited to 'addons/web/static/src/js/framework/session.js')
-rw-r--r--addons/web/static/src/js/framework/session.js393
1 files changed, 393 insertions, 0 deletions
diff --git a/addons/web/static/src/js/framework/session.js b/addons/web/static/src/js/framework/session.js
new file mode 100644
index 00000000000..f81fec5ab54
--- /dev/null
+++ b/addons/web/static/src/js/framework/session.js
@@ -0,0 +1,393 @@
+odoo.define('web.Session', ['web.ajax', 'web.core', 'web.utils'], function (require) {
+"use strict";
+
+var ajax = require('web.ajax');
+var core = require('web.core');
+var utils = require('web.utils');
+
+var _t = core._t;
+var qweb = core.qweb;
+var mixins = core.mixins;
+
+var Session = core.Class.extend(mixins.EventDispatcherMixin, {
+ /**
+
+ @param parent The parent of the newly created object.
+ or `null` if the server to contact is the origin server.
+ @param {Dict} options A dictionary that can contain the following options:
+
+ * "override_session": Default to false. If true, the current session object will
+ not try to re-use a previously created session id stored in a cookie.
+ * "session_id": Default to null. If specified, the specified session_id will be used
+ by this session object. Specifying this option automatically implies that the option
+ "override_session" is set to true.
+ */
+ init: function(parent, origin, options) {
+ mixins.EventDispatcherMixin.init.call(this, parent);
+ options = options || {};
+ this.module_list = (options.modules && options.modules.slice()) || (window.odoo._modules && window.odoo._modules.slice()) || [];
+ this.server = null;
+ this.session_id = options.session_id || null;
+ this.override_session = options.override_session || !!options.session_id || false;
+ this.avoid_recursion = false;
+ this.use_cors = options.use_cors || false;
+ this.setup(origin);
+ this.debug = ($.deparam($.param.querystring()).debug !== undefined);
+ // TODO: session store in cookie should be optional
+ this.qweb_mutex = new utils.Mutex();
+ },
+ setup: function(origin, options) {
+ // must be able to customize server
+ var window_origin = location.protocol + "//" + location.host;
+ origin = origin ? origin.replace( /\/+$/, '') : window_origin;
+ if (!_.isUndefined(this.origin) && this.origin !== origin)
+ throw new Error('Session already bound to ' + this.origin);
+ else
+ this.origin = origin;
+ this.prefix = this.origin;
+ this.server = this.origin; // keep chs happy
+ this.origin_server = this.origin === window_origin;
+ options = options || {};
+ if ('use_cors' in options) {
+ this.use_cors = options.use_cors;
+ }
+ },
+ /**
+ * Setup a session
+ */
+ session_bind: function(origin) {
+ var self = this;
+ this.setup(origin);
+ qweb.default_dict._s = this.origin;
+ this.uid = null;
+ this.username = null;
+ this.user_context= {};
+ this.db = null;
+ this.module_loaded = {};
+ _(this.module_list).each(function (mod) {
+ self.module_loaded[mod] = true;
+ });
+ this.active_id = null;
+ return this.session_init();
+ },
+ /**
+ * Init a session, reloads from cookie, if it exists
+ */
+ session_init: function () {
+ var self = this;
+ return this.session_reload().then(function() {
+ var modules = self.module_list.join(',');
+ var deferred = self.load_qweb(modules);
+ if(self.session_is_valid()) {
+ return deferred.then(function() { return self.load_modules(); });
+ }
+ return $.when(
+ deferred,
+ self.rpc('/web/webclient/bootstrap_translations', {mods: self.module_list}).then(function(trans) {
+ _t.database.set_bundle(trans);
+ })
+ );
+ });
+ },
+ session_is_valid: function() {
+ var db = $.deparam.querystring().db;
+ if (db && this.db !== db) {
+ return false;
+ }
+ return !!this.uid;
+ },
+ /**
+ * The session is validated by restoration of a previous session
+ */
+ session_authenticate: function() {
+ var self = this;
+ return $.when(this._session_authenticate.apply(this, arguments)).then(function() {
+ return self.load_modules();
+ });
+ },
+ /**
+ * The session is validated either by login or by restoration of a previous session
+ */
+ _session_authenticate: function(db, login, password) {
+ var self = this;
+ var params = {db: db, login: login, password: password};
+ return this.rpc("/web/session/authenticate", params).then(function(result) {
+ if (!result.uid) {
+ return $.Deferred().reject();
+ }
+ delete result.session_id;
+ _.extend(self, result);
+ });
+ },
+ session_logout: function() {
+ $.bbq.removeState();
+ return this.rpc("/web/session/destroy", {});
+ },
+ get_cookie: function (name) {
+ if (!this.name) { return null; }
+ var nameEQ = this.name + '|' + name + '=';
+ var cookies = document.cookie.split(';');
+ for(var i=0; i<cookies.length; ++i) {
+ var cookie = cookies[i].replace(/^\s*/, '');
+ if(cookie.indexOf(nameEQ) === 0) {
+ try {
+ return JSON.parse(decodeURIComponent(cookie.substring(nameEQ.length)));
+ } catch(err) {
+ // wrong cookie, delete it
+ this.set_cookie(name, '', -1);
+ }
+ }
+ }
+ return null;
+ },
+ /**
+ * Create a new cookie with the provided name and value
+ *
+ * @private
+ * @param name the cookie's name
+ * @param value the cookie's value
+ * @param ttl the cookie's time to live, 1 year by default, set to -1 to delete
+ */
+ set_cookie: function (name, value, ttl) {
+ if (!this.name) { return; }
+ ttl = ttl || 24*60*60*365;
+ document.cookie = [
+ this.name + '|' + name + '=' + encodeURIComponent(JSON.stringify(value)),
+ 'path=/',
+ 'max-age=' + ttl,
+ 'expires=' + new Date(new Date().getTime() + ttl*1000).toGMTString()
+ ].join(';');
+ },
+ /**
+ * Load additional web addons of that instance and init them
+ *
+ */
+ load_modules: function() {
+ var self = this;
+ return this.rpc('/web/session/modules', {}).then(function(result) {
+ var all_modules = _.uniq(self.module_list.concat(result));
+ var to_load = _.difference(result, self.module_list).join(',');
+ self.module_list = all_modules;
+
+ var loaded = self.load_translations();
+ var locale = "/web/webclient/locale/" + self.user_context.lang || 'en_US';
+ var file_list = [ locale ];
+ if(to_load.length) {
+ loaded = $.when(
+ loaded,
+ self.rpc('/web/webclient/csslist', {mods: to_load}).done(self.load_css.bind(self)),
+ self.load_qweb(to_load),
+ self.rpc('/web/webclient/jslist', {mods: to_load}).done(function(files) {
+ file_list = file_list.concat(files);
+ })
+ );
+ }
+ return loaded.then(function () {
+ return self.load_js(file_list);
+ }).done(function() {
+ self.on_modules_loaded();
+ self.trigger('module_loaded');
+ });
+ });
+ },
+ load_translations: function() {
+ return _t.database.load_translations(this, this.module_list, this.user_context.lang);
+ },
+ load_css: function (files) {
+ var self = this;
+ _.each(files, function (file) {
+ ajax.loadCSS(self.url(file, null));
+ });
+ },
+ load_js: function(files) {
+ var self = this;
+ var d = $.Deferred();
+ if (files.length !== 0) {
+ var file = files.shift();
+ var url = self.url(file, null);
+ ajax.loadJS(url).done(d.resolve);
+ } else {
+ d.resolve();
+ }
+ return d;
+ },
+ load_qweb: function(mods) {
+ var self = this;
+ self.qweb_mutex.exec(function() {
+ return self.rpc('/web/proxy/load', {path: '/web/webclient/qweb?mods=' + mods}).then(function(xml) {
+ if (!xml) { return; }
+ qweb.add_template(_.str.trim(xml));
+ });
+ });
+ return self.qweb_mutex.def;
+ },
+ on_modules_loaded: function() {
+ var openerp = window.openerp;
+ for(var j=0; j<this.module_list.length; j++) {
+ var mod = this.module_list[j];
+ if(this.module_loaded[mod])
+ continue;
+ openerp[mod] = {};
+ // init module mod
+ var fct = openerp._openerp[mod];
+ if(typeof(fct) === "function") {
+ openerp._openerp[mod] = {};
+ for (var k in fct) {
+ openerp._openerp[mod][k] = fct[k];
+ }
+ fct(openerp, openerp._openerp[mod]);
+ }
+ this.module_loaded[mod] = true;
+ }
+ },
+ get_file: function (options) {
+ if (this.override_session){
+ options.data.session_id = this.session_id;
+ }
+ ajax.get_file(options);
+ },
+ synchronized_mode: function(to_execute) {
+ var synch = this.synch;
+ this.synch = true;
+ try {
+ return to_execute();
+ } finally {
+ this.synch = synch;
+ }
+ },
+ /**
+ * (re)loads the content of a session: db name, username, user id, session
+ * context and status of the support contract
+ *
+ * @returns {$.Deferred} deferred indicating the session is done reloading
+ */
+ session_reload: function () {
+ var self = this;
+ return self.rpc("/web/session/get_session_info", {}).then(function(result) {
+ delete result.session_id;
+ _.extend(self, result);
+ });
+ },
+ check_session_id: function() {
+ var self = this;
+ if (this.avoid_recursion)
+ return $.when();
+ if (this.session_id)
+ return $.when(); // we already have the session id
+ if (!this.use_cors && (this.override_session || ! this.origin_server)) {
+ // If we don't use the origin server we consider we should always create a new session.
+ // Even if some browsers could support cookies when using jsonp that behavior is
+ // not consistent and the browser creators are tending to removing that feature.
+ this.avoid_recursion = true;
+ return this.rpc("/gen_session_id", {}).then(function(result) {
+ self.session_id = result;
+ }).always(function() {
+ self.avoid_recursion = false;
+ });
+ } else {
+ // normal use case, just use the cookie
+ self.session_id = utils.get_cookie("session_id");
+ return $.when();
+ }
+ },
+ /**
+ * Executes an RPC call, registering the provided callbacks.
+ *
+ * Registers a default error callback if none is provided, and handles
+ * setting the correct session id and session context in the parameter
+ * objects
+ *
+ * @param {String} url RPC endpoint
+ * @param {Object} params call parameters
+ * @param {Object} options additional options for rpc call
+ * @param {Function} success_callback function to execute on RPC call success
+ * @param {Function} error_callback function to execute on RPC call failure
+ * @returns {jQuery.Deferred} jquery-provided ajax deferred
+ */
+ rpc: function(url, params, options) {
+ var self = this;
+ options = _.clone(options || {});
+ var shadow = options.shadow || false;
+ delete options.shadow;
+
+ return self.check_session_id().then(function() {
+ // TODO: remove
+ if (! _.isString(url)) {
+ _.extend(options, url);
+ url = url.url;
+ }
+ // TODO correct handling of timeouts
+ if (! shadow)
+ self.trigger('request');
+ var fct;
+ if (self.origin_server) {
+ fct = ajax.jsonRpc;
+ if (self.override_session) {
+ options.headers = _.extend({}, options.headers, {
+ "X-Openerp-Session-Id": self.override_session ? self.session_id || '' : ''
+ });
+ }
+ } else if (self.use_cors) {
+ fct = ajax.jsonRpc;
+ url = self.url(url, null);
+ options.session_id = self.session_id || '';
+ if (self.override_session) {
+ options.headers = _.extend({}, options.headers, {
+ "X-Openerp-Session-Id": self.override_session ? self.session_id || '' : ''
+ });
+ }
+ } else {
+ fct = ajax.jsonpRpc;
+ url = self.url(url, null);
+ options.session_id = self.session_id || '';
+ }
+ var p = fct(url, "call", params, options);
+ p = p.then(function (result) {
+ if (! shadow)
+ self.trigger('response');
+ return result;
+ }, function(type, error, textStatus, errorThrown) {
+ if (type === "server") {
+ if (! shadow)
+ self.trigger('response');
+ if (error.code === 100) {
+ self.uid = false;
+ }
+ return $.Deferred().reject(error, $.Event());
+ } else {
+ if (! shadow)
+ self.trigger('response_failed');
+ var nerror = {
+ code: -32098,
+ message: "XmlHttpRequestError " + errorThrown,
+ data: {type: "xhr"+textStatus, debug: error.responseText, objects: [error, errorThrown] }
+ };
+ return $.Deferred().reject(nerror, $.Event());
+ }
+ });
+ return p.fail(function() { // Allow deferred user to disable rpc_error call in fail
+ p.fail(function(error, event) {
+ if (!event.isDefaultPrevented()) {
+ self.trigger('error', error, event);
+ }
+ });
+ });
+ });
+ },
+ url: function(path, params) {
+ params = _.extend(params || {});
+ if (this.override_session || (! this.origin_server))
+ params.session_id = this.session_id;
+ var qs = $.param(params);
+ if (qs.length > 0)
+ qs = "?" + qs;
+ var prefix = _.any(['http://', 'https://', '//'], function(el) {
+ return path.length >= el.length && path.slice(0, el.length) === el;
+ }) ? '' : this.prefix;
+ return prefix + path + qs;
+ },
+});
+
+return Session;
+
+});