[IMP] move openerp session into coresetup

bzr revid: al@openerp.com-20120430150244-wh38i70f6pweu4yy
This commit is contained in:
Antony Lesuisse 2012-04-30 17:02:44 +02:00
parent e20149bc04
commit cf322b0064
2 changed files with 443 additions and 442 deletions

View File

@ -1341,448 +1341,6 @@ instance.web.JsonRPC = instance.web.CallbackEnabled.extend({
},
});
instance.web.Session = instance.web.JsonRPC.extend( /** @lends instance.web.Session# */{
init: function() {
this._super.apply(this, arguments);
// TODO: session store in cookie should be optional
this.name = instance._session_id;
this.qweb_mutex = new $.Mutex();
},
rpc: function(url, params, success_callback, error_callback) {
params.session_id = this.session_id;
return this._super(url, params, success_callback, error_callback);
},
/**
* Setup a sessionm
*/
session_bind: function(origin) {
var self = this;
this.setup(origin);
instance.web.qweb.default_dict['_s'] = this.origin;
this.session_id = false;
this.uid = false;
this.username = false;
this.user_context= {};
this.db = false;
this.openerp_entreprise = false;
this.module_list = instance._modules.slice();
this.module_loaded = {};
_(this.module_list).each(function (mod) {
self.module_loaded[mod] = true;
});
this.context = {};
this.shortcuts = [];
this.active_id = null;
return this.session_init();
},
/**
* Init a session, reloads from cookie, if it exists
*/
session_init: function () {
var self = this;
// TODO: session store in cookie should be optional
this.session_id = this.get_cookie('session_id');
return this.session_reload().pipe(function(result) {
var modules = instance._modules.join(',');
var deferred = self.rpc('/web/webclient/qweblist', {mods: modules}).pipe(self.do_load_qweb);
if(self.session_is_valid()) {
return deferred.pipe(function() { return self.load_modules(); });
}
return deferred;
});
},
/**
* (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 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...)
_.extend(self, {
session_id: result.session_id,
db: result.db,
username: result.login,
uid: result.uid,
user_context: result.context,
openerp_entreprise: result.openerp_entreprise
});
});
},
session_is_valid: function() {
return !!this.uid;
},
/**
* The session is validated either by login or by restoration of a previous session
*/
session_authenticate: function(db, login, password, _volatile) {
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).pipe(function(result) {
_.extend(self, {
session_id: result.session_id,
db: result.db,
username: result.login,
uid: result.uid,
user_context: result.context,
openerp_entreprise: result.openerp_entreprise
});
if (!_volatile) {
self.set_cookie('session_id', self.session_id);
}
return self.load_modules();
});
},
session_logout: function() {
this.set_cookie('session_id', '');
return this.rpc("/web/session/destroy", {});
},
on_session_valid: function() {
},
/**
* 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
* @param name the cookie's name
*/
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) {
return JSON.parse(decodeURIComponent(cookie.substring(nameEQ.length)));
}
}
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
*
* @param {Boolean} [no_session_valid_signal=false] prevents load_module from triggering ``on_session_valid``.
*/
load_modules: function(no_session_valid_signal) {
var self = this;
return this.rpc('/web/session/modules', {}).pipe(function(result) {
var lang = self.user_context.lang,
all_modules = _.uniq(self.module_list.concat(result));
var params = { mods: all_modules, lang: lang};
var to_load = _.difference(result, self.module_list).join(',');
self.module_list = all_modules;
var loaded = $.Deferred().resolve().promise();
if (to_load.length) {
loaded = $.when(
self.rpc('/web/webclient/csslist', {mods: to_load}, self.do_load_css),
self.rpc('/web/webclient/qweblist', {mods: to_load}).pipe(self.do_load_qweb),
self.rpc('/web/webclient/translations', params).pipe(function(trans) {
instance.web._t.database.set_bundle(trans);
var file_list = ["/web/static/lib/datejs/globalization/" + lang.replace("_", "-") + ".js"];
return self.rpc('/web/webclient/jslist', {mods: to_load}).pipe(function(files) {
return self.do_load_js(file_list.concat(files));
}).then(function () {
if (!Date.CultureInfo.pmDesignator) {
// If no am/pm designator is specified but the openerp
// datetime format uses %i, date.js won't be able to
// correctly format a date. See bug#938497.
Date.CultureInfo.amDesignator = 'AM';
Date.CultureInfo.pmDesignator = 'PM';
}
});
}))
}
return loaded.then(function() {
self.on_modules_loaded();
if (!no_session_valid_signal) {
self.on_session_valid();
}
});
});
},
do_load_css: function (files) {
var self = this;
_.each(files, function (file) {
$('head').append($('<link>', {
'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_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).then(function () {
d.resolve();
});
};
var head = document.head || document.getElementsByTagName('head')[0];
head.appendChild(tag);
} else {
d.resolve();
}
return d;
},
do_load_qweb: function(files) {
var self = this;
_.each(files, function(file) {
self.qweb_mutex.exec(function() {
return self.rpc('/web/proxy/load', {path: file}).pipe(function(xml) {
if (!xml) { return; }
instance.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++) {
var mod = this.module_list[j];
if(this.module_loaded[mod])
continue;
instance[mod] = {};
// init module mod
if(instance._openerp[mod] != undefined) {
instance._openerp[mod](instance);
this.module_loaded[mod] = true;
}
}
},
get_url: function (file) {
return this.prefix + file;
},
/**
* Cooperative file download implementation, for ajaxy APIs.
*
* Requires that the server side implements an httprequest correctly
* setting the `fileToken` cookie to the value provided as the `token`
* parameter. The cookie *must* be set on the `/` path and *must not* be
* `httpOnly`.
*
* It would probably also be a good idea for the response to use a
* `Content-Disposition: attachment` header, especially if the MIME is a
* "known" type (e.g. text/plain, or for some browsers application/json
*
* @param {Object} options
* @param {String} [options.url] used to dynamically create a form
* @param {Object} [options.data] data to add to the form submission. If can be used without a form, in which case a form is created from scratch. Otherwise, added to form data
* @param {HTMLFormElement} [options.form] the form to submit in order to fetch the file
* @param {Function} [options.success] callback in case of download success
* @param {Function} [options.error] callback in case of request error, provided with the error body
* @param {Function} [options.complete] called after both ``success`` and ``error` callbacks have executed
*/
get_file: function (options) {
// need to detect when the file is done downloading (not used
// yet, but we'll need it to fix the UI e.g. with a throbber
// while dump is being generated), iframe load event only fires
// when the iframe content loads, so we need to go smarter:
// http://geekswithblogs.net/GruffCode/archive/2010/10/28/detecting-the-file-download-dialog-in-the-browser.aspx
var timer, token = new Date().getTime(),
cookie_name = 'fileToken', cookie_length = cookie_name.length,
CHECK_INTERVAL = 1000, id = _.uniqueId('get_file_frame'),
remove_form = false;
var $form, $form_data = $('<div>');
var complete = function () {
if (options.complete) { options.complete(); }
clearTimeout(timer);
$form_data.remove();
$target.remove();
if (remove_form && $form) { $form.remove(); }
};
var $target = $('<iframe style="display: none;">')
.attr({id: id, name: id})
.appendTo(document.body)
.load(function () {
try {
if (options.error) {
options.error(JSON.parse(
this.contentDocument.body.childNodes[1].textContent
));
}
} finally {
complete();
}
});
if (options.form) {
$form = $(options.form);
} else {
remove_form = true;
$form = $('<form>', {
action: options.url,
method: 'POST'
}).appendTo(document.body);
}
_(_.extend({}, options.data || {},
{session_id: this.session_id, token: token}))
.each(function (value, key) {
var $input = $form.find('[name=' + key +']');
if (!$input.length) {
$input = $('<input type="hidden" name="' + key + '">')
.appendTo($form_data);
}
$input.val(value)
});
$form
.append($form_data)
.attr('target', id)
.get(0).submit();
var waitLoop = function () {
var cookies = document.cookie.split(';');
// setup next check
timer = setTimeout(waitLoop, CHECK_INTERVAL);
for (var i=0; i<cookies.length; ++i) {
var cookie = cookies[i].replace(/^\s*/, '');
if (!cookie.indexOf(cookie_name === 0)) { continue; }
var cookie_val = cookie.substring(cookie_length + 1);
if (parseInt(cookie_val, 10) !== token) { continue; }
// clear cookie
document.cookie = _.str.sprintf("%s=;expires=%s;path=/",
cookie_name, new Date().toGMTString());
if (options.success) { options.success(); }
complete();
return;
}
};
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;
}
}
});
instance.web.TranslationDataBase = instance.web.Class.extend(/** @lends instance.web.TranslationDataBase# */{
/**
* @constructs instance.web.TranslationDataBase
* @extends instance.web.Class
*/
init: function() {
this.db = {};
this.parameters = {"direction": 'ltr',
"date_format": '%m/%d/%Y',
"time_format": '%H:%M:%S',
"grouping": [],
"decimal_point": ".",
"thousands_sep": ","};
},
set_bundle: function(translation_bundle) {
var self = this;
this.db = {};
var modules = _.keys(translation_bundle.modules);
modules.sort();
if (_.include(modules, "web")) {
modules = ["web"].concat(_.without(modules, "web"));
}
_.each(modules, function(name) {
self.add_module_translation(translation_bundle.modules[name]);
});
if (translation_bundle.lang_parameters) {
this.parameters = translation_bundle.lang_parameters;
this.parameters.grouping = py.eval(
this.parameters.grouping);
}
},
add_module_translation: function(mod) {
var self = this;
_.each(mod.messages, function(message) {
self.db[message.id] = message.string;
});
},
build_translation_function: function() {
var self = this;
var fcnt = function(str) {
var tmp = self.get(str);
return tmp === undefined ? str : tmp;
};
fcnt.database = this;
return fcnt;
},
get: function(key) {
if (this.db[key])
return this.db[key];
return undefined;
}
});
/**
* @deprecated use :class:`instance.web.Widget`
*/
instance.web.OldWidget = instance.web.Widget.extend({
init: function(parent, element_id) {
this._super(parent);
this.element_id = element_id;
this.element_id = this.element_id || _.uniqueId('widget-');
var tmp = document.getElementById(this.element_id);
this.$element = tmp ? $(tmp) : $(document.createElement(this.tagName));
},
renderElement: function() {
var rendered = this.render();
if (rendered) {
var elem = $(rendered);
this.$element.replaceWith(elem);
this.$element = elem;
}
return this;
},
render: function (additional) {
if (this.template)
return instance.web.qweb.render(this.template, _.extend({widget: this}, additional || {}));
return null;
}
});
}
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:

View File

@ -11,6 +11,449 @@ if (!console.debug) {
openerp.web.coresetup = function(instance) {
/**
* @deprecated use :class:`instance.web.Widget`
*/
instance.web.OldWidget = instance.web.Widget.extend({
init: function(parent, element_id) {
this._super(parent);
this.element_id = element_id;
this.element_id = this.element_id || _.uniqueId('widget-');
var tmp = document.getElementById(this.element_id);
this.$element = tmp ? $(tmp) : $(document.createElement(this.tagName));
},
renderElement: function() {
var rendered = this.render();
if (rendered) {
var elem = $(rendered);
this.$element.replaceWith(elem);
this.$element = elem;
}
return this;
},
render: function (additional) {
if (this.template)
return instance.web.qweb.render(this.template, _.extend({widget: this}, additional || {}));
return null;
}
});
/** Session openerp specific RPC class */
instance.web.Session = instance.web.JsonRPC.extend( /** @lends instance.web.Session# */{
init: function() {
this._super.apply(this, arguments);
// TODO: session store in cookie should be optional
this.name = instance._session_id;
this.qweb_mutex = new $.Mutex();
},
rpc: function(url, params, success_callback, error_callback) {
params.session_id = this.session_id;
return this._super(url, params, success_callback, error_callback);
},
/**
* Setup a sessionm
*/
session_bind: function(origin) {
var self = this;
this.setup(origin);
instance.web.qweb.default_dict['_s'] = this.origin;
this.session_id = false;
this.uid = false;
this.username = false;
this.user_context= {};
this.db = false;
this.openerp_entreprise = false;
this.module_list = instance._modules.slice();
this.module_loaded = {};
_(this.module_list).each(function (mod) {
self.module_loaded[mod] = true;
});
this.context = {};
this.shortcuts = [];
this.active_id = null;
return this.session_init();
},
/**
* Init a session, reloads from cookie, if it exists
*/
session_init: function () {
var self = this;
// TODO: session store in cookie should be optional
this.session_id = this.get_cookie('session_id');
return this.session_reload().pipe(function(result) {
var modules = instance._modules.join(',');
var deferred = self.rpc('/web/webclient/qweblist', {mods: modules}).pipe(self.do_load_qweb);
if(self.session_is_valid()) {
return deferred.pipe(function() { return self.load_modules(); });
}
return deferred;
});
},
/**
* (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 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...)
_.extend(self, {
session_id: result.session_id,
db: result.db,
username: result.login,
uid: result.uid,
user_context: result.context,
openerp_entreprise: result.openerp_entreprise
});
});
},
session_is_valid: function() {
return !!this.uid;
},
/**
* The session is validated either by login or by restoration of a previous session
*/
session_authenticate: function(db, login, password, _volatile) {
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).pipe(function(result) {
_.extend(self, {
session_id: result.session_id,
db: result.db,
username: result.login,
uid: result.uid,
user_context: result.context,
openerp_entreprise: result.openerp_entreprise
});
if (!_volatile) {
self.set_cookie('session_id', self.session_id);
}
return self.load_modules();
});
},
session_logout: function() {
this.set_cookie('session_id', '');
return this.rpc("/web/session/destroy", {});
},
on_session_valid: function() {
},
/**
* 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
* @param name the cookie's name
*/
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) {
return JSON.parse(decodeURIComponent(cookie.substring(nameEQ.length)));
}
}
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
*
* @param {Boolean} [no_session_valid_signal=false] prevents load_module from triggering ``on_session_valid``.
*/
load_modules: function(no_session_valid_signal) {
var self = this;
return this.rpc('/web/session/modules', {}).pipe(function(result) {
var lang = self.user_context.lang,
all_modules = _.uniq(self.module_list.concat(result));
var params = { mods: all_modules, lang: lang};
var to_load = _.difference(result, self.module_list).join(',');
self.module_list = all_modules;
var loaded = $.Deferred().resolve().promise();
if (to_load.length) {
loaded = $.when(
self.rpc('/web/webclient/csslist', {mods: to_load}, self.do_load_css),
self.rpc('/web/webclient/qweblist', {mods: to_load}).pipe(self.do_load_qweb),
self.rpc('/web/webclient/translations', params).pipe(function(trans) {
instance.web._t.database.set_bundle(trans);
var file_list = ["/web/static/lib/datejs/globalization/" + lang.replace("_", "-") + ".js"];
return self.rpc('/web/webclient/jslist', {mods: to_load}).pipe(function(files) {
return self.do_load_js(file_list.concat(files));
}).then(function () {
if (!Date.CultureInfo.pmDesignator) {
// If no am/pm designator is specified but the openerp
// datetime format uses %i, date.js won't be able to
// correctly format a date. See bug#938497.
Date.CultureInfo.amDesignator = 'AM';
Date.CultureInfo.pmDesignator = 'PM';
}
});
}))
}
return loaded.then(function() {
self.on_modules_loaded();
if (!no_session_valid_signal) {
self.on_session_valid();
}
});
});
},
do_load_css: function (files) {
var self = this;
_.each(files, function (file) {
$('head').append($('<link>', {
'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_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).then(function () {
d.resolve();
});
};
var head = document.head || document.getElementsByTagName('head')[0];
head.appendChild(tag);
} else {
d.resolve();
}
return d;
},
do_load_qweb: function(files) {
var self = this;
_.each(files, function(file) {
self.qweb_mutex.exec(function() {
return self.rpc('/web/proxy/load', {path: file}).pipe(function(xml) {
if (!xml) { return; }
instance.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++) {
var mod = this.module_list[j];
if(this.module_loaded[mod])
continue;
instance[mod] = {};
// init module mod
if(instance._openerp[mod] != undefined) {
instance._openerp[mod](instance);
this.module_loaded[mod] = true;
}
}
},
get_url: function (file) {
return this.prefix + file;
},
/**
* Cooperative file download implementation, for ajaxy APIs.
*
* Requires that the server side implements an httprequest correctly
* setting the `fileToken` cookie to the value provided as the `token`
* parameter. The cookie *must* be set on the `/` path and *must not* be
* `httpOnly`.
*
* It would probably also be a good idea for the response to use a
* `Content-Disposition: attachment` header, especially if the MIME is a
* "known" type (e.g. text/plain, or for some browsers application/json
*
* @param {Object} options
* @param {String} [options.url] used to dynamically create a form
* @param {Object} [options.data] data to add to the form submission. If can be used without a form, in which case a form is created from scratch. Otherwise, added to form data
* @param {HTMLFormElement} [options.form] the form to submit in order to fetch the file
* @param {Function} [options.success] callback in case of download success
* @param {Function} [options.error] callback in case of request error, provided with the error body
* @param {Function} [options.complete] called after both ``success`` and ``error` callbacks have executed
*/
get_file: function (options) {
// need to detect when the file is done downloading (not used
// yet, but we'll need it to fix the UI e.g. with a throbber
// while dump is being generated), iframe load event only fires
// when the iframe content loads, so we need to go smarter:
// http://geekswithblogs.net/GruffCode/archive/2010/10/28/detecting-the-file-download-dialog-in-the-browser.aspx
var timer, token = new Date().getTime(),
cookie_name = 'fileToken', cookie_length = cookie_name.length,
CHECK_INTERVAL = 1000, id = _.uniqueId('get_file_frame'),
remove_form = false;
var $form, $form_data = $('<div>');
var complete = function () {
if (options.complete) { options.complete(); }
clearTimeout(timer);
$form_data.remove();
$target.remove();
if (remove_form && $form) { $form.remove(); }
};
var $target = $('<iframe style="display: none;">')
.attr({id: id, name: id})
.appendTo(document.body)
.load(function () {
try {
if (options.error) {
options.error(JSON.parse(
this.contentDocument.body.childNodes[1].textContent
));
}
} finally {
complete();
}
});
if (options.form) {
$form = $(options.form);
} else {
remove_form = true;
$form = $('<form>', {
action: options.url,
method: 'POST'
}).appendTo(document.body);
}
_(_.extend({}, options.data || {},
{session_id: this.session_id, token: token}))
.each(function (value, key) {
var $input = $form.find('[name=' + key +']');
if (!$input.length) {
$input = $('<input type="hidden" name="' + key + '">')
.appendTo($form_data);
}
$input.val(value)
});
$form
.append($form_data)
.attr('target', id)
.get(0).submit();
var waitLoop = function () {
var cookies = document.cookie.split(';');
// setup next check
timer = setTimeout(waitLoop, CHECK_INTERVAL);
for (var i=0; i<cookies.length; ++i) {
var cookie = cookies[i].replace(/^\s*/, '');
if (!cookie.indexOf(cookie_name === 0)) { continue; }
var cookie_val = cookie.substring(cookie_length + 1);
if (parseInt(cookie_val, 10) !== token) { continue; }
// clear cookie
document.cookie = _.str.sprintf("%s=;expires=%s;path=/",
cookie_name, new Date().toGMTString());
if (options.success) { options.success(); }
complete();
return;
}
};
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;
}
}
});
/** OpenERP Translations */
instance.web.TranslationDataBase = instance.web.Class.extend(/** @lends instance.web.TranslationDataBase# */{
/**
* @constructs instance.web.TranslationDataBase
* @extends instance.web.Class
*/
init: function() {
this.db = {};
this.parameters = {"direction": 'ltr',
"date_format": '%m/%d/%Y',
"time_format": '%H:%M:%S',
"grouping": [],
"decimal_point": ".",
"thousands_sep": ","};
},
set_bundle: function(translation_bundle) {
var self = this;
this.db = {};
var modules = _.keys(translation_bundle.modules);
modules.sort();
if (_.include(modules, "web")) {
modules = ["web"].concat(_.without(modules, "web"));
}
_.each(modules, function(name) {
self.add_module_translation(translation_bundle.modules[name]);
});
if (translation_bundle.lang_parameters) {
this.parameters = translation_bundle.lang_parameters;
this.parameters.grouping = py.eval(
this.parameters.grouping);
}
},
add_module_translation: function(mod) {
var self = this;
_.each(mod.messages, function(message) {
self.db[message.id] = message.string;
});
},
build_translation_function: function() {
var self = this;
var fcnt = function(str) {
var tmp = self.get(str);
return tmp === undefined ? str : tmp;
};
fcnt.database = this;
return fcnt;
},
get: function(key) {
if (this.db[key])
return this.db[key];
return undefined;
}
});
/** Configure default qweb */
instance.web._t = new instance.web.TranslationDataBase().build_translation_function();
/**