1from django.core.exceptions import ImproperlyConfigured, SuspiciousFileOperation
2from django.template.utils import get_app_template_dirs
3from django.utils._os import safe_join
4from django.utils.functional import cached_property
5
6
7class BaseEngine:
8 # Core methods: engines have to provide their own implementation
9 # (except for from_string which is optional).
10
11 def __init__(self, params):
12 """
13 Initialize the template engine.
14
15 `params` is a dict of configuration settings.
16 """
17 params = params.copy()
18 self.name = params.pop("NAME")
19 self.dirs = list(params.pop("DIRS"))
20 self.app_dirs = params.pop("APP_DIRS")
21 if params:
22 raise ImproperlyConfigured(
23 "Unknown parameters: {}".format(", ".join(params))
24 )
25
26 def check(self, **kwargs):
27 return []
28
29 @property
30 def app_dirname(self):
31 raise ImproperlyConfigured(
32 "{} doesn't support loading templates from installed "
33 "applications.".format(self.__class__.__name__)
34 )
35
36 def from_string(self, template_code):
37 """
38 Create and return a template for the given source code.
39
40 This method is optional.
41 """
42 raise NotImplementedError(
43 "subclasses of BaseEngine should provide a from_string() method"
44 )
45
46 def get_template(self, template_name):
47 """
48 Load and return a template for the given name.
49
50 Raise TemplateDoesNotExist if no such template exists.
51 """
52 raise NotImplementedError(
53 "subclasses of BaseEngine must provide a get_template() method"
54 )
55
56 # Utility methods: they are provided to minimize code duplication and
57 # security issues in third-party backends.
58
59 @cached_property
60 def template_dirs(self):
61 """
62 Return a list of directories to search for templates.
63 """
64 # Immutable return value because it's cached and shared by callers.
65 template_dirs = tuple(self.dirs)
66 if self.app_dirs:
67 template_dirs += get_app_template_dirs(self.app_dirname)
68 return template_dirs
69
70 def iter_template_filenames(self, template_name):
71 """
72 Iterate over candidate files for template_name.
73
74 Ignore files that don't lie inside configured template dirs to avoid
75 directory traversal attacks.
76 """
77 for template_dir in self.template_dirs:
78 try:
79 yield safe_join(template_dir, template_name)
80 except SuspiciousFileOperation:
81 # The joined path was located outside of this template_dir
82 # (it might be inside another one, so this isn't fatal).
83 pass