Skip to content

Commit

Permalink
Merge pull request #1138 from ambv/issue20126
Browse files Browse the repository at this point in the history
Fixed #20126 -- XViewMiddleware moved to django.contrib.admindocs.middleware
  • Loading branch information
mjtamlyn committed May 19, 2013
2 parents a7e2835 + 6607626 commit 3d5a546
Show file tree
Hide file tree
Showing 12 changed files with 124 additions and 41 deletions.
23 changes: 23 additions & 0 deletions django/contrib/admindocs/middleware.py
@@ -0,0 +1,23 @@
from django.conf import settings
from django import http

class XViewMiddleware(object):
"""
Adds an X-View header to internal HEAD requests -- used by the documentation system.
"""
def process_view(self, request, view_func, view_args, view_kwargs):
"""
If the request method is HEAD and either the IP is internal or the
user is a logged-in staff member, quickly return with an x-header
indicating the view function. This is used by the documentation module
to lookup the view function for an arbitrary page.
"""
assert hasattr(request, 'user'), (
"The XView middleware requires authentication middleware to be "
"installed. Edit your MIDDLEWARE_CLASSES setting to insert "
"'django.contrib.auth.middleware.AuthenticationMiddleware'.")
if request.method == 'HEAD' and (request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS or
(request.user.is_active and request.user.is_staff)):
response = http.HttpResponse()
response['X-View'] = "%s.%s" % (view_func.__module__, view_func.__name__)
return response
27 changes: 5 additions & 22 deletions django/middleware/doc.py
@@ -1,23 +1,6 @@
from django.conf import settings """XViewMiddleware has been moved to django.contrib.admindocs.middleware."""
from django import http


class XViewMiddleware(object): import warnings
""" warnings.warn(__doc__, PendingDeprecationWarning, stacklevel=2)
Adds an X-View header to internal HEAD requests -- used by the documentation system.
""" from django.contrib.admindocs.middleware import XViewMiddleware
def process_view(self, request, view_func, view_args, view_kwargs):
"""
If the request method is HEAD and either the IP is internal or the
user is a logged-in staff member, quickly return with an x-header
indicating the view function. This is used by the documentation module
to lookup the view function for an arbitrary page.
"""
assert hasattr(request, 'user'), (
"The XView middleware requires authentication middleware to be "
"installed. Edit your MIDDLEWARE_CLASSES setting to insert "
"'django.contrib.auth.middleware.AuthenticationMiddleware'.")
if request.method == 'HEAD' and (request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS or
(request.user.is_active and request.user.is_staff)):
response = http.HttpResponse()
response['X-View'] = "%s.%s" % (view_func.__module__, view_func.__name__)
return response
10 changes: 5 additions & 5 deletions docs/ref/contrib/admin/admindocs.txt
Expand Up @@ -31,7 +31,7 @@ the following:
* **Optional:** Linking to templates requires the :setting:`ADMIN_FOR` * **Optional:** Linking to templates requires the :setting:`ADMIN_FOR`
setting to be configured. setting to be configured.
* **Optional:** Using the admindocs bookmarklets requires the * **Optional:** Using the admindocs bookmarklets requires the
:mod:`XViewMiddleware<django.middleware.doc>` to be installed. :mod:`XViewMiddleware<django.contrib.admindocs.middleware>` to be installed.


Once those steps are complete, you can start browsing the documentation by Once those steps are complete, you can start browsing the documentation by
going to your admin interface and clicking the "Documentation" link in the going to your admin interface and clicking the "Documentation" link in the
Expand Down Expand Up @@ -156,7 +156,7 @@ Edit this object
Using these bookmarklets requires that you are either logged into the Using these bookmarklets requires that you are either logged into the
:mod:`Django admin <django.contrib.admin>` as a :mod:`Django admin <django.contrib.admin>` as a
:class:`~django.contrib.auth.models.User` with :class:`~django.contrib.auth.models.User` with
:attr:`~django.contrib.auth.models.User.is_staff` set to `True`, or :attr:`~django.contrib.auth.models.User.is_staff` set to `True`, or that the
that the :mod:`django.middleware.doc` middleware and :mod:`XViewMiddleware <django.contrib.admindocs.middleware>` is installed and
:mod:`XViewMiddleware <django.middleware.doc>` are installed and you you are accessing the site from an IP address listed in
are accessing the site from an IP address listed in :setting:`INTERNAL_IPS`. :setting:`INTERNAL_IPS`.
13 changes: 0 additions & 13 deletions docs/ref/middleware.txt
Expand Up @@ -71,19 +71,6 @@ Adds a few conveniences for perfectionists:
* Sends broken link notification emails to :setting:`MANAGERS` (see * Sends broken link notification emails to :setting:`MANAGERS` (see
:doc:`/howto/error-reporting`). :doc:`/howto/error-reporting`).


