diff --git a/addons/web/static/src/js/chrome.js b/addons/web/static/src/js/chrome.js index bed4a159535..01bfb63a580 100644 --- a/addons/web/static/src/js/chrome.js +++ b/addons/web/static/src/js/chrome.js @@ -146,8 +146,8 @@ instance.web.Dialog = instance.web.Widget.extend({ this.$buttons = $('
'); this.$el.dialog("widget").append(this.$buttons); } - var res = this.start(); this.dialog_inited = true; + var res = this.start(); return res; }, close: function() { diff --git a/addons/web/static/src/js/corelib.js b/addons/web/static/src/js/corelib.js index 62ac48f0c61..da7e8b584ef 100644 --- a/addons/web/static/src/js/corelib.js +++ b/addons/web/static/src/js/corelib.js @@ -820,9 +820,10 @@ instance.web.Widget = instance.web.Class.extend(instance.web.WidgetMixin, { * 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; }, diff --git a/addons/web/static/src/js/data_export.js b/addons/web/static/src/js/data_export.js index 29aea6362f7..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,13 +22,7 @@ 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();} - }); + 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')) { diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index 1c32d5ef63e..4d69e07489a 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -30,7 +30,7 @@ instance.web.form.FieldManagerMixin = { /** * Must return the asked field as in fields_get. */ - get_field: function(field_name) {}, + get_field_desc: function(field_name) {}, /** * Returns the current value of a field present in the view. See the get_value() method * method in FieldInterface for further information. @@ -44,6 +44,21 @@ instance.web.form.FieldManagerMixin = { @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'); @@ -1085,7 +1100,7 @@ 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() { @@ -1103,12 +1118,34 @@ 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]; }, 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); + }, + _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)); + }, }); /** @@ -1528,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); @@ -1641,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}); @@ -1694,8 +1771,9 @@ instance.web.form.FormWidget = instance.web.Widget.extend(instance.web.form.Invi */ init: function(field_manager, node) { this._super(field_manager); - this.view = 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, this.field_manager, this.modifiers.invisible); @@ -1737,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; } } @@ -1771,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. @@ -1802,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; @@ -1813,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; @@ -2001,7 +2059,7 @@ instance.web.form.AbstractField = instance.web.form.FormWidget.extend(instance.w var self = this this._super(field_manager, node); 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 || '{}'); @@ -2015,11 +2073,11 @@ instance.web.form.AbstractField = instance.web.form.FormWidget.extend(instance.w renderElement: function() { var self = this; this._super(); - if (this.field.translate) { + 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.$el.find('label[for=' + this.id_for_label + ']'); + 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() { @@ -2884,7 +2942,7 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc 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"), @@ -3479,7 +3537,7 @@ 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) { @@ -3595,7 +3653,7 @@ 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) { @@ -3623,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); }); @@ -3879,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 = {}; @@ -4149,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) { @@ -4551,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: { + 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.on("change:value", this, this.on_selection_changed); this.selection.setElement(this.$(".oe_form_view_reference_selection")); this.selection.renderElement(); @@ -4564,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: { + 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.on("change:value", this, this.data_changed); this.m2o.setElement(this.$(".oe_form_view_reference_m2o")); this.m2o.renderElement(); @@ -4608,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, { @@ -4709,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() { @@ -4753,7 +4810,9 @@ instance.web.form.FieldBinaryFile = instance.web.form.FieldBinary.extend({ } else { this.$el.find('a').show(!!this.get('value')); if (this.get('value')) { - var show_value = _t("Download") + " " + (this.view.datarecord[this.node.attrs.filename] || ''); + var show_value = _t("Download") + if (this.view) + show_value += " " + (this.view.datarecord[this.node.attrs.filename] || ''); this.$el.find('a').text(show_value); } } @@ -4765,12 +4824,6 @@ instance.web.form.FieldBinaryFile = instance.web.form.FieldBinary.extend({ 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.$el.find('input').eq(0).val(''); @@ -4819,10 +4872,12 @@ 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(''); } }); diff --git a/addons/web/static/src/js/views.js b/addons/web/static/src/js/views.js index 7b5fcc70289..99f4d3e35e8 100644 --- a/addons/web/static/src/js/views.js +++ b/addons/web/static/src/js/views.js @@ -99,6 +99,10 @@ instance.web.ActionManager = instance.web.Widget.extend({ 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) { @@ -220,14 +224,14 @@ instance.web.ActionManager = instance.web.Widget.extend({ } }); }, - do_action: function(action, on_close, clear_breadcrumbs) { + 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, on_close, clear_breadcrumbs); + 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 }).pipe(function(result) { - return self.do_action(result.result, on_close, clear_breadcrumbs); + return self.do_action(result.result, on_close, clear_breadcrumbs, on_reverse_breadcrumb); }); } if (!action.type) { @@ -249,7 +253,7 @@ instance.web.ActionManager = instance.web.Widget.extend({ console.error("Action manager can't handle action of type " + action.type, action); return $.Deferred().reject(); } - return this[type](action, on_close, clear_breadcrumbs); + return this[type](action, on_close, clear_breadcrumbs, on_reverse_breadcrumb); }, null_action: function() { this.dialog_stop(); @@ -300,24 +304,24 @@ instance.web.ActionManager = instance.web.Widget.extend({ return this.inner_widget.appendTo(this.$el); } }, - ir_actions_act_window: function (action, on_close, clear_breadcrumbs) { + ir_actions_act_window: function (action, on_close, clear_breadcrumbs, on_reverse_breadcrumb) { var self = this; 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_close, clear_breadcrumbs); + 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) { + ir_actions_client: function (action, on_close, clear_breadcrumbs, on_reverse_breadcrumb) { var self = this; 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); + return this.do_action(next, on_close, clear_breadcrumbs, on_reverse_breadcrumb); } return $.when(); } @@ -329,13 +333,14 @@ instance.web.ActionManager = instance.web.Widget.extend({ post_process: function(widget) { self.push_breadcrumb({ widget: widget, - title: action.name + title: action.name, + on_reverse_breadcrumb: on_reverse_breadcrumb, }); if (action.tag !== 'reload') { self.do_push_state({}); } } - }, on_close, clear_breadcrumbs); + }, on_close, clear_breadcrumbs, on_reverse_breadcrumb); }, ir_actions_act_window_close: function (action, on_closed) { if (!this.dialog && on_closed) { @@ -343,13 +348,13 @@ instance.web.ActionManager = instance.web.Widget.extend({ } this.dialog_stop(); }, - ir_actions_server: function (action, on_closed, clear_breadcrumbs) { + 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, clear_breadcrumbs) + self.do_action(action, on_closed, clear_breadcrumbs, on_reverse_breadcrumb) }); }, ir_actions_report_xml: function(action, on_closed) { @@ -544,7 +549,7 @@ instance.web.ViewManager = instance.web.Widget.extend({ set_title: function(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) { @@ -589,7 +594,8 @@ instance.web.ViewManager = instance.web.Widget.extend({ titles.pop(); } return titles; - } + }, + on_reverse_breadcrumb: on_reverse_breadcrumb, }); }, /**