Skip to content

Commit

Permalink
Merge pull request #2571 from pamelafox/startfix
Browse files Browse the repository at this point in the history
Fix errors caused in jinja StrictUndefined mode
  • Loading branch information
samuelhwilliams authored Nov 21, 2024
2 parents 1a18428 + 9d97757 commit 2389693
Show file tree
Hide file tree
Showing 10 changed files with 48 additions and 23 deletions.
7 changes: 7 additions & 0 deletions doc/changelog.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
Changelog
=========

2.0.0a3
-------

Fixes:

* Jinja templates can now be loaded in StrictUndefined mode.

2.0.0a2
-------

Expand Down
2 changes: 2 additions & 0 deletions examples/sqla/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from admin import app
from admin.data import build_sample_db
from jinja2 import StrictUndefined

# Build a sample db on the fly, if one does not exist yet.
app_dir = op.join(op.realpath(os.path.dirname(__file__)), "admin")
Expand All @@ -13,4 +14,5 @@

if __name__ == "__main__":
# Start app
app.jinja_env.undefined = StrictUndefined
app.run(debug=True)
6 changes: 6 additions & 0 deletions flask_admin/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,12 @@ def index(self):
)
"""

extra_css: list[str] = []
"""Extra CSS files to include in the page"""

extra_js: list[str] = []
"""Extra JavaScript files to include in the page"""

@property
def _template_args(self):
"""
Expand Down
10 changes: 7 additions & 3 deletions flask_admin/templates/bootstrap4/admin/actions.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,16 @@
{% macro form(actions, url) %}
{% if actions %}
<form id="action_form" action="{{ url }}" method="POST" class="d-none">
{% if action_form.csrf_token %}
{% if action_form.csrf_token is defined and action_form.csrf_token %}
{{ action_form.csrf_token }}
{% elif csrf_token %}
{% elif csrf_token is defined and csrf_token %}
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
{% endif %}
{{ action_form.url(value=return_url) }}
{% if return_url is defined and return_url %}
{{ action_form.url(value=return_url) }}
{% else %}
{{ action_form.url() }}
{% endif %}
{{ action_form.action() }}
</form>
{% endif %}
Expand Down
4 changes: 4 additions & 0 deletions flask_admin/templates/bootstrap4/admin/file/list.html
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,9 @@
{% if name != '..' and admin_view.can_delete_dirs %}
<form class="icon" method="POST" action="{{ get_url('.delete') }}">
{{ delete_form.path(value=path) }}
{% if delete_form.csrf_token is defined and delete_form.csrf_token %}
{{ delete_form.csrf_token }}
{% endif %}
<button onclick="return confirm('{{ _gettext('Are you sure you want to delete \\\'%(name)s\\\' recursively?', name=name) }}')">
<i class="fa fa-times glyphicon glyphicon-remove"></i>
</button>
Expand All @@ -95,7 +97,9 @@
{% else %}
<form class="icon" method="POST" action="{{ get_url('.delete') }}">
{{ delete_form.path(value=path) }}
{% if delete_form.csrf_token is defined and delete_form.csrf_token %}
{{ delete_form.csrf_token }}
{% endif %}
<button onclick="return confirm('{{ _gettext('Are you sure you want to delete \\\'%(name)s\\\'?', name=name) }}')">
<i class="fa fa-trash glyphicon glyphicon-trash"></i>
</button>
Expand Down
28 changes: 14 additions & 14 deletions flask_admin/templates/bootstrap4/admin/lib.html
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@
{% set prepend = kwargs.pop('prepend', None) %}
{% set append = kwargs.pop('append', None) %}
<div class="form-group {{ kwargs.get('column_class', '') }}">
<label for="{{ field.id }}" class="col-form-label {% if field.widget.input_type == 'checkbox' %}d-block mb-0{% endif %}">{{ field.label.text }}
<label for="{{ field.id }}" class="col-form-label {% if field.widget.input_type is defined and field.widget.input_type == 'checkbox' %}d-block mb-0{% endif %}">{{ field.label.text }}
{% if h.is_required_form_field(field) %}
<strong class="text-danger">&#42;</strong>
{%- else -%}
Expand All @@ -135,9 +135,9 @@
</div>
{%- endif -%}
{% endif %}
{% if field.widget.input_type == 'checkbox' %}
{% if field.widget.input_type is defined and field.widget.input_type == 'checkbox' %}
{% set _class = kwargs.setdefault('class', '') %}
{% elif field.widget.input_type == 'file' %}
{% elif field.widget.input_type is defined and field.widget.input_type == 'file' %}
{% set _class = kwargs.setdefault('class', 'form-control-file') %}
{% else %}
{% set _class = kwargs.setdefault('class', 'form-control') %}
Expand Down Expand Up @@ -179,10 +179,10 @@ <h3>{{ text }}</h3>
{% if form.hidden_tag is defined %}
{{ form.hidden_tag() }}
{% else %}
{% if csrf_token %}
{% if csrf_token is defined and csrf_token %}
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
{% endif %}
{% for f in form if f.widget.input_type == 'hidden' %}
{% for f in form if f.widget.input_type is defined and f.widget.input_type == 'hidden' %}
{{ f }}
{% endfor %}
{% endif %}
Expand All @@ -192,7 +192,7 @@ <h3>{{ text }}</h3>
{{ r(form, form_opts=form_opts) }}
{% endfor %}
{% else %}
{% for f in form if f.widget.input_type != 'hidden' %}
{% for f in form if f.widget.input_type is undefined or f.widget.input_type != 'hidden' %}
{% if form_opts %}
{% set kwargs = form_opts.widget_args.get(f.short_name, {}) %}
{% else %}
Expand Down Expand Up @@ -228,7 +228,7 @@ <h3>{{ text }}</h3>
{% if extra %}
{{ extra }}
{% endif %}
{% if cancel_url %}
{% if cancel_url is defined and cancel_url %}
<a href="{{ cancel_url }}" class="btn btn-danger" role="button" {% if is_modal %}data-dismiss="modal"{% endif %}>{{ _gettext('Cancel') }}</a>
{% endif %}
</div>
Expand All @@ -247,39 +247,39 @@ <h3>{{ text }}</h3>
<link href="{{ admin_static.url(filename='vendor/select2/select2.css', v='4.2.1') }}" rel="stylesheet">
<link href="{{ admin_static.url(filename='vendor/select2/select2-bootstrap4.css', v='1.4.6') }}" rel="stylesheet">
<link href="{{ admin_static.url(filename='vendor/bootstrap-daterangepicker/daterangepicker-bs4.css', v='1.3.22') }}" rel="stylesheet">
{% if config.FLASK_ADMIN_MAPS %}
{% if config.FLASK_ADMIN_MAPS is defined and config.FLASK_ADMIN_MAPS %}
<link href="{{ admin_static.url(filename='vendor/leaflet/leaflet.css', v='1.0.2') }}" rel="stylesheet">
<link href="{{ admin_static.url(filename='vendor/leaflet/leaflet.draw.css', v='0.4.6') }}" rel="stylesheet">
{% endif %}
{% if editable_columns %}
{% if editable_columns is defined and editable_columns %}
<link href="{{ admin_static.url(filename='vendor/x-editable/css/bootstrap4-editable.css', v='1.5.1.1') }}" rel="stylesheet">
{% endif %}
{% endmacro %}

