Merge pull request #16856 from open-craft/mtyaka/template-block-overrides
Allow theme template block overrides.
This commit is contained in:
@@ -18,6 +18,15 @@ from openedx.core.djangoapps.theming.helpers import get_template_path_with_theme
|
||||
from . import LOOKUP
|
||||
|
||||
|
||||
class TopLevelTemplateURI(unicode):
|
||||
"""
|
||||
A marker class for template URIs used to signal the template lookup infrastructure that the template corresponding
|
||||
to the URI should be looked up straight in the standard edx-platform location instead of trying to locate an
|
||||
overridding template in the current theme first.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class DynamicTemplateLookup(TemplateLookup):
|
||||
"""
|
||||
A specialization of the standard mako `TemplateLookup` class which allows
|
||||
@@ -51,6 +60,28 @@ class DynamicTemplateLookup(TemplateLookup):
|
||||
self._collection.clear()
|
||||
self._uri_cache.clear()
|
||||
|
||||
def adjust_uri(self, uri, calling_uri):
|
||||
"""
|
||||
This method is called by mako when including a template in another template or when inheriting an existing mako
|
||||
template. The method adjusts the `uri` to make it relative to the calling template's location.
|
||||
|
||||
This method is overridden to detect when a template from a theme tries to override the same template from a
|
||||
standard location, for example when the dashboard.html template is overridden in the theme while at the same
|
||||
time inheriting from the standard LMS dashboard.html template.
|
||||
|
||||
When this self-inheritance is detected, the uri is wrapped in the TopLevelTemplateURI marker class to ensure
|
||||
that template lookup skips the current theme and looks up the built-in template in standard locations.
|
||||
"""
|
||||
# Make requested uri relative to the calling uri.
|
||||
relative_uri = super(DynamicTemplateLookup, self).adjust_uri(uri, calling_uri)
|
||||
# Is the calling template (calling_uri) which is including or inheriting current template (uri)
|
||||
# located inside a theme?
|
||||
if calling_uri != strip_site_theme_templates_path(calling_uri):
|
||||
# Is the calling template trying to include/inherit itself?
|
||||
if calling_uri == get_template_path_with_theme(relative_uri):
|
||||
return TopLevelTemplateURI(relative_uri)
|
||||
return relative_uri
|
||||
|
||||
def get_template(self, uri):
|
||||
"""
|
||||
Overridden method for locating a template in either the database or the site theme.
|
||||
@@ -68,15 +99,24 @@ class DynamicTemplateLookup(TemplateLookup):
|
||||
# if microsite template is not present or request is not in microsite then
|
||||
# let mako find and serve a template
|
||||
if not template:
|
||||
try:
|
||||
# Try to find themed template, i.e. see if current theme overrides the template
|
||||
template = super(DynamicTemplateLookup, self).get_template(get_template_path_with_theme(uri))
|
||||
except TopLevelLookupException:
|
||||
# strip off the prefix path to theme and look in default template dirs
|
||||
template = super(DynamicTemplateLookup, self).get_template(strip_site_theme_templates_path(uri))
|
||||
if isinstance(uri, TopLevelTemplateURI):
|
||||
template = self._get_toplevel_template(uri)
|
||||
else:
|
||||
try:
|
||||
# Try to find themed template, i.e. see if current theme overrides the template
|
||||
template = super(DynamicTemplateLookup, self).get_template(get_template_path_with_theme(uri))
|
||||
except TopLevelLookupException:
|
||||
template = self._get_toplevel_template(uri)
|
||||
|
||||
return template
|
||||
|
||||
def _get_toplevel_template(self, uri):
|
||||
"""
|
||||
Lookup a default/toplevel template, ignoring current theme.
|
||||
"""
|
||||
# Strip off the prefix path to theme and look in default template dirs.
|
||||
return super(DynamicTemplateLookup, self).get_template(strip_site_theme_templates_path(uri))
|
||||
|
||||
|
||||
def clear_lookups(namespace):
|
||||
"""
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
<%page expression_filter="h"/>
|
||||
|
||||
# Include template which does not exist in the theme.
|
||||
<%include file="/courseware/error-message.html" />
|
||||
# Include template which is overriden in the theme.
|
||||
<%include file="/courseware/info.html" />
|
||||
# Include custom template which only exists in the theme.
|
||||
<%include file="/courseware/test-theme-custom.html" />
|
||||
@@ -0,0 +1,2 @@
|
||||
<%page expression_filter="h"/>
|
||||
<p>This overrides the courseware/info.html template.</p>
|
||||
@@ -0,0 +1,2 @@
|
||||
<%page expression_filter="h"/>
|
||||
<p>This is a custom template.</p>
|
||||
9
common/test/test-theme/lms/templates/dashboard.html
Normal file
9
common/test/test-theme/lms/templates/dashboard.html
Normal file
@@ -0,0 +1,9 @@
|
||||
<%page expression_filter="h"/>
|
||||
<%!
|
||||
from openedx.core.djangolib.markup import HTML
|
||||
%>
|
||||
|
||||
<%inherit file="dashboard.html" />
|
||||
<%block name="pagetitle">Overridden Title!</%block>
|
||||
${HTML(parent.body())}
|
||||
<%block name="bodyextra">Overriden Body Extra!</%block>
|
||||
@@ -64,6 +64,81 @@ class TestComprehensiveThemeLMS(TestCase):
|
||||
result = staticfiles.finders.find('test-theme/images/logo.png')
|
||||
self.assertEqual(result, settings.TEST_THEME / 'lms/static/images/logo.png')
|
||||
|
||||
@with_comprehensive_theme("test-theme")
|
||||
def test_override_block_in_parent(self):
|
||||
"""
|
||||
Test that theme title is used instead of parent title.
|
||||
"""
|
||||
self._login()
|
||||
dashboard_url = reverse('dashboard')
|
||||
resp = self.client.get(dashboard_url)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
# This string comes from the 'pagetitle' block of the overriding theme.
|
||||
self.assertContains(resp, "Overridden Title!")
|
||||
|
||||
@with_comprehensive_theme("test-theme")
|
||||
def test_override_block_in_grandparent(self):
|
||||
"""
|
||||
Test that theme title is used instead of parent's parent's title.
|
||||
"""
|
||||
self._login()
|
||||
dashboard_url = reverse('dashboard')
|
||||
resp = self.client.get(dashboard_url)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
# This string comes from the 'bodyextra' block of the overriding theme.
|
||||
self.assertContains(resp, "Overriden Body Extra!")
|
||||
|
||||
@with_comprehensive_theme("test-theme")
|
||||
def test_parent_content_in_self_inherited_template(self):
|
||||
"""
|
||||
Test that parent's body is present in self inherited template.
|
||||
"""
|
||||
self._login()
|
||||
dashboard_url = reverse('dashboard')
|
||||
resp = self.client.get(dashboard_url)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
# This string comes from the default dashboard.html template.
|
||||
self.assertContains(resp, "Explore courses")
|
||||
|
||||
@with_comprehensive_theme("test-theme")
|
||||
def test_include_default_template(self):
|
||||
"""
|
||||
Test that theme template can include template which is not part of the theme.
|
||||
"""
|
||||
self._login()
|
||||
courses_url = reverse('courses')
|
||||
resp = self.client.get(courses_url)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
# The courses.html template includes the error-message.html template.
|
||||
# Verify that the error message is included in the output.
|
||||
self.assertContains(resp, "this module is temporarily unavailable")
|
||||
|
||||
@with_comprehensive_theme("test-theme")
|
||||
def test_include_overridden_template(self):
|
||||
"""
|
||||
Test that theme template can include template which is overridden in the active theme.
|
||||
"""
|
||||
self._login()
|
||||
courses_url = reverse('courses')
|
||||
resp = self.client.get(courses_url)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
# The courses.html template includes the info.html file, which is overriden in the theme.
|
||||
self.assertContains(resp, "This overrides the courseware/info.html template.")
|
||||
|
||||
@with_comprehensive_theme("test-theme")
|
||||
def test_include_custom_template(self):
|
||||
"""
|
||||
Test that theme template can include template which is only present in the theme, but has no standard LMS
|
||||
equivalent.
|
||||
"""
|
||||
self._login()
|
||||
courses_url = reverse('courses')
|
||||
resp = self.client.get(courses_url)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
# The courses.html template includes the test-theme.custom.html file.
|
||||
# Verify its contents are present in the output.
|
||||
self.assertContains(resp, "This is a custom template.")
|
||||
|
||||
|
||||
@skip_unless_cms
|
||||
class TestComprehensiveThemeCMS(TestCase):
|
||||
|
||||
Reference in New Issue
Block a user