Commit d638cdc4 authored by Thomas Grainger's avatar Thomas Grainger Committed by Tim Graham
Browse files

Fixed #25165 -- Removed inline JavaScript from the admin.

This allows setting a Content-Security-Policy HTTP header
(refs #15727).

Special thanks to blighj, the original author of this patch.
parent 105028ee
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -14,7 +14,7 @@
        "no-octal-escape": [2],
        "no-underscore-dangle": [2],
        "no-unused-vars": [2, {"vars": "local", "args": "none"}],
        "no-script-url": [1],
        "no-script-url": [2],
        "no-shadow": [2, {"hoist": "functions"}],
        "quotes": [0, "single"],
        "linebreak-style": [2, "unix"],
+1 −0
Original line number Diff line number Diff line
@@ -74,6 +74,7 @@ def delete_selected(modeladmin, request, queryset):
        protected=protected,
        opts=opts,
        action_checkbox_name=helpers.ACTION_CHECKBOX_NAME,
        media=modeladmin.media,
    )

    request.current_app = modeladmin.admin_site.name
+15 −1
Original line number Diff line number Diff line
from __future__ import unicode_literals

import json
import warnings

from django import forms
@@ -18,7 +19,7 @@ from django.utils.deprecation import RemovedInDjango20Warning
from django.utils.encoding import force_text, smart_text
from django.utils.html import conditional_escape, format_html
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import ugettext, ugettext_lazy as _

ACTION_CHECKBOX_NAME = '_selected_action'

@@ -276,6 +277,19 @@ class InlineAdminFormSet(object):
                    'help_text': form_field.help_text,
                }

    def inline_formset_data(self):
        verbose_name = self.opts.verbose_name
        return json.dumps({
            'name': '#%s' % self.formset.prefix,
            'options': {
                'prefix': self.formset.prefix,
                'addText': ugettext('Add another %(verbose_name)s') % {
                    'verbose_name': capfirst(verbose_name),
                },
                'deleteText': ugettext('Remove'),
            }
        })

    def _media(self):
        media = self.opts.media + self.formset.media
        for fs in self:
+21 −10
Original line number Diff line number Diff line
from __future__ import unicode_literals

