summaryrefslogtreecommitdiff
path: root/addons/web/static/src/js
diff options
context:
space:
mode:
authorsma (Tiny) <sma@tinyerp.com>2011-10-03 16:57:31 +0530
committersma (Tiny) <sma@tinyerp.com>2011-10-03 16:57:31 +0530
commit7e9e542418f791b024de401236eeb8e006be42d4 (patch)
tree91e49927b0de50c8c690a2eb608b425a12b4ac7c /addons/web/static/src/js
parent3e11ca62990fde34e537e233e3b099d22f8c8373 (diff)
parent2dae9519d7c3d0a4c2217b63939bb4eb1d83d39d (diff)
[MERGE]
bzr revid: sma@tinyerp.com-20110926105300-72fwa60uvvdf1qgg bzr revid: sma@tinyerp.com-20111003112731-uobxur1ryqb5laxy
Diffstat (limited to 'addons/web/static/src/js')
-rw-r--r--addons/web/static/src/js/boot.js9
-rw-r--r--addons/web/static/src/js/chrome.js239
-rw-r--r--addons/web/static/src/js/core.js12
-rw-r--r--addons/web/static/src/js/data.js43
-rw-r--r--addons/web/static/src/js/data_import.js289
-rw-r--r--addons/web/static/src/js/formats.js19
-rw-r--r--addons/web/static/src/js/search.js66
-rw-r--r--addons/web/static/src/js/view_form.js509
-rw-r--r--addons/web/static/src/js/view_list.js54
-rw-r--r--addons/web/static/src/js/view_list_editable.js11
-rw-r--r--addons/web/static/src/js/views.js35
11 files changed, 945 insertions, 341 deletions
diff --git a/addons/web/static/src/js/boot.js b/addons/web/static/src/js/boot.js
index 38f8eaa1e7c..f951d160866 100644
--- a/addons/web/static/src/js/boot.js
+++ b/addons/web/static/src/js/boot.js
@@ -19,9 +19,9 @@
/**
* OpenERP instance constructor
*
- * @param {Boolean} skip_init if true, skips the built-in initialization
+ * @param {Array} modules list of modules to initialize
*/
- init: function(skip_init) {
+ init: function(modules) {
var new_instance = {
// links to the global openerp
_openerp: openerp,
@@ -35,8 +35,9 @@
web_mobile: {}
};
openerp.sessions[new_instance._session_id] = new_instance;
- if (!skip_init){
- openerp.web(new_instance);
+ modules = modules || ["web"];
+ for(var i=0; i < modules.length; i++) {
+ openerp[modules[i]](new_instance);
}
return new_instance;
}
diff --git a/addons/web/static/src/js/chrome.js b/addons/web/static/src/js/chrome.js
index d114f485c62..1955298ba27 100644
--- a/addons/web/static/src/js/chrome.js
+++ b/addons/web/static/src/js/chrome.js
@@ -1,7 +1,6 @@
/*---------------------------------------------------------
* OpenERP Web chrome
*---------------------------------------------------------*/
-
openerp.web.chrome = function(openerp) {
var QWeb = openerp.web.qweb;
@@ -129,6 +128,7 @@ openerp.web.Dialog = openerp.web.OldWidget.extend(/** @lends openerp.web.Dialog#
// Destroy widget
this.close();
this.$dialog.dialog('destroy');
+ this._super();
}
});
@@ -218,9 +218,8 @@ openerp.web.Database = openerp.web.Widget.extend(/** @lends openerp.web.Database
this.$element.closest(".openerp")
.removeClass("login-mode")
.addClass("database_block");
-
+
var self = this;
-
var fetch_db = this.rpc("/web/database/get_list", {}, function(result) {
self.db_list = result.db_list;
});
@@ -232,7 +231,7 @@ openerp.web.Database = openerp.web.Widget.extend(/** @lends openerp.web.Database
self.lang_list = result.lang_list;
});
$.when(fetch_db, fetch_langs).then(function () {self.do_create();});
-
+
this.$element.find('#db-create').click(this.do_create);
this.$element.find('#db-drop').click(this.do_drop);
this.$element.find('#db-backup').click(this.do_backup);
@@ -254,7 +253,7 @@ openerp.web.Database = openerp.web.Widget.extend(/** @lends openerp.web.Database
.removeClass("database_block")
.end()
.empty();
-
+ this._super();
},
/**
* Converts a .serializeArray() result into a dict. Does not bother folding
@@ -300,9 +299,9 @@ openerp.web.Database = openerp.web.Widget.extend(/** @lends openerp.web.Database
var admin = result[1][0];
setTimeout(function () {
- self.stop();
self.widget_parent.do_login(
info.db, admin.login, admin.password);
+ self.stop();
$.unblockUI();
});
});
@@ -399,7 +398,7 @@ openerp.web.Database = openerp.web.Widget.extend(/** @lends openerp.web.Database
do_restore: function() {
var self = this;
self.$option_id.html(QWeb.render("RestoreDB", self));
-
+
self.$option_id.find("form[name=restore_db_form]").validate({
submitHandler: function (form) {
$.blockUI({message:'<img src="/web/static/src/img/throbber2.gif">'});
@@ -464,15 +463,19 @@ openerp.web.Database = openerp.web.Widget.extend(/** @lends openerp.web.Database
openerp.web.Login = openerp.web.Widget.extend(/** @lends openerp.web.Login# */{
remember_creditentials: true,
+
+ template: "Login",
+ identifier_prefix: 'oe-app-login-',
/**
* @constructs openerp.web.Login
* @extends openerp.web.Widget
- *
+ *
* @param parent
* @param element_id
*/
- init: function(parent, element_id) {
- this._super(parent, element_id);
+
+ init: function(parent) {
+ this._super(parent);
this.has_local_storage = typeof(localStorage) != 'undefined';
this.selected_db = null;
this.selected_login = null;
@@ -487,17 +490,6 @@ openerp.web.Login = openerp.web.Widget.extend(/** @lends openerp.web.Login# */{
},
start: function() {
var self = this;
- this.rpc("/web/database/get_list", {}, function(result) {
- self.db_list = result.db_list;
- self.display();
- }, function() {
- self.display();
- });
- },
- display: function() {
- var self = this;
-
- this.$element.html(QWeb.render("Login", this));
this.database = new openerp.web.Database(
this, "oe_database", "oe_db_options");
@@ -506,6 +498,17 @@ openerp.web.Login = openerp.web.Widget.extend(/** @lends openerp.web.Login# */{
});
this.$element.find("form").submit(this.on_submit);
+
+ this.rpc("/web/database/get_list", {}, function(result) {
+ var tpl = openerp.web.qweb.render('Login_dblist', {db_list: result.db_list, selected_db: self.selected_db});
+ self.$element.find("input[name=db]").replaceWith(tpl)
+ },
+ function(error, event) {
+ if (error.data.fault_code === 'AccessDenied') {
+ event.preventDefault();
+ }
+ });
+
},
on_login_invalid: function() {
this.$element.closest(".openerp").addClass("login-mode");
@@ -574,14 +577,13 @@ openerp.web.Header = openerp.web.Widget.extend(/** @lends openerp.web.Header# *
/**
* @constructs openerp.web.Header
* @extends openerp.web.Widget
- *
+ *
* @param parent
*/
init: function(parent) {
this._super(parent);
this.qs = "?" + jQuery.param.querystring();
this.$content = $();
- console.debug("initializing header with id", this.element_id);
this.update_promise = $.Deferred().resolve();
},
start: function() {
@@ -666,7 +668,7 @@ openerp.web.Header = openerp.web.Widget.extend(/** @lends openerp.web.Header# *
});
});
},
-
+
on_action: function(action) {
},
on_preferences: function(){
@@ -702,10 +704,11 @@ openerp.web.Header = openerp.web.Widget.extend(/** @lends openerp.web.Header# *
},
Save: function(){
var inner_viewmanager = action_manager.inner_viewmanager;
- inner_viewmanager.views[inner_viewmanager.active_view].controller.do_save(function(){
- inner_viewmanager.start();
+ inner_viewmanager.views[inner_viewmanager.active_view].controller.do_save()
+ .then(function() {
+ self.dialog.stop();
+ window.location.reload();
});
- $(this).dialog('destroy')
}
}
});
@@ -713,7 +716,7 @@ openerp.web.Header = openerp.web.Widget.extend(/** @lends openerp.web.Header# *
action_manager.appendTo(this.dialog);
action_manager.render(this.dialog);
},
-
+
change_password :function() {
var self = this;
this.dialog = new openerp.web.Dialog(this,{
@@ -728,21 +731,13 @@ openerp.web.Header = openerp.web.Widget.extend(/** @lends openerp.web.Header# *
submitHandler: function (form) {
self.rpc("/web/session/change_password",{
'fields': $(form).serializeArray()
- }, function(result) {
- if (result.error) {
- self.display_error(result);
+ }, function(result) {
+ if (result.error) {
+ self.display_error(result);
return;
- }
- else {
- if (result.new_password) {
- self.session.password = result.new_password;
- var session = new openerp.web.Session(self.session.server, self.session.port);
- session.start();
- session.session_login(self.session.db, self.session.login, self.session.password)
- }
- }
- self.notification.notify("Changed Password", "Password has been changed successfully");
- self.dialog.close();
+ } else {
+ self.session.logout();
+ }
});
}
});
@@ -766,7 +761,7 @@ openerp.web.Menu = openerp.web.Widget.extend(/** @lends openerp.web.Menu# */{
/**
* @constructs openerp.web.Menu
* @extends openerp.web.Widget
- *
+ *
* @param parent
* @param element_id
* @param secondary_menu_id
@@ -776,74 +771,132 @@ openerp.web.Menu = openerp.web.Widget.extend(/** @lends openerp.web.Menu# */{
this.secondary_menu_id = secondary_menu_id;
this.$secondary_menu = $("#" + secondary_menu_id).hide();
this.menu = false;
+ this.folded = false;
+ if (window.localStorage) {
+ this.folded = localStorage.getItem('oe_menu_folded') === 'true';
+ }
+ this.float_timeout = 700;
},
start: function() {
+ this.$secondary_menu.addClass(this.folded ? 'oe_folded' : 'oe_unfolded');
+ this.reload();
+ },
+ reload: function() {
this.rpc("/web/menu/load", {}, this.on_loaded);
},
on_loaded: function(data) {
this.data = data;
- this.$element.html(QWeb.render("Menu", this.data));
- for (var i = 0; i < this.data.data.children.length; i++) {
- var v = { menu : this.data.data.children[i] };
- this.$secondary_menu.append(QWeb.render("Menu.secondary", v));
- }
- this.$secondary_menu.find("div.menu_accordion").accordion({
- animated : false,
- autoHeight : false,
- icons : false
- });
- this.$secondary_menu.find("div.submenu_accordion").accordion({
- animated : false,
- autoHeight : false,
- active: false,
- collapsible: true,
- header: 'h4'
- });
-
+ this.$element.html(QWeb.render("Menu", { widget : this }));
+ this.$secondary_menu.html(QWeb.render("Menu.secondary", { widget : this }));
this.$element.add(this.$secondary_menu).find("a").click(this.on_menu_click);
+ this.$secondary_menu.find('.oe_toggle_secondary_menu').click(this.on_toggle_fold);
+ },
+ on_toggle_fold: function() {
+ this.$secondary_menu.toggleClass('oe_folded').toggleClass('oe_unfolded');
+ if (this.folded) {
+ this.$secondary_menu.find('.oe_secondary_menu.active').show();
+ } else {
+ this.$secondary_menu.find('.oe_secondary_menu').hide();
+ }
+ this.folded = !this.folded;
+ if (window.localStorage) {
+ localStorage.setItem('oe_menu_folded', this.folded.toString());
+ }
},
on_menu_click: function(ev, id) {
id = id || 0;
- var $menu, $parent, $secondary;
+ var $clicked_menu, manual = false;
if (id) {
// We can manually activate a menu with it's id (for hash url mapping)
- $menu = this.$element.find('a[data-menu=' + id + ']');
- if (!$menu.length) {
- $menu = this.$secondary_menu.find('a[data-menu=' + id + ']');
+ manual = true;
+ $clicked_menu = this.$element.find('a[data-menu=' + id + ']');
+ if (!$clicked_menu.length) {
+ $clicked_menu = this.$secondary_menu.find('a[data-menu=' + id + ']');
}
} else {
- $menu = $(ev.currentTarget);
- id = $menu.data('menu');
- }
- if (this.$secondary_menu.has($menu).length) {
- $secondary = $menu.parents('.menu_accordion');
- $parent = this.$element.find('a[data-menu=' + $secondary.data('menu-parent') + ']');
- } else {
- $parent = $menu;
- $secondary = this.$secondary_menu.find('.menu_accordion[data-menu-parent=' + $menu.attr('data-menu') + ']');
+ $clicked_menu = $(ev.currentTarget);
+ id = $clicked_menu.data('menu');
}
- this.$secondary_menu.find('.menu_accordion').hide();
- // TODO: ui-accordion : collapse submenus and expand the good one
- $secondary.show();
-
- if (id) {
+ if (this.do_menu_click($clicked_menu, manual) && id) {
this.session.active_id = id;
- this.rpc('/web/menu/action', {'menu_id': id},
- this.on_menu_action_loaded);
+ this.rpc('/web/menu/action', {'menu_id': id}, this.on_menu_action_loaded);
}
+ ev.stopPropagation();
+ return false;
+ },
+ do_menu_click: function($clicked_menu, manual) {
+ var $sub_menu, $main_menu,
+ active = $clicked_menu.is('.active'),
+ sub_menu_visible = false;
+
+ if (this.$secondary_menu.has($clicked_menu).length) {
+ $sub_menu = $clicked_menu.parents('.oe_secondary_menu');
+ $main_menu = this.$element.find('a[data-menu=' + $sub_menu.data('menu-parent') + ']');
+ } else {
+ $sub_menu = this.$secondary_menu.find('.oe_secondary_menu[data-menu-parent=' + $clicked_menu.attr('data-menu') + ']');
+ $main_menu = $clicked_menu;
+ }
+
+ sub_menu_visible = $sub_menu.is(':visible');
+ this.$secondary_menu.find('.oe_secondary_menu').hide();
$('.active', this.$element.add(this.$secondary_menu.show())).removeClass('active');
- $parent.addClass('active');
- $menu.addClass('active');
- $menu.parent('h4').addClass('active');
+ $main_menu.add($clicked_menu).add($sub_menu).addClass('active');
- if (this.$secondary_menu.has($menu).length) {
- return !$menu.is(".leaf");
+ if (!(this.folded && manual)) {
+ this.do_show_secondary($sub_menu, $main_menu);
+ }
+
+ if ($main_menu != $clicked_menu) {
+ if ($clicked_menu.is('.submenu')) {
+ $sub_menu.find('.submenu.opened').each(function() {
+ if (!$(this).next().has($clicked_menu).length && !$(this).is($clicked_menu)) {
+ $(this).removeClass('opened').next().hide();
+ }
+ });
+ $clicked_menu.toggleClass('opened').next().toggle();
+ } else if ($clicked_menu.is('.leaf')) {
+ $sub_menu.toggle(!this.folded);
+ return true;
+ }
+ } else if (this.folded) {
+ if (active && sub_menu_visible) {
+ $sub_menu.hide();
+ return true;
+ }
} else {
- return false;
+ return true;
}
+ return false;
+ },
+ do_show_secondary: function($sub_menu, $main_menu) {
+ var self = this;
+ if (this.folded) {
+ var css = $main_menu.position(),
+ fold_width = this.$secondary_menu.width() + 2,
+ window_width = $(window).width();
+ css.top += 33;
+ css.left -= Math.round(($sub_menu.width() - $main_menu.width()) / 2);
+ css.left = css.left < fold_width ? fold_width : css.left;
+ if ((css.left + $sub_menu.width()) > window_width) {
+ delete(css.left);
+ css.right = 1;
+ }
+ $sub_menu.css(css);
+ $sub_menu.mouseenter(function() {
+ clearTimeout($sub_menu.data('timeoutId'));
+ }).mouseleave(function(evt) {
+ var timeoutId = setTimeout(function() {
+ if (self.folded) {
+ $sub_menu.hide();
+ }
+ }, self.float_timeout);
+ $sub_menu.data('timeoutId', timeoutId);
+ });
+ }
+ $sub_menu.show();
},
on_menu_action_loaded: function(data) {
var self = this;
@@ -860,7 +913,7 @@ openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClie
/**
* @constructs openerp.web.WebClient
* @extends openerp.web.Widget
- *
+ *
* @param element_id
*/
init: function(element_id) {
@@ -883,7 +936,7 @@ openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClie
openerp.web.Widget.prototype.notification = new openerp.web.Notification(this, "oe_notification");
this.header = new openerp.web.Header(this);
- this.login = new openerp.web.Login(this, "oe_login");
+ this.login = new openerp.web.Login(this);
this.header.on_logout.add(this.login.on_logout);
this.header.on_action.add(this.on_menu_action);
@@ -903,9 +956,8 @@ openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClie
start: function() {
this.header.appendTo($("#oe_header"));
this.session.start();
- this.login.start();
+ this.login.appendTo($('#oe_login'));
this.menu.start();
- console.debug("The openerp client has been initialized.");
},
on_logged: function() {
if(this.action_manager)
@@ -951,7 +1003,7 @@ openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClie
self.execute_home_action(home_action[0], ds);
})
},
- default_home: function () {
+ default_home: function () {
},
/**
* Bundles the execution of the home action
@@ -976,7 +1028,6 @@ openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClie
},
do_url_set_hash: function(url) {
if(!this.url_external_hashchange) {
- console.log("url set #hash to",url);
this.url_internal_hashchange = true;
jQuery.bbq.pushState(url);
}
@@ -984,10 +1035,8 @@ openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClie
on_url_hashchange: function() {
if(this.url_internal_hashchange) {
this.url_internal_hashchange = false;
- console.log("url jump to FLAG OFF");
} else {
var url = jQuery.deparam.fragment();
- console.log("url jump to",url);
this.url_external_hashchange = true;
this.action_manager.on_url_hashchange(url);
this.url_external_hashchange = false;
diff --git a/addons/web/static/src/js/core.js b/addons/web/static/src/js/core.js
index dba47ee758c..9d4d2124008 100644
--- a/addons/web/static/src/js/core.js
+++ b/addons/web/static/src/js/core.js
@@ -1,7 +1,10 @@
/*---------------------------------------------------------
* OpenERP Web core
*--------------------------------------------------------*/
-
+var console;
+if (!console) {
+ console = {log: function () {}};
+}
if (!console.debug) {
console.debug = console.log;
}
@@ -483,7 +486,6 @@ openerp.web.Session = openerp.web.CallbackEnabled.extend( /** @lends openerp.web
self.user_context = result.context;
self.db = result.db;
self.session_save();
- self.on_session_valid();
return true;
}).then(success_callback);
},
@@ -772,7 +774,6 @@ openerp.web.SessionAware = openerp.web.CallbackEnabled.extend(/** @lends openerp
* // 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 * /);
*
@@ -916,6 +917,8 @@ openerp.web.Widget = openerp.web.SessionAware.extend(/** @lends openerp.web.Widg
* @returns {jQuery.Deferred}
*/
start: function() {
+ /* The default implementation is only useful for retro-compatibility, it is
+ not necessary to call it using _super() when using Widget for new components. */
if (!this.$element) {
var tmp = document.getElementById(this.element_id);
this.$element = tmp ? $(tmp) : undefined;
@@ -923,7 +926,7 @@ openerp.web.Widget = openerp.web.SessionAware.extend(/** @lends openerp.web.Widg
return $.Deferred().done().promise();
},
/**
- * Destroys the current widget, also destory all its children before destroying itself.
+ * Destroys the current widget, also destroy all its children before destroying itself.
*/
stop: function() {
_.each(_.clone(this.widget_children), function(el) {
@@ -944,7 +947,6 @@ openerp.web.Widget = openerp.web.SessionAware.extend(/** @lends openerp.web.Widg
* If that's not the case this method will simply return `false`.
*/
do_action: function(action, on_finished) {
- console.log('Widget.do_action', action, on_finished);
if (this.widget_parent) {
return this.widget_parent.do_action(action, on_finished);
}
diff --git a/addons/web/static/src/js/data.js b/addons/web/static/src/js/data.js
index e5d323c8a96..fcf6a65dcf5 100644
--- a/addons/web/static/src/js/data.js
+++ b/addons/web/static/src/js/data.js
@@ -233,6 +233,7 @@ openerp.web.StaticDataGroup = openerp.web.GrouplessDataGroup.extend( /** @lends
});
openerp.web.DataSet = openerp.web.Widget.extend( /** @lends openerp.web.DataSet# */{
+ identifier_prefix: "dataset",
/**
* DateaManagement interface between views and the collection of selected
* OpenERP records (represents the view's state?)
@@ -242,16 +243,12 @@ openerp.web.DataSet = openerp.web.Widget.extend( /** @lends openerp.web.DataSet
*
* @param {String} model the OpenERP model this dataset will manage
*/
- init: function(source_controller, model, context) {
- // we don't want the dataset to be a child of anything!
- this._super(null);
- this.session = source_controller ? source_controller.session : undefined;
+ init: function(parent, model, context) {
+ this._super(parent);
this.model = model;
this.context = context || {};
this.index = null;
},
- start: function() {
- },
previous: function () {
this.index -= 1;
if (this.index < 0) {
@@ -549,13 +546,11 @@ openerp.web.DataSetSearch = openerp.web.DataSet.extend(/** @lends openerp.web.D
sort: this.sort(),
offset: offset,
limit: options.limit || false
- }, function (result) {
+ }).pipe(function (result) {
self.ids = result.ids;
self.offset = offset;
- if (callback) {
- callback(result.records);
- }
- });
+ return result.records;
+ }).then(callback);
},
get_domain: function (other_domain) {
if (other_domain) {
@@ -603,20 +598,27 @@ openerp.web.DataSetSearch = openerp.web.DataSet.extend(/** @lends openerp.web.D
});
openerp.web.BufferedDataSet = openerp.web.DataSetStatic.extend({
virtual_id_prefix: "one2many_v_id_",
- virtual_id_regex: /one2many_v_id_.*/,
debug_mode: true,
init: function() {
this._super.apply(this, arguments);
this.reset_ids([]);
+ this.last_default_get = {};
+ },
+ default_get: function(fields, callback) {
+ return this._super(fields).then(this.on_default_get).then(callback);
+ },
+ on_default_get: function(res) {
+ this.last_default_get = res;
},
create: function(data, callback, error_callback) {
- var cached = {id:_.uniqueId(this.virtual_id_prefix), values: data};
+ var cached = {id:_.uniqueId(this.virtual_id_prefix), values: data,
+ defaults: this.last_default_get};
this.to_create.push(cached);
this.cache.push(cached);
this.on_change();
- var to_return = $.Deferred().then(callback);
- to_return.resolve({result: cached.id});
- return to_return.promise();
+ var prom = $.Deferred().then(callback);
+ setTimeout(function() {prom.resolve({result: cached.id});}, 0);
+ return prom.promise();
},
write: function (id, data, options, callback) {
var self = this;
@@ -676,7 +678,8 @@ openerp.web.BufferedDataSet = openerp.web.DataSetStatic.extend({
var cached = _.detect(self.cache, function(x) {return x.id === id;});
var created = _.detect(self.to_create, function(x) {return x.id === id;});
if (created) {
- _.each(fields, function(x) {if (cached.values[x] === undefined) cached.values[x] = false;});
+ _.each(fields, function(x) {if (cached.values[x] === undefined)
+ cached.values[x] = created.defaults[x] || false;});
} else {
if (!cached || !_.all(fields, function(x) {return cached.values[x] !== undefined}))
to_get.push(id);
@@ -715,7 +718,13 @@ openerp.web.BufferedDataSet = openerp.web.DataSetStatic.extend({
return completion.promise();
}
});
+openerp.web.BufferedDataSet.virtual_id_regex = /^one2many_v_id_.*$/;
+
openerp.web.ReadOnlyDataSetSearch = openerp.web.DataSetSearch.extend({
+ default_get: function(fields, callback) {
+ return this._super(fields, callback).then(this.on_default_get);
+ },
+ on_default_get: function(result) {},
create: function(data, callback, error_callback) {
this.on_create(data);
var to_return = $.Deferred().then(callback);
diff --git a/addons/web/static/src/js/data_import.js b/addons/web/static/src/js/data_import.js
new file mode 100644
index 00000000000..9c686846751
--- /dev/null
+++ b/addons/web/static/src/js/data_import.js
@@ -0,0 +1,289 @@
+openerp.web.data_import = function(openerp) {
+var QWeb = openerp.web.qweb;
+/**
+ * Safari does not deal well at all with raw JSON data being returned. As a
+ * result, we're going to cheat by using a pseudo-jsonp: instead of getting
+ * JSON data in the iframe, we're getting a ``script`` tag which consists of a
+ * function call and the returned data (the json dump).
+ *
+ * The function is an auto-generated name bound to ``window``, which calls
+ * back into the callback provided here.
+ *
+ * @param {Object} form the form element (DOM or jQuery) to use in the call
+ * @param {Object} attributes jquery.form attributes object
+ * @param {Function} callback function to call with the returned data
+ */
+function jsonp(form, attributes, callback) {
+ attributes = attributes || {};
+ var options = {jsonp: _.uniqueId('import_callback_')};
+ window[options.jsonp] = function () {
+ delete window[options.jsonp];
+ callback.apply(null, arguments);
+ };
+ if ('data' in attributes) {
+ _.extend(attributes.data, options);
+ } else {
+ _.extend(attributes, {data: options});
+ }
+ $(form).ajaxSubmit(attributes);
+}
+
+openerp.web.DataImport = openerp.web.Dialog.extend({
+ template: 'ImportDataView',
+ dialog_title: "Import Data",
+ init: function(parent, dataset){
+ var self = this;
+ this._super(parent, {});
+ this.model = parent.model;
+ this.fields = [];
+ this.all_fields = [];
+ this.required_fields = null;
+
+ var convert_fields = function (root, prefix) {
+ prefix = prefix || '';
+ _(root.fields).each(function (f) {
+ self.all_fields.push(prefix + f.name);
+ if (f.fields) {
+ convert_fields(f, prefix + f.id + '/');
+ }
+ });
+ };
+ this.ready = $.Deferred.queue().then(function () {
+ self.required_fields = _(self.fields).chain()
+ .filter(function (field) { return field.required; })
+ .pluck('name')
+ .value();
+ convert_fields(self);
+ self.all_fields.sort();
+ });
+ },
+ start: function() {
+ var self = this;
+ this._super();
+ this.open({
+ modal: true,
+ width: '70%',
+ height: 'auto',
+ position: 'top',
+ buttons: [
+ {text: "Close", click: function() { self.stop(); }},
+ {text: "Import File", click: function() { self.do_import(); }, 'class': 'oe-dialog-import-button'}
+ ],
+ close: function(event, ui) {
+ self.stop();
+ }
+ });
+ this.toggle_import_button(false);
+ this.$element.find('#csvfile').change(this.on_autodetect_data);
+ this.$element.find('fieldset').change(this.on_autodetect_data);
+ this.$element.find('fieldset legend').click(function() {
+ $(this).next().toggle();
+ });
+ this.ready.push(new openerp.web.DataSet(this, this.model).call(
+ 'fields_get', [], function (fields) {
+ self.graft_fields(fields);
+ }));
+ },
+ graft_fields: function (fields, parent, level) {
+ parent = parent || this;
+ level = level || 0;
+
+ var self = this;
+ _(fields).each(function (field, field_name) {
+ var f = {
+ id: field_name,
+ name: field_name,
+ string: field.string,
+ required: field.required
+ };
+
+ switch (field.type) {
+ case 'many2many':
+ case 'many2one':
+ f.name += '/id';
+ break;
+ case 'one2many':
+ f.name += '/id';
+ f.fields = [];
+ // only fetch sub-fields to a depth of 2 levels
+ if (level < 2) {
+ self.ready.push(new openerp.web.DataSet(self, field.relation).call(
+ 'fields_get', [], function (fields) {
+ self.graft_fields(fields, f, level+1);
+ }));
+ }
+ break;
+ }
+ parent.fields.push(f);
+ });
+ },
+ toggle_import_button: function (newstate) {
+ this.$dialog.dialog('widget')
+ .find('.oe-dialog-import-button')
+ .button('option', 'disabled', !newstate);
+ },
+ do_import: function() {
+ if(!this.$element.find('#csvfile').val()) { return; }
+ var lines_to_skip = parseInt(this.$element.find('#csv_skip').val(), 10);
+ var with_headers = this.$element.find('#file_has_headers').prop('checked');
+ if (!lines_to_skip && with_headers) {
+ lines_to_skip = 1;
+ }
+ var indices = [], fields = [];
+ this.$element.find(".sel_fields").each(function(index, element) {
+ var val = element.value;
+ if (!val) {
+ return;
+ }
+ indices.push(index);
+ fields.push(val);
+ });
+
+ jsonp(this.$element.find('#import_data'), {
+ url: '/web/import/import_data',
+ data: {
+ model: this.model,
+ meta: JSON.stringify({
+ skip: lines_to_skip,
+ indices: indices,
+ fields: fields
+ })
+ }
+ }, this.on_import_results);
+ },
+ on_autodetect_data: function() {
+ if(!this.$element.find('#csvfile').val()) { return; }
+ jsonp(this.$element.find('#import_data'), {
+ url: '/web/import/detect_data'
+ }, this.on_import_results);
+ },
+ on_import_results: function(results) {
+ this.$element.find('#result').empty();
+ var headers, result_node = this.$element.find("#result");
+
+ if (results['records']) {
+ var lines_to_skip = parseInt(this.$element.find('#csv_skip').val(), 10),
+ with_headers = this.$element.find('#file_has_headers').prop('checked');
+ headers = with_headers ? results.records[0] : null;
+
+ result_node.append(QWeb.render('ImportView.result', {
+ 'headers': headers,
+ 'records': lines_to_skip ? results.records.slice(lines_to_skip)
+ : with_headers ? results.records.slice(1)
+ : results.records
+ }));
+ } else if (results['error']) {
+ result_node.append(QWeb.render('ImportView.error', {
+ 'error': results['error']}));
+ } else if (results['success']) {
+ if (this.widget_parent.widget_parent.active_view == "list") {
+ this.widget_parent.reload_content();
+ }
+ this.stop();
+ return;
+ }
+
+ var self = this;
+ this.ready.then(function () {
+ var $fields = self.$element.find('.sel_fields').bind('blur', function () {
+ if (this.value && !_(self.all_fields).contains(this.value)) {
+ this.value = '';
+ }
+ }).autocomplete({
+ minLength: 0,
+ source: self.all_fields,
+ change: self.on_check_field_values
+ }).focus(function () {
+ $(this).autocomplete('search');
+ });
+ // Column auto-detection
+ _(headers).each(function (header, index) {
+ var f =_(self.fields).detect(function (field) {
+ // TODO: levenshtein between header and field.string
+ return field.name === header || field.string.toLowerCase() === header;
+ });
+ if (f) {
+ $fields.eq(index).val(f.name);
+ }
+ });
+ self.on_check_field_values();
+ });
+ },
+ /**
+ * Looks through all the field selections, and tries to find if two
+ * (or more) columns were matched to the same model field.
+ *
+ * Returns a map of the multiply-mapped fields to an array of offending
+ * columns (not actually columns, but the inputs containing the same field
+ * names).
+ *
+ * Also has the side-effect of marking the discovered inputs with the class
+ * ``duplicate_fld``.
+ *
+ * @returns {Object<String, Array<String>>} map of duplicate field matches to same-valued inputs
+ */
+ find_duplicate_fields: function() {
+ // Maps values to DOM nodes, in order to discover duplicates
+ var values = {}, duplicates = {};
+ this.$element.find(".sel_fields").each(function(index, element) {
+ var value = element.value;
+ var $element = $(element).removeClass('duplicate_fld');
+ if (!value) { return; }
+
+ if (!(value in values)) {
+ values[value] = element;
+ } else {
+ var same_valued_field = values[value];
+ if (value in duplicates) {
+ duplicates[value].push(element);
+ } else {
+ duplicates[value] = [same_valued_field, element];
+ }
+ $element.add(same_valued_field).addClass('duplicate_fld');
+ }
+ });
+ return duplicates;
+ },
+ on_check_field_values: function () {
+ this.$element.find("#message, #msg").remove();
+
+ var required_valid = this.check_required();
+
+ var duplicates = this.find_duplicate_fields();
+ if (_.isEmpty(duplicates)) {
+ this.toggle_import_button(required_valid);
+ } else {
+ var $err = $('<div id="msg" style="color: red;">Destination fields should only be selected once, some fields are selected more than once:</div>').insertBefore(this.$element.find('#result'));
+ var $dupes = $('<dl>').appendTo($err);
+ _(duplicates).each(function(elements, value) {
+ $('<dt>').text(value).appendTo($dupes);
+ _(elements).each(function(element) {
+ var cell = $(element).closest('td');
+ $('<dd>').text(cell.parent().children().index(cell)).appendTo($dupes);
+ });
+ });
+ this.toggle_import_button(false);
+ }
+
+ },
+ check_required: function() {
+ if (!this.required_fields.length) { return true; }
+
+ var selected_fields = _(this.$element.find('.sel_fields').get()).chain()
+ .pluck('value')
+ .compact()
+ .value();
+
+ var missing_fields = _.difference(this.required_fields, selected_fields);
+ if (missing_fields.length) {
+ this.$element.find("#result").before('<div id="message" style="color:red">*Required Fields are not selected : ' + missing_fields + '.</div>');
+ return false;
+ }
+ return true;
+ },
+ stop: function() {
+ $(this.$dialog).remove();
+ this._super();
+ }
+});
+};
diff --git a/addons/web/static/src/js/formats.js b/addons/web/static/src/js/formats.js
index b0dc4fcc289..e7feedf26a5 100644
--- a/addons/web/static/src/js/formats.js
+++ b/addons/web/static/src/js/formats.js
@@ -82,7 +82,12 @@ openerp.web.parse_value = function (value, descriptor, value_if_empty) {
}
switch (descriptor.widget || descriptor.type) {
case 'integer':
- var tmp = Number(value);
+ var tmp;
+ do {
+ tmp = value;
+ value = value.replace(openerp.web._t.database.parameters.thousands_sep, "");
+ } while(tmp !== value);
+ tmp = Number(value);
if (isNaN(tmp))
throw value + " is not a correct integer";
return tmp;
@@ -113,26 +118,26 @@ openerp.web.parse_value = function (value, descriptor, value_if_empty) {
var tmp = Date.parseExact(value, _.sprintf("%s %s", Date.CultureInfo.formatPatterns.shortDate,
Date.CultureInfo.formatPatterns.longTime));
if (tmp !== null)
- return tmp;
+ return openerp.web.datetime_to_str(tmp);
tmp = Date.parse(value);
if (tmp !== null)
- return tmp;
+ return openerp.web.datetime_to_str(tmp);
throw value + " is not a valid datetime";
case 'date':
var tmp = Date.parseExact(value, Date.CultureInfo.formatPatterns.shortDate);
if (tmp !== null)
- return tmp;
+ return openerp.web.date_to_str(tmp);
tmp = Date.parse(value);
if (tmp !== null)
- return tmp;
+ return openerp.web.date_to_str(tmp);
throw value + " is not a valid date";
case 'time':
var tmp = Date.parseExact(value, Date.CultureInfo.formatPatterns.longTime);
if (tmp !== null)
- return tmp;
+ return openerp.web.time_to_str(tmp);
tmp = Date.parse(value);
if (tmp !== null)
- return tmp;
+ return openerp.web.time_to_str(tmp);
throw value + " is not a valid time";
}
return value;
diff --git a/addons/web/static/src/js/search.js b/addons/web/static/src/js/search.js
index 5db66ab7260..db91d6bfd27 100644
--- a/addons/web/static/src/js/search.js
+++ b/addons/web/static/src/js/search.js
@@ -327,7 +327,8 @@ openerp.web.SearchView = openerp.web.Widget.extend(/** @lends openerp.web.Search
this.notification.notify("Invalid Search", "triggered from search view");
},
do_clear: function () {
- $('.filter_label').removeClass('enabled');
+ this.$element.find('.filter_label').removeClass('enabled');
+ this.enabled_filters.splice(0);
var string = $('a.searchview_group_string');
_.each(string, function(str){
$(str).closest('div.searchview_group').removeClass("expanded").addClass('folded');
@@ -542,6 +543,7 @@ openerp.web.search.FilterGroup = openerp.web.search.Input.extend(/** @lends open
var domains = _(this.filters).chain()
.filter(function (filter) { return filter.is_enabled(); })
.map(function (filter) { return filter.attrs.domain; })
+ .reject(_.isEmpty)
.value();
if (!domains.length) { return; }
@@ -720,7 +722,11 @@ openerp.web.search.NumberField = openerp.web.search.Field.extend(/** @lends open
openerp.web.search.IntegerField = openerp.web.search.NumberField.extend(/** @lends openerp.web.search.IntegerField# */{
error_message: "not a valid integer",
parse: function (value) {
- return parseInt(value, 10);
+ try {
+ return openerp.web.parse_value(value, {'widget': 'integer'});
+ } catch (e) {
+ return NaN;
+ }
}
});
/**
@@ -730,7 +736,11 @@ openerp.web.search.IntegerField = openerp.web.search.NumberField.extend(/** @len
openerp.web.search.FloatField = openerp.web.search.NumberField.extend(/** @lends openerp.web.search.FloatField# */{
error_message: "not a valid number",
parse: function (value) {
- return parseFloat(value);
+ try {
+ return openerp.web.parse_value(value, {'widget': 'float'});
+ } catch (e) {
+ return NaN;
+ }
}
});
/**
@@ -785,20 +795,18 @@ openerp.web.search.BooleanField = openerp.web.search.SelectionField.extend(/** @
* @extends openerp.web.search.DateField
*/
openerp.web.search.DateField = openerp.web.search.Field.extend(/** @lends openerp.web.search.DateField# */{
- /**
- * enables date picker on the HTML widgets
- */
+ template: "SearchView.date",
start: function () {
this._super();
- this.$element.addClass('field_date').datepicker({
- dateFormat: 'yy-mm-dd'
- });
- },
- stop: function () {
- this.$element.datepicker('destroy');
+ this.datewidget = new openerp.web.DateWidget(this);
+ this.datewidget.prependTo(this.$element);
+ this.datewidget.$element.find("input").attr("size", 15);
+ this.datewidget.$element.find("input").attr("autofocus",
+ this.attrs.default_focus === '1' ? 'autofocus' : undefined);
+ this.datewidget.set_value(this.defaults[this.attrs.name] || false);
},
get_value: function () {
- return this.$element.val();
+ return this.datewidget.get_value() || null;
}
});
/**
@@ -1113,7 +1121,7 @@ openerp.web.search.ExtendedSearchProposition.Char = openerp.web.OldWidget.extend
}
});
openerp.web.search.ExtendedSearchProposition.DateTime = openerp.web.OldWidget.extend({
- template: 'SearchView.extended_search.proposition.datetime',
+ template: 'SearchView.extended_search.proposition.empty',
identifier_prefix: 'extended-search-proposition-datetime',
operators: [
{value: "=", text: "is equal to"},
@@ -1124,18 +1132,16 @@ openerp.web.search.ExtendedSearchProposition.DateTime = openerp.web.OldWidget.ex
{value: "<=", text: "less or equal than"}
],
get_value: function() {
- return this.$element.val();
+ return this.datewidget.get_value();
},
start: function() {
this._super();
- this.$element.datetimepicker({
- dateFormat: 'yy-mm-dd',
- timeFormat: 'hh:mm:ss'
- });
+ this.datewidget = new openerp.web.DateTimeWidget(this);
+ this.datewidget.prependTo(this.$element);
}
});
openerp.web.search.ExtendedSearchProposition.Date = openerp.web.OldWidget.extend({
- template: 'SearchView.extended_search.proposition.date',
+ template: 'SearchView.extended_search.proposition.empty',
identifier_prefix: 'extended-search-proposition-date',
operators: [
{value: "=", text: "is equal to"},
@@ -1146,14 +1152,12 @@ openerp.web.search.ExtendedSearchProposition.Date = openerp.web.OldWidget.extend
{value: "<=", text: "less or equal than"}
],
get_value: function() {
- return this.$element.val();
+ return this.datewidget.get_value();
},
start: function() {
this._super();
- this.$element.datepicker({
- dateFormat: 'yy-mm-dd',
- timeFormat: 'hh:mm:ss'
- });
+ this.datewidget = new openerp.web.DateWidget(this);
+ this.datewidget.prependTo(this.$element);
}
});
openerp.web.search.ExtendedSearchProposition.Integer = openerp.web.OldWidget.extend({
@@ -1168,11 +1172,11 @@ openerp.web.search.ExtendedSearchProposition.Integer = openerp.web.OldWidget.ext
{value: "<=", text: "less or equal than"}
],
get_value: function() {
- var value = parseFloat(this.$element.val());
- if(value != 0 && !value) {
+ try {
+ return openerp.web.parse_value(this.$element.val(), {'widget': 'integer'});
+ } catch (e) {
return "";
}
- return Math.round(value);
}
});
openerp.web.search.ExtendedSearchProposition.Float = openerp.web.OldWidget.extend({
@@ -1187,11 +1191,11 @@ openerp.web.search.ExtendedSearchProposition.Float = openerp.web.OldWidget.exten
{value: "<=", text: "less or equal than"}
],
get_value: function() {
- var value = parseFloat(this.$element.val());
- if(value != 0 && !value) {
+ try {
+ return openerp.web.parse_value(this.$element.val(), {'widget': 'float'});
+ } catch (e) {
return "";
}
- return value;
}
});
openerp.web.search.ExtendedSearchProposition.Selection = openerp.web.OldWidget.extend({
diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js
index d3293b4f2ad..11ebad8d093 100644
--- a/addons/web/static/src/js/view_form.js
+++ b/addons/web/static/src/js/view_form.js
@@ -26,7 +26,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
this.set_default_options(options);
this.dataset = dataset;
this.model = dataset.model;
- this.view_id = view_id;
+ this.view_id = view_id || false;
this.fields_view = {};
this.widgets = {};
this.widgets_counter = 0;
@@ -41,7 +41,8 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
this.has_been_loaded = $.Deferred();
this.$form_header = null;
this.translatable_fields = [];
- _.defaults(this.options, {"always_show_new_button": true});
+ _.defaults(this.options, {"always_show_new_button": true,
+ "not_interactible_on_create": false});
},
start: function() {
this._super();
@@ -72,17 +73,25 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
_.each(this.widgets, function(w) {
w.stop();
});
+ this._super();
+ },
+ reposition: function ($e) {
+ this.$element = $e;
+ this.on_loaded();
},
on_loaded: function(data) {
var self = this;
- this.fields_view = data;
- var frame = new (this.registry.get_object('frame'))(this, this.fields_view.arch);
+ if (data) {
+ this.fields_view = data;
+ var frame = new (this.registry.get_object('frame'))(this, this.fields_view.arch);
- this.$element.html(QWeb.render(this.form_template, { 'frame': frame, 'view': this }));
+ this.rendered = QWeb.render(this.form_template, { 'frame': frame, 'view': this });
+ }
+ this.$element.html(this.rendered);
_.each(this.widgets, function(w) {
w.start();
});
- this.$form_header = this.$element.find('#' + this.element_id + '_header');
+ this.$form_header = this.$element.find('.oe_form_header:first');
this.$form_header.find('div.oe_form_pager button[data-pager-action]').click(function() {
var action = $(this).data('pager-action');
self.on_pager_action(action);
@@ -93,6 +102,17 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
this.$form_header.find('button.oe_form_button_cancel').click(this.do_cancel);
this.$form_header.find('button.oe_form_button_new').click(this.on_button_new);
this.$form_header.find('button.oe_form_button_duplicate').click(this.on_button_duplicate);
+ this.$form_header.find('button.oe_form_button_toggle').click(function () {
+ self.translatable_fields = [];
+ self.widgets = {};
+ self.fields = {};
+ self.$form_header.find('button').unbind('click');
+ self.registry = self.registry === openerp.web.form.widgets
+ ? openerp.web.form.readonly
+ : openerp.web.form.widgets;
+ self.on_loaded(self.fields_view);
+ self.reload();
+ });
if (this.options.sidebar && this.options.sidebar_id) {
this.sidebar = new openerp.web.Sidebar(this, this.options.sidebar_id);
@@ -196,7 +216,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
}
},
do_update_pager: function(hide_index) {
- var $pager = this.$element.find('#' + this.element_id + '_header div.oe_form_pager');
+ var $pager = this.$form_header.find('div.oe_form_pager');
var index = hide_index ? '-' : this.dataset.index + 1;
$pager.find('span.oe_pager_index').html(index);
$pager.find('span.oe_pager_count').html(this.dataset.ids.length);
@@ -304,9 +324,15 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
var def = $.Deferred();
$.when(this.has_been_loaded).then(function() {
if (self.can_be_discarded()) {
- self.dataset.default_get(_.keys(self.fields_view.fields)).then(self.on_record_loaded).then(function() {
+ var keys = _.keys(self.fields_view.fields);
+ if (keys.length) {
+ self.dataset.default_get(keys).then(self.on_record_loaded).then(function() {
+ def.resolve();
+ });
+ } else {
+ self.on_record_loaded({});
def.resolve();
- });
+ }
}
});
return def.promise();
@@ -339,7 +365,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
do_save: function(success, prepend_on_create) {
var self = this;
if (!this.ready) {
- return false;
+ return $.Deferred().reject();
}
var form_dirty = false,
form_invalid = false,
@@ -361,23 +387,18 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
if (form_invalid) {
first_invalid_field.focus();
this.on_invalid();
- return false;
- } else if (form_dirty) {
+ return $.Deferred().reject();
+ } else {
console.log("About to save", values);
if (!this.datarecord.id) {
- return this.dataset.create(values, function(r) {
- self.on_created(r, success, prepend_on_create);
- });
+ return this.dataset.create(values).pipe(function(r) {
+ return self.on_created(r, undefined, prepend_on_create);
+ }).then(success);
} else {
- return this.dataset.write(this.datarecord.id, values, {}, function(r) {
- self.on_saved(r, success);
- });
+ return this.dataset.write(this.datarecord.id, values, {}).pipe(function(r) {
+ return self.on_saved(r);
+ }).then(success);
}
- } else {
- setTimeout(function() {
- self.on_saved({ result: true }, success);
- });
- return true;
}
},
do_save_edit: function() {
@@ -401,11 +422,10 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
on_saved: function(r, success) {
if (!r.result) {
// should not happen in the server, but may happen for internal purpose
+ return $.Deferred().reject();
} else {
- if (success) {
- success(r);
- }
this.reload();
+ return $.Deferred().then(success).resolve(r);
}
},
/**
@@ -424,6 +444,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
on_created: function(r, success, prepend_on_create) {
if (!r.result) {
// should not happen in the server, but may happen for internal purpose
+ return $.Deferred().reject();
} else {
this.datarecord.id = r.result;
if (!prepend_on_create) {
@@ -438,10 +459,8 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
this.sidebar.attachments.do_update();
}
console.debug("The record has been created with id #" + this.datarecord.id);
- if (success) {
- success(_.extend(r, {created: true}));
- }
this.reload();
+ return $.Deferred().then(success).resolve(_.extend(r, {created: true}));
}
},
do_search: function (domains, contexts, groupbys) {
@@ -471,6 +490,24 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
get_selected_ids: function() {
var id = this.dataset.ids[this.dataset.index];
return id ? [id] : [];
+ },
+ recursive_save: function() {
+ var self = this;
+ return $.when(this.do_save()).pipe(function(res) {
+ if (self.dataset.parent_view)
+ return self.dataset.parent_view.recursive_save();
+ });
+ },
+ is_interactible_record: function() {
+ var id = this.datarecord.id;
+ if (!id) {
+ if (this.options.not_interactible_on_create)
+ return false;
+ } else if (typeof(id) === "string") {
+ if(openerp.web.BufferedDataSet.virtual_id_regex.test(id))
+ return false;
+ }
+ return true;
}
});
openerp.web.FormDialog = openerp.web.Dialog.extend({
@@ -618,6 +655,7 @@ openerp.web.form.compute_domain = function(expr, fields) {
openerp.web.form.Widget = openerp.web.Widget.extend(/** @lends openerp.web.form.Widget# */{
template: 'Widget',
+ identifier_prefix: 'formview-widget-',
/**
* @constructs openerp.web.form.Widget
* @extends openerp.web.Widget
@@ -631,11 +669,13 @@ openerp.web.form.Widget = openerp.web.Widget.extend(/** @lends openerp.web.form.
this.modifiers = JSON.parse(this.node.attrs.modifiers || '{}');
this.type = this.type || node.tag;
this.element_name = this.element_name || this.type;
- this.element_id = [this.view.element_id, this.element_name, this.view.widgets_counter++].join("_");
+ this.element_class = [
+ 'formview', this.view.view_id, this.element_name,
+ this.view.widgets_counter++].join("_");
- this._super(view, this.element_id);
+ this._super(view);
- this.view.widgets[this.element_id] = this;
+ this.view.widgets[this.element_class] = this;
this.children = node.children;
this.colspan = parseInt(node.attrs.colspan || 1, 10);
this.decrease_max_width = 0;
@@ -648,12 +688,7 @@ openerp.web.form.Widget = openerp.web.Widget.extend(/** @lends openerp.web.form.
this.width = this.node.attrs.width;
},
start: function() {
- this.$element = $('#' + this.element_id);
- },
- stop: function() {
- if (this.$element) {
- this.$element.remove();
- }
+ this.$element = this.view.$element.find('.' + this.element_class);
},
process_modifiers: function() {
var compute_domain = openerp.web.form.compute_domain;
@@ -757,13 +792,24 @@ openerp.web.form.WidgetNotebook = openerp.web.form.Widget.extend({
for (var i = 0; i < node.children.length; i++) {
var n = node.children[i];
if (n.tag == "page") {
- var page = new openerp.web.form.WidgetNotebookPage(this.view, n, this, this.pages.length);
+ var page = new openerp.web.form.WidgetNotebookPage(
+ this.view, n, this, this.pages.length);
this.pages.push(page);
}
}
},
start: function() {
+ var self = this;
this._super.apply(this, arguments);
+ this.$element.find('> ul > li').each(function (index, tab_li) {
+ var page = self.pages[index],
+ id = _.uniqueId(self.element_name + '-');
+ page.element_id = id;
+ $(tab_li).find('a').attr('href', '#' + id);
+ });
+ this.$element.find('> div').each(function (index, page) {
+ page.id = self.pages[index].element_id;
+ });
this.$element.tabs();
this.view.on_button_new.add_last(this.do_select_first_visible_tab);
},
@@ -785,11 +831,11 @@ openerp.web.form.WidgetNotebookPage = openerp.web.form.WidgetFrame.extend({
this.index = index;
this.element_name = 'page_' + index;
this._super(view, node);
- this.element_tab_id = this.element_id + '_tab';
},
start: function() {
this._super.apply(this, arguments);
- this.$element_tab = $('#' + this.element_tab_id);
+ this.$element_tab = this.notebook.$element.find(
+ '> ul > li:eq(' + this.index + ')');
},
update_dom: function() {
if (this.invisible && this.index === this.notebook.$element.tabs('option', 'selected')) {
@@ -801,9 +847,9 @@ openerp.web.form.WidgetNotebookPage = openerp.web.form.WidgetFrame.extend({
});
openerp.web.form.WidgetSeparator = openerp.web.form.Widget.extend({
+ template: 'WidgetSeparator',
init: function(view, node) {
this._super(view, node);
- this.template = "WidgetSeparator";
this.orientation = node.attrs.orientation || 'horizontal';
if (this.orientation === 'vertical') {
this.width = '1';
@@ -813,9 +859,10 @@ openerp.web.form.WidgetSeparator = openerp.web.form.Widget.extend({
});
openerp.web.form.WidgetButton = openerp.web.form.Widget.extend({
+ template: 'WidgetButton',
init: function(view, node) {
this._super(view, node);
- this.template = "WidgetButton";
+ this.force_disabled = false;
if (this.string) {
// We don't have button key bindings in the webclient
this.string = this.string.replace(/_/g, '');
@@ -827,45 +874,74 @@ openerp.web.form.WidgetButton = openerp.web.form.Widget.extend({
},
start: function() {
this._super.apply(this, arguments);
- this.$element.click(this.on_click);
+ this.$element.find("button").click(this.on_click);
},
- on_click: function(saved) {
+ on_click: function() {
var self = this;
- if ((!this.node.attrs.special && this.view.dirty_for_user && saved !== true) || !this.view.datarecord.id) {
- this.view.do_save(function() {
- self.on_click(true);
- });
- } else {
- if (this.node.attrs.confirm) {
- var dialog = $('<div>' + this.node.attrs.confirm + '</div>').dialog({
+ this.force_disabled = true;
+ this.check_disable();
+ this.execute_action().always(function() {
+ self.force_disabled = false;
+ self.check_disable();
+ });
+ },
+ execute_action: function() {
+ var self = this;
+ var exec_action = function() {
+ if (self.node.attrs.confirm) {
+ var def = $.Deferred();
+ var dialog = $('<div>' + self.node.attrs.confirm + '</div>').dialog({
title: 'Confirm',
modal: true,
buttons: {
Ok: function() {
- self.on_confirmed();
- $(this).dialog("close");
+ self.on_confirmed().then(function() {
+ def.resolve();
+ });
+ $(self).dialog("close");
},
Cancel: function() {
- $(this).dialog("close");
+ def.resolve();
+ $(self).dialog("close");
}
}
});
+ return def.promise();
} else {
- this.on_confirmed();
+ return self.on_confirmed();
}
+ };
+ if ((!this.node.attrs.special && this.view.dirty_for_user) || !this.view.datarecord.id) {
+ return this.view.recursive_save().pipe(exec_action);
+ } else {
+ return exec_action();
}
},
on_confirmed: function() {
var self = this;
- this.view.do_execute_action(
+ return this.view.do_execute_action(
this.node.attrs, this.view.dataset, this.view.datarecord.id, function () {
self.view.reload();
});
+ },
+ update_dom: function() {
+ this._super();
+ this.check_disable();
+ },
+ check_disable: function() {
+ if (this.force_disabled || !this.view.is_interactible_record()) {
+ this.$element.find("button").attr("disabled", "disabled");
+ this.$element.find("button").css("color", "grey");
+ } else {
+ this.$element.find("button").removeAttr("disabled");
+ this.$element.find("button").css("color", "");
+ }
}
});
openerp.web.form.WidgetLabel = openerp.web.form.Widget.extend({
+ template: 'WidgetLabel',
init: function(view, node) {
this.element_name = 'label_' + node.attrs.name;
@@ -876,7 +952,6 @@ openerp.web.form.WidgetLabel = openerp.web.form.Widget.extend({
this.template = "WidgetParagraph";
this.colspan = parseInt(this.node.attrs.colspan || 1, 10);
} else {
- this.template = "WidgetLabel";
this.colspan = 1;
this.width = '1%';
this.decrease_max_width = 1;
@@ -895,7 +970,7 @@ openerp.web.form.WidgetLabel = openerp.web.form.Widget.extend({
var self = this;
this.$element.find("label").dblclick(function() {
var widget = self['for'] || self;
- console.log(widget.element_id , widget);
+ console.log(widget.element_class , widget);
window.w = widget;
});
}
@@ -960,7 +1035,7 @@ openerp.web.form.Field = openerp.web.form.Widget.extend(/** @lends openerp.web.f
return !this.invalid;
},
is_dirty: function() {
- return this.dirty;
+ return this.dirty && !this.readonly;
},
get_on_change_value: function() {
return this.get_value();
@@ -995,37 +1070,40 @@ openerp.web.form.Field = openerp.web.form.Widget.extend(/** @lends openerp.web.f
focus: function() {
},
_build_view_fields_values: function() {
- var a_dataset = this.view.dataset || {};
+ var a_dataset = this.view.dataset;
var fields_values = this.view.get_fields_values();
var parent_values = a_dataset.parent_view ? a_dataset.parent_view.get_fields_values() : {};
fields_values.parent = parent_values;
return fields_values;
},
+ _build_eval_context: function() {
+ var a_dataset = this.view.dataset;
+ return new openerp.web.CompoundContext(a_dataset.get_context(), this._build_view_fields_values());
+ },
/**
* Builds a new context usable for operations related to fields by merging
* the fields'context with the action's context.
*/
build_context: function() {
- // I previously belevied contexts should be herrited, but now I doubt it
- //var a_context = this.view.dataset.get_context() || {};
var f_context = this.field.context || null;
// maybe the default_get should only be used when we do a default_get?
- var v_context1 = this.node.attrs.default_get || {};
- var v_context2 = this.node.attrs.context || {};
- var v_context = new openerp.web.CompoundContext(v_context1, v_context2);
- if (v_context1.__ref || v_context2.__ref || true) { //TODO niv: remove || true
- var fields_values = this._build_view_fields_values();
+ var v_contexts = _.compact([this.node.attrs.default_get || null,
+ this.node.attrs.context || null]);
+ var v_context = new openerp.web.CompoundContext();
+ _.each(v_contexts, function(x) {v_context.add(x);});
+ if (_.detect(v_contexts, function(x) {return !!x.__ref;})) {
+ var fields_values = this._build_eval_context();
v_context.set_eval_context(fields_values);
}
// if there is a context on the node, overrides the model's context
- var ctx = f_context || v_context;
+ var ctx = v_contexts.length > 0 ? v_context : f_context;
return ctx;
},
build_domain: function() {
var f_domain = this.field.domain || null;
var v_domain = this.node.attrs.domain || [];
if (!(v_domain instanceof Array) || true) { //TODO niv: remove || true
- var fields_values = this._build_view_fields_values();
+ var fields_values = this._build_eval_context();
v_domain = new openerp.web.CompoundDomain(v_domain).set_eval_context(fields_values);
}
// if there is a domain on the node, overrides the model's domain
@@ -1034,10 +1112,7 @@ openerp.web.form.Field = openerp.web.form.Widget.extend(/** @lends openerp.web.f
});
openerp.web.form.FieldChar = openerp.web.form.Field.extend({
- init: function(view, node) {
- this._super(view, node);
- this.template = "FieldChar";
- },
+ template: 'FieldChar',
start: function() {
this._super.apply(this, arguments);
this.$element.find('input').change(this.on_ui_change);
@@ -1046,6 +1121,7 @@ openerp.web.form.FieldChar = openerp.web.form.Field.extend({
this._super.apply(this, arguments);
var show_value = openerp.web.format_value(value, this, '');
this.$element.find('input').val(show_value);
+ return show_value;
},
update_dom: function() {
this._super.apply(this, arguments);
@@ -1070,10 +1146,7 @@ openerp.web.form.FieldChar = openerp.web.form.Field.extend({
});
openerp.web.form.FieldEmail = openerp.web.form.FieldChar.extend({
- init: function(view, node) {
- this._super(view, node);
- this.template = "FieldEmail";
- },
+ template: 'FieldEmail',
start: function() {
this._super.apply(this, arguments);
this.$element.find('button').click(this.on_button_clicked);
@@ -1084,18 +1157,11 @@ openerp.web.form.FieldEmail = openerp.web.form.FieldChar.extend({
} else {
location.href = 'mailto:' + this.value;
}
- },
- set_value: function(value) {
- this._super.apply(this, arguments);
- this.$element.find('a').attr('href', 'mailto:' + this.$element.find('input').val());
}
});
openerp.web.form.FieldUrl = openerp.web.form.FieldChar.extend({
- init: function(view, node) {
- this._super(view, node);
- this.template = "FieldUrl";
- },
+ template: 'FieldUrl',
start: function() {
this._super.apply(this, arguments);
this.$element.find('button').click(this.on_button_clicked);
@@ -1120,16 +1186,13 @@ openerp.web.form.FieldFloat = openerp.web.form.FieldChar.extend({
}
});
-openerp.web.form.FieldDatetime = openerp.web.form.Field.extend({
- init: function(view, node) {
- this._super(view, node);
- this.template = "FieldDate";
- this.jqueryui_object = 'datetimepicker';
- },
+openerp.web.DateTimeWidget = openerp.web.Widget.extend({
+ template: "web.datetimepicker",
+ jqueryui_object: 'datetimepicker',
+ type_of_date: "datetime",
start: function() {
var self = this;
- this._super.apply(this, arguments);
- this.$element.find('input').change(this.on_ui_change);
+ this.$element.find('input').change(this.on_change);
this.picker({
onSelect: this.on_picker_select,
changeMonth: true,
@@ -1147,6 +1210,8 @@ openerp.web.form.FieldDatetime = openerp.web.form.Field.extend({
this.$element.find('button.oe_datepicker_close').click(function() {
self.$element.find('.oe_datepicker').hide();
});
+ this.set_readonly(false);
+ this.value = false;
},
picker: function() {
return $.fn[this.jqueryui_object].apply(this.$element.find('.oe_datepicker_container'), arguments);
@@ -1156,69 +1221,99 @@ openerp.web.form.FieldDatetime = openerp.web.form.Field.extend({
this.$element.find('input').val(date ? this.format_client(date) : '').change();
},
set_value: function(value) {
- value = this.parse(value);
- this._super(value);
+ this.value = value;
this.$element.find('input').val(value ? this.format_client(value) : '');
},
get_value: function() {
- return this.format(this.value);
+ return this.value;
},
set_value_from_ui: function() {
var value = this.$element.find('input').val() || false;
this.value = this.parse_client(value);
- this._super();
},
- update_dom: function() {
- this._super.apply(this, arguments);
+ set_readonly: function(readonly) {
+ this.readonly = readonly;
this.$element.find('input').attr('disabled', this.readonly);
- this.$element.find('img.oe_datepicker_trigger').toggleClass('oe_input_icon_disabled', this.readonly);
+ this.$element.find('img.oe_datepicker_trigger').toggleClass('oe_input_icon_disabled', readonly);
},
- validate: function() {
- this.invalid = false;
+ is_valid: function(required) {
var value = this.$element.find('input').val();
if (value === "") {
- this.invalid = this.required;
+ return !required;
} else {
try {
this.parse_client(value);
- this.invalid = false;
+ return true;
} catch(e) {
- this.invalid = true;
+ return false;
}
}
},
focus: function() {
this.$element.find('input').focus();
},
- parse: openerp.web.auto_str_to_date,
parse_client: function(v) {
- return openerp.web.parse_value(v, this.field);
- },
- format: function(val) {
- return openerp.web.auto_date_to_str(val, this.field.type);
+ return openerp.web.parse_value(v, {"widget": this.type_of_date});
},
format_client: function(v) {
- return openerp.web.format_value(v, this.field);
+ return openerp.web.format_value(v, {"widget": this.type_of_date});
+ },
+ on_change: function() {
+ if (this.is_valid()) {
+ this.set_value_from_ui();
+ }
}
});
-openerp.web.form.FieldDate = openerp.web.form.FieldDatetime.extend({
- init: function(view, node) {
- this._super(view, node);
- this.jqueryui_object = 'datepicker';
- },
+openerp.web.DateWidget = openerp.web.DateTimeWidget.extend({
+ jqueryui_object: 'datepicker',
+ type_of_date: "date",
on_picker_select: function(text, instance) {
this._super(text, instance);
this.$element.find('.oe_datepicker').hide();
}
});
-openerp.web.form.FieldText = openerp.web.form.Field.extend({
- init: function(view, node) {
- this._super(view, node);
- this.template = "FieldText";
+openerp.web.form.FieldDatetime = openerp.web.form.Field.extend({
+ template: "EmptyComponent",
+ build_widget: function() {
+ return new openerp.web.DateTimeWidget(this);
},
start: function() {
+ var self = this;
+ this._super.apply(this, arguments);
+ this.datewidget = this.build_widget();
+ this.datewidget.on_change.add(this.on_ui_change);
+ this.datewidget.appendTo(this.$element);
+ },
+ set_value: function(value) {
+ this._super(value);
+ this.datewidget.set_value(value);
+ },
+ get_value: function() {
+ return this.datewidget.get_value();
+ },
+ update_dom: function() {
+ this._super.apply(this, arguments);
+ this.datewidget.set_readonly(this.readonly);
+ },
+ validate: function() {
+ this.invalid = !this.datewidget.is_valid(this.required);
+ },
+ focus: function() {
+ this.datewidget.focus();
+ }
+});
+
+openerp.web.form.FieldDate = openerp.web.form.FieldDatetime.extend({
+ build_widget: function() {
+ return new openerp.web.DateWidget(this);
+ }
+});
+
+openerp.web.form.FieldText = openerp.web.form.Field.extend({
+ template: 'FieldText',
+ start: function() {
this._super.apply(this, arguments);
this.$element.find('textarea').change(this.on_ui_change);
},
@@ -1250,10 +1345,7 @@ openerp.web.form.FieldText = openerp.web.form.Field.extend({
});
openerp.web.form.FieldBoolean = openerp.web.form.Field.extend({
- init: function(view, node) {
- this._super(view, node);
- this.template = "FieldBoolean";
- },
+ template: 'FieldBoolean',
start: function() {
var self = this;
this._super.apply(this, arguments);
@@ -1284,10 +1376,7 @@ openerp.web.form.FieldBoolean = openerp.web.form.Field.extend({
});
openerp.web.form.FieldProgressBar = openerp.web.form.Field.extend({
- init: function(view, node) {
- this._super(view, node);
- this.template = "FieldProgressBar";
- },
+ template: 'FieldProgressBar',
start: function() {
this._super.apply(this, arguments);
this.$element.find('div').progressbar({
@@ -1310,10 +1399,10 @@ openerp.web.form.FieldTextXml = openerp.web.form.Field.extend({
});
openerp.web.form.FieldSelection = openerp.web.form.Field.extend({
+ template: 'FieldSelection',
init: function(view, node) {
var self = this;
this._super(view, node);
- this.template = "FieldSelection";
this.values = this.field.selection;
_.each(this.values, function(v, i) {
if (v[0] === false && v[1] === '') {
@@ -1420,9 +1509,9 @@ openerp.web.form.dialog = function(content, options) {
};
openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({
+ template: 'FieldMany2One',
init: function(view, node) {
this._super(view, node);
- this.template = "FieldMany2One";
this.limit = 7;
this.value = null;
this.cm_id = _.uniqueId('m2o_cm_');
@@ -1435,7 +1524,7 @@ openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({
this.$input = this.$element.find("input");
this.$drop_down = this.$element.find(".oe-m2o-drop-down-button");
this.$menu_btn = this.$element.find(".oe-m2o-cm-button");
-
+
// context menu
var init_context_menu_def = $.Deferred().then(function(e) {
var rdataset = new openerp.web.DataSetStatic(self, "ir.values", self.build_context());
@@ -1443,7 +1532,7 @@ openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({
[[self.field.relation, false]], false, rdataset.get_context()], false, 0)
.then(function(result) {
self.related_entries = result;
-
+
var $cmenu = $("#" + self.cm_id);
$cmenu.append(QWeb.render("FieldMany2One.context_menu", {widget: self}));
var bindings = {};
@@ -1747,10 +1836,10 @@ var commands = {
}
};
openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({
+ template: 'FieldOne2Many',
multi_selection: false,
init: function(view, node) {
this._super(view, node);
- this.template = "FieldOne2Many";
this.is_started = $.Deferred();
this.form_last_update = $.Deferred();
this.disable_utility_classes = true;
@@ -1781,6 +1870,8 @@ openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({
}
if(view.view_type === "list") {
view.options.selectable = self.multi_selection;
+ } else if (view.view_type === "form") {
+ view.options.not_interactible_on_create = true;
}
views.push(view);
});
@@ -1840,6 +1931,7 @@ openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({
switch (command[0]) {
case commands.CREATE:
obj['id'] = _.uniqueId(self.dataset.virtual_id_prefix);
+ obj.defaults = {};
self.dataset.to_create.push(obj);
self.dataset.cache.push(_.clone(obj));
ids.push(obj.id);
@@ -1869,6 +1961,7 @@ openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({
_.each(value, function(command) {
var obj = {values: command};
obj['id'] = _.uniqueId(self.dataset.virtual_id_prefix);
+ obj.defaults = {};
self.dataset.to_create.push(obj);
self.dataset.cache.push(_.clone(obj));
ids.push(obj.id);
@@ -1913,11 +2006,7 @@ openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({
var view = this.viewmanager.views[this.viewmanager.active_view].controller;
if (this.viewmanager.active_view === "form") {
var res = $.when(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()) {
+ if (!res.isResolved() && !res.isRejected()) {
throw "Asynchronous get_value() is not supported in form view.";
}
return res;
@@ -1931,9 +2020,8 @@ openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({
},
validate: function() {
this.invalid = false;
- var self = this;
- var view = self.viewmanager.views[self.viewmanager.active_view].controller;
- if (self.viewmanager.active_view === "form") {
+ var view = this.viewmanager.views[this.viewmanager.active_view].controller;
+ if (this.viewmanager.active_view === "form") {
for (var f in view.fields) {
f = view.fields[f];
if (!f.is_valid()) {
@@ -1970,6 +2058,7 @@ openerp.web.form.One2ManyListView = openerp.web.ListView.extend({
} else {
var self = this;
var pop = new openerp.web.form.SelectCreatePopup(this);
+ pop.on_default_get.add(self.dataset.on_default_get);
pop.select_element(self.o2m.field.relation,{
initial_view: "form",
alternative_form_view: self.o2m.field.views ? self.o2m.field.views["form"] : undefined,
@@ -1979,7 +2068,8 @@ openerp.web.form.One2ManyListView = openerp.web.ListView.extend({
self.o2m.dataset.on_change();
});
},
- parent_view: self.o2m.view
+ parent_view: self.o2m.view,
+ form_view_options: {'not_interactible_on_create':true}
}, self.o2m.build_domain(), self.o2m.build_context());
pop.on_select_elements.add_last(function() {
self.o2m.reload_current_view();
@@ -1995,7 +2085,8 @@ openerp.web.form.One2ManyListView = openerp.web.ListView.extend({
parent_view: self.o2m.view,
read_function: function() {
return self.o2m.dataset.read_ids.apply(self.o2m.dataset, arguments);
- }
+ },
+ form_view_options: {'not_interactible_on_create':true}
});
pop.on_write.add(function(id, data) {
self.o2m.dataset.write(id, data, {}, function(r) {
@@ -2006,10 +2097,10 @@ openerp.web.form.One2ManyListView = openerp.web.ListView.extend({
});
openerp.web.form.FieldMany2Many = openerp.web.form.Field.extend({
+ template: 'FieldMany2Many',
multi_selection: false,
init: function(view, node) {
this._super(view, node);
- this.template = "FieldMany2Many";
this.list_id = _.uniqueId("many2many");
this.is_started = $.Deferred();
},
@@ -2110,6 +2201,8 @@ openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends ope
* - alternative_form_view
* - create_function (defaults to a naive saving behavior)
* - parent_view
+ * - form_view_options
+ * - list_view_options
*/
select_element: function(model, options, domain, context) {
var self = this;
@@ -2131,6 +2224,7 @@ openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends ope
this.dataset = new openerp.web.ReadOnlyDataSetSearch(this, this.model,
this.context);
this.dataset.parent_view = this.options.parent_view;
+ this.dataset.on_default_get.add(this.on_default_get);
if (this.options.initial_view == "search") {
this.setup_search_view();
} else { // "form"
@@ -2173,7 +2267,7 @@ openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends ope
});
self.view_list = new openerp.web.form.SelectCreateListView(self,
self.dataset, false,
- {'deletable': false});
+ _.extend({'deletable': false}, self.options.list_view_options || {}));
self.view_list.popup = self;
self.view_list.appendTo($("#" + self.element_id + "_view_list")).pipe(function() {
self.view_list.do_show();
@@ -2209,7 +2303,7 @@ openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends ope
this.view_list.$element.hide();
}
this.dataset.index = null;
- this.view_form = new openerp.web.FormView(this, this.dataset, false);
+ this.view_form = new openerp.web.FormView(this, this.dataset, false, self.options.form_view_options);
if (this.options.alternative_form_view) {
this.view_form.set_embedded_view(this.options.alternative_form_view);
}
@@ -2253,7 +2347,8 @@ openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends ope
this.on_select_elements(this.created_elements);
}
this.stop();
- }
+ },
+ on_default_get: function(res) {}
});
openerp.web.form.SelectCreateListView = openerp.web.ListView.extend({
@@ -2283,6 +2378,7 @@ openerp.web.form.FormOpenPopup = openerp.web.OldWidget.extend(/** @lends openerp
* - auto_write (default true)
* - read_function
* - parent_view
+ * - form_view_options
*/
show_element: function(model, row_id, context, options) {
this.model = model;
@@ -2318,7 +2414,7 @@ openerp.web.form.FormOpenPopup = openerp.web.OldWidget.extend(/** @lends openerp
on_write_completed: function() {},
setup_form_view: function() {
var self = this;
- this.view_form = new openerp.web.FormView(this, this.dataset, false);
+ this.view_form = new openerp.web.FormView(this, this.dataset, false, self.options.form_view_options);
if (this.options.alternative_form_view) {
this.view_form.set_embedded_view(this.options.alternative_form_view);
}
@@ -2351,9 +2447,9 @@ openerp.web.form.FormOpenDataset = openerp.web.ReadOnlyDataSetSearch.extend({
});
openerp.web.form.FieldReference = openerp.web.form.Field.extend({
+ template: 'FieldReference',
init: function(view, node) {
this._super(view, node);
- this.template = "FieldReference";
this.fields_view = {
fields: {
selection: {
@@ -2488,10 +2584,7 @@ openerp.web.form.FieldBinary = openerp.web.form.Field.extend({
});
openerp.web.form.FieldBinaryFile = openerp.web.form.FieldBinary.extend({
- init: function(view, node) {
- this._super(view, node);
- this.template = "FieldBinaryFile";
- },
+ template: 'FieldBinaryFile',
set_value: function(value) {
this._super.apply(this, arguments);
var show_value = (value != null && value !== false) ? value : '';
@@ -2519,10 +2612,7 @@ openerp.web.form.FieldBinaryFile = openerp.web.form.FieldBinary.extend({
});
openerp.web.form.FieldBinaryImage = openerp.web.form.FieldBinary.extend({
- init: function(view, node) {
- this._super(view, node);
- this.template = "FieldBinaryImage";
- },
+ template: 'FieldBinaryImage',
start: function() {
this._super.apply(this, arguments);
this.$image = this.$element.find('img.oe-binary-image');
@@ -2597,6 +2687,93 @@ openerp.web.form.FieldStatus = openerp.web.form.Field.extend({
}
});
+openerp.web.form.WidgetNotebookReadonly = openerp.web.form.WidgetNotebook.extend({
+ template: 'WidgetNotebook.readonly'
+});
+openerp.web.form.FieldReadonly = openerp.web.form.Field.extend({
+
+});
+openerp.web.form.FieldCharReadonly = openerp.web.form.FieldReadonly.extend({
+ template: 'FieldChar.readonly',
+ set_value: function (value) {
+ this._super.apply(this, arguments);
+ var show_value = openerp.web.format_value(value, this, '');
+ this.$element.find('div').text(show_value);
+ return show_value;
+ }
+});
+openerp.web.form.FieldURIReadonly = openerp.web.form.FieldCharReadonly.extend({
+ template: 'FieldURI.readonly',
+ scheme: null,
+ set_value: function (value) {
+ var displayed = this._super.apply(this, arguments);
+ this.$element.find('a')
+ .attr('href', this.scheme + ':' + displayed)
+ .text(displayed);
+ }
+});
+openerp.web.form.FieldEmailReadonly = openerp.web.form.FieldURIReadonly.extend({
+ scheme: 'mailto'
+});
+openerp.web.form.FieldUrlReadonly = openerp.web.form.FieldURIReadonly.extend({
+ set_value: function (value) {
+ var s = /(\w+):(\.+)/.match(value);
+ if (!(s[0] === 'http' || s[0] === 'https')) { return; }
+ this.scheme = s[0];
+ this._super(s[1]);
+ }
+});
+openerp.web.form.FieldBooleanReadonly = openerp.web.form.FieldCharReadonly.extend({
+ set_value: function (value) {
+ this._super(value ? '\u2714' : '\u2718');
+ }
+});
+openerp.web.form.FieldSelectionReadonly = openerp.web.form.FieldReadonly.extend({
+ template: 'FieldChar.readonly',
+ init: function(view, node) {
+ // lifted straight from r/w version
+ var self = this;
+ this._super(view, node);
+ this.values = this.field.selection;
+ _.each(this.values, function(v, i) {
+ if (v[0] === false && v[1] === '') {
+ self.values.splice(i, 1);
+ }
+ });
+ this.values.unshift([false, '']);
+ },
+ set_value: function (value) {
+ value = value === null ? false : value;
+ value = value instanceof Array ? value[0] : value;
+ var option = _(this.values)
+ .detect(function (record) { return record[0] === value; });
+ this._super(value);
+ this.$element.find('div').text(option ? option[1] : this.values[0][1]);
+ }
+});
+openerp.web.form.FieldMany2OneReadonly = openerp.web.form.FieldCharReadonly.extend({
+ set_value: function (value) {
+ value = value || null;
+ this.invalid = false;
+ var self = this;
+ this.tmp_value = value;
+ self.update_dom();
+ self.on_value_changed();
+ var real_set_value = function(rval) {
+ self.$element.find('div').text(rval ? rval[1] : '');
+ };
+ if(typeof(value) === "number") {
+ var dataset = new openerp.web.DataSetStatic(
+ this, this.field.relation, self.build_context());
+ dataset.name_get([value], function(data) {
+ real_set_value(data[0]);
+ }).fail(function() {self.tmp_value = undefined;});
+ } else {
+ setTimeout(function() {real_set_value(value);}, 0);
+ }
+ }
+});
+
/**
* Registry of form widgets, called by :js:`openerp.web.FormView`
*/
@@ -2629,6 +2806,22 @@ openerp.web.form.widgets = new openerp.web.Registry({
'binary': 'openerp.web.form.FieldBinaryFile',
'statusbar': 'openerp.web.form.FieldStatus'
});
+openerp.web.form.readonly = openerp.web.form.widgets.clone({
+ 'notebook': 'openerp.web.form.WidgetNotebookReadonly',
+ 'char': 'openerp.web.form.FieldCharReadonly',
+ 'email': 'openerp.web.form.FieldEmailReadonly',
+ 'url': 'openerp.web.form.FieldUrlReadonly',
+ 'text': 'openerp.web.form.FieldCharReadonly',
+ 'text_wiki' : 'openerp.web.form.FieldCharReadonly',
+ 'date': 'openerp.web.form.FieldCharReadonly',
+ 'datetime': 'openerp.web.form.FieldCharReadonly',
+ 'selection' : 'openerp.web.form.FieldSelectionReadonly',
+ 'many2one': 'openerp.web.form.FieldMany2OneReadonly',
+ 'boolean': 'openerp.web.form.FieldBooleanReadonly',
+ 'float': 'openerp.web.form.FieldCharReadonly',
+ 'integer': 'openerp.web.form.FieldCharReadonly',
+ 'float_time': 'openerp.web.form.FieldCharReadonly'
+});
};
diff --git a/addons/web/static/src/js/view_list.js b/addons/web/static/src/js/view_list.js
index f8a89cd34a1..8a9deaab228 100644
--- a/addons/web/static/src/js/view_list.js
+++ b/addons/web/static/src/js/view_list.js
@@ -49,6 +49,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
this.model = dataset.model;
this.view_id = view_id;
this.previous_colspan = null;
+ this.colors = null;
this.columns = [];
@@ -75,6 +76,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
}
self.compute_aggregates();
});
+
},
/**
* Retrieves the view's number of records per page (|| section)
@@ -132,6 +134,31 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
return this.reload_view(null, null, true);
},
/**
+ * Returns the color for the provided record in the current view (from the
+ * ``@colors`` attribute)
+ *
+ * @param {Record} record record for the current row
+ * @returns {String} CSS color declaration
+ */
+ color_for: function (record) {
+ if (!this.colors) { return ''; }
+ var context = _.extend({}, record.attributes, {
+ uid: this.session.uid,
+ current_date: new Date().toString('yyyy-MM-dd')
+ // TODO: time, datetime, relativedelta
+ });
+ for(var i=0, len=this.colors.length; i<len; ++i) {
+ var pair = this.colors[i],
+ color = pair[0],
+ expression = pair[1];
+ if (py.evaluate(expression, context)) {
+ return 'color: ' + color + ';';
+ }
+ // TODO: handle evaluation errors
+ }
+ return '';
+ },
+ /**
* Called after loading the list view's description, sets up such things
* as the view table's columns, renders the table itself and hooks up the
* various table-level and row-level DOM events (action buttons, deletion
@@ -159,6 +186,17 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
this.fields_view = data;
this.name = "" + this.fields_view.arch.attrs.string;
+ if (this.fields_view.arch.attrs.colors) {
+ this.colors = _(this.fields_view.arch.attrs.colors.split(';')).chain()
+ .compact()
+ .map(function(color_pair) {
+ var pair = color_pair.split(':'),
+ color = pair[0],
+ expr = pair[1];
+ return [color, py.parse(py.tokenize(expr)), expr];
+ }).value();
+ }
+
this.setup_columns(this.fields_view.fields, grouped);
this.$element.html(QWeb.render("ListView", this));
@@ -415,8 +453,8 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
*/
do_search: function (domains, contexts, groupbys) {
return this.rpc('/web/session/eval_domain_and_context', {
- domains: [this.dataset.get_domain()].concat(domains),
- contexts: [this.dataset.get_context()].concat(contexts),
+ domains: _([this.dataset.get_domain()].concat(domains)).compact(),
+ contexts: _([this.dataset.get_context()].concat(contexts)).compact(),
group_by_seq: groupbys
}, $.proxy(this, 'do_actual_search'));
},
@@ -541,6 +579,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
if (_.isEmpty(records)) {
records = this.groups.get_records();
}
+ records = _(records).compact();
var count = 0, sums = {};
_(columns).each(function (column) {
@@ -1235,6 +1274,9 @@ openerp.web.ListView.Groups = openerp.web.Class.extend( /** @lends openerp.web.L
},
get_records: function () {
if (_(this.children).isEmpty()) {
+ if (!this.datagroup.length) {
+ return;
+ }
return {
count: this.datagroup.length,
values: this.datagroup.aggregates
@@ -1351,10 +1393,10 @@ var Record = openerp.web.Class.extend(/** @lends Record# */{
* @returns {Object} record displayable in a form view
*/
toForm: function () {
- var form_data = {};
- _(this.attributes).each(function (value, key) {
- form_data[key] = {value: value};
- });
+ var form_data = {}, attrs = this.attributes;
+ for(var k in attrs) {
+ form_data[k] = {value: attrs[k]};
+ }
return {data: form_data};
}
diff --git a/addons/web/static/src/js/view_list_editable.js b/addons/web/static/src/js/view_list_editable.js
index b5a5a3d8c10..beb2c3ac0a9 100644
--- a/addons/web/static/src/js/view_list_editable.js
+++ b/addons/web/static/src/js/view_list_editable.js
@@ -182,9 +182,11 @@ openerp.web.list_editable = function (openerp) {
self.edition_id = record_id;
self.edition_form = _.extend(new openerp.web.ListEditableFormView(self, self.dataset, false), {
form_template: 'ListView.row.form',
- registry: openerp.web.list.form.widgets
+ registry: openerp.web.list.form.widgets,
+ $element: $new_row
});
- self.edition_form.appendTo($("#" + $new_row.attr('id')));
+ // HA HA
+ self.edition_form.appendTo();
$.when(self.edition_form.on_loaded(self.get_form_fields_view())).then(function () {
// put in $.when just in case FormView.on_loaded becomes asynchronous
$new_row.find('td')
@@ -335,6 +337,9 @@ openerp.web.list_editable = function (openerp) {
});
openerp.web.ListEditableFormView = openerp.web.FormView.extend({
- init_view: function() {}
+ init_view: function() {},
+ _render_and_insert: function () {
+ return this.start();
+ }
});
};
diff --git a/addons/web/static/src/js/views.js b/addons/web/static/src/js/views.js
index fa27e59c0af..be47cfc3364 100644
--- a/addons/web/static/src/js/views.js
+++ b/addons/web/static/src/js/views.js
@@ -86,7 +86,7 @@ db.web.ActionManager = db.web.Widget.extend({
console.log("Action manager can't handle action of type " + action.type, action);
return;
}
- this[type](action, on_close);
+ return this[type](action, on_close);
},
ir_actions_act_window: function (action, on_close) {
if (action.target === 'new') {
@@ -163,7 +163,7 @@ db.web.ViewManager = db.web.Widget.extend(/** @lends db.web.ViewManager# */{
*/
init: function(parent, dataset, views) {
this._super(parent);
- this.model = dataset.model;
+ this.model = dataset ? dataset.model : undefined;
this.dataset = dataset;
this.searchview = null;
this.active_view = null;
@@ -184,7 +184,6 @@ db.web.ViewManager = db.web.Widget.extend(/** @lends db.web.ViewManager# */{
start: function() {
this._super();
var self = this;
- this.dataset.start();
this.$element.find('.oe_vm_switch button').click(function() {
self.on_mode_switch($(this).data('view-type'));
});
@@ -332,6 +331,7 @@ db.web.ViewManagerAction = db.web.ViewManager.extend(/** @lends oepnerp.web.View
// dataset initialization will take the session from ``this``, so if we
// do not have it yet (and we don't, because we've not called our own
// ``_super()``) rpc requests will blow up.
+ this._super(parent, null, action.views);
this.session = parent.session;
this.action = action;
var dataset = new db.web.DataSetSearch(this, action.res_model, action.context, action.domain);
@@ -339,7 +339,7 @@ db.web.ViewManagerAction = db.web.ViewManager.extend(/** @lends oepnerp.web.View
dataset.ids.push(action.res_id);
dataset.index = 0;
}
- this._super(parent, dataset, action.views);
+ this.dataset = dataset;
this.flags = this.action.flags || {};
if (action.res_model == 'board.board' && action.views.length == 1 && action.views) {
// Not elegant but allows to avoid form chrome (pager, save/new
@@ -470,7 +470,7 @@ db.web.ViewManagerAction = db.web.ViewManager.extend(/** @lends oepnerp.web.View
* Intercept do_action resolution from children views
*/
on_action_executed: function () {
- new db.web.DataSet(this, 'res.log')
+ return new db.web.DataSet(this, 'res.log')
.call('get', [], this.do_display_log);
},
/**
@@ -770,28 +770,31 @@ db.web.View = db.web.Widget.extend(/** @lends db.web.View# */{
var self = this;
var result_handler = function () {
if (on_closed) { on_closed.apply(null, arguments); }
- self.widget_parent.on_action_executed.apply(null, arguments);
+ return self.widget_parent.on_action_executed.apply(null, arguments);
};
var handler = function (r) {
var action = r.result;
if (action && action.constructor == Object) {
- action.context = action.context || {};
- _.extend(action.context, {
- active_id: record_id || false,
- active_ids: [record_id || false],
- active_model: dataset.model
+ return self.rpc('/web/session/eval_domain_and_context', {
+ contexts: [dataset.get_context(), action.context || {}, {
+ active_id: record_id || false,
+ active_ids: [record_id || false],
+ active_model: dataset.model
+ }],
+ domains: []
+ }).pipe(function (results) {
+ action.context = results.context
+ return self.do_action(action, result_handler);
});
- action.context = new db.web.CompoundContext(dataset.get_context(), action.context);
- self.do_action(action, result_handler);
} else {
- result_handler();
+ return result_handler();
}
};
var context = new db.web.CompoundContext(dataset.get_context(), action_data.context || {});
if (action_data.special) {
- handler({result: {"type":"ir.actions.act_window_close"}});
+ return handler({result: {"type":"ir.actions.act_window_close"}});
} else if (action_data.type=="object") {
return dataset.call_button(action_data.name, [[record_id], context], handler);
} else if (action_data.type=="action") {
@@ -863,6 +866,8 @@ db.web.View = db.web.Widget.extend(/** @lends db.web.View# */{
console.log('Todo');
},
on_sidebar_import: function() {
+ var import_view = new db.web.DataImport(this, this.dataset);
+ import_view.start();
},
on_sidebar_export: function() {
var export_view = new db.web.DataExport(this, this.dataset);