Files
edx-platform/common/djangoapps/edxmako/paths.py
Zia Fazal 954dae584a ziafazal/WL-328: Multi-Site Comprehensive Theming
ziafazal: improvements need for multi-tenancy
ziafazal: fixed broken tests
ziafazal: no need to add setting in test.py
ziafazal: added hostname validation
ziafazal: changes after feedback from mattdrayer
ziafazal: fixed branding and microsite broken tests
ziafazal: make STATICFILES_DIRS to list
ziafazal: added theme directory to mako lookup for tests
ziafazal: added more protection in test_util
saleem-latif: Enable SCSS Overrides for Comprehensive Theming
saleem-latif: Incoporate feedback changes, Correct test failures, add tests and enable theming for django templates
saleem-latif: Correct errors in python tests
mattdrayer: Fix invalid release reference
mattdrayer: Update django-wiki reference to latest release
2016-03-14 13:42:53 -04:00

136 lines
4.5 KiB
Python

"""
Set up lookup paths for mako templates.
"""
import hashlib
import contextlib
import os
import pkg_resources
from django.conf import settings
from mako.lookup import TemplateLookup
from mako.exceptions import TopLevelLookupException
from . import LOOKUP
from openedx.core.djangoapps.theming.helpers import (
get_template as themed_template,
get_template_path_with_theme,
strip_site_theme_templates_path,
)
class DynamicTemplateLookup(TemplateLookup):
"""
A specialization of the standard mako `TemplateLookup` class which allows
for adding directories progressively.
"""
def __init__(self, *args, **kwargs):
super(DynamicTemplateLookup, self).__init__(*args, **kwargs)
self.__original_module_directory = self.template_args['module_directory']
def __repr__(self):
return "<{0.__class__.__name__} {0.directories}>".format(self)
def add_directory(self, directory, prepend=False):
"""
Add a new directory to the template lookup path.
"""
if prepend:
self.directories.insert(0, os.path.normpath(directory))
else:
self.directories.append(os.path.normpath(directory))
# Since the lookup path has changed, the compiled modules might be
# wrong because now "foo.html" might be a completely different template,
# and "foo.html.py" in the module directory has no way to know that.
# Update the module_directory argument to point to a directory
# specifically for this lookup path.
unique = hashlib.md5(":".join(str(d) for d in self.directories)).hexdigest()
self.template_args['module_directory'] = os.path.join(self.__original_module_directory, unique)
# Also clear the internal caches. Ick.
self._collection.clear()
self._uri_cache.clear()
def get_template(self, uri):
"""
Overridden method for locating a template in either the database or the site theme.
If not found, template lookup will be done in comprehensive theme for current site
by prefixing path to theme.
e.g if uri is `main.html` then new uri would be something like this `/red-theme/lms/static/main.html`
If still unable to find a template, it will fallback to the default template directories after stripping off
the prefix path to theme.
"""
template = themed_template(uri)
if not template:
try:
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))
return template
def clear_lookups(namespace):
"""
Remove mako template lookups for the given namespace.
"""
if namespace in LOOKUP:
del LOOKUP[namespace]
def add_lookup(namespace, directory, package=None, prepend=False):
"""
Adds a new mako template lookup directory to the given namespace.
If `package` is specified, `pkg_resources` is used to look up the directory
inside the given package. Otherwise `directory` is assumed to be a path
in the filesystem.
"""
templates = LOOKUP.get(namespace)
if not templates:
LOOKUP[namespace] = templates = DynamicTemplateLookup(
module_directory=settings.MAKO_MODULE_DIR,
output_encoding='utf-8',
input_encoding='utf-8',
default_filters=['decode.utf8'],
encoding_errors='replace',
)
if package:
directory = pkg_resources.resource_filename(package, directory)
templates.add_directory(directory, prepend=prepend)
def lookup_template(namespace, name):
"""
Look up a Mako template by namespace and name.
"""
return LOOKUP[namespace].get_template(name)
@contextlib.contextmanager
def save_lookups():
"""
A context manager to save and restore the Mako template lookup path.
Useful for testing.
"""
# Make a copy of the list of directories for each namespace.
namespace_dirs = {namespace: list(look.directories) for namespace, look in LOOKUP.items()}
try:
yield
finally:
# Get rid of all the lookups.
LOOKUP.clear()
# Re-create the lookups from our saved list.
for namespace, directories in namespace_dirs.items():
for directory in directories:
add_lookup(namespace, directory)