[IMP] base: allow restricting / removing exports right
Add a new group allowing administrators to remove the ability for users to bulk-export data from the database. It's pretty minor as technically the user can still access the underlying object through the basic methods but it's still a bit of a roadblock. Users can export by default. Task 2170900 closes odoo/odoo#45400 Signed-off-by: Xavier Morel (xmo) <xmo@odoo.com>
This commit is contained in:
committed by
Xavier Morel
parent
2711129809
commit
c248594ee5
@@ -22,7 +22,7 @@ class XlsxCreatorCase(common.HttpCase):
|
||||
super().setUp()
|
||||
self.model = self.env[self.model_name]
|
||||
|
||||
mail_new_test_user(self.env, login='fof', password='123456789')
|
||||
u = mail_new_test_user(self.env, login='fof', password='123456789', groups='base.group_user,base.group_allow_export')
|
||||
self.authenticate('fof', '123456789')
|
||||
|
||||
self.worksheet = {} # mock worksheet
|
||||
|
||||
@@ -58,6 +58,9 @@ var ListController = BasicController.extend({
|
||||
this.fieldChangedPrevented = false;
|
||||
this.isPageSelected = false; // true iff all records of the page are selected
|
||||
this.isDomainSelected = false; // true iff the user selected all records matching the domain
|
||||
session.user_has_group('base.group_allow_export').then(has_group => {
|
||||
this.isExportEnable = has_group;
|
||||
});
|
||||
Object.defineProperty(this, 'mode', {
|
||||
get: () => this.renderer.isEditable() ? 'edit' : 'readonly',
|
||||
set: () => {},
|
||||
@@ -372,10 +375,13 @@ var ListController = BasicController.extend({
|
||||
return null;
|
||||
}
|
||||
const props = this._super(...arguments);
|
||||
const otherActionItems = [{
|
||||
description: _t("Export"),
|
||||
callback: () => this._onExportData(),
|
||||
}];
|
||||
const otherActionItems = [];
|
||||
if (this.isExportEnable) {
|
||||
otherActionItems.push({
|
||||
description: _t("Export"),
|
||||
callback: () => this._onExportData()
|
||||
});
|
||||
}
|
||||
if (this.archiveEnabled) {
|
||||
otherActionItems.push({
|
||||
description: _t("Archive"),
|
||||
|
||||
@@ -177,7 +177,7 @@ QUnit.module('Views', {
|
||||
});
|
||||
|
||||
QUnit.test('list with delete="0"', async function (assert) {
|
||||
assert.expect(4);
|
||||
assert.expect(3);
|
||||
|
||||
const list = await createView({
|
||||
View: ListView,
|
||||
@@ -192,13 +192,7 @@ QUnit.module('Views', {
|
||||
assert.ok(list.$('tbody td.o_list_record_selector').length, 'should have at least one record');
|
||||
|
||||
await testUtils.dom.click(list.$('tbody td.o_list_record_selector:first input'));
|
||||
assert.containsOnce(list.el, 'div.o_control_panel .o_cp_action_menus');
|
||||
await cpHelpers.toggleActionMenu(list);
|
||||
assert.deepEqual(
|
||||
cpHelpers.getMenuItemTexts(list),
|
||||
['Export'],
|
||||
'action menu should not have Delete button'
|
||||
);
|
||||
assert.containsNone(list.el, 'div.o_control_panel .o_cp_action_menus .o_dropdown_menu');
|
||||
|
||||
list.destroy();
|
||||
});
|
||||
@@ -222,6 +216,40 @@ QUnit.module('Views', {
|
||||
list.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('list with export button', async function (assert) {
|
||||
assert.expect(4);
|
||||
|
||||
const list = await createView({
|
||||
View: ListView,
|
||||
model: 'foo',
|
||||
data: this.data,
|
||||
viewOptions: {hasActionMenus: true},
|
||||
arch: '<tree><field name="foo"/></tree>',
|
||||
session: {
|
||||
async user_has_group(group) {
|
||||
if (group === 'base.group_allow_export') {
|
||||
return true;
|
||||
}
|
||||
return this._super(...arguments);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
assert.containsNone(list.el, 'div.o_control_panel .o_cp_action_menus');
|
||||
assert.ok(list.$('tbody td.o_list_record_selector').length, 'should have at least one record');
|
||||
|
||||
await testUtils.dom.click(list.$('tbody td.o_list_record_selector:first input'));
|
||||
assert.containsOnce(list.el, 'div.o_control_panel .o_cp_action_menus');
|
||||
await cpHelpers.toggleActionMenu(list);
|
||||
assert.deepEqual(
|
||||
cpHelpers.getMenuItemTexts(list),
|
||||
['Export', 'Delete'],
|
||||
'action menu should have Export button'
|
||||
);
|
||||
|
||||
list.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('simple editable rendering', async function (assert) {
|
||||
assert.expect(15);
|
||||
|
||||
@@ -4538,7 +4566,7 @@ QUnit.module('Views', {
|
||||
await testUtils.dom.click(list.$('.o_list_record_selector:first input'));
|
||||
|
||||
await cpHelpers.toggleActionMenu(list);
|
||||
assert.deepEqual(cpHelpers.getMenuItemTexts(list), ['Export', 'Delete', 'Action event']);
|
||||
assert.deepEqual(cpHelpers.getMenuItemTexts(list), ['Delete', 'Action event']);
|
||||
|
||||
list.destroy();
|
||||
});
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
<field name="password">demo</field>
|
||||
<field name="signature" type="xml"><span>-- <br/>+Mr Demo</span></field>
|
||||
<field name="company_id" ref="main_company"/>
|
||||
<field name="groups_id" eval="[(6,0,[ref('base.group_user'), ref('base.group_partner_manager')])]"/>
|
||||
<field name="groups_id" eval="[(6,0,[ref('base.group_user'), ref('base.group_partner_manager'), ref('base.group_allow_export')])]"/>
|
||||
<field name="image_1920" type="base64" file="base/static/img/user_demo-image.jpg"/>
|
||||
</record>
|
||||
|
||||
|
||||
@@ -36,6 +36,11 @@
|
||||
<record model="res.groups" id="group_no_one">
|
||||
<field name="name">Technical Features</field>
|
||||
</record>
|
||||
<record id="group_allow_export" model="res.groups">
|
||||
<field name="name">Access to export feature</field>
|
||||
<field name="category_id" ref="base.module_category_hidden"/>
|
||||
<field name="users" eval="[(4, ref('base.user_root')), (4, ref('base.user_admin'))]"/>
|
||||
</record>
|
||||
<record model="res.groups" id="group_user">
|
||||
<field name="implied_ids" eval="[(4, ref('group_no_one'))]"/>
|
||||
<field name="users" eval="[(4, ref('base.user_root')), (4, ref('base.user_admin'))]"/>
|
||||
@@ -47,7 +52,7 @@
|
||||
</record>
|
||||
|
||||
<record id="default_user" model="res.users">
|
||||
<field name="groups_id" eval="[(4,ref('base.group_partner_manager'))]"/>
|
||||
<field name="groups_id" eval="[(4, ref('base.group_partner_manager')), (4, ref('base.group_allow_export'))]"/>
|
||||
</record>
|
||||
|
||||
<!--
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"access_ir_attachment_group_user","ir_attachment group_user","model_ir_attachment","group_user",1,1,1,1
|
||||
"access_ir_attachment_group_portal_public","ir_attachment group_portal_public","model_ir_attachment",,0,0,0,0
|
||||
"access_ir_cron_group_cron","ir_cron group_cron","model_ir_cron","group_system",1,1,1,1
|
||||
"access_ir_exports_group_system","ir_exports group_system","model_ir_exports","base.group_user",1,1,1,1
|
||||
"access_ir_exports_group_system","ir_exports group_system","model_ir_exports","base.group_allow_export",1,1,1,1
|
||||
"access_ir_exports_line_group_system","ir_exports_line group_system","model_ir_exports_line","base.group_user",1,1,1,1
|
||||
"access_ir_model_group_erp_manager","ir_model group_erp_manager","model_ir_model","group_erp_manager",1,1,1,1
|
||||
"access_ir_model_constraint_group_erp_manager","ir_model_constraint group_erp_manager","model_ir_model_constraint","group_erp_manager",1,1,1,1
|
||||
|
||||
|
@@ -912,6 +912,8 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})):
|
||||
|
||||
This method is used when exporting data via client menu
|
||||
"""
|
||||
if not (self.env.user._is_admin() or self.env.user.has_group('base.group_allow_export')):
|
||||
raise UserError(_("You don't have the rights to export data. Please contact an Administrator."))
|
||||
fields_to_export = [fix_import_export_id_paths(f) for f in fields_to_export]
|
||||
return {'datas': self._export_rows(fields_to_export)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user