diff --git a/web_m2x_options/README.rst b/web_m2x_options/README.rst index 0013a2ba..3f626252 100644 --- a/web_m2x_options/README.rst +++ b/web_m2x_options/README.rst @@ -1,172 +1 @@ -.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg - :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html - :alt: License: AGPL-3 - -================================== -Add new options for many2one field -================================== - -Description ------------ - -This modules modifies "many2one" and "many2manytags" form widgets so as to add some new display -control options. - -Options provided includes possibility to remove "Create..." and/or "Create and -Edit..." entries from many2one drop down. You can also change default number of -proposition appearing in the drop-down. Or prevent the dialog box poping in -case of validation error. - -If not specified, the module will avoid proposing any of the create options -if the current user has no permission rights to create the related object. - -Usage -===== - -.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas - :alt: Try me on Runbot - :target: https://runbot.odoo-community.org/runbot/162/8.0 - -For further information, please visit: - -* https://www.odoo.com/forum/help-1 - -in the field's options dict ---------------------------- - -``create`` *boolean* (Default: depends if user have create rights) - - Whether to display the "Create..." entry in dropdown panel. - -``create_edit`` *boolean* (Default: depends if user have create rights) - - Whether to display "Create and Edit..." entry in dropdown panel - -``m2o_dialog`` *boolean* (Default: depends if user have create rights) - - Whether to display the many2one dialog in case of validation error. - -``limit`` *int* (Default: openerp default value is ``7``) - - Number of displayed record in drop-down panel - -``search_more`` *boolean* - - Used to force disable/enable search more button. - -``field_color`` *string* - - A string to define the field used to define color. - This option has to be used with colors. - -``colors`` *dictionary* - - A dictionary to link field value with a HTML color. - This option has to be used with field_color. - -``no_open_edit`` *boolean* (Default: value of ``no_open`` which is ``False`` if not set) - - Causes a many2one not to offer to click through in edit mode, but well in read mode - -``open`` *boolean* (Default: ``False``) - - Makes many2many_tags buttons that open the linked resource - -``no_color_picker`` *boolean* (Default: ``False``) - - Deactivates the color picker on many2many_tags buttons to do nothing (ignored if open is set) - -ir.config_parameter options ---------------------------- - -Now you can disable "Create..." and "Create and Edit..." entry for all widgets in the odoo instance. -If you disable one option, you can enable it for particular field by setting "create: True" option directly on the field definition. - -``web_m2x_options.create`` *boolean* (Default: depends if user have create rights) - - Whether to display the "Create..." entry in dropdown panel for all fields in the odoo instance. - -``web_m2x_options.create_edit`` *boolean* (Default: depends if user have create rights) - - Whether to display "Create and Edit..." entry in dropdown panel for all fields in the odoo instance. - -``web_m2x_options.m2o_dialog`` *boolean* (Default: depends if user have create rights) - - Whether to display the many2one dialog in case of validation error for all fields in the odoo instance. - -``web_m2x_options.limit`` *int* (Default: openerp default value is ``7``) - - Number of displayed record in drop-down panel for all fields in the odoo instance - -``web_m2x_options.search_more`` *boolean* (Default: default value is ``False``) - - Whether the field should always show "Search more..." entry or not. - -To add these parameters go to Configuration -> Technical -> Parameters -> System Parameters and add new parameters like: - -- web_m2x_options.create: False -- web_m2x_options.create_edit: False -- web_m2x_options.m2o_dialog: False -- web_m2x_options.limit: 10 -- web_m2x_options.search_more: True - - -Example -------- - -Your XML form view definition could contain:: - - ... - - ... - -Known issues -============ - -Double check that you have no inherited view that remove ``options`` you set on a field ! -If nothing works, add a debugger in the first line of ``get_search_result method`` and enable debug mode in Odoo. When you write something in a many2one field, javascript debugger should pause. If not verify your installation. - -Roadmap -======= - -- Instead of making the tags rectangle clickable, I think it's better to put the text as a clickable link, so we will get a consistent behaviour/aspect with other clickable elements (many2one...). -- In edit mode, it would be great to add an icon like the one on many2one fields to allow to open the many2many in a popup window. -- Include this feature as a configurable option via parameter to have this behaviour by default in all many2many tags. - -Bug Tracker -=========== - -Bugs are tracked on `GitHub Issues `_. -In case of trouble, please check there if your issue has already been reported. -If you spotted it first, help us smashing it by providing a detailed and welcomed feedback -`here `_. - -Credits -======= - -Contributors ------------- - -* David Coninckx -* Emanuel Cino -* Holger Brunn -* Nicolas JEUDY -* Yannick Vaucher -* Zakaria Makrelouf -* Jairo Llopis -* David Vidal - -Maintainer ----------- - -.. image:: https://odoo-community.org/logo.png - :alt: Odoo Community Association - :target: https://odoo-community.org - -This module is maintained by the OCA. - -OCA, or the Odoo Community Association, is a nonprofit organization whose -mission is to support the collaborative development of Odoo features and -promote its widespread use. - -To contribute to this module, please visit http://odoo-community.org. +**This file is going to be generated by oca-gen-addon-readme.** diff --git a/web_m2x_options/__init__.py b/web_m2x_options/__init__.py index 57d631c3..0650744f 100644 --- a/web_m2x_options/__init__.py +++ b/web_m2x_options/__init__.py @@ -1 +1 @@ -# coding: utf-8 +from . import models diff --git a/web_m2x_options/__manifest__.py b/web_m2x_options/__manifest__.py index fd51b416..04b0986e 100644 --- a/web_m2x_options/__manifest__.py +++ b/web_m2x_options/__manifest__.py @@ -1,18 +1,23 @@ -# -*- coding: utf-8 -*- { "name": 'web_m2x_options', - "version": "10.0.1.1.0", + "version": "11.0.1.0.0", + 'category': 'Web', + "author": "ACSONE SA/NV, " + "0k.io, " + "Tecnativa, " + "Odoo Community Association (OCA)", + 'website': 'https://github.com/OCA/web', + 'license': 'AGPL-3', "depends": [ 'base', 'web', ], + 'data': [ + 'views/view.xml' + ], 'qweb': [ 'static/src/xml/base.xml', ], - 'license': 'AGPL-3', - 'data': ['views/view.xml'], - "author": "ACSONE SA/NV, 0k.io, Tecnativa, " - "Odoo Community Association (OCA)", 'installable': True, } diff --git a/web_m2x_options/models/__init__.py b/web_m2x_options/models/__init__.py new file mode 100644 index 00000000..bd3aa9f1 --- /dev/null +++ b/web_m2x_options/models/__init__.py @@ -0,0 +1 @@ +from . import ir_config_parameter diff --git a/web_m2x_options/models/ir_config_parameter.py b/web_m2x_options/models/ir_config_parameter.py new file mode 100644 index 00000000..cf01e687 --- /dev/null +++ b/web_m2x_options/models/ir_config_parameter.py @@ -0,0 +1,12 @@ +from odoo import api, models + + +class IrConfigParameter(models.Model): + _inherit = 'ir.config_parameter' + + @api.model + def get_web_m2x_options(self): + opts = ['web_m2x_options.create', 'web_m2x_options.create_edit', + 'web_m2x_options.limit', 'web_m2x_options.search_more', + 'web_m2x_options.m2o_dialog'] + return self.sudo().search_read([['key', 'in', opts]], ["key", "value"]) diff --git a/web_m2x_options/readme/CONTRIBUTORS.rst b/web_m2x_options/readme/CONTRIBUTORS.rst new file mode 100644 index 00000000..1e4aebda --- /dev/null +++ b/web_m2x_options/readme/CONTRIBUTORS.rst @@ -0,0 +1,11 @@ +* David Coninckx +* Emanuel Cino +* Holger Brunn +* Nicolas JEUDY +* Yannick Vaucher +* Zakaria Makrelouf +* `Tecnativa `_: + + * Jairo Llopis + * David Vidal + * Ernesto Tejeda diff --git a/web_m2x_options/readme/DESCRIPTION.rst b/web_m2x_options/readme/DESCRIPTION.rst new file mode 100644 index 00000000..7108400f --- /dev/null +++ b/web_m2x_options/readme/DESCRIPTION.rst @@ -0,0 +1,10 @@ +This modules modifies "many2one" and "many2manytags" form widgets so as to add some new display +control options. + +Options provided includes possibility to remove "Create..." and/or "Create and +Edit..." entries from many2one drop down. You can also change default number of +proposition appearing in the drop-down. Or prevent the dialog box poping in +case of validation error. + +If not specified, the module will avoid proposing any of the create options +if the current user has no permission rights to create the related object. diff --git a/web_m2x_options/readme/ROADMAP.rst b/web_m2x_options/readme/ROADMAP.rst new file mode 100644 index 00000000..dfb07916 --- /dev/null +++ b/web_m2x_options/readme/ROADMAP.rst @@ -0,0 +1,6 @@ +Double check that you have no inherited view that remove ``options`` you set on a field ! +If nothing works, add a debugger in the first line of ``_search method`` and enable debug mode in Odoo. When you write something in a many2one field, javascript debugger should pause. If not verify your installation. + +- Instead of making the tags rectangle clickable, I think it's better to put the text as a clickable link, so we will get a consistent behaviour/aspect with other clickable elements (many2one...). +- In edit mode, it would be great to add an icon like the one on many2one fields to allow to open the many2many in a popup window. +- Include this feature as a configurable option via parameter to have this behaviour by default in all many2many tags. diff --git a/web_m2x_options/readme/USAGE.rst b/web_m2x_options/readme/USAGE.rst new file mode 100644 index 00000000..49e57cb6 --- /dev/null +++ b/web_m2x_options/readme/USAGE.rst @@ -0,0 +1,88 @@ +in the field's options dict +--------------------------- + +``create`` *boolean* (Default: depends if user have create rights) + + Whether to display the "Create..." entry in dropdown panel. + +``create_edit`` *boolean* (Default: depends if user have create rights) + + Whether to display "Create and Edit..." entry in dropdown panel + +``m2o_dialog`` *boolean* (Default: depends if user have create rights) + + Whether to display the many2one dialog in case of validation error. + +``limit`` *int* (Default: openerp default value is ``7``) + + Number of displayed record in drop-down panel + +``search_more`` *boolean* + + Used to force disable/enable search more button. + +``field_color`` *string* + + A string to define the field used to define color. + This option has to be used with colors. + +``colors`` *dictionary* + + A dictionary to link field value with a HTML color. + This option has to be used with field_color. + +``no_open_edit`` *boolean* (Default: value of ``no_open`` which is ``False`` if not set) + + Causes a many2one not to offer to click through in edit mode, but well in read mode + +``open`` *boolean* (Default: ``False``) + + Makes many2many_tags buttons that open the linked resource + +``no_color_picker`` *boolean* (Default: ``False``) + + Deactivates the color picker on many2many_tags buttons to do nothing (ignored if open is set) + +ir.config_parameter options +--------------------------- + +Now you can disable "Create..." and "Create and Edit..." entry for all widgets in the odoo instance. +If you disable one option, you can enable it for particular field by setting "create: True" option directly on the field definition. + +``web_m2x_options.create`` *boolean* (Default: depends if user have create rights) + + Whether to display the "Create..." entry in dropdown panel for all fields in the odoo instance. + +``web_m2x_options.create_edit`` *boolean* (Default: depends if user have create rights) + + Whether to display "Create and Edit..." entry in dropdown panel for all fields in the odoo instance. + +``web_m2x_options.m2o_dialog`` *boolean* (Default: depends if user have create rights) + + Whether to display the many2one dialog in case of validation error for all fields in the odoo instance. + +``web_m2x_options.limit`` *int* (Default: openerp default value is ``7``) + + Number of displayed record in drop-down panel for all fields in the odoo instance + +``web_m2x_options.search_more`` *boolean* (Default: default value is ``False``) + + Whether the field should always show "Search more..." entry or not. + +To add these parameters go to Configuration -> Technical -> Parameters -> System Parameters and add new parameters like: + +- web_m2x_options.create: False +- web_m2x_options.create_edit: False +- web_m2x_options.m2o_dialog: False +- web_m2x_options.limit: 10 +- web_m2x_options.search_more: True + + +Example +------- + +Your XML form view definition could contain:: + + ... + + ... diff --git a/web_m2x_options/static/src/js/form.js b/web_m2x_options/static/src/js/form.js index 2ec92a96..1bc13004 100644 --- a/web_m2x_options/static/src/js/form.js +++ b/web_m2x_options/static/src/js/form.js @@ -7,157 +7,158 @@ odoo.define('web_m2x_options.web_m2x_options', function (require) { var core = require('web.core'), data = require('web.data'), Dialog = require('web.Dialog'), - Model = require('web.Model'), - form_relational = require('web.form_relational'), - _t = core._t; - - var OPTIONS = ['web_m2x_options.create', - 'web_m2x_options.create_edit', - 'web_m2x_options.limit', - 'web_m2x_options.search_more', - 'web_m2x_options.m2o_dialog',]; - - // In odoo 9.c FielMany2One is not exposed by form_relational - // To bypass this limitation we use the widget registry to get the - // reference to the FielMany2One widget. - var FieldMany2One = core.form_widget_registry.get('many2one'); + view_dialogs = require('web.view_dialogs'), + relational_fields = require('web.relational_fields'), + FieldMany2One = relational_fields.FieldMany2One, + FormFieldMany2ManyTags = relational_fields.FormFieldMany2ManyTags, + rpc = require('web.rpc'), + _t = core._t; + + var web_m2x_options = rpc.query({ + model: "ir.config_parameter", + method: 'get_web_m2x_options', + }); var M2ODialog = Dialog.extend({ template: "M2ODialog", - init: function(parent) { - this.name = parent.string; + init: function (parent, name, value) { + this.name = name; + this.value = value; this._super(parent, { - title: _.str.sprintf(_t("Create a %s"), parent.string), + title: _.str.sprintf(_t("Create a %s"), this.name), size: 'medium', - buttons: [ - {text: _t('Create'), classes: 'btn-primary', click: function() { - if (this.$("input").val() !== ''){ - this.getParent()._quick_create(this.$("input").val()); - this.close(); + buttons: [{ + text: _t('Create'), + classes: 'btn-primary', + click: function () { + if (this.$("input").val() !== '') { + this.trigger_up('quick_create', {value: this.$('input').val()}); + this.close(true); } else { - e.preventDefault(); this.$("input").focus(); } - }}, - - {text: _t('Create and edit'), classes: 'btn-primary', close: true, click: function() { - this.getParent()._search_create_popup("form", undefined, this.getParent()._create_context(this.$("input").val())); - }}, - - {text: _t('Cancel'), close: true} - ] + }, + }, { + text: _t('Create and edit'), + classes: 'btn-primary', + close: true, + click: function () { + this.trigger_up('search_create_popup', { + view_type: 'form', + value: this.$('input').val(), + }); + }, + }, { + text: _t('Cancel'), + close: true, + }], }); }, - start: function() { - var text = _.str.sprintf(_t("You are creating a new %s, are you sure it does not exist yet?"), this.name); - this.$("p").text(text); - this.$("input").val(this.getParent().$input.val()); + start: function () { + this.$("p").text(_.str.sprintf(_t("You are creating a new %s, are you sure it does not exist yet?"), this.name)); + this.$("input").val(this.value); + }, + /** + * @override + * @param {boolean} isSet + */ + close: function (isSet) { + this.isSet = isSet; + this._super.apply(this, arguments); + }, + /** + * @override + */ + destroy: function () { + if (!this.isSet) { + this.trigger_up('closed_unset'); + } + this._super.apply(this, arguments); }, }); FieldMany2One.include({ - start: function() { + start: function () { this._super.apply(this, arguments); return this.get_options(); }, - get_options: function() { + get_options: function () { var self = this; - if (!_.isUndefined(this.view) && _.isUndefined(this.view.ir_options_loaded)) { - this.view.ir_options_loaded = $.Deferred(); - this.view.ir_options = {}; - (new Model("ir.config_parameter")) - .query(["key", "value"]).filter([['key', 'in', OPTIONS]]) - .all().then(function(records) { - _(records).each(function(record) { - self.view.ir_options[record.key] = record.value; - }); - self.view.ir_options_loaded.resolve(); + if (_.isUndefined(this.ir_options_loaded)) { + this.ir_options_loaded = $.Deferred(); + this.ir_options = {}; + web_m2x_options.done(function (records) { + _(records).each(function(record) { + self.ir_options[record.key] = record.value; + }); + self.ir_options_loaded.resolve(); }); - return this.view.ir_options_loaded; } return $.when(); }, - is_option_set: function(option) { - if (_.isUndefined(option)) { - return false - } - var is_string = typeof option === 'string' - var is_bool = typeof option === 'boolean' - if (is_string) { - return option === 'true' || option === 'True' - } else if (is_bool) { - return option - } + is_option_set: function (option) { + if (_.isUndefined(option)) + return false; + if (typeof option === 'string') + return option === 'true' || option === 'True'; + if (typeof option === 'boolean') + return option; return false }, - show_error_displayer: function () { - if(this.is_option_set(this.options.m2o_dialog) || - _.isUndefined(this.options.m2o_dialog) && this.is_option_set(this.view.ir_options['web_m2x_options.m2o_dialog']) || - this.can_create && _.isUndefined(this.options.m2o_dialog) && _.isUndefined(this.view.ir_options['web_m2x_options.m2o_dialog'])) { - new M2ODialog(this).open(); + _onInputFocusout: function () { + var m2o_dialog_opt = this.is_option_set(this.nodeOptions.m2o_dialog) || _.isUndefined(this.nodeOptions.m2o_dialog) && this.is_option_set(this.ir_options['web_m2x_options.m2o_dialog']) || _.isUndefined(this.nodeOptions.m2o_dialog) && _.isUndefined(this.ir_options['web_m2x_options.m2o_dialog']); + if (this.can_create && this.floating && m2o_dialog_opt) { + new M2ODialog(this, this.string, this.$input.val()).open(); } }, - get_search_result: function (search_val) { - var Objects = new Model(this.field.relation); - var def = $.Deferred(); + _search: function (search_val) { var self = this; + var def = $.Deferred(); + this.orderer.add(def); + // add options limit used to change number of selections record // returned. - if (_.isUndefined(this.view)) - return this._super.apply(this, arguments); - if (!_.isUndefined(this.view.ir_options['web_m2x_options.limit'])) { - this.limit = parseInt(this.view.ir_options['web_m2x_options.limit'], 10); + if (!_.isUndefined(this.ir_options['web_m2x_options.limit'])) { + this.limit = parseInt(this.ir_options['web_m2x_options.limit'], 10); } - if (typeof this.options.limit === 'number') { - this.limit = this.options.limit; + if (typeof this.nodeOptions.limit === 'number') { + this.limit = this.nodeOptions.limit; } // add options field_color and colors to color item(s) depending on field_color value - this.field_color = this.options.field_color - this.colors = this.options.colors - - var dataset = new data.DataSet(this, this.field.relation, - self.build_context()); - var blacklist = this.get_search_blacklist(); - this.last_query = search_val; + this.field_color = this.nodeOptions.field_color; + this.colors = this.nodeOptions.colors; - function searcher (domain) { - return self.orderer.add(dataset.name_search( - search_val, - domain, - 'ilike', self.limit + 1, - self.build_context())); - } - try { - var search_result = searcher(new data.CompoundDomain( - self.build_domain(), [["id", "not in", blacklist]])); - // In search views sometimes the field domain cannot be evaluated - } catch (error) { - var search_result = searcher([["id", "not in", blacklist]]); - } + var context = this.record.getContext(this.recordParams); + var domain = this.record.getDomain(this.recordParams); - if (!(self.options && (self.is_option_set(self.options.create) || self.is_option_set(self.options.create_edit)))) { - this.create_rights = this.create_rights || (function(){ - return new Model(self.field.relation).call( - "check_access_rights", ["create", false]); - })(); + var blacklisted_ids = this._getSearchBlacklist(); + if (blacklisted_ids.length > 0) { + domain.push(['id', 'not in', blacklisted_ids]); } - $.when(search_result, this.create_rights).then(function (data, can_create) { - - self.can_create = can_create; // for ``.show_error_displayer()`` - self.last_search = data; + this._rpc({ + model: this.field.relation, + method: "name_search", + kwargs: { + name: search_val, + args: domain, + operator: "ilike", + limit: this.limit + 1, + context: context, + } + }).then(function (result) { // possible selections for the m2o - var values = _.map(data, function (x) { - x[1] = x[1].split("\n")[0]; + var values = _.map(result, function (x) { + x[1] = self._getDisplayName(x[1]); return { - label: _.str.escapeHTML(x[1]), + label: _.str.escapeHTML(x[1].trim()) || data.noDisplayContent, value: x[1], name: x[1], id: x[0], @@ -170,94 +171,104 @@ odoo.define('web_m2x_options.web_m2x_options', function (require) { for (var index in values) { value_ids.push(values[index].id); } - // RPC request to get field_color from Objects - Objects.query([self.field_color]) - .filter([['id', 'in', value_ids]]) - .all().done(function (objects) { - for (var index in objects) { - for (var index_value in values) { - if (values[index_value].id == objects[index].id) { - // Find value in values by comparing ids - var value = values[index_value]; - // Find color with field value as key - var color = self.colors[objects[index][self.field_color]] || 'black'; - value.label = ''+value.label+''; - break; - } - } - } - def.resolve(values); - }); + self._rpc({ + model: self.field.relation, + method: 'search_read', + fields: [self.field_color], + domain: [['id', 'in', value_ids]] + }).then(function (objects) { + for (var index in objects) { + for (var index_value in values) { + if (values[index_value].id == objects[index].id) { + // Find value in values by comparing ids + var value = values[index_value]; + // Find color with field value as key + var color = self.colors[objects[index][self.field_color]] || 'black'; + value.label = '' + value.label + ''; + break; + } + } + } + def.resolve(values); + }) + } // search more... if more results that max - var can_search_more = (self.options && self.is_option_set(self.options.search_more)), - search_more_undef = _.isUndefined(self.options.search_more) && _.isUndefined(self.view.ir_options['web_m2x_options.search_more']), - search_more = self.is_option_set(self.view.ir_options['web_m2x_options.search_more']); + var can_search_more = (self.nodeOptions && self.is_option_set(self.nodeOptions.search_more)), + search_more_undef = _.isUndefined(self.nodeOptions.search_more) && _.isUndefined(self.ir_options['web_m2x_options.search_more']), + search_more = self.is_option_set(self.ir_options['web_m2x_options.search_more']); - if (values.length > self.limit && (can_search_more || search_more_undef || search_more)) { + if (values.length > self.limit) { values = values.slice(0, self.limit); - values.push({ - label: _t("Search More..."), - action: function () { - // limit = 160 for improving performance, similar - // to Odoo implementation here: - // https://github.com/odoo/odoo/blob/feeac2a4f1cd777770dd2b42534904ac71f23e46/addons/web/static/src/js/views/form_common.js#L213 - dataset.name_search( - search_val, self.build_domain(), - 'ilike', 160).done(function (data) { - self._search_create_popup("search", data); - }); - }, - classname: 'oe_m2o_dropdown_option o_m2o_dropdown_option' - }); - } - - // quick create - - var raw_result = _(data.result).map(function (x) { - return x[1]; - }); - var quick_create = self.is_option_set(self.options.create) || self.is_option_set(self.options.quick_create), - quick_create_undef = _.isUndefined(self.options.create) && _.isUndefined(self.options.quick_create), - m2x_create_undef = _.isUndefined(self.view.ir_options['web_m2x_options.create']), - m2x_create = self.is_option_set(self.view.ir_options['web_m2x_options.create']); - var show_create = (!self.options && (m2x_create_undef || m2x_create)) || (self.options && (quick_create || (quick_create_undef && (m2x_create_undef || m2x_create)))); - if (self.can_create && show_create){ - if (search_val.length > 0 && - !_.include(raw_result, search_val)) { - + if (can_search_more || search_more_undef || search_more) { values.push({ - label: _.str.sprintf( - _t('Create "%s"'), - $('').text(search_val).html()), + label: _t("Search More..."), action: function () { - self._quick_create(search_val); + // limit = 80 for improving performance, similar + // to Odoo implementation here: + // https://github.com/odoo/odoo/commit/8c3cdce539d87775b59b3f2d5ceb433f995821bf + self._rpc({ + model: self.field.relation, + method: 'name_search', + kwargs: { + name: search_val, + args: domain, + operator: "ilike", + limit: 80, + context: context, + }, + }) + .then(self._searchCreatePopup.bind(self, "search")); }, - classname: 'oe_m2o_dropdown_option o_m2o_dropdown_option' + classname: 'o_m2o_dropdown_option', }); } } - // create... - var create_edit = self.is_option_set(self.options.create) || self.is_option_set(self.options.create_edit), - create_edit_undef = _.isUndefined(self.options.create) && _.isUndefined(self.options.create_edit), - m2x_create_edit_undef = _.isUndefined(self.view.ir_options['web_m2x_options.create_edit']), - m2x_create_edit = self.is_option_set(self.view.ir_options['web_m2x_options.create_edit']); - var show_create_edit = (!self.options && (m2x_create_edit_undef || m2x_create_edit)) || (self.options && (create_edit || (create_edit_undef && (m2x_create_edit_undef || m2x_create_edit)))); - if (self.can_create && show_create_edit){ + var create_enabled = self.can_create && !self.nodeOptions.no_create; + // quick create + var raw_result = _.map(result, function (x) { return x[1]; }); + var quick_create = self.is_option_set(self.nodeOptions.create), + quick_create_undef = _.isUndefined(self.nodeOptions.create), + m2x_create_undef = _.isUndefined(self.ir_options['web_m2x_options.create']), + m2x_create = self.is_option_set(self.ir_options['web_m2x_options.create']); + var show_create = (!self.nodeOptions && (m2x_create_undef || m2x_create)) || (self.nodeOptions && (quick_create || (quick_create_undef && (m2x_create_undef || m2x_create)))); + if (create_enabled && !self.nodeOptions.no_quick_create && + search_val.length > 0 && !_.contains(raw_result, search_val) && + show_create) { + values.push({ + label: _.str.sprintf(_t('Create "%s"'), + $('').text(search_val).html()), + action: self._quickCreate.bind(self, search_val), + classname: 'o_m2o_dropdown_option' + }); + } + // create and edit ... + + var create_edit = self.is_option_set(self.nodeOptions.create) || self.is_option_set(self.nodeOptions.create_edit), + create_edit_undef = _.isUndefined(self.nodeOptions.create) && _.isUndefined(self.nodeOptions.create_edit), + m2x_create_edit_undef = _.isUndefined(self.ir_options['web_m2x_options.create_edit']), + m2x_create_edit = self.is_option_set(self.ir_options['web_m2x_options.create_edit']); + var show_create_edit = (!self.nodeOptions && (m2x_create_edit_undef || m2x_create_edit)) || (self.nodeOptions && (create_edit || (create_edit_undef && (m2x_create_edit_undef || m2x_create_edit)))); + if (create_enabled && !self.nodeOptions.no_create_edit && show_create_edit) { + var createAndEditAction = function () { + // Clear the value in case the user clicks on discard + self.$('input').val(''); + return self._searchCreatePopup("form", false, self._createContext(search_val)); + }; 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 o_m2o_dropdown_option' + action: createAndEditAction, + classname: 'o_m2o_dropdown_option', + }); + } else if (values.length === 0) { + values.push({ + label: _t("No results to show..."), }); } // Check if colors specified to wait for RPC - if (!(self.field_color && self.colors)){ + if (!(self.field_color && self.colors)) { def.resolve(values); } }); @@ -266,173 +277,82 @@ odoo.define('web_m2x_options.web_m2x_options', function (require) { } }); - form_relational.FieldMany2ManyTags.include({ - init: function () { - this.events["click .badge"] = "open_badge"; - return this._super.apply(this, arguments); - }, - show_error_displayer: function () { - if ((typeof this.options.m2o_dialog === 'undefined' && this.can_create) || - this.options.m2o_dialog) { - new M2ODialog(this).open(); - } - }, + FormFieldMany2ManyTags.include({ + events: _.extend({}, FormFieldMany2ManyTags.prototype.events, { + 'click .badge': '_onOpenBadge', + }), - start: function() { - this._super.apply(this, arguments); - return this.get_options(); + _onDeleteTag: function (event) { + var result = this._super.apply(this, arguments); + event.stopPropagation(); + return result; }, - get_options: function() { - var self = this; - if (_.isUndefined(this.view.ir_options_loaded)) { - this.view.ir_options_loaded = $.Deferred(); - this.view.ir_options = {}; - (new Model("ir.config_parameter")) - .query(["key", "value"]).filter([['key', 'in', OPTIONS]]) - .all().then(function(records) { - _(records).each(function(record) { - self.view.ir_options[record.key] = record.value; - }); - self.view.ir_options_loaded.resolve(); - }); - } - return this.view.ir_options_loaded; - }, - - is_option_set: function(option) { - if (_.isUndefined(option)) { - return false - } - var is_string = typeof option === 'string' - var is_bool = typeof option === 'boolean' - if (is_string) { - return option === 'true' || option === 'True' - } else if (is_bool) { - return option - } + is_option_set: function (option) { + if (_.isUndefined(option)) + return false; + if (typeof option === 'string') + return option === 'true' || option === 'True'; + if (typeof option === 'boolean') + return option; return false }, - /** - * Call this method to search using a string. - */ - - get_search_result: function(search_val) { + _onOpenBadge: function (event) { var self = this; - - // add options limit used to change number of selections record - // returned. - - if (!_.isUndefined(this.view.ir_options['web_m2x_options.limit'])) { - this.limit = parseInt(this.view.ir_options['web_m2x_options.limit'], 10); - } - - if (typeof this.options.limit === 'number') { - this.limit = this.options.limit; - } - - var dataset = new data.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 data.CompoundDomain(self.build_domain(), [["id", "not in", blacklist]]), - '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) { - x[1] = x[1].split("\n")[0]; - return { - label: _.str.escapeHTML(x[1]), - value: x[1], - name: x[1], - id: x[0], - }; - }); - - // search more... if more results that max - if (values.length > self.limit) { - values = values.slice(0, self.limit); - values.push({ - label: _t("Search More..."), - action: function() { - - // limit = 80 for improving performance, similar - // to Odoo implementation here: - // https://github.com/odoo/odoo/commit/8c3cdce539d87775b59b3f2d5ceb433f995821bf - dataset.name_search(search_val, self.build_domain(), 'ilike', 80).done(function(data) { - self._search_create_popup("search", data); - }); - }, - classname: 'oe_m2o_dropdown_option o_m2o_dropdown_option' - }); - } - // quick create - var quick_create = self.is_option_set(self.options.create) || self.is_option_set(self.options.quick_create), - quick_create_undef = _.isUndefined(self.options.create) && _.isUndefined(self.options.quick_create), - m2x_create_undef = _.isUndefined(self.view.ir_options['web_m2x_options.create']), - m2x_create = self.is_option_set(self.view.ir_options['web_m2x_options.create']); - var show_create = (!self.options && (m2x_create_undef || m2x_create)) || (self.options && (quick_create || (quick_create_undef && (m2x_create_undef || m2x_create)))); - if (show_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('Create "%s"'), - $('').text(search_val).html()), - action: function() { - self._quick_create(search_val); - }, - classname: 'oe_m2o_dropdown_option o_m2o_dropdown_option' + var open = (self.nodeOptions && self.is_option_set(self.nodeOptions.open)); + var no_color_picker = (self.nodeOptions && self.is_option_set(self.nodeOptions.no_color_picker)); + if (open) { + var context = self.record.getContext(self.recordParams); + var id = parseInt($(event.currentTarget).data('id'), 10); + + if (self.mode === 'readonly') { + event.preventDefault(); + event.stopPropagation(); + self._rpc({ + model: self.field.relation, + method: 'get_formview_action', + args: [[id]], + context: context, + }) + .then(function (action) { + self.trigger_up('do_action', {action: action}); }); - } } - - // create... - var create_edit = self.is_option_set(self.options.create) || self.is_option_set(self.options.create_edit), - create_edit_undef = _.isUndefined(self.options.create) && _.isUndefined(self.options.create_edit), - m2x_create_edit_undef = _.isUndefined(self.view.ir_options['web_m2x_options.create_edit']), - m2x_create_edit = self.is_option_set(self.view.ir_options['web_m2x_options.create_edit']); - var show_create_edit = (!self.options && (m2x_create_edit_undef || m2x_create_edit)) || (self.options && (create_edit || (create_edit_undef && (m2x_create_edit_undef || m2x_create_edit)))); - if (show_create_edit){ - - 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 o_m2o_dropdown_option' - }); + else { + $.when( + self._rpc({ + model: self.field.relation, + method: 'get_formview_id', + args: [[id]], + context: context, + }), + self._rpc({ + model: self.field.relation, + method: 'check_access_rights', + kwargs: {operation: 'write', raise_exception: false} + }) + ).then(function (view_id, write_access) { + var can_write = 'can_write' in self.attrs ? JSON.parse(self.attrs.can_write) : true; + new view_dialogs.FormViewDialog(self, { + res_model: self.field.relation, + res_id: id, + context: context, + title: _t("Open: ") + self.string, + view_id: view_id, + readonly: !can_write || !write_access, + on_saved: function (record, changed) { + if (changed) { + self._setValue(self.value.data, {forceChange: true}); + self.trigger_up('reload', {db_id: self.value.id}); + } + }, + }).open(); + }) } - - return values; - }) - }, - - open_badge: function(ev){ - var self = this; - var open = (self.options && self.is_option_set(self.options.open)); - var no_color_picker = (self.options && self.is_option_set(self.options.no_color_picker)); - if(open){ - self.mutex.exec(function(){ - var id = parseInt($(ev.currentTarget).data('id'), 10); - self.do_action({ - type: 'ir.actions.act_window', - res_model: self.field.relation, - views: [[false, 'form']], - res_id: id, - target: "new" - }); - }.bind(this)); - }else if(no_color_picker){ - self.mutex.exec(function(){ - return - }.bind(this)); - }else{ - self.open_color_picker(ev); + } else if (!no_color_picker) { + self._onOpenColorPicker(event); } }, - - }); + }) }); diff --git a/web_m2x_options/static/src/xml/base.xml b/web_m2x_options/static/src/xml/base.xml index c095218b..7a2c66c5 100644 --- a/web_m2x_options/static/src/xml/base.xml +++ b/web_m2x_options/static/src/xml/base.xml @@ -6,7 +6,7 @@ - !(widget.options.no_open || widget.options.no_open_edit) + !(widget.nodeOptions.no_open || widget.nodeOptions.no_open_edit) diff --git a/web_m2x_options/views/view.xml b/web_m2x_options/views/view.xml index 85d435c1..e25ef6aa 100644 --- a/web_m2x_options/views/view.xml +++ b/web_m2x_options/views/view.xml @@ -1,13 +1,12 @@ - - +