From: Michael Tremer Date: Sun, 12 Jan 2025 12:44:25 +0000 (+0000) Subject: web: Replace the Tornado template engine with Jinja X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6ae9e272bcfc55626a4b1b07edb53aa758876800;p=pbs.git web: Replace the Tornado template engine with Jinja We need this to support any async code in the templates. Signed-off-by: Michael Tremer --- diff --git a/src/web/__init__.py b/src/web/__init__.py index 667f1062..05651a40 100644 --- a/src/web/__init__.py +++ b/src/web/__init__.py @@ -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, diff --git a/src/web/base.py b/src/web/base.py index 7b8d65f0..0cbfb142 100644 --- a/src/web/base.py +++ b/src/web/base.py @@ -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