diff --git a/django/contrib/admin/forms.py b/django/contrib/admin/forms.py new file mode 100644 index 0000000000..5a245a39af --- /dev/null +++ b/django/contrib/admin/forms.py @@ -0,0 +1,43 @@ +from django import forms + +from django.contrib.auth import authenticate +from django.contrib.auth.forms import AuthenticationForm +from django.contrib.auth.models import User + +from django.utils.translation import ugettext_lazy, ugettext as _ + +ERROR_MESSAGE = ugettext_lazy("Please enter a correct username and password. " + "Note that both fields are case-sensitive.") + +class AdminAuthenticationForm(AuthenticationForm): + """ + A custom authentication form used in the admin app. + + """ + this_is_the_login_form = forms.BooleanField(widget=forms.HiddenInput, initial=1, + error_messages={'required': ugettext_lazy("Please log in again, because your session has expired.")}) + + def clean(self): + username = self.cleaned_data.get('username') + password = self.cleaned_data.get('password') + message = ERROR_MESSAGE + + if username and password: + self.user_cache = authenticate(username=username, password=password) + if self.user_cache is None: + if username is not None and u'@' in username: + # Mistakenly entered e-mail address instead of username? Look it up. + try: + user = User.objects.get(email=username) + except (User.DoesNotExist, User.MultipleObjectsReturned): + # Nothing to do here, moving along. + pass + else: + if user.check_password(password): + message = _("Your e-mail address is not your username." + " Try '%s' instead.") % user.username + raise forms.ValidationError(message) + elif not self.user_cache.is_active or not self.user_cache.is_staff: + raise forms.ValidationError(message) + self.check_for_test_cookie() + return self.cleaned_data diff --git a/django/contrib/admin/sites.py b/django/contrib/admin/sites.py index b995c0f2df..0aa730f79e 100644 --- a/django/contrib/admin/sites.py +++ b/django/contrib/admin/sites.py @@ -1,8 +1,8 @@ import re from django import http, template -from django.contrib.admin import ModelAdmin -from django.contrib.admin import actions -from django.contrib.auth import authenticate, login +from django.contrib.admin import ModelAdmin, actions +from django.contrib.admin.forms import AdminAuthenticationForm, ERROR_MESSAGE +from django.contrib.auth import REDIRECT_FIELD_NAME, authenticate, login from django.views.decorators.csrf import csrf_protect from django.db.models.base import ModelBase from django.core.exceptions import ImproperlyConfigured @@ -15,7 +15,6 @@ from django.utils.translation import ugettext_lazy, ugettext as _ from django.views.decorators.cache import never_cache from django.conf import settings -ERROR_MESSAGE = ugettext_lazy("Please enter a correct username and password. Note that both fields are case-sensitive.") LOGIN_FORM_KEY = 'this_is_the_login_form' class AlreadyRegistered(Exception): @@ -32,7 +31,7 @@ class AdminSite(object): functions that present a full admin interface for the collection of registered models. """ - + login_form = None index_template = None app_index_template = None login_template = None @@ -127,12 +126,12 @@ class AdminSite(object): """ return self._global_actions[name] + @property def actions(self): """ Get all the enabled actions as an iterable of (name, func). """ return self._actions.iteritems() - actions = property(actions) def has_permission(self, request): """ @@ -240,9 +239,9 @@ class AdminSite(object): ) return urlpatterns + @property def urls(self): return self.get_urls(), self.app_name, self.name - urls = property(urls) def password_change(self, request): """ @@ -254,18 +253,22 @@ class AdminSite(object): else: url = reverse('admin:password_change_done', current_app=self.name) defaults = { + 'current_app': self.name, 'post_change_redirect': url } if self.password_change_template is not None: defaults['template_name'] = self.password_change_template return password_change(request, **defaults) - def password_change_done(self, request): + def password_change_done(self, request, extra_context=None): """ Displays the "success" page after a password change. """ from django.contrib.auth.views import password_change_done - defaults = {} + defaults = { + 'current_app': self.name, + 'extra_context': extra_context or {}, + } if self.password_change_done_template is not None: defaults['template_name'] = self.password_change_done_template return password_change_done(request, **defaults) @@ -283,69 +286,44 @@ class AdminSite(object): from django.views.i18n import null_javascript_catalog as javascript_catalog return javascript_catalog(request, packages='django.conf') - def logout(self, request): + @never_cache + def logout(self, request, extra_context=None): """ Logs out the user for the given HttpRequest. This should *not* assume the user is already logged in. """ from django.contrib.auth.views import logout - defaults = {} + defaults = { + 'current_app': self.name, + 'extra_context': extra_context or {}, + } if self.logout_template is not None: defaults['template_name'] = self.logout_template return logout(request, **defaults) - logout = never_cache(logout) - def login(self, request): + @never_cache + def login(self, request, extra_context=None): """ Displays the login form for the given HttpRequest. """ - from django.contrib.auth.models import User - - # If this isn't already the login page, display it. - if LOGIN_FORM_KEY not in request.POST: - if request.POST: - message = _("Please log in again, because your session has expired.") - else: - message = "" - return self.display_login_form(request, message) - - # Check that the user accepts cookies. - if not request.session.test_cookie_worked(): - message = _("Looks like your browser isn't configured to accept cookies. Please enable cookies, reload this page, and try again.") - return self.display_login_form(request, message) - else: - request.session.delete_test_cookie() - - # Check the password. - username = request.POST.get('username', None) - password = request.POST.get('password', None) - user = authenticate(username=username, password=password) - if user is None: - message = ERROR_MESSAGE - if username is not None and u'@' in username: - # Mistakenly entered e-mail address instead of username? Look it up. - try: - user = User.objects.get(email=username) - except (User.DoesNotExist, User.MultipleObjectsReturned): - message = _("Usernames cannot contain the '@' character.") - else: - if user.check_password(password): - message = _("Your e-mail address is not your username." - " Try '%s' instead.") % user.username - else: - message = _("Usernames cannot contain the '@' character.") - return self.display_login_form(request, message) - - # The user data is correct; log in the user in and continue. - else: - if user.is_active and user.is_staff: - login(request, user) - return http.HttpResponseRedirect(request.get_full_path()) - else: - return self.display_login_form(request, ERROR_MESSAGE) - login = never_cache(login) + from django.contrib.auth.views import login + context = { + 'title': _('Log in'), + 'root_path': self.root_path, + 'app_path': request.get_full_path(), + REDIRECT_FIELD_NAME: request.get_full_path(), + } + context.update(extra_context or {}) + defaults = { + 'extra_context': context, + 'current_app': self.name, + 'authentication_form': self.login_form or AdminAuthenticationForm, + 'template_name': self.login_template or 'admin/login.html', + } + return login(request, **defaults) + @never_cache def index(self, request, extra_context=None): """ Displays the main admin index page, which lists all of the installed @@ -396,21 +374,6 @@ class AdminSite(object): return render_to_response(self.index_template or 'admin/index.html', context, context_instance=context_instance ) - index = never_cache(index) - - def display_login_form(self, request, error_message='', extra_context=None): - request.session.set_test_cookie() - context = { - 'title': _('Log in'), - 'app_path': request.get_full_path(), - 'error_message': error_message, - 'root_path': self.root_path, - } - context.update(extra_context or {}) - context_instance = template.RequestContext(request, current_app=self.name) - return render_to_response(self.login_template or 'admin/login.html', context, - context_instance=context_instance - ) def app_index(self, request, app_label, extra_context=None): user = request.user diff --git a/django/contrib/admin/templates/admin/login.html b/django/contrib/admin/templates/admin/login.html index df6212697a..ad7b14e965 100644 --- a/django/contrib/admin/templates/admin/login.html +++ b/django/contrib/admin/templates/admin/login.html @@ -12,17 +12,31 @@ {% block breadcrumbs %}{% endblock %} {% block content %} -{% if error_message %} -
{{ error_message }}
+{% if form.errors and not form.non_field_errors and not form.this_is_the_login_form.errors %} ++{% blocktrans count form.errors.items|length as counter %}Please correct the error below.{% plural %}Please correct the errors below.{% endblocktrans %} +
{% endif %} + +{% if form.non_field_errors or form.this_is_the_login_form.errors %} +{% for error in form.non_field_errors|add:form.this_is_the_login_form.errors %} ++ {{ error }} +
+{% endfor %} +{% endif %} +