summaryrefslogtreecommitdiff
path: root/django/contrib/flatpages
diff options
context:
space:
mode:
Diffstat (limited to 'django/contrib/flatpages')
-rw-r--r--django/contrib/flatpages/admin.py4
-rw-r--r--django/contrib/flatpages/fixtures/sample_flatpages.json63
-rw-r--r--django/contrib/flatpages/templatetags/__init__.py0
-rw-r--r--django/contrib/flatpages/templatetags/flatpages.py99
-rw-r--r--django/contrib/flatpages/tests/__init__.py5
-rw-r--r--django/contrib/flatpages/tests/csrf.py76
-rw-r--r--django/contrib/flatpages/tests/forms.py22
-rw-r--r--django/contrib/flatpages/tests/middleware.py67
-rw-r--r--django/contrib/flatpages/tests/templates/404.html1
-rw-r--r--django/contrib/flatpages/tests/templates/flatpages/default.html10
-rw-r--r--django/contrib/flatpages/tests/templates/registration/login.html0
-rw-r--r--django/contrib/flatpages/tests/templatetags.py134
-rw-r--r--django/contrib/flatpages/tests/urls.py8
-rw-r--r--django/contrib/flatpages/tests/views.py72
-rw-r--r--django/contrib/flatpages/views.py14
15 files changed, 571 insertions, 4 deletions
diff --git a/django/contrib/flatpages/admin.py b/django/contrib/flatpages/admin.py
index b6fdba3d53..1b377e967b 100644
--- a/django/contrib/flatpages/admin.py
+++ b/django/contrib/flatpages/admin.py
@@ -5,11 +5,11 @@ from django.utils.translation import ugettext_lazy as _
class FlatpageForm(forms.ModelForm):
- url = forms.RegexField(label=_("URL"), max_length=100, regex=r'^[-\w/]+$',
+ url = forms.RegexField(label=_("URL"), max_length=100, regex=r'^[-\w/\.~]+$',
help_text = _("Example: '/about/contact/'. Make sure to have leading"
" and trailing slashes."),
error_message = _("This value must contain only letters, numbers,"
- " underscores, dashes or slashes."))
+ " dots, underscores, dashes, slashes or tildes."))
class Meta:
model = FlatPage
diff --git a/django/contrib/flatpages/fixtures/sample_flatpages.json b/django/contrib/flatpages/fixtures/sample_flatpages.json
new file mode 100644
index 0000000000..885af1eb60
--- /dev/null
+++ b/django/contrib/flatpages/fixtures/sample_flatpages.json
@@ -0,0 +1,63 @@
+[
+ {
+ "pk": 1,
+ "model": "flatpages.flatpage",
+ "fields": {
+ "registration_required": false,
+ "title": "A Flatpage",
+ "url": "/flatpage/",
+ "template_name": "",
+ "sites": [
+ 1
+ ],
+ "content": "Isn't it flat!",
+ "enable_comments": false
+ }
+ },
+ {
+ "pk": 2,
+ "model": "flatpages.flatpage",
+ "fields": {
+ "registration_required": false,
+ "title": "A Nested Flatpage",
+ "url": "/location/flatpage/",
+ "template_name": "",
+ "sites": [
+ 1
+ ],
+ "content": "Isn't it flat and deep!",
+ "enable_comments": false
+ }
+ },
+
+ {
+ "pk": 101,
+ "model": "flatpages.flatpage",
+ "fields": {
+ "registration_required": true,
+ "title": "Sekrit Flatpage",
+ "url": "/sekrit/",
+ "template_name": "",
+ "sites": [
+ 1
+ ],
+ "content": "Isn't it sekrit!",
+ "enable_comments": false
+ }
+ },
+ {
+ "pk": 102,
+ "model": "flatpages.flatpage",
+ "fields": {
+ "registration_required": true,
+ "title": "Sekrit Nested Flatpage",
+ "url": "/location/sekrit/",
+ "template_name": "",
+ "sites": [
+ 1
+ ],
+ "content": "Isn't it sekrit and deep!",
+ "enable_comments": false
+ }
+ }
+] \ No newline at end of file
diff --git a/django/contrib/flatpages/templatetags/__init__.py b/django/contrib/flatpages/templatetags/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/django/contrib/flatpages/templatetags/__init__.py
diff --git a/django/contrib/flatpages/templatetags/flatpages.py b/django/contrib/flatpages/templatetags/flatpages.py
new file mode 100644
index 0000000000..5c76699b79
--- /dev/null
+++ b/django/contrib/flatpages/templatetags/flatpages.py
@@ -0,0 +1,99 @@
+from django import template
+from django.contrib.flatpages.models import FlatPage
+from django.utils.translation import ugettext as _
+from django.conf import settings
+
+
+register = template.Library()
+
+
+class FlatpageNode(template.Node):
+ def __init__(self, context_name, starts_with=None, user=None):
+ self.context_name = context_name
+ if starts_with:
+ self.starts_with = template.Variable(starts_with)
+ else:
+ self.starts_with = None
+ if user:
+ self.user = template.Variable(user)
+ else:
+ self.user = None
+
+ def render(self, context):
+ flatpages = FlatPage.objects.filter(sites__id=settings.SITE_ID)
+ # If a prefix was specified, add a filter
+ if self.starts_with:
+ flatpages = flatpages.filter(
+ url__startswith=self.starts_with.resolve(context))
+
+ # If the provided user is not authenticated, or no user
+ # was provided, filter the list to only public flatpages.
+ if self.user:
+ user = self.user.resolve(context)
+ if not user.is_authenticated():
+ flatpages = flatpages.filter(registration_required=False)
+ else:
+ flatpages = flatpages.filter(registration_required=False)
+
+ context[self.context_name] = flatpages
+ return ''
+
+
+def get_flatpages(parser, token):
+ """
+ Retrieves all flatpage objects available for the current site and
+ visible to the specific user (or visible to all users if no user is
+ specified). Populates the template context with them in a variable
+ whose name is defined by the ``as`` clause.
+
+ An optional ``for`` clause can be used to control the user whose
+ permissions are to be used in determining which flatpages are visible.
+
+ An optional argument, ``starts_with``, can be applied to limit the
+ returned flatpages to those beginning with a particular base URL.
+ This argument can be passed as a variable or a string, as it resolves
+ from the template context.
+
+ Syntax::
+
+ {% get_flatpages ['url_starts_with'] [for user] as context_name %}
+
+ Example usage::
+
+ {% get_flatpages as flatpages %}
+ {% get_flatpages for someuser as flatpages %}
+ {% get_flatpages '/about/' as about_pages %}
+ {% get_flatpages prefix as about_pages %}
+ {% get_flatpages '/about/' for someuser as about_pages %}
+ """
+ bits = token.split_contents()
+ syntax_message = _("%(tag_name)s expects a syntax of %(tag_name)s "
+ "['url_starts_with'] [for user] as context_name" %
+ dict(tag_name=bits[0]))
+ # Must have at 3-6 bits in the tag
+ if len(bits) >= 3 and len(bits) <= 6:
+
+ # If there's an even number of bits, there's no prefix
+ if len(bits) % 2 == 0:
+ prefix = bits[1]
+ else:
+ prefix = None
+
+ # The very last bit must be the context name
+ if bits[-2] != 'as':
+ raise template.TemplateSyntaxError(syntax_message)
+ context_name = bits[-1]
+
+ # If there are 5 or 6 bits, there is a user defined
+ if len(bits) >= 5:
+ if bits[-4] != 'for':
+ raise template.TemplateSyntaxError(syntax_message)
+ user = bits[-3]
+ else:
+ user = None
+
+ return FlatpageNode(context_name, starts_with=prefix, user=user)
+ else:
+ raise template.TemplateSyntaxError(syntax_message)
+
+register.tag('get_flatpages', get_flatpages)
diff --git a/django/contrib/flatpages/tests/__init__.py b/django/contrib/flatpages/tests/__init__.py
new file mode 100644
index 0000000000..5dd5e89dca
--- /dev/null
+++ b/django/contrib/flatpages/tests/__init__.py
@@ -0,0 +1,5 @@
+from django.contrib.flatpages.tests.csrf import *
+from django.contrib.flatpages.tests.forms import *
+from django.contrib.flatpages.tests.middleware import *
+from django.contrib.flatpages.tests.templatetags import *
+from django.contrib.flatpages.tests.views import *
diff --git a/django/contrib/flatpages/tests/csrf.py b/django/contrib/flatpages/tests/csrf.py
new file mode 100644
index 0000000000..b65ee382a6
--- /dev/null
+++ b/django/contrib/flatpages/tests/csrf.py
@@ -0,0 +1,76 @@
+import os
+from django.conf import settings
+from django.contrib.auth.models import User
+from django.test import TestCase, Client
+
+class FlatpageCSRFTests(TestCase):
+ fixtures = ['sample_flatpages']
+ urls = 'django.contrib.flatpages.tests.urls'
+
+ def setUp(self):
+ self.client = Client(enforce_csrf_checks=True)
+ self.old_MIDDLEWARE_CLASSES = settings.MIDDLEWARE_CLASSES
+ flatpage_middleware_class = 'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware'
+ csrf_middleware_class = 'django.middleware.csrf.CsrfViewMiddleware'
+ if csrf_middleware_class not in settings.MIDDLEWARE_CLASSES:
+ settings.MIDDLEWARE_CLASSES += (csrf_middleware_class,)
+ if flatpage_middleware_class not in settings.MIDDLEWARE_CLASSES:
+ settings.MIDDLEWARE_CLASSES += (flatpage_middleware_class,)
+ self.old_TEMPLATE_DIRS = settings.TEMPLATE_DIRS
+ settings.TEMPLATE_DIRS = (
+ os.path.join(
+ os.path.dirname(__file__),
+ 'templates'
+ ),
+ )
+
+ def tearDown(self):
+ settings.MIDDLEWARE_CLASSES = self.old_MIDDLEWARE_CLASSES
+ settings.TEMPLATE_DIRS = self.old_TEMPLATE_DIRS
+
+ def test_view_flatpage(self):
+ "A flatpage can be served through a view, even when the middleware is in use"
+ response = self.client.get('/flatpage_root/flatpage/')
+ self.assertEquals(response.status_code, 200)
+ self.assertContains(response, "<p>Isn't it flat!</p>")
+
+ def test_view_non_existent_flatpage(self):
+ "A non-existent flatpage raises 404 when served through a view, even when the middleware is in use"
+ response = self.client.get('/flatpage_root/no_such_flatpage/')
+ self.assertEquals(response.status_code, 404)
+
+ def test_view_authenticated_flatpage(self):
+ "A flatpage served through a view can require authentication"
+ response = self.client.get('/flatpage_root/sekrit/')
+ self.assertRedirects(response, '/accounts/login/?next=/flatpage_root/sekrit/')
+ User.objects.create_user('testuser', 'test@example.com', 's3krit')
+ self.client.login(username='testuser',password='s3krit')
+ response = self.client.get('/flatpage_root/sekrit/')
+ self.assertEquals(response.status_code, 200)
+ self.assertContains(response, "<p>Isn't it sekrit!</p>")
+
+ def test_fallback_flatpage(self):
+ "A flatpage can be served by the fallback middlware"
+ response = self.client.get('/flatpage/')
+ self.assertEquals(response.status_code, 200)
+ self.assertContains(response, "<p>Isn't it flat!</p>")
+
+ def test_fallback_non_existent_flatpage(self):
+ "A non-existent flatpage raises a 404 when served by the fallback middlware"
+ response = self.client.get('/no_such_flatpage/')
+ self.assertEquals(response.status_code, 404)
+
+ def test_post_view_flatpage(self):
+ "POSTing to a flatpage served through a view will raise a CSRF error if no token is provided (Refs #14156)"
+ response = self.client.post('/flatpage_root/flatpage/')
+ self.assertEquals(response.status_code, 403)
+
+ def test_post_fallback_flatpage(self):
+ "POSTing to a flatpage served by the middleware will raise a CSRF error if no token is provided (Refs #14156)"
+ response = self.client.post('/flatpage/')
+ self.assertEquals(response.status_code, 403)
+
+ def test_post_unknown_page(self):
+ "POSTing to an unknown page isn't caught as a 403 CSRF error"
+ response = self.client.post('/no_such_page/')
+ self.assertEquals(response.status_code, 404)
diff --git a/django/contrib/flatpages/tests/forms.py b/django/contrib/flatpages/tests/forms.py
new file mode 100644
index 0000000000..969d347b39
--- /dev/null
+++ b/django/contrib/flatpages/tests/forms.py
@@ -0,0 +1,22 @@
+from django.contrib.flatpages.admin import FlatpageForm
+from django.test import TestCase
+
+class FlatpageAdminFormTests(TestCase):
+ def setUp(self):
+ self.form_data = {
+ 'title': "A test page",
+ 'content': "This is a test",
+ 'sites': [1],
+ }
+
+ def test_flatpage_admin_form_url_validation(self):
+ "The flatpage admin form validates correctly validates urls"
+ self.assertTrue(FlatpageForm(data=dict(url='/new_flatpage/', **self.form_data)).is_valid())
+ self.assertTrue(FlatpageForm(data=dict(url='/some.special~chars/', **self.form_data)).is_valid())
+ self.assertTrue(FlatpageForm(data=dict(url='/some.very_special~chars-here/', **self.form_data)).is_valid())
+
+ self.assertFalse(FlatpageForm(data=dict(url='/a space/', **self.form_data)).is_valid())
+ self.assertFalse(FlatpageForm(data=dict(url='/a % char/', **self.form_data)).is_valid())
+ self.assertFalse(FlatpageForm(data=dict(url='/a ! char/', **self.form_data)).is_valid())
+ self.assertFalse(FlatpageForm(data=dict(url='/a & char/', **self.form_data)).is_valid())
+ self.assertFalse(FlatpageForm(data=dict(url='/a ? char/', **self.form_data)).is_valid())
diff --git a/django/contrib/flatpages/tests/middleware.py b/django/contrib/flatpages/tests/middleware.py
new file mode 100644
index 0000000000..bedaffc8ac
--- /dev/null
+++ b/django/contrib/flatpages/tests/middleware.py
@@ -0,0 +1,67 @@
+import os
+from django.conf import settings
+from django.contrib.auth.models import User
+from django.test import TestCase
+
+class FlatpageMiddlewareTests(TestCase):
+ fixtures = ['sample_flatpages']
+ urls = 'django.contrib.flatpages.tests.urls'
+
+ def setUp(self):
+ self.old_MIDDLEWARE_CLASSES = settings.MIDDLEWARE_CLASSES
+ flatpage_middleware_class = 'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware'
+ if flatpage_middleware_class not in settings.MIDDLEWARE_CLASSES:
+ settings.MIDDLEWARE_CLASSES += (flatpage_middleware_class,)
+ self.old_TEMPLATE_DIRS = settings.TEMPLATE_DIRS
+ settings.TEMPLATE_DIRS = (
+ os.path.join(
+ os.path.dirname(__file__),
+ 'templates'
+ ),
+ )
+
+ def tearDown(self):
+ settings.MIDDLEWARE_CLASSES = self.old_MIDDLEWARE_CLASSES
+ settings.TEMPLATE_DIRS = self.old_TEMPLATE_DIRS
+
+ def test_view_flatpage(self):
+ "A flatpage can be served through a view, even when the middleware is in use"
+ response = self.client.get('/flatpage_root/flatpage/')
+ self.assertEquals(response.status_code, 200)
+ self.assertContains(response, "<p>Isn't it flat!</p>")
+
+ def test_view_non_existent_flatpage(self):
+ "A non-existent flatpage raises 404 when served through a view, even when the middleware is in use"
+ response = self.client.get('/flatpage_root/no_such_flatpage/')
+ self.assertEquals(response.status_code, 404)
+
+ def test_view_authenticated_flatpage(self):
+ "A flatpage served through a view can require authentication"
+ response = self.client.get('/flatpage_root/sekrit/')
+ self.assertRedirects(response, '/accounts/login/?next=/flatpage_root/sekrit/')
+ User.objects.create_user('testuser', 'test@example.com', 's3krit')
+ self.client.login(username='testuser',password='s3krit')
+ response = self.client.get('/flatpage_root/sekrit/')
+ self.assertEquals(response.status_code, 200)
+ self.assertContains(response, "<p>Isn't it sekrit!</p>")
+
+ def test_fallback_flatpage(self):
+ "A flatpage can be served by the fallback middlware"
+ response = self.client.get('/flatpage/')
+ self.assertEquals(response.status_code, 200)
+ self.assertContains(response, "<p>Isn't it flat!</p>")
+
+ def test_fallback_non_existent_flatpage(self):
+ "A non-existent flatpage raises a 404 when served by the fallback middlware"
+ response = self.client.get('/no_such_flatpage/')
+ self.assertEquals(response.status_code, 404)
+
+ def test_fallback_authenticated_flatpage(self):
+ "A flatpage served by the middleware can require authentication"
+ response = self.client.get('/sekrit/')
+ self.assertRedirects(response, '/accounts/login/?next=/sekrit/')
+ User.objects.create_user('testuser', 'test@example.com', 's3krit')
+ self.client.login(username='testuser',password='s3krit')
+ response = self.client.get('/sekrit/')
+ self.assertEquals(response.status_code, 200)
+ self.assertContains(response, "<p>Isn't it sekrit!</p>")
diff --git a/django/contrib/flatpages/tests/templates/404.html b/django/contrib/flatpages/tests/templates/404.html
new file mode 100644
index 0000000000..5fd5f3cf3b
--- /dev/null
+++ b/django/contrib/flatpages/tests/templates/404.html
@@ -0,0 +1 @@
+<h1>Oh Noes!</h1> \ No newline at end of file
diff --git a/django/contrib/flatpages/tests/templates/flatpages/default.html b/django/contrib/flatpages/tests/templates/flatpages/default.html
new file mode 100644
index 0000000000..1410e17adf
--- /dev/null
+++ b/django/contrib/flatpages/tests/templates/flatpages/default.html
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"
+ "http://www.w3.org/TR/REC-html40/loose.dtd">
+<html>
+<head>
+<title>{{ flatpage.title }}</title>
+</head>
+<body>
+<p>{{ flatpage.content }}</p>
+</body>
+</html>
diff --git a/django/contrib/flatpages/tests/templates/registration/login.html b/django/contrib/flatpages/tests/templates/registration/login.html
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/django/contrib/flatpages/tests/templates/registration/login.html
diff --git a/django/contrib/flatpages/tests/templatetags.py b/django/contrib/flatpages/tests/templatetags.py
new file mode 100644
index 0000000000..9f42381c29
--- /dev/null
+++ b/django/contrib/flatpages/tests/templatetags.py
@@ -0,0 +1,134 @@
+import os
+from django.conf import settings
+from django.contrib.auth.models import AnonymousUser, User
+from django.template import Template, Context, TemplateSyntaxError
+from django.test import TestCase
+
+class FlatpageTemplateTagTests(TestCase):
+ fixtures = ['sample_flatpages']
+ urls = 'django.contrib.flatpages.tests.urls'
+
+ def setUp(self):
+ self.old_MIDDLEWARE_CLASSES = settings.MIDDLEWARE_CLASSES
+ flatpage_middleware_class = 'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware'
+ if flatpage_middleware_class not in settings.MIDDLEWARE_CLASSES:
+ settings.MIDDLEWARE_CLASSES += (flatpage_middleware_class,)
+ self.old_TEMPLATE_DIRS = settings.TEMPLATE_DIRS
+ settings.TEMPLATE_DIRS = (
+ os.path.join(
+ os.path.dirname(__file__),
+ 'templates'
+ ),
+ )
+ self.me = User.objects.create_user('testuser', 'test@example.com', 's3krit')
+
+ def tearDown(self):
+ settings.MIDDLEWARE_CLASSES = self.old_MIDDLEWARE_CLASSES
+ settings.TEMPLATE_DIRS = self.old_TEMPLATE_DIRS
+
+ def test_get_flatpages_tag(self):
+ "The flatpage template tag retrives unregistered prefixed flatpages by default"
+ out = Template(
+ "{% load flatpages %}"
+ "{% get_flatpages as flatpages %}"
+ "{% for page in flatpages %}"
+ "{{ page.title }},"
+ "{% endfor %}"
+ ).render(Context())
+ self.assertEquals(out, "A Flatpage,A Nested Flatpage,")
+
+ def test_get_flatpages_tag_for_anon_user(self):
+ "The flatpage template tag retrives unregistered flatpages for an anonymous user"
+ out = Template(
+ "{% load flatpages %}"
+ "{% get_flatpages for anonuser as flatpages %}"
+ "{% for page in flatpages %}"
+ "{{ page.title }},"
+ "{% endfor %}"
+ ).render(Context({
+ 'anonuser': AnonymousUser()
+ }))
+ self.assertEquals(out, "A Flatpage,A Nested Flatpage,")
+
+ def test_get_flatpages_tag_for_user(self):
+ "The flatpage template tag retrives all flatpages for an authenticated user"
+ out = Template(
+ "{% load flatpages %}"
+ "{% get_flatpages for me as flatpages %}"
+ "{% for page in flatpages %}"
+ "{{ page.title }},"
+ "{% endfor %}"
+ ).render(Context({
+ 'me': self.me
+ }))
+ self.assertEquals(out, "A Flatpage,A Nested Flatpage,Sekrit Nested Flatpage,Sekrit Flatpage,")
+
+ def test_get_flatpages_with_prefix(self):
+ "The flatpage template tag retrives unregistered prefixed flatpages by default"
+ out = Template(
+ "{% load flatpages %}"
+ "{% get_flatpages '/location/' as location_flatpages %}"
+ "{% for page in location_flatpages %}"
+ "{{ page.title }},"
+ "{% endfor %}"
+ ).render(Context())
+ self.assertEquals(out, "A Nested Flatpage,")
+
+ def test_get_flatpages_with_prefix_for_anon_user(self):
+ "The flatpage template tag retrives unregistered prefixed flatpages for an anonymous user"
+ out = Template(
+ "{% load flatpages %}"
+ "{% get_flatpages '/location/' for anonuser as location_flatpages %}"
+ "{% for page in location_flatpages %}"
+ "{{ page.title }},"
+ "{% endfor %}"
+ ).render(Context({
+ 'anonuser': AnonymousUser()
+ }))
+ self.assertEquals(out, "A Nested Flatpage,")
+
+ def test_get_flatpages_with_prefix_for_user(self):
+ "The flatpage template tag retrive prefixed flatpages for an authenticated user"
+ out = Template(
+ "{% load flatpages %}"
+ "{% get_flatpages '/location/' for me as location_flatpages %}"
+ "{% for page in location_flatpages %}"
+ "{{ page.title }},"
+ "{% endfor %}"
+ ).render(Context({
+ 'me': self.me
+ }))
+ self.assertEquals(out, "A Nested Flatpage,Sekrit Nested Flatpage,")
+
+ def test_get_flatpages_with_variable_prefix(self):
+ "The prefix for the flatpage template tag can be a template variable"
+ out = Template(
+ "{% load flatpages %}"
+ "{% get_flatpages location_prefix as location_flatpages %}"
+ "{% for page in location_flatpages %}"
+ "{{ page.title }},"
+ "{% endfor %}"
+ ).render(Context({
+ 'location_prefix': '/location/'
+ }))
+ self.assertEquals(out, "A Nested Flatpage,")
+
+ def test_parsing_errors(self):
+ "There are various ways that the flatpages template tag won't parse"
+ render = lambda t: Template(t).render(Context())
+
+ self.assertRaises(TemplateSyntaxError, render,
+ "{% load flatpages %}{% get_flatpages %}")
+ self.assertRaises(TemplateSyntaxError, render,
+ "{% load flatpages %}{% get_flatpages as %}")
+ self.assertRaises(TemplateSyntaxError, render,
+ "{% load flatpages %}{% get_flatpages cheesecake flatpages %}")
+ self.assertRaises(TemplateSyntaxError, render,
+ "{% load flatpages %}{% get_flatpages as flatpages asdf%}")
+ self.assertRaises(TemplateSyntaxError, render,
+ "{% load flatpages %}{% get_flatpages cheesecake user as flatpages %}")
+ self.assertRaises(TemplateSyntaxError, render,
+ "{% load flatpages %}{% get_flatpages for user as flatpages asdf%}")
+ self.assertRaises(TemplateSyntaxError, render,
+ "{% load flatpages %}{% get_flatpages prefix for user as flatpages asdf%}")
+
diff --git a/django/contrib/flatpages/tests/urls.py b/django/contrib/flatpages/tests/urls.py
new file mode 100644
index 0000000000..3cffd09d0f
--- /dev/null
+++ b/django/contrib/flatpages/tests/urls.py
@@ -0,0 +1,8 @@
+from django.conf.urls.defaults import *
+
+# special urls for flatpage test cases
+urlpatterns = patterns('',
+ (r'^flatpage_root', include('django.contrib.flatpages.urls')),
+ (r'^accounts/', include('django.contrib.auth.urls')),
+)
+
diff --git a/django/contrib/flatpages/tests/views.py b/django/contrib/flatpages/tests/views.py
new file mode 100644
index 0000000000..89bdde2d92
--- /dev/null
+++ b/django/contrib/flatpages/tests/views.py
@@ -0,0 +1,72 @@
+import os
+from django.conf import settings
+from django.contrib.auth.models import User
+from django.contrib.flatpages.models import FlatPage
+from django.test import TestCase
+
+class FlatpageViewTests(TestCase):
+ fixtures = ['sample_flatpages']
+ urls = 'django.contrib.flatpages.tests.urls'
+
+ def setUp(self):
+ self.old_MIDDLEWARE_CLASSES = settings.MIDDLEWARE_CLASSES
+ flatpage_middleware_class = 'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware'
+ if flatpage_middleware_class in settings.MIDDLEWARE_CLASSES:
+ settings.MIDDLEWARE_CLASSES = tuple(m for m in settings.MIDDLEWARE_CLASSES if m != flatpage_middleware_class)
+ self.old_TEMPLATE_DIRS = settings.TEMPLATE_DIRS
+ settings.TEMPLATE_DIRS = (
+ os.path.join(
+ os.path.dirname(__file__),
+ 'templates'
+ ),
+ )
+
+ def tearDown(self):
+ settings.MIDDLEWARE_CLASSES = self.old_MIDDLEWARE_CLASSES
+ settings.TEMPLATE_DIRS = self.old_TEMPLATE_DIRS
+
+ def test_view_flatpage(self):
+ "A flatpage can be served through a view"
+ response = self.client.get('/flatpage_root/flatpage/')
+ self.assertEquals(response.status_code, 200)
+ self.assertContains(response, "<p>Isn't it flat!</p>")
+
+ def test_view_non_existent_flatpage(self):
+ "A non-existent flatpage raises 404 when served through a view"
+ response = self.client.get('/flatpage_root/no_such_flatpage/')
+ self.assertEquals(response.status_code, 404)
+
+ def test_view_authenticated_flatpage(self):
+ "A flatpage served through a view can require authentication"
+ response = self.client.get('/flatpage_root/sekrit/')
+ self.assertRedirects(response, '/accounts/login/?next=/flatpage_root/sekrit/')
+ User.objects.create_user('testuser', 'test@example.com', 's3krit')
+ self.client.login(username='testuser',password='s3krit')
+ response = self.client.get('/flatpage_root/sekrit/')
+ self.assertEquals(response.status_code, 200)
+ self.assertContains(response, "<p>Isn't it sekrit!</p>")
+
+ def test_fallback_flatpage(self):
+ "A fallback flatpage won't be served if the middleware is disabled"
+ response = self.client.get('/flatpage/')
+ self.assertEquals(response.status_code, 404)
+
+ def test_fallback_non_existent_flatpage(self):
+ "A non-existent flatpage won't be served if the fallback middlware is disabled"
+ response = self.client.get('/no_such_flatpage/')
+ self.assertEquals(response.status_code, 404)
+
+ def test_view_flatpage_special_chars(self):
+ "A flatpage with special chars in the URL can be served through a view"
+ fp = FlatPage.objects.create(
+ url="/some.very_special~chars-here/",
+ title="A very special page",
+ content="Isn't it special!",
+ enable_comments=False,
+ registration_required=False,
+ )
+ fp.sites.add(1)
+
+ response = self.client.get('/flatpage_root/some.very_special~chars-here/')
+ self.assertEquals(response.status_code, 200)
+ self.assertContains(response, "<p>Isn't it special!</p>")
diff --git a/django/contrib/flatpages/views.py b/django/contrib/flatpages/views.py
index 336600328d..88ef4da65e 100644
--- a/django/contrib/flatpages/views.py
+++ b/django/contrib/flatpages/views.py
@@ -13,10 +13,13 @@ DEFAULT_TEMPLATE = 'flatpages/default.html'
# when a 404 is raised, which often means CsrfViewMiddleware.process_view
# has not been called even if CsrfViewMiddleware is installed. So we need
# to use @csrf_protect, in case the template needs {% csrf_token %}.
-@csrf_protect
+# However, we can't just wrap this view; if no matching flatpage exists,
+# or a redirect is required for authentication, the 404 needs to be returned
+# without any CSRF checks. Therefore, we only
+# CSRF protect the internal implementation.
def flatpage(request, url):
"""
- Flat page view.
+ Public interface to the flat page view.
Models: `flatpages.flatpages`
Templates: Uses the template defined by the ``template_name`` field,
@@ -30,6 +33,13 @@ def flatpage(request, url):
if not url.startswith('/'):
url = "/" + url
f = get_object_or_404(FlatPage, url__exact=url, sites__id__exact=settings.SITE_ID)
+ return render_flatpage(request, f)
+
+@csrf_protect
+def render_flatpage(request, f):
+ """
+ Internal interface to the flat page view.
+ """
# If registration is required for accessing this page, and the user isn't
# logged in, redirect to the login page.
if f.registration_required and not request.user.is_authenticated():