From 30610719d58502233d1e916eafbbbe69409a539f Mon Sep 17 00:00:00 2001 From: Malcolm Tredinnick Date: Fri, 10 Sep 2010 16:56:36 +0000 Subject: [PATCH] Adding related objects in the admin (via popup) respects user permissions. Patch from SmileyChris. Fixed #1035. git-svn-id: http://code.djangoproject.com/svn/django/trunk@13708 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/contrib/admin/options.py | 8 +++++++- django/contrib/admin/widgets.py | 9 +++++++-- tests/regressiontests/admin_views/tests.py | 22 ++++++++++++++++++++++ 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py index 3aeb9d88a0..3b6e2b794e 100644 --- a/django/contrib/admin/options.py +++ b/django/contrib/admin/options.py @@ -107,7 +107,13 @@ class BaseModelAdmin(object): # rendered output. formfield can be None if it came from a # OneToOneField with parent_link=True or a M2M intermediary. if formfield and db_field.name not in self.raw_id_fields: - formfield.widget = widgets.RelatedFieldWidgetWrapper(formfield.widget, db_field.rel, self.admin_site) + related_modeladmin = self.admin_site._registry.get( + db_field.rel.to) + can_add_related = bool(related_modeladmin and + related_modeladmin.has_add_permission(request)) + formfield.widget = widgets.RelatedFieldWidgetWrapper( + formfield.widget, db_field.rel, self.admin_site, + can_add_related=can_add_related) return formfield diff --git a/django/contrib/admin/widgets.py b/django/contrib/admin/widgets.py index 1d321d0620..134effc74e 100644 --- a/django/contrib/admin/widgets.py +++ b/django/contrib/admin/widgets.py @@ -205,13 +205,18 @@ class RelatedFieldWidgetWrapper(forms.Widget): This class is a wrapper to a given widget to add the add icon for the admin interface. """ - def __init__(self, widget, rel, admin_site): + def __init__(self, widget, rel, admin_site, can_add_related=None): self.is_hidden = widget.is_hidden self.needs_multipart_form = widget.needs_multipart_form self.attrs = widget.attrs self.choices = widget.choices self.widget = widget self.rel = rel + # Backwards compatible check for whether a user can add related + # objects. + if can_add_related is None: + can_add_related = rel_to in self.admin_site._registry + self.can_add_related = can_add_related # so we can check if the related object is registered with this AdminSite self.admin_site = admin_site @@ -236,7 +241,7 @@ class RelatedFieldWidgetWrapper(forms.Widget): related_url = '%s%s/%s/add/' % info self.widget.choices = self.choices output = [self.widget.render(name, value, *args, **kwargs)] - if rel_to in self.admin_site._registry: # If the related object has an admin interface: + if self.can_add_related: # TODO: "id_" is hard-coded here. This should instead use the correct # API to determine the ID dynamically. output.append(u' ' % \ diff --git a/tests/regressiontests/admin_views/tests.py b/tests/regressiontests/admin_views/tests.py index f76765236e..80319f0329 100644 --- a/tests/regressiontests/admin_views/tests.py +++ b/tests/regressiontests/admin_views/tests.py @@ -604,6 +604,28 @@ class AdminViewPermissionsTest(TestCase): 'Plural error message not found in response to post with multiple errors.') self.client.get('/test_admin/admin/logout/') + def testConditionallyShowAddSectionLink(self): + """ + The foreign key widget should only show the "add related" button if the + user has permission to add that related item. + """ + # Set up and log in user. + url = '/test_admin/admin/admin_views/article/add/' + add_link_text = ' class="add-another"' + self.client.get('/test_admin/admin/') + self.client.post('/test_admin/admin/', self.adduser_login) + # The add user can't add sections yet, so they shouldn't see the "add + # section" link. + response = self.client.get(url) + self.assertNotContains(response, add_link_text) + # Allow the add user to add sections too. Now they can see the "add + # section" link. + add_user = User.objects.get(username='adduser') + perm = get_perm(Section, Section._meta.get_add_permission()) + add_user.user_permissions.add(perm) + response = self.client.get(url) + self.assertContains(response, add_link_text) + def testCustomModelAdminTemplates(self): self.client.get('/test_admin/admin/') self.client.post('/test_admin/admin/', self.super_login)