From 0decef337f7f00d73b380531ac22cbc843d6e806 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Sun, 22 Feb 2015 13:03:28 +0100 Subject: [PATCH] Fixed #24405 -- Made admin related object JavaScript overridable Consolidated it in one file and move the DOM related stuff to the template so it is easily overridable. To override the popup behavior you need to add handlers for the custom events triggered in the admin_change_form_document_ready template block. --- .../admin/js/admin/RelatedObjectLookups.js | 43 +++++++++++-------- .../static/admin/js/related-widget-wrapper.js | 23 ---------- .../admin/templates/admin/change_form.html | 31 ++++++++++++- django/contrib/admin/widgets.py | 5 +-- 4 files changed, 57 insertions(+), 45 deletions(-) delete mode 100644 django/contrib/admin/static/admin/js/related-widget-wrapper.js diff --git a/django/contrib/admin/static/admin/js/admin/RelatedObjectLookups.js b/django/contrib/admin/static/admin/js/admin/RelatedObjectLookups.js index ffba7cdf24..4a577c4187 100644 --- a/django/contrib/admin/static/admin/js/admin/RelatedObjectLookups.js +++ b/django/contrib/admin/static/admin/js/admin/RelatedObjectLookups.js @@ -27,14 +27,16 @@ function windowname_to_id(text) { return text; } -function showAdminPopup(triggeringLink, name_regexp) { +function showAdminPopup(triggeringLink, name_regexp, add_popup) { var name = triggeringLink.id.replace(name_regexp, ''); name = id_to_windowname(name); var href = triggeringLink.href; - if (href.indexOf('?') == -1) { - href += '?_popup=1'; - } else { - href += '&_popup=1'; + if (add_popup) { + if (href.indexOf('?') == -1) { + href += '?_popup=1'; + } else { + href += '&_popup=1'; + } } var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes'); win.focus(); @@ -42,7 +44,7 @@ function showAdminPopup(triggeringLink, name_regexp) { } function showRelatedObjectLookupPopup(triggeringLink) { - return showAdminPopup(triggeringLink, /^lookup_/); + return showAdminPopup(triggeringLink, /^lookup_/, true); } function dismissRelatedLookupPopup(win, chosenId) { @@ -57,12 +59,22 @@ function dismissRelatedLookupPopup(win, chosenId) { } function showRelatedObjectPopup(triggeringLink) { - var name = triggeringLink.id.replace(/^(change|add|delete)_/, ''); - name = id_to_windowname(name); - var href = triggeringLink.href; - var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes'); - win.focus(); - return false; + return showAdminPopup(triggeringLink, /^(change|add|delete)_/, false); +} + +function updateRelatedObjectLinks(triggeringLink) { + var $this = django.jQuery(triggeringLink); + var siblings = $this.nextAll('.change-related, .delete-related'); + if (!siblings.length) return; + var value = $this.val(); + if (value) { + siblings.each(function() { + var elm = django.jQuery(this); + elm.attr('href', elm.attr('data-href-template').replace('__fk__', value)); + }); + } else { + siblings.removeAttr('href'); + } } function dismissAddRelatedObjectPopup(win, newId, newRepr) { @@ -72,13 +84,10 @@ function dismissAddRelatedObjectPopup(win, newId, newRepr) { newRepr = html_unescape(newRepr); var name = windowname_to_id(win.name); var elem = document.getElementById(name); - var o; if (elem) { var elemName = elem.nodeName.toUpperCase(); if (elemName == 'SELECT') { - o = new Option(newRepr, newId); - elem.options[elem.options.length] = o; - o.selected = true; + elem.options[elem.options.length] = new Option(newRepr, newId, true, true); } else if (elemName == 'INPUT') { if (elem.className.indexOf('vManyToManyRawIdAdminField') != -1 && elem.value) { elem.value += ',' + newId; @@ -90,7 +99,7 @@ function dismissAddRelatedObjectPopup(win, newId, newRepr) { django.jQuery(elem).trigger('change'); } else { var toId = name + "_to"; - o = new Option(newRepr, newId); + var o = new Option(newRepr, newId); SelectBox.add_to_cache(toId, o); SelectBox.redisplay(toId); } diff --git a/django/contrib/admin/static/admin/js/related-widget-wrapper.js b/django/contrib/admin/static/admin/js/related-widget-wrapper.js deleted file mode 100644 index dbb1f58c8c..0000000000 --- a/django/contrib/admin/static/admin/js/related-widget-wrapper.js +++ /dev/null @@ -1,23 +0,0 @@ -django.jQuery(function($){ - function updateLinks() { - var $this = $(this); - var siblings = $this.nextAll('.change-related, .delete-related'); - if (!siblings.length) return; - var value = $this.val(); - if (value) { - siblings.each(function(){ - var elm = $(this); - elm.attr('href', elm.attr('data-href-template').replace('__fk__', value)); - }); - } else siblings.removeAttr('href'); - } - var container = $(document); - container.on('change', '.related-widget-wrapper select', updateLinks); - container.find('.related-widget-wrapper select').each(updateLinks); - container.on('click', '.related-widget-wrapper-link', function(event){ - if (this.href) { - showRelatedObjectPopup(this); - } - event.preventDefault(); - }); -}); diff --git a/django/contrib/admin/templates/admin/change_form.html b/django/contrib/admin/templates/admin/change_form.html index 5e6c0c6321..0e2255f6ec 100644 --- a/django/contrib/admin/templates/admin/change_form.html +++ b/django/contrib/admin/templates/admin/change_form.html @@ -73,12 +73,39 @@ $(document).ready(function() { $('.add-another').click(function(e) { e.preventDefault(); - showAddAnotherPopup(this); + var event = $.Event('django:add-another-related'); + $(this).trigger(event); + if (!event.isDefaultPrevented()) { + showAddAnotherPopup(this); + } }); $('.related-lookup').click(function(e) { e.preventDefault(); - showRelatedObjectLookupPopup(this); + var event = $.Event('django:lookup-related'); + $(this).trigger(event); + if (!event.isDefaultPrevented()) { + showRelatedObjectLookupPopup(this); + } }); + $('body').on('click', '.related-widget-wrapper-link', function(e) { + e.preventDefault(); + if (this.href) { + var event = $.Event('django:show-related', {href: this.href}); + $(this).trigger(event); + if (!event.isDefaultPrevented()) { + showRelatedObjectPopup(this); + } + } + }); + $('body').on('change', '.related-widget-wrapper select', function(e) { + var event = $.Event('django:update-related'); + $(this).trigger(event); + if (!event.isDefaultPrevented()) { + updateRelatedObjectLinks(this); + } + }); + $('.related-widget-wrapper select').trigger('change'); + {% if adminform and add %} $('form#{{ opts.model_name }}_form :input:visible:enabled:first').focus() {% endif %} diff --git a/django/contrib/admin/widgets.py b/django/contrib/admin/widgets.py index 2e88e930bd..251cc0ef1c 100644 --- a/django/contrib/admin/widgets.py +++ b/django/contrib/admin/widgets.py @@ -10,7 +10,7 @@ from django.contrib.admin.templatetags.admin_static import static from django.core.urlresolvers import reverse from django.db.models.deletion import CASCADE from django.forms.utils import flatatt -from django.forms.widgets import Media, RadioFieldRenderer +from django.forms.widgets import RadioFieldRenderer from django.template.loader import render_to_string from django.utils import six from django.utils.encoding import force_text @@ -272,8 +272,7 @@ class RelatedFieldWidgetWrapper(forms.Widget): @property def media(self): - media = Media(js=['admin/js/related-widget-wrapper.js']) - return self.widget.media + media + return self.widget.media def get_related_url(self, info, action, *args): return reverse("admin:%s_%s_%s" % (info + (action,)),