View metadata middleware
------------------------

.. module:: django.middleware.doc
:synopsis: Middleware to help your app self-document.

.. class:: XViewMiddleware

Sends custom ``X-View`` HTTP headers to HEAD requests that come from IP
addresses defined in the :setting:`INTERNAL_IPS` setting. This is used by
Django's :doc:`automatic documentation system </ref/contrib/admin/admindocs>`.
Depends on :class:`~django.contrib.auth.middleware.AuthenticationMiddleware`.

GZip middleware GZip middleware
--------------- ---------------


Expand Down
2 changes: 1 addition & 1 deletion docs/ref/settings.txt
Expand Up @@ -1243,7 +1243,7 @@ Default: ``()`` (Empty tuple)
A tuple of IP addresses, as strings, that: A tuple of IP addresses, as strings, that:


* See debug comments, when :setting:`DEBUG` is ``True`` * See debug comments, when :setting:`DEBUG` is ``True``
* Receive X headers if the ``XViewMiddleware`` is installed (see * Receive X headers in admindocs if the ``XViewMiddleware`` is installed (see
:doc:`/topics/http/middleware`) :doc:`/topics/http/middleware`)


.. setting:: LANGUAGE_CODE .. setting:: LANGUAGE_CODE
Expand Down
4 changes: 4 additions & 0 deletions docs/releases/1.6.txt
Expand Up @@ -502,6 +502,10 @@ Miscellaneous
ineffective so it has been removed, along with its generic implementation, ineffective so it has been removed, along with its generic implementation,
previously available in ``django.core.xheaders``. previously available in ``django.core.xheaders``.


* The ``XViewMiddleware`` has been moved from ``django.middleware.doc`` to
``django.contrib.admindocs.middleware`` because it is an implementation
detail of admindocs, proven not to be reusable in general.

Features deprecated in 1.6 Features deprecated in 1.6
========================== ==========================


Expand Down
Empty file added tests/admin_docs/__init__.py
Empty file.
17 changes: 17 additions & 0 deletions tests/admin_docs/fixtures/data.xml
@@ -0,0 +1,17 @@
<?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>
</django-objects>
Empty file added tests/admin_docs/models.py
Empty file.
45 changes: 45 additions & 0 deletions tests/admin_docs/tests.py
@@ -0,0 +1,45 @@
from django.contrib.auth.models import User
from django.test import TestCase
from django.test.utils import override_settings


@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',))
class XViewMiddlewareTest(TestCase):
fixtures = ['data.xml']
urls = 'admin_docs.urls'

def test_xview_func(self):
user = User.objects.get(username='super')
response = self.client.head('/xview/func/')
self.assertFalse('X-View' in response)
self.client.login(username='super', password='secret')
response = self.client.head('/xview/func/')
self.assertTrue('X-View' in response)
self.assertEqual(response['X-View'], 'admin_docs.views.xview')
user.is_staff = False
user.save()
response = self.client.head('/xview/func/')
self.assertFalse('X-View' in response)
user.is_staff = True
user.is_active = False
user.save()
response = self.client.head('/xview/func/')
self.assertFalse('X-View' in response)

def test_xview_class(self):
user = User.objects.get(username='super')
response = self.client.head('/xview/class/')
self.assertFalse('X-View' in response)
self.client.login(username='super', password='secret')
response = self.client.head('/xview/class/')
self.assertTrue('X-View' in response)
self.assertEqual(response['X-View'], 'admin_docs.views.XViewClass')
user.is_staff = False
user.save()
response = self.client.head('/xview/class/')
self.assertFalse('X-View' in response)
user.is_staff = True
user.is_active = False
user.save()
response = self.client.head('/xview/class/')
self.assertFalse('X-View' in response)
11 changes: 11 additions & 0 deletions tests/admin_docs/urls.py
@@ -0,0 +1,11 @@
# coding: utf-8
from __future__ import absolute_import

from django.conf.urls import patterns

from . import views

urlpatterns = patterns('',
(r'^xview/func/$', views.xview_dec(views.xview)),
(r'^xview/class/$', views.xview_dec(views.XViewClass.as_view())),
)
13 changes: 13 additions & 0 deletions tests/admin_docs/views.py
@@ -0,0 +1,13 @@
from django.http import HttpResponse
from django.utils.decorators import decorator_from_middleware
from django.views.generic import View
from django.contrib.admindocs.middleware import XViewMiddleware

xview_dec = decorator_from_middleware(XViewMiddleware)

def xview(request):
return HttpResponse()

class XViewClass(View):
def get(self, request):
return HttpResponse()

0 comments on commit 3d5a546

Please sign in to comment.