]> git.ipfire.org Git - pbs.git/commitdiff
web: Replace the Tornado template engine with Jinja
authorMichael Tremer <michael.tremer@ipfire.org>
Sun, 12 Jan 2025 12:44:25 +0000 (12:44 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Sun, 12 Jan 2025 12:44:25 +0000 (12:44 +0000)
We need this to support any async code in the templates.

Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/web/__init__.py
src/web/base.py

index 667f106243f22f44bb6999c36c928befbf0f7e6c..05651a401a9f63ad84b0bf555e457e75f00211db 100644 (file)
@@ -39,6 +39,7 @@ class Application(tornado.web.Application):
                        login_url = "/login",
                        template_path = TEMPLATESDIR,
                        static_path = STATICDIR,
+
                        ui_modules = {
                                "Highlight"          : ui_modules.HighlightModule,
                                "Text"               : ui_modules.TextModule,
index 7b8d65f0c415910f78274f859a33206efa44d924..0cbfb142954a467ebc8e466505b139af6eba582d 100644 (file)
@@ -4,6 +4,7 @@ import asyncio
 import base64
 import functools
 import http.client
+import jinja2
 import json
 import kerberos
 import logging
@@ -208,6 +209,28 @@ class BaseHandler(tornado.web.RequestHandler):
                return self.locale.format_date(date, relative=relative,
                        shorter=shorter, full_format=full_format)
 
+       # Template Loader
+
+       def create_template_loader(self, template_path):
+               """
+                       Creates a new template loader using Jinja
+               """
+               env = jinja2.Environment(
+                       # Load templates from the filesystem
+                       loader       = jinja2.FileSystemLoader(template_path),
+
+                       # Automatically escape
+                       autoescape   = True,
+
+                       # Enable asyncio
+                       #enable_async = True,
+
+                       # Cache templates for forever
+                       cache_size   = -1,
+               )
+
+               return JinjaTemplateLoader(env)
+
        def get_template_namespace(self):
                ns = tornado.web.RequestHandler.get_template_namespace(self)
 
@@ -223,6 +246,28 @@ class BaseHandler(tornado.web.RequestHandler):
 
                return ns
 
+       def render_string(self, template_name, **kwargs):
+               """
+                       Generates the given template with the given arguments.
+               """
+               template_path = self.get_template_path()
+
+               with self._template_loader_lock:
+                       try:
+                               loader = self._template_loaders[template_path]
+                       except KeyError:
+                               loader = self._template_loaders[template_path] = \
+                                       self.create_template_loader(template_path)
+
+               # Load the template
+               template = loader.load(template_name)
+
+               # Make the namespace
+               namespace = self.get_template_namespace()
+               namespace.update(kwargs)
+
+               return template.render(**namespace)
+
        def write_error(self, code, exc_info=None, **kwargs):
                try:
                        message = http.client.responses[code]
@@ -431,6 +476,28 @@ class APIMixin(KerberosAuthMixin):
                return message
 
 
+class JinjaTemplateLoader(tornado.template.BaseLoader):
+       """
+               Wraps around Jinja to allow using it as template engine
+       """
+       def __init__(self, env):
+               super().__init__()
+
+               # Store the environment
+               self.env = env
+
+       def resolve_path(self, name, parent_path=None):
+               """
+                       A function to convert any untrusted, relative paths into absolute.
+
+                       We will leave this problem for Jinja.
+               """
+               return self.env.join_path(name, parent_path)
+
+       def _create_template(self, name):
+               return self.env.get_template(name=name)
+
+
 def authenticated(method):
        """
                This is our custom authentication wrapper which supports an