Index: tests/regressiontests/admin_views/__init__.py
===================================================================
--- tests/regressiontests/admin_views/__init__.py	
+++ tests/regressiontests/admin_views/__init__.py	
Index: tests/regressiontests/admin_views/fixtures/admin-views-users.xml
===================================================================
--- tests/regressiontests/admin_views/fixtures/admin-views-users.xml	
+++ tests/regressiontests/admin_views/fixtures/admin-views-users.xml	
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="utf-8"?>
+<django-objects version="1.0">
+    <object pk="100" model="auth.user">
+        <field type="CharField" name="username">super</field>
+        <field type="CharField" name="first_name">Super</field>
+        <field type="CharField" name="last_name">User</field>
+        <field type="CharField" name="email">super@example.com</field>
+        <field type="CharField" name="password">sha1$995a3$6011485ea3834267d719b4c801409b8b1ddd0158</field>
+        <field type="BooleanField" name="is_staff">True</field>
+        <field type="BooleanField" name="is_active">True</field>
+        <field type="BooleanField" name="is_superuser">True</field>
+        <field type="DateTimeField" name="last_login">2007-05-30 13:20:10</field>
+        <field type="DateTimeField" name="date_joined">2007-05-30 13:20:10</field>
+        <field to="auth.group" name="groups" rel="ManyToManyRel"></field>
+        <field to="auth.permission" name="user_permissions" rel="ManyToManyRel"></field>
+    </object>
+    <object pk="101" model="auth.user">
+        <field type="CharField" name="username">adduser</field>
+        <field type="CharField" name="first_name">Add</field>
+        <field type="CharField" name="last_name">User</field>
+        <field type="CharField" name="email">auser@example.com</field>
+        <field type="CharField" name="password">sha1$995a3$6011485ea3834267d719b4c801409b8b1ddd0158</field>
+        <field type="BooleanField" name="is_staff">True</field>
+        <field type="BooleanField" name="is_active">True</field>
+        <field type="BooleanField" name="is_superuser">False</field>
+        <field type="DateTimeField" name="last_login">2007-05-30 13:20:10</field>
+        <field type="DateTimeField" name="date_joined">2007-05-30 13:20:10</field>
+        <field to="auth.group" name="groups" rel="ManyToManyRel"></field>
+        <field to="auth.permission" name="user_permissions" rel="ManyToManyRel"></field>
+    </object>
+    <object pk="102" model="auth.user">
+        <field type="CharField" name="username">changeuser</field>
+        <field type="CharField" name="first_name">Change</field>
+        <field type="CharField" name="last_name">User</field>
+        <field type="CharField" name="email">cuser@example.com</field>
+        <field type="CharField" name="password">sha1$995a3$6011485ea3834267d719b4c801409b8b1ddd0158</field>
+        <field type="BooleanField" name="is_staff">True</field>
+        <field type="BooleanField" name="is_active">True</field>
+        <field type="BooleanField" name="is_superuser">False</field>
+        <field type="DateTimeField" name="last_login">2007-05-30 13:20:10</field>
+        <field type="DateTimeField" name="date_joined">2007-05-30 13:20:10</field>
+        <field to="auth.group" name="groups" rel="ManyToManyRel"></field>
+        <field to="auth.permission" name="user_permissions" rel="ManyToManyRel"></field>
+    </object>
+    <object pk="103" model="auth.user">
+        <field type="CharField" name="username">deleteuser</field>
+        <field type="CharField" name="first_name">Delete</field>
+        <field type="CharField" name="last_name">User</field>
+        <field type="CharField" name="email">duser@example.com</field>
+        <field type="CharField" name="password">sha1$995a3$6011485ea3834267d719b4c801409b8b1ddd0158</field>
+        <field type="BooleanField" name="is_staff">True</field>
+        <field type="BooleanField" name="is_active">True</field>
+        <field type="BooleanField" name="is_superuser">False</field>
+        <field type="DateTimeField" name="last_login">2007-05-30 13:20:10</field>
+        <field type="DateTimeField" name="date_joined">2007-05-30 13:20:10</field>
+        <field to="auth.group" name="groups" rel="ManyToManyRel"></field>
+        <field to="auth.permission" name="user_permissions" rel="ManyToManyRel"></field>
+    </object>
+    <object pk="104" model="auth.user">
+        <field type="CharField" name="username">joepublic</field>
+        <field type="CharField" name="first_name">Joe</field>
+        <field type="CharField" name="last_name">Public</field>
+        <field type="CharField" name="email">joepublic@example.com</field>
+        <field type="CharField" name="password">sha1$995a3$6011485ea3834267d719b4c801409b8b1ddd0158</field>
+        <field type="BooleanField" name="is_staff">False</field>
+        <field type="BooleanField" name="is_active">True</field>
+        <field type="BooleanField" name="is_superuser">False</field>
+        <field type="DateTimeField" name="last_login">2007-05-30 13:20:10</field>
+        <field type="DateTimeField" name="date_joined">2007-05-30 13:20:10</field>
+        <field to="auth.group" name="groups" rel="ManyToManyRel"></field>
+        <field to="auth.permission" name="user_permissions" rel="ManyToManyRel"></field>
+    </object>
+    <object pk="1" model="admin_views.article">
+        <field type="TextField" name="content">&lt;p&gt;test content&lt;/p&gt;</field>
+        <field type="DateTimeField" name="date">2008-03-18 11:54:58</field>
+    </object>
+</django-objects>
\ No newline at end of file
Index: tests/regressiontests/admin_views/models.py
===================================================================
--- tests/regressiontests/admin_views/models.py	
+++ tests/regressiontests/admin_views/models.py	
@@ -0,0 +1,13 @@
+from django.db import models
+from django.contrib import admin
+
+class Article(models.Model):
+    """An simple article to test admin views. Test backwards compabilty."""
+    content = models.TextField()
+    date = models.DateTimeField()
+        
+class ArticleAdmin(admin.ModelAdmin):
+        list_display = ('content', 'date')
+        list_filter = ('date',)
+        
+admin.site.register(Article, ArticleAdmin)
\ No newline at end of file
Index: tests/regressiontests/admin_views/tests.py
===================================================================
--- tests/regressiontests/admin_views/tests.py	
+++ tests/regressiontests/admin_views/tests.py	
@@ -0,0 +1,197 @@
+
+from django.test import TestCase
+from django.contrib.auth.models import User, Permission
+from django.contrib.contenttypes.models import ContentType
+from django.contrib.admin.sites import LOGIN_FORM_KEY, _encode_post_data
+
+# local test models
+from models import Article
+
+def get_perm(Model, perm):
+    """Return the permission object, for the Model"""
+    ct = ContentType.objects.get_for_model(Model)
+    return Permission.objects.get(content_type=ct,codename=perm)
+    
+
+class AdminViewPermissionsTest(TestCase):
+    """Tests for Admin Views Permissions."""
+    
+    fixtures = ['admin-views-users.xml']
+    
+    def setUp(self):
+        """Test setup."""
+        # Setup permissions, for our users who can add, change, and delete. 
+        # We can't put this into the fixture, because the content type id
+        # and the permission id could be different on each run of the test.
+        
+        opts = Article._meta
+        
+        # User who can add Articles
+        add_user = User.objects.get(username='adduser')
+        add_user.user_permissions.add(get_perm(Article, opts.get_add_permission()))
+        
+        # User who can change Articles
+        change_user = User.objects.get(username='changeuser')
+        change_user.user_permissions.add(get_perm(Article, opts.get_change_permission()))
+        
+        # User who can delete Articles
+        delete_user = User.objects.get(username='deleteuser')
+        delete_user.user_permissions.add(get_perm(Article, opts.get_delete_permission()))
+        
+        # login POST dicts
+        self.super_login = {'post_data': _encode_post_data({}),
+                     LOGIN_FORM_KEY: 1,
+                     'username': 'super',
+                     'password': 'secret'}
+        self.adduser_login = {'post_data': _encode_post_data({}),
+                     LOGIN_FORM_KEY: 1,
+                     'username': 'adduser',
+                     'password': 'secret'}
+        self.changeuser_login = {'post_data': _encode_post_data({}),
+                     LOGIN_FORM_KEY: 1,
+                     'username': 'changeuser',
+                     'password': 'secret'}
+        self.deleteuser_login = {'post_data': _encode_post_data({}),
+                     LOGIN_FORM_KEY: 1,
+                     'username': 'deleteuser',
+                     'password': 'secret'}
+        self.joepublic_login = {'post_data': _encode_post_data({}),
+                     LOGIN_FORM_KEY: 1,
+                     'username': 'joepublic',
+                     'password': 'secret'}
+           
+        
+    def testLogin(self):
+        """Make sure only staff members can log in.
+        
+        Successful posts to the login page will redirect to the orignal url.
+        Unsuccessfull attempts will continue to render the login page with 
+        a 200 status code.
+        """
+        # Super User
+        request = self.client.get('/test_admin/admin/')
+        self.failUnlessEqual(request.status_code, 200)
+        login = self.client.post('/test_admin/admin/', self.super_login)
+        self.assertRedirects(login, '/test_admin/admin/')
+        self.assertFalse(login.context)
+        self.client.get('/test_admin/admin/logout/')
+        
+        # Add User
+        request = self.client.get('/test_admin/admin/')
+        self.failUnlessEqual(request.status_code, 200)
+        login = self.client.post('/test_admin/admin/', self.adduser_login)
+        self.assertRedirects(login, '/test_admin/admin/')
+        self.assertFalse(login.context)
+        self.client.get('/test_admin/admin/logout/')
+        
+        # Change User
+        request = self.client.get('/test_admin/admin/')
+        self.failUnlessEqual(request.status_code, 200)
+        login = self.client.post('/test_admin/admin/', self.changeuser_login)
+        self.assertRedirects(login, '/test_admin/admin/')
+        self.assertFalse(login.context)
+        self.client.get('/test_admin/admin/logout/')
+        
+        # Delete User
+        request = self.client.get('/test_admin/admin/')
+        self.failUnlessEqual(request.status_code, 200)
+        login = self.client.post('/test_admin/admin/', self.deleteuser_login)
+        self.assertRedirects(login, '/test_admin/admin/')
+        self.assertFalse(login.context)
+        self.client.get('/test_admin/admin/logout/')
+        
+        # Regular User should not be able to login.
+        request = self.client.get('/test_admin/admin/')
+        self.failUnlessEqual(request.status_code, 200)
+        login = self.client.post('/test_admin/admin/', self.joepublic_login)
+        self.failUnlessEqual(login.status_code, 200)
+        # Login.context is a list of context dicts we just need to check the first one.
+        self.assert_(login.context[0].get('error_message'))
+    
+    def testAddView(self):
+        """Test add view restricts access and actually adds items."""
+        
+        add_dict = {'content': '<p>great article</p>',
+                    'date_0': '2008-03-18', 'date_1': '10:54:39'}
+        
+        # Change User should not have access to add articles
+        self.client.get('/test_admin/admin/')
+        self.client.post('/test_admin/admin/', self.changeuser_login)
+        request = self.client.get('/test_admin/admin/admin_views/article/add/')
+        self.failUnlessEqual(request.status_code, 403)
+        # Try POST just to make sure
+        post = self.client.post('/test_admin/admin/admin_views/article/add/', add_dict)
+        self.failUnlessEqual(post.status_code, 403)
+        self.failUnlessEqual(Article.objects.all().count(), 1)
+        self.client.get('/test_admin/admin/logout/')
+        
+        # Add user may login and POST to add view, then redirect to admin root
+        self.client.get('/test_admin/admin/')
+        self.client.post('/test_admin/admin/', self.adduser_login)
+        post = self.client.post('/test_admin/admin/admin_views/article/add/', add_dict)
+        self.assertRedirects(post, '/test_admin/admin/')
+        self.failUnlessEqual(Article.objects.all().count(), 2)
+        self.client.get('/test_admin/admin/logout/')
+        
+        # Super can add too, but is redirected to the change list view
+        self.client.get('/test_admin/admin/')
+        self.client.post('/test_admin/admin/', self.super_login)
+        post = self.client.post('/test_admin/admin/admin_views/article/add/', add_dict)
+        self.assertRedirects(post, '/test_admin/admin/admin_views/article/')
+        self.failUnlessEqual(Article.objects.all().count(), 3)
+        self.client.get('/test_admin/admin/logout/')
+        
+    def testChangeView(self):
+        """Change view should restrict access and allow users to edit items."""
+        
+        change_dict = {'content': '<p>edited article</p>',
+                    'date_0': '2008-03-18', 'date_1': '10:54:39'}
+        
+        # add user shoud not be able to view the list of article or change any of them
+        self.client.get('/test_admin/admin/')
+        self.client.post('/test_admin/admin/', self.adduser_login)
+        request = self.client.get('/test_admin/admin/admin_views/article/')
+        self.failUnlessEqual(request.status_code, 403)
+        request = self.client.get('/test_admin/admin/admin_views/article/1/')
+        self.failUnlessEqual(request.status_code, 403)
+        post = self.client.post('/test_admin/admin/admin_views/article/1/', change_dict)
+        self.failUnlessEqual(post.status_code, 403)
+        self.client.get('/test_admin/admin/logout/')
+        
+        # change user can view all items and edit them
+        self.client.get('/test_admin/admin/')
+        self.client.post('/test_admin/admin/', self.changeuser_login)
+        request = self.client.get('/test_admin/admin/admin_views/article/')
+        self.failUnlessEqual(request.status_code, 200)
+        request = self.client.get('/test_admin/admin/admin_views/article/1/')
+        self.failUnlessEqual(request.status_code, 200)
+        post = self.client.post('/test_admin/admin/admin_views/article/1/', change_dict)
+        self.assertRedirects(post, '/test_admin/admin/admin_views/article/')
+        self.failUnlessEqual(Article.objects.get(pk=1).content, '<p>edited article</p>')
+        self.client.get('/test_admin/admin/logout/')
+
+    def testDeleteView(self):
+        """Delete view should restrict access and actually delete items."""
+
+        delete_dict = {'post': 'yes'}
+        
+        # add user shoud not be able to delete articles
+        self.client.get('/test_admin/admin/')
+        self.client.post('/test_admin/admin/', self.adduser_login)
+        request = self.client.get('/test_admin/admin/admin_views/article/1/delete/')
+        self.failUnlessEqual(request.status_code, 403)
+        post = self.client.post('/test_admin/admin/admin_views/article/1/delete/', delete_dict)
+        self.failUnlessEqual(post.status_code, 403)
+        self.failUnlessEqual(Article.objects.all().count(), 1)
+        self.client.get('/test_admin/admin/logout/')
+        
+        # Delete user can delete
+        self.client.get('/test_admin/admin/')
+        self.client.post('/test_admin/admin/', self.deleteuser_login)
+        request = self.client.get('/test_admin/admin/admin_views/article/1/delete/')
+        self.failUnlessEqual(request.status_code, 200)
+        post = self.client.post('/test_admin/admin/admin_views/article/1/delete/', delete_dict)
+        # TODO: http://code.djangoproject.com/ticket/6819 or the next line fails
+        self.assertRedirects(post, '/test_admin/admin/')
+        self.failUnlessEqual(Article.objects.all().count(), 0)
+        self.client.get('/test_admin/admin/logout/')
\ No newline at end of file
Index: tests/regressiontests/admin_views/urls.py
===================================================================
--- tests/regressiontests/admin_views/urls.py	
+++ tests/regressiontests/admin_views/urls.py	
@@ -0,0 +1,7 @@
+from django.conf.urls.defaults import *
+from django.contrib import admin
+
+urlpatterns = patterns('',
+    (r'^admin/doc/', include('django.contrib.admindocs.urls')),
+    (r'^admin/(.*)', admin.site.root),
+)
\ No newline at end of file
Index: tests/urls.py
===================================================================
--- tests/urls.py	(revision 7299)
+++ tests/urls.py	(working copy)
@@ -17,4 +17,7 @@
 
     # test urlconf for middleware tests
     (r'^middleware/', include('regressiontests.middleware.urls')),
+    
+    # test admin views
+    (r'^test_admin/', include('regressiontests.admin_views.urls')),
 )
