Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Override templates in a custom directory #701

Merged
merged 1 commit into from Jul 21, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions news/PR701.feature
@@ -0,0 +1 @@
Allow template override with a custom directory, see the ``TEMPLATES_CUSTOM_DIRECTORIES`` configration value
15 changes: 15 additions & 0 deletions noggin.cfg.example
Expand Up @@ -87,6 +87,21 @@ MAIL_SUPPRESS_SEND = True
# will only be accessible to users that have this role.
# STAGE_USERS_ROLE = "Stage User Managers"

# Additional directories to look up folders in. The templates in these directories
# will override the app's template with the same filename. You can also create the
# following templates to insert HTML in the output:
# - `after-navbar.html`: will be inserted between the navbar and the main content
# on every page
# - `before-footer.html`: will be inserted between the main content and the footer
# on every page
# - `head.html`: will be inserted at the end of the <head> tag on every page
# TEMPLATES_CUSTOM_DIRECTORIES = []

# The following sources will be added to the Content Security Policy for images.
# This can be useful if you want to add images in custom templates.
# https://content-security-policy.com/img-src/
# ACCEPT_IMAGES_FROM = []

# Spam checking
# BASSET_URL = None
# SPAMCHECK_TOKEN_EXPIRATION = 60 # in minutes
Expand Down
18 changes: 16 additions & 2 deletions noggin/app.py
Expand Up @@ -2,6 +2,7 @@
from logging.config import dictConfig

import flask_talisman
import jinja2
from flask import Flask
from flask_healthz import healthz
from flask_mail import Mail
Expand Down Expand Up @@ -55,6 +56,15 @@ def create_app(config=None):
if app.config.get("TEMPLATES_AUTO_RELOAD"):
app.jinja_env.auto_reload = True

# Custom template folders
if app.config["TEMPLATES_CUSTOM_DIRECTORIES"]:
app.jinja_loader = jinja2.ChoiceLoader(
[
jinja2.FileSystemLoader(app.config["TEMPLATES_CUSTOM_DIRECTORIES"]),
app.jinja_loader,
]
)

# Logging
if app.config.get("LOGGING"):
dictConfig(app.config["LOGGING"])
Expand Down Expand Up @@ -83,9 +93,13 @@ def create_app(config=None):
# https://csp.withgoogle.com/docs/strict-csp.html#example
"'strict-dynamic'",
],
"img-src": ["'self'", "seccdn.libravatar.org"],
"img-src": ["'self'", "seccdn.libravatar.org"]
+ app.config["ACCEPT_IMAGES_FROM"],
# The style-src directive needs to be specified (even if it's the same as default-src)
# to add the nonce.
"style-src": "'self'",
},
content_security_policy_nonce_in=['script-src'],
content_security_policy_nonce_in=['script-src', 'style-src'],
)

# Template filters
Expand Down
3 changes: 3 additions & 0 deletions noggin/defaults.py
Expand Up @@ -36,6 +36,9 @@

STAGE_USERS_ROLE = "Stage User Managers"

TEMPLATES_CUSTOM_DIRECTORIES = []
ACCEPT_IMAGES_FROM = []

BASSET_URL = None
SPAMCHECK_TOKEN_EXPIRATION = 60 # in minutes

Expand Down
3 changes: 3 additions & 0 deletions noggin/templates/base.html
Expand Up @@ -8,10 +8,13 @@
<link href="{{ url_for('static', filename='fonts/font-awesome.css') }}" rel="stylesheet" />
<link href="{{ url_for('static', filename='css/main.css') }}" rel="stylesheet" type="text/css">
<title>{% block title %}{% endblock %}{% if self.title() %} - {% endif %}{% block website %}noggin{% endblock %}</title>
{% include "head.html" ignore missing %}
</head>
<body>
{% block navbar %} {% endblock %}
{% include "after-navbar.html" ignore missing %}
{% block bodycontent %}{% endblock %}
{% include "before-footer.html" ignore missing %}
{% block footer %}{% endblock %}
{% block scripts %}
{% if current_user %}
Expand Down
30 changes: 30 additions & 0 deletions noggin/tests/unit/test_app.py
Expand Up @@ -36,3 +36,33 @@ def test_logging(mocker, app_config):
logging_config = mocker.patch("noggin.app.dictConfig")
create_app(config)
logging_config.assert_called_once_with("dummy-logging-config")


def test_templates_custom_directory_insertion(app_config, tmp_path):
tpl_dir = tmp_path / "templates"
os.makedirs(tpl_dir)
config = app_config.copy()
config["TEMPLATES_CUSTOM_DIRECTORIES"] = [tpl_dir]
app = create_app(config)
# Use the template placeholder
with open(tpl_dir / "after-navbar.html", "w") as tpl:
tpl.write("TESTING TEMPLATES CUSTOM DIR\n")
with app.test_client() as client:
response = client.get('/')
assert response.status_code == 200
assert "TESTING TEMPLATES CUSTOM DIR" in response.get_data(as_text=True)


def test_templates_custom_directory_override(app_config, tmp_path):
tpl_dir = tmp_path / "templates"
os.makedirs(tpl_dir)
config = app_config.copy()
config["TEMPLATES_CUSTOM_DIRECTORIES"] = [tpl_dir]
app = create_app(config)
# Override the whole page template
with open(tpl_dir / "index.html", "w") as tpl:
tpl.write("TESTING TEMPLATES CUSTOM DIR\n")
with app.test_client() as client:
response = client.get('/')
assert response.status_code == 200
assert response.get_data(as_text=True) == "TESTING TEMPLATES CUSTOM DIR"