Code

Ticket #7028: patch7028-1.2.4.patch

File patch7028-1.2.4.patch, 15.7 KB (added by marcob, 3 years ago)

Patch against 1.2.4 (no patch for tests, save file admin_list.py with unix fileformat)

  • django/contrib/admin/media/js/admin/RelatedObjectLookups.js

     
    1111    return text; 
    1212} 
    1313 
     14function html_escape(text) { 
     15    text = text.replace(/&/g, '&'); 
     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 
     23 
    1424// IE doesn't accept periods or dashes in the window name, but the element IDs 
    1525// we use to generate popup window names may contain them, therefore we map them 
    1626// to allowed characters in a reversible way so that we can locate the correct  
     
    2737    return text; 
    2838} 
    2939 
     40function getAdminMediaPrefix() { 
     41    // Deduce admin_media_prefix by looking at the <script>s in the 
     42    // current document and finding the URL of *this* module. 
     43    // Copy-paste from DateTimeShortcuts.js, makes sense as a 
     44    // separate function in core.js. 
     45    var scripts = document.getElementsByTagName('script'); 
     46 
     47    for (var i=0; i < scripts.length; i++) { 
     48        if (scripts[i].src.match(/RelatedObjectLookups/)) { 
     49            var idx = scripts[i].src.indexOf('js/admin/RelatedObjectLookups'); 
     50            return scripts[i].src.substring(0, idx); 
     51        } 
     52    } 
     53    // poor man's assert 
     54    alert('This line is unreachable. Please file a bug if you see this message.'); 
     55} 
     56 
     57var CLEAR_RAW_ID = '<a href="#" onclick="return clearRawId(this);">' + 
     58'<img src="' + getAdminMediaPrefix() + 'img/admin/icon_deletelink.gif" ' + 
     59'width="10" height="10" alt="Clear" title="Clear" /></a>'; 
     60 
     61// FIXME: the following produce 'gettext is not defined' errors in FireBug. 
     62// Needs to be tracked down. 
     63// (jsi18n is generally included before this in admin templates) 
     64// 
     65// 'width="10" height="10" alt="' + gettext('Clear') + '" title="' + 
     66// gettext('Clear') + '" /></a>'; 
     67 
     68function showRelatedObjectPopup(triggeringLink) { 
     69    var name = triggeringLink.parentNode.id.replace(/^view_lookup_/, ''); 
     70    name = id_to_windowname(name); 
     71    return openPopupWindow(triggeringLink.href, '_popup', name); 
     72} 
     73 
    3074function showRelatedObjectLookupPopup(triggeringLink) { 
    3175    var name = triggeringLink.id.replace(/^lookup_/, ''); 
    3276    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; 
     77    return openPopupWindow(triggeringLink.href, 'pop', name); 
    4278} 
    4379 
    44 function dismissRelatedLookupPopup(win, chosenId) { 
     80function dismissRelatedLookupPopup(win, chosenId, chosenIdHref, chosenName) { 
    4581    var name = windowname_to_id(win.name); 
    4682    var elem = document.getElementById(name); 
     83    var nameElem = document.getElementById("view_lookup_" + name); 
     84 
    4785    if (elem.className.indexOf('vManyToManyRawIdAdminField') != -1 && elem.value) { 
    4886        elem.value += ',' + chosenId; 
    4987    } else { 
    5088        document.getElementById(name).value = chosenId; 
    5189    } 
     90 
     91    if (nameElem) { 
     92      nameElem.innerHTML = '<a href="' + chosenIdHref + '" ' + 
     93       'onclick="return showRelatedObjectPopup(this);">' + 
     94        html_escape(chosenName) + '</a> ' + CLEAR_RAW_ID; 
     95    } 
     96 
    5297    win.close(); 
    5398} 
    5499 
    55100function showAddAnotherPopup(triggeringLink) { 
    56101    var name = triggeringLink.id.replace(/^add_/, ''); 
    57102    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; 
     103    return openPopupWindow(triggeringLink.href, '_popup', name); 
    67104} 
    68105 
    69106function dismissAddAnotherPopup(win, newId, newRepr) { 
    70107    // newId and newRepr are expected to have previously been escaped by 
    71108    // django.utils.html.escape. 
    72109    newId = html_unescape(newId); 
     110    var newRepr_escaped = newRepr; 
    73111    newRepr = html_unescape(newRepr); 
    74112    var name = windowname_to_id(win.name); 
    75113    var elem = document.getElementById(name); 
     
    84122            } else { 
    85123                elem.value = newId; 
    86124            } 
     125 
     126            var nameElem = document.getElementById("view_lookup_" + name); 
     127            if (nameElem) { 
     128                var chosenIdHref = win.location.href.replace(/\/add\/[^\/]*$/, 
     129                    '/' + newId + '/'); 
     130                nameElem.innerHTML = '<a href="' + chosenIdHref + '" ' + 
     131                  'onclick="return showRelatedObjectPopup(this);">' + 
     132                  newRepr_escaped + '</a> ' + CLEAR_RAW_ID; 
     133            } 
    87134        } 
    88135    } else { 
    89136        var toId = name + "_to"; 
     
    94141    } 
    95142    win.close(); 
    96143} 
     144 
     145function clearRawId(triggeringLink) { 
     146    triggeringLink.parentNode.previousSibling.previousSibling.previousSibling.previousSibling.value = ''; 
     147    triggeringLink.parentNode.innerHTML = ''; 
     148    return false; 
     149} 
     150 
     151function openPopupWindow(href, popup_var, name) { 
     152    if (href.indexOf('?') == -1) { 
     153        href += '?'; 
     154    } else { 
     155        href  += '&'; 
     156    } 
     157    href += popup_var + '=1'; 
     158    var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes'); 
     159    win.focus(); 
     160    return false; 
     161} 
  • django/contrib/admin/options.py

     
    55from django.contrib.contenttypes.models import ContentType 
    66from django.contrib.admin import widgets 
    77from django.contrib.admin import helpers 
    8 from django.contrib.admin.util import unquote, flatten_fieldsets, get_deleted_objects, model_ngettext, model_format_dict 
     8from django.contrib.admin.util import unquote, flatten_fieldsets, get_deleted_objects, model_ngettext, model_format_dict, obj_label, get_related_url 
    99from django.contrib import messages 
    1010from django.views.decorators.csrf import csrf_protect 
    1111from django.core.exceptions import PermissionDenied, ValidationError 
     
    708708                return HttpResponseRedirect(request.path + "?_popup=1") 
    709709            else: 
    710710                return HttpResponseRedirect(request.path) 
     711        elif request.POST.has_key("_popup"): 
     712            # object changed via raw id link popup 
     713            obj_id = repr(force_unicode(obj._get_pk_val()))[1:] 
     714            obj_url = get_related_url(obj, obj.pk) 
     715            label = obj_label(obj).replace("&#39;", r"\'") 
     716            return HttpResponse('<script type="text/javascript">opener.dismissRelatedLookupPopup(' 
     717            "window, %s, '%s', '%s');</script>" % (obj_id, obj_url, label)) 
    711718        elif request.POST.has_key("_saveasnew"): 
    712719            msg = _('The %(name)s "%(obj)s" was added successfully. You may edit it again below.') % {'name': force_unicode(opts.verbose_name), 'obj': obj} 
    713720            self.message_user(request, msg) 
  • django/contrib/admin/util.py

     
    55from django.utils import formats 
    66from django.utils.html import escape 
    77from django.utils.safestring import mark_safe 
    8 from django.utils.text import capfirst 
     8from django.utils.text import capfirst, truncate_words 
    99from django.utils.encoding import force_unicode, smart_unicode, smart_str 
    1010from django.utils.translation import ungettext, ugettext as _ 
    1111from django.core.urlresolvers import reverse, NoReverseMatch 
     
    338338        return formats.number_format(value) 
    339339    else: 
    340340        return smart_unicode(value) 
     341 
     342def get_related_url(rel_to, action=None, admin_site=None): 
     343    reverse_path = 'admin:%s_%s' 
     344    rel_path = '%s%s/%s/' 
     345    params = [rel_to._meta.app_label, rel_to._meta.object_name.lower()] 
     346 
     347    if action: 
     348        reverse_path += '_%s' 
     349        rel_path += '%s/' 
     350        params.append(action) 
     351 
     352    if admin_site: 
     353        try: 
     354            return reverse(reverse_path % tuple(params), 
     355                    current_app=admin_site.name) 
     356        except NoReverseMatch: 
     357            params.insert(0, admin_site.root_path) 
     358            return rel_path % tuple(params) 
     359 
     360    params.insert(0, '../../../') 
     361    return rel_path % tuple(params) 
     362 
     363def obj_label(obj): 
     364    return escape(truncate_words(obj, 7)) 
  • django/contrib/admin/templatetags/admin_list.py

     
    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 
     
    177178                attr = pk 
    178179            value = result.serializable_value(attr) 
    179180            result_id = repr(force_unicode(value))[1:] 
    180             yield mark_safe(u'<%s%s><a href="%s"%s>%s</a></%s>' % \ 
    181                 (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)) 
     181            result_name = obj_label(result) 
     182            result_url = get_related_url(result, result.pk) 
     183            yield mark_safe(u'<%s%s><a href="%s"%s>%s</a></%s>' % 
     184                (table_tag, row_class, url, 
     185                    (cl.is_popup 
     186                        and ' onclick="opener.dismissRelatedLookupPopup(' 
     187                            "window, %s, '%s', '%s'); return false;\"" % 
     188                            (result_id, result_url, 
     189                                result_name.replace("&#39;", r"\'")) 
     190                        or ''), 
     191                    conditional_escape(result_repr), table_tag)) 
    182192        else: 
    183193            # By default the fields come from ModelAdmin.list_editable, but if we pull 
    184194            # the fields out of the form instead of list_editable custom admins 
  • django/contrib/admin/widgets.py

     
    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    """ 
     
    113111    def render(self, name, value, attrs=None): 
    114112        if attrs is None: 
    115113            attrs = {} 
    116         related_url = '../../../%s/%s/' % (self.rel.to._meta.app_label, self.rel.to._meta.object_name.lower()) 
     114        related_url = get_related_url(self.rel.to) 
    117115        params = self.url_parameters() 
    118116        if params: 
    119117            url = '?' + '&amp;'.join(['%s=%s' % (k, v) for k, v in params.items()]) 
     
    124122        output = [super(ForeignKeyRawIdWidget, self).render(name, value, attrs)] 
    125123        # TODO: "id_" is hard-coded here. This should instead use the correct 
    126124        # API to determine the ID dynamically. 
    127         output.append('<a href="%s%s" class="related-lookup" id="lookup_id_%s" onclick="return showRelatedObjectLookupPopup(this);"> ' % \ 
     125        output.append(' <a href="%s%s" class="related-lookup" id="lookup_id_%s" onclick="return showRelatedObjectLookupPopup(this);"> ' % \ 
    128126            (related_url, url, name)) 
    129         output.append('<img src="%simg/admin/selector-search.gif" width="16" height="16" alt="%s" /></a>' % (settings.ADMIN_MEDIA_PREFIX, _('Lookup'))) 
    130         if value: 
    131             output.append(self.label_for_value(value)) 
     127        output.append('<img src="%(prefix)simg/admin/selector-search.gif" width="16" height="16" alt="%(title)s" title="%(title)s" /></a>' % {'prefix': settings.ADMIN_MEDIA_PREFIX, 'title': _('Lookup')}) 
     128        output.append(self.label_for_value(value, 'view_lookup_id_%s' % name)) 
    132129        return mark_safe(u''.join(output)) 
    133130 
    134131    def base_url_parameters(self): 
     
    150147        params.update({TO_FIELD_VAR: self.rel.get_related_field().name}) 
    151148        return params 
    152149 
    153     def label_for_value(self, value): 
    154         key = self.rel.get_related_field().name 
    155         try: 
    156             obj = self.rel.to._default_manager.using(self.db).get(**{key: value}) 
    157             return '&nbsp;<strong>%s</strong>' % escape(truncate_words(obj, 14)) 
    158         except (ValueError, self.rel.to.DoesNotExist): 
    159             return '' 
     150    def label_for_value(self, value, name): 
     151        if value: 
     152            key = self.rel.get_related_field().name 
     153            obj = self.rel.to._default_manager.using( 
     154                    self.db).get(**{key: value}) 
     155            related_url = get_related_url(obj, obj.pk) 
     156            return (' <strong id="%(name)s"><a href="%(url)s" ' 
     157                    'onclick="return showRelatedObjectPopup(this);">%(label)s</a> ' 
     158                    '<a href="#" onclick="return clearRawId(this);">' 
     159                    '<img src="%(prefix)simg/admin/icon_deletelink.gif" ' 
     160                    'width="10" height="10" alt="%(clear)s" title="%(clear)s" />' 
     161                    '</a></strong>' % {'name': name, 'url': related_url, 
     162                        'label': obj_label(obj), 
     163                        'prefix': settings.ADMIN_MEDIA_PREFIX, 
     164                        'clear': _("Clear"),} 
     165                    ) 
     166        else: 
     167            # a placeholder that will be filled in 
     168            # JavaScript dismissRelatedLookupPopup() 
     169            return ' <strong id="%s"></strong>' % name 
    160170 
    161171class ManyToManyRawIdWidget(ForeignKeyRawIdWidget): 
    162172    """ 
     
    176186    def url_parameters(self): 
    177187        return self.base_url_parameters() 
    178188 
    179     def label_for_value(self, value): 
     189    def label_for_value(self, value, name): 
    180190        return '' 
    181191 
    182192    def value_from_datadict(self, data, files, name): 
     
    226236    media = property(_media) 
    227237 
    228238    def render(self, name, value, *args, **kwargs): 
    229         rel_to = self.rel.to 
    230         info = (rel_to._meta.app_label, rel_to._meta.object_name.lower()) 
    231         try: 
    232             related_url = reverse('admin:%s_%s_add' % info, current_app=self.admin_site.name) 
    233         except NoReverseMatch: 
    234             info = (self.admin_site.root_path, rel_to._meta.app_label, rel_to._meta.object_name.lower()) 
    235             related_url = '%s%s/%s/add/' % info 
    236239        self.widget.choices = self.choices 
    237240        output = [self.widget.render(name, value, *args, **kwargs)] 
     241        rel_to = self.rel.to 
    238242        if rel_to in self.admin_site._registry: # If the related object has an admin interface: 
     243            related_url = get_related_url(rel_to, 'add', self.admin_site) 
    239244            # TODO: "id_" is hard-coded here. This should instead use the correct 
    240245            # API to determine the ID dynamically. 
    241246            output.append(u'<a href="%s" class="add-another" id="add_id_%s" onclick="return showAddAnotherPopup(this);"> ' % \