Code

Ticket #7028: ticket7028-4.patch

File ticket7028-4.patch, 18.8 KB (added by mrts, 4 years ago)

Removed a leftover from a merge conflict, added Marco to authors.

  • AUTHORS

    diff --git a/AUTHORS b/AUTHORS
    index 55211a6..e11415c 100644
    a b answer newbie questions, and generally made Django that much better: 
    8080    Matías Bordese 
    8181    Sean Brant 
    8282    Andrew Brehaut <http://brehaut.net/blog> 
     83    Marco Beri <marcoberi@gmail.com> 
    8384    brut.alll@gmail.com 
    8485    btoll@bestweb.net 
    8586    Jonathan Buchanan <jonathan.buchanan@gmail.com> 
  • django/contrib/admin/media/js/admin/RelatedObjectLookups.js

    diff --git a/django/contrib/admin/media/js/admin/RelatedObjectLookups.js b/django/contrib/admin/media/js/admin/RelatedObjectLookups.js
    index 1bc78f8..7ff0e83 100644
    a b function html_unescape(text) { 
    1111    return text; 
    1212} 
    1313 
     14function html_escape(text) { 
     15    text = text.replace(/&/g, '&amp;'); 
     16    text = text.replace(/</g, '&lt;'); 
     17    text = text.replace(/>/g, '&gt;'); 
     18    text = text.replace(/"/g, '&quot;'); 
     19    text = text.replace(/'/g, '&#39;'); 
     20    return text; 
     21} 
     22 
    1423// IE doesn't accept periods or dashes in the window name, but the element IDs 
    1524// we use to generate popup window names may contain them, therefore we map them 
    16 // to allowed characters in a reversible way so that we can locate the correct  
     25// to allowed characters in a reversible way so that we can locate the correct 
    1726// element when the popup window is dismissed. 
    1827function id_to_windowname(text) { 
    1928    text = text.replace(/\./g, '__dot__'); 
    function windowname_to_id(text) { 
    2736    return text; 
    2837} 
    2938 
     39function showRelatedObjectPopup(triggeringLink) { 
     40    // TODO: use proper id in name to update the label on change 
     41    // see admin/options.py#641 
     42    return openPopupWindow(triggeringLink.href, '_popup', 'showrelatedobject'); 
     43} 
     44 
    3045function showRelatedObjectLookupPopup(triggeringLink) { 
    3146    var name = triggeringLink.id.replace(/^lookup_/, ''); 
    3247    name = id_to_windowname(name); 
    33     var href; 
    34     if (triggeringLink.href.search(/\?/) >= 0) { 
    35         href = triggeringLink.href + '&pop=1'; 
    36     } else { 
    37         href = triggeringLink.href + '?pop=1'; 
    38     } 
    39     var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes'); 
    40     win.focus(); 
    41     return false; 
     48    return openPopupWindow(triggeringLink.href, 'pop', name); 
    4249} 
    4350 
    44 function dismissRelatedLookupPopup(win, chosenId) { 
     51function dismissRelatedLookupPopup(win, chosenId, chosenIdHref, chosenName) { 
    4552    var name = windowname_to_id(win.name); 
    4653    var elem = document.getElementById(name); 
     54    var nameElem = document.getElementById("view_lookup_" + name); 
     55 
    4756    if (elem.className.indexOf('vManyToManyRawIdAdminField') != -1 && elem.value) { 
    4857        elem.value += ',' + chosenId; 
    4958    } else { 
    5059        document.getElementById(name).value = chosenId; 
    5160    } 
     61 
     62    if (nameElem) { 
     63      nameElem.innerHTML = '<a href="' + chosenIdHref + '" ' + 
     64       'onclick="return showRelatedObjectPopup(this);">' + 
     65        html_escape(chosenName) + '</a>'; 
     66    } 
     67 
    5268    win.close(); 
    5369} 
    5470 
    5571function showAddAnotherPopup(triggeringLink) { 
    5672    var name = triggeringLink.id.replace(/^add_/, ''); 
    5773    name = id_to_windowname(name); 
    58     href = triggeringLink.href 
    59     if (href.indexOf('?') == -1) { 
    60         href += '?_popup=1'; 
    61     } else { 
    62         href  += '&_popup=1'; 
    63     } 
    64     var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes'); 
    65     win.focus(); 
    66     return false; 
     74    return openPopupWindow(triggeringLink.href, '_popup', name); 
    6775} 
    6876 
    6977function dismissAddAnotherPopup(win, newId, newRepr) { 
    7078    // newId and newRepr are expected to have previously been escaped by 
    7179    // django.utils.html.escape. 
    7280    newId = html_unescape(newId); 
     81    var newRepr_escaped = newRepr; 
    7382    newRepr = html_unescape(newRepr); 
    7483    var name = windowname_to_id(win.name); 
    7584    var elem = document.getElementById(name); 
    function dismissAddAnotherPopup(win, newId, newRepr) { 
    8493            } else { 
    8594                elem.value = newId; 
    8695            } 
     96 
     97            var nameElem = document.getElementById("view_lookup_" + name); 
     98            if (nameElem) { 
     99                var chosenIdHref = win.location.href.replace(/\/add\/[^\/]*$/, 
     100                    '/' + newId + '/'); 
     101                nameElem.innerHTML = '<a href="' + chosenIdHref + '" ' + 
     102                  'onclick="return showRelatedObjectPopup(this);">' + 
     103                  newRepr_escaped + '</a>'; 
     104            } 
    87105        } 
    88106    } else { 
    89107        var toId = name + "_to"; 
    function dismissAddAnotherPopup(win, newId, newRepr) { 
    94112    } 
    95113    win.close(); 
    96114} 
     115 
     116function openPopupWindow(href, popup_var, name) { 
     117    if (href.indexOf('?') == -1) { 
     118        href += '?'; 
     119    } else { 
     120        href  += '&'; 
     121    } 
     122    href += popup_var + '=1'; 
     123    var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes'); 
     124    win.focus(); 
     125    return false; 
     126} 
  • django/contrib/admin/options.py

    diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py
    index 8f68eb1..a3aaf4c 100644
    a b class ModelAdmin(BaseModelAdmin): 
    674674                return HttpResponseRedirect(request.path + "?_popup=1") 
    675675            else: 
    676676                return HttpResponseRedirect(request.path) 
     677        elif request.POST.has_key("_popup"): 
     678            # object changed via raw id link popup 
     679            return HttpResponse('<script type="text/javascript">window.close();</script>') 
     680            # TODO: this should update the object label in the href tag 
     681            # see admin/media/js/admin/RelatedObjectLookups.js#39 
     682            # return HttpResponse('<script type="text/javascript"> 
     683            #   opener.dismissRelatedObjectPopup(window, "%s", "%s");</script>' % \ 
     684                # escape() calls force_unicode. 
     685                # (escape(pk_value), escape(obj))) 
    677686        elif request.POST.has_key("_saveasnew"): 
    678687            msg = _('The %(name)s "%(obj)s" was added successfully. You may edit it again below.') % {'name': force_unicode(opts.verbose_name), 'obj': obj} 
    679688            self.message_user(request, msg) 
  • django/contrib/admin/templatetags/admin_list.py

    diff --git a/django/contrib/admin/templatetags/admin_list.py b/django/contrib/admin/templatetags/admin_list.py
    index 7cd3f0a..bf8fba2 100644
    a b from django.conf import settings 
    44from django.contrib.admin.util import lookup_field, display_for_field, label_for_field 
    55from django.contrib.admin.views.main import ALL_VAR, EMPTY_CHANGELIST_VALUE 
    66from django.contrib.admin.views.main import ORDER_VAR, ORDER_TYPE_VAR, PAGE_VAR, SEARCH_VAR 
     7from django.contrib.admin.util import obj_label, get_related_url 
    78from django.core.exceptions import ObjectDoesNotExist 
    89from django.db import models 
    910from django.forms.forms import pretty_name 
    def items_for_result(cl, result, form): 
    166167                attr = pk 
    167168            value = result.serializable_value(attr) 
    168169            result_id = repr(force_unicode(value))[1:] 
    169             yield mark_safe(u'<%s%s><a href="%s"%s>%s</a></%s>' % \ 
    170                 (table_tag, row_class, url, (cl.is_popup and ' onclick="opener.dismissRelatedLookupPopup(window, %s); return false;"' % result_id or ''), conditional_escape(result_repr), table_tag)) 
     170            result_name = obj_label(result) 
     171            result_url = get_related_url(result, result.pk) 
     172            yield mark_safe(u'<%s%s><a href="%s"%s>%s</a></%s>' % 
     173                (table_tag, row_class, url, 
     174                    (cl.is_popup 
     175                        and ' onclick="opener.dismissRelatedLookupPopup(' 
     176                            "window, %s, '%s', '%s'); return false;\"" % 
     177                            (result_id, result_url, 
     178                                result_name.replace("&#39;", r"\'")) 
     179                        or ''), 
     180                    conditional_escape(result_repr), table_tag)) 
    171181        else: 
    172182            # By default the fields come from ModelAdmin.list_editable, but if we pull 
    173183            # the fields out of the form instead of list_editable custom admins 
  • django/contrib/admin/util.py

    diff --git a/django/contrib/admin/util.py b/django/contrib/admin/util.py
    index d252a80..dbaea60 100644
    a b from django.db import models 
    33from django.utils import formats 
    44from django.utils.html import escape 
    55from django.utils.safestring import mark_safe 
    6 from django.utils.text import capfirst 
     6from django.utils.text import capfirst, truncate_words 
    77from django.utils.encoding import force_unicode, smart_unicode, smart_str 
    88from django.utils.translation import ungettext, ugettext as _ 
    99from django.core.urlresolvers import reverse, NoReverseMatch 
    def display_for_field(value, field): 
    305305        return formats.number_format(value) 
    306306    else: 
    307307        return smart_unicode(value) 
     308 
     309def get_related_url(rel_to, action=None, admin_site=None): 
     310    reverse_path = 'admin:%s_%s' 
     311    rel_path = '%s%s/%s/' 
     312    params = [rel_to._meta.app_label, rel_to._meta.object_name.lower()] 
     313 
     314    if action: 
     315        reverse_path += '_%s' 
     316        rel_path += '%s/' 
     317        params.append(action) 
     318 
     319    if admin_site: 
     320        try: 
     321            return reverse(reverse_path % tuple(params), 
     322                    current_app=admin_site.name) 
     323        except NoReverseMatch: 
     324            params.insert(0, admin_site.root_path) 
     325            return rel_path % tuple(params) 
     326 
     327    params.insert(0, '../../../') 
     328    return rel_path % tuple(params) 
     329 
     330def obj_label(obj): 
     331    return escape(truncate_words(obj, 7)) 
  • django/contrib/admin/widgets.py

    diff --git a/django/contrib/admin/widgets.py b/django/contrib/admin/widgets.py
    index 120df94..911e40c 100644
    a b import django.utils.copycompat as copy 
    77from django import forms 
    88from django.forms.widgets import RadioFieldRenderer 
    99from django.forms.util import flatatt 
    10 from django.utils.html import escape 
    11 from django.utils.text import truncate_words 
    1210from django.utils.translation import ugettext as _ 
    1311from django.utils.safestring import mark_safe 
    1412from django.utils.encoding import force_unicode 
    1513from django.conf import settings 
    16 from django.core.urlresolvers import reverse, NoReverseMatch 
     14from django.contrib.admin.util import get_related_url, obj_label 
    1715 
    1816class FilteredSelectMultiple(forms.SelectMultiple): 
    1917    """ 
    class ForeignKeyRawIdWidget(forms.TextInput): 
    110108    def render(self, name, value, attrs=None): 
    111109        if attrs is None: 
    112110            attrs = {} 
    113         related_url = '../../../%s/%s/' % (self.rel.to._meta.app_label, self.rel.to._meta.object_name.lower()) 
     111        # it would make sense to bind admin_site to ForeignKeyRawIdWidget as 
     112        # well (see RelatedFieldWidgetWrapper.__init__) for proper URL reversing 
     113        related_url = get_related_url(self.rel.to) 
    114114        params = self.url_parameters() 
    115115        if params: 
    116116            url = '?' + '&amp;'.join(['%s=%s' % (k, v) for k, v in params.items()]) 
    class ForeignKeyRawIdWidget(forms.TextInput): 
    124124        output.append('<a href="%s%s" class="related-lookup" id="lookup_id_%s" onclick="return showRelatedObjectLookupPopup(this);"> ' % \ 
    125125            (related_url, url, name)) 
    126126        output.append('<img src="%simg/admin/selector-search.gif" width="16" height="16" alt="%s" /></a>' % (settings.ADMIN_MEDIA_PREFIX, _('Lookup'))) 
    127         if value: 
    128             output.append(self.label_for_value(value)) 
     127        output.append(self.label_for_value(value, 'view_lookup_id_%s' % name)) 
    129128        return mark_safe(u''.join(output)) 
    130129 
    131130    def base_url_parameters(self): 
    class ForeignKeyRawIdWidget(forms.TextInput): 
    147146        params.update({TO_FIELD_VAR: self.rel.get_related_field().name}) 
    148147        return params 
    149148 
    150     def label_for_value(self, value): 
    151         key = self.rel.get_related_field().name 
    152         obj = self.rel.to._default_manager.using(self.db).get(**{key: value}) 
    153         return '&nbsp;<strong>%s</strong>' % escape(truncate_words(obj, 14)) 
     149    def label_for_value(self, value, name): 
     150        if value: 
     151            key = self.rel.get_related_field().name 
     152            obj = self.rel.to._default_manager.using( 
     153                    self.db).get(**{key: value}) 
     154            related_url = get_related_url(obj, obj.pk) 
     155            return ('&nbsp;<strong id="%s"><a href="%s" ' 
     156                    'onclick="return showRelatedObjectPopup(this);">%s</a>' 
     157                    '</strong>' % (name, related_url, obj_label(obj))) 
     158        else: 
     159            # a placeholder that will be filled in 
     160            # JavaScript dismissRelatedLookupPopup() 
     161            return '&nbsp;<strong id="%s"></strong>' % name 
    154162 
    155163class ManyToManyRawIdWidget(ForeignKeyRawIdWidget): 
    156164    """ 
    class ManyToManyRawIdWidget(ForeignKeyRawIdWidget): 
    171179    def url_parameters(self): 
    172180        return self.base_url_parameters() 
    173181 
    174     def label_for_value(self, value): 
     182    def label_for_value(self, value, name): 
    175183        return '' 
    176184 
    177185    def value_from_datadict(self, data, files, name): 
    class RelatedFieldWidgetWrapper(forms.Widget): 
    221229    media = property(_media) 
    222230 
    223231    def render(self, name, value, *args, **kwargs): 
    224         rel_to = self.rel.to 
    225         info = (rel_to._meta.app_label, rel_to._meta.object_name.lower()) 
    226         try: 
    227             related_url = reverse('admin:%s_%s_add' % info, current_app=self.admin_site.name) 
    228         except NoReverseMatch: 
    229             info = (self.admin_site.root_path, rel_to._meta.app_label, rel_to._meta.object_name.lower()) 
    230             related_url = '%s%s/%s/add/' % info 
    231232        self.widget.choices = self.choices 
    232233        output = [self.widget.render(name, value, *args, **kwargs)] 
     234        rel_to = self.rel.to 
    233235        if rel_to in self.admin_site._registry: # If the related object has an admin interface: 
     236            related_url = get_related_url(rel_to, 'add', self.admin_site) 
    234237            # TODO: "id_" is hard-coded here. This should instead use the correct 
    235238            # API to determine the ID dynamically. 
    236239            output.append(u'<a href="%s" class="add-another" id="add_id_%s" onclick="return showAddAnotherPopup(this);"> ' % \ 
  • tests/regressiontests/admin_widgets/models.py

    diff --git a/tests/regressiontests/admin_widgets/models.py b/tests/regressiontests/admin_widgets/models.py
    index b9fe67c..e3e47ec 100644
    a b from django.db import models 
    44from django.core.files.storage import default_storage 
    55from django.contrib.auth.models import User 
    66 
    7 class MyFileField(models.FileField):  
    8     pass  
     7class MyFileField(models.FileField): 
     8    pass 
    99 
    1010class Member(models.Model): 
    1111    name = models.CharField(max_length=100) 
    Currently: <a target="_blank" href="%(STORAGE_URL)salbums/hybrid_theory.jpg">alb 
    116116>>> rel = Album._meta.get_field('band').rel 
    117117>>> w = ForeignKeyRawIdWidget(rel) 
    118118>>> print conditional_escape(w.render('test', band.pk, attrs={})) 
    119 <input type="text" name="test" value="1" class="vForeignKeyRawIdAdminField" /><a href="../../../admin_widgets/band/?t=id" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="%(ADMIN_MEDIA_PREFIX)simg/admin/selector-search.gif" width="16" height="16" alt="Lookup" /></a>&nbsp;<strong>Linkin Park</strong> 
     119<input type="text" name="test" value="1" class="vForeignKeyRawIdAdminField" /><a href="../../../admin_widgets/band/?t=id" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="%(ADMIN_MEDIA_PREFIX)simg/admin/selector-search.gif" width="16" height="16" alt="Lookup" /></a>&nbsp;<strong id="view_lookup_id_test"><a href="../../../admin_widgets/band/1/" onclick="return showRelatedObjectPopup(this);">Linkin Park</a></strong> 
    120120 
    121121>>> m1 = Member.objects.create(pk=1, name='Chester') 
    122122>>> m2 = Member.objects.create(pk=2, name='Mike') 
    True 
    147147>>> rel = Inventory._meta.get_field('parent').rel 
    148148>>> w = ForeignKeyRawIdWidget(rel) 
    149149>>> print w.render('test', core.parent_id, attrs={}) 
    150 <input type="text" name="test" value="86" class="vForeignKeyRawIdAdminField" /><a href="../../../admin_widgets/inventory/?t=barcode" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="%(ADMIN_MEDIA_PREFIX)simg/admin/selector-search.gif" width="16" height="16" alt="Lookup" /></a>&nbsp;<strong>Apple</strong> 
     150<input type="text" name="test" value="86" class="vForeignKeyRawIdAdminField" /><a href="../../../admin_widgets/inventory/?t=barcode" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="%(ADMIN_MEDIA_PREFIX)simg/admin/selector-search.gif" width="16" height="16" alt="Lookup" /></a>&nbsp;<strong id="view_lookup_id_test"><a href="../../../admin_widgets/inventory/1/" onclick="return showRelatedObjectPopup(this);">Apple</a></strong> 
    151151 
    152152# see #9258 
    153153>>> hidden = Inventory.objects.create(barcode=93, name='Hidden', hidden=True) 
    154154>>> child_of_hidden = Inventory.objects.create(barcode=94, name='Child of hidden', parent=hidden) 
    155155>>> print w.render('test', child_of_hidden.parent_id, attrs={}) 
    156 <input type="text" name="test" value="93" class="vForeignKeyRawIdAdminField" /><a href="../../../admin_widgets/inventory/?t=barcode" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="%(ADMIN_MEDIA_PREFIX)simg/admin/selector-search.gif" width="16" height="16" alt="Lookup" /></a>&nbsp;<strong>Hidden</strong> 
     156<input type="text" name="test" value="93" class="vForeignKeyRawIdAdminField" /><a href="../../../admin_widgets/inventory/?t=barcode" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="%(ADMIN_MEDIA_PREFIX)simg/admin/selector-search.gif" width="16" height="16" alt="Lookup" /></a>&nbsp;<strong id="view_lookup_id_test"><a href="../../../admin_widgets/inventory/4/" onclick="return showRelatedObjectPopup(this);">Hidden</a></strong> 
     157 
     158>>> apostrophe = Inventory.objects.create(barcode=88, name="'Apostrophe', and <javascript> and a really really` really really really really really long one!") 
     159>>> apostrophic_child = Inventory.objects.create(barcode=89, name="Apostrophe's child", parent=apostrophe) 
     160>>> print w.render('test', apostrophic_child.parent_id, attrs={}) 
     161<input type="text" name="test" value="88" class="vForeignKeyRawIdAdminField" /><a href="../../../admin_widgets/inventory/?t=barcode" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="%(ADMIN_MEDIA_PREFIX)simg/admin/selector-search.gif" width="16" height="16" alt="Lookup" /></a>&nbsp;<strong id="view_lookup_id_test"><a href="../../../admin_widgets/inventory/6/" onclick="return showRelatedObjectPopup(this);">&#39;Apostrophe&#39;, and &lt;javascript&gt; and a really really` ...</a></strong> 
    157162""" % { 
    158163    'ADMIN_MEDIA_PREFIX': settings.ADMIN_MEDIA_PREFIX, 
    159164    'STORAGE_URL': default_storage.url(''),