Ticket #7028: patch7028-8.patch
File patch7028-8.patch, 21.4 KB (added by , 15 years ago) |
---|
-
django/contrib/admin/media/js/admin/RelatedObjectLookups.js
11 11 return text; 12 12 } 13 13 14 function html_escape(text) { 15 text = text.replace(/&/g, '&'); 16 text = text.replace(/</g, '<'); 17 text = text.replace(/>/g, '>'); 18 text = text.replace(/"/g, '"'); 19 text = text.replace(/'/g, '''); 20 return text; 21 } 22 23 14 24 // IE doesn't accept periods or dashes in the window name, but the element IDs 15 25 // we use to generate popup window names may contain them, therefore we map them 16 26 // to allowed characters in a reversible way so that we can locate the correct … … 27 37 return text; 28 38 } 29 39 40 function 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 57 var 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 68 function 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 30 74 function showRelatedObjectLookupPopup(triggeringLink) { 31 75 var name = triggeringLink.id.replace(/^lookup_/, ''); 32 76 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); 42 78 } 43 79 44 function dismissRelatedLookupPopup(win, chosenId ) {80 function dismissRelatedLookupPopup(win, chosenId, chosenIdHref, chosenName) { 45 81 var name = windowname_to_id(win.name); 46 82 var elem = document.getElementById(name); 83 var nameElem = document.getElementById("view_lookup_" + name); 84 47 85 if (elem.className.indexOf('vManyToManyRawIdAdminField') != -1 && elem.value) { 48 86 elem.value += ',' + chosenId; 49 87 } else { 50 88 document.getElementById(name).value = chosenId; 51 89 } 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 52 97 win.close(); 53 98 } 54 99 55 100 function showAddAnotherPopup(triggeringLink) { 56 101 var name = triggeringLink.id.replace(/^add_/, ''); 57 102 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); 67 104 } 68 105 69 106 function dismissAddAnotherPopup(win, newId, newRepr) { 70 107 // newId and newRepr are expected to have previously been escaped by 71 108 // django.utils.html.escape. 72 109 newId = html_unescape(newId); 110 var newRepr_escaped = newRepr; 73 111 newRepr = html_unescape(newRepr); 74 112 var name = windowname_to_id(win.name); 75 113 var elem = document.getElementById(name); … … 84 122 } else { 85 123 elem.value = newId; 86 124 } 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 } 87 134 } 88 135 } else { 89 136 var toId = name + "_to"; … … 94 141 } 95 142 win.close(); 96 143 } 144 145 function clearRawId(triggeringLink) { 146 triggeringLink.parentNode.previousSibling.previousSibling.previousSibling.previousSibling.value = ''; 147 triggeringLink.parentNode.innerHTML = ''; 148 return false; 149 } 150 151 function 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
5 5 from django.contrib.contenttypes.models import ContentType 6 6 from django.contrib.admin import widgets 7 7 from django.contrib.admin import helpers 8 from django.contrib.admin.util import unquote, flatten_fieldsets, get_deleted_objects, model_ngettext, model_format_dict 8 from django.contrib.admin.util import unquote, flatten_fieldsets, get_deleted_objects, model_ngettext, model_format_dict, obj_label, get_related_url 9 9 from django.contrib import messages 10 10 from django.views.decorators.csrf import csrf_protect 11 11 from django.core.exceptions import PermissionDenied, ValidationError … … 682 682 return HttpResponseRedirect(request.path + "?_popup=1") 683 683 else: 684 684 return HttpResponseRedirect(request.path) 685 elif request.POST.has_key("_popup"): 686 # object changed via raw id link popup 687 obj_id = repr(force_unicode(obj._get_pk_val()))[1:] 688 obj_url = get_related_url(obj, obj.pk) 689 label = obj_label(obj).replace("'", r"\'") 690 return HttpResponse('<script type="text/javascript">opener.dismissRelatedLookupPopup(' 691 "window, %s, '%s', '%s');</script>" % (obj_id, obj_url, label)) 685 692 elif request.POST.has_key("_saveasnew"): 686 693 msg = _('The %(name)s "%(obj)s" was added successfully. You may edit it again below.') % {'name': force_unicode(opts.verbose_name), 'obj': obj} 687 694 self.message_user(request, msg) -
django/contrib/admin/util.py
4 4 from django.utils import formats 5 5 from django.utils.html import escape 6 6 from django.utils.safestring import mark_safe 7 from django.utils.text import capfirst 7 from django.utils.text import capfirst, truncate_words 8 8 from django.utils.encoding import force_unicode, smart_unicode, smart_str 9 9 from django.utils.translation import ungettext, ugettext as _ 10 10 from django.core.urlresolvers import reverse, NoReverseMatch … … 333 333 return formats.number_format(value) 334 334 else: 335 335 return smart_unicode(value) 336 337 def get_related_url(rel_to, action=None, admin_site=None): 338 reverse_path = 'admin:%s_%s' 339 rel_path = '%s%s/%s/' 340 params = [rel_to._meta.app_label, rel_to._meta.object_name.lower()] 341 342 if action: 343 reverse_path += '_%s' 344 rel_path += '%s/' 345 params.append(action) 346 347 if admin_site: 348 try: 349 return reverse(reverse_path % tuple(params), 350 current_app=admin_site.name) 351 except NoReverseMatch: 352 params.insert(0, admin_site.root_path) 353 return rel_path % tuple(params) 354 355 params.insert(0, '../../../') 356 return rel_path % tuple(params) 357 358 def obj_label(obj): 359 return escape(truncate_words(obj, 7)) -
django/contrib/admin/templatetags/admin_list.py
4 4 from django.contrib.admin.util import lookup_field, display_for_field, label_for_field 5 5 from django.contrib.admin.views.main import ALL_VAR, EMPTY_CHANGELIST_VALUE 6 6 from django.contrib.admin.views.main import ORDER_VAR, ORDER_TYPE_VAR, PAGE_VAR, SEARCH_VAR 7 from django.contrib.admin.util import obj_label, get_related_url 7 8 from django.core.exceptions import ObjectDoesNotExist 8 9 from django.db import models 9 10 from django.forms.forms import pretty_name … … 177 178 attr = pk 178 179 value = result.serializable_value(attr) 179 180 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("'", r"\'")) 190 or ''), 191 conditional_escape(result_repr), table_tag)) 182 192 else: 183 193 # By default the fields come from ModelAdmin.list_editable, but if we pull 184 194 # the fields out of the form instead of list_editable custom admins -
django/contrib/admin/widgets.py
7 7 from django import forms 8 8 from django.forms.widgets import RadioFieldRenderer 9 9 from django.forms.util import flatatt 10 from django.utils.html import escape11 from django.utils.text import truncate_words12 10 from django.utils.translation import ugettext as _ 13 11 from django.utils.safestring import mark_safe 14 12 from django.utils.encoding import force_unicode 15 13 from django.conf import settings 16 from django.co re.urlresolvers import reverse, NoReverseMatch14 from django.contrib.admin.util import get_related_url, obj_label 17 15 18 16 class FilteredSelectMultiple(forms.SelectMultiple): 19 17 """ … … 113 111 def render(self, name, value, attrs=None): 114 112 if attrs is None: 115 113 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) 117 115 params = self.url_parameters() 118 116 if params: 119 117 url = '?' + '&'.join(['%s=%s' % (k, v) for k, v in params.items()]) … … 124 122 output = [super(ForeignKeyRawIdWidget, self).render(name, value, attrs)] 125 123 # TODO: "id_" is hard-coded here. This should instead use the correct 126 124 # 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);"> ' % \ 128 126 (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)) 132 129 return mark_safe(u''.join(output)) 133 130 134 131 def base_url_parameters(self): … … 150 147 params.update({TO_FIELD_VAR: self.rel.get_related_field().name}) 151 148 return params 152 149 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 except self.rel.to.DoesNotExist: 158 return '' 159 return ' <strong>%s</strong>' % escape(truncate_words(obj, 14)) 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 160 170 161 171 class ManyToManyRawIdWidget(ForeignKeyRawIdWidget): 162 172 """ … … 177 187 def url_parameters(self): 178 188 return self.base_url_parameters() 179 189 180 def label_for_value(self, value ):190 def label_for_value(self, value, name): 181 191 return '' 182 192 183 193 def value_from_datadict(self, data, files, name): … … 227 237 media = property(_media) 228 238 229 239 def render(self, name, value, *args, **kwargs): 230 rel_to = self.rel.to231 info = (rel_to._meta.app_label, rel_to._meta.object_name.lower())232 try:233 related_url = reverse('admin:%s_%s_add' % info, current_app=self.admin_site.name)234 except NoReverseMatch:235 info = (self.admin_site.root_path, rel_to._meta.app_label, rel_to._meta.object_name.lower())236 related_url = '%s%s/%s/add/' % info237 240 self.widget.choices = self.choices 238 241 output = [self.widget.render(name, value, *args, **kwargs)] 242 rel_to = self.rel.to 239 243 if rel_to in self.admin_site._registry: # If the related object has an admin interface: 244 related_url = get_related_url(rel_to, 'add', self.admin_site) 240 245 # TODO: "id_" is hard-coded here. This should instead use the correct 241 246 # API to determine the ID dynamically. 242 247 output.append(u'<a href="%s" class="add-another" id="add_id_%s" onclick="return showAddAnotherPopup(this);"> ' % \ -
tests/regressiontests/admin_widgets/models.py
122 122 >>> rel = Album._meta.get_field('band').rel 123 123 >>> w = ForeignKeyRawIdWidget(rel) 124 124 >>> print conditional_escape(w.render('test', band.pk, attrs={})) 125 <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> <strong>Linkin Park</strong>125 <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" title="Lookup" /></a> <strong id="view_lookup_id_test"><a href="../../../admin_widgets/band/1/" onclick="return showRelatedObjectPopup(this);">Linkin Park</a> <a href="#" onclick="return clearRawId(this);"><img src="%(ADMIN_MEDIA_PREFIX)simg/admin/icon_deletelink.gif" width="10" height="10" alt="Clear" title="Clear" /></a></strong> 126 126 127 127 >>> m1 = Member.objects.create(pk=1, name='Chester') 128 128 >>> m2 = Member.objects.create(pk=2, name='Mike') … … 131 131 >>> rel = Band._meta.get_field('members').rel 132 132 >>> w = ManyToManyRawIdWidget(rel) 133 133 >>> print conditional_escape(w.render('test', [m1.pk, m2.pk], attrs={})) 134 <input type="text" name="test" value="1,2" class="vManyToManyRawIdAdminField" /><a href="../../../admin_widgets/member/" 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> 134 ||| FIXME -- ManyToManyRawIdWidget has not been implemented ||| 135 135 >>> w._has_changed(None, None) 136 136 False 137 137 >>> w._has_changed([], None) … … 153 153 >>> rel = Inventory._meta.get_field('parent').rel 154 154 >>> w = ForeignKeyRawIdWidget(rel) 155 155 >>> print w.render('test', core.parent_id, attrs={}) 156 <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> <strong>Apple</strong>156 <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" title="Lookup" /></a> <strong id="view_lookup_id_test"><a href="../../../admin_widgets/inventory/1/" onclick="return showRelatedObjectPopup(this);">Apple</a> <a href="#" onclick="return clearRawId(this);"><img src="%(ADMIN_MEDIA_PREFIX)simg/admin/icon_deletelink.gif" width="10" height="10" alt="Clear" title="Clear" /></a></strong> 157 157 158 158 # see #9258 159 159 >>> hidden = Inventory.objects.create(barcode=93, name='Hidden', hidden=True) 160 160 >>> child_of_hidden = Inventory.objects.create(barcode=94, name='Child of hidden', parent=hidden) 161 161 >>> print w.render('test', child_of_hidden.parent_id, attrs={}) 162 <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> <strong>Hidden</strong> 162 <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" title="Lookup" /></a> <strong id="view_lookup_id_test"><a href="../../../admin_widgets/inventory/4/" onclick="return showRelatedObjectPopup(this);">Hidden</a> <a href="#" onclick="return clearRawId(this);"><img src="%(ADMIN_MEDIA_PREFIX)simg/admin/icon_deletelink.gif" width="10" height="10" alt="Clear" title="Clear" /></a></strong> 163 164 >>> apostrophe = Inventory.objects.create(barcode=88, name="'Apostrophe', and <javascript> and a really really` really really really really really long one!") 165 >>> apostrophic_child = Inventory.objects.create(barcode=89, name="Apostrophe's child", parent=apostrophe) 166 >>> print w.render('test', apostrophic_child.parent_id, attrs={}) 167 <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" title="Lookup" /></a> <strong id="view_lookup_id_test"><a href="../../../admin_widgets/inventory/6/" onclick="return showRelatedObjectPopup(this);">'Apostrophe', and <javascript> and a really really` ...</a> <a href="#" onclick="return clearRawId(this);"><img src="%(ADMIN_MEDIA_PREFIX)simg/admin/icon_deletelink.gif" width="10" height="10" alt="Clear" title="Clear" /></a></strong> 163 168 """ % { 164 169 'ADMIN_MEDIA_PREFIX': settings.ADMIN_MEDIA_PREFIX, 165 170 'STORAGE_URL': default_storage.url(''),