summaryrefslogtreecommitdiff
path: root/addons/base/static/src
diff options
context:
space:
mode:
authorXavier Morel <xmo@openerp.com>2011-08-02 11:47:17 +0200
committerXavier Morel <xmo@openerp.com>2011-08-02 11:47:17 +0200
commit620e3ad55bb5a62a1e7785de0cb5842aab3e8d2f (patch)
tree55bf2daf9be45b15530ea4bdc18f500a032473bf /addons/base/static/src
parentc6052839901278578b218a11a26b4fb46f7a29ba (diff)
parent0c153d1249372b77a2123f3d157e31eb0c2548e0 (diff)
[MERGE] updates from trunk
bzr revid: xmo@openerp.com-20110802094717-yxmi281j1l139tws
Diffstat (limited to 'addons/base/static/src')
-rw-r--r--addons/base/static/src/css/base.css10
-rw-r--r--addons/base/static/src/css/data_export.css85
-rw-r--r--addons/base/static/src/img/collapse.gifbin0 -> 80 bytes
-rw-r--r--addons/base/static/src/img/expand.gifbin0 -> 81 bytes
-rw-r--r--addons/base/static/src/img/header.gifbin0 -> 182 bytes
-rw-r--r--addons/base/static/src/js/boot.js (renamed from addons/base/static/src/js/base.js)8
-rw-r--r--addons/base/static/src/js/chrome.js277
-rw-r--r--addons/base/static/src/js/controller.js248
-rw-r--r--addons/base/static/src/js/core.js572
-rw-r--r--addons/base/static/src/js/data.js33
-rw-r--r--addons/base/static/src/js/data_export.js402
-rw-r--r--addons/base/static/src/js/form.js151
-rw-r--r--addons/base/static/src/js/list.js2
-rw-r--r--addons/base/static/src/js/search.js55
-rw-r--r--addons/base/static/src/js/view_help.js4
-rw-r--r--addons/base/static/src/js/view_tree.js2
-rw-r--r--addons/base/static/src/js/views.js105
-rw-r--r--addons/base/static/src/xml/base.xml163
18 files changed, 1485 insertions, 632 deletions
diff --git a/addons/base/static/src/css/base.css b/addons/base/static/src/css/base.css
index 483a433eb99..dce17649310 100644
--- a/addons/base/static/src/css/base.css
+++ b/addons/base/static/src/css/base.css
@@ -30,6 +30,9 @@ body.openerp {
.openerp .oe-number {
text-align: right !important;
}
+.openerp .oe_hide {
+ display: none !important;
+}
/* STATES */
.openerp .on_logged,
@@ -1169,3 +1172,10 @@ background: linear-gradient(top, #ffffff 0%,#ebe9e9 100%); /* W3C */
font-size: 1.2em;
font-weight: bold;
}
+
+.openerp .dhx_mini_calendar {
+ -moz-box-shadow: none;
+ -khtml-box-shadow: none;
+ -webkit-box-shadow: none;
+ box-shadow: none;
+}
diff --git a/addons/base/static/src/css/data_export.css b/addons/base/static/src/css/data_export.css
new file mode 100644
index 00000000000..7b4b9707b39
--- /dev/null
+++ b/addons/base/static/src/css/data_export.css
@@ -0,0 +1,85 @@
+.openerp .oe_export_row tr{
+ background-color: #FFFFFF;
+ font-size: 0.8em;
+ height: 22px;
+}
+
+.openerp tr.ui-selected td {
+ background-color: #CCCCCC;
+}
+
+.openerp .oe_export_requiredfield {
+ background-color: #D2D2FF;
+}
+
+.openerp .oe_export_readonlyfield{
+ background-color: #999999;
+}
+
+.openerp .oe_export_row:hover{
+ background-color: #F3F3F3;
+}
+
+.openerp .oe_export_fields_selector_export {
+ width: 100%;
+ height: 400px;
+}
+
+.openerp .oe_export_fields_selector_left {
+ width: 50%;
+}
+
+.openerp div#left_field_panel {
+ overflow: scroll;
+ width: 100%;
+ height: 400px;
+ border: solid #999999 1px;
+}
+
+.openerp .oe_export_fields_selector_center {
+ width: 102px;
+}
+
+.openerp .oe_export_fields_selector_right {
+ width: 45%;
+ height: 400px;
+}
+
+.openerp .oe_export_fields_selector_export select{
+ width: 100%;
+ height: 100%;
+}
+
+.openerp .oe_export_tree_header{
+ border: 0.5px solid #E3E3E3;
+ text-align: left;
+ white-space: nowrap;
+ padding: 4px 5px;
+ background: url(/base/static/src/img/header.gif);
+}
+
+
+.openerp table.tree_grid{
+ border: 1px solid #E3E3E3;
+ text-align: left;
+ white-space: nowrap;
+ background-color:#E3E3E3;
+ border-collapse: collapse;
+ width: 100%;
+}
+
+.openerp table.tree_grid a:hover {
+ color: blue;
+ border: none;
+}
+
+.openerp table.tree_grid a {
+ color: #5F5C5C;
+ border: none;
+ display: block;
+}
+
+.openerp .oe_export_button_export {
+ border: 1px solid #006;
+ background-color: #F3F3F3;
+}
diff --git a/addons/base/static/src/img/collapse.gif b/addons/base/static/src/img/collapse.gif
new file mode 100644
index 00000000000..759e72ebcfe
--- /dev/null
+++ b/addons/base/static/src/img/collapse.gif
Binary files differ
diff --git a/addons/base/static/src/img/expand.gif b/addons/base/static/src/img/expand.gif
new file mode 100644
index 00000000000..ca98d0b5f2b
--- /dev/null
+++ b/addons/base/static/src/img/expand.gif
Binary files differ
diff --git a/addons/base/static/src/img/header.gif b/addons/base/static/src/img/header.gif
new file mode 100644
index 00000000000..f40bd9c0f6a
--- /dev/null
+++ b/addons/base/static/src/img/header.gif
Binary files differ
diff --git a/addons/base/static/src/js/base.js b/addons/base/static/src/js/boot.js
index 28022f8b764..c16f2d261f5 100644
--- a/addons/base/static/src/js/base.js
+++ b/addons/base/static/src/js/boot.js
@@ -1,5 +1,5 @@
//---------------------------------------------------------
-// OpenERP initialisation and black magic about the pool
+// OpenERP Web Boostrap
//---------------------------------------------------------
/**
@@ -57,9 +57,8 @@
// OpenERP base module split
//---------------------------------------------------------
-/** @namespace */
openerp.base = function(instance) {
- openerp.base.controller(instance);
+ openerp.base.core(instance);
openerp.base.dates(instance);
openerp.base.chrome(instance);
openerp.base.data(instance);
@@ -87,6 +86,9 @@ openerp.base = function(instance) {
if (openerp.base.view_tree) {
openerp.base.view_tree(instance);
}
+ if (openerp.base.data_export) {
+ openerp.base.data_export(instance);
+ }
};
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:
diff --git a/addons/base/static/src/js/chrome.js b/addons/base/static/src/js/chrome.js
index 2019776932f..c456996f6ba 100644
--- a/addons/base/static/src/js/chrome.js
+++ b/addons/base/static/src/js/chrome.js
@@ -4,241 +4,7 @@
openerp.base.chrome = function(openerp) {
-/**
- * Base error for lookup failure
- *
- * @class
- */
-openerp.base.NotFound = openerp.base.Class.extend( /** @lends openerp.base.NotFound# */ {
-});
-openerp.base.KeyNotFound = openerp.base.NotFound.extend( /** @lends openerp.base.KeyNotFound# */ {
- /**
- * Thrown when a key could not be found in a mapping
- *
- * @constructs
- * @extends openerp.base.NotFound
- * @param {String} key the key which could not be found
- */
- init: function (key) {
- this.key = key;
- },
- toString: function () {
- return "The key " + this.key + " was not found";
- }
-});
-openerp.base.ObjectNotFound = openerp.base.NotFound.extend( /** @lends openerp.base.ObjectNotFound# */ {
- /**
- * Thrown when an object path does not designate a valid class or object
- * in the openerp hierarchy.
- *
- * @constructs
- * @extends openerp.base.NotFound
- * @param {String} path the invalid object path
- */
- init: function (path) {
- this.path = path;
- },
- toString: function () {
- return "Could not find any object of path " + this.path;
- }
-});
-openerp.base.Registry = openerp.base.Class.extend( /** @lends openerp.base.Registry# */ {
- /**
- * Stores a mapping of arbitrary key (strings) to object paths (as strings
- * as well).
- *
- * Resolves those paths at query time in order to always fetch the correct
- * object, even if those objects have been overloaded/replaced after the
- * registry was created.
- *
- * An object path is simply a dotted name from the openerp root to the
- * object pointed to (e.g. ``"openerp.base.Session"`` for an OpenERP
- * session object).
- *
- * @constructs
- * @param {Object} mapping a mapping of keys to object-paths
- */
- init: function (mapping) {
- this.map = mapping || {};
- },
- /**
- * Retrieves the object matching the provided key string.
- *
- * @param {String} key the key to fetch the object for
- * @returns {Class} the stored class, to initialize
- *
- * @throws {openerp.base.KeyNotFound} if the object was not in the mapping
- * @throws {openerp.base.ObjectNotFound} if the object path was invalid
- */
- get_object: function (key) {
- var path_string = this.map[key];
- if (path_string === undefined) {
- throw new openerp.base.KeyNotFound(key);
- }
-
- var object_match = openerp;
- var path = path_string.split('.');
- // ignore first section
- for(var i=1; i<path.length; ++i) {
- object_match = object_match[path[i]];
-
- if (object_match === undefined) {
- throw new openerp.base.ObjectNotFound(path_string);
- }
- }
- return object_match;
- },
- /**
- * Tries a number of keys, and returns the first object matching one of
- * the keys.
- *
- * @param {Array} keys a sequence of keys to fetch the object for
- * @returns {Class} the first class found matching an object
- *
- * @throws {openerp.base.KeyNotFound} if none of the keys was in the mapping
- * @trows {openerp.base.ObjectNotFound} if a found object path was invalid
- */
- get_any: function (keys) {
- for (var i=0; i<keys.length; ++i) {
- try {
- return this.get_object(keys[i]);
- } catch (e) {
- if (e instanceof openerp.base.KeyNotFound) {
- continue;
- }
- throw e;
- }
- }
- throw new openerp.base.KeyNotFound(keys.join(','));
- },
- /**
- * Adds a new key and value to the registry.
- *
- * This method can be chained.
- *
- * @param {String} key
- * @param {String} object_path fully qualified dotted object path
- * @returns {openerp.base.Registry} itself
- */
- add: function (key, object_path) {
- this.map[key] = object_path;
- return this;
- },
- /**
- * Creates and returns a copy of the current mapping, with the provided
- * mapping argument added in (replacing existing keys if needed)
- *
- * @param {Object} [mapping={}] a mapping of keys to object-paths
- */
- clone: function (mapping) {
- return new openerp.base.Registry(
- _.extend({}, this.map, mapping || {}));
- }
-});
-
-/**
- * OpenERP session aware controller
- * a controller takes an already existing dom element and manage it
- */
-openerp.base.Controller = openerp.base.Controller.extend( /** @lends openerp.base.Controller# */{
- init: function(parent, element_id) {
- this._super(parent, element_id);
- if(this.controller_parent && this.controller_parent.session) {
- this.session = this.controller_parent.session;
- }
- },
- /**
- * Performs a JSON-RPC call
- *
- * @param {String} url endpoint url
- * @param {Object} data RPC parameters
- * @param {Function} success RPC call success callback
- * @param {Function} error RPC call error callback
- * @returns {jQuery.Deferred} deferred object for the RPC call
- */
- rpc: function(url, data, success, error) {
- return this.session.rpc(url, data, success, error);
- },
- do_action: function(action, on_finished) {
- return this.parent.do_action(action, on_finished);
- }
-});
-
-/**
- * OpenERP session aware widget
- * A widget is a controller that doesnt take an element_id
- * it render its own html render() that you should insert into the dom
- * and bind it at start()
- */
-openerp.base.BaseWidget = openerp.base.Controller.extend({
- /**
- * The name of the QWeb template that will be used for rendering. Must be
- * redefined in subclasses or the render() method can not be used.
- *
- * @type string
- */
- template: null,
- /**
- * The prefix used to generate an id automatically. Should be redefined in
- * subclasses. If it is not defined, a default identifier will be used.
- *
- * @type string
- */
- identifier_prefix: 'generic-identifier',
- /**
- * Base class for widgets. Handle rendering (based on a QWeb template),
- * identifier generation, parenting and destruction of the widget.
- * Also initialize the identifier.
- *
- * @constructs
- * @params {openerp.base.search.BaseWidget} parent The parent widget.
- */
- init: function (parent) {
- this._super(parent);
- this.make_id(this.identifier_prefix);
- },
- /**
- * Sets and returns a globally unique identifier for the widget.
- *
- * If a prefix is appended, the identifier will be appended to it.
- *
- * @params sections prefix sections, empty/falsy sections will be removed
- */
- make_id: function () {
- this.element_id = _.uniqueId(_.toArray(arguments).join('_'));
- return this.element_id;
- },
- /**
- * Render the widget. This.template must be defined.
- * The content of the current object is passed as context to the template.
- *
- * @param {object} additional Additional context arguments to pass to the template.
- */
- render: function (additional) {
- return QWeb.render(this.template, _.extend({}, this, additional != null ? additional : {}));
- },
- /**
- * "Starts" the widgets. Called at the end of the rendering, this allows
- * to get a jQuery object referring to the DOM ($element attribute).
- */
- start: function () {
- this._super();
- var tmp = document.getElementById(this.element_id);
- this.$element = tmp ? $(tmp) : null;
- },
- /**
- * "Stops" the widgets. Called when the view destroys itself, this
- * lets the widgets clean up after themselves.
- */
- stop: function () {
- if(this.$element != null) {
- this.$element.remove();
- }
- this._super();
- }
-});
-
-openerp.base.Session = openerp.base.Controller.extend( /** @lends openerp.base.Session# */{
+openerp.base.Session = openerp.base.Widget.extend( /** @lends openerp.base.Session# */{
/**
* @constructs
* @param element_id to use for exception reporting
@@ -506,7 +272,7 @@ openerp.base.Session = openerp.base.Controller.extend( /** @lends openerp.base.S
}
});
-openerp.base.Notification = openerp.base.Controller.extend({
+openerp.base.Notification = openerp.base.Widget.extend({
init: function(parent, element_id) {
this._super(parent, element_id);
this.$element.notify({
@@ -528,7 +294,7 @@ openerp.base.Notification = openerp.base.Controller.extend({
}
});
-openerp.base.Dialog = openerp.base.BaseWidget.extend({
+openerp.base.Dialog = openerp.base.OldWidget.extend({
dialog_title: "",
identifier_prefix: 'dialog',
init: function (parent, options) {
@@ -544,8 +310,8 @@ openerp.base.Dialog = openerp.base.BaseWidget.extend({
max_height: '100%',
autoOpen: false,
buttons: {},
- close: function () {
- self.stop();
+ beforeClose: function () {
+ self.on_close();
}
};
for (var f in this) {
@@ -609,7 +375,15 @@ openerp.base.Dialog = openerp.base.BaseWidget.extend({
this.set_options(options);
this.$dialog.dialog(this.options).dialog('open');
},
+ close: function() {
+ // Closes the dialog but leave it in a state where it could be opened again.
+ this.$dialog.dialog('close');
+ },
+ on_close: function() {
+ },
stop: function () {
+ // Destroy widget
+ this.close();
this.$dialog.dialog('destroy');
}
});
@@ -650,7 +424,7 @@ openerp.base.CrashManager = openerp.base.Dialog.extend({
}
});
-openerp.base.Loading = openerp.base.Controller.extend({
+openerp.base.Loading = openerp.base.Widget.extend({
init: function(parent, element_id) {
this._super(parent, element_id);
this.count = 0;
@@ -669,7 +443,7 @@ openerp.base.Loading = openerp.base.Controller.extend({
}
});
-openerp.base.Database = openerp.base.Controller.extend({
+openerp.base.Database = openerp.base.Widget.extend({
init: function(parent, element_id, option_id) {
this._super(parent, element_id);
this.$option_id = $('#' + option_id);
@@ -968,7 +742,7 @@ openerp.base.Database = openerp.base.Controller.extend({
}
});
-openerp.base.Login = openerp.base.Controller.extend({
+openerp.base.Login = openerp.base.Widget.extend({
remember_creditentials: true,
init: function(parent, element_id) {
@@ -986,9 +760,6 @@ openerp.base.Login = openerp.base.Controller.extend({
this.selected_login = this.selected_login || "admin";
this.selected_password = this.selected_password || "a";
}
- if(this.parent && this.parent.session) {
- this.session = this.parent.session;
- }
},
start: function() {
var self = this;
@@ -1069,11 +840,9 @@ openerp.base.Login = openerp.base.Controller.extend({
}
});
-openerp.base.Header = openerp.base.Controller.extend({
+openerp.base.Header = openerp.base.Widget.extend({
init: function(parent, element_id) {
this._super(parent, element_id);
- if(parent)
- this.session = parent.session;
},
start: function() {
this.do_update();
@@ -1085,7 +854,7 @@ openerp.base.Header = openerp.base.Controller.extend({
on_logout: function() {}
});
-openerp.base.Menu = openerp.base.Controller.extend({
+openerp.base.Menu = openerp.base.Widget.extend({
init: function(parent, element_id, secondary_menu_id) {
this._super(parent, element_id);
this.secondary_menu_id = secondary_menu_id;
@@ -1166,16 +935,16 @@ openerp.base.Menu = openerp.base.Controller.extend({
}
});
-openerp.base.Homepage = openerp.base.Controller.extend({
+openerp.base.Homepage = openerp.base.Widget.extend({
});
-openerp.base.Preferences = openerp.base.Controller.extend({
+openerp.base.Preferences = openerp.base.Widget.extend({
});
-openerp.base.ImportExport = openerp.base.Controller.extend({
+openerp.base.ImportExport = openerp.base.Widget.extend({
});
-openerp.base.WebClient = openerp.base.Controller.extend({
+openerp.base.WebClient = openerp.base.Widget.extend({
init: function(element_id) {
this._super(null, element_id);
@@ -1192,7 +961,7 @@ openerp.base.WebClient = openerp.base.Controller.extend({
this.crashmanager.start(false);
// Do you autorize this ? will be replaced by notify() in controller
- openerp.base.Controller.prototype.notification = new openerp.base.Notification(this, "oe_notification");
+ openerp.base.Widget.prototype.notification = new openerp.base.Notification(this, "oe_notification");
this.header = new openerp.base.Header(this, "oe_header");
this.login = new openerp.base.Login(this, "oe_login");
diff --git a/addons/base/static/src/js/controller.js b/addons/base/static/src/js/controller.js
deleted file mode 100644
index c9ca3193e70..00000000000
--- a/addons/base/static/src/js/controller.js
+++ /dev/null
@@ -1,248 +0,0 @@
-/*---------------------------------------------------------
- * OpenERP controller framework
- *--------------------------------------------------------*/
-
-openerp.base.controller = function(instance) {
-/**
- * John Resig Class with factory improvement
- */
-(function() {
- var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
- // The base Class implementation (does nothing)
- this.Class = function(){};
-
- // Create a new Class that inherits from this class
- Class.extend = function(prop) {
- var _super = this.prototype;
-
- // Instantiate a base class (but only create the instance,
- // don't run the init constructor)
- initializing = true;
- var prototype = new this();
- initializing = false;
-
- // Copy the properties over onto the new prototype
- for (var name in prop) {
- // Check if we're overwriting an existing function
- prototype[name] = typeof prop[name] == "function" &&
- typeof _super[name] == "function" && fnTest.test(prop[name]) ?
- (function(name, fn){
- return function() {
- var tmp = this._super;
-
- // Add a new ._super() method that is the same method
- // but on the super-class
- this._super = _super[name];
-
- // The method only need to be bound temporarily, so we
- // remove it when we're done executing
- var ret = fn.apply(this, arguments);
- this._super = tmp;
-
- return ret;
- };
- })(name, prop[name]) :
- prop[name];
- }
-
- // The dummy class constructor
- function Class() {
- // All construction is actually done in the init method
- if ( !initializing && this.init ) {
- var ret = this.init.apply(this, arguments);
- if (ret) { return ret; }
- }
- return this;
- }
-
- // Populate our constructed prototype object
- Class.prototype = prototype;
-
- // Enforce the constructor to be what we expect
- Class.constructor = Class;
-
- // And make this class extendable
- Class.extend = arguments.callee;
-
- return Class;
- };
-})();
-
-// todo change john resig class to keep window clean
-instance.base.Class = window.Class
-
-instance.base.callback = function(obj, method) {
- var callback = function() {
- var args = Array.prototype.slice.call(arguments);
- var r;
- for(var i = 0; i < callback.callback_chain.length; i++) {
- var c = callback.callback_chain[i];
- if(c.unique) {
- callback.callback_chain.splice(i, 1);
- i -= 1;
- }
- r = c.callback.apply(c.self, c.args.concat(args));
- // TODO special value to stop the chain
- // openerp.base.callback_stop
- }
- return r;
- };
- callback.callback_chain = [];
- callback.add = function(f) {
- if(typeof(f) == 'function') {
- f = { callback: f, args: Array.prototype.slice.call(arguments, 1) };
- }
- f.self = f.self || null;
- f.args = f.args || [];
- f.unique = !!f.unique;
- if(f.position == 'last') {
- callback.callback_chain.push(f);
- } else {
- callback.callback_chain.unshift(f);
- }
- return callback;
- };
- callback.add_first = function(f) {
- return callback.add.apply(null,arguments);
- };
- callback.add_last = function(f) {
- return callback.add({
- callback: f,
- args: Array.prototype.slice.call(arguments, 1),
- position: "last"
- });
- };
-
- return callback.add({
- callback: method,
- self:obj,
- args:Array.prototype.slice.call(arguments, 2)
- });
-};
-
-/**
- * Generates an inherited class that replaces all the methods by null methods (methods
- * that does nothing and always return undefined).
- *
- * @param {Class} claz
- * @param {dict} add Additional functions to override.
- * @return {Class}
- */
-instance.base.generate_null_object_class = function(claz, add) {
- var newer = {};
- var copy_proto = function(prototype) {
- for (var name in prototype) {
- if(typeof prototype[name] == "function") {
- newer[name] = function() {};
- }
- }
- if (prototype.prototype)
- copy_proto(prototype.prototype);
- };
- copy_proto(claz.prototype);
- newer.init = instance.base.Controller.prototype.init;
- var tmpclass = claz.extend(newer);
- return tmpclass.extend(add || {});
-};
-
-/**
- * OpenERP Controller
- * TODO merge BaseWidget with Controller
- */
-instance.base.Controller = instance.base.Class.extend( /** @lends instance.base.Controller# */{
- /**
- * @constructs
- * rpc operations, event binding and callback calling should be done in
- * start() instead of init so that events can be hooked in between.
- */
- init: function(parent, element_id) {
- this.element_id = element_id;
- this.$element = $('#' + element_id);
- if (element_id) {
- instance.screen[element_id] = this;
- }
- // save the parent children relationship
- this.controller_parent = parent;
- this.controller_children = [];
- if(parent && parent.controller_children) {
- parent.controller_children.push(this);
- }
- // backward compatibility
- this.parent = this.controller_parent;
- this.children = this.controller_children;
-
- // Transform on_* method into openerp.base.callbacks
- for (var name in this) {
- if(typeof(this[name]) == "function") {
- this[name].debug_name = name;
- // bind ALL function to this not only on_and _do ?
- if((/^on_|^do_/).test(name)) {
- this[name] = instance.base.callback(this, this[name]);
- }
- }
- }
- },
- /**
- * Event binding, rpc and callback calling required to initialize the
- * object should happen here
- *
- * Returns a promise object letting callers (subclasses and direct callers)
- * know when this component is done starting
- *
- * @returns {jQuery.Deferred}
- */
- start: function() {
- // returns an already fulfilled promise. Maybe we could return nothing?
- // $.when can take non-deferred and in that case it simply considers
- // them all as fulfilled promises.
- // But in thise case we *have* to ensure callers use $.when and don't
- // try to call deferred methods on this return value.
- return $.Deferred().done().promise();
- },
- stop: function() {
- if (this.parent && this.parent.children) {
- this.parent.children = _.without(this.parent.children, this);
- this.parent.controller_children = this.parent.children;
- }
- this.parent = null;
- this.controller_parent = null;
- },
- log: function() {
- var args = Array.prototype.slice.call(arguments);
- var caller = arguments.callee.caller;
- // TODO add support for line number using
- // https://github.com/emwendelin/javascript-stacktrace/blob/master/stacktrace.js
- // args.unshift("" + caller.debug_name);
- this.on_log.apply(this,args);
- },
- on_log: function() {
- if(window.openerp.debug || (window.location.search.indexOf('?debug') !== -1)) {
- var notify = false;
- var body = false;
- if(window.console) {
- console.log(arguments);
- } else {
- body = true;
- }
- var a = Array.prototype.slice.call(arguments, 0);
- for(var i = 0; i < a.length; i++) {
- var v = a[i]==null ? "null" : a[i].toString();
- if(i==0) {
- notify = v.match(/^not/);
- body = v.match(/^bod/);
- }
- if(body) {
- $('<pre></pre>').text(v).appendTo($('body'));
- }
- if(notify && this.notification) {
- this.notification.notify("Logging:",v);
- }
- }
- }
-
- }
-});
-
-};
-
-// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:
diff --git a/addons/base/static/src/js/core.js b/addons/base/static/src/js/core.js
new file mode 100644
index 00000000000..e57d79e40bb
--- /dev/null
+++ b/addons/base/static/src/js/core.js
@@ -0,0 +1,572 @@
+/*---------------------------------------------------------
+ * OpenERP controller framework
+ *--------------------------------------------------------*/
+
+openerp.base.core = function(openerp) {
+/**
+ * John Resig Class with factory improvement
+ */
+(function() {
+ var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
+ // The base Class implementation (does nothing)
+ openerp.base.Class = function(){};
+
+ // Create a new Class that inherits from this class
+ openerp.base.Class.extend = function(prop) {
+ var _super = this.prototype;
+
+ // Instantiate a base class (but only create the instance,
+ // don't run the init constructor)
+ initializing = true;
+ var prototype = new this();
+ initializing = false;
+
+ // Copy the properties over onto the new prototype
+ for (var name in prop) {
+ // Check if we're overwriting an existing function
+ prototype[name] = typeof prop[name] == "function" &&
+ typeof _super[name] == "function" && fnTest.test(prop[name]) ?
+ (function(name, fn){
+ return function() {
+ var tmp = this._super;
+
+ // Add a new ._super() method that is the same method
+ // but on the super-class
+ this._super = _super[name];
+
+ // The method only need to be bound temporarily, so we
+ // remove it when we're done executing
+ var ret = fn.apply(this, arguments);
+ this._super = tmp;
+
+ return ret;
+ };
+ })(name, prop[name]) :
+ prop[name];
+ }
+
+ // The dummy class constructor
+ function Class() {
+ // All construction is actually done in the init method
+ if ( !initializing && this.init ) {
+ var ret = this.init.apply(this, arguments);
+ if (ret) { return ret; }
+ }
+ return this;
+ }
+
+ // Populate our constructed prototype object
+ Class.prototype = prototype;
+
+ // Enforce the constructor to be what we expect
+ Class.constructor = Class;
+
+ // And make this class extendable
+ Class.extend = arguments.callee;
+
+ return Class;
+ };
+})();
+
+openerp.base.callback = function(obj, method) {
+ var callback = function() {
+ var args = Array.prototype.slice.call(arguments);
+ var r;
+ for(var i = 0; i < callback.callback_chain.length; i++) {
+ var c = callback.callback_chain[i];
+ if(c.unique) {
+ callback.callback_chain.splice(i, 1);
+ i -= 1;
+ }
+ r = c.callback.apply(c.self, c.args.concat(args));
+ // TODO special value to stop the chain
+ // openerp.base.callback_stop
+ }
+ return r;
+ };
+ callback.callback_chain = [];
+ callback.add = function(f) {
+ if(typeof(f) == 'function') {
+ f = { callback: f, args: Array.prototype.slice.call(arguments, 1) };
+ }
+ f.self = f.self || null;
+ f.args = f.args || [];
+ f.unique = !!f.unique;
+ if(f.position == 'last') {
+ callback.callback_chain.push(f);
+ } else {
+ callback.callback_chain.unshift(f);
+ }
+ return callback;
+ };
+ callback.add_first = function(f) {
+ return callback.add.apply(null,arguments);
+ };
+ callback.add_last = function(f) {
+ return callback.add({
+ callback: f,
+ args: Array.prototype.slice.call(arguments, 1),
+ position: "last"
+ });
+ };
+
+ return callback.add({
+ callback: method,
+ self:obj,
+ args:Array.prototype.slice.call(arguments, 2)
+ });
+};
+
+/**
+ * Generates an inherited class that replaces all the methods by null methods (methods
+ * that does nothing and always return undefined).
+ *
+ * @param {Class} claz
+ * @param {dict} add Additional functions to override.
+ * @return {Class}
+ */
+openerp.base.generate_null_object_class = function(claz, add) {
+ var newer = {};
+ var copy_proto = function(prototype) {
+ for (var name in prototype) {
+ if(typeof prototype[name] == "function") {
+ newer[name] = function() {};
+ }
+ }
+ if (prototype.prototype)
+ copy_proto(prototype.prototype);
+ };
+ copy_proto(claz.prototype);
+ newer.init = openerp.base.Widget.prototype.init;
+ var tmpclass = claz.extend(newer);
+ return tmpclass.extend(add || {});
+};
+
+/**
+ * Base error for lookup failure
+ *
+ * @class
+ */
+openerp.base.NotFound = openerp.base.Class.extend( /** @lends openerp.base.NotFound# */ {
+});
+openerp.base.KeyNotFound = openerp.base.NotFound.extend( /** @lends openerp.base.KeyNotFound# */ {
+ /**
+ * Thrown when a key could not be found in a mapping
+ *
+ * @constructs
+ * @extends openerp.base.NotFound
+ * @param {String} key the key which could not be found
+ */
+ init: function (key) {
+ this.key = key;
+ },
+ toString: function () {
+ return "The key " + this.key + " was not found";
+ }
+});
+openerp.base.ObjectNotFound = openerp.base.NotFound.extend( /** @lends openerp.base.ObjectNotFound# */ {
+ /**
+ * Thrown when an object path does not designate a valid class or object
+ * in the openerp hierarchy.
+ *
+ * @constructs
+ * @extends openerp.base.NotFound
+ * @param {String} path the invalid object path
+ */
+ init: function (path) {
+ this.path = path;
+ },
+ toString: function () {
+ return "Could not find any object of path " + this.path;
+ }
+});
+openerp.base.Registry = openerp.base.Class.extend( /** @lends openerp.base.Registry# */ {
+ /**
+ * Stores a mapping of arbitrary key (strings) to object paths (as strings
+ * as well).
+ *
+ * Resolves those paths at query time in order to always fetch the correct
+ * object, even if those objects have been overloaded/replaced after the
+ * registry was created.
+ *
+ * An object path is simply a dotted name from the openerp root to the
+ * object pointed to (e.g. ``"openerp.base.Session"`` for an OpenERP
+ * session object).
+ *
+ * @constructs
+ * @param {Object} mapping a mapping of keys to object-paths
+ */
+ init: function (mapping) {
+ this.map = mapping || {};
+ },
+ /**
+ * Retrieves the object matching the provided key string.
+ *
+ * @param {String} key the key to fetch the object for
+ * @returns {Class} the stored class, to initialize
+ *
+ * @throws {openerp.base.KeyNotFound} if the object was not in the mapping
+ * @throws {openerp.base.ObjectNotFound} if the object path was invalid
+ */
+ get_object: function (key) {
+ var path_string = this.map[key];
+ if (path_string === undefined) {
+ throw new openerp.base.KeyNotFound(key);
+ }
+
+ var object_match = openerp;
+ var path = path_string.split('.');
+ // ignore first section
+ for(var i=1; i<path.length; ++i) {
+ object_match = object_match[path[i]];
+
+ if (object_match === undefined) {
+ throw new openerp.base.ObjectNotFound(path_string);
+ }
+ }
+ return object_match;
+ },
+ /**
+ * Tries a number of keys, and returns the first object matching one of
+ * the keys.
+ *
+ * @param {Array} keys a sequence of keys to fetch the object for
+ * @returns {Class} the first class found matching an object
+ *
+ * @throws {openerp.base.KeyNotFound} if none of the keys was in the mapping
+ * @trows {openerp.base.ObjectNotFound} if a found object path was invalid
+ */
+ get_any: function (keys) {
+ for (var i=0; i<keys.length; ++i) {
+ try {
+ return this.get_object(keys[i]);
+ } catch (e) {
+ if (e instanceof openerp.base.KeyNotFound) {
+ continue;
+ }
+ throw e;
+ }
+ }
+ throw new openerp.base.KeyNotFound(keys.join(','));
+ },
+ /**
+ * Adds a new key and value to the registry.
+ *
+ * This method can be chained.
+ *
+ * @param {String} key
+ * @param {String} object_path fully qualified dotted object path
+ * @returns {openerp.base.Registry} itself
+ */
+ add: function (key, object_path) {
+ this.map[key] = object_path;
+ return this;
+ },
+ /**
+ * Creates and returns a copy of the current mapping, with the provided
+ * mapping argument added in (replacing existing keys if needed)
+ *
+ * @param {Object} [mapping={}] a mapping of keys to object-paths
+ */
+ clone: function (mapping) {
+ return new openerp.base.Registry(
+ _.extend({}, this.map, mapping || {}));
+ }
+});
+
+/**
+ * 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;
+
+ // Transform on_* method into openerp.base.callbacks
+ for (var name in this) {
+ if(typeof(this[name]) == "function") {
+ this[name].debug_name = name;
+ // bind ALL function to this not only on_and _do ?
+ if((/^on_|^do_/).test(name)) {
+ this[name] = openerp.base.callback(this, this[name]);
+ }
+ }
+ }
+ },
+ /**
+ * Performs a JSON-RPC call
+ *
+ * @param {String} url endpoint url
+ * @param {Object} data RPC parameters
+ * @param {Function} success RPC call success callback
+ * @param {Function} error RPC call error callback
+ * @returns {jQuery.Deferred} deferred object for the RPC call
+ */
+ rpc: function(url, data, success, error) {
+ return this.session.rpc(url, data, success, error);
+ },
+ log: function() {
+ var args = Array.prototype.slice.call(arguments);
+ var caller = arguments.callee.caller;
+ // TODO add support for line number using
+ // https://github.com/emwendelin/javascript-stacktrace/blob/master/stacktrace.js
+ // args.unshift("" + caller.debug_name);
+ this.on_log.apply(this,args);
+ },
+ on_log: function() {
+ if(window.openerp.debug || (window.location.search.indexOf('?debug') !== -1)) {
+ var notify = false;
+ var body = false;
+ if(window.console) {
+ console.log(arguments);
+ } else {
+ body = true;
+ }
+ var a = Array.prototype.slice.call(arguments, 0);
+ for(var i = 0; i < a.length; i++) {
+ var v = a[i]==null ? "null" : a[i].toString();
+ if(i==0) {
+ notify = v.match(/^not/);
+ body = v.match(/^bod/);
+ }
+ if(body) {
+ $('<pre></pre>').text(v).appendTo($('body'));
+ }
+ if(notify && this.notification) {
+ this.notification.notify("Logging:",v);
+ }
+ }
+ }
+ }
+});
+
+/**
+ * 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
+ * redefined in subclasses or the default render() method can not be used.
+ *
+ * @type string
+ */
+ template: null,
+ /**
+ * The prefix used to generate an id automatically. Should be redefined in
+ * subclasses. If it is not defined, a generic identifier will be used.
+ *
+ * @type string
+ */
+ identifier_prefix: 'generic-identifier-',
+ /**
+ * Construct the widget and set its parent if a parent is given.
+ *
+ * @constructs
+ * @param {openerp.base.Widget} parent Binds the current instance to the given Widget instance.
+ * When that widget is destroyed by calling stop(), the current instance will be
+ * destroyed too. Can be null.
+ * @param {String} element_id Deprecated. Sets the element_id. Only useful when you want
+ * to bind the current Widget to an already existing part of the DOM, which is not compatible
+ * with the DOM insertion methods provided by the current implementation of Widget. So
+ * for new components this argument should not be provided any more.
+ */
+ init: function(parent, /** @deprecated */ element_id) {
+ this._super((parent || {}).session);
+ // if given an element_id, try to get the associated DOM element and save
+ // a reference in this.$element. Else just generate a unique identifier.
+ this.element_id = element_id;
+ this.element_id = this.element_id || _.uniqueId(this.identifier_prefix);
+ var tmp = document.getElementById(this.element_id);
+ this.$element = tmp ? $(tmp) : undefined;
+
+ this.widget_parent = parent;
+ this.widget_children = [];
+ if(parent && parent.widget_children) {
+ parent.widget_children.push(this);
+ }
+ // useful to know if the widget was destroyed and should not be used anymore
+ this.widget_is_stopped = false;
+ },
+ /**
+ * Render the current widget and appends it to the given jQuery object or Widget.
+ *
+ * @param target A jQuery object or a Widget instance.
+ */
+ appendTo: function(target) {
+ var self = this;
+ return this._render_and_insert(function(t) {
+ self.$element.appendTo(t);
+ }, target);
+ },
+ /**
+ * Render the current widget and prepends it to the given jQuery object or Widget.
+ *
+ * @param target A jQuery object or a Widget instance.
+ */
+ prependTo: function(target) {
+ var self = this;
+ return this._render_and_insert(function(t) {
+ self.$element.prependTo(t);
+ }, target);
+ },
+ /**
+ * Render the current widget and inserts it after to the given jQuery object or Widget.
+ *
+ * @param target A jQuery object or a Widget instance.
+ */
+ insertAfter: function(target) {
+ var self = this;
+ return this._render_and_insert(function(t) {
+ self.$element.insertAfter(t);
+ }, target);
+ },
+ /**
+ * Render the current widget and inserts it before to the given jQuery object or Widget.
+ *
+ * @param target A jQuery object or a Widget instance.
+ */
+ insertBefore: function(target) {
+ var self = this;
+ return this._render_and_insert(function(t) {
+ self.$element.insertBefore(t);
+ }, target);
+ },
+ _render_and_insert: function(insertion, target) {
+ var rendered = this.render();
+ this.$element = $(rendered);
+ if (target instanceof openerp.base.Widget)
+ target = target.$element;
+ insertion(target);
+ return this.start();
+ },
+ /**
+ * Renders the widget using QWeb, `this.template` must be defined.
+ * The context given to QWeb contains the "widget" key that references `this`.
+ *
+ * @param {Object} additional Additional context arguments to pass to the template.
+ */
+ render: function (additional) {
+ return QWeb.render(this.template, _.extend({widget: this}, additional || {}));
+ },
+ /**
+ * Method called after rendering. Mostly used to bind actions, perform asynchronous
+ * calls, etc...
+ *
+ * By convention, the method should return a promise to inform the caller when
+ * this widget has been initialized.
+ *
+ * @returns {jQuery.Deferred}
+ */
+ start: function() {
+ if (!this.$element) {
+ var tmp = document.getElementById(this.element_id);
+ this.$element = tmp ? $(tmp) : undefined;
+ }
+ return $.Deferred().done().promise();
+ },
+ /**
+ * Destroys the current widget, also destory all its children before destroying itself.
+ */
+ stop: function() {
+ _.each(_.clone(this.widget_children), function(el) {
+ el.stop();
+ });
+ if(this.$element != null) {
+ this.$element.remove();
+ }
+ if (this.widget_parent && this.widget_parent.widget_children) {
+ this.widget_parent.widget_children = _.without(this.widget_parent.widget_children, this);
+ }
+ this.widget_parent = null;
+ this.widget_is_stopped = true;
+ },
+ /**
+ * Inform the action manager to do an action. Of course, this suppose that
+ * the action manager can be found amongst the ancestors of the current widget.
+ * If that's not the case this method will simply return `false`.
+ */
+ do_action: function(action, on_finished) {
+ if (this.widget_parent) {
+ return this.widget_parent.do_action(action, on_finished);
+ }
+ return false;
+ },
+ rpc: function(url, data, success, error) {
+ var def = $.Deferred().then(success, error);
+ var self = this;
+ this._super(url, data). then(function() {
+ if (!self.widget_is_stopped)
+ def.resolve.apply(def, arguments);
+ }, function() {
+ if (!self.widget_is_stopped)
+ def.reject.apply(def, arguments);
+ });
+ return def.promise();
+ }
+});
+
+/**
+ * @deprecated
+ * For retro compatibility only, the only difference with is that render() uses
+ * directly `this` instead of context with a "widget" key.
+ */
+openerp.base.OldWidget = openerp.base.Widget.extend({
+ render: function (additional) {
+ return QWeb.render(this.template, _.extend(_.extend({}, this), additional || {}));
+ }
+});
+
+};
+// 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 2e89dacceb8..5545e32df17 100644
--- a/addons/base/static/src/js/data.js
+++ b/addons/base/static/src/js/data.js
@@ -18,7 +18,7 @@ openerp.base.serialize_sort = function (criterion) {
}).join(', ');
};
-openerp.base.DataGroup = openerp.base.Controller.extend( /** @lends openerp.base.DataGroup# */{
+openerp.base.DataGroup = openerp.base.Widget.extend( /** @lends openerp.base.DataGroup# */{
/**
* Management interface between views and grouped collections of OpenERP
* records.
@@ -30,7 +30,7 @@ openerp.base.DataGroup = openerp.base.Controller.extend( /** @lends openerp.bas
* content of the current grouping level.
*
* @constructs
- * @extends openerp.base.Controller
+ * @extends openerp.base.Widget
*
* @param {openerp.base.Session} session Current OpenERP session
* @param {String} model name of the model managed by this DataGroup
@@ -232,13 +232,13 @@ openerp.base.StaticDataGroup = openerp.base.GrouplessDataGroup.extend( /** @lend
}
});
-openerp.base.DataSet = openerp.base.Controller.extend( /** @lends openerp.base.DataSet# */{
+openerp.base.DataSet = openerp.base.Widget.extend( /** @lends openerp.base.DataSet# */{
/**
* DateaManagement interface between views and the collection of selected
* OpenERP records (represents the view's state?)
*
* @constructs
- * @extends openerp.base.Controller
+ * @extends openerp.base.Widget
*
* @param {String} model the OpenERP model this dataset will manage
*/
@@ -291,14 +291,18 @@ openerp.base.DataSet = openerp.base.Controller.extend( /** @lends openerp.base.
* Read the indexed record.
*/
read_index: function (fields, callback) {
+ var def = $.Deferred().then(callback);
if (_.isEmpty(this.ids)) {
- return $.Deferred().reject().promise();
+ def.reject();
} else {
fields = fields || false;
- return this.read_ids([this.ids[this.index]], fields, function(records) {
- callback(records[0]);
+ return this.read_ids([this.ids[this.index]], fields).then(function(records) {
+ def.resolve(records[0]);
+ }, function() {
+ def.reject.apply(def, arguments);
});
}
+ return def.promise();
},
default_get: function(fields, callback) {
return this.rpc('/base/dataset/default_get', {
@@ -492,24 +496,33 @@ openerp.base.BufferedDataSet = openerp.base.DataSetStatic.extend({
this.to_create.push(cached);
this.cache.push(cached);
var to_return = $.Deferred().then(callback);
- setTimeout(function() {to_return.resolve({result: cached.id});}, 0);
+ to_return.resolve({result: cached.id});
return to_return.promise();
},
write: function (id, data, callback) {
var self = this;
var record = _.detect(this.to_create, function(x) {return x.id === id;});
record = record || _.detect(this.to_write, function(x) {return x.id === id;});
+ var dirty = false;
if (record) {
+ for (k in data) {
+ if (record.values[k] === undefined || record.values[k] !== data[k]) {
+ dirty = true;
+ break;
+ }
+ }
$.extend(record.values, data);
} else {
+ dirty = true;
record = {id: id, values: data};
self.to_write.push(record);
}
var cached = _.detect(this.cache, function(x) {return x.id === id;});
$.extend(cached.values, record.values);
- this.on_change();
+ if (dirty)
+ this.on_change();
var to_return = $.Deferred().then(callback);
- setTimeout(function () {to_return.resolve({result: true});}, 0);
+ to_return.resolve({result: true});
return to_return.promise();
},
unlink: function(ids, callback, error_callback) {
diff --git a/addons/base/static/src/js/data_export.js b/addons/base/static/src/js/data_export.js
new file mode 100644
index 00000000000..ad440302c69
--- /dev/null
+++ b/addons/base/static/src/js/data_export.js
@@ -0,0 +1,402 @@
+openerp.base.data_export = function(openerp) {
+openerp.base.DataExport = openerp.base.Dialog.extend({
+ init: function(parent, dataset) {
+ this._super(parent);
+ this.dataset = dataset;
+ },
+ start: function() {
+ var self = this;
+ self._super(false);
+ self.template = 'ExportTreeView';
+ self.dialog_title = "Export Data";
+ self.open({
+ modal: true,
+ width: '55%',
+ height: 'auto',
+ position: 'top',
+ buttons : {
+ "Close" : function() {
+ self.close();
+ },
+ "Export To File" : function() {
+ self.on_click_export_data();
+ }
+ },
+ close: function(event, ui){ self.close();}
+ });
+ self.on_show_exists_export_list();
+ self.$element.removeClass('ui-dialog-content ui-widget-content');
+ self.$element.find('#add_field').click(function() {
+ if ($('#field-tree-structure tr.ui-selected')) {
+ var fld = self.$element.find('#field-tree-structure tr.ui-selected').find('a');
+ for (var i = 0; i < fld.length; i++) {
+ var id = $(fld[i]).attr('id').split('-')[1];
+ var string = $(fld[i]).attr('string');
+ self.add_field(id, string);
+ }
+ self.$element.find('#field-tree-structure tr').removeClass('ui-selected');
+ }
+ });
+ self.$element.find('#remove_field').click(function() {
+ self.$element.find('#fields_list option:selected').remove();
+ });
+ self.$element.find('#remove_all_field').click(function() {
+ self.$element.find('#fields_list option').remove();
+ });
+ self.$element.find('#export_new_list').click(function() {
+ self.on_show_save_list();
+ });
+ var import_comp = self.$element.find('#import_compat option:selected').val(),
+ params = {
+ import_compat: parseInt(import_comp)
+ };
+ self.rpc('/base/export/get_fields', { model: self.dataset.model, params: params }, self.on_show_data);
+
+ self.$element.find('#import_compat').change(function() {
+ self.$element.find('#fields_list option').remove();
+ self.$element.find('#field-tree-structure').remove();
+ var import_comp = self.$element.find("#import_compat option:selected").val();
+ if (import_comp) {
+ var params = {
+ import_compat: parseInt(import_comp)
+ }
+ self.rpc("/base/export/get_fields", { model: self.dataset.model, params: params}, self.on_show_data);
+ }
+ });
+ },
+ on_show_exists_export_list: function() {
+ var self = this;
+ if (self.$element.find('#saved_export_list').is(':hidden')) {
+ self.$element.find('#ExistsExportList').show();
+ } else {
+ this.rpc('/base/export/exist_export_lists', { 'model': this.dataset.model}, function(export_list) {
+ if (export_list.length) {
+ self.$element.find('#ExistsExportList').append(QWeb.render('Exists.ExportList', {'existing_exports': export_list}));
+ self.$element.find('#saved_export_list').change(function() {
+ self.$element.find('#fields_list option').remove();
+ var export_id = self.$element.find('#saved_export_list option:selected').val();
+ if (export_id) {
+ self.rpc('/base/export/namelist', {'model': self.dataset.model, export_id: parseInt(export_id)}, self.do_load_export_field);
+ }
+ });
+ self.$element.find('#delete_export_list').click(function() {
+ var select_exp = self.$element.find('#saved_export_list option:selected');
+ if (select_exp.val()) {
+ self.rpc('/base/export/delete_export', { export_id: parseInt(select_exp.val())}, {});
+ select_exp.remove();
+ if (self.$element.find('#saved_export_list option').length <= 1) {
+ self.$element.find('#ExistsExportList').hide();
+ }
+ }
+ });
+ }
+ });
+ }
+ },
+ do_load_export_field: function(field_list) {
+ var export_node = this.$element.find("#fields_list");
+ for (var key in field_list) {
+ export_node.append(new Option(field_list[key], key));
+ }
+ },
+ on_show_save_list: function() {
+ var self = this;
+ var current_node = self.$element.find("#savenewlist");
+ if (!(current_node.find("label")).length) {
+ current_node.append(QWeb.render('ExportNewList'));
+ current_node.find("#add_export_list").click(function() {
+ var value = current_node.find("#savelist_name").val();
+ if (value) {
+ self.do_save_export_list(value);
+ } else {
+ alert("Pleae Enter Save Field List Name");
+ }
+ });
+ } else {
+ if (current_node.is(':hidden')) {
+ current_node.show();
+ current_node.find("#savelist_name").val("");
+ } else {
+ current_node.hide();
+ }
+ }
+ },
+ do_save_export_list: function(value) {
+ var self = this;
+ var export_field = self.get_fields();
+ if (export_field.length) {
+ self.rpc("/base/export/save_export_lists", {"model": self.dataset.model, "name":value, "field_list":export_field}, function(exp_id) {
+ if (exp_id) {
+ if (self.$element.find("#saved_export_list").length > 0) {
+ self.$element.find("#saved_export_list").append(new Option(value, exp_id));
+ } else {
+ self.on_show_exists_export_list();
+ }
+ if (self.$element.find("#saved_export_list").is(":hidden")) {
+ self.on_show_exists_export_list();
+ }
+ }
+ });
+ self.on_show_save_list();
+ self.$element.find("#fields_list option").remove();
+ }
+ },
+ on_click: function(id, result) {
+ var self = this;
+ self.field_id = id.split("-")[1];
+ var is_loaded = 0;
+ _.each(result, function(record) {
+ if (record['id'] == self.field_id && (record['children']).length >= 1) {
+ var model = record['params']['model'],
+ prefix = record['params']['prefix'],
+ name = record['params']['name'];
+ $(record['children']).each(function(e, childid) {
+ if (self.$element.find("tr[id='treerow-" + childid + "']").length > 0) {
+ if (self.$element.find("tr[id='treerow-" + childid + "']").is(':hidden')) {
+ is_loaded = -1;
+ } else {
+ is_loaded++;
+ }
+ }
+ });
+ if (is_loaded == 0) {
+ if (self.$element.find("tr[id='treerow-" + self.field_id +"']").find('img').attr('src') === '/base/static/src/img/expand.gif') {
+ if (model) {
+ var import_comp = self.$element.find("#import_compat option:selected").val();
+ var params = {
+ import_compat: parseInt(import_comp),
+ parent_field_type : record['field_type']
+ }
+ self.rpc("/base/export/get_fields", {
+ model: model,
+ prefix: prefix,
+ name: name,
+ field_parent : self.field_id,
+ params: params
+ }, function(results) {
+ self.on_show_data(results);
+ });
+ }
+ }
+ } else if (is_loaded > 0) {
+ self.showcontent(self.field_id, true);
+ } else {
+ self.showcontent(self.field_id, false);
+ }
+ }
+ });
+ },
+ on_show_data: function(result) {
+ var self = this;
+ var imp_cmpt = parseInt(self.$element.find("#import_compat option:selected").val());
+ var current_tr = self.$element.find("tr[id='treerow-" + self.field_id + "']");
+ if (current_tr.length >= 1) {
+ current_tr.find('img').attr('src','/base/static/src/img/collapse.gif');
+ current_tr.after(QWeb.render('ExportTreeView-Secondary.children', {'fields': result}));
+ } else {
+ self.$element.find('#left_field_panel').append(QWeb.render('ExportTreeView-Secondary', {'fields': result}));
+ }
+ _.each(result, function(record) {
+ if ((record.field_type == "one2many") && imp_cmpt) {
+ var o2m_fld = self.$element.find("tr[id='treerow-" + record.id + "']").find('#tree-column');
+ o2m_fld.addClass("oe_export_readonlyfield");
+ }
+ if ((record.required == true) || record.required == "True") {
+ var required_fld = self.$element.find("tr[id='treerow-" + record.id + "']").find('#tree-column');
+ required_fld.addClass("oe_export_requiredfield");
+ }
+ self.$element.find("img[id='parentimg-" + record.id +"']").click(function() {
+ self.on_click(this.id, result);
+ });
+
+ self.$element.find("tr[id='treerow-" + record.id + "']").click(function(e) {
+ if (e.shiftKey == true) {
+ var frst_click, scnd_click = '';
+ if (self.row_index == 0) {
+ self.row_index = this.rowIndex;
+ frst_click = self.$element.find("tr[id^='treerow-']")[self.row_index-1];
+ $(frst_click).addClass("ui-selected");
+ } else {
+ if (this.rowIndex >=self.row_index) {
+ for (i = (self.row_index-1); i < this.rowIndex; i++) {
+ scnd_click = self.$element.find("tr[id^='treerow-']")[i];
+ if (!$(scnd_click).find('#tree-column').hasClass("oe_export_readonlyfield")) {
+ $(scnd_click).addClass("ui-selected");
+ }
+ }
+ } else {
+ for (i = (self.row_index-1); i >= (this.rowIndex-1); i--) {
+ scnd_click = self.$element.find("tr[id^='treerow-']")[i];
+ if (!$(scnd_click).find('#tree-column').hasClass("oe_export_readonlyfield")) {
+ $(scnd_click).addClass("ui-selected");
+ }
+ }
+ }
+ }
+ }
+ self.row_index = this.rowIndex;
+
+ self.$element.find("tr[id='treerow-" + record.id + "']").keyup(function(e) {
+ self.row_index = 0;
+ });
+ var o2m_selection = self.$element.find("tr[id='treerow-" + record.id + "']").find('#tree-column');
+ if ($(o2m_selection).hasClass("oe_export_readonlyfield")) {
+ return false;
+ }
+ var selected = self.$element.find("tr.ui-selected");
+ if ($(this).hasClass("ui-selected") && (e.ctrlKey == true)) {
+ $(this).find('a').blur();
+ $(this).removeClass("ui-selected");
+ } else if ($(this).hasClass("ui-selected") && (e.ctrlKey == false) && (e.shiftKey == false)) {
+ selected.find('a').blur();
+ selected.removeClass("ui-selected");
+ $(this).find('a').focus();
+ $(this).addClass("ui-selected");
+ } else if (!$(this).hasClass("ui-selected") && (e.ctrlKey == false) && (e.shiftKey == false)) {
+ selected.find('a').blur();
+ selected.removeClass("ui-selected");
+ $(this).find('a').focus();
+ $(this).addClass("ui-selected");
+ } else if (!$(this).hasClass("ui-selected") && (e.ctrlKey == true)) {
+ $(this).find('a').focus();
+ $(this).addClass("ui-selected");
+ }
+ return false;
+ });
+
+ self.$element.find("tr[id='treerow-" + record.id + "']").keydown(function(e) {
+ var keyCode = e.keyCode || e.which;
+ arrow = {left: 37, up: 38, right: 39, down: 40 };
+ switch (keyCode) {
+ case arrow.left:
+ if ($(this).find('img').attr('src') === '/base/static/src/img/collapse.gif') {
+ self.on_click(this.id, result);
+ }
+ break;
+ case arrow.up:
+ var elem = this;
+ $(elem).removeClass("ui-selected");
+ while ($(elem).prev().is(":visible") == false) {
+ elem = $(elem).prev();
+ }
+ if (!$(elem).prev().find('#tree-column').hasClass("oe_export_readonlyfield")) {
+ $(elem).prev().addClass("ui-selected");
+ }
+ $(elem).prev().find('a').focus();
+ break;
+ case arrow.right:
+ if ($(this).find('img').attr('src') == '/base/static/src/img/expand.gif') {
+ self.on_click(this.id, result);
+ }
+ break;
+ case arrow.down:
+ var elem = this;
+ $(elem).removeClass("ui-selected");
+ while($(elem).next().is(":visible") == false) {
+ elem = $(elem).next();
+ }
+ if (!$(elem).next().find('#tree-column').hasClass("oe_export_readonlyfield")) {
+ $(elem).next().addClass("ui-selected");
+ }
+ $(elem).next().find('a').focus();
+ break;
+ }
+ });
+ self.$element.find("tr[id='treerow-" + record.id + "']").dblclick(function(e) {
+ var $o2m_selection = self.$element.find("tr[id^='treerow-" + record.id + "']").find('#tree-column');
+ if (!$o2m_selection.hasClass("oe_export_readonlyfield")) {
+ var field_id = $(this).find("a").attr("id");
+ if (field_id) {
+ self.add_field(field_id.split('-')[1], $(this).find("a").attr("string"));
+ }
+ }
+ });
+ });
+ self.$element.find('#fields_list').mouseover(function(event) {
+ if (event.relatedTarget) {
+ if (event.relatedTarget.attributes['id'] && event.relatedTarget.attributes['string']) {
+ var field_id = event.relatedTarget.attributes["id"]["value"];
+ if (field_id && field_id.split("-")[0] === 'export') {
+ if (!self.$element.find("tr[id='treerow-" + field_id.split("-")[1] + "']").find('#tree-column').hasClass("oe_export_readonlyfield")) {
+ self.add_field(field_id.split("-")[1], event.relatedTarget.attributes["string"]["value"]);
+ }
+ }
+ }
+ }
+ });
+ },
+ showcontent: function(id, flag) {
+ // show & hide the contents
+ var first_child = this.$element.find("tr[id='treerow-" + id + "']").find('img');
+ if (flag) {
+ first_child.attr('src', '/base/static/src/img/expand.gif');
+ }
+ else {
+ first_child.attr('src', '/base/static/src/img/collapse.gif');
+ }
+ var child_field = this.$element.find("tr[id^='treerow-" + id +"/']");
+ var child_len = (id.split("/")).length + 1;
+ for (var i = 0; i < child_field.length; i++) {
+ if (flag) {
+ $(child_field[i]).hide();
+ } else {
+ if (child_len == (child_field[i].id.split("/")).length) {
+ if ($(child_field[i]).find('img').attr('src') == '/base/static/src/img/collapse.gif') {
+ $(child_field[i]).find('img').attr('src', '/base/static/src/img/expand.gif');
+ }
+ $(child_field[i]).show();
+ }
+ }
+ }
+ },
+ add_field: function(field_id, string) {
+ var field_list = this.$element.find('#fields_list');
+ if (this.$element.find("#fields_list option[value='" + field_id + "']") && !this.$element.find("#fields_list option[value='" + field_id + "']").length) {
+ field_list.append(new Option(string, field_id));
+ }
+ },
+ get_fields: function() {
+ var export_field = [];
+ this.$element.find("#fields_list option").each(function() {
+ export_field.push($(this).val());
+ });
+ if (!export_field.length) {
+ alert('Please select fields to save export list...');
+ }
+ return export_field;
+ },
+ on_click_export_data: function() {
+ var self = this;
+ var export_field = {};
+ var flag = true;
+ self.$element.find("#fields_list option").each(function() {
+ export_field[$(this).val()] = $(this).text();
+ flag = false;
+ });
+ if (flag) {
+ alert('Please select fields to export...');
+ return;
+ }
+
+ var import_comp = self.$element.find("#import_compat option:selected").val(),
+ export_format = self.$element.find("#export_format").val();
+
+ self.rpc("/base/export/export_data", {
+ model: self.dataset.model,
+ fields: export_field,
+ ids: self.dataset.ids,
+ domain: self.dataset.domain,
+ import_compat: parseInt(import_comp),
+ export_format: export_format
+ }, function(data) {
+ window.location = "data:text/csv/excel;charset=utf8," + data;
+ self.close();
+ });
+ },
+ close: function() {
+ $(this.$dialog).remove();
+ this._super();
+ }
+});
+
+};
diff --git a/addons/base/static/src/js/form.js b/addons/base/static/src/js/form.js
index e66a073643c..f8e2e91667d 100644
--- a/addons/base/static/src/js/form.js
+++ b/addons/base/static/src/js/form.js
@@ -20,7 +20,6 @@ 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.view_manager = parent || new openerp.base.NullViewManager();
this.dataset = dataset;
this.model = dataset.model;
this.view_id = view_id;
@@ -31,8 +30,7 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormVi
this.datarecord = {};
this.ready = false;
this.show_invalid = true;
- this.touched = false;
- this.flags = this.view_manager.flags || {};
+ this.dirty = false;
this.default_focus_field = null;
this.default_focus_button = null;
this.registry = openerp.base.form.widgets;
@@ -50,9 +48,6 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormVi
return def.promise();
} else {
var context = new openerp.base.CompoundContext(this.dataset.get_context());
- if (this.view_manager.action && this.view_manager.action.context) {
- context.add(this.view_manager.action.context);
- }
return this.rpc("/base/formview/load", {"model": this.model, "view_id": this.view_id,
toolbar: this.options.sidebar, context: context}, this.on_loaded);
}
@@ -93,9 +88,10 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormVi
if (this.options.sidebar && this.options.sidebar_id) {
this.sidebar = new openerp.base.Sidebar(this, this.options.sidebar_id);
this.sidebar.start();
- this.sidebar.attachments = new openerp.base.form.SidebarAttachments(this.sidebar, this.sidebar.add_section("Attachments"), this);
- this.sidebar.add_toolbar(data.fields_view.toolbar);
this.sidebar.do_unfold();
+ this.sidebar.attachments = new openerp.base.form.SidebarAttachments(this.sidebar, this.sidebar.add_section('attachments', "Attachments"), this);
+ this.sidebar.add_toolbar(data.fields_view.toolbar);
+ this.set_common_sidebar_sections(this.sidebar);
}
this.has_been_loaded.resolve();
},
@@ -134,22 +130,22 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormVi
this.$form_header.find('.oe_form_on_update').show();
this.$form_header.find('button.oe_form_button_new').show();
}
- this.touched = false;
+ this.dirty = false;
this.datarecord = record;
for (var f in this.fields) {
var field = this.fields[f];
- field.touched = false;
+ field.dirty = false;
field.set_value(this.datarecord[f] || false);
field.validate();
}
if (!record.id) {
// New record: Second pass in order to trigger the onchanges
- this.touched = true;
+ this.dirty = true;
this.show_invalid = false;
for (var f in record) {
var field = this.fields[f];
if (field) {
- field.touched = true;
+ field.dirty = true;
this.do_onchange(field);
}
}
@@ -268,7 +264,7 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormVi
processed.push(field.name);
if (field.get_value() != value) {
field.set_value(value);
- field.touched = true;
+ field.dirty = true;
if (_.indexOf(processed, field.name) < 0) {
this.do_onchange(field, processed);
}
@@ -316,37 +312,40 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormVi
if (!this.ready) {
return false;
}
- var invalid = false,
+ var form_dirty = false,
+ form_invalid = false,
values = {},
first_invalid_field = null;
for (var f in this.fields) {
f = this.fields[f];
- if (f.invalid) {
- invalid = true;
+ if (!f.is_valid()) {
+ form_invalid = true;
f.update_dom();
if (!first_invalid_field) {
first_invalid_field = f;
}
- } else if (f.touched) {
+ } else if (f.is_dirty()) {
+ form_dirty = true;
values[f.name] = f.get_value();
}
}
- if (invalid) {
+ if (form_invalid) {
first_invalid_field.focus();
this.on_invalid();
return false;
- } else {
+ } else if (form_dirty) {
this.log("About to save", values);
if (!this.datarecord.id) {
- this.dataset.create(values, function(r) {
+ return this.dataset.create(values, function(r) {
self.on_created(r, success, prepend_on_create);
});
} else {
- this.dataset.write(this.datarecord.id, values, function(r) {
+ return this.dataset.write(this.datarecord.id, values, function(r) {
self.on_saved(r, success);
});
}
- return true;
+ } else {
+ return false;
}
},
do_save_edit: function() {
@@ -360,7 +359,7 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormVi
on_invalid: function() {
var msg = "<ul>";
_.each(this.fields, function(f) {
- if (f.invalid) {
+ if (!f.is_valid()) {
msg += "<li>" + f.string + "</li>";
}
});
@@ -443,7 +442,7 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormVi
/** @namespace */
openerp.base.form = {};
-openerp.base.form.SidebarAttachments = openerp.base.Controller.extend({
+openerp.base.form.SidebarAttachments = openerp.base.Widget.extend({
init: function(parent, element_id, form_view) {
this._super(parent, element_id);
this.view = form_view;
@@ -550,7 +549,7 @@ openerp.base.form.compute_domain = function(expr, fields) {
return _.all(stack);
};
-openerp.base.form.Widget = openerp.base.Controller.extend({
+openerp.base.form.Widget = openerp.base.Widget.extend({
template: 'Widget',
init: function(view, node) {
this.view = view;
@@ -716,6 +715,10 @@ openerp.base.form.WidgetButton = openerp.base.form.Widget.extend({
init: function(view, node) {
this._super(view, node);
this.template = "WidgetButton";
+ if (this.string) {
+ // We don't have button key bindings in the webclient
+ this.string = this.string.replace(/_/g, '');
+ }
if (node.attrs.default_focus == '1') {
// TODO fme: provide enter key binding to widgets
this.view.default_focus_button = this;
@@ -727,7 +730,7 @@ openerp.base.form.WidgetButton = openerp.base.form.Widget.extend({
},
on_click: function(saved) {
var self = this;
- if (!this.node.attrs.special && this.view.touched && saved !== true) {
+ if (!this.node.attrs.special && this.view.dirty && saved !== true) {
this.view.do_save(function() {
self.on_click(true);
});
@@ -772,11 +775,12 @@ openerp.base.form.WidgetLabel = openerp.base.form.Widget.extend({
if (this.node.tag == 'label' && this.node.attrs.colspan) {
this.is_field_label = false;
this.template = "WidgetParagraph";
+ this.colspan = this.node.attrs.colspan;
} else {
this.is_field_label = true;
this.template = "WidgetLabel";
+ this.colspan = 1;
}
- this.colspan = 1;
},
render: function () {
if (this['for'] && this.type !== 'label') {
@@ -807,7 +811,7 @@ openerp.base.form.Field = openerp.base.form.Widget.extend({
this.readonly = this.modifiers['readonly'] === true;
this.required = this.modifiers['required'] === true;
this.invalid = false;
- this.touched = false;
+ this.dirty = false;
},
set_value: function(value) {
this.value = value;
@@ -820,21 +824,29 @@ openerp.base.form.Field = openerp.base.form.Widget.extend({
get_value: function() {
return this.value;
},
+ is_valid: function() {
+ return !this.invalid;
+ },
+ is_dirty: function() {
+ return this.dirty;
+ },
get_on_change_value: function() {
return this.get_value();
},
update_dom: function() {
this._super.apply(this, arguments);
- this.$element.toggleClass('disabled', this.readonly);
- this.$element.toggleClass('required', this.required);
- if (this.view.show_invalid) {
- this.$element.toggleClass('invalid', this.invalid);
+ if (!this.disable_utility_classes) {
+ this.$element.toggleClass('disabled', this.readonly);
+ this.$element.toggleClass('required', this.required);
+ if (this.view.show_invalid) {
+ this.$element.toggleClass('invalid', !this.is_valid());
+ }
}
},
on_ui_change: function() {
- this.touched = this.view.touched = true;
+ this.dirty = this.view.dirty = true;
this.validate();
- if (!this.invalid) {
+ if (this.is_valid()) {
this.set_value_from_ui();
this.view.do_onchange(this);
this.view.on_form_changed();
@@ -932,7 +944,7 @@ openerp.base.form.FieldEmail = openerp.base.form.FieldChar.extend({
this.$element.find('button').click(this.on_button_clicked);
},
on_button_clicked: function() {
- if (!this.value || this.invalid) {
+ if (!this.value || !this.is_valid()) {
this.notification.warn("E-mail error", "Can't send email to invalid e-mail address");
} else {
location.href = 'mailto:' + this.value;
@@ -973,7 +985,7 @@ openerp.base.form.FieldFloat = openerp.base.form.FieldChar.extend({
if (value === false || value === undefined) {
// As in GTK client, floats default to 0
value = 0;
- this.touched = true;
+ this.dirty = true;
}
var show_value = value.toFixed(2);
this.$element.find('input').val(show_value);
@@ -993,7 +1005,7 @@ openerp.base.form.FieldInteger = openerp.base.form.FieldFloat.extend({
if (value === false || value === undefined) {
// TODO fme: check if GTK client default integers to 0 (like it does with floats)
value = 0;
- this.touched = true;
+ this.dirty = true;
}
var show_value = parseInt(value, 10);
this.$element.find('input').val(show_value);
@@ -1080,7 +1092,7 @@ openerp.base.form.FieldFloatTime = openerp.base.form.FieldChar.extend({
if (value === false || value === undefined) {
// As in GTK client, floats default to 0
value = 0;
- this.touched = true;
+ this.dirty = true;
}
var show_value = _.sprintf("%02d:%02d", Math.floor(value), Math.round((value % 1) * 60));
this.$element.find('input').val(show_value);
@@ -1287,7 +1299,6 @@ openerp.base.form.dialog = function(content, options) {
options.autoOpen = true;
var dialog = new openerp.base.Dialog(null, options);
dialog.$dialog = $(content).dialog(dialog.options);
- console.log(dialog.options)
return dialog.$dialog;
}
@@ -1470,7 +1481,7 @@ openerp.base.form.FieldMany2One = openerp.base.form.Field.extend({
}, self.build_domain(),
new openerp.base.CompoundContext(self.build_context(), context || {}));
pop.on_select_elements.add(function(element_ids) {
- var dataset = new openerp.base.DataSetStatic(this, this.field.relation, self.build_context());
+ 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();
@@ -1587,6 +1598,7 @@ openerp.base.form.FieldOne2Many = openerp.base.form.Field.extend({
this.template = "FieldOne2Many";
this.is_started = $.Deferred();
this.form_last_update = $.Deferred();
+ this.disable_utility_classes = true;
},
start: function() {
this._super.apply(this, arguments);
@@ -1632,9 +1644,16 @@ openerp.base.form.FieldOne2Many = openerp.base.form.Field.extend({
controller.on_record_loaded.add_last(function() {
once.resolve();
});
+ controller.on_pager_action.add_first(function() {
+ self.save_form_view();
+ });
+ controller.$element.find(".oe_form_button_save_edit").hide();
}
self.is_started.resolve();
});
+ this.viewmanager.on_mode_switch.add_first(function() {
+ self.save_form_view();
+ });
setTimeout(function () {
self.viewmanager.start();
}, 0);
@@ -1704,6 +1723,9 @@ openerp.base.form.FieldOne2Many = openerp.base.form.Field.extend({
this._super(value);
this.dataset.reset_ids(value);
}
+ if (this.dataset.index === null && this.dataset.ids.length > 0) {
+ this.dataset.index = 0;
+ }
$.when(this.is_started).then(function() {
self.reload_current_view();
});
@@ -1728,9 +1750,50 @@ openerp.base.form.FieldOne2Many = openerp.base.form.Field.extend({
this.dataset.to_delete, function(x) {
return commands['delete'](x.id);}));
},
+ save_form_view: function() {
+ if (this.viewmanager && this.viewmanager.views && this.viewmanager.active_view &&
+ this.viewmanager.views[this.viewmanager.active_view] &&
+ this.viewmanager.views[this.viewmanager.active_view].controller) {
+ var view = this.viewmanager.views[this.viewmanager.active_view].controller;
+ if (this.viewmanager.active_view === "form") {
+ var res = view.do_save();
+ if (res === false) {
+ // ignore
+ } else if (res.isRejected()) {
+ throw "Save or create on one2many dataset is not supposed to fail.";
+ } else if (!res.isResolved()) {
+ throw "Asynchronous get_value() is not supported in form view.";
+ }
+ return res;
+ }
+ }
+ return false;
+ },
+ is_valid: function() {
+ this.validate();
+ return this._super();
+ },
validate: function() {
this.invalid = false;
- // TODO niv
+ var self = this;
+ var view = self.viewmanager.views[self.viewmanager.active_view].controller;
+ if (self.viewmanager.active_view === "form") {
+ for (var f in view.fields) {
+ f = view.fields[f];
+ if (!f.is_valid()) {
+ this.invalid = true;
+ return;
+ }
+ }
+ }
+ },
+ is_dirty: function() {
+ this.save_form_view();
+ return this._super();
+ },
+ update_dom: function() {
+ this._super.apply(this, arguments);
+ this.$element.toggleClass('disabled', this.readonly);
}
});
@@ -1797,7 +1860,7 @@ openerp.base.form.FieldMany2Many = openerp.base.form.Field.extend({
var self = this;
- this.dataset = new openerp.base.form.Many2ManyDataSet( this, this.field.relation);
+ this.dataset = new openerp.base.form.Many2ManyDataSet(this, this.field.relation);
this.dataset.m2m = this;
this.dataset.on_unlink.add_last(function(ids) {
self.on_ui_change();
@@ -1872,7 +1935,7 @@ openerp.base.form.Many2ManyListView = openerp.base.ListView.extend({
}
});
-openerp.base.form.SelectCreatePopup = openerp.base.BaseWidget.extend({
+openerp.base.form.SelectCreatePopup = openerp.base.OldWidget.extend({
identifier_prefix: "selectcreatepopup",
template: "SelectCreatePopup",
/**
@@ -2012,7 +2075,7 @@ openerp.base.form.SelectCreateListView = openerp.base.ListView.extend({
}
});
-openerp.base.form.FormOpenPopup = openerp.base.BaseWidget.extend({
+openerp.base.form.FormOpenPopup = openerp.base.OldWidget.extend({
identifier_prefix: "formopenpopup",
template: "FormOpenPopup",
/**
diff --git a/addons/base/static/src/js/list.js b/addons/base/static/src/js/list.js
index a4e516721ca..d64d83329c0 100644
--- a/addons/base/static/src/js/list.js
+++ b/addons/base/static/src/js/list.js
@@ -111,7 +111,6 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
this.columns = [];
this.options = _.extend({}, this.defaults, options || {});
- this.flags = this.view_manager.flags || {};
this.set_groups(new openerp.base.ListView.Groups(this));
@@ -272,6 +271,7 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
this.sidebar = new openerp.base.Sidebar(this, this.options.sidebar_id);
this.sidebar.start();
this.sidebar.add_toolbar(data.fields_view.toolbar);
+ this.set_common_sidebar_sections(this.sidebar);
}
},
/**
diff --git a/addons/base/static/src/js/search.js b/addons/base/static/src/js/search.js
index 8a51abe1335..763746bb0bf 100644
--- a/addons/base/static/src/js/search.js
+++ b/addons/base/static/src/js/search.js
@@ -1,6 +1,6 @@
openerp.base.search = function(openerp) {
-openerp.base.SearchView = openerp.base.Controller.extend({
+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();
@@ -386,13 +386,13 @@ openerp.base.search.Invalid = openerp.base.Class.extend( /** @lends openerp.base
': [' + this.value + '] is ' + this.message);
}
});
-openerp.base.search.Widget = openerp.base.Controller.extend( /** @lends openerp.base.search.Widget# */{
+openerp.base.search.Widget = openerp.base.Widget.extend( /** @lends openerp.base.search.Widget# */{
template: null,
/**
* Root class of all search widgets
*
* @constructs
- * @extends openerp.base.Controller
+ * @extends openerp.base.Widget
*
* @param view the ancestor view of this widget
*/
@@ -851,7 +851,7 @@ openerp.base.search.ManyToManyField = openerp.base.search.CharField.extend({
// TODO: .related_columns (Array), .context, .domain
});
-openerp.base.search.ExtendedSearch = openerp.base.BaseWidget.extend({
+openerp.base.search.ExtendedSearch = openerp.base.OldWidget.extend({
template: 'SearchView.extended_search',
identifier_prefix: 'extended-search',
init: function (parent, model) {
@@ -860,9 +860,7 @@ openerp.base.search.ExtendedSearch = openerp.base.BaseWidget.extend({
},
add_group: function() {
var group = new openerp.base.search.ExtendedSearchGroup(this, this.fields);
- var render = group.render();
- this.$element.find('.searchview_extended_groups_list').append(render);
- group.start();
+ group.appendTo(this.$element.find('.searchview_extended_groups_list'));
this.check_last_element();
},
start: function () {
@@ -891,7 +889,7 @@ openerp.base.search.ExtendedSearch = openerp.base.BaseWidget.extend({
if(this.$element.closest("table.oe-searchview-render-line").css("display") == "none") {
return null;
}
- return _.reduce(this.children,
+ return _.reduce(this.widget_children,
function(mem, x) { return mem.concat(x.get_domain());}, []);
},
on_activate: function() {
@@ -910,14 +908,14 @@ openerp.base.search.ExtendedSearch = openerp.base.BaseWidget.extend({
}
},
check_last_element: function() {
- _.each(this.children, function(x) {x.set_last_group(false);});
- if (this.children.length >= 1) {
- this.children[this.children.length - 1].set_last_group(true);
+ _.each(this.widget_children, function(x) {x.set_last_group(false);});
+ if (this.widget_children.length >= 1) {
+ this.widget_children[this.widget_children.length - 1].set_last_group(true);
}
}
});
-openerp.base.search.ExtendedSearchGroup = openerp.base.BaseWidget.extend({
+openerp.base.search.ExtendedSearchGroup = openerp.base.OldWidget.extend({
template: 'SearchView.extended_search.group',
identifier_prefix: 'extended-search-group',
init: function (parent, fields) {
@@ -926,7 +924,7 @@ openerp.base.search.ExtendedSearchGroup = openerp.base.BaseWidget.extend({
},
add_prop: function() {
var prop = new openerp.base.search.ExtendedSearchProposition(this, this.fields);
- var render = prop.render({'index': this.children.length - 1});
+ var render = prop.render({'index': this.widget_children.length - 1});
this.$element.find('.searchview_extended_propositions_list').append(render);
prop.start();
},
@@ -943,7 +941,7 @@ openerp.base.search.ExtendedSearchGroup = openerp.base.BaseWidget.extend({
});
},
get_domain: function() {
- var props = _(this.children).chain().map(function(x) {
+ var props = _(this.widget_children).chain().map(function(x) {
return x.get_proposition();
}).compact().value();
var choice = this.$element.find(".searchview_extended_group_choice").val();
@@ -953,9 +951,9 @@ openerp.base.search.ExtendedSearchGroup = openerp.base.BaseWidget.extend({
props);
},
stop: function() {
- var parent = this.parent;
- if (this.parent.children.length == 1)
- this.parent.hide();
+ var parent = this.widget_parent;
+ if (this.widget_parent.widget_children.length == 1)
+ this.widget_parent.hide();
this._super();
parent.check_last_element();
},
@@ -967,7 +965,7 @@ openerp.base.search.ExtendedSearchGroup = openerp.base.BaseWidget.extend({
}
});
-openerp.base.search.ExtendedSearchProposition = openerp.base.BaseWidget.extend({
+openerp.base.search.ExtendedSearchProposition = openerp.base.OldWidget.extend({
template: 'SearchView.extended_search.proposition',
identifier_prefix: 'extended-search-proposition',
init: function (parent, fields) {
@@ -992,9 +990,12 @@ openerp.base.search.ExtendedSearchProposition = openerp.base.BaseWidget.extend({
});
},
stop: function() {
- if (this.parent.children.length == 1)
- this.parent.stop();
+ var parent;
+ if (this.widget_parent.widget_children.length == 1)
+ parent = this.widget_parent;
this._super();
+ if (parent)
+ parent.stop();
},
changed: function() {
var nval = this.$element.find(".searchview_extended_prop_field").val();
@@ -1054,7 +1055,7 @@ openerp.base.search.ExtendedSearchProposition = openerp.base.BaseWidget.extend({
}
});
-openerp.base.search.ExtendedSearchProposition.Char = openerp.base.BaseWidget.extend({
+openerp.base.search.ExtendedSearchProposition.Char = openerp.base.OldWidget.extend({
template: 'SearchView.extended_search.proposition.char',
identifier_prefix: 'extended-search-proposition-char',
operators: [
@@ -1071,7 +1072,7 @@ openerp.base.search.ExtendedSearchProposition.Char = openerp.base.BaseWidget.ext
return this.$element.val();
}
});
-openerp.base.search.ExtendedSearchProposition.DateTime = openerp.base.BaseWidget.extend({
+openerp.base.search.ExtendedSearchProposition.DateTime = openerp.base.OldWidget.extend({
template: 'SearchView.extended_search.proposition.datetime',
identifier_prefix: 'extended-search-proposition-datetime',
operators: [
@@ -1093,7 +1094,7 @@ openerp.base.search.ExtendedSearchProposition.DateTime = openerp.base.BaseWidget
});
}
});
-openerp.base.search.ExtendedSearchProposition.Date = openerp.base.BaseWidget.extend({
+openerp.base.search.ExtendedSearchProposition.Date = openerp.base.OldWidget.extend({
template: 'SearchView.extended_search.proposition.date',
identifier_prefix: 'extended-search-proposition-date',
operators: [
@@ -1115,7 +1116,7 @@ openerp.base.search.ExtendedSearchProposition.Date = openerp.base.BaseWidget.ext
});
}
});
-openerp.base.search.ExtendedSearchProposition.Integer = openerp.base.BaseWidget.extend({
+openerp.base.search.ExtendedSearchProposition.Integer = openerp.base.OldWidget.extend({
template: 'SearchView.extended_search.proposition.integer',
identifier_prefix: 'extended-search-proposition-integer',
operators: [
@@ -1134,7 +1135,7 @@ openerp.base.search.ExtendedSearchProposition.Integer = openerp.base.BaseWidget.
return Math.round(value);
}
});
-openerp.base.search.ExtendedSearchProposition.Float = openerp.base.BaseWidget.extend({
+openerp.base.search.ExtendedSearchProposition.Float = openerp.base.OldWidget.extend({
template: 'SearchView.extended_search.proposition.float',
identifier_prefix: 'extended-search-proposition-float',
operators: [
@@ -1153,7 +1154,7 @@ openerp.base.search.ExtendedSearchProposition.Float = openerp.base.BaseWidget.ex
return value;
}
});
-openerp.base.search.ExtendedSearchProposition.Selection = openerp.base.BaseWidget.extend({
+openerp.base.search.ExtendedSearchProposition.Selection = openerp.base.OldWidget.extend({
template: 'SearchView.extended_search.proposition.selection',
identifier_prefix: 'extended-search-proposition-selection',
operators: [
@@ -1167,7 +1168,7 @@ openerp.base.search.ExtendedSearchProposition.Selection = openerp.base.BaseWidge
return this.$element.val();
}
});
-openerp.base.search.ExtendedSearchProposition.Boolean = openerp.base.BaseWidget.extend({
+openerp.base.search.ExtendedSearchProposition.Boolean = openerp.base.OldWidget.extend({
template: 'SearchView.extended_search.proposition.boolean',
identifier_prefix: 'extended-search-proposition-boolean',
operators: [
diff --git a/addons/base/static/src/js/view_help.js b/addons/base/static/src/js/view_help.js
index 704c982eb3a..aa98f59c3ab 100644
--- a/addons/base/static/src/js/view_help.js
+++ b/addons/base/static/src/js/view_help.js
@@ -4,10 +4,10 @@
openerp.base.view_help = function(openerp) {
-openerp.base.ProcessView = openerp.base.Controller.extend({
+openerp.base.ProcessView = openerp.base.Widget.extend({
});
-openerp.base.HelpView = openerp.base.Controller.extend({
+openerp.base.HelpView = openerp.base.Widget.extend({
});
};
diff --git a/addons/base/static/src/js/view_tree.js b/addons/base/static/src/js/view_tree.js
index ceb5478a4a7..90972f84973 100644
--- a/addons/base/static/src/js/view_tree.js
+++ b/addons/base/static/src/js/view_tree.js
@@ -5,7 +5,7 @@
openerp.base.view_tree = function(openerp) {
openerp.base.views.add('tree', 'openerp.base.TreeView');
-openerp.base.TreeView = openerp.base.Controller.extend({
+openerp.base.TreeView = openerp.base.Widget.extend({
/**
* Genuine tree view (the one displayed as a tree, not the list)
*/
diff --git a/addons/base/static/src/js/views.js b/addons/base/static/src/js/views.js
index b98b0bc637f..7e95ff67f83 100644
--- a/addons/base/static/src/js/views.js
+++ b/addons/base/static/src/js/views.js
@@ -4,7 +4,7 @@
openerp.base.views = function(openerp) {
-openerp.base.ActionManager = openerp.base.Controller.extend({
+openerp.base.ActionManager = openerp.base.Widget.extend({
// process all kind of actions
init: function(parent, element_id) {
this._super(parent, element_id);
@@ -98,18 +98,21 @@ openerp.base.ActionManager = openerp.base.Controller.extend({
openerp.base.ActionDialog = openerp.base.Dialog.extend({
identifier_prefix: 'action_dialog',
- stop: function() {
+ 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();
}
}
});
-openerp.base.ViewManager = openerp.base.Controller.extend({
+openerp.base.ViewManager = openerp.base.Widget.extend({
init: function(parent, element_id, dataset, views) {
this._super(parent, element_id);
this.model = dataset.model;
@@ -126,6 +129,7 @@ openerp.base.ViewManager = openerp.base.Controller.extend({
* @returns {jQuery.Deferred} initial view loading promise
*/
start: function() {
+ this._super();
var self = this;
this.dataset.start();
this.$element.html(QWeb.render("ViewManager", {"prefix": this.element_id, views: this.views_src}));
@@ -216,7 +220,7 @@ openerp.base.ViewManager = openerp.base.Controller.extend({
},
/**
* Event launched when a controller has been inited.
- *
+ *
* @param {String} view_type type of view
* @param {String} view the inited controller
*/
@@ -330,7 +334,7 @@ openerp.base.ViewManagerAction = openerp.base.ViewManager.extend({
}
},
stop: function() {
- // should be replaced by automatic destruction implemented in BaseWidget
+ // should be replaced by automatic destruction implemented in Widget
this._super();
},
/**
@@ -357,10 +361,11 @@ openerp.base.ViewManagerAction = openerp.base.ViewManager.extend({
}
});
-openerp.base.Sidebar = openerp.base.Controller.extend({
+openerp.base.Sidebar = openerp.base.Widget.extend({
init: function(parent, element_id) {
this._super(parent, element_id);
this.items = {};
+ this.sections = {};
},
start: function() {
var self = this;
@@ -382,11 +387,11 @@ openerp.base.Sidebar = openerp.base.Controller.extend({
classname: 'oe_sidebar_' + type[0]
}
}
- self.add_section(type[1], items);
+ self.add_section(type[0], type[1], items);
}
});
},
- add_section: function(name, items) {
+ add_section: function(code, name, items) {
// For each section, we pass a name/label and optionally an array of items.
// If no items are passed, then the section will be created as a custom section
// returning back an element_id to be used by a custom controller.
@@ -395,11 +400,12 @@ openerp.base.Sidebar = openerp.base.Controller.extend({
// label: label to be displayed for the link,
// action: action to be launch when the link is clicked,
// callback: a function to be executed when the link is clicked,
- // classname: optionnal dom class name for the line,
+ // classname: optional dom class name for the line,
+ // title: optional title for the link
// }
// Note: The item should have one action or/and a callback
var self = this,
- section_id = _.uniqueId(this.element_id + '_section_');
+ section_id = _.uniqueId(this.element_id + '_section_' + code + '_');
if (items) {
for (var i = 0; i < items.length; i++) {
items[i].element_id = _.uniqueId(section_id + '_item_');
@@ -409,6 +415,7 @@ openerp.base.Sidebar = openerp.base.Controller.extend({
var $section = $(QWeb.render("Sidebar.section", {
section_id: section_id,
name: name,
+ classname: 'oe_sidebar_' + code,
items: items
}));
if (items) {
@@ -426,6 +433,7 @@ openerp.base.Sidebar = openerp.base.Controller.extend({
});
}
$section.appendTo(this.$element.find('div.sidebar-actions'));
+ this.sections[code] = $section;
return section_id;
},
do_fold: function() {
@@ -439,26 +447,7 @@ openerp.base.Sidebar = openerp.base.Controller.extend({
}
});
-openerp.base.Export = openerp.base.Dialog.extend({
- dialog_title: "Export",
- template: 'ExportDialog',
- identifier_prefix: 'export_dialog',
- init: function (session, model, domain) {
- this._super();
- },
- start: function () {
- this._super();
- this.$element.html(this.render());
- },
- on_button_Export: function() {
- console.log("Export")
- },
- on_button_Cancel: function() {
- this.$element.dialog("close");
- }
-});
-
-openerp.base.View = openerp.base.Controller.extend({
+openerp.base.View = openerp.base.Widget.extend({
set_default_options: function(options) {
this.options = options || {};
_.defaults(this.options, {
@@ -522,11 +511,65 @@ openerp.base.View = openerp.base.Controller.extend({
* 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;
+ },
+ set_common_sidebar_sections: function(sidebar) {
+ sidebar.add_section('customize', "Customize", [
+ {
+ label: "Manage Views",
+ callback: this.on_sidebar_manage_view,
+ title: "Manage views of the current object"
+ }, {
+ label: "Edit Workflow",
+ callback: this.on_sidebar_edit_workflow,
+ title: "Manage views of the current object",
+ classname: 'oe_hide oe_sidebar_edit_workflow'
+ }, {
+ label: "Customize Object",
+ callback: this.on_sidebar_customize_object,
+ title: "Manage views of the current object"
+ }
+ ]);
+ sidebar.add_section('other', "Other Options", [
+ {
+ label: "Import",
+ callback: this.on_sidebar_import
+ }, {
+ label: "Export",
+ callback: this.on_sidebar_export
+ }, {
+ label: "Translate",
+ callback: this.on_sidebar_translate,
+ classname: 'oe_hide oe_sidebar_translate'
+ }, {
+ label: "View Log",
+ callback: this.on_sidebar_view_log,
+ classname: 'oe_hide oe_sidebar_view_log'
+ }
+ ]);
+ },
+ on_sidebar_manage_view: function() {
+ console.log('Todo');
+ },
+ on_sidebar_edit_workflow: function() {
+ console.log('Todo');
+ },
+ on_sidebar_customize_object: function() {
+ console.log('Todo');
+ },
+ on_sidebar_import: function() {
+ },
+ on_sidebar_export: function() {
+ var export_view = new openerp.base.DataExport(this, this.dataset);
+ export_view.start(false);
+ },
+ on_sidebar_translate: function() {
+ },
+ on_sidebar_view_log: function() {
}
});
diff --git a/addons/base/static/src/xml/base.xml b/addons/base/static/src/xml/base.xml
index a5c91b32b17..ac610e4cced 100644
--- a/addons/base/static/src/xml/base.xml
+++ b/addons/base/static/src/xml/base.xml
@@ -294,7 +294,7 @@
<div class="oe_login_right_pane">
<p>We think that daily job activities can be more intuitive, efficient, automated, .. and even fun.</p>
<h3>OpenERP's vision to be:</h3>
-
+
<table cellpadding="0" cellspacing="0" width="100%" style="border:none;">
<tbody>
<tr>
@@ -326,7 +326,7 @@
</tr>
</tbody>
</table>
-
+
</div>
</t>
<t t-name="Header">
@@ -439,10 +439,10 @@
</t>
<t t-name="Sidebar.section">
<h2><t t-esc="name"/></h2>
- <div t-att-id="section_id">
+ <div t-att-id="section_id" t-att-class="classname">
<ul t-if="items">
<li t-foreach="items" t-as="item" t-att-class="item.classname">
- <a class="oe_sidebar_action_a" t-att-id="item.element_id" href="#">
+ <a class="oe_sidebar_action_a" t-att-id="item.element_id" t-att-title="item.title" href="#">
<t t-esc="item.label"/>
</a>
</li>
@@ -452,11 +452,11 @@
<table t-name="ListView" class="oe-listview-content">
<t t-set="columns_count" t-value="visible_columns.length + (options.selectable ? 1 : 0) + (options.deletable ? 1 : 0)"/>
<thead class="ui-widget-header">
- <tr t-if="flags.action_buttons !== false or flags.pager !== false">
+ <tr t-if="options.action_buttons !== false or options.pager !== false">
<th t-att-colspan="columns_count">
<table>
<tr>
- <td t-if="flags.action_buttons !== false" class="oe-actions">
+ <td t-if="options.action_buttons !== false" class="oe-actions">
<button type="button" class="oe-list-add"
t-if="options.addable">
<t t-esc="options.addable"/>
@@ -466,7 +466,7 @@
Delete
</button>
</td>
- <th t-if="flags.pager !== false" class="oe-list-pager">
+ <th t-if="options.pager !== false" class="oe-list-pager">
<button type="button" disabled="disabled"
data-pager-action="first">First</button>
<button type="button" disabled="disabled"
@@ -524,7 +524,7 @@
t-att-data-index="row_index">
<t t-foreach="columns" t-as="column">
<td t-if="column.meta">
-
+
</td>
</t>
<th t-if="options.selectable" class="oe-record-selector" width="1">
@@ -548,7 +548,7 @@
<t t-name="FormView">
<div class="oe_form_header" t-att-id="view.element_id + '_header'">
<h2 class="oe_view_title"><t t-esc="view.fields_view.arch.attrs.string"/> <button class="oe_get_xml_view">xml</button></h2>
- <div class="oe_form_buttons" t-if="view.flags.action_buttons !== false">
+ <div class="oe_form_buttons" t-if="view.options.action_buttons !== false">
<!--<button type="button" class="oe_form_button_save">
<span class="oe_form_on_update">Save</span>
<span class="oe_form_on_create">Create</span>
@@ -560,7 +560,7 @@
<!--<button type="button" class="oe_form_button_cancel">Cancel</button>-->
<button type="button" class="oe_form_button_new">New</button>
</div>
- <div class="oe_form_pager" t-if="view.flags.pager !== false">
+ <div class="oe_form_pager" t-if="view.options.pager !== false">
<button type="button" data-pager-action="first">First</button>
<button type="button" data-pager-action="previous">&lt;&lt;</button>
@@ -1012,7 +1012,7 @@
<option value="all">All the following conditions must match</option>
<option value="none">None of the following conditions must match</option>
</select>
- <a class="searchview_extended_delete_group"
+ <a class="searchview_extended_delete_group"
href="javascript:void(0)"><span></span></a>
<div class="searchview_extended_propositions_list">
</div>
@@ -1135,4 +1135,145 @@
.unwrap();
</t>
</t>
+
+<t t-name="ExportView">
+ <a id="exportview" href="javascript: void(0)" style="text-decoration: none;color: #3D3D3D;">Export</a>
+</t>
+
+<t t-name="ExportTreeView">
+ <table class="view" style="background-color: #F3F3F3;">
+ <tr>
+ <td align="left">
+ This wizard will export all data that matches the current search criteria to a CSV file.
+ You can export all data or only the fields that can be reimported after modification.
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <table>
+ <tr>
+ <td class="label"><label>Export Type:</label></td>
+ <td>
+ <select id="import_compat" name="import_compat">
+ <option value="1">Import Compatible Export</option>
+ <option value="0">Export all Data</option>
+ </select>
+ </td>
+ <td class="label"><label>Export Format</label></td>
+ <td>
+ <select id="export_format" name="export_format">
+ <option value="csv">CSV</option>
+ <option value="xls">Excel</option>
+ </select>
+ </td>
+
+ </tr>
+ </table>
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ <table class="oe_export_fields_selector_export">
+ <tr>
+ <th class="oe_view_title" valign="bottom">Available fields</th>
+ <th class="oe_view_title"></th>
+ <th class="oe_view_title">Fields to export
+ <a style="color: blue; text-decoration: none;" href="#" id="export_new_list">Save fields list</a>
+ <div id="savenewlist"></div>
+ <div id="ExistsExportList"></div>
+ </th>
+ </tr>
+ <tr>
+ <td class="oe_export_fields_selector_left">
+ <div id="left_field_panel">
+ </div>
+ </td>
+ <td>
+ <table class="oe_export_fields_selector_center">
+ <tr>
+ <td align="center">
+ <button id="add_field" class="oe_export_button_export">Add</button>
+ </td>
+ </tr>
+ <tr>
+ <td align="center">
+ <button id="remove_field" class="oe_export_button_export">Remove</button>
+ </td>
+ </tr>
+ <tr>
+ <td align="center">
+ <button id="remove_all_field" class="oe_export_button_export">Remove All</button>
+ </td>
+ </tr>
+ </table>
+ </td>
+ <td class="oe_export_fields_selector_right">
+ <select name="fields_list" id="fields_list" multiple="multiple"></select>
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+</t>
+
+<t t-name="ExportTreeView-Secondary">
+ <table id="field-tree-structure" class="oe_export_fields_selector_export" cellspacing="0" cellpadding="0">
+ <tr><th class="oe_export_tree_header"> Name </th></tr>
+ <t t-call="ExportTreeView-Secondary.children"/>
+ </table>
+</t>
+<t t-name="ExportTreeView-Secondary.children">
+ <t t-foreach="fields" t-as="field" >
+ <tr t-att-id="'treerow-' + field.id" class="oe_export_row">
+ <td>
+ <table class="tree_grid" border="0">
+ <tr class="oe_export_row">
+ <t t-foreach="(field.id).split('/')" t-as="level" >
+ <t t-if="(field.id).split('/')[0] != level">
+ <td width="18">&amp;nbsp;</td>
+ </t>
+ </t>
+ <td valign="top" align="left" style="cursor: pointer;" width="18">
+ <t t-if="(field.children).length >= 1">
+ <t t-if="(field.id).split('/').length != 3">
+ <img t-att-id="'parentimg-' + field.id" src="/base/static/src/img/expand.gif" width="16" height="16" border="0"/>
+ </t>
+ </t>
+ </td>
+ <td id="tree-column" valign="middle" align="left" style="cursor: pointer;">
+ <a t-att-id="'export-' + field.id" t-att-string="field.string" href="javascript: void(0);" style="text-decoration: none;">
+ <t t-esc="field.string"/>
+ </a>
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ </t>
+</t>
+
+<t t-name="ExportNewList">
+ <tr>
+ <th><label>Save as:</label></th>
+ <td><input size="10" type="text" id="savelist_name"/></td>
+ <td><button class="oe_export_button_export" id="add_export_list">Ok</button></td>
+ </tr>
+</t>
+
+<t t-name="Exists.ExportList">
+ <tr><th align="right"><label >Saved exports:</label></th></tr>
+ <tr align="left">
+ <td>
+ <select id="saved_export_list" style="width: 100%;">
+ <option></option>
+ <t t-foreach="existing_exports" t-as="export">
+ <option t-att-value="export.id"><t t-esc="export.name"/></option>
+ </t>
+ </select>
+ </td>
+ <td><button class="oe_export_button_export" id="delete_export_list">Delete</button></td>
+ </tr>
+</t>
</templates>