From 3152c14192dcccac79b7b7a76eba91b380eba78a Mon Sep 17 00:00:00 2001 From: Diane Kaplan Date: Wed, 14 Apr 2021 14:11:21 -0400 Subject: [PATCH] fix: hide gated content message for library content questions (learning MFE only) (#27277) --- .../xmodule/xmodule/tests/test_vertical.py | 46 ++++++++++++++++--- common/lib/xmodule/xmodule/vertical_block.py | 20 +++++++- 2 files changed, 58 insertions(+), 8 deletions(-) diff --git a/common/lib/xmodule/xmodule/tests/test_vertical.py b/common/lib/xmodule/xmodule/tests/test_vertical.py index 17cc6c3dc4..e3a36ab38c 100644 --- a/common/lib/xmodule/xmodule/tests/test_vertical.py +++ b/common/lib/xmodule/xmodule/tests/test_vertical.py @@ -82,11 +82,14 @@ class BaseVerticalBlockTest(XModuleXmlImportTest): Tests for the BaseVerticalBlock. """ test_html = 'Test HTML' - test_problem = 'Test Problem' + test_problem = 'Test_Problem' + test_html_nested = 'Nest Nested HTML' + test_problem_nested = 'Nest_Nested_Problem' def setUp(self): super().setUp() - # construct module + # construct module: course/sequence/vertical - problems + # \_ nested_vertical / problems course = xml.CourseFactory.build() sequence = xml.SequenceFactory.build(parent=course) vertical = xml.VerticalFactory.build(parent=sequence) @@ -95,6 +98,10 @@ class BaseVerticalBlockTest(XModuleXmlImportTest): xml.HtmlFactory(parent=vertical, url_name='test-html', text=self.test_html) xml.ProblemFactory(parent=vertical, url_name='test-problem', text=self.test_problem) + nested_vertical = xml.VerticalFactory.build(parent=vertical) + xml.HtmlFactory(parent=nested_vertical, url_name='test_html_nested', text=self.test_html_nested) + xml.ProblemFactory(parent=nested_vertical, url_name='test_problem_nested', text=self.test_problem_nested) + self.course = self.process_xml(course) course_seq = self.course.get_children()[0] self.module_system = get_test_system() @@ -109,6 +116,10 @@ class BaseVerticalBlockTest(XModuleXmlImportTest): self.problem_block = self.vertical.get_children()[1] self.problem_block.has_score = True self.problem_block.graded = True + self.extra_vertical_block = self.vertical.get_children()[2] # VerticalBlockWithMixins + self.nested_problem_block = self.extra_vertical_block.get_children()[1] + self.nested_problem_block.has_score = True + self.nested_problem_block.graded = True self.username = "bilbo" self.default_context = {"bookmarked": False, "username": self.username} @@ -195,21 +206,42 @@ class VerticalBlockTestCase(BaseVerticalBlockTest): assert "'completed': None" in html assert "'past_due': False" in html - @ddt.data(True, False) - def test_render_access_denied_blocks(self, has_access_error): + @ddt.data((True, True), (True, False), (False, True), (False, False)) + @ddt.unpack + def test_render_access_denied_blocks(self, node_has_access_error, child_has_access_error): """ Tests access denied blocks are not rendered when hide_access_error_blocks is True """ self.module_system._services['bookmarks'] = Mock() self.module_system._services['user'] = StubUserService() self.vertical.due = datetime.now(pytz.UTC) + timedelta(days=-1) - self.problem_block.has_access_error = has_access_error + self.problem_block.has_access_error = node_has_access_error + self.nested_problem_block.has_access_error = child_has_access_error context = {'username': self.username, 'hide_access_error_blocks': True} html = self.module_system.render(self.vertical, STUDENT_VIEW, context).content - if has_access_error: + if node_has_access_error and child_has_access_error: assert self.test_problem not in html - else: + assert self.test_problem_nested not in html + if node_has_access_error and not child_has_access_error: + assert self.test_problem not in html + assert self.test_problem_nested in html + if not node_has_access_error and child_has_access_error: assert self.test_problem in html + assert self.test_problem_nested not in html + if not node_has_access_error and not child_has_access_error: + assert self.test_problem in html + assert self.test_problem_nested in html + + @ddt.data(True, False) + def test_block_has_access_error(self, has_access_error): + """ Tests block_has_access_error gives the correct result for child node questions """ + # Use special block from setup (vertical/nested_vertical/problem) + # has_access_error is set on problem an extra level down, so we have to recurse to pass + + self.nested_problem_block.has_access_error = has_access_error + should_block = self.vertical.block_has_access_error(self.vertical) + + assert should_block == has_access_error @ddt.unpack @ddt.data( diff --git a/common/lib/xmodule/xmodule/vertical_block.py b/common/lib/xmodule/xmodule/vertical_block.py index ad970d6221..f7a4e39a64 100644 --- a/common/lib/xmodule/xmodule/vertical_block.py +++ b/common/lib/xmodule/xmodule/vertical_block.py @@ -82,7 +82,8 @@ class VerticalBlock(SequenceFields, XModuleFields, StudioEditableBlock, XmlParse # pylint: disable=no-member for child in child_blocks: - if context.get('hide_access_error_blocks') and getattr(child, 'has_access_error', False): + child_has_access_error = self.block_has_access_error(child) + if context.get('hide_access_error_blocks') and child_has_access_error: continue child_block_context = copy(child_context) if child in list(child_blocks_to_complete_on_view): @@ -130,6 +131,23 @@ class VerticalBlock(SequenceFields, XModuleFields, StudioEditableBlock, XmlParse return fragment + def block_has_access_error(self, block): + """ + Returns whether has_access_error is True for the given block (itself or any child) + """ + # Check its access attribute (regular question will have it set) + has_access_error = getattr(block, 'has_access_error', False) + if has_access_error: + return True + + # Check child nodes if they exist (e.g. randomized library question aka LibraryContentBlock) + for child in block.get_children(): + has_access_error = getattr(child, 'has_access_error', False) + if has_access_error: + return True + has_access_error = self.block_has_access_error(child) + return has_access_error + def student_view(self, context): """ Renders the student view of the block in the LMS.