Преглед изворни кода

Fixed #31524 -- Removed minified static assets from the admin.

Jon Dufresne пре 4 година
родитељ
комит
81ffedaacc

+ 0 - 1
MANIFEST.in

@@ -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

+ 0 - 52
django/contrib/admin/bin/compress.py

@@ -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 - 3
django/contrib/admin/helpers.py

@@ -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):

+ 4 - 5
django/contrib/admin/options.py

@@ -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):

+ 0 - 7
django/contrib/admin/static/admin/js/actions.min.js

@@ -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()})};

+ 0 - 2
django/contrib/admin/static/admin/js/collapse.min.js

@@ -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)})});

+ 0 - 11
django/contrib/admin/static/admin/js/inlines.min.js

@@ -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)}})})};

+ 0 - 1
django/contrib/admin/static/admin/js/prepopulate.min.js

@@ -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)})}};

+ 0 - 27
docs/internals/contributing/writing-code/javascript.txt

@@ -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/

+ 5 - 0
docs/releases/3.1.txt

@@ -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

+ 1 - 1
tests/admin_inlines/tests.py

@@ -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')

+ 4 - 12
tests/admin_views/tests.py

@@ -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')