Add csp compliance through django-csp, if installed (#34)
This commit is contained in:
parent
ec7d839793
commit
148ca6a257
15
README.rst
15
README.rst
|
@ -42,6 +42,21 @@ Example:
|
|||
)
|
||||
|
||||
|
||||
For Django 1.8+, if django-csp is installed, nonces will be added to style and script tags.
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
INSTALLED_APPS = (
|
||||
...
|
||||
'rangefilter',
|
||||
"csp",
|
||||
...
|
||||
)
|
||||
|
||||
|
||||
|
||||
Example usage
|
||||
-------------
|
||||
|
||||
|
|
|
@ -10,6 +10,11 @@ try:
|
|||
except ImportError:
|
||||
pytz = None
|
||||
|
||||
try:
|
||||
import csp
|
||||
except ImportError:
|
||||
csp = None
|
||||
|
||||
from collections import OrderedDict
|
||||
|
||||
from django import forms
|
||||
|
@ -100,7 +105,10 @@ class DateRangeFilter(admin.filters.FieldListFilter):
|
|||
def get_template(self):
|
||||
if django.VERSION[:2] <= (1, 8):
|
||||
return 'rangefilter/date_filter_1_8.html'
|
||||
return 'rangefilter/date_filter.html'
|
||||
else:
|
||||
if csp:
|
||||
return 'rangefilter/date_filter_csp.html'
|
||||
return 'rangefilter/date_filter.html'
|
||||
|
||||
template = property(get_template)
|
||||
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
(function() {
|
||||
'use strict';
|
||||
django.jQuery(".admindatefilter").each(
|
||||
function(){
|
||||
var form_id = django.jQuery(this).find("form").attr('id').slice(0,-5);
|
||||
var qs_name = form_id+"-query-string";
|
||||
var query_string = django.jQuery('input#'+qs_name).val();
|
||||
var form_name = form_id+"-form";
|
||||
|
||||
// Bind submit buttons
|
||||
django.jQuery(this).find("input[type=select]").bind("click",
|
||||
function(event){
|
||||
event.preventDefault();
|
||||
var form_data = django.jQuery('#'+form_name).serialize();
|
||||
window.location = window.location.pathname + query_string + '&' + form_data;
|
||||
});
|
||||
|
||||
// Bind reset buttons
|
||||
django.jQuery(this).find("input[type=reset]").bind("click",
|
||||
function(){
|
||||
window.location = window.location.pathname + query_string;
|
||||
});
|
||||
});
|
||||
})();
|
|
@ -0,0 +1,144 @@
|
|||
{% load i18n rangefilter_compat %}
|
||||
<h3>{% blocktrans with filter_title=title %}By {{ filter_title }}{% endblocktrans %}</h3>
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'admin/css/widgets.css' %}">
|
||||
<style nonce="{{ spec.request.csp_nonce }}">
|
||||
.button, input[type=submit], input[type=button], .submit-row input, a.button,
|
||||
.button, input[type=reset] {
|
||||
background: #79aec8;
|
||||
padding: 4px 5px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
}
|
||||
.admindatefilter {
|
||||
padding-left: 15px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid #eaeaea;
|
||||
}
|
||||
.admindatefilter p {
|
||||
padding-left: 0px;
|
||||
line-height: 0;
|
||||
}
|
||||
.admindatefilter p.datetime {
|
||||
line-height: 0;
|
||||
}
|
||||
.admindatefilter .timezonewarning {
|
||||
display: none;
|
||||
}
|
||||
.admindatefilter .datetimeshortcuts a:first-child {
|
||||
margin-right: 4px;
|
||||
display: none;
|
||||
}
|
||||
.calendarbox {
|
||||
z-index: 1100;
|
||||
}
|
||||
.clockbox {
|
||||
z-index: 1100;
|
||||
margin-left: -8em !important;
|
||||
margin-top: 5em !important;
|
||||
}
|
||||
.admindatefilter .datetimeshortcuts {
|
||||
font-size: 0;
|
||||
float: right;
|
||||
position: absolute;
|
||||
padding-top: 4px;
|
||||
}
|
||||
.admindatefilter a {
|
||||
color: #999;
|
||||
position: absolute;
|
||||
padding-top: 3px;
|
||||
padding-left: 4px;
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
.calendarbox {
|
||||
margin-left: -16em !important;
|
||||
margin-top: 9em !important;
|
||||
}
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
.calendarbox {
|
||||
overflow: visible;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<!--
|
||||
Force load jsi18n, issues #5
|
||||
https://github.com/django/django/blob/stable/1.10.x/django/contrib/admin/templates/admin/change_list.html#L7
|
||||
-->
|
||||
<script type="text/javascript" src="{% url 'admin:jsi18n' %}"></script>
|
||||
|
||||
<script type="text/javascript" nonce="{{ spec.request.csp_nonce }}">
|
||||
// Code below makes sure that the DateTimeShortcuts.js is loaded exactly once
|
||||
// regardless the presence of AdminDateWidget
|
||||
// How it worked:
|
||||
// - First Django loads the model formset with predefined widgets for different
|
||||
// field types. If there's a date based field, then it loads the AdminDateWidget
|
||||
// and it's required media to context under {{media.js}} in admin/change_list.html.
|
||||
// (Note: it accumulates media in django.forms.widgets.Media object,
|
||||
// which prevents duplicates, but the DateRangeFilter is not included yet
|
||||
// since it's not model field related.
|
||||
// List of predefined widgets is in django.contrib.admin.options.FORMFIELD_FOR_DBFIELD_DEFAULTS)
|
||||
// - After that Django starts rendering forms, which have the {{form.media}}
|
||||
// tag. Only then the DjangoRangeFilter.get_media is called and rendered,
|
||||
// which creates the duplicates.
|
||||
// How it works:
|
||||
// - first step is the same, if there's a AdminDateWidget to be loaded then
|
||||
// nothing changes
|
||||
// - DOM gets rendered and if the AdminDateWidget was rendered then
|
||||
// the DateTimeShortcuts.js is initiated which sets the window.DateTimeShortcuts.
|
||||
// Otherwise, the window.DateTimeShortcuts is undefined.
|
||||
// - The lines below check if the DateTimeShortcuts has been set and if not
|
||||
// then the DateTimeShortcuts.js and calendar.js is rendered
|
||||
//
|
||||
// https://github.com/silentsokolov/django-admin-rangefilter/issues/9
|
||||
//
|
||||
// Django 2.1
|
||||
// https://github.com/silentsokolov/django-admin-rangefilter/issues/21
|
||||
|
||||
// Make a promise out of script embed
|
||||
function embedScript(url) {
|
||||
return new Promise(function pr(resolve, reject) {
|
||||
var newScript = document.createElement("script");
|
||||
newScript.type = "text/javascript";
|
||||
newScript.src = url;
|
||||
newScript.onload = resolve;
|
||||
newScript.setAttribute("nonce", "{{ spec.request.csp_nonce }}");
|
||||
document.head.appendChild(newScript);
|
||||
});
|
||||
}
|
||||
|
||||
django.jQuery('document').ready(function () {
|
||||
if (!('DateTimeShortcuts' in window)) {
|
||||
var promiseList = [];
|
||||
|
||||
{% for m in spec.form.js %}
|
||||
promiseList.push(embedScript("{{ m }}"));
|
||||
{% endfor %}
|
||||
|
||||
Promise.all(promiseList).then(function() {
|
||||
django.jQuery('.datetimeshortcuts').remove();
|
||||
window.DateTimeShortcuts.init();
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="admindatefilter">
|
||||
<form method="GET" action="." id="{{ choices.0.system_name }}-form">
|
||||
{{ spec.form.as_p }}
|
||||
{% for choice in choices %}
|
||||
<input type="hidden" id="{{ choice.system_name }}-query-string" value="{{ choice.query_string }}">
|
||||
{% endfor %}
|
||||
<div class="controls">
|
||||
<input type="submit" class="button" value="{% trans "Search" %}">
|
||||
<input type="reset" class="button" value="{% trans "Reset" %}">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!--
|
||||
JS not requiring template variables refactored as an iife
|
||||
-->
|
||||
<script type="text/javascript" src="{% static 'rangefilter/iife.js' %}"></script>
|
Loading…
Reference in New Issue