Merge pull request #28571 from open-craft/jill/bd-13-render_template
feat!: [BD-13] Deprecate ModuleSystem.render_template
This commit is contained in:
@@ -14,7 +14,6 @@ from xmodule.modulestore.django import modulestore
|
||||
from xmodule.tabs import StaticTab
|
||||
|
||||
from cms.djangoapps.models.settings.course_grading import CourseGradingModel
|
||||
from common.djangoapps.edxmako.shortcuts import render_to_string
|
||||
from common.djangoapps.student import auth
|
||||
from common.djangoapps.student.roles import CourseCreatorRole, OrgContentCreatorRole
|
||||
from openedx.core.toggles import ENTRANCE_EXAMS
|
||||
@@ -43,13 +42,6 @@ def event(request):
|
||||
return HttpResponse(status=204)
|
||||
|
||||
|
||||
def render_from_lms(template_name, dictionary, namespace='main'):
|
||||
"""
|
||||
Render a template using the LMS Mako templates
|
||||
"""
|
||||
return render_to_string(template_name, dictionary, namespace="lms." + namespace)
|
||||
|
||||
|
||||
def get_parent_xblock(xblock):
|
||||
"""
|
||||
Returns the xblock that is the parent of the specified xblock, or None if it has no parent.
|
||||
|
||||
@@ -32,6 +32,7 @@ from xblock.fields import Scope
|
||||
from cms.djangoapps.contentstore.config.waffle import SHOW_REVIEW_RULES_FLAG
|
||||
from cms.djangoapps.models.settings.course_grading import CourseGradingModel
|
||||
from cms.lib.xblock.authoring_mixin import VISIBILITY_VIEW
|
||||
from common.djangoapps.edxmako.services import MakoService
|
||||
from common.djangoapps.edxmako.shortcuts import render_to_string
|
||||
from common.djangoapps.static_replace import replace_static_urls
|
||||
from common.djangoapps.student.auth import has_studio_read_access, has_studio_write_access
|
||||
@@ -309,6 +310,8 @@ class StudioEditModuleRuntime:
|
||||
return DjangoXBlockUserService(self._user)
|
||||
if service_name == "studio_user_permissions":
|
||||
return StudioPermissionsService(self._user)
|
||||
if service_name == "mako":
|
||||
return MakoService()
|
||||
if service_name == "settings":
|
||||
return SettingsService()
|
||||
if service_name == "lti-configuration":
|
||||
|
||||
@@ -19,6 +19,7 @@ from cms.djangoapps.xblock_config.models import StudioConfig
|
||||
from cms.lib.xblock.field_data import CmsFieldData
|
||||
from common.djangoapps import static_replace
|
||||
from common.djangoapps.edxmako.shortcuts import render_to_string
|
||||
from common.djangoapps.edxmako.services import MakoService
|
||||
from common.djangoapps.xblock_django.user_service import DjangoXBlockUserService
|
||||
from lms.djangoapps.lms_xblock.field_data import LmsFieldData
|
||||
from openedx.core.lib.license import wrap_with_license
|
||||
@@ -43,7 +44,6 @@ from xmodule.x_module import AUTHOR_VIEW, PREVIEW_VIEWS, STUDENT_VIEW, ModuleSys
|
||||
|
||||
from ..utils import get_visibility_partition_info
|
||||
from .access import get_user_role
|
||||
from .helpers import render_from_lms
|
||||
from .session_kv_store import SessionKeyValueStore
|
||||
|
||||
__all__ = ['preview_handler']
|
||||
@@ -185,9 +185,10 @@ def _preview_module_system(request, descriptor, field_data):
|
||||
)
|
||||
]
|
||||
|
||||
mako_service = MakoService(namespace_prefix='lms.')
|
||||
if settings.FEATURES.get("LICENSING", False):
|
||||
# stick the license wrapper in front
|
||||
wrappers.insert(0, wrap_with_license)
|
||||
wrappers.insert(0, partial(wrap_with_license, mako_service=mako_service))
|
||||
|
||||
return PreviewModuleSystem(
|
||||
static_url=settings.STATIC_URL,
|
||||
@@ -195,7 +196,6 @@ def _preview_module_system(request, descriptor, field_data):
|
||||
track_function=lambda event_type, event: None,
|
||||
filestore=descriptor.runtime.resources_fs,
|
||||
get_module=partial(_load_preview_module, request),
|
||||
render_template=render_from_lms,
|
||||
debug=True,
|
||||
replace_urls=partial(static_replace.replace_static_urls, data_directory=None, course_id=course_id),
|
||||
can_execute_unsafe_code=(lambda: can_execute_unsafe_code(course_id)),
|
||||
@@ -213,6 +213,7 @@ def _preview_module_system(request, descriptor, field_data):
|
||||
services={
|
||||
"field-data": field_data,
|
||||
"i18n": ModuleI18nService,
|
||||
'mako': mako_service,
|
||||
"settings": SettingsService(),
|
||||
"user": DjangoXBlockUserService(request.user, anonymous_user_id='student'),
|
||||
"partitions": StudioPartitionService(course_id=course_id),
|
||||
|
||||
@@ -8,6 +8,7 @@ from unittest import mock
|
||||
|
||||
import ddt
|
||||
from django.test.client import Client, RequestFactory
|
||||
from web_fragments.fragment import Fragment
|
||||
from xblock.core import XBlock, XBlockAside
|
||||
|
||||
from cms.djangoapps.contentstore.utils import reverse_usage_url
|
||||
@@ -168,13 +169,20 @@ class GetPreviewHtmlTestCase(ModuleStoreTestCase):
|
||||
|
||||
@XBlock.needs("field-data")
|
||||
@XBlock.needs("i18n")
|
||||
@XBlock.needs("mako")
|
||||
@XBlock.needs("user")
|
||||
@XBlock.needs("teams_configuration")
|
||||
class PureXBlock(XBlock):
|
||||
"""
|
||||
Pure XBlock to use in tests.
|
||||
"""
|
||||
pass # lint-amnesty, pylint: disable=unnecessary-pass
|
||||
def student_view(self, context):
|
||||
"""
|
||||
Renders the output that a student will see.
|
||||
"""
|
||||
fragment = Fragment()
|
||||
fragment.add_content(self.runtime.service(self, 'mako').render_template('edxmako.html', context))
|
||||
return fragment
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
@@ -206,3 +214,25 @@ class StudioXBlockServiceBindingTest(ModuleStoreTestCase):
|
||||
)
|
||||
service = runtime.service(descriptor, expected_service)
|
||||
self.assertIsNotNone(service)
|
||||
|
||||
|
||||
class CmsModuleSystemShimTest(ModuleStoreTestCase):
|
||||
"""
|
||||
Tests that the deprecated attributes in the Module System (XBlock Runtime) return the expected values.
|
||||
"""
|
||||
def setUp(self):
|
||||
"""
|
||||
Set up the user and other fields that will be used to instantiate the runtime.
|
||||
"""
|
||||
super().setUp()
|
||||
self.course = CourseFactory.create()
|
||||
self.user = UserFactory()
|
||||
self.request = RequestFactory().get('/dummy-url')
|
||||
self.request.user = self.user
|
||||
self.request.session = {}
|
||||
|
||||
@XBlock.register_temp_plugin(PureXBlock, identifier='pure')
|
||||
def test_render_template(self):
|
||||
descriptor = ItemFactory(category="pure", parent=self.course)
|
||||
html = get_preview_fragment(self.request, descriptor, {'element_id': 142}).content
|
||||
assert '<div id="142" ns="main">Testing the MakoService</div>' in html
|
||||
|
||||
@@ -39,7 +39,7 @@ class AuthoringMixin(XBlockMixin):
|
||||
"""
|
||||
fragment = Fragment()
|
||||
from cms.djangoapps.contentstore.utils import reverse_course_url
|
||||
fragment.add_content(self.system.render_template('visibility_editor.html', {
|
||||
fragment.add_content(self.runtime.service(self, 'mako').render_template('visibility_editor.html', {
|
||||
'xblock': self,
|
||||
'manage_groups_url': reverse_course_url('group_configurations_list_handler', self.location.course_key),
|
||||
}))
|
||||
|
||||
30
common/djangoapps/edxmako/services.py
Normal file
30
common/djangoapps/edxmako/services.py
Normal file
@@ -0,0 +1,30 @@
|
||||
"""
|
||||
Supports rendering an XBlock to HTML using mako templates.
|
||||
"""
|
||||
|
||||
from xblock.reference.plugins import Service
|
||||
|
||||
from common.djangoapps.edxmako.shortcuts import render_to_string
|
||||
|
||||
|
||||
class MakoService(Service):
|
||||
"""
|
||||
A service for rendering XBlocks to HTML using mako templates.
|
||||
|
||||
Args:
|
||||
namespace_prefix(string): optional prefix to the mako namespace used to find the template file.
|
||||
e.g to access LMS templates from within Studio code, pass namespace_prefix='lms.'
|
||||
"""
|
||||
def __init__(
|
||||
self,
|
||||
namespace_prefix='',
|
||||
**kwargs
|
||||
):
|
||||
super().__init__(**kwargs)
|
||||
self.namespace_prefix = namespace_prefix
|
||||
|
||||
def render_template(self, template_file, dictionary, namespace='main'):
|
||||
"""
|
||||
Takes (template_file, dictionary) and returns rendered HTML.
|
||||
"""
|
||||
return render_to_string(template_file, dictionary, namespace=self.namespace_prefix + namespace)
|
||||
@@ -14,6 +14,7 @@ from edx_django_utils.cache import RequestCache
|
||||
|
||||
from common.djangoapps.edxmako import LOOKUP, add_lookup
|
||||
from common.djangoapps.edxmako.request_context import get_template_request_context
|
||||
from common.djangoapps.edxmako.services import MakoService
|
||||
from common.djangoapps.edxmako.shortcuts import (
|
||||
is_any_marketing_link_set,
|
||||
is_marketing_link_set,
|
||||
@@ -208,3 +209,23 @@ class MakoRequestContextTest(TestCase):
|
||||
the threadlocal REQUEST_CONTEXT.context. This is meant to run in CMS.
|
||||
"""
|
||||
assert "We're having trouble rendering your component" in render_to_string('html_error.html', None)
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class MakoServiceTestCase(TestCase):
|
||||
"""
|
||||
Tests for the MakoService
|
||||
"""
|
||||
@ddt.data(
|
||||
(MakoService(),
|
||||
'<div id="mako_id" ns="main">Testing the MakoService</div>\n'),
|
||||
(MakoService(namespace_prefix='lms.'),
|
||||
'<div id="mako_id" ns="main">Testing the MakoService</div>\n'),
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_render_template(self, service, expected_html):
|
||||
"""
|
||||
Tests MakoService.render_template returns the expected rendered content.
|
||||
"""
|
||||
html = service.render_template('templates/edxmako.html', {'element_id': 'mako_id'})
|
||||
assert html == expected_html
|
||||
|
||||
@@ -6,6 +6,7 @@ import textwrap
|
||||
from lxml import etree
|
||||
from pkg_resources import resource_string
|
||||
from web_fragments.fragment import Fragment
|
||||
from xblock.core import XBlock
|
||||
from xblock.fields import Scope, String
|
||||
|
||||
from openedx.core.djangolib.markup import HTML, Text
|
||||
@@ -29,6 +30,7 @@ log = logging.getLogger(__name__)
|
||||
_ = lambda text: text
|
||||
|
||||
|
||||
@XBlock.needs('mako')
|
||||
class AnnotatableBlock(
|
||||
RawMixin,
|
||||
XmlMixin,
|
||||
@@ -201,7 +203,7 @@ class AnnotatableBlock(
|
||||
'content_html': self._render_content()
|
||||
}
|
||||
|
||||
return self.system.render_template('annotatable.html', context)
|
||||
return self.runtime.service(self, 'mako').render_template('annotatable.html', context)
|
||||
|
||||
def student_view(self, context): # lint-amnesty, pylint: disable=unused-argument
|
||||
"""
|
||||
@@ -219,7 +221,7 @@ class AnnotatableBlock(
|
||||
Return the studio view.
|
||||
"""
|
||||
fragment = Fragment(
|
||||
self.system.render_template(self.mako_template, self.get_context())
|
||||
self.runtime.service(self, 'mako').render_template(self.mako_template, self.get_context())
|
||||
)
|
||||
add_webpack_to_fragment(fragment, 'AnnotatableBlockStudio')
|
||||
shim_xmodule_js(fragment, self.studio_js_module_name)
|
||||
|
||||
@@ -120,6 +120,7 @@ class Randomization(String):
|
||||
|
||||
@XBlock.needs('user')
|
||||
@XBlock.needs('i18n')
|
||||
@XBlock.needs('mako')
|
||||
@XBlock.wants('call_to_action')
|
||||
class ProblemBlock(
|
||||
ScorableXBlockMixin,
|
||||
@@ -392,7 +393,7 @@ class ProblemBlock(
|
||||
Return the studio view.
|
||||
"""
|
||||
fragment = Fragment(
|
||||
self.system.render_template(self.mako_template, self.get_context())
|
||||
self.runtime.service(self, 'mako').render_template(self.mako_template, self.get_context())
|
||||
)
|
||||
add_webpack_to_fragment(fragment, 'ProblemBlockStudio')
|
||||
shim_xmodule_js(fragment, 'MarkdownEditingDescriptor')
|
||||
@@ -821,7 +822,7 @@ class ProblemBlock(
|
||||
filestore=self.runtime.filestore,
|
||||
i18n=self.runtime.service(self, "i18n"),
|
||||
node_path=self.runtime.node_path,
|
||||
render_template=self.runtime.render_template,
|
||||
render_template=self.runtime.service(self, 'mako').render_template,
|
||||
seed=seed, # Why do we do this if we have self.seed?
|
||||
STATIC_URL=self.runtime.STATIC_URL,
|
||||
xqueue=self.runtime.xqueue,
|
||||
@@ -915,7 +916,7 @@ class ProblemBlock(
|
||||
"""
|
||||
curr_score, total_possible = self.get_display_progress()
|
||||
|
||||
return self.runtime.render_template('problem_ajax.html', {
|
||||
return self.runtime.service(self, 'mako').render_template('problem_ajax.html', {
|
||||
'element_id': self.location.html_id(),
|
||||
'id': str(self.location),
|
||||
'ajax_url': self.ajax_url,
|
||||
@@ -1263,7 +1264,7 @@ class ProblemBlock(
|
||||
'submit_disabled_cta': submit_disabled_ctas[0] if submit_disabled_ctas else None,
|
||||
}
|
||||
|
||||
html = self.runtime.render_template('problem.html', context)
|
||||
html = self.runtime.service(self, 'mako').render_template('problem.html', context)
|
||||
|
||||
if encapsulate:
|
||||
html = HTML('<div id="problem_{id}" class="problem" data-url="{ajax_url}">{html}</div>').format(
|
||||
@@ -1573,7 +1574,7 @@ class ProblemBlock(
|
||||
|
||||
return {
|
||||
'answers': new_answers,
|
||||
'correct_status_html': self.runtime.render_template(
|
||||
'correct_status_html': self.runtime.service(self, 'mako').render_template(
|
||||
'status_span.html',
|
||||
{'status': Status('correct', self.runtime.service(self, "i18n").ugettext)}
|
||||
)
|
||||
|
||||
@@ -11,6 +11,7 @@ from lxml import etree
|
||||
from opaque_keys.edx.locator import BlockUsageLocator
|
||||
from pkg_resources import resource_string
|
||||
from web_fragments.fragment import Fragment
|
||||
from xblock.core import XBlock
|
||||
from xblock.fields import ReferenceList, Scope, String
|
||||
|
||||
from openedx.core.djangolib.markup import HTML, Text
|
||||
@@ -38,6 +39,7 @@ log = logging.getLogger('edx.' + __name__)
|
||||
_ = lambda text: text
|
||||
|
||||
|
||||
@XBlock.needs('mako')
|
||||
class ConditionalBlock(
|
||||
SequenceMixin,
|
||||
MakoTemplateBlockBase,
|
||||
@@ -245,7 +247,7 @@ class ConditionalBlock(
|
||||
|
||||
def get_html(self):
|
||||
required_html_ids = [descriptor.location.html_id() for descriptor in self.get_required_blocks]
|
||||
return self.system.render_template('conditional_ajax.html', {
|
||||
return self.runtime.service(self, 'mako').render_template('conditional_ajax.html', {
|
||||
'element_id': self.location.html_id(),
|
||||
'ajax_url': self.ajax_url,
|
||||
'depends': ';'.join(required_html_ids)
|
||||
@@ -271,7 +273,7 @@ class ConditionalBlock(
|
||||
Return the studio view.
|
||||
"""
|
||||
fragment = Fragment(
|
||||
self.system.render_template(self.mako_template, self.get_context())
|
||||
self.runtime.service(self, 'mako').render_template(self.mako_template, self.get_context())
|
||||
)
|
||||
add_webpack_to_fragment(fragment, 'ConditionalBlockStudio')
|
||||
shim_xmodule_js(fragment, self.studio_js_module_name)
|
||||
@@ -284,8 +286,7 @@ class ConditionalBlock(
|
||||
if not self.is_condition_satisfied():
|
||||
context = {'module': self,
|
||||
'message': self.conditional_message}
|
||||
html = self.system.render_template('conditional_module.html',
|
||||
context)
|
||||
html = self.runtime.service(self, 'mako').render_template('conditional_module.html', context)
|
||||
return json.dumps({'fragments': [{'content': html}], 'message': bool(self.conditional_message)})
|
||||
|
||||
fragments = [child.render(STUDENT_VIEW).to_dict() for child in self.get_display_items()]
|
||||
|
||||
@@ -11,6 +11,7 @@ import sys
|
||||
|
||||
from lxml import etree
|
||||
from web_fragments.fragment import Fragment
|
||||
from xblock.core import XBlock
|
||||
from xblock.field_data import DictFieldData
|
||||
from xblock.fields import Scope, ScopeIds, String
|
||||
|
||||
@@ -43,6 +44,7 @@ class ErrorFields:
|
||||
display_name = String(scope=Scope.settings)
|
||||
|
||||
|
||||
@XBlock.needs('mako')
|
||||
class ErrorBlock(
|
||||
ErrorFields,
|
||||
XModuleDescriptorToXBlockMixin,
|
||||
@@ -62,7 +64,7 @@ class ErrorBlock(
|
||||
"""
|
||||
Return a fragment that contains the html for the student view.
|
||||
"""
|
||||
fragment = Fragment(self.system.render_template('module-error.html', {
|
||||
fragment = Fragment(self.runtime.service(self, 'mako').render_template('module-error.html', {
|
||||
'staff_access': True,
|
||||
'data': self.contents,
|
||||
'error': self.error_msg,
|
||||
@@ -209,7 +211,7 @@ class NonStaffErrorBlock(ErrorBlock): # pylint: disable=abstract-method
|
||||
"""
|
||||
Return a fragment that contains the html for the student view.
|
||||
"""
|
||||
fragment = Fragment(self.system.render_template('module-error.html', {
|
||||
fragment = Fragment(self.runtime.service(self, 'mako').render_template('module-error.html', {
|
||||
'staff_access': False,
|
||||
'data': '',
|
||||
'error': '',
|
||||
|
||||
@@ -43,6 +43,7 @@ _ = lambda text: text
|
||||
|
||||
|
||||
@XBlock.needs("i18n")
|
||||
@XBlock.needs("mako")
|
||||
@XBlock.needs("user")
|
||||
class HtmlBlockMixin( # lint-amnesty, pylint: disable=abstract-method
|
||||
XmlMixin, EditingMixin,
|
||||
@@ -131,7 +132,7 @@ class HtmlBlockMixin( # lint-amnesty, pylint: disable=abstract-method
|
||||
Return the studio view.
|
||||
"""
|
||||
fragment = Fragment(
|
||||
self.system.render_template(self.mako_template, self.get_context())
|
||||
self.runtime.service(self, 'mako').render_template(self.mako_template, self.get_context())
|
||||
)
|
||||
add_webpack_to_fragment(fragment, 'HtmlBlockStudio')
|
||||
shim_xmodule_js(fragment, 'HTMLEditingDescriptor')
|
||||
@@ -484,7 +485,10 @@ class CourseInfoBlock(CourseInfoFields, HtmlBlockMixin): # lint-amnesty, pylint
|
||||
'visible_updates': course_updates[:3],
|
||||
'hidden_updates': course_updates[3:],
|
||||
}
|
||||
return self.system.render_template(f"{self.TEMPLATE_DIR}/course_updates.html", context)
|
||||
return self.runtime.service(self, 'mako').render_template(
|
||||
f"{self.TEMPLATE_DIR}/course_updates.html",
|
||||
context,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def order_updates(self, updates): # lint-amnesty, pylint: disable=bad-classmethod-argument
|
||||
|
||||
@@ -68,6 +68,7 @@ def _get_capa_types():
|
||||
@XBlock.wants('library_tools') # Only needed in studio
|
||||
@XBlock.wants('studio_user_permissions') # Only available in studio
|
||||
@XBlock.wants('user')
|
||||
@XBlock.needs('mako')
|
||||
class LibraryContentBlock(
|
||||
MakoTemplateBlockBase,
|
||||
XmlMixin,
|
||||
@@ -364,7 +365,7 @@ class LibraryContentBlock(
|
||||
'content': rendered_child.content,
|
||||
})
|
||||
|
||||
fragment.add_content(self.system.render_template('vert_module.html', {
|
||||
fragment.add_content(self.runtime.service(self, 'mako').render_template('vert_module.html', {
|
||||
'items': contents,
|
||||
'xblock_context': context,
|
||||
'show_bookmark_button': False,
|
||||
@@ -386,10 +387,11 @@ class LibraryContentBlock(
|
||||
if is_root:
|
||||
# User has clicked the "View" link. Show a preview of all possible children:
|
||||
if self.children: # pylint: disable=no-member
|
||||
fragment.add_content(self.system.render_template("library-block-author-preview-header.html", {
|
||||
'max_count': self.max_count,
|
||||
'display_name': self.display_name or self.url_name,
|
||||
}))
|
||||
fragment.add_content(self.runtime.service(self, 'mako').render_template(
|
||||
"library-block-author-preview-header.html", {
|
||||
'max_count': self.max_count,
|
||||
'display_name': self.display_name or self.url_name,
|
||||
}))
|
||||
context['can_edit_visibility'] = False
|
||||
context['can_move'] = False
|
||||
self.render_children(context, fragment, can_reorder=False, can_add=False)
|
||||
@@ -406,7 +408,7 @@ class LibraryContentBlock(
|
||||
Return the studio view.
|
||||
"""
|
||||
fragment = Fragment(
|
||||
self.system.render_template(self.mako_template, self.get_context())
|
||||
self.runtime.service(self, 'mako').render_template(self.mako_template, self.get_context())
|
||||
)
|
||||
add_webpack_to_fragment(fragment, 'LibraryContentBlockStudio')
|
||||
shim_xmodule_js(fragment, self.studio_js_module_name)
|
||||
|
||||
@@ -17,6 +17,7 @@ log = logging.getLogger(__name__)
|
||||
_ = lambda text: text
|
||||
|
||||
|
||||
@XBlock.needs('mako')
|
||||
class LibraryRoot(XBlock):
|
||||
"""
|
||||
The LibraryRoot is the root XBlock of a content library. All other blocks in
|
||||
@@ -103,7 +104,7 @@ class LibraryRoot(XBlock):
|
||||
})
|
||||
|
||||
fragment.add_content(
|
||||
self.runtime.render_template("studio_render_paged_children_view.html", {
|
||||
self.runtime.service(self, 'mako').render_template("studio_render_paged_children_view.html", {
|
||||
'items': contents,
|
||||
'xblock_context': context,
|
||||
'can_add': can_add,
|
||||
|
||||
@@ -270,6 +270,7 @@ class LTIFields:
|
||||
|
||||
|
||||
@XBlock.needs("i18n")
|
||||
@XBlock.needs("mako")
|
||||
@XBlock.needs("user")
|
||||
class LTIBlock(
|
||||
LTIFields,
|
||||
@@ -401,7 +402,7 @@ class LTIBlock(
|
||||
# Add our specific template information (the raw data body)
|
||||
context.update({'data': self.data})
|
||||
fragment = Fragment(
|
||||
self.system.render_template(self.mako_template, context)
|
||||
self.runtime.service(self, 'mako').render_template(self.mako_template, context)
|
||||
)
|
||||
add_webpack_to_fragment(fragment, 'LTIBlockStudio')
|
||||
shim_xmodule_js(fragment, self.studio_js_module_name)
|
||||
@@ -517,7 +518,7 @@ class LTIBlock(
|
||||
Return the student view.
|
||||
"""
|
||||
fragment = Fragment()
|
||||
fragment.add_content(self.system.render_template('lti.html', self.get_context()))
|
||||
fragment.add_content(self.runtime.service(self, 'mako').render_template('lti.html', self.get_context()))
|
||||
add_webpack_to_fragment(fragment, 'LTIBlockPreview')
|
||||
shim_xmodule_js(fragment, 'LTI')
|
||||
return fragment
|
||||
@@ -527,7 +528,7 @@ class LTIBlock(
|
||||
"""
|
||||
This is called to get context with new oauth params to iframe.
|
||||
"""
|
||||
template = self.system.render_template('lti_form.html', self.get_context())
|
||||
template = self.runtime.service(self, 'mako').render_template('lti_form.html', self.get_context())
|
||||
return Response(template, content_type='text/html')
|
||||
|
||||
def get_user_id(self):
|
||||
|
||||
@@ -9,11 +9,25 @@ from .x_module import DescriptorSystem, XModuleDescriptor, shim_xmodule_js
|
||||
|
||||
|
||||
class MakoDescriptorSystem(DescriptorSystem): # lint-amnesty, pylint: disable=abstract-method
|
||||
"""
|
||||
Descriptor system that renders mako templates.
|
||||
"""
|
||||
def __init__(self, render_template, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
|
||||
self.render_template = render_template
|
||||
|
||||
# Add the MakoService to the descriptor system.
|
||||
#
|
||||
# This is not needed by most XBlocks, because they are initialized with a full runtime ModuleSystem that already
|
||||
# has the MakoService.
|
||||
# However, there are a few cases where the XBlock only has the descriptor system instead of the full module
|
||||
# runtime. Specifically:
|
||||
# * in the Instructor Dashboard bulk emails tab, when rendering the HtmlBlock for its WYSIWYG editor.
|
||||
# * during testing, when using the ModuleSystemTestCase to fetch factory-created blocks.
|
||||
from common.djangoapps.edxmako.services import MakoService
|
||||
self._services['mako'] = MakoService()
|
||||
|
||||
|
||||
class MakoTemplateBlockBase:
|
||||
"""
|
||||
|
||||
@@ -18,6 +18,7 @@ from web_fragments.fragment import Fragment
|
||||
|
||||
from lxml import etree
|
||||
from openedx.core.djangolib.markup import Text, HTML
|
||||
from xblock.core import XBlock
|
||||
from xblock.fields import Boolean, Dict, List, Scope, String # lint-amnesty, pylint: disable=wrong-import-order
|
||||
from xmodule.mako_module import MakoTemplateBlockBase
|
||||
from xmodule.stringify import stringify_children
|
||||
@@ -36,6 +37,7 @@ log = logging.getLogger(__name__)
|
||||
_ = lambda text: text
|
||||
|
||||
|
||||
@XBlock.needs('mako')
|
||||
class PollBlock(
|
||||
MakoTemplateBlockBase,
|
||||
XmlMixin,
|
||||
@@ -162,7 +164,7 @@ class PollBlock(
|
||||
'ajax_url': self.ajax_url,
|
||||
'configuration_json': self.dump_poll(),
|
||||
}
|
||||
fragment.add_content(self.system.render_template('poll.html', params))
|
||||
fragment.add_content(self.runtime.service(self, 'mako').render_template('poll.html', params))
|
||||
add_webpack_to_fragment(fragment, 'PollBlockPreview')
|
||||
shim_xmodule_js(fragment, 'Poll')
|
||||
return fragment
|
||||
|
||||
@@ -244,6 +244,7 @@ class ProctoringFields:
|
||||
@XBlock.needs('user')
|
||||
@XBlock.needs('bookmarks')
|
||||
@XBlock.needs('i18n')
|
||||
@XBlock.needs('mako')
|
||||
@XBlock.wants('content_type_gating')
|
||||
class SequenceBlock(
|
||||
SequenceMixin,
|
||||
@@ -537,7 +538,7 @@ class SequenceBlock(
|
||||
if not self._can_user_view_content(course):
|
||||
banner_text = self._hidden_content_banner_text(course)
|
||||
|
||||
hidden_content_html = self.system.render_template(
|
||||
hidden_content_html = self.runtime.service(self, 'mako').render_template(
|
||||
'hidden_content.html',
|
||||
{
|
||||
'self_paced': course.self_paced,
|
||||
@@ -606,7 +607,7 @@ class SequenceBlock(
|
||||
|
||||
fragment = Fragment()
|
||||
params = self._get_render_metadata(context, display_items, prereq_met, prereq_meta_info, banner_text, view, fragment) # lint-amnesty, pylint: disable=line-too-long
|
||||
fragment.add_content(self.system.render_template("seq_module.html", params))
|
||||
fragment.add_content(self.runtime.service(self, 'mako').render_template("seq_module.html", params))
|
||||
|
||||
self._capture_full_seq_item_metrics(display_items)
|
||||
self._capture_current_unit_metrics(display_items)
|
||||
|
||||
@@ -123,6 +123,7 @@ def get_split_user_partitions(user_partitions):
|
||||
|
||||
@XBlock.needs("i18n")
|
||||
@XBlock.needs('user_tags') # pylint: disable=abstract-method
|
||||
@XBlock.needs('mako')
|
||||
@XBlock.needs('partitions')
|
||||
@XBlock.needs('user')
|
||||
class SplitTestBlock( # lint-amnesty, pylint: disable=abstract-method
|
||||
@@ -301,7 +302,7 @@ class SplitTestBlock( # lint-amnesty, pylint: disable=abstract-method
|
||||
sorted_inactive_contents = sorted(inactive_contents, key=itemgetter('group_name'))
|
||||
|
||||
# Use the new template
|
||||
fragment.add_content(self.system.render_template('split_test_staff_view.html', {
|
||||
fragment.add_content(self.runtime.service(self, 'mako').render_template('split_test_staff_view.html', {
|
||||
'items': sorted_active_contents + sorted_inactive_contents,
|
||||
}))
|
||||
fragment.add_css('.split-test-child { display: none; }')
|
||||
@@ -328,7 +329,7 @@ class SplitTestBlock( # lint-amnesty, pylint: disable=abstract-method
|
||||
fragment, inactive_children, context
|
||||
)
|
||||
|
||||
fragment.add_content(self.system.render_template('split_test_author_view.html', {
|
||||
fragment.add_content(self.runtime.service(self, 'mako').render_template('split_test_author_view.html', {
|
||||
'split_test': self,
|
||||
'is_root': is_root,
|
||||
'is_configured': self.is_configured,
|
||||
@@ -367,7 +368,7 @@ class SplitTestBlock( # lint-amnesty, pylint: disable=abstract-method
|
||||
Return the studio view.
|
||||
"""
|
||||
fragment = Fragment(
|
||||
self.system.render_template(self.mako_template, self.get_context())
|
||||
self.runtime.service(self, 'mako').render_template(self.mako_template, self.get_context())
|
||||
)
|
||||
add_webpack_to_fragment(fragment, 'SplitTestBlockStudio')
|
||||
shim_xmodule_js(fragment, self.studio_js_module_name)
|
||||
@@ -386,7 +387,7 @@ class SplitTestBlock( # lint-amnesty, pylint: disable=abstract-method
|
||||
return self._staff_view(context)
|
||||
else:
|
||||
child_fragment = self.child.render(STUDENT_VIEW, context)
|
||||
fragment = Fragment(self.system.render_template('split_test_student_view.html', {
|
||||
fragment = Fragment(self.runtime.service(self, 'mako').render_template('split_test_student_view.html', {
|
||||
'child_content': child_fragment.content,
|
||||
'child_id': self.child.scope_ids.usage_id,
|
||||
}))
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
"""
|
||||
Mixin to support editing in Studio.
|
||||
"""
|
||||
from xblock.core import XBlock
|
||||
from xmodule.x_module import AUTHOR_VIEW, STUDENT_VIEW, module_attr
|
||||
|
||||
|
||||
@XBlock.needs('mako')
|
||||
class StudioEditableBlock:
|
||||
"""
|
||||
Helper methods for supporting Studio editing of XBlocks.
|
||||
|
||||
This class is only intended to be used with an XBlock!
|
||||
"""
|
||||
# Avoids AttributeError caused by the @XBlock.needs decorator.
|
||||
_services_requested = {}
|
||||
|
||||
has_author_view = True
|
||||
|
||||
def render_children(self, context, fragment, can_reorder=False, can_add=False):
|
||||
@@ -31,7 +36,7 @@ class StudioEditableBlock:
|
||||
'content': rendered_child.content
|
||||
})
|
||||
|
||||
fragment.add_content(self.system.render_template("studio_render_children_view.html", { # pylint: disable=no-member
|
||||
fragment.add_content(self.runtime.service(self, 'mako').render_template("studio_render_children_view.html", { # pylint: disable=no-member
|
||||
'items': contents,
|
||||
'xblock_context': context,
|
||||
'can_add': can_add,
|
||||
|
||||
@@ -3,6 +3,7 @@ Template module
|
||||
"""
|
||||
|
||||
from string import Template
|
||||
from xblock.core import XBlock
|
||||
|
||||
from lxml import etree
|
||||
from pkg_resources import resource_string
|
||||
@@ -40,6 +41,7 @@ class CustomTagTemplateBlock( # pylint: disable=abstract-method
|
||||
"""
|
||||
|
||||
|
||||
@XBlock.needs('mako')
|
||||
class CustomTagBlock(CustomTagTemplateBlock): # pylint: disable=abstract-method
|
||||
"""
|
||||
This module supports tags of the form
|
||||
@@ -85,7 +87,7 @@ class CustomTagBlock(CustomTagTemplateBlock): # pylint: disable=abstract-method
|
||||
Return the studio view.
|
||||
"""
|
||||
fragment = Fragment(
|
||||
self.system.render_template(self.mako_template, self.get_context())
|
||||
self.runtime.service(self, 'mako').render_template(self.mako_template, self.get_context())
|
||||
)
|
||||
add_webpack_to_fragment(fragment, 'CustomTagBlockStudio')
|
||||
shim_xmodule_js(fragment, 'XMLEditingDescriptor')
|
||||
|
||||
@@ -11,7 +11,6 @@ Run like this:
|
||||
import inspect
|
||||
import json
|
||||
import os
|
||||
import pprint
|
||||
import sys
|
||||
import traceback
|
||||
import unittest
|
||||
@@ -34,7 +33,7 @@ from xmodule.modulestore import ModuleStoreEnum
|
||||
from xmodule.modulestore.draft_and_published import ModuleStoreDraftAndPublished
|
||||
from xmodule.modulestore.inheritance import InheritanceMixin
|
||||
from xmodule.modulestore.xml import CourseLocationManager
|
||||
from xmodule.tests.helpers import StubUserService
|
||||
from xmodule.tests.helpers import mock_render_template, StubMakoService, StubUserService
|
||||
from xmodule.x_module import ModuleSystem, XModuleDescriptor, XModuleMixin
|
||||
|
||||
|
||||
@@ -93,18 +92,13 @@ def get_test_system(
|
||||
course_id=CourseKey.from_string('/'.join(['org', 'course', 'run'])),
|
||||
user=None,
|
||||
user_is_staff=False,
|
||||
render_template=None,
|
||||
):
|
||||
"""
|
||||
Construct a test ModuleSystem instance.
|
||||
|
||||
By default, the render_template() method simply returns the repr of the
|
||||
context it is passed. You can override this behavior by monkey patching::
|
||||
|
||||
system = get_test_system()
|
||||
system.render_template = my_render_func
|
||||
|
||||
where `my_render_func` is a function of the form my_render_func(template, context).
|
||||
|
||||
By default, the descriptor system's render_template() method simply returns the repr of the
|
||||
context it is passed. You can override this by passing in a different render_template argument.
|
||||
"""
|
||||
if not user:
|
||||
user = Mock(name='get_test_system.user', is_staff=False)
|
||||
@@ -114,6 +108,8 @@ def get_test_system(
|
||||
user_is_staff=user_is_staff,
|
||||
)
|
||||
|
||||
mako_service = StubMakoService(render_template=render_template)
|
||||
|
||||
descriptor_system = get_test_descriptor_system()
|
||||
|
||||
def get_module(descriptor):
|
||||
@@ -136,7 +132,6 @@ def get_test_system(
|
||||
static_url='/static',
|
||||
track_function=Mock(name='get_test_system.track_function'),
|
||||
get_module=get_module,
|
||||
render_template=mock_render_template,
|
||||
replace_urls=str,
|
||||
get_real_user=lambda __: user,
|
||||
filestore=Mock(name='get_test_system.filestore', root_path='.'),
|
||||
@@ -144,6 +139,7 @@ def get_test_system(
|
||||
hostname="edx.org",
|
||||
services={
|
||||
'user': user_service,
|
||||
'mako': mako_service,
|
||||
},
|
||||
xqueue={
|
||||
'interface': None,
|
||||
@@ -161,7 +157,7 @@ def get_test_system(
|
||||
)
|
||||
|
||||
|
||||
def get_test_descriptor_system():
|
||||
def get_test_descriptor_system(render_template=None):
|
||||
"""
|
||||
Construct a test DescriptorSystem instance.
|
||||
"""
|
||||
@@ -171,7 +167,7 @@ def get_test_descriptor_system():
|
||||
load_item=Mock(name='get_test_descriptor_system.load_item'),
|
||||
resources_fs=Mock(name='get_test_descriptor_system.resources_fs'),
|
||||
error_tracker=Mock(name='get_test_descriptor_system.error_tracker'),
|
||||
render_template=mock_render_template,
|
||||
render_template=render_template or mock_render_template,
|
||||
mixins=(InheritanceMixin, XModuleMixin),
|
||||
field_data=field_data,
|
||||
services={'field-data': field_data},
|
||||
@@ -180,16 +176,6 @@ def get_test_descriptor_system():
|
||||
return descriptor_system
|
||||
|
||||
|
||||
def mock_render_template(*args, **kwargs):
|
||||
"""
|
||||
Pretty-print the args and kwargs.
|
||||
|
||||
Allows us to not depend on any actual template rendering mechanism,
|
||||
while still returning a unicode object
|
||||
"""
|
||||
return pprint.pformat((args, kwargs)).encode().decode()
|
||||
|
||||
|
||||
class ModelsTest(unittest.TestCase): # lint-amnesty, pylint: disable=missing-class-docstring
|
||||
|
||||
def test_load_class(self):
|
||||
|
||||
@@ -5,6 +5,7 @@ Utility methods for unit tests.
|
||||
|
||||
import filecmp
|
||||
from unittest.mock import Mock
|
||||
import pprint
|
||||
|
||||
from path import Path as path
|
||||
from xblock.reference.user_service import UserService, XBlockUser
|
||||
@@ -30,6 +31,31 @@ def directories_equal(directory1, directory2):
|
||||
return compare_dirs(path(directory1), path(directory2))
|
||||
|
||||
|
||||
def mock_render_template(*args, **kwargs):
|
||||
"""
|
||||
Pretty-print the args and kwargs.
|
||||
|
||||
Allows us to not depend on any actual template rendering mechanism,
|
||||
while still returning a unicode object
|
||||
"""
|
||||
return pprint.pformat((args, kwargs)).encode().decode()
|
||||
|
||||
|
||||
class StubMakoService:
|
||||
"""
|
||||
Stub MakoService for testing modules that use mako templates.
|
||||
"""
|
||||
|
||||
def __init__(self, render_template=None):
|
||||
self._render_template = render_template or mock_render_template
|
||||
|
||||
def render_template(self, *args, **kwargs):
|
||||
"""
|
||||
Invokes the configured render_template method.
|
||||
"""
|
||||
return self._render_template(*args, **kwargs)
|
||||
|
||||
|
||||
class StubUserService(UserService):
|
||||
"""
|
||||
Stub UserService for testing the sequence module.
|
||||
|
||||
@@ -79,7 +79,8 @@ class CapaFactory:
|
||||
response_num, input_num))
|
||||
|
||||
@classmethod
|
||||
def create(cls, attempts=None, problem_state=None, correct=False, xml=None, override_get_score=True, **kwargs):
|
||||
def create(cls, attempts=None, problem_state=None, correct=False, xml=None, override_get_score=True,
|
||||
render_template=None, **kwargs):
|
||||
"""
|
||||
All parameters are optional, and are added to the created problem if specified.
|
||||
|
||||
@@ -95,6 +96,8 @@ class CapaFactory:
|
||||
module.
|
||||
|
||||
attempts: also added to instance state. Will be converted to an int.
|
||||
|
||||
render_template: pass function or Mock for testing
|
||||
"""
|
||||
location = BlockUsageLocator(
|
||||
CourseLocator("edX", "capa_test", "2012_Fall", deprecated=True),
|
||||
@@ -113,8 +116,11 @@ class CapaFactory:
|
||||
# since everything else is a string.
|
||||
field_data['attempts'] = int(attempts)
|
||||
|
||||
system = get_test_system(course_id=location.course_key, user_is_staff=kwargs.get('user_is_staff', False))
|
||||
system.render_template = Mock(return_value="<div>Test Template HTML</div>")
|
||||
system = get_test_system(
|
||||
course_id=location.course_key,
|
||||
user_is_staff=kwargs.get('user_is_staff', False),
|
||||
render_template=render_template or Mock(return_value="<div>Test Template HTML</div>"),
|
||||
)
|
||||
module = ProblemBlock(
|
||||
system,
|
||||
DictFieldData(field_data),
|
||||
@@ -1520,7 +1526,8 @@ class ProblemBlockTest(unittest.TestCase): # lint-amnesty, pylint: disable=miss
|
||||
# assert that we got here without exploding
|
||||
|
||||
def test_get_problem_html(self):
|
||||
module = CapaFactory.create()
|
||||
render_template = Mock(return_value="<div>Test Template HTML</div>")
|
||||
module = CapaFactory.create(render_template=render_template)
|
||||
|
||||
# We've tested the show/hide button logic in other tests,
|
||||
# so here we hard-wire the values
|
||||
@@ -1532,9 +1539,6 @@ class ProblemBlockTest(unittest.TestCase): # lint-amnesty, pylint: disable=miss
|
||||
module.should_show_reset_button = Mock(return_value=show_reset_button)
|
||||
module.should_show_save_button = Mock(return_value=show_save_button)
|
||||
|
||||
# Mock the system rendering function
|
||||
module.system.render_template = Mock(return_value="<div>Test Template HTML</div>")
|
||||
|
||||
# Patch the capa problem's HTML rendering
|
||||
with patch('capa.capa_problem.LoncapaProblem.get_html') as mock_html:
|
||||
mock_html.return_value = "<div>Test Problem HTML</div>"
|
||||
@@ -1549,7 +1553,7 @@ class ProblemBlockTest(unittest.TestCase): # lint-amnesty, pylint: disable=miss
|
||||
assert html == '<div>Test Template HTML</div>'
|
||||
|
||||
# Check the rendering context
|
||||
render_args, _ = module.system.render_template.call_args
|
||||
render_args, _ = render_template.call_args
|
||||
assert len(render_args) == 2
|
||||
|
||||
template_name = render_args[0]
|
||||
@@ -1584,9 +1588,10 @@ class ProblemBlockTest(unittest.TestCase): # lint-amnesty, pylint: disable=miss
|
||||
def test_demand_hint(self):
|
||||
# HTML generation is mocked out to be meaningless here, so instead we check
|
||||
# the context dict passed into HTML generation.
|
||||
module = CapaFactory.create(xml=self.demand_xml)
|
||||
render_template = Mock(return_value="<div>Test Template HTML</div>")
|
||||
module = CapaFactory.create(xml=self.demand_xml, render_template=render_template)
|
||||
module.get_problem_html() # ignoring html result
|
||||
context = module.system.render_template.call_args[0][1]
|
||||
context = render_template.call_args[0][1]
|
||||
assert context['demand_hint_possible']
|
||||
assert context['should_enable_next_hint']
|
||||
|
||||
@@ -1621,9 +1626,10 @@ class ProblemBlockTest(unittest.TestCase): # lint-amnesty, pylint: disable=miss
|
||||
<hint>Only demand hint</hint>
|
||||
</demandhint>
|
||||
</problem>"""
|
||||
module = CapaFactory.create(xml=test_xml)
|
||||
render_template = Mock(return_value="<div>Test Template HTML</div>")
|
||||
module = CapaFactory.create(xml=test_xml, render_template=render_template)
|
||||
module.get_problem_html() # ignoring html result
|
||||
context = module.system.render_template.call_args[0][1]
|
||||
context = render_template.call_args[0][1]
|
||||
assert context['demand_hint_possible']
|
||||
assert context['should_enable_next_hint']
|
||||
|
||||
@@ -1652,9 +1658,10 @@ class ProblemBlockTest(unittest.TestCase): # lint-amnesty, pylint: disable=miss
|
||||
You can add an optional hint like this. Problems that have a hint include a hint button, and this text appears the first time learners select the button.</hint>
|
||||
</demandhint>
|
||||
</problem>"""
|
||||
module = CapaFactory.create(xml=test_xml)
|
||||
render_template = Mock(return_value="<div>Test Template HTML</div>")
|
||||
module = CapaFactory.create(xml=test_xml, render_template=render_template)
|
||||
module.get_problem_html() # ignoring html result
|
||||
context = module.system.render_template.call_args[0][1]
|
||||
context = render_template.call_args[0][1]
|
||||
assert context['demand_hint_possible']
|
||||
assert context['should_enable_next_hint']
|
||||
|
||||
@@ -1696,7 +1703,8 @@ class ProblemBlockTest(unittest.TestCase): # lint-amnesty, pylint: disable=miss
|
||||
rendering, a "dummy" problem is created with an error
|
||||
message to display to the user.
|
||||
"""
|
||||
module = CapaFactory.create()
|
||||
render_template = Mock(return_value="<div>Test Template HTML</div>")
|
||||
module = CapaFactory.create(render_template=render_template)
|
||||
|
||||
# Save the original problem so we can compare it later
|
||||
original_problem = module.lcp
|
||||
@@ -1705,9 +1713,6 @@ class ProblemBlockTest(unittest.TestCase): # lint-amnesty, pylint: disable=miss
|
||||
# is asked to render itself as HTML
|
||||
module.lcp.get_html = Mock(side_effect=Exception("Test"))
|
||||
|
||||
# Stub out the get_test_system rendering function
|
||||
module.system.render_template = Mock(return_value="<div>Test Template HTML</div>")
|
||||
|
||||
# Turn off DEBUG
|
||||
module.system.DEBUG = False
|
||||
|
||||
@@ -1717,7 +1722,7 @@ class ProblemBlockTest(unittest.TestCase): # lint-amnesty, pylint: disable=miss
|
||||
assert html is not None
|
||||
|
||||
# Check the rendering context
|
||||
render_args, _ = module.system.render_template.call_args
|
||||
render_args, _ = render_template.call_args
|
||||
context = render_args[1]
|
||||
assert 'error' in context['problem']['html']
|
||||
|
||||
@@ -1728,16 +1733,14 @@ class ProblemBlockTest(unittest.TestCase): # lint-amnesty, pylint: disable=miss
|
||||
"""
|
||||
Test the html response when an error occurs with DEBUG on
|
||||
"""
|
||||
module = CapaFactory.create()
|
||||
render_template = Mock(return_value="<div>Test Template HTML</div>")
|
||||
module = CapaFactory.create(render_template=render_template)
|
||||
|
||||
# Simulate throwing an exception when the capa problem
|
||||
# is asked to render itself as HTML
|
||||
error_msg = "Superterrible error happened: ☠"
|
||||
module.lcp.get_html = Mock(side_effect=Exception(error_msg))
|
||||
|
||||
# Stub out the get_test_system rendering function
|
||||
module.system.render_template = Mock(return_value="<div>Test Template HTML</div>")
|
||||
|
||||
# Make sure DEBUG is on
|
||||
module.system.DEBUG = True
|
||||
|
||||
@@ -1747,7 +1750,7 @@ class ProblemBlockTest(unittest.TestCase): # lint-amnesty, pylint: disable=miss
|
||||
assert html is not None
|
||||
|
||||
# Check the rendering context
|
||||
render_args, _ = module.system.render_template.call_args
|
||||
render_args, _ = render_template.call_args
|
||||
context = render_args[1]
|
||||
assert error_msg in context['problem']['html']
|
||||
|
||||
@@ -2111,9 +2114,10 @@ class ProblemBlockTest(unittest.TestCase): # lint-amnesty, pylint: disable=miss
|
||||
"""
|
||||
Verify that if problem display name is not provided then a default name is used.
|
||||
"""
|
||||
module = CapaFactory.create(display_name=display_name)
|
||||
render_template = Mock(return_value="<div>Test Template HTML</div>")
|
||||
module = CapaFactory.create(display_name=display_name, render_template=render_template)
|
||||
module.get_problem_html()
|
||||
render_args, _ = module.system.render_template.call_args
|
||||
render_args, _ = render_template.call_args
|
||||
context = render_args[1]
|
||||
assert context['problem']['name'] == module.location.block_type
|
||||
|
||||
|
||||
@@ -166,7 +166,8 @@ class ConditionalBlockBasicTest(unittest.TestCase):
|
||||
# because get_test_system returns the repr of the context dict passed to render_template,
|
||||
# we reverse it here
|
||||
html = modules['cond_module'].render(STUDENT_VIEW).content
|
||||
expected = modules['cond_module'].xmodule_runtime.render_template('conditional_ajax.html', {
|
||||
mako_service = modules['cond_module'].xmodule_runtime.service(modules['cond_module'], 'mako')
|
||||
expected = mako_service.render_template('conditional_ajax.html', {
|
||||
'ajax_url': modules['cond_module'].ajax_url,
|
||||
'element_id': 'i4x-edX-conditional_test-conditional-SampleConditional',
|
||||
'depends': 'i4x-edX-conditional_test-problem-SampleProblem',
|
||||
@@ -241,7 +242,8 @@ class ConditionalBlockXmlTest(unittest.TestCase):
|
||||
|
||||
module = self.get_module_for_location(location)
|
||||
html = module.render(STUDENT_VIEW).content
|
||||
html_expect = module.xmodule_runtime.render_template(
|
||||
mako_service = module.xmodule_runtime.service(module, 'mako')
|
||||
html_expect = mako_service.render_template(
|
||||
'conditional_ajax.html',
|
||||
{
|
||||
# Test ajax url is just usage-id / handler_name
|
||||
|
||||
@@ -103,8 +103,7 @@ class CapaFactoryWithDelay:
|
||||
# since everything else is a string.
|
||||
field_data['attempts'] = int(attempts)
|
||||
|
||||
system = get_test_system()
|
||||
system.render_template = Mock(return_value="<div>Test Template HTML</div>")
|
||||
system = get_test_system(render_template=Mock(return_value="<div>Test Template HTML</div>"))
|
||||
module = ProblemBlock(
|
||||
system,
|
||||
DictFieldData(field_data),
|
||||
|
||||
@@ -22,8 +22,7 @@ class TabsEditingDescriptorTestCase(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
system = get_test_descriptor_system()
|
||||
system.render_template = Mock(return_value="<div>Test Template HTML</div>")
|
||||
system = get_test_descriptor_system(render_template=Mock())
|
||||
self.tabs = [
|
||||
{
|
||||
'name': "Test_css",
|
||||
|
||||
@@ -315,7 +315,7 @@ class CourseInfoBlockTestCase(unittest.TestCase):
|
||||
template_name = f"{info_module.TEMPLATE_DIR}/course_updates.html"
|
||||
info_module.get_html()
|
||||
# Assertion to validate that render function is called with the expected context
|
||||
info_module.system.render_template.assert_called_once_with(
|
||||
info_module.runtime.service(info_module, 'mako').render_template.assert_called_once_with(
|
||||
template_name,
|
||||
expected_context
|
||||
)
|
||||
|
||||
@@ -347,8 +347,7 @@ class EditableMetadataFieldsTest(unittest.TestCase):
|
||||
non_editable_fields.append(TestModuleDescriptor.due)
|
||||
return non_editable_fields
|
||||
|
||||
system = get_test_descriptor_system()
|
||||
system.render_template = Mock(return_value="<div>Test Template HTML</div>")
|
||||
system = get_test_descriptor_system(render_template=Mock())
|
||||
return system.construct_xblock_from_class(TestModuleDescriptor, field_data=field_data, scope_ids=Mock())
|
||||
|
||||
def assert_field_values(self, editable_fields, name, field, explicitly_set, value, default_value, # lint-amnesty, pylint: disable=dangerous-default-value
|
||||
|
||||
@@ -48,7 +48,7 @@ class VerticalFields:
|
||||
)
|
||||
|
||||
|
||||
@XBlock.needs('user', 'bookmarks')
|
||||
@XBlock.needs('user', 'bookmarks', 'mako')
|
||||
@XBlock.wants('completion')
|
||||
@XBlock.wants('call_to_action')
|
||||
class VerticalBlock(
|
||||
@@ -151,7 +151,7 @@ class VerticalBlock(
|
||||
child_context['username'], str(self.location)), # pylint: disable=no-member
|
||||
})
|
||||
|
||||
fragment.add_content(self.system.render_template('vert_module.html', fragment_context))
|
||||
fragment.add_content(self.runtime.service(self, 'mako').render_template('vert_module.html', fragment_context))
|
||||
|
||||
add_webpack_to_fragment(fragment, 'VerticalStudentView')
|
||||
fragment.initialize_js('VerticalStudentView')
|
||||
|
||||
@@ -108,6 +108,7 @@ EXPORT_IMPORT_STATIC_DIR = 'static'
|
||||
|
||||
|
||||
@XBlock.wants('settings', 'completion', 'i18n', 'request_cache')
|
||||
@XBlock.needs('mako')
|
||||
class VideoBlock(
|
||||
VideoFields, VideoTranscriptsMixin, VideoStudioViewHandlers, VideoStudentViewHandlers,
|
||||
TabsEditingMixin, EmptyDataRawMixin, XmlMixin, EditingMixin,
|
||||
@@ -245,7 +246,7 @@ class VideoBlock(
|
||||
Return the studio view.
|
||||
"""
|
||||
fragment = Fragment(
|
||||
self.system.render_template(self.mako_template, self.get_context())
|
||||
self.runtime.service(self, 'mako').render_template(self.mako_template, self.get_context())
|
||||
)
|
||||
add_webpack_to_fragment(fragment, 'VideoBlockStudio')
|
||||
shim_xmodule_js(fragment, 'TabsEditingDescriptor')
|
||||
@@ -468,7 +469,7 @@ class VideoBlock(
|
||||
'transcript_download_formats_list': self.fields['transcript_download_format'].values, # lint-amnesty, pylint: disable=unsubscriptable-object
|
||||
'license': getattr(self, "license", None),
|
||||
}
|
||||
return self.system.render_template('video.html', context)
|
||||
return self.runtime.service(self, 'mako').render_template('video.html', context)
|
||||
|
||||
def validate(self):
|
||||
"""
|
||||
|
||||
@@ -44,6 +44,7 @@ def pretty_bool(value):
|
||||
return value in bool_dict
|
||||
|
||||
|
||||
@XBlock.needs('mako')
|
||||
class WordCloudBlock( # pylint: disable=abstract-method
|
||||
EmptyDataRawMixin,
|
||||
XmlMixin,
|
||||
@@ -279,7 +280,7 @@ class WordCloudBlock( # pylint: disable=abstract-method
|
||||
Renders the output that a student will see.
|
||||
"""
|
||||
fragment = Fragment()
|
||||
fragment.add_content(self.system.render_template('word_cloud.html', {
|
||||
fragment.add_content(self.runtime.service(self, 'mako').render_template('word_cloud.html', {
|
||||
'ajax_url': self.ajax_url,
|
||||
'display_name': self.display_name,
|
||||
'instructions': self.instructions,
|
||||
@@ -304,7 +305,7 @@ class WordCloudBlock( # pylint: disable=abstract-method
|
||||
Return the studio view.
|
||||
"""
|
||||
fragment = Fragment(
|
||||
self.system.render_template(self.mako_template, self.get_context())
|
||||
self.runtime.service(self, 'mako').render_template(self.mako_template, self.get_context())
|
||||
)
|
||||
add_webpack_to_fragment(fragment, 'WordCloudBlockStudio')
|
||||
shim_xmodule_js(fragment, self.studio_js_module_name)
|
||||
|
||||
@@ -1809,6 +1809,23 @@ class ModuleSystemShim:
|
||||
return self._services['user'].get_current_user().opt_attrs.get(ATTR_KEY_USER_IS_STAFF)
|
||||
return None
|
||||
|
||||
@property
|
||||
def render_template(self):
|
||||
"""
|
||||
Returns a function that takes (template_file, context), and returns rendered html.
|
||||
|
||||
Deprecated in favor of the mako service.
|
||||
"""
|
||||
warnings.warn(
|
||||
'Use of runtime.render_template is deprecated. '
|
||||
'Use MakoService.render_template or a JavaScript-based template instead.',
|
||||
DeprecationWarning, stacklevel=2,
|
||||
)
|
||||
render_service = self._services.get('mako')
|
||||
if render_service:
|
||||
return render_service.render_template
|
||||
return None
|
||||
|
||||
|
||||
class ModuleSystem(MetricsMixin, ConfigurableFragmentWrapper, ModuleSystemShim, Runtime):
|
||||
"""
|
||||
@@ -1824,7 +1841,7 @@ class ModuleSystem(MetricsMixin, ConfigurableFragmentWrapper, ModuleSystemShim,
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, static_url, track_function, get_module, render_template,
|
||||
self, static_url, track_function, get_module,
|
||||
replace_urls, descriptor_runtime, filestore=None,
|
||||
debug=False, hostname="", xqueue=None, publish=None, node_path="",
|
||||
course_id=None,
|
||||
@@ -1846,9 +1863,6 @@ class ModuleSystem(MetricsMixin, ConfigurableFragmentWrapper, ModuleSystemShim,
|
||||
module instance object. If the current user does not have
|
||||
access to that location, returns None.
|
||||
|
||||
render_template - a function that takes (template_file, context), and
|
||||
returns rendered html.
|
||||
|
||||
filestore - A filestore ojbect. Defaults to an instance of OSFS based
|
||||
at settings.DATA_DIR.
|
||||
|
||||
@@ -1904,7 +1918,6 @@ class ModuleSystem(MetricsMixin, ConfigurableFragmentWrapper, ModuleSystemShim,
|
||||
self.track_function = track_function
|
||||
self.filestore = filestore
|
||||
self.get_module = get_module
|
||||
self.render_template = render_template
|
||||
self.DEBUG = self.debug = debug
|
||||
self.HOSTNAME = self.hostname = hostname
|
||||
self.replace_urls = replace_urls
|
||||
|
||||
1
common/test/templates/edxmako.html
Normal file
1
common/test/templates/edxmako.html
Normal file
@@ -0,0 +1 @@
|
||||
<%page expression_filter="h"/><div id="${element_id}" ns="main">Testing the MakoService</div>
|
||||
@@ -52,7 +52,6 @@ from lms.djangoapps.courseware.masquerade import (
|
||||
setup_masquerade
|
||||
)
|
||||
from lms.djangoapps.courseware.model_data import DjangoKeyValueStore, FieldDataCache
|
||||
from common.djangoapps.edxmako.shortcuts import render_to_string
|
||||
from lms.djangoapps.courseware.field_overrides import OverrideFieldData
|
||||
from lms.djangoapps.courseware.services import UserStateService
|
||||
from lms.djangoapps.grades.api import GradesUtilService
|
||||
@@ -90,6 +89,7 @@ from common.djangoapps.student.roles import CourseBetaTesterRole
|
||||
from common.djangoapps.track import contexts
|
||||
from common.djangoapps.util import milestones_helpers
|
||||
from common.djangoapps.util.json_request import JsonResponse
|
||||
from common.djangoapps.edxmako.services import MakoService
|
||||
from common.djangoapps.xblock_django.user_service import DjangoXBlockUserService
|
||||
from xmodule.contentstore.django import contentstore
|
||||
from xmodule.error_module import ErrorBlock, NonStaffErrorBlock
|
||||
@@ -703,8 +703,9 @@ def get_module_system_for_user(
|
||||
if is_masquerading_as_specific_student(user, course_id):
|
||||
block_wrappers.append(filter_displayed_blocks)
|
||||
|
||||
mako_service = MakoService()
|
||||
if settings.FEATURES.get("LICENSING", False):
|
||||
block_wrappers.append(wrap_with_license)
|
||||
block_wrappers.append(partial(wrap_with_license, mako_service=mako_service))
|
||||
|
||||
# Wrap the output display in a single div to allow for the XModule
|
||||
# javascript to be bound correctly
|
||||
@@ -770,7 +771,6 @@ def get_module_system_for_user(
|
||||
|
||||
system = LmsModuleSystem(
|
||||
track_function=track_function,
|
||||
render_template=render_to_string,
|
||||
static_url=settings.STATIC_URL,
|
||||
xqueue=xqueue,
|
||||
# TODO (cpennington): Figure out how to share info between systems
|
||||
@@ -810,6 +810,7 @@ def get_module_system_for_user(
|
||||
services={
|
||||
'fs': FSService(),
|
||||
'field-data': field_data,
|
||||
'mako': mako_service,
|
||||
'user': user_service,
|
||||
'verification': XBlockVerificationService(),
|
||||
'proctoring': ProctoringService(),
|
||||
|
||||
@@ -7,6 +7,7 @@ import ast
|
||||
import json
|
||||
from collections import OrderedDict
|
||||
from datetime import timedelta
|
||||
from unittest.mock import Mock
|
||||
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user
|
||||
@@ -63,11 +64,11 @@ class BaseTestXmodule(ModuleStoreTestCase):
|
||||
METADATA = {}
|
||||
MODEL_DATA = {'data': '<some_module></some_module>'}
|
||||
|
||||
def new_module_runtime(self):
|
||||
def new_module_runtime(self, render_template=None):
|
||||
"""
|
||||
Generate a new ModuleSystem that is minimally set up for testing
|
||||
"""
|
||||
return get_test_system(course_id=self.course.id)
|
||||
return get_test_system(course_id=self.course.id, render_template=render_template)
|
||||
|
||||
def new_descriptor_runtime(self):
|
||||
runtime = get_test_descriptor_system()
|
||||
@@ -143,12 +144,14 @@ class BaseTestXmodule(ModuleStoreTestCase):
|
||||
|
||||
class XModuleRenderingTestBase(BaseTestXmodule): # lint-amnesty, pylint: disable=missing-class-docstring
|
||||
|
||||
def new_module_runtime(self):
|
||||
def new_module_runtime(self, render_template=None):
|
||||
"""
|
||||
Create a runtime that actually does html rendering
|
||||
"""
|
||||
runtime = super().new_module_runtime()
|
||||
runtime.render_template = render_to_string
|
||||
if not render_template:
|
||||
render_template = render_to_string
|
||||
runtime = super().new_module_runtime(render_template=render_template)
|
||||
runtime.modulestore = Mock()
|
||||
return runtime
|
||||
|
||||
|
||||
|
||||
@@ -42,7 +42,6 @@ class TestDiscussionXBlock(XModuleRenderingTestBase):
|
||||
self.patchers = []
|
||||
self.course_id = "test_course"
|
||||
self.runtime = self.new_module_runtime()
|
||||
self.runtime.modulestore = mock.Mock()
|
||||
|
||||
self.discussion_id = str(uuid.uuid4())
|
||||
self.data = DictFieldData({
|
||||
@@ -131,7 +130,8 @@ class TestViews(TestDiscussionXBlock):
|
||||
self.template_canary = 'canary'
|
||||
self.render_template = mock.Mock()
|
||||
self.render_template.return_value = self.template_canary
|
||||
self.block.runtime.render_template = self.render_template
|
||||
self.runtime = self.new_module_runtime(render_template=self.render_template)
|
||||
self.block.runtime = self.runtime
|
||||
self.has_permission_mock = mock.Mock()
|
||||
self.has_permission_mock.return_value = False
|
||||
self.block.has_permission = self.has_permission_mock
|
||||
|
||||
@@ -2687,3 +2687,17 @@ class LmsModuleSystemShimTest(SharedModuleStoreTestCase):
|
||||
assert runtime.seed == 0
|
||||
assert runtime.user_id is None
|
||||
assert not runtime.user_is_staff
|
||||
|
||||
def test_render_template(self):
|
||||
runtime, _ = render.get_module_system_for_user(
|
||||
self.user,
|
||||
self.student_data,
|
||||
self.descriptor,
|
||||
self.course.id,
|
||||
self.track_function,
|
||||
self.xqueue_callback_url_prefix,
|
||||
self.request_token,
|
||||
course=self.course,
|
||||
)
|
||||
rendered = runtime.render_template('templates/edxmako.html', {'element_id': 'hi'}) # pylint: disable=not-callable
|
||||
assert rendered == '<div id="hi" ns="main">Testing the MakoService</div>\n'
|
||||
|
||||
@@ -130,9 +130,9 @@ class TestVideoYouTube(TestVideo): # lint-amnesty, pylint: disable=missing-clas
|
||||
'poster': 'null',
|
||||
}
|
||||
|
||||
mako_service = self.item_descriptor.xmodule_runtime.service(self.item_descriptor, 'mako')
|
||||
assert get_context_dict_from_string(context) ==\
|
||||
get_context_dict_from_string(self.item_descriptor.xmodule_runtime.render_template('video.html',
|
||||
expected_context))
|
||||
get_context_dict_from_string(mako_service.render_template('video.html', expected_context))
|
||||
|
||||
|
||||
class TestVideoNonYouTube(TestVideo): # pylint: disable=test-inherits-tests
|
||||
@@ -211,8 +211,9 @@ class TestVideoNonYouTube(TestVideo): # pylint: disable=test-inherits-tests
|
||||
'poster': 'null',
|
||||
}
|
||||
|
||||
mako_service = self.item_descriptor.xmodule_runtime.service(self.item_descriptor, 'mako')
|
||||
expected_result = get_context_dict_from_string(
|
||||
self.item_descriptor.xmodule_runtime.render_template('video.html', expected_context)
|
||||
mako_service.render_template('video.html', expected_context)
|
||||
)
|
||||
assert get_context_dict_from_string(context) == expected_result
|
||||
assert expected_result['download_video_link'] == 'example.mp4'
|
||||
@@ -383,9 +384,9 @@ class TestGetHtmlMethod(BaseTestVideoXBlock):
|
||||
'metadata': json.dumps(metadata)
|
||||
})
|
||||
|
||||
mako_service = self.item_descriptor.xmodule_runtime.service(self.item_descriptor, 'mako')
|
||||
assert get_context_dict_from_string(context) ==\
|
||||
get_context_dict_from_string(self.item_descriptor.xmodule_runtime.render_template('video.html',
|
||||
expected_context))
|
||||
get_context_dict_from_string(mako_service.render_template('video.html', expected_context))
|
||||
|
||||
def test_get_html_source(self):
|
||||
# lint-amnesty, pylint: disable=invalid-name, redefined-outer-name
|
||||
@@ -491,9 +492,9 @@ class TestGetHtmlMethod(BaseTestVideoXBlock):
|
||||
'metadata': json.dumps(expected_context['metadata'])
|
||||
})
|
||||
|
||||
mako_service = self.item_descriptor.xmodule_runtime.service(self.item_descriptor, 'mako')
|
||||
assert get_context_dict_from_string(context) ==\
|
||||
get_context_dict_from_string(self.item_descriptor.xmodule_runtime.render_template('video.html',
|
||||
expected_context))
|
||||
get_context_dict_from_string(mako_service.render_template('video.html', expected_context))
|
||||
|
||||
def test_get_html_with_non_existent_edx_video_id(self):
|
||||
"""
|
||||
@@ -631,9 +632,9 @@ class TestGetHtmlMethod(BaseTestVideoXBlock):
|
||||
'metadata': json.dumps(expected_context['metadata'])
|
||||
})
|
||||
|
||||
mako_service = self.item_descriptor.xmodule_runtime.service(self.item_descriptor, 'mako')
|
||||
assert get_context_dict_from_string(context) ==\
|
||||
get_context_dict_from_string(self.item_descriptor.xmodule_runtime.render_template('video.html',
|
||||
expected_context))
|
||||
get_context_dict_from_string(mako_service.render_template('video.html', expected_context))
|
||||
|
||||
def test_get_html_with_existing_edx_video_id(self):
|
||||
"""
|
||||
@@ -659,9 +660,9 @@ class TestGetHtmlMethod(BaseTestVideoXBlock):
|
||||
# context returned by get_html when provided with above data
|
||||
# expected_context, a dict to assert with context
|
||||
context, expected_context = self.helper_get_html_with_edx_video_id(data)
|
||||
mako_service = self.item_descriptor.xmodule_runtime.service(self.item_descriptor, 'mako')
|
||||
assert get_context_dict_from_string(context) ==\
|
||||
get_context_dict_from_string(self.item_descriptor.xmodule_runtime.render_template('video.html',
|
||||
expected_context))
|
||||
get_context_dict_from_string(mako_service.render_template('video.html', expected_context))
|
||||
|
||||
def test_get_html_with_existing_unstripped_edx_video_id(self):
|
||||
"""
|
||||
@@ -690,9 +691,9 @@ class TestGetHtmlMethod(BaseTestVideoXBlock):
|
||||
# expected_context, a dict to assert with context
|
||||
context, expected_context = self.helper_get_html_with_edx_video_id(data)
|
||||
|
||||
mako_service = self.item_descriptor.xmodule_runtime.service(self.item_descriptor, 'mako')
|
||||
assert get_context_dict_from_string(context) ==\
|
||||
get_context_dict_from_string(self.item_descriptor.xmodule_runtime.render_template('video.html',
|
||||
expected_context))
|
||||
get_context_dict_from_string(mako_service.render_template('video.html', expected_context))
|
||||
|
||||
def encode_and_create_video(self, edx_video_id):
|
||||
"""
|
||||
@@ -899,9 +900,9 @@ class TestGetHtmlMethod(BaseTestVideoXBlock):
|
||||
'metadata': json.dumps(expected_context['metadata'])
|
||||
})
|
||||
|
||||
mako_service = self.item_descriptor.xmodule_runtime.service(self.item_descriptor, 'mako')
|
||||
assert get_context_dict_from_string(context) ==\
|
||||
get_context_dict_from_string(self.item_descriptor.xmodule_runtime.render_template('video.html',
|
||||
expected_context))
|
||||
get_context_dict_from_string(mako_service.render_template('video.html', expected_context))
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
def test_get_html_cdn_source_external_video(self):
|
||||
@@ -1001,9 +1002,9 @@ class TestGetHtmlMethod(BaseTestVideoXBlock):
|
||||
'metadata': json.dumps(expected_context['metadata'])
|
||||
})
|
||||
|
||||
mako_service = self.item_descriptor.xmodule_runtime.service(self.item_descriptor, 'mako')
|
||||
assert get_context_dict_from_string(context) ==\
|
||||
get_context_dict_from_string(self.item_descriptor.xmodule_runtime.render_template('video.html',
|
||||
expected_context))
|
||||
get_context_dict_from_string(mako_service.render_template('video.html', expected_context))
|
||||
|
||||
@ddt.data(
|
||||
(True, ['youtube', 'desktop_webm', 'desktop_mp4', 'hls']),
|
||||
@@ -2234,7 +2235,8 @@ class TestVideoWithBumper(TestVideo): # pylint: disable=test-inherits-tests
|
||||
}))
|
||||
}
|
||||
|
||||
expected_content = self.item_descriptor.xmodule_runtime.render_template('video.html', expected_context)
|
||||
mako_service = self.item_descriptor.xmodule_runtime.service(self.item_descriptor, 'mako')
|
||||
expected_content = mako_service.render_template('video.html', expected_context)
|
||||
assert get_context_dict_from_string(content) == get_context_dict_from_string(expected_content)
|
||||
|
||||
|
||||
@@ -2325,8 +2327,9 @@ class TestAutoAdvanceVideo(TestVideo): # lint-amnesty, pylint: disable=test-inh
|
||||
autoadvance_flag=autoadvance_must_be,
|
||||
)
|
||||
|
||||
mako_service = self.item_descriptor.xmodule_runtime.service(self.item_descriptor, 'mako')
|
||||
with override_settings(FEATURES=self.FEATURES):
|
||||
expected_content = self.item_descriptor.xmodule_runtime.render_template('video.html', expected_context)
|
||||
expected_content = mako_service.render_template('video.html', expected_context)
|
||||
|
||||
assert get_context_dict_from_string(content) == get_context_dict_from_string(expected_content)
|
||||
|
||||
|
||||
@@ -63,7 +63,6 @@ class TestHandlerUrl(TestCase):
|
||||
static_url='/static',
|
||||
track_function=Mock(),
|
||||
get_module=Mock(),
|
||||
render_template=Mock(),
|
||||
replace_urls=str,
|
||||
course_id=self.course_key,
|
||||
user=Mock(),
|
||||
@@ -130,7 +129,6 @@ class TestUserServiceAPI(TestCase):
|
||||
static_url='/static',
|
||||
track_function=Mock(),
|
||||
get_module=Mock(),
|
||||
render_template=Mock(),
|
||||
replace_urls=str,
|
||||
user=self.user,
|
||||
course_id=self.course_id,
|
||||
@@ -186,7 +184,6 @@ class TestBadgingService(ModuleStoreTestCase):
|
||||
static_url='/static',
|
||||
track_function=Mock(),
|
||||
get_module=Mock(),
|
||||
render_template=Mock(),
|
||||
replace_urls=str,
|
||||
course_id=self.course_id,
|
||||
user=self.user,
|
||||
@@ -242,7 +239,6 @@ class TestI18nService(ModuleStoreTestCase):
|
||||
static_url='/static',
|
||||
track_function=Mock(),
|
||||
get_module=Mock(),
|
||||
render_template=Mock(),
|
||||
replace_urls=str,
|
||||
course_id=self.course.id,
|
||||
user=Mock(),
|
||||
|
||||
@@ -495,6 +495,20 @@ ENTERPRISE_CONSENT_API_URL = 'http://enterprise.example.com/consent/api/v1/'
|
||||
ACTIVATION_EMAIL_FROM_ADDRESS = 'test_activate@edx.org'
|
||||
|
||||
TEMPLATES[0]['OPTIONS']['debug'] = True
|
||||
TEMPLATES.append(
|
||||
{
|
||||
# This separate copy of the Mako backend is used to test rendering previews in the 'lms.main' namespace
|
||||
'NAME': 'preview',
|
||||
'BACKEND': 'common.djangoapps.edxmako.backend.Mako',
|
||||
'APP_DIRS': False,
|
||||
'DIRS': MAKO_TEMPLATE_DIRS_BASE,
|
||||
'OPTIONS': {
|
||||
'context_processors': CONTEXT_PROCESSORS,
|
||||
'debug': False,
|
||||
'namespace': 'lms.main',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
########################## VIDEO TRANSCRIPTS STORAGE ############################
|
||||
VIDEO_TRANSCRIPTS_SETTINGS = dict(
|
||||
|
||||
@@ -19,6 +19,7 @@ from xblock.field_data import SplitFieldData
|
||||
from xblock.fields import Scope
|
||||
from xblock.runtime import KvsFieldData, MemoryIdManager, Runtime
|
||||
|
||||
from common.djangoapps.edxmako.services import MakoService
|
||||
from common.djangoapps.track import contexts as track_contexts
|
||||
from common.djangoapps.track import views as track_views
|
||||
from common.djangoapps.xblock_django.user_service import DjangoXBlockUserService
|
||||
@@ -235,6 +236,8 @@ class XBlockRuntime(RuntimeShim, Runtime):
|
||||
user_is_staff=self.user.is_staff,
|
||||
anonymous_user_id=self.anonymous_student_id,
|
||||
)
|
||||
elif service_name == "mako":
|
||||
return MakoService()
|
||||
elif service_name == "i18n":
|
||||
return ModuleI18nService(block=block)
|
||||
# Check if the XBlockRuntimeSystem wants to handle this:
|
||||
|
||||
@@ -3,12 +3,12 @@ Code to wrap web fragments with a license.
|
||||
"""
|
||||
|
||||
|
||||
def wrap_with_license(block, view, frag, context): # pylint: disable=unused-argument
|
||||
def wrap_with_license(block, view, frag, context, mako_service): # pylint: disable=unused-argument
|
||||
"""
|
||||
In the LMS, display the custom license underneath the XBlock.
|
||||
"""
|
||||
license = getattr(block, "license", None) # pylint: disable=redefined-builtin
|
||||
if license:
|
||||
context = {"license": license}
|
||||
frag.content += block.runtime.render_template('license_wrapper.html', context)
|
||||
frag.content += mako_service.render_template('license_wrapper.html', context)
|
||||
return frag
|
||||
|
||||
@@ -32,6 +32,7 @@ def _(text):
|
||||
|
||||
@XBlock.needs('user') # pylint: disable=abstract-method
|
||||
@XBlock.needs('i18n')
|
||||
@XBlock.needs('mako')
|
||||
class DiscussionXBlock(XBlock, StudioEditableXBlockMixin, XmlParserMixin): # lint-amnesty, pylint: disable=abstract-method
|
||||
"""
|
||||
Provides a discussion forum that is inline with other content in the courseware.
|
||||
@@ -202,7 +203,8 @@ class DiscussionXBlock(XBlock, StudioEditableXBlockMixin, XmlParserMixin): # li
|
||||
'login_msg': login_msg,
|
||||
}
|
||||
|
||||
fragment.add_content(self.runtime.render_template('discussion/_discussion_inline.html', context))
|
||||
fragment.add_content(self.runtime.service(self, 'mako').render_template('discussion/_discussion_inline.html',
|
||||
context))
|
||||
fragment.initialize_js('DiscussionInlineBlock')
|
||||
|
||||
return fragment
|
||||
@@ -212,7 +214,7 @@ class DiscussionXBlock(XBlock, StudioEditableXBlockMixin, XmlParserMixin): # li
|
||||
Renders author view for Studio.
|
||||
"""
|
||||
fragment = Fragment()
|
||||
fragment.add_content(self.runtime.render_template(
|
||||
fragment.add_content(self.runtime.service(self, 'mako').render_template(
|
||||
'discussion/_discussion_inline_studio.html',
|
||||
{'discussion_id': self.discussion_id}
|
||||
))
|
||||
|
||||
Reference in New Issue
Block a user