From 26fcdb404fee5ecedf66f69aafa804c1700cd3d8 Mon Sep 17 00:00:00 2001 From: Zakaria Makrelouf Date: Wed, 9 Nov 2016 13:58:36 +0100 Subject: [PATCH 1/3] [ADD] Add Most Recently Used Option to M20 Field --- web_m2x_options/README.rst | 20 ++++- web_m2x_options/static/src/js/form.js | 113 +++++++++++++++++++++++++- 2 files changed, 127 insertions(+), 6 deletions(-) diff --git a/web_m2x_options/README.rst b/web_m2x_options/README.rst index dff75f05fe73..112614317778 100644 --- a/web_m2x_options/README.rst +++ b/web_m2x_options/README.rst @@ -17,6 +17,12 @@ 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. +The options provided includes as well, the possibility to propose the Most +Recently used (MRU) values stored in the localstorage if nothing has been typed +in the field and the uer desplays the drop-down. When the user start typing the +field falls back to the normal behaviour. + + Usage ===== @@ -69,6 +75,10 @@ in the field's options dict Makes many2many_tags buttons that open the linked resource +``search_mru`` *boolean* (Default: ``False``) + + Display the MRU list stored in the localstorage before the user start typing. + ir.config_parameter options --------------------------- @@ -95,6 +105,10 @@ If you disable one option, you can enable it for particular field by setting "cr Whether the field should always show "Search more..." entry or not. +``web_m2x_options.search_mru`` *boolean* (Default: default value is ``False``) + + Display the MRU list stored in the localstorage before the user start typing. + To add these parameters go to Configuration -> Technical -> Parameters -> System Parameters and add new parameters like: - web_m2x_options.create: False @@ -102,7 +116,7 @@ To add these parameters go to Configuration -> Technical -> Parameters -> System - web_m2x_options.m2o_dialog: False - web_m2x_options.limit: 10 - web_m2x_options.search_more: True - +- web_m2x_options.search_mru: False Example ------- @@ -110,13 +124,13 @@ 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 ! +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 diff --git a/web_m2x_options/static/src/js/form.js b/web_m2x_options/static/src/js/form.js index f2a281725b4b..2fc9017aee6c 100644 --- a/web_m2x_options/static/src/js/form.js +++ b/web_m2x_options/static/src/js/form.js @@ -5,7 +5,7 @@ odoo.define('web_m2x_options.web_m2x_options', function (require) { "use strict"; var $ = require("$"); - var core = require('web.core'), + var core = require('web.core'), data = require('web.data'), Dialog = require('web.Dialog'), Model = require('web.Model'), @@ -17,7 +17,8 @@ odoo.define('web_m2x_options.web_m2x_options', function (require) { 'web_m2x_options.create_edit', 'web_m2x_options.limit', 'web_m2x_options.search_more', - 'web_m2x_options.m2o_dialog',]; + 'web_m2x_options.m2o_dialog', + 'web_m2x_options.search_mru',]; // In odoo 9.c FielMany2One is not exposed by form_relational // To bypass this limitation we use the widget registry to get the @@ -104,6 +105,26 @@ odoo.define('web_m2x_options.web_m2x_options', function (require) { } }, + compute_mru_key: function(){ + var self = this, + model = self.view.model, + db = self.session.db, + view_id = self.view.fields_view.view_id || self.view.dataset.parent_view.fields_view.view_id; + return db + "/" + model + "/" + view_id + "/" + self.name; + }, + + get_search_mru: function(){ + var mru_option = 'web_m2x_options_mru', + self = this; + var restore_mru_list = JSON.parse(localStorage.getItem(mru_option)), + key = self.compute_mru_key(); + if (restore_mru_list) { + if (!_.isUndefined(restore_mru_list[key])){ + return ['id', 'in', restore_mru_list[key]]; + } + } + return []; + }, get_search_result: function (search_val) { var Objects = new Model(this.field.relation); var def = $.Deferred(); @@ -126,13 +147,27 @@ odoo.define('web_m2x_options.web_m2x_options', function (require) { var dataset = new data.DataSet(this, this.field.relation, self.build_context()); + var domain_list = []; var blacklist = this.get_search_blacklist(); + if(!_(blacklist).isEmpty()){ + domain_list.push(['id', 'not in', blacklist]); + } + var can_search_mru = (self.options && self.is_option_set(self.options.search_mru)), + search_mru_undef = _.isUndefined(self.options.search_mru), + search_mru = self.is_option_set(self.view.ir_options['web_m2x_options.search_mru']); + + var mru_list = self.get_search_mru(); + if(search_val == "" && (can_search_mru || (search_mru_undef && search_mru))){ + if (!_(mru_list).isEmpty()){ + domain_list.push(mru_list); + } + } this.last_query = search_val; var search_result = this.orderer.add(dataset.name_search( search_val, new data.CompoundDomain( - self.build_domain(), [["id", "not in", blacklist]]), + self.build_domain(), domain_list), 'ilike', this.limit + 1, self.build_context())); @@ -182,6 +217,16 @@ odoo.define('web_m2x_options.web_m2x_options', function (require) { def.resolve(values); }); } + // add label favorites if favorites option is set and + // search_val is empty + if(search_val == "" && (can_search_mru || (search_mru_undef && search_mru))){ + if (!_(mru_list).isEmpty() && !_(values).isEmpty()){ + values.unshift({ + label: _t("Most Recently Used:"), + // classname: 'oe_m2o_dropdown_option', + }); + } + } // search more... if more results that max var can_search_more = (self.options && self.is_option_set(self.options.search_more)), @@ -256,6 +301,68 @@ odoo.define('web_m2x_options.web_m2x_options', function (require) { }); return def; + }, + + update_mru_list: function(){ + var self = this, + mru_option = 'web_m2x_options_mru'; + var key = self.compute_mru_key(); + // check if the localstorage has some items for the current model + if (localStorage.getItem(mru_option)) { + var restore_mru_list = JSON.parse(localStorage.getItem(mru_option)); + if (restore_mru_list[key]) { + var queue = restore_mru_list[key]; + // if the element doesn't exist in the stack + if (queue.indexOf(self.get_value(true)) < 0 && self.get_value(true)){ + if (queue.length < 5) { + // add the new element at the beginning + queue.unshift(self.get_value(true)); + }else { + // remove the last element + queue.pop(); + // add the new element at the beginning + queue.unshift(self.get_value(true)); + } + restore_mru_list[key] = queue; + }else{ + // if the element already exist in the stack + if (queue.indexOf(self.get_value(true)) >= 0 && self.get_value(true)){ + var index = queue.indexOf(self.get_value(true)); + // remove the element from the list + queue.splice(index, 1); + // and put it back at the beginning + queue.unshift(self.get_value(true)); + } + } + }else{ + // if the element is the first one + if (self.get_value(true)){ + restore_mru_list[key] = [self.get_value(true)]; + } + } + localStorage.setItem(mru_option, JSON.stringify(restore_mru_list)); + }else { + // first time to create an entry in the localstorage + if (self.get_value(true)){ + var values = {} + values[key] = [self.get_value(true)] + localStorage.setItem(mru_option, JSON.stringify(values)); + } + } + }, + + commit_value: function() { + var self = this; + // if the field value has changed and has favorites option + if (self._dirty_flag){ + var can_search_mru = (self.options && self.is_option_set(self.options.search_mru)), + search_mru_undef = _.isUndefined(self.options.search_mru), + search_mru = self.is_option_set(self.view.ir_options['web_m2x_options.search_mru']); + + if(can_search_mru || (search_mru_undef && search_mru)){ + self.update_mru_list(); + } + } } }); From da4782e4209cadf70cc7daef3988d3fe3254d9cb Mon Sep 17 00:00:00 2001 From: "Laurent Mignon (ACSONE)" Date: Thu, 1 Dec 2016 11:22:54 +0100 Subject: [PATCH 2/3] [IMP] Improve styling of the display of preferred items, Also display the 'Search More' entry when the list of preferred items is displayed Remove annoying label at the top of the list of preferred items --- web_m2x_options/static/src/js/form.js | 20 ++++++++----------- .../static/src/less/web_m2x_options.less | 3 +++ web_m2x_options/views/view.xml | 1 + 3 files changed, 12 insertions(+), 12 deletions(-) create mode 100644 web_m2x_options/static/src/less/web_m2x_options.less diff --git a/web_m2x_options/static/src/js/form.js b/web_m2x_options/static/src/js/form.js index 2fc9017aee6c..17498658b369 100644 --- a/web_m2x_options/static/src/js/form.js +++ b/web_m2x_options/static/src/js/form.js @@ -157,9 +157,11 @@ odoo.define('web_m2x_options.web_m2x_options', function (require) { search_mru = self.is_option_set(self.view.ir_options['web_m2x_options.search_mru']); var mru_list = self.get_search_mru(); + var in_search_mru = false; if(search_val == "" && (can_search_mru || (search_mru_undef && search_mru))){ if (!_(mru_list).isEmpty()){ domain_list.push(mru_list); + in_search_mru = true; } } this.last_query = search_val; @@ -184,12 +186,16 @@ odoo.define('web_m2x_options.web_m2x_options', function (require) { // possible selections for the m2o var values = _.map(data, function (x) { x[1] = x[1].split("\n")[0]; - return { + var val= { label: _.str.escapeHTML(x[1]), value: x[1], name: x[1], id: x[0], }; + if (in_search_mru){ + val['classname'] = 'web_m2x_dropdown_option_mru'; + } + return val; }); // Search result value colors @@ -217,23 +223,13 @@ odoo.define('web_m2x_options.web_m2x_options', function (require) { def.resolve(values); }); } - // add label favorites if favorites option is set and - // search_val is empty - if(search_val == "" && (can_search_mru || (search_mru_undef && search_mru))){ - if (!_(mru_list).isEmpty() && !_(values).isEmpty()){ - values.unshift({ - label: _t("Most Recently Used:"), - // classname: 'oe_m2o_dropdown_option', - }); - } - } // 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']); - if (values.length > self.limit && (can_search_more || search_more_undef || search_more)) { + if ((values.length > self.limit || in_search_mru) && (can_search_more || search_more_undef || search_more)) { values = values.slice(0, self.limit); values.push({ label: _t("Search More..."), diff --git a/web_m2x_options/static/src/less/web_m2x_options.less b/web_m2x_options/static/src/less/web_m2x_options.less new file mode 100644 index 000000000000..7bd2b5338799 --- /dev/null +++ b/web_m2x_options/static/src/less/web_m2x_options.less @@ -0,0 +1,3 @@ +.web_m2x_dropdown_option_mru{ + font-style: italic; + } diff --git a/web_m2x_options/views/view.xml b/web_m2x_options/views/view.xml index 9628625b5103..139b30fb5e23 100644 --- a/web_m2x_options/views/view.xml +++ b/web_m2x_options/views/view.xml @@ -6,6 +6,7 @@ From 962acddc7d3cb0aab35d07af13d7e7ca0eba8532 Mon Sep 17 00:00:00 2001 From: "Laurent Mignon (ACSONE)" Date: Thu, 1 Dec 2016 11:45:24 +0100 Subject: [PATCH 3/3] [IMP] Keep the order of in the stored list of mru to display the result list --- web_m2x_options/static/src/js/form.js | 42 +++++++++++++++++---------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/web_m2x_options/static/src/js/form.js b/web_m2x_options/static/src/js/form.js index 17498658b369..41d4e6155d33 100644 --- a/web_m2x_options/static/src/js/form.js +++ b/web_m2x_options/static/src/js/form.js @@ -113,18 +113,19 @@ odoo.define('web_m2x_options.web_m2x_options', function (require) { return db + "/" + model + "/" + view_id + "/" + self.name; }, - get_search_mru: function(){ + get_mru_ids: function(){ var mru_option = 'web_m2x_options_mru', self = this; - var restore_mru_list = JSON.parse(localStorage.getItem(mru_option)), + var restore_mru_ids = JSON.parse(localStorage.getItem(mru_option)), key = self.compute_mru_key(); - if (restore_mru_list) { - if (!_.isUndefined(restore_mru_list[key])){ - return ['id', 'in', restore_mru_list[key]]; + if (restore_mru_ids) { + if (!_.isUndefined(restore_mru_ids[key])){ + return restore_mru_ids[key]; } } return []; }, + get_search_result: function (search_val) { var Objects = new Model(this.field.relation); var def = $.Deferred(); @@ -156,11 +157,12 @@ odoo.define('web_m2x_options.web_m2x_options', function (require) { search_mru_undef = _.isUndefined(self.options.search_mru), search_mru = self.is_option_set(self.view.ir_options['web_m2x_options.search_mru']); - var mru_list = self.get_search_mru(); + var mru_ids = []; var in_search_mru = false; if(search_val == "" && (can_search_mru || (search_mru_undef && search_mru))){ - if (!_(mru_list).isEmpty()){ - domain_list.push(mru_list); + mru_ids = self.get_mru_ids(); + if (!_(mru_ids).isEmpty()){ + domain_list.push(['id', 'in', mru_ids]); in_search_mru = true; } } @@ -197,6 +199,14 @@ odoo.define('web_m2x_options.web_m2x_options', function (require) { } return val; }); + // If we are in a mru search, reorder the result list in the + // same order as the one stored to keep the saved preference + // order (The most recent ones first) + if (in_search_mru){ + values = _(values).sortBy(function(item){ + return mru_ids.indexOf(item.id); + }); + } // Search result value colors if (self.colors && self.field_color) { @@ -299,15 +309,15 @@ odoo.define('web_m2x_options.web_m2x_options', function (require) { return def; }, - update_mru_list: function(){ + update_mru_ids: function(){ var self = this, mru_option = 'web_m2x_options_mru'; var key = self.compute_mru_key(); // check if the localstorage has some items for the current model if (localStorage.getItem(mru_option)) { - var restore_mru_list = JSON.parse(localStorage.getItem(mru_option)); - if (restore_mru_list[key]) { - var queue = restore_mru_list[key]; + var restore_mru_ids = JSON.parse(localStorage.getItem(mru_option)); + if (restore_mru_ids[key]) { + var queue = restore_mru_ids[key]; // if the element doesn't exist in the stack if (queue.indexOf(self.get_value(true)) < 0 && self.get_value(true)){ if (queue.length < 5) { @@ -319,7 +329,7 @@ odoo.define('web_m2x_options.web_m2x_options', function (require) { // add the new element at the beginning queue.unshift(self.get_value(true)); } - restore_mru_list[key] = queue; + restore_mru_ids[key] = queue; }else{ // if the element already exist in the stack if (queue.indexOf(self.get_value(true)) >= 0 && self.get_value(true)){ @@ -333,10 +343,10 @@ odoo.define('web_m2x_options.web_m2x_options', function (require) { }else{ // if the element is the first one if (self.get_value(true)){ - restore_mru_list[key] = [self.get_value(true)]; + restore_mru_ids[key] = [self.get_value(true)]; } } - localStorage.setItem(mru_option, JSON.stringify(restore_mru_list)); + localStorage.setItem(mru_option, JSON.stringify(restore_mru_ids)); }else { // first time to create an entry in the localstorage if (self.get_value(true)){ @@ -356,7 +366,7 @@ odoo.define('web_m2x_options.web_m2x_options', function (require) { search_mru = self.is_option_set(self.view.ir_options['web_m2x_options.search_mru']); if(can_search_mru || (search_mru_undef && search_mru)){ - self.update_mru_list(); + self.update_mru_ids(); } } }