diff --git a/addons/portal_project/__init__.py b/addons/portal_project/__init__.py index 26c654db9dd..164c7826e47 100644 --- a/addons/portal_project/__init__.py +++ b/addons/portal_project/__init__.py @@ -19,3 +19,4 @@ # ############################################################################## +import project diff --git a/addons/portal_project/project.py b/addons/portal_project/project.py new file mode 100644 index 00000000000..fb783c8a531 --- /dev/null +++ b/addons/portal_project/project.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2013-TODAY OpenERP S.A (). +# +# 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.osv import osv + + +class portal_project(osv.Model): + """ Update of mail_mail class, to add the signin URL to notifications. """ + _inherit = 'project.project' + + def _get_visibility_selection(self, cr, uid, context=None): + """ Override to add portal option. """ + selection = super(portal_project, self)._get_visibility_selection(cr, uid, context=context) + idx = [item[0] for item in selection].index('public') + selection.insert((idx + 1), ('portal', 'Portal Users and Employees')) + return selection + # return [('public', 'All Users'), + # ('portal', 'Portal Users and Employees'), + # ('employees', 'Employees Only'), + # ('followers', 'Followers Only')] diff --git a/addons/portal_project/security/ir.model.access.csv b/addons/portal_project/security/ir.model.access.csv index 0e743050568..a999f5555d9 100644 --- a/addons/portal_project/security/ir.model.access.csv +++ b/addons/portal_project/security/ir.model.access.csv @@ -5,3 +5,9 @@ access_task_type,task_type,project.model_project_task_type,portal.group_portal,1 access_task_work,task_work,project.model_project_task_work,portal.group_portal,1,0,0,0 access_project_category,project_category,project.model_project_category,portal.group_portal,1,0,0,0 access_account_analytic_account,account_analytic_account,analytic.model_account_analytic_account,portal.group_portal,1,0,0,0 +access_project_anonymous,project,project.model_project_project,portal.group_anonymous,1,0,0,0 +access_task_anonymous,task,project.model_project_task,portal.group_anonymous,1,0,0,0 +access_task_type_anonymous,task_type,project.model_project_task_type,portal.group_anonymous,1,0,0,0 +access_task_work_anonymous,task_work,project.model_project_task_work,portal.group_anonymous,1,0,0,0 +access_project_category_anonymous,project_category,project.model_project_category,portal.group_anonymous,1,0,0,0 +access_account_analytic_account_anonymous,account_analytic_account,analytic.model_account_analytic_account,portal.group_anonymous,1,0,0,0 \ No newline at end of file diff --git a/addons/portal_project/security/portal_security.xml b/addons/portal_project/security/portal_security.xml index 0d995cfd70b..8d58ed70aeb 100644 --- a/addons/portal_project/security/portal_security.xml +++ b/addons/portal_project/security/portal_security.xml @@ -2,19 +2,68 @@ + + Project: employees: public, portal, employees or following + ['|', + ('privacy_visibility', 'in', ['public', 'portal', 'employees']), + '&', + ('privacy_visibility', '=', 'followers'), + ('message_follower_ids', 'in', [user.partner_id.id]), + ] + + + - Portal Projects - - ['|', ('privacy_visibility', '=', 'public'), ('message_follower_ids', 'in', [user.partner_id.id])] + Project: portal users: public, portal or following + + ['|', + ('privacy_visibility', 'in', ['public', 'portal']), + '&', + ('privacy_visibility', '=', 'followers'), + ('message_follower_ids', 'in', [user.partner_id.id]), + ] + + Project: anonymous users: public only + + [('privacy_visibility', '=', 'public')] + + + + + Project/Task: employees: public, portal, employee or following or assigned + ['|', + ('user_id', '=', user.id), + '|', + ('project_id.privacy_visibility', 'in', ['public', 'portal', 'employees']), + '&', + ('project_id.privacy_visibility', '=', 'followers'), + ('message_follower_ids', 'in', [user.partner_id.id]), + ] + + - Portal Tasks - - [('message_follower_ids','in', [user.partner_id.id])] + Project/Task: portal users: public or portal and following + + ['|', + ('project_id.privacy_visibility', '=', 'public'), + '&', + ('project_id.privacy_visibility', 'in', ['portal', 'followers']), + '|', + ('message_follower_ids','in', [user.partner_id.id]), + ('user_id', '=', user.id), + ] + + Project/Task: anonymous users: public only + + [('project_id.privacy_visibility', '=', 'public')] + + + diff --git a/addons/portal_project/tests/__init__.py b/addons/portal_project/tests/__init__.py new file mode 100644 index 00000000000..c79181f7710 --- /dev/null +++ b/addons/portal_project/tests/__init__.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Business Applications +# Copyright (c) 2012-TODAY OpenERP S.A. +# +# 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 . import test_access_rights + +checks = [ + test_access_rights, +] + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/portal_project/tests/test_access_rights.py b/addons/portal_project/tests/test_access_rights.py new file mode 100644 index 00000000000..5e177283faa --- /dev/null +++ b/addons/portal_project/tests/test_access_rights.py @@ -0,0 +1,300 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Business Applications +# Copyright (c) 2013-TODAY OpenERP S.A. +# +# 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.osv.orm import except_orm +from openerp.tests import common +from openerp.tools import mute_logger + + +class TestPortalProject(common.TransactionCase): + + def setUp(self): + super(TestPortalProject, self).setUp() + cr, uid = self.cr, self.uid + + # Useful models + self.project_project = self.registry('project.project') + self.project_task = self.registry('project.task') + self.res_users = self.registry('res.users') + self.res_partner = self.registry('res.partner') + + # Find Employee group + group_employee_ref = self.registry('ir.model.data').get_object_reference(cr, uid, 'base', 'group_user') + self.group_employee_id = group_employee_ref and group_employee_ref[1] or False + + # Find Project User group + group_project_user_ref = self.registry('ir.model.data').get_object_reference(cr, uid, 'project', 'group_project_user') + self.group_project_user_id = group_project_user_ref and group_project_user_ref[1] or False + + # Find Project Manager group + group_project_manager_ref = self.registry('ir.model.data').get_object_reference(cr, uid, 'project', 'group_project_manager') + self.group_project_manager_id = group_project_manager_ref and group_project_manager_ref[1] or False + + # Find Portal group + group_portal_ref = self.registry('ir.model.data').get_object_reference(cr, uid, 'portal', 'group_portal') + self.group_portal_id = group_portal_ref and group_portal_ref[1] or False + + # Find Anonymous group + group_anonymous_ref = self.registry('ir.model.data').get_object_reference(cr, uid, 'portal', 'group_anonymous') + self.group_anonymous_id = group_anonymous_ref and group_anonymous_ref[1] or False + + # Test users to use through the various tests + self.user_alfred_id = self.res_users.create(cr, uid, { + 'name': 'Alfred EmployeeUser', + 'login': 'alfred', + 'alias_name': 'alfred', + 'groups_id': [(6, 0, [self.group_employee_id, self.group_project_user_id])] + }) + self.user_bert_id = self.res_users.create(cr, uid, { + 'name': 'Bert Nobody', + 'login': 'bert', + 'alias_name': 'bert', + 'groups_id': [(6, 0, [])] + }) + self.user_chell_id = self.res_users.create(cr, uid, { + 'name': 'Chell Portal', + 'login': 'chell', + 'alias_name': 'chell', + 'groups_id': [(6, 0, [self.group_portal_id])] + }) + self.user_donovan_id = self.res_users.create(cr, uid, { + 'name': 'Donovan Anonymous', + 'login': 'donovan', + 'alias_name': 'donovan', + 'groups_id': [(6, 0, [self.group_anonymous_id])] + }) + self.user_ernest_id = self.res_users.create(cr, uid, { + 'name': 'Ernest Manager', + 'login': 'ernest', + 'alias_name': 'ernest', + 'groups_id': [(6, 0, [self.group_project_manager_id])] + }) + + # Test 'Pigs' project + self.project_pigs_id = self.project_project.create(cr, uid, + {'name': 'Pigs', 'alias_contact': 'everyone', 'privacy_visibility': 'public'}, + {'mail_create_nolog': True}) + # Various test tasks + self.task_1_id = self.project_task.create(cr, uid, + {'name': 'Test1', 'user_id': False, 'project_id': self.project_pigs_id}, + {'mail_create_nolog': True}) + self.task_2_id = self.project_task.create(cr, uid, + {'name': 'Test2', 'user_id': False, 'project_id': self.project_pigs_id}, + {'mail_create_nolog': True}) + self.task_3_id = self.project_task.create(cr, uid, + {'name': 'Test3', 'user_id': False, 'project_id': self.project_pigs_id}, + {'mail_create_nolog': True}) + self.task_4_id = self.project_task.create(cr, uid, + {'name': 'Test4', 'user_id': self.user_alfred_id, 'project_id': self.project_pigs_id}, + {'mail_create_nolog': True}) + self.task_5_id = self.project_task.create(cr, uid, + {'name': 'Test5', 'user_id': self.user_chell_id, 'project_id': self.project_pigs_id}, + {'mail_create_nolog': True}) + self.task_6_id = self.project_task.create(cr, uid, + {'name': 'Test6', 'user_id': self.user_donovan_id, 'project_id': self.project_pigs_id}, + {'mail_create_nolog': True}) + + @mute_logger('openerp.addons.base.ir.ir_model', 'openerp.osv.orm') + def test_00_project_access_rights(self): + """ Test basic project access rights, for project and portal_project """ + cr, uid, pigs_id = self.cr, self.uid, self.project_pigs_id + + # ---------------------------------------- + # CASE1: public project + # ---------------------------------------- + + # Do: Alfred reads project -> ok (employee ok public) + self.project_project.read(cr, self.user_alfred_id, pigs_id, ['name']) + # Test: all project tasks visible + task_ids = self.project_task.search(cr, self.user_alfred_id, [('project_id', '=', pigs_id)]) + test_task_ids = set([self.task_1_id, self.task_2_id, self.task_3_id, self.task_4_id, self.task_5_id, self.task_6_id]) + self.assertEqual(set(task_ids), test_task_ids, + 'access rights: project user cannot see all tasks of a public project') + # Test: all project tasks readable + self.project_task.read(cr, self.user_alfred_id, task_ids, ['name']) + # Test: all project tasks writable + self.project_task.write(cr, self.user_alfred_id, task_ids, {'description': 'TestDescription'}) + + # Do: Bert reads project -> crash, no group + self.assertRaises(except_orm, self.project_project.read, + cr, self.user_bert_id, pigs_id, ['name']) + # Test: no project task visible + self.assertRaises(except_orm, self.project_task.search, + cr, self.user_bert_id, [('project_id', '=', pigs_id)]) + # Test: no project task readable + self.assertRaises(except_orm, self.project_task.read, + cr, self.user_bert_id, task_ids, ['name']) + # Test: no project task writable + self.assertRaises(except_orm, self.project_task.write, + cr, self.user_bert_id, task_ids, {'description': 'TestDescription'}) + + # Do: Chell reads project -> ok (portal ok public) + self.project_project.read(cr, self.user_chell_id, pigs_id, ['name']) + # Test: all project tasks visible + task_ids = self.project_task.search(cr, self.user_chell_id, [('project_id', '=', pigs_id)]) + self.assertEqual(set(task_ids), test_task_ids, + 'access rights: project user cannot see all tasks of a public project') + # Test: all project tasks readable + self.project_task.read(cr, self.user_chell_id, task_ids, ['name']) + # Test: no project task writable + self.assertRaises(except_orm, self.project_task.write, + cr, self.user_chell_id, task_ids, {'description': 'TestDescription'}) + + # Do: Donovan reads project -> ok (anonymous ok public) + self.project_project.read(cr, self.user_donovan_id, pigs_id, ['name']) + # Test: all project tasks visible + task_ids = self.project_task.search(cr, self.user_donovan_id, [('project_id', '=', pigs_id)]) + self.assertEqual(set(task_ids), test_task_ids, + 'access rights: anonymous user cannot see all tasks of a public project') + # Test: all project tasks readable + self.project_task.read(cr, self.user_donovan_id, task_ids, ['name']) + # Test: no project task writable + self.assertRaises(except_orm, self.project_task.write, + cr, self.user_donovan_id, task_ids, {'description': 'TestDescription'}) + + # ---------------------------------------- + # CASE2: portal project + # ---------------------------------------- + self.project_project.write(cr, uid, [pigs_id], {'privacy_visibility': 'portal'}) + + # Do: Alfred reads project -> ok (employee ok public) + self.project_project.read(cr, self.user_alfred_id, pigs_id, ['name']) + # Test: all project tasks visible + task_ids = self.project_task.search(cr, self.user_alfred_id, [('project_id', '=', pigs_id)]) + self.assertEqual(set(task_ids), test_task_ids, + 'access rights: project user cannot see all tasks of a portal project') + + # Do: Bert reads project -> crash, no group + self.assertRaises(except_orm, self.project_project.read, + cr, self.user_bert_id, pigs_id, ['name']) + # Test: no project task searchable + self.assertRaises(except_orm, self.project_task.search, + cr, self.user_bert_id, [('project_id', '=', pigs_id)]) + + # Data: task follower + self.project_task.message_subscribe_users(cr, self.user_alfred_id, [self.task_1_id, self.task_3_id], [self.user_chell_id]) + + # Do: Chell reads project -> ok (portal ok public) + self.project_project.read(cr, self.user_chell_id, pigs_id, ['name']) + # Test: only followed project tasks visible + assigned + task_ids = self.project_task.search(cr, self.user_chell_id, [('project_id', '=', pigs_id)]) + test_task_ids = set([self.task_1_id, self.task_3_id, self.task_5_id]) + self.assertEqual(set(task_ids), test_task_ids, + 'access rights: portal user should see the followed tasks of a portal project') + + # Do: Donovan reads project -> ko (anonymous ko portal) + self.assertRaises(except_orm, self.project_project.read, + cr, self.user_donovan_id, pigs_id, ['name']) + # Test: no project task visible + task_ids = self.project_task.search(cr, self.user_donovan_id, [('project_id', '=', pigs_id)]) + self.assertFalse(task_ids, 'access rights: anonymous user should not see tasks of a portal project') + + # Data: task follower cleaning + self.project_task.message_unsubscribe_users(cr, self.user_alfred_id, [self.task_1_id, self.task_3_id], [self.user_chell_id]) + + # ---------------------------------------- + # CASE3: employee project + # ---------------------------------------- + self.project_project.write(cr, uid, [pigs_id], {'privacy_visibility': 'employees'}) + + # Do: Alfred reads project -> ok (employee ok employee) + self.project_project.read(cr, self.user_alfred_id, pigs_id, ['name']) + # Test: all project tasks visible + task_ids = self.project_task.search(cr, self.user_alfred_id, [('project_id', '=', pigs_id)]) + test_task_ids = set([self.task_1_id, self.task_2_id, self.task_3_id, self.task_4_id, self.task_5_id, self.task_6_id]) + self.assertEqual(set(task_ids), test_task_ids, + 'access rights: project user cannot see all tasks of an employees project') + + # Do: Bert reads project -> crash, no group + self.assertRaises(except_orm, self.project_project.read, + cr, self.user_bert_id, pigs_id, ['name']) + + # Do: Chell reads project -> ko (portal ko employee) + self.assertRaises(except_orm, self.project_project.read, + cr, self.user_chell_id, pigs_id, ['name']) + # Test: no project task visible + assigned + task_ids = self.project_task.search(cr, self.user_chell_id, [('project_id', '=', pigs_id)]) + self.assertFalse(task_ids, 'access rights: portal user should not see tasks of an employees project, even if assigned') + + # Do: Donovan reads project -> ko (anonymous ko employee) + self.assertRaises(except_orm, self.project_project.read, + cr, self.user_donovan_id, pigs_id, ['name']) + # Test: no project task visible + task_ids = self.project_task.search(cr, self.user_donovan_id, [('project_id', '=', pigs_id)]) + self.assertFalse(task_ids, 'access rights: anonymous user should not see tasks of an employees project') + + # ---------------------------------------- + # CASE4: followers project + # ---------------------------------------- + self.project_project.write(cr, uid, [pigs_id], {'privacy_visibility': 'followers'}) + + # Do: Alfred reads project -> ko (employee ko followers) + self.assertRaises(except_orm, self.project_project.read, + cr, self.user_alfred_id, pigs_id, ['name']) + # Test: no project task visible + task_ids = self.project_task.search(cr, self.user_alfred_id, [('project_id', '=', pigs_id)]) + test_task_ids = set([self.task_4_id]) + self.assertEqual(set(task_ids), test_task_ids, + 'access rights: employee user should not see tasks of a not-followed followers project, only assigned') + + # Do: Bert reads project -> crash, no group + self.assertRaises(except_orm, self.project_project.read, + cr, self.user_bert_id, pigs_id, ['name']) + + # Do: Chell reads project -> ko (portal ko employee) + self.assertRaises(except_orm, self.project_project.read, + cr, self.user_chell_id, pigs_id, ['name']) + # Test: no project task visible + task_ids = self.project_task.search(cr, self.user_chell_id, [('project_id', '=', pigs_id)]) + test_task_ids = set([self.task_5_id]) + self.assertEqual(set(task_ids), test_task_ids, + 'access rights: portal user should not see tasks of a not-followed followers project, only assigned') + + # Do: Donovan reads project -> ko (anonymous ko employee) + self.assertRaises(except_orm, self.project_project.read, + cr, self.user_donovan_id, pigs_id, ['name']) + # Test: no project task visible + task_ids = self.project_task.search(cr, self.user_donovan_id, [('project_id', '=', pigs_id)]) + self.assertFalse(task_ids, 'access rights: anonymous user should not see tasks of a followers project') + + # Data: subscribe Alfred, Chell and Donovan as follower + self.project_project.message_subscribe_users(cr, uid, [pigs_id], [self.user_alfred_id, self.user_chell_id, self.user_donovan_id]) + self.project_task.message_subscribe_users(cr, self.user_alfred_id, [self.task_1_id, self.task_3_id], [self.user_chell_id, self.user_alfred_id]) + + # Do: Alfred reads project -> ok (follower ok followers) + self.project_project.read(cr, self.user_alfred_id, pigs_id, ['name']) + # Test: followed + assigned tasks visible + task_ids = self.project_task.search(cr, self.user_alfred_id, [('project_id', '=', pigs_id)]) + test_task_ids = set([self.task_1_id, self.task_3_id, self.task_4_id]) + self.assertEqual(set(task_ids), test_task_ids, + 'access rights: employee user should not see followed + assigned tasks of a follower project') + + # Do: Chell reads project -> ok (follower ok follower) + self.project_project.read(cr, self.user_chell_id, pigs_id, ['name']) + # Test: followed + assigned tasks visible + task_ids = self.project_task.search(cr, self.user_chell_id, [('project_id', '=', pigs_id)]) + test_task_ids = set([self.task_1_id, self.task_3_id, self.task_5_id]) + self.assertEqual(set(task_ids), test_task_ids, + 'access rights: employee user should not see followed + assigned tasks of a follower project') + + # Do: Donovan reads project -> ko (anonymous ko follower even if follower) + self.assertRaises(except_orm, self.project_project.read, + cr, self.user_donovan_id, pigs_id, ['name']) diff --git a/addons/portal_project_issue/security/portal_security.xml b/addons/portal_project_issue/security/portal_security.xml index a121eeb593e..472c82e4705 100644 --- a/addons/portal_project_issue/security/portal_security.xml +++ b/addons/portal_project_issue/security/portal_security.xml @@ -3,14 +3,29 @@ - Portal Personal Issues - - [('message_follower_ids','in', [user.partner_id.id])] + Project/Issue: portal users: public or portal and following + + ['|', + ('project_id.privacy_visibility', '=', 'public'), + '&', + ('project_id.privacy_visibility', 'in', ['portal', 'followers']), + '|', + ('message_follower_ids','in', [user.partner_id.id]), + ('user_id', '=', user.id), + ] - - - - + + + + Project/Issue: employees: public, portal, employee or following or assigned + ['|', + ('user_id', '=', user.id), + '|', + ('project_id.privacy_visibility', 'in', ['public', 'portal', 'employees']), + '&', + ('project_id.privacy_visibility', '=', 'followers'), + ('message_follower_ids', 'in', [user.partner_id.id]), + ] diff --git a/addons/project/project.py b/addons/project/project.py index 877c45703b4..739a54fe403 100644 --- a/addons/project/project.py +++ b/addons/project/project.py @@ -208,6 +208,12 @@ class project(osv.osv): """Overriden in project_issue to offer more options""" return [('project.task', "Tasks")] + def _get_visibility_selection(self, cr, uid, context=None): + """ Overriden in portal_project to offer more options """ + return [('public', 'All Users'), + ('employees', 'Employees Only'), + ('followers', 'Followers Only')] + def attachment_tree_view(self, cr, uid, ids, context): task_ids = self.pool.get('project.task').search(cr, uid, [('project_id', 'in', ids)]) domain = [ @@ -229,6 +235,8 @@ class project(osv.osv): } # Lambda indirection method to avoid passing a copy of the overridable method when declaring the field _alias_models = lambda self, *args, **kwargs: self._get_alias_models(*args, **kwargs) + _visibility_selection = lambda self, *args, **kwargs: self._get_visibility_selection(*args, **kwargs) + _columns = { 'complete_name': fields.function(_complete_name, string="Project Name", type='char', size=250), 'active': fields.boolean('Active', help="If the active field is set to False, it will allow you to hide the project without removing it."), @@ -267,7 +275,7 @@ class project(osv.osv): "with Tasks (or optionally Issues if the Issue Tracker module is installed)."), 'alias_model': fields.selection(_alias_models, "Alias Model", select=True, required=True, help="The kind of document created when an email is received on this project's email alias"), - 'privacy_visibility': fields.selection([('public','Public'), ('followers','Followers Only')], 'Privacy / Visibility', required=True), + 'privacy_visibility': fields.selection(_visibility_selection, 'Privacy / Visibility', required=True), 'state': fields.selection([('template', 'Template'),('draft','New'),('open','In Progress'), ('cancelled', 'Cancelled'),('pending','Pending'),('close','Closed')], 'Status', required=True,), 'doc_count':fields.function(_get_attached_docs, string="Number of documents attached", type='int') } diff --git a/addons/project/security/project_security.xml b/addons/project/security/project_security.xml index f1d8c7e760f..4bbaa9b788e 100644 --- a/addons/project/security/project_security.xml +++ b/addons/project/security/project_security.xml @@ -38,39 +38,62 @@ - Project multi-company + Project: multi-company - ['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])] + ['|', + ('company_id', '=', False), + ('company_id', 'child_of', [user.company_id.id]), + ] + + + + Project: project manager: see all + + [(1, '=', 1)] + - Public Members + Project: employees: public, employees or followers - - ['|',('privacy_visibility','in',[False,'public']),('message_follower_ids','in',[user.partner_id.id])] + ['|', + ('privacy_visibility', 'in', ['public', 'employees']), + ('message_follower_ids', 'in', [user.partner_id.id]) + ] + - Task multi-company + Project/Task: multi-company - ['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])] + ['|', + ('company_id', '=', False), + ('company_id', 'child_of', [user.company_id.id]), + ] - Tasks According to User and Project + Project/Task: employees: public or employee or following or assigned - - ['|','|','|',('user_id','=',False),('user_id','=',user.id),('project_id.members','in', [user.id]),('project_id.user_id','=',user.id)] - + ['|', + ('user_id', '=', user.id), + '|', + ('project_id.privacy_visibility', 'in', ['public', 'employees']), + '&', + ('project_id.privacy_visibility', '=', 'followers'), + ('message_follower_ids', 'in', [user.partner_id.id]), + ] + - Project Managers: all tasks from all projects + Project/Task: project manager: see all - [(1,'=',1)] + [(1, '=', 1)] + diff --git a/addons/project_issue/security/project_issue_security.xml b/addons/project_issue/security/project_issue_security.xml index c3119073a75..5215a94a8b7 100644 --- a/addons/project_issue/security/project_issue_security.xml +++ b/addons/project_issue/security/project_issue_security.xml @@ -1,5 +1,27 @@ + + + Project/Issue: project manager: see all + + [(1, '=', 1)] + + + + + Project/Issue: employees: public or employee or following or assigned + + ['|', + '|', + ('project_id.privacy_visibility', 'in', ['public', 'employees']), + '&', + ('project_id.privacy_visibility', '=', 'followers'), + ('message_follower_ids', 'in', [user.id]), + ('user_id', '=', user.id), + ] + + +