From ae00081ee252abb570e1fb8ff62433ac308dce85 Mon Sep 17 00:00:00 2001 From: Holger Brunn Date: Tue, 20 Oct 2015 13:33:24 +0200 Subject: [PATCH 1/7] [FIX] count needactions from the same model only once --- web_menu_navbar_needaction/models/ir_ui_menu.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/web_menu_navbar_needaction/models/ir_ui_menu.py b/web_menu_navbar_needaction/models/ir_ui_menu.py index 052446ad..8cc1b553 100644 --- a/web_menu_navbar_needaction/models/ir_ui_menu.py +++ b/web_menu_navbar_needaction/models/ir_ui_menu.py @@ -27,10 +27,15 @@ class IrUiMenu(models.Model): def get_navbar_needaction_data(self): result = {} for this in self: - result[this.id] = sum(map( - lambda x: x['needaction_counter'], - self.search([('id', 'child_of', this.ids)]) - ._filter_visible_menus().get_needaction_data() - .itervalues()) - ) + count_per_model = {} + for menu_id, needaction in self.search( + [('id', 'child_of', this.ids)])._filter_visible_menus()\ + .get_needaction_data().iteritems(): + if needaction['needaction_enabled']: + model = self.env['ir.ui.menu'].browse(menu_id).action\ + .res_model + count_per_model[model] = max( + count_per_model.get(model), + needaction['needaction_counter']) + result[this.id] = sum(count_per_model.itervalues()) return result From 1994a8b98e15b67e56d45e98400b91461b1aad08 Mon Sep 17 00:00:00 2001 From: Holger Brunn Date: Tue, 20 Oct 2015 13:51:51 +0200 Subject: [PATCH 2/7] [IMP] reload navbar needaction when a mail was read --- web_menu_navbar_needaction/__manifest__.py | 1 + .../static/src/js/web_menu_navbar_needaction.js | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/web_menu_navbar_needaction/__manifest__.py b/web_menu_navbar_needaction/__manifest__.py index 82f1fadd..586c074d 100644 --- a/web_menu_navbar_needaction/__manifest__.py +++ b/web_menu_navbar_needaction/__manifest__.py @@ -26,6 +26,7 @@ "summary": "Show the sum of submenus' needaction counters in main menu", "depends": [ 'web', + 'mail', ], "data": [ "data/ir_config_parameter.xml", diff --git a/web_menu_navbar_needaction/static/src/js/web_menu_navbar_needaction.js b/web_menu_navbar_needaction/static/src/js/web_menu_navbar_needaction.js index 93a859ad..799b6120 100644 --- a/web_menu_navbar_needaction/static/src/js/web_menu_navbar_needaction.js +++ b/web_menu_navbar_needaction/static/src/js/web_menu_navbar_needaction.js @@ -75,4 +75,15 @@ openerp.web_menu_navbar_needaction = function(instance) }); }, }) + + instance.mail.Thread.include({ + message_fetch_set_read: function (message_list) + { + this._super.apply(this, arguments); + return this.render_mutex.exec(function() + { + instance.client.menu.refresh_navbar_needaction(); + }); + }, + }) } From c75987972b3e3949133bdb2bccfe0bcb0cd825bb Mon Sep 17 00:00:00 2001 From: Holger Brunn Date: Tue, 20 Sep 2016 21:34:55 +0200 Subject: [PATCH 3/7] [IMP] web_menu_navbar_needaction: reflow, new features (#265) [IMP] web_menu_navbar_needaction: Several improvements: * Make the menu reflow after updating needactions * Allow to disable needaction completely or to set a custom domain * Support for clicking the number to end up on the first action * No need to block the UI for our request * Don't crash on corner cases, filter out search defaults from context, disable custom filters * Allow to define needaction domains for any menu * Support server actions * Support models implementing the function, but not the interface * Show a main menu's child needaction counters --- web_menu_navbar_needaction/README.rst | 3 + web_menu_navbar_needaction/__manifest__.py | 1 + .../models/ir_ui_menu.py | 108 +++++++++++++++++- .../src/css/web_menu_navbar_needaction.css | 4 + .../src/js/web_menu_navbar_needaction.js | 57 +++++++-- .../views/ir_ui_menu.xml | 16 +++ 6 files changed, 177 insertions(+), 12 deletions(-) create mode 100644 web_menu_navbar_needaction/views/ir_ui_menu.xml diff --git a/web_menu_navbar_needaction/README.rst b/web_menu_navbar_needaction/README.rst index 81d54813..9e663d1a 100644 --- a/web_menu_navbar_needaction/README.rst +++ b/web_menu_navbar_needaction/README.rst @@ -1,5 +1,6 @@ .. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg :alt: License: AGPL-3 + ================================ Needaction counters in main menu ================================ @@ -15,6 +16,8 @@ To configure the update frequency, set the configuration parameter `web_menu_nav To disable updates, set the parameter to 0. +For more fine-grained control over the menus, you can turn off needaction for a menu by setting its field `needaction` to `False`, or in case you need a different needaction domain, set `needaction_domain` on the menuitem to the domain you need. A side effect of this is that you can use this addon to add needaction capabilities to any model, independent of it implementing the respective mixin by simply filling in your domain there. + Usage ===== diff --git a/web_menu_navbar_needaction/__manifest__.py b/web_menu_navbar_needaction/__manifest__.py index 586c074d..d735bbbd 100644 --- a/web_menu_navbar_needaction/__manifest__.py +++ b/web_menu_navbar_needaction/__manifest__.py @@ -29,6 +29,7 @@ 'mail', ], "data": [ + "views/ir_ui_menu.xml", "data/ir_config_parameter.xml", 'views/templates.xml', ], diff --git a/web_menu_navbar_needaction/models/ir_ui_menu.py b/web_menu_navbar_needaction/models/ir_ui_menu.py index 8cc1b553..c5ab8ac6 100644 --- a/web_menu_navbar_needaction/models/ir_ui_menu.py +++ b/web_menu_navbar_needaction/models/ir_ui_menu.py @@ -17,25 +17,123 @@ # along with this program. If not, see . # ############################################################################## -from openerp import models, api +import operator +from openerp import _, models, api, fields +from openerp.tools.safe_eval import safe_eval +from openerp.exceptions import Warning as UserError +from openerp.osv import expression class IrUiMenu(models.Model): _inherit = 'ir.ui.menu' + needaction = fields.Boolean( + help='Set to False to disable needaction for specific menu items', + default=True) + needaction_domain = fields.Text( + help='If your menu item needs a different domain, set it here. It ' + 'will override the model\'s needaction domain completely.') + @api.multi def get_navbar_needaction_data(self): result = {} for this in self: count_per_model = {} + action_menu = self.env['ir.ui.menu'].browse([]) + if not this.needaction: + continue for menu_id, needaction in self.search( [('id', 'child_of', this.ids)])._filter_visible_menus()\ .get_needaction_data().iteritems(): if needaction['needaction_enabled']: - model = self.env['ir.ui.menu'].browse(menu_id).action\ - .res_model + menu = self.env['ir.ui.menu'].browse(menu_id) + model = menu._get_needaction_model() count_per_model[model] = max( count_per_model.get(model), - needaction['needaction_counter']) - result[this.id] = sum(count_per_model.itervalues()) + needaction['needaction_counter'] + ) + if needaction['needaction_counter'] and not action_menu: + action_menu = menu + result[this.id] = { + 'count': sum(count_per_model.itervalues()), + } + if action_menu: + result[this.id].update({ + 'action_id': action_menu.action and + action_menu.action.id or None, + 'action_domain': action_menu._eval_needaction_domain(), + }) + return result + + @api.multi + def get_needaction_data(self): + result = super(IrUiMenu, self).get_needaction_data() + for this in self.sorted(operator.itemgetter('parent_left'), True): + data = result[this.id] + if data['needaction_enabled'] or this.needaction and\ + this.needaction_domain: + if not this.needaction: + data['needaction_enabled'] = False + data['needaction_counter'] = 0 + continue + if this.needaction_domain and\ + this._get_needaction_model() is not None: + data['needaction_enabled'] = True + data['needaction_counter'] = this._get_needaction_model()\ + .search_count(this._eval_needaction_domain()) + if not data['needaction_enabled'] and this.needaction and\ + this.child_id and this.parent_id and this.parent_id.parent_id: + # if the user didn't turn it off, show counters for submenus + # but only from the 3rd level (the first that is closed by + # default) + for child in this.child_id: + data['needaction_counter'] += result.get(child.id, {}).get( + 'needaction_counter', 0) + data['needaction_enabled'] = bool(data['needaction_counter']) return result + + @api.multi + def _eval_needaction_domain(self): + self.ensure_one() + eval_context = { + 'uid': self.env.user.id, + 'user': self.env.user, + } + if self.needaction_domain: + return safe_eval(self.needaction_domain, locals_dict=eval_context) + model = self._get_needaction_model() + if model is None or not hasattr(model, '_needaction_domain_get'): + return [] + return expression.AND([ + safe_eval( + 'domain' in self.action._fields and self.action.domain or '[]', + locals_dict=eval_context), + model._needaction_domain_get(), + ]) + + @api.multi + def _get_needaction_model(self): + if not self.action: + return None + model = None + if 'res_model' in self.action._fields: + model = self.action.res_model + elif 'model_id' in self.action._fields: + model = self.action.model_id.model + if model in self.env.registry: + return self.env[model] + return None + + @api.constrains('needaction_domain') + @api.multi + def _check_needaction_domain(self): + for this in self: + try: + expression.AND([ + this._eval_needaction_domain(), + expression.TRUE_DOMAIN, + ]) + except Exception as ex: + raise UserError( + _('Cannot evaluate %s to a search domain:\n%s') % + (self.needaction_domain, ex)) diff --git a/web_menu_navbar_needaction/static/src/css/web_menu_navbar_needaction.css b/web_menu_navbar_needaction/static/src/css/web_menu_navbar_needaction.css index 259502de..fc858479 100644 --- a/web_menu_navbar_needaction/static/src/css/web_menu_navbar_needaction.css +++ b/web_menu_navbar_needaction/static/src/css/web_menu_navbar_needaction.css @@ -2,3 +2,7 @@ { margin-left: .3em; } +#oe_main_menu_navbar .badge:hover +{ + transform: scale(1.1); +} diff --git a/web_menu_navbar_needaction/static/src/js/web_menu_navbar_needaction.js b/web_menu_navbar_needaction/static/src/js/web_menu_navbar_needaction.js index 799b6120..dcb26826 100644 --- a/web_menu_navbar_needaction/static/src/js/web_menu_navbar_needaction.js +++ b/web_menu_navbar_needaction/static/src/js/web_menu_navbar_needaction.js @@ -50,29 +50,72 @@ openerp.web_menu_navbar_needaction = function(instance) .map(function() { return parseInt(jQuery(this).attr('data-menu')); }) .get(); return new instance.web.Model('ir.ui.menu') - .call('get_navbar_needaction_data', [this.navbar_menu_ids]) + .call('get_navbar_needaction_data', [this.navbar_menu_ids], + {}, {shadow: true}) .then(this.proxy(this.process_navbar_needaction)); }, process_navbar_needaction: function(data) { var self = this; - _.each(data, function (needaction_count, menu_id) + _.each(data, function (needaction_data, menu_id) { var $item = self.$el.parents('body').find( _.str.sprintf('#oe_main_menu_navbar a[data-menu="%s"]', menu_id)); - if(!$item.length) + if(!$item.length || _.isEmpty(needaction_data)) { return; } $item.find('.badge').remove(); - if(needaction_count) + if(needaction_data.count) { - $item.append( - instance.web.qweb.render("Menu.needaction_counter", - {widget : {needaction_counter: needaction_count}})); + var $counter = jQuery( + instance.web.qweb.render("Menu.needaction_counter", + { + widget: { + needaction_counter: needaction_data.count, + } + })) + .appendTo($item); + if(needaction_data.action_id) + { + $counter.click(function(ev) + { + var parent = self.getParent(); + ev.stopPropagation(); + ev.preventDefault(); + return parent.menu_dm.add( + self.rpc('/web/action/load', { + action_id: needaction_data.action_id, + })) + .then(function(action) + { + return parent.action_mutex.exec(function() + { + action.domain = needaction_data.action_domain; + action.context = new instance.web.CompoundContext( + action.context || {} + ); + action.context = instance.web.pyeval.eval( + 'context', action.context + ); + _.each(_.keys(action.context), function(key) + { + if(key.startsWith('search_default')) + { + delete action.context[key]; + } + }); + return parent.action_manager.do_action( + action, {disable_custom_filters: true} + ); + }); + }); + }); + } } }); + instance.web.bus.trigger('resize'); }, }) diff --git a/web_menu_navbar_needaction/views/ir_ui_menu.xml b/web_menu_navbar_needaction/views/ir_ui_menu.xml new file mode 100644 index 00000000..22b82faa --- /dev/null +++ b/web_menu_navbar_needaction/views/ir_ui_menu.xml @@ -0,0 +1,16 @@ + + + + + ir.ui.menu + + + + + + + + + + + From 0df04ac2524e00403defe17e64d0e91b57b9b1fd Mon Sep 17 00:00:00 2001 From: Holger Brunn Date: Fri, 28 Apr 2017 15:07:21 +0200 Subject: [PATCH 4/7] [FIX] we need to parse the parameter to an int --- .../static/src/js/web_menu_navbar_needaction.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web_menu_navbar_needaction/static/src/js/web_menu_navbar_needaction.js b/web_menu_navbar_needaction/static/src/js/web_menu_navbar_needaction.js index dcb26826..783fce3e 100644 --- a/web_menu_navbar_needaction/static/src/js/web_menu_navbar_needaction.js +++ b/web_menu_navbar_needaction/static/src/js/web_menu_navbar_needaction.js @@ -36,7 +36,7 @@ openerp.web_menu_navbar_needaction = function(instance) }, refresh_navbar_needaction: function(timeout) { - if(timeout) + if(parseInt(timeout)) { setTimeout(this.proxy(this.refresh_navbar_needaction), timeout, timeout); } From 8c6401378053e1d7401e8640ac8f9894c792591c Mon Sep 17 00:00:00 2001 From: Jairo Llopis Date: Tue, 30 May 2017 16:39:52 +0200 Subject: [PATCH 5/7] Explicit radix According to https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt#Parameters, we should always specify the radix. --- .../static/src/js/web_menu_navbar_needaction.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web_menu_navbar_needaction/static/src/js/web_menu_navbar_needaction.js b/web_menu_navbar_needaction/static/src/js/web_menu_navbar_needaction.js index 783fce3e..0db8a2c8 100644 --- a/web_menu_navbar_needaction/static/src/js/web_menu_navbar_needaction.js +++ b/web_menu_navbar_needaction/static/src/js/web_menu_navbar_needaction.js @@ -36,7 +36,7 @@ openerp.web_menu_navbar_needaction = function(instance) }, refresh_navbar_needaction: function(timeout) { - if(parseInt(timeout)) + if(parseInt(timeout, 10)) { setTimeout(this.proxy(this.refresh_navbar_needaction), timeout, timeout); } From c52e355ef143fbeae780dfec1278c7e8be0d65db Mon Sep 17 00:00:00 2001 From: Holger Brunn Date: Thu, 10 Aug 2017 09:29:28 +0200 Subject: [PATCH 6/7] [IMP] stop hammering the server when our call caused a problem --- .../src/js/web_menu_navbar_needaction.js | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/web_menu_navbar_needaction/static/src/js/web_menu_navbar_needaction.js b/web_menu_navbar_needaction/static/src/js/web_menu_navbar_needaction.js index 0db8a2c8..ef06d453 100644 --- a/web_menu_navbar_needaction/static/src/js/web_menu_navbar_needaction.js +++ b/web_menu_navbar_needaction/static/src/js/web_menu_navbar_needaction.js @@ -30,18 +30,25 @@ openerp.web_menu_navbar_needaction = function(instance) new instance.web.Model('ir.config_parameter') .call('get_param', ['web_menu_navbar_needaction.refresh_timeout']) - .then(self.proxy(self.refresh_navbar_needaction)) + .then(self.proxy(self.set_interval_navbar_needaction)) }); return result; }, - refresh_navbar_needaction: function(timeout) + set_interval_navbar_needaction: function(timeout) { if(parseInt(timeout, 10)) { - setTimeout(this.proxy(this.refresh_navbar_needaction), timeout, timeout); + this.navbar_needaction_timer = setInterval( + this.proxy(this.load_navbar_needaction), timeout + ); } return this.load_navbar_needaction(); }, + clear_interval_navbar_needaction: function(error, ev) + { + clearInterval(this.navbar_needaction_timer); + ev.preventDefault(); + }, load_navbar_needaction: function() { this.navbar_menu_ids = this.$el.parents('body') @@ -52,7 +59,8 @@ openerp.web_menu_navbar_needaction = function(instance) return new instance.web.Model('ir.ui.menu') .call('get_navbar_needaction_data', [this.navbar_menu_ids], {}, {shadow: true}) - .then(this.proxy(this.process_navbar_needaction)); + .then(this.proxy(this.process_navbar_needaction)) + .fail(this.proxy(this.clear_interval_navbar_needaction)); }, process_navbar_needaction: function(data) { @@ -120,12 +128,17 @@ openerp.web_menu_navbar_needaction = function(instance) }) instance.mail.Thread.include({ - message_fetch_set_read: function (message_list) + message_fetch_set_read: function(message_list) { this._super.apply(this, arguments); - return this.render_mutex.exec(function() + return this.message_fetch_set_read_navbar_needaction(message_list); + }, + message_fetch_set_read_navbar_needaction: function() + { + // don't return the deferred object, this should be asynchronous + this.render_mutex.exec(function() { - instance.client.menu.refresh_navbar_needaction(); + instance.client.menu.load_navbar_needaction(); }); }, }) From 65562a5c08b102f52eaa5755d02b581055085ce3 Mon Sep 17 00:00:00 2001 From: Holger Brunn Date: Tue, 28 Aug 2018 16:15:22 +0200 Subject: [PATCH 7/7] [ADD] migrate web_menu_navbar_needaction to 10.0 --- web_menu_navbar_needaction/README.rst | 69 +-- web_menu_navbar_needaction/__init__.py | 20 +- web_menu_navbar_needaction/__manifest__.py | 26 +- .../data/ir_config_parameter.xml | 14 +- .../data/ir_ui_menu.xml | 7 + web_menu_navbar_needaction/models/__init__.py | 20 +- .../models/ir_ui_menu.py | 39 +- .../readme/CONFIGURE.rst | 3 + .../readme/CONTRIBUTORS.rst | 1 + .../readme/DESCRIPTION.rst | 3 + .../static/description/index.html | 404 ++++++++++++++++++ .../src/js/web_menu_navbar_needaction.js | 184 ++++---- .../tests/test_web_menu_navbar_needaction.py | 25 +- .../views/ir_ui_menu.xml | 1 - .../views/templates.xml | 18 +- 15 files changed, 590 insertions(+), 244 deletions(-) create mode 100644 web_menu_navbar_needaction/data/ir_ui_menu.xml create mode 100644 web_menu_navbar_needaction/readme/CONFIGURE.rst create mode 100644 web_menu_navbar_needaction/readme/CONTRIBUTORS.rst create mode 100644 web_menu_navbar_needaction/readme/DESCRIPTION.rst create mode 100644 web_menu_navbar_needaction/static/description/index.html diff --git a/web_menu_navbar_needaction/README.rst b/web_menu_navbar_needaction/README.rst index 9e663d1a..a761e33c 100644 --- a/web_menu_navbar_needaction/README.rst +++ b/web_menu_navbar_needaction/README.rst @@ -1,14 +1,39 @@ -.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg - :alt: License: AGPL-3 - ================================ Needaction counters in main menu ================================ +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fweb-lightgray.png?logo=github + :target: https://github.com/OCA/web/tree/10.0/web_menu_navbar_needaction + :alt: OCA/web +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/web-10-0/web-10-0-web_menu_navbar_needaction + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/162/10.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + This module shows the sum of all submenus' needaction counters in main menus. This way, users get visual feedback where there's still work to do without having to open the respective menu beforehand. The counters are updated periodically, so users will also be notified if there are new records that require attention. +**Table of contents** + +.. contents:: + :local: + Configuration ============= @@ -16,46 +41,42 @@ To configure the update frequency, set the configuration parameter `web_menu_nav To disable updates, set the parameter to 0. -For more fine-grained control over the menus, you can turn off needaction for a menu by setting its field `needaction` to `False`, or in case you need a different needaction domain, set `needaction_domain` on the menuitem to the domain you need. A side effect of this is that you can use this addon to add needaction capabilities to any model, independent of it implementing the respective mixin by simply filling in your domain there. - -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 - 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 `_. +If you spotted it first, help us smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. Credits ======= +Authors +~~~~~~~ + +* Therp BV + Contributors ------------- +~~~~~~~~~~~~ * Holger Brunn -Maintainer ----------- +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. .. 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 module is part of the `OCA/web `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/web_menu_navbar_needaction/__init__.py b/web_menu_navbar_needaction/__init__.py index cdb7d736..289e8cdb 100644 --- a/web_menu_navbar_needaction/__init__.py +++ b/web_menu_navbar_needaction/__init__.py @@ -1,20 +1,4 @@ # -*- coding: utf-8 -*- -############################################################################## -# -# This module copyright (C) 2015 Therp BV . -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## +# Copyright 2015-2018 Therp BV +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). from . import models diff --git a/web_menu_navbar_needaction/__manifest__.py b/web_menu_navbar_needaction/__manifest__.py index d735bbbd..a42182f7 100644 --- a/web_menu_navbar_needaction/__manifest__.py +++ b/web_menu_navbar_needaction/__manifest__.py @@ -1,25 +1,9 @@ # -*- coding: utf-8 -*- -############################################################################## -# -# This module copyright (C) 2015 Therp BV . -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## +# Copyright 2015-2018 Therp BV +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). { "name": "Needaction counters in main menu", - "version": "8.0.1.0.0", + "version": "10.0.1.0.0", "author": "Therp BV,Odoo Community Association (OCA)", "license": "AGPL-3", "category": "Usability", @@ -29,11 +13,9 @@ 'mail', ], "data": [ + "data/ir_ui_menu.xml", "views/ir_ui_menu.xml", "data/ir_config_parameter.xml", 'views/templates.xml', ], - "auto_install": False, - 'installable': False, - "application": False, } diff --git a/web_menu_navbar_needaction/data/ir_config_parameter.xml b/web_menu_navbar_needaction/data/ir_config_parameter.xml index 757fd1b8..2ce2ebde 100644 --- a/web_menu_navbar_needaction/data/ir_config_parameter.xml +++ b/web_menu_navbar_needaction/data/ir_config_parameter.xml @@ -1,9 +1,7 @@ - - - - web_menu_navbar_needaction.refresh_timeout - 600000 - - - + + + web_menu_navbar_needaction.refresh_timeout + 600000 + + diff --git a/web_menu_navbar_needaction/data/ir_ui_menu.xml b/web_menu_navbar_needaction/data/ir_ui_menu.xml new file mode 100644 index 00000000..37d40cf6 --- /dev/null +++ b/web_menu_navbar_needaction/data/ir_ui_menu.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/web_menu_navbar_needaction/models/__init__.py b/web_menu_navbar_needaction/models/__init__.py index 66a84b35..09adcdd9 100644 --- a/web_menu_navbar_needaction/models/__init__.py +++ b/web_menu_navbar_needaction/models/__init__.py @@ -1,20 +1,4 @@ # -*- coding: utf-8 -*- -############################################################################## -# -# This module copyright (C) 2015 Therp BV . -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## +# Copyright 2015-2018 Therp BV +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). from . import ir_ui_menu diff --git a/web_menu_navbar_needaction/models/ir_ui_menu.py b/web_menu_navbar_needaction/models/ir_ui_menu.py index c5ab8ac6..1006cabe 100644 --- a/web_menu_navbar_needaction/models/ir_ui_menu.py +++ b/web_menu_navbar_needaction/models/ir_ui_menu.py @@ -1,22 +1,6 @@ # -*- coding: utf-8 -*- -############################################################################## -# -# This module copyright (C) 2015 Therp BV (). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## +# Copyright 2015-2018 Therp BV +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). import operator from openerp import _, models, api, fields from openerp.tools.safe_eval import safe_eval @@ -36,15 +20,22 @@ class IrUiMenu(models.Model): @api.multi def get_navbar_needaction_data(self): + """Return aggregated needaction data for all children of self""" result = {} for this in self: count_per_model = {} action_menu = self.env['ir.ui.menu'].browse([]) if not this.needaction: continue + custom_needaction = this._get_navbar_needaction_data_custom() + if custom_needaction: + result[this.id] = custom_needaction + continue for menu_id, needaction in self.search( [('id', 'child_of', this.ids)])._filter_visible_menus()\ .get_needaction_data().iteritems(): + if needaction.get('needaction_from_children'): + continue if needaction['needaction_enabled']: menu = self.env['ir.ui.menu'].browse(menu_id) model = menu._get_needaction_model() @@ -65,6 +56,17 @@ class IrUiMenu(models.Model): }) return result + @api.multi + def _get_navbar_needaction_data_custom(self): + """Return nonstandard needaction data for a menu item""" + self.ensure_one() + if self == self.env.ref('mail.mail_channel_menu_root_chat'): + return { + 'count': self.env['mail.message']._needaction_count(), + 'action_id': + self.env.ref('mail.mail_channel_action_client_chat').id, + } + @api.multi def get_needaction_data(self): result = super(IrUiMenu, self).get_needaction_data() @@ -90,6 +92,7 @@ class IrUiMenu(models.Model): data['needaction_counter'] += result.get(child.id, {}).get( 'needaction_counter', 0) data['needaction_enabled'] = bool(data['needaction_counter']) + data['needaction_from_children'] = True return result @api.multi diff --git a/web_menu_navbar_needaction/readme/CONFIGURE.rst b/web_menu_navbar_needaction/readme/CONFIGURE.rst new file mode 100644 index 00000000..027a2be3 --- /dev/null +++ b/web_menu_navbar_needaction/readme/CONFIGURE.rst @@ -0,0 +1,3 @@ +To configure the update frequency, set the configuration parameter `web_menu_navbar_needaction.refresh_timeout` to the amount of milliseconds after which the counters should be updated. Don't do this too often, as this will cause a lot of queries to the database. The default it 600000, which is equivalent to 10 minutes. + +To disable updates, set the parameter to 0. diff --git a/web_menu_navbar_needaction/readme/CONTRIBUTORS.rst b/web_menu_navbar_needaction/readme/CONTRIBUTORS.rst new file mode 100644 index 00000000..b120a956 --- /dev/null +++ b/web_menu_navbar_needaction/readme/CONTRIBUTORS.rst @@ -0,0 +1 @@ +* Holger Brunn diff --git a/web_menu_navbar_needaction/readme/DESCRIPTION.rst b/web_menu_navbar_needaction/readme/DESCRIPTION.rst new file mode 100644 index 00000000..bae16105 --- /dev/null +++ b/web_menu_navbar_needaction/readme/DESCRIPTION.rst @@ -0,0 +1,3 @@ +This module shows the sum of all submenus' needaction counters in main menus. This way, users get visual feedback where there's still work to do without having to open the respective menu beforehand. + +The counters are updated periodically, so users will also be notified if there are new records that require attention. diff --git a/web_menu_navbar_needaction/static/description/index.html b/web_menu_navbar_needaction/static/description/index.html new file mode 100644 index 00000000..1a1c440a --- /dev/null +++ b/web_menu_navbar_needaction/static/description/index.html @@ -0,0 +1,404 @@ + + + + + + +Needaction counters in main menu + + + +
+

Needaction counters in main menu

+ + +

Beta License: AGPL-3 OCA/web Translate me on Weblate Try me on Runbot

+

This module shows the sum of all submenus’ needaction counters in main menus. This way, users get visual feedback where there’s still work to do without having to open the respective menu beforehand.

+

The counters are updated periodically, so users will also be notified if there are new records that require attention.

+

Table of contents

+ +
+

Configuration

+

To configure the update frequency, set the configuration parameter web_menu_navbar_needaction.refresh_timeout to the amount of milliseconds after which the counters should be updated. Don’t do this too often, as this will cause a lot of queries to the database. The default it 600000, which is equivalent to 10 minutes.

+

To disable updates, set the parameter to 0.

+
+
+

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.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Therp BV
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

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.

+

This module is part of the OCA/web project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/web_menu_navbar_needaction/static/src/js/web_menu_navbar_needaction.js b/web_menu_navbar_needaction/static/src/js/web_menu_navbar_needaction.js index ef06d453..75194e84 100644 --- a/web_menu_navbar_needaction/static/src/js/web_menu_navbar_needaction.js +++ b/web_menu_navbar_needaction/static/src/js/web_menu_navbar_needaction.js @@ -1,145 +1,117 @@ //-*- coding: utf-8 -*- -//############################################################################ -// -// This module copyright (C) 2015 Therp BV . -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// -//############################################################################ +// Copyright 2015-2018 Therp BV +// License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +odoo.define('web_menu_navbar_needaction', function(require) { + var bus = require('web.core').bus, + chat_manager = require('mail.chat_manager'), + CompoundContext = require('web.data').CompoundContext, + pyeval = require('web.pyeval'), + Menu = require('web.Menu'), + Model = require('web.Model'), + qweb = require('web.core').qweb; -openerp.web_menu_navbar_needaction = function(instance) -{ - instance.web.Menu.include({ - init: function() - { + Menu.include({ + init: function() { var self = this, result = this._super.apply(this, arguments); - this.on('menu_bound', this, function() - { - new instance.web.Model('ir.config_parameter') + this.on('menu_bound', this, function() { + new Model('ir.config_parameter') .call('get_param', ['web_menu_navbar_needaction.refresh_timeout']) - .then(self.proxy(self.set_interval_navbar_needaction)) + .then(self.proxy('set_interval_navbar_needaction')); }); return result; }, - set_interval_navbar_needaction: function(timeout) - { - if(parseInt(timeout, 10)) - { + start: function() { + chat_manager.bus.on( + 'update_needaction', this, this.load_navbar_needaction + ); + return this._super.apply(this, arguments); + }, + set_interval_navbar_needaction: function(timeout) { + if(parseInt(timeout, 10)) { this.navbar_needaction_timer = setInterval( - this.proxy(this.load_navbar_needaction), timeout + this.proxy('load_navbar_needaction'), timeout ); } return this.load_navbar_needaction(); }, - clear_interval_navbar_needaction: function(error, ev) - { + clear_interval_navbar_needaction: function(error, ev) { clearInterval(this.navbar_needaction_timer); ev.preventDefault(); }, - load_navbar_needaction: function() - { + load_navbar_needaction: function() { this.navbar_menu_ids = this.$el.parents('body') .find('#oe_main_menu_navbar a[data-menu]') - .filter(function() { return parseInt(jQuery(this).attr('data-menu')); }) - .map(function() { return parseInt(jQuery(this).attr('data-menu')); }) + .filter(function() { + return parseInt(jQuery(this).attr('data-menu'), 10); + }) + .map(function() { + return parseInt(jQuery(this).attr('data-menu'), 10); + }) .get(); - return new instance.web.Model('ir.ui.menu') + return new Model('ir.ui.menu') .call('get_navbar_needaction_data', [this.navbar_menu_ids], {}, {shadow: true}) - .then(this.proxy(this.process_navbar_needaction)) - .fail(this.proxy(this.clear_interval_navbar_needaction)); + .then(this.proxy('process_navbar_needaction')) + .fail(this.proxy('clear_interval_navbar_needaction')); }, - process_navbar_needaction: function(data) - { + process_navbar_needaction: function(data) { var self = this; - _.each(data, function (needaction_data, menu_id) - { + _.each(data, function (needaction_data, menu_id) { var $item = self.$el.parents('body').find( _.str.sprintf('#oe_main_menu_navbar a[data-menu="%s"]', menu_id)); - if(!$item.length || _.isEmpty(needaction_data)) - { + if(!$item.length || _.isEmpty(needaction_data)) { return; } $item.find('.badge').remove(); - if(needaction_data.count) - { - var $counter = jQuery( - instance.web.qweb.render("Menu.needaction_counter", - { + if(needaction_data.count) { + var $counter = jQuery(qweb.render( + "Menu.needaction_counter", { widget: { needaction_counter: needaction_data.count, } - })) - .appendTo($item); - if(needaction_data.action_id) - { - $counter.click(function(ev) - { - var parent = self.getParent(); - ev.stopPropagation(); - ev.preventDefault(); - return parent.menu_dm.add( - self.rpc('/web/action/load', { - action_id: needaction_data.action_id, - })) - .then(function(action) - { - return parent.action_mutex.exec(function() - { - action.domain = needaction_data.action_domain; - action.context = new instance.web.CompoundContext( - action.context || {} - ); - action.context = instance.web.pyeval.eval( - 'context', action.context - ); - _.each(_.keys(action.context), function(key) - { - if(key.startsWith('search_default')) - { - delete action.context[key]; - } - }); - return parent.action_manager.do_action( - action, {disable_custom_filters: true} - ); - }); - }); - }); + } + )).appendTo($item); + if(needaction_data.action_id) { + $counter.click( + needaction_data, + self.proxy('navbar_needaction_click') + ); } } }); - instance.web.bus.trigger('resize'); - }, - }) - - instance.mail.Thread.include({ - message_fetch_set_read: function(message_list) - { - this._super.apply(this, arguments); - return this.message_fetch_set_read_navbar_needaction(message_list); + bus.trigger('resize'); }, - message_fetch_set_read_navbar_needaction: function() - { - // don't return the deferred object, this should be asynchronous - this.render_mutex.exec(function() - { - instance.client.menu.load_navbar_needaction(); + navbar_needaction_click: function(ev) { + var needaction_data = ev.data, + parent = this.getParent(); + ev.stopPropagation(); + ev.preventDefault(); + return parent.menu_dm.add( + this.rpc('/web/action/load', { + action_id: needaction_data.action_id, + })) + .then(function(action) { + return parent.action_mutex.exec(function() { + action.domain = needaction_data.action_domain; + action.context = new CompoundContext( + action.context || {} + ); + action.context = pyeval.eval( + 'context', action.context + ); + _.each(_.keys(action.context), function(key) { + if(key.startsWith('search_default')) { + delete action.context[key]; + } + }); + return parent.action_manager.do_action( + action, {disable_custom_filters: true} + ); + }); }); }, - }) -} + }); +}); diff --git a/web_menu_navbar_needaction/tests/test_web_menu_navbar_needaction.py b/web_menu_navbar_needaction/tests/test_web_menu_navbar_needaction.py index a0fd17c0..321b6dcc 100644 --- a/web_menu_navbar_needaction/tests/test_web_menu_navbar_needaction.py +++ b/web_menu_navbar_needaction/tests/test_web_menu_navbar_needaction.py @@ -1,23 +1,8 @@ # -*- coding: utf-8 -*- -############################################################################## -# -# This module copyright (C) 2015 Therp BV . -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## -from openerp.tests.common import TransactionCase +# Copyright 2015-2018 Therp BV +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +from odoo.exceptions import ValidationError +from odoo.tests.common import TransactionCase class TestWebMenuNavbarNeedaction(TransactionCase): @@ -25,3 +10,5 @@ class TestWebMenuNavbarNeedaction(TransactionCase): main_menus = self.env['ir.ui.menu'].search([('parent_id', '=', False)]) data = main_menus.get_navbar_needaction_data() self.assertEqual(len(main_menus), len(data)) + with self.assertRaises(ValidationError): + main_menus[:1].write({'needaction_domain': '['}) diff --git a/web_menu_navbar_needaction/views/ir_ui_menu.xml b/web_menu_navbar_needaction/views/ir_ui_menu.xml index 22b82faa..2618e0ff 100644 --- a/web_menu_navbar_needaction/views/ir_ui_menu.xml +++ b/web_menu_navbar_needaction/views/ir_ui_menu.xml @@ -6,7 +6,6 @@ - diff --git a/web_menu_navbar_needaction/views/templates.xml b/web_menu_navbar_needaction/views/templates.xml index 0f23eddd..542aa1b9 100644 --- a/web_menu_navbar_needaction/views/templates.xml +++ b/web_menu_navbar_needaction/views/templates.xml @@ -1,11 +1,9 @@ - - - - - + + +