Merge pull request #1820 from carltongibson/login-dropdown

Hide login link in browsable API if the login view is not registered.
This commit is contained in:
Tom Christie 2014-09-05 09:07:14 +01:00
commit 2e632e5af2
7 changed files with 123 additions and 23 deletions

View File

@ -5,14 +5,14 @@
<html> <html>
<head> <head>
{% block head %} {% block head %}
{% block meta %} {% block meta %}
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta name="robots" content="NONE,NOARCHIVE" /> <meta name="robots" content="NONE,NOARCHIVE" />
{% endblock %} {% endblock %}
<title>{% block title %}Django REST framework{% endblock %}</title> <title>{% block title %}Django REST framework{% endblock %}</title>
{% block style %} {% block style %}
{% block bootstrap_theme %} {% block bootstrap_theme %}
<link rel="stylesheet" type="text/css" href="{% static "rest_framework/css/bootstrap.min.css" %}"/> <link rel="stylesheet" type="text/css" href="{% static "rest_framework/css/bootstrap.min.css" %}"/>
@ -21,7 +21,7 @@
<link rel="stylesheet" type="text/css" href="{% static "rest_framework/css/prettify.css" %}"/> <link rel="stylesheet" type="text/css" href="{% static "rest_framework/css/prettify.css" %}"/>
<link rel="stylesheet" type="text/css" href="{% static "rest_framework/css/default.css" %}"/> <link rel="stylesheet" type="text/css" href="{% static "rest_framework/css/default.css" %}"/>
{% endblock %} {% endblock %}
{% endblock %} {% endblock %}
</head> </head>
@ -44,17 +44,9 @@
<ul class="nav pull-right"> <ul class="nav pull-right">
{% block userlinks %} {% block userlinks %}
{% if user.is_authenticated %} {% if user.is_authenticated %}
<li class="dropdown"> {% optional_logout request user %}
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
{{ user }}
<b class="caret"></b>
</a>
<ul class="dropdown-menu">
<li>{% optional_logout request %}</li>
</ul>
</li>
{% else %} {% else %}
<li>{% optional_login request %}</li> {% optional_login request %}
{% endif %} {% endif %}
{% endblock %} {% endblock %}
</ul> </ul>
@ -85,7 +77,7 @@
<div class="btn-group format-selection"> <div class="btn-group format-selection">
<a class="btn btn-primary js-tooltip" href='{{ request.get_full_path }}' <a class="btn btn-primary js-tooltip" href='{{ request.get_full_path }}'
rel="nofollow" title="Make a GET request on the {{ name }} resource">GET</a> rel="nofollow" title="Make a GET request on the {{ name }} resource">GET</a>
<button class="btn btn-primary dropdown-toggle js-tooltip" data-toggle="dropdown" <button class="btn btn-primary dropdown-toggle js-tooltip" data-toggle="dropdown"
title="Specify a format for the GET request"> title="Specify a format for the GET request">
<span class="caret"></span> <span class="caret"></span>
@ -144,7 +136,7 @@
</div> </div>
{% if display_edit_forms %} {% if display_edit_forms %}
{% if post_form or raw_data_post_form %} {% if post_form or raw_data_post_form %}
<div {% if post_form %}class="tabbable"{% endif %}> <div {% if post_form %}class="tabbable"{% endif %}>
{% if post_form %} {% if post_form %}
@ -190,7 +182,7 @@
</div> </div>
</div> </div>
{% endif %} {% endif %}
{% if put_form or raw_data_put_form or raw_data_patch_form %} {% if put_form or raw_data_put_form or raw_data_patch_form %}
<div {% if put_form %}class="tabbable"{% endif %}> <div {% if put_form %}class="tabbable"{% endif %}>
{% if put_form %} {% if put_form %}
@ -246,7 +238,7 @@
{% endif %} {% endif %}
</div> </div>
<!-- END Content --> <!-- END Content -->
<footer> <footer>
{% block footer %} {% block footer %}
<p>Sponsored by <a href="http://dabapps.com/">DabApps</a>.</p> <p>Sponsored by <a href="http://dabapps.com/">DabApps</a>.</p>

View File

