From dc3d2ac98c1bcfad74d3e9523caf07e7e9fb15aa Mon Sep 17 00:00:00 2001
From: Christopher Medrela <chris.medrela@gmail.com>
Date: Sat, 23 Feb 2013 18:57:48 +0100
Subject: [PATCH] Fixed #11277 -- Hid labels of fields with HiddenInput widget
 in admin forms.

Thanks bartTC for the report.
---
 django/contrib/admin/helpers.py               |  3 ++
 .../contrib/admin/static/admin/css/forms.css  |  4 ++
 .../templates/admin/includes/fieldset.html    |  4 +-
 tests/admin_views/admin.py                    | 36 +++++++++++++++-
 tests/admin_views/models.py                   |  9 ++++
 tests/admin_views/tests.py                    | 42 +++++++++++++++++++
 6 files changed, 95 insertions(+), 3 deletions(-)

diff --git a/django/contrib/admin/helpers.py b/django/contrib/admin/helpers.py
index cd784f5cbd..622664fc6b 100644
--- a/django/contrib/admin/helpers.py
+++ b/django/contrib/admin/helpers.py
@@ -84,6 +84,9 @@ class Fieldline(object):
             self.fields = [field]
         else:
             self.fields = field
+        self.has_visible_field = not all(field in self.form.fields and
+                                         self.form.fields[field].widget.is_hidden
+                                         for field in self.fields)
         self.model_admin = model_admin
         if readonly_fields is None:
             readonly_fields = ()
diff --git a/django/contrib/admin/static/admin/css/forms.css b/django/contrib/admin/static/admin/css/forms.css
index 4885f62566..a187f895fd 100644
--- a/django/contrib/admin/static/admin/css/forms.css
+++ b/django/contrib/admin/static/admin/css/forms.css
@@ -18,6 +18,10 @@ form .form-row p {
     font-size: 11px;
 }
 
+.hidden {
+    display: none;
+}
+
 /* FORM LABELS */
 
 form h4 {
diff --git a/django/contrib/admin/templates/admin/includes/fieldset.html b/django/contrib/admin/templates/admin/includes/fieldset.html
index 24b069cb2f..f38564c5af 100644
--- a/django/contrib/admin/templates/admin/includes/fieldset.html
+++ b/django/contrib/admin/templates/admin/includes/fieldset.html
@@ -4,10 +4,10 @@
         <div class="description">{{ fieldset.description|safe }}</div>
     {% endif %}
     {% for line in fieldset %}
-        <div class="form-row{% if line.fields|length_is:'1' and line.errors %} errors{% endif %}{% for field in line %}{% if field.field.name %} field-{{ field.field.name }}{% endif %}{% endfor %}">
+        <div class="form-row{% if line.fields|length_is:'1' and line.errors %} errors{% endif %}{% if not line.has_visible_field %} hidden{% endif %}{% for field in line %}{% if field.field.name %} field-{{ field.field.name }}{% endif %}{% endfor %}">
             {% if line.fields|length_is:'1' %}{{ line.errors }}{% endif %}
             {% for field in line %}
-                <div{% if not line.fields|length_is:'1' %} class="field-box{% if field.field.name %} field-{{ field.field.name }}{% endif %}{% if not field.is_readonly and field.errors %} errors{% endif %}"{% elif field.is_checkbox %} class="checkbox-row"{% endif %}>
+                <div{% if not line.fields|length_is:'1' %} class="field-box{% if field.field.name %} field-{{ field.field.name }}{% endif %}{% if not field.is_readonly and field.errors %} errors{% endif %}{% if field.field.is_hidden %} hidden{% endif %}"{% elif field.is_checkbox %} class="checkbox-row"{% endif %}>
                     {% if not line.fields|length_is:'1' and not field.is_readonly %}{{ field.errors }}{% endif %}
                     {% if field.is_checkbox %}
                         {{ field.field }}{{ field.label_tag }}
diff --git a/tests/admin_views/admin.py b/tests/admin_views/admin.py
index 8d864378ce..5ecc085a78 100644
--- a/tests/admin_views/admin.py
+++ b/tests/admin_views/admin.py
@@ -30,7 +30,8 @@ from .models import (Article, Chapter, Account, Media, Child, Parent, Picture,
     AdminOrderedField, AdminOrderedModelMethod, AdminOrderedAdminMethod,
     AdminOrderedCallable, Report, Color2, UnorderedObject, MainPrepopulated,
     RelatedPrepopulated, UndeletableObject, UnchangeableObject, UserMessenger, Simple, Choice,
-    ShortMessage, Telegram, FilteredManager)
+    ShortMessage, Telegram, FilteredManager, EmptyModelHidden,
+    EmptyModelVisible, EmptyModelMixin)
 
 
 def callable_year(dt_value):
