diff --git a/base_import_security_group/README.rst b/base_import_security_group/README.rst index 15847ef1f..ecf0f43ae 100644 --- a/base_import_security_group/README.rst +++ b/base_import_security_group/README.rst @@ -1,7 +1,9 @@ .. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg - :alt: License: AGPL-3 + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 -Group-based permissions for importing CSV files +=============================================== +Group-based permissions for importing CSV files =============================================== This module makes importing data from CSV files optional for each user, @@ -10,9 +12,8 @@ Any other user not belonging to such group will not have the "Import" button available anywhere. The action will even be blocked internally (to prevent XMLRPC access, for example). - -Usage -===== +Configuration +============= To allow a user to import data from CSV files, just follow this steps: @@ -21,20 +22,20 @@ To allow a user to import data from CSV files, just follow this steps: * Within the "Access Rights" tab and "Technical Settings" group, check the option "Allow importing CSV files". +Usage +===== -For further information, please visit: - -- https://www.odoo.com/forum/help-1 - +.. 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/8.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 -`here `_. - +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 smash it by providing detailed and welcomed feedback. Credits ======= @@ -43,13 +44,20 @@ Contributors ------------ * Alejandro Santana +* Stefan Rijnhart +* Antonio Esposito + +Icon +---- + +Iconic fonts used in module icon are Font Awesome: http://fontawesome.io/ Maintainer ---------- -.. image:: http://odoo-community.org/logo.png +.. image:: https://odoo-community.org/logo.png :alt: Odoo Community Association - :target: http://odoo-community.org + :target: https://odoo-community.org This module is maintained by the OCA. @@ -57,9 +65,4 @@ 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. - -Icon ----- - -Iconic fonts used in module icon are Font Awesome: http://fontawesome.io/ +To contribute to this module, please visit https://odoo-community.org. diff --git a/base_import_security_group/__init__.py b/base_import_security_group/__init__.py index 5b9194b2a..7a98b5d49 100644 --- a/base_import_security_group/__init__.py +++ b/base_import_security_group/__init__.py @@ -2,4 +2,4 @@ # License and authorship info in: # __openerp__.py file at the root folder of this module. -from . import models +from .hooks import post_load diff --git a/base_import_security_group/__openerp__.py b/base_import_security_group/__openerp__.py index 45dd183b4..f3815468b 100644 --- a/base_import_security_group/__openerp__.py +++ b/base_import_security_group/__openerp__.py @@ -1,36 +1,17 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# Odoo, Open Source Management Solution -# -# Copyright (c) All rights reserved: -# (c) 2015 Anubía, soluciones en la nube,SL (http://www.anubia.es) -# Alejandro Santana -# -# 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 http://www.gnu.org/licenses -# -############################################################################## +# coding: utf-8 +# Copyright 2015 Anubía, soluciones en la nube,SL (http://www.anubia.es) +# Alejandro Santana +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). { 'name': 'Optional CSV import', - 'version': '1.0', + 'version': '8.0.1.0.0', 'category': 'Server tools', 'summary': 'Group-based permissions for importing CSV files', 'license': 'AGPL-3', 'author': 'Odoo Community Association (OCA), ' 'Alejandro Santana ', 'maintainer': 'Odoo Community Association (OCA)', - 'website': 'http://odoo-community.org', + 'website': 'https://github.com/OCA/server-tools', 'depends': [ 'base_import' ], @@ -41,7 +22,9 @@ 'js': [ 'static/src/js/import.js', ], + 'qweb': [ + 'static/src/xml/base_import_security_group.xml', + ], 'installable': True, - 'application': False, - 'auto_install': False, + 'post_load': 'post_load', } diff --git a/base_import_security_group/hooks.py b/base_import_security_group/hooks.py new file mode 100644 index 000000000..e30a152a3 --- /dev/null +++ b/base_import_security_group/hooks.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +# License and authorship info in: +# __openerp__.py file at the root folder of this module. + +from openerp import api +from openerp.models import BaseModel +import logging + +_logger = logging.getLogger(__name__) +base_load = BaseModel.load + + +@api.model +def load_import_optional(self, fields=None, data=None): + '''Overriding this method we only allow its execution if the current + user belongs to the group allowed for CSV data import. + An exception is raised otherwise, and the import attempt is logged. + This method is monkeypatched and will also affect other databases on the + same Odoo instance. Therefore, check if the group actually exist as + evidence that this module is installed. + ''' + group_ref = 'base_import_security_group.group_import_csv' + group = self.env.ref(group_ref, raise_if_not_found=False) + if not group or self.env.user.has_group(group_ref): + return base_load(self, fields=fields, data=data) + msg = 'User (ID: %s) is not allowed to import data in model %s.' % ( + self.env.uid, self._name) + _logger.info(msg) + return {'ids': False, 'messages': [{ + 'type': 'error', + 'message': msg, + 'moreinfo': False}]} + + +def post_load(): + BaseModel.load = load_import_optional diff --git a/base_import_security_group/i18n/base_import_security_group.pot b/base_import_security_group/i18n/base_import_security_group.pot deleted file mode 100644 index 9ace411c3..000000000 --- a/base_import_security_group/i18n/base_import_security_group.pot +++ /dev/null @@ -1,21 +0,0 @@ -# Translation of Odoo Server. -# This file contains the translation of the following modules: -# * base_import_security_group -# -msgid "" -msgstr "" -"Project-Id-Version: Odoo Server 8.0\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-08-06 18:37+0000\n" -"PO-Revision-Date: 2015-08-06 18:37+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_import_security_group -#: model:res.groups,name:base_import_security_group.group_import_csv -msgid "Import CSV files" -msgstr "" diff --git a/base_import_security_group/models/__init__.py b/base_import_security_group/models/__init__.py deleted file mode 100644 index 5b9194b2a..000000000 --- a/base_import_security_group/models/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# -*- coding: utf-8 -*- -# License and authorship info in: -# __openerp__.py file at the root folder of this module. - -from . import models diff --git a/base_import_security_group/models/models.py b/base_import_security_group/models/models.py deleted file mode 100644 index 8ebfa8b2e..000000000 --- a/base_import_security_group/models/models.py +++ /dev/null @@ -1,36 +0,0 @@ -# -*- coding: utf-8 -*- -# License and authorship info in: -# __openerp__.py file at the root folder of this module. - -from openerp import api -from openerp.models import BaseModel -import logging - -_logger = logging.getLogger(__name__) -base_load = BaseModel.load - - -@api.model -def load_import_optional(self, fields=None, data=None): - '''Overriding this method we only allow its execution - if current user belongs to the group allowed for CSV data import. - An exception is raised otherwise, and also log the import attempt. - ''' - res = {} - current_user = self.env['res.users'].browse(self.env.uid) - allowed_group = 'base_import_security_group.group_import_csv' - if current_user and current_user.has_group(allowed_group): - res = base_load(self, fields=fields, data=data) - else: - msg = ('User (ID: %s) is not allowed to import data ' - 'in model %s.') % (self.env.uid, self._name) - _logger.error(msg) - messages = [] - info = {} - messages.append(dict(info, type='error', message=msg, moreinfo=None)) - res = {'ids': None, 'messages': messages} - return res - -# Monkey patch function -# Because BaseModel _name = None -BaseModel.load = load_import_optional diff --git a/base_import_security_group/static/src/js/import.js b/base_import_security_group/static/src/js/import.js index eec119c1b..d0a9b7c4f 100644 --- a/base_import_security_group/static/src/js/import.js +++ b/base_import_security_group/static/src/js/import.js @@ -2,56 +2,15 @@ // __openerp__.py file at the root folder of this module. openerp.base_import_security_group = function (instance) { - var QWeb = instance.web.qweb; - var _t = instance.web._t; - var _lt = instance.web._lt; - - instance.web.ListView.prototype.defaults.import_enabled = false; - base_import_security_group = instance.web.ListView.include({ + instance.web.ListView.include({ load_list: function () { var self = this; - var Users = new openerp.web.Model('res.users'); - - self._super.apply(self, arguments); - - Users.call('has_group', ['base_import_security_group.group_import_csv']) - .then(function (result) { - var import_enabled = result; + this._super.apply(self, arguments); + new openerp.web.Model('res.users').call('has_group', ['base_import_security_group.group_import_csv']) + .then(function (import_enabled) { self.options.import_enabled = import_enabled; - if (import_enabled) { - if (self.$buttons) { - self.$buttons.remove(); - } - self.$buttons = $(QWeb.render("ListView.buttons", {'widget': self})); - if (self.options.$buttons) { - self.$buttons.appendTo(self.options.$buttons); - } else { - self.$el.find('.oe_list_buttons').replaceWith(self.$buttons); - } - self.$buttons.find('.oe_list_add') - .click(self.proxy('do_add_record')) - .prop('disabled', self.grouped); - - self.$buttons.on('click', '.oe_list_button_import', function () { - self.do_action({ - type: 'ir.actions.client', - tag: 'import', - params: { - model: self.dataset.model, - // self.dataset.get_context() could be a compound? - // not sure. action's context should be evaluated - // so safer bet. Odd that timezone & al in it - // though - context: self.getParent().action.context - } - }, { - on_reverse_breadcrumb: function () { - self.reload(); - } - }); - return false; - }); + self.$buttons.find('span.oe_alternative').show(); } }); } diff --git a/base_import_security_group/static/src/xml/base_import_security_group.xml b/base_import_security_group/static/src/xml/base_import_security_group.xml new file mode 100644 index 000000000..a7f09b34d --- /dev/null +++ b/base_import_security_group/static/src/xml/base_import_security_group.xml @@ -0,0 +1,8 @@ + + + + + this.attr('style', 'display: none;'); + + + diff --git a/base_import_security_group/tests/__init__.py b/base_import_security_group/tests/__init__.py new file mode 100644 index 000000000..7d697d0bf --- /dev/null +++ b/base_import_security_group/tests/__init__.py @@ -0,0 +1 @@ +from . import test_base_import_security_group diff --git a/base_import_security_group/tests/test_base_import_security_group.py b/base_import_security_group/tests/test_base_import_security_group.py new file mode 100644 index 000000000..f2383f8c2 --- /dev/null +++ b/base_import_security_group/tests/test_base_import_security_group.py @@ -0,0 +1,62 @@ +# coding: utf-8 +# Copyright 2017 Stefan Rijnhart +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from openerp import tests +from openerp.tools import mute_logger + + +@tests.common.at_install(False) +@tests.common.post_install(True) +class TestImportGroup(tests.HttpCase): + def setUp(self): + super(TestImportGroup, self).setUp() + self.group_ref = 'base_import_security_group.group_import_csv' + self.group = self.env.ref(self.group_ref) + + def has_button_import(self, falsify=False): + """ + Verify that the button is either visible or invisible. + After the adjacent button is loaded, allow for a second for + the asynchronous call to finish and update the visibility """ + code = """ + window.setTimeout(function () { + if (%s$('.oe_list_button_import').is(':visible')) { + console.log('ok'); + } else { + console.log('error'); + }; + }, 1000); + """ % ('!' if falsify else '') + link = '/web#action=%s' % self.env.ref('base.action_res_users').id + with mute_logger('openerp.addons.base.res.res_users'): + # Mute debug log about failing row lock upon user login + self.phantom_js( + link, code, "$('button.oe_list_add').length", + login=self.env.user.login) + + def load(self): + self.env['res.partner'].load( + ['name'], [['test_base_import_security_group']]) + return self.env['res.partner'].search( + [('name', '=', 'test_base_import_security_group')]) + + def test_01_in_group(self): + """ Show import button to users in the import group """ + self.env.user.groups_id += self.group + self.assertTrue(self.env.user.has_group(self.group_ref)) + self.has_button_import() + self.assertTrue(self.load()) + + def test_02_not_in_group(self): + """ Don't show import button to users not in the import group """ + self.env.user.groups_id -= self.group + self.assertFalse(self.env.user.has_group(self.group_ref)) + self.has_button_import(falsify=True) + self.assertFalse(self.load()) + + def test_03_no_group(self): + """ When the group does not exist, allow import (monkeypatch to assume + that this module is not installed in that case). """ + self.group.unlink() + self.assertFalse(self.env.user.has_group(self.group_ref)) + self.assertTrue(self.load())