mirror of
https://github.com/jazzband/django-admin2.git
synced 2026-04-25 17:14:45 +00:00
Merge pull request #146 from d1ffuz0r/issue_101
#101 #103 added change password, login, logout views
This commit is contained in:
commit
9e2349be7f
10 changed files with 271 additions and 10 deletions
|
|
@ -125,6 +125,18 @@ class Admin2(object):
|
|||
view=self.index_view.as_view(**self.get_index_kwargs()),
|
||||
name='dashboard'
|
||||
),
|
||||
url(regex='^auth/user/(?P<pk>\d+)/update/password/$',
|
||||
view=views.PasswordChangeView.as_view(),
|
||||
name='password_change'
|
||||
),
|
||||
url(regex='^password_change_done/$',
|
||||
view=views.PasswordChangeDoneView.as_view(),
|
||||
name='password_change_done'
|
||||
),
|
||||
url(regex='^logout/$',
|
||||
view=views.LogoutView.as_view(),
|
||||
name='logout'
|
||||
),
|
||||
url(
|
||||
regex=r'^(?P<app_label>\w+)/$',
|
||||
view=self.app_index_view.as_view(**self.get_app_index_kwargs()),
|
||||
|
|
@ -136,6 +148,7 @@ class Admin2(object):
|
|||
name='api-index'
|
||||
),
|
||||
)
|
||||
|
||||
for model, model_admin in self.registry.iteritems():
|
||||
model_options = utils.model_options(model)
|
||||
urlpatterns += patterns('',
|
||||
|
|
|
|||
25
djadmin2/templates/admin2/bootstrap/auth/login.html
Normal file
25
djadmin2/templates/admin2/bootstrap/auth/login.html
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
{% extends "admin2/bootstrap/base.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
{% load admin2_tags %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="span12">
|
||||
{% if form.errors %}
|
||||
<p class="error-note">
|
||||
{% blocktrans count counter=form.errors.items|length %}Please correct the error below.{% plural %}Please correct the errors below.{% endblocktrans %}
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<input type="hidden" name="this_is_the_login_form" value="1" />
|
||||
<input type="hidden" name="next" value="{{ next }}" />
|
||||
<button class="btn btn-small btn-success" type="submit">{% trans "Log in" %}</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock content %}
|
||||
18
djadmin2/templates/admin2/bootstrap/auth/logout.html
Normal file
18
djadmin2/templates/admin2/bootstrap/auth/logout.html
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
{% extends "admin2/bootstrap/base.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
{% load admin2_tags %}
|
||||
|
||||
{% block breadcrumbs %}
|
||||
<li><a href="{% url "admin2:dashboard" %}">Home</a> <span class="divider">/</span></li>
|
||||
<li class="active">{% trans "Logout" %}</li>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="span12">
|
||||
<p>{% trans "Thanks for spending some quality time with the Web site today." %}</p>
|
||||
<p><p><a href="{% url 'admin2:dashboard' %}">{% trans 'Log in again' %}</a></p></p>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
{% extends "admin2/bootstrap/base.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
{% load admin2_tags %}
|
||||
|
||||
{% block title %}{% trans 'Password change successful' %}{% endblock %}
|
||||
{% block page_title %}{% trans 'Password change successful' %}{% endblock %}
|
||||
|
||||
{% block breadcrumbs %}
|
||||
<li><a href="{% url "admin2:dashboard" %}">Home</a> <span class="divider">/</span></li>
|
||||
<li class="active">{% trans "Password change successful" %}</li>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="span12">
|
||||
<p>{% trans 'Your password was changed.' %}</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
{% extends "admin2/bootstrap/base.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
{% load admin2_tags %}
|
||||
|
||||
{% block page_title %}{% trans "Password change" %}: {{ form.user }}{% endblock %}
|
||||
|
||||
{% block breadcrumbs %}
|
||||
<li><a href="{% url "admin2:dashboard" %}">Home</a> <span class="divider">/</span></li>
|
||||
<li class="active">{% trans "Password change" %} <span class="divider">/</span></li>
|
||||
<li class="active">{{ form.user }}</li>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="span12">
|
||||
<p>Please enter your old password, for security's sake, and then enter your new password twice so we can verify you typed it in correctly.</p>
|
||||
|
||||
{% if form.errors %}
|
||||
<p class="error-note">
|
||||
{% blocktrans count counter=form.errors.items|length %}Please correct the error below.{% plural %}Please correct the errors below.{% endblocktrans %}
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<button class="btn btn-small btn-success" type="submit" name="_save">{% trans "Change my password" %}</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock content %}
|
||||
|
|
@ -13,16 +13,18 @@
|
|||
<div class="navbar navbar-inverse navbar-static-top">
|
||||
<div class="navbar-inner">
|
||||
<div class="container-fluid">
|
||||
<a class="brand" href="{% url 'admin2:dashboard' %}">Django-Admin2</a>
|
||||
<a tabindex="1" class="brand" href="{% url 'admin2:dashboard' %}">Django-Admin2</a>
|
||||
|
||||
<ul class="nav pull-right">
|
||||
<li><a tabindex="-1" href="{% url 'admin2:api-index' %}">{% trans "API" %}</a></li>
|
||||
<li><a tabindex="2" href="{% url 'admin2:api-index' %}">{% trans "API" %}</a></li>
|
||||
|
||||
{% if docsroot %}
|
||||
<li><a href="{{ docsroot }}">{% trans 'Documentation' %}</a></li>
|
||||
<li><a tabindex="3" href="{{ docsroot }}">{% trans 'Documentation' %}</a></li>
|
||||
{% endif %}
|
||||
|
||||
{% if user.is_authenticated %}
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
|
||||
<a tabindex="4" href="#" class="dropdown-toggle" data-toggle="dropdown">
|
||||
{% if user.get_full_name %}
|
||||
{{ user.get_full_name }}
|
||||
{% else %}
|
||||
|
|
@ -34,11 +36,12 @@
|
|||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
{% if user.has_usable_password %}
|
||||
<li><a tabindex="-1" href="TODO">{% trans "Change password" %}</a></li>
|
||||
<li><a href="{% url 'admin2:password_change' user.id %}">{% trans "Change password" %}</a></li>
|
||||
{% endif %}
|
||||
<li><a tabindex="-1" href="TODO">{% trans "Log out" %}</a></li>
|
||||
<li><a href="{% url 'admin2:logout' %}">{% trans "Log out" %}</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -32,4 +32,4 @@ class Admin2Test(TestCase):
|
|||
|
||||
def test_get_urls(self):
|
||||
self.admin2.register(Thing)
|
||||
self.assertEquals(5, len(self.admin2.get_urls()))
|
||||
self.assertEquals(8, len(self.admin2.get_urls()))
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@ import os
|
|||
|
||||
from django.contrib.auth.views import redirect_to_login
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.core.urlresolvers import reverse, reverse_lazy
|
||||
from django.forms.models import modelform_factory
|
||||
|
||||
from django.http import HttpResponseRedirect
|
||||
from braces.views import AccessMixin
|
||||
|
||||
from . import constants, permissions
|
||||
|
|
@ -14,6 +14,7 @@ from .utils import admin2_urlname, model_options
|
|||
class PermissionMixin(AccessMixin):
|
||||
do_not_call_in_templates = True
|
||||
permission_classes = (permissions.IsStaffPermission,)
|
||||
login_url = reverse_lazy('admin2:dashboard')
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.permissions = [
|
||||
|
|
@ -60,6 +61,8 @@ class Admin2Mixin(PermissionMixin):
|
|||
model_name = None
|
||||
app_label = None
|
||||
|
||||
index_path = reverse_lazy('admin2:dashboard')
|
||||
|
||||
def get_template_names(self):
|
||||
return [os.path.join(constants.ADMIN2_THEME_DIRECTORY, self.default_template_name)]
|
||||
|
||||
|
|
@ -74,6 +77,27 @@ class Admin2Mixin(PermissionMixin):
|
|||
return self.form_class
|
||||
return modelform_factory(self.get_model())
|
||||
|
||||
def is_user(self, request):
|
||||
return hasattr(request, 'user') and not (request.user.is_active and
|
||||
request.user.is_staff)
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
|
||||
if self.is_user(request):
|
||||
from .views import LoginView
|
||||
|
||||
if request.path == reverse('admin2:logout'):
|
||||
return HttpResponseRedirect(self.index_path)
|
||||
|
||||
if request.path == self.index_path:
|
||||
extra = {
|
||||
'next': request.GET.get('next', self.index_path)
|
||||
}
|
||||
return LoginView().dispatch(request, extra_context=extra,
|
||||
*args, **kwargs)
|
||||
|
||||
return super(Admin2Mixin, self).dispatch(request, *args, **kwargs)
|
||||
|
||||
|
||||
class AdminModel2Mixin(Admin2Mixin):
|
||||
model_admin = None
|
||||
|
|
|
|||
|
|
@ -1,4 +1,10 @@
|
|||
from django.core.urlresolvers import reverse
|
||||
from django.contrib.admin.forms import AdminAuthenticationForm
|
||||
from django.contrib.auth.forms import (PasswordChangeForm,
|
||||
AdminPasswordChangeForm)
|
||||
from django.contrib.auth.views import (logout as auth_logout,
|
||||
login as auth_login)
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.core.urlresolvers import reverse, reverse_lazy
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.utils.encoding import force_text
|
||||
from django.utils.text import capfirst
|
||||
|
|
@ -128,3 +134,53 @@ class ModelDeleteView(AdminModel2Mixin, generic.DeleteView):
|
|||
'deletable_objects': collector.nested(_format_callback)
|
||||
})
|
||||
return context
|
||||
|
||||
|
||||
class PasswordChangeView(Admin2Mixin, generic.UpdateView):
|
||||
|
||||
default_template_name = 'auth/password_change_form.html'
|
||||
form_class = AdminPasswordChangeForm
|
||||
admin_form_class = PasswordChangeForm
|
||||
model = get_user_model()
|
||||
success_url = reverse_lazy('admin2:password_change_done')
|
||||
|
||||
def get_form_kwargs(self, **kwargs):
|
||||
data = {'user': self.get_object()}
|
||||
|
||||
if self.request.method in ('POST', 'PUT'):
|
||||
data.update({
|
||||
'data': self.request.POST
|
||||
})
|
||||
|
||||
return data
|
||||
|
||||
def get_form_class(self):
|
||||
if self.request.user == self.get_object():
|
||||
return self.admin_form_class
|
||||
return super(PasswordChangeView, self).get_form_class()
|
||||
|
||||
|
||||
class PasswordChangeDoneView(Admin2Mixin, generic.TemplateView):
|
||||
|
||||
default_template_name = 'auth/password_change_done.html'
|
||||
|
||||
|
||||
class LoginView(Admin2Mixin, generic.TemplateView):
|
||||
|
||||
default_template_name = 'auth/login.html'
|
||||
authentication_form = AdminAuthenticationForm
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
return auth_login(request,
|
||||
authentication_form=self.authentication_form,
|
||||
template_name=self.get_template_names(),
|
||||
*args, **kwargs)
|
||||
|
||||
|
||||
class LogoutView(Admin2Mixin, generic.TemplateView):
|
||||
|
||||
default_template_name = 'auth/logout.html'
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
return auth_logout(request, template_name=self.get_template_names(),
|
||||
*args, **kwargs)
|
||||
|
|
|
|||
|
|
@ -160,3 +160,72 @@ class PostDeleteActionTest(BaseIntegrationTest):
|
|||
post_data, follow=True)
|
||||
self.assertContains(response, "Successfully deleted 2 posts")
|
||||
|
||||
|
||||
class TestAuthViews(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.client = Client()
|
||||
self.user = get_user_model()(username='user', is_staff=True,
|
||||
is_superuser=True)
|
||||
self.user.set_password("password")
|
||||
self.user.save()
|
||||
|
||||
def test_login_required_redirect_to_index(self):
|
||||
index_path = reverse('admin2:dashboard') + '?next=/admin2/blog/post/'
|
||||
target_path = reverse('admin2:blog_post_index')
|
||||
self.assertRedirects(self.client.get(target_path), index_path)
|
||||
|
||||
def test_login_required_logined_successful(self):
|
||||
index_path = reverse('admin2:dashboard')
|
||||
self.client.login(username=self.user.username,
|
||||
password='password')
|
||||
self.assertContains(self.client.get(index_path),
|
||||
reverse('admin2:blog_post_index'))
|
||||
|
||||
def test_change_password_for_myself(self):
|
||||
self.client.login(username=self.user.username,
|
||||
password='password')
|
||||
request = self.client.post(reverse('admin2:password_change',
|
||||
kwargs={'pk': self.user.pk}),
|
||||
{'old_password': 'password',
|
||||
'new_password1': 'user',
|
||||
'new_password2': 'user'})
|
||||
self.assertRedirects(request, reverse('admin2:password_change_done'))
|
||||
self.client.logout()
|
||||
|
||||
self.assertFalse(self.client.login(username=self.user.username,
|
||||
password='password'))
|
||||
self.assertTrue(self.client.login(username=self.user.username,
|
||||
password='user'))
|
||||
|
||||
def test_change_password(self):
|
||||
self.client.login(username=self.user.username,
|
||||
password='password')
|
||||
|
||||
new_user = get_user_model()(username='new_user')
|
||||
new_user.set_password("new_user")
|
||||
new_user.save()
|
||||
|
||||
request = self.client.post(reverse('admin2:password_change',
|
||||
kwargs={'pk': new_user.pk}),
|
||||
{'old_password': 'new_user',
|
||||
'password1': 'new_user_password',
|
||||
'password2': 'new_user_password'})
|
||||
self.assertRedirects(request, reverse('admin2:password_change_done'))
|
||||
self.client.logout()
|
||||
|
||||
self.assertFalse(self.client.login(username=new_user.username,
|
||||
password='new_user'))
|
||||
self.assertTrue(self.client.login(username=new_user.username,
|
||||
password='new_user_password'))
|
||||
|
||||
def test_logout(self):
|
||||
self.client.login(username=self.user.username,
|
||||
password='password')
|
||||
logout_path = reverse('admin2:logout')
|
||||
request = self.client.get(logout_path)
|
||||
self.assertContains(request, 'Log in again')
|
||||
|
||||
index_path = reverse('admin2:dashboard') + '?next=/admin2/blog/post/'
|
||||
target_path = reverse('admin2:blog_post_index')
|
||||
self.assertRedirects(self.client.get(target_path), index_path)
|
||||
|
|
|
|||
Loading…
Reference in a new issue