{% macro form_js() %}
{% if config.FLASK_ADMIN_MAPS %}
{% if config.FLASK_ADMIN_MAPS is defined and config.FLASK_ADMIN_MAPS %}
<script {{ admin_csp_nonce_attribute }}>
window.FLASK_ADMIN_MAPS = true;
window.FLASK_ADMIN_MAPBOX_MAP_ID = "{{ config.FLASK_ADMIN_MAPBOX_MAP_ID }}";
{% if config.FLASK_ADMIN_MAPBOX_ACCESS_TOKEN %}
{% if config.FLASK_ADMIN_MAPBOX_ACCESS_TOKEN is defined and config.FLASK_ADMIN_MAPBOX_ACCESS_TOKEN %}
window.FLASK_ADMIN_MAPBOX_ACCESS_TOKEN = "{{ config.FLASK_ADMIN_MAPBOX_ACCESS_TOKEN }}";
{% endif %}
{% if config.FLASK_ADMIN_DEFAULT_CENTER_LAT and config.FLASK_ADMIN_DEFAULT_CENTER_LONG %}
{% if config.FLASK_ADMIN_DEFAULT_CENTER_LAT is defined and config.FLASK_ADMIN_DEFAULT_CENTER_LONG %}
window.FLASK_ADMIN_DEFAULT_CENTER_LAT = "{{ config.FLASK_ADMIN_DEFAULT_CENTER_LAT }}";
window.FLASK_ADMIN_DEFAULT_CENTER_LONG = "{{ config.FLASK_ADMIN_DEFAULT_CENTER_LONG }}";
{% endif %}
</script>
<script {{ admin_csp_nonce_attribute }} src="{{ admin_static.url(filename='vendor/leaflet/leaflet.js', v='1.0.2') }}"></script>
<script {{ admin_csp_nonce_attribute }} src="{{ admin_static.url(filename='vendor/leaflet/leaflet.draw.js', v='0.4.6') }}"></script>
{% if config.FLASK_ADMIN_MAPS_SEARCH %}
{% if config.FLASK_ADMIN_MAPS_SEARCH is defined and config.FLASK_ADMIN_MAPS_SEARCH %}
<script {{ admin_csp_nonce_attribute }}>
window.FLASK_ADMIN_MAPS_SEARCH = "{{ config.FLASK_ADMIN_MAPS_SEARCH }}";
</script>
<script {{ admin_csp_nonce_attribute }} src="https://maps.googleapis.com/maps/api/js?v=3&libraries=places&key={{ config.get('FLASK_ADMIN_GOOGLE_MAPS_API_KEY') }}"></script>
{% endif %}
{% endif %}
<script {{ admin_csp_nonce_attribute }} src="{{ admin_static.url(filename='vendor/bootstrap-daterangepicker/daterangepicker.js', v='1.3.22') }}"></script>
{% if editable_columns %}
{% if editable_columns is defined and editable_columns %}
<script {{ admin_csp_nonce_attribute }} src="{{ admin_static.url(filename='vendor/x-editable/js/bootstrap4-editable.min.js', v='1.5.1.1') }}"></script>
{% endif %}
<script {{ admin_csp_nonce_attribute }} src="{{ admin_static.url(filename='admin/js/form.js', v='1.0.1') }}"></script>
Expand Down
4 changes: 2 additions & 2 deletions flask_admin/templates/bootstrap4/admin/model/list.html
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,9 @@
<td class="col-{{c}}">
{% if admin_view.is_editable(c) %}
{% set form = list_forms[get_pk_value(row)] %}
{% if form.csrf_token %}
{% if form.csrf_token is defined and form.csrf_token %}
{{ form[c](pk=get_pk_value(row), display_value=get_value(row, c), csrf=form.csrf_token._value()) }}
{% elif csrf_token %}
{% elif csrf_token is defined and csrf_token %}
{{ form[c](pk=get_pk_value(row), display_value=get_value(row, c), csrf=csrf_token()) }}
{% else %}
{{ form[c](pk=get_pk_value(row), display_value=get_value(row, c)) }}
Expand Down
4 changes: 2 additions & 2 deletions flask_admin/templates/bootstrap4/admin/model/row_actions.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@
<form class="icon" method="POST" action="{{ get_url('.delete_view') }}">
{{ delete_form.id(value=get_pk_value(row)) }}
{{ delete_form.url(value=return_url) }}
{% if delete_form.csrf_token %}
{% if delete_form.csrf_token is defined and delete_form.csrf_token %}
{{ delete_form.csrf_token }}
{% elif csrf_token %}
{% elif csrf_token is defined and csrf_token %}
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
{% endif %}
<button onclick="return faHelpers.safeConfirm('{{ _gettext('Are you sure you want to delete this record?') }}');" title="{{ _gettext('Delete Record') }}">
Expand Down
3 changes: 2 additions & 1 deletion flask_admin/tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import pytest
from flask import Flask
from jinja2 import StrictUndefined

from flask_admin import Admin

Expand All @@ -9,7 +10,7 @@ def app():
app = Flask(__name__)
app.config["SECRET_KEY"] = "1"
app.config["WTF_CSRF_ENABLED"] = False

app.jinja_env.undefined = StrictUndefined
yield app


Expand Down
3 changes: 2 additions & 1 deletion flask_admin/tests/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from flask import request
from flask import url_for
from flask.views import MethodView
from jinja2 import StrictUndefined

from flask_admin import base

Expand All @@ -18,7 +19,7 @@ def app():
app = Flask(__name__)
app.config["SECRET_KEY"] = "1"
app.config["WTF_CSRF_ENABLED"] = False

app.jinja_env.undefined = StrictUndefined
yield app


Expand Down

0 comments on commit 2389693

Please sign in to comment.