import copy
import json
import operator
from collections import OrderedDict
from functools import partial, reduce, update_wrapper
@@ -40,7 +41,7 @@ from django.template.response import SimpleTemplateResponse, TemplateResponse
from django.utils import six
from django.utils.decorators import method_decorator
from django.utils.encoding import force_text, python_2_unicode_compatible
from django.utils.html import escape, escapejs, format_html
from django.utils.html import escape, format_html
from django.utils.http import urlencode, urlquote
from django.utils.safestring import mark_safe
from django.utils.text import capfirst, get_text_list
@@ -568,9 +569,9 @@ class ModelAdmin(BaseModelAdmin):
        extra = '' if settings.DEBUG else '.min'
        js = [
            'core.js',
            'admin/RelatedObjectLookups.js',
            'vendor/jquery/jquery%s.js' % extra,
            'jquery.init.js',
            'admin/RelatedObjectLookups.js',
            'actions%s.js' % extra,
            'urlify.js',
            'prepopulate%s.js' % extra,
@@ -1084,9 +1085,12 @@ class ModelAdmin(BaseModelAdmin):
            else:
                attr = obj._meta.pk.attname
            value = obj.serializable_value(attr)
            return SimpleTemplateResponse('admin/popup_response.html', {
            popup_response_data = json.dumps({
                'value': value,
                'obj': obj,
                'obj': six.text_type(obj),
            })
            return SimpleTemplateResponse('admin/popup_response.html', {
                'popup_response_data': popup_response_data,
            })

        elif "_continue" in request.POST:
@@ -1132,11 +1136,14 @@ class ModelAdmin(BaseModelAdmin):
            # Retrieve the `object_id` from the resolved pattern arguments.
            value = request.resolver_match.args[0]
            new_value = obj.serializable_value(attr)
            return SimpleTemplateResponse('admin/popup_response.html', {
            popup_response_data = json.dumps({
                'action': 'change',
                'value': escape(value),
                'obj': escapejs(obj),
                'new_value': escape(new_value),
                'value': value,
                'obj': six.text_type(obj),
                'new_value': new_value,
            })
            return SimpleTemplateResponse('admin/popup_response.html', {
                'popup_response_data': popup_response_data,
            })

        opts = self.model._meta
@@ -1300,9 +1307,12 @@ class ModelAdmin(BaseModelAdmin):
        opts = self.model._meta

        if IS_POPUP_VAR in request.POST:
            return SimpleTemplateResponse('admin/popup_response.html', {
            popup_response_data = json.dumps({
                'action': 'delete',
                'value': escape(obj_id),
                'value': obj_id,
            })
            return SimpleTemplateResponse('admin/popup_response.html', {
                'popup_response_data': popup_response_data,
            })

        self.message_user(request,
@@ -1332,6 +1342,7 @@ class ModelAdmin(BaseModelAdmin):
        context.update(
            to_field_var=TO_FIELD_VAR,
            is_popup_var=IS_POPUP_VAR,
            media=self.media,
        )

        return TemplateResponse(request,
+12 −4
Original line number Diff line number Diff line
@@ -75,15 +75,15 @@ Requires core.js, SelectBox.js and addevent.js.
            filter_input.id = field_id + '_input';

            selector_available.appendChild(from_box);
            var choose_all = quickElement('a', selector_available, gettext('Choose all'), 'title', interpolate(gettext('Click to choose all %s at once.'), [field_name]), 'href', 'javascript:void(0);', 'id', field_id + '_add_all_link');
            var choose_all = quickElement('a', selector_available, gettext('Choose all'), 'title', interpolate(gettext('Click to choose all %s at once.'), [field_name]), 'href', '#', 'id', field_id + '_add_all_link');
            choose_all.className = 'selector-chooseall';

            // <ul class="selector-chooser">
            var selector_chooser = quickElement('ul', selector_div);
            selector_chooser.className = 'selector-chooser';
            var add_link = quickElement('a', quickElement('li', selector_chooser), gettext('Choose'), 'title', gettext('Choose'), 'href', 'javascript:void(0);', 'id', field_id + '_add_link');
            var add_link = quickElement('a', quickElement('li', selector_chooser), gettext('Choose'), 'title', gettext('Choose'), 'href', '#', 'id', field_id + '_add_link');
            add_link.className = 'selector-add';
            var remove_link = quickElement('a', quickElement('li', selector_chooser), gettext('Remove'), 'title', gettext('Remove'), 'href', 'javascript:void(0);', 'id', field_id + '_remove_link');
            var remove_link = quickElement('a', quickElement('li', selector_chooser), gettext('Remove'), 'title', gettext('Remove'), 'href', '#', 'id', field_id + '_remove_link');
            remove_link.className = 'selector-remove';

            // <div class="selector-chosen">
@@ -105,7 +105,7 @@ Requires core.js, SelectBox.js and addevent.js.

            var to_box = quickElement('select', selector_chosen, '', 'id', field_id + '_to', 'multiple', 'multiple', 'size', from_box.size, 'name', from_box.getAttribute('name'));
            to_box.className = 'filtered';
            var clear_all = quickElement('a', selector_chosen, gettext('Remove all'), 'title', interpolate(gettext('Click to remove all chosen %s at once.'), [field_name]), 'href', 'javascript:void(0);', 'id', field_id + '_remove_all_link');
            var clear_all = quickElement('a', selector_chosen, gettext('Remove all'), 'title', interpolate(gettext('Click to remove all chosen %s at once.'), [field_name]), 'href', '#', 'id', field_id + '_remove_all_link');
            clear_all.className = 'selector-clearall';

            from_box.setAttribute('name', from_box.getAttribute('name') + '_old');
@@ -195,4 +195,12 @@ Requires core.js, SelectBox.js and addevent.js.
        }
    };

    addEvent(window, 'load', function(e) {
        $('select.selectfilter, select.selectfilterstacked').each(function() {
            var $el = $(this),
                data = $el.data();
            SelectFilter.init($el.attr('id'), data.fieldName, parseInt(data.isStacked, 10));
        });
    });

})(django.jQuery);
Loading