From 7fed8926843d792cdd9a450b51186c2feab5a254 Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Thu, 31 Mar 2011 14:25:22 +0200 Subject: [PATCH 01/26] [ADD] retain currently activated view bzr revid: fme@openerp.com-20110331122522-3v7coio2wpnzsszn --- addons/base/static/src/js/views.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/addons/base/static/src/js/views.js b/addons/base/static/src/js/views.js index b0b79c3e5f7..9eabb419151 100644 --- a/addons/base/static/src/js/views.js +++ b/addons/base/static/src/js/views.js @@ -35,12 +35,14 @@ openerp.base.ViewManager = openerp.base.Controller.extend({ this.searchview_id = false; this.searchview = null; this.search_visible = true; + this.active_view = null; // this.views = { "list": { "view_id":1234, "controller": instance} } this.views = {}; }, start: function() { }, on_mode_switch: function(view_type) { + this.active_view = view_type; for (var i in this.views) { this.views[i].controller.$element.toggle(i === view_type); } From 4722165efb3cfaffd4d4f4215d47c1eccc9b8b1a Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Thu, 31 Mar 2011 14:25:42 +0200 Subject: [PATCH 02/26] [REM] Removed wrong action reference in action manager bzr revid: fme@openerp.com-20110331122542-gnlxfg2unxvlqut8 --- addons/base/static/src/js/views.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/addons/base/static/src/js/views.js b/addons/base/static/src/js/views.js index 9eabb419151..4fa2e7c2fc9 100644 --- a/addons/base/static/src/js/views.js +++ b/addons/base/static/src/js/views.js @@ -8,7 +8,6 @@ openerp.base.ActionManager = openerp.base.Controller.extend({ // process all kind of actions init: function(session, element_id) { this._super(session, element_id); - this.action = null; this.viewmanager = null; }, /** @@ -17,7 +16,6 @@ openerp.base.ActionManager = openerp.base.Controller.extend({ */ do_action: function(action) { // instantiate the right controllers by understanding the action - this.action = action; if(action.type == "ir.actions.act_window") { this.viewmanager = new openerp.base.ViewManager(this.session,this.element_id); this.viewmanager.do_action_window(action); From 911af887b9665635e6d3b479cffdeb832b04528c Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Thu, 31 Mar 2011 14:35:19 +0200 Subject: [PATCH 03/26] [IMP] do_action really switch to first view in sequence bzr revid: fme@openerp.com-20110331123519-uk4pkd5azix545ji --- addons/base/static/src/js/views.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/base/static/src/js/views.js b/addons/base/static/src/js/views.js index 4fa2e7c2fc9..a472ac5e748 100644 --- a/addons/base/static/src/js/views.js +++ b/addons/base/static/src/js/views.js @@ -106,7 +106,7 @@ openerp.base.ViewManager = openerp.base.Controller.extend({ } } // switch to the first one in sequence - this.on_mode_switch("tree"); + this.on_mode_switch(action.views[0][1]); }, // create when root, also add to parent when o2m on_create: function() { From f64b7d3b8c17acc045727afbf1ffdd7fdfcad271 Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Thu, 31 Mar 2011 15:20:58 +0200 Subject: [PATCH 04/26] [IMP] Lazy loading of views bzr revid: fme@openerp.com-20110331132058-w6g87wsxof3kyiqv --- addons/base/static/src/css/base.css | 5 +++ addons/base/static/src/js/views.js | 50 +++++++++++++++++------------ addons/base/static/src/xml/base.xml | 9 +++--- 3 files changed, 39 insertions(+), 25 deletions(-) diff --git a/addons/base/static/src/css/base.css b/addons/base/static/src/css/base.css index 58adf4183e4..a66a93eb017 100644 --- a/addons/base/static/src/css/base.css +++ b/addons/base/static/src/css/base.css @@ -395,6 +395,11 @@ body.openerp { padding-left: 10px; } +/* View Manager */ +.openerp .views_switchers { + text-align: right; +} + /* Form */ .openerp .required.error { border: 1px solid #900; diff --git a/addons/base/static/src/js/views.js b/addons/base/static/src/js/views.js index a472ac5e748..164e5d6cf72 100644 --- a/addons/base/static/src/js/views.js +++ b/addons/base/static/src/js/views.js @@ -41,8 +41,31 @@ openerp.base.ViewManager = openerp.base.Controller.extend({ }, on_mode_switch: function(view_type) { this.active_view = view_type; + var view = this.views[view_type]; + if (!view.controller) { + // Lazy loading of views + var controller; + switch (view_type) { + case 'tree': + controller = new openerp.base.ListView(this.session, this.element_id + "_view_tree", this.dataset, view.view_id); + break; + case 'form': + controller = new openerp.base.FormView(this.session, this.element_id + "_view_form", this.dataset, view.view_id); + break; + case 'calendar': + controller = new openerp.base.CalendarView(this.session, this.element_id + "_view_calendar", this.dataset, view.view_id); + break; + case 'gantt': + controller = new openerp.base.GanttView(this.session, this.element_id + "_view_gantt", this.dataset, view.view_id); + break; + } + controller.start(); + this.views[view_type].controller = controller; + } for (var i in this.views) { - this.views[i].controller.$element.toggle(i === view_type); + if (this.views[i].controller) { + this.views[i].controller.$element.toggle(i === view_type); + } } }, /** @@ -86,25 +109,12 @@ openerp.base.ViewManager = openerp.base.Controller.extend({ searchview.do_search); } } - for(var i = 0; i < action.views.length; i++) { - var view_id, controller; - view_id = action.views[i][0]; - if(action.views[i][1] == "tree") { - controller = new openerp.base.ListView(this.session, this.element_id + "_view_tree", this.dataset, view_id); - controller.start(); - this.views.tree = { view_id: view_id, controller: controller }; - this.$element.find(prefix_id + "_button_tree").bind('click',function(){ - self.on_mode_switch("tree"); - }); - } else if(action.views[i][1] == "form") { - controller = new openerp.base.FormView(this.session, this.element_id + "_view_form", this.dataset, view_id); - controller.start(); - this.views.form = { view_id: view_id, controller: controller }; - this.$element.find(prefix_id + "_button_form").bind('click',function(){ - self.on_mode_switch("form"); - }); - } - } + this.$element.find('.views_switchers button').click(function() { + self.on_mode_switch($(this).data('view-type')); + }); + _.each(action.views, function(view) { + self.views[view[1]] = { view_id: view[0], controller: null }; + }); // switch to the first one in sequence this.on_mode_switch(action.views[0][1]); }, diff --git a/addons/base/static/src/xml/base.xml b/addons/base/static/src/xml/base.xml index 3124f5a5686..4cc8bb135a6 100644 --- a/addons/base/static/src/xml/base.xml +++ b/addons/base/static/src/xml/base.xml @@ -126,12 +126,11 @@ -
- +
- +
From 95bd1d12370ed6df3741204a9dc874639da0ba5f Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 31 Mar 2011 15:43:46 +0200 Subject: [PATCH 05/26] [IMP] have action manager correctly stop the view manager bzr revid: xmo@openerp.com-20110331134346-bn5fuq62ypcjs0fz --- addons/base/static/src/js/views.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/addons/base/static/src/js/views.js b/addons/base/static/src/js/views.js index b0b79c3e5f7..438f9fac3e4 100644 --- a/addons/base/static/src/js/views.js +++ b/addons/base/static/src/js/views.js @@ -18,7 +18,12 @@ openerp.base.ActionManager = openerp.base.Controller.extend({ do_action: function(action) { // instantiate the right controllers by understanding the action this.action = action; + // TODO: handle target=new + console.log(action); if(action.type == "ir.actions.act_window") { + if (this.viewmanager) { + this.viewmanager.stop(); + } this.viewmanager = new openerp.base.ViewManager(this.session,this.element_id); this.viewmanager.do_action_window(action); this.viewmanager.start(); From 4d76991c55bc25091cf2941160736e2c9e9139a5 Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Thu, 31 Mar 2011 16:44:22 +0200 Subject: [PATCH 06/26] [FIX] Search is now done by the view and not by the viewmanager bzr revid: fme@openerp.com-20110331144422-34wmf2bauc0lmz7d --- addons/base/static/src/js/form.js | 2 ++ addons/base/static/src/js/list.js | 17 +++++++++++++++++ addons/base/static/src/js/views.js | 30 ++++++++++-------------------- 3 files changed, 29 insertions(+), 20 deletions(-) diff --git a/addons/base/static/src/js/form.js b/addons/base/static/src/js/form.js index 9c0f8e9bf6b..be82b19c857 100644 --- a/addons/base/static/src/js/form.js +++ b/addons/base/static/src/js/form.js @@ -91,6 +91,8 @@ openerp.base.FormView = openerp.base.Controller.extend({ }, on_saved: function() { // Check response for exceptions, display error + }, + do_search: function (domains, contexts, groupbys) { } }); diff --git a/addons/base/static/src/js/list.js b/addons/base/static/src/js/list.js index f572bbb6eb9..4d4f6b4878c 100644 --- a/addons/base/static/src/js/list.js +++ b/addons/base/static/src/js/list.js @@ -8,6 +8,9 @@ openerp.base.ListView = openerp.base.Controller.extend({ this.model = dataset.model; this.view_id = view_id; this.name = ""; + // TODO: default to action.limit + // TODO: decide if limit is a property of DataSet and thus global to all views (calendar ?) + this.limit = 80; this.cols = []; @@ -72,6 +75,20 @@ openerp.base.ListView = openerp.base.Controller.extend({ return record.values; })); + }, + do_search: function (domains, contexts, groupbys) { + var self = this; + this.rpc('/base/session/eval_domain_and_context', { + domains: domains, + contexts: contexts, + group_by_seq: groupbys + }, function (results) { + // TODO: handle non-empty results.group_by with read_group + self.dataset.set({ + context: results.context, + domain: results.domain + }).fetch(0, self.limit); + }); } }); diff --git a/addons/base/static/src/js/views.js b/addons/base/static/src/js/views.js index 164e5d6cf72..5acdd44ae65 100644 --- a/addons/base/static/src/js/views.js +++ b/addons/base/static/src/js/views.js @@ -34,6 +34,7 @@ openerp.base.ViewManager = openerp.base.Controller.extend({ this.searchview = null; this.search_visible = true; this.active_view = null; + this.auto_search = false; // this.views = { "list": { "view_id":1234, "controller": instance} } this.views = {}; }, @@ -61,6 +62,10 @@ openerp.base.ViewManager = openerp.base.Controller.extend({ } controller.start(); this.views[view_type].controller = controller; + if (this.auto_search) { + this.searchview.on_loaded.add_last(this.searchview.do_search); + this.auto_search = false; + } } for (var i in this.views) { if (this.views[i].controller) { @@ -95,19 +100,18 @@ openerp.base.ViewManager = openerp.base.Controller.extend({ this.$element.html(QWeb.render("ViewManager", {"prefix": this.element_id, views: action.views})); this.searchview_id = false; - if(this.search_visible && action.search_view_id) { + if (this.search_visible && action.search_view_id) { this.searchview_id = action.search_view_id[0]; var searchview = this.searchview = new openerp.base.SearchView( this.session, this.element_id + "_search", this.dataset, this.searchview_id, this.search_defaults()); - searchview.on_search.add(this.do_search); + searchview.on_search.add(function() { + self.views[self.active_view].controller.do_search.apply(self, arguments); + }); searchview.start(); - if (action['auto_search']) { - searchview.on_loaded.add_last( - searchview.do_search); - } + this.auto_search = action.auto_search; } this.$element.find('.views_switchers button').click(function() { self.on_mode_switch($(this).data('view-type')); @@ -124,20 +128,6 @@ openerp.base.ViewManager = openerp.base.Controller.extend({ on_remove: function() { }, on_edit: function() { - }, - do_search: function (domains, contexts, groupbys) { - var self = this; - this.rpc('/base/session/eval_domain_and_context', { - domains: domains, - contexts: contexts, - group_by_seq: groupbys - }, function (results) { - // TODO: handle non-empty results.group_by with read_group - self.dataset.set({ - context: results.context, - domain: results.domain - }).fetch(0, self.action.limit); - }); } }); From 16d580a5732ea13312d295183110cfd17eea461d Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 31 Mar 2011 16:54:32 +0200 Subject: [PATCH 07/26] [FIX] generify view management using a registry bzr revid: xmo@openerp.com-20110331145432-qg0kigi1xtgxvk1i --- addons/base/static/src/js/form.js | 1 + addons/base/static/src/js/list.js | 1 + addons/base/static/src/js/views.js | 89 ++++++++++++++++------------- addons/base/static/src/xml/base.xml | 4 +- 4 files changed, 53 insertions(+), 42 deletions(-) diff --git a/addons/base/static/src/js/form.js b/addons/base/static/src/js/form.js index 9c0f8e9bf6b..3bca7be030d 100644 --- a/addons/base/static/src/js/form.js +++ b/addons/base/static/src/js/form.js @@ -1,6 +1,7 @@ openerp.base.form = function (openerp) { +openerp.base.views.add('form', 'openerp.base.FormView'); openerp.base.FormView = openerp.base.Controller.extend({ init: function(session, element_id, dataset, view_id) { this._super(session, element_id); diff --git a/addons/base/static/src/js/list.js b/addons/base/static/src/js/list.js index f572bbb6eb9..986470e0f0b 100644 --- a/addons/base/static/src/js/list.js +++ b/addons/base/static/src/js/list.js @@ -1,6 +1,7 @@ openerp.base.list = function (openerp) { +openerp.base.views.add('tree', 'openerp.base.ListView'); openerp.base.ListView = openerp.base.Controller.extend({ init: function(session, element_id, dataset, view_id) { this._super(session, element_id); diff --git a/addons/base/static/src/js/views.js b/addons/base/static/src/js/views.js index 438f9fac3e4..55e99d9fef9 100644 --- a/addons/base/static/src/js/views.js +++ b/addons/base/static/src/js/views.js @@ -19,7 +19,6 @@ openerp.base.ActionManager = openerp.base.Controller.extend({ // instantiate the right controllers by understanding the action this.action = action; // TODO: handle target=new - console.log(action); if(action.type == "ir.actions.act_window") { if (this.viewmanager) { this.viewmanager.stop(); @@ -31,23 +30,27 @@ openerp.base.ActionManager = openerp.base.Controller.extend({ } }); +/** + * Registry for all the main views + */ +openerp.base.views = new openerp.base.Registry(); openerp.base.ViewManager = openerp.base.Controller.extend({ -// This will be ViewManager Abstract/Common + // This will be ViewManager Abstract/Common init: function(session, element_id) { this._super(session, element_id); this.action = null; this.dataset = null; - this.searchview_id = false; this.searchview = null; - this.search_visible = true; // this.views = { "list": { "view_id":1234, "controller": instance} } this.views = {}; }, start: function() { }, on_mode_switch: function(view_type) { - for (var i in this.views) { - this.views[i].controller.$element.toggle(i === view_type); + for (var type in this.views) { + this.views[type].controller.$element.toggle(type === view_type); + this.$element.find('#' + this.element_id + '_button_' + type) + .attr('disabled', type === view_type); } }, /** @@ -67,6 +70,28 @@ openerp.base.ViewManager = openerp.base.Controller.extend({ }); return defaults; }, + /** + * Sets up the current viewmanager's search view. + * + * @param action the action being executed + */ + setup_search_view:function (action) { + if (this.searchview) { + this.searchview.stop(); + } + + var searchview = this.searchview = new openerp.base.SearchView( + this.session, this.element_id + "_search", + this.dataset, action.search_view_id[0] || false, + this.search_defaults()); + searchview.on_search.add(this.do_search); + searchview.start(); + + if (action['auto_search']) { + searchview.on_loaded.add_last( + searchview.do_search); + } + }, do_action_window: function(action) { var self = this; var prefix_id = "#" + this.element_id; @@ -76,42 +101,23 @@ openerp.base.ViewManager = openerp.base.Controller.extend({ this.$element.html(QWeb.render("ViewManager", {"prefix": this.element_id, views: action.views})); - this.searchview_id = false; - if(this.search_visible && action.search_view_id) { - this.searchview_id = action.search_view_id[0]; - var searchview = this.searchview = new openerp.base.SearchView( - this.session, this.element_id + "_search", - this.dataset, this.searchview_id, - this.search_defaults()); - searchview.on_search.add(this.do_search); - searchview.start(); + this.setup_search_view(action); + + _.each(action.views, function (view_descriptor) { + var view_id = view_descriptor[0], + view_type = view_descriptor[1]; + var view = new (openerp.base.views.get_object(view_type))( + self.session, self.element_id + '_view_' + view_type, + self.dataset, view_id); + view.start(); + self.views[view_type] = { view_id: view_id, controller: view }; + self.$element.find(prefix_id + '_button_' + view_type).click(function () { + self.on_mode_switch(view_type); + }); + }); - if (action['auto_search']) { - searchview.on_loaded.add_last( - searchview.do_search); - } - } - for(var i = 0; i < action.views.length; i++) { - var view_id, controller; - view_id = action.views[i][0]; - if(action.views[i][1] == "tree") { - controller = new openerp.base.ListView(this.session, this.element_id + "_view_tree", this.dataset, view_id); - controller.start(); - this.views.tree = { view_id: view_id, controller: controller }; - this.$element.find(prefix_id + "_button_tree").bind('click',function(){ - self.on_mode_switch("tree"); - }); - } else if(action.views[i][1] == "form") { - controller = new openerp.base.FormView(this.session, this.element_id + "_view_form", this.dataset, view_id); - controller.start(); - this.views.form = { view_id: view_id, controller: controller }; - this.$element.find(prefix_id + "_button_form").bind('click',function(){ - self.on_mode_switch("form"); - }); - } - } // switch to the first one in sequence - this.on_mode_switch("tree"); + this.on_mode_switch(action.view_mode.split(',')[0]); }, // create when root, also add to parent when o2m on_create: function() { @@ -234,10 +240,12 @@ openerp.base.BaseWidget = openerp.base.Controller.extend({ } }); +openerp.base.views.add('calendar', 'openerp.base.CalendarView'); openerp.base.CalendarView = openerp.base.Controller.extend({ // Dhtmlx scheduler ? }); +openerp.base.views.add('gantt', 'openerp.base.GanttView'); openerp.base.GanttView = openerp.base.Controller.extend({ // Dhtmlx gantt ? }); @@ -246,6 +254,7 @@ openerp.base.DiagramView = openerp.base.Controller.extend({ // }); +openerp.base.views.add('graph', 'openerp.base.GraphView'); openerp.base.GraphView = openerp.base.Controller.extend({ }); diff --git a/addons/base/static/src/xml/base.xml b/addons/base/static/src/xml/base.xml index 006f8106105..5b72291bb0e 100644 --- a/addons/base/static/src/xml/base.xml +++ b/addons/base/static/src/xml/base.xml @@ -134,9 +134,9 @@
-
+
-
+
From 79ce2be95efecbe39614a533524484abc4a99cd5 Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Thu, 31 Mar 2011 17:14:41 +0200 Subject: [PATCH 08/26] [IMP] DataSet has no more notion of fields. These are now inherent to the views. bzr revid: fme@openerp.com-20110331151441-z5xtn0j4kohi72ja --- addons/base/static/src/js/data.js | 27 +++++++-------------------- addons/base/static/src/js/list.js | 2 +- 2 files changed, 8 insertions(+), 21 deletions(-) diff --git a/addons/base/static/src/js/data.js b/addons/base/static/src/js/data.js index 2fd4d9f8b6b..ad4028fb155 100644 --- a/addons/base/static/src/js/data.js +++ b/addons/base/static/src/js/data.js @@ -32,8 +32,6 @@ openerp.base.DataSet = openerp.base.Controller.extend( this._super(session); this.model = model; - this._fields = null; - this._ids = []; this._active_ids = null; this._active_id_index = 0; @@ -43,12 +41,6 @@ openerp.base.DataSet = openerp.base.Controller.extend( this._context = {}; }, start: function() { - // TODO: fields_view_get fields selection? - this.rpc("/base/dataset/fields", {"model":this.model}, this.on_fields); - }, - on_fields: function(result) { - this._fields = result.fields; - this.on_ready(); }, /** @@ -63,12 +55,11 @@ openerp.base.DataSet = openerp.base.Controller.extend( * @param {Number} [limit=null] The maximum number of records to return * @returns itself */ - fetch: function (offset, limit) { + fetch: function (fields, offset, limit) { offset = offset || 0; - limit = limit || null; this.rpc('/base/dataset/find', { model: this.model, - fields: this._fields, + fields: fields, domain: this._domain, context: this._context, sort: this._sort, @@ -77,9 +68,7 @@ openerp.base.DataSet = openerp.base.Controller.extend( }, _.bind(function (records) { var data_records = _.map( records, function (record) { - return new openerp.base.DataRecord( - this.session, this.model, - this._fields, record); + return new openerp.base.DataRecord(this.session, this.model, fields, record); }, this); this.on_fetch(data_records, { @@ -116,16 +105,14 @@ openerp.base.DataSet = openerp.base.Controller.extend( * * @returns itself */ - active_ids: function () { + active_ids: function (fields) { this.rpc('/base/dataset/get', { ids: this.get_active_ids(), model: this.model }, _.bind(function (records) { this.on_active_ids(_.map( records, function (record) { - return new openerp.base.DataRecord( - this.session, this.model, - this._fields, record); + return new openerp.base.DataRecord(this.session, this.model, fields, record); }, this)); }, this)); return this; @@ -149,7 +136,7 @@ openerp.base.DataSet = openerp.base.Controller.extend( * * @returns itself */ - active_id: function () { + active_id: function (fields) { this.rpc('/base/dataset/get', { ids: [this.get_active_id()], model: this.model @@ -158,7 +145,7 @@ openerp.base.DataSet = openerp.base.Controller.extend( this.on_active_id( record && new openerp.base.DataRecord( this.session, this.model, - this._fields, record)); + fields, record)); }, this)); return this; }, diff --git a/addons/base/static/src/js/list.js b/addons/base/static/src/js/list.js index 4d4f6b4878c..e63a063af7f 100644 --- a/addons/base/static/src/js/list.js +++ b/addons/base/static/src/js/list.js @@ -87,7 +87,7 @@ openerp.base.ListView = openerp.base.Controller.extend({ self.dataset.set({ context: results.context, domain: results.domain - }).fetch(0, self.limit); + }).fetch(self.fields_view.fields, 0, self.limit); }); } }); From baf8d2d9a9b46c2216bf90bec48d68efb19d9acb Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 31 Mar 2011 17:25:31 +0200 Subject: [PATCH 09/26] [IMP] server behavior fix: split tree view mode into tree and list based on view_type, nuke view_type bzr revid: xmo@openerp.com-20110331152531-cpff6js0k8v4dtk9 --- addons/base/controllers/main.py | 30 +++++++++++++++++ addons/base/static/src/js/list.js | 2 +- addons/base/static/src/js/views.js | 8 +++++ addons/base/test/test_menu.py | 52 ++++++++++++++++++++++++++++++ 4 files changed, 91 insertions(+), 1 deletion(-) diff --git a/addons/base/controllers/main.py b/addons/base/controllers/main.py index 40d1c7f5823..6e99aed0c36 100644 --- a/addons/base/controllers/main.py +++ b/addons/base/controllers/main.py @@ -230,8 +230,38 @@ class Menu(openerpweb.Controller): req.session.evaluation_context( action['context'])) or [] + self.fix_view_modes(action) + return {"action": actions} + def fix_view_modes(self, action): + """ For historical reasons, OpenERP has weird dealings in relation to + view_mode and the view_type attribute (on window actions): + + * one of the view modes is ``tree``, which stands for both list views + and tree views + * the choice is made by checking ``view_type``, which is either + ``form`` for a list view or ``tree`` for an actual tree view + + This methods simply folds the view_type into view_mode by adding a + new view mode ``list`` which is the result of the ``tree`` view_mode + in conjunction with the ``form`` view_type. + + TODO: this should go into the doc, some kind of "peculiarities" section + + :param dict action: an action descriptor + :returns: nothing, the action is modified in place + """ + if action.pop('view_type') != 'form': + return + + action['view_mode'] = ','.join( + mode if mode != 'tree' else 'list' + for mode in action['view_mode'].split(',')) + action['views'] = [ + [id, mode if mode != 'tree' else 'list'] + for id, mode in action['views'] + ] class DataSet(openerpweb.Controller): _cp_path = "/base/dataset" diff --git a/addons/base/static/src/js/list.js b/addons/base/static/src/js/list.js index 986470e0f0b..2f82f9b50ed 100644 --- a/addons/base/static/src/js/list.js +++ b/addons/base/static/src/js/list.js @@ -1,7 +1,7 @@ openerp.base.list = function (openerp) { -openerp.base.views.add('tree', 'openerp.base.ListView'); +openerp.base.views.add('list', 'openerp.base.ListView'); openerp.base.ListView = openerp.base.Controller.extend({ init: function(session, element_id, dataset, view_id) { this._super(session, element_id); diff --git a/addons/base/static/src/js/views.js b/addons/base/static/src/js/views.js index 55e99d9fef9..a66bf3ab4e9 100644 --- a/addons/base/static/src/js/views.js +++ b/addons/base/static/src/js/views.js @@ -250,6 +250,14 @@ openerp.base.GanttView = openerp.base.Controller.extend({ // Dhtmlx gantt ? }); +openerp.base.views.add('tree', 'openerp.base.TreeView'); +/** + * Genuine tree view (the one displayed as a tree, not the list) + */ +openerp.base.TreeView = openerp.base.Controller.extend({ + +}); + openerp.base.DiagramView = openerp.base.Controller.extend({ // }); diff --git a/addons/base/test/test_menu.py b/addons/base/test/test_menu.py index c63b19591aa..8f2c5bc42fc 100644 --- a/addons/base/test/test_menu.py +++ b/addons/base/test/test_menu.py @@ -97,3 +97,55 @@ class LoadTest(unittest2.TestCase): }] }] ) + +class ActionMungerTest(unittest2.TestCase): + def setUp(self): + self.menu = base.controllers.main.Menu() + def test_actual_treeview(self): + action = { + "views": [[False, "tree"], [False, "form"], + [False, "calendar"]], + "view_type": "tree", + "view_id": False, + "view_mode": "tree,form,calendar" + } + changed = action.copy() + del action['view_type'] + self.menu.fix_view_modes(changed) + + self.assertEqual(changed, action) + + def test_list_view(self): + action = { + "views": [[False, "tree"], [False, "form"], + [False, "calendar"]], + "view_type": "form", + "view_id": False, + "view_mode": "tree,form,calendar" + } + self.menu.fix_view_modes(action) + + self.assertEqual(action, { + "views": [[False, "list"], [False, "form"], + [False, "calendar"]], + "view_id": False, + "view_mode": "list,form,calendar" + }) + + def test_redundant_views(self): + + action = { + "views": [[False, "tree"], [False, "form"], + [False, "calendar"], [42, "tree"]], + "view_type": "form", + "view_id": False, + "view_mode": "tree,form,calendar" + } + self.menu.fix_view_modes(action) + + self.assertEqual(action, { + "views": [[False, "list"], [False, "form"], + [False, "calendar"], [42, "list"]], + "view_id": False, + "view_mode": "list,form,calendar" + }) From 6de5177cf314fa80fbf2e2b48ee29a153638db60 Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Thu, 31 Mar 2011 18:03:09 +0200 Subject: [PATCH 10/26] [ADD] calendar image bzr revid: fme@openerp.com-20110331160309-0jybr51uf03ygizh --- addons/base/static/src/img/ui/field_calendar.png | Bin 0 -> 454 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 addons/base/static/src/img/ui/field_calendar.png diff --git a/addons/base/static/src/img/ui/field_calendar.png b/addons/base/static/src/img/ui/field_calendar.png new file mode 100644 index 0000000000000000000000000000000000000000..99a1f3f9ef1b07bf0385eefb5f3d6d5fcd2af4b3 GIT binary patch literal 454 zcmV;%0XhDOP)Q^fPI6jb+d|@`N^P8ZCnlXtvx#1dz>s4p`63Ko=O*@E0e*Xlvl05*JydPcV3@b8j^s^kyFprPd^ z&icYiRsRH(uhHoZi4Wr@agX?Q3g_`8xQiqrr+3)l64*Gt#ag67KTkmJl!#DCa~f$K w)a@oQr=i7MY3xA*ql1<)h&dH)5CJvm8}XjbDoaBt5C8xG07*qoM6N<$f(WI^)c^nh literal 0 HcmV?d00001 From 6910a9c19af797bdee9c7d2dc315154810738c40 Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Thu, 31 Mar 2011 18:03:23 +0200 Subject: [PATCH 11/26] [ADD] Activated simple datepicker on date field bzr revid: fme@openerp.com-20110331160323-opcovdlczzi6ugxs --- addons/base/static/src/css/base.css | 3 +++ addons/base/static/src/js/form.js | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/addons/base/static/src/css/base.css b/addons/base/static/src/css/base.css index 58adf4183e4..2fb34ecf78c 100644 --- a/addons/base/static/src/css/base.css +++ b/addons/base/static/src/css/base.css @@ -413,6 +413,9 @@ body.openerp { border-radius: 3px; background: white; } +.openerp input.field_date { + background: url('../img/ui/field_calendar.png') no-repeat right center; +} /* Widgets */ .openerp .separator { diff --git a/addons/base/static/src/js/form.js b/addons/base/static/src/js/form.js index 9c0f8e9bf6b..65238e1def3 100644 --- a/addons/base/static/src/js/form.js +++ b/addons/base/static/src/js/form.js @@ -462,6 +462,10 @@ openerp.base.form.FieldDate = openerp.base.form.FieldChar.extend({ init: function(view, node) { this._super(view, node); this.template = "FieldDate"; + }, + start: function() { + this._super.apply(this, arguments); + this.$element.find('input').datepicker(); } }); From de1912d2a138d54a085d52007883aa30e5d0e697 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Fri, 1 Apr 2011 11:06:53 +0200 Subject: [PATCH 12/26] [IMP] enabling/disabling of switcher buttons on view switch bzr revid: xmo@openerp.com-20110401090653-jr21w79p5e5pdfts --- addons/base/static/src/css/base.css | 2 +- addons/base/static/src/js/views.js | 8 +++++++- addons/base/static/src/xml/base.xml | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/addons/base/static/src/css/base.css b/addons/base/static/src/css/base.css index 4fc11cd1172..a92f80f8f56 100644 --- a/addons/base/static/src/css/base.css +++ b/addons/base/static/src/css/base.css @@ -396,7 +396,7 @@ body.openerp { } /* View Manager */ -.openerp .views_switchers { +.openerp .views-switchers { text-align: right; } diff --git a/addons/base/static/src/js/views.js b/addons/base/static/src/js/views.js index 45a823f0181..8fa11b96049 100644 --- a/addons/base/static/src/js/views.js +++ b/addons/base/static/src/js/views.js @@ -55,6 +55,12 @@ openerp.base.ViewManager = openerp.base.Controller.extend({ controller.start(); this.views[view_type].controller = controller; } + + this.$element + .find('.views-switchers button').attr('disabled', true) + .filter('[data-view-type!="' + view_type + '"]') + .removeAttr('disabled'); + for (var i in this.views) { if (this.views[i].controller) { this.views[i].controller.$element.toggle(i === view_type); @@ -108,7 +114,7 @@ openerp.base.ViewManager = openerp.base.Controller.extend({ this.setup_search_view(action); - this.$element.find('.views_switchers button').click(function() { + this.$element.find('.views-switchers button').click(function() { self.on_mode_switch($(this).data('view-type')); }); _.each(action.views, function(view) { diff --git a/addons/base/static/src/xml/base.xml b/addons/base/static/src/xml/base.xml index 6dac4cb1b94..d610ea001b4 100644 --- a/addons/base/static/src/xml/base.xml +++ b/addons/base/static/src/xml/base.xml @@ -126,7 +126,7 @@ -
+
+ + +
+ + to + + +
+
- to -
From 1c07f1dd5315134973601c80fa91299f20cd4f16 Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Mon, 4 Apr 2011 10:28:42 +0200 Subject: [PATCH 21/26] [FIX] Fix dataset testsuite that I broke in rev #4648 bzr revid: fme@openerp.com-20110404082842-i95u0pg31xj8vy1t --- addons/base/static/test/dataset.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/addons/base/static/test/dataset.js b/addons/base/static/test/dataset.js index 6492d505f31..8cbb23ad265 100644 --- a/addons/base/static/test/dataset.js +++ b/addons/base/static/test/dataset.js @@ -179,7 +179,9 @@ $(document).ready(function () { equal(event.limit, 42); start(); }); - dataset.fetch(20, 42); + dataset.fetch({ + name : { type : 'char' } + }, 20, 42); }); asyncTest("Domain and context propagation", 3, function () { var dataset = new openerp.base.DataSet( From 3cc735cc2cce4c82dd6d31a745936227dac64ff0 Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Mon, 4 Apr 2011 11:00:47 +0200 Subject: [PATCH 22/26] [IMP] Improved date widget styling bzr revid: fme@openerp.com-20110404090047-yvmbfnnt910b553n --- addons/base/static/src/css/base.css | 4 ++++ addons/base/static/src/xml/base.xml | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/addons/base/static/src/css/base.css b/addons/base/static/src/css/base.css index 4fc11cd1172..53a1c619e88 100644 --- a/addons/base/static/src/css/base.css +++ b/addons/base/static/src/css/base.css @@ -420,6 +420,10 @@ body.openerp { } .openerp input.field_date { background: url('../img/ui/field_calendar.png') no-repeat right center; + background-origin: content-box; + -moz-background-origin: content; + -moz-background-origin: content-box; + -webkit-background-origin: content-box; } /* Widgets */ diff --git a/addons/base/static/src/xml/base.xml b/addons/base/static/src/xml/base.xml index 4f04fe21761..052d6e51942 100644 --- a/addons/base/static/src/xml/base.xml +++ b/addons/base/static/src/xml/base.xml @@ -326,10 +326,10 @@ (?)
- to -
From 96e08cf3d1ab38fc3664de7f274958ee38f14542 Mon Sep 17 00:00:00 2001 From: Antony Lesuisse Date: Fri, 1 Apr 2011 12:44:54 +0200 Subject: [PATCH 23/26] action handling proposal bzr revid: al@openerp.com-20110401104454-djtm0728azy66g76 --- addons/base/static/src/js/form.js | 2 +- addons/base/static/src/js/views.js | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/addons/base/static/src/js/form.js b/addons/base/static/src/js/form.js index c07f5e6b0e3..a3c46cab584 100644 --- a/addons/base/static/src/js/form.js +++ b/addons/base/static/src/js/form.js @@ -92,7 +92,7 @@ openerp.base.FormView = openerp.base.Controller.extend({ on_saved: function() { // Check response for exceptions, display error }, - do_search: function (domains, contexts, groupbys) { + on_action: function (action) { } }); diff --git a/addons/base/static/src/js/views.js b/addons/base/static/src/js/views.js index 5acdd44ae65..4419a4ca96e 100644 --- a/addons/base/static/src/js/views.js +++ b/addons/base/static/src/js/views.js @@ -61,6 +61,7 @@ openerp.base.ViewManager = openerp.base.Controller.extend({ break; } controller.start(); + // controller.on_action.add(this.on_action); ?? this.views[view_type].controller = controller; if (this.auto_search) { this.searchview.on_loaded.add_last(this.searchview.do_search); @@ -129,6 +130,11 @@ openerp.base.ViewManager = openerp.base.Controller.extend({ }, on_edit: function() { } + /** + * Called when one of the view want to execute an action + */ + on_action: function(action) { + } }); openerp.base.ViewManagerRoot = openerp.base.Controller.extend({ From 0a5a8cd81a5c53966328af3f9c314c994c394590 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Fri, 1 Apr 2011 12:45:00 +0200 Subject: [PATCH 24/26] [IMP] show and hide search view based on whether the currently displayed view is searchable Note: searchability is *opt-out*, a view is searchable until it unambiguously states 'do not search me' bzr revid: xmo@openerp.com-20110401104500-n7g6so01xb8oow3n --- addons/base/controllers/main.py | 1 + addons/base/static/src/js/form.js | 15 ++++++++++++++- addons/base/static/src/js/search.js | 6 ++++++ addons/base/static/src/js/views.js | 6 ++++++ 4 files changed, 27 insertions(+), 1 deletion(-) diff --git a/addons/base/controllers/main.py b/addons/base/controllers/main.py index 0258b86e616..73d44041248 100644 --- a/addons/base/controllers/main.py +++ b/addons/base/controllers/main.py @@ -88,6 +88,7 @@ class Session(openerpweb.Controller): @openerpweb.jsonrequest def login(self, req, db, login, password): req.session.login(db, login, password) + return { "session_id": req.session_id, "uid": req.session._uid, diff --git a/addons/base/static/src/js/form.js b/addons/base/static/src/js/form.js index 85cbce98bd8..707a5149542 100644 --- a/addons/base/static/src/js/form.js +++ b/addons/base/static/src/js/form.js @@ -2,7 +2,20 @@ openerp.base.form = function (openerp) { openerp.base.views.add('form', 'openerp.base.FormView'); -openerp.base.FormView = openerp.base.Controller.extend({ +openerp.base.FormView = openerp.base.Controller.extend( + /** @lends openerp.base.FormView# */{ + /** + * Indicates that this view is not searchable, and thus that no search + * view should be displayed (if there is one active). + */ + searchable: false, + /** + * @constructs + * @param {openerp.base.Session} session the current openerp session + * @param {String} element_id this view's root element id + * @param {openerp.base.DataSet} dataset the dataset this view will work with + * @param {String} view_id the identifier of the OpenERP view object + */ init: function(session, element_id, dataset, view_id) { this._super(session, element_id); this.dataset = dataset; diff --git a/addons/base/static/src/js/search.js b/addons/base/static/src/js/search.js index 5d6b175ac88..ef00a051626 100644 --- a/addons/base/static/src/js/search.js +++ b/addons/base/static/src/js/search.js @@ -16,6 +16,12 @@ openerp.base.SearchView = openerp.base.Controller.extend({ //this.log('Starting SearchView '+this.model+this.view_id) this.rpc("/base/searchview/load", {"model": this.model, "view_id":this.view_id}, this.on_loaded); }, + show: function () { + this.$element.show(); + }, + hide: function () { + this.$element.hide(); + }, /** * Builds a list of widget rows (each row is an array of widgets) * diff --git a/addons/base/static/src/js/views.js b/addons/base/static/src/js/views.js index 9b0572b434d..4e26766bafc 100644 --- a/addons/base/static/src/js/views.js +++ b/addons/base/static/src/js/views.js @@ -56,6 +56,12 @@ openerp.base.ViewManager = openerp.base.Controller.extend({ this.views[view_type].controller = controller; } + if (view.controller.searchable === false) { + this.searchview.hide(); + } else { + this.searchview.show(); + } + this.$element .find('.views-switchers button').removeAttr('disabled') .filter('[data-view-type="' + view_type + '"]') From f880ecb5d98f31d3ad3051df96f55b76bc0820a2 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Fri, 1 Apr 2011 13:50:05 +0200 Subject: [PATCH 25/26] [FIX] race condition on auto_search due to desynchronized loading of search view and {any default main view} If loading of search view completes before loading of content view, do_search is immediately called on search view and forwarded to content view which is *not* ready yet and therefore blows up. Solved by having start() methods return a promise object (or a non-promise object taken as a fulfilled promise) which the viewmanager can use (from both search view and initial content view) to synchronize when both are ready and it's possible to start a search bzr revid: xmo@openerp.com-20110401115005-inceqsj7qn4k3b8e --- addons/base/static/src/js/chrome.js | 44 +++++++++++++++++++++++++++-- addons/base/static/src/js/form.js | 2 +- addons/base/static/src/js/list.js | 2 +- addons/base/static/src/js/search.js | 2 +- addons/base/static/src/js/views.js | 25 +++++++++++----- 5 files changed, 62 insertions(+), 13 deletions(-) diff --git a/addons/base/static/src/js/chrome.js b/addons/base/static/src/js/chrome.js index 14577c9d0ab..523f2c8a2b7 100644 --- a/addons/base/static/src/js/chrome.js +++ b/addons/base/static/src/js/chrome.js @@ -184,8 +184,19 @@ openerp.base.BasicController = Class.extend( * Controller start * event binding, rpc and callback calling required to initialize the * object can happen here + * + * Returns a promise object letting callers (subclasses and direct callers) + * know when this component is done starting + * + * @returns {jQuery.Deferred} */ start: function() { + // returns an already fulfilled promise. Maybe we could return nothing? + // $.when can take non-deferred and in that case it simply considers + // them all as fulfilled promises. + // But in thise case we *have* to ensure callers use $.when and don't + // try to call deferred methods on this return value. + return $.Deferred().done().promise(); }, stop: function() { }, @@ -250,6 +261,19 @@ openerp.base.Session = openerp.base.BasicController.extend( } }); }, + /** + * Executes an RPC call, registering the provided callbacks. + * + * Registers a default error callback if none is provided, and handles + * setting the correct session id and session context in the parameter + * objects + * + * @param {String} url RPC endpoint + * @param {Object} params call parameters + * @param {Function} success_callback function to execute on RPC call success + * @param {Function} error_callback function to execute on RPC call failure + * @returns {jQuery.Deferred} jquery-provided ajax deferred + */ rpc: function(url, params, success_callback, error_callback) { // Construct a JSON-RPC2 request, method is currently unused params.session_id = this.session_id; @@ -259,17 +283,22 @@ openerp.base.Session = openerp.base.BasicController.extend( error_callback = typeof(error_callback) != "undefined" ? error_callback : this.on_rpc_error; // Call using the rpc_mode - this.rpc_ajax(url, { + return this.rpc_ajax(url, { jsonrpc: "2.0", method: "call", params: params, id:null }, success_callback, error_callback); }, + /** + * Raw JSON-RPC call + * + * @returns {jQuery.Deferred} ajax-based deferred object + */ rpc_ajax: function(url, payload, success_callback, error_callback) { var self = this; this.on_rpc_request(); - $.ajax({ + return $.ajax({ type: "POST", url: url, dataType: 'json', @@ -446,9 +475,18 @@ openerp.base.Controller = openerp.base.BasicController.extend( if(this.session) this.session.log.apply(this.session,arguments); }, + /** + * Performs a JSON-RPC call + * + * @param {String} url endpoint url + * @param {Object} data RPC parameters + * @param {Function} success RPC call success callback + * @param {Function} error RPC call error callback + * @returns {jQuery.Deferred} deferred object for the RPC call + */ rpc: function(url, data, success, error) { // TODO: support additional arguments ? - this.session.rpc(url, data, success, error); + return this.session.rpc(url, data, success, error); } }); diff --git a/addons/base/static/src/js/form.js b/addons/base/static/src/js/form.js index 707a5149542..65ebd80d751 100644 --- a/addons/base/static/src/js/form.js +++ b/addons/base/static/src/js/form.js @@ -30,7 +30,7 @@ openerp.base.FormView = openerp.base.Controller.extend( }, start: function() { //this.log('Starting FormView '+this.model+this.view_id) - this.rpc("/base/formview/load", {"model": this.model, "view_id": this.view_id}, this.on_loaded); + return this.rpc("/base/formview/load", {"model": this.model, "view_id": this.view_id}, this.on_loaded); }, on_loaded: function(data) { var self = this; diff --git a/addons/base/static/src/js/list.js b/addons/base/static/src/js/list.js index 5c8599176a1..c64f08a79cf 100644 --- a/addons/base/static/src/js/list.js +++ b/addons/base/static/src/js/list.js @@ -23,7 +23,7 @@ openerp.base.ListView = openerp.base.Controller.extend({ }, start: function() { //this.log('Starting ListView '+this.model+this.view_id) - this.rpc("/base/listview/load", {"model": this.model, "view_id":this.view_id}, this.on_loaded); + return this.rpc("/base/listview/load", {"model": this.model, "view_id":this.view_id}, this.on_loaded); }, on_loaded: function(data) { this.fields_view = data.fields_view; diff --git a/addons/base/static/src/js/search.js b/addons/base/static/src/js/search.js index ef00a051626..b66d1d759aa 100644 --- a/addons/base/static/src/js/search.js +++ b/addons/base/static/src/js/search.js @@ -14,7 +14,7 @@ openerp.base.SearchView = openerp.base.Controller.extend({ }, start: function() { //this.log('Starting SearchView '+this.model+this.view_id) - this.rpc("/base/searchview/load", {"model": this.model, "view_id":this.view_id}, this.on_loaded); + return this.rpc("/base/searchview/load", {"model": this.model, "view_id":this.view_id}, this.on_loaded); }, show: function () { this.$element.show(); diff --git a/addons/base/static/src/js/views.js b/addons/base/static/src/js/views.js index 4e26766bafc..c914edfea85 100644 --- a/addons/base/static/src/js/views.js +++ b/addons/base/static/src/js/views.js @@ -44,7 +44,14 @@ openerp.base.ViewManager = openerp.base.Controller.extend({ }, start: function() { }, + /** + * Asks the view manager to switch visualization mode. + * + * @param {String} view_type type of view to display + * @returns {jQuery.Deferred} new view loading promise + */ on_mode_switch: function(view_type) { + var view_promise; this.active_view = view_type; var view = this.views[view_type]; if (!view.controller) { @@ -52,7 +59,7 @@ openerp.base.ViewManager = openerp.base.Controller.extend({ var controller = new (openerp.base.views.get_object(view_type))( this.session, this.element_id + "_view_" + view_type, this.dataset, view.view_id); - controller.start(); + view_promise = controller.start(); this.views[view_type].controller = controller; } @@ -72,6 +79,7 @@ openerp.base.ViewManager = openerp.base.Controller.extend({ this.views[i].controller.$element.toggle(i === view_type); } } + return view_promise; }, /** * Extract search view defaults from the current action's context. @@ -94,6 +102,7 @@ openerp.base.ViewManager = openerp.base.Controller.extend({ * Sets up the current viewmanager's search view. * * @param action the action being executed + * @returns {jQuery.Deferred} search view startup deferred */ setup_search_view:function (action) { var self = this; @@ -108,7 +117,7 @@ openerp.base.ViewManager = openerp.base.Controller.extend({ searchview.on_search.add(function() { self.views[self.active_view].controller.do_search.apply(self, arguments); }); - searchview.start(); + return searchview.start(); }, do_action_window: function(action) { var self = this; @@ -118,7 +127,7 @@ openerp.base.ViewManager = openerp.base.Controller.extend({ this.$element.html(QWeb.render("ViewManager", {"prefix": this.element_id, views: action.views})); - this.setup_search_view(action); + var searchview_loaded = this.setup_search_view(action); this.$element.find('.views-switchers button').click(function() { self.on_mode_switch($(this).data('view-type')); @@ -127,11 +136,13 @@ openerp.base.ViewManager = openerp.base.Controller.extend({ self.views[view[1]] = { view_id: view[0], controller: null }; }); - if (action['auto_search']) { - this.searchview.on_loaded.add_last(this.searchview.do_search); - } // switch to the first one in sequence - this.on_mode_switch(action.views[0][1]); + var inital_view_loaded = this.on_mode_switch(action.views[0][1]); + + if (action['auto_search']) { + $.when(searchview_loaded, inital_view_loaded) + .then(this.searchview.do_search); + } }, // create when root, also add to parent when o2m on_create: function() { From 1865a1dae078836966d76f8525640988c8a836e3 Mon Sep 17 00:00:00 2001 From: Antony Lesuisse Date: Mon, 4 Apr 2011 13:38:03 +0200 Subject: [PATCH 26/26] typo bzr revid: al@openerp.com-20110404113803-hme1ls2rxahxa64w --- addons/base/static/src/js/views.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/base/static/src/js/views.js b/addons/base/static/src/js/views.js index 75eb4cef82d..d5fface8139 100644 --- a/addons/base/static/src/js/views.js +++ b/addons/base/static/src/js/views.js @@ -149,7 +149,7 @@ openerp.base.ViewManager = openerp.base.Controller.extend({ on_remove: function() { }, on_edit: function() { - } + }, /** * Called when one of the view want to execute an action */