@ -41,22 +41,31 @@ def optional_login(request):
except NoReverseMatch: except NoReverseMatch:
return '' return ''
snippet = "<a href='%s?next=%s'>Log in</a>" % (login_url, escape(request.path)) snippet = "<li><a href='{href}?next={next}'>Log in</a></li>".format(href=login_url, next=escape(request.path))
return snippet return snippet
@register.simple_tag @register.simple_tag
def optional_logout(request): def optional_logout(request, user):
""" """
Include a logout snippet if REST framework's logout view is in the URLconf. Include a logout snippet if REST framework's logout view is in the URLconf.
""" """
try: try:
logout_url = reverse('rest_framework:logout') logout_url = reverse('rest_framework:logout')
except NoReverseMatch: except NoReverseMatch:
return '' return '<li class="navbar-text">{user}</li>'.format(user=user)
snippet = "<a href='%s?next=%s'>Log out</a>" % (logout_url, escape(request.path)) snippet = """<li class="dropdown">
return snippet <a href="#" class="dropdown-toggle" data-toggle="dropdown">
{user}
<b class="caret"></b>
</a>
<ul class="dropdown-menu">
<li><a href='{href}?next={next}'>Log out</a></li>
</ul>
</li>"""
return snippet.format(user=user, href=logout_url, next=escape(request.path))
@register.simple_tag @register.simple_tag

View File

View File

@ -0,0 +1,10 @@
from __future__ import unicode_literals
from django.conf.urls import patterns, url, include
from .views import MockView
urlpatterns = patterns(
'',
(r'^$', MockView.as_view()),
url(r'^auth/', include('rest_framework.urls', namespace='rest_framework')),
)

View File

@ -0,0 +1,9 @@
from __future__ import unicode_literals
from django.conf.urls import patterns
from .views import MockView
urlpatterns = patterns(
'',
(r'^$', MockView.as_view()),
)

View File

@ -0,0 +1,65 @@
from __future__ import unicode_literals
from django.contrib.auth.models import User
from django.test import TestCase
from rest_framework.test import APIClient
class DropdownWithAuthTests(TestCase):
"""Tests correct dropdown behaviour with Auth views enabled."""
urls = 'tests.browsable_api.auth_urls'
def setUp(self):
self.client = APIClient(enforce_csrf_checks=True)
self.username = 'john'
self.email = 'lennon@thebeatles.com'
self.password = 'password'
self.user = User.objects.create_user(self.username, self.email, self.password)
def tearDown(self):
self.client.logout()
def test_name_shown_when_logged_in(self):
self.client.login(username=self.username, password=self.password)
response = self.client.get('/')
self.assertContains(response, 'john')
def test_logout_shown_when_logged_in(self):
self.client.login(username=self.username, password=self.password)
response = self.client.get('/')
self.assertContains(response, '>Log out<')
def test_login_shown_when_logged_out(self):
response = self.client.get('/')
self.assertContains(response, '>Log in<')
class NoDropdownWithoutAuthTests(TestCase):
"""Tests correct dropdown behaviour with Auth views NOT enabled."""
urls = 'tests.browsable_api.no_auth_urls'
def setUp(self):
self.client = APIClient(enforce_csrf_checks=True)
self.username = 'john'
self.email = 'lennon@thebeatles.com'
self.password = 'password'
self.user = User.objects.create_user(self.username, self.email, self.password)
def tearDown(self):
self.client.logout()
def test_name_shown_when_logged_in(self):
self.client.login(username=self.username, password=self.password)
response = self.client.get('/')
self.assertContains(response, 'john')
def test_dropdown_not_shown_when_logged_in(self):
self.client.login(username=self.username, password=self.password)
response = self.client.get('/')
self.assertNotContains(response, '<li class="dropdown">')
def test_dropdown_not_shown_when_logged_out(self):
response = self.client.get('/')
self.assertNotContains(response, '<li class="dropdown">')

View File

@ -0,0 +1,15 @@
from __future__ import unicode_literals
from rest_framework.views import APIView
from rest_framework import authentication
from rest_framework import renderers
from rest_framework.response import Response
class MockView(APIView):
authentication_classes = (authentication.SessionAuthentication,)
renderer_classes = (renderers.BrowsableAPIRenderer,)
def get(self, request):
return Response({'a': 1, 'b': 2, 'c': 3})