Ticket #7028: patch7028-1.2.4.patch
File patch7028-1.2.4.patch, 15.7 KB (added by , 14 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 … … 708 708 return HttpResponseRedirect(request.path + "?_popup=1") 709 709 else: 710 710 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("'", r"\'") 716 return HttpResponse('<script type="text/javascript">opener.dismissRelatedLookupPopup(' 717 "window, %s, '%s', '%s');</script>" % (obj_id, obj_url, label)) 711 718 elif request.POST.has_key("_saveasnew"): 712 719 msg = _('The %(name)s "%(obj)s" was added successfully. You may edit it again below.') % {'name': force_unicode(opts.verbose_name), 'obj': obj} 713 720 self.message_user(request, msg) -
django/contrib/admin/util.py
5 5 from django.utils import formats 6 6 from django.utils.html import escape 7 7 from django.utils.safestring import mark_safe 8 from django.utils.text import capfirst 8 from django.utils.text import capfirst, truncate_words 9 9 from django.utils.encoding import force_unicode, smart_unicode, smart_str 10 10 from django.utils.translation import ungettext, ugettext as _ 11 11 from django.core.urlresolvers import reverse, NoReverseMatch … … 338 338 return formats.number_format(value) 339 339 else: 340 340 return smart_unicode(value) 341 342 def 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 363 def obj_label(obj): 364 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 return ' <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 160 170 161 171 class ManyToManyRawIdWidget(ForeignKeyRawIdWidget): 162 172 """ … … 176 186 def url_parameters(self): 177 187 return self.base_url_parameters() 178 188 179 def label_for_value(self, value ):189 def label_for_value(self, value, name): 180 190 return '' 181 191 182 192 def value_from_datadict(self, data, files, name): … … 226 236 media = property(_media) 227 237 228 238 def render(self, name, value, *args, **kwargs): 229 rel_to = self.rel.to230 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/' % info236 239 self.widget.choices = self.choices 237 240 output = [self.widget.render(name, value, *args, **kwargs)] 241 rel_to = self.rel.to 238 242 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) 239 244 # TODO: "id_" is hard-coded here. This should instead use the correct 240 245 # API to determine the ID dynamically. 241 246 output.append(u'<a href="%s" class="add-another" id="add_id_%s" onclick="return showAddAnotherPopup(this);"> ' % \