diff --git a/addons/knowledge/models/knowledge_article.py b/addons/knowledge/models/knowledge_article.py index 8fa05ef2c04..e596bf80448 100644 --- a/addons/knowledge/models/knowledge_article.py +++ b/addons/knowledge/models/knowledge_article.py @@ -1167,8 +1167,8 @@ class Article(models.Model): :return children: the children articles which were not detached, meaning that current user has write access on them """ all_descendants_sudo = self.sudo()._get_descendants() - writable_descendants = all_descendants_sudo.with_env(self.env)._filter_access_rules_python('write') - other_descendants_sudo = all_descendants_sudo - writable_descendants + writable_descendants_sudo = all_descendants_sudo.with_env(self.env)._filter_access_rules_python('write') + other_descendants_sudo = all_descendants_sudo - writable_descendants_sudo # copy rights to allow breaking the hierarchy while keeping access for members # do this on synchronized articles as desynchronized one do not inherit from parent @@ -1177,9 +1177,13 @@ class Article(models.Model): 'article_member_ids': article_sudo._copy_access_from_parents_commands() }) - # create new root articles: direct children of these articles, reset desync - new_roots_woperm = other_descendants_sudo.filtered(lambda article: article.parent_id in self and not article.internal_permission) - new_roots_wperm = other_descendants_sudo.filtered(lambda article: article.parent_id in self and article.internal_permission) + # create new root articles and reset desync: direct children of these articles + + # the writable descendants. Indeed they are going to be modified the same way + # as "self" (archived / moved to private) -> all their children should be detached + new_roots_woperm = other_descendants_sudo.filtered( + lambda article: article.parent_id in (self + writable_descendants_sudo) and not article.internal_permission) + new_roots_wperm = other_descendants_sudo.filtered( + lambda article: article.parent_id in (self + writable_descendants_sudo) and article.internal_permission) if new_roots_wperm: new_roots_wperm.write({ 'is_desynchronized': False, @@ -1192,7 +1196,7 @@ class Article(models.Model): 'parent_id': False, }) - return writable_descendants + return writable_descendants_sudo def _move_and_make_private(self, parent=False, before_article=False): """ Set as private: remove members, ensure current user is the only member @@ -1228,7 +1232,7 @@ class Article(models.Model): ) # first detach unwritable children (see ``_detach_unwritable_descendants``) - writable_descendants = self._detach_unwritable_descendants() + writable_descendants_sudo = self._detach_unwritable_descendants() article_values = { # reset internal permission if parent is set (will inherit) or force private (aka 'none') @@ -1269,7 +1273,7 @@ class Article(models.Model): # remove all specific memberships configurations on children. They now inherit # the only 'write' member from their parent now, making them private. - writable_descendants.with_context(knowledge_member_skip_writable_check=True).write({ + writable_descendants_sudo.with_context(knowledge_member_skip_writable_check=True).write({ 'internal_permission': False, 'article_member_ids': [(5, 0)], }) diff --git a/addons/knowledge/tests/test_knowledge_article_business.py b/addons/knowledge/tests/test_knowledge_article_business.py index 73fbab5393e..1f86e2834b7 100644 --- a/addons/knowledge/tests/test_knowledge_article_business.py +++ b/addons/knowledge/tests/test_knowledge_article_business.py @@ -490,6 +490,7 @@ class TestKnowledgeArticleBusiness(KnowledgeCommonWData): Specific setup for this test # - Playground workspace w+ (customer-r+) # - Child1 " " (customer-r+) + # - ReadMemb GrandChild " w+ (employee-r+) # - Gd Child1 " " # - Gd Child2 " " # - GdGd Child1 " " @@ -513,13 +514,15 @@ class TestKnowledgeArticleBusiness(KnowledgeCommonWData): })] }) - # add 3 extra articles for further checks + # add 4 extra articles for further checks # - one to which 'employee' only has 'read' access (as member) # - one to which 'employee' only has 'read' access (internal) # - one invisible, "employee2" has access to that one in write mode + # - one to which 'employee' only has 'read' access (as member) as grandchild (descendants testing) [wkspace_child_read_member_access, wkspace_child_read_internal_access, - wkspace_child_no_access] = self.env['knowledge.article'].sudo().create([ + wkspace_child_no_access, + wkspace_grandchild_read_member_access] = self.env['knowledge.article'].sudo().create([ { 'article_member_ids': [(0, 0, { 'partner_id': self.partner_employee.id, @@ -544,6 +547,14 @@ class TestKnowledgeArticleBusiness(KnowledgeCommonWData): 'internal_permission': 'write', 'name': 'Hidden Child', 'parent_id': article_workspace.id, + }, { + 'article_member_ids': [(0, 0, { + 'partner_id': self.partner_employee.id, + 'permission': 'read', + })], + 'internal_permission': 'write', + 'name': 'Read Member GrandChild', + 'parent_id': workspace_children[0].id, } ]) with self.assertRaises(exceptions.AccessError): @@ -601,6 +612,14 @@ class TestKnowledgeArticleBusiness(KnowledgeCommonWData): {self.partner_employee: 'none', self.customer: 'read'} ) + self.assertEqual(wkspace_grandchild_read_member_access.category, 'workspace') + self.assertFalse(wkspace_grandchild_read_member_access.parent_id) + self.assertMembers( + wkspace_grandchild_read_member_access, + 'write', + {self.partner_employee: 'read', + self.customer: 'read'} + ) # 'Hidden Child' is still not accessible for employee with self.assertRaises(exceptions.AccessError):