Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 0 additions & 8 deletions cms/djangoapps/contentstore/views/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
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
Expand Down Expand Up @@ -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.
Expand Down
3 changes: 3 additions & 0 deletions cms/djangoapps/contentstore/views/item.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
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
Expand Down Expand Up @@ -309,6 +310,8 @@ 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":
Expand Down
7 changes: 4 additions & 3 deletions cms/djangoapps/contentstore/views/preview.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
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
Expand All @@ -43,7 +44,6 @@

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']
Expand Down Expand Up @@ -185,17 +185,17 @@ 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,
# TODO (cpennington): Do we want to track how instructors are using the preview problems?
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)),
Expand All @@ -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),
Expand Down
32 changes: 31 additions & 1 deletion cms/djangoapps/contentstore/views/tests/test_preview.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

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
Expand Down Expand Up @@ -168,13 +169,20 @@ 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.
"""
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
Expand Down Expand Up @@ -206,3 +214,25 @@ 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 '<div id="142" ns="main">Testing the MakoService</div>' in html
7 changes: 2 additions & 5 deletions cms/lib/xblock/authoring_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,11 @@


@XBlock.needs("i18n")
@XBlock.needs("mako")
class AuthoringMixin(XBlockMixin):
"""
Mixin class that provides authoring capabilities for XBlocks.
"""
_services_requested = {
'i18n': 'need',
}

def _get_studio_resource_url(self, relative_url):
"""
Returns the Studio URL to a static resource.
Expand All @@ -39,7 +36,7 @@ def visibility_view(self, _context=None):
"""
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),
}))
Expand Down
21 changes: 21 additions & 0 deletions cms/lib/xblock/test/test_authoring_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@

from django.conf import settings
from django.test.utils import override_settings
from xblock.core import XBlock

from common.djangoapps.course_modes.tests.factories import CourseModeFactory
from common.lib.xmodule.xmodule.tests.test_export import PureXBlock
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
from xmodule.partitions.partitions import (
Expand All @@ -32,6 +34,7 @@ class AuthoringMixinTestCase(ModuleStoreTestCase):
FEATURES_WITH_ENROLLMENT_TRACK_DISABLED = settings.FEATURES.copy()
FEATURES_WITH_ENROLLMENT_TRACK_DISABLED['ENABLE_ENROLLMENT_TRACK_USER_PARTITION'] = False

@XBlock.register_temp_plugin(PureXBlock, 'pure')
def setUp(self):
"""
Create a simple course with a video component.
Expand All @@ -58,8 +61,14 @@ def setUp(self):
parent_location=vertical.location,
display_name='Test Vertical'
)
pure = ItemFactory.create(
category='pure',
parent_location=vertical.location,
display_name='Test Pure'
)
self.vertical_location = vertical.location
self.video_location = video.location
self.pure_location = pure.location
self.pet_groups = [Group(1, 'Cat Lovers'), Group(2, 'Dog Lovers')]

def create_content_groups(self, content_groups):
Expand Down Expand Up @@ -258,3 +267,15 @@ def test_missing_enrollment_mode(self):
self.video_location,
[self.ENROLLMENT_GROUPS_TITLE, 'audit course', 'verified course', self.GROUP_NO_LONGER_EXISTS]
)

def test_pure_xblock_visibility(self):
self.create_content_groups(self.pet_groups)
self.verify_visibility_view_contains(
self.pure_location,
[self.CONTENT_GROUPS_TITLE, 'Cat Lovers', 'Dog Lovers']
)

self.verify_visibility_view_does_not_contain(
self.pure_location,
[self.NO_CONTENT_OR_ENROLLMENT_GROUPS, self.ENROLLMENT_GROUPS_TITLE]
)
30 changes: 30 additions & 0 deletions common/djangoapps/edxmako/services.py
Original file line number Diff line number Diff line change
@@ -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)
21 changes: 21 additions & 0 deletions common/djangoapps/edxmako/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

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,
Expand Down Expand Up @@ -208,3 +209,23 @@ 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(),
'<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 changes: 4 additions & 2 deletions common/lib/xmodule/xmodule/annotatable_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
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
Expand All @@ -29,6 +30,7 @@
_ = lambda text: text


@XBlock.needs('mako')
class AnnotatableBlock(
RawMixin,
XmlMixin,
Expand Down Expand Up @@ -201,7 +203,7 @@ def get_html(self):
'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
"""
Expand All @@ -219,7 +221,7 @@ def studio_view(self, _context):
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)
Expand Down
11 changes: 6 additions & 5 deletions common/lib/xmodule/xmodule/capa_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ def from_json(self, value):

@XBlock.needs('user')
@XBlock.needs('i18n')
@XBlock.needs('mako')
@XBlock.wants('call_to_action')
class ProblemBlock(
ScorableXBlockMixin,
Expand Down Expand Up @@ -392,7 +393,7 @@ def studio_view(self, _context):
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')
Expand Down Expand Up @@ -821,7 +822,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.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,
Expand Down Expand Up @@ -915,7 +916,7 @@ def get_html(self):
"""
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,
Expand Down Expand Up @@ -1263,7 +1264,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.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(
Expand Down Expand Up @@ -1573,7 +1574,7 @@ def get_answer(self, _data):

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)}
)
Expand Down
Loading