summaryrefslogtreecommitdiff
path: root/addons/base/static/src/js
diff options
context:
space:
mode:
authorXavier Morel <xmo@openerp.com>2011-08-16 16:25:15 +0200
committerXavier Morel <xmo@openerp.com>2011-08-16 16:25:15 +0200
commitffae7cb4b3e7350d19b273feb100ba73cd022214 (patch)
tree15a06c6746e4d6f9d7042a269362ed72728eeceb /addons/base/static/src/js
parentd33c95fc4568516c22e12fdcce901d8250c9d218 (diff)
parent36a1a0f1c3f6fe471453d52955f97110220599a3 (diff)
[MERGE] from trunk
bzr revid: xmo@openerp.com-20110816142515-ohq33zvua5uq8cd3
Diffstat (limited to 'addons/base/static/src/js')
-rw-r--r--addons/base/static/src/js/boot.js24
-rw-r--r--addons/base/static/src/js/chrome.js343
-rw-r--r--addons/base/static/src/js/core.js432
-rw-r--r--addons/base/static/src/js/data.js245
-rw-r--r--addons/base/static/src/js/dates.js137
-rw-r--r--addons/base/static/src/js/form.js169
-rw-r--r--addons/base/static/src/js/formats.js208
-rw-r--r--addons/base/static/src/js/list.js130
-rw-r--r--addons/base/static/src/js/search.js168
-rw-r--r--addons/base/static/src/js/view_tree.js190
-rw-r--r--addons/base/static/src/js/views.js307
11 files changed, 1287 insertions, 1066 deletions
diff --git a/addons/base/static/src/js/boot.js b/addons/base/static/src/js/boot.js
index 5754f5ccd14..3d1d1e75e7c 100644
--- a/addons/base/static/src/js/boot.js
+++ b/addons/base/static/src/js/boot.js
@@ -1,6 +1,6 @@
-//---------------------------------------------------------
-// OpenERP Web Boostrap
-//---------------------------------------------------------
+/*---------------------------------------------------------
+ * OpenERP Web Boostrap Code
+ *---------------------------------------------------------*/
/**
* @name openerp
@@ -14,13 +14,8 @@
/** @lends openerp */
var openerp = this.openerp = {
- /**
- * Debug flag turns on logging
- */
+ // debug flag
debug: true,
- // element_ids registry linked to all controllers on the page
- // TODO rename to elements, or keep gtk naming?
- screen: {},
// Per session namespace
// openerp.<module> will map to
// openerp.sessions.sessionname.<module> using a closure
@@ -40,8 +35,6 @@
// this unique id will be replaced by hostname_databasename by
// openerp.base.Connection on the first connection
_session_id: "session" + session_counter++,
- screen: openerp.screen,
- sessions: openerp.sessions,
base: {},
web_mobile: {}
};
@@ -51,16 +44,17 @@
}
return new_instance;
}
+ // TODO add initrpc to init core only for RPC
};
})();
-//---------------------------------------------------------
-// OpenERP base module split
-//---------------------------------------------------------
+/*---------------------------------------------------------
+ * OpenERP Web base module split
+ *---------------------------------------------------------*/
openerp.base = function(instance) {
openerp.base.core(instance);
- openerp.base.dates(instance);
+ openerp.base.formats(instance);
openerp.base.chrome(instance);
openerp.base.data(instance);
if (openerp.base.views) {
diff --git a/addons/base/static/src/js/chrome.js b/addons/base/static/src/js/chrome.js
index ecce83342a2..927a000f002 100644
--- a/addons/base/static/src/js/chrome.js
+++ b/addons/base/static/src/js/chrome.js
@@ -1,287 +1,9 @@
/*---------------------------------------------------------
- * OpenERP base library
+ * OpenERP Web chrome
*---------------------------------------------------------*/
openerp.base.chrome = function(openerp) {
-openerp.base.Session = openerp.base.Widget.extend( /** @lends openerp.base.Session# */{
- /**
- * @constructs
- * @param element_id to use for exception reporting
- * @param server
- * @param port
- */
- init: function(parent, element_id, server, port) {
- this._super(parent, element_id);
- this.server = (server == undefined) ? location.hostname : server;
- this.port = (port == undefined) ? location.port : port;
- this.rpc_mode = (server == location.hostname) ? "ajax" : "jsonp";
- this.debug = true;
- this.db = "";
- this.login = "";
- this.password = "";
- this.uid = false;
- this.session_id = false;
- this.module_list = [];
- this.module_loaded = {"base": true};
- this.context = {};
- this.shortcuts = [];
- this.active_id = null;
- },
- start: function() {
- this.session_restore();
- },
- /**
- * 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 {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, success_callback, error_callback) {
- var self = this;
- // Construct a JSON-RPC2 request, method is currently unused
- params.session_id = this.session_id;
-
- // Call using the rpc_mode
- var deferred = $.Deferred();
- this.rpc_ajax(url, {
- jsonrpc: "2.0",
- method: "call",
- params: params,
- id:null
- }).then(function () {deferred.resolve.apply(deferred, arguments);},
- function(error) {deferred.reject(error, $.Event());});
- return deferred.fail(function() {
- deferred.fail(function(error, event) {
- if (!event.isDefaultPrevented()) {
- self.on_rpc_error(error, event);
- }
- });
- }).then(success_callback, error_callback).promise();
- },
- /**
- * Raw JSON-RPC call
- *
- * @returns {jQuery.Deferred} ajax-based deferred object
- */
- rpc_ajax: function(url, payload) {
- var self = this;
- this.on_rpc_request();
- // url can be an $.ajax option object
- if (_.isString(url)) {
- url = {
- url: url
- }
- }
- var ajax = _.extend({
- type: "POST",
- url: url,
- dataType: 'json',
- contentType: 'application/json',
- data: JSON.stringify(payload),
- processData: false
- }, url);
- var deferred = $.Deferred();
- $.ajax(ajax).done(function(response, textStatus, jqXHR) {
- self.on_rpc_response();
- if (!response.error) {
- deferred.resolve(response["result"], textStatus, jqXHR);
- return;
- }
- if (response.error.data.type !== "session_invalid") {
- deferred.reject(response.error);
- return;
- }
- self.uid = false;
- self.on_session_invalid(function() {
- self.rpc(url, payload.params,
- function() {
- deferred.resolve.apply(deferred, arguments);
- },
- function(error, event) {
- event.preventDefault();
- deferred.reject.apply(deferred, arguments);
- });
- });
- }).fail(function(jqXHR, textStatus, errorThrown) {
- self.on_rpc_response();
- var error = {
- code: -32098,
- message: "XmlHttpRequestError " + errorThrown,
- data: {type: "xhr"+textStatus, debug: jqXHR.responseText, objects: [jqXHR, errorThrown] }
- };
- deferred.reject(error);
- });
- return deferred.promise();
- },
- on_rpc_request: function() {
- },
- on_rpc_response: function() {
- },
- on_rpc_error: function(error) {
- },
- /**
- * The session is validated either by login or by restoration of a previous session
- */
- on_session_valid: function() {
- if(!openerp._modules_loaded)
- this.load_modules();
- },
- on_session_invalid: function(contination) {
- },
- session_is_valid: function() {
- return this.uid;
- },
- session_login: function(db, login, password, success_callback) {
- var self = this;
- this.db = db;
- this.login = login;
- this.password = password;
- var params = { db: this.db, login: this.login, password: this.password };
- this.rpc("/base/session/login", params, function(result) {
- self.session_id = result.session_id;
- self.uid = result.uid;
- self.session_save();
- self.on_session_valid();
- if (success_callback)
- success_callback();
- });
- },
- session_logout: function() {
- this.uid = false;
- },
- /**
- * Reloads uid and session_id from local storage, if they exist
- */
- session_restore: function () {
- this.uid = this.get_cookie('uid');
- this.session_id = this.get_cookie('session_id');
- this.db = this.get_cookie('db');
- this.login = this.get_cookie('login');
- // we should do an rpc to confirm that this session_id is valid and if it is retrieve the information about db and login
- // then call on_session_valid
- this.on_session_valid();
- },
- /**
- * Saves the session id and uid locally
- */
- session_save: function () {
- this.set_cookie('uid', this.uid);
- this.set_cookie('session_id', this.session_id);
- this.set_cookie('db', this.db);
- this.set_cookie('login', this.login);
- },
- logout: function() {
- delete this.uid;
- delete this.session_id;
- delete this.db;
- delete this.login;
- this.set_cookie('uid', '');
- this.set_cookie('session_id', '');
- this.set_cookie('db', '');
- this.set_cookie('login', '');
- this.on_session_invalid(function() {});
- },
- /**
- * Fetches a cookie stored by an openerp session
- *
- * @private
- * @param name the cookie's name
- */
- get_cookie: function (name) {
- var nameEQ = this.element_id + '|' + 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) {
- ttl = ttl || 24*60*60*365;
- document.cookie = [
- this.element_id + '|' + name + '=' + encodeURIComponent(JSON.stringify(value)),
- '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;
- this.rpc('/base/session/modules', {}, function(result) {
- self.module_list = result;
- var modules = self.module_list.join(',');
- if(self.debug || true) {
- self.rpc('/base/webclient/csslist', {"mods": modules}, self.do_load_css);
- self.rpc('/base/webclient/jslist', {"mods": modules}, self.do_load_js);
- } else {
- self.do_load_css(["/base/webclient/css?mods="+modules]);
- self.do_load_js(["/base/webclient/js?mods="+modules]);
- }
- openerp._modules_loaded = true;
- });
- },
- do_load_css: function (files) {
- _.each(files, function (file) {
- $('head').append($('<link>', {
- 'href': file,
- 'rel': 'stylesheet',
- 'type': 'text/css'
- }));
- });
- },
- do_load_js: function(files) {
- var self = this;
- if(files.length != 0) {
- var file = files.shift();
- var tag = document.createElement('script');
- tag.type = 'text/javascript';
- tag.src = 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);
- };
- document.head.appendChild(tag);
- } else {
- this.on_modules_loaded();
- }
- },
- 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;
- openerp[mod] = {};
- // init module mod
- if(openerp._openerp[mod] != undefined) {
- openerp._openerp[mod](openerp);
- this.module_loaded[mod] = true;
- }
- }
- }
-});
-
openerp.base.Notification = openerp.base.Widget.extend({
init: function(parent, element_id) {
this._super(parent, element_id);
@@ -307,10 +29,10 @@ openerp.base.Notification = openerp.base.Widget.extend({
openerp.base.Dialog = openerp.base.OldWidget.extend({
dialog_title: "",
identifier_prefix: 'dialog',
- init: function (parent, options) {
+ init: function (parent, dialog_options) {
var self = this;
this._super(parent);
- this.options = {
+ this.dialog_options = {
modal: true,
width: 'auto',
min_width: 0,
@@ -320,27 +42,25 @@ openerp.base.Dialog = openerp.base.OldWidget.extend({
max_height: '100%',
autoOpen: false,
buttons: {},
- beforeClose: function () {
- self.on_close();
- }
+ beforeClose: function () { self.on_close(); }
};
for (var f in this) {
if (f.substr(0, 10) == 'on_button_') {
- this.options.buttons[f.substr(10)] = this[f];
+ this.dialog_options.buttons[f.substr(10)] = this[f];
}
}
- if (options) {
- this.set_options(options);
+ if (dialog_options) {
+ this.set_options(dialog_options);
}
},
set_options: function(options) {
options = options || {};
- options.width = this.get_width(options.width || this.options.width);
- options.min_width = this.get_width(options.min_width || this.options.min_width);
- options.max_width = this.get_width(options.max_width || this.options.max_width);
- options.height = this.get_height(options.height || this.options.height);
- options.min_height = this.get_height(options.min_height || this.options.min_height);
- options.max_height = this.get_height(options.max_height || this.options.max_width);
+ options.width = this.get_width(options.width || this.dialog_options.width);
+ options.min_width = this.get_width(options.min_width || this.dialog_options.min_width);
+ options.max_width = this.get_width(options.max_width || this.dialog_options.max_width);
+ options.height = this.get_height(options.height || this.dialog_options.height);
+ options.min_height = this.get_height(options.min_height || this.dialog_options.min_height);
+ options.max_height = this.get_height(options.max_height || this.dialog_options.max_width);
if (options.width !== 'auto') {
if (options.width > options.max_width) options.width = options.max_width;
@@ -353,7 +73,7 @@ openerp.base.Dialog = openerp.base.OldWidget.extend({
if (!options.title && this.dialog_title) {
options.title = this.dialog_title;
}
- _.extend(this.options, options);
+ _.extend(this.dialog_options, options);
},
get_width: function(val) {
return this.get_size(val.toString(), $(window.top).width());
@@ -370,20 +90,18 @@ openerp.base.Dialog = openerp.base.OldWidget.extend({
return parseInt(val, 10);
}
},
- start: function (auto_open) {
- this.$dialog = $('<div id="' + this.element_id + '"></div>').dialog(this.options);
- if (auto_open !== false) {
- this.open();
- }
+ start: function () {
+ this.$dialog = $('<div id="' + this.element_id + '"></div>').dialog(this.dialog_options);
this._super();
+ return this;
},
- open: function(options) {
+ open: function(dialog_options) {
// TODO fme: bind window on resize
if (this.template) {
this.$element.html(this.render());
}
- this.set_options(options);
- this.$dialog.dialog(this.options).dialog('open');
+ this.set_options(dialog_options);
+ this.$dialog.dialog(this.dialog_options).dialog('open');
},
close: function() {
// Closes the dialog but leave it in a state where it could be opened again.
@@ -913,7 +631,7 @@ openerp.base.Header = openerp.base.Widget.extend({
},
on_logout: function() {
- this.$element.find('#shortcuts ul li').remove();
+ this.$element.find('.oe-shortcuts ul').empty();
}
});
@@ -1005,12 +723,10 @@ openerp.base.Homepage = openerp.base.Widget.extend({
openerp.base.Preferences = openerp.base.Widget.extend({
});
-openerp.base.ImportExport = openerp.base.Widget.extend({
-});
-
openerp.base.WebClient = openerp.base.Widget.extend({
init: function(element_id) {
this._super(null, element_id);
+ openerp.webclient = this;
QWeb.add_template("/base/static/src/xml/base.xml");
var params = {};
@@ -1019,10 +735,10 @@ openerp.base.WebClient = openerp.base.Widget.extend({
}
this.$element.html(QWeb.render("Interface", params));
- this.session = new openerp.base.Session(this,"oe_errors");
+ this.session = new openerp.base.Session();
this.loading = new openerp.base.Loading(this,"oe_loading");
this.crashmanager = new openerp.base.CrashManager(this);
- this.crashmanager.start(false);
+ this.crashmanager.start();
// Do you autorize this ? will be replaced by notify() in controller
openerp.base.Widget.prototype.notification = new openerp.base.Notification(this, "oe_notification");
@@ -1038,8 +754,6 @@ openerp.base.WebClient = openerp.base.Widget.extend({
this.menu = new openerp.base.Menu(this, "oe_menu", "oe_secondary_menu");
this.menu.on_action.add(this.on_menu_action);
this.header.on_action.add(this.on_menu_action);
-
-
},
start: function() {
this.session.start();
@@ -1049,8 +763,10 @@ openerp.base.WebClient = openerp.base.Widget.extend({
this.notification.notify("OpenERP Client", "The openerp client has been initialized.");
},
on_logged: function() {
- this.action_manager = new openerp.base.ActionManager(this, "oe_app");
- this.action_manager.start();
+ if(this.action_manager)
+ this.action_manager.stop();
+ this.action_manager = new openerp.base.ActionManager(this);
+ this.action_manager.appendTo($("#oe_app"));
// if using saved actions, load the action and give it to action manager
var parameters = jQuery.deparam(jQuery.param.querystring());
@@ -1090,7 +806,8 @@ openerp.base.WebClient = openerp.base.Widget.extend({
self.execute_home_action(home_action[0], ds);
})
},
- default_home: function () { },
+ default_home: function () {
+ },
/**
* Bundles the execution of the home action
*
diff --git a/addons/base/static/src/js/core.js b/addons/base/static/src/js/core.js
index 80dba3cd10c..96c6cb1372a 100644
--- a/addons/base/static/src/js/core.js
+++ b/addons/base/static/src/js/core.js
@@ -1,5 +1,5 @@
/*---------------------------------------------------------
- * OpenERP controller framework
+ * OpenERP Web core
*--------------------------------------------------------*/
openerp.base.core = function(openerp) {
@@ -305,16 +305,8 @@ openerp.base.Registry = openerp.base.Class.extend( /** @lends openerp.base.Regis
}
});
-/**
- * Utility class that any class is allowed to extend to easy common manipulations.
- *
- * It provides rpc calls, callback on all methods preceded by "on_" or "do_" and a
- * logging facility.
- */
-openerp.base.SessionAware = openerp.base.Class.extend({
- init: function(session) {
- this.session = session;
-
+openerp.base.CallbackEnabled = openerp.base.Class.extend({
+ init: function() {
// Transform on_* method into openerp.base.callbacks
for (var name in this) {
if(typeof(this[name]) == "function") {
@@ -325,6 +317,19 @@ openerp.base.SessionAware = openerp.base.Class.extend({
}
}
}
+ }
+});
+
+/**
+ * Utility class that any class is allowed to extend to easy common manipulations.
+ *
+ * It provides rpc calls, callback on all methods preceded by "on_" or "do_" and a
+ * logging facility.
+ */
+openerp.base.SessionAware = openerp.base.CallbackEnabled.extend({
+ init: function(session) {
+ this._super();
+ this.session = session;
},
/**
* Performs a JSON-RPC call
@@ -373,59 +378,6 @@ openerp.base.SessionAware = openerp.base.Class.extend({
}
});
-/**
- * Base class for all visual components. Provides a lot of functionalities helpful
- * for the management of a part of the DOM.
- *
- * Widget handles:
- * - Rendering with QWeb.
- * - Life-cycle management and parenting (when a parent is destroyed, all its children are
- * destroyed too).
- * - Insertion in DOM.
- *
- * Widget also extends SessionAware for ease of use.
- *
- * Guide to create implementations of the Widget class:
- * ==============================================
- *
- * Here is a sample child class:
- *
- * MyWidget = openerp.base.Widget.extend({
- * // the name of the QWeb template to use for rendering
- * template: "MyQWebTemplate",
- * // identifier prefix, it is useful to put an obvious one for debugging
- * identifier_prefix: 'my-id-prefix-',
- *
- * init: function(parent) {
- * this._super(parent);
- * // stuff that you want to init before the rendering
- * },
- * start: function() {
- * this._super();
- * // stuff you want to make after the rendering, `this.$element` holds a correct value
- * this.$element.find(".my_button").click(/* an example of event binding * /);
- *
- * // if you have some asynchronous operations, it's a good idea to return
- * // a promise in start()
- * var promise = this.rpc(...);
- * return promise;
- * }
- * });
- *
- * Now this class can simply be used with the following syntax:
- *
- * var my_widget = new MyWidget(this);
- * my_widget.appendTo($(".some-div"));
- *
- * With these two lines, the MyWidget instance was inited, rendered, it was inserted into the
- * DOM inside the ".some-div" div and its events were binded.
- *
- * And of course, when you don't need that widget anymore, just do:
- *
- * my_widget.stop();
- *
- * That will kill the widget in a clean way and erase its content from the dom.
- */
openerp.base.Widget = openerp.base.SessionAware.extend({
/**
* The name of the QWeb template that will be used for rendering. Must be
@@ -574,6 +526,18 @@ openerp.base.Widget = openerp.base.SessionAware.extend({
}
return false;
},
+ do_notify: function() {
+ if (this.widget_parent) {
+ return this.widget_parent.do_notify.apply(this,arguments);
+ }
+ return false;
+ },
+ do_warn: function() {
+ if (this.widget_parent) {
+ return this.widget_parent.do_warn.apply(this,arguments);
+ }
+ return false;
+ },
rpc: function(url, data, success, error) {
var def = $.Deferred().then(success, error);
var self = this;
@@ -599,5 +563,345 @@ openerp.base.OldWidget = openerp.base.Widget.extend({
}
});
+openerp.base.TranslationDataBase = openerp.base.Class.extend({
+ 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).sort();
+ if (_.include(modules, "base")) {
+ modules = ["base"].concat(_.without(modules, "base"));
+ }
+ _.each(modules, function(name) {
+ self.add_module_translation(translation_bundle.modules[name]);
+ });
+ if (translation_bundle.lang_parameters) {
+ this.parameters = translation_bundle.lang_parameters;
+ }
+ },
+ add_module_translation: function(mod) {
+ var self = this;
+ _.each(mod.messages, function(message) {
+ if (self.db[message.id] === undefined) {
+ 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;
+ }
+});
+
+openerp.base._t = new openerp.base.TranslationDataBase().build_translation_function();
+
+openerp.base.Session = openerp.base.CallbackEnabled.extend( /** @lends openerp.base.Session# */{
+ /**
+ * @constructs
+ * @param element_id to use for exception reporting
+ * @param server
+ * @param port
+ */
+ init: function(server, port) {
+ this._super();
+ this.server = (server == undefined) ? location.hostname : server;
+ this.port = (port == undefined) ? location.port : port;
+ this.rpc_mode = (server == location.hostname) ? "ajax" : "jsonp";
+ this.debug = true;
+ this.db = "";
+ this.login = "";
+ this.password = "";
+ this.user_context= {};
+ this.uid = false;
+ this.session_id = false;
+ this.module_list = [];
+ this.module_loaded = {"base": true};
+ this.context = {};
+ this.shortcuts = [];
+ this.active_id = null;
+ },
+ start: function() {
+ this.session_restore();
+ },
+ /**
+ * 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 {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, success_callback, error_callback) {
+ var self = this;
+ // Construct a JSON-RPC2 request, method is currently unused
+ params.session_id = this.session_id;
+
+ // Call using the rpc_mode
+ var deferred = $.Deferred();
+ this.rpc_ajax(url, {
+ jsonrpc: "2.0",
+ method: "call",
+ params: params,
+ id:null
+ }).then(function () {deferred.resolve.apply(deferred, arguments);},
+ function(error) {deferred.reject(error, $.Event());});
+ return deferred.fail(function() {
+ deferred.fail(function(error, event) {
+ if (!event.isDefaultPrevented()) {
+ self.on_rpc_error(error, event);
+ }
+ });
+ }).then(success_callback, error_callback).promise();
+ },
+ /**
+ * Raw JSON-RPC call
+ *
+ * @returns {jQuery.Deferred} ajax-based deferred object
+ */
+ rpc_ajax: function(url, payload) {
+ var self = this;
+ this.on_rpc_request();
+ // url can be an $.ajax option object
+ if (_.isString(url)) {
+ url = {
+ url: url
+ }
+ }
+ var ajax = _.extend({
+ type: "POST",
+ url: url,
+ dataType: 'json',
+ contentType: 'application/json',
+ data: JSON.stringify(payload),
+ processData: false
+ }, url);
+ var deferred = $.Deferred();
+ $.ajax(ajax).done(function(response, textStatus, jqXHR) {
+ self.on_rpc_response();
+ if (!response.error) {
+ deferred.resolve(response["result"], textStatus, jqXHR);
+ return;
+ }
+ if (response.error.data.type !== "session_invalid") {
+ deferred.reject(response.error);
+ return;
+ }
+ self.uid = false;
+ self.on_session_invalid(function() {
+ self.rpc(url, payload.params,
+ function() {
+ deferred.resolve.apply(deferred, arguments);
+ },
+ function(error, event) {
+ event.preventDefault();
+ deferred.reject.apply(deferred, arguments);
+ });
+ });
+ }).fail(function(jqXHR, textStatus, errorThrown) {
+ self.on_rpc_response();
+ var error = {
+ code: -32098,
+ message: "XmlHttpRequestError " + errorThrown,
+ data: {type: "xhr"+textStatus, debug: jqXHR.responseText, objects: [jqXHR, errorThrown] }
+ };
+ deferred.reject(error);
+ });
+ return deferred.promise();
+ },
+ on_rpc_request: function() {
+ },
+ on_rpc_response: function() {
+ },
+ on_rpc_error: function(error) {
+ },
+ /**
+ * The session is validated either by login or by restoration of a previous session
+ */
+ on_session_valid: function() {
+ if(!openerp._modules_loaded)
+ this.load_modules();
+ },
+ on_session_invalid: function(contination) {
+ },
+ session_is_valid: function() {
+ return this.uid;
+ },
+ session_login: function(db, login, password, success_callback) {
+ var self = this;
+ this.db = db;
+ this.login = login;
+ this.password = password;
+ var params = { db: this.db, login: this.login, password: this.password };
+ this.rpc("/base/session/login", params, function(result) {
+ self.session_id = result.session_id;
+ self.uid = result.uid;
+ self.user_context = result.context;
+ self.session_save();
+ self.on_session_valid();
+ if (success_callback)
+ success_callback();
+ });
+ },
+ session_logout: function() {
+ this.uid = false;
+ },
+ /**
+ * Reloads uid and session_id from local storage, if they exist
+ */
+ session_restore: function () {
+ this.uid = this.get_cookie('uid');
+ this.session_id = this.get_cookie('session_id');
+ this.db = this.get_cookie('db');
+ this.login = this.get_cookie('login');
+ this.user_context = this.get_cookie("user_context");
+ // we should do an rpc to confirm that this session_id is valid and if it is retrieve the information about db and login
+ // then call on_session_valid
+ this.on_session_valid();
+ },
+ /**
+ * Saves the session id and uid locally
+ */
+ session_save: function () {
+ this.set_cookie('uid', this.uid);
+ this.set_cookie('session_id', this.session_id);
+ this.set_cookie('db', this.db);
+ this.set_cookie('login', this.login);
+ this.set_cookie('user_context', this.user_context);
+ },
+ logout: function() {
+ delete this.uid;
+ delete this.session_id;
+ delete this.db;
+ delete this.login;
+ this.set_cookie('uid', '');
+ this.set_cookie('session_id', '');
+ this.set_cookie('db', '');
+ this.set_cookie('login', '');
+ this.on_session_invalid(function() {});
+ },
+ /**
+ * Fetches a cookie stored by an openerp session
+ *
+ * @private
+ * @param name the cookie's name
+ */
+ get_cookie: function (name) {
+ var nameEQ = this.element_id + '|' + 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) {
+ ttl = ttl || 24*60*60*365;
+ document.cookie = [
+ this.element_id + '|' + name + '=' + encodeURIComponent(JSON.stringify(value)),
+ '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;
+ this.rpc('/base/session/modules', {}, function(result) {
+ self.module_list = result;
+ var lang = self.user_context.lang;
+ self.rpc('/base/webclient/translations',{
+ mods: ["base"].concat(result),
+ lang: lang})
+ .then(function(transs) {
+ openerp.base._t.database.set_bundle(transs);
+ var modules = self.module_list.join(',');
+ if(self.debug || true) {
+ self.rpc('/base/webclient/csslist', {"mods": modules}, self.do_load_css);
+ self.rpc('/base/webclient/jslist', {"mods": modules}, self.do_load_js);
+ } else {
+ self.do_load_css(["/base/webclient/css?mods="+modules]);
+ self.do_load_js(["/base/webclient/js?mods="+modules]);
+ }
+ openerp._modules_loaded = true;
+ });
+ });
+ },
+ do_load_css: function (files) {
+ _.each(files, function (file) {
+ $('head').append($('<link>', {
+ 'href': file,
+ 'rel': 'stylesheet',
+ 'type': 'text/css'
+ }));
+ });
+ },
+ do_load_js: function(files) {
+ var self = this;
+ if(files.length != 0) {
+ var file = files.shift();
+ var tag = document.createElement('script');
+ tag.type = 'text/javascript';
+ tag.src = 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);
+ };
+ document.head.appendChild(tag);
+ } else {
+ this.on_modules_loaded();
+ }
+ },
+ 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;
+ openerp[mod] = {};
+ // init module mod
+ if(openerp._openerp[mod] != undefined) {
+ openerp._openerp[mod](openerp);
+ this.module_loaded[mod] = true;
+ }
+ }
+ }
+});
+
};
+
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:
diff --git a/addons/base/static/src/js/data.js b/addons/base/static/src/js/data.js
index fb926a2a592..244c057d544 100644
--- a/addons/base/static/src/js/data.js
+++ b/addons/base/static/src/js/data.js
@@ -268,9 +268,13 @@ openerp.base.DataSet = openerp.base.Widget.extend( /** @lends openerp.base.Data
},
/**
* Read records.
+ *
+ * @param {Array} ids identifiers of the records to read
+ * @param {Array} fields fields to read and return, by default all fields are returned
+ * @param {Function} callback function called with read result
+ * @returns {$.Deferred}
*/
read_ids: function (ids, fields, callback) {
- var self = this;
return this.rpc('/base/dataset/get', {
model: this.model,
ids: ids,
@@ -282,13 +286,20 @@ openerp.base.DataSet = openerp.base.Widget.extend( /** @lends openerp.base.Data
* Read a slice of the records represented by this DataSet, based on its
* domain and context.
*
- * @param {Number} [offset=0] The index from which selected records should be returned
- * @param {Number} [limit=null] The maximum number of records to return
+ * @params {Object} options
+ * @param {Array} [options.fields] fields to read and return, by default all fields are returned
+ * @param {Number} [options.offset=0] The index from which selected records should be returned
+ * @param {Number} [options.limit=null] The maximum number of records to return
+ * @param {Function} callback function called with read_slice result
+ * @returns {$.Deferred}
*/
- read_slice: function (fields, offset, limit, callback) {
- },
+ read_slice: function (options, callback) { return null; },
/**
- * Read the indexed record.
+ * Reads the current dataset record (from its index)
+ *
+ * @params {Array} [fields] fields to read and return, by default all fields are returned
+ * @params {Function} callback function called with read_index result
+ * @returns {$.Deferred}
*/
read_index: function (fields, callback) {
var def = $.Deferred().then(callback);
@@ -304,6 +315,13 @@ openerp.base.DataSet = openerp.base.Widget.extend( /** @lends openerp.base.Data
}
return def.promise();
},
+ /**
+ * Reads default values for the current model
+ *
+ * @param {Array} [fields] fields to get default values for, by default all defaults are read
+ * @param {Function} callback function called with default_get result
+ * @returns {$.Deferred}
+ */
default_get: function(fields, callback) {
return this.rpc('/base/dataset/default_get', {
model: this.model,
@@ -311,6 +329,14 @@ openerp.base.DataSet = openerp.base.Widget.extend( /** @lends openerp.base.Data
context: this.get_context()
}, callback);
},
+ /**
+ * Creates a new record in db
+ *
+ * @param {Object} data field values to set on the new record
+ * @param {Function} callback function called with operation result
+ * @param {Function} error_callback function called in case of creation error
+ * @returns {$.Deferred}
+ */
create: function(data, callback, error_callback) {
return this.rpc('/base/dataset/create', {
model: this.model,
@@ -318,6 +344,15 @@ openerp.base.DataSet = openerp.base.Widget.extend( /** @lends openerp.base.Data
context: this.get_context()
}, callback, error_callback);
},
+ /**
+ * Saves the provided data in an existing db record
+ *
+ * @param {Number|String} id identifier for the record to alter
+ * @param {Object} data field values to write into the record
+ * @param {Function} callback function called with operation result
+ * @param {Function} error_callback function called in case of write error
+ * @returns {$.Deferred}
+ */
write: function (id, data, callback, error_callback) {
return this.rpc('/base/dataset/save', {
model: this.model,
@@ -326,11 +361,27 @@ openerp.base.DataSet = openerp.base.Widget.extend( /** @lends openerp.base.Data
context: this.get_context()
}, callback, error_callback);
},
+ /**
+ * Deletes an existing record from the database
+ *
+ * @param {Number|String} ids identifier of the record to delete
+ * @param {Function} callback function called with operation result
+ * @param {Function} error_callback function called in case of deletion error
+ */
unlink: function(ids, callback, error_callback) {
var self = this;
return this.call_and_eval("unlink", [ids, this.get_context()], null, 1,
callback, error_callback);
},
+ /**
+ * Calls an arbitrary RPC method
+ *
+ * @param {String} method name of the method (on the current model) to call
+ * @param {Array} [args] arguments to pass to the method
+ * @param {Function} callback
+ * @param {Function} error_callback
+ * @returns {$.Deferred}
+ */
call: function (method, args, callback, error_callback) {
return this.rpc('/base/dataset/call', {
model: this.model,
@@ -338,6 +389,17 @@ openerp.base.DataSet = openerp.base.Widget.extend( /** @lends openerp.base.Data
args: args || []
}, callback, error_callback);
},
+ /**
+ * Calls an arbitrary method, with more crazy
+ *
+ * @param {String} method
+ * @param {Array} [args]
+ * @param {Number} [domain_id] index of a domain to evaluate in the args array
+ * @param {Number} [context_id] index of a context to evaluate in the args array
+ * @param {Function} callback
+ * @param {Function }error_callback
+ * @returns {$.Deferred}
+ */
call_and_eval: function (method, args, domain_id, context_id, callback, error_callback) {
return this.rpc('/base/dataset/call', {
model: this.model,
@@ -347,6 +409,15 @@ openerp.base.DataSet = openerp.base.Widget.extend( /** @lends openerp.base.Data
args: args || []
}, callback, error_callback);
},
+ /**
+ * Calls a button method, usually returning some sort of action
+ *
+ * @param {String} method
+ * @param {Array} [args]
+ * @param {Function} callback
+ * @param {Function} error_callback
+ * @returns {$.Deferred}
+ */
call_button: function (method, args, callback, error_callback) {
return this.rpc('/base/dataset/call_button', {
model: this.model,
@@ -356,17 +427,34 @@ openerp.base.DataSet = openerp.base.Widget.extend( /** @lends openerp.base.Data
args: args || []
}, callback, error_callback);
},
+ /**
+ * Fetches the "readable name" for records, based on intrinsic rules
+ *
+ * @param {Array} ids
+ * @param {Function} callback
+ * @returns {$.Deferred}
+ */
name_get: function(ids, callback) {
return this.call_and_eval('name_get', [ids, this.get_context()], null, 1, callback);
},
- /*
- * args = domain
+ /**
+ *
+ * @param {String} name name to perform a search for/on
+ * @param {Array} [domain=[]] filters for the objects returned, OpenERP domain
+ * @param {String} [operator='ilike'] matching operator to use with the provided name value
+ * @param {Number} [limit=100] maximum number of matches to return
+ * @param {Function} callback function to call with name_search result
+ * @returns {$.Deferred}
*/
- name_search: function (name, args, operator, limit, callback) {
+ name_search: function (name, domain, operator, limit, callback) {
return this.call_and_eval('name_search',
- [name || '', args || false, operator || 'ilike', this.get_context(), limit || 100],
+ [name || '', domain || false, operator || 'ilike', this.get_context(), limit || 100],
1, 3, callback);
},
+ /**
+ * @param name
+ * @param callback
+ */
name_create: function(name, callback) {
return this.call_and_eval('name_create', [name, this.get_context()], null, 1, callback);
},
@@ -377,7 +465,10 @@ openerp.base.DataSet = openerp.base.Widget.extend( /** @lends openerp.base.Data
signal: signal
}, callback);
},
- get_context: function() {
+ get_context: function(request_context) {
+ if (request_context) {
+ return new openerp.base.CompoundContext(this.context, request_context);
+ }
return this.context;
}
});
@@ -387,9 +478,11 @@ openerp.base.DataSetStatic = openerp.base.DataSet.extend({
// all local records
this.ids = ids || [];
},
- read_slice: function (fields, offset, limit, callback) {
- var self = this;
- offset = offset || 0;
+ read_slice: function (options, callback) {
+ var self = this,
+ offset = options.offset || 0,
+ limit = options.limit || false,
+ fields = options.fields || false;
var end_pos = limit && limit !== -1 ? offset + limit : undefined;
return this.read_ids(this.ids.slice(offset, end_pos), fields, callback);
},
@@ -426,25 +519,30 @@ openerp.base.DataSetSearch = openerp.base.DataSet.extend({
// is it necessary ?
this.ids = [];
},
- read_slice: function (fields, offset, limit, callback) {
+ /**
+ * Read a slice of the records represented by this DataSet, based on its
+ * domain and context.
+ *
+ * @params {Object} options
+ * @param {Array} [options.fields] fields to read and return, by default all fields are returned
+ * @param {Object} [options.context] context data to add to the request payload, on top of the DataSet's own context
+ * @param {Array} [options.domain] domain data to add to the request payload, ANDed with the dataset's domain
+ * @param {Number} [options.offset=0] The index from which selected records should be returned
+ * @param {Number} [options.limit=null] The maximum number of records to return
+ * @param {Function} callback function called with read_slice result
+ * @returns {$.Deferred}
+ */
+ read_slice: function (options, callback) {
var self = this;
- offset = offset || 0;
- // cached search, not sure it's a good idea
- if(this.offset <= offset) {
- var start = offset - this.offset;
- if(this.ids.length - start >= limit) {
- // TODO: check if this could work do only read if possible
- // return read_ids(ids.slice(start,start+limit),fields,callback)
- }
- }
+ var offset = options.offset || 0;
return this.rpc('/base/dataset/search_read', {
model: this.model,
- fields: fields,
- domain: this.domain,
- context: this.get_context(),
+ fields: options.fields || false,
+ domain: this.get_domain(options.domain),
+ context: this.get_context(options.context),
sort: this.sort(),
offset: offset,
- limit: limit
+ limit: options.limit || false
}, function (result) {
self.ids = result.ids;
self.offset = offset;
@@ -453,6 +551,12 @@ openerp.base.DataSetSearch = openerp.base.DataSet.extend({
}
});
},
+ get_domain: function (other_domain) {
+ if (other_domain) {
+ return new openerp.base.CompoundDomain(this.domain, other_domain);
+ }
+ return this.domain;
+ },
/**
* Reads or changes sort criteria on the dataset.
*
@@ -628,48 +732,51 @@ openerp.base.ReadOnlyDataSetSearch = openerp.base.DataSetSearch.extend({
on_unlink: function(ids) {}
});
-openerp.base.CompoundContext = function() {
- this.__ref = "compound_context";
- this.__contexts = [];
- this.__eval_context = null;
- var self = this;
- _.each(arguments, function(x) {
- self.add(x);
- });
-};
-openerp.base.CompoundContext.prototype.add = function(context) {
- this.__contexts.push(context);
- return this;
-};
-openerp.base.CompoundContext.prototype.set_eval_context = function(eval_context) {
- this.__eval_context = eval_context;
- return this;
-};
-openerp.base.CompoundContext.prototype.get_eval_context = function() {
- return this.__eval_context;
-};
-
-openerp.base.CompoundDomain = function() {
- this.__ref = "compound_domain";
- this.__domains = [];
- this.__eval_context = null;
- var self = this;
- _.each(arguments, function(x) {
- self.add(x);
- });
-};
-openerp.base.CompoundDomain.prototype.add = function(domain) {
- this.__domains.push(domain);
- return this;
-};
-openerp.base.CompoundDomain.prototype.set_eval_context = function(eval_context) {
- this.__eval_context = eval_context;
- return this;
-};
-openerp.base.CompoundDomain.prototype.get_eval_context = function() {
- return this.__eval_context;
-};
+openerp.base.CompoundContext = openerp.base.Class.extend({
+ init: function () {
+ this.__ref = "compound_context";
+ this.__contexts = [];
+ this.__eval_context = null;
+ var self = this;
+ _.each(arguments, function(x) {
+ self.add(x);
+ });
+ },
+ add: function (context) {
+ this.__contexts.push(context);
+ return this;
+ },
+ set_eval_context: function (eval_context) {
+ this.__eval_context = eval_context;
+ return this;
+ },
+ get_eval_context: function () {
+ return this.__eval_context;
+ }
+});
+openerp.base.CompoundDomain = openerp.base.Class.extend({
+ init: function () {
+ this.__ref = "compound_domain";
+ this.__domains = [];
+ this.__eval_context = null;
+ var self = this;
+ _.each(arguments, function(x) {
+ self.add(x);
+ });
+ },
+ add: function(domain) {
+ this.__domains.push(domain);
+ return this;
+ },
+ set_eval_context: function(eval_context) {
+ this.__eval_context = eval_context;
+ return this;
+ },
+ get_eval_context: function() {
+ return this.__eval_context;
+ }
+});
};
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:
diff --git a/addons/base/static/src/js/dates.js b/addons/base/static/src/js/dates.js
deleted file mode 100644
index 8daea39773f..00000000000
--- a/addons/base/static/src/js/dates.js
+++ /dev/null
@@ -1,137 +0,0 @@
-
-openerp.base.dates = function(openerp) {
-
-/**
- * Converts a string to a Date javascript object using OpenERP's
- * datetime string format (exemple: '2011-12-01 15:12:35').
- *
- * The timezone is assumed to be UTC (standard for OpenERP 6.1)
- * and will be converted to the browser's timezone.
- *
- * @param {String} str A string representing a datetime.
- * @returns {Date}
- */
-openerp.base.parse_datetime = function(str) {
- if(!str) {
- return str;
- }
- var regex = /\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d/;
- var res = regex.exec(str);
- if ( res[0] != str ) {
- throw "'" + str + "' is not a valid datetime";
- }
- var obj = Date.parse(str + " GMT");
- if (! obj) {
- throw "'" + str + "' is not a valid datetime";
- }
- return obj;
-};
-
-/**
- * Converts a string to a Date javascript object using OpenERP's
- * date string format (exemple: '2011-12-01').
- *
- * @param {String} str A string representing a date.
- * @returns {Date}
- */
-openerp.base.parse_date = function(str) {
- if(!str) {
- return str;
- }
- var regex = /\d\d\d\d-\d\d-\d\d/;
- var res = regex.exec(str);
- if ( res[0] != str ) {
- throw "'" + str + "' is not a valid date";
- }
- var obj = Date.parse(str);
- if (! obj) {
- throw "'" + str + "' is not a valid date";
- }
- return obj;
-};
-
-/**
- * Converts a string to a Date javascript object using OpenERP's
- * time string format (exemple: '15:12:35').
- *
- * @param {String} str A string representing a time.
- * @returns {Date}
- */
-openerp.base.parse_time = function(str) {
- if(!str) {
- return str;
- }
- var regex = /\d\d:\d\d:\d\d/;
- var res = regex.exec(str);
- if ( res[0] != str ) {
- throw "'" + str + "' is not a valid time";
- }
- var obj = Date.parse(str);
- if (! obj) {
- throw "'" + str + "' is not a valid time";
- }
- return obj;
-};
-
-/*
- * Just a simple function to add some '0' if an integer it too small.
- */
-var fts = function(str, size) {
- str = "" + str;
- var to_add = "";
- _.each(_.range(size - str.length), function() {
- to_add = to_add + "0";
- });
- return to_add + str;
-};
-
-/**
- * Converts a Date javascript object to a string using OpenERP's
- * datetime string format (exemple: '2011-12-01 15:12:35').
- *
- * The timezone of the Date object is assumed to be the one of the
- * browser and it will be converted to UTC (standard for OpenERP 6.1).
- *
- * @param {Date} obj
- * @returns {String} A string representing a datetime.
- */
-openerp.base.format_datetime = function(obj) {
- if (!obj) {
- return false;
- }
- return fts(obj.getUTCFullYear(),4) + "-" + fts(obj.getUTCMonth() + 1,2) + "-"
- + fts(obj.getUTCDate(),2) + " " + fts(obj.getUTCHours(),2) + ":"
- + fts(obj.getUTCMinutes(),2) + ":" + fts(obj.getUTCSeconds(),2);
-};
-
-/**
- * Converts a Date javascript object to a string using OpenERP's
- * date string format (exemple: '2011-12-01').
- *
- * @param {Date} obj
- * @returns {String} A string representing a date.
- */
-openerp.base.format_date = function(obj) {
- if (!obj) {
- return false;
- }
- return fts(obj.getFullYear(),4) + "-" + fts(obj.getMonth() + 1,2) + "-"
- + fts(obj.getDate(),2);
-};
-
-/**
- * Converts a Date javascript object to a string using OpenERP's
- * time string format (exemple: '15:12:35').
- *
- * @param {Date} obj
- * @returns {String} A string representing a time.
- */
-openerp.base.format_time = function(obj) {
- if (!obj) {
- return false;
- }
- return fts(obj.getHours(),2) + ":" + fts(obj.getMinutes(),2) + ":"
- + fts(obj.getSeconds(),2);
-};
-
-};
diff --git a/addons/base/static/src/js/form.js b/addons/base/static/src/js/form.js
index 628567b42ef..6eeff025e1f 100644
--- a/addons/base/static/src/js/form.js
+++ b/addons/base/static/src/js/form.js
@@ -1,4 +1,6 @@
openerp.base.form = function (openerp) {
+
+var _t = openerp.base._t;
openerp.base.views.add('form', 'openerp.base.FormView');
openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormView# */{
@@ -19,7 +21,7 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormVi
*/
init: function(parent, element_id, dataset, view_id, options) {
this._super(parent, element_id);
- this.set_default_options();
+ this.set_default_options(options);
this.dataset = dataset;
this.model = dataset.model;
this.view_id = view_id;
@@ -36,7 +38,6 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormVi
this.registry = openerp.base.form.widgets;
this.has_been_loaded = $.Deferred();
this.$form_header = null;
- this.options = options || {};
_.defaults(this.options, {"always_show_new_button": true});
},
start: function() {
@@ -156,7 +157,7 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormVi
if (this.sidebar) {
this.sidebar.attachments.do_update();
}
- if (this.default_focus_field) {
+ if (this.default_focus_field && !this.embedded_view) {
this.default_focus_field.focus();
}
},
@@ -452,12 +453,14 @@ openerp.base.form.SidebarAttachments = openerp.base.Widget.extend({
this.on_attachments_loaded([]);
} else {
(new openerp.base.DataSetSearch(
- this, 'ir.attachment', this.view.dataset.get_context(),
- [['res_model', '=', this.view.dataset.model],
- ['res_id', '=', this.view.datarecord.id],
- ['type', 'in', ['binary', 'url']]])).read_slice(
- ['name', 'url', 'type'], false, false,
- this.on_attachments_loaded);
+ this, 'ir.attachment', this.view.dataset.get_context(),
+ [
+ ['res_model', '=', this.view.dataset.model],
+ ['res_id', '=', this.view.datarecord.id],
+ ['type', 'in', ['binary', 'url']]
+ ])).read_slice(
+ {fields: ['name', 'url', 'type']},
+ this.on_attachments_loaded);
}
},
on_attachments_loaded: function(attachments) {
@@ -511,36 +514,40 @@ openerp.base.form.compute_domain = function(expr, fields) {
}
}
- var field = fields[ex[0]].get_value ? fields[ex[0]].get_value() : fields[ex[0]].value;
+ var field = fields[ex[0]];
+ if (!field) {
+ throw new Error("Domain references unknown field : " + ex[0]);
+ }
+ var field_value = field.get_value ? fields[ex[0]].get_value() : fields[ex[0]].value;
var op = ex[1];
var val = ex[2];
switch (op.toLowerCase()) {
case '=':
case '==':
- stack.push(field == val);
+ stack.push(field_value == val);
break;
case '!=':
case '<>':
- stack.push(field != val);
+ stack.push(field_value != val);
break;
case '<':
- stack.push(field < val);
+ stack.push(field_value < val);
break;
case '>':
- stack.push(field > val);
+ stack.push(field_value > val);
break;
case '<=':
- stack.push(field <= val);
+ stack.push(field_value <= val);
break;
case '>=':
- stack.push(field >= val);
+ stack.push(field_value >= val);
break;
case 'in':
- stack.push(_(val).contains(field));
+ stack.push(_(val).contains(field_value));
break;
case 'not in':
- stack.push(!_(val).contains(field));
+ stack.push(!_(val).contains(field_value));
break;
default:
this.log("Unsupported operator in modifiers :", op);
@@ -679,6 +686,16 @@ openerp.base.form.WidgetNotebook = openerp.base.form.Widget.extend({
start: function() {
this._super.apply(this, arguments);
this.$element.tabs();
+ this.view.on_button_new.add_last(this.do_select_first_visible_tab);
+ },
+ do_select_first_visible_tab: function() {
+ for (var i = 0; i < this.pages.length; i++) {
+ var page = this.pages[i];
+ if (page.invisible === false) {
+ this.$element.tabs('select', page.index);
+ break;
+ }
+ }
}
});
@@ -696,8 +713,8 @@ openerp.base.form.WidgetNotebookPage = openerp.base.form.WidgetFrame.extend({
this.$element_tab = $('#' + this.element_tab_id);
},
update_dom: function() {
- if (this.invisible) {
- this.notebook.$element.tabs('select', 0);
+ if (this.invisible && this.index === this.notebook.$element.tabs('option', 'selected')) {
+ this.notebook.do_select_first_visible_tab();
}
this.$element_tab.toggle(!this.invisible);
this.$element.toggle(!this.invisible);
@@ -758,8 +775,7 @@ openerp.base.form.WidgetButton = openerp.base.form.Widget.extend({
var self = this;
this.view.execute_action(
- this.node.attrs, this.view.dataset, this.session.action_manager,
- this.view.datarecord.id, function () {
+ this.node.attrs, this.view.dataset, this.view.datarecord.id, function () {
self.view.reload();
});
}
@@ -794,7 +810,7 @@ openerp.base.form.WidgetLabel = openerp.base.form.Widget.extend({
var self = this;
this.$element.find("label").dblclick(function() {
var widget = self['for'] || self;
- console.log(widget.element_id , widget);
+ self.log(widget.element_id , widget);
});
}
});
@@ -1306,7 +1322,7 @@ openerp.base.form.dialog = function(content, options) {
}, options || {});
options.autoOpen = true;
var dialog = new openerp.base.Dialog(null, options);
- dialog.$dialog = $(content).dialog(dialog.options);
+ dialog.$dialog = $(content).dialog(dialog.dialog_options);
return dialog.$dialog;
}
@@ -1440,7 +1456,7 @@ openerp.base.form.FieldMany2One = openerp.base.form.Field.extend({
// search more... if more results that max
if (values.length > self.limit) {
values = values.slice(0, self.limit);
- values.push({label: "<em>   Search More...</em>", action: function() {
+ values.push({label: _t("<em>   Search More...</em>"), action: function() {
dataset.name_search(search_val, self.build_domain(), 'ilike'
, false, function(data) {
self._change_int_value(null);
@@ -1453,13 +1469,13 @@ openerp.base.form.FieldMany2One = openerp.base.form.Field.extend({
if (search_val.length > 0 &&
!_.include(raw_result, search_val) &&
(!self.value || search_val !== self.value[1])) {
- values.push({label: '<em>   Create "<strong>' +
- $('<span />').text(search_val).html() + '</strong>"</em>', action: function() {
+ values.push({label: _.sprintf(_t('<em>   Create "<strong>%s</strong>"</em>'),
+ $('<span />').text(search_val).html()), action: function() {
self._quick_create(search_val);
}});
}
// create...
- values.push({label: "<em>   Create and Edit...</em>", action: function() {
+ values.push({label: _t("<em>   Create and Edit...</em>"), action: function() {
self._change_int_value(null);
self._search_create_popup("form", undefined, {"default_name": search_val});
}});
@@ -1492,7 +1508,6 @@ openerp.base.form.FieldMany2One = openerp.base.form.Field.extend({
var dataset = new openerp.base.DataSetStatic(self, self.field.relation, self.build_context());
dataset.name_get([element_ids[0]], function(data) {
self._change_int_ext_value(data[0]);
- pop.stop();
});
});
},
@@ -1624,20 +1639,22 @@ openerp.base.form.FieldOne2Many = openerp.base.form.Field.extend({
modes = !!modes ? modes.split(",") : ["tree", "form"];
var views = [];
_.each(modes, function(mode) {
- var view = {view_id: false, view_type: mode == "tree" ? "list" : mode};
+ var view = {
+ view_id: false,
+ view_type: mode == "tree" ? "list" : mode,
+ options: { sidebar : false }
+ };
if (self.field.views && self.field.views[mode]) {
view.embedded_view = self.field.views[mode];
}
if(view.view_type === "list") {
- view.options = {
- 'selectable': self.multi_selection
- };
+ view.options.selectable = self.multi_selection;
}
views.push(view);
});
this.views = views;
- this.viewmanager = new openerp.base.ViewManager(this, this.element_id, this.dataset, views);
+ this.viewmanager = new openerp.base.ViewManager(this, this.dataset, views);
this.viewmanager.registry = openerp.base.views.clone({
list: 'openerp.base.form.One2ManyListView',
form: 'openerp.base.form.One2ManyFormView'
@@ -1663,7 +1680,7 @@ openerp.base.form.FieldOne2Many = openerp.base.form.Field.extend({
self.save_form_view();
});
setTimeout(function () {
- self.viewmanager.start();
+ self.viewmanager.appendTo(self.$element);
}, 0);
},
reload_current_view: function() {
@@ -1826,16 +1843,16 @@ openerp.base.form.One2ManyListView = openerp.base.ListView.extend({
pop.select_element(self.o2m.field.relation,{
initial_view: "form",
alternative_form_view: self.o2m.field.views ? self.o2m.field.views["form"] : undefined,
- auto_create: false,
+ create_function: function(data) {
+ return self.o2m.dataset.create(data, function(r) {
+ self.o2m.dataset.set_ids(self.o2m.dataset.ids.concat([r.result]));
+ self.o2m.dataset.on_change();
+ });
+ },
parent_view: self.o2m.view
}, self.o2m.build_domain(), self.o2m.build_context());
- pop.on_create.add(function(data) {
- self.o2m.dataset.create(data, function(r) {
- self.o2m.dataset.set_ids(self.o2m.dataset.ids.concat([r.result]));
- self.o2m.dataset.on_change();
- pop.stop();
- self.o2m.reload_current_view();
- });
+ pop.on_select_elements.add_last(function() {
+ self.o2m.reload_current_view();
});
}
},
@@ -1874,7 +1891,7 @@ openerp.base.form.FieldMany2Many = openerp.base.form.Field.extend({
self.on_ui_change();
});
- this.list_view = new openerp.base.form.Many2ManyListView(new openerp.base.NullViewManager(this), this.list_id, this.dataset, false, {
+ this.list_view = new openerp.base.form.Many2ManyListView(this, this.list_id, this.dataset, false, {
'addable': 'Add',
'selectable': self.multi_selection
});
@@ -1930,7 +1947,6 @@ openerp.base.form.Many2ManyListView = openerp.base.ListView.extend({
self.reload_content();
}
});
- pop.stop();
});
},
do_activate_record: function(index, id) {
@@ -1952,16 +1968,22 @@ openerp.base.form.SelectCreatePopup = openerp.base.OldWidget.extend({
* - initial_view: form or search (default search)
* - disable_multiple_selection
* - alternative_form_view
- * - auto_create (default true)
+ * - create_function (defaults to a naive saving behavior)
* - parent_view
*/
select_element: function(model, options, domain, context) {
+ var self = this;
this.model = model;
this.domain = domain || [];
this.context = context || {};
- this.options = _.defaults(options || {}, {"initial_view": "search", "auto_create": true});
+ this.options = _.defaults(options || {}, {"initial_view": "search", "create_function": function() {
+ return self.create_row.apply(self, arguments);
+ }});
this.initial_ids = this.options.initial_ids;
- openerp.base.form.dialog(this.render());
+ this.created_elements = [];
+ openerp.base.form.dialog(this.render(), {close:function() {
+ self.check_exit();
+ }});
this.start();
},
start: function() {
@@ -2007,6 +2029,7 @@ openerp.base.form.SelectCreatePopup = openerp.base.OldWidget.extend({
}
$sbutton.click(function() {
self.on_select_elements(self.selected_ids);
+ self.stop();
});
self.view_list = new openerp.base.form.SelectCreateListView(self,
self.element_id + "_view_list", self.dataset, false,
@@ -2019,15 +2042,11 @@ openerp.base.form.SelectCreatePopup = openerp.base.OldWidget.extend({
});
this.searchview.start();
},
- on_create: function(data) {
- if (!this.options.auto_create)
- return;
+ create_row: function(data) {
var self = this;
var wdataset = new openerp.base.DataSetSearch(this, this.model, this.context, this.domain);
wdataset.parent_view = this.options.parent_view;
- wdataset.create(data, function(r) {
- self.on_select_elements([r.result]);
- });
+ return wdataset.create(data);
},
on_select_elements: function(element_ids) {
},
@@ -2055,18 +2074,43 @@ openerp.base.form.SelectCreatePopup = openerp.base.OldWidget.extend({
this.view_form.start();
this.view_form.on_loaded.add_last(function() {
var $buttons = self.view_form.$element.find(".oe_form_buttons");
- $buttons.html(QWeb.render("SelectCreatePopup.form.buttons"));
+ $buttons.html(QWeb.render("SelectCreatePopup.form.buttons", {widget:self}));
+ var $nbutton = $buttons.find(".oe_selectcreatepopup-form-save-new");
+ $nbutton.click(function() {
+ self._created = $.Deferred().then(function() {
+ self._created = undefined;
+ self.view_form.on_button_new();
+ });
+ self.view_form.do_save();
+ });
var $nbutton = $buttons.find(".oe_selectcreatepopup-form-save");
$nbutton.click(function() {
+ self._created = $.Deferred().then(function() {
+ self._created = undefined;
+ self.check_exit();
+ });
self.view_form.do_save();
});
var $cbutton = $buttons.find(".oe_selectcreatepopup-form-close");
$cbutton.click(function() {
- self.stop();
+ self.check_exit();
+ });
+ });
+ this.dataset.on_create.add(function(data) {
+ self.options.create_function(data).then(function(r) {
+ self.created_elements.push(r.result);
+ if (self._created) {
+ self._created.resolve();
+ }
});
});
- this.dataset.on_create.add(this.on_create);
this.view_form.do_show();
+ },
+ check_exit: function() {
+ if (this.created_elements.length > 0) {
+ this.on_select_elements(this.created_elements);
+ }
+ this.stop();
}
});
@@ -2076,6 +2120,7 @@ openerp.base.form.SelectCreateListView = openerp.base.ListView.extend({
},
select_record: function(index) {
this.popup.on_select_elements([this.dataset.ids[index]]);
+ this.popup.stop();
},
do_select: function(ids, records) {
this._super(ids, records);
@@ -2199,8 +2244,8 @@ openerp.base.form.FieldBinary = openerp.base.form.Field.extend({
delete(window[this.iframe]);
if (size === false) {
this.notification.warn("File Upload", "There was a problem while uploading your file");
- // TODO: use openerp web exception handler
- console.log("Error while uploading file : ", name);
+ // TODO: use openerp web crashmanager
+ this.log("Error while uploading file : ", name);
} else {
this.on_file_uploaded_and_valid.apply(this, arguments);
this.on_ui_change();
@@ -2309,10 +2354,8 @@ openerp.base.form.widgets = new openerp.base.Registry({
'url' : 'openerp.base.form.FieldUrl',
'text' : 'openerp.base.form.FieldText',
'text_wiki' : 'openerp.base.form.FieldText',
-// 'date' : 'openerp.base.form.FieldDate',
-// 'datetime' : 'openerp.base.form.FieldDatetime',
- 'date' : 'openerp.base.form.FieldChar',
- 'datetime' : 'openerp.base.form.FieldChar',
+ 'date' : 'openerp.base.form.FieldDate',
+ 'datetime' : 'openerp.base.form.FieldDatetime',
'selection' : 'openerp.base.form.FieldSelection',
'many2one' : 'openerp.base.form.FieldMany2One',
'many2many' : 'openerp.base.form.FieldMany2Many',
diff --git a/addons/base/static/src/js/formats.js b/addons/base/static/src/js/formats.js
new file mode 100644
index 00000000000..074cabe5eee
--- /dev/null
+++ b/addons/base/static/src/js/formats.js
@@ -0,0 +1,208 @@
+
+openerp.base.formats = function(openerp) {
+
+/**
+ * Converts a string to a Date javascript object using OpenERP's
+ * datetime string format (exemple: '2011-12-01 15:12:35').
+ *
+ * The timezone is assumed to be UTC (standard for OpenERP 6.1)
+ * and will be converted to the browser's timezone.
+ *
+ * @param {String} str A string representing a datetime.
+ * @returns {Date}
+ */
+openerp.base.parse_datetime = function(str) {
+ if(!str) {
+ return str;
+ }
+ var regex = /\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d/;
+ var res = regex.exec(str);
+ if ( res[0] != str ) {
+ throw "'" + str + "' is not a valid datetime";
+ }
+ var obj = Date.parse(str + " GMT");
+ if (! obj) {
+ throw "'" + str + "' is not a valid datetime";
+ }
+ return obj;
+};
+
+/**
+ * Converts a string to a Date javascript object using OpenERP's
+ * date string format (exemple: '2011-12-01').
+ *
+ * @param {String} str A string representing a date.
+ * @returns {Date}
+ */
+openerp.base.parse_date = function(str) {
+ if(!str) {
+ return str;
+ }
+ var regex = /\d\d\d\d-\d\d-\d\d/;
+ var res = regex.exec(str);
+ if ( res[0] != str ) {
+ throw "'" + str + "' is not a valid date";
+ }
+ var obj = Date.parse(str);
+ if (! obj) {
+ throw "'" + str + "' is not a valid date";
+ }
+ return obj;
+};
+
+/**
+ * Converts a string to a Date javascript object using OpenERP's
+ * time string format (exemple: '15:12:35').
+ *
+ * @param {String} str A string representing a time.
+ * @returns {Date}
+ */
+openerp.base.parse_time = function(str) {
+ if(!str) {
+ return str;
+ }
+ var regex = /\d\d:\d\d:\d\d/;
+ var res = regex.exec(str);
+ if ( res[0] != str ) {
+ throw "'" + str + "' is not a valid time";
+ }
+ var obj = Date.parse(str);
+ if (! obj) {
+ throw "'" + str + "' is not a valid time";
+ }
+ return obj;
+};
+
+/*
+ * Left-pad provided arg 1 with zeroes until reaching size provided by second
+ * argument.
+ *
+ * @param {Number|String} str value to pad
+ * @param {Number} size size to reach on the final padded value
+ * @returns {String} padded string
+ */
+var zpad = function(str, size) {
+ str = "" + str;
+ return new Array(size - str.length + 1).join('0') + str;
+};
+
+/**
+ * Converts a Date javascript object to a string using OpenERP's
+ * datetime string format (exemple: '2011-12-01 15:12:35').
+ *
+ * The timezone of the Date object is assumed to be the one of the
+ * browser and it will be converted to UTC (standard for OpenERP 6.1).
+ *
+ * @param {Date} obj
+ * @returns {String} A string representing a datetime.
+ */
+openerp.base.format_datetime = function(obj) {
+ if (!obj) {
+ return false;
+ }
+ return zpad(obj.getUTCFullYear(),4) + "-" + zpad(obj.getUTCMonth() + 1,2) + "-"
+ + zpad(obj.getUTCDate(),2) + " " + zpad(obj.getUTCHours(),2) + ":"
+ + zpad(obj.getUTCMinutes(),2) + ":" + zpad(obj.getUTCSeconds(),2);
+};
+
+/**
+ * Converts a Date javascript object to a string using OpenERP's
+ * date string format (exemple: '2011-12-01').
+ *
+ * @param {Date} obj
+ * @returns {String} A string representing a date.
+ */
+openerp.base.format_date = function(obj) {
+ if (!obj) {
+ return false;
+ }
+ return zpad(obj.getFullYear(),4) + "-" + zpad(obj.getMonth() + 1,2) + "-"
+ + zpad(obj.getDate(),2);
+};
+
+/**
+ * Converts a Date javascript object to a string using OpenERP's
+ * time string format (exemple: '15:12:35').
+ *
+ * @param {Date} obj
+ * @returns {String} A string representing a time.
+ */
+openerp.base.format_time = function(obj) {
+ if (!obj) {
+ return false;
+ }
+ return zpad(obj.getHours(),2) + ":" + zpad(obj.getMinutes(),2) + ":"
+ + zpad(obj.getSeconds(),2);
+};
+
+/**
+ * Formats a single atomic value based on a field descriptor
+ *
+ * @param {Object} value read from OpenERP
+ * @param {Object} descriptor union of orm field and view field
+ * @param {Object} [descriptor.widget] widget to use to display the value
+ * @param {Object} descriptor.type fallback if no widget is provided, or if the provided widget is unknown
+ * @param {Object} [descriptor.digits] used for the formatting of floats
+ * @param {String} [value_if_empty=''] returned if the ``value`` argument is considered empty
+ */
+openerp.base.format_value = function (value, descriptor, value_if_empty) {
+ // If NaN value, display as with a `false` (empty cell)
+ if (typeof value === 'number' && isNaN(value)) {
+ value = false;
+ }
+ switch (value) {
+ case false:
+ case Infinity:
+ case -Infinity:
+ return value_if_empty === undefined ? '' : value_if_empty;
+ }
+ switch (descriptor.widget || descriptor.type) {
+ case 'integer':
+ return _.sprintf('%d', value);
+ case 'float':
+ var precision = descriptor.digits ? descriptor.digits[1] : 2;
+ return _.sprintf('%.' + precision + 'f', value);
+ case 'float_time':
+ return _.sprintf("%02d:%02d",
+ Math.floor(value),
+ Math.round((value % 1) * 60));
+ case 'progressbar':
+ return _.sprintf(
+ '<progress value="%.2f" max="100.0">%.2f%%</progress>',
+ value, value);
+ case 'many2one':
+ // name_get value format
+ return value[1];
+ default:
+ return value;
+ }
+};
+
+/**
+ * Formats a provided cell based on its field type
+ *
+ * @param {Object} row_data record whose values should be displayed in the cell
+ * @param {Object} column column descriptor
+ * @param {"button"|"field"} column.tag base control type
+ * @param {String} column.type widget type for a field control
+ * @param {String} [column.string] button label
+ * @param {String} [column.icon] button icon
+ * @param {String} [value_if_empty=''] what to display if the field's value is ``false``
+ */
+openerp.base.format_cell = function (row_data, column, value_if_empty) {
+ var attrs = column.modifiers_for(row_data);
+ if (attrs.invisible) { return ''; }
+ if (column.tag === 'button') {
+ return [
+ '<button type="button" title="', column.string || '', '">',
+ '<img src="/base/static/src/img/icons/', column.icon, '.png"',
+ ' alt="', column.string || '', '"/>',
+ '</button>'
+ ].join('')
+ }
+
+ return openerp.base.format_value(
+ row_data[column.id].value, column, value_if_empty);
+}
+
+};
diff --git a/addons/base/static/src/js/list.js b/addons/base/static/src/js/list.js
index 04336e163c3..a0440221520 100644
--- a/addons/base/static/src/js/list.js
+++ b/addons/base/static/src/js/list.js
@@ -1,63 +1,5 @@
openerp.base.list = function (openerp) {
openerp.base.views.add('list', 'openerp.base.ListView');
-openerp.base.list = {
- /**
- * Formats the rendring of a given value based on its field type
- *
- * @param {Object} row_data record whose values should be displayed in the cell
- * @param {Object} column column descriptor
- * @param {"button"|"field"} column.tag base control type
- * @param {String} column.type widget type for a field control
- * @param {String} [column.string] button label
- * @param {String} [column.icon] button icon
- * @param {String} [value_if_empty=''] what to display if the field's value is ``false``
- */
- render_cell: function (row_data, column, value_if_empty) {
- var attrs = column.modifiers_for(row_data);
- if (attrs.invisible) { return ''; }
- if (column.tag === 'button') {
- return [
- '<button type="button" title="', column.string || '', '">',
- '<img src="/base/static/src/img/icons/', column.icon, '.png"',
- ' alt="', column.string || '', '"/>',
- '</button>'
- ].join('')
- }
-
- var value = row_data[column.id].value;
-
- // If NaN value, display as with a `false` (empty cell)
- if (typeof value === 'number' && isNaN(value)) {
- value = false;
- }
- switch (value) {
- case false:
- case Infinity:
- case -Infinity:
- return value_if_empty === undefined ? '' : value_if_empty;
- }
- switch (column.widget || column.type) {
- case 'integer':
- return _.sprintf('%d', value);
- case 'float':
- var precision = column.digits ? column.digits[1] : 2;
- return _.sprintf('%.' + precision + 'f', value);
- case 'float_time':
- return _.sprintf("%02d:%02d",
- Math.floor(value),
- Math.round((value % 1) * 60));
- case 'progressbar':
- return _.sprintf(
- '<progress value="%.2f" max="100.0">%.2f%%</progress>',
- value, value);
- case 'many2one':
- // name_get value format
- return value[1];
- default:
- return value;
- }
- }
-};
openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListView# */ {
defaults: {
// records can be selected one by one
@@ -85,8 +27,7 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
* the default behaviors and possible options for the list view.
*
* @constructs
- * @param view_manager
- * @param session An OpenERP session object
+ * @param parent parent object
* @param element_id the id of the DOM elements this view should link itself to
* @param {openerp.base.DataSet} dataset the dataset the view should work with
* @param {String} view_id the listview's identifier, if any
@@ -102,16 +43,13 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
*/
init: function(parent, element_id, dataset, view_id, options) {
this._super(parent, element_id);
- this.set_default_options();
- this.view_manager = parent || new openerp.base.NullViewManager();
+ this.set_default_options(_.extend({}, this.defaults, options || {}));
this.dataset = dataset;
this.model = dataset.model;
this.view_id = view_id;
this.columns = [];
- this.options = _.extend({}, this.defaults, options || {});
-
this.set_groups(new openerp.base.ListView.Groups(this));
if (this.dataset instanceof openerp.base.DataSetStatic) {
@@ -123,7 +61,7 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
/**
* Retrieves the view's number of records per page (|| section)
*
- * options > defaults > view_manager.action.limit > indefinite
+ * options > defaults > parent.action.limit > indefinite
*
* @returns {Number|null}
*/
@@ -131,7 +69,7 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
if (this._limit === undefined) {
this._limit = (this.options.limit
|| this.defaults.limit
- || (this.view_manager.action || {}).limit
+ || (this.widget_parent.action || {}).limit
|| null);
}
return this._limit;
@@ -157,7 +95,7 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
self.do_delete(ids);
},
'action': function (e, action_name, id, callback) {
- self.do_action(action_name, id, callback);
+ self.do_button_action(action_name, id, callback);
},
'row_link': function (e, id, dataset) {
self.do_activate_record(dataset.index, id, dataset);
@@ -267,7 +205,7 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
})
.val(self._limit || 'NaN');
});
- if (this.options.sidebar && this.options.sidebar_id) {
+ if (!this.sidebar && this.options.sidebar && this.options.sidebar_id) {
this.sidebar = new openerp.base.Sidebar(this, this.options.sidebar_id);
this.sidebar.start();
this.sidebar.add_toolbar(data.fields_view.toolbar);
@@ -389,9 +327,7 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
view = view || 'form';
this.dataset.index = index;
_.delay(_.bind(function () {
- if(this.view_manager) {
- this.view_manager.on_mode_switch(view);
- }
+ this.do_switch_view(view);
}, this));
},
do_show: function () {
@@ -416,8 +352,9 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
* Reloads the list view based on the current settings (dataset & al)
*
* @param {Boolean} [grouped] Should the list be displayed grouped
+ * @param {Object} [context] context to send the server while loading the view
*/
- reload_view: function (grouped) {
+ reload_view: function (grouped, context) {
var self = this;
var callback = function (field_view_get) {
self.on_loaded(field_view_get, grouped);
@@ -428,7 +365,7 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
return this.rpc('/base/listview/load', {
model: this.model,
view_id: this.view_id,
- context: this.dataset.get_context(),
+ context: this.dataset.get_context(context),
toolbar: this.options.sidebar
}, callback);
}
@@ -464,11 +401,10 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
* @param {Object} results results of evaluating domain and process for a search
*/
do_actual_search: function (results) {
- this.dataset.context = results.context;
- this.dataset.domain = results.domain;
this.groups.datagroup = new openerp.base.DataGroup(
this, this.model,
- results.domain, results.context,
+ this.dataset.get_domain(results.domain),
+ this.dataset.get_context(results.context),
results.group_by);
this.groups.datagroup.sort = this.dataset._sort;
@@ -476,7 +412,7 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
results.group_by = null;
}
- this.reload_view(!!results.group_by).then(
+ this.reload_view(!!results.group_by, results.context).then(
$.proxy(this, 'reload_content'));
},
/**
@@ -519,17 +455,16 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
* @param {Object} id id of the record the action should be called on
* @param {Function} callback should be called after the action is executed, if non-null
*/
- do_action: function (name, id, callback) {
+ do_button_action: function (name, id, callback) {
var self = this,
action = _.detect(this.columns, function (field) {
return field.name === name;
});
if (!action) { return; }
- this.execute_action(
- action, this.dataset, this.session.action_manager, id, function () {
- $.when(callback.apply(this, arguments).then(function () {
- self.compute_aggregates();
- }));
+ this.execute_action(action, this.dataset, id, function () {
+ $.when(callback.apply(this, arguments).then(function () {
+ self.compute_aggregates();
+ }));
});
},
/**
@@ -541,11 +476,11 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
*/
do_activate_record: function (index, id, dataset) {
var self = this;
- _.extend(this.dataset, {
- domain: dataset.domain,
- context: dataset.get_context()
- }).read_slice([], 0, false, function () {
- self.select_record(index);
+ this.dataset.read_slice({
+ context: dataset.get_context(),
+ domain: dataset.get_domain()
+ }, function () {
+ self.select_record(index);
});
},
/**
@@ -644,7 +579,7 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
}
$footer_cells.filter(_.sprintf('[data-field=%s]', column.id))
- .html(openerp.base.list.render_cell(aggregation, column));
+ .html(openerp.base.format_cell(aggregation, column));
});
}
// TODO: implement reorder (drag and drop rows)
@@ -732,7 +667,7 @@ openerp.base.ListView.List = openerp.base.Class.extend( /** @lends openerp.base.
this.$current = this.$_element.clone(true);
this.$current.empty().append(
QWeb.render('ListView.rows', _.extend({
- render_cell: openerp.base.list.render_cell}, this)));
+ render_cell: openerp.base.format_cell}, this)));
},
/**
* Gets the ids of all currently selected records, if any
@@ -837,7 +772,7 @@ openerp.base.ListView.List = openerp.base.Class.extend( /** @lends openerp.base.
var old_index = this.dataset.index;
this.dataset.index = record_index;
read_p = this.dataset.read_index(
- _.filter(_.pluck(this.columns, 'name'), _.identity),
+ _.pluck(_(this.columns).filter(function (r) {return r.tag === 'field';}), 'name'),
function (record) {
var form_record = self.transform_record(record);
self.rows.splice(record_index, 1, form_record);
@@ -863,7 +798,7 @@ openerp.base.ListView.List = openerp.base.Class.extend( /** @lends openerp.base.
row: this.rows[record_index],
row_parity: (record_index % 2 === 0) ? 'even' : 'odd',
row_index: record_index,
- render_cell: openerp.base.list.render_cell
+ render_cell: openerp.base.format_cell
});
},
/**
@@ -1023,7 +958,7 @@ openerp.base.ListView.Groups = openerp.base.Class.extend( /** @lends openerp.bas
row_data[group.grouped_on] = group;
var group_column = _(self.columns).detect(function (column) {
return column.id === group.grouped_on; });
- $group_column.html(openerp.base.list.render_cell(
+ $group_column.html(openerp.base.format_cell(
row_data, group_column, "Undefined"
));
if (group.openable) {
@@ -1099,10 +1034,11 @@ openerp.base.ListView.Groups = openerp.base.Class.extend( /** @lends openerp.bas
d = new $.Deferred(),
page = this.datagroup.openable ? this.page : view.page;
- dataset.read_slice(
- _.filter(_.pluck(_.select(this.columns, function(x) {return x.tag == "field";}), 'name'), _.identity),
- page * limit, limit,
- function (records) {
+ dataset.read_slice({
+ fields: _.pluck(_.select(this.columns, function(x) {return x.tag == "field"}), 'name'),
+ offset: page * limit,
+ limit: limit
+ }, function (records) {
if (!self.datagroup.openable) {
view.configure_pager(dataset);
} else {
diff --git a/addons/base/static/src/js/search.js b/addons/base/static/src/js/search.js
index 96781b46307..a680c84a3fb 100644
--- a/addons/base/static/src/js/search.js
+++ b/addons/base/static/src/js/search.js
@@ -3,7 +3,6 @@ openerp.base.search = function(openerp) {
openerp.base.SearchView = openerp.base.Widget.extend({
init: function(parent, element_id, dataset, view_id, defaults) {
this._super(parent, element_id);
- this.view_manager = parent || new openerp.base.NullViewManager();
this.dataset = dataset;
this.model = dataset.model;
this.view_id = view_id;
@@ -361,9 +360,9 @@ openerp.base.search.fields = new openerp.base.Registry({
'selection': 'openerp.base.search.SelectionField',
'datetime': 'openerp.base.search.DateTimeField',
'date': 'openerp.base.search.DateField',
- 'one2many': 'openerp.base.search.OneToManyField',
'many2one': 'openerp.base.search.ManyToOneField',
- 'many2many': 'openerp.base.search.ManyToManyField'
+ 'many2many': 'openerp.base.search.CharField',
+ 'one2many': 'openerp.base.search.CharField'
});
openerp.base.search.Invalid = openerp.base.Class.extend( /** @lends openerp.base.search.Invalid# */{
/**
@@ -622,39 +621,6 @@ openerp.base.search.CharField = openerp.base.search.Field.extend( /** @lends ope
return this.$element.val();
}
});
-openerp.base.search.BooleanField = openerp.base.search.Field.extend({
- template: 'SearchView.field.selection',
- init: function () {
- this._super.apply(this, arguments);
- this.attrs.selection = [
- ['true', 'Yes'],
- ['false', 'No']
- ];
- },
- /**
- * Search defaults likely to be boolean values (for a boolean field).
- *
- * In the HTML, we only get strings, and our strings here are
- * <code>'true'</code> and <code>'false'</code>, so ensure we get only
- * those by truth-testing the default value.
- *
- * @param {Object} defaults default values for this search view
- */
- render: function (defaults) {
- var name = this.attrs.name;
- if (name in defaults) {
- defaults[name] = defaults[name] ? "true" : "false";
- }
- return this._super(defaults);
- },
- get_value: function () {
- switch (this.$element.val()) {
- case 'false': return false;
- case 'true': return true;
- default: return null;
- }
- }
-});
openerp.base.search.NumberField = openerp.base.search.Field.extend(/** @lends openerp.base.search.NumberField# */{
get_value: function () {
if (!this.$element.val()) {
@@ -691,93 +657,73 @@ openerp.base.search.FloatField = openerp.base.search.NumberField.extend(/** @len
return parseFloat(value);
}
});
-openerp.base.search.SelectionField = openerp.base.search.Field.extend({
+/**
+ * @class
+ * @extends openerp.base.search.Field
+ */
+openerp.base.search.SelectionField = openerp.base.search.Field.extend(/** @lends openerp.base.search.SelectionField# */{
template: 'SearchView.field.selection',
get_value: function () {
return this.$element.val();
}
});
-openerp.base.search.DateField = openerp.base.search.Field.extend( /** @lends openerp.base.search.DateField# */{
- template: 'SearchView.fields.date',
+openerp.base.search.BooleanField = openerp.base.search.SelectionField.extend(/** @lends openerp.base.search.BooleanField# */{
/**
- * enables date picker on the HTML widgets
+ * @constructs
+ * @extends openerp.base.search.BooleanField
*/
- start: function () {
- this._super();
- this.$element.find('input').datepicker({
- dateFormat: 'yy-mm-dd'
- });
- },
- stop: function () {
- this.$element.find('input').datepicker('destroy');
+ init: function () {
+ this._super.apply(this, arguments);
+ this.attrs.selection = [
+ ['true', 'Yes'],
+ ['false', 'No']
+ ];
},
/**
- * Returns an object with two optional keys ``from`` and ``to`` providing
- * the values for resp. the from and to sections of the date widget.
+ * Search defaults likely to be boolean values (for a boolean field).
*
- * If a key is absent, then the corresponding field was not filled.
+ * In the HTML, we only want/get strings, and our strings here are ``true``
+ * and ``false``, so ensure we use precisely those by truth-testing the
+ * default value (iif there is one in the view's defaults).
*
- * @returns {Object}
+ * @param {Object} defaults default values for this search view
+ * @returns {String} rendered boolean field
*/
- get_values: function () {
- var values_array = this.$element.find('input').serializeArray();
-
- if (!values_array || !values_array[0]) {
- throw new openerp.base.search.Invalid(
- this.attrs.name, null, "widget not ready");
- }
- var from = values_array[0].value,
- to = values_array[1].value;
-
- var field_values = {};
- if (from) {
- field_values.from = from;
- }
- if (to) {
- field_values.to = to;
+ render: function (defaults) {
+ var name = this.attrs.name;
+ if (name in defaults) {
+ defaults[name] = defaults[name] ? "true" : "false";
}
- return field_values;
+ return this._super(defaults);
},
- get_context: function () {
- var values = this.get_values();
- if (!this.attrs.context || _.isEmpty(values)) {
- return null;
+ get_value: function () {
+ switch (this.$element.val()) {
+ case 'false': return false;
+ case 'true': return true;
+ default: return null;
}
- return _.extend(
- {}, this.attrs.context,
- {own_values: {self: values}});
+ }
+});
+openerp.base.search.DateField = openerp.base.search.Field.extend( /** @lends openerp.base.search.DateField# */{
+ /**
+ * enables date picker on the HTML widgets
+ */
+ start: function () {
+ this._super();
+ this.$element.addClass('field_date').datepicker({
+ dateFormat: 'yy-mm-dd'
+ });
},
- get_domain: function () {
- var values = this.get_values();
- if (_.isEmpty(values)) {
- return null;
- }
- var domain = this.attrs['filter_domain'];
- if (!domain) {
- domain = [];
- if (values.from) {
- domain.push([this.attrs.name, '>=', values.from]);
- }
- if (values.to) {
- domain.push([this.attrs.name, '<=', values.to]);
- }
- return domain;
- }
-
- return _.extend(
- {}, domain,
- {own_values: {self: values}});
+ stop: function () {
+ this.$element.datepicker('destroy');
+ },
+ get_value: function () {
+ return this.$element.val();
}
});
openerp.base.search.DateTimeField = openerp.base.search.DateField.extend({
- // TODO: time?
-});
-openerp.base.search.OneToManyField = openerp.base.search.CharField.extend({
- // TODO: .relation, .context, .domain
});
openerp.base.search.ManyToOneField = openerp.base.search.CharField.extend({
- // TODO: @widget
- // TODO: .selection, .context, .domain
init: function (view_section, field, view) {
this._super(view_section, field, view);
var self = this;
@@ -845,9 +791,6 @@ openerp.base.search.ManyToOneField = openerp.base.search.CharField.extend({
return this._super();
}
});
-openerp.base.search.ManyToManyField = openerp.base.search.CharField.extend({
- // TODO: .related_columns (Array), .context, .domain
-});
openerp.base.search.ExtendedSearch = openerp.base.OldWidget.extend({
template: 'SearchView.extended_search',
@@ -930,11 +873,10 @@ openerp.base.search.ExtendedSearchGroup = openerp.base.OldWidget.extend({
this._super();
var _this = this;
this.add_prop();
- this.$element.find('.searchview_extended_add_proposition').click(function (e) {
+ this.$element.find('.searchview_extended_add_proposition').click(function () {
_this.add_prop();
});
- var delete_btn = this.$element.find('.searchview_extended_delete_group');
- delete_btn.click(function (e) {
+ this.$element.find('.searchview_extended_delete_group').click(function () {
_this.stop();
});
},
@@ -944,7 +886,7 @@ openerp.base.search.ExtendedSearchGroup = openerp.base.OldWidget.extend({
}).compact().value();
var choice = this.$element.find(".searchview_extended_group_choice").val();
var op = choice == "all" ? "&" : "|";
- return [].concat(choice == "none" ? ['!'] : [],
+ return choice == "none" ? ['!'] : [].concat(
_.map(_.range(_.max([0,props.length - 1])), function() { return op; }),
props);
},
@@ -956,10 +898,7 @@ openerp.base.search.ExtendedSearchGroup = openerp.base.OldWidget.extend({
parent.check_last_element();
},
set_last_group: function(is_last) {
- if(is_last)
- this.$element.addClass("last_group");
- else
- this.$element.removeClass("last_group");
+ this.$element.toggleClass('last_group', is_last);
}
});
@@ -982,8 +921,7 @@ openerp.base.search.ExtendedSearchProposition = openerp.base.OldWidget.extend({
this.$element.find(".searchview_extended_prop_field").change(function() {
_this.changed();
});
- var delete_btn = this.$element.find('.searchview_extended_delete_prop');
- delete_btn.click(function (e) {
+ this.$element.find('.searchview_extended_delete_prop').click(function () {
_this.stop();
});
},
diff --git a/addons/base/static/src/js/view_tree.js b/addons/base/static/src/js/view_tree.js
index 90972f84973..5c3c7457e1c 100644
--- a/addons/base/static/src/js/view_tree.js
+++ b/addons/base/static/src/js/view_tree.js
@@ -5,22 +5,202 @@
openerp.base.view_tree = function(openerp) {
openerp.base.views.add('tree', 'openerp.base.TreeView');
-openerp.base.TreeView = openerp.base.Widget.extend({
/**
* Genuine tree view (the one displayed as a tree, not the list)
*/
+openerp.base.TreeView = openerp.base.View.extend({
+ /**
+ * Indicates that this view is not searchable, and thus that no search
+ * view should be displayed (if there is one active).
+ */
+ searchable : false,
+
+ init: function(parent, element_id, dataset, view_id, options) {
+ this._super(parent, element_id);
+ this.dataset = dataset;
+ this.model = dataset.model;
+ this.view_id = view_id;
+
+ this.records = {};
+
+ this.options = _.extend({}, this.defaults, options || {});
+ },
+
start: function () {
this._super();
- this.$element.append('Tree view');
+ return this.rpc("/base/treeview/load", {
+ model: this.model,
+ view_id: this.view_id,
+ toolbar: this.view_manager ? !!this.view_manager.sidebar : false
+ }, this.on_loaded);
+ },
+ /**
+ * Returns the list of fields needed to correctly read objects.
+ *
+ * Gathers the names of all fields in fields_view_get, and adds the
+ * field_parent (children_field in the tree view) if it's not already one
+ * of the fields to fetch
+ *
+ * @returns {Array} an array of fields which can be provided to DataSet.read_slice and others
+ */
+ fields_list: function () {
+ var fields = _.keys(this.fields);
+ if (!_(fields).contains(this.children_field)) {
+ fields.push(this.children_field);
+ }
+ return fields;
+ },
+ on_loaded: function (fields_view) {
+ var self = this;
+ var has_toolbar = !!fields_view.arch.attrs.toolbar;
+ // field name in OpenERP is kinda stupid: this is the name of the field
+ // holding the ids to the children of the current node, why call it
+ // field_parent?
+ this.children_field = fields_view['field_parent'];
+ this.fields_view = fields_view;
+ _(this.fields_view.arch.children).each(function (field) {
+ if (field.attrs.modifiers) {
+ field.attrs.modifiers = JSON.parse(field.attrs.modifiers);
+ }
+ });
+ this.fields = fields_view.fields;
+ this.hook_row_click();
+ this.$element.html(QWeb.render('TreeView', {
+ 'title': this.fields_view.arch.attrs.string,
+ 'fields_view': this.fields_view.arch.children,
+ 'fields': this.fields,
+ 'toolbar': has_toolbar
+ }));
+
+ this.dataset.read_slice({fields: this.fields_list()}, function (records) {
+ if (!has_toolbar) {
+ // WARNING: will do a second read on the same ids, but only on
+ // first load so not very important
+ self.getdata(null, _(records).pluck('id'));
+ return;
+ }
+
+ var $select = self.$element.find('select')
+ .change(function () {
+ var $option = $(this).find(':selected');
+ self.getdata($option.val(), $option.data('children'));
+ });
+ _(records).each(function (record) {
+ self.records[record.id] = record;
+ $('<option>')
+ .val(record.id)
+ .text(record.name)
+ .data('children', record[self.children_field])
+ .appendTo($select);
+ });
+
+ if (!_.isEmpty(records)) {
+ $select.change();
+ }
+ });
+ },
+ /**
+ * Sets up opening a row
+ */
+ hook_row_click: function () {
+ var self = this;
+ this.$element.delegate('.treeview-td span, .treeview-tr span', 'click', function (e) {
+ e.stopImmediatePropagation();
+ self.activate($(this).closest('tr').data('id'));
+ });
+
+ this.$element.delegate('.treeview-tr', 'click', function () {
+ var is_loaded = 0,
+ $this = $(this),
+ record_id = $this.data('id'),
+ record = self.records[record_id],
+ children_ids = record[self.children_field];
+
+ _(children_ids).each(function(childid) {
+ if (self.$element.find('#treerow_' + childid).length) {
+ if (self.$element.find('#treerow_' + childid).is(':hidden')) {
+ is_loaded = -1;
+ } else {
+ is_loaded++;
+ }
+ }
+ });
+ if (is_loaded === 0) {
+ if (!$this.parent().hasClass('oe-open')) {
+ self.getdata(record_id, children_ids);
+ }
+ } else {
+ self.showcontent(record_id, is_loaded < 0);
+ }
+ });
},
+ // get child data of selected value
+ getdata: function (id, children_ids) {
+ var self = this;
+
+ self.dataset.read_ids(children_ids, this.fields_list(), function (records) {
+ _(records).each(function (record) {
+ self.records[record.id] = record;
+ });
+
+ var $curr_node = self.$element.find('#treerow_' + id);
+ var children_rows = QWeb.render('TreeView.rows', {
+ 'records': records,
+ 'children_field': self.children_field,
+ 'fields_view': self.fields_view.arch.children,
+ 'fields': self.fields,
+ 'level': $curr_node.data('level') || 0,
+ 'render': openerp.base.format_value
+ });
+
+ if ($curr_node.length) {
+ $curr_node.addClass('oe-open');
+ $curr_node.after(children_rows);
+ } else {
+ self.$element.find('tbody').html(children_rows);
+ }
+ });
+ },
+
+ // Get details in listview
+ activate: function(id) {
+ var self = this;
+ this.rpc('/base/treeview/action', {
+ id: id,
+ model: this.dataset.model,
+ context: new openerp.base.CompoundContext(
+ this.dataset.get_context(), {
+ active_model: this.dataset.model,
+ active_id: id,
+ active_ids: [id]})
+ }, function (actions) {
+ if (!actions.length) { return; }
+ var action = actions[0][2];
+ self.do_action(action);
+ });
+ },
+
+ // show & hide the contents
+ showcontent: function (record_id, show) {
+ this.$element.find('#treerow_' + record_id)
+ .toggleClass('oe-open', show);
+
+ _(this.records[record_id][this.children_field]).each(function (child_id) {
+ var $child_row = this.$element.find('#treerow_' + child_id);
+ if ($child_row.hasClass('oe-open')) {
+ this.showcontent(child_id, false);
+ }
+ $child_row.toggle(show);
+ }, this);
+ },
+
do_show: function () {
this.$element.show();
},
+
do_hide: function () {
this.$element.hide();
+ this.hidden = true;
}
});
-
};
-
-// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:
diff --git a/addons/base/static/src/js/views.js b/addons/base/static/src/js/views.js
index 9d299f23791..f21c6387d32 100644
--- a/addons/base/static/src/js/views.js
+++ b/addons/base/static/src/js/views.js
@@ -4,72 +4,83 @@
openerp.base.views = function(openerp) {
+/**
+ * Registry for all the client actions key: tag value: widget
+ */
openerp.base.client_actions = new openerp.base.Registry();
+
openerp.base.ActionManager = openerp.base.Widget.extend({
-// process all kind of actions
- init: function(parent, element_id) {
- this._super(parent, element_id);
- this.viewmanager = null;
- this.current_dialog = null;
- // Temporary linking view_manager to session.
- // Will use parent to find it when implementation will be done.
- this.session.action_manager = this;
+ identifier_prefix: "actionmanager",
+ init: function(parent) {
+ this._super(parent);
+ this.inner_viewmanager = null;
+ this.dialog = null;
+ this.dialog_viewmanager = null;
+ this.client_widget = null;
+ },
+ render: function() {
+ return "<div id='"+this.element_id+"'></div>";
+ },
+ dialog_stop: function () {
+ if (this.dialog) {
+ this.dialog_viewmanager.stop();
+ this.dialog_viewmanager = null;
+ this.dialog.stop();
+ this.dialog = null;
+ }
+ },
+ inner_stop: function () {
+ if (this.inner_viewmanager) {
+ this.inner_viewmanager.stop();
+ this.inner_viewmanager = null;
+ }
},
- do_action: function(action, on_closed) {
+ do_action: function(action, on_close) {
+ var type = action.type.replace(/\./g,'_');
+ var popup = action.target === 'new';
action.flags = _.extend({
- sidebar : action.target != 'new',
- search_view : action.target != 'new',
- new_window : false,
- views_switcher : action.target != 'new',
- action_buttons : action.target != 'new',
- pager : action.target != 'new'
+ views_switcher : !popup,
+ search_view : !popup,
+ action_buttons : !popup,
+ sidebar : !popup,
+ pager : !popup
}, action.flags || {});
- // instantiate the right controllers by understanding the action
- if (!(action.type in this)) {
- console.log("Action manager can't handle action of type " + action.type, action);
+ if (!(type in this)) {
+ this.log("Action manager can't handle action of type " + action.type, action);
return;
}
- this[action.type](action, on_closed);
- },
-
- 'ir.actions.act_window': function (action, on_closed) {
- if (!action.target && this.current_dialog) {
- action.flags.new_window = true;
- }
- if (action.target == 'new') {
- var dialog = this.current_dialog = new openerp.base.ActionDialog(this, {
- title: action.name,
- width: '50%'
- });
- if (on_closed) {
- dialog.close_callback = on_closed;
+ this[type](action, on_close);
+ },
+ ir_actions_act_window: function (action, on_close) {
+ if (action.target === 'new') {
+ if (this.dialog == null) {
+ this.dialog = new openerp.base.Dialog(this, { title: action.name, width: '80%' });
+ if(on_close)
+ this.dialog.on_close.add(on_close);
+ this.dialog.start();
+ } else {
+ this.dialog_viewmanager.stop();
}
- dialog.start(false);
- var viewmanager = dialog.viewmanager = new openerp.base.ViewManagerAction(this, dialog.element_id, action);
- viewmanager.start();
- dialog.open();
- } else if (action.flags.new_window) {
- action.flags.new_window = false;
+ this.dialog_viewmanager = new openerp.base.ViewManagerAction(this, action);
+ this.dialog_viewmanager.appendTo(this.dialog.$element);
+ this.dialog.open();
+ } else {
+ this.dialog_stop();
+ this.inner_stop();
+ this.inner_viewmanager = new openerp.base.ViewManagerAction(this, action);
+ this.inner_viewmanager.appendTo(this.$element);
+ }
+ /* new window code
this.rpc("/base/session/save_session_action", { the_action : action}, function(key) {
- var url = window.location.protocol + "//" + window.location.host +
- window.location.pathname + "?" + jQuery.param({ s_action : "" + key });
- window.open(url);
- if (on_closed) {
- on_closed();
- }
+ var url = window.location.protocol + "//" + window.location.host + window.location.pathname + "?" + jQuery.param({ s_action : "" + key });
+ window.open(url,'_blank');
});
- } else {
- if (this.viewmanager) {
- this.viewmanager.stop();
- }
- this.viewmanager = new openerp.base.ViewManagerAction(this, this.element_id, action);
- this.viewmanager.start();
- }
+ */
},
- 'ir.actions.act_window_close': function (action, on_closed) {
- this.close_dialog();
+ ir_actions_act_window_close: function (action, on_closed) {
+ this.dialog_stop();
},
- 'ir.actions.server': function (action, on_closed) {
+ ir_actions_server: function (action, on_closed) {
var self = this;
this.rpc('/base/action/run', {
action_id: action.id,
@@ -78,47 +89,28 @@ openerp.base.ActionManager = openerp.base.Widget.extend({
self.do_action(action, on_closed)
});
},
- 'ir.actions.client': function (action) {
- var Handler = openerp.base.client_actions.get_object(action.tag);
- new Handler(this, this.element_id, action.params).start();
- },
- close_dialog: function () {
- if (this.current_dialog) {
- this.current_dialog.stop();
- this.current_dialog = null;
- }
- }
-});
-
-openerp.base.ActionDialog = openerp.base.Dialog.extend({
- identifier_prefix: 'action_dialog',
- on_close: function() {
- this._super(this, arguments);
- if (this.close_callback) {
- this.close_callback();
- }
- },
- stop: function() {
- this._super(this, arguments);
- if (this.viewmanager) {
- this.viewmanager.stop();
- }
+ ir_actions_client: function (action) {
+ this.client_widget = openerp.base.client_actions.get_object(action.tag);
+ new this.client_widget(this, this.element_id, action.params).start();
}
});
openerp.base.ViewManager = openerp.base.Widget.extend({
- init: function(parent, element_id, dataset, views) {
- this._super(parent, element_id);
+ identifier_prefix: "viewmanager",
+ init: function(parent, dataset, views) {
+ this._super(parent);
this.model = dataset.model;
this.dataset = dataset;
this.searchview = null;
this.active_view = null;
- this.views_src = _.map(views, function(x)
- {return x instanceof Array? {view_id: x[0], view_type: x[1]} : x;});
+ this.views_src = _.map(views, function(x) {return x instanceof Array? {view_id: x[0], view_type: x[1]} : x;});
this.views = {};
this.flags = this.flags || {};
this.registry = openerp.base.views;
},
+ render: function() {
+ return QWeb.render("ViewManager", {"prefix": this.element_id, views: this.views_src})
+ },
/**
* @returns {jQuery.Deferred} initial view loading promise
*/
@@ -126,28 +118,26 @@ openerp.base.ViewManager = openerp.base.Widget.extend({
this._super();
var self = this;
this.dataset.start();
-
- this.$element.html(QWeb.render("ViewManager", {"prefix": this.element_id, views: this.views_src}));
this.$element.find('.oe_vm_switch button').click(function() {
self.on_mode_switch($(this).data('view-type'));
});
-
+ var views_ids = {};
_.each(this.views_src, function(view) {
self.views[view.view_type] = $.extend({}, view, {
controller : null,
options : _.extend({
- sidebar_id : self.element_id + '_sidebar_' + view.view_type
- }, self.flags)
+ sidebar_id : self.element_id + '_sidebar_' + view.view_type,
+ action : self.action,
+ action_views_ids : views_ids
+ }, self.flags, view.options || {})
});
+ views_ids[view.view_type] = view.view_id;
});
if (this.flags.views_switcher === false) {
this.$element.find('.oe_vm_switch').hide();
}
// switch to the first one in sequence
return this.on_mode_switch(this.views_src[0].view_type);
-
- },
- stop: function() {
},
/**
* Asks the view manager to switch visualization mode.
@@ -168,7 +158,8 @@ openerp.base.ViewManager = openerp.base.Widget.extend({
if (view.embedded_view) {
controller.set_embedded_view(view.embedded_view);
}
- if (view_type === 'list' && this.flags.search_view === false && this.action && this.action['auto_search']) {
+ controller.do_switch_view.add_last(this.on_mode_switch);
+ if (view_type === 'list' && this.flags.search_view === false && this.action && this.action['auto_search']) {
// In case the search view is not instantiated: manually call ListView#search
var domains = !_(self.action.domain).isEmpty()
? [self.action.domain] : [],
@@ -217,11 +208,12 @@ openerp.base.ViewManager = openerp.base.Widget.extend({
},
/**
* Event launched when a controller has been inited.
- *
+ *
* @param {String} view_type type of view
* @param {String} view the inited controller
*/
- on_controller_inited: function(view_type, view) {},
+ on_controller_inited: function(view_type, view) {
+ },
/**
* Sets up the current viewmanager's search view.
*
@@ -238,9 +230,8 @@ openerp.base.ViewManager = openerp.base.Widget.extend({
this.searchview.hide();
}
this.searchview.on_search.add(function(domains, contexts, groupbys) {
- self.views[self.active_view].controller.do_search.call(
- self, domains.concat(self.domains()),
- contexts.concat(self.contexts()), groupbys);
+ var controller = self.views[self.active_view].controller;
+ controller.do_search.call(controller, domains, contexts, groupbys);
});
return this.searchview.start();
},
@@ -254,50 +245,22 @@ openerp.base.ViewManager = openerp.base.Widget.extend({
on_remove: function() {
},
on_edit: function() {
- },
- /**
- * Domains added on searches by the view manager, to override in subsequent
- * view manager in order to add new pieces of domains to searches
- *
- * @returns an empty list
- */
- domains: function () {
- return [];
- },
- /**
- * Contexts added on searches by the view manager.
- *
- * @returns an empty list
- */
- contexts: function () {
- return [];
- }
-});
-
-openerp.base.NullViewManager = openerp.base.generate_null_object_class(openerp.base.ViewManager, {
- init: function(parent) {
- this._super(parent);
- if(parent)
- this.session = parent.session;
- this.action = {flags: {}};
}
});
-// TODO Will move to action Manager
openerp.base.ViewManagerAction = openerp.base.ViewManager.extend({
- init: function(parent, element_id, action) {
+ init: function(parent, action) {
this.session = parent.session;
+ this.action = action;
var dataset;
if (!action.res_id) {
- dataset = new openerp.base.DataSetSearch(this, action.res_model, action.context || null);
+ dataset = new openerp.base.DataSetSearch(this, action.res_model, action.context, action.domain);
} else {
- dataset = new openerp.base.DataSetStatic(this, action.res_model, {}, [action.res_id]);
- if (action.context) {
- // TODO fme: should normalize all DataSets constructors to (session, model, context, ...)
- dataset.context = action.context;
- }
+ this.action.flags.search_view = false;
+ dataset = new openerp.base.DataSetStatic(this, action.res_model, action.context, [action.res_id]);
+ dataset.index = 0;
}
- this._super(parent, element_id, dataset, action.views);
+ this._super(parent, dataset, action.views);
this.action = action;
this.flags = this.action.flags || {};
if (action.res_model == 'board.board' && action.views.length == 1 && action.views) {
@@ -307,6 +270,7 @@ openerp.base.ViewManagerAction = openerp.base.ViewManager.extend({
},
start: function() {
var inital_view_loaded = this._super();
+
var search_defaults = {};
_.each(this.action.context, function (value, key) {
var match = /^search_default_(.*)$/.exec(key);
@@ -329,32 +293,6 @@ openerp.base.ViewManagerAction = openerp.base.ViewManager.extend({
}
}
},
- stop: function() {
- // should be replaced by automatic destruction implemented in Widget
- this._super();
- },
- /**
- * adds action domain to the search domains
- *
- * @returns the action's domain
- */
- domains: function () {
- if (!this.action.domain) {
- return [];
- }
- return [this.action.domain];
- },
- /**
- * adds action context to the search contexts
- *
- * @returns the action's context
- */
- contexts: function () {
- if (!this.action.context) {
- return [];
- }
- return [this.action.context];
- },
on_mode_switch: function (view_type) {
this._super(view_type);
this.shortcut_check(this.views[view_type]);
@@ -394,8 +332,6 @@ openerp.base.ViewManagerAction = openerp.base.ViewManager.extend({
$shortcut_toggle.addClass("oe-shortcut-remove");
}
});
-
-
}
});
@@ -491,7 +427,9 @@ openerp.base.View = openerp.base.Widget.extend({
_.defaults(this.options, {
// All possible views options should be defaulted here
sidebar_id: null,
- sidebar: true
+ sidebar: true,
+ action: null,
+ action_views_ids: {}
});
},
/**
@@ -503,17 +441,12 @@ openerp.base.View = openerp.base.Widget.extend({
* @param {String} [action_data.type='workflow'] the action type, if present, one of ``'object'``, ``'action'`` or ``'workflow'``
* @param {Object} [action_data.context=null] additional action context, to add to the current context
* @param {openerp.base.DataSet} dataset a dataset object used to communicate with the server
- * @param {openerp.base.ActionManager} action_manager object able to actually execute the action, if any is fetched
* @param {Object} [record_id] the identifier of the object on which the action is to be applied
* @param {Function} on_closed callback to execute when dialog is closed or when the action does not generate any result (no new action)
*/
- execute_action: function (action_data, dataset, action_manager, record_id, on_closed) {
+ execute_action: function (action_data, dataset, record_id, on_closed) {
var self = this;
- if (action_manager.current_dialog) {
- on_closed = action_manager.current_dialog.close_callback;
- }
var handler = function (r) {
- action_manager.close_dialog();
var action = r.result;
if (action && action.constructor == Object) {
action.context = action.context || {};
@@ -522,38 +455,36 @@ openerp.base.View = openerp.base.Widget.extend({
active_ids: [record_id || false],
active_model: dataset.model
});
- action.flags = {
- new_window: true
- };
- action_manager.do_action(action, on_closed);
+ self.do_action(action, on_closed);
} else if (on_closed) {
on_closed(action);
}
};
- if (!action_data.special) {
- var context = new openerp.base.CompoundContext(dataset.get_context(), action_data.context || {});
- switch(action_data.type) {
- case 'object':
- return dataset.call_button(action_data.name, [[record_id], context], handler);
- case 'action':
- return this.rpc('/base/action/load', { action_id: parseInt(action_data.name, 10), context: context }, handler);
- default:
- return dataset.exec_workflow(record_id, action_data.name, handler);
- }
- } else {
- action_manager.close_dialog();
- }
+ var context = new openerp.base.CompoundContext(dataset.get_context(), action_data.context || {});
+
+ if (action_data.special) {
+ handler({result: {"type":"ir.actions.act_window_close"}});
+ } else if (action_data.type=="object") {
+ return dataset.call_button(action_data.name, [[record_id], context], handler);
+ } else if (action_data.type=="action") {
+ return this.rpc('/base/action/load', { action_id: parseInt(action_data.name, 10), context: context }, handler);
+ } else {
+ return dataset.exec_workflow(record_id, action_data.name, handler);
+ }
},
/**
* Directly set a view to use instead of calling fields_view_get. This method must
* be called before start(). When an embedded view is set, underlying implementations
* of openerp.base.View must use the provided view instead of any other one.
- *
+ *
* @param embedded_view A view.
*/
set_embedded_view: function(embedded_view) {
this.embedded_view = embedded_view;
+ this.options.sidebar = false;
+ },
+ do_switch_view: function(view) {
},
set_common_sidebar_sections: function(sidebar) {
sidebar.add_section('customize', "Customize", [
@@ -598,16 +529,16 @@ openerp.base.View = openerp.base.Widget.extend({
}
},
on_sidebar_edit_workflow: function() {
- console.log('Todo');
+ this.log('Todo');
},
on_sidebar_customize_object: function() {
- console.log('Todo');
+ this.log('Todo');
},
on_sidebar_import: function() {
},
on_sidebar_export: function() {
var export_view = new openerp.base.DataExport(this, this.dataset);
- export_view.start(false);
+ export_view.start();
},
on_sidebar_translate: function() {
},