Transformer: VisibilityTransformer
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
"""
|
||||
Tests for VisibilityTransformer.
|
||||
"""
|
||||
import ddt
|
||||
|
||||
from ..visibility import VisibilityTransformer
|
||||
from .test_helpers import BlockParentsMapTestCase, update_block
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class VisibilityTransformerTestCase(BlockParentsMapTestCase):
|
||||
"""
|
||||
VisibilityTransformer Test
|
||||
"""
|
||||
# Following test cases are based on BlockParentsMapTestCase.parents_map
|
||||
@ddt.data(
|
||||
({}, {0, 1, 2, 3, 4, 5, 6}, {}),
|
||||
({0}, {}, {1, 2, 3, 4, 5, 6}),
|
||||
({1}, {0, 2, 5, 6}, {3, 4}),
|
||||
({2}, {0, 1, 3, 4, 6}, {5}),
|
||||
({3}, {0, 1, 2, 4, 5, 6}, {}),
|
||||
({4}, {0, 1, 2, 3, 5, 6}, {}),
|
||||
({5}, {0, 1, 2, 3, 4, 6}, {}),
|
||||
({6}, {0, 1, 2, 3, 4, 5}, {}),
|
||||
({1, 2}, {0}, {3, 4, 5, 6}),
|
||||
({2, 4}, {0, 1, 3}, {5, 6}),
|
||||
({1, 2, 3, 4, 5, 6}, {0}, {}),
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_block_visibility(
|
||||
self, staff_only_blocks, expected_visible_blocks, blocks_with_differing_access
|
||||
):
|
||||
for idx, _ in enumerate(self.parents_map):
|
||||
block = self.get_block(idx)
|
||||
block.visible_to_staff_only = (idx in staff_only_blocks)
|
||||
update_block(block)
|
||||
|
||||
self.assert_transform_results(
|
||||
self.student,
|
||||
expected_visible_blocks,
|
||||
blocks_with_differing_access,
|
||||
[VisibilityTransformer()],
|
||||
)
|
||||
80
lms/djangoapps/course_blocks/transformers/visibility.py
Normal file
80
lms/djangoapps/course_blocks/transformers/visibility.py
Normal file
@@ -0,0 +1,80 @@
|
||||
"""
|
||||
Visibility Transformer implementation.
|
||||
"""
|
||||
from openedx.core.lib.block_cache.transformer import BlockStructureTransformer
|
||||
|
||||
|
||||
class VisibilityTransformer(BlockStructureTransformer):
|
||||
"""
|
||||
A transformer that enforces the visible_to_staff_only field on
|
||||
blocks by removing blocks from the block structure for which the
|
||||
user does not have access. The visible_to_staff_only field on a
|
||||
block is percolated down to its descendants, so that all blocks
|
||||
enforce the visibility settings from their ancestors.
|
||||
|
||||
For a block with multiple parents, access is denied only if
|
||||
visibility is denied for all its parents.
|
||||
|
||||
Staff users are exempted from visibility rules.
|
||||
"""
|
||||
VERSION = 1
|
||||
|
||||
MERGED_VISIBLE_TO_STAFF_ONLY = 'merged_visible_to_staff_only'
|
||||
|
||||
@classmethod
|
||||
def name(cls):
|
||||
"""
|
||||
Unique identifier for the transformer's class;
|
||||
same identifier used in setup.py.
|
||||
"""
|
||||
return "visibility"
|
||||
|
||||
@classmethod
|
||||
def get_visible_to_staff_only(cls, block_structure, block_key):
|
||||
"""
|
||||
Returns whether the block with the given block_key in the
|
||||
given block_structure should be visible to staff only per
|
||||
computed value from ancestry chain.
|
||||
"""
|
||||
return block_structure.get_transformer_block_field(
|
||||
block_key, cls, cls.MERGED_VISIBLE_TO_STAFF_ONLY, False
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def collect(cls, block_structure):
|
||||
"""
|
||||
Collects any information that's necessary to execute this
|
||||
transformer's transform method.
|
||||
"""
|
||||
for block_key in block_structure.topological_traversal():
|
||||
|
||||
# compute merged value of visible_to_staff_only from all parents
|
||||
parents = block_structure.get_parents(block_key)
|
||||
all_parents_visible_to_staff_only = all( # pylint: disable=invalid-name
|
||||
cls.get_visible_to_staff_only(block_structure, parent_key)
|
||||
for parent_key in parents
|
||||
) if parents else False
|
||||
|
||||
# set the merged value for this block
|
||||
block_structure.set_transformer_block_field(
|
||||
block_key,
|
||||
cls,
|
||||
cls.MERGED_VISIBLE_TO_STAFF_ONLY,
|
||||
# merge visible_to_staff_only from all parents and this block
|
||||
(
|
||||
all_parents_visible_to_staff_only or
|
||||
block_structure.get_xblock(block_key).visible_to_staff_only
|
||||
)
|
||||
)
|
||||
|
||||
def transform(self, usage_info, block_structure):
|
||||
"""
|
||||
Mutates block_structure based on the given usage_info.
|
||||
"""
|
||||
# Users with staff access bypass the Visibility check.
|
||||
if usage_info.has_staff_access:
|
||||
return
|
||||
|
||||
block_structure.remove_block_if(
|
||||
lambda block_key: self.get_visible_to_staff_only(block_structure, block_key)
|
||||
)
|
||||
Reference in New Issue
Block a user