From d4f8096790e3a76e3219d37ea17a7154b426d2fe Mon Sep 17 00:00:00 2001 From: qsm-odoo Date: Mon, 23 May 2016 15:18:03 +0200 Subject: [PATCH] [FIX] web,base: m2o empty name results M2O use the autocomplete lib to display its content. The problem is that if a result is empty (or only composed of whitespaces), a divider is displayed in the dropdown. Commit 1d9f7d279a74aaf6aaf25f91e2313b23b4122827 was fixing this by only displaying non-empty name results... but it did not work as many models implement the name_search method the wrong way. The new solution is to add a default display for record values which are empty strings, at the right places in the webclient components. --- addons/web/static/src/js/control_panel.js | 4 ++- addons/web/static/src/js/framework/data.js | 22 ++++++------- addons/web/static/src/js/views/form_common.js | 25 +++++++------- .../src/js/views/form_relational_widgets.js | 33 +++++++++---------- addons/web/static/src/xml/base.xml | 4 +-- 5 files changed, 43 insertions(+), 45 deletions(-) diff --git a/addons/web/static/src/js/control_panel.js b/addons/web/static/src/js/control_panel.js index 3a233564368..24e94b94fbb 100644 --- a/addons/web/static/src/js/control_panel.js +++ b/addons/web/static/src/js/control_panel.js @@ -32,6 +32,7 @@ odoo.define('web.ControlPanel', function (require) { "use strict"; var core = require('web.core'); +var data = require('web.data'); var Widget = require('web.Widget'); var ControlPanel = Widget.extend({ @@ -190,8 +191,9 @@ var ControlPanel = Widget.extend({ _render_breadcrumbs_li: function (bc, index, length) { var self = this; var is_last = (index === length-1); + var li_content = _.escape(bc.title.trim()) || data.noDisplayContent; var $bc = $('
  • ') - .append(is_last ? _.escape(bc.title) : $('').text(bc.title)) + .append(is_last ? li_content : $('').html(li_content)) .toggleClass('active', is_last); if (!is_last) { $bc.click(function () { diff --git a/addons/web/static/src/js/framework/data.js b/addons/web/static/src/js/framework/data.js index 592e402d927..e969616e7b6 100644 --- a/addons/web/static/src/js/framework/data.js +++ b/addons/web/static/src/js/framework/data.js @@ -29,7 +29,7 @@ function serialize_sort(criterion) { } /** - * Reverse of the serialize_sort function: convert an array of SQL-like sort + * Reverse of the serialize_sort function: convert an array of SQL-like sort * descriptors into a list of fields prefixed with '-' if necessary. */ function deserialize_sort(criterion) { @@ -205,7 +205,7 @@ var Query = Class.extend({ * Creates a new query with the provided parameter lazy replacing the current * query's own. * - * @param {Boolean} lazy indicates if the read_group should return only the + * @param {Boolean} lazy indicates if the read_group should return only the * first level of groupby records, or should return the records grouped by * all levels at once (so, it makes only 1 db request). * @returns {openerp.web.Query} @@ -276,11 +276,11 @@ var QueryGroup = Class.extend({ var group_size = fixed_group[count_key] || fixed_group.__count || 0; var leaf_group = fixed_group.__context.group_by.length === 0; - var value = (grouping_fields.length === 1) + var value = (grouping_fields.length === 1) ? fixed_group[grouping_fields[0]] : _.map(grouping_fields, function (field) { return fixed_group[field]; }); - var grouped_on = (grouping_fields.length === 1) - ? grouping_fields[0] + var grouped_on = (grouping_fields.length === 1) + ? grouping_fields[0] : grouping_fields; this.attributes = { folded: !!(fixed_group.__fold), @@ -371,7 +371,7 @@ var DataSet = Class.extend(mixins.PropertiesMixin, { read_ids: function (ids, fields, options) { if (_.isEmpty(ids)) return $.Deferred().resolve([]); - + options = options || {}; var method = 'read'; var ids_arg = ids; @@ -525,7 +525,7 @@ var DataSet = Class.extend(mixins.PropertiesMixin, { return this._model.call('name_get', [ids], {context: this.get_context()}); }, /** - * + * * @param {String} name name to perform a search for/on * @param {Array} [domain=[]] filters for the objects returned, OpenERP domain * @param {String} [operator='ilike'] matching operator to use with the provided name value @@ -580,7 +580,7 @@ var DataSet = Class.extend(mixins.PropertiesMixin, { return undefined; }, /** - * Set the sort criteria on the dataset. + * Set the sort criteria on the dataset. * * @param {Array} fields_list: list of fields order descriptors, as used by * Odoo's ORM (such as 'name desc', 'product_id', 'order_date asc') @@ -769,7 +769,7 @@ var BufferedDataSet = DataSetStatic.extend({ cached.values = _.extend({'id': id}, cached.from_read, cached.changes, cached.readonly_fields); return cached; }, - create: function(data, options) { + create: function(data, options) { var changes = _.extend({}, this.last_default_get, data); var cached = this._update_cache(_.uniqueId(this.virtual_id_prefix), _.extend({'changes': changes, 'to_create': true}, options)); this.trigger("dataset_changed", data, options); @@ -863,7 +863,7 @@ var BufferedDataSet = DataSetStatic.extend({ // sorting an array where all items are considered equal is a worst-case that // will randomize the array with an unstable sort! Therefore we must avoid // sorting if there are no sort_fields (i.e. all items are considered equal) - // See also: http://ecma262-5.com/ELS5_Section_15.htm#Section_15.4.4.11 + // See also: http://ecma262-5.com/ELS5_Section_15.htm#Section_15.4.4.11 // http://code.google.com/p/v8/issues/detail?id=90 if (sort_fields.length) { records.sort(function (a, b) { @@ -1118,7 +1118,6 @@ function compute_domain (expr, fields) { return _.all(stack, _.identity); } - return { Query: Query, DataSet: DataSet, @@ -1129,6 +1128,7 @@ return { CompoundContext: CompoundContext, CompoundDomain: CompoundDomain, compute_domain: compute_domain, + noDisplayContent: "" + _t("Unnamed") + "", }; }); diff --git a/addons/web/static/src/js/views/form_common.js b/addons/web/static/src/js/views/form_common.js index a4bd092dcd1..f869842415c 100644 --- a/addons/web/static/src/js/views/form_common.js +++ b/addons/web/static/src/js/views/form_common.js @@ -166,7 +166,7 @@ var ReinitializeFieldMixin = _.extend({}, ReinitializeWidgetMixin, { /** A mixin containing some useful methods to handle completion inputs. - + The widget containing this option can have these arguments in its widget options: - no_quick_create: if true, it will disable the quick create */ @@ -190,16 +190,15 @@ var CompletionFieldMixin = { exclusion_domain.push(['id', 'not in', ids_blacklist]); } - var empty_search = !search_val.trim(); return this.orderer.add(dataset.name_search( - empty_search ? ["", " ", " ", " "] : search_val, new data.CompoundDomain(self.build_domain(), exclusion_domain), - empty_search ? "not in" : 'ilike', this.limit + 1, self.build_context())).then(function (data) { - self.last_search = data; + search_val, new data.CompoundDomain(self.build_domain(), exclusion_domain), + 'ilike', this.limit + 1, self.build_context())).then(function(_data) { + self.last_search = _data; // possible selections for the m2o - var values = _.map(data, function(x) { + var values = _.map(_data, function(x) { x[1] = x[1].split("\n")[0]; return { - label: _.str.escapeHTML(x[1]), + label: _.str.escapeHTML(x[1].trim()) || data.noDisplayContent, value: x[1], name: x[1], id: x[0], @@ -212,15 +211,15 @@ var CompletionFieldMixin = { values.push({ label: _t("Search More..."), action: function() { - dataset.name_search(search_val, self.build_domain(), 'ilike', 160).done(function(data) { - self._search_create_popup("search", data); + dataset.name_search(search_val, self.build_domain(), 'ilike', 160).done(function(_data) { + self._search_create_popup("search", _data); }); }, classname: 'o_m2o_dropdown_option' }); } // quick create - var raw_result = _(data.result).map(function(x) {return x[1];}); + var raw_result = _(_data.result).map(function(x) {return x[1];}); if (search_val.length > 0 && !_.include(raw_result, search_val) && ! (self.options && (self.options.no_create || self.options.no_quick_create))) { self.can_create && values.push({ @@ -905,7 +904,7 @@ var FormViewDialog = ViewDialog.extend({ var self = this; var _super = this._super.bind(this); this.init_dataset(); - + if (this.res_id) { this.dataset.ids = [this.res_id]; this.dataset.index = 0; @@ -972,7 +971,7 @@ var SelectCreateDialog = ViewDialog.extend({ _.defaults(this.options, { initial_view: "search" }); this.initial_ids = this.options.initial_ids; }, - + open: function() { if(this.options.initial_view !== "search") { return this.create_edit_record(); @@ -1101,7 +1100,7 @@ var DomainEditorDialog = SelectCreateDialog.extend({ get_groupby: function () { return []; }, get_domain: function () { return options.default_domain; }, }, - values: [{label: _t("Selected domain"), value: null}], + values: [{label: _t("Selected domain"), value: null}], }}); this._super(parent, options); diff --git a/addons/web/static/src/js/views/form_relational_widgets.js b/addons/web/static/src/js/views/form_relational_widgets.js index 8e64599f470..7acf464ae3c 100644 --- a/addons/web/static/src/js/views/form_relational_widgets.js +++ b/addons/web/static/src/js/views/form_relational_widgets.js @@ -177,7 +177,7 @@ var FieldMany2One = common.AbstractField.extend(common.CompletionFieldMixin, com this.$input.change(input_changed); this.$input.on('click', function() { if (self.$input.autocomplete("widget").is(":visible")) { - self.$input.autocomplete("close"); + self.$input.autocomplete("close"); } else { if (self.get("value") && ! self.floating) { self.$input.autocomplete("search", ""); @@ -308,7 +308,7 @@ var FieldMany2One = common.AbstractField.extend(common.CompletionFieldMixin, com render_value: function(no_recurse) { var self = this; if (!this.get("value")) { - this.display_string(""); + this.display_string(null); return; } var display = this.display_value["" + this.get("value")]; @@ -336,14 +336,15 @@ var FieldMany2One = common.AbstractField.extend(common.CompletionFieldMixin, com } } }, - display_string: function(str) { + display_string: function (str) { + var noValue = (str === null); if (!this.get("effective_readonly")) { - this.$input.val(str.split("\n")[0]); + this.$input.val(noValue ? "" : (str.split("\n")[0].trim() || $(data.noDisplayContent).text())); this.current_display = this.$input.val(); this.$follow_button.toggle(!this.is_false()); this.$el.toggleClass('o_with_button', !!this.$follow_button && this.$follow_button.length > 0 && !this.is_false()); } else { - this.$el.html(_.escape(str).split("\n").join("
    ")); + this.$el.html(noValue ? "" : (_.escape(str.trim()).split("\n").join("
    ") || data.noDisplayContent)); // Define callback to perform when clicking on the field if (!this.options.no_open) { // Remove potential previously added event handler @@ -627,7 +628,7 @@ var AbstractManyField = common.AbstractField.extend({ add_ids = [], command_list = [], id, index, record; - + _.each(this.get('value'), function (id) { index = starting_ids.indexOf(id); if (index !== -1) { @@ -697,7 +698,7 @@ var FieldX2Many = AbstractManyField.extend({ default_view: 'tree', init: function(field_manager, node) { this._super(field_manager, node); - + this.is_loaded = $.Deferred(); this.initial_is_loaded = this.is_loaded; this.is_started = false; @@ -1422,19 +1423,15 @@ var FieldMany2ManyTags = AbstractManyField.extend(common.CompletionFieldMixin, c }, render_value: function() { var self = this; - var values = self.get("value"); - var handle_names = function(data) { - if (self.isDestroyed()) - return; - var indexed = {}; - _.each(data, function(el) { - indexed[el['id']] = el; + var values = this.get("value"); + var handle_names = function(_data) { + _.each(_data, function(el) { + el.display_name = el.display_name.trim() ? _.str.escapeHTML(el.display_name) : data.noDisplayContent; }); - data = _.map(values, function(el) { return indexed[el]; }); - self.render_tag(data); + self.render_tag(_data); }; if (!values || values.length > 0) { - return self.get_render_data(values).done(handle_names); + return this.alive(this.get_render_data(values)).done(handle_names); } else { handle_names([]); } @@ -1493,7 +1490,7 @@ var FieldMany2ManyTags = AbstractManyField.extend(common.CompletionFieldMixin, c var old_color = tag.data('color'); tag.removeClass('o_tag_color_' + old_color); tag.data('color', color); - tag.addClass('o_tag_color_' + color); + tag.addClass('o_tag_color_' + color); }); }, }); diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index 594ccfdf58e..a75e4d994ed 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -750,7 +750,7 @@ - + @@ -1120,7 +1120,7 @@
    - +

    Available fields