summaryrefslogtreecommitdiff
path: root/addons/web/static/src/js
diff options
context:
space:
mode:
authorXavier Morel <xmo@openerp.com>2012-10-05 14:49:19 +0200
committerXavier Morel <xmo@openerp.com>2012-10-05 14:49:19 +0200
commitb965ae5cf79f87a838d8a941f154390737895c4a (patch)
tree168596c17d07c92af9bc29d3acc78f1ea5ecbf81 /addons/web/static/src/js
parent6a23c24da13f5907d4c407e919bff187a3a0ae1d (diff)
parentc8c4086fd655b09f5fc0a4bde4d43d95745520f1 (diff)
[MERGE] from trunk
bzr revid: xmo@openerp.com-20121005124919-yfkbzk3mjm0tot4e
Diffstat (limited to 'addons/web/static/src/js')
-rw-r--r--addons/web/static/src/js/boot.js5
-rw-r--r--addons/web/static/src/js/chrome.js525
-rw-r--r--addons/web/static/src/js/corelib.js78
-rw-r--r--addons/web/static/src/js/coresetup.js262
-rw-r--r--addons/web/static/src/js/data.js395
-rw-r--r--addons/web/static/src/js/data_export.js132
-rw-r--r--addons/web/static/src/js/data_import.js393
-rw-r--r--addons/web/static/src/js/formats.js81
-rw-r--r--addons/web/static/src/js/search.js273
-rw-r--r--addons/web/static/src/js/test_support.js13
-rw-r--r--addons/web/static/src/js/view_form.js1390
-rw-r--r--addons/web/static/src/js/view_list.js462
-rw-r--r--addons/web/static/src/js/view_list_editable.js89
-rw-r--r--addons/web/static/src/js/view_tree.js26
-rw-r--r--addons/web/static/src/js/views.js525
15 files changed, 2236 insertions, 2413 deletions
diff --git a/addons/web/static/src/js/boot.js b/addons/web/static/src/js/boot.js
index dd014846884..8d75175e1e3 100644
--- a/addons/web/static/src/js/boot.js
+++ b/addons/web/static/src/js/boot.js
@@ -37,7 +37,10 @@
};
openerp.instances[new_instance._session_id] = new_instance;
for(var i=0; i < modules.length; i++) {
- openerp[modules[i]](new_instance,new_instance[modules[i]]);
+ new_instance[modules[i]] = {};
+ if (openerp[modules[i]]) {
+ openerp[modules[i]](new_instance,new_instance[modules[i]]);
+ }
}
return new_instance;
}
diff --git a/addons/web/static/src/js/chrome.js b/addons/web/static/src/js/chrome.js
index 86fb457582d..01bfb63a580 100644
--- a/addons/web/static/src/js/chrome.js
+++ b/addons/web/static/src/js/chrome.js
@@ -13,7 +13,7 @@ instance.web.Notification = instance.web.Widget.extend({
},
start: function() {
this._super.apply(this, arguments);
- this.$element.notify({
+ this.$el.notify({
speed: 500,
expires: 2500
});
@@ -24,7 +24,7 @@ instance.web.Notification = instance.web.Widget.extend({
if (sticky) {
opts.expires = false;
}
- this.$element.notify('create', {
+ this.$el.notify('create', {
title: title,
text: text
}, opts);
@@ -35,7 +35,7 @@ instance.web.Notification = instance.web.Widget.extend({
if (sticky) {
opts.expires = false;
}
- this.$element.notify('create', 'oe_notification_alert', {
+ this.$el.notify('create', 'oe_notification_alert', {
title: title,
text: text
}, opts);
@@ -66,9 +66,9 @@ instance.web.Dialog = instance.web.Widget.extend({
max_width: '95%',
height: 'auto',
min_height: 0,
- max_height: this.get_height('100%') - 140,
+ max_height: this.get_height('100%') - 200,
autoOpen: false,
- position: [false, 50],
+ position: [false, 40],
buttons: {},
beforeClose: function () { self.on_close(); },
resizeStop: this.on_resized
@@ -79,6 +79,9 @@ instance.web.Dialog = instance.web.Widget.extend({
}
}
if (options) {
+ if (options.buttons) {
+ this.params_buttons = true;
+ }
_.extend(this.dialog_options, options);
}
},
@@ -109,7 +112,7 @@ instance.web.Dialog = instance.web.Widget.extend({
} else if (val.slice(-1) == "%") {
return Math.round(available_size / 100 * parseInt(val.slice(0, -1), 10));
} else {
- return parseInt(val, 10);
+ return parseInt(val, 10);
}
},
renderElement: function() {
@@ -123,22 +126,32 @@ instance.web.Dialog = instance.web.Widget.extend({
if (! this.dialog_inited)
this.init_dialog();
var o = this.get_options(options);
- instance.web.dialog(this.$element, o).dialog('open');
+ if (! this.params_buttons) {
+ this.$buttons.appendTo($("body"));
+ }
+ instance.web.dialog(this.$el, o).dialog('open');
+ if (! this.params_buttons) {
+ this.$buttons.appendTo(this.$el.dialog("widget"));
+ }
if (o.height === 'auto' && o.max_height) {
- this.$element.css({ 'max-height': o.max_height, 'overflow-y': 'auto' });
+ this.$el.css({ 'max-height': o.max_height, 'overflow-y': 'auto' });
}
return this;
},
init_dialog: function(options) {
this.renderElement();
var o = this.get_options(options);
- instance.web.dialog(this.$element, o);
- var res = this.start();
+ instance.web.dialog(this.$el, o);
+ if (! this.params_buttons) {
+ this.$buttons = $('<div class="ui-dialog-buttonpane ui-widget-content ui-helper-clearfix" />');
+ this.$el.dialog("widget").append(this.$buttons);
+ }
this.dialog_inited = true;
+ var res = this.start();
return res;
},
close: function() {
- this.$element.dialog('close');
+ this.$el.dialog('close');
},
on_close: function() {
if (this.__tmp_dialog_destroying)
@@ -161,7 +174,7 @@ instance.web.Dialog = instance.web.Widget.extend({
this.__tmp_dialog_destroying = undefined;
}
if (! this.isDestroyed()) {
- this.$element.dialog('destroy');
+ this.$el.dialog('destroy');
}
this._super();
}
@@ -193,29 +206,9 @@ instance.web.CrashManager = instance.web.CallbackEnabled.extend({
on_traceback: function(error) {
var self = this;
var buttons = {};
- if (instance.connection.openerp_entreprise) {
- buttons[_t("Send OpenERP Enterprise Report")] = function() {
- var $this = $(this);
- var issuename = $('#issuename').val();
- var explanation = $('#explanation').val();
- var remark = $('#remark').val();
- // Call the send method from server to send mail with details
- new instance.web.DataSet(self, 'publisher_warranty.contract').call_and_eval('send', [error.data,explanation,remark,issuename]).then(function(result){
- if (result === false) {
- alert('There was a communication error.');
- } else {
- $this.dialog('close');
- }
- });
- };
- buttons[_t("Dont send")] = function() {
- $(this).dialog("close");
- };
- } else {
- buttons[_t("Ok")] = function() {
- $(this).dialog("close");
- };
- }
+ buttons[_t("Ok")] = function() {
+ $(this).dialog("close");
+ };
var dialog = new instance.web.Dialog(this, {
title: "OpenERP " + _.str.capitalize(error.type),
width: '80%',
@@ -224,7 +217,7 @@ instance.web.CrashManager = instance.web.CallbackEnabled.extend({
min_height: '600px',
buttons: buttons
}).open();
- dialog.$element.html(QWeb.render('CrashManager.error', {session: instance.connection, error: error}));
+ dialog.$el.html(QWeb.render('CrashManager.error', {session: instance.session, error: error}));
},
on_javascript_exception: function(exception) {
this.on_traceback({
@@ -241,22 +234,20 @@ instance.web.Loading = instance.web.Widget.extend({
this._super(parent);
this.count = 0;
this.blocked_ui = false;
- var self = this;
- this.request_call = function() {
- self.on_rpc_event(1);
- };
- this.response_call = function() {
- self.on_rpc_event(-1);
- };
- this.session.on_rpc_request.add_first(this.request_call);
- this.session.on_rpc_response.add_last(this.response_call);
+ this.session.on("request", this, this.request_call);
+ this.session.on("response", this, this.response_call);
+ this.session.on("error", this, this.response_call);
},
destroy: function() {
- this.session.on_rpc_request.remove(this.request_call);
- this.session.on_rpc_response.remove(this.response_call);
this.on_rpc_event(-this.count);
this._super();
},
+ request_call: function() {
+ this.on_rpc_event(1);
+ },
+ response_call: function() {
+ this.on_rpc_event(-1);
+ },
on_rpc_event : function(increment) {
var self = this;
if (!this.count && increment === 1) {
@@ -269,13 +260,13 @@ instance.web.Loading = instance.web.Widget.extend({
this.count += increment;
if (this.count > 0) {
- if (instance.connection.debug) {
- this.$element.text(_.str.sprintf( _t("Loading (%d)"), this.count));
+ if (instance.session.debug) {
+ this.$el.text(_.str.sprintf( _t("Loading (%d)"), this.count));
} else {
- this.$element.text(_t("Loading"));
+ this.$el.text(_t("Loading"));
}
- this.$element.show();
- this.getParent().$element.addClass('oe_wait');
+ this.$el.show();
+ this.getParent().$el.addClass('oe_wait');
} else {
this.count = 0;
clearTimeout(this.long_running_timer);
@@ -284,8 +275,8 @@ instance.web.Loading = instance.web.Widget.extend({
this.blocked_ui = false;
instance.web.unblockUI();
}
- this.$element.fadeOut();
- this.getParent().$element.removeClass('oe_wait');
+ this.$el.fadeOut();
+ this.getParent().$el.removeClass('oe_wait');
}
}
});
@@ -300,6 +291,7 @@ instance.web.DatabaseManager = instance.web.Widget.extend({
},
start: function() {
var self = this;
+ $('.oe_secondary_menus_container,.oe_user_menu_placeholder').empty();
var fetch_db = this.rpc("/web/database/get_list", {}).pipe(
function(result) {
self.db_list = result.db_list;
@@ -315,17 +307,27 @@ instance.web.DatabaseManager = instance.web.Widget.extend({
},
do_render: function() {
var self = this;
- self.$element.html(QWeb.render("DatabaseManager", { widget : self }));
- self.$element.find(".oe_database_manager_menu").tabs({
- show: function(event, ui) {
- $('*[autofocus]:first', ui.panel).focus();
- }
+ instance.webclient.toggle_bars(true);
+ self.$el.html(QWeb.render("DatabaseManager", { widget : self }));
+ $('.oe_user_menu_placeholder').append(QWeb.render("DatabaseManager.user_menu",{ widget : self }));
+ $('.oe_secondary_menus_container').append(QWeb.render("DatabaseManager.menu",{ widget : self }));
+ $('ul.oe_secondary_submenu > li:first').addClass('oe_active')
+ $('ul.oe_secondary_submenu > li').bind('click', function (event) {
+ var menuitem = $(this);
+ menuitem.addClass('oe_active').siblings().removeClass('oe_active');
+ var form_id =menuitem.find('a').attr('href');
+ $(form_id).show().siblings().hide();
+ event.preventDefault();
});
- self.$element.find("form[name=create_db_form]").validate({ submitHandler: self.do_create });
- self.$element.find("form[name=drop_db_form]").validate({ submitHandler: self.do_drop });
- self.$element.find("form[name=backup_db_form]").validate({ submitHandler: self.do_backup });
- self.$element.find("form[name=restore_db_form]").validate({ submitHandler: self.do_restore });
- self.$element.find("form[name=change_pwd_form]").validate({
+ $('#back-to-login').click(self.do_exit);
+ self.$el.find("td").addClass("oe_form_group_cell");
+ self.$el.find("tr td:first-child").addClass("oe_form_group_cell_label");
+ self.$el.find("label").addClass("oe_form_label");
+ self.$el.find("form[name=create_db_form]").validate({ submitHandler: self.do_create });
+ self.$el.find("form[name=drop_db_form]").validate({ submitHandler: self.do_drop });
+ self.$el.find("form[name=backup_db_form]").validate({ submitHandler: self.do_backup });
+ self.$el.find("form[name=restore_db_form]").validate({ submitHandler: self.do_restore });
+ self.$el.find("form[name=change_pwd_form]").validate({
messages: {
old_pwd: "Please enter your previous password",
new_pwd: "Please enter your new password",
@@ -336,10 +338,9 @@ instance.web.DatabaseManager = instance.web.Widget.extend({
},
submitHandler: self.do_change_password
});
- self.$element.find("#back_to_login").click(self.do_exit);
},
destroy: function () {
- this.$element.find('#db-create, #db-drop, #db-backup, #db-restore, #db-change-password, #back-to-login').unbind('click').end().empty();
+ this.$el.find('#db-create, #db-drop, #db-backup, #db-restore, #db-change-password, #back-to-login').unbind('click').end().empty();
this._super();
},
/**
@@ -400,11 +401,13 @@ instance.web.DatabaseManager = instance.web.Widget.extend({
'db': form_obj['db_name'],
'login': 'admin',
'password': form_obj['create_admin_pwd'],
+ 'login_successful': function() {
+ self.do_action("reload");
+ },
},
};
self.do_action(client_action);
});
-
},
do_drop: function(form) {
var self = this;
@@ -483,11 +486,14 @@ instance.web.DatabaseManager = instance.web.Widget.extend({
self.display_error(result);
return;
}
+ self.unblockUI();
self.do_notify("Changed Password", "Password has been changed successfully");
});
},
do_exit: function () {
- this.do_action("login");
+ this.$el.remove();
+ instance.webclient.toggle_bars(false);
+ this.do_action('login');
}
});
instance.web.client_actions.add("database_manager", "instance.web.DatabaseManager");
@@ -495,11 +501,11 @@ instance.web.client_actions.add("database_manager", "instance.web.DatabaseManage
instance.web.Login = instance.web.Widget.extend({
template: "Login",
remember_credentials: true,
- _db_list: null,
init: function(parent, params) {
this._super(parent);
this.has_local_storage = typeof(localStorage) != 'undefined';
+ this.db_list = null;
this.selected_db = null;
this.selected_login = null;
this.params = params || {};
@@ -518,54 +524,47 @@ instance.web.Login = instance.web.Widget.extend({
},
start: function() {
var self = this;
- self.$element.find("form").submit(self.on_submit);
- self.$element.find('.oe_login_manage_db').click(function() {
+ self.$el.find("form").submit(self.on_submit);
+ self.$el.find('.oe_login_manage_db').click(function() {
self.do_action("database_manager");
});
- return self.load_db_list().then(self.on_db_list_loaded).then(function() {
- if (self.params.db) {
- self.do_login(self.params.db, self.params.login, self.params.password);
+ var d;
+ if (self.params.db) {
+ if (self.params.login && self.params.password) {
+ d = self.do_login(self.params.db, self.params.login, self.params.password);
}
- });
- },
- load_db_list: function (force) {
- var d = $.when(), self = this;
- if (_.isNull(this._db_list) || force) {
- d = self.rpc("/web/database/get_list", {}, function(result) {
- self._db_list = _.clone(result.db_list);
- }, function(error, event) {
- if (error.data.fault_code === 'AccessDenied') {
- event.preventDefault();
- }
- });
+ } else {
+ d = self.rpc("/web/database/get_list", {}).done(self.on_db_loaded).fail(self.on_db_failed);
}
return d;
},
- on_db_list_loaded: function () {
- var self = this;
- var list = this._db_list;
- var dbdiv = this.$element.find('div.oe_login_dbpane');
- this.$element.find("[name=db]").replaceWith(instance.web.qweb.render('Login.dblist', { db_list: list, selected_db: this.selected_db}));
- if(list.length === 0) {
+ on_db_loaded: function (result) {
+ this.db_list = result.db_list;
+ this.$("[name=db]").replaceWith(QWeb.render('Login.dblist', { db_list: this.db_list, selected_db: this.selected_db}));
+ if(this.db_list.length === 0) {
this.do_action("database_manager");
- } else if(list && list.length === 1) {
- dbdiv.hide();
+ } else if(this.db_list.length === 1) {
+ this.$('div.oe_login_dbpane').hide();
} else {
- dbdiv.show();
+ this.$('div.oe_login_dbpane').show();
+ }
+ },
+ on_db_failed: function (error, event) {
+ if (error.data.fault_code === 'AccessDenied') {
+ event.preventDefault();
}
},
on_submit: function(ev) {
if(ev) {
ev.preventDefault();
}
- var $e = this.$element;
- var db = $e.find("form [name=db]").val();
+ var db = this.$("form [name=db]").val();
if (!db) {
this.do_warn("Login", "No database selected !");
return false;
}
- var login = $e.find("form input[name=login]").val();
- var password = $e.find("form input[name=password]").val();
+ var login = this.$("form input[name=login]").val();
+ var password = this.$("form input[name=password]").val();
this.do_login(db, login, password);
},
@@ -578,7 +577,7 @@ instance.web.Login = instance.web.Widget.extend({
*/
do_login: function (db, login, password) {
var self = this;
- this.$element.removeClass('oe_login_invalid');
+ self.hide_error();
self.$(".oe_login_pane").fadeOut("slow");
return this.session.session_authenticate(db, login, password).pipe(function() {
if (self.has_local_storage) {
@@ -595,20 +594,95 @@ instance.web.Login = instance.web.Widget.extend({
}
}
self.trigger('login_successful');
- },function () {
- self.$(".oe_login_pane").fadeIn("fast");
- self.$element.addClass("oe_login_invalid");
+ }, function () {
+ self.$(".oe_login_pane").fadeIn("fast", function() {
+ self.show_error("Invalid username or password");
+ });
});
},
- show: function () {
- this.$element.show();
+ show_error: function(message) {
+ this.$el.addClass("oe_login_invalid");
+ this.$(".oe_login_error_message").text(message);
+ },
+ hide_error: function() {
+ this.$el.removeClass('oe_login_invalid');
},
- hide: function () {
- this.$element.hide();
- }
});
instance.web.client_actions.add("login", "instance.web.Login");
+/**
+ * Client action to reload the whole interface.
+ * If params has an entry 'menu_id', it opens the given menu entry.
+ */
+instance.web.Reload = function(parent, params) {
+ var menu_id = (params && params.menu_id) || false;
+ var l = window.location;
+
+ var sobj = $.deparam(l.search.substr(1));
+ sobj.ts = new Date().getTime();
+ var search = '?' + $.param(sobj);
+
+ var hash = l.hash;
+ if (menu_id) {
+ hash = "#menu_id=" + menu_id;
+ }
+ var url = l.protocol + "//" + l.host + l.pathname + search + hash;
+ window.location = url;
+};
+instance.web.client_actions.add("reload", "instance.web.Reload");
+
+/**
+ * Client action to go back in breadcrumb history.
+ * If can't go back in history stack, will go back to home.
+ */
+instance.web.HistoryBack = function(parent, params) {
+ if (!parent.history_back()) {
+ window.location = '/' + (window.location.search || '');
+ }
+};
+instance.web.client_actions.add("history_back", "instance.web.HistoryBack");
+
+/**
+ * Client action to go back home.
+ */
+instance.web.Home = instance.web.Widget.extend({
+ init: function(parent, params) {
+ window.location = '/' + (window.location.search || '');
+ }
+});
+instance.web.client_actions.add("home", "instance.web.Home");
+
+instance.web.ChangePassword = instance.web.Widget.extend({
+ template: "ChangePassword",
+ start: function() {
+ var self = this;
+ self.$el.validate({
+ submitHandler: function (form) {
+ self.rpc("/web/session/change_password",{
+ 'fields': $(form).serializeArray()
+ }, function(result) {
+ if (result.error) {
+ self.display_error(result);
+ return;
+ } else {
+ instance.webclient.on_logout();
+ }
+ });
+ }
+ });
+ },
+ display_error: function (error) {
+ return instance.web.dialog($('<div>'), {
+ modal: true,
+ title: error.title,
+ buttons: [
+ {text: _t("Ok"), click: function() { $(this).dialog("close"); }}
+ ]
+ }).html(error.error);
+ },
+})
+instance.web.client_actions.add("change_password", "instance.web.ChangePassword");
+
instance.web.Menu = instance.web.Widget.extend({
template: 'Menu',
init: function() {
@@ -619,7 +693,7 @@ instance.web.Menu = instance.web.Widget.extend({
},
start: function() {
this._super.apply(this, arguments);
- this.$secondary_menus = this.getParent().$element.find('.oe_secondary_menus_container');
+ this.$secondary_menus = this.getParent().$el.find('.oe_secondary_menus_container');
this.$secondary_menus.on('click', 'a[data-menu]', this.on_menu_click);
return this.do_reload();
},
@@ -631,8 +705,13 @@ instance.web.Menu = instance.web.Widget.extend({
this.data = data;
this.renderElement();
this.limit_entries();
+ // Hide toplevel item if there is only one
+ var $toplevel = this.$("li")
+ if($toplevel.length == 1) {
+ $toplevel.hide();
+ }
this.$secondary_menus.html(QWeb.render("Menu.secondary", { widget : this }));
- this.$element.on('click', 'a[data-menu]', this.on_menu_click);
+ this.$el.on('click', 'a[data-menu]', this.on_menu_click);
// Hide second level submenus
this.$secondary_menus.find('.oe_menu_toggler').siblings('.oe_secondary_submenu').hide();
if (self.current_menu) {
@@ -647,8 +726,9 @@ instance.web.Menu = instance.web.Widget.extend({
}
if (maximum_visible_links < this.data.data.children.length) {
var $more = $(QWeb.render('Menu.more')),
- $index = this.$element.find('li').eq(maximum_visible_links - 1);
+ $index = this.$el.find('li').eq(maximum_visible_links - 1);
$index.after($more);
+ //$('.oe_topbar').append($more);
$more.find('.oe_menu_more').append($index.next().nextAll());
}
},
@@ -664,20 +744,22 @@ instance.web.Menu = instance.web.Widget.extend({
* @param {Number} id database id of the terminal menu to select
*/
open_menu: function (id) {
+ this.current_menu = id;
+ this.session.active_id = id;
var $clicked_menu, $sub_menu, $main_menu;
- $clicked_menu = this.$element.add(this.$secondary_menus).find('a[data-menu=' + id + ']');
+ $clicked_menu = this.$el.add(this.$secondary_menus).find('a[data-menu=' + id + ']');
this.trigger('open_menu', id, $clicked_menu);
if (this.$secondary_menus.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') + ']');
+ $main_menu = this.$el.find('a[data-menu=' + $sub_menu.data('menu-parent') + ']');
} else {
$sub_menu = this.$secondary_menus.find('.oe_secondary_menu[data-menu-parent=' + $clicked_menu.attr('data-menu') + ']');
$main_menu = $clicked_menu;
}
// Activate current main menu
- this.$element.find('.oe_active').removeClass('oe_active');
+ this.$el.find('.oe_active').removeClass('oe_active');
$main_menu.addClass('oe_active');
// Show current sub menu
@@ -704,7 +786,7 @@ instance.web.Menu = instance.web.Widget.extend({
* @param {Number} id the action_id to match
*/
open_action: function (id) {
- var $menu = this.$element.add(this.$secondary_menus).find('a[data-action-id="' + id + '"]');
+ var $menu = this.$el.add(this.$secondary_menus).find('a[data-action-id="' + id + '"]');
var menu_id = $menu.data('menu');
if (menu_id) {
this.open_menu(menu_id);
@@ -720,14 +802,14 @@ instance.web.Menu = instance.web.Widget.extend({
if (!id) { return; }
// find back the menuitem in dom to get the action
- var $item = this.$element.find('a[data-menu=' + id + ']');
+ var $item = this.$el.find('a[data-menu=' + id + ']');
if (!$item.length) {
$item = this.$secondary_menus.find('a[data-menu=' + id + ']');
}
var action_id = $item.data('action-id');
// If first level menu doesnt have action trigger first leaf
if (!action_id) {
- if(this.$element.has($item).length) {
+ if(this.$el.has($item).length) {
var $sub_menu = this.$secondary_menus.find('.oe_secondary_menu[data-menu-parent=' + id + ']');
var $items = $sub_menu.find('a[data-action-id]').filter('[data-action-id!=""]');
if($items.length) {
@@ -736,16 +818,15 @@ instance.web.Menu = instance.web.Widget.extend({
}
}
}
- this.open_menu(id);
- this.current_menu = id;
- this.session.active_id = id;
if (action_id) {
this.trigger('menu_click', {
action_id: action_id,
needaction: needaction,
- id: id
+ id: id,
+ previous_menu_id: this.current_menu // Here we don't know if action will fail (in which case we have to revert menu)
}, $item);
}
+ this.open_menu(id);
},
/**
* Jquery event handler for menu click
@@ -768,7 +849,7 @@ instance.web.UserMenu = instance.web.Widget.extend({
start: function() {
var self = this;
this._super.apply(this, arguments);
- this.$element.on('click', '.oe_dropdown_menu li a[data-menu]', function(ev) {
+ this.$el.on('click', '.oe_dropdown_menu li a[data-menu]', function(ev) {
ev.preventDefault();
var f = self['on_menu_' + $(this).data('menu')];
if (f) {
@@ -776,53 +857,22 @@ instance.web.UserMenu = instance.web.Widget.extend({
}
});
},
- change_password :function() {
- var self = this;
- this.dialog = new instance.web.Dialog(this, {
- title: _t("Change Password"),
- width : 'auto'
- }).open();
- this.dialog.$element.html(QWeb.render("UserMenu.password", self));
- this.dialog.$element.find("form[name=change_password_form]").validate({
- submitHandler: function (form) {
- self.rpc("/web/session/change_password",{
- 'fields': $(form).serializeArray()
- }, function(result) {
- if (result.error) {
- self.display_error(result);
- return;
- } else {
- instance.webclient.on_logout();
- }
- });
- }
- });
- },
- display_error: function (error) {
- return instance.web.dialog($('<div>'), {
- modal: true,
- title: error.title,
- buttons: [
- {text: _("Ok"), click: function() { $(this).dialog("close"); }}
- ]
- }).html(error.error);
- },
do_update: function () {
var self = this;
var fct = function() {
- var $avatar = self.$element.find('.oe_topbar_avatar');
+ var $avatar = self.$el.find('.oe_topbar_avatar');
$avatar.attr('src', $avatar.data('default-src'));
if (!self.session.uid)
return;
var func = new instance.web.Model("res.users").get_func("read");
return func(self.session.uid, ["name", "company_id"]).pipe(function(res) {
var topbar_name = res.name;
- if(instance.connection.debug)
- topbar_name = _.str.sprintf("%s (%s)", topbar_name, instance.connection.db);
+ if(instance.session.debug)
+ topbar_name = _.str.sprintf("%s (%s)", topbar_name, instance.session.db);
if(res.company_id[0] > 1)
topbar_name = _.str.sprintf("%s (%s)", topbar_name, res.company_id[1]);
- self.$element.find('.oe_topbar_name').text(topbar_name);
- var avatar_src = _.str.sprintf('%s/web/binary/image?session_id=%s&model=res.users&field=avatar&id=%s', self.session.prefix, self.session.session_id, self.session.uid);
+ self.$el.find('.oe_topbar_name').text(topbar_name);
+ var avatar_src = _.str.sprintf('%s/web/binary/image?session_id=%s&model=res.users&field=image_small&id=%s', self.session.prefix, self.session.session_id, self.session.uid);
$avatar.attr('src', avatar_src);
});
};
@@ -834,44 +884,12 @@ instance.web.UserMenu = instance.web.Widget.extend({
},
on_menu_settings: function() {
var self = this;
- var action_manager = new instance.web.ActionManager(this);
- var dataset = new instance.web.DataSet (this,'res.users',this.context);
- dataset.call ('action_get','',function (result){
- self.rpc('/web/action/load', {action_id:result}, function(result){
- action_manager.do_action(_.extend(result['result'], {
- target: 'inline',
- res_id: self.session.uid,
- res_model: 'res.users',
- flags: {
- action_buttons: false,
- search_view: false,
- sidebar: false,
- views_switcher: false,
- pager: false
- }
- }));
+ if (!this.getParent().has_uncommitted_changes()) {
+ self.rpc("/web/action/load", { action_id: "base.action_res_users_my" }, function(result) {
+ result.result.res_id = instance.session.uid;
+ self.getParent().action_manager.do_action(result.result);
});
- });
- this.dialog = new instance.web.Dialog(this,{
- title: _t("Preferences"),
- width: '700px',
- buttons: [
- {text: _t("Change password"), click: function(){ self.change_password(); }},
- {text: _t("Cancel"), click: function(){ $(this).dialog('destroy'); }},
- {text: _t("Save"), click: function(){
- var inner_widget = action_manager.inner_widget;
- inner_widget.views[inner_widget.active_view].controller.do_save()
- .then(function() {
- self.dialog.destroy();
- // needs to refresh interface in case language changed
- window.location.reload();
- });
- }
- }
- ]
- }).open();
- action_manager.appendTo(this.dialog.$element);
- action_manager.renderElement(this.dialog);
+ }
},
on_menu_about: function() {
var self = this;
@@ -883,7 +901,7 @@ instance.web.UserMenu = instance.web.Widget.extend({
window.location.href, 'debug');
});
instance.web.dialog($help, {autoOpen: true,
- modal: true, width: 580, height: 290, resizable: false, title: _t("About")});
+ modal: true, width: 507, height: 290, resizable: false, title: _t("About")});
});
},
});
@@ -896,7 +914,7 @@ instance.web.Client = instance.web.Widget.extend({
},
start: function() {
var self = this;
- return instance.connection.session_bind(this.origin).pipe(function() {
+ return instance.session.session_bind(this.origin).pipe(function() {
var $e = $(QWeb.render(self._template, {}));
self.replaceElement($e);
self.bind_events();
@@ -905,10 +923,10 @@ instance.web.Client = instance.web.Widget.extend({
},
bind_events: function() {
var self = this;
- this.$element.on('mouseenter', '.oe_systray > div:not([data-tipsy=true])', function() {
+ this.$el.on('mouseenter', '.oe_systray > div:not([data-tipsy=true])', function() {
$(this).attr('data-tipsy', 'true').tipsy().trigger('mouseenter');
});
- this.$element.on('click', '.oe_dropdown_toggle', function(ev) {
+ this.$el.on('click', '.oe_dropdown_toggle', function(ev) {
ev.preventDefault();
var $toggle = $(this);
var $menu = $toggle.siblings('.oe_dropdown_menu');
@@ -930,22 +948,29 @@ instance.web.Client = instance.web.Widget.extend({
}, 0);
});
instance.web.bus.on('click', this, function(ev) {
+ $.fn.tipsy.clear();
if (!$(ev.target).is('input[type=file]')) {
- self.$element.find('.oe_dropdown_menu.oe_opened').removeClass('oe_opened');
+ self.$el.find('.oe_dropdown_menu.oe_opened, .oe_dropdown_toggle.oe_opened').removeClass('oe_opened');
}
});
},
show_common: function() {
var self = this;
this.crashmanager = new instance.web.CrashManager();
- instance.connection.on_rpc_error.add(this.crashmanager.on_rpc_error);
+ instance.session.on_rpc_error.add(this.crashmanager.on_rpc_error);
self.notification = new instance.web.Notification(this);
- self.notification.appendTo(self.$element);
+ self.notification.appendTo(self.$el);
self.loading = new instance.web.Loading(self);
- self.loading.appendTo(self.$element);
+ self.loading.appendTo(self.$el);
self.action_manager = new instance.web.ActionManager(self);
self.action_manager.appendTo(self.$('.oe_application'));
},
+ toggle_bars: function(value) {
+ this.$('tr:has(td.oe_topbar),.oe_leftbar').toggle(value);
+ },
+ has_uncommitted_changes: function() {
+ return false;
+ },
});
instance.web.WebClient = instance.web.Client.extend({
@@ -957,6 +982,9 @@ instance.web.WebClient = instance.web.Client.extend({
start: function() {
var self = this;
return $.when(this._super()).pipe(function() {
+ self.$el.on('click', '.oe_logo', function() {
+ self.action_manager.do_action('home');
+ });
if (jQuery.param !== undefined && jQuery.deparam(jQuery.param.querystring()).kitten !== undefined) {
$("body").addClass("kitten-mode-activated");
if ($.blockUI) {
@@ -987,38 +1015,48 @@ instance.web.WebClient = instance.web.Client.extend({
};
},
show_login: function() {
- this.$('.oe_topbar').hide();
- this.action_manager.do_action("login");
- this.action_manager.inner_widget.on('login_successful', this, this.show_application);
+ this.toggle_bars(false);
+
+ var action = {
+ 'type': 'ir.actions.client',
+ 'tag': 'login'
+ };
+ var state = $.bbq.getState(true);
+ if (state.action === "login") {
+ action.params = state;
+ }
+
+ this.action_manager.do_action(action);
+ this.action_manager.inner_widget.on('login_successful', this, function() {
+ this.do_push_state(state);
+ this._current_state = null; // ensure the state will be loaded
+ this.show_application(); // will load the state we just pushed
+ });
},
show_application: function() {
var self = this;
- self.$('.oe_topbar').show();
+ self.toggle_bars(true);
self.menu = new instance.web.Menu(self);
- self.menu.replace(this.$element.find('.oe_menu_placeholder'));
+ self.menu.replace(this.$el.find('.oe_menu_placeholder'));
self.menu.on('menu_click', this, this.on_menu_action);
self.user_menu = new instance.web.UserMenu(self);
- self.user_menu.replace(this.$element.find('.oe_user_menu_placeholder'));
+ self.user_menu.replace(this.$el.find('.oe_user_menu_placeholder'));
self.user_menu.on_menu_logout.add(this.proxy('on_logout'));
self.user_menu.on_action.add(this.proxy('on_menu_action'));
self.user_menu.do_update();
self.bind_hashchange();
- if (!self.session.openerp_entreprise) {
- var version_label = _t("OpenERP - Unsupported/Community Version");
- self.$element.find('.oe_footer_powered').append(_.str.sprintf('<span> - <a href="http://www.openerp.com/support-or-publisher-warranty-contract" target="_blank">%s</a></span>', version_label));
- }
self.set_title();
},
destroy_content: function() {
_.each(_.clone(this.getChildren()), function(el) {
el.destroy();
});
- this.$element.children().remove();
+ this.$el.children().remove();
},
do_reload: function() {
var self = this;
return this.session.session_reload().pipe(function () {
- instance.connection.load_modules(true).pipe(
+ instance.session.load_modules(true).pipe(
self.menu.proxy('do_reload')); });
},
@@ -1032,28 +1070,28 @@ instance.web.WebClient = instance.web.Client.extend({
},
on_logout: function() {
var self = this;
- this.session.session_logout().then(function () {
- $(window).unbind('hashchange', self.on_hashchange);
- self.do_push_state({});
- //would be cool to be able to do this, but I think it will make addons do strange things
- //this.show_login();
- window.location.reload();
- });
+ if (!this.has_uncommitted_changes()) {
+ this.session.session_logout().then(function () {
+ $(window).unbind('hashchange', self.on_hashchange);
+ self.do_push_state({});
+ window.location.reload();
+ });
+ }
},
bind_hashchange: function() {
var self = this;
$(window).bind('hashchange', this.on_hashchange);
var state = $.bbq.getState(true);
- if (! _.isEmpty(state)) {
- $(window).trigger('hashchange');
- } else {
+ if (_.isEmpty(state) || state.action == "login") {
self.menu.has_been_loaded.then(function() {
- var first_menu_id = self.menu.$element.find("a:first").data("menu");
+ var first_menu_id = self.menu.$el.find("a:first").data("menu");
if(first_menu_id) {
self.menu.menu_click(first_menu_id);
}
});
+ } else {
+ $(window).trigger('hashchange');
}
},
on_hashchange: function(event) {
@@ -1081,34 +1119,35 @@ instance.web.WebClient = instance.web.Client.extend({
},
on_menu_action: function(options) {
var self = this;
- this.rpc("/web/action/load", { action_id: options.action_id })
- .then(function (result) {
+ return this.rpc("/web/action/load", { action_id: options.action_id })
+ .pipe(function (result) {
var action = result.result;
if (options.needaction) {
action.context.search_default_needaction_pending = true;
}
- self.action_manager.clear_breadcrumbs();
- self.action_manager.do_action(action);
- });
- },
- do_action: function(action) {
- var self = this;
- // TODO replace by client action menuclick
- if(action.menu_id) {
- this.do_reload().then(function () {
- self.menu.menu_click(action.menu_id);
+ return $.when(self.action_manager.do_action(action, null, true)).fail(function() {
+ self.menu.open_menu(options.previous_menu_id);
+ });
});
- }
},
set_content_full_screen: function(fullscreen) {
if (fullscreen) {
- $(".oe_webclient", this.$element).addClass("oe_content_full_screen");
+ $(".oe_webclient", this.$el).addClass("oe_content_full_screen");
$("body").css({'overflow-y':'hidden'});
} else {
- $(".oe_webclient", this.$element).removeClass("oe_content_full_screen");
+ $(".oe_webclient", this.$el).removeClass("oe_content_full_screen");
$("body").css({'overflow-y':'scroll'});
}
- }
+ },
+ has_uncommitted_changes: function() {
+ var $e = $.Event('clear_uncommitted_changes');
+ instance.web.bus.trigger('clear_uncommitted_changes', $e);
+ if ($e.isDefaultPrevented()) {
+ return true;
+ } else {
+ return this._super.apply(this, arguments);
+ }
+ },
});
instance.web.EmbeddedClient = instance.web.Client.extend({
@@ -1125,7 +1164,7 @@ instance.web.EmbeddedClient = instance.web.Client.extend({
start: function() {
var self = this;
return $.when(this._super()).pipe(function() {
- return instance.connection.session_authenticate(self.dbname, self.login, self.key, true).pipe(function() {
+ return instance.session.session_authenticate(self.dbname, self.login, self.key, true).pipe(function() {
return self.rpc("/web/action/load", { action_id: self.action_id }, function(result) {
var action = result.result;
action.flags = _.extend({
diff --git a/addons/web/static/src/js/corelib.js b/addons/web/static/src/js/corelib.js
index bc506161db5..228cee5e411 100644
--- a/addons/web/static/src/js/corelib.js
+++ b/addons/web/static/src/js/corelib.js
@@ -525,8 +525,8 @@ instance.web.WidgetMixin = _.extend({},instance.web.CallbackEnabledMixin, {
_.each(this.getChildren(), function(el) {
el.destroy();
});
- if(this.$element) {
- this.$element.remove();
+ if(this.$el) {
+ this.$el.remove();
}
instance.web.PropertiesMixin.destroy.call(this);
},
@@ -538,7 +538,7 @@ instance.web.WidgetMixin = _.extend({},instance.web.CallbackEnabledMixin, {
appendTo: function(target) {
var self = this;
return this.__widgetRenderAndInsert(function(t) {
- self.$element.appendTo(t);
+ self.$el.appendTo(t);
}, target);
},
/**
@@ -549,7 +549,7 @@ instance.web.WidgetMixin = _.extend({},instance.web.CallbackEnabledMixin, {
prependTo: function(target) {
var self = this;
return this.__widgetRenderAndInsert(function(t) {
- self.$element.prependTo(t);
+ self.$el.prependTo(t);
}, target);
},
/**
@@ -560,7 +560,7 @@ instance.web.WidgetMixin = _.extend({},instance.web.CallbackEnabledMixin, {
insertAfter: function(target) {
var self = this;
return this.__widgetRenderAndInsert(function(t) {
- self.$element.insertAfter(t);
+ self.$el.insertAfter(t);
}, target);
},
/**
@@ -571,7 +571,7 @@ instance.web.WidgetMixin = _.extend({},instance.web.CallbackEnabledMixin, {
insertBefore: function(target) {
var self = this;
return this.__widgetRenderAndInsert(function(t) {
- self.$element.insertBefore(t);
+ self.$el.insertBefore(t);
}, target);
},
/**
@@ -581,7 +581,7 @@ instance.web.WidgetMixin = _.extend({},instance.web.CallbackEnabledMixin, {
*/
replace: function(target) {
return this.__widgetRenderAndInsert(_.bind(function(t) {
- this.$element.replaceAll(t);
+ this.$el.replaceAll(t);
}, this), target);
},
__widgetRenderAndInsert: function(insertion, target) {
@@ -640,8 +640,8 @@ instance.web.CallbackEnabled = instance.web.Class.extend(instance.web.CallbackEn
* // stuff that you want to init before the rendering
* },
* start: function() {
- * // 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 * /);
+ * // stuff you want to make after the rendering, `this.$el` holds a correct value
+ * this.$el.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()
@@ -692,7 +692,7 @@ instance.web.Widget = instance.web.Class.extend(instance.web.WidgetMixin, {
instance.web.WidgetMixin.init.call(this,parent);
// FIXME: this should not be
this.setElement(this._make_descriptive());
- this.session = instance.connection;
+ this.session = instance.session;
},
/**
* Renders the element. The default implementation renders the widget using QWeb,
@@ -718,15 +718,15 @@ instance.web.Widget = instance.web.Class.extend(instance.web.WidgetMixin, {
* @returns {*} this
*/
replaceElement: function ($el) {
- var $oldel = this.$element;
+ var $oldel = this.$el;
this.setElement($el);
- if ($oldel && !$oldel.is(this.$element)) {
- $oldel.replaceWith(this.$element);
+ if ($oldel && !$oldel.is(this.$el)) {
+ $oldel.replaceWith(this.$el);
}
return this;
},
/**
- * Re-sets the widget's root element (el/$el/$element).
+ * Re-sets the widget's root element (el/$el/$el).
*
* Includes:
* * re-delegating events
@@ -738,14 +738,14 @@ instance.web.Widget = instance.web.Class.extend(instance.web.WidgetMixin, {
* @return {*} this
*/
setElement: function (element) {
- // NB: completely useless, as WidgetMixin#init creates a $element
+ // NB: completely useless, as WidgetMixin#init creates a $el
// always
- if (this.$element) {
+ if (this.$el) {
this.undelegateEvents();
}
- this.$element = (element instanceof $) ? element : $(element);
- this.el = this.$element[0];
+ this.$el = (element instanceof $) ? element : $(element);
+ this.el = this.$el[0];
this.delegateEvents();
@@ -797,32 +797,33 @@ instance.web.Widget = instance.web.Class.extend(instance.web.WidgetMixin, {
event += '.widget_events';
if (!selector) {
- this.$element.on(event, method);
+ this.$el.on(event, method);
} else {
- this.$element.on(event, selector, method);
+ this.$el.on(event, selector, method);
}
}
},
undelegateEvents: function () {
- this.$element.off('.widget_events');
+ this.$el.off('.widget_events');
},
/**
- * Shortcut for ``this.$element.find(selector)``
+ * Shortcut for ``this.$el.find(selector)``
*
* @param {String} selector CSS selector, rooted in $el
* @returns {jQuery} selector match
*/
$: function(selector) {
- return this.$element.find(selector);
+ return this.$el.find(selector);
},
/**
* Informs the action manager to do an action. This supposes 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.getParent()) {
- return this.getParent().do_action(action, on_finished);
+ do_action: function() {
+ var parent = this.getParent();
+ if (parent) {
+ return parent.do_action.apply(parent, arguments);
}
return false;
},
@@ -841,7 +842,7 @@ instance.web.Widget = instance.web.Class.extend(instance.web.WidgetMixin, {
rpc: function(url, data, success, error) {
var def = $.Deferred().then(success, error);
var self = this;
- instance.connection.rpc(url, data). then(function() {
+ instance.session.rpc(url, data). then(function() {
if (!self.isDestroyed())
def.resolve.apply(def, arguments);
}, function() {
@@ -863,7 +864,7 @@ instance.web.Registry = instance.web.Class.extend({
*
* An object path is simply a dotted name from the instance root to the
* object pointed to (e.g. ``"instance.web.Session"`` for an OpenERP
- * connection object).
+ * session object).
*
* @constructs instance.web.Registry
* @param {Object} mapping a mapping of keys to object-paths
@@ -972,6 +973,11 @@ instance.web.Registry = instance.web.Class.extend({
});
instance.web.JsonRPC = instance.web.CallbackEnabled.extend({
+ triggers: {
+ 'request': 'Request sent',
+ 'response': 'Response received',
+ 'error': 'HTTP Error response or timeout received',
+ },
/**
* @constructs instance.web.JsonRPC
* @extends instance.web.CallbackEnabled
@@ -1089,12 +1095,12 @@ instance.web.JsonRPC = instance.web.CallbackEnabled.extend({
id: _.uniqueId('r')
};
var deferred = $.Deferred();
- this.on_rpc_request();
+ this.trigger('request', url, payload);
var aborter = params.aborter;
delete params.aborter;
var request = this.rpc_function(url, payload).then(
function (response, textStatus, jqXHR) {
- self.on_rpc_response();
+ self.trigger('response', response);
if (!response.error) {
if (url.url === '/web/session/eval_domain_and_context') {
self.test_eval(params, response.result);
@@ -1102,18 +1108,12 @@ instance.web.JsonRPC = instance.web.CallbackEnabled.extend({
deferred.resolve(response["result"], textStatus, jqXHR);
} else if (response.error.data.type === "session_invalid") {
self.uid = false;
- // TODO deprecate or use a deferred on login.do_ask_login()
- self.on_session_invalid(function() {
- self.rpc(url, payload.params,
- function() { deferred.resolve.apply(deferred, arguments); },
- function() { deferred.reject.apply(deferred, arguments); });
- });
} else {
deferred.reject(response.error, $.Event());
}
},
function(jqXHR, textStatus, errorThrown) {
- self.on_rpc_response();
+ self.trigger('error');
var error = {
code: -32098,
message: "XmlHttpRequestError " + errorThrown,
@@ -1221,10 +1221,6 @@ instance.web.JsonRPC = instance.web.CallbackEnabled.extend({
return deferred;
}
},
- on_rpc_request: function() {
- },
- on_rpc_response: function() {
- },
on_rpc_error: function(error) {
},
get_url: function (file) {
diff --git a/addons/web/static/src/js/coresetup.js b/addons/web/static/src/js/coresetup.js
index 2f2fb4f7832..d55408ebc83 100644
--- a/addons/web/static/src/js/coresetup.js
+++ b/addons/web/static/src/js/coresetup.js
@@ -11,32 +11,6 @@ if (!console.debug) {
openerp.web.coresetup = function(instance) {
-/**
- * @deprecated use :class:`instance.web.Widget`
- */
-instance.web.OldWidget = instance.web.Widget.extend({
- init: function(parent, element_id) {
- this._super(parent);
- this.element_id = element_id;
- this.element_id = this.element_id || _.uniqueId('widget-');
-
- var tmp = document.getElementById(this.element_id);
- this.setElement(tmp || this._make_descriptive());
- },
- renderElement: function() {
- var rendered = this.render();
- if (rendered) {
- this.replaceElement($(rendered));
- }
- return this;
- },
- render: function (additional) {
- if (this.template)
- return instance.web.qweb.render(this.template, _.extend({widget: this}, additional || {}));
- return null;
- }
-});
-
/** Session openerp specific RPC class */
instance.web.Session = instance.web.JsonRPC.extend( /** @lends instance.web.Session# */{
init: function() {
@@ -61,7 +35,6 @@ instance.web.Session = instance.web.JsonRPC.extend( /** @lends instance.web.Sess
this.username = false;
this.user_context= {};
this.db = false;
- this.openerp_entreprise = false;
this.module_list = instance._modules.slice();
this.module_loaded = {};
_(this.module_list).each(function (mod) {
@@ -84,7 +57,12 @@ instance.web.Session = instance.web.JsonRPC.extend( /** @lends instance.web.Sess
if(self.session_is_valid()) {
return deferred.pipe(function() { return self.load_modules(); });
}
- return deferred;
+ return $.when(
+ deferred,
+ self.rpc('/web/webclient/bootstrap_translations', {mods: instance._modules}).pipe(function(trans) {
+ instance.web._t.database.set_bundle(trans);
+ })
+ );
});
},
/**
@@ -104,8 +82,7 @@ instance.web.Session = instance.web.JsonRPC.extend( /** @lends instance.web.Sess
db: result.db,
username: result.login,
uid: result.uid,
- user_context: result.context,
- openerp_entreprise: result.openerp_entreprise
+ user_context: result.context
});
});
},
@@ -129,8 +106,7 @@ instance.web.Session = instance.web.JsonRPC.extend( /** @lends instance.web.Sess
db: result.db,
username: result.login,
uid: result.uid,
- user_context: result.context,
- openerp_entreprise: result.openerp_entreprise
+ user_context: result.context
});
if (!_volatile) {
self.set_cookie('session_id', self.session_id);
@@ -142,20 +118,6 @@ instance.web.Session = instance.web.JsonRPC.extend( /** @lends instance.web.Sess
this.set_cookie('session_id', '');
return this.rpc("/web/session/destroy", {});
},
- on_session_valid: function() {
- },
- /**
- * Called when a rpc call fail due to an invalid session.
- * By default, it's a noop
- */
- on_session_invalid: function(retry_callback) {
- },
- /**
- * Fetches a cookie stored by an openerp session
- *
- * @private
- * @param name the cookie's name
- */
get_cookie: function (name) {
if (!this.name) { return null; }
var nameEQ = this.name + '|' + name + '=';
@@ -189,9 +151,8 @@ instance.web.Session = instance.web.JsonRPC.extend( /** @lends instance.web.Sess
/**
* Load additional web addons of that instance and init them
*
- * @param {Boolean} [no_session_valid_signal=false] prevents load_module from triggering ``on_session_valid``.
*/
- load_modules: function(no_session_valid_signal) {
+ load_modules: function() {
var self = this;
return this.rpc('/web/session/modules', {}).pipe(function(result) {
var lang = self.user_context.lang,
@@ -200,32 +161,31 @@ instance.web.Session = instance.web.JsonRPC.extend( /** @lends instance.web.Sess
var to_load = _.difference(result, self.module_list).join(',');
self.module_list = all_modules;
- var loaded = $.Deferred().resolve().promise();
- if (to_load.length) {
+ var loaded = self.rpc('/web/webclient/translations', params).then(function(trans) {
+ instance.web._t.database.set_bundle(trans);
+ });
+ var file_list = ["/web/static/lib/datejs/globalization/" + lang.replace("_", "-") + ".js"];
+ if(to_load.length) {
loaded = $.when(
- self.rpc('/web/webclient/csslist', {mods: to_load}, self.do_load_css),
+ loaded,
+ self.rpc('/web/webclient/csslist', {mods: to_load}).then(self.do_load_css),
self.rpc('/web/webclient/qweblist', {mods: to_load}).pipe(self.do_load_qweb),
- self.rpc('/web/webclient/translations', params).pipe(function(trans) {
- instance.web._t.database.set_bundle(trans);
- var file_list = ["/web/static/lib/datejs/globalization/" + lang.replace("_", "-") + ".js"];
- return self.rpc('/web/webclient/jslist', {mods: to_load}).pipe(function(files) {
- return self.do_load_js(file_list.concat(files));
- }).then(function () {
- if (!Date.CultureInfo.pmDesignator) {
- // If no am/pm designator is specified but the openerp
- // datetime format uses %i, date.js won't be able to
- // correctly format a date. See bug#938497.
- Date.CultureInfo.amDesignator = 'AM';
- Date.CultureInfo.pmDesignator = 'PM';
- }
- });
- }))
+ self.rpc('/web/webclient/jslist', {mods: to_load}).then(function(files) {
+ file_list = file_list.concat(files);
+ })
+ );
}
- return loaded.then(function() {
+ return loaded.pipe(function () {
+ return self.do_load_js(file_list);
+ }).then(function() {
self.on_modules_loaded();
self.trigger('module_loaded');
- if (!no_session_valid_signal) {
- self.on_session_valid();
+ if (!Date.CultureInfo.pmDesignator) {
+ // If no am/pm designator is specified but the openerp
+ // datetime format uses %i, date.js won't be able to
+ // correctly format a date. See bug#938497.
+ Date.CultureInfo.amDesignator = 'AM';
+ Date.CultureInfo.pmDesignator = 'PM';
}
});
});
@@ -540,52 +500,14 @@ $.async_when = function() {
// special tweak for the web client
var old_async_when = $.async_when;
$.async_when = function() {
- if (instance.connection.synch)
+ if (instance.session.synch)
return $.when.apply(this, arguments);
else
return old_async_when.apply(this, arguments);
};
-/** Setup blockui */
-if ($.blockUI) {
- $.blockUI.defaults.baseZ = 1100;
- $.blockUI.defaults.message = '<div class="oe_blockui_spin" style="height: 50px">';
- $.blockUI.defaults.css.border = '0';
- $.blockUI.defaults.css["background-color"] = '';
- $.blockUI.spinners = [];
-}
-instance.web.blockUI = function() {
- var tmp = $.blockUI.apply($, arguments);
- var target = $(".oe_blockui_spin")[0];
- var opts = {
- lines: 13, // The number of lines to draw
- length: 7, // The length of each line
- width: 4, // The line thickness
- radius: 10, // The radius of the inner circle
- rotate: 0, // The rotation offset
- color: '#FFF', // #rgb or #rrggbb
- speed: 1, // Rounds per second
- trail: 60, // Afterglow percentage
- shadow: false, // Whether to render a shadow
- hwaccel: false, // Whether to use hardware acceleration
- className: 'spinner', // The CSS class to assign to the spinner
- zIndex: 2e9, // The z-index (defaults to 2000000000)
- top: 'auto', // Top position relative to parent in px
- left: 'auto' // Left position relative to parent in px
- };
- var spinner = new Spinner(opts).spin(target);
- $.blockUI.spinners.push(spinner);
- return tmp;
-}
-instance.web.unblockUI = function() {
- _.each($.blockUI.spinners, function(el) {
- el.stop();
- });
- return $.unblockUI.apply($, arguments);
-}
-
/** Setup default session */
-instance.connection = new instance.web.Session();
+instance.session = new instance.web.Session();
/** Configure default qweb */
instance.web._t = new instance.web.TranslationDataBase().build_translation_function();
@@ -604,8 +526,8 @@ instance.web._lt = function (s) {
return {toString: function () { return instance.web._t(s); }}
};
instance.web.qweb = new QWeb2.Engine();
-instance.web.qweb.default_dict['__debug__'] = instance.connection.debug; // Which one ?
-instance.web.qweb.debug = instance.connection.debug;
+instance.web.qweb.default_dict['__debug__'] = instance.session.debug; // Which one ?
+instance.web.qweb.debug = instance.session.debug;
instance.web.qweb.default_dict = {
'_' : _,
'_t' : instance.web._t
@@ -620,13 +542,9 @@ instance.web.qweb.preprocess_node = function() {
if (translation && translation.value === 'off') {
return;
}
- var ts = _.str.trim(this.node.data);
- if (ts.length === 0) {
- return;
- }
- var tr = instance.web._t(ts);
- if (tr !== ts) {
- this.node.data = tr;
+ var match = /^(\s*)(.+?)(\s*)$/.exec(this.node.data);
+ if (match) {
+ this.node.data = match[1] + instance.web._t(match[2]) + match[3];
}
break;
case 1:
@@ -662,42 +580,96 @@ var _t = instance.web._t;
_t('%d years ago');
}
-instance.connection.on('module_loaded', this, function () {
+instance.session.on('module_loaded', this, function () {
// provide timeago.js with our own translator method
$.timeago.settings.translator = instance.web._t;
});
+/** Setup blockui */
+if ($.blockUI) {
+ $.blockUI.defaults.baseZ = 1100;
+ $.blockUI.defaults.message = '<div class="openerp oe_blockui_spin_container" style="background-color: transparent;">';
+ $.blockUI.defaults.css.border = '0';
+ $.blockUI.defaults.css["background-color"] = '';
+}
+
+var messages_by_seconds = function() {
+ return [
+ [0, _t("Loading...")],
+ [20, _t("Still loading...")],
+ [60, _t("Still loading...<br />Please be patient.")],
+ [120, _t("Don't leave yet,<br />it's still loading...")],
+ [300, _t("You may not believe it,<br />but the application is actually loading...")],
+ [420, _t("Take a minute to get a coffee,<br />because it's loading...")],
+ [3600, _t("Maybe you should consider reloading the application by pressing F5...")],
+ ];
+};
+
+instance.web.Throbber = instance.web.Widget.extend({
+ template: "Throbber",
+ start: function() {
+ var opts = {
+ lines: 13, // The number of lines to draw
+ length: 7, // The length of each line
+ width: 4, // The line thickness
+ radius: 10, // The radius of the inner circle
+ rotate: 0, // The rotation offset
+ color: '#FFF', // #rgb or #rrggbb
+ speed: 1, // Rounds per second
+ trail: 60, // Afterglow percentage
+ shadow: false, // Whether to render a shadow
+ hwaccel: false, // Whether to use hardware acceleration
+ className: 'spinner', // The CSS class to assign to the spinner
+ zIndex: 2e9, // The z-index (defaults to 2000000000)
+ top: 'auto', // Top position relative to parent in px
+ left: 'auto' // Left position relative to parent in px
+ };
+ this.spin = new Spinner(opts).spin(this.$el[0]);
+ this.start_time = new Date().getTime();
+ this.act_message();
+ },
+ act_message: function() {
+ var self = this;
+ setTimeout(function() {
+ if (self.isDestroyed())
+ return;
+ var seconds = (new Date().getTime() - self.start_time) / 1000;
+ var mes;
+ _.each(messages_by_seconds(), function(el) {
+ if (seconds >= el[0])
+ mes = el[1];
+ });
+ self.$(".oe_throbber_message").html(mes);
+ self.act_message();
+ }, 1000);
+ },
+ destroy: function() {
+ if (this.spin)
+ this.spin.stop();
+ this._super();
+ },
+});
+instance.web.Throbber.throbbers = [];
+
+instance.web.blockUI = function() {
+ var tmp = $.blockUI.apply($, arguments);
+ var throbber = new instance.web.Throbber();
+ instance.web.Throbber.throbbers.push(throbber);
+ throbber.appendTo($(".oe_blockui_spin_container"));
+ return tmp;
+}
+instance.web.unblockUI = function() {
+ _.each(instance.web.Throbber.throbbers, function(el) {
+ el.destroy();
+ });
+ return $.unblockUI.apply($, arguments);
+}
+
/**
* Registry for all the client actions key: tag value: widget
*/
instance.web.client_actions = new instance.web.Registry();
-/**
- * Client action to reload the whole interface.
- * If params has an entry 'menu_id', it opens the given menu entry.
- */
-instance.web.Reload = instance.web.Widget.extend({
- init: function(parent, params) {
- this._super(parent);
- this.menu_id = (params && params.menu_id) || false;
- },
- start: function() {
- var l = window.location;
- var timestamp = new Date().getTime();
- var search = "?ts=" + timestamp;
- if (l.search) {
- search = l.search + "&ts=" + timestamp;
- }
- var hash = l.hash;
- if (this.menu_id) {
- hash = "#menu_id=" + this.menu_id;
- }
- var url = l.protocol + "//" + l.host + l.pathname + search + hash;
- window.location = url;
- }
-});
-instance.web.client_actions.add("reload", "instance.web.Reload");
-
};
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:
diff --git a/addons/web/static/src/js/data.js b/addons/web/static/src/js/data.js
index 2589afe5503..2b82e618203 100644
--- a/addons/web/static/src/js/data.js
+++ b/addons/web/static/src/js/data.js
@@ -58,7 +58,7 @@ instance.web.Query = instance.web.Class.extend({
},
_execute: function () {
var self = this;
- return instance.connection.rpc('/web/dataset/search_read', {
+ return instance.session.rpc('/web/dataset/search_read', {
model: this._model.name,
fields: this._fields || false,
domain: this._model.domain(this._filter),
@@ -107,7 +107,7 @@ instance.web.Query = instance.web.Class.extend({
* Performs a groups read according to the provided grouping criterion
*
* @param {String|Array<String>} grouping
- * @returns {jQuery.Deferred<Array<openerp.web.data.Group>> | null}
+ * @returns {jQuery.Deferred<Array<openerp.web.QueryGroup>> | null}
*/
group_by: function (grouping) {
if (grouping === undefined) {
@@ -130,7 +130,7 @@ instance.web.Query = instance.web.Class.extend({
orderby: instance.web.serialize_sort(this._order_by) || false
}).pipe(function (results) {
return _(results).map(function (result) {
- return new instance.web.data.Group(
+ return new instance.web.QueryGroup(
self._model.name, grouping[0], result);
});
});
@@ -194,7 +194,54 @@ instance.web.Query = instance.web.Class.extend({
}
});
-instance.web.Model = instance.web.Class.extend(/** @lends openerp.web.Model# */{
+instance.web.QueryGroup = instance.web.Class.extend({
+ init: function (model, grouping_field, read_group_group) {
+ // In cases where group_by_no_leaf and no group_by, the result of
+ // read_group has aggregate fields but no __context or __domain.
+ // Create default (empty) values for those so that things don't break
+ var fixed_group = _.extend(
+ {__context: {group_by: []}, __domain: []},
+ read_group_group);
+
+ var aggregates = {};
+ _(fixed_group).each(function (value, key) {
+ if (key.indexOf('__') === 0
+ || key === grouping_field
+ || key === grouping_field + '_count') {
+ return;
+ }
+ aggregates[key] = value || 0;
+ });
+
+ this.model = new instance.web.Model(
+ model, fixed_group.__context, fixed_group.__domain);
+
+ var group_size = fixed_group[grouping_field + '_count'] || fixed_group.__count || 0;
+ var leaf_group = fixed_group.__context.group_by.length === 0;
+ this.attributes = {
+ folded: !!(fixed_group.__fold),
+ grouped_on: grouping_field,
+ // if terminal group (or no group) and group_by_no_leaf => use group.__count
+ length: group_size,
+ value: fixed_group[grouping_field],
+ // A group is open-able if it's not a leaf in group_by_no_leaf mode
+ has_children: !(leaf_group && fixed_group.__context['group_by_no_leaf']),
+
+ aggregates: aggregates
+ };
+ },
+ get: function (key) {
+ return this.attributes[key];
+ },
+ subgroups: function () {
+ return this.model.query().group_by(this.model.context().group_by);
+ },
+ query: function () {
+ return this.model.query.apply(this.model, arguments);
+ }
+});
+
+instance.web.Model = instance.web.Class.extend({
/**
* @constructs instance.web.Model
* @extends instance.web.Class
@@ -233,7 +280,7 @@ instance.web.Model = instance.web.Class.extend(/** @lends openerp.web.Model# */{
kwargs = args;
args = [];
}
- return instance.connection.rpc('/web/dataset/call_kw', {
+ return instance.session.rpc('/web/dataset/call_kw', {
model: this.name,
method: method,
args: args,
@@ -256,7 +303,7 @@ instance.web.Model = instance.web.Class.extend(/** @lends openerp.web.Model# */{
* @param {String} signal signal to trigger on the workflow
*/
exec_workflow: function (id, signal) {
- return instance.connection.rpc('/web/dataset/exec_workflow', {
+ return instance.session.rpc('/web/dataset/exec_workflow', {
model: this.name,
id: id,
signal: signal
@@ -282,7 +329,7 @@ instance.web.Model = instance.web.Class.extend(/** @lends openerp.web.Model# */{
*/
context: function (context) {
return new instance.web.CompoundContext(
- instance.connection.user_context, this._context, context || {});
+ instance.session.user_context, this._context, context || {});
},
/**
* Button action caller, needs to perform cleanup if an action is returned
@@ -292,7 +339,7 @@ instance.web.Model = instance.web.Class.extend(/** @lends openerp.web.Model# */{
* FIXME: remove when evaluator integrated
*/
call_button: function (method, args) {
- return instance.connection.rpc('/web/dataset/call_button', {
+ return instance.session.rpc('/web/dataset/call_button', {
model: this.name,
method: method,
domain_id: null,
@@ -302,144 +349,7 @@ instance.web.Model = instance.web.Class.extend(/** @lends openerp.web.Model# */{
},
});
-instance.web.Traverser = instance.web.Class.extend(/** @lends openerp.web.Traverser# */{
- /**
- * @constructs instance.web.Traverser
- * @extends instance.web.Class
- *
- * @param {instance.web.Model} model instance this traverser is bound to
- */
- init: function (model) {
- this._model = model;
- this._index = 0;
- },
-
- /**
- * Gets and sets the current index
- *
- * @param {Number} [idx]
- * @returns {Number} current index
- */
- index: function (idx) {
- if (idx) { this._index = idx; }
- return this._index;
- },
- /**
- * Returns the model this traverser is currently bound to
- *
- * @returns {openerp.web.Model}
- */
- model: function () {
- return this._model;
- },
- /**
- * Fetches the size of the backing model's match
- *
- * @returns {Deferred<Number>} deferred count
- */
- size: function () {
- return this._model.query().count();
- },
-
- /**
- * Record at the current index for the collection, fails if there is no
- * record at the current index.
- *
- * @returns {Deferred<>}
- */
- current: function (fields) {
- return this._model.query(fields).first().pipe(function (record) {
- if (record == null) {
- return $.Deferred()
- .reject('No record at index' + this._index)
- .promise();
- }
- return record;
- });
- },
- next: function (fields) {
- var self = this;
- this._index++;
- return this.size().pipe(function (s) {
- if (self._index >= s) {
- self._index = 0;
- }
- return self.current(fields);
- });
- },
- previous: function (fields) {
- var self = this;
- this._index--;
- if (this._index < 0) {
- return this.size().pipe(function (s) {
- self._index = s-1;
- return self.current(fields);
- });
- }
- return this.current(fields);
- }
-
-});
-
-/**
- * Utility objects, should never need to be instantiated from outside of this
- * module
- *
- * @namespace
- */
-instance.web.data = {
- Group: instance.web.Class.extend(/** @lends openerp.web.data.Group# */{
- /**
- * @constructs instance.web.data.Group
- * @extends instance.web.Class
- */
- init: function (model, grouping_field, read_group_group) {
- // In cases where group_by_no_leaf and no group_by, the result of
- // read_group has aggregate fields but no __context or __domain.
- // Create default (empty) values for those so that things don't break
- var fixed_group = _.extend(
- {__context: {group_by: []}, __domain: []},
- read_group_group);
-
- var aggregates = {};
- _(fixed_group).each(function (value, key) {
- if (key.indexOf('__') === 0
- || key === grouping_field
- || key === grouping_field + '_count') {
- return;
- }
- aggregates[key] = value || 0;
- });
-
- this.model = new instance.web.Model(
- model, fixed_group.__context, fixed_group.__domain);
-
- var group_size = fixed_group[grouping_field + '_count'] || fixed_group.__count || 0;
- var leaf_group = fixed_group.__context.group_by.length === 0;
- this.attributes = {
- grouped_on: grouping_field,
- // if terminal group (or no group) and group_by_no_leaf => use group.__count
- length: group_size,
- value: fixed_group[grouping_field],
- // A group is open-able if it's not a leaf in group_by_no_leaf mode
- has_children: !(leaf_group && fixed_group.__context['group_by_no_leaf']),
-
- aggregates: aggregates
- };
- },
- get: function (key) {
- return this.attributes[key];
- },
- subgroups: function () {
- return this.model.query().group_by(this.model.context().group_by);
- },
- query: function () {
- return this.model.query.apply(this.model, arguments);
- }
- })
-};
-
-instance.web.DataGroup = instance.web.OldWidget.extend( /** @lends openerp.web.DataGroup# */{
+instance.web.DataGroup = instance.web.CallbackEnabled.extend({
/**
* Management interface between views and grouped collections of OpenERP
* records.
@@ -451,9 +361,9 @@ instance.web.DataGroup = instance.web.OldWidget.extend( /** @lends openerp.web.
* content of the current grouping level.
*
* @constructs instance.web.DataGroup
- * @extends instance.web.OldWidget
+ * @extends instance.web.CallbackEnabled
*
- * @param {instance.web.OldWidget} parent widget
+ * @param {instance.web.CallbackEnabled} parent widget
* @param {String} model name of the model managed by this DataGroup
* @param {Array} domain search domain for this DataGroup
* @param {Object} context context of the DataGroup's searches
@@ -471,49 +381,47 @@ instance.web.DataGroup = instance.web.OldWidget.extend( /** @lends openerp.web.
},
list: function (fields, ifGroups, ifRecords) {
var self = this;
- $.when(this.model.query(fields)
- .order_by(this.sort)
- .group_by(this.group_by)).then(function (groups) {
- if (!groups) {
- ifRecords(_.extend(
- new instance.web.DataSetSearch(
- self, self.model.name,
- self.model.context(),
- self.model.domain()),
- {_sort: self.sort}));
+ var query = this.model.query(fields).order_by(this.sort).group_by(this.group_by);
+ $.when(query).then(function (querygroups) {
+ // leaf node
+ if (!querygroups) {
+ var ds = new instance.web.DataSetSearch(self, self.model.name, self.model.context(), self.model.domain());
+ ds._sort = self.sort;
+ ifRecords(ds);
return;
}
- ifGroups(_(groups).map(function (group) {
+ // internal node
+ var child_datagroups = _(querygroups).map(function (group) {
var child_context = _.extend(
{}, self.model.context(), group.model.context());
- return _.extend(
- new instance.web.DataGroup(
- self, self.model.name, group.model.domain(),
- child_context, group.model._context.group_by,
- self.level + 1),
- {
- __context: child_context,
- __domain: group.model.domain(),
- grouped_on: group.get('grouped_on'),
- length: group.get('length'),
- value: group.get('value'),
- openable: group.get('has_children'),
- aggregates: group.get('aggregates')
- }, {sort: self.sort});
- }));
+ var child_dg = new instance.web.DataGroup(
+ self, self.model.name, group.model.domain(),
+ child_context, group.model._context.group_by,
+ self.level + 1);
+ child_dg.sort = self.sort;
+ // copy querygroup properties
+ child_dg.__context = child_context;
+ child_dg.__domain = group.model.domain();
+ child_dg.folded = group.get('folded');
+ child_dg.grouped_on = group.get('grouped_on');
+ child_dg.length = group.get('length');
+ child_dg.value = group.get('value');
+ child_dg.openable = group.get('has_children');
+ child_dg.aggregates = group.get('aggregates');
+ return child_dg;
+ });
+ ifGroups(child_datagroups);
});
}
});
-instance.web.ContainerDataGroup = instance.web.DataGroup.extend({ });
-instance.web.GrouplessDataGroup = instance.web.DataGroup.extend({ });
-instance.web.StaticDataGroup = instance.web.GrouplessDataGroup.extend( /** @lends openerp.web.StaticDataGroup# */ {
+instance.web.StaticDataGroup = instance.web.DataGroup.extend({
/**
- * A specialization of groupless data groups, relying on a single static
+ * A specialization of data groups, relying on a single static
* dataset as its records provider.
*
* @constructs instance.web.StaticDataGroup
- * @extends instance.web.GrouplessDataGroup
+ * @extends instance.web.DataGroup
* @param {openep.web.DataSetStatic} dataset a static dataset backing the groups
*/
init: function (dataset) {
@@ -524,13 +432,12 @@ instance.web.StaticDataGroup = instance.web.GrouplessDataGroup.extend( /** @lend
}
});
-instance.web.DataSet = instance.web.OldWidget.extend( /** @lends openerp.web.DataSet# */{
+instance.web.DataSet = instance.web.CallbackEnabled.extend({
/**
- * DateaManagement interface between views and the collection of selected
- * OpenERP records (represents the view's state?)
+ * Collection of OpenERP records, used to share records and the current selection between views.
*
* @constructs instance.web.DataSet
- * @extends instance.web.OldWidget
+ * @extends instance.web.CallbackEnabled
*
* @param {String} model the OpenERP model this dataset will manage
*/
@@ -644,15 +551,10 @@ instance.web.DataSet = instance.web.OldWidget.extend( /** @lends openerp.web.Da
* Creates a new record in db
*
* @param {Object} data field values to set on the new record
- * @param {Function} callback function called with operation result
- * @param {Function} error_callback function called in case of creation error
* @returns {$.Deferred}
*/
- create: function(data, callback, error_callback) {
- return this._model.call('create',
- [data], {context: this._model.context()})
- .pipe(function (r) { return {result: r}; })
- .then(callback, error_callback);
+ create: function(data) {
+ return this._model.call('create', [data], {context: this._model.context()});
},
/**
* Saves the provided data in an existing db record
@@ -663,24 +565,17 @@ instance.web.DataSet = instance.web.OldWidget.extend( /** @lends openerp.web.Da
* @param {Function} error_callback function called in case of write error
* @returns {$.Deferred}
*/
- write: function (id, data, options, callback, error_callback) {
+ write: function (id, data, options) {
options = options || {};
- return this._model.call('write',
- [[id], data], {context: this._model.context(options.context)})
- .pipe(function (r) { return {result: r}})
- .then(callback, error_callback);
+ return this._model.call('write', [[id], data], {context: this._model.context(options.context)});
},
/**
* Deletes an existing record from the database
*
* @param {Number|String} ids identifier of the record to delete
- * @param {Function} callback function called with operation result
- * @param {Function} error_callback function called in case of deletion error
*/
- unlink: function(ids, callback, error_callback) {
- return this._model.call('unlink',
- [ids], {context: this._model.context()})
- .then(callback, error_callback);
+ unlink: function(ids) {
+ return this._model.call('unlink', [ids], {context: this._model.context()});
},
/**
* Calls an arbitrary RPC method
@@ -701,43 +596,35 @@ instance.web.DataSet = instance.web.OldWidget.extend( /** @lends openerp.web.Da
* @param {Array} [args]
* @param {Number} [domain_index] index of a domain to evaluate in the args array
* @param {Number} [context_index] index of a context to evaluate in the args array
- * @param {Function} callback
- * @param {Function} error_callback
* @returns {$.Deferred}
*/
- call_and_eval: function (method, args, domain_index, context_index, callback, error_callback) {
- return this.rpc('/web/dataset/call', {
+ call_and_eval: function (method, args, domain_index, context_index) {
+ return instance.session.rpc('/web/dataset/call', {
model: this.model,
method: method,
domain_id: domain_index == undefined ? null : domain_index,
context_id: context_index == undefined ? null : context_index,
args: args || []
- }, callback, error_callback);
+ });
},
/**
* Calls a button method, usually returning some sort of action
*
* @param {String} method
* @param {Array} [args]
- * @param {Function} callback
- * @param {Function} error_callback
* @returns {$.Deferred}
*/
- call_button: function (method, args, callback, error_callback) {
- return this._model.call_button(method, args)
- .then(callback, error_callback);
+ call_button: function (method, args) {
+ return this._model.call_button(method, args);
},
/**
* Fetches the "readable name" for records, based on intrinsic rules
*
* @param {Array} ids
- * @param {Function} callback
* @returns {$.Deferred}
*/
- name_get: function(ids, callback) {
- return this._model.call('name_get',
- [ids], {context: this._model.context()})
- .then(callback);
+ name_get: function(ids) {
+ return this._model.call('name_get', [ids], {context: this._model.context()});
},
/**
*
@@ -748,28 +635,24 @@ instance.web.DataSet = instance.web.OldWidget.extend( /** @lends openerp.web.Da
* @param {Function} callback function to call with name_search result
* @returns {$.Deferred}
*/
- name_search: function (name, domain, operator, limit, callback) {
+ name_search: function (name, domain, operator, limit) {
return this._model.call('name_search', {
name: name || '',
args: domain || false,
operator: operator || 'ilike',
context: this._model.context(),
limit: limit || 0
- }).then(callback);
+ });
},
/**
* @param name
- * @param callback
*/
- name_create: function(name, callback) {
- return this._model.call('name_create',
- [name], {context: this._model.context()})
- .then(callback);
+ name_create: function(name) {
+ return this._model.call('name_create', [name], {context: this._model.context()});
},
- exec_workflow: function (id, signal, callback) {
+ exec_workflow: function (id, signal) {
return this._model.exec_workflow(id, signal)
- .pipe(function (result) { return { result: result }; })
- .then(callback);
+ .pipe(function (result) { return { result: result }; });
},
get_context: function(request_context) {
return this._model.context(request_context);
@@ -805,7 +688,24 @@ instance.web.DataSet = instance.web.OldWidget.extend( /** @lends openerp.web.Da
alter_ids: function(n_ids) {
this.ids = n_ids;
},
+ /**
+ * Resequence records.
+ *
+ * @param {Array} ids identifiers of the records to resequence
+ * @returns {$.Deferred}
+ */
+ resequence: function (ids, options) {
+ options = options || {};
+ return instance.session.rpc('/web/dataset/resequence', {
+ model: this.model,
+ ids: ids,
+ context: this._model.context(options.context),
+ }).pipe(function (results) {
+ return results;
+ });
+ },
});
+
instance.web.DataSetStatic = instance.web.DataSet.extend({
init: function(parent, model, context, ids) {
this._super(parent, model, context);
@@ -836,7 +736,8 @@ instance.web.DataSetStatic = instance.web.DataSet.extend({
this.set_ids(_.without.apply(null, [this.ids].concat(ids)));
}
});
-instance.web.DataSetSearch = instance.web.DataSet.extend(/** @lends openerp.web.DataSetSearch */{
+
+instance.web.DataSetSearch = instance.web.DataSet.extend({
/**
* @constructs instance.web.DataSetSearch
* @extends instance.web.DataSet
@@ -886,7 +787,7 @@ instance.web.DataSetSearch = instance.web.DataSet.extend(/** @lends openerp.web
},
unlink: function(ids, callback, error_callback) {
var self = this;
- return this._super(ids, function(result) {
+ return this._super(ids).then(function(result) {
self.ids = _(self.ids).difference(ids);
if (self._length) {
self._length -= 1;
@@ -895,9 +796,7 @@ instance.web.DataSetSearch = instance.web.DataSet.extend(/** @lends openerp.web
self.index = self.index <= self.ids.length - 1 ?
self.index : (self.ids.length > 0 ? self.ids.length -1 : 0);
}
- if (callback)
- callback(result);
- }, error_callback);
+ });
},
size: function () {
if (this._length !== undefined) {
@@ -906,6 +805,7 @@ instance.web.DataSetSearch = instance.web.DataSet.extend(/** @lends openerp.web
return this._super();
}
});
+
instance.web.BufferedDataSet = instance.web.DataSetStatic.extend({
virtual_id_prefix: "one2many_v_id_",
debug_mode: true,
@@ -920,16 +820,14 @@ instance.web.BufferedDataSet = instance.web.DataSetStatic.extend({
on_default_get: function(res) {
this.last_default_get = res;
},
- create: function(data, callback, error_callback) {
+ create: function(data) {
var cached = {id:_.uniqueId(this.virtual_id_prefix), values: data,
defaults: this.last_default_get};
this.to_create.push(_.extend(_.clone(cached), {values: _.clone(cached.values)}));
this.cache.push(cached);
- var prom = $.Deferred().then(callback);
- prom.resolve({result: cached.id});
- return prom.promise();
+ return $.Deferred().resolve(cached.id).promise();
},
- write: function (id, data, options, callback) {
+ write: function (id, data, options) {
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;});
@@ -955,9 +853,7 @@ instance.web.BufferedDataSet = instance.web.DataSetStatic.extend({
$.extend(cached.values, record.values);
if (dirty)
this.on_change();
- var to_return = $.Deferred().then(callback);
- to_return.resolve({result: true});
- return to_return.promise();
+ return $.Deferred().resolve(true).promise();
},
unlink: function(ids, callback, error_callback) {
var self = this;
@@ -981,7 +877,8 @@ instance.web.BufferedDataSet = instance.web.DataSetStatic.extend({
this.cache = [];
this.delete_all = false;
},
- on_change: function() {},
+ on_change: function() {
+ },
read_ids: function (ids, fields, options) {
var self = this;
var to_get = [];
@@ -1053,7 +950,7 @@ instance.web.BufferedDataSet = instance.web.DataSetStatic.extend({
}
return completion.promise();
},
- call_button: function (method, args, callback, error_callback) {
+ call_button: function (method, args) {
var id = args[0][0], index;
for(var i=0, len=this.cache.length; i<len; ++i) {
var record = this.cache[i];
@@ -1064,7 +961,7 @@ instance.web.BufferedDataSet = instance.web.DataSetStatic.extend({
break;
}
}
- return this._super(method, args, callback, error_callback);
+ return this._super(method, args);
},
alter_ids: function(n_ids) {
this._super(n_ids);
@@ -1096,23 +993,23 @@ instance.web.ProxyDataSet = instance.web.DataSetSearch.extend({
return this._super.apply(this, arguments);
}
},
- create: function(data, callback, error_callback) {
+ create: function(data) {
if (this.create_function) {
- return this.create_function(data, this._super).then(callback, error_callback);
+ return this.create_function(data, this._super);
} else {
return this._super.apply(this, arguments);
}
},
- write: function (id, data, options, callback, error_callback) {
+ write: function (id, data, options) {
if (this.write_function) {
- return this.write_function(id, data, options, this._super).then(callback, error_callback);
+ return this.write_function(id, data, options, this._super);
} else {
return this._super.apply(this, arguments);
}
},
- unlink: function(ids, callback, error_callback) {
+ unlink: function(ids) {
if (this.unlink_function) {
- return this.unlink_function(ids, this._super).then(callback, error_callback);
+ return this.unlink_function(ids, this._super);
} else {
return this._super.apply(this, arguments);
}
@@ -1165,7 +1062,7 @@ instance.web.CompoundDomain = instance.web.Class.extend({
}
});
-instance.web.DropMisordered = instance.web.Class.extend(/** @lends openerp.web.DropMisordered# */{
+instance.web.DropMisordered = instance.web.Class.extend({
/**
* @constructs instance.web.DropMisordered
* @extends instance.web.Class
diff --git a/addons/web/static/src/js/data_export.js b/addons/web/static/src/js/data_export.js
index f8e669e1b4a..1353f59d4a5 100644
--- a/addons/web/static/src/js/data_export.js
+++ b/addons/web/static/src/js/data_export.js
@@ -5,7 +5,15 @@ instance.web.DataExport = instance.web.Dialog.extend({
template: 'ExportTreeView',
dialog_title: {toString: function () { return _t("Export Data"); }},
init: function(parent, dataset) {
- this._super(parent);
+ var self = this;
+ options = {
+ buttons : [
+ {text: _t("Close"), click: function() { self.close(); }},
+ {text: _t("Export To File"), click: function() { self.on_click_export_data(); }}
+ ],
+ close: function(event, ui){ self.close();}
+ }
+ this._super(parent, options);
this.records = {};
this.dataset = dataset;
this.exports = new instance.web.DataSetSearch(
@@ -14,38 +22,32 @@ instance.web.DataExport = instance.web.Dialog.extend({
start: function() {
var self = this;
this._super.apply(this, arguments);
- this.open({
- buttons : [
- {text: _t("Close"), click: function() { self.close(); }},
- {text: _t("Export To File"), click: function() { self.on_click_export_data(); }}
- ],
- close: function(event, ui){ self.close();}
- });
- self.$element.removeClass('ui-dialog-content ui-widget-content');
- self.$element.find('#add_field').click(function() {
+ this.open();
+ self.$el.removeClass('ui-dialog-content ui-widget-content');
+ self.$el.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');
+ var fld = self.$el.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.$el.find('#field-tree-structure tr').removeClass('ui-selected');
}
});
- self.$element.find('#remove_field').click(function() {
- self.$element.find('#fields_list option:selected').remove();
+ self.$el.find('#remove_field').click(function() {
+ self.$el.find('#fields_list option:selected').remove();
});
- self.$element.find('#remove_all_field').click(function() {
- self.$element.find('#fields_list').empty();
+ self.$el.find('#remove_all_field').click(function() {
+ self.$el.find('#fields_list').empty();
});
- this.$element.find('#export_new_list').click(this.on_show_save_list);
+ this.$el.find('#export_new_list').click(this.on_show_save_list);
var got_fields = new $.Deferred();
- this.$element.find('#import_compat').change(function() {
- self.$element.find('#fields_list').empty();
- self.$element.find('#field-tree-structure').remove();
- var import_comp = self.$element.find("#import_compat").val();
+ this.$el.find('#import_compat').change(function() {
+ self.$el.find('#fields_list').empty();
+ self.$el.find('#field-tree-structure').remove();
+ var import_comp = self.$el.find("#import_compat").val();
self.rpc("/web/export/get_fields", {
model: self.dataset.model,
import_compat: Boolean(import_comp)
@@ -61,7 +63,7 @@ instance.web.DataExport = instance.web.Dialog.extend({
this.show_exports_list());
},
do_setup_export_formats: function (formats) {
- var $fmts = this.$element.find('#export_format');
+ var $fmts = this.$el.find('#export_format');
_(formats).each(function (format) {
var opt = new Option(format.label, format.tag);
if (format.error) {
@@ -76,8 +78,8 @@ instance.web.DataExport = instance.web.Dialog.extend({
},
show_exports_list: function() {
var self = this;
- if (self.$element.find('#saved_export_list').is(':hidden')) {
- self.$element.find('#ExistsExportList').show();
+ if (self.$el.find('#saved_export_list').is(':hidden')) {
+ self.$el.find('#ExistsExportList').show();
return;
}
return this.exports.read_slice(['name'], {
@@ -86,35 +88,35 @@ instance.web.DataExport = instance.web.Dialog.extend({
if (!export_list.length) {
return;
}
- 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();
+ self.$el.find('#ExistsExportList').append(QWeb.render('Exists.ExportList', {'existing_exports': export_list}));
+ self.$el.find('#saved_export_list').change(function() {
+ self.$el.find('#fields_list option').remove();
+ var export_id = self.$el.find('#saved_export_list option:selected').val();
if (export_id) {
self.rpc('/web/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');
+ self.$el.find('#delete_export_list').click(function() {
+ var select_exp = self.$el.find('#saved_export_list option:selected');
if (select_exp.val()) {
self.exports.unlink([parseInt(select_exp.val(), 10)]);
select_exp.remove();
- if (self.$element.find('#saved_export_list option').length <= 1) {
- self.$element.find('#ExistsExportList').hide();
+ if (self.$el.find('#saved_export_list option').length <= 1) {
+ self.$el.find('#ExistsExportList').hide();
}
}
});
});
},
do_load_export_field: function(field_list) {
- var export_node = this.$element.find("#fields_list");
+ var export_node = this.$el.find("#fields_list");
_(field_list).each(function (field) {
export_node.append(new Option(field.label, field.name));
});
},
on_show_save_list: function() {
var self = this;
- var current_node = self.$element.find("#savenewlist");
+ var current_node = self.$el.find("#savenewlist");
if (!(current_node.find("label")).length) {
current_node.append(QWeb.render('ExportNewList'));
current_node.find("#add_export_list").click(function() {
@@ -150,14 +152,14 @@ instance.web.DataExport = instance.web.Dialog.extend({
if (!export_list_id.result) {
return;
}
- self.$element.find("#saved_export_list").append(
+ self.$el.find("#saved_export_list").append(
new Option(value, export_list_id.result));
- if (self.$element.find("#saved_export_list").is(":hidden")) {
+ if (self.$el.find("#saved_export_list").is(":hidden")) {
self.show_exports_list();
}
});
this.on_show_save_list();
- this.$element.find("#fields_list option").remove();
+ this.$el.find("#fields_list option").remove();
},
on_click: function(id, record) {
var self = this;
@@ -173,7 +175,7 @@ instance.web.DataExport = instance.web.Dialog.extend({
}
if (!record.loaded) {
- var import_comp = self.$element.find("#import_compat").val();
+ var import_comp = self.$el.find("#import_compat").val();
self.rpc("/web/export/get_fields", {
model: model,
prefix: prefix,
@@ -191,44 +193,44 @@ instance.web.DataExport = instance.web.Dialog.extend({
},
on_show_data: function(result, after) {
var self = this;
- var imp_cmpt = Boolean(self.$element.find("#import_compat").val());
+ var imp_cmpt = Boolean(self.$el.find("#import_compat").val());
if (after) {
- var current_tr = self.$element.find("tr[id='treerow-" + after + "']");
+ var current_tr = self.$el.find("tr[id='treerow-" + after + "']");
current_tr.addClass('open');
current_tr.find('img').attr('src','/web/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}));
+ self.$el.find('#left_field_panel').append(QWeb.render('ExportTreeView-Secondary', {'fields': result}));
}
_.each(result, function(record) {
self.records[record.id] = record.value;
if (record.required) {
- var required_fld = self.$element.find("tr[id='treerow-" + record.id + "']").find('#tree-column');
+ var required_fld = self.$el.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.$el.find("img[id='parentimg-" + record.id +"']").click(function() {
self.on_click(this.id, record);
});
- self.$element.find("tr[id='treerow-" + record.id + "']").click(function(e) {
+ self.$el.find("tr[id='treerow-" + record.id + "']").click(function(e) {
if (e.shiftKey) {
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 = self.$el.find("tr[id^='treerow-']")[self.row_index-1];
$(frst_click).addClass("ui-selected");
} else {
if (this.rowIndex >=self.row_index) {
for (var i = (self.row_index-1); i < this.rowIndex; i++) {
- scnd_click = self.$element.find("tr[id^='treerow-']")[i];
+ scnd_click = self.$el.find("tr[id^='treerow-']")[i];
if (!$(scnd_click).find('#tree-column').hasClass("oe_export_readonlyfield")) {
$(scnd_click).addClass("ui-selected");
}
}
} else {
for (var i = (self.row_index-1); i >= (this.rowIndex-1); i--) {
- scnd_click = self.$element.find("tr[id^='treerow-']")[i];
+ scnd_click = self.$el.find("tr[id^='treerow-']")[i];
if (!$(scnd_click).find('#tree-column').hasClass("oe_export_readonlyfield")) {
$(scnd_click).addClass("ui-selected");
}
@@ -238,10 +240,10 @@ instance.web.DataExport = instance.web.Dialog.extend({
}
self.row_index = this.rowIndex;
- self.$element.find("tr[id='treerow-" + record.id + "']").keyup(function() {
+ self.$el.find("tr[id='treerow-" + record.id + "']").keyup(function() {
self.row_index = 0;
});
- var o2m_selection = self.$element.find("tr[id='treerow-" + record.id + "']").find('#tree-column');
+ var o2m_selection = self.$el.find("tr[id='treerow-" + record.id + "']").find('#tree-column');
if ($(o2m_selection).hasClass("oe_export_readonlyfield")) {
return false;
}
@@ -252,14 +254,14 @@ instance.web.DataExport = instance.web.Dialog.extend({
$(this).addClass('ui-selected').find('a').focus();
}
} else if (!e.shiftKey) {
- self.$element.find("tr.ui-selected")
+ self.$el.find("tr.ui-selected")
.removeClass("ui-selected").find('a').blur();
$(this).addClass("ui-selected").find('a').focus();
}
return false;
});
- self.$element.find("tr[id='treerow-" + record.id + "']").keydown(function(e) {
+ self.$el.find("tr[id='treerow-" + record.id + "']").keydown(function(e) {
var keyCode = e.keyCode || e.which;
var arrow = {left: 37, up: 38, right: 39, down: 40 };
switch (keyCode) {
@@ -297,19 +299,19 @@ instance.web.DataExport = instance.web.Dialog.extend({
break;
}
});
- self.$element.find("tr[id='treerow-" + record.id + "']").dblclick(function() {
- var $o2m_selection = self.$element.find("tr[id^='treerow-" + record.id + "']").find('#tree-column');
+ self.$el.find("tr[id='treerow-" + record.id + "']").dblclick(function() {
+ var $o2m_selection = self.$el.find("tr[id^='treerow-" + record.id + "']").find('#tree-column');
if (!$o2m_selection.hasClass("oe_export_readonlyfield")) {
self.add_field(record.id, $(this).find("a").attr("string"));
}
});
});
- self.$element.find('#fields_list').mouseover(function(event) {
+ self.$el.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")) {
+ if (!self.$el.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"]);
}
}
@@ -319,7 +321,7 @@ instance.web.DataExport = instance.web.Dialog.extend({
},
showcontent: function(id) {
// show & hide the contents
- var $this = this.$element.find("tr[id='treerow-" + id + "']");
+ var $this = this.$el.find("tr[id='treerow-" + id + "']");
var is_open = $this.hasClass('open');
$this.toggleClass('open');
@@ -329,7 +331,7 @@ instance.web.DataExport = instance.web.Dialog.extend({
} else {
first_child.attr('src', '/web/static/src/img/collapse.gif');
}
- var child_field = this.$element.find("tr[id^='treerow-" + id +"/']");
+ var child_field = this.$el.find("tr[id^='treerow-" + id +"/']");
var child_len = (id.split("/")).length + 1;
for (var i = 0; i < child_field.length; i++) {
var $child = $(child_field[i]);
@@ -345,15 +347,15 @@ instance.web.DataExport = instance.web.Dialog.extend({
}
},
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) {
+ var field_list = this.$el.find('#fields_list');
+ if (this.$el.find("#fields_list option[value='" + field_id + "']")
+ && !this.$el.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() {
+ this.$el.find("#fields_list option").each(function() {
export_field.push($(this).val());
});
if (!export_field.length) {
@@ -363,7 +365,7 @@ instance.web.DataExport = instance.web.Dialog.extend({
},
on_click_export_data: function() {
var self = this;
- var exported_fields = this.$element.find('#fields_list option').map(function () {
+ var exported_fields = this.$el.find('#fields_list option').map(function () {
// DOM property is textContent, but IE8 only knows innerText
return {name: self.records[this.value] || this.value,
label: this.textContent || this.innerText};
@@ -375,7 +377,7 @@ instance.web.DataExport = instance.web.Dialog.extend({
}
exported_fields.unshift({name: 'id', label: 'External ID'});
- var export_format = this.$element.find("#export_format").val();
+ var export_format = this.$el.find("#export_format").val();
instance.web.blockUI();
this.session.get_file({
url: '/web/export/' + export_format,
@@ -385,13 +387,13 @@ instance.web.DataExport = instance.web.Dialog.extend({
ids: this.dataset.ids,
domain: this.dataset.domain,
import_compat: Boolean(
- this.$element.find("#import_compat").val())
+ this.$el.find("#import_compat").val())
})},
complete: instance.web.unblockUI
});
},
close: function() {
- this.$element.remove();
+ this.$el.remove();
this._super();
}
});
diff --git a/addons/web/static/src/js/data_import.js b/addons/web/static/src/js/data_import.js
deleted file mode 100644
index 620878569ce..00000000000
--- a/addons/web/static/src/js/data_import.js
+++ /dev/null
@@ -1,393 +0,0 @@
-openerp.web.data_import = function(instance) {
-var QWeb = instance.web.qweb,
- _t = instance.web._t;
-/**
- * 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);
-}
-
-instance.web.DataImport = instance.web.Dialog.extend({
- template: 'ImportDataView',
- dialog_title: {toString: function () { return _t("Import Data"); }},
- init: function(parent, dataset){
- var self = this;
- this._super(parent, {});
- this.model = parent.model;
- this.fields = [];
- this.all_fields = [];
- this.fields_with_defaults = [];
- 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 &&
- !_.include(self.fields_with_defaults, field.id); })
- .pluck('id')
- .uniq()
- .value();
- convert_fields(self);
- self.all_fields.sort();
- });
- },
- start: function() {
- var self = this;
- this._super();
- this.open({
- buttons: [
- {text: _t("Close"), click: function() { self.destroy(); }},
- {text: _t("Import File"), click: function() { self.do_import(); }, 'class': 'oe_import_dialog_button'}
- ],
- close: function(event, ui) {
- self.destroy();
- }
- });
- 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.delegate('fieldset legend', 'click', function() {
- $(this).parent().toggleClass('oe_closed');
- });
- this.ready.push(new instance.web.DataSet(this, this.model).call(
- 'fields_get', [], function (fields) {
- self.graft_fields(fields);
- self.ready.push(new instance.web.DataSet(self, self.model)
- .default_get(_.pluck(self.fields, 'id')).then(function (fields) {
- _.each(fields, function(val, key) {
- if (val) {
- self.fields_with_defaults.push(key);
- }
- });
- })
- )
- }));
- },
- graft_fields: function (fields, parent, level) {
- parent = parent || this;
- level = level || 0;
-
- var self = this;
- if (level === 0) {
- parent.fields.push({
- id: 'id',
- name: 'id',
- string: _t('External ID'),
- required: false
- });
- }
- _(fields).each(function (field, field_name) {
- // Ignore spec for id field
- // Don't import function fields (function and related)
- if (field_name === 'id') {
- return;
- }
- // Skip if there's no state which could disable @readonly,
- // if a field is ever always readonly we can't import it
- if (field.readonly) {
- // no states at all
- if (_.isEmpty(field.states)) { return; }
- // no state altering @readonly
- if (!_.any(field.states, function (modifiers) {
- return _(modifiers).chain().pluck(0).contains('readonly').value();
- })) { return; }
- }
-
- var f = {
- id: field_name,
- name: field_name,
- string: field.string,
- required: field.required
- };
-
- switch (field.type) {
- case 'many2many':
- case 'many2one':
- // push a copy for the bare many2one field, to allow importing
- // using name_search too - even if we default to exporting the XML ID
- var many2one_field = _.extend({}, f)
- parent.fields.push(many2one_field);
- 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 instance.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) {
- instance.web.dialog(this.$element, 'widget')
- .find('.oe_import_dialog_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['error']) {
- result_node.append(QWeb.render('ImportView.error', {
- 'error': results['error']}));
- this.$element.find('fieldset').removeClass('oe_closed');
- return;
- }
- if (results['success']) {
- if (this.getParent().getParent().active_view == "list") {
- this.getParent().reload_content();
- }
- this.destroy();
- return;
- }
-
- 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
- }));
- this.$element.find('fieldset').addClass('oe_closed');
- }
- this.$element.find('form').removeClass('oe_import_no_result');
-
- this.$element.delegate('.oe_m2o_drop_down_button', 'click', function () {
- $(this).prev('input').focus();
- });
-
- 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 field_name = self.match_column_to_field(header);
- if (field_name) {
- $fields.eq(index).val(field_name);
- }
- });
- self.on_check_field_values();
- });
- },
- /**
- * Returns the name of the field (nested) matching the provided column name
- *
- * @param {String} name column name to look for
- * @param {Array} [fields] fields to look into for the provided name
- * @returns {String|undefined}
- */
- match_column_to_field: function (name, fields) {
- fields = fields || this.fields;
- var f;
- f = _(fields).detect(function (field) {
- return field.name === name
- });
- if (!f) {
- f = _(fields).detect(function (field) {
- // TODO: levenshtein between header and field.string
- return field.string.toLowerCase() === name.toLowerCase();
- });
- }
- if (f) { return f.name; }
-
- // if ``name`` is a path (o2m), we need to recurse through its .fields
- var index = name.indexOf('/');
- if (index === -1) { return undefined; }
- // Get the first path section, try to find the matching field
- var column_name = name.substring(0, index);
- f = _(fields).detect(function (field) {
- // field.name for o2m is $foo/id, so we want to match on id
- return field.id === column_name;
- });
- if (!f) {
- f = _(fields).detect(function (field) {
- return field.string.toLowerCase() === column_name.toLowerCase();
- });
- }
- if (!f) { return undefined; }
-
- // if we found a matching field for the first path section, recurse in
- // its own .fields to try and get the rest of the path matched
- var rest = this.match_column_to_field(
- name.substring(index+1), f.fields);
- if (!rest) { return undefined; }
- return f.id + '/' + rest;
- },
- /**
- * 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;">'+_t("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() {
- var self = this;
- if (!self.required_fields.length) { return true; }
-
- // Resolve field id based on column name, as there may be
- // several ways to provide the value for a given field and
- // thus satisfy the requirement.
- // (e.g. m2o_id or m2o_id/id columns may be provided)
- var resolve_field_id = function(column_name) {
- var f = _.detect(self.fields, function(field) {
- return field.name === column_name;
- });
- if (!f) { return column_name; };
- return f.id;
- };
-
- var selected_fields = _(this.$element.find('.sel_fields').get()).chain()
- .pluck('value')
- .compact()
- .map(resolve_field_id)
- .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">' + _t("*Required Fields are not selected :") + missing_fields + '.</div>');
- return false;
- }
- return true;
- },
- destroy: function() {
- this.$element.remove();
- this._super();
- }
-});
-};
diff --git a/addons/web/static/src/js/formats.js b/addons/web/static/src/js/formats.js
index 6ceb7090e44..3e5e6fecd5e 100644
--- a/addons/web/static/src/js/formats.js
+++ b/addons/web/static/src/js/formats.js
@@ -138,6 +138,9 @@ instance.web.format_value = function (value, descriptor, value_if_empty) {
return value[1];
case 'one2many':
case 'many2many':
+ if (typeof value === 'string') {
+ return value;
+ }
return _.str.sprintf(_t("(%d records)"), value.length);
case 'datetime':
if (typeof(value) == "string")
@@ -271,82 +274,4 @@ instance.web.auto_date_to_str = function(value, type) {
}
};
-/**
- * Formats a provided cell based on its field type. Most of the field types
- * return a correctly formatted value, but some tags and fields are
- * special-cased in their handling:
- *
- * * buttons will return an actual ``<button>`` tag with a bunch of error handling
- *
- * * boolean fields will return a checkbox input, potentially disabled
- *
- * * binary fields will return a link to download the binary data as a file
- *
- * @param {Object} row_data record whose values should be displayed in the cell
- * @param {Object} column column descriptor
- * @param {"button"|"field"} column.tag base control type
- * @param {String} column.type widget type for a field control
- * @param {String} [column.string] button label
- * @param {String} [column.icon] button icon
- * @param {Object} [options]
- * @param {String} [options.value_if_empty=''] what to display if the field's value is ``false``
- * @param {Boolean} [options.process_modifiers=true] should the modifiers be computed ?
- * @param {String} [options.model] current record's model
- * @param {Number} [options.id] current record's id
- *
- */
-instance.web.format_cell = function (row_data, column, options) {
- options = options || {};
- var attrs = {};
- if (options.process_modifiers !== false) {
- attrs = column.modifiers_for(row_data);
- }
- if (attrs.invisible) { return ''; }
-
- if (column.tag === 'button') {
- return _.template('<button type="button" title="<%-title%>" <%=additional_attributes%> >' +
- '<img src="<%-prefix%>/web/static/src/img/icons/<%-icon%>.png" alt="<%-alt%>"/>' +
- '</button>', {
- title: column.string || '',
- additional_attributes: isNaN(row_data["id"].value) && instance.web.BufferedDataSet.virtual_id_regex.test(row_data["id"].value) ?
- 'disabled="disabled" class="oe_list_button_disabled"' : '',
- prefix: instance.connection.prefix,
- icon: column.icon,
- alt: column.string || ''
- });
- }
- if (!row_data[column.id]) {
- return options.value_if_empty === undefined ? '' : options.value_if_empty;
- }
-
- switch (column.widget || column.type) {
- case "boolean":
- return _.str.sprintf('<input type="checkbox" %s disabled="disabled"/>',
- row_data[column.id].value ? 'checked="checked"' : '');
- case "binary":
- var text = _t("Download"),
- download_url = _.str.sprintf('/web/binary/saveas?session_id=%s&model=%s&field=%s&id=%d', instance.connection.session_id, options.model, column.id, options.id);
- if (column.filename) {
- download_url += '&filename_field=' + column.filename;
- if (row_data[column.filename]) {
- text = _.str.sprintf(_t("Download \"%s\""), instance.web.format_value(
- row_data[column.filename].value, {type: 'char'}));
- }
- }
- return _.template('<a href="<%-href%>"><%-text%></a> (%<-size%>)', {
- text: text,
- href: download_url,
- size: row_data[column.id].value
- });
- case 'progressbar':
- return _.template(
- '<progress value="<%-value%>" max="100"><%-value%>%</progress>', {
- value: _.str.sprintf("%.0f", row_data[column.id].value || 0)
- });
- }
-
- return _.escape(instance.web.format_value(
- row_data[column.id].value, column, options.value_if_empty));
-}
-
};
diff --git a/addons/web/static/src/js/search.js b/addons/web/static/src/js/search.js
index 53daa40ae31..71e955e80a6 100644
--- a/addons/web/static/src/js/search.js
+++ b/addons/web/static/src/js/search.js
@@ -126,21 +126,21 @@ my.InputView = instance.web.Widget.extend({
template: 'SearchView.InputView',
start: function () {
var p = this._super.apply(this, arguments);
- this.$element.on('focus', this.proxy('onFocus'));
- this.$element.on('blur', this.proxy('onBlur'));
- this.$element.on('keydown', this.proxy('onKeydown'));
+ this.$el.on('focus', this.proxy('onFocus'));
+ this.$el.on('blur', this.proxy('onBlur'));
+ this.$el.on('keydown', this.proxy('onKeydown'));
return p;
},
onFocus: function () {
this.trigger('focused', this);
},
onBlur: function () {
- this.$element.text('');
+ this.$el.text('');
this.trigger('blurred', this);
},
getSelection: function () {
// get Text node
- var root = this.$element[0].childNodes[0];
+ var root = this.$el[0].childNodes[0];
if (!root || !root.textContent) {
// if input does not have a child node, or the child node is an
// empty string, then the selection can only be (0, 0)
@@ -202,7 +202,7 @@ my.InputView = instance.web.Widget.extend({
break;
case $.ui.keyCode.RIGHT:
sel = this.getSelection();
- var len = this.$element.text().length;
+ var len = this.$el.text().length;
if (sel.start !== len || sel.start !== sel.end) {
e.stopPropagation();
}
@@ -223,17 +223,17 @@ my.FacetView = instance.web.Widget.extend({
},
start: function () {
var self = this;
- this.$element.on('focus', function () { self.trigger('focused', self); });
- this.$element.on('blur', function () { self.trigger('blurred', self); });
- this.$element.on('click', function (e) {
+ this.$el.on('focus', function () { self.trigger('focused', self); });
+ this.$el.on('blur', function () { self.trigger('blurred', self); });
+ this.$el.on('click', function (e) {
if ($(e.target).is('.oe_facet_remove')) {
self.model.destroy();
return false;
}
- self.$element.focus();
+ self.$el.focus();
e.stopPropagation();
});
- this.$element.on('keydown', function (e) {
+ this.$el.on('keydown', function (e) {
var keys = $.ui.keyCode;
switch (e.which) {
case keys.BACKSPACE:
@@ -242,7 +242,7 @@ my.FacetView = instance.web.Widget.extend({
return false;
}
});
- var $e = self.$element.find('> span:last-child');
+ var $e = self.$el.find('> span:last-child');
var q = $.when(this._super());
return q.pipe(function () {
var values = self.model.values.map(function (value) {
@@ -253,7 +253,7 @@ my.FacetView = instance.web.Widget.extend({
});
},
model_changed: function () {
- this.$element.text(this.$element.text() + '*');
+ this.$el.text(this.$el.text() + '*');
}
});
my.FacetValueView = instance.web.Widget.extend({
@@ -268,7 +268,7 @@ my.FacetValueView = instance.web.Widget.extend({
this._super();
},
model_changed: function () {
- this.$element.text(this.$element.text() + '*');
+ this.$el.text(this.$el.text() + '*');
}
});
@@ -313,7 +313,7 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
.on('add change reset remove', this.proxy('renderFacets'));
if (this.hidden) {
- this.$element.hide();
+ this.$el.hide();
}
if (this.headless) {
this.ready.resolve();
@@ -331,12 +331,12 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
}
// Launch a search on clicking the oe_searchview_search button
- this.$element.on('click', 'button.oe_searchview_search', function (e) {
+ this.$el.on('click', 'button.oe_searchview_search', function (e) {
e.stopImmediatePropagation();
self.do_search();
});
- this.$element.on('keydown',
+ this.$el.on('keydown',
'.oe_searchview_input, .oe_searchview_facet', function (e) {
switch(e.which) {
case $.ui.keyCode.LEFT:
@@ -350,31 +350,31 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
}
});
- this.$element.on('click', '.oe_searchview_clear', function (e) {
+ this.$el.on('click', '.oe_searchview_clear', function (e) {
e.stopImmediatePropagation();
self.query.reset();
});
- this.$element.on('click', '.oe_searchview_unfold_drawer', function (e) {
+ this.$el.on('click', '.oe_searchview_unfold_drawer', function (e) {
e.stopImmediatePropagation();
- self.$element.toggleClass('oe_searchview_open_drawer');
+ self.$el.toggleClass('oe_searchview_open_drawer');
});
instance.web.bus.on('click', this, function(ev) {
if ($(ev.target).parents('.oe_searchview').length === 0) {
- self.$element.removeClass('oe_searchview_open_drawer');
+ self.$el.removeClass('oe_searchview_open_drawer');
}
});
// Focus last input if the view itself is clicked (empty section of
// facets element)
- this.$element.on('click', function (e) {
- if (e.target === self.$element.find('.oe_searchview_facets')[0]) {
- self.$element.find('.oe_searchview_input:last').focus();
+ this.$el.on('click', function (e) {
+ if (e.target === self.$el.find('.oe_searchview_facets')[0]) {
+ self.$el.find('.oe_searchview_input:last').focus();
}
});
// when the completion list opens/refreshes, automatically select the
// first completion item so if the user just hits [RETURN] or [TAB] it
// automatically selects it
- this.$element.on('autocompleteopen', function () {
- var menu = self.$element.data('autocomplete').menu;
+ this.$el.on('autocompleteopen', function () {
+ var menu = self.$el.data('autocomplete').menu;
menu.activate(
$.Event({ type: "mouseenter" }),
menu.element.children().first());
@@ -383,15 +383,15 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
return $.when(p, this.ready);
},
show: function () {
- this.$element.show();
+ this.$el.show();
},
hide: function () {
- this.$element.hide();
+ this.$el.hide();
},
subviewForRoot: function (subview_root) {
return _(this.input_subviews).detect(function (subview) {
- return subview.$element[0] === subview_root;
+ return subview.$el[0] === subview_root;
});
},
siblingSubview: function (subview, direction, wrap_around) {
@@ -406,12 +406,12 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
focusPreceding: function (subview_root) {
return this.siblingSubview(
this.subviewForRoot(subview_root), -1, true)
- .$element.focus();
+ .$el.focus();
},
focusFollowing: function (subview_root) {
return this.siblingSubview(
this.subviewForRoot(subview_root), +1, true)
- .$element.focus();
+ .$el.focus();
},
/**
@@ -430,19 +430,19 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
// autocomplete only correctly handles being initialized on the actual
// editable element (and only an element with a @value in 1.8 e.g.
- // input or textarea), cheat by setting val() on $element
- this.$element.on('keydown', function () {
+ // input or textarea), cheat by setting val() on $el
+ this.$el.on('keydown', function () {
// keydown is triggered *before* the element's value is set, so
// delay this. Pray that setTimeout are executed in FIFO (if they
// have the same delay) as autocomplete uses the exact same trick.
// FIXME: brittle as fuck
setTimeout(function () {
- self.$element.val(self.currentInputValue());
+ self.$el.val(self.currentInputValue());
}, 0);
});
- this.$element.autocomplete({
+ this.$el.autocomplete({
source: this.proxy('complete_global_search'),
select: this.proxy('select_completion'),
focus: function (e) { e.preventDefault(); },
@@ -479,7 +479,7 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
* div[contenteditable].oe_searchview_input)
*/
currentInputValue: function () {
- return this.$element.find('div.oe_searchview_input:focus').text();
+ return this.$el.find('div.oe_searchview_input:focus').text();
},
/**
* Provide auto-completion result for req.term (an array to `resp`)
@@ -509,22 +509,22 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
var input_index = _(this.input_subviews).indexOf(
this.subviewForRoot(
- this.$element.find('div.oe_searchview_input:focus')[0]));
+ this.$el.find('div.oe_searchview_input:focus')[0]));
this.query.add(ui.item.facet, {at: input_index / 2});
},
childFocused: function () {
- this.$element.addClass('oe_focused');
+ this.$el.addClass('oe_focused');
},
childBlurred: function () {
- var val = this.$element.val();
- this.$element.val('');
- var complete = this.$element.data('autocomplete');
+ var val = this.$el.val();
+ this.$el.val('');
+ var complete = this.$el.data('autocomplete');
if ((val && complete.term === undefined) || complete.previous !== undefined) {
throw new Error("new jquery.ui version altering implementation" +
" details relied on");
}
delete complete.term;
- this.$element.removeClass('oe_focused')
+ this.$el.removeClass('oe_focused')
.trigger('blur');
},
/**
@@ -538,7 +538,7 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
// _2: undefined if event=change, otherwise model
var self = this;
var started = [];
- var $e = this.$element.find('div.oe_searchview_facets');
+ var $e = this.$el.find('div.oe_searchview_facets');
_.invoke(this.input_subviews, 'destroy');
this.input_subviews = [];
@@ -569,7 +569,7 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
input_to_focus = self.input_subviews[(options.at + 1) * 2];
}
- input_to_focus.$element.focus();
+ input_to_focus.$el.focus();
});
},
@@ -635,6 +635,16 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
return null;
}
},
+
+ add_common_inputs: function() {
+ // add Filters to this.inputs, need view.controls filled
+ (new instance.web.search.Filters(this));
+ // add custom filters to this.inputs
+ (new instance.web.search.CustomFilters(this));
+ // add Advanced to this.inputs
+ (new instance.web.search.Advanced(this));
+ },
+
on_loaded: function(data) {
var self = this;
this.fields_view = data.fields_view;
@@ -649,20 +659,13 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
data.fields_view['arch'].children,
data.fields_view.fields);
- // add Filters to this.inputs, need view.controls filled
- (new instance.web.search.Filters(this));
- // add custom filters to this.inputs
- (new instance.web.search.CustomFilters(this));
- // add Advanced to this.inputs
- (new instance.web.search.Advanced(this));
+ this.add_common_inputs();
// build drawer
var drawer_started = $.when.apply(
null, _(this.select_for_drawer()).invoke(
- 'appendTo', this.$element.find('.oe_searchview_drawer')));
+ 'appendTo', this.$el.find('.oe_searchview_drawer')));
- new instance.web.search.AddToReporting(this).appendTo($('.oe_searchview_drawer', this.$element));
-
// load defaults
var defaults_fetched = $.when.apply(null, _(this.inputs).invoke(
'facet_for_defaults', this.defaults)).then(function () {
@@ -677,15 +680,12 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
*/
on_filters_management: function(e) {
var self = this;
- var select = this.$element.find(".oe_search-view-filters-management");
+ var select = this.$el.find(".oe_search-view-filters-management");
var val = select.val();
switch(val) {
case 'advanced_filter':
this.extended_search.on_activate();
break;
- case 'add_to_dashboard':
- this.on_add_to_dashboard();
- break;
case '':
this.do_clear();
}
@@ -989,7 +989,7 @@ instance.web.search.FilterGroup = instance.web.search.Input.extend(/** @lends in
this.view.query.on('add remove change reset', this.proxy('search_change'));
},
start: function () {
- this.$element.on('click', 'li', this.proxy('toggle_filter'));
+ this.$el.on('click', 'li', this.proxy('toggle_filter'));
return $.when(null);
},
/**
@@ -998,7 +998,7 @@ instance.web.search.FilterGroup = instance.web.search.Input.extend(/** @lends in
*/
search_change: function () {
var self = this;
- var $filters = this.$element.find('> li').removeClass('oe_selected');
+ var $filters = this.$el.find('> li').removeClass('oe_selected');
var facet = this.view.query.find(_.bind(this.match_facet, this));
if (!facet) { return; }
facet.values.each(function (v) {
@@ -1299,17 +1299,17 @@ instance.web.search.CharField = instance.web.search.Field.extend( /** @lends ins
});
instance.web.search.NumberField = instance.web.search.Field.extend(/** @lends instance.web.search.NumberField# */{
value_from: function () {
- if (!this.$element.val()) {
+ if (!this.$el.val()) {
return null;
}
- var val = this.parse(this.$element.val()),
- check = Number(this.$element.val());
+ var val = this.parse(this.$el.val()),
+ check = Number(this.$el.val());
if (isNaN(val) || val !== check) {
- this.$element.addClass('error');
+ this.$el.addClass('error');
throw new instance.web.search.Invalid(
- this.attrs.name, this.$element.val(), this.error_message);
+ this.attrs.name, this.$el.val(), this.error_message);
}
- this.$element.removeClass('error');
+ this.$el.removeClass('error');
return val;
}
});
@@ -1493,16 +1493,13 @@ instance.web.search.ManyToOneField = instance.web.search.CharField.extend({
if (value.length === 2 && _.isString(value[1])) {
return $.when(facet_from(this, value));
}
- if (value.length > 1) {
- // more than one search_default m2o id? Should we OR them?
- throw new Error(
- _("M2O search fields do not currently handle multiple default values"));
- }
+ assert(value.length <= 1,
+ _t("M2O search fields do not currently handle multiple default values"));
// there are many cases of {search_default_$m2ofield: [id]}, need
// to handle this as if it were a single value.
value = value[0];
}
- return this.model.call('name_get', [value], {}).pipe(function (names) {
+ return this.model.call('name_get', [value]).pipe(function (names) {
if (_(names).isEmpty()) { return null; }
return facet_from(self, names[0]);
})
@@ -1542,9 +1539,9 @@ instance.web.search.CustomFilters = instance.web.search.Input.extend({
self.clear_selection();
})
.on('reset', this.proxy('clear_selection'));
- this.$element.on('submit', 'form', this.proxy('save_current'));
- this.$element.on('click', 'h4', function () {
- self.$element.toggleClass('oe_opened');
+ this.$el.on('submit', 'form', this.proxy('save_current'));
+ this.$el.on('click', 'h4', function () {
+ self.$el.toggleClass('oe_opened');
});
// FIXME: local eval of domain and context to get rid of special endpoint
return this.rpc('/web/searchview/get_filters', {
@@ -1552,7 +1549,7 @@ instance.web.search.CustomFilters = instance.web.search.Input.extend({
}).pipe(this.proxy('set_filters'));
},
clear_selection: function () {
- this.$element.find('li.oe_selected').removeClass('oe_selected');
+ this.$el.find('li.oe_selected').removeClass('oe_selected');
},
append_filter: function (filter) {
var self = this;
@@ -1564,7 +1561,7 @@ instance.web.search.CustomFilters = instance.web.search.Input.extend({
} else {
var id = filter.id;
$filter = this.filters[key] = $('<li></li>')
- .appendTo(this.$element.find('.oe_searchview_custom_list'))
+ .appendTo(this.$el.find('.oe_searchview_custom_list'))
.addClass(filter.user_id ? 'oe_searchview_custom_private'
: 'oe_searchview_custom_public')
.text(filter.name);
@@ -1581,7 +1578,7 @@ instance.web.search.CustomFilters = instance.web.search.Input.extend({
$filter.unbind('click').click(function () {
self.view.query.reset([{
- category: _("Custom Filter"),
+ category: _t("Custom Filter"),
icon: 'M',
field: {
get_context: function () { return filter.context; },
@@ -1599,8 +1596,8 @@ instance.web.search.CustomFilters = instance.web.search.Input.extend({
},
save_current: function () {
var self = this;
- var $name = this.$element.find('input:first');
- var private_filter = !this.$element.find('input:last').prop('checked');
+ var $name = this.$el.find('input:first');
+ var private_filter = !this.$el.find('input:last').prop('checked');
var search = this.view.build_search_data();
this.rpc('/web/session/eval_domain_and_context', {
@@ -1613,7 +1610,7 @@ instance.web.search.CustomFilters = instance.web.search.Input.extend({
}
var filter = {
name: $name.val(),
- user_id: private_filter ? instance.connection.uid : false,
+ user_id: private_filter ? instance.session.uid : false,
model_id: self.view.model,
context: results.context,
domain: results.domain
@@ -1622,7 +1619,7 @@ instance.web.search.CustomFilters = instance.web.search.Input.extend({
return self.model.call('create_or_replace', [filter]).then(function (id) {
filter.id = id;
self.append_filter(filter);
- self.$element
+ self.$el
.removeClass('oe_opened')
.find('form')[0].reset();
});
@@ -1667,8 +1664,8 @@ instance.web.search.Filters = instance.web.search.Input.extend({
}
return $.when(
- this.render_column(col1, $('<div>').appendTo(this.$element)),
- this.render_column(col2, $('<div>').appendTo(this.$element)));
+ this.render_column(col1, $('<div>').appendTo(this.$el)),
+ this.render_column(col2, $('<div>').appendTo(this.$el)));
},
render_column: function (column, $el) {
return $.when.apply(null, _(column).map(function (group) {
@@ -1678,84 +1675,16 @@ instance.web.search.Filters = instance.web.search.Input.extend({
}));
}
});
-instance.web.search.AddToReporting = instance.web.Widget.extend({
- template: 'SearchView.addtoreporting',
- _in_drawer: true,
- start: function () {
- var self = this;
- this.$element
- .on('click', 'h4', this.proxy('show_option'))
- .on('submit', 'form', function (e) {
- e.preventDefault();
- self.add_dashboard();
- });
- return this.load_data().then(this.proxy("render_data"));
- },
- load_data:function(){
- if (!instance.webclient) { return $.Deferred().reject(); }
- var dashboard_menu = instance.webclient.menu.data.data.children;
- return new instance.web.Model('ir.model.data')
- .query(['res_id'])
- .filter([['name','=','menu_reporting_dashboard']])
- .first().pipe(function (result) {
- var menu = _(dashboard_menu).chain()
- .pluck('children')
- .flatten(true)
- .find(function (child) { return child.id === result.res_id; })
- .value();
- return menu ? menu.children : [];
- });
- },
- render_data: function(dashboard_choices){
- var selection = instance.web.qweb.render(
- "SearchView.addtoreporting.selection", {
- selections: dashboard_choices});
- this.$("input").before(selection)
- },
- add_dashboard:function(){
- var self = this;
- var getParent = this.getParent();
- var view_parent = this.getParent().getParent();
- if (! view_parent.action || ! this.$element.find("select").val())
- return this.do_warn("Can't find dashboard action");
- var data = getParent.build_search_data();
- var context = new instance.web.CompoundContext(getParent.dataset.get_context() || []);
- var domain = new instance.web.CompoundDomain(getParent.dataset.get_domain() || []);
- _.each(data.contexts, context.add, context);
- _.each(data.domains, domain.add, domain);
- this.rpc('/web/searchview/add_to_dashboard', {
- menu_id: this.$element.find("select").val(),
- action_id: view_parent.action.id,
- context_to_save: context,
- domain: domain,
- view_mode: view_parent.active_view,
- name: this.$element.find("input").val()
- }, function(r) {
- if (r === false) {
- self.do_warn("Could not add filter to dashboard");
- } else {
- self.$element.toggleClass('oe_opened');
- self.do_notify("Filter added to dashboard", '');
- }
- });
- },
- show_option:function(){
- this.$element.toggleClass('oe_opened');
- if (! this.$element.hasClass('oe_opened'))
- return;
- this.$("input").val(this.getParent().fields_view.name || "" );
- }
-});
instance.web.search.Advanced = instance.web.search.Input.extend({
template: 'SearchView.advanced',
_in_drawer: true,
start: function () {
var self = this;
- this.$element
+ this.$el
.on('keypress keydown keyup', function (e) { e.stopPropagation(); })
.on('click', 'h4', function () {
- self.$element.toggleClass('oe_opened');
+ self.$el.toggleClass('oe_opened');
}).on('click', 'button.oe_add_condition', function () {
self.append_proposition();
}).on('submit', 'form', function (e) {
@@ -1774,7 +1703,7 @@ instance.web.search.Advanced = instance.web.search.Input.extend({
},
append_proposition: function () {
return (new instance.web.search.ExtendedSearchProposition(this, this.fields))
- .appendTo(this.$element.find('ul'));
+ .appendTo(this.$el.find('ul'));
},
commit_search: function () {
var self = this;
@@ -1801,15 +1730,15 @@ instance.web.search.Advanced = instance.web.search.Input.extend({
// add new empty proposition
this.append_proposition();
// TODO: API on searchview
- this.view.$element.removeClass('oe_searchview_open_drawer');
+ this.view.$el.removeClass('oe_searchview_open_drawer');
}
});
-instance.web.search.ExtendedSearchProposition = instance.web.OldWidget.extend(/** @lends instance.web.search.ExtendedSearchProposition# */{
+instance.web.search.ExtendedSearchProposition = instance.web.Widget.extend(/** @lends instance.web.search.ExtendedSearchProposition# */{
template: 'SearchView.extended_search.proposition',
/**
* @constructs instance.web.search.ExtendedSearchProposition
- * @extends instance.web.OldWidget
+ * @extends instance.web.Widget
*
* @param parent
* @param fields
@@ -1825,16 +1754,16 @@ instance.web.search.ExtendedSearchProposition = instance.web.OldWidget.extend(/*
},
start: function () {
var _this = this;
- this.$element.find(".searchview_extended_prop_field").change(function() {
+ this.$el.find(".searchview_extended_prop_field").change(function() {
_this.changed();
});
- this.$element.find('.searchview_extended_delete_prop').click(function () {
+ this.$el.find('.searchview_extended_delete_prop').click(function () {
_this.destroy();
});
this.changed();
},
changed: function() {
- var nval = this.$element.find(".searchview_extended_prop_field").val();
+ var nval = this.$el.find(".searchview_extended_prop_field").val();
if(this.attrs.selected == null || nval != this.attrs.selected.name) {
this.select_field(_.detect(this.fields, function(x) {return x.name == nval;}));
}
@@ -1849,7 +1778,7 @@ instance.web.search.ExtendedSearchProposition = instance.web.OldWidget.extend(/*
if(this.attrs.selected != null) {
this.value.destroy();
this.value = null;
- this.$element.find('.searchview_extended_prop_op').html('');
+ this.$el.find('.searchview_extended_prop_op').html('');
}
this.attrs.selected = field;
if(field == null) {
@@ -1865,9 +1794,9 @@ instance.web.search.ExtendedSearchProposition = instance.web.OldWidget.extend(/*
_.each(this.value.operators, function(operator) {
$('<option>', {value: operator.value})
.text(String(operator.text))
- .appendTo(self.$element.find('.searchview_extended_prop_op'));
+ .appendTo(self.$el.find('.searchview_extended_prop_op'));
});
- var $value_loc = this.$element.find('.searchview_extended_prop_value').empty();
+ var $value_loc = this.$el.find('.searchview_extended_prop_value').empty();
this.value.appendTo($value_loc);
},
@@ -1875,7 +1804,7 @@ instance.web.search.ExtendedSearchProposition = instance.web.OldWidget.extend(/*
if ( this.attrs.selected == null)
return null;
var field = this.attrs.selected;
- var op = this.$element.find('.searchview_extended_prop_op')[0];
+ var op = this.$el.find('.searchview_extended_prop_op')[0];
var operator = op.options[op.selectedIndex];
return {
label: _.str.sprintf(_t('%(field)s %(operator)s "%(value)s"'), {
@@ -1919,7 +1848,7 @@ instance.web.search.ExtendedSearchProposition.Char = instance.web.search.Extende
{value: "!=", text: _lt("is not equal to")}
],
get_value: function() {
- return this.$element.val();
+ return this.$el.val();
}
});
instance.web.search.ExtendedSearchProposition.DateTime = instance.web.search.ExtendedSearchProposition.Field.extend({
@@ -1943,7 +1872,7 @@ instance.web.search.ExtendedSearchProposition.DateTime = instance.web.search.Ext
start: function() {
var ready = this._super();
this.datewidget = new (this.widget())(this);
- this.datewidget.appendTo(this.$element);
+ this.datewidget.appendTo(this.$el);
return ready;
}
});
@@ -1961,11 +1890,11 @@ instance.web.search.ExtendedSearchProposition.Integer = instance.web.search.Exte
{value: "<=", text: _lt("less or equal than")}
],
toString: function () {
- return this.$element.val();
+ return this.$el.val();
},
get_value: function() {
try {
- return instance.web.parse_value(this.$element.val(), {'widget': 'integer'});
+ return instance.web.parse_value(this.$el.val(), {'widget': 'integer'});
} catch (e) {
return "";
}
@@ -1985,11 +1914,11 @@ instance.web.search.ExtendedSearchProposition.Float = instance.web.search.Extend
{value: "<=", text: _lt("less or equal than")}
],
toString: function () {
- return this.$element.val();
+ return this.$el.val();
},
get_value: function() {
try {
- return instance.web.parse_value(this.$element.val(), {'widget': 'float'});
+ return instance.web.parse_value(this.$el.val(), {'widget': 'float'});
} catch (e) {
return "";
}
@@ -2002,12 +1931,12 @@ instance.web.search.ExtendedSearchProposition.Selection = instance.web.search.Ex
{value: "!=", text: _lt("is not")}
],
toString: function () {
- var select = this.$element[0];
+ var select = this.$el[0];
var option = select.options[select.selectedIndex];
return option.label || option.text;
},
get_value: function() {
- return this.$element.val();
+ return this.$el.val();
}
});
instance.web.search.ExtendedSearchProposition.Boolean = instance.web.search.ExtendedSearchProposition.Field.extend({
diff --git a/addons/web/static/src/js/test_support.js b/addons/web/static/src/js/test_support.js
index d46366326b7..8e1317ba2b0 100644
--- a/addons/web/static/src/js/test_support.js
+++ b/addons/web/static/src/js/test_support.js
@@ -1,18 +1,17 @@
openerp.test_support = {
- setup_connection: function (connection) {
+ setup_session: function (session) {
var origin = location.protocol+"//"+location.host;
- _.extend(connection, {
+ _.extend(session, {
origin: origin,
prefix: origin,
server: origin, // keep chs happy
//openerp.web.qweb.default_dict['_s'] = this.origin;
- rpc_function: connection.rpc_json,
+ rpc_function: session.rpc_json,
session_id: false,
uid: false,
username: false,
user_context: {},
db: false,
- openerp_entreprise: false,
// this.module_list = openerp._modules.slice();
// this.module_loaded = {};
// _(this.module_list).each(function (mod) {
@@ -22,7 +21,7 @@ openerp.test_support = {
shortcuts: [],
active_id: null
});
- return connection.session_reload();
+ return session.session_reload();
},
module: function (title, tested_core, nonliterals) {
var conf = QUnit.config.openerp = {};
@@ -31,10 +30,10 @@ openerp.test_support = {
QUnit.stop();
var oe = conf.openerp = window.openerp.init();
window.openerp.web[tested_core](oe);
- var done = openerp.test_support.setup_connection(oe.connection);
+ var done = openerp.test_support.setup_session(oe.session);
if (nonliterals) {
done = done.pipe(function () {
- return oe.connection.rpc('/tests/add_nonliterals', {
+ return oe.session.rpc('/tests/add_nonliterals', {
domains: nonliterals.domains || [],
contexts: nonliterals.contexts || []
}).then(function (r) {
diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js
index c9914c29dbc..4d69e07489a 100644
--- a/addons/web/static/src/js/view_form.js
+++ b/addons/web/static/src/js/view_form.js
@@ -13,23 +13,52 @@ instance.web.form = {};
* Properties:
* - display_invalid_fields : if true, all fields where is_valid() return true should
* be displayed as invalid.
+ * - actual_mode : the current mode of the field manager. Can be "view", "edit" or "create".
* Events:
* - view_content_has_changed : when the values of the fields have changed. When
* this event is triggered all fields should reprocess their modifiers.
+ * - field_changed:<field_name> : when the value of a field change, an event is triggered
+ * named "field_changed:<field_name>" with <field_name> replaced by the name of the field.
+ * This event is not related to the on_change mechanism of OpenERP and is always called
+ * when the value of a field is setted or changed. This event is only triggered when the
+ * value of the field is syntactically valid, but it can be triggered when the value
+ * is sematically invalid (ie, when a required field is false). It is possible that an event
+ * about a precise field is never triggered even if that field exists in the view, in that
+ * case the value of the field is assumed to be false.
*/
instance.web.form.FieldManagerMixin = {
/**
* Must return the asked field as in fields_get.
*/
- get_field: function(field_name) {},
+ get_field_desc: function(field_name) {},
/**
- * Called by the field when the translate button is clicked.
+ * Returns the current value of a field present in the view. See the get_value() method
+ * method in FieldInterface for further information.
*/
- open_translate_dialog: function(field) {},
+ get_field_value: function(field_name) {},
/**
- * Returns true when the view is in create mode.
- */
- is_create_mode: function() {},
+ Gives new values for the fields contained in the view. The new values could not be setted
+ right after the call to this method. Setting new values can trigger on_changes.
+
+ @param (dict) values A dictonnary with key = field name and value = new value.
+ @return (Deferred) Is resolved after all the values are setted.
+ */
+ set_values: function(values) {},
+ /**
+ Computes an OpenERP domain.
+
+ @param (list) expression An OpenERP domain.
+ @return (boolean) The computed value of the domain.
+ */
+ compute_domain: function(expression) {},
+ /**
+ Builds an evaluation context for the resolution of the fields' contexts. Please note
+ the field are only supposed to use this context to evualuate their own, they should not
+ extend it.
+
+ @return (CompoundContext) An OpenERP context.
+ */
+ build_eval_context: function() {},
};
instance.web.views.add('form', 'instance.web.FormView');
@@ -74,6 +103,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
this.default_focus_button = null;
this.fields_registry = instance.web.form.widgets;
this.tags_registry = instance.web.form.tags;
+ this.widgets_registry = instance.web.form.custom_widgets;
this.has_been_loaded = $.Deferred();
this.translatable_fields = [];
_.defaults(this.options, {
@@ -96,14 +126,19 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
self.on("change:actual_mode", self, self.init_pager);
self.init_pager();
});
+ instance.web.bus.on('clear_uncommitted_changes', this, function(e) {
+ if (!this.can_be_discarded()) {
+ e.preventDefault();
+ }
+ });
},
destroy: function() {
_.each(this.get_widgets(), function(w) {
w.off('focused blurred');
w.destroy();
});
- if (this.$element) {
- this.$element.off('.formBlur');
+ if (this.$el) {
+ this.$el.off('.formBlur');
}
this._super();
},
@@ -120,13 +155,14 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
this.rendering_engine.set_fields_registry(this.fields_registry);
this.rendering_engine.set_tags_registry(this.tags_registry);
+ this.rendering_engine.set_widgets_registry(this.widgets_registry);
if (!this.extract_qweb_template(data)) {
this.rendering_engine.set_fields_view(data);
- var $dest = this.$element.hasClass("oe_form_container") ? this.$element : this.$element.find('.oe_form_container');
+ var $dest = this.$el.hasClass("oe_form_container") ? this.$el : this.$el.find('.oe_form_container');
this.rendering_engine.render_to($dest);
}
- this.$element.on('mousedown.formBlur', function () {
+ this.$el.on('mousedown.formBlur', function () {
self.__clicked_inside = true;
});
@@ -134,36 +170,47 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
if (this.options.$buttons) {
this.$buttons.appendTo(this.options.$buttons);
} else {
- this.$element.find('.oe_form_buttons').replaceWith(this.$buttons);
+ this.$el.find('.oe_form_buttons').replaceWith(this.$buttons);
}
this.$buttons.on('click','.oe_form_button_create',this.on_button_create);
this.$buttons.on('click','.oe_form_button_edit',this.on_button_edit);
this.$buttons.on('click','.oe_form_button_save',this.on_button_save);
this.$buttons.on('click','.oe_form_button_cancel',this.on_button_cancel);
- this.$sidebar = this.options.$sidebar || this.$element.find('.oe_form_sidebar');
+ this.$sidebar = this.options.$sidebar || this.$el.find('.oe_form_sidebar');
if (!this.sidebar && this.options.$sidebar) {
this.sidebar = new instance.web.Sidebar(this);
this.sidebar.appendTo(this.$sidebar);
- if(this.fields_view.toolbar) {
+ if (this.fields_view.toolbar) {
this.sidebar.add_toolbar(this.fields_view.toolbar);
}
- this.sidebar.add_items('other', [
- { label: _t('Delete'), callback: self.on_button_delete },
- { label: _t('Duplicate'), callback: self.on_button_duplicate },
+ this.sidebar.add_items('other', _.compact([
+ self.is_action_enabled('delete') && { label: _t('Delete'), callback: self.on_button_delete },
+ self.is_action_enabled('create') && { label: _t('Duplicate'), callback: self.on_button_duplicate },
{ label: _t('Set Default'), callback: function (item) { self.open_defaults_dialog(); } }
- ]);
+ ]));
}
+ this.has_been_loaded.resolve();
+
// Add bounce effect on button 'Edit' when click on readonly page view.
- this.$element.find(".oe_form_field, .oe_form_group_cell").on('click', function (e) {
+ this.$el.find(".oe_form_group_row,.oe_form_field,label").on('click', function (e) {
if(self.get("actual_mode") == "view") {
var $button = self.options.$buttons.find(".oe_form_button_edit");
- $button.wrap('<div>').css('margin-right','4px').addClass('oe_left oe_bounce');
+ $button.effect('bounce', {distance: 18, times: 5}, 150);
+ e.stopPropagation();
+ instance.web.bus.trigger('click', e);
}
});
+ //bounce effect on red button when click on statusbar.
+ this.$el.find(".oe_form_field_status:not(.oe_form_status_clickable)").on('click', function (e) {
+ if((self.get("actual_mode") == "view")) {
+ var $button = self.$el.find(".oe_highlight:not(.oe_form_invisible)").css({'float':'left','clear':'none'});
+ $button.effect('bounce', {distance:18, times: 5}, 150);
+ e.stopPropagation();
+ }
+ });
- this.has_been_loaded.resolve();
return $.when();
},
extract_qweb_template: function(fvg) {
@@ -214,7 +261,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
this.default_focus_field = null;
this.default_focus_button = null;
this.translatable_fields = [];
- this.$element.find('.oe_form_container').empty();
+ this.$el.find('.oe_form_container').empty();
},
widgetFocused: function() {
@@ -254,7 +301,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
/**
*
* @param {Object} [options]
- * @param {Boolean} [editable=false] whether the form should be switched to edition mode. A value of ``false`` will keep the current mode.
+ * @param {Boolean} [mode=undefined] If specified, switch the form to specified mode. Can be "edit" or "view".
* @param {Boolean} [reload=true] whether the form should reload its content on show, or use the currently loaded record
* @return {$.Deferred}
*/
@@ -262,19 +309,16 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
var self = this;
options = options || {};
if (this.sidebar) {
- this.sidebar.$element.show();
+ this.sidebar.$el.show();
}
if (this.$buttons) {
this.$buttons.show();
}
- if (this.$pager) {
- this.$pager.show();
- }
- this.$element.show().css({
+ this.$el.show().css({
opacity: '0',
filter: 'alpha(opacity = 0)'
});
- this.$element.add(this.$buttons).removeClass('oe_form_dirty');
+ this.$el.add(this.$buttons).removeClass('oe_form_dirty');
var shown = this.has_been_loaded;
if (options.reload !== false) {
@@ -291,10 +335,8 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
});
}
return shown.pipe(function() {
- if (options.editable) {
- self.to_edit_mode();
- }
- self.$element.css({
+ self._actualize_mode(options.mode || self.options.initial_mode);
+ self.$el.css({
opacity: '1',
filter: 'alpha(opacity = 100)'
});
@@ -302,7 +344,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
},
do_hide: function () {
if (this.sidebar) {
- this.sidebar.$element.hide();
+ this.sidebar.$el.hide();
}
if (this.$buttons) {
this.$buttons.hide();
@@ -321,18 +363,20 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
}
this.datarecord = record;
this._actualize_mode();
- this.set({ 'title' : record.id ? record.display_name : "New record" });
+ this.set({ 'title' : record.id ? record.display_name : "New" });
if (this.qweb) {
this.kill_current_form();
this.rendering_engine.set_fields_view(this.get_fvg_from_qweb(record));
- var $dest = this.$element.hasClass("oe_form_container") ? this.$element : this.$element.find('.oe_form_container');
+ var $dest = this.$el.hasClass("oe_form_container") ? this.$el : this.$el.find('.oe_form_container');
this.rendering_engine.render_to($dest);
}
_(this.fields).each(function (field, f) {
field._dirty_flag = false;
+ field._inhibit_on_change_flag = true;
var result = field.set_value(self.datarecord[f] || false);
+ field._inhibit_on_change_flag = false;
set_values.push(result);
});
return $.when.apply(null, set_values).pipe(function() {
@@ -355,8 +399,10 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
}
if (record.id) {
self.do_push_state({id:record.id});
+ } else {
+ self.do_push_state({});
}
- self.$element.add(self.$buttons).removeClass('oe_form_dirty');
+ self.$el.add(self.$buttons).removeClass('oe_form_dirty');
self.autofocus();
});
},
@@ -378,7 +424,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
this.trigger("view_content_has_changed");
},
do_notify_change: function() {
- this.$element.add(this.$buttons).addClass('oe_form_dirty');
+ this.$el.add(this.$buttons).addClass('oe_form_dirty');
},
on_pager_action: function(action) {
if (this.can_be_discarded()) {
@@ -405,11 +451,11 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
this.$pager.remove();
if (this.get("actual_mode") === "create")
return;
- this.$pager = $(QWeb.render("FormView.pager", {'widget':self}));
+ this.$pager = $(QWeb.render("FormView.pager", {'widget':self})).hide();
if (this.options.$pager) {
this.$pager.appendTo(this.options.$pager);
} else {
- this.$element.find('.oe_form_pager').replaceWith(this.$pager);
+ this.$el.find('.oe_form_pager').replaceWith(this.$pager);
}
this.$pager.on('click','a[data-pager-action]',function() {
var action = $(this).data('pager-action');
@@ -418,10 +464,12 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
this.do_update_pager();
},
do_update_pager: function(hide_index) {
- var index = hide_index ? '-' : this.dataset.index + 1;
- this.$pager.find('button').prop('disabled', this.dataset.ids.length < 2).end()
- .find('span.oe_pager_index').html(index).end()
- .find('span.oe_pager_count').html(this.dataset.ids.length);
+ this.$pager.toggle(this.dataset.ids.length > 1);
+ if (hide_index) {
+ $(".oe_form_pager_state", this.$pager).html("");
+ } else {
+ $(".oe_form_pager_state", this.$pager).html(_.str.sprintf(_t("%d / %d"), this.dataset.index + 1, this.dataset.ids.length));
+ }
},
parse_on_change: function (on_change, widget) {
var self = this;
@@ -570,22 +618,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
on_processed_onchange: function(result, processed) {
try {
if (result.value) {
- for (var f in result.value) {
- if (!result.value.hasOwnProperty(f)) { continue; }
- var field = this.fields[f];
- // If field is not defined in the view, just ignore it
- if (field) {
- var value_ = result.value[f];
- if (field.get_value() != value_) {
- field.set_value(value_);
- field._dirty_flag = true;
- if (!_.contains(processed, field.name)) {
- this.do_onchange(field, processed);
- }
- }
- }
- }
- this.on_form_changed();
+ this._internal_set_values(result.value, processed);
}
if (!_.isEmpty(result.warning)) {
instance.web.dialog($(QWeb.render("CrashManager.warning", result.warning)), {
@@ -616,6 +649,33 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
return $.Deferred().reject();
}
},
+ _internal_set_values: function(values, exclude) {
+ exclude = exclude || [];
+ for (var f in values) {
+ if (!values.hasOwnProperty(f)) { continue; }
+ var field = this.fields[f];
+ // If field is not defined in the view, just ignore it
+ if (field) {
+ var value_ = values[f];
+ if (field.get_value() != value_) {
+ field._inhibit_on_change_flag = true;
+ field.set_value(value_);
+ field._inhibit_on_change_flag = false;
+ field._dirty_flag = true;
+ if (!_.contains(exclude, field.name)) {
+ this.do_onchange(field, exclude);
+ }
+ }
+ }
+ }
+ this.on_form_changed();
+ },
+ set_values: function(values) {
+ var self = this;
+ return this.on_change_mutex.exec(function() {
+ self._internal_set_values(values);
+ });
+ },
/**
* Ask the view to switch to view mode if possible. The view may not do it
* if the current record is not yet saved. It will then stay in create mode.
@@ -631,32 +691,36 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
this._actualize_mode("edit");
},
/**
- * Reactualize actual_mode.
+ * Ask the view to switch to a precise mode if possible. The view is free to
+ * not respect this command if the state of the dataset is not compatible with
+ * the new mode. For example, it is not possible to switch to edit mode if
+ * the current record is not yet saved in database.
+ *
+ * @param {string} [new_mode] Can be "edit", "view", "create" or undefined. If
+ * undefined the view will test the actual mode to check if it is still consistent
+ * with the dataset state.
*/
_actualize_mode: function(switch_to) {
var mode = switch_to || this.get("actual_mode");
- if (! this.datarecord.id)
+ if (! this.datarecord.id) {
mode = "create";
+ } else if (mode === "create") {
+ mode = "edit";
+ }
this.set({actual_mode: mode});
},
check_actual_mode: function(source, options) {
var self = this;
if(this.get("actual_mode") === "view") {
- self.$element.removeClass('oe_form_editable').addClass('oe_form_readonly');
+ self.$el.removeClass('oe_form_editable').addClass('oe_form_readonly');
self.$buttons.find('.oe_form_buttons_edit').hide();
self.$buttons.find('.oe_form_buttons_view').show();
self.$sidebar.show();
- _.each(this.fields,function(field){
- field.set({"force_readonly": true});
- });
} else {
- self.$element.removeClass('oe_form_readonly').addClass('oe_form_editable');
+ self.$el.removeClass('oe_form_readonly').addClass('oe_form_editable');
self.$buttons.find('.oe_form_buttons_edit').show();
self.$buttons.find('.oe_form_buttons_view').hide();
self.$sidebar.hide();
- _.each(this.fields,function(field){
- field.set({"force_readonly": false});
- });
this.autofocus();
}
},
@@ -684,8 +748,12 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
},
on_button_cancel: function(event) {
if (this.can_be_discarded()) {
- this.to_view_mode();
- this.on_record_loaded(this.datarecord);
+ if (this.get('actual_mode') === 'create') {
+ this.trigger('history_back');
+ } else {
+ this.to_view_mode();
+ this.on_record_loaded(this.datarecord);
+ }
}
return false;
},
@@ -737,17 +805,24 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
return def.promise();
},
can_be_discarded: function() {
- return !this.$element.is('.oe_form_dirty') || confirm(_t("Warning, the record has been modified, your changes will be discarded."));
+ if (this.$el.is('.oe_form_dirty')) {
+ if (!confirm(_t("Warning, the record has been modified, your changes will be discarded.\n\nAre you sure you want to leave this page ?"))) {
+ return false;
+ }
+ this.$el.removeClass('oe_form_dirty');
+ }
+ return true;
},
/**
* Triggers saving the form's record. Chooses between creating a new
* record or saving an existing one depending on whether the record
* already has an id property.
*
- * @param {Function} [success] callback on save success
- * @param {Boolean} [prepend_on_create=false] if ``do_save`` creates a new record, should that record be inserted at the start of the dataset (by default, records are added at the end)
+ * @param {Boolean} [prepend_on_create=false] if ``do_save`` creates a new
+ * record, should that record be inserted at the start of the dataset (by
+ * default, records are added at the end)
*/
- do_save: function(success, prepend_on_create) {
+ do_save: function(prepend_on_create) {
var self = this;
return this.mutating_mutex.exec(function() { return self.is_initialized.pipe(function() {
try {
@@ -782,21 +857,21 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
self.set({'display_invalid_fields': false});
var save_deferral;
if (!self.datarecord.id) {
- //console.log("FormView(", self, ") : About to create", values);
+ // Creation save
save_deferral = self.dataset.create(values).pipe(function(r) {
- return self.on_created(r, undefined, prepend_on_create);
+ return self.on_created(r, prepend_on_create);
}, null);
} else if (_.isEmpty(values) && ! self.force_dirty) {
- //console.log("FormView(", self, ") : Nothing to save");
+ // Not dirty, noop save
save_deferral = $.Deferred().resolve({}).promise();
} else {
self.force_dirty = false;
- //console.log("FormView(", self, ") : About to save", values);
+ // Write save
save_deferral = self.dataset.write(self.datarecord.id, values, {}).pipe(function(r) {
return self.on_saved(r);
}, null);
}
- return save_deferral.then(success);
+ return save_deferral;
}
} catch (e) {
console.error(e);
@@ -815,14 +890,19 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
warnings.push('</ul>');
this.do_warn("The following fields are invalid :", warnings.join(''));
},
- on_saved: function(r, success) {
- if (!r.result) {
+ /**
+ * Reload the form after saving
+ *
+ * @param {Object} r result of the write function.
+ */
+ on_saved: function(r) {
+ if (!r) {
// should not happen in the server, but may happen for internal purpose
return $.Deferred().reject();
} else {
return $.when(this.reload()).pipe(function () {
- return r; })
- .then(success);
+ return r;
+ });
}
},
/**
@@ -835,15 +915,15 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
* * Updates the pager and sidebar displays
*
* @param {Object} r
- * @param {Function} success callback to execute after having updated the dataset
- * @param {Boolean} [prepend_on_create=false] adds the newly created record at the beginning of the dataset instead of the end
+ * @param {Boolean} [prepend_on_create=false] adds the newly created record
+ * at the beginning of the dataset instead of the end
*/
- on_created: function(r, success, prepend_on_create) {
- if (!r.result) {
+ on_created: function(r, prepend_on_create) {
+ if (!r) {
// should not happen in the server, but may happen for internal purpose
return $.Deferred().reject();
} else {
- this.datarecord.id = r.result;
+ this.datarecord.id = r;
if (!prepend_on_create) {
this.dataset.alter_ids(this.dataset.ids.concat([this.datarecord.id]));
this.dataset.index = this.dataset.ids.length - 1;
@@ -857,8 +937,8 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
}
//openerp.log("The record has been created with id #" + this.datarecord.id);
return $.when(this.reload()).pipe(function () {
- return _.extend(r, {created: true}); })
- .then(success);
+ return _.extend(r, {created: true});
+ });
}
},
on_action: function (action) {
@@ -911,6 +991,15 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
return self.dataset.parent_view.recursive_save();
});
},
+ recursive_reload: function() {
+ var self = this;
+ var pre = $.when();
+ if (self.dataset.parent_view)
+ pre = self.dataset.parent_view.recursive_reload();
+ return pre.pipe(function() {
+ return self.reload();
+ });
+ },
is_dirty: function() {
return _.any(this.fields, function (value_) {
return value_._dirty_flag;
@@ -941,19 +1030,17 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|| field.get('invisible')
|| field.get("readonly")
|| field.field.type === 'one2many'
- || field.field.type === 'many2many') {
+ || field.field.type === 'many2many'
+ || field.field.type === 'binary') {
return false;
}
var displayed = value;
- switch (field.field.type) {
- case 'selection':
+ if (field instanceof instance.web.form.FieldSelection) {
displayed = _(field.values).find(function (option) {
return option[0] === value;
})[1];
- break;
- case 'many2one':
+ } else if (field instanceof instance.web.form.FieldMany2One) {
displayed = field.get_displayed();
- break;
}
return {
@@ -981,14 +1068,14 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
buttons: [
{text: _t("Close"), click: function () { d.close(); }},
{text: _t("Save default"), click: function () {
- var $defaults = d.$element.find('#formview_default_fields');
+ var $defaults = d.$el.find('#formview_default_fields');
var field_to_set = $defaults.val();
if (!field_to_set) {
$defaults.parent().addClass('oe_form_invalid');
return;
}
- var condition = d.$element.find('#formview_default_conditions').val(),
- all_users = d.$element.find('#formview_default_all').is(':checked');
+ var condition = d.$el.find('#formview_default_conditions').val(),
+ all_users = d.$el.find('#formview_default_all').is(':checked');
new instance.web.DataSet(self, 'ir.values').call(
'set_default', [
self.dataset.model,
@@ -1013,10 +1100,16 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
field.on('focused', null, this.proxy('widgetFocused'))
.on('blurred', null, this.proxy('widgetBlurred'));
- if (this.get_field(name).translate) {
+ if (this.get_field_desc(name).translate) {
this.translatable_fields.push(field);
}
field.on('changed_value', this, function() {
+ if (field.is_syntax_valid()) {
+ this.trigger('field_changed:' + name);
+ }
+ if (field._inhibit_on_change_flag) {
+ return;
+ }
field._dirty_flag = true;
if (field.is_syntax_valid()) {
this.do_onchange(field);
@@ -1025,14 +1118,33 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
}
});
},
- get_field: function(field_name) {
+ get_field_desc: function(field_name) {
return this.fields_view.fields[field_name];
},
- is_create_mode: function() {
- return this.get("actual_mode") === "create";
+ get_field_value: function(field_name) {
+ return this.fields[field_name].get_value();
+ },
+ compute_domain: function(expression) {
+ return instance.web.form.compute_domain(expression, this.fields);
},
- open_translate_dialog: function(field) {
- return this._super(field);
+ _build_view_fields_values: function(blacklist) {
+ var a_dataset = this.dataset;
+ var fields_values = this.get_fields_values(blacklist);
+ var active_id = a_dataset.ids[a_dataset.index];
+ _.extend(fields_values, {
+ active_id: active_id || false,
+ active_ids: active_id ? [active_id] : [],
+ active_model: a_dataset.model,
+ parent: {}
+ });
+ if (a_dataset.parent_view) {
+ fields_values.parent = a_dataset.parent_view.get_fields_values([a_dataset.child_name]);
+ }
+ return fields_values;
+ },
+ build_eval_context: function(blacklist) {
+ var a_dataset = this.dataset;
+ return new instance.web.CompoundContext(a_dataset.get_context(), this._build_view_fields_values(blacklist));
},
});
@@ -1042,7 +1154,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
instance.web.form.FormRenderingEngineInterface = instance.web.Class.extend({
set_fields_view: function(fields_view) {},
set_fields_registry: function(fields_registry) {},
- render_to: function($element) {},
+ render_to: function($el) {},
});
/**
@@ -1067,6 +1179,9 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
set_fields_registry: function(fields_registry) {
this.fields_registry = fields_registry;
},
+ set_widgets_registry: function(widgets_registry) {
+ this.widgets_registry = widgets_registry;
+ },
// Backward compatibility tools, current default version: v6.1
process_version: function() {
if (this.version < 7.0) {
@@ -1091,6 +1206,7 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
this.fields_to_init = [];
this.tags_to_init = [];
+ this.widgets_to_init = [];
this.labels = {};
this.process(this.$form);
@@ -1120,6 +1236,12 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
var w = new (obj)(self.view, instance.web.xml_to_json($elem[0]));
w.replace($elem);
});
+ _.each(this.widgets_to_init, function($elem) {
+ var widget_type = $elem.attr("type");
+ var obj = self.widgets_registry.get_object(widget_type);
+ var w = new (obj)(self.view, instance.web.xml_to_json($elem[0]));
+ w.replace($elem);
+ });
// TODO: return a deferred
},
render_element: function(template /* dictionaries */) {
@@ -1162,6 +1284,10 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
return $tag;
}
},
+ process_widget: function($widget) {
+ this.widgets_to_init.push($widget);
+ return $widget;
+ },
process_sheet: function($sheet) {
var $new_sheet = this.render_element('FormRenderingSheet', $sheet.getAttributes());
this.handle_common_properties($new_sheet, $sheet);
@@ -1377,6 +1503,10 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
if (! page.__ic)
return;
page.__ic.on("change:effective_invisible", null, function() {
+ if (!page.__ic.get('effective_invisible')) {
+ $new_notebook.tabs('select', i);
+ return;
+ }
var current = $new_notebook.tabs("option", "selected");
if (! pages[current].__ic || ! pages[current].__ic.get("effective_invisible"))
return;
@@ -1435,6 +1565,48 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
},
});
+/**
+ Welcome.
+
+ If you read this documentation, it probably means that you were asked to use a form view widget outside of
+ a form view. Before going further, you must understand that those fields were never really created for
+ that usage. Don't think that this class will hold the answer to all your problems, at best it will allow
+ you to hack the system with more style.
+*/
+instance.web.form.DefaultFieldManager = instance.web.Widget.extend({
+ init: function(parent, eval_context) {
+ this._super(parent);
+ this.field_descs = {};
+ this.eval_context = eval_context || {};
+ },
+ get_field_desc: function(field_name) {
+ if (this.field_descs[field_name] === undefined) {
+ this.field_descs[field_name] = {
+ string: field_name,
+ };
+ }
+ return this.field_descs[field_name];
+ },
+ extend_field_desc: function(fields) {
+ var self = this;
+ _.each(fields, function(v, k) {
+ _.extend(self.get_field_desc(k), v);
+ });
+ },
+ get_field_value: function(field_name) {
+ return false;
+ },
+ set_values: function(values) {
+ // nothing
+ },
+ compute_domain: function(expression) {
+ return instance.web.form.compute_domain(expression, {});
+ },
+ build_eval_context: function() {
+ return new instance.web.CompoundContext(this.eval_context);
+ },
+});
+
instance.web.form.FormDialog = instance.web.Dialog.extend({
init: function(parent, options, view_id, dataset) {
this._super(parent, options);
@@ -1447,7 +1619,7 @@ instance.web.form.FormDialog = instance.web.Dialog.extend({
this.form = new instance.web.FormView(this, this.dataset, this.view_id, {
pager: false
});
- this.form.appendTo(this.$element);
+ this.form.appendTo(this.$el);
this.form.on_created.add_last(this.on_form_dialog_saved);
this.form.on_saved.add_last(this.on_form_dialog_saved);
return this;
@@ -1539,7 +1711,7 @@ instance.web.form.compute_domain = function(expr, fields) {
/**
* Must be applied over an class already possessing the PropertiesMixin.
*
- * Apply the result of the "invisible" domain to this.$element.
+ * Apply the result of the "invisible" domain to this.$el.
*/
instance.web.form.InvisibilityChangerMixin = {
init: function(field_manager, invisible_domain) {
@@ -1548,9 +1720,7 @@ instance.web.form.InvisibilityChangerMixin = {
this._ic_invisible_modifier = invisible_domain;
this._ic_field_manager.on("view_content_has_changed", this, function() {
var result = self._ic_invisible_modifier === undefined ? false :
- instance.web.form.compute_domain(
- self._ic_invisible_modifier,
- self._ic_field_manager.fields);
+ self._ic_field_manager.compute_domain(self._ic_invisible_modifier);
self.set({"invisible": result});
});
this.set({invisible: this._ic_invisible_modifier === true, force_invisible: false});
@@ -1570,40 +1740,61 @@ instance.web.form.InvisibilityChangerMixin = {
this._check_visibility();
},
_check_visibility: function() {
- this.$element.toggleClass('oe_form_invisible', this.get("effective_invisible"));
+ this.$el.toggleClass('oe_form_invisible', this.get("effective_invisible"));
},
};
instance.web.form.InvisibilityChanger = instance.web.Class.extend(instance.web.PropertiesMixin, instance.web.form.InvisibilityChangerMixin, {
- init: function(parent, field_manager, invisible_domain, $element) {
+ init: function(parent, field_manager, invisible_domain, $el) {
this.setParent(parent);
instance.web.PropertiesMixin.init.call(this);
instance.web.form.InvisibilityChangerMixin.init.call(this, field_manager, invisible_domain);
- this.$element = $element;
+ this.$el = $el;
this.start();
},
});
+/**
+ Base class for all fields, custom widgets and buttons to be displayed in the form view.
+
+ Properties:
+ - effective_readonly: when it is true, the widget is displayed as readonly. Vary depending
+ the values of the "readonly" property and the "mode" property on the field manager.
+*/
instance.web.form.FormWidget = instance.web.Widget.extend(instance.web.form.InvisibilityChangerMixin, {
/**
* @constructs instance.web.form.FormWidget
* @extends instance.web.Widget
*
- * @param view
+ * @param field_manager
* @param node
*/
- init: function(view, node) {
- this._super(view);
- this.view = view;
+ init: function(field_manager, node) {
+ this._super(field_manager);
+ this.field_manager = field_manager;
+ if (this.field_manager instanceof instance.web.FormView)
+ this.view = this.field_manager;
this.node = node;
this.modifiers = JSON.parse(this.node.attrs.modifiers || '{}');
- instance.web.form.InvisibilityChangerMixin.init.call(this, view, this.modifiers.invisible);
+ instance.web.form.InvisibilityChangerMixin.init.call(this, this.field_manager, this.modifiers.invisible);
+
+ this.field_manager.on("view_content_has_changed", this, this.process_modifiers);
- this.view.on("view_content_has_changed", this, this.process_modifiers);
+ this.set({required: this.modifiers['required'] === true});
+ // some events to make the property "effective_readonly" sync automatically with "readonly" and
+ // "mode" on field_manager
+ var self = this;
+ this.set({"readonly": this.modifiers['readonly'] === true});
+ var test_effective_readonly = function() {
+ self.set({"effective_readonly": self.get("readonly") || self.field_manager.get("actual_mode") === "view"});
+ };
+ this.on("change:readonly", this, test_effective_readonly);
+ this.field_manager.on("change:actual_mode", this, test_effective_readonly);
+ test_effective_readonly.call(this);
},
renderElement: function() {
this._super();
- this.$element.addClass(this.node.attrs["class"] || "");
+ this.$el.addClass(this.node.attrs["class"] || "");
},
destroy: function() {
$.fn.tipsy.clear();
@@ -1624,12 +1815,11 @@ instance.web.form.FormWidget = instance.web.Widget.extend(instance.web.form.Invi
});
},
process_modifiers: function() {
- var compute_domain = instance.web.form.compute_domain;
var to_set = {};
for (var a in this.modifiers) {
if (!this.modifiers.hasOwnProperty(a)) { continue; }
if (!_.include(["invisible"], a)) {
- var val = compute_domain(this.modifiers[a], this.view.fields);
+ var val = this.field_manager.compute_domain(this.modifiers[a]);
to_set[a] = val;
}
}
@@ -1637,7 +1827,7 @@ instance.web.form.FormWidget = instance.web.Widget.extend(instance.web.form.Invi
},
do_attach_tooltip: function(widget, trigger, options) {
widget = widget || this;
- trigger = trigger || this.$element;
+ trigger = trigger || this.$el;
options = _.extend({
delayIn: 500,
delayOut: 0,
@@ -1648,7 +1838,7 @@ instance.web.form.FormWidget = instance.web.Widget.extend(instance.web.form.Invi
template = 'WidgetLabel.tooltip';
}
return QWeb.render(template, {
- debug: instance.connection.debug,
+ debug: instance.session.debug,
widget: widget
})},
gravity: $.fn.tipsy.autoBounds(50, 'nw'),
@@ -1658,25 +1848,6 @@ instance.web.form.FormWidget = instance.web.Widget.extend(instance.web.form.Invi
}, options || {});
$(trigger).tipsy(options);
},
- _build_view_fields_values: function(blacklist) {
- var a_dataset = this.view.dataset;
- var fields_values = this.view.get_fields_values(blacklist);
- var active_id = a_dataset.ids[a_dataset.index];
- _.extend(fields_values, {
- active_id: active_id || false,
- active_ids: active_id ? [active_id] : [],
- active_model: a_dataset.model,
- parent: {}
- });
- if (a_dataset.parent_view) {
- fields_values.parent = a_dataset.parent_view.get_fields_values([a_dataset.child_name]);
- }
- return fields_values;
- },
- _build_eval_context: function(blacklist) {
- var a_dataset = this.view.dataset;
- return new instance.web.CompoundContext(a_dataset.get_context(), this._build_view_fields_values(blacklist));
- },
/**
* Builds a new context usable for operations related to fields by merging
* the fields'context with the action's context.
@@ -1689,7 +1860,7 @@ instance.web.form.FormWidget = instance.web.Widget.extend(instance.web.form.Invi
}
if (v_context.__ref || true) { //TODO: remove true
- var fields_values = this._build_eval_context(blacklist);
+ var fields_values = this.field_manager.build_eval_context(blacklist);
v_context = new instance.web.CompoundContext(v_context).set_eval_context(fields_values);
}
return v_context;
@@ -1700,7 +1871,7 @@ instance.web.form.FormWidget = instance.web.Widget.extend(instance.web.form.Invi
// if there is a domain on the node, overrides the model's domain
var final_domain = n_domain !== null ? n_domain : f_domain;
if (!(final_domain instanceof Array) || true) { //TODO: remove true
- var fields_values = this._build_eval_context();
+ var fields_values = this.field_manager.build_eval_context();
final_domain = new instance.web.CompoundDomain(final_domain).set_eval_context(fields_values);
}
return final_domain;
@@ -1709,8 +1880,8 @@ instance.web.form.FormWidget = instance.web.Widget.extend(instance.web.form.Invi
instance.web.form.WidgetButton = instance.web.form.FormWidget.extend({
template: 'WidgetButton',
- init: function(view, node) {
- this._super(view, node);
+ init: function(field_manager, node) {
+ this._super(field_manager, node);
this.force_disabled = false;
this.string = (this.node.attrs.string || '').replace(/_/g, '');
if (JSON.parse(this.node.attrs.default_focus || "0")) {
@@ -1724,11 +1895,11 @@ instance.web.form.WidgetButton = instance.web.form.FormWidget.extend({
},
start: function() {
this._super.apply(this, arguments);
- this.$element.click(this.on_click);
- if (this.node.attrs.help || instance.connection.debug) {
+ this.$el.click(this.on_click);
+ if (this.node.attrs.help || instance.session.debug) {
this.do_attach_tooltip();
}
- this.setupFocus(this.$element);
+ this.setupFocus(this.$el);
},
on_click: function() {
var self = this;
@@ -1782,25 +1953,22 @@ instance.web.form.WidgetButton = instance.web.form.FormWidget.extend({
return this.view.do_execute_action(
_.extend({}, this.node.attrs, {context: context}),
this.view.dataset, this.view.datarecord.id, function () {
- self.view.reload();
+ self.view.recursive_reload();
});
},
check_disable: function() {
var disabled = (this.force_disabled || !this.view.is_interactible_record());
- this.$element.prop('disabled', disabled);
- this.$element.css('color', disabled ? 'grey' : '');
+ this.$el.prop('disabled', disabled);
+ this.$el.css('color', disabled ? 'grey' : '');
}
});
/**
* Interface to be implemented by fields.
*
- * Properties:
- * - readonly: boolean. If set to true the field should appear in readonly mode.
- * - force_readonly: boolean, When it is true, the field should always appear
- * in read only mode, no matter what the value of the "readonly" property can be.
* Events:
- * - changed_value: triggered to inform the view to check on_changes
+ * - changed_value: triggered when the value of the field has changed. This can be due
+ * to a user interaction or a call to set_value().
*
*/
instance.web.form.FieldInterface = {
@@ -1864,14 +2032,16 @@ instance.web.form.FieldInterface = {
* Must set the focus on the field. Return false if field is not focusable.
*/
focus: function() {},
+ /**
+ * Called when the translate button is clicked.
+ */
+ on_translate: function() {},
};
/**
* Abstract class for classes implementing FieldInterface.
*
* Properties:
- * - effective_readonly: when it is true, the widget is displayed as readonly. Vary depending
- * the values of the "readonly" property and the "force_readonly" property on the field manager.
* - value: useful property to hold the value of the field. By default, set_value() and get_value()
* set and retrieve the value property. Changing the value property also triggers automatically
* a 'changed_value' event that inform the view to trigger on_changes.
@@ -1888,42 +2058,28 @@ instance.web.form.AbstractField = instance.web.form.FormWidget.extend(instance.w
init: function(field_manager, node) {
var self = this
this._super(field_manager, node);
- this.field_manager = field_manager;
this.name = this.node.attrs.name;
- this.field = this.field_manager.get_field(this.name);
+ this.field = this.field_manager.get_field_desc(this.name);
this.widget = this.node.attrs.widget;
this.string = this.node.attrs.string || this.field.string || this.name;
+ this.options = JSON.parse(this.node.attrs.options || '{}');
this.set({'value': false});
- this.set({required: this.modifiers['required'] === true});
- // some events to make the property "effective_readonly" sync automatically with "readonly" and
- // "force_readonly"
- this.set({"readonly": this.modifiers['readonly'] === true});
- this.set({"force_readonly": false});
- var test_effective_readonly = function() {
- self.set({"effective_readonly": self.get("readonly") || !!self.get("force_readonly")});
- };
- this.on("change:readonly", this, test_effective_readonly);
- this.on("change:force_readonly", this, test_effective_readonly);
- test_effective_readonly.call(this);
this.on("change:value", this, function() {
- if (! this._inhibit_on_change)
- this.trigger('changed_value');
+ this.trigger('changed_value');
this._check_css_flags();
});
},
renderElement: function() {
var self = this;
this._super();
- if (this.field.translate) {
- this.$element.addClass('oe_form_field_translatable');
- this.$element.find('.oe_field_translate').click(_.bind(function() {
- this.field_manager.open_translate_dialog(this);
- }, this));
+ if (this.field.translate && this.view) {
+ this.$el.addClass('oe_form_field_translatable');
+ this.$el.find('.oe_field_translate').click(this.on_translate);
}
- this.$label = this.view.$element.find('label[for=' + this.id_for_label + ']');
- if (instance.connection.debug) {
- this.do_attach_tooltip(this, this.$label[0] || this.$element);
+ this.$label = this.view ? this.view.$el.find('label[for=' + this.id_for_label + ']') : $();
+ if (instance.session.debug) {
+ this.do_attach_tooltip(this, this.$label[0] || this.$el);
this.$label.off('dblclick').on('dblclick', function() {
console.log("Field '%s' of type '%s' in View: %o", self.name, (self.node.attrs.widget || self.field.type), self.view);
window.w = self;
@@ -1942,12 +2098,10 @@ instance.web.form.AbstractField = instance.web.form.FormWidget.extend(instance.w
* Private. Do not use.
*/
_set_required: function() {
- this.$element.toggleClass('oe_form_required', this.get("required"));
+ this.$el.toggleClass('oe_form_required', this.get("required"));
},
set_value: function(value_) {
- this._inhibit_on_change = true;
this.set({'value': value_});
- this._inhibit_on_change = false;
},
get_value: function() {
return this.get('value');
@@ -1967,37 +2121,34 @@ instance.web.form.AbstractField = instance.web.form.FormWidget.extend(instance.w
},
_check_css_flags: function() {
if (this.field.translate) {
- this.$element.find('.oe_field_translate').toggle(!this.field_manager.is_create_mode());
+ this.$el.find('.oe_field_translate').toggle(this.field_manager.get('actual_mode') !== "create");
}
if (!this.disable_utility_classes) {
if (this.field_manager.get('display_invalid_fields')) {
- this.$element.toggleClass('oe_form_invalid', !this.is_valid());
+ this.$el.toggleClass('oe_form_invalid', !this.is_valid());
}
}
},
focus: function() {
return false;
},
- /**
- * Utility method to get the widget options defined in the field xml description.
- */
- get_definition_options: function() {
- if (!this.definition_options) {
- var str = this.node.attrs.options || '{}';
- this.definition_options = JSON.parse(str);
- }
- return this.definition_options;
- },
set_input_id: function(id) {
this.id_for_label = id;
},
+ on_translate: function() {
+ var self = this;
+ var trans = new instance.web.DataSet(this, 'ir.translation');
+ return trans.call_button('translate_fields', [this.view.dataset.model, this.view.datarecord.id, this.name, this.view.dataset.get_context()]).then(function(r) {
+ self.do_action(r);
+ });
+ },
});
/**
- * A mixin to apply on any field that has to completely re-render when its readonly state
+ * A mixin to apply on any FormWidget that has to completely re-render when its readonly state
* switch.
*/
-instance.web.form.ReinitializeFieldMixin = {
+instance.web.form.ReinitializeWidgetMixin = {
/**
* Default implementation of start(), use it or call explicitly initialize_field().
*/
@@ -2010,10 +2161,8 @@ instance.web.form.ReinitializeFieldMixin = {
this.destroy_content();
this.renderElement();
this.initialize_content();
- this.render_value();
});
this.initialize_content();
- this.render_value();
},
/**
* Called to destroy anything that could have been created previously, called before a
@@ -2024,11 +2173,25 @@ instance.web.form.ReinitializeFieldMixin = {
* Called to initialize the content.
*/
initialize_content: function() {},
+};
+
+/**
+ * A mixin to apply on any field that has to completely re-render when its readonly state
+ * switch.
+ */
+instance.web.form.ReinitializeFieldMixin = _.extend({}, instance.web.form.ReinitializeWidgetMixin, {
+ initialize_field: function() {
+ instance.web.form.ReinitializeWidgetMixin.initialize_field.call(this);
+ this.on("change:effective_readonly", this, function() {
+ this.render_value();
+ });
+ this.render_value();
+ },
/**
* Called to render the value. Should also be explicitly called at the end of a set_value().
*/
render_value: function() {},
-};
+});
instance.web.form.FieldChar = instance.web.form.AbstractField.extend(instance.web.form.ReinitializeFieldMixin, {
template: 'FieldChar',
@@ -2039,7 +2202,7 @@ instance.web.form.FieldChar = instance.web.form.AbstractField.extend(instance.we
},
initialize_content: function() {
var self = this;
- var $input = this.$element.find('input');
+ var $input = this.$el.find('input');
$input.change(function() {
self.set({'value': instance.web.parse_value($input.val(), self)});
});
@@ -2052,18 +2215,18 @@ instance.web.form.FieldChar = instance.web.form.AbstractField.extend(instance.we
render_value: function() {
var show_value = instance.web.format_value(this.get('value'), this, '');
if (!this.get("effective_readonly")) {
- this.$element.find('input').val(show_value);
+ this.$el.find('input').val(show_value);
} else {
if (this.password) {
show_value = new Array(show_value.length + 1).join('*');
}
- this.$element.text(show_value);
+ this.$el.text(show_value);
}
},
is_syntax_valid: function() {
if (!this.get("effective_readonly")) {
try {
- var value_ = instance.web.parse_value(this.$element.find('input').val(), this, '');
+ var value_ = instance.web.parse_value(this.$el.find('input').val(), this, '');
return true;
} catch(e) {
return false;
@@ -2075,7 +2238,7 @@ instance.web.form.FieldChar = instance.web.form.AbstractField.extend(instance.we
return this.get('value') === '' || this._super();
},
focus: function() {
- this.$element.find('input:first').focus();
+ this.$('input:first').focus();
}
});
@@ -2087,7 +2250,7 @@ instance.web.form.FieldEmail = instance.web.form.FieldChar.extend({
template: 'FieldEmail',
initialize_content: function() {
this._super();
- var $button = this.$element.find('button');
+ var $button = this.$el.find('button');
$button.click(this.on_button_clicked);
this.setupFocus($button);
},
@@ -2095,7 +2258,7 @@ instance.web.form.FieldEmail = instance.web.form.FieldChar.extend({
if (!this.get("effective_readonly")) {
this._super();
} else {
- this.$element.find('a')
+ this.$el.find('a')
.attr('href', 'mailto:' + this.get('value'))
.text(this.get('value') || '');
}
@@ -2113,7 +2276,7 @@ instance.web.form.FieldUrl = instance.web.form.FieldChar.extend({
template: 'FieldUrl',
initialize_content: function() {
this._super();
- var $button = this.$element.find('button');
+ var $button = this.$el.find('button');
$button.click(this.on_button_clicked);
this.setupFocus($button);
},
@@ -2126,7 +2289,7 @@ instance.web.form.FieldUrl = instance.web.form.FieldChar.extend({
if (!s) {
tmp = "http://" + this.get('value');
}
- this.$element.find('a').attr('href', tmp).text(this.get('value') ? tmp : '');
+ this.$el.find('a').attr('href', tmp).text(this.get('value') ? tmp : '');
}
},
on_button_clicked: function() {
@@ -2159,10 +2322,13 @@ instance.web.form.FieldFloat = instance.web.form.FieldChar.extend({
value_ = 0;
}
this._super.apply(this, [value_]);
+ },
+ focus: function () {
+ this.$('input:first').select();
}
});
-instance.web.DateTimeWidget = instance.web.OldWidget.extend({
+instance.web.DateTimeWidget = instance.web.Widget.extend({
template: "web.datepicker",
jqueryui_object: 'datetimepicker',
type_of_date: "datetime",
@@ -2172,8 +2338,8 @@ instance.web.DateTimeWidget = instance.web.OldWidget.extend({
},
start: function() {
var self = this;
- this.$input = this.$element.find('input.oe_datepicker_master');
- this.$input_picker = this.$element.find('input.oe_datepicker_container');
+ this.$input = this.$el.find('input.oe_datepicker_master');
+ this.$input_picker = this.$el.find('input.oe_datepicker_container');
this.$input.change(this.on_change);
this.picker({
onClose: this.on_picker_select,
@@ -2181,9 +2347,10 @@ instance.web.DateTimeWidget = instance.web.OldWidget.extend({
changeMonth: true,
changeYear: true,
showWeek: true,
- showButtonPanel: true
+ showButtonPanel: true,
+ firstDay: Date.CultureInfo.firstDayOfWeek
});
- this.$element.find('img.oe_datepicker_trigger').click(function() {
+ this.$el.find('img.oe_datepicker_trigger').click(function() {
if (self.get("effective_readonly") || self.picker('widget').is(':visible')) {
self.$input.focus();
return;
@@ -2220,7 +2387,7 @@ instance.web.DateTimeWidget = instance.web.OldWidget.extend({
set_readonly: function(readonly) {
this.readonly = readonly;
this.$input.prop('readonly', this.readonly);
- this.$element.find('img.oe_datepicker_trigger').toggleClass('oe_input_icon_disabled', readonly);
+ this.$el.find('img.oe_datepicker_trigger').toggleClass('oe_input_icon_disabled', readonly);
},
is_valid_: function() {
var value_ = this.$input.val();
@@ -2270,7 +2437,7 @@ instance.web.form.FieldDatetime = instance.web.form.AbstractField.extend(instanc
this.datewidget.on_change.add_last(_.bind(function() {
this.set({'value': this.datewidget.get_value()});
}, this));
- this.datewidget.appendTo(this.$element);
+ this.datewidget.appendTo(this.$el);
this.setupFocus(this.datewidget.$input);
}
},
@@ -2282,7 +2449,7 @@ instance.web.form.FieldDatetime = instance.web.form.AbstractField.extend(instanc
if (!this.get("effective_readonly")) {
this.datewidget.set_value(this.get('value'));
} else {
- this.$element.text(instance.web.format_value(this.get('value'), this, ''));
+ this.$el.text(instance.web.format_value(this.get('value'), this, ''));
}
},
is_syntax_valid: function() {
@@ -2310,16 +2477,21 @@ instance.web.form.FieldDate = instance.web.form.FieldDatetime.extend({
instance.web.form.FieldText = instance.web.form.AbstractField.extend(instance.web.form.ReinitializeFieldMixin, {
template: 'FieldText',
+ init: function (field_manager, node) {
+ this._super(field_manager, node);
+ },
initialize_content: function() {
- this.$textarea = this.$element.find('textarea');
+ var self = this;
+ this.$textarea = this.$el.find('textarea');
+ this.default_height = this.$textarea.css('height');
if (!this.get("effective_readonly")) {
this.$textarea.change(_.bind(function() {
- this.set({'value': instance.web.parse_value(this.$textarea.val(), this)});
+ self.set({'value': instance.web.parse_value(self.$textarea.val(), self)});
}, this));
} else {
this.$textarea.attr('disabled', 'disabled');
}
- this.$element.keyup(function (e) {
+ this.$el.keyup(function (e) {
if (e.which === $.ui.keyCode.ENTER) {
e.stopPropagation();
}
@@ -2327,15 +2499,17 @@ instance.web.form.FieldText = instance.web.form.AbstractField.extend(instance.we
this.setupFocus(this.$textarea);
},
set_value: function(value_) {
- this._super.apply(this, arguments);
+ this._super(value_);
this.render_value();
+ $(window).resize();
},
render_value: function() {
var show_value = instance.web.format_value(this.get('value'), this, '');
- this.$textarea.val(show_value);
- if (show_value && this.view.options.resize_textareas) {
- this.do_resize(this.view.options.resize_textareas);
+ if (show_value === '') {
+ this.$textarea.css('height', parseInt(this.default_height)+"px");
}
+ this.$textarea.val(show_value);
+ this.$textarea.autosize();
},
is_syntax_valid: function() {
if (!this.get("effective_readonly")) {
@@ -2351,29 +2525,9 @@ instance.web.form.FieldText = instance.web.form.AbstractField.extend(instance.we
is_false: function() {
return this.get('value') === '' || this._super();
},
- focus: function($element) {
+ focus: function($el) {
this.$textarea.focus();
},
- do_resize: function(max_height) {
- max_height = parseInt(max_height, 10);
- var $input = this.$textarea,
- $div = $('<div style="position: absolute; z-index: 1000; top: 0"/>').width($input.width()),
- new_height;
- $div.text($input.val());
- _.each('font-family,font-size,white-space'.split(','), function(style) {
- $div.css(style, $input.css(style));
- });
- $div.appendTo($('body'));
- new_height = $div.height();
- if (new_height < 90) {
- new_height = 90;
- }
- if (!isNaN(max_height) && new_height > max_height) {
- new_height = max_height;
- }
- $div.remove();
- $input.height(new_height);
- },
});
/**
@@ -2383,44 +2537,50 @@ instance.web.form.FieldText = instance.web.form.AbstractField.extend(instance.we
* To find more information about CLEditor configutation: go to
* http://premiumsoftware.net/cleditor/docs/GettingStarted.html
*/
-instance.web.form.FieldTextHtml = instance.web.form.FieldText.extend({
-
- initialize_content: function() {
- this.$textarea = this.$element.find('textarea');
- var width = ((this.node.attrs || {}).editor_width || 468);
- var height = ((this.node.attrs || {}).editor_height || 100);
- this.$textarea.cleditor({
- width: width, // width not including margins, borders or padding
- height: height, // height not including margins, borders or padding
- controls: // controls to add to the toolbar
- "bold italic underline strikethrough | size " +
- "| removeformat | bullets numbering | outdent " +
- "indent | link unlink",
- sizes: // sizes in the font size popup
- "1,2,3,4,5,6,7",
- bodyStyle: // style to assign to document body contained within the editor
- "margin:4px; font:12px monospace; cursor:text; color:#1F1F1F"
- });
- this.$cleditor = this.$textarea.cleditor()[0];
- // call super now, because cleditor resets the disable attr
+instance.web.form.FieldTextHtml = instance.web.form.AbstractField.extend(instance.web.form.ReinitializeFieldMixin, {
+ template: 'FieldTextHtml',
+ init: function() {
this._super.apply(this, arguments);
- // propagate disabled property to cleditor
- this.$cleditor.disable(this.$textarea.prop('disabled'));
},
-
+ initialize_content: function() {
+ var self = this;
+ if (! this.get("effective_readonly")) {
+ self._updating_editor = false;
+ this.$textarea = this.$el.find('textarea');
+ var width = ((this.node.attrs || {}).editor_width || 468);
+ var height = ((this.node.attrs || {}).editor_height || 250);
+ this.$textarea.cleditor({
+ width: width, // width not including margins, borders or padding
+ height: height, // height not including margins, borders or padding
+ controls: // controls to add to the toolbar
+ "bold italic underline strikethrough " +
+ "| removeformat | bullets numbering | outdent " +
+ "indent | link unlink | source",
+ bodyStyle: // style to assign to document body contained within the editor
+ "margin:4px; font:12px monospace; cursor:text; color:#1F1F1F"
+ });
+ this.$cleditor = this.$textarea.cleditor()[0];
+ this.$cleditor.change(function() {
+ if (! self._updating_editor) {
+ self.$cleditor.updateTextArea();
+ self.set({'value': self.$textarea.val()});
+ }
+ });
+ }
+ },
set_value: function(value_) {
this._super.apply(this, arguments);
- this._dirty_flag = true;
+ this.render_value();
},
-
render_value: function() {
- this._super.apply(this, arguments);
- this.$cleditor.updateFrame();
- },
-
- get_value: function() {
- this.$cleditor.updateTextArea();
- return this.$textarea.val();
+ if (! this.get("effective_readonly")) {
+ this.$textarea.val(this.get('value') || '');
+ this._updating_editor = true;
+ this.$cleditor.updateFrame();
+ this._updating_editor = false;
+ } else {
+ this.$el.html(this.get('value'));
+ }
},
});
@@ -2429,9 +2589,9 @@ instance.web.form.FieldBoolean = instance.web.form.AbstractField.extend({
start: function() {
var self = this;
this._super.apply(this, arguments);
- this.$checkbox = $("input", this.$element);
+ this.$checkbox = $("input", this.$el);
this.setupFocus(this.$checkbox);
- this.$element.click(_.bind(function() {
+ this.$el.click(_.bind(function() {
this.set({'value': this.$checkbox.is(':checked')});
}, this));
var check_readonly = function() {
@@ -2453,7 +2613,7 @@ instance.web.form.FieldProgressBar = instance.web.form.AbstractField.extend({
template: 'FieldProgressBar',
start: function() {
this._super.apply(this, arguments);
- this.$element.progressbar({
+ this.$el.progressbar({
value: this.get('value'),
disabled: this.get("effective_readonly")
});
@@ -2465,7 +2625,7 @@ instance.web.form.FieldProgressBar = instance.web.form.AbstractField.extend({
show_value = 0;
}
var formatted_value = instance.web.format_value(show_value, { type : 'float' }, '0');
- this.$element.progressbar('option', 'value', show_value).find('span').html(formatted_value + '%');
+ this.$el.progressbar('option', 'value', show_value).find('span').html(formatted_value + '%');
}
});
@@ -2496,9 +2656,9 @@ instance.web.form.FieldSelection = instance.web.form.AbstractField.extend(instan
// changing the selected value), takes the action as validating the
// row
var ischanging = false;
- var $select = this.$element.find('select')
+ var $select = this.$el.find('select')
.change(_.bind(function() {
- this.set({'value': this.values[this.$element.find('select')[0].selectedIndex][0]});
+ this.set({'value': this.values[this.$el.find('select')[0].selectedIndex][0]});
}, this))
.change(function () { ischanging = true; })
.click(function () { ischanging = false; })
@@ -2521,27 +2681,27 @@ instance.web.form.FieldSelection = instance.web.form.AbstractField.extend(instan
for (var i = 0, ii = this.values.length; i < ii; i++) {
if (this.values[i][0] === this.get('value')) index = i;
}
- this.$element.find('select')[0].selectedIndex = index;
+ this.$el.find('select')[0].selectedIndex = index;
} else {
var self = this;
var option = _(this.values)
.detect(function (record) { return record[0] === self.get('value'); });
- this.$element.text(option ? option[1] : this.values[0][1]);
+ this.$el.text(option ? option[1] : this.values[0][1]);
}
},
is_syntax_valid: function() {
if (this.get("effective_readonly")) {
return true;
}
- var value_ = this.values[this.$element.find('select')[0].selectedIndex];
+ var value_ = this.values[this.$el.find('select')[0].selectedIndex];
return !! value_;
},
focus: function() {
- this.$element.find('select:first').focus();
+ this.$el.find('select:first').focus();
}
});
-// jquery autocomplete tweak to allow html
+// jquery autocomplete tweak to allow html and classnames
(function() {
var proto = $.ui.autocomplete.prototype,
initSource = proto._initSource;
@@ -2568,7 +2728,8 @@ instance.web.form.FieldSelection = instance.web.form.AbstractField.extend(instan
return $( "<li></li>" )
.data( "item.autocomplete", item )
.append( $( "<a></a>" )[ this.options.html ? "html" : "text" ]( item.label ) )
- .appendTo( ul );
+ .appendTo( ul )
+ .addClass(item.classname);
}
});
})();
@@ -2589,6 +2750,7 @@ instance.web.form.CompletionFieldMixin = {
var dataset = new instance.web.DataSet(this, this.field.relation, self.build_context());
var blacklist = this.get_search_blacklist();
+ this.last_query = search_val;
return this.orderer.add(dataset.name_search(
search_val, new instance.web.CompoundDomain(self.build_domain(), [["id", "not in", blacklist]]),
@@ -2608,25 +2770,36 @@ instance.web.form.CompletionFieldMixin = {
// search more... if more results that max
if (values.length > self.limit) {
values = values.slice(0, self.limit);
- values.push({label: _t("<em>   Search More...</em>"), action: function() {
- dataset.name_search(search_val, self.build_domain(), 'ilike'
- , false, function(data) {
- self._search_create_popup("search", data);
- });
- }});
+ values.push({
+ label: _t("Search More..."),
+ action: function() {
+ dataset.name_search(search_val, self.build_domain(), 'ilike', false).then(function(data) {
+ self._search_create_popup("search", data);
+ });
+ },
+ classname: 'oe_m2o_dropdown_option'
+ });
}
// quick create
var raw_result = _(data.result).map(function(x) {return x[1];});
if (search_val.length > 0 && !_.include(raw_result, search_val)) {
- values.push({label: _.str.sprintf(_t('<em>   Create "<strong>%s</strong>"</em>'),
- $('<span />').text(search_val).html()), action: function() {
- self._quick_create(search_val);
- }});
+ values.push({
+ label: _.str.sprintf(_t('Create "<strong>%s</strong>"'),
+ $('<span />').text(search_val).html()),
+ action: function() {
+ self._quick_create(search_val);
+ },
+ classname: 'oe_m2o_dropdown_option'
+ });
}
// create...
- values.push({label: _t("<em>   Create and Edit...</em>"), action: function() {
- self._search_create_popup("form", undefined, {});
- }});
+ values.push({
+ label: _t("Create and Edit..."),
+ action: function() {
+ self._search_create_popup("form", undefined, self._create_context(search_val));
+ },
+ classname: 'oe_m2o_dropdown_option'
+ });
return values;
});
@@ -2637,11 +2810,11 @@ instance.web.form.CompletionFieldMixin = {
_quick_create: function(name) {
var self = this;
var slow_create = function () {
- self._search_create_popup("form", undefined, {"default_name": name});
+ self._search_create_popup("form", undefined, self._create_context(name));
};
- if (self.get_definition_options().quick_create === undefined || self.get_definition_options().quick_create) {
+ if (self.options.quick_create === undefined || self.options.quick_create) {
new instance.web.DataSet(this, this.field.relation, self.build_context())
- .name_create(name, function(data) {
+ .name_create(name).then(function(data) {
self.add_id(data[0]);
}).fail(function(error, event) {
event.preventDefault();
@@ -2674,8 +2847,43 @@ instance.web.form.CompletionFieldMixin = {
* To implement.
*/
add_id: function(id) {},
+ _create_context: function(name) {
+ var tmp = {};
+ var field = (this.options || {}).create_name_field;
+ if (field === undefined)
+ field = "name";
+ if (field !== false && name && (this.options || {}).quick_create !== false)
+ tmp["default_" + field] = name;
+ return tmp;
+ },
};
+instance.web.form.M2ODialog = instance.web.Dialog.extend({
+ template: "M2ODialog",
+ init: function(parent) {
+ this._super(parent, {
+ title: _.str.sprintf(_t("Add %s"), parent.string),
+ width: 312,
+ });
+ },
+ start: function() {
+ var self = this;
+ this.$buttons.html(QWeb.render("M2ODialog.buttons"));
+ this.$("input").val(this.getParent().last_query);
+ this.$buttons.find(".oe_form_m2o_qc_button").click(function(){
+ self.getParent()._quick_create(self.$("input").val());
+ self.destroy();
+ });
+ this.$buttons.find(".oe_form_m2o_sc_button").click(function(){
+ self.getParent()._search_create_popup("form", undefined, self.getParent()._create_context(self.$("input").val()));
+ self.destroy();
+ });
+ this.$buttons.find(".oe_form_m2o_cancel_button").click(function(){
+ self.destroy();
+ });
+ },
+});
+
instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instance.web.form.CompletionFieldMixin, instance.web.form.ReinitializeFieldMixin, {
template: "FieldMany2One",
init: function(field_manager, node) {
@@ -2695,33 +2903,46 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
this.floating = false;
this.render_value();
});
+ instance.web.bus.on('click', this, function() {
+ if (!this.get("effective_readonly") && this.$input && this.$input.autocomplete('widget').is(':visible')) {
+ this.$input.autocomplete("close");
+ }
+ });
},
initialize_content: function() {
if (!this.get("effective_readonly"))
this.render_editable();
this.render_value();
},
+ init_error_displayer: function() {
+ // nothing
+ },
+ hide_error_displayer: function() {
+ // doesn't work
+ },
+ show_error_displayer: function() {
+ new instance.web.form.M2ODialog(this).open();
+ },
render_editable: function() {
var self = this;
- this.$input = this.$element.find("input");
+ this.$input = this.$el.find("input");
- self.$input.tipsy({
- title: function() {
- return "No element was selected, you should create or select one from the dropdown list.";
- },
- trigger:'manual',
- fade: true,
+ this.init_error_displayer();
+
+ self.$input.on('focus', function() {
+ self.hide_error_displayer();
});
- this.$drop_down = this.$element.find(".oe_m2o_drop_down_button");
- this.$follow_button = $(".oe_m2o_cm_button", this.$element);
+ this.$drop_down = this.$el.find(".oe_m2o_drop_down_button");
+ this.$follow_button = $(".oe_m2o_cm_button", this.$el);
- this.$follow_button.click(function() {
+ this.$follow_button.click(function(ev) {
+ ev.preventDefault();
if (!self.get('value')) {
self.focus();
return;
}
- var pop = new instance.web.form.FormOpenPopup(self.view);
+ var pop = new instance.web.form.FormOpenPopup(self);
pop.show_element(
self.field.relation,
self.get("value"),
@@ -2738,7 +2959,7 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
});
// some behavior for input
- this.$input.keydown(function() {
+ var input_changed = function() {
if (self.current_display !== self.$input.val()) {
self.current_display = self.$input.val();
if (self.$input.val() === "") {
@@ -2748,7 +2969,9 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
self.floating = true;
}
}
- });
+ };
+ this.$input.keydown(input_changed);
+ this.$input.change(input_changed);
this.$drop_down.click(function() {
if (self.$input.autocomplete("widget").is(":visible")) {
self.$input.autocomplete("close");
@@ -2761,10 +2984,10 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
}
}
});
- var tip_def = $.Deferred();
- var untip_def = $.Deferred();
- var tip_delay = 200;
- var tip_duration = 3000;
+ self.ed_def = $.Deferred();
+ self.uned_def = $.Deferred();
+ var ed_delay = 200;
+ var ed_duration = 15000;
var anyoneLoosesFocus = function() {
var used = false;
if (self.floating) {
@@ -2784,27 +3007,42 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
}
self.floating = false;
}
- if (used && self.get("value") === false) {
- tip_def.reject();
- untip_def.reject();
- tip_def = $.Deferred();
- tip_def.then(function() {
- self.$input.tipsy("show");
+ if (used && self.get("value") === false && ! self.no_ed) {
+ self.ed_def.reject();
+ self.uned_def.reject();
+ self.ed_def = $.Deferred();
+ self.ed_def.then(function() {
+ self.show_error_displayer();
});
setTimeout(function() {
- tip_def.resolve();
- untip_def.reject();
- untip_def = $.Deferred();
- untip_def.then(function() {
- self.$input.tipsy("hide");
+ self.ed_def.resolve();
+ self.uned_def.reject();
+ self.uned_def = $.Deferred();
+ self.uned_def.then(function() {
+ self.hide_error_displayer();
});
- setTimeout(function() {untip_def.resolve();}, tip_duration);
- }, tip_delay);
+ setTimeout(function() {self.uned_def.resolve();}, ed_duration);
+ }, ed_delay);
} else {
- tip_def.reject();
+ self.no_ed = false;
+ self.ed_def.reject();
}
};
- this.$input.focusout(anyoneLoosesFocus);
+ var ignore_blur = false;
+ this.$input.on({
+ focusout: anyoneLoosesFocus,
+ focus: function () { self.trigger('focused'); },
+ autocompleteopen: function () { ignore_blur = true; },
+ autocompleteclose: function () { ignore_blur = false; },
+ blur: function () {
+ // autocomplete open
+ if (ignore_blur) { return; }
+ if (_(self.getChildren()).any(function (child) {
+ return child instanceof instance.web.form.AbstractFormPopup;
+ })) { return; }
+ self.trigger('blurred');
+ }
+ });
var isSelecting = false;
// autocomplete
@@ -2822,7 +3060,6 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
self.display_value["" + item.id] = item.name;
self.set({value: item.id});
} else if (item.action) {
- self.floating = true;
item.action();
// Cancel widget blurring, to avoid form blur event
self.trigger('focused');
@@ -2847,9 +3084,8 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
}
isSelecting = false;
});
- this.setupFocus(this.$input.add(this.$follow_button));
+ this.setupFocus(this.$follow_button);
},
-
render_value: function(no_recurse) {
var self = this;
if (! this.get("value")) {
@@ -2863,7 +3099,7 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
}
if (! no_recurse) {
var dataset = new instance.web.DataSetStatic(this, this.field.relation, self.build_context());
- dataset.name_get([self.get("value")], function(data) {
+ dataset.name_get([self.get("value")]).then(function(data) {
self.display_value["" + self.get("value")] = data[0][1];
self.render_value(true);
});
@@ -2874,22 +3110,21 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
if (!this.get("effective_readonly")) {
this.$input.val(str.split("\n")[0]);
this.current_display = this.$input.val();
+ if(this.is_false()){
+ this.$('.oe_m2o_cm_button').css({'display':'none'});
+ }
} else {
var lines = _.escape(str).split("\n");
var link = "";
var follow = "";
- if (! this.get_definition_options().highlight_first_line) {
- link = lines.join("<br />");
- } else {
- link = lines[0];
- follow = _.rest(lines).join("<br />");
- if (follow)
- link += "<br />";
- }
- var $link = this.$element.find('.oe_form_uri')
+ link = lines[0];
+ follow = _.rest(lines).join("<br />");
+ if (follow)
+ link += "<br />";
+ var $link = this.$el.find('.oe_form_uri')
.unbind('click')
.html(link);
- if (! this.get_definition_options().no_open)
+ if (! this.options.no_open)
$link.click(function () {
self.do_action({
type: 'ir.actions.act_window',
@@ -2901,14 +3136,14 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
});
return false;
});
- $(".oe_form_m2o_follow", this.$element).html(follow);
+ $(".oe_form_m2o_follow", this.$el).html(follow);
}
},
set_value: function(value_) {
var self = this;
if (value_ instanceof Array) {
this.display_value = {};
- if (! this.get_definition_options().always_reload) {
+ if (! this.options.always_reload) {
this.display_value["" + value_[0]] = value_[1];
}
value_ = value_[0];
@@ -2930,7 +3165,17 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
},
focus: function () {
this.$input.focus();
- }
+ },
+ _quick_create: function() {
+ this.no_ed = true;
+ this.ed_def.reject();
+ return instance.web.form.CompletionFieldMixin._quick_create.apply(this, arguments);
+ },
+ _search_create_popup: function() {
+ this.no_ed = true;
+ this.ed_def.reject();
+ return instance.web.form.CompletionFieldMixin._search_create_popup.apply(this, arguments);
+ },
});
/*
@@ -2992,7 +3237,7 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({
},
start: function() {
this._super.apply(this, arguments);
- this.$element.addClass('oe_form_field_one2many');
+ this.$el.addClass('oe_form_field oe_form_field_one2many');
var self = this;
@@ -3102,7 +3347,7 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({
}
} else if (view_type === "form") {
if (self.get("effective_readonly")) {
- $(".oe_form_buttons", controller.$element).children().remove();
+ $(".oe_form_buttons", controller.$el).children().remove();
}
controller.on_record_loaded.add_last(function() {
once.resolve();
@@ -3123,7 +3368,7 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({
});
this.is_setted.then(function() {
$.async_when().then(function () {
- self.viewmanager.appendTo(self.$element);
+ self.viewmanager.appendTo(self.$el);
});
});
return def;
@@ -3205,6 +3450,7 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({
this.dataset.index = 0;
}
self.is_setted.resolve();
+ this.trigger_on_change();
return self.reload_current_view();
},
get_value: function() {
@@ -3257,7 +3503,7 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({
}, this));
},
is_syntax_valid: function() {
- if (!this.viewmanager.views[this.viewmanager.active_view])
+ if (! this.viewmanager || ! this.viewmanager.views[this.viewmanager.active_view])
return true;
var view = this.viewmanager.views[this.viewmanager.active_view].controller;
switch (this.viewmanager.active_view) {
@@ -3291,12 +3537,12 @@ instance.web.form.One2ManyViewManager = instance.web.ViewManager.extend({
}
var self = this;
var id = self.o2m.dataset.index !== null ? self.o2m.dataset.ids[self.o2m.dataset.index] : null;
- var pop = new instance.web.form.FormOpenPopup(self.o2m.view);
+ var pop = new instance.web.form.FormOpenPopup(this);
pop.show_element(self.o2m.field.relation, id, self.o2m.build_context(), {
title: _t("Open: ") + self.o2m.string,
create_function: function(data) {
return self.o2m.dataset.create(data).then(function(r) {
- self.o2m.dataset.set_ids(self.o2m.dataset.ids.concat([r.result]));
+ self.o2m.dataset.set_ids(self.o2m.dataset.ids.concat([r]));
self.o2m.dataset.on_change();
});
},
@@ -3334,6 +3580,7 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({
ListType: instance.web.form.One2ManyList
}));
this.on('edit:before', this, this.proxy('_before_edit'));
+ this.on('edit:after', this, this.proxy('_after_edit'));
this.on('save:before cancel:before', this, this.proxy('_before_unedit'));
this.records
@@ -3343,7 +3590,7 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({
},
start: function () {
var ret = this._super();
- this.$element
+ this.$el
.off('mousedown.handleButtons')
.on('mousedown.handleButtons', 'table button', this.proxy('_button_down'));
return ret;
@@ -3357,7 +3604,7 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({
// If the form has not been modified, the view can only be valid
// NB: is_dirty will also be set on defaults/onchanges/whatever?
// oe_form_dirty seems to only be set on actual user actions
- if (!form.$element.is('.oe_form_dirty')) {
+ if (!form.$el.is('.oe_form_dirty')) {
return true;
}
this.o2m._dirty_flag = true;
@@ -3385,7 +3632,7 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({
alternative_form_view: self.o2m.field.views ? self.o2m.field.views["form"] : undefined,
create_function: function(data, callback, error_callback) {
return self.o2m.dataset.create(data).then(function(r) {
- self.o2m.dataset.set_ids(self.o2m.dataset.ids.concat([r.result]));
+ self.o2m.dataset.set_ids(self.o2m.dataset.ids.concat([r]));
self.o2m.dataset.on_change();
}).then(callback, error_callback);
},
@@ -3406,11 +3653,11 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({
},
do_activate_record: function(index, id) {
var self = this;
- var pop = new instance.web.form.FormOpenPopup(self.o2m.view);
+ var pop = new instance.web.form.FormOpenPopup(self);
pop.show_element(self.o2m.field.relation, id, self.o2m.build_context(), {
title: _t("Open: ") + self.o2m.string,
write_function: function(id, data) {
- return self.o2m.dataset.write(id, data, {}, function(r) {
+ return self.o2m.dataset.write(id, data, {}).then(function() {
self.o2m.reload_current_view();
});
},
@@ -3421,7 +3668,7 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({
return self.o2m.dataset.read_ids.apply(self.o2m.dataset, arguments);
},
form_view_options: {'not_interactible_on_create':true},
- readonly: self.o2m.get("effective_readonly")
+ readonly: !this.is_action_enabled('edit') || self.o2m.get("effective_readonly")
});
},
do_button_action: function (name, id, callback) {
@@ -3434,7 +3681,10 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({
var parent_form = this.o2m.view;
var self = this;
this.ensure_saved().pipe(function () {
- return parent_form.do_save();
+ if (parent_form)
+ return parent_form.do_save();
+ else
+ return $.when();
}).then(function () {
self.handle_button(name, id, callback);
});
@@ -3444,6 +3694,12 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({
this.__ignore_blur = false;
this.editor.form.on('blurred', this, this._on_form_blur);
},
+ _after_edit: function () {
+ // The form's blur thing may be jiggered during the edition setup,
+ // potentially leading to the o2m instasaving the row. Cancel any
+ // blurring triggered the edition startup here
+ this.editor.form.widgetFocused();
+ },
_before_unedit: function () {
this.editor.form.off('blurred', this, this._on_form_blur);
},
@@ -3465,7 +3721,7 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({
return;
}
// FIXME: why isn't there an API for this?
- if (this.editor.form.$element.hasClass('oe_form_dirty')) {
+ if (this.editor.form.$el.hasClass('oe_form_dirty')) {
this.save_edition();
return;
}
@@ -3477,11 +3733,37 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({
// the current row *anyway*, then create a new one/edit the next one)
this.__ignore_blur = true;
this._super.apply(this, arguments);
+ },
+ do_delete: function (ids) {
+ var self = this;
+ var next = $.when();
+ var _super = this._super;
+ // handle deletion of an item which does not exist
+ // TODO: better handle that in the editable list?
+ var false_id_index = _(ids).indexOf(false);
+ if (false_id_index !== -1) {
+ ids.splice(false_id_index, 1);
+ next = this.cancel_edition(true);
+ }
+ return next.pipe(function () {
+ // wheeee
+ var confirm = window.confirm;
+ window.confirm = function () { return true; };
+ try {
+ return _super.call(self, ids);
+ } finally {
+ window.confirm = confirm;
+ }
+ });
}
});
instance.web.form.One2ManyList = instance.web.ListView.List.extend({
pad_table_to: function (count) {
- this._super(count > 0 ? count - 1 : 0);
+ if (!this.view.is_action_enabled('create')) {
+ this._super(count);
+ } else {
+ this._super(count > 0 ? count - 1 : 0);
+ }
// magical invocation of wtf does that do
if (this.view.o2m.get('effective_readonly')) {
@@ -3494,30 +3776,42 @@ instance.web.form.One2ManyList = instance.web.ListView.List.extend({
}).length;
if (this.options.selectable) { columns++; }
if (this.options.deletable) { columns++; }
+
+ if (!this.view.is_action_enabled('create')) {
+ return;
+ }
+
var $cell = $('<td>', {
colspan: columns,
'class': 'oe_form_field_one2many_list_row_add'
- }).text(_t("Add a row"))
- .mousedown(function () {
- // FIXME: needs to be an official API somehow
- if (self.view.editor.is_editing()) {
- self.view.__ignore_blur = true;
- }
- })
- .click(function (e) {
- e.preventDefault();
- e.stopPropagation();
- // FIXME: there should also be an API for that one
- if (self.view.editor.form.__blur_timeout) {
- clearTimeout(self.view.editor.form.__blur_timeout);
- self.view.editor.form.__blur_timeout = false;
- }
- self.view.ensure_saved().then(function () {
- self.view.do_add_record();
- });
- });
- this.$current.append(
- $('<tr>').append($cell))
+ }).append(
+ $('<a>', {href: '#'}).text(_t("Add a row"))
+ .mousedown(function () {
+ // FIXME: needs to be an official API somehow
+ if (self.view.editor.is_editing()) {
+ self.view.__ignore_blur = true;
+ }
+ })
+ .click(function (e) {
+ e.preventDefault();
+ e.stopPropagation();
+ // FIXME: there should also be an API for that one
+ if (self.view.editor.form.__blur_timeout) {
+ clearTimeout(self.view.editor.form.__blur_timeout);
+ self.view.editor.form.__blur_timeout = false;
+ }
+ self.view.ensure_saved().then(function () {
+ self.view.do_add_record();
+ });
+ }));
+
+ var $padding = this.$current.find('tr:not([data-id]):first');
+ var $newrow = $('<tr>').append($cell);
+ if ($padding.length) {
+ $padding.before($newrow);
+ } else {
+ this.$current.append($newrow)
+ }
}
});
@@ -3564,7 +3858,7 @@ instance.web.form.FieldMany2ManyTags = instance.web.form.AbstractField.extend(in
if (this.get("effective_readonly"))
return;
var self = this;
- self.$text = $("textarea", this.$element);
+ self.$text = $("textarea", this.$el);
self.$text.textext({
plugins : 'tags arrow autocomplete',
autocomplete: {
@@ -3622,7 +3916,7 @@ instance.web.form.FieldMany2ManyTags = instance.web.form.AbstractField.extend(in
self._drop_shown = true;
});
self.tags = self.$text.textext()[0].tags();
- $("textarea", this.$element).focusout(function() {
+ $("textarea", this.$el).focusout(function() {
self.$text.trigger("setInputData", "");
}).keydown(function(e) {
if (e.which === $.ui.keyCode.TAB && self._drop_shown) {
@@ -3646,7 +3940,7 @@ instance.web.form.FieldMany2ManyTags = instance.web.form.AbstractField.extend(in
},
render_value: function() {
var self = this;
- var dataset = new instance.web.DataSetStatic(this, this.field.relation, self.view.dataset.get_context());
+ var dataset = new instance.web.DataSetStatic(this, this.field.relation, self.build_context());
var values = self.get("value")
var handle_names = function(data) {
var indexed = {};
@@ -3656,10 +3950,10 @@ instance.web.form.FieldMany2ManyTags = instance.web.form.AbstractField.extend(in
data = _.map(values, function(el) { return indexed[el]; });
if (! self.get("effective_readonly")) {
self.tags.containerElement().children().remove();
- $("textarea", self.$element).css("padding-left", "3px");
+ $("textarea", self.$el).css("padding-left", "3px");
self.tags.addTags(_.map(data, function(el) {return {name: el[1], id:el[0]};}));
} else {
- self.$element.html(QWeb.render("FieldMany2ManyTag", {elements: data}));
+ self.$el.html(QWeb.render("FieldMany2ManyTag", {elements: data}));
}
};
if (! values || values.length > 0) {
@@ -3687,7 +3981,7 @@ instance.web.form.FieldMany2Many = instance.web.form.AbstractField.extend({
},
start: function() {
this._super.apply(this, arguments);
- this.$element.addClass('oe_form_field oe_form_field_many2many');
+ this.$el.addClass('oe_form_field oe_form_field_many2many');
var self = this;
@@ -3750,7 +4044,7 @@ instance.web.form.FieldMany2Many = instance.web.form.AbstractField.extend({
loaded.resolve();
});
$.async_when().then(function () {
- self.list_view.appendTo(self.$element);
+ self.list_view.appendTo(self.$el);
});
return loaded;
},
@@ -3878,7 +4172,7 @@ instance.web.form.FieldMany2ManyKanban = instance.web.form.AbstractField.extend(
});
this.kanban_view.do_switch_view.add_last(_.bind(this.open_popup, this));
$.async_when().then(function () {
- self.kanban_view.appendTo(self.$element);
+ self.kanban_view.appendTo(self.$el);
});
return loaded;
},
@@ -3916,7 +4210,7 @@ instance.web.form.FieldMany2ManyKanban = instance.web.form.AbstractField.extend(
});
} else {
var id = self.dataset.ids[self.dataset.index];
- var pop = new instance.web.form.FormOpenPopup(self.view);
+ var pop = new instance.web.form.FormOpenPopup(this);
pop.show_element(self.field.relation, id, self.build_context(), {
title: _t("Open: ") + self.string,
write_function: function(id, data, options) {
@@ -3962,7 +4256,7 @@ instance.web.form.Many2ManyQuickCreate = instance.web.Widget.extend({
},
start: function () {
var self = this;
- self.$text = this.$element.find('input').css("width", "200px");
+ self.$text = this.$el.find('input').css("width", "200px");
self.$text.textext({
plugins : 'arrow autocomplete',
autocomplete: {
@@ -4019,7 +4313,7 @@ instance.web.form.Many2ManyQuickCreate = instance.web.Widget.extend({
/**
* Class with everything which is common between FormOpenPopup and SelectCreatePopup.
*/
-instance.web.form.AbstractFormPopup = instance.web.OldWidget.extend({
+instance.web.form.AbstractFormPopup = instance.web.Widget.extend({
template: "AbstractFormPopup.render",
/**
* options:
@@ -4049,7 +4343,7 @@ instance.web.form.AbstractFormPopup = instance.web.OldWidget.extend({
this.dataset.create_function = function(data, sup) {
var fct = self.options.create_function || sup;
return fct.call(this, data).then(function(r) {
- self.created_elements.push(r.result);
+ self.created_elements.push(r);
});
};
this.dataset.write_function = function(id, data, options, sup) {
@@ -4070,8 +4364,8 @@ instance.web.form.AbstractFormPopup = instance.web.OldWidget.extend({
},
title: this.options.title || "",
buttons: [{text:"tmp"}],
- }, this.$element).open();
- this.$buttonpane = dialog.$element.dialog("widget").find(".ui-dialog-buttonpane").html("");
+ }, this.$el).open();
+ this.$buttonpane = dialog.$el.dialog("widget").find(".ui-dialog-buttonpane").html("");
this.start();
},
on_write_completed: function() {},
@@ -4094,10 +4388,13 @@ instance.web.form.AbstractFormPopup = instance.web.OldWidget.extend({
if (this.options.alternative_form_view) {
this.view_form.set_embedded_view(this.options.alternative_form_view);
}
- this.view_form.appendTo(this.$element.find(".oe_popup_form"));
+ this.view_form.appendTo(this.$el.find(".oe_popup_form"));
this.view_form.on_loaded.add_last(function() {
var multi_select = self.row_id === null && ! self.options.disable_multiple_selection;
- self.$buttonpane.html(QWeb.render("AbstractFormPopup.buttons", {multi_select: multi_select}));
+ self.$buttonpane.html(QWeb.render("AbstractFormPopup.buttons", {
+ multi_select: multi_select,
+ readonly: self.row_id !== null && self.options.readonly,
+ }));
var $snbutton = self.$buttonpane.find(".oe_abstractformpopup-form-save-new");
$snbutton.click(function() {
$.when(self.view_form.do_save()).then(function() {
@@ -4118,11 +4415,6 @@ instance.web.form.AbstractFormPopup = instance.web.OldWidget.extend({
$cbutton.click(function() {
self.check_exit();
});
- if (self.row_id !== null && self.options.readonly) {
- $snbutton.hide();
- $sbutton.hide();
- $cbutton.text(_t("Close"));
- }
self.view_form.do_show();
});
},
@@ -4136,7 +4428,7 @@ instance.web.form.AbstractFormPopup = instance.web.OldWidget.extend({
this.destroy();
},
destroy: function () {
- this.$element.dialog('close');
+ this.$el.dialog('close');
this._super();
},
});
@@ -4228,7 +4520,7 @@ instance.web.form.SelectCreatePopup = instance.web.form.AbstractFormPopup.extend
e.cancel = true;
});
self.view_list.popup = self;
- self.view_list.appendTo($(".oe_popup_list", self.$element)).pipe(function() {
+ self.view_list.appendTo($(".oe_popup_list", self.$el)).pipe(function() {
self.view_list.do_show();
}).pipe(function() {
self.searchview.do_search();
@@ -4246,7 +4538,7 @@ instance.web.form.SelectCreatePopup = instance.web.form.AbstractFormPopup.extend
});
});
});
- this.searchview.appendTo($(".oe_popup_list", self.$element));
+ this.searchview.appendTo($(".oe_popup_list", self.$el));
},
do_search: function(domains, contexts, groupbys) {
var self = this;
@@ -4259,11 +4551,12 @@ instance.web.form.SelectCreatePopup = instance.web.form.AbstractFormPopup.extend
});
},
on_click_element: function(ids) {
+ var self = this;
this.selected_ids = ids || [];
if(this.selected_ids.length > 0) {
- this.$element.find(".oe_selectcreatepopup-search-select").removeAttr('disabled');
+ self.$buttonpane.find(".oe_selectcreatepopup-search-select").removeAttr('disabled');
} else {
- this.$element.find(".oe_selectcreatepopup-search-select").attr('disabled', "disabled");
+ self.$buttonpane.find(".oe_selectcreatepopup-search-select").attr('disabled', "disabled");
}
},
new_object: function() {
@@ -4271,7 +4564,7 @@ instance.web.form.SelectCreatePopup = instance.web.form.AbstractFormPopup.extend
this.searchview.hide();
}
if (this.view_list) {
- this.view_list.$element.hide();
+ this.view_list.$el.hide();
}
this.setup_form_view();
},
@@ -4304,7 +4597,7 @@ instance.web.form.FieldReference = instance.web.form.AbstractField.extend(instan
var sel = this.selection.get_value();
this.m2o.field.relation = sel;
this.m2o.set_value(false);
- this.m2o.$element.toggle(sel !== false);
+ this.m2o.$el.toggle(sel !== false);
}
},
destroy_content: function() {
@@ -4319,11 +4612,21 @@ instance.web.form.FieldReference = instance.web.form.AbstractField.extend(instan
},
initialize_content: function() {
var self = this;
- this.selection = new instance.web.form.FieldSelection(this, { attrs: {
- name: 'selection'
+ var fm = new instance.web.form.DefaultFieldManager(this);
+ fm.extend_field_desc({
+ "selection": {
+ selection: this.field_manager.get_field_desc(this.name).selection,
+ type: "selection",
+ },
+ "m2o": {
+ relation: null,
+ type: "many2one",
+ },
+ });
+ this.selection = new instance.web.form.FieldSelection(fm, { attrs: {
+ name: 'selection',
+ modifiers: JSON.stringify({readonly: this.get('effective_readonly')}),
}});
- this.selection.view = this.view;
- this.selection.set({force_readonly: this.get('effective_readonly')});
this.selection.on("change:value", this, this.on_selection_changed);
this.selection.setElement(this.$(".oe_form_view_reference_selection"));
this.selection.renderElement();
@@ -4332,11 +4635,10 @@ instance.web.form.FieldReference = instance.web.form.AbstractField.extend(instan
.on('focused', null, function () {self.trigger('focused')})
.on('blurred', null, function () {self.trigger('blurred')});
- this.m2o = new instance.web.form.FieldMany2One(this, { attrs: {
- name: 'm2o'
+ this.m2o = new instance.web.form.FieldMany2One(fm, { attrs: {
+ name: 'm2o',
+ modifiers: JSON.stringify({readonly: this.get('effective_readonly')}),
}});
- this.m2o.view = this.view;
- this.m2o.set({force_readonly: this.get("effective_readonly")});
this.m2o.on("change:value", this, this.data_changed);
this.m2o.setElement(this.$(".oe_form_view_reference_m2o"));
this.m2o.renderElement();
@@ -4376,20 +4678,6 @@ instance.web.form.FieldReference = instance.web.form.AbstractField.extend(instan
this.set({'value': false});
}
},
- get_field: function(name) {
- if (name === "selection") {
- return {
- selection: this.view.fields_view.fields[this.name].selection,
- type: "selection",
- };
- } else if (name === "m2o") {
- return {
- relation: null,
- type: "many2one",
- };
- }
- throw Exception("Should not happen");
- },
});
instance.web.form.FieldBinary = instance.web.form.AbstractField.extend(instance.web.form.ReinitializeFieldMixin, {
@@ -4408,9 +4696,9 @@ instance.web.form.FieldBinary = instance.web.form.AbstractField.extend(instance.
this._super.apply(this, arguments);
},
initialize_content: function() {
- this.$element.find('input.oe_form_binary_file').change(this.on_file_change);
- this.$element.find('button.oe_form_binary_file_save').click(this.on_save_as);
- this.$element.find('.oe_form_binary_file_clear').click(this.on_clear);
+ this.$el.find('input.oe_form_binary_file').change(this.on_file_change);
+ this.$el.find('button.oe_form_binary_file_save').click(this.on_save_as);
+ this.$el.find('.oe_form_binary_file_clear').click(this.on_clear);
},
human_filesize : function(size) {
var units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
@@ -4427,10 +4715,10 @@ instance.web.form.FieldBinary = instance.web.form.AbstractField.extend(instance.
// http://deepliquid.com/projects/Jcrop/demos.php?demo=handler
if ($(e.target).val() !== '') {
- this.$element.find('form.oe_form_binary_form input[name=session_id]').val(this.session.session_id);
- this.$element.find('form.oe_form_binary_form').submit();
- this.$element.find('.oe_form_binary_progress').show();
- this.$element.find('.oe_form_binary').hide();
+ this.$el.find('form.oe_form_binary_form input[name=session_id]').val(this.session.session_id);
+ this.$el.find('form.oe_form_binary_form').submit();
+ this.$el.find('.oe_form_binary_progress').show();
+ this.$el.find('.oe_form_binary').hide();
}
},
on_file_uploaded: function(size, name, content_type, file_base64) {
@@ -4442,8 +4730,8 @@ instance.web.form.FieldBinary = instance.web.form.AbstractField.extend(instance.
this.filename = name;
this.on_file_uploaded_and_valid.apply(this, arguments);
}
- this.$element.find('.oe_form_binary_progress').hide();
- this.$element.find('.oe_form_binary').show();
+ this.$el.find('.oe_form_binary_progress').hide();
+ this.$el.find('.oe_form_binary').show();
},
on_file_uploaded_and_valid: function(size, name, content_type, file_base64) {
},
@@ -4477,9 +4765,10 @@ instance.web.form.FieldBinary = instance.web.form.AbstractField.extend(instance.
},
set_filename: function(value) {
var filename = this.node.attrs.filename;
- if (this.view.fields[filename]) {
- this.view.fields[filename].set_value(value);
- this.view.fields[filename].on_ui_change();
+ if (filename) {
+ var tmp = {};
+ tmp[filename] = value;
+ this.field_manager.set_values(tmp);
}
},
on_clear: function() {
@@ -4497,7 +4786,7 @@ instance.web.form.FieldBinaryFile = instance.web.form.FieldBinary.extend({
this._super();
if (this.get("effective_readonly")) {
var self = this;
- this.$element.find('a').click(function() {
+ this.$el.find('a').click(function() {
if (self.get('value')) {
self.on_save_as();
}
@@ -4517,12 +4806,14 @@ instance.web.form.FieldBinaryFile = instance.web.form.FieldBinary.extend({
} else {
show_value = (this.get('value') != null && this.get('value') !== false) ? this.get('value') : '';
}
- this.$element.find('input').eq(0).val(show_value);
+ this.$el.find('input').eq(0).val(show_value);
} else {
- this.$element.find('a').show(!!this.get('value'));
+ this.$el.find('a').show(!!this.get('value'));
if (this.get('value')) {
- var show_value = _t("Download") + " " + (this.view.datarecord[this.node.attrs.filename] || '');
- this.$element.find('a').text(show_value);
+ var show_value = _t("Download")
+ if (this.view)
+ show_value += " " + (this.view.datarecord[this.node.attrs.filename] || '');
+ this.$el.find('a').text(show_value);
}
}
},
@@ -4530,18 +4821,12 @@ instance.web.form.FieldBinaryFile = instance.web.form.FieldBinary.extend({
this.binary_value = true;
this.set({'value': file_base64});
var show_value = name + " (" + this.human_filesize(size) + ")";
- this.$element.find('input').eq(0).val(show_value);
+ this.$el.find('input').eq(0).val(show_value);
this.set_filename(name);
},
- set_filename: function(value_) {
- var filename = this.node.attrs.filename;
- if (this.view.fields[filename]) {
- this.view.fields[filename].set({value: value_});
- }
- },
on_clear: function() {
this._super.apply(this, arguments);
- this.$element.find('input').eq(0).val('');
+ this.$el.find('input').eq(0).val('');
this.set_filename('');
}
});
@@ -4553,18 +4838,31 @@ instance.web.form.FieldBinaryImage = instance.web.form.FieldBinary.extend({
this.render_value();
},
render_value: function() {
+ var self = this;
var url;
- if (this.get('value') && this.get('value').substr(0, 10).indexOf(' ') == -1) {
+ if (this.get('value') && ! /^\d+(\.\d*)? \w+$/.test(this.get('value'))) {
url = 'data:image/png;base64,' + this.get('value');
} else if (this.get('value')) {
+ var id = escape(JSON.stringify(this.view.datarecord.id || null));
+ var field = this.name;
+ if (this.options.preview_image)
+ field = this.options.preview_image;
url = '/web/binary/image?session_id=' + this.session.session_id + '&model=' +
- this.view.dataset.model +'&id=' + (this.view.datarecord.id || '') + '&field=' + this.name + '&t=' + (new Date().getTime());
+ this.view.dataset.model +'&id=' + id + '&field=' + field + '&t=' + (new Date().getTime());
} else {
url = "/web/static/src/img/placeholder.png";
}
- var img = QWeb.render("FieldBinaryImage-img", { widget: this, url: url });
- this.$element.find('> img').remove();
- this.$element.prepend(img);
+ var $img = $(QWeb.render("FieldBinaryImage-img", { widget: this, url: url }));
+ this.$el.find('> img').remove();
+ this.$el.prepend($img);
+ $img.load(function() {
+ if (! self.options.size)
+ return;
+ $img.css("max-width", "" + self.options.size[0] + "px");
+ $img.css("max-height", "" + self.options.size[1] + "px");
+ $img.css("margin-left", "" + (self.options.size[0] - $img.width()) / 2 + "px");
+ $img.css("margin-top", "" + (self.options.size[1] - $img.height()) / 2 + "px");
+ });
},
on_file_change: function() {
this.render_value();
@@ -4574,24 +4872,33 @@ instance.web.form.FieldBinaryImage = instance.web.form.FieldBinary.extend({
this.set({'value': file_base64});
this.binary_value = true;
this.render_value();
+ this.set_filename(name);
},
on_clear: function() {
this._super.apply(this, arguments);
this.render_value();
+ this.set_filename('');
}
});
instance.web.form.FieldStatus = instance.web.form.AbstractField.extend({
template: "FieldStatus",
+ init: function(field_manager, node) {
+ this._super(field_manager, node);
+ this.options.clickable = this.options.clickable || (this.node.attrs || {}).clickable || false;
+ this.options.visible = this.options.visible || (this.node.attrs || {}).statusbar_visible || false;
+ this.selected_value = null;
+ },
start: function() {
this._super();
- this.selected_value = null;
- if (this.$element.parent().is('header')) {
- this.$element.after('<div class="oe_clear"/>');
+ // backward compatibility
+ this.loaded = new $.Deferred();
+ if (this.options.clickable) {
+ this.$el.on('click','li',this.on_click_stage);
}
- // preview in start only for selection fields, because of the dynamic behavior of many2one fields.
- if (this.field.type in ['selection']) {
- this.render_list();
+ // TODO move the following into css :after
+ if (this.$el.parent().is('header')) {
+ this.$el.after('<div class="oe_clear"/>');
}
},
set_value: function(value_) {
@@ -4607,97 +4914,73 @@ instance.web.form.FieldStatus = instance.web.form.AbstractField.extend({
}
// trick to be sure all values are loaded in the form, therefore
// enabling the evaluation of dynamic domains
+ self.selection = [];
$.async_when().then(function() {
- return self.render_list();
+ self.get_selection();
});
+ return this.loaded;
},
-
- /** Get the status list and render them
- * to_show: [[identifier, value_to_display]] where
- * - identifier = key for a selection, id for a many2one
- * - display_val = label that will be displayed
- * - ex: [[0, "New"]] (many2one) or [["new", "In Progress"]] (selection)
- */
- render_list: function() {
- var self = this;
- // get selection values, filter them and render them
- var selection_done = this.get_selection().pipe(self.proxy('filter_selection')).pipe(self.proxy('render_elements'));
- },
-
- /** Get the selection list to be displayed in the statusbar widget.
+ /** Get the selection and render it
+ * selection: [[identifier, value_to_display], ...]
* For selection fields: this is directly given by this.field.selection
- * For many2one fields :
- * - perform a search on the relation of the many2one field (given by
- * field.relation )
- * - get the field domain for the search
- * - self.build_domain() gives the domain given by the view or by
- * the field
- * - if the optional statusbar_fold attribute is set to true, make
- * an AND with build_domain to hide all 'fold=true' columns
- * - make an OR with current value, to be sure it is displayed,
- * with the correct order, even if it is folded
+ * For many2one fields: perform a search on the relation of the many2one field
*/
get_selection: function() {
var self = this;
if (this.field.type == "many2one") {
- this.selection = [];
- // get fold information from widget
- var fold = ((this.node.attrs || {}).statusbar_fold || true);
- // build final domain: if fold option required, add the
- if (fold == true) {
- var domain = new instance.web.CompoundDomain(['|'], ['&'], self.build_domain(), [['fold', '=', false]], [['id', '=', self.selected_value]]);
- } else {
- var domain = new instance.web.CompoundDomain(['|'], self.build_domain(), [['id', '=', self.selected_value]]);
+ var domain = [];
+ if(!_.isEmpty(this.field.domain) || !_.isEmpty(this.node.attrs.domain)) {
+ domain = new instance.web.CompoundDomain(['|'], self.build_domain(), [['id', '=', self.selected_value]]);
}
- // get a DataSetSearch on the current field relation (ex: crm.lead.stage_id -> crm.case.stage)
- var model_ext = new instance.web.DataSetSearch(this, this.field.relation, self.build_context(), domain);
- // fetch selection
- var read_defer = model_ext.read_slice(['name'], {}).pipe( function (records) {
- _(records).each(function (record) {
- self.selection.push([record.id, record.name]);
- });
+ var ds = new instance.web.DataSetSearch(this, this.field.relation, self.build_context(), domain);
+ ds.read_slice(['name'], {}).done( function (records) {
+ for(var i = 0; i < records.length; i++) {
+ self.selection.push([records[i].id, records[i].name]);
+ }
+ self.render_elements();
+ self.loaded.resolve();
});
} else {
- this.selection = this.field.selection;
- var read_defer = new $.Deferred().resolve();
- }
- return read_defer;
- },
-
- /** Filters this.selection, according to values coming from the statusbar_visible
- * attribute of the field. For example: statusbar_visible="draft,open"
- * Currently, the key of (key, label) pairs has to be used in the
- * selection of visible items. This feature is not meant to be used
- * with many2one fields.
- */
- filter_selection: function() {
- var self = this;
- var shown = _.map(((this.node.attrs || {}).statusbar_visible || "").split(","),
- function(x) { return _.str.trim(x); });
- shown = _.select(shown, function(x) { return x.length > 0; });
-
- if (shown.length == 0) {
- this.to_show = this.selection;
- } else {
- this.to_show = _.select(this.selection, function(x) {
- return _.indexOf(shown, x[0]) !== -1 || x[0] === self.selected_value;
- });
+ this.loaded.resolve();
+ // For field type selection filter values according to
+ // statusbar_visible attribute of the field. For example:
+ // statusbar_visible="draft,open".
+ var selection = this.field.selection;
+ for(var i=0; i< selection.length; i++) {
+ var key = selection[i][0];
+ if(key == this.selected_value || !this.options.visible || this.options.visible.indexOf(key) != -1) {
+ this.selection.push(selection[i]);
+ }
+ }
+ this.render_elements();
}
},
-
/** Renders the widget. This function also checks for statusbar_colors='{"pending": "blue"}'
* attribute in the widget. This allows to set a given color to a given
* state (given by the key of (key, label)).
*/
render_elements: function () {
- var content = instance.web.qweb.render("FieldStatus.content", {widget: this, _:_});
- this.$element.html(content);
-
+ var self = this;
+ var content = instance.web.qweb.render("FieldStatus.content", {widget: this});
+ this.$el.html(content);
var colors = JSON.parse((this.node.attrs || {}).statusbar_colors || "{}");
var color = colors[this.selected_value];
if (color) {
- var elem = this.$element.find("li.oe_form_steps_active span");
- elem.css("color", color);
+ this.$("oe_active").css("color", color);
+ }
+ },
+ on_click_stage: function (ev) {
+ var self = this;
+ var $li = $(ev.currentTarget);
+ var val = parseInt($li.data("id"));
+ if (val != self.selected_value) {
+ this.view.recursive_save().then(function() {
+ var change = {};
+ change[self.name] = val;
+ self.view.dataset.write(self.view.datarecord.id, change).then(function() {
+ self.view.reload();
+ });
+ });
}
},
});
@@ -4714,7 +4997,7 @@ instance.web.form.widgets = new instance.web.Registry({
'email' : 'instance.web.form.FieldEmail',
'url' : 'instance.web.form.FieldUrl',
'text' : 'instance.web.form.FieldText',
- 'text_html' : 'instance.web.form.FieldTextHtml',
+ 'html' : 'instance.web.form.FieldTextHtml',
'date' : 'instance.web.form.FieldDate',
'datetime' : 'instance.web.form.FieldDatetime',
'selection' : 'instance.web.form.FieldSelection',
@@ -4745,6 +5028,9 @@ instance.web.form.tags = new instance.web.Registry({
'button' : 'instance.web.form.WidgetButton',
});
+instance.web.form.custom_widgets = new instance.web.Registry({
+});
+
};
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:
diff --git a/addons/web/static/src/js/view_list.js b/addons/web/static/src/js/view_list.js
index 57be3ca6db1..fd34ffddb7b 100644
--- a/addons/web/static/src/js/view_list.js
+++ b/addons/web/static/src/js/view_list.js
@@ -21,8 +21,6 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
// whether the view rows can be reordered (via vertical drag & drop)
'reorderable': true,
'action_buttons': true,
- // if true, the 'Import', 'Export', etc... buttons will be shown
- 'import_enabled': true,
},
/**
* Core class for list-type displays.
@@ -145,7 +143,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
* @returns {$.Deferred} loading promise
*/
start: function() {
- this.$element.addClass('oe_list');
+ this.$el.addClass('oe_list');
return this.reload_view(null, null, true);
},
/**
@@ -248,20 +246,26 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
this.setup_columns(this.fields_view.fields, grouped);
- this.$element.html(QWeb.render(this._template, this));
- this.$element.addClass(this.fields_view.arch.attrs['class']);
+ this.$el.html(QWeb.render(this._template, this));
+ this.$el.addClass(this.fields_view.arch.attrs['class']);
+
+ // add css classes that reflect the (absence of) access rights
+ this.$el.toggleClass('oe_list_cannot_create', !this.is_action_enabled('create'))
+ .toggleClass('oe_list_cannot_edit', !this.is_action_enabled('edit'))
+ .toggleClass('oe_list_cannot_delete', !this.is_action_enabled('delete'));
+
// Head hook
// Selecting records
- this.$element.find('.oe_list_record_selector').click(function(){
- self.$element.find('.oe_list_record_selector input').prop('checked',
- self.$element.find('.oe_list_record_selector').prop('checked') || false);
+ this.$el.find('.oe_list_record_selector').click(function(){
+ self.$el.find('.oe_list_record_selector input').prop('checked',
+ self.$el.find('.oe_list_record_selector').prop('checked') || false);
var selection = self.groups.get_selection();
$(self.groups).trigger(
'selected', [selection.ids, selection.records]);
});
// Sorting columns
- this.$element.find('thead').delegate('th.oe_sortable[data-id]', 'click', function (e) {
+ this.$el.find('thead').delegate('th.oe_sortable[data-id]', 'click', function (e) {
e.stopPropagation();
var $this = $(this);
self.dataset.sort($this.data('id'));
@@ -275,21 +279,17 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
self.reload_content();
});
- // Add button and Import link
+ // Add button
if (!this.$buttons) {
this.$buttons = $(QWeb.render("ListView.buttons", {'widget':self}));
if (this.options.$buttons) {
this.$buttons.appendTo(this.options.$buttons);
} else {
- this.$element.find('.oe_list_buttons').replaceWith(this.$buttons);
+ this.$el.find('.oe_list_buttons').replaceWith(this.$buttons);
}
this.$buttons.find('.oe_list_add')
.click(this.proxy('do_add_record'))
.prop('disabled', grouped);
- this.$buttons.on('click', '.oe_list_button_import', function() {
- self.on_sidebar_import();
- return false;
- });
}
// Pager
@@ -298,7 +298,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
if (this.options.$buttons) {
this.$pager.appendTo(this.options.$pager);
} else {
- this.$element.find('.oe_list_pager').replaceWith(this.$pager);
+ this.$el.find('.oe_list_pager').replaceWith(this.$pager);
}
this.$pager
@@ -351,13 +351,12 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
if (!this.sidebar && this.options.$sidebar) {
this.sidebar = new instance.web.Sidebar(this);
this.sidebar.appendTo(this.options.$sidebar);
- this.sidebar.add_items('other', [
- { label: _t("Import"), callback: this.on_sidebar_import },
+ this.sidebar.add_items('other', _.compact([
{ label: _t("Export"), callback: this.on_sidebar_export },
- { label: _t('Delete'), callback: this.do_delete_selected }
- ]);
+ self.is_action_enabled('delete') && { label: _t('Delete'), callback: this.do_delete_selected }
+ ]));
this.sidebar.add_toolbar(this.fields_view.toolbar);
- this.sidebar.$element.hide();
+ this.sidebar.$el.hide();
}
},
/**
@@ -399,73 +398,26 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
* @param {Boolean} [grouped] Should the grouping columns (group and count) be displayed
*/
setup_columns: function (fields, grouped) {
- var domain_computer = instance.web.form.compute_domain;
-
- var noop = function () { return {}; };
- var field_to_column = function (field) {
- var name = field.attrs.name;
- var column = _.extend({id: name, tag: field.tag},
- fields[name], field.attrs);
- // modifiers computer
- if (column.modifiers) {
- var modifiers = JSON.parse(column.modifiers);
- column.modifiers_for = function (fields) {
- var out = {};
-
- for (var attr in modifiers) {
- if (!modifiers.hasOwnProperty(attr)) { continue; }
- var modifier = modifiers[attr];
- out[attr] = _.isBoolean(modifier)
- ? modifier
- : domain_computer(modifier, fields);
- }
-
- return out;
- };
- if (modifiers['tree_invisible']) {
- column.invisible = '1';
- } else {
- delete column.invisible;
- }
- column.modifiers = modifiers;
- } else {
- column.modifiers_for = noop;
- column.modifiers = {};
- }
- return column;
- };
-
+ var registry = instance.web.list.columns;
+ var reorder = this.options.reorderable;
this.columns.splice(0, this.columns.length);
- this.columns.push.apply(
- this.columns,
- _(this.fields_view.arch.children).map(field_to_column));
+ this.columns.push.apply(this.columns,
+ _(this.fields_view.arch.children).map(function (field) {
+ var id = field.attrs.name;
+ if(field.attrs.widget == 'handle' && !reorder){
+ field.attrs.reorderable = reorder || true;
+ }
+ return registry.for_(id, fields[id], field);
+ }));
if (grouped) {
- this.columns.unshift({
- id: '_group', tag: '', string: _t("Group"), meta: true,
- modifiers_for: function () { return {}; },
- modifiers: {}
- });
+ this.columns.unshift(
+ new instance.web.list.MetaColumn('_group', _t("Group")));
}
this.visible_columns = _.filter(this.columns, function (column) {
- return column.invisible !== '1';
+ return column.invisible !== '1' && !column.reorderable;
});
-
- this.aggregate_columns = _(this.visible_columns)
- .map(function (column) {
- if (column.type !== 'integer' && column.type !== 'float') {
- return {};
- }
- var aggregation_func = column['group_operator'] || 'sum';
- if (!(aggregation_func in column)) {
- return {};
- }
-
- return _.extend({}, column, {
- 'function': aggregation_func,
- label: column[aggregation_func]
- });
- });
+ this.aggregate_columns = _(this.visible_columns).invoke('to_aggregate');
},
/**
* Used to handle a click on a table row, if no other handler caught the
@@ -500,7 +452,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
},
do_hide: function () {
if (this.sidebar) {
- this.sidebar.$element.hide();
+ this.sidebar.$el.hide();
}
if (this.$buttons) {
this.$buttons.hide();
@@ -524,7 +476,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
if (this.embedded_view) {
return $.Deferred().then(callback).resolve(this.embedded_view);
} else {
- return this.rpc('/web/listview/load', {
+ return this.rpc('/web/view/load', {
model: this.model,
view_id: this.view_id,
view_type: "tree",
@@ -540,10 +492,10 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
*/
reload_content: function () {
var self = this;
- self.$element.find('.oe_list_record_selector').prop('checked', false);
+ self.$el.find('.oe_list_record_selector').prop('checked', false);
this.records.reset();
var reloaded = $.Deferred();
- this.$element.find('.oe_list_content').append(
+ this.$el.find('.oe_list_content').append(
this.groups.render(function () {
if (self.dataset.index == null) {
var has_one = false;
@@ -645,7 +597,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
if (!ids.length) {
this.dataset.index = 0;
if (this.sidebar) {
- this.sidebar.$element.hide();
+ this.sidebar.$el.hide();
}
this.compute_aggregates();
return;
@@ -653,7 +605,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
this.dataset.index = _(this.dataset.ids).indexOf(ids[0]);
if (this.sidebar) {
- this.sidebar.$element.show();
+ this.sidebar.$el.show();
}
this.compute_aggregates(_(records).map(function (record) {
@@ -674,6 +626,10 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
* Base handling of buttons, can be called when overriding do_button_action
* in order to bypass parent overrides.
*
+ * The callback will be provided with the ``id`` as its parameter, in case
+ * handle_button's caller had to alter the ``id`` (or even create one)
+ * while not being ``callback``'s creator.
+ *
* This method should not be overridden.
*
* @param {String} name action name
@@ -699,7 +655,8 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
c.add(action.context);
}
action.context = c;
- this.do_execute_action(action, this.dataset, id, callback);
+ this.do_execute_action(
+ action, this.dataset, id, _.bind(callback, null, id));
},
/**
* Handles the activation of a record (clicking on it)
@@ -808,16 +765,14 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
},
display_aggregates: function (aggregation) {
var self = this;
- var $footer_cells = this.$element.find('.oe_list_footer');
+ var $footer_cells = this.$el.find('.oe_list_footer');
_(this.aggregate_columns).each(function (column) {
if (!column['function']) {
return;
}
$footer_cells.filter(_.str.sprintf('[data-field=%s]', column.id))
- .html(instance.web.format_cell(aggregation, column, {
- process_modifiers: false
- }));
+ .html(column.format(aggregation, { process_modifiers: false }));
});
},
get_selected_ids: function() {
@@ -836,7 +791,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
pad_columns: function (count, options) {
options = options || {};
// padding for action/pager header
- var $first_header = this.$element.find('thead tr:first th');
+ var $first_header = this.$el.find('thead tr:first th');
var colspan = $first_header.attr('colspan');
if (colspan) {
if (!this.previous_colspan) {
@@ -845,7 +800,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
$first_header.attr('colspan', parseInt(colspan, 10) + count);
}
// Padding for column titles, footer and data rows
- var $rows = this.$element
+ var $rows = this.$el
.find('.oe_list_header_columns, tr:not(thead tr)')
.not(options['except']);
var newcols = new Array(count+1).join('<td class="oe_list_padding"></td>');
@@ -859,25 +814,29 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
* Removes all padding columns of the table
*/
unpad_columns: function () {
- this.$element.find('.oe_list_padding').remove();
+ this.$el.find('.oe_list_padding').remove();
if (this.previous_colspan) {
- this.$element
+ this.$el
.find('thead tr:first th')
.attr('colspan', this.previous_colspan);
this.previous_colspan = null;
}
},
no_result: function () {
- this.$element.find('.oe_view_nocontent').remove();
+ this.$el.find('.oe_view_nocontent').remove();
if (this.groups.group_by
|| !this.options.action
|| !this.options.action.help) {
return;
}
- this.$element.find('table:first').hide();
- this.$element.prepend(
+ this.$el.find('table:first').hide();
+ this.$el.prepend(
$('<div class="oe_view_nocontent">').html(this.options.action.help)
);
+ var create_nocontent = this.$buttons;
+ this.$el.find('.oe_view_nocontent').click(function() {
+ create_nocontent.effect('bounce', {distance: 18, times: 5}, 150);
+ });
}
});
instance.web.ListView.List = instance.web.Class.extend( /** @lends instance.web.ListView.List# */{
@@ -967,8 +926,7 @@ instance.web.ListView.List = instance.web.Class.extend( /** @lends instance.web.
this.records.bind(event, callback);
}, this);
- this.$_element = $('<tbody>')
- .appendTo(document.body)
+ this.$current = $('<tbody>')
.delegate('th.oe_list_record_selector', 'click', function (e) {
e.stopPropagation();
var selection = self.get_selection();
@@ -991,8 +949,8 @@ instance.web.ListView.List = instance.web.Class.extend( /** @lends instance.web.
// of digits, nice when storing actual numbers, not nice when
// storing strings composed only of digits. Force the action
// name to be a string
- $(self).trigger('action', [field.toString(), record_id, function () {
- return self.reload_record(self.records.get(record_id));
+ $(self).trigger('action', [field.toString(), record_id, function (id) {
+ return self.reload_record(self.records.get(id));
}]);
})
.delegate('a', 'click', function (e) {
@@ -1031,7 +989,7 @@ instance.web.ListView.List = instance.web.Class.extend( /** @lends instance.web.
// to get a correctly displayable value in the field
var model = ref_match[1],
id = parseInt(ref_match[2], 10);
- new instance.web.DataSet(this.view, model).name_get([id], function(names) {
+ new instance.web.DataSet(this.view, model).name_get([id]).then(function(names) {
if (!names.length) { return; }
record.set(column.id, names[0][1]);
});
@@ -1047,23 +1005,40 @@ instance.web.ListView.List = instance.web.Class.extend( /** @lends instance.web.
// and let the various registered events handle refreshing the
// row
new instance.web.DataSet(this.view, column.relation)
- .name_get([value], function (names) {
+ .name_get([value]).then(function (names) {
if (!names.length) { return; }
record.set(column.id, names[0]);
});
}
+ } else if (column.type === 'many2many') {
+ value = record.get(column.id);
+ // non-resolved (string) m2m values are arrays
+ if (value instanceof Array && !_.isEmpty(value)) {
+ var ids;
+ // they come in two shapes:
+ if (value[0] instanceof Array) {
+ var command = value[0];
+ // 1. an array of m2m commands (usually (6, false, ids))
+ if (command[0] !== 6) {
+ throw new Error(_t("Unknown m2m command ") + command[0]);
+ }
+ ids = command[2];
+ } else {
+ // 2. an array of ids
+ ids = value;
+ }
+ new instance.web.Model(column.relation)
+ .call('name_get', [ids]).then(function (names) {
+ record.set(column.id, _(names).pluck(1).join(', '));
+ })
+ }
}
- return instance.web.format_cell(record.toForm().data, column, {
+ return column.format(record.toForm().data, {
model: this.dataset.model,
id: record.get('id')
});
},
render: function () {
- var self = this;
- if (this.$current) {
- this.$current.remove();
- }
- this.$current = this.$_element.clone(true);
this.$current.empty().append(
QWeb.render('ListView.rows', _.extend({
render_cell: function () {
@@ -1139,7 +1114,6 @@ instance.web.ListView.List = instance.web.Class.extend( /** @lends instance.web.
if (!this.$current) { return; }
this.$current.remove();
this.$current = null;
- this.$_element.remove();
},
get_records: function () {
return this.records.map(function (record) {
@@ -1306,7 +1280,7 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we
self.bind_child_events(child);
child.datagroup = group;
- var $row = child.$row = $('<tr>');
+ var $row = child.$row = $('<tr class="oe_group_header">');
if (group.openable && group.length) {
$row.click(function (e) {
if (!$row.data('open')) {
@@ -1340,10 +1314,9 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we
}
var group_label;
try {
- group_label = instance.web.format_cell(
- row_data, group_column, {
- value_if_empty: _t("Undefined"),
- process_modifiers: false
+ group_label = group_column.format(row_data, {
+ value_if_empty: _t("Undefined"),
+ process_modifiers: false
});
} catch (e) {
group_label = row_data[group_column.id].value;
@@ -1367,7 +1340,7 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we
$row.append('<td>');
}
_(self.columns).chain()
- .filter(function (column) {return !column.invisible;})
+ .filter(function (column) { return column.invisible !== '1'; })
.each(function (column) {
if (column.meta) {
// do not do anything
@@ -1375,8 +1348,7 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we
var r = {};
r[column.id] = {value: group.aggregates[column.id]};
$('<td class="oe_number">')
- .html(instance.web.format_cell(
- r, column, {process_modifiers: false}))
+ .html(column.format(r, {process_modifiers: false}))
.appendTo($row);
} else {
$row.append('<td>');
@@ -1463,18 +1435,31 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we
},
setup_resequence_rows: function (list, dataset) {
// drag and drop enabled if list is not sorted and there is a
- // "sequence" column in the view.
+ // visible column with @widget=handle or "sequence" column in the view.
if ((dataset.sort && dataset.sort())
|| !_(this.columns).any(function (column) {
- return column.name === 'sequence'; })) {
+ return column.widget === 'handle'
+ || column.name === 'sequence'; })) {
return;
}
+ var sequence_field = _(this.columns).find(function (c) {
+ return c.widget === 'handle';
+ });
+ var seqname = sequence_field ? sequence_field.name : 'sequence';
+
// ondrop, move relevant record & fix sequences
list.$current.sortable({
axis: 'y',
items: '> tr[data-id]',
- containment: 'parent',
- helper: 'clone',
+ helper: 'clone'
+ });
+ if (sequence_field) {
+ list.$current.sortable('option', 'handle', '.oe_list_field_handle');
+ }
+ list.$current.sortable('option', {
+ start: function (e, ui) {
+ ui.placeholder.height(ui.item.height());
+ },
stop: function (event, ui) {
var to_move = list.records.get(ui.item.data('id')),
target_id = ui.item.prev().data('id'),
@@ -1492,7 +1477,7 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we
var record, index = to,
// if drag to 1st row (to = 0), start sequencing from 0
// (exclusive lower bound)
- seq = to ? list.records.at(to - 1).get('sequence') : 0;
+ seq = to ? list.records.at(to - 1).get(seqname) : 0;
while (++seq, record = list.records.at(index++)) {
// write are independent from one another, so we can just
// launch them all at the same time and we don't really
@@ -1502,37 +1487,39 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we
// when synchronous (without setTimeout)
(function (dataset, id, seq) {
$.async_when().then(function () {
- dataset.write(id, {sequence: seq});
+ var attrs = {};
+ attrs[seqname] = seq;
+ dataset.write(id, attrs);
});
}(dataset, record.get('id'), seq));
- record.set('sequence', seq);
+ record.set(seqname, seq);
}
}
});
},
render: function (post_render) {
var self = this;
- var $element = $('<tbody>');
- this.elements = [$element[0]];
+ var $el = $('<tbody>');
+ this.elements = [$el[0]];
this.datagroup.list(
_(this.view.visible_columns).chain()
.filter(function (column) { return column.tag === 'field' })
.pluck('name').value(),
function (groups) {
- $element[0].appendChild(
+ $el[0].appendChild(
self.render_groups(groups));
if (post_render) { post_render(); }
}, function (dataset) {
self.render_dataset(dataset).then(function (list) {
self.children[null] = list;
self.elements =
- [list.$current.replaceAll($element)[0]];
+ [list.$current.replaceAll($el)[0]];
self.setup_resequence_rows(list, dataset);
if (post_render) { post_render(); }
});
});
- return $element;
+ return $el;
},
/**
* Returns the ids of all selected records for this group, and the records
@@ -1959,6 +1946,213 @@ instance.web.list = {
Events: Events,
Record: Record,
Collection: Collection
-}
+};
+/**
+ * Registry for column objects used to format table cells (and some other tasks
+ * e.g. aggregation computations).
+ *
+ * Maps a field or button to a Column type via its ``$tag.$widget``,
+ * ``$tag.$type`` or its ``$tag`` (alone).
+ *
+ * This specific registry has a dedicated utility method ``for_`` taking a
+ * field (from fields_get/fields_view_get.field) and a node (from a view) and
+ * returning the right object *already instantiated from the data provided*.
+ *
+ * @type {instance.web.Registry}
+ */
+instance.web.list.columns = new instance.web.Registry({
+ 'field': 'instance.web.list.Column',
+ 'field.boolean': 'instance.web.list.Boolean',
+ 'field.binary': 'instance.web.list.Binary',
+ 'field.progressbar': 'instance.web.list.ProgressBar',
+ 'field.handle': 'instance.web.list.Handle',
+ 'button': 'instance.web.list.Button',
+});
+instance.web.list.columns.for_ = function (id, field, node) {
+ var description = _.extend({tag: node.tag}, field, node.attrs);
+ var tag = description.tag;
+ var Type = this.get_any([
+ tag + '.' + description.widget,
+ tag + '.'+ description.type,
+ tag
+ ]);
+ return new Type(id, node.tag, description)
+};
+
+instance.web.list.Column = instance.web.Class.extend({
+ init: function (id, tag, attrs) {
+ _.extend(attrs, {
+ id: id,
+ tag: tag
+ });
+
+ this.modifiers = attrs.modifiers ? JSON.parse(attrs.modifiers) : {};
+ delete attrs.modifiers;
+ _.extend(this, attrs);
+
+ if (this.modifiers['tree_invisible']) {
+ this.invisible = '1';
+ } else { delete this.invisible; }
+ },
+ modifiers_for: function (fields) {
+ var out = {};
+ var domain_computer = instance.web.form.compute_domain;
+
+ for (var attr in this.modifiers) {
+ if (!this.modifiers.hasOwnProperty(attr)) { continue; }
+ var modifier = this.modifiers[attr];
+ out[attr] = _.isBoolean(modifier)
+ ? modifier
+ : domain_computer(modifier, fields);
+ }
+
+ return out;
+ },
+ to_aggregate: function () {
+ if (this.type !== 'integer' && this.type !== 'float') {
+ return {};
+ }
+ var aggregation_func = this['group_operator'] || 'sum';
+ if (!(aggregation_func in this)) {
+ return {};
+ }
+ var C = function (fn, label) {
+ this['function'] = fn;
+ this.label = label;
+ };
+ C.prototype = this;
+ return new C(aggregation_func, this[aggregation_func]);
+ },
+ /**
+ *
+ * @param row_data record whose values should be displayed in the cell
+ * @param {Object} [options]
+ * @param {String} [options.value_if_empty=''] what to display if the field's value is ``false``
+ * @param {Boolean} [options.process_modifiers=true] should the modifiers be computed ?
+ * @param {String} [options.model] current record's model
+ * @param {Number} [options.id] current record's id
+ * @return {String}
+ */
+ format: function (row_data, options) {
+ options = options || {};
+ var attrs = {};
+ if (options.process_modifiers !== false) {
+ attrs = this.modifiers_for(row_data);
+ }
+ if (attrs.invisible) { return ''; }
+
+ if (!row_data[this.id]) {
+ return options.value_if_empty === undefined
+ ? ''
+ : options.value_if_empty;
+ }
+ return this._format(row_data, options);
+ },
+ /**
+ * Method to override in order to provide alternative HTML content for the
+ * cell. Column._format will simply call ``instance.web.format_value`` and
+ * escape the output.
+ *
+ * The output of ``_format`` will *not* be escaped by ``format``, any
+ * escaping *must be done* by ``format``.
+ *
+ * @private
+ */
+ _format: function (row_data, options) {
+ return _.escape(instance.web.format_value(
+ row_data[this.id].value, this, options.value_if_empty));
+ }
+});
+instance.web.list.MetaColumn = instance.web.list.Column.extend({
+ meta: true,
+ init: function (id, string) {
+ this._super(id, '', {string: string});
+ }
+});
+instance.web.list.Button = instance.web.list.Column.extend({
+ /**
+ * Return an actual ``<button>`` tag
+ */
+ format: function (row_data, options) {
+ options = options || {};
+ var attrs = {};
+ if (options.process_modifiers !== false) {
+ attrs = this.modifiers_for(row_data);
+ }
+ if (attrs.invisible) { return ''; }
+
+ return QWeb.render('ListView.row.button', {
+ widget: this,
+ prefix: instance.session.prefix,
+ disabled: attrs.readonly
+ || isNaN(row_data.id.value)
+ || instance.web.BufferedDataSet.virtual_id_regex.test(row_data.id.value)
+ });
+ }
+});
+instance.web.list.Boolean = instance.web.list.Column.extend({
+ /**
+ * Return a potentially disabled checkbox input
+ *
+ * @private
+ */
+ _format: function (row_data, options) {
+ return _.str.sprintf('<input type="checkbox" %s disabled="disabled"/>',
+ row_data[this.id].value ? 'checked="checked"' : '');
+ }
+});
+instance.web.list.Binary = instance.web.list.Column.extend({
+ /**
+ * Return a link to the binary data as a file
+ *
+ * @private
+ */
+ _format: function (row_data, options) {
+ var text = _t("Download");
+ var download_url = _.str.sprintf(
+ '/web/binary/saveas?session_id=%s&model=%s&field=%s&id=%d',
+ instance.session.session_id, options.model, this.id, options.id);
+ if (this.filename) {
+ download_url += '&filename_field=' + this.filename;
+ if (row_data[this.filename]) {
+ text = _.str.sprintf(_t("Download \"%s\""), instance.web.format_value(
+ row_data[this.filename].value, {type: 'char'}));
+ }
+ }
+ return _.template('<a href="<%-href%>"><%-text%></a> (%<-size%>)', {
+ text: text,
+ href: download_url,
+ size: row_data[this.id].value
+ });
+ }
+});
+instance.web.list.ProgressBar = instance.web.list.Column.extend({
+ /**
+ * Return a formatted progress bar display
+ *
+ * @private
+ */
+ _format: function (row_data, options) {
+ return _.template(
+ '<progress value="<%-value%>" max="100"><%-value%>%</progress>', {
+ value: _.str.sprintf("%.0f", row_data[this.id].value || 0)
+ });
+ }
+});
+instance.web.list.Handle = instance.web.list.Column.extend({
+ init: function () {
+ this._super.apply(this, arguments);
+ // Handle overrides the field to not be form-editable.
+ this.modifiers.readonly = true;
+ },
+ /**
+ * Return styling hooks for a drag handle
+ *
+ * @private
+ */
+ _format: function (row_data, options) {
+ return '<div class="oe_list_handle">';
+ }
+});
};
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:
diff --git a/addons/web/static/src/js/view_list_editable.js b/addons/web/static/src/js/view_list_editable.js
index 038593c54c3..892221a347b 100644
--- a/addons/web/static/src/js/view_list_editable.js
+++ b/addons/web/static/src/js/view_list_editable.js
@@ -45,16 +45,22 @@ openerp.web.list_editable = function (instance) {
}
});
this.on('edit:after', this, function () {
- self.$element.add(self.$buttons).addClass('oe_editing');
+ self.$el.add(self.$buttons).addClass('oe_editing');
});
this.on('save:after cancel:after', this, function () {
- self.$element.add(self.$buttons).removeClass('oe_editing');
+ self.$el.add(self.$buttons).removeClass('oe_editing');
});
},
destroy: function () {
instance.web.bus.off('resize', this, this.resize_fields);
this._super();
},
+ do_hide: function () {
+ if (this.editor.is_editing()) {
+ this.cancel_edition(true);
+ }
+ this._super();
+ },
/**
* Handles the activation of a record in editable mode (making a record
* editable), called *after* the record has become editable.
@@ -87,8 +93,8 @@ openerp.web.list_editable = function (instance) {
*/
do_add_record: function () {
if (this.editable()) {
- this.$element.find('table:first').show();
- this.$element.find('.oe_view_nocontent').remove();
+ this.$el.find('table:first').show();
+ this.$el.find('.oe_view_nocontent').remove();
this.start_edition();
} else {
this._super();
@@ -99,6 +105,7 @@ openerp.web.list_editable = function (instance) {
// tree/@editable takes priority on everything else if present.
var result = this._super(data, grouped);
if (this.editable()) {
+ this.$el.addClass('oe_list_editable');
// FIXME: any hook available to ensure this is only done once?
this.$buttons
.off('click', '.oe_list_save')
@@ -108,7 +115,7 @@ openerp.web.list_editable = function (instance) {
e.preventDefault();
self.cancel_edition();
});
- this.$element
+ this.$el
.off('click', 'tbody td:not(.oe_list_field_cell)')
.on('click', 'tbody td:not(.oe_list_field_cell)', function () {
if (!self.editor.is_editing()) {
@@ -119,10 +126,12 @@ openerp.web.list_editable = function (instance) {
// Editor is not restartable due to formview not being
// restartable
this.editor = this.make_editor();
- var editor_ready = this.editor.prependTo(this.$element)
+ var editor_ready = this.editor.prependTo(this.$el)
.then(this.proxy('setup_events'));
return $.when(result, editor_ready);
+ } else {
+ this.$el.removeClass('oe_list_editable');
}
return result;
@@ -135,10 +144,13 @@ openerp.web.list_editable = function (instance) {
make_editor: function () {
return new instance.web.list.Editor(this);
},
- do_button_action: function () {
+ do_button_action: function (name, id, callback) {
var self = this, args = arguments;
- this.ensure_saved().then(function () {
- self.handle_button.apply(self, args);
+ this.ensure_saved().then(function (done) {
+ if (!id && done.created) {
+ id = done.record.get('id');
+ }
+ self.handle_button.call(self, name, id, callback);
});
},
/**
@@ -197,7 +209,7 @@ openerp.web.list_editable = function (instance) {
}
// FIXME: need better way to get the field back from bubbling (delegated) DOM events somehow
- field.$element.attr('data-fieldname', field_name);
+ field.$el.attr('data-fieldname', field_name);
self.fields_for_resize.push({field: field, cell: cell});
}, options).pipe(function () {
$recordRow.addClass('oe_edition');
@@ -242,10 +254,11 @@ openerp.web.list_editable = function (instance) {
var $cell = $(cell);
var position = $cell.position();
- field.$element.css({
+ // jquery does not understand !important
+ field.$el.attr('style', 'width: '+$cell.outerWidth()+'px !important');
+ field.$el.css({
top: position.top,
left: position.left,
- width: $cell.outerWidth(),
minHeight: $cell.outerHeight()
});
},
@@ -281,16 +294,17 @@ openerp.web.list_editable = function (instance) {
});
},
/**
+ * @param {Boolean} [force=false] discards the data even if the form has been edited
* @return {jQuery.Deferred}
*/
- cancel_edition: function () {
+ cancel_edition: function (force) {
var self = this;
return this.with_event('cancel', {
editor: this.editor,
form: this.editor.form,
cancel: false
}, function () {
- return this.editor.cancel().pipe(function (attrs) {
+ return this.editor.cancel(force).pipe(function (attrs) {
if (attrs.id) {
var record = self.records.get(attrs.id);
if (!record) {
@@ -346,14 +360,17 @@ openerp.web.list_editable = function (instance) {
'class': 'oe_form_container',
version: '7.0'
});
- _(view.arch.children).each(function (widget) {
- var modifiers = JSON.parse(widget.attrs.modifiers || '{}');
- widget.attrs.nolabel = true;
- if (modifiers['tree_invisible'] || widget.tag === 'button') {
- modifiers.invisible = true;
- }
- widget.attrs.modifiers = JSON.stringify(modifiers);
- });
+ _(view.arch.children).chain()
+ .zip(this.columns)
+ .each(function (ar) {
+ var widget = ar[0], column = ar[1];
+ var modifiers = _.extend({}, column.modifiers);
+ widget.attrs.nolabel = true;
+ if (modifiers['tree_invisible'] || widget.tag === 'button') {
+ modifiers.invisible = true;
+ }
+ widget.attrs.modifiers = JSON.stringify(modifiers);
+ });
return view;
},
handle_onwrite: function (source_record) {
@@ -383,7 +400,7 @@ openerp.web.list_editable = function (instance) {
},
setup_events: function () {
var self = this;
- this.editor.$element.on('keyup keydown', function (e) {
+ this.editor.$el.on('keyup keydown', function (e) {
if (!self.editor.is_editing()) { return; }
var key = _($.ui.keyCode).chain()
.map(function (v, k) { return {name: k, code: v}; })
@@ -532,7 +549,7 @@ openerp.web.list_editable = function (instance) {
if (--field_index < 0) { return $.when(); }
field = fields[fields_order[field_index]];
- } while (!field.$element.is(':visible'));
+ } while (!field.$el.is(':visible'));
// and focus it
field.focus();
@@ -555,7 +572,7 @@ openerp.web.list_editable = function (instance) {
if (++field_index >= fields_order.length) { return $.when(); }
field = fields[fields_order[field_index]];
- } while (!field.$element.is(':visible'));
+ } while (!field.$el.is(':visible'));
field.focus();
return $.when();
@@ -564,11 +581,11 @@ openerp.web.list_editable = function (instance) {
var form = this.editor.form;
var last_field = _(form.fields_order).chain()
.map(function (name) { return form.fields[name]; })
- .filter(function (field) { return field.$element.is(':visible'); })
+ .filter(function (field) { return field.$el.is(':visible'); })
.last()
.value();
// tabbed from last field in form
- if (last_field && last_field.$element.has(e.target).length) {
+ if (last_field && last_field.$el.has(e.target).length) {
e.preventDefault();
return this._next();
}
@@ -612,7 +629,7 @@ openerp.web.list_editable = function (instance) {
var _super = this._super();
this.form.embedded_view = this._validate_view(
this.delegate.edition_view(this));
- var form_ready = this.form.appendTo(this.$element).then(
+ var form_ready = this.form.appendTo(this.$el).then(
self.form.proxy('do_hide'));
return $.when(_super, form_ready);
},
@@ -670,7 +687,7 @@ openerp.web.list_editable = function (instance) {
// Is actually in the form
&& (field = form.fields[focus_field])
// And is visible
- && field.$element.is(':visible')) {
+ && field.$el.is(':visible')) {
// focus it
field.focus();
return;
@@ -679,7 +696,7 @@ openerp.web.list_editable = function (instance) {
_(form.fields_order).detect(function (name) {
// look for first visible field in fields_order, focus it
var field = form.fields[name];
- if (!field.$element.is(':visible')) {
+ if (!field.$el.is(':visible')) {
return false;
}
// Stop as soon as a field got focused
@@ -708,7 +725,7 @@ openerp.web.list_editable = function (instance) {
save: function () {
var self = this;
return this.form
- .do_save(null, this.delegate.prepends_on_create())
+ .do_save(this.delegate.prepends_on_create())
.pipe(function (result) {
var created = result.created && !self.record.id;
if (created) {
@@ -717,13 +734,13 @@ openerp.web.list_editable = function (instance) {
return self.cancel();
});
},
- cancel: function () {
- var record = this.record;
- this.record = null;
- if (!this.form.can_be_discarded()) {
+ cancel: function (force) {
+ if (!(force || this.form.can_be_discarded())) {
return $.Deferred().reject({
message: "The form's data can not be discarded"}).promise();
}
+ var record = this.record;
+ this.record = null;
this.form.do_hide();
return $.when(record);
}
@@ -742,7 +759,7 @@ openerp.web.list_editable = function (instance) {
instance.web.ListView.List.include(/** @lends instance.web.ListView.List# */{
row_clicked: function (event) {
- if (!this.view.editable()) {
+ if (!this.view.editable() || ! this.view.is_action_enabled('edit')) {
return this._super.apply(this, arguments);
}
var record_id = $(event.currentTarget).data('id');
diff --git a/addons/web/static/src/js/view_tree.js b/addons/web/static/src/js/view_tree.js
index 2181b2aa157..e0493a446e2 100644
--- a/addons/web/static/src/js/view_tree.js
+++ b/addons/web/static/src/js/view_tree.js
@@ -84,13 +84,13 @@ instance.web.TreeView = instance.web.View.extend(/** @lends instance.web.TreeVie
});
this.fields = fields_view.fields;
this.hook_row_click();
- this.$element.html(QWeb.render('TreeView', {
+ this.$el.html(QWeb.render('TreeView', {
'title': this.fields_view.arch.attrs.string,
'fields_view': this.fields_view.arch.children,
'fields': this.fields,
'toolbar': has_toolbar
}));
- this.$element.addClass(this.fields_view.arch.attrs['class']);
+ this.$el.addClass(this.fields_view.arch.attrs['class']);
this.dataset.read_slice(this.fields_list()).then(function(records) {
self.store_record(records);
@@ -102,7 +102,7 @@ instance.web.TreeView = instance.web.View.extend(/** @lends instance.web.TreeVie
return;
}
- var $select = self.$element.find('select')
+ var $select = self.$el.find('select')
.change(function () {
var $option = $(this).find(':selected');
self.getdata($option.val());
@@ -120,10 +120,10 @@ instance.web.TreeView = instance.web.View.extend(/** @lends instance.web.TreeVie
$select.change();
}
});
- this.$element.find("#tree_view_expand").click(function(){
+ this.$el.find("#tree_view_expand").click(function(){
self.expand_all();
});
- this.$element.find("#tree_view_collapse").click(function(){
+ this.$el.find("#tree_view_collapse").click(function(){
self.collpase_all();
});
// TODO store open nodes in url ?...
@@ -143,14 +143,14 @@ instance.web.TreeView = instance.web.View.extend(/** @lends instance.web.TreeVie
},
expand_all: function(){
var self = this;
- var tr = this.$element.find(".oe-treeview-table tbody tr[id^='treerow_']");
+ var tr = this.$el.find(".oe-treeview-table tbody tr[id^='treerow_']");
_.each(tr,function(rec){
self.showcontent($(rec).attr('data-id'),true);
});
},
collpase_all: function(){
var self = this;
- var root_tr = this.$element.find(".oe-treeview-table tbody tr[data-level='"+1+"']");
+ var root_tr = this.$el.find(".oe-treeview-table tbody tr[data-level='"+1+"']");
_.each(root_tr,function(rec){
if($(rec).hasClass('oe_open')){
self.showcontent($(rec).attr('data-id'),false);
@@ -187,12 +187,12 @@ instance.web.TreeView = instance.web.View.extend(/** @lends instance.web.TreeVie
*/
hook_row_click: function () {
var self = this;
- this.$element.delegate('.treeview-td span, .treeview-tr span', 'click', function (e) {
+ this.$el.delegate('.treeview-td span, .treeview-tr span', 'click', function (e) {
e.stopImmediatePropagation();
self.activate($(this).closest('tr').data('id'));
});
- this.$element.delegate('.treeview-tr', 'click', function () {
+ this.$el.delegate('.treeview-tr', 'click', function () {
var $this = $(this),
record_id = $this.data('id'),
bool = $this.parent().hasClass('oe_open');
@@ -220,7 +220,7 @@ instance.web.TreeView = instance.web.View.extend(/** @lends instance.web.TreeVie
render_data: function(groupby){
var self = this;
_.each(_.keys(groupby),function(key){
- var $curr_node = self.$element.find('#treerow_' + key);
+ var $curr_node = self.$el.find('#treerow_' + key);
var record = groupby[key];
var children_rows = QWeb.render('TreeView.rows', {
'records': record,
@@ -235,7 +235,7 @@ instance.web.TreeView = instance.web.View.extend(/** @lends instance.web.TreeVie
$curr_node.addClass('oe_open');
$curr_node.after(children_rows);
} else {
- self.$element.find('tbody').html(children_rows);
+ self.$el.find('tbody').html(children_rows);
}
});
self.collpase_all();
@@ -272,11 +272,11 @@ instance.web.TreeView = instance.web.View.extend(/** @lends instance.web.TreeVie
// show & hide the contents
showcontent: function (record_id, show) {
- this.$element.find('#treerow_' + record_id)
+ this.$el.find('#treerow_' + record_id)
.toggleClass('oe_open', show);
_(this.records[record_id][this.children_field]).each(function (child_id) {
- var $child_row = this.$element.find('#treerow_' + child_id);
+ var $child_row = this.$el.find('#treerow_' + child_id);
if ($child_row.hasClass('oe_open')) {
this.showcontent(child_id, false);
}
diff --git a/addons/web/static/src/js/views.js b/addons/web/static/src/js/views.js
index 64817d53bc5..99f4d3e35e8 100644
--- a/addons/web/static/src/js/views.js
+++ b/addons/web/static/src/js/views.js
@@ -14,10 +14,13 @@ instance.web.ActionManager = instance.web.Widget.extend({
this.dialog = null;
this.dialog_widget = null;
this.breadcrumbs = [];
+ this.on('history_back', this, function() {
+ return this.history_back();
+ });
},
start: function() {
this._super.apply(this, arguments);
- this.$element.on('click', '.oe_breadcrumb_item', this.on_breadcrumb_clicked);
+ this.$el.on('click', 'a.oe_breadcrumb_item', this.on_breadcrumb_clicked);
},
dialog_stop: function () {
if (this.dialog) {
@@ -50,10 +53,10 @@ instance.web.ActionManager = instance.web.Widget.extend({
}
var item = _.extend({
show: function(index) {
- this.widget.$element.show();
+ this.widget.$el.show();
},
hide: function() {
- this.widget.$element.hide();
+ this.widget.$el.hide();
},
destroy: function() {
this.widget.destroy();
@@ -65,25 +68,58 @@ instance.web.ActionManager = instance.web.Widget.extend({
item.id = _.uniqueId('breadcrumb_');
this.breadcrumbs.push(item);
},
+ history_back: function() {
+ var last = this.breadcrumbs.slice(-1)[0];
+ if (!last) {
+ return false;
+ }
+ var title = last.get_title();
+ if (_.isArray(title) && title.length > 1) {
+ return this.select_breadcrumb(this.breadcrumbs.length - 1, title.length - 2);
+ } else if (this.breadcrumbs.length === 1) {
+ // Only one single titled item in breadcrumb, most of the time you want to trigger back to home
+ return false;
+ } else {
+ var prev = this.breadcrumbs[this.breadcrumbs.length - 2];
+ title = prev.get_title();
+ return this.select_breadcrumb(this.breadcrumbs.length - 2, _.isArray(title) ? title.length - 1 : undefined);
+ }
+ },
on_breadcrumb_clicked: function(ev) {
var $e = $(ev.target);
var id = $e.data('id');
- var item;
+ var index;
for (var i = this.breadcrumbs.length - 1; i >= 0; i--) {
- var it = this.breadcrumbs[i];
- if (it.id == id) {
- item = it;
+ if (this.breadcrumbs[i].id == id) {
+ index = i;
break;
}
- this.remove_breadcrumb(i);
}
- var index = $e.parent().find('.oe_breadcrumb_item[data-id=' + $e.data('id') + ']').index($e);
- item.show(index, $e);
+ var subindex = $e.parent().find('a.oe_breadcrumb_item[data-id=' + $e.data('id') + ']').index($e);
+ this.select_breadcrumb(index, subindex);
+ },
+ select_breadcrumb: function(index, subindex) {
+ var next_item = this.breadcrumbs[index + 1];
+ if (next_item && next_item.on_reverse_breadcrumb) {
+ next_item.on_reverse_breadcrumb(this.breadcrumbs[index].widget);
+ }
+ for (var i = this.breadcrumbs.length - 1; i >= 0; i--) {
+ if (i > index) {
+ if (this.remove_breadcrumb(i) === false) {
+ return false;
+ }
+ }
+ }
+ var item = this.breadcrumbs[index];
+ item.show(subindex);
this.inner_widget = item.widget;
+ return true;
},
clear_breadcrumbs: function() {
- while (this.breadcrumbs.length) {
- this.remove_breadcrumb(0);
+ for (var i = this.breadcrumbs.length - 1; i >= 0; i--) {
+ if (this.remove_breadcrumb(0) === false) {
+ break;
+ }
}
},
remove_breadcrumb: function(index) {
@@ -93,9 +129,17 @@ instance.web.ActionManager = instance.web.Widget.extend({
return item.widget === it.widget;
});
if (!dups.length) {
- item.destroy();
+ if (this.getParent().has_uncommitted_changes()) {
+ this.inner_widget = item.widget;
+ this.breadcrumbs.splice(index, 0, item);
+ return false;
+ } else {
+ item.destroy();
+ }
}
}
+ var last_widget = this.breadcrumbs.slice(-1)[0];
+ this.inner_widget = last_widget && last_widget.widget;
},
get_title: function() {
var titles = [];
@@ -108,7 +152,7 @@ instance.web.ActionManager = instance.web.Widget.extend({
for (var j = 0; j < tit.length; j += 1) {
var label = _.escape(tit[j]);
if (i === this.breadcrumbs.length - 1 && j === tit.length - 1) {
- titles.push(label);
+ titles.push(_.str.sprintf('<span class="oe_breadcrumb_item">%s</span>', label));
} else {
titles.push(_.str.sprintf('<a href="#" class="oe_breadcrumb_item" data-id="%s">%s</a>', item.id, label));
}
@@ -117,6 +161,7 @@ instance.web.ActionManager = instance.web.Widget.extend({
return titles.join(' <span class="oe_fade">/</span> ');
},
do_push_state: function(state) {
+ state = state || {};
if (this.getParent() && this.getParent().do_push_state) {
if (this.inner_action) {
state['title'] = this.inner_action.name;
@@ -124,23 +169,34 @@ instance.web.ActionManager = instance.web.Widget.extend({
state['model'] = this.inner_action.res_model;
}
if (this.inner_action.id) {
- state['action_id'] = this.inner_action.id;
+ state['action'] = this.inner_action.id;
+ } else if (this.inner_action.type == 'ir.actions.client') {
+ state['action'] = this.inner_action.tag;
+ //state = _.extend(this.inner_action.params || {}, state);
}
}
- this.getParent().do_push_state(state);
+ if(!this.dialog) {
+ this.getParent().do_push_state(state);
+ }
}
},
do_load_state: function(state, warm) {
var self = this,
action_loaded;
- if (state.action_id) {
- var run_action = (!this.inner_widget || !this.inner_widget.action) || this.inner_widget.action.id !== state.action_id;
- if (run_action) {
+ if (state.action) {
+ if (_.isString(state.action) && instance.web.client_actions.contains(state.action)) {
+ var action_client = {type: "ir.actions.client", tag: state.action, params: state};
this.null_action();
- action_loaded = this.do_action(state.action_id);
- instance.webclient.menu.has_been_loaded.then(function() {
- instance.webclient.menu.open_action(state.action_id);
- });
+ action_loaded = this.do_action(action_client);
+ } else {
+ var run_action = (!this.inner_widget || !this.inner_widget.action) || this.inner_widget.action.id !== state.action;
+ if (run_action) {
+ this.null_action();
+ action_loaded = this.do_action(state.action);
+ instance.webclient.menu.has_been_loaded.then(function() {
+ instance.webclient.menu.open_action(state.action);
+ });
+ }
}
} else if (state.model && state.id) {
// TODO handle context & domain ?
@@ -154,24 +210,12 @@ instance.web.ActionManager = instance.web.Widget.extend({
action_loaded = this.do_action(action);
} else if (state.sa) {
// load session action
- var self = this;
this.null_action();
action_loaded = this.rpc('/web/session/get_session_action', {key: state.sa}).pipe(function(action) {
if (action) {
return self.do_action(action);
}
});
- } else if (state.client_action) {
- this.null_action();
- var action = state.client_action;
- if(_.isString(action)) {
- action = {
- type: 'ir.actions.client',
- tag: action,
- params: state,
- };
- }
- this.ir_actions_client(action);
}
$.when(action_loaded || null).then(function() {
@@ -180,23 +224,23 @@ instance.web.ActionManager = instance.web.Widget.extend({
}
});
},
- do_action: function(action, on_close) {
+ do_action: function(action, on_close, clear_breadcrumbs, on_reverse_breadcrumb) {
if (_.isString(action) && instance.web.client_actions.contains(action)) {
var action_client = { type: "ir.actions.client", tag: action };
- return this.do_action(action_client);
+ return this.do_action(action_client, on_close, clear_breadcrumbs, on_reverse_breadcrumb);
} else if (_.isNumber(action) || _.isString(action)) {
var self = this;
- return self.rpc("/web/action/load", { action_id: action }, function(result) {
- self.do_action(result.result, on_close);
+ return self.rpc("/web/action/load", { action_id: action }).pipe(function(result) {
+ return self.do_action(result.result, on_close, clear_breadcrumbs, on_reverse_breadcrumb);
});
}
if (!action.type) {
console.error("No type for action", action);
- return;
+ return $.Deferred().reject();
}
var type = action.type.replace(/\./g,'_');
var popup = action.target === 'new';
- var inline = action.target === 'inline';
+ var inline = action.target === 'inline' || action.target === 'inlineview';
action.flags = _.extend({
views_switcher : !popup && !inline,
search_view : !popup && !inline,
@@ -207,83 +251,96 @@ instance.web.ActionManager = instance.web.Widget.extend({
}, action.flags || {});
if (!(type in this)) {
console.error("Action manager can't handle action of type " + action.type, action);
- return;
+ return $.Deferred().reject();
}
- return this[type](action, on_close);
+ return this[type](action, on_close, clear_breadcrumbs, on_reverse_breadcrumb);
},
null_action: function() {
this.dialog_stop();
this.clear_breadcrumbs();
},
-
- do_ir_actions_common: function(action, on_close) {
- var self = this, klass, widget, add_breadcrumb;
- if (action.type === 'ir.actions.client') {
- var ClientWidget = instance.web.client_actions.get_object(action.tag);
- widget = new ClientWidget(this, action.params);
- klass = 'oe_act_client';
- add_breadcrumb = function() {
- self.push_breadcrumb({
- widget: widget,
- title: action.name
- });
+ /**
+ *
+ * @param {Object} executor
+ * @param {Object} executor.action original action
+ * @param {Function<instance.web.Widget>} executor.widget function used to fetch the widget instance
+ * @param {String} executor.klass CSS class to add on the dialog root, if action.target=new
+ * @param {Function<instance.web.Widget, undefined>} executor.post_process cleanup called after a widget has been added as inner_widget
+ * @param on_close
+ * @param clear_breadcrumbs
+ * @return {*}
+ */
+ ir_actions_common: function(executor, on_close, clear_breadcrumbs) {
+ if (this.inner_widget && executor.action.target !== 'new') {
+ if (this.getParent().has_uncommitted_changes()) {
+ return $.Deferred().reject();
+ } else if (clear_breadcrumbs) {
+ this.clear_breadcrumbs();
}
- } else {
- widget = new instance.web.ViewManagerAction(this, action);
- klass = 'oe_act_window';
- add_breadcrumb = widget.proxy('add_breadcrumb');
}
- if (action.target === 'new') {
+ var widget = executor.widget();
+ if (executor.action.target === 'new') {
if (this.dialog === null) {
// These buttons will be overwrited by <footer> if any
this.dialog = new instance.web.Dialog(this, {
buttons: { "Close": function() { $(this).dialog("close"); }},
- dialogClass: klass
+ dialogClass: executor.klass
});
if(on_close)
this.dialog.on_close.add(on_close);
} else {
this.dialog_widget.destroy();
}
- this.dialog.dialog_title = action.name;
+ this.dialog.dialog_title = executor.action.name;
this.dialog_widget = widget;
- this.dialog_widget.appendTo(this.dialog.$element);
+ var initialized = this.dialog_widget.appendTo(this.dialog.$el);
this.dialog.open();
+ return initialized;
} else {
this.dialog_stop();
- if(action.menu_id) {
- return this.getParent().do_action(action, function () {
- instance.webclient.menu.open_menu(action.menu_id);
- });
- }
- this.inner_action = action;
+ this.inner_action = executor.action;
this.inner_widget = widget;
- add_breadcrumb();
- this.inner_widget.appendTo(this.$element);
+ executor.post_process(widget);
+ return this.inner_widget.appendTo(this.$el);
}
},
+ ir_actions_act_window: function (action, on_close, clear_breadcrumbs, on_reverse_breadcrumb) {
+ var self = this;
- ir_actions_act_window: function (action, on_close) {
+ return this.ir_actions_common({
+ widget: function () { return new instance.web.ViewManagerAction(self, action); },
+ action: action,
+ klass: 'oe_act_window',
+ post_process: function (widget) { widget.add_breadcrumb(on_reverse_breadcrumb); }
+ }, on_close, clear_breadcrumbs, on_reverse_breadcrumb);
+ },
+ ir_actions_client: function (action, on_close, clear_breadcrumbs, on_reverse_breadcrumb) {
var self = this;
- if (_(['base.module.upgrade', 'base.setup.installer'])
- .contains(action.res_model)) {
- var old_close = on_close;
- on_close = function () {
- instance.webclient.do_reload().then(old_close);
- };
+ var ClientWidget = instance.web.client_actions.get_object(action.tag);
+
+ if (!(ClientWidget.prototype instanceof instance.web.Widget)) {
+ var next;
+ if (next = ClientWidget(this, action.params)) {
+ return this.do_action(next, on_close, clear_breadcrumbs, on_reverse_breadcrumb);
+ }
+ return $.when();
}
- if (action.target !== 'new') {
- if(action.menu_id) {
- this.dialog_stop();
- return this.getParent().do_action(action, function () {
- instance.webclient.menu.open_menu(action.menu_id);
+
+ return this.ir_actions_common({
+ widget: function () { return new ClientWidget(self, action.params); },
+ action: action,
+ klass: 'oe_act_client',
+ post_process: function(widget) {
+ self.push_breadcrumb({
+ widget: widget,
+ title: action.name,
+ on_reverse_breadcrumb: on_reverse_breadcrumb,
});
+ if (action.tag !== 'reload') {
+ self.do_push_state({});
+ }
}
- }
- return this.do_ir_actions_common(action, on_close);
- },
- ir_actions_client: function (action, on_close) {
- return this.do_ir_actions_common(action, on_close);
+ }, on_close, clear_breadcrumbs, on_reverse_breadcrumb);
},
ir_actions_act_window_close: function (action, on_closed) {
if (!this.dialog && on_closed) {
@@ -291,13 +348,13 @@ instance.web.ActionManager = instance.web.Widget.extend({
}
this.dialog_stop();
},
- ir_actions_server: function (action, on_closed) {
+ ir_actions_server: function (action, on_closed, clear_breadcrumbs, on_reverse_breadcrumb) {
var self = this;
this.rpc('/web/action/run', {
action_id: action.id,
context: action.context || {}
}).then(function (action) {
- self.do_action(action, on_closed)
+ self.do_action(action, on_closed, clear_breadcrumbs, on_reverse_breadcrumb)
});
},
ir_actions_report_xml: function(action, on_closed) {
@@ -326,9 +383,6 @@ instance.web.ActionManager = instance.web.Widget.extend({
ir_actions_act_url: function (action) {
window.open(action.url, action.target === 'self' ? '_self' : '_blank');
},
- ir_ui_menu: function (action) {
- this.getParent().do_action(action);
- }
});
instance.web.ViewManager = instance.web.Widget.extend({
@@ -362,7 +416,7 @@ instance.web.ViewManager = instance.web.Widget.extend({
start: function() {
this._super();
var self = this;
- this.$element.find('.oe_view_manager_switch a').click(function() {
+ this.$el.find('.oe_view_manager_switch a').click(function() {
self.on_mode_switch($(this).data('view-type'));
}).tipsy();
var views_ids = {};
@@ -371,9 +425,9 @@ instance.web.ViewManager = instance.web.Widget.extend({
deferred : $.Deferred(),
controller : null,
options : _.extend({
- $buttons : self.$element.find('.oe_view_manager_buttons'),
- $sidebar : self.flags.sidebar ? self.$element.find('.oe_view_manager_sidebar') : undefined,
- $pager : self.$element.find('.oe_view_manager_pager'),
+ $buttons : self.$el.find('.oe_view_manager_buttons'),
+ $sidebar : self.flags.sidebar ? self.$el.find('.oe_view_manager_sidebar') : undefined,
+ $pager : self.$el.find('.oe_view_manager_pager'),
action : self.action,
action_views_ids : views_ids
}, self.flags, self.flags[view.view_type] || {}, view.options || {})
@@ -381,7 +435,7 @@ instance.web.ViewManager = instance.web.Widget.extend({
views_ids[view.view_type] = view.view_id;
});
if (this.flags.views_switcher === false) {
- this.$element.find('.oe_view_manager_switch').hide();
+ this.$el.find('.oe_view_manager_switch').hide();
}
// If no default view defined, switch to the first one in sequence
var default_view = this.flags.default_view || this.views_src[0].view_type;
@@ -398,8 +452,10 @@ instance.web.ViewManager = instance.web.Widget.extend({
var self = this;
var view = this.views[view_type];
var view_promise;
- if(!view)
+ var form = this.views['form'];
+ if (!view || (form && form.controller && !form.controller.can_be_discarded())) {
return $.Deferred().reject();
+ }
if (!no_store) {
this.views_history.push(view_type);
@@ -418,9 +474,9 @@ instance.web.ViewManager = instance.web.Widget.extend({
this.searchview[(view.controller.searchable === false || this.searchview.hidden) ? 'hide' : 'show']();
}
- this.$element
+ this.$el
.find('.oe_view_manager_switch a').parent().removeClass('active');
- this.$element
+ this.$el
.find('.oe_view_manager_switch a').filter('[data-view-type="' + view_type + '"]')
.parent().addClass('active');
@@ -428,7 +484,7 @@ instance.web.ViewManager = instance.web.Widget.extend({
_.each(_.keys(self.views), function(view_name) {
var controller = self.views[view_name].controller;
if (controller) {
- var container = self.$element.find(".oe_view_manager_view_" + view_name + ":first");
+ var container = self.$el.find(".oe_view_manager_view_" + view_name + ":first");
if (view_name === view_type) {
container.show();
controller.do_show(view_options || {});
@@ -437,10 +493,10 @@ instance.web.ViewManager = instance.web.Widget.extend({
controller.do_hide();
}
// put the <footer> in the dialog's buttonpane
- if (self.$element.parent('.ui-dialog-content') && self.$element.find('footer')) {
- self.$element.parent('.ui-dialog-content').parent().find('div.ui-dialog-buttonset').hide()
- self.$element.find('footer').appendTo(
- self.$element.parent('.ui-dialog-content').parent().find('div.ui-dialog-buttonpane')
+ if (self.$el.parent('.ui-dialog-content') && self.$el.find('footer')) {
+ self.$el.parent('.ui-dialog-content').parent().find('div.ui-dialog-buttonset').hide()
+ self.$el.find('footer').appendTo(
+ self.$el.parent('.ui-dialog-content').parent().find('div.ui-dialog-buttonpane')
);
}
}
@@ -451,17 +507,19 @@ instance.web.ViewManager = instance.web.Widget.extend({
// Lazy loading of views
var self = this;
var view = this.views[view_type];
- var controllerclass = this.registry.get_object(view_type);
+ var viewclass = this.registry.get_object(view_type);
var options = _.clone(view.options);
- if (view_type === "form" && this.action) {
- switch (this.action.target) {
- case 'new':
- case 'inline':
- options.initial_mode = 'edit';
- break;
- }
+ if (view_type === "form" && this.action && (this.action.target == 'new' || this.action.target == 'inline')) {
+ options.initial_mode = 'edit';
}
- var controller = new controllerclass(this, this.dataset, view.view_id, options);
+ var controller = new viewclass(this, this.dataset, view.view_id, options);
+
+ controller.on('history_back', this, function() {
+ var am = self.getParent();
+ if (am && am.trigger) {
+ return am.trigger('history_back');
+ }
+ });
controller.on("change:title", this, function() {
if (self.active_view === view_type) {
@@ -475,7 +533,7 @@ instance.web.ViewManager = instance.web.Widget.extend({
controller.do_switch_view.add_last(_.bind(this.switch_view, this));
controller.do_prev_view.add_last(this.on_prev_view);
- var container = this.$element.find(".oe_view_manager_view_" + view_type);
+ var container = this.$el.find(".oe_view_manager_view_" + view_type);
var view_promise = controller.appendTo(container);
this.views[view_type].controller = controller;
this.views[view_type].deferred.resolve(view_type);
@@ -489,9 +547,9 @@ instance.web.ViewManager = instance.web.Widget.extend({
});
},
set_title: function(title) {
- this.$element.find('.oe_view_title_text:first').text(title);
+ this.$el.find('.oe_view_title_text:first').text(title);
},
- add_breadcrumb: function() {
+ add_breadcrumb: function(on_reverse_breadcrumb) {
var self = this;
var views = [this.active_view || this.views_src[0].view_type];
this.on_mode_switch.add(function(mode) {
@@ -506,9 +564,9 @@ instance.web.ViewManager = instance.web.Widget.extend({
this.getParent().push_breadcrumb({
widget: this,
action: this.action,
- show: function(index, $e) {
+ show: function(index) {
var view_to_select = views[index];
- self.$element.show();
+ self.$el.show();
if (self.active_view !== view_to_select) {
self.on_mode_switch(view_to_select);
}
@@ -529,13 +587,15 @@ instance.web.ViewManager = instance.web.Widget.extend({
}
return controller.get('title');
});
- if (next && next.action && next.action.res_id && self.active_view === 'form' && self.model === next.action.res_model && id === next.action.res_id) {
+ if (next && next.action && next.action.res_id && self.dataset &&
+ self.active_view === 'form' && self.dataset.model === next.action.res_model && id === next.action.res_id) {
// If the current active view is a formview and the next item in the breadcrumbs
// is an action on same object (model / res_id), then we omit the current formview's title
titles.pop();
}
return titles;
- }
+ },
+ on_reverse_breadcrumb: on_reverse_breadcrumb,
});
},
/**
@@ -584,7 +644,7 @@ instance.web.ViewManager = instance.web.Widget.extend({
this.searchview = new instance.web.SearchView(this, this.dataset, view_id, search_defaults, this.flags.search_view === false);
this.searchview.on_search.add(this.do_searchview_search);
- return this.searchview.appendTo(this.$element.find(".oe_view_manager_view_search"));
+ return this.searchview.appendTo(this.$el.find(".oe_view_manager_view_search"));
},
do_searchview_search: function(domains, contexts, groupbys) {
var self = this,
@@ -701,8 +761,8 @@ instance.web.ViewManagerAction = instance.web.ViewManager.extend({
var manager_ready = $.when(searchview_loaded, main_view_loaded);
- this.$element.find('.oe_debug_view').change(this.on_debug_changed);
- this.$element.addClass("oe_view_manager_" + (this.action.target || 'current'));
+ this.$el.find('.oe_debug_view').change(this.on_debug_changed);
+ this.$el.addClass("oe_view_manager_" + (this.action.target || 'current'));
return manager_ready;
},
on_debug_changed: function (evt) {
@@ -714,7 +774,15 @@ instance.web.ViewManagerAction = instance.web.ViewManager.extend({
switch (val) {
case 'fvg':
var dialog = new instance.web.Dialog(this, { title: _t("Fields View Get"), width: '95%' }).open();
- $('<pre>').text(instance.web.json_node_to_xml(current_view.fields_view.arch, true)).appendTo(dialog.$element);
+ $('<pre>').text(instance.web.json_node_to_xml(current_view.fields_view.arch, true)).appendTo(dialog.$el);
+ break;
+ case 'tests':
+ this.do_action({
+ name: "JS Tests",
+ target: 'new',
+ type : 'ir.actions.act_url',
+ url: '/web/static/test/test.html'
+ })
break;
case 'perm_read':
var ids = current_view.get_selected_ids();
@@ -733,6 +801,17 @@ instance.web.ViewManagerAction = instance.web.ViewManager.extend({
case 'toggle_layout_outline':
current_view.rendering_engine.toggle_layout_debugging();
break;
+ case 'translate':
+ this.do_action({
+ name: "Technical Translation",
+ res_model : 'ir.translation',
+ domain : [['type', '!=', 'object'], '|', ['name', '=', this.dataset.model], ['name', 'ilike', this.dataset.model + ',']],
+ views: [[false, 'list'], [false, 'form']],
+ type : 'ir.actions.act_window',
+ view_type : "list",
+ view_mode : "list"
+ });
+ break;
case 'fields':
this.dataset.call_and_eval(
'fields_get', [false, {}], null, 1).then(function (fields) {
@@ -780,6 +859,21 @@ instance.web.ViewManagerAction = instance.web.ViewManager.extend({
}
});
break;
+ case 'print_workflow':
+ if (current_view.get_selected_ids && current_view.get_selected_ids().length == 1) {
+ instance.web.blockUI();
+ var action = {
+ context: { active_ids: current_view.get_selected_ids() },
+ report_name: "workflow.instance.graph",
+ datas: {
+ model: this.dataset.model,
+ id: current_view.get_selected_ids()[0],
+ nested: true,
+ }
+ };
+ this.session.get_file({ url: '/web/report', data: {action: JSON.stringify(action)}, complete: instance.web.unblockUI });
+ }
+ break;
default:
if (val) {
console.log("No debug handler for ", val);
@@ -812,7 +906,7 @@ instance.web.ViewManagerAction = instance.web.ViewManager.extend({
var controller = self.views[self.active_view].controller,
fvg = controller.fields_view,
view_id = (fvg && fvg.view_id) || '--';
- self.$element.find('.oe_debug_view').html(QWeb.render('ViewManagerDebug', {
+ self.$el.find('.oe_debug_view').html(QWeb.render('ViewManagerDebug', {
view: controller,
view_manager: self
}));
@@ -826,7 +920,7 @@ instance.web.ViewManagerAction = instance.web.ViewManager.extend({
return r;
},
set_title: function(title) {
- this.$element.find('.oe_breadcrumb_title:first').html(this.getParent().get_title());
+ this.$el.find('.oe_breadcrumb_title:first').html(this.getParent().get_title());
},
do_push_state: function(state) {
if (this.getParent() && this.getParent().do_push_state) {
@@ -858,18 +952,12 @@ instance.web.Sidebar = instance.web.Widget.extend({
var view = this.getParent();
this.sections = [
{ 'name' : 'print', 'label' : _t('Print'), },
- { 'name' : 'files', 'label' : _t('Attachment'), },
{ 'name' : 'other', 'label' : _t('More'), }
];
this.items = {
'print' : [],
- 'files' : [],
'other' : []
};
- if (this.session.uid === 1) {
- var item = { label: _t("Translate"), callback: view.on_sidebar_translate, title: _t("Technical translation") };
- this.items.other.push(item);
- }
this.fileupload_id = _.uniqueId('oe_fileupload');
$(window).on(this.fileupload_id, function() {
var args = [].slice.call(arguments).slice(1);
@@ -885,7 +973,7 @@ instance.web.Sidebar = instance.web.Widget.extend({
var self = this;
this._super(this);
this.redraw();
- this.$element.on('click','.oe_dropdown_menu li a', function(event) {
+ this.$el.on('click','.oe_dropdown_menu li a', function(event) {
var section = $(this).data('section');
var index = $(this).data('index');
var item = self.items[section][index];
@@ -901,7 +989,7 @@ instance.web.Sidebar = instance.web.Widget.extend({
},
redraw: function() {
var self = this;
- self.$element.html(QWeb.render('Sidebar', {widget: self}));
+ self.$el.html(QWeb.render('Sidebar', {widget: self}));
// Hides Sidebar sections when item list is empty
this.$('.oe_form_dropdown_section').each(function() {
@@ -1008,12 +1096,12 @@ instance.web.Sidebar = instance.web.Widget.extend({
self.items['files'] = attachments;
self.redraw();
this.$('.oe_sidebar_add_attachment .oe_form_binary_file').change(this.on_attachment_changed);
- this.$element.find('.oe_sidebar_delete_item').click(this.on_attachment_delete);
+ this.$el.find('.oe_sidebar_delete_item').click(this.on_attachment_delete);
},
on_attachment_changed: function(e) {
var $e = $(e.target);
if ($e.val() !== '') {
- this.$element.find('form.oe_form_binary_form').submit();
+ this.$el.find('form.oe_form_binary_form').submit();
$e.parent().find('input[type=file]').prop('disabled', true);
$e.parent().find('button').prop('disabled', true).find('img, span').toggle();
this.$('.oe_sidebar_add_attachment span').text(_t('Uploading...'));
@@ -1034,126 +1122,7 @@ instance.web.Sidebar = instance.web.Widget.extend({
}
});
-instance.web.TranslateDialog = instance.web.Dialog.extend({
- dialog_title: {toString: function () { return _t("Translations"); }},
- init: function(view) {
- // TODO fme: should add the language to fields_view_get because between the fields view get
- // and the moment the user opens the translation dialog, the user language could have been changed
- this.view_language = view.session.user_context.lang;
- this['on_button_' + _t("Save")] = this.on_btn_save;
- this['on_button_' + _t("Close")] = this.on_btn_close;
- this._super(view, {
- width: '80%',
- height: '80%'
- });
- this.view = view;
- this.view_type = view.fields_view.type || '';
- this.$fields_form = null;
- this.$view_form = null;
- this.$sidebar_form = null;
- this.translatable_fields_keys = _.map(this.view.translatable_fields || [], function(i) { return i.name });
- this.languages = null;
- this.languages_loaded = $.Deferred();
- (new instance.web.DataSetSearch(this, 'res.lang', this.view.dataset.get_context(),
- [['translatable', '=', '1']])).read_slice(['code', 'name'], { sort: 'id' }).then(this.on_languages_loaded);
- },
- start: function() {
- var self = this;
- this._super();
- $.when(this.languages_loaded).then(function() {
- self.$element.html(instance.web.qweb.render('TranslateDialog', { widget: self }));
- self.$fields_form = self.$element.find('.oe_translation_form');
- self.$fields_form.find('.oe_trad_field').change(function() {
- $(this).toggleClass('touched', ($(this).val() != $(this).attr('data-value')));
- });
- });
- return this;
- },
- on_languages_loaded: function(langs) {
- this.languages = langs;
- this.languages_loaded.resolve();
- },
- do_load_fields_values: function(callback) {
- var self = this,
- deffered = [];
- this.$fields_form.find('.oe_trad_field').val('').removeClass('touched');
- _.each(self.languages, function(lg) {
- var deff = $.Deferred();
- deffered.push(deff);
- var callback = function(values) {
- _.each(self.translatable_fields_keys, function(f) {
- self.$fields_form.find('.oe_trad_field[name="' + lg.code + '-' + f + '"]').val(values[0][f] || '').attr('data-value', values[0][f] || '');
- });
- deff.resolve();
- };
- if (lg.code === self.view_language) {
- var values = {};
- _.each(self.translatable_fields_keys, function(field) {
- values[field] = self.view.fields[field].get_value();
- });
- callback([values]);
- } else {
- self.rpc('/web/dataset/get', {
- model: self.view.dataset.model,
- ids: [self.view.datarecord.id],
- fields: self.translatable_fields_keys,
- context: self.view.dataset.get_context({
- 'lang': lg.code
- })}, callback);
- }
- });
- $.when.apply(null, deffered).then(callback);
- },
- open: function(field) {
- var self = this,
- sup = this._super;
- $.when(this.languages_loaded).then(function() {
- if (self.view.translatable_fields && self.view.translatable_fields.length) {
- self.do_load_fields_values(function() {
- sup.call(self);
- // desactivated because it created an exception, plus it does not seem very useful
- /*
- if (field) {
- var $field_input = self.$element.find('tr[data-field="' + field.name + '"] td:nth-child(2) *:first-child');
- self.$element.scrollTo($field_input);
- $field_input.focus();
- }*/
- });
- } else {
- sup.call(self);
- }
- });
- },
- on_btn_save: function() {
- var trads = {},
- self = this,
- trads_mutex = new $.Mutex();
- self.$fields_form.find('.oe_trad_field.touched').each(function() {
- var field = $(this).attr('name').split('-');
- if (!trads[field[0]]) {
- trads[field[0]] = {};
- }
- trads[field[0]][field[1]] = $(this).val();
- });
- _.each(trads, function(data, code) {
- if (code === self.view_language) {
- _.each(data, function(value, field) {
- self.view.fields[field].set_value(value);
- });
- }
- trads_mutex.exec(function() {
- return self.view.dataset.write(self.view.datarecord.id, data, { context : { 'lang': code } });
- });
- });
- this.close();
- },
- on_btn_close: function() {
- this.close();
- }
-});
-
instance.web.View = instance.web.Widget.extend({
- template: "EmptyComponent",
// name displayed in view switchers
display_name: '',
/**
@@ -1204,12 +1173,6 @@ instance.web.View = instance.web.Widget.extend({
action_views_ids: {}
});
},
- open_translate_dialog: function(field) {
- if (!this.translate_dialog) {
- this.translate_dialog = new instance.web.TranslateDialog(this).start();
- }
- this.translate_dialog.open(field);
- },
/**
* Fetches and executes the action identified by ``action_data``.
*
@@ -1233,7 +1196,7 @@ instance.web.View = instance.web.Widget.extend({
var context = new instance.web.CompoundContext(dataset.get_context(), action_data.context || {});
var handler = function (r) {
- var action = r.result;
+ var action = r;
if (action && action.constructor == Object) {
var ncontext = new instance.web.CompoundContext(context);
if (record_id) {
@@ -1274,11 +1237,11 @@ instance.web.View = instance.web.Widget.extend({
}
}
args.push(context);
- return dataset.call_button(action_data.name, args, handler);
+ return dataset.call_button(action_data.name, args).then(handler);
} else if (action_data.type=="action") {
return this.rpc('/web/action/load', { action_id: action_data.name, context: context, do_not_eval: true}, handler);
} else {
- return dataset.exec_workflow(record_id, action_data.name, handler);
+ return dataset.exec_workflow(record_id, action_data.name).then(handler);
}
},
/**
@@ -1292,10 +1255,10 @@ instance.web.View = instance.web.Widget.extend({
this.embedded_view = embedded_view;
},
do_show: function () {
- this.$element.show();
+ this.$el.show();
},
do_hide: function () {
- this.$element.hide();
+ this.$el.hide();
},
do_push_state: function(state) {
if (this.getParent() && this.getParent().do_push_state) {
@@ -1322,23 +1285,8 @@ instance.web.View = instance.web.Widget.extend({
},
do_search: function(view) {
},
- on_sidebar_import: function() {
- var import_view = new instance.web.DataImport(this, this.dataset);
- import_view.start();
- },
on_sidebar_export: function() {
- var export_view = new instance.web.DataExport(this, this.dataset);
- export_view.start();
- },
- on_sidebar_translate: function() {
- return this.do_action({
- res_model : 'ir.translation',
- domain : [['type', '!=', 'object'], '|', ['name', '=', this.dataset.model], ['name', 'ilike', this.dataset.model + ',']],
- views: [[false, 'list'], [false, 'form']],
- type : 'ir.actions.act_window',
- view_type : "list",
- view_mode : "list"
- });
+ new instance.web.DataExport(this, this.dataset).open();
},
sidebar_context: function () {
return $.when();
@@ -1349,6 +1297,15 @@ instance.web.View = instance.web.Widget.extend({
*/
reload: function () {
return $.when();
+ },
+ /**
+ * Return whether the user can perform the action ('create', 'edit', 'delete') in this view.
+ * An action is disabled by setting the corresponding attribute in the view's main element,
+ * like: <form string="" create="false" edit="false" delete="false">
+ */
+ is_action_enabled: function(action) {
+ var attrs = this.fields_view.arch.attrs;
+ return (action in attrs) ? JSON.parse(attrs[action]) : true;
}
});