[FW][FIX] knowledge: correctly handle complete hierarchy when detaching children
Currently, when archiving or moving an article to private, we "detach" children of that article to which the current user does not have (write) access to. This essentially means that these unreachable articles are detached from their parent and set as root articles. See ``_detach_unwritable_descendants' for more details. However, during that process, we incorrectly considered only the direct children of the archived / moved article. This commit fixes it by taking into account the whole hierarchy that is going to be modified, as archiving and making private actually propagates to all the (reachable) article children. Task-2859616 X-original-commit: odoo/odoo@a31f48dfa4 Part-of: odoo/odoo#95471
This commit is contained in:
committed by
Thibault Delavallée
parent
48b99e2f0c
commit
1a84e7293b
@@ -1167,8 +1167,8 @@ class Article(models.Model):
|
||||
:return <knowledge.article> 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)],
|
||||
})
|
||||
|
||||
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user