feat: adds VerticalBlockRenderCompleted filter hook (#31388)
* feat: adds VerticalBlockChildrenLoaded filter call This introduces the VerticalBlockChildrenLoaded filter that is run after all the child blocks are fetched before rendering a student or the public view. This will allow modifying the contents of the VerticalBlock before presenting it to the students.
This commit is contained in:
@@ -182,6 +182,10 @@ well as the trigger location in this same repository.
|
||||
- org.openedx.learning.dashboard.render.started.v1
|
||||
- `2022-06-14 <https://github.com/eduNEXT/edx-platform/blob/master/common/djangoapps/student/views/dashboard.py#L878>`_
|
||||
|
||||
* - `VerticalChildRenderStarted <https://github.com/openedx/openedx-filters/blob/main/openedx_filters/learning/filters.py#L427>`_
|
||||
- org.openedx.learning.veritical_child_block.render.started.v1
|
||||
- `2022-08-18 <https://github.com/openedx/edx-platform/blob/master/xmodule/vertical_block.py#L122>`_
|
||||
* - `VerticalBlockChildRenderStarted <https://github.com/openedx/openedx-filters/blob/main/openedx_filters/learning/filters.py#L427>`_
|
||||
- org.openedx.learning.veritical_block_child.render.started.v1
|
||||
- `2022-08-18 <https://github.com/openedx/edx-platform/blob/master/xmodule/vertical_block.py#L170>`_
|
||||
|
||||
* - `VerticalBlockRenderCompleted <https://github.com/openedx/openedx-filters/blob/main/openedx_filters/learning/filters.py#L476>`_
|
||||
- org.openedx.learning.veritical_block.render.completed.v1
|
||||
- `2022-02-18 <https://github.com/openedx/edx-platform/blob/master/xmodule/vertical_block.py#L121>`_
|
||||
|
||||
@@ -14,6 +14,9 @@ import pytz
|
||||
import ddt
|
||||
from fs.memoryfs import MemoryFS
|
||||
from django.contrib.auth.models import AnonymousUser
|
||||
from django.test import override_settings
|
||||
from openedx_filters import PipelineStep
|
||||
from openedx_filters.learning.filters import VerticalBlockChildRenderStarted, VerticalBlockRenderCompleted
|
||||
|
||||
from . import get_test_system
|
||||
from .helpers import StubUserService
|
||||
@@ -78,6 +81,62 @@ class StubCompletionService:
|
||||
return self._completion_value == 1 if self._enabled else None
|
||||
|
||||
|
||||
class TestVerticalBlockChildRenderStep(PipelineStep):
|
||||
"""
|
||||
Utility class for testing filters on vertical block children
|
||||
"""
|
||||
filter_content = "Altered Content"
|
||||
|
||||
def run_filter(self, block, context): # lint-amnesty, pylint: disable=arguments-differ
|
||||
"""Pipeline step that changes child content"""
|
||||
if type(block).__name__ == "HtmlBlockWithMixins":
|
||||
block.get_html = lambda: TestVerticalBlockChildRenderStep.filter_content
|
||||
return {"block": block, "context": context}
|
||||
|
||||
|
||||
class TestPreventVerticalBlockChildRender(PipelineStep):
|
||||
"""
|
||||
Utility class to test vertical block children are skipped in rendering.
|
||||
"""
|
||||
|
||||
def run_filter(self, block, context): # lint-amnesty, pylint: disable=arguments-differ
|
||||
"""Pipeline step that raises exceptions during child block rendering"""
|
||||
if type(block).__name__ == "HtmlBlockWithMixins":
|
||||
raise VerticalBlockChildRenderStarted.PreventChildBlockRender(
|
||||
"Skip block test exception"
|
||||
)
|
||||
|
||||
|
||||
class TestVerticalBlockRenderCompletedStep(PipelineStep):
|
||||
"""
|
||||
Utility class for testing filters on vertical block render completion
|
||||
"""
|
||||
filter_content = "Extra content added"
|
||||
|
||||
def run_filter(self, block, fragment, context, view): # lint-amnesty, pylint: disable=arguments-differ
|
||||
"""Pipeline step that alters the output of the fragment"""
|
||||
fragment.content += TestVerticalBlockRenderCompletedStep.filter_content
|
||||
return {
|
||||
"block": block,
|
||||
"fragment": fragment,
|
||||
"context": context,
|
||||
"view": view
|
||||
}
|
||||
|
||||
|
||||
class TestPreventVerticalBlockRenderStep(PipelineStep):
|
||||
"""
|
||||
Utility class for testing VerticalBlock render can be stopped.
|
||||
"""
|
||||
filter_content = "<div class=\"alert alert-danger\">Assignments are not available for Audit students.<div>"
|
||||
|
||||
def run_filter(self, block, fragment, context, view): # lint-amnesty, pylint: disable=arguments-differ
|
||||
"""Pipeline step that raises an exception"""
|
||||
raise VerticalBlockRenderCompleted.PreventVerticalBlockRender(
|
||||
TestPreventVerticalBlockRenderStep.filter_content
|
||||
)
|
||||
|
||||
|
||||
class BaseVerticalBlockTest(XModuleXmlImportTest):
|
||||
"""
|
||||
Tests for the BaseVerticalBlock.
|
||||
@@ -289,3 +348,92 @@ class VerticalBlockTestCase(BaseVerticalBlockTest):
|
||||
html = self.module_system.render(self.vertical, AUTHOR_VIEW, context).content
|
||||
assert self.test_html in html
|
||||
assert self.test_problem in html
|
||||
|
||||
@override_settings(
|
||||
OPEN_EDX_FILTERS_CONFIG={
|
||||
"org.openedx.learning.vertical_block_child.render.started.v1": {
|
||||
"pipeline": [
|
||||
"xmodule.tests.test_vertical.TestVerticalBlockChildRenderStep"
|
||||
],
|
||||
"fail_silently": False,
|
||||
},
|
||||
},
|
||||
)
|
||||
def test_vertical_block_child_render_started_filter_execution(self):
|
||||
"""
|
||||
Test the VerticalBlockChildRenderStarted filter's effects on student view.
|
||||
"""
|
||||
self.module_system._services['bookmarks'] = Mock()
|
||||
self.module_system._services['user'] = StubUserService(user=Mock())
|
||||
self.module_system._services['completion'] = StubCompletionService(enabled=True, completion_value=0)
|
||||
|
||||
html = self.module_system.render(self.vertical, STUDENT_VIEW, self.default_context).content
|
||||
|
||||
assert TestVerticalBlockChildRenderStep.filter_content in html
|
||||
|
||||
@override_settings(
|
||||
OPEN_EDX_FILTERS_CONFIG={
|
||||
"org.openedx.learning.vertical_block_child.render.started.v1": {
|
||||
"pipeline": [
|
||||
"xmodule.tests.test_vertical.TestPreventVerticalBlockChildRender"
|
||||
],
|
||||
"fail_silently": False,
|
||||
},
|
||||
},
|
||||
)
|
||||
def test_vertical_block_child_render_is_skipped_on_filter_exception(self):
|
||||
"""
|
||||
Test VerticalBlockChildRenderStarted filter can be used to skip child blocks.
|
||||
"""
|
||||
self.module_system._services['bookmarks'] = Mock()
|
||||
self.module_system._services['user'] = StubUserService(user=Mock())
|
||||
self.module_system._services['completion'] = StubCompletionService(enabled=True, completion_value=0)
|
||||
|
||||
html = self.module_system.render(self.vertical, STUDENT_VIEW, self.default_context).content
|
||||
|
||||
assert self.test_html not in html
|
||||
assert self.test_html_nested not in html
|
||||
|
||||
@override_settings(
|
||||
OPEN_EDX_FILTERS_CONFIG={
|
||||
"org.openedx.learning.vertical_block.render.completed.v1": {
|
||||
"pipeline": [
|
||||
"xmodule.tests.test_vertical.TestVerticalBlockRenderCompletedStep"
|
||||
],
|
||||
"fail_silently": False,
|
||||
},
|
||||
},
|
||||
)
|
||||
def test_vertical_block_render_completed_filter_execution(self):
|
||||
"""
|
||||
Test the VerticalBlockRenderCompleted filter's execution.
|
||||
"""
|
||||
self.module_system._services['bookmarks'] = Mock()
|
||||
self.module_system._services['user'] = StubUserService(user=Mock())
|
||||
self.module_system._services['completion'] = StubCompletionService(enabled=True, completion_value=0)
|
||||
|
||||
html = self.module_system.render(self.vertical, STUDENT_VIEW, self.default_context).content
|
||||
|
||||
assert TestVerticalBlockRenderCompletedStep.filter_content in html
|
||||
|
||||
@override_settings(
|
||||
OPEN_EDX_FILTERS_CONFIG={
|
||||
"org.openedx.learning.vertical_block.render.completed.v1": {
|
||||
"pipeline": [
|
||||
"xmodule.tests.test_vertical.TestPreventVerticalBlockRenderStep"
|
||||
],
|
||||
"fail_silently": False,
|
||||
},
|
||||
},
|
||||
)
|
||||
def test_vertical_block_render_output_is_changed_on_filter_exception(self):
|
||||
"""
|
||||
Test VerticalBlockRenderCompleted filter can be used to prevent vertical block from rendering.
|
||||
"""
|
||||
self.module_system._services['bookmarks'] = Mock()
|
||||
self.module_system._services['user'] = StubUserService(user=Mock())
|
||||
self.module_system._services['completion'] = StubCompletionService(enabled=True, completion_value=0)
|
||||
|
||||
html = self.module_system.render(self.vertical, STUDENT_VIEW, self.default_context).content
|
||||
|
||||
assert TestPreventVerticalBlockRenderStep.filter_content == html
|
||||
|
||||
@@ -13,7 +13,7 @@ from lxml import etree
|
||||
from web_fragments.fragment import Fragment
|
||||
from xblock.core import XBlock # lint-amnesty, pylint: disable=wrong-import-order
|
||||
from xblock.fields import Boolean, Scope
|
||||
from openedx_filters.learning.filters import VerticalBlockChildRenderStarted
|
||||
from openedx_filters.learning.filters import VerticalBlockChildRenderStarted, VerticalBlockRenderCompleted
|
||||
from xmodule.mako_block import MakoTemplateBlockBase
|
||||
from xmodule.progress import Progress
|
||||
from xmodule.seq_block import SequenceFields
|
||||
@@ -72,7 +72,7 @@ class VerticalBlock(
|
||||
|
||||
show_in_read_only_mode = True
|
||||
|
||||
def _student_or_public_view(self, context, view):
|
||||
def _student_or_public_view(self, context, view): # lint-amnesty, pylint: disable=too-many-statements
|
||||
"""
|
||||
Renders the requested view type of the block in the LMS.
|
||||
"""
|
||||
@@ -117,11 +117,15 @@ class VerticalBlock(
|
||||
'mark-completed-on-view-after-delay': complete_on_view_delay
|
||||
}
|
||||
|
||||
# .. filter_implemented_name: VerticalBlockChildRenderStarted
|
||||
# .. filter_type: org.openedx.learning.vertical_block_child.render.started.v1
|
||||
child, child_block_context = VerticalBlockChildRenderStarted.run_filter(
|
||||
block=child, context=child_block_context
|
||||
)
|
||||
try:
|
||||
# .. filter_implemented_name: VerticalBlockChildRenderStarted
|
||||
# .. filter_type: org.openedx.learning.vertical_block_child.render.started.v1
|
||||
child, child_block_context = VerticalBlockChildRenderStarted.run_filter(
|
||||
block=child, context=child_block_context
|
||||
)
|
||||
except VerticalBlockChildRenderStarted.PreventChildBlockRender as exc:
|
||||
log.info("Skipping %s from vertical block. Reason: %s", child, exc.message)
|
||||
continue
|
||||
|
||||
rendered_child = child.render(view, child_block_context)
|
||||
fragment.add_fragment_resources(rendered_child)
|
||||
@@ -162,6 +166,16 @@ class VerticalBlock(
|
||||
add_webpack_to_fragment(fragment, 'VerticalStudentView')
|
||||
fragment.initialize_js('VerticalStudentView')
|
||||
|
||||
try:
|
||||
# .. filter_implemented_name: VerticalBlockRenderCompleted
|
||||
# .. filter_type: org.openedx.learning.vertical_block.render.completed.v1
|
||||
_, fragment, context, view = VerticalBlockRenderCompleted.run_filter(
|
||||
block=self, fragment=fragment, context=context, view=view
|
||||
)
|
||||
except VerticalBlockRenderCompleted.PreventVerticalBlockRender as exc:
|
||||
log.info("VerticalBlock rendering stopped. Reason: %s", exc.message)
|
||||
fragment.content = exc.message
|
||||
|
||||
return fragment
|
||||
|
||||
def block_has_access_error(self, block):
|
||||
|
||||
Reference in New Issue
Block a user