mirror of
https://github.com/django/django.git
synced 2024-12-22 09:05:43 +00:00
Fixed #31524 -- Removed minified static assets from the admin.
This commit is contained in:
parent
6c19c2ca51
commit
81ffedaacc
@ -7,7 +7,6 @@ include MANIFEST.in
|
||||
include package.json
|
||||
include *.rst
|
||||
graft django
|
||||
prune django/contrib/admin/bin
|
||||
graft docs
|
||||
graft extras
|
||||
graft js_tests
|
||||
|
@ -1,52 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
import argparse
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
js_path = Path(__file__).parents[1] / 'static' / 'admin' / 'js'
|
||||
|
||||
|
||||
def main():
|
||||
description = """With no file paths given this script will automatically
|
||||
compress files of the admin app. Requires the Google Closure Compiler library
|
||||
and Java version 7 or later."""
|
||||
parser = argparse.ArgumentParser(description=description)
|
||||
parser.add_argument('file', nargs='*')
|
||||
parser.add_argument("-v", "--verbose", action="store_true", dest="verbose")
|
||||
parser.add_argument("-q", "--quiet", action="store_false", dest="verbose")
|
||||
options = parser.parse_args()
|
||||
|
||||
if not options.file:
|
||||
if options.verbose:
|
||||
sys.stdout.write("No filenames given; defaulting to admin scripts\n")
|
||||
files = [
|
||||
js_path / f
|
||||
for f in ["actions.js", "collapse.js", "inlines.js", "prepopulate.js"]
|
||||
]
|
||||
else:
|
||||
files = [Path(f) for f in options.file]
|
||||
|
||||
for file_path in files:
|
||||
to_compress = file_path.expanduser()
|
||||
if to_compress.exists():
|
||||
to_compress_min = to_compress.with_suffix('.min.js')
|
||||
cmd = ['npx']
|
||||
if not options.verbose:
|
||||
cmd.append('-q')
|
||||
cmd.extend([
|
||||
'google-closure-compiler',
|
||||
'--language_out=ECMASCRIPT_2015',
|
||||
'--rewrite_polyfills=false',
|
||||
'--js', str(to_compress),
|
||||
'--js_output_file', str(to_compress_min),
|
||||
])
|
||||
if options.verbose:
|
||||
sys.stdout.write("Running: %s\n" % ' '.join(cmd))
|
||||
subprocess.run(cmd)
|
||||
else:
|
||||
sys.stdout.write("File %s not found. Sure it exists?\n" % to_compress)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,7 +1,6 @@
|
||||
import json
|
||||
|
||||
from django import forms
|
||||
from django.conf import settings
|
||||
from django.contrib.admin.utils import (
|
||||
display_for_field, flatten_fieldsets, help_text_for_field, label_for_field,
|
||||
lookup_field,
|
||||
@ -80,8 +79,7 @@ class Fieldset:
|
||||
@property
|
||||
def media(self):
|
||||
if 'collapse' in self.classes:
|
||||
extra = '' if settings.DEBUG else '.min'
|
||||
return forms.Media(js=['admin/js/collapse%s.js' % extra])
|
||||
return forms.Media(js=['admin/js/collapse.js'])
|
||||
return forms.Media()
|
||||
|
||||
def __iter__(self):
|
||||
|
@ -642,9 +642,9 @@ class ModelAdmin(BaseModelAdmin):
|
||||
'jquery.init.js',
|
||||
'core.js',
|
||||
'admin/RelatedObjectLookups.js',
|
||||
'actions%s.js' % extra,
|
||||
'actions.js',
|
||||
'urlify.js',
|
||||
'prepopulate%s.js' % extra,
|
||||
'prepopulate.js',
|
||||
'vendor/xregexp/xregexp%s.js' % extra,
|
||||
]
|
||||
return forms.Media(js=['admin/js/%s' % url for url in js])
|
||||
@ -2024,12 +2024,11 @@ class InlineModelAdmin(BaseModelAdmin):
|
||||
@property
|
||||
def media(self):
|
||||
extra = '' if settings.DEBUG else '.min'
|
||||
js = ['vendor/jquery/jquery%s.js' % extra, 'jquery.init.js',
|
||||
'inlines%s.js' % extra]
|
||||
js = ['vendor/jquery/jquery%s.js' % extra, 'jquery.init.js', 'inlines.js']
|
||||
if self.filter_vertical or self.filter_horizontal:
|
||||
js.extend(['SelectBox.js', 'SelectFilter2.js'])
|
||||
if self.classes and 'collapse' in self.classes:
|
||||
js.append('collapse%s.js' % extra)
|
||||
js.append('collapse.js')
|
||||
return forms.Media(js=['admin/js/%s' % url for url in js])
|
||||
|
||||
def get_extra(self, request, obj=None, **kwargs):
|
||||
|
@ -1,7 +0,0 @@
|
||||
'use strict';{const a=django.jQuery;let e;a.fn.actions=function(g){const b=a.extend({},a.fn.actions.defaults,g),f=a(this);let k=!1;const l=function(){a(b.acrossClears).hide();a(b.acrossQuestions).show();a(b.allContainer).hide()},m=function(){a(b.acrossClears).show();a(b.acrossQuestions).hide();a(b.actionContainer).toggleClass(b.selectedClass);a(b.allContainer).show();a(b.counterContainer).hide()},n=function(){a(b.acrossClears).hide();a(b.acrossQuestions).hide();a(b.allContainer).hide();a(b.counterContainer).show()},
|
||||
p=function(){n();a(b.acrossInput).val(0);a(b.actionContainer).removeClass(b.selectedClass)},q=function(c){c?l():n();a(f).prop("checked",c).parent().parent().toggleClass(b.selectedClass,c)},h=function(){const c=a(f).filter(":checked").length,d=a(".action-counter").data("actionsIcnt");a(b.counterContainer).html(interpolate(ngettext("%(sel)s of %(cnt)s selected","%(sel)s of %(cnt)s selected",c),{sel:c,cnt:d},!0));a(b.allToggle).prop("checked",function(){let a;c===f.length?(a=!0,l()):(a=!1,p());return a})};
|
||||
a(b.counterContainer).show();a(this).filter(":checked").each(function(c){a(this).parent().parent().toggleClass(b.selectedClass);h();1===a(b.acrossInput).val()&&m()});a(b.allToggle).show().on("click",function(){q(a(this).prop("checked"));h()});a("a",b.acrossQuestions).on("click",function(c){c.preventDefault();a(b.acrossInput).val(1);m()});a("a",b.acrossClears).on("click",function(c){c.preventDefault();a(b.allToggle).prop("checked",!1);p();q(0);h()});e=null;a(f).on("click",function(c){c||(c=window.event);
|
||||
const d=c.target?c.target:c.srcElement;if(e&&a.data(e)!==a.data(d)&&!0===c.shiftKey){let c=!1;a(e).prop("checked",d.checked).parent().parent().toggleClass(b.selectedClass,d.checked);a(f).each(function(){if(a.data(this)===a.data(e)||a.data(this)===a.data(d))c=c?!1:!0;c&&a(this).prop("checked",d.checked).parent().parent().toggleClass(b.selectedClass,d.checked)})}a(d).parent().parent().toggleClass(b.selectedClass,d.checked);e=d;h()});a("form#changelist-form table#result_list tr").on("change","td:gt(0) :input",
|
||||
function(){k=!0});a('form#changelist-form button[name="index"]').on("click",function(a){if(k)return confirm(gettext("You have unsaved changes on individual editable fields. If you run an action, your unsaved changes will be lost."))});a('form#changelist-form input[name="_save"]').on("click",function(c){let d=!1;a("select option:selected",b.actionContainer).each(function(){a(this).val()&&(d=!0)});if(d)return k?confirm(gettext("You have selected an action, but you haven\u2019t saved your changes to individual fields yet. Please click OK to save. You\u2019ll need to re-run the action.")):
|
||||
confirm(gettext("You have selected an action, and you haven\u2019t made any changes on individual fields. You\u2019re probably looking for the Go button rather than the Save button."))})};a.fn.actions.defaults={actionContainer:"div.actions",counterContainer:"span.action-counter",allContainer:"div.actions span.all",acrossInput:"div.actions input.select-across",acrossQuestions:"div.actions span.question",acrossClears:"div.actions span.clear",allToggle:"#action-toggle",selectedClass:"selected"};a(document).ready(function(){const g=
|
||||
a("tr input.action-select");0<g.length&&g.actions()})};
|
@ -1,2 +0,0 @@
|
||||
'use strict';window.addEventListener("load",function(){var c=document.querySelectorAll("fieldset.collapse");for(const [a,b]of c.entries())if(0===b.querySelectorAll("div.errors, ul.errorlist").length){b.classList.add("collapsed");c=b.querySelector("h2");const d=document.createElement("a");d.id="fieldsetcollapser"+a;d.className="collapse-toggle";d.href="#";d.textContent=gettext("Show");c.appendChild(document.createTextNode(" ("));c.appendChild(d);c.appendChild(document.createTextNode(")"))}const e=
|
||||
function(a){if(a.target.matches(".collapse-toggle")){a.preventDefault();a.stopPropagation();const b=a.target.closest("fieldset");b.classList.contains("collapsed")?(a.target.textContent=gettext("Hide"),b.classList.remove("collapsed")):(a.target.textContent=gettext("Show"),b.classList.add("collapsed"))}};document.querySelectorAll("fieldset.module").forEach(function(a){a.addEventListener("click",e)})});
|
@ -1,11 +0,0 @@
|
||||
'use strict';{const b=django.jQuery;b.fn.formset=function(c){const a=b.extend({},b.fn.formset.defaults,c),e=b(this),l=e.parent(),m=function(a,d,h){const g=new RegExp("("+d+"-(\\d+|__prefix__))");d=d+"-"+h;b(a).prop("for")&&b(a).prop("for",b(a).prop("for").replace(g,d));a.id&&(a.id=a.id.replace(g,d));a.name&&(a.name=a.name.replace(g,d))},f=b("#id_"+a.prefix+"-TOTAL_FORMS").prop("autocomplete","off");let n=parseInt(f.val(),10);const h=b("#id_"+a.prefix+"-MAX_NUM_FORMS").prop("autocomplete","off"),q=
|
||||
b("#id_"+a.prefix+"-MIN_NUM_FORMS").prop("autocomplete","off");let k;const t=function(g){g.preventDefault();g=b("#"+a.prefix+"-empty");const d=g.clone(!0);d.removeClass(a.emptyCssClass).addClass(a.formCssClass).attr("id",a.prefix+"-"+n);r(d);d.find("*").each(function(){m(this,a.prefix,f.val())});d.insertBefore(b(g));b(f).val(parseInt(f.val(),10)+1);n+=1;""!==h.val()&&0>=h.val()-f.val()&&k.parent().hide();p(d.closest(".inline-group"));a.added&&a.added(d);b(document).trigger("formset:added",[d,a.prefix])},
|
||||
r=function(b){b.is("tr")?b.children(":last").append('<div><a class="'+a.deleteCssClass+'" href="#">'+a.deleteText+"</a></div>"):b.is("ul")||b.is("ol")?b.append('<li><a class="'+a.deleteCssClass+'" href="#">'+a.deleteText+"</a></li>"):b.children(":first").append('<span><a class="'+a.deleteCssClass+'" href="#">'+a.deleteText+"</a></span>");b.find("a."+a.deleteCssClass).on("click",u.bind(this))},u=function(g){g.preventDefault();var d=b(g.target).closest("."+a.formCssClass);g=d.closest(".inline-group");
|
||||
var f=d.prev();f.length&&f.hasClass("row-form-errors")&&f.remove();d.remove();--n;a.removed&&a.removed(d);b(document).trigger("formset:removed",[d,a.prefix]);d=b("."+a.formCssClass);b("#id_"+a.prefix+"-TOTAL_FORMS").val(d.length);(""===h.val()||0<h.val()-d.length)&&k.parent().show();p(g);let c;f=function(){m(this,a.prefix,c)};c=0;for(g=d.length;c<g;c++)m(b(d).get(c),a.prefix,c),b(d.get(c)).find("*").each(f)},p=function(a){""!==q.val()&&0<=q.val()-f.val()?a.find(".inline-deletelink").hide():a.find(".inline-deletelink").show()};
|
||||
e.each(function(c){b(this).not("."+a.emptyCssClass).addClass(a.formCssClass)});e.filter("."+a.formCssClass+":not(.has_original):not(."+a.emptyCssClass+")").each(function(){r(b(this))});p(e);k=a.addButton;(function(){if(null===k)if("TR"===e.prop("tagName")){const b=e.eq(-1).children().length;l.append('<tr class="'+a.addCssClass+'"><td colspan="'+b+'"><a href="#">'+a.addText+"</a></tr>");k=l.find("tr:last a")}else e.filter(":last").after('<div class="'+a.addCssClass+'"><a href="#">'+a.addText+"</a></div>"),
|
||||
k=e.filter(":last").next().find("a");k.on("click",t)})();c=""===h.val()||0<h.val()-f.val();e.length&&c?k.parent().show():k.parent().hide();return this};b.fn.formset.defaults={prefix:"form",addText:"add another",deleteText:"remove",addCssClass:"add-row",deleteCssClass:"delete-row",emptyCssClass:"empty-row",formCssClass:"dynamic-form",added:null,removed:null,addButton:null};b.fn.tabularFormset=function(c,a){c=b(this);const e=function(){"undefined"!==typeof SelectFilter&&(b(".selectfilter").each(function(a,
|
||||
b){a=b.name.split("-");SelectFilter.init(b.id,a[a.length-1],!1)}),b(".selectfilterstacked").each(function(a,b){a=b.name.split("-");SelectFilter.init(b.id,a[a.length-1],!0)}))},l=function(a){a.find(".prepopulated_field").each(function(){const c=b(this).find("input, select, textarea"),n=c.data("dependency_list")||[],h=[];b.each(n,function(b,c){h.push("#"+a.find(".field-"+c).find("input, select, textarea").attr("id"))});h.length&&c.prepopulate(h,c.attr("maxlength"))})};c.formset({prefix:a.prefix,addText:a.addText,
|
||||
formCssClass:"dynamic-"+a.prefix,deleteCssClass:"inline-deletelink",deleteText:a.deleteText,emptyCssClass:"empty-form",added:function(a){l(a);"undefined"!==typeof DateTimeShortcuts&&(b(".datetimeshortcuts").remove(),DateTimeShortcuts.init());e()},addButton:a.addButton});return c};b.fn.stackedFormset=function(c,a){const e=b(this),l=function(a){b(c).find(".inline_label").each(function(a){a+=1;b(this).html(b(this).html().replace(/(#\d+)/g,"#"+a))})},m=function(){"undefined"!==typeof SelectFilter&&(b(".selectfilter").each(function(a,
|
||||
b){a=b.name.split("-");SelectFilter.init(b.id,a[a.length-1],!1)}),b(".selectfilterstacked").each(function(a,b){a=b.name.split("-");SelectFilter.init(b.id,a[a.length-1],!0)}))},f=function(a){a.find(".prepopulated_field").each(function(){const c=b(this).find("input, select, textarea"),f=c.data("dependency_list")||[],e=[];b.each(f,function(b,c){e.push("#"+a.find(".form-row .field-"+c).find("input, select, textarea").attr("id"))});e.length&&c.prepopulate(e,c.attr("maxlength"))})};e.formset({prefix:a.prefix,
|
||||
addText:a.addText,formCssClass:"dynamic-"+a.prefix,deleteCssClass:"inline-deletelink",deleteText:a.deleteText,emptyCssClass:"empty-form",removed:l,added:function(a){f(a);"undefined"!==typeof DateTimeShortcuts&&(b(".datetimeshortcuts").remove(),DateTimeShortcuts.init());m();l(a)},addButton:a.addButton});return e};b(document).ready(function(){b(".js-inline-admin-formset").each(function(){var c=b(this).data();const a=c.inlineFormset;switch(c.inlineType){case "stacked":c=a.name+"-group .inline-related";
|
||||
b(c).stackedFormset(c,a.options);break;case "tabular":c=a.name+"-group .tabular.inline-related tbody:first > tr.form-row",b(c).tabularFormset(c,a.options)}})})};
|
@ -1 +0,0 @@
|
||||
'use strict';{const b=django.jQuery;b.fn.prepopulate=function(d,f,g){return this.each(function(){const a=b(this),h=function(){if(!a.data("_changed")){var e=[];b.each(d,function(a,c){c=b(c);0<c.val().length&&e.push(c.val())});a.val(URLify(e.join(" "),f,g))}};a.data("_changed",!1);a.on("change",function(){a.data("_changed",!0)});if(!a.val())b(d.join(",")).on("keyup change focus",h)})}};
|
@ -38,32 +38,6 @@ JavaScript patches
|
||||
Django's admin system leverages the jQuery framework to increase the
|
||||
capabilities of the admin interface. In conjunction, there is an emphasis on
|
||||
admin JavaScript performance and minimizing overall admin media file size.
|
||||
Serving compressed or "minified" versions of JavaScript files is considered
|
||||
best practice in this regard.
|
||||
|
||||
To that end, patches for JavaScript files should include both the original
|
||||
code for future development (e.g. ``foo.js``), and a compressed version for
|
||||
production use (e.g. ``foo.min.js``). Any links to the file in the codebase
|
||||
should point to the compressed version.
|
||||
|
||||
Compressing JavaScript
|
||||
----------------------
|
||||
|
||||
To simplify the process of providing optimized JavaScript code, Django
|
||||
includes a handy Python script which should be used to create a "minified"
|
||||
version. To run it:
|
||||
|
||||
.. console::
|
||||
|
||||
$ python django/contrib/admin/bin/compress.py
|
||||
|
||||
Behind the scenes, ``compress.py`` is a front-end for Google's
|
||||
`Closure Compiler`_ which is written in Java. The Closure Compiler library is
|
||||
not bundled with Django, but will be installed automatically by ``npm``. The
|
||||
Closure Compiler library requires `Java`_ 7 or higher.
|
||||
|
||||
Please don't forget to run ``compress.py`` and include the ``diff`` of the
|
||||
minified scripts when submitting patches for Django's JavaScript.
|
||||
|
||||
.. _javascript-tests:
|
||||
|
||||
@ -143,7 +117,6 @@ Then run the tests with:
|
||||
|
||||
$ npm test
|
||||
|
||||
.. _Closure Compiler: https://developers.google.com/closure/compiler/
|
||||
.. _EditorConfig: https://editorconfig.org/
|
||||
.. _Java: https://www.java.com
|
||||
.. _eslint: https://eslint.org/
|
||||
|
@ -673,6 +673,11 @@ Miscellaneous
|
||||
* The undocumented ``django.contrib.postgres.fields.jsonb.JsonAdapter`` class
|
||||
is removed.
|
||||
|
||||
* Minified JavaScript files are no longer included with the admin. If you
|
||||
require these files to be minified, consider using a third party app or
|
||||
external build tool. The minified vendored JavaScript files packaged with the
|
||||
admin (e.g. :ref:`jquery.min.js <contrib-admin-jquery>`) are still included.
|
||||
|
||||
.. _deprecated-features-3.1:
|
||||
|
||||
Features deprecated in 3.1
|
||||
|
@ -510,7 +510,7 @@ class TestInlineMedia(TestDataMixin, TestCase):
|
||||
'my_awesome_inline_scripts.js',
|
||||
'custom_number.js',
|
||||
'admin/js/jquery.init.js',
|
||||
'admin/js/inlines.min.js',
|
||||
'admin/js/inlines.js',
|
||||
]
|
||||
)
|
||||
self.assertContains(response, 'my_awesome_inline_scripts.js')
|
||||
|
@ -1229,26 +1229,18 @@ class AdminJavaScriptTest(TestCase):
|
||||
response = self.client.get(reverse('admin:admin_views_section_add'))
|
||||
self.assertNotContains(response, 'vendor/jquery/jquery.js')
|
||||
self.assertContains(response, 'vendor/jquery/jquery.min.js')
|
||||
self.assertNotContains(response, 'prepopulate.js')
|
||||
self.assertContains(response, 'prepopulate.min.js')
|
||||
self.assertNotContains(response, 'actions.js')
|
||||
self.assertContains(response, 'actions.min.js')
|
||||
self.assertNotContains(response, 'collapse.js')
|
||||
self.assertContains(response, 'collapse.min.js')
|
||||
self.assertNotContains(response, 'inlines.js')
|
||||
self.assertContains(response, 'inlines.min.js')
|
||||
self.assertContains(response, 'prepopulate.js')
|
||||
self.assertContains(response, 'actions.js')
|
||||
self.assertContains(response, 'collapse.js')
|
||||
self.assertContains(response, 'inlines.js')
|
||||
with override_settings(DEBUG=True):
|
||||
response = self.client.get(reverse('admin:admin_views_section_add'))
|
||||
self.assertContains(response, 'vendor/jquery/jquery.js')
|
||||
self.assertNotContains(response, 'vendor/jquery/jquery.min.js')
|
||||
self.assertContains(response, 'prepopulate.js')
|
||||
self.assertNotContains(response, 'prepopulate.min.js')
|
||||
self.assertContains(response, 'actions.js')
|
||||
self.assertNotContains(response, 'actions.min.js')
|
||||
self.assertContains(response, 'collapse.js')
|
||||
self.assertNotContains(response, 'collapse.min.js')
|
||||
self.assertContains(response, 'inlines.js')
|
||||
self.assertNotContains(response, 'inlines.min.js')
|
||||
|
||||
|
||||
@override_settings(ROOT_URLCONF='admin_views.urls')
|
||||
|
Loading…
Reference in New Issue
Block a user