@@ -709,6 +710,36 @@ class ChoiceList(admin.ModelAdmin):
     fields = ['choice']
 
 
+# Tests for ticket 11277 ----------------------------------
+
+class FormWithoutHiddenField(forms.ModelForm):
+    first = forms.CharField()
+    second = forms.CharField()
+
+class FormWithoutVisibleField(forms.ModelForm):
+    first = forms.CharField(widget=forms.HiddenInput)
+    second = forms.CharField(widget=forms.HiddenInput)
+
+class FormWithVisibleAndHiddenField(forms.ModelForm):
+    first = forms.CharField(widget=forms.HiddenInput)
+    second = forms.CharField()
+
+class EmptyModelVisibleAdmin(admin.ModelAdmin):
+    form = FormWithoutHiddenField
+    fieldsets = (
+        (None, {
+            'fields':(('first', 'second'),),
+        }),
+    )
+
+class EmptyModelHiddenAdmin(admin.ModelAdmin):
+    form = FormWithoutVisibleField
+    fieldsets = EmptyModelVisibleAdmin.fieldsets
+
+class EmptyModelMixinAdmin(admin.ModelAdmin):
+    form = FormWithVisibleAndHiddenField
+    fieldsets = EmptyModelVisibleAdmin.fieldsets
+
 site = admin.AdminSite(name="admin")
 site.register(Article, ArticleAdmin)
 site.register(CustomArticle, CustomArticleAdmin)
@@ -790,6 +821,9 @@ site.register(Color2, CustomTemplateFilterColorAdmin)
 site.register(Simple, AttributeErrorRaisingAdmin)
 site.register(UserMessenger, MessageTestingAdmin)
 site.register(Choice, ChoiceList)
+site.register(EmptyModelHidden, EmptyModelHiddenAdmin)
+site.register(EmptyModelVisible, EmptyModelVisibleAdmin)
+site.register(EmptyModelMixin, EmptyModelMixinAdmin)
 
 # Register core models we need in our tests
 from django.contrib.auth.models import User, Group
diff --git a/tests/admin_views/models.py b/tests/admin_views/models.py
index c09411dc27..adccae6c26 100644
--- a/tests/admin_views/models.py
+++ b/tests/admin_views/models.py
@@ -704,3 +704,12 @@ class FilteredManager(models.Model):
 
     pk_gt_1 = _Manager()
     objects = models.Manager()
+
+class EmptyModelVisible(models.Model):
+    """ See ticket #11277. """
+
+class EmptyModelHidden(models.Model):
+    """ See ticket #11277. """
+
+class EmptyModelMixin(models.Model):
+    """ See ticket #11277. """
diff --git a/tests/admin_views/tests.py b/tests/admin_views/tests.py
index 9d1b0fad69..25a4b02495 100644
--- a/tests/admin_views/tests.py
+++ b/tests/admin_views/tests.py
@@ -4571,5 +4571,47 @@ class AdminKeepChangeListFiltersTests(TestCase):
         response = self.client.post(self.get_delete_url(), {'post': 'yes'})
         self.assertRedirects(response, self.get_changelist_url())
 
+
 class NamespacedAdminKeepChangeListFiltersTests(AdminKeepChangeListFiltersTests):
     admin_site = site2
+
+
+@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',))
+class TestLabelVisibility(TestCase):
+    """ #11277 -Labels of hidden fields in admin were not hidden. """
+
+    urls = "admin_views.urls"
+    fixtures = ['admin-views-users.xml']
+
+    def setUp(self):
+        self.client.login(username='super', password='secret')
+
+    def test_all_fields_visible(self):
+        response = self.client.get('/test_admin/admin/admin_views/emptymodelvisible/add/')
+        self.assert_fieldline_visible(response)
+        self.assert_field_visible(response, 'first')
+        self.assert_field_visible(response, 'second')
+
+    def test_all_fields_hidden(self):
+        response = self.client.get('/test_admin/admin/admin_views/emptymodelhidden/add/')
+        self.assert_fieldline_hidden(response)
+        self.assert_field_hidden(response, 'first')
+        self.assert_field_hidden(response, 'second')
+
+    def test_mixin(self):
+        response = self.client.get('/test_admin/admin/admin_views/emptymodelmixin/add/')
+        self.assert_fieldline_visible(response)
+        self.assert_field_hidden(response, 'first')
+        self.assert_field_visible(response, 'second')
+
+    def assert_field_visible(self, response, field_name):
+        self.assertContains(response, '<div class="field-box field-%s">' % field_name)
+
+    def assert_field_hidden(self, response, field_name):
+        self.assertContains(response, '<div class="field-box field-%s hidden">' % field_name)
+
+    def assert_fieldline_visible(self, response):
+        self.assertContains(response, '<div class="form-row field-first field-second">')
+
+    def assert_fieldline_hidden(self, response):
+        self.assertContains(response, '<div class="form-row hidden')