makoloader.py 3.75 KB
Newer Older
1
import logging
2

3
from django.conf import settings
4
from django.core.exceptions import ImproperlyConfigured
5 6 7
from django.template.base import TemplateDoesNotExist
from django.template.loaders.filesystem import Loader as FilesystemLoader
from django.template.loaders.app_directories import Loader as AppDirectoriesLoader
8
from django.template import Engine
9

David Baumgold committed
10
from edxmako.template import Template
11

12
from openedx.core.lib.tempdir import mkdtemp_clean
13 14

log = logging.getLogger(__name__)
15

Calen Pennington committed
16

17 18 19 20 21
class MakoLoader(object):
    """
    This is a Django loader object which will load the template as a
    Mako template if the first line is "## mako". It is based off BaseLoader
    in django.template.loader.
22
    We need this in order to be able to include mako templates inside main_django.html.
23
    """
Calen Pennington committed
24

25 26 27 28 29
    is_usable = False

    def __init__(self, base_loader):
        # base_loader is an instance of a BaseLoader subclass
        self.base_loader = base_loader
Calen Pennington committed
30

31
        module_directory = getattr(settings, 'MAKO_MODULE_DIR', None)
Calen Pennington committed
32

33 34
        if module_directory is None:
            log.warning("For more caching of mako templates, set the MAKO_MODULE_DIR in settings!")
35
            module_directory = mkdtemp_clean()
Calen Pennington committed
36

37
        self.module_directory = module_directory
Calen Pennington committed
38

39 40 41 42
    def __call__(self, template_name, template_dirs=None):
        return self.load_template(template_name, template_dirs)

    def load_template(self, template_name, template_dirs=None):
43
        source, file_path = self.load_template_source(template_name, template_dirs)
Calen Pennington committed
44

45 46 47 48
        # In order to allow dynamic template overrides, we need to cache templates based on their absolute paths
        # rather than relative paths, overriding templates would have same relative paths.
        module_directory = self.module_directory.rstrip("/") + "/{dir_hash}/".format(dir_hash=hash(file_path))

49 50
        if source.startswith("## mako\n"):
            # This is a mako template
51
            template = Template(filename=file_path,
52
                                module_directory=module_directory,
53 54
                                input_encoding='utf-8',
                                output_encoding='utf-8',
55 56
                                default_filters=['decode.utf8'],
                                encoding_errors='replace',
57
                                uri=template_name)
58 59 60 61
            return template, None
        else:
            # This is a regular template
            try:
62
                template = Engine.get_default().from_string(source)
63
                return template, None
64 65 66 67
            except ImproperlyConfigured:
                # Either no DjangoTemplates engine was configured -or- multiple engines
                # were configured, making the get_default() call above fail.
                raise
68
            except TemplateDoesNotExist:
69 70 71
                # If compiling the loaded template raises TemplateDoesNotExist, back off to
                # returning the source and display name for the requested template.
                # This allows for eventual correct identification of the actual template that does
72
                # not exist.
73
                return source, file_path
Calen Pennington committed
74

75
    def load_template_source(self, template_name, template_dirs=None):
76
        # Just having this makes the template load as an instance, instead of a class.
77
        return self.base_loader.load_template_source(template_name, template_dirs)
78 79 80

    def reset(self):
        self.base_loader.reset()
Calen Pennington committed
81

82 83 84

class MakoFilesystemLoader(MakoLoader):
    is_usable = True
85
    _accepts_engine_in_init = True
Calen Pennington committed
86

87 88
    def __init__(self, *args):
        MakoLoader.__init__(self, FilesystemLoader(*args))
Calen Pennington committed
89 90


91 92
class MakoAppDirectoriesLoader(MakoLoader):
    is_usable = True
93
    _accepts_engine_in_init = True
Calen Pennington committed
94

95 96
    def __init__(self, *args):
        MakoLoader.__init__(self, AppDirectoriesLoader(*args))