Mercurial > public > sg101
changeset 659:8e6b8ffe5f34
For issue #31, implement a forgot username feature.
author | Brian Neal <bgneal@gmail.com> |
---|---|
date | Sat, 11 May 2013 23:39:46 -0500 |
parents | 2adf01661ac5 |
children | 0dd84cff2477 |
files | accounts/forms.py accounts/tests/view_tests.py accounts/urls.py accounts/views.py sg101/templates/accounts/ajax_login_form.html sg101/templates/accounts/forgot_user_email.txt sg101/templates/accounts/login.html sg101/templates/accounts/username_query.html sg101/templates/accounts/username_sent.html |
diffstat | 9 files changed, 149 insertions(+), 23 deletions(-) [+] |
line wrap: on
line diff
--- a/accounts/forms.py Sat May 11 16:21:55 2013 -0500 +++ b/accounts/forms.py Sat May 11 23:39:46 2013 -0500 @@ -80,7 +80,7 @@ self._validation_error("A pending user with that email address already exists.", email) elif IllegalEmail.objects.filter(email=email).count(): self._validation_error("That email address is not allowed.", email) - + # email is ok return email @@ -141,7 +141,8 @@ }) subject = 'Registration Confirmation for ' + site.name - send_mail(subject, msg, admin_email, [self.cleaned_data['email']]) + send_mail(subject, msg, admin_email, [self.cleaned_data['email']], + defer=False) logging.info('Accounts/registration conf. email sent to %s for user %s; IP = %s', self.cleaned_data['email'], pending_user.username, self.ip) @@ -150,3 +151,32 @@ def _validation_error(self, msg, param=None): logging.error('Accounts/registration [%s]: %s (%s)', self.ip, msg, param) raise forms.ValidationError(msg) + + +class ForgotUsernameForm(forms.Form): + """Form used to recover lost username""" + email = forms.EmailField(widget=forms.TextInput(attrs={'class': 'text'})) + + def save(self): + """Email the username associated with the email address to the user.""" + email = self.cleaned_data['email'] + try: + user = User.objects.get(email=email) + except User.DoesNotExist: + # nothing to do; we don't tell the user as this gives away info + # about our database + return + + site = Site.objects.get_current() + admin_email = settings.ADMINS[0][1] + + subject = 'Forgotten username for %s' % site.name + msg = render_to_string('accounts/forgot_user_email.txt', { + 'user': user, + 'site': site, + 'admin_email': admin_email, + }) + send_mail(subject, msg, admin_email, [email], defer=False) + + logging.info('Forgotten username email sent to {} <{}>'.format( + user.username, email))
--- a/accounts/tests/view_tests.py Sat May 11 16:21:55 2013 -0500 +++ b/accounts/tests/view_tests.py Sat May 11 23:39:46 2013 -0500 @@ -6,6 +6,7 @@ from django.test import TestCase from django.core.urlresolvers import reverse +from django.core import mail from django.contrib.auth.models import User from django.contrib.auth.hashers import check_password @@ -252,3 +253,44 @@ self.assertTrue(datetime.datetime.now() - pending.date_joined < datetime.timedelta(minutes=1)) self.assertTrue(check_password('my_password', pending.password)) + + +class ForgotUsernameTest(TestCase): + + def setUp(self): + u = User.objects.create_user('existing_user', 'existing_user@example.com', 'pw') + u.save() + + def test_get_query_view(self): + """Test a simple get of the username query view""" + response = self.client.get(reverse('accounts-username_query')) + self.assertEqual(response.status_code, 200) + + def test_get_username_sent_view(self): + """Test a simple get of the username sent view""" + response = self.client.get(reverse('accounts-username_sent')) + self.assertEqual(response.status_code, 200) + + def test_invalid_email(self): + """Test form submittal of unknown email address.""" + response = self.client.post(reverse('accounts-username_query'), { + 'email': 'bad_address@example.com', + }, + follow=True) + + self.assertRedirects(response, reverse('accounts-username_sent')) + + self.assertEqual(len(mail.outbox), 0) + + def test_valid_email(self): + """Test form submittal of valid email address.""" + response = self.client.post(reverse('accounts-username_query'), { + 'email': 'existing_user@example.com', + }, + follow=True) + + self.assertRedirects(response, reverse('accounts-username_sent')) + + self.assertEqual(len(mail.outbox), 1) + if len(mail.outbox): + self.assertTrue(mail.outbox[0].subject.startswith('Forgotten username'))
--- a/accounts/urls.py Sat May 11 16:21:55 2013 -0500 +++ b/accounts/urls.py Sat May 11 23:39:46 2013 -0500 @@ -1,6 +1,7 @@ """urls for the accounts application""" from django.conf.urls import patterns, url from django.conf import settings +from django.views.generic import TemplateView urlpatterns = patterns('accounts.views', url(r'^login/ajax/$', 'login_ajax', name='accounts-login_ajax'), @@ -43,5 +44,11 @@ 'django.contrib.auth.views.password_reset_complete', kwargs={'template_name': 'accounts/password_reset_complete.html'}, name='accounts-password_reset_success'), + url(r'^username/query/$', + 'accounts.views.username_query', + name='accounts-username_query'), + url(r'^username/sent/$', + TemplateView.as_view(template_name='accounts/username_sent.html'), + name='accounts-username_sent'), )
--- a/accounts/views.py Sat May 11 16:21:55 2013 -0500 +++ b/accounts/views.py Sat May 11 23:39:46 2013 -0500 @@ -5,10 +5,9 @@ import datetime import logging -from django.shortcuts import render_to_response +from django.shortcuts import render from django.template import RequestContext from django.template.loader import render_to_string -from django.contrib.auth.models import User from django.http import HttpResponse, HttpResponseRedirect from django.core.urlresolvers import reverse from django.conf import settings @@ -17,7 +16,7 @@ from django.utils import simplejson from accounts.models import PendingUser -from accounts.forms import RegisterForm +from accounts.forms import RegisterForm, ForgotUsernameForm from accounts import create_new_user from antispam.decorators import rate_limit @@ -37,10 +36,7 @@ else: form = RegisterForm() - return render_to_response('accounts/register.html', { - 'form': form, - }, - context_instance = RequestContext(request)) + return render(request, 'accounts/register.html', {'form': form}) ####################################################################### @@ -48,8 +44,7 @@ if request.user.is_authenticated(): return HttpResponseRedirect(settings.LOGIN_REDIRECT_URL) - return render_to_response('accounts/register_thanks.html', - context_instance = RequestContext(request)) + return render(request, 'accounts/register_thanks.html') ####################################################################### @@ -66,24 +61,21 @@ pending_user = PendingUser.objects.get(username = username) except PendingUser.DoesNotExist: logging.error('Accounts register_confirm [%s]: user does not exist: %s', ip, username) - return render_to_response('accounts/register_failure.html', { - 'username': username, - }, - context_instance = RequestContext(request)) + return render(request, + 'accounts/register_failure.html', + {'username': username}) if pending_user.key != key: logging.error('Accounts register_confirm [%s]: key error: %s', ip, username) - return render_to_response('accounts/register_failure.html', { - 'username': username, - }, - context_instance = RequestContext(request)) + return render(request, + 'accounts/register_failure.html', + {'username': username}) create_new_user(pending_user, ip) - return render_to_response('accounts/register_success.html', { - 'username': username, - }, - context_instance = RequestContext(request)) + return render(request, + 'accounts/register_success.html', + {'username': username}) ####################################################################### @@ -115,3 +107,20 @@ return HttpResponse(simplejson.dumps(response), content_type='application/json') + +####################################################################### + +def username_query(request): + """This view handles forgotten username queries.""" + if request.user.is_authenticated(): + return HttpResponseRedirect(settings.LOGIN_REDIRECT_URL) + + if request.method == 'POST': + form = ForgotUsernameForm(data=request.POST) + if form.is_valid(): + form.save() + return HttpResponseRedirect(reverse('accounts-username_sent')) + else: + form = ForgotUsernameForm() + + return render(request, 'accounts/username_query.html', {'form': form})
--- a/sg101/templates/accounts/ajax_login_form.html Sat May 11 16:21:55 2013 -0500 +++ b/sg101/templates/accounts/ajax_login_form.html Sat May 11 23:39:46 2013 -0500 @@ -10,6 +10,7 @@ </fieldset> </form> <ul> +<li>Forgot your username? You can recover it <a href="{% url 'accounts-username_query' %}">here</a>.</li> <li>Forgot your password? You can reset it <a href="{% url 'accounts-password_reset' %}">here</a>.</li> <li>Don't have an account? Why don't you <a href="{% url 'accounts-register' %}">register</a>?</li> <li>Having problems? Please <a href="{% url 'contact-form' %}">contact us</a>.</li>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sg101/templates/accounts/forgot_user_email.txt Sat May 11 23:39:46 2013 -0500 @@ -0,0 +1,14 @@ +Hello, + +{{ site.name }} received a request to recover a forgotten username for the +email address {{ user.email }}. + +The requested username is {{ user.username }}. + +If you did not request this information, simply ignore this email. If you have +questions, please contact {{ admin_email }}. + +Regards, + +The staff at {{ site.name }} +http://{{ site.domain }}
--- a/sg101/templates/accounts/login.html Sat May 11 16:21:55 2013 -0500 +++ b/sg101/templates/accounts/login.html Sat May 11 23:39:46 2013 -0500 @@ -13,6 +13,7 @@ </form> <ul> +<li>Forgot your username? You can recover it <a href="{% url 'accounts-username_query' %}">here</a>.</li> <li>Forgot your password? You can reset it <a href="{% url 'accounts-password_reset' %}">here</a>.</li> <li>Don't have an account? Why don't you <a href="{% url 'accounts-register' %}">register</a>?</li> <li>Having problems? Please <a href="{% url 'contact-form' %}">contact us</a>.</li>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sg101/templates/accounts/username_query.html Sat May 11 23:39:46 2013 -0500 @@ -0,0 +1,13 @@ +{% extends 'base.html' %} +{% block title %}Forgotten Username{% endblock %} +{% block content %} +<h2>Forgotten Username</h2> +<p> +Forget your username? No problem. Please enter the email address you used to +register with the site below and we'll send your username to you. +</p> +<form action="." method="post">{% csrf_token %} + {{ form.as_p }} + <p><input type="submit" value="Submit" /></p> +</form> +{% endblock %}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sg101/templates/accounts/username_sent.html Sat May 11 23:39:46 2013 -0500 @@ -0,0 +1,9 @@ +{% extends 'base.html' %} +{% block title %}Username Sent{% endblock %} +{% block content %} +<h2>Username Sent</h2> +<p> +Thank you. We have emailed the associated username to the email address you +provided. +</p> +{% endblock %}