diff --git a/base_technical_features/README.rst b/base_technical_features/README.rst new file mode 100644 index 0000000..a10fb8e --- /dev/null +++ b/base_technical_features/README.rst @@ -0,0 +1,77 @@ +.. 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 + +========================================================== +Access to technical features without activating debug mode +========================================================== + +In Odoo 9.0, the debug mode grants every employee user access to the technical +features. This module enables persistent access to technical features based on +user preference. + +Configuration +============= + +After installation of this module, every employee can still access technical +features for the applications that they have access to by enabling debug mode. +Additionally, users can check the *Technical feature* field in their +preferences to gain permanent access to the menus and views that fall under +this category. + +.. figure:: static/description/user_preferences.png + :alt: User preferences + +Upon installation of this module, this preference is already +set for the administrator user of the database. + +In the background, this preference is mapped to the *Technical feature (w/o +debug mode)* group that this module adds. As an administrator, you can +therefore manage this preference from the regular Users and Groups menu items. + +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/149/9.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 +`_. + +Credits +======= + +Images +------ + +* Odoo Community Association: `Icon `_. + +Contributors +------------ + +* Stefan Rijnhart + +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 https://odoo-community.org. diff --git a/base_technical_features/__init__.py b/base_technical_features/__init__.py new file mode 100644 index 0000000..0650744 --- /dev/null +++ b/base_technical_features/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/base_technical_features/__openerp__.py b/base_technical_features/__openerp__.py new file mode 100644 index 0000000..14383e9 --- /dev/null +++ b/base_technical_features/__openerp__.py @@ -0,0 +1,18 @@ +# coding: utf-8 +# © 2016 Opener B.V. () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +{ + "name": "Technical features group", + "summary": "Access to technical features without activating debug mode", + "version": "9.0.1.0.0", + "category": "Usability", + "website": "https://github.com/oca/server-tools", + "author": "Opener B.V., Odoo Community Association (OCA)", + "data": [ + 'security/res_groups.xml', + 'views/res_users.xml', + 'data/res_users.xml', + ], + "license": "AGPL-3", + "installable": True, +} diff --git a/base_technical_features/data/res_users.xml b/base_technical_features/data/res_users.xml new file mode 100644 index 0000000..e9f87e5 --- /dev/null +++ b/base_technical_features/data/res_users.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/base_technical_features/i18n/nl.po b/base_technical_features/i18n/nl.po new file mode 100644 index 0000000..8d7ef8f --- /dev/null +++ b/base_technical_features/i18n/nl.po @@ -0,0 +1,42 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_technical_features +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 9.0c\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2016-01-03 16:11+0000\n" +"PO-Revision-Date: 2016-01-03 16:11+0000\n" +"Last-Translator: <>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users_show_technical_features +msgid "Show field Technical Features" +msgstr "Toon veld Technische mogelijkheden" + +#. module: base_technical_features +#: model:res.groups,name:base_technical_features.group_technical_features +msgid "Technical Features (w/o debug mode)" +msgstr "Technische mogelijkheden (zonder debugmodus)" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users_technical_features +msgid "Technical features" +msgstr "Technische mogelijkheden" + +#. module: base_technical_features +#: code:addons/base_technical_features/models/res_users.py:45 +#, python-format +msgid "The user does not have access to technical features." +msgstr "De gebruiker heeft geen toegang tot technische mogelijkheden." + +#. module: base_technical_features +#: model:ir.model.fields,help:base_technical_features.field_res_users_show_technical_features +msgid "Whether to display the technical features field in the user preferences." +msgstr "Geeft aan of het veld Technische mogelijkheden wordt getoond in de voorkeuren van de gebruiker." diff --git a/base_technical_features/models/__init__.py b/base_technical_features/models/__init__.py new file mode 100644 index 0000000..b26f7f4 --- /dev/null +++ b/base_technical_features/models/__init__.py @@ -0,0 +1,3 @@ +from . import basemodel_monkeypatch +from . import ir_ui_menu +from . import res_users diff --git a/base_technical_features/models/basemodel_monkeypatch.py b/base_technical_features/models/basemodel_monkeypatch.py new file mode 100644 index 0000000..0652a12 --- /dev/null +++ b/base_technical_features/models/basemodel_monkeypatch.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +# © 2016 Opener B.V. () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from openerp import models, api + + +class BaseModelMonkeyPatch(models.AbstractModel): + _name = 'basemodel.monkeypatch' + + def _register_hook(self, cr): + if not hasattr( + models.BaseModel, 'base_technical_features_user_has_groups'): + + models.BaseModel.base_technical_features_user_has_groups = ( + models.BaseModel.user_has_groups) + + @api.cr_uid_context + def user_has_groups(self, cr, uid, groups, context=None): + """ Return True for users in the technical features group when + membership of the original group is checked, even if debug mode + is not enabled. + """ + if ('base.group_no_one' in groups.split(',') and + self.pool['res.users'].has_group( + cr, uid, + 'base_technical_features.group_technical_features')): + return True + return self.base_technical_features_user_has_groups( + cr, uid, groups, context=context) + + models.BaseModel.user_has_groups = user_has_groups + + return super(BaseModelMonkeyPatch, self)._register_hook(cr) diff --git a/base_technical_features/models/ir_ui_menu.py b/base_technical_features/models/ir_ui_menu.py new file mode 100644 index 0000000..112fe39 --- /dev/null +++ b/base_technical_features/models/ir_ui_menu.py @@ -0,0 +1,18 @@ +# coding: utf-8 +# © 2016 Opener B.V. () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from openerp import api, models + + +class IrUiMenu(models.Model): + _inherit = 'ir.ui.menu' + + @api.model + def _visible_menu_ids(self, debug=False): + """ Set debug = True, so that group_no_one is not filtered out of the + user's groups """ + if not debug: + debug = self.pool['res.users'].has_group( + self.env.cr, self.env.uid, + 'base_technical_features.group_technical_features') + return super(IrUiMenu, self)._visible_menu_ids(debug=debug) diff --git a/base_technical_features/models/res_users.py b/base_technical_features/models/res_users.py new file mode 100644 index 0000000..824caef --- /dev/null +++ b/base_technical_features/models/res_users.py @@ -0,0 +1,59 @@ +# coding: utf-8 +# © 2016 Opener B.V. () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from openerp import api, fields, models +from openerp.exceptions import AccessError +from openerp.tools.translate import _ + + +class ResUsers(models.Model): + _inherit = 'res.users' + + technical_features = fields.Boolean( + compute='get_technical_features', + inverse='set_technical_features') + show_technical_features = fields.Boolean( + string='Show field Technical Features', + compute='get_show_technical_features', + help=('Whether to display the technical features field in the user ' + 'preferences.')) + + @api.multi + @api.depends('groups_id') + def get_show_technical_features(self): + """ Only display the technical features checkbox in the user + preferences if the user has access to them """ + users = self.env.ref('base.group_no_one').users + for user in self: + user.show_technical_features = user in users + + @api.multi + @api.depends('groups_id') + def get_technical_features(self): + """ Map user membership to boolean field value """ + users = self.env.ref( + 'base_technical_features.group_technical_features').users + for user in self: + user.technical_features = user in users + + @api.multi + def set_technical_features(self): + """ Map boolean field value to group membership, but checking + access """ + group = self.env.ref( + 'base_technical_features.group_technical_features') + for user in self: + if self.env.ref('base.group_no_one') not in user.groups_id: + raise AccessError( + _('The user does not have access to technical ' + 'features.')) + if user.technical_features: + self.sudo().write({'groups_id': [(4, group.id)]}) + else: + self.sudo().write({'groups_id': [(3, group.id)]}) + + def __init__(self, pool, cr): + super(ResUsers, self).__init__(pool, cr) + self.SELF_READABLE_FIELDS += [ + 'technical_features', 'show_technical_features'] + self.SELF_WRITEABLE_FIELDS.append('technical_features') diff --git a/base_technical_features/security/res_groups.xml b/base_technical_features/security/res_groups.xml new file mode 100644 index 0000000..12846ec --- /dev/null +++ b/base_technical_features/security/res_groups.xml @@ -0,0 +1,7 @@ + + + + Technical Features (w/o debug mode) + + + diff --git a/base_technical_features/static/description/icon.png b/base_technical_features/static/description/icon.png new file mode 100644 index 0000000..3a0328b Binary files /dev/null and b/base_technical_features/static/description/icon.png differ diff --git a/base_technical_features/static/description/user_preferences.png b/base_technical_features/static/description/user_preferences.png new file mode 100644 index 0000000..c30837e Binary files /dev/null and b/base_technical_features/static/description/user_preferences.png differ diff --git a/base_technical_features/tests/__init__.py b/base_technical_features/tests/__init__.py new file mode 100644 index 0000000..f52b4eb --- /dev/null +++ b/base_technical_features/tests/__init__.py @@ -0,0 +1 @@ +from . import test_base_technical_features diff --git a/base_technical_features/tests/test_base_technical_features.py b/base_technical_features/tests/test_base_technical_features.py new file mode 100644 index 0000000..3e9ac03 --- /dev/null +++ b/base_technical_features/tests/test_base_technical_features.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- +from openerp import api +from openerp.exceptions import AccessError +from openerp.tests import common +from lxml import etree + + +class TestBaseTechnicalFeatures(common.TransactionCase): + + def test_01_visible_menus(self): + """ A technical feature is visible to the user with the technical \ + features group """ + menu_obj = self.env['ir.ui.menu'].with_context( + {'ir.ui.menu.full_list': True}) + menu_id = menu_obj.search( + [('groups_id', '=', self.env.ref('base.group_no_one').id)], + limit=1).id + self.env.user.write({'technical_features': False}) + self.assertNotIn(menu_id, menu_obj._visible_menu_ids()) + self.env.user.write({'technical_features': True}) + self.assertIn(menu_id, menu_obj._visible_menu_ids()) + + def test02_visible_fields(self): + """ A technical field is visible when its form is loaded by a user \ + with the technical features group """ + + def get_partner_field_invisible(): + xml = etree.fromstring( + self.env['res.users'].fields_view_get( + view_id=self.env.ref('base.view_users_form').id + )['arch'].encode('utf-8')) + return xml.xpath( + '//div/group/field[@name="partner_id"]')[0].get('invisible') + + self.env['basemodel.monkeypatch']._register_hook() + self.env.user.write({'technical_features': False}) + self.assertEqual(get_partner_field_invisible(), '1') + self.env.user.write({'technical_features': True}) + self.assertEqual(get_partner_field_invisible(), None) + + def test03_user_access(self): + """ Setting the user pref raises an access error if the user is not \ + in group_no_one """ + user = self.env['res.users'].create({ + 'name': 'Test user technical features', + 'login': 'testusertechnicalfeatures', + 'groups_id': [(6, 0, [])]}) + with api.Environment.manage(): + env = api.Environment( + self.env.cr, user.id, self.env.context) + with self.assertRaises(AccessError): + env['res.users'].browse(user.id).write( + {'technical_features': True}) + with self.assertRaises(AccessError): + user.write({'technical_features': True}) + user.write({'groups_id': [(4, self.env.ref('base.group_no_one').id)]}) + with api.Environment.manage(): + env = api.Environment( + self.env.cr, user.id, self.env.context) + env['res.users'].browse(user.id).write({ + 'technical_features': True}) diff --git a/base_technical_features/views/res_users.xml b/base_technical_features/views/res_users.xml new file mode 100644 index 0000000..3fabefd --- /dev/null +++ b/base_technical_features/views/res_users.xml @@ -0,0 +1,16 @@ + + + + Add technical features checkbox to user preferences form + res.users + + + + + + + + + +