diff --git a/cms/djangoapps/contentstore/views/helpers.py b/cms/djangoapps/contentstore/views/helpers.py index 424fe764754e..ac114bfd371c 100644 --- a/cms/djangoapps/contentstore/views/helpers.py +++ b/cms/djangoapps/contentstore/views/helpers.py @@ -14,6 +14,7 @@ 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 @@ -42,6 +43,13 @@ 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. diff --git a/cms/djangoapps/contentstore/views/item.py b/cms/djangoapps/contentstore/views/item.py index 800c47456aa1..3ed065eb0bf5 100644 --- a/cms/djangoapps/contentstore/views/item.py +++ b/cms/djangoapps/contentstore/views/item.py @@ -32,7 +32,6 @@ 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 @@ -310,8 +309,6 @@ def service(self, block, service_name): 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": diff --git a/cms/djangoapps/contentstore/views/preview.py b/cms/djangoapps/contentstore/views/preview.py index 5689ef558a19..1abafe5fb9bc 100644 --- a/cms/djangoapps/contentstore/views/preview.py +++ b/cms/djangoapps/contentstore/views/preview.py @@ -19,7 +19,6 @@ 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 @@ -44,6 +43,7 @@ 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,10 +185,9 @@ 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, partial(wrap_with_license, mako_service=mako_service)) + wrappers.insert(0, wrap_with_license) return PreviewModuleSystem( static_url=settings.STATIC_URL, @@ -196,6 +195,7 @@ 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,7 +213,6 @@ 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), diff --git a/cms/djangoapps/contentstore/views/tests/test_preview.py b/cms/djangoapps/contentstore/views/tests/test_preview.py index 18f9da6fce83..07bfc854211c 100644 --- a/cms/djangoapps/contentstore/views/tests/test_preview.py +++ b/cms/djangoapps/contentstore/views/tests/test_preview.py @@ -8,7 +8,6 @@ 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 @@ -169,20 +168,13 @@ def test_block_branch_not_changed_by_preview_handler(self, default_store): @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. """ - 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 + pass # lint-amnesty, pylint: disable=unnecessary-pass @ddt.ddt @@ -214,25 +206,3 @@ def test_expected_services_exist(self, expected_service): ) 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 '
Testing the MakoService
' in html diff --git a/cms/lib/xblock/authoring_mixin.py b/cms/lib/xblock/authoring_mixin.py index 342c5ff05486..39f8f1062825 100644 --- a/cms/lib/xblock/authoring_mixin.py +++ b/cms/lib/xblock/authoring_mixin.py @@ -39,7 +39,7 @@ def visibility_view(self, _context=None): """ fragment = Fragment() from cms.djangoapps.contentstore.utils import reverse_course_url - fragment.add_content(self.runtime.service(self, 'mako').render_template('visibility_editor.html', { + fragment.add_content(self.system.render_template('visibility_editor.html', { 'xblock': self, 'manage_groups_url': reverse_course_url('group_configurations_list_handler', self.location.course_key), })) diff --git a/common/djangoapps/edxmako/services.py b/common/djangoapps/edxmako/services.py deleted file mode 100644 index 6ee2b096d63e..000000000000 --- a/common/djangoapps/edxmako/services.py +++ /dev/null @@ -1,30 +0,0 @@ -""" -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) diff --git a/common/djangoapps/edxmako/tests.py b/common/djangoapps/edxmako/tests.py index 69d350cf44d3..2e0bf2e48e60 100644 --- a/common/djangoapps/edxmako/tests.py +++ b/common/djangoapps/edxmako/tests.py @@ -14,7 +14,6 @@ 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, @@ -209,23 +208,3 @@ def test_render_to_string_when_no_global_context_cms(self): 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(), - '
Testing the MakoService
\n'), - (MakoService(namespace_prefix='lms.'), - '
Testing the MakoService
\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 diff --git a/common/lib/xmodule/xmodule/annotatable_module.py b/common/lib/xmodule/xmodule/annotatable_module.py index e07f81231d66..1c7a80122bdf 100644 --- a/common/lib/xmodule/xmodule/annotatable_module.py +++ b/common/lib/xmodule/xmodule/annotatable_module.py @@ -6,7 +6,6 @@ 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 @@ -30,7 +29,6 @@ _ = lambda text: text -@XBlock.needs('mako') class AnnotatableBlock( RawMixin, XmlMixin, @@ -203,7 +201,7 @@ def get_html(self): 'content_html': self._render_content() } - return self.runtime.service(self, 'mako').render_template('annotatable.html', context) + return self.system.render_template('annotatable.html', context) def student_view(self, context): # lint-amnesty, pylint: disable=unused-argument """ @@ -221,7 +219,7 @@ def studio_view(self, _context): Return the studio view. """ fragment = Fragment( - self.runtime.service(self, 'mako').render_template(self.mako_template, self.get_context()) + self.system.render_template(self.mako_template, self.get_context()) ) add_webpack_to_fragment(fragment, 'AnnotatableBlockStudio') shim_xmodule_js(fragment, self.studio_js_module_name) diff --git a/common/lib/xmodule/xmodule/capa_module.py b/common/lib/xmodule/xmodule/capa_module.py index 26a42d59d6a6..25a133d19e1c 100644 --- a/common/lib/xmodule/xmodule/capa_module.py +++ b/common/lib/xmodule/xmodule/capa_module.py @@ -120,7 +120,6 @@ def from_json(self, value): @XBlock.needs('user') @XBlock.needs('i18n') -@XBlock.needs('mako') @XBlock.wants('call_to_action') class ProblemBlock( ScorableXBlockMixin, @@ -393,7 +392,7 @@ def studio_view(self, _context): Return the studio view. """ fragment = Fragment( - self.runtime.service(self, 'mako').render_template(self.mako_template, self.get_context()) + self.system.render_template(self.mako_template, self.get_context()) ) add_webpack_to_fragment(fragment, 'ProblemBlockStudio') shim_xmodule_js(fragment, 'MarkdownEditingDescriptor') @@ -822,7 +821,7 @@ def new_lcp(self, state, text=None): filestore=self.runtime.filestore, i18n=self.runtime.service(self, "i18n"), node_path=self.runtime.node_path, - render_template=self.runtime.service(self, 'mako').render_template, + render_template=self.runtime.render_template, seed=seed, # Why do we do this if we have self.seed? STATIC_URL=self.runtime.STATIC_URL, xqueue=self.runtime.xqueue, @@ -916,7 +915,7 @@ def get_html(self): """ curr_score, total_possible = self.get_display_progress() - return self.runtime.service(self, 'mako').render_template('problem_ajax.html', { + return self.runtime.render_template('problem_ajax.html', { 'element_id': self.location.html_id(), 'id': str(self.location), 'ajax_url': self.ajax_url, @@ -1264,7 +1263,7 @@ def get_problem_html(self, encapsulate=True, submit_notification=False): 'submit_disabled_cta': submit_disabled_ctas[0] if submit_disabled_ctas else None, } - html = self.runtime.service(self, 'mako').render_template('problem.html', context) + html = self.runtime.render_template('problem.html', context) if encapsulate: html = HTML('
{html}
').format( @@ -1574,7 +1573,7 @@ def get_answer(self, _data): return { 'answers': new_answers, - 'correct_status_html': self.runtime.service(self, 'mako').render_template( + 'correct_status_html': self.runtime.render_template( 'status_span.html', {'status': Status('correct', self.runtime.service(self, "i18n").ugettext)} ) diff --git a/common/lib/xmodule/xmodule/conditional_module.py b/common/lib/xmodule/xmodule/conditional_module.py index 3605f36bf828..8a2d50c63bc1 100644 --- a/common/lib/xmodule/xmodule/conditional_module.py +++ b/common/lib/xmodule/xmodule/conditional_module.py @@ -11,7 +11,6 @@ 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 @@ -39,7 +38,6 @@ _ = lambda text: text -@XBlock.needs('mako') class ConditionalBlock( SequenceMixin, MakoTemplateBlockBase, @@ -247,7 +245,7 @@ def student_view(self, _context): def get_html(self): required_html_ids = [descriptor.location.html_id() for descriptor in self.get_required_blocks] - return self.runtime.service(self, 'mako').render_template('conditional_ajax.html', { + return self.system.render_template('conditional_ajax.html', { 'element_id': self.location.html_id(), 'ajax_url': self.ajax_url, 'depends': ';'.join(required_html_ids) @@ -273,7 +271,7 @@ def studio_view(self, _context): Return the studio view. """ fragment = Fragment( - self.runtime.service(self, 'mako').render_template(self.mako_template, self.get_context()) + self.system.render_template(self.mako_template, self.get_context()) ) add_webpack_to_fragment(fragment, 'ConditionalBlockStudio') shim_xmodule_js(fragment, self.studio_js_module_name) @@ -286,7 +284,8 @@ def handle_ajax(self, _dispatch, _data): if not self.is_condition_satisfied(): context = {'module': self, 'message': self.conditional_message} - html = self.runtime.service(self, 'mako').render_template('conditional_module.html', context) + html = self.system.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()] diff --git a/common/lib/xmodule/xmodule/error_module.py b/common/lib/xmodule/xmodule/error_module.py index a96631954e1d..2cefcc9da9a9 100644 --- a/common/lib/xmodule/xmodule/error_module.py +++ b/common/lib/xmodule/xmodule/error_module.py @@ -11,7 +11,6 @@ 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 @@ -44,7 +43,6 @@ class ErrorFields: display_name = String(scope=Scope.settings) -@XBlock.needs('mako') class ErrorBlock( ErrorFields, XModuleDescriptorToXBlockMixin, @@ -64,7 +62,7 @@ def student_view(self, _context): """ Return a fragment that contains the html for the student view. """ - fragment = Fragment(self.runtime.service(self, 'mako').render_template('module-error.html', { + fragment = Fragment(self.system.render_template('module-error.html', { 'staff_access': True, 'data': self.contents, 'error': self.error_msg, @@ -211,7 +209,7 @@ def student_view(self, _context): """ Return a fragment that contains the html for the student view. """ - fragment = Fragment(self.runtime.service(self, 'mako').render_template('module-error.html', { + fragment = Fragment(self.system.render_template('module-error.html', { 'staff_access': False, 'data': '', 'error': '', diff --git a/common/lib/xmodule/xmodule/html_module.py b/common/lib/xmodule/xmodule/html_module.py index de22e1a823d6..0329bd0bc2c0 100644 --- a/common/lib/xmodule/xmodule/html_module.py +++ b/common/lib/xmodule/xmodule/html_module.py @@ -43,7 +43,6 @@ @XBlock.needs("i18n") -@XBlock.needs("mako") @XBlock.needs("user") class HtmlBlockMixin( # lint-amnesty, pylint: disable=abstract-method XmlMixin, EditingMixin, @@ -132,7 +131,7 @@ def studio_view(self, _context): Return the studio view. """ fragment = Fragment( - self.runtime.service(self, 'mako').render_template(self.mako_template, self.get_context()) + self.system.render_template(self.mako_template, self.get_context()) ) add_webpack_to_fragment(fragment, 'HtmlBlockStudio') shim_xmodule_js(fragment, 'HTMLEditingDescriptor') @@ -485,10 +484,7 @@ def get_html(self): 'visible_updates': course_updates[:3], 'hidden_updates': course_updates[3:], } - return self.runtime.service(self, 'mako').render_template( - f"{self.TEMPLATE_DIR}/course_updates.html", - context, - ) + return self.system.render_template(f"{self.TEMPLATE_DIR}/course_updates.html", context) @classmethod def order_updates(self, updates): # lint-amnesty, pylint: disable=bad-classmethod-argument diff --git a/common/lib/xmodule/xmodule/library_content_module.py b/common/lib/xmodule/xmodule/library_content_module.py index d3bce47bb8af..18dbb9ae11b6 100644 --- a/common/lib/xmodule/xmodule/library_content_module.py +++ b/common/lib/xmodule/xmodule/library_content_module.py @@ -69,7 +69,6 @@ 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, @@ -366,7 +365,7 @@ def student_view(self, context): # lint-amnesty, pylint: disable=missing-functi 'content': rendered_child.content, }) - fragment.add_content(self.runtime.service(self, 'mako').render_template('vert_module.html', { + fragment.add_content(self.system.render_template('vert_module.html', { 'items': contents, 'xblock_context': context, 'show_bookmark_button': False, @@ -388,11 +387,10 @@ def author_view(self, context): 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.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, - })) + 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, + })) context['can_edit_visibility'] = False context['can_move'] = False self.render_children(context, fragment, can_reorder=False, can_add=False) @@ -409,7 +407,7 @@ def studio_view(self, _context): Return the studio view. """ fragment = Fragment( - self.runtime.service(self, 'mako').render_template(self.mako_template, self.get_context()) + self.system.render_template(self.mako_template, self.get_context()) ) add_webpack_to_fragment(fragment, 'LibraryContentBlockStudio') shim_xmodule_js(fragment, self.studio_js_module_name) diff --git a/common/lib/xmodule/xmodule/library_root_xblock.py b/common/lib/xmodule/xmodule/library_root_xblock.py index 32581330aa0b..ed2fc452a33e 100644 --- a/common/lib/xmodule/xmodule/library_root_xblock.py +++ b/common/lib/xmodule/xmodule/library_root_xblock.py @@ -17,7 +17,6 @@ _ = lambda text: text -@XBlock.needs('mako') class LibraryRoot(XBlock): """ The LibraryRoot is the root XBlock of a content library. All other blocks in @@ -104,7 +103,7 @@ def render_children(self, context, fragment, can_reorder=False, can_add=False): }) fragment.add_content( - self.runtime.service(self, 'mako').render_template("studio_render_paged_children_view.html", { + self.runtime.render_template("studio_render_paged_children_view.html", { 'items': contents, 'xblock_context': context, 'can_add': can_add, diff --git a/common/lib/xmodule/xmodule/lti_module.py b/common/lib/xmodule/xmodule/lti_module.py index 29acd2b155d5..f38cb482e8ee 100644 --- a/common/lib/xmodule/xmodule/lti_module.py +++ b/common/lib/xmodule/xmodule/lti_module.py @@ -270,7 +270,6 @@ class LTIFields: @XBlock.needs("i18n") -@XBlock.needs("mako") @XBlock.needs("user") class LTIBlock( LTIFields, @@ -402,7 +401,7 @@ def studio_view(self, _context): # Add our specific template information (the raw data body) context.update({'data': self.data}) fragment = Fragment( - self.runtime.service(self, 'mako').render_template(self.mako_template, context) + self.system.render_template(self.mako_template, context) ) add_webpack_to_fragment(fragment, 'LTIBlockStudio') shim_xmodule_js(fragment, self.studio_js_module_name) @@ -518,7 +517,7 @@ def student_view(self, _context): Return the student view. """ fragment = Fragment() - fragment.add_content(self.runtime.service(self, 'mako').render_template('lti.html', self.get_context())) + fragment.add_content(self.system.render_template('lti.html', self.get_context())) add_webpack_to_fragment(fragment, 'LTIBlockPreview') shim_xmodule_js(fragment, 'LTI') return fragment @@ -528,7 +527,7 @@ def preview_handler(self, _, __): """ This is called to get context with new oauth params to iframe. """ - template = self.runtime.service(self, 'mako').render_template('lti_form.html', self.get_context()) + template = self.system.render_template('lti_form.html', self.get_context()) return Response(template, content_type='text/html') def get_user_id(self): diff --git a/common/lib/xmodule/xmodule/mako_module.py b/common/lib/xmodule/xmodule/mako_module.py index b34e040798ff..ab1a5ed51399 100644 --- a/common/lib/xmodule/xmodule/mako_module.py +++ b/common/lib/xmodule/xmodule/mako_module.py @@ -9,25 +9,11 @@ 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: """ diff --git a/common/lib/xmodule/xmodule/poll_module.py b/common/lib/xmodule/xmodule/poll_module.py index 6ec06009e5d8..6c739f4a91f6 100644 --- a/common/lib/xmodule/xmodule/poll_module.py +++ b/common/lib/xmodule/xmodule/poll_module.py @@ -18,7 +18,6 @@ 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 @@ -37,7 +36,6 @@ _ = lambda text: text -@XBlock.needs('mako') class PollBlock( MakoTemplateBlockBase, XmlMixin, @@ -164,7 +162,7 @@ def student_view(self, _context): 'ajax_url': self.ajax_url, 'configuration_json': self.dump_poll(), } - fragment.add_content(self.runtime.service(self, 'mako').render_template('poll.html', params)) + fragment.add_content(self.system.render_template('poll.html', params)) add_webpack_to_fragment(fragment, 'PollBlockPreview') shim_xmodule_js(fragment, 'Poll') return fragment diff --git a/common/lib/xmodule/xmodule/seq_module.py b/common/lib/xmodule/xmodule/seq_module.py index c71ffce6b346..6ccd4fea8a39 100644 --- a/common/lib/xmodule/xmodule/seq_module.py +++ b/common/lib/xmodule/xmodule/seq_module.py @@ -244,7 +244,6 @@ def is_proctored_exam(self, value): @XBlock.needs('user') @XBlock.needs('bookmarks') @XBlock.needs('i18n') -@XBlock.needs('mako') @XBlock.wants('content_type_gating') class SequenceBlock( SequenceMixin, @@ -538,7 +537,7 @@ def _hidden_content_student_view(self, context): if not self._can_user_view_content(course): banner_text = self._hidden_content_banner_text(course) - hidden_content_html = self.runtime.service(self, 'mako').render_template( + hidden_content_html = self.system.render_template( 'hidden_content.html', { 'self_paced': course.self_paced, @@ -607,7 +606,7 @@ def _student_or_public_view(self, context, prereq_met, prereq_meta_info, banner_ 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.runtime.service(self, 'mako').render_template("seq_module.html", params)) + fragment.add_content(self.system.render_template("seq_module.html", params)) self._capture_full_seq_item_metrics(display_items) self._capture_current_unit_metrics(display_items) diff --git a/common/lib/xmodule/xmodule/split_test_module.py b/common/lib/xmodule/xmodule/split_test_module.py index 870f6ea5e0d6..734ea19c2c18 100644 --- a/common/lib/xmodule/xmodule/split_test_module.py +++ b/common/lib/xmodule/xmodule/split_test_module.py @@ -123,7 +123,6 @@ 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 @@ -302,7 +301,7 @@ def _staff_view(self, context): sorted_inactive_contents = sorted(inactive_contents, key=itemgetter('group_name')) # Use the new template - fragment.add_content(self.runtime.service(self, 'mako').render_template('split_test_staff_view.html', { + fragment.add_content(self.system.render_template('split_test_staff_view.html', { 'items': sorted_active_contents + sorted_inactive_contents, })) fragment.add_css('.split-test-child { display: none; }') @@ -329,7 +328,7 @@ def author_view(self, context): fragment, inactive_children, context ) - fragment.add_content(self.runtime.service(self, 'mako').render_template('split_test_author_view.html', { + fragment.add_content(self.system.render_template('split_test_author_view.html', { 'split_test': self, 'is_root': is_root, 'is_configured': self.is_configured, @@ -368,7 +367,7 @@ def studio_view(self, context): Return the studio view. """ fragment = Fragment( - self.runtime.service(self, 'mako').render_template(self.mako_template, self.get_context()) + self.system.render_template(self.mako_template, self.get_context()) ) add_webpack_to_fragment(fragment, 'SplitTestBlockStudio') shim_xmodule_js(fragment, self.studio_js_module_name) @@ -387,7 +386,7 @@ def student_view(self, context): return self._staff_view(context) else: child_fragment = self.child.render(STUDENT_VIEW, context) - fragment = Fragment(self.runtime.service(self, 'mako').render_template('split_test_student_view.html', { + fragment = Fragment(self.system.render_template('split_test_student_view.html', { 'child_content': child_fragment.content, 'child_id': self.child.scope_ids.usage_id, })) diff --git a/common/lib/xmodule/xmodule/studio_editable.py b/common/lib/xmodule/xmodule/studio_editable.py index cd62283bc250..8352c781d14e 100644 --- a/common/lib/xmodule/xmodule/studio_editable.py +++ b/common/lib/xmodule/xmodule/studio_editable.py @@ -1,20 +1,15 @@ """ 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): @@ -36,7 +31,7 @@ def render_children(self, context, fragment, can_reorder=False, can_add=False): 'content': rendered_child.content }) - fragment.add_content(self.runtime.service(self, 'mako').render_template("studio_render_children_view.html", { # pylint: disable=no-member + fragment.add_content(self.system.render_template("studio_render_children_view.html", { # pylint: disable=no-member 'items': contents, 'xblock_context': context, 'can_add': can_add, diff --git a/common/lib/xmodule/xmodule/template_module.py b/common/lib/xmodule/xmodule/template_module.py index 51bc6ff2b209..8729190a073f 100644 --- a/common/lib/xmodule/xmodule/template_module.py +++ b/common/lib/xmodule/xmodule/template_module.py @@ -3,7 +3,6 @@ """ from string import Template -from xblock.core import XBlock from lxml import etree from pkg_resources import resource_string @@ -41,7 +40,6 @@ class CustomTagTemplateBlock( # pylint: disable=abstract-method """ -@XBlock.needs('mako') class CustomTagBlock(CustomTagTemplateBlock): # pylint: disable=abstract-method """ This module supports tags of the form @@ -87,7 +85,7 @@ def studio_view(self, _context): Return the studio view. """ fragment = Fragment( - self.runtime.service(self, 'mako').render_template(self.mako_template, self.get_context()) + self.system.render_template(self.mako_template, self.get_context()) ) add_webpack_to_fragment(fragment, 'CustomTagBlockStudio') shim_xmodule_js(fragment, 'XMLEditingDescriptor') diff --git a/common/lib/xmodule/xmodule/tests/__init__.py b/common/lib/xmodule/xmodule/tests/__init__.py index 533bff5c6952..c91da247d8da 100644 --- a/common/lib/xmodule/xmodule/tests/__init__.py +++ b/common/lib/xmodule/xmodule/tests/__init__.py @@ -11,6 +11,7 @@ import inspect import json import os +import pprint import sys import traceback import unittest @@ -33,7 +34,7 @@ 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 mock_render_template, StubMakoService, StubUserService +from xmodule.tests.helpers import StubUserService from xmodule.x_module import ModuleSystem, XModuleDescriptor, XModuleMixin @@ -92,13 +93,18 @@ 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 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. + 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). + """ if not user: user = Mock(name='get_test_system.user', is_staff=False) @@ -108,8 +114,6 @@ 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): @@ -132,6 +136,7 @@ def get_module(descriptor): 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='.'), @@ -139,7 +144,6 @@ def get_module(descriptor): hostname="edx.org", services={ 'user': user_service, - 'mako': mako_service, }, xqueue={ 'interface': None, @@ -157,7 +161,7 @@ def get_module(descriptor): ) -def get_test_descriptor_system(render_template=None): +def get_test_descriptor_system(): """ Construct a test DescriptorSystem instance. """ @@ -167,7 +171,7 @@ def get_test_descriptor_system(render_template=None): 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=render_template or mock_render_template, + render_template=mock_render_template, mixins=(InheritanceMixin, XModuleMixin), field_data=field_data, services={'field-data': field_data}, @@ -176,6 +180,16 @@ def get_test_descriptor_system(render_template=None): 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): diff --git a/common/lib/xmodule/xmodule/tests/helpers.py b/common/lib/xmodule/xmodule/tests/helpers.py index 22dc86892431..4e9e9d1d14db 100644 --- a/common/lib/xmodule/xmodule/tests/helpers.py +++ b/common/lib/xmodule/xmodule/tests/helpers.py @@ -5,7 +5,6 @@ import filecmp from unittest.mock import Mock -import pprint from path import Path as path from xblock.reference.user_service import UserService, XBlockUser @@ -31,31 +30,6 @@ def compare_dirs(dir1, dir2): 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. diff --git a/common/lib/xmodule/xmodule/tests/test_capa_module.py b/common/lib/xmodule/xmodule/tests/test_capa_module.py index 4121cac007c8..58de4adc261e 100644 --- a/common/lib/xmodule/xmodule/tests/test_capa_module.py +++ b/common/lib/xmodule/xmodule/tests/test_capa_module.py @@ -79,8 +79,7 @@ def answer_key(cls, response_num=2, input_num=1): response_num, input_num)) @classmethod - def create(cls, attempts=None, problem_state=None, correct=False, xml=None, override_get_score=True, - render_template=None, **kwargs): + def create(cls, attempts=None, problem_state=None, correct=False, xml=None, override_get_score=True, **kwargs): """ All parameters are optional, and are added to the created problem if specified. @@ -96,8 +95,6 @@ def create(cls, attempts=None, problem_state=None, correct=False, xml=None, over 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), @@ -116,11 +113,8 @@ def create(cls, attempts=None, problem_state=None, correct=False, xml=None, over # 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), - render_template=render_template or Mock(return_value="
Test Template HTML
"), - ) + system = get_test_system(course_id=location.course_key, user_is_staff=kwargs.get('user_is_staff', False)) + system.render_template = Mock(return_value="
Test Template HTML
") module = ProblemBlock( system, DictFieldData(field_data), @@ -1526,8 +1520,7 @@ def test_no_max_attempts(self): # assert that we got here without exploding def test_get_problem_html(self): - render_template = Mock(return_value="
Test Template HTML
") - module = CapaFactory.create(render_template=render_template) + module = CapaFactory.create() # We've tested the show/hide button logic in other tests, # so here we hard-wire the values @@ -1539,6 +1532,9 @@ def test_get_problem_html(self): 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="
Test Template HTML
") + # Patch the capa problem's HTML rendering with patch('capa.capa_problem.LoncapaProblem.get_html') as mock_html: mock_html.return_value = "
Test Problem HTML
" @@ -1553,7 +1549,7 @@ def test_get_problem_html(self): assert html == '
Test Template HTML
' # Check the rendering context - render_args, _ = render_template.call_args + render_args, _ = module.system.render_template.call_args assert len(render_args) == 2 template_name = render_args[0] @@ -1588,10 +1584,9 @@ def test_get_problem_html(self): 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. - render_template = Mock(return_value="
Test Template HTML
") - module = CapaFactory.create(xml=self.demand_xml, render_template=render_template) + module = CapaFactory.create(xml=self.demand_xml) module.get_problem_html() # ignoring html result - context = render_template.call_args[0][1] + context = module.system.render_template.call_args[0][1] assert context['demand_hint_possible'] assert context['should_enable_next_hint'] @@ -1626,10 +1621,9 @@ def test_single_demand_hint(self): Only demand hint """ - render_template = Mock(return_value="
Test Template HTML
") - module = CapaFactory.create(xml=test_xml, render_template=render_template) + module = CapaFactory.create(xml=test_xml) module.get_problem_html() # ignoring html result - context = render_template.call_args[0][1] + context = module.system.render_template.call_args[0][1] assert context['demand_hint_possible'] assert context['should_enable_next_hint'] @@ -1658,10 +1652,9 @@ def test_image_hint(self): 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. """ - render_template = Mock(return_value="
Test Template HTML
") - module = CapaFactory.create(xml=test_xml, render_template=render_template) + module = CapaFactory.create(xml=test_xml) module.get_problem_html() # ignoring html result - context = render_template.call_args[0][1] + context = module.system.render_template.call_args[0][1] assert context['demand_hint_possible'] assert context['should_enable_next_hint'] @@ -1703,8 +1696,7 @@ def test_get_problem_html_error(self): rendering, a "dummy" problem is created with an error message to display to the user. """ - render_template = Mock(return_value="
Test Template HTML
") - module = CapaFactory.create(render_template=render_template) + module = CapaFactory.create() # Save the original problem so we can compare it later original_problem = module.lcp @@ -1713,6 +1705,9 @@ def test_get_problem_html_error(self): # 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="
Test Template HTML
") + # Turn off DEBUG module.system.DEBUG = False @@ -1722,7 +1717,7 @@ def test_get_problem_html_error(self): assert html is not None # Check the rendering context - render_args, _ = render_template.call_args + render_args, _ = module.system.render_template.call_args context = render_args[1] assert 'error' in context['problem']['html'] @@ -1733,14 +1728,16 @@ def test_get_problem_html_error_w_debug(self): """ Test the html response when an error occurs with DEBUG on """ - render_template = Mock(return_value="
Test Template HTML
") - module = CapaFactory.create(render_template=render_template) + module = CapaFactory.create() # 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="
Test Template HTML
") + # Make sure DEBUG is on module.system.DEBUG = True @@ -1750,7 +1747,7 @@ def test_get_problem_html_error_w_debug(self): assert html is not None # Check the rendering context - render_args, _ = render_template.call_args + render_args, _ = module.system.render_template.call_args context = render_args[1] assert error_msg in context['problem']['html'] @@ -2114,10 +2111,9 @@ def test_problem_no_display_name(self, display_name): """ Verify that if problem display name is not provided then a default name is used. """ - render_template = Mock(return_value="
Test Template HTML
") - module = CapaFactory.create(display_name=display_name, render_template=render_template) + module = CapaFactory.create(display_name=display_name) module.get_problem_html() - render_args, _ = render_template.call_args + render_args, _ = module.system.render_template.call_args context = render_args[1] assert context['problem']['name'] == module.location.block_type diff --git a/common/lib/xmodule/xmodule/tests/test_conditional.py b/common/lib/xmodule/xmodule/tests/test_conditional.py index ac862add812c..13f2dc6a9504 100644 --- a/common/lib/xmodule/xmodule/tests/test_conditional.py +++ b/common/lib/xmodule/xmodule/tests/test_conditional.py @@ -166,8 +166,7 @@ def test_get_html(self): # 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 - mako_service = modules['cond_module'].xmodule_runtime.service(modules['cond_module'], 'mako') - expected = mako_service.render_template('conditional_ajax.html', { + expected = modules['cond_module'].xmodule_runtime.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', @@ -242,8 +241,7 @@ def test_conditional_module(self, _): module = self.get_module_for_location(location) html = module.render(STUDENT_VIEW).content - mako_service = module.xmodule_runtime.service(module, 'mako') - html_expect = mako_service.render_template( + html_expect = module.xmodule_runtime.render_template( 'conditional_ajax.html', { # Test ajax url is just usage-id / handler_name diff --git a/common/lib/xmodule/xmodule/tests/test_delay_between_attempts.py b/common/lib/xmodule/xmodule/tests/test_delay_between_attempts.py index fd1afccdb849..480437a1c82f 100644 --- a/common/lib/xmodule/xmodule/tests/test_delay_between_attempts.py +++ b/common/lib/xmodule/xmodule/tests/test_delay_between_attempts.py @@ -103,7 +103,8 @@ def create( # since everything else is a string. field_data['attempts'] = int(attempts) - system = get_test_system(render_template=Mock(return_value="
Test Template HTML
")) + system = get_test_system() + system.render_template = Mock(return_value="
Test Template HTML
") module = ProblemBlock( system, DictFieldData(field_data), diff --git a/common/lib/xmodule/xmodule/tests/test_editing_module.py b/common/lib/xmodule/xmodule/tests/test_editing_module.py index 0f29595be6a0..2515adf11931 100644 --- a/common/lib/xmodule/xmodule/tests/test_editing_module.py +++ b/common/lib/xmodule/xmodule/tests/test_editing_module.py @@ -22,7 +22,8 @@ class TabsEditingDescriptorTestCase(unittest.TestCase): def setUp(self): super().setUp() - system = get_test_descriptor_system(render_template=Mock()) + system = get_test_descriptor_system() + system.render_template = Mock(return_value="
Test Template HTML
") self.tabs = [ { 'name': "Test_css", diff --git a/common/lib/xmodule/xmodule/tests/test_html_module.py b/common/lib/xmodule/xmodule/tests/test_html_module.py index 13fa83d268f9..5695941d0b3e 100644 --- a/common/lib/xmodule/xmodule/tests/test_html_module.py +++ b/common/lib/xmodule/xmodule/tests/test_html_module.py @@ -315,7 +315,7 @@ def test_updates_order(self): 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.runtime.service(info_module, 'mako').render_template.assert_called_once_with( + info_module.system.render_template.assert_called_once_with( template_name, expected_context ) diff --git a/common/lib/xmodule/xmodule/tests/test_xml_module.py b/common/lib/xmodule/xmodule/tests/test_xml_module.py index 9b4d4fc9103c..2776c52a6bb0 100644 --- a/common/lib/xmodule/xmodule/tests/test_xml_module.py +++ b/common/lib/xmodule/xmodule/tests/test_xml_module.py @@ -347,7 +347,8 @@ def non_editable_metadata_fields(self): non_editable_fields.append(TestModuleDescriptor.due) return non_editable_fields - system = get_test_descriptor_system(render_template=Mock()) + system = get_test_descriptor_system() + system.render_template = Mock(return_value="
Test Template HTML
") 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 diff --git a/common/lib/xmodule/xmodule/vertical_block.py b/common/lib/xmodule/xmodule/vertical_block.py index 14baf8678466..4d1c82477b5f 100644 --- a/common/lib/xmodule/xmodule/vertical_block.py +++ b/common/lib/xmodule/xmodule/vertical_block.py @@ -48,7 +48,7 @@ class VerticalFields: ) -@XBlock.needs('user', 'bookmarks', 'mako') +@XBlock.needs('user', 'bookmarks') @XBlock.wants('completion') @XBlock.wants('call_to_action') class VerticalBlock( @@ -151,7 +151,7 @@ def _student_or_public_view(self, context, view): child_context['username'], str(self.location)), # pylint: disable=no-member }) - fragment.add_content(self.runtime.service(self, 'mako').render_template('vert_module.html', fragment_context)) + fragment.add_content(self.system.render_template('vert_module.html', fragment_context)) add_webpack_to_fragment(fragment, 'VerticalStudentView') fragment.initialize_js('VerticalStudentView') diff --git a/common/lib/xmodule/xmodule/video_module/video_module.py b/common/lib/xmodule/xmodule/video_module/video_module.py index 2216c88488ca..8bcccb630672 100644 --- a/common/lib/xmodule/xmodule/video_module/video_module.py +++ b/common/lib/xmodule/xmodule/video_module/video_module.py @@ -108,7 +108,6 @@ @XBlock.wants('settings', 'completion', 'i18n', 'request_cache') -@XBlock.needs('mako') class VideoBlock( VideoFields, VideoTranscriptsMixin, VideoStudioViewHandlers, VideoStudentViewHandlers, TabsEditingMixin, EmptyDataRawMixin, XmlMixin, EditingMixin, @@ -246,7 +245,7 @@ def studio_view(self, _context): Return the studio view. """ fragment = Fragment( - self.runtime.service(self, 'mako').render_template(self.mako_template, self.get_context()) + self.system.render_template(self.mako_template, self.get_context()) ) add_webpack_to_fragment(fragment, 'VideoBlockStudio') shim_xmodule_js(fragment, 'TabsEditingDescriptor') @@ -469,7 +468,7 @@ def get_html(self, view=STUDENT_VIEW): # lint-amnesty, pylint: disable=argument 'transcript_download_formats_list': self.fields['transcript_download_format'].values, # lint-amnesty, pylint: disable=unsubscriptable-object 'license': getattr(self, "license", None), } - return self.runtime.service(self, 'mako').render_template('video.html', context) + return self.system.render_template('video.html', context) def validate(self): """ diff --git a/common/lib/xmodule/xmodule/word_cloud_module.py b/common/lib/xmodule/xmodule/word_cloud_module.py index 5fc1c737be65..8ada5d143655 100644 --- a/common/lib/xmodule/xmodule/word_cloud_module.py +++ b/common/lib/xmodule/xmodule/word_cloud_module.py @@ -44,7 +44,6 @@ def pretty_bool(value): return value in bool_dict -@XBlock.needs('mako') class WordCloudBlock( # pylint: disable=abstract-method EmptyDataRawMixin, XmlMixin, @@ -280,7 +279,7 @@ def student_view(self, context): # lint-amnesty, pylint: disable=unused-argumen Renders the output that a student will see. """ fragment = Fragment() - fragment.add_content(self.runtime.service(self, 'mako').render_template('word_cloud.html', { + fragment.add_content(self.system.render_template('word_cloud.html', { 'ajax_url': self.ajax_url, 'display_name': self.display_name, 'instructions': self.instructions, @@ -305,7 +304,7 @@ def studio_view(self, _context): Return the studio view. """ fragment = Fragment( - self.runtime.service(self, 'mako').render_template(self.mako_template, self.get_context()) + self.system.render_template(self.mako_template, self.get_context()) ) add_webpack_to_fragment(fragment, 'WordCloudBlockStudio') shim_xmodule_js(fragment, self.studio_js_module_name) diff --git a/common/lib/xmodule/xmodule/x_module.py b/common/lib/xmodule/xmodule/x_module.py index 056528ebd4ba..593d8f117665 100644 --- a/common/lib/xmodule/xmodule/x_module.py +++ b/common/lib/xmodule/xmodule/x_module.py @@ -1809,23 +1809,6 @@ def user_is_staff(self): 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): """ @@ -1841,7 +1824,7 @@ class ModuleSystem(MetricsMixin, ConfigurableFragmentWrapper, ModuleSystemShim, """ def __init__( - self, static_url, track_function, get_module, + self, static_url, track_function, get_module, render_template, replace_urls, descriptor_runtime, filestore=None, debug=False, hostname="", xqueue=None, publish=None, node_path="", course_id=None, @@ -1863,6 +1846,9 @@ def __init__( 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. @@ -1918,6 +1904,7 @@ def __init__( 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 diff --git a/common/test/templates/edxmako.html b/common/test/templates/edxmako.html deleted file mode 100644 index fd95201801c2..000000000000 --- a/common/test/templates/edxmako.html +++ /dev/null @@ -1 +0,0 @@ -<%page expression_filter="h"/>
Testing the MakoService
diff --git a/lms/djangoapps/courseware/module_render.py b/lms/djangoapps/courseware/module_render.py index b4acc4c3f680..d6ac518e9519 100644 --- a/lms/djangoapps/courseware/module_render.py +++ b/lms/djangoapps/courseware/module_render.py @@ -52,6 +52,7 @@ 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 @@ -89,7 +90,6 @@ 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,9 +703,8 @@ def rebind_noauth_module_to_user(module, real_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(partial(wrap_with_license, mako_service=mako_service)) + block_wrappers.append(wrap_with_license) # Wrap the output display in a single div to allow for the XModule # javascript to be bound correctly @@ -771,6 +770,7 @@ def rebind_noauth_module_to_user(module, real_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,7 +810,6 @@ def rebind_noauth_module_to_user(module, real_user): services={ 'fs': FSService(), 'field-data': field_data, - 'mako': mako_service, 'user': user_service, 'verification': XBlockVerificationService(), 'proctoring': ProctoringService(), diff --git a/lms/djangoapps/courseware/tests/helpers.py b/lms/djangoapps/courseware/tests/helpers.py index 83a27122bb44..d500bf5bbf7e 100644 --- a/lms/djangoapps/courseware/tests/helpers.py +++ b/lms/djangoapps/courseware/tests/helpers.py @@ -7,7 +7,6 @@ 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 @@ -64,11 +63,11 @@ class BaseTestXmodule(ModuleStoreTestCase): METADATA = {} MODEL_DATA = {'data': ''} - def new_module_runtime(self, render_template=None): + def new_module_runtime(self): """ Generate a new ModuleSystem that is minimally set up for testing """ - return get_test_system(course_id=self.course.id, render_template=render_template) + return get_test_system(course_id=self.course.id) def new_descriptor_runtime(self): runtime = get_test_descriptor_system() @@ -144,14 +143,12 @@ def get_url(self, dispatch): class XModuleRenderingTestBase(BaseTestXmodule): # lint-amnesty, pylint: disable=missing-class-docstring - def new_module_runtime(self, render_template=None): + def new_module_runtime(self): """ Create a runtime that actually does html rendering """ - if not render_template: - render_template = render_to_string - runtime = super().new_module_runtime(render_template=render_template) - runtime.modulestore = Mock() + runtime = super().new_module_runtime() + runtime.render_template = render_to_string return runtime diff --git a/lms/djangoapps/courseware/tests/test_discussion_xblock.py b/lms/djangoapps/courseware/tests/test_discussion_xblock.py index b268f7c87824..e48973570a6d 100644 --- a/lms/djangoapps/courseware/tests/test_discussion_xblock.py +++ b/lms/djangoapps/courseware/tests/test_discussion_xblock.py @@ -42,6 +42,7 @@ def setUp(self): 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({ @@ -130,8 +131,7 @@ def setUp(self): self.template_canary = 'canary' self.render_template = mock.Mock() self.render_template.return_value = self.template_canary - self.runtime = self.new_module_runtime(render_template=self.render_template) - self.block.runtime = self.runtime + self.block.runtime.render_template = self.render_template self.has_permission_mock = mock.Mock() self.has_permission_mock.return_value = False self.block.has_permission = self.has_permission_mock diff --git a/lms/djangoapps/courseware/tests/test_module_render.py b/lms/djangoapps/courseware/tests/test_module_render.py index 1ea9b2908cc3..bc19f06e8353 100644 --- a/lms/djangoapps/courseware/tests/test_module_render.py +++ b/lms/djangoapps/courseware/tests/test_module_render.py @@ -2687,17 +2687,3 @@ def test_user_service_with_anonymous_user(self): 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 == '
Testing the MakoService
\n' diff --git a/lms/djangoapps/courseware/tests/test_video_mongo.py b/lms/djangoapps/courseware/tests/test_video_mongo.py index bb3a94be2976..8c487a1902a3 100644 --- a/lms/djangoapps/courseware/tests/test_video_mongo.py +++ b/lms/djangoapps/courseware/tests/test_video_mongo.py @@ -130,9 +130,9 @@ def test_video_constructor(self): '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(mako_service.render_template('video.html', expected_context)) + get_context_dict_from_string(self.item_descriptor.xmodule_runtime.render_template('video.html', + expected_context)) class TestVideoNonYouTube(TestVideo): # pylint: disable=test-inherits-tests @@ -211,9 +211,8 @@ def test_video_constructor(self): 'poster': 'null', } - mako_service = self.item_descriptor.xmodule_runtime.service(self.item_descriptor, 'mako') expected_result = get_context_dict_from_string( - mako_service.render_template('video.html', expected_context) + self.item_descriptor.xmodule_runtime.render_template('video.html', expected_context) ) assert get_context_dict_from_string(context) == expected_result assert expected_result['download_video_link'] == 'example.mp4' @@ -384,9 +383,9 @@ def test_get_html_track(self): '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(mako_service.render_template('video.html', expected_context)) + get_context_dict_from_string(self.item_descriptor.xmodule_runtime.render_template('video.html', + expected_context)) def test_get_html_source(self): # lint-amnesty, pylint: disable=invalid-name, redefined-outer-name @@ -492,9 +491,9 @@ def test_get_html_source(self): '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(mako_service.render_template('video.html', expected_context)) + get_context_dict_from_string(self.item_descriptor.xmodule_runtime.render_template('video.html', + expected_context)) def test_get_html_with_non_existent_edx_video_id(self): """ @@ -632,9 +631,9 @@ def test_get_html_with_mocked_edx_video_id(self): '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(mako_service.render_template('video.html', expected_context)) + get_context_dict_from_string(self.item_descriptor.xmodule_runtime.render_template('video.html', + expected_context)) def test_get_html_with_existing_edx_video_id(self): """ @@ -660,9 +659,9 @@ def test_get_html_with_existing_edx_video_id(self): # 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(mako_service.render_template('video.html', expected_context)) + get_context_dict_from_string(self.item_descriptor.xmodule_runtime.render_template('video.html', + expected_context)) def test_get_html_with_existing_unstripped_edx_video_id(self): """ @@ -691,9 +690,9 @@ def test_get_html_with_existing_unstripped_edx_video_id(self): # 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(mako_service.render_template('video.html', expected_context)) + get_context_dict_from_string(self.item_descriptor.xmodule_runtime.render_template('video.html', + expected_context)) def encode_and_create_video(self, edx_video_id): """ @@ -900,9 +899,9 @@ def side_effect(*args, **kwargs): # lint-amnesty, pylint: disable=unused-argume '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(mako_service.render_template('video.html', expected_context)) + get_context_dict_from_string(self.item_descriptor.xmodule_runtime.render_template('video.html', + expected_context)) # pylint: disable=invalid-name def test_get_html_cdn_source_external_video(self): @@ -1002,9 +1001,9 @@ def test_get_html_cdn_source_external_video(self): '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(mako_service.render_template('video.html', expected_context)) + get_context_dict_from_string(self.item_descriptor.xmodule_runtime.render_template('video.html', + expected_context)) @ddt.data( (True, ['youtube', 'desktop_webm', 'desktop_mp4', 'hls']), @@ -2235,8 +2234,7 @@ def test_bumper_metadata(self, get_url_for_profiles, get_bumper_settings, is_bum })) } - mako_service = self.item_descriptor.xmodule_runtime.service(self.item_descriptor, 'mako') - expected_content = mako_service.render_template('video.html', expected_context) + expected_content = self.item_descriptor.xmodule_runtime.render_template('video.html', expected_context) assert get_context_dict_from_string(content) == get_context_dict_from_string(expected_content) @@ -2327,9 +2325,8 @@ def assert_content_matches_expectations(self, autoadvanceenabled_must_be, autoad 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 = mako_service.render_template('video.html', expected_context) + expected_content = self.item_descriptor.xmodule_runtime.render_template('video.html', expected_context) assert get_context_dict_from_string(content) == get_context_dict_from_string(expected_content) diff --git a/lms/djangoapps/lms_xblock/test/test_runtime.py b/lms/djangoapps/lms_xblock/test/test_runtime.py index 3e74f82f8c75..42c20de05b71 100644 --- a/lms/djangoapps/lms_xblock/test/test_runtime.py +++ b/lms/djangoapps/lms_xblock/test/test_runtime.py @@ -63,6 +63,7 @@ def setUp(self): static_url='/static', track_function=Mock(), get_module=Mock(), + render_template=Mock(), replace_urls=str, course_id=self.course_key, user=Mock(), @@ -129,6 +130,7 @@ def setUp(self): static_url='/static', track_function=Mock(), get_module=Mock(), + render_template=Mock(), replace_urls=str, user=self.user, course_id=self.course_id, @@ -184,6 +186,7 @@ def mock_get_real_user(_anon_id): static_url='/static', track_function=Mock(), get_module=Mock(), + render_template=Mock(), replace_urls=str, course_id=self.course_id, user=self.user, @@ -239,6 +242,7 @@ def setUp(self): static_url='/static', track_function=Mock(), get_module=Mock(), + render_template=Mock(), replace_urls=str, course_id=self.course.id, user=Mock(), diff --git a/lms/envs/test.py b/lms/envs/test.py index 43286bfb6edd..d43db2672cfe 100644 --- a/lms/envs/test.py +++ b/lms/envs/test.py @@ -495,20 +495,6 @@ 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( diff --git a/openedx/core/djangoapps/xblock/runtime/runtime.py b/openedx/core/djangoapps/xblock/runtime/runtime.py index 93984037c82f..558259d99842 100644 --- a/openedx/core/djangoapps/xblock/runtime/runtime.py +++ b/openedx/core/djangoapps/xblock/runtime/runtime.py @@ -19,7 +19,6 @@ 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 @@ -236,8 +235,6 @@ def service(self, block, service_name): 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: diff --git a/openedx/core/lib/license/wrapper.py b/openedx/core/lib/license/wrapper.py index 23fb4f04926a..2d1701540820 100644 --- a/openedx/core/lib/license/wrapper.py +++ b/openedx/core/lib/license/wrapper.py @@ -3,12 +3,12 @@ """ -def wrap_with_license(block, view, frag, context, mako_service): # pylint: disable=unused-argument +def wrap_with_license(block, view, frag, context): # 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 += mako_service.render_template('license_wrapper.html', context) + frag.content += block.runtime.render_template('license_wrapper.html', context) return frag diff --git a/openedx/core/lib/xblock_builtin/xblock_discussion/xblock_discussion/__init__.py b/openedx/core/lib/xblock_builtin/xblock_discussion/xblock_discussion/__init__.py index 61ad62e25d58..079b1765cbb4 100644 --- a/openedx/core/lib/xblock_builtin/xblock_discussion/xblock_discussion/__init__.py +++ b/openedx/core/lib/xblock_builtin/xblock_discussion/xblock_discussion/__init__.py @@ -32,7 +32,6 @@ 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. @@ -203,8 +202,7 @@ def student_view(self, context=None): 'login_msg': login_msg, } - fragment.add_content(self.runtime.service(self, 'mako').render_template('discussion/_discussion_inline.html', - context)) + fragment.add_content(self.runtime.render_template('discussion/_discussion_inline.html', context)) fragment.initialize_js('DiscussionInlineBlock') return fragment @@ -214,7 +212,7 @@ def author_view(self, context=None): # pylint: disable=unused-argument Renders author view for Studio. """ fragment = Fragment() - fragment.add_content(self.runtime.service(self, 'mako').render_template( + fragment.add_content(self.runtime.render_template( 'discussion/_discussion_inline_studio.html', {'discussion_id': self.discussion_id} ))