From 656360c24044e06c881baa648f2aad9d671c3bd8 Mon Sep 17 00:00:00 2001 From: Jannis Leidel Date: Sun, 19 Jun 2011 11:24:39 +0000 Subject: [PATCH] Fixed #12202 -- Removed hardcoded password reset subject and added a subject_template_name parameter to the password_reset view. Thanks, Ramiro Morales, Claude Paroz and agabel. git-svn-id: http://code.djangoproject.com/svn/django/trunk@16438 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- MANIFEST.in | 1 + .../contrib/auth/fixtures/authtestdata.json | 2 +- django/contrib/auth/forms.py | 15 ++- .../auth/locale/en/LC_MESSAGES/django.po | 98 +++++++++---------- .../registration/password_reset_subject.txt | 3 + django/contrib/auth/tests/forms.py | 13 +++ .../registration/password_reset_subject.txt | 1 + django/contrib/auth/views.py | 2 + docs/topics/auth.txt | 6 ++ 9 files changed, 84 insertions(+), 57 deletions(-) create mode 100644 django/contrib/auth/templates/registration/password_reset_subject.txt create mode 100644 django/contrib/auth/tests/templates/registration/password_reset_subject.txt diff --git a/MANIFEST.in b/MANIFEST.in index 497b834f26c..ebdc6cf0161 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -17,6 +17,7 @@ recursive-include django/contrib/admin/templates * recursive-include django/contrib/admin/media * recursive-include django/contrib/admindocs/templates * recursive-include django/contrib/auth/fixtures * +recursive-include django/contrib/auth/templates * recursive-include django/contrib/auth/tests/templates * recursive-include django/contrib/comments/templates * recursive-include django/contrib/databrowse/templates * diff --git a/django/contrib/auth/fixtures/authtestdata.json b/django/contrib/auth/fixtures/authtestdata.json index e0bdc24e72d..c2867430e6e 100644 --- a/django/contrib/auth/fixtures/authtestdata.json +++ b/django/contrib/auth/fixtures/authtestdata.json @@ -31,7 +31,7 @@ "groups": [], "user_permissions": [], "password": "sha1$6efc0$f93efe9fd7542f25a7be94871ea45aa95de57161", - "email": "testclient@example.com", + "email": "testclient2@example.com", "date_joined": "2006-12-17 07:03:31" } }, diff --git a/django/contrib/auth/forms.py b/django/contrib/auth/forms.py index fde8f037c23..99b685c10d5 100644 --- a/django/contrib/auth/forms.py +++ b/django/contrib/auth/forms.py @@ -120,8 +120,11 @@ class PasswordResetForm(forms.Form): raise forms.ValidationError(_("That e-mail address doesn't have an associated user account. Are you sure you've registered?")) return email - def save(self, domain_override=None, email_template_name='registration/password_reset_email.html', - use_https=False, token_generator=default_token_generator, from_email=None, request=None): + def save(self, domain_override=None, + subject_template_name='registration/password_reset_subject.txt', + email_template_name='registration/password_reset_email.html', + use_https=False, token_generator=default_token_generator, + from_email=None, request=None): """ Generates a one-use only link for resetting password and sends to the user """ @@ -133,7 +136,6 @@ class PasswordResetForm(forms.Form): domain = current_site.domain else: site_name = domain = domain_override - t = loader.get_template(email_template_name) c = { 'email': user.email, 'domain': domain, @@ -143,8 +145,11 @@ class PasswordResetForm(forms.Form): 'token': token_generator.make_token(user), 'protocol': use_https and 'https' or 'http', } - send_mail(_("Password reset on %s") % site_name, - t.render(Context(c)), from_email, [user.email]) + subject = loader.render_to_string(subject_template_name, c) + # Email subject *must not* contain newlines + subject = ''.join(subject.splitlines()) + email = loader.render_to_string(email_template_name, c) + send_mail(subject, email, from_email, [user.email]) class SetPasswordForm(forms.Form): """ diff --git a/django/contrib/auth/locale/en/LC_MESSAGES/django.po b/django/contrib/auth/locale/en/LC_MESSAGES/django.po index aa2dc35b69b..61e34747c2c 100644 --- a/django/contrib/auth/locale/en/LC_MESSAGES/django.po +++ b/django/contrib/auth/locale/en/LC_MESSAGES/django.po @@ -4,7 +4,7 @@ msgid "" msgstr "" "Project-Id-Version: Django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-03-15 13:14-0400\n" +"POT-Creation-Date: 2011-06-19 13:08+0200\n" "PO-Revision-Date: 2010-05-13 15:35+0200\n" "Last-Translator: Django team\n" "Language-Team: English \n" @@ -12,27 +12,27 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: admin.py:28 +#: admin.py:29 msgid "Personal info" msgstr "" -#: admin.py:29 +#: admin.py:30 msgid "Permissions" msgstr "" -#: admin.py:30 +#: admin.py:31 msgid "Important dates" msgstr "" -#: admin.py:31 +#: admin.py:32 msgid "Groups" msgstr "" -#: admin.py:113 +#: admin.py:116 msgid "Password changed successfully." msgstr "" -#: admin.py:123 +#: admin.py:126 #, python-format msgid "Change password: %s" msgstr "" @@ -49,7 +49,7 @@ msgstr "" msgid "This value may contain only letters, numbers and @/./+/-/_ characters." msgstr "" -#: forms.py:17 forms.py:67 forms.py:193 +#: forms.py:17 forms.py:67 forms.py:201 msgid "Password" msgstr "" @@ -65,7 +65,7 @@ msgstr "" msgid "A user with that username already exists." msgstr "" -#: forms.py:37 forms.py:163 forms.py:205 +#: forms.py:37 forms.py:171 forms.py:213 msgid "The two password fields didn't match." msgstr "" @@ -89,154 +89,150 @@ msgstr "" msgid "E-mail" msgstr "" -#: forms.py:117 +#: forms.py:120 msgid "" "That e-mail address doesn't have an associated user account. Are you sure " "you've registered?" msgstr "" -#: forms.py:143 -#, python-format -msgid "Password reset on %s" -msgstr "" - -#: forms.py:151 +#: forms.py:159 msgid "New password" msgstr "" -#: forms.py:152 +#: forms.py:160 msgid "New password confirmation" msgstr "" -#: forms.py:177 +#: forms.py:185 msgid "Old password" msgstr "" -#: forms.py:185 +#: forms.py:193 msgid "Your old password was entered incorrectly. Please enter it again." msgstr "" -#: forms.py:194 +#: forms.py:202 msgid "Password (again)" msgstr "" -#: models.py:76 models.py:104 +#: models.py:77 models.py:105 msgid "name" msgstr "" -#: models.py:78 +#: models.py:79 msgid "codename" msgstr "" -#: models.py:82 +#: models.py:83 msgid "permission" msgstr "" -#: models.py:83 models.py:105 +#: models.py:84 models.py:106 msgid "permissions" msgstr "" -#: models.py:108 +#: models.py:109 msgid "group" msgstr "" -#: models.py:109 models.py:216 +#: models.py:110 models.py:217 msgid "groups" msgstr "" -#: models.py:206 +#: models.py:207 msgid "username" msgstr "" -#: models.py:206 +#: models.py:207 msgid "" "Required. 30 characters or fewer. Letters, numbers and @/./+/-/_ characters" msgstr "" -#: models.py:207 +#: models.py:208 msgid "first name" msgstr "" -#: models.py:208 +#: models.py:209 msgid "last name" msgstr "" -#: models.py:209 +#: models.py:210 msgid "e-mail address" msgstr "" -#: models.py:210 +#: models.py:211 msgid "password" msgstr "" -#: models.py:210 +#: models.py:211 msgid "" "Use '[algo]$[salt]$[hexdigest]' or use the change " "password form." msgstr "" -#: models.py:211 +#: models.py:212 msgid "staff status" msgstr "" -#: models.py:211 +#: models.py:212 msgid "Designates whether the user can log into this admin site." msgstr "" -#: models.py:212 +#: models.py:213 msgid "active" msgstr "" -#: models.py:212 +#: models.py:213 msgid "" "Designates whether this user should be treated as active. Unselect this " "instead of deleting accounts." msgstr "" -#: models.py:213 +#: models.py:214 msgid "superuser status" msgstr "" -#: models.py:213 +#: models.py:214 msgid "" "Designates that this user has all permissions without explicitly assigning " "them." msgstr "" -#: models.py:214 +#: models.py:215 msgid "last login" msgstr "" -#: models.py:215 +#: models.py:216 msgid "date joined" msgstr "" -#: models.py:217 +#: models.py:218 msgid "" "In addition to the permissions manually assigned, this user will also get " "all permissions granted to each group he/she is in." msgstr "" -#: models.py:218 +#: models.py:219 msgid "user permissions" msgstr "" -#: models.py:222 +#: models.py:223 msgid "user" msgstr "" -#: models.py:223 +#: models.py:224 msgid "users" msgstr "" -#: models.py:406 -msgid "message" -msgstr "" - -#: views.py:91 +#: views.py:93 msgid "Logged out" msgstr "" -#: management/commands/createsuperuser.py:23 +#: management/commands/createsuperuser.py:24 msgid "Enter a valid e-mail address." msgstr "" + +#: templates/registration/password_reset_subject.txt:2 +#, python-format +msgid "Password reset on %(site_name)s" +msgstr "" diff --git a/django/contrib/auth/templates/registration/password_reset_subject.txt b/django/contrib/auth/templates/registration/password_reset_subject.txt new file mode 100644 index 00000000000..45a354b9993 --- /dev/null +++ b/django/contrib/auth/templates/registration/password_reset_subject.txt @@ -0,0 +1,3 @@ +{% load i18n %}{% autoescape off %} +{% blocktrans %}Password reset on {{ site_name }}{% endblocktrans %} +{% endautoescape %} \ No newline at end of file diff --git a/django/contrib/auth/tests/forms.py b/django/contrib/auth/tests/forms.py index 6f9e01d127d..1fca20a4979 100644 --- a/django/contrib/auth/tests/forms.py +++ b/django/contrib/auth/tests/forms.py @@ -1,3 +1,7 @@ +from __future__ import with_statement +import os +from django.conf import settings +from django.core import mail from django.contrib.auth.models import User from django.contrib.auth.forms import UserCreationForm, AuthenticationForm, PasswordChangeForm, SetPasswordForm, UserChangeForm, PasswordResetForm from django.test import TestCase @@ -251,6 +255,15 @@ class PasswordResetFormTest(TestCase): self.assertTrue(form.is_valid()) self.assertEqual(form.cleaned_data['email'], email) + def test_custom_email_subject(self): + template_path = os.path.join(os.path.dirname(__file__), 'templates') + with self.settings(TEMPLATE_DIRS=(template_path,)): + data = {'email': 'testclient@example.com'} + form = PasswordResetForm(data) + self.assertTrue(form.is_valid()) + form.save() + self.assertEqual(len(mail.outbox), 1) + self.assertEqual(mail.outbox[0].subject, u'Custom password reset on example.com') def test_bug_5605(self): # bug #5605, preserve the case of the user name (before the @ in the diff --git a/django/contrib/auth/tests/templates/registration/password_reset_subject.txt b/django/contrib/auth/tests/templates/registration/password_reset_subject.txt new file mode 100644 index 00000000000..904b645c6c8 --- /dev/null +++ b/django/contrib/auth/tests/templates/registration/password_reset_subject.txt @@ -0,0 +1 @@ +{% autoescape off %}Custom password reset on {{ site_name }}{% endautoescape %} \ No newline at end of file diff --git a/django/contrib/auth/views.py b/django/contrib/auth/views.py index 4995f89caf7..82759e9e4f4 100644 --- a/django/contrib/auth/views.py +++ b/django/contrib/auth/views.py @@ -135,6 +135,7 @@ def redirect_to_login(next, login_url=None, def password_reset(request, is_admin_site=False, template_name='registration/password_reset_form.html', email_template_name='registration/password_reset_email.html', + subject_template_name='registration/password_reset_subject.txt', password_reset_form=PasswordResetForm, token_generator=default_token_generator, post_reset_redirect=None, @@ -151,6 +152,7 @@ def password_reset(request, is_admin_site=False, 'token_generator': token_generator, 'from_email': from_email, 'email_template_name': email_template_name, + 'subject_template_name': subject_template_name, 'request': request, } if is_admin_site: diff --git a/docs/topics/auth.txt b/docs/topics/auth.txt index 2862b4c04cf..228f53ba2b5 100644 --- a/docs/topics/auth.txt +++ b/docs/topics/auth.txt @@ -964,6 +964,12 @@ includes a few other useful built-in views located in generating the email with the new password. This will default to :file:`registration/password_reset_email.html` if not supplied. + * ``subject_template_name``: The full name of a template to use for + the subject of the email with the new password. This will default + to :file:`registration/password_reset_subject.txt` if not supplied. + + .. versionadded:: 1.4 + * ``password_reset_form``: Form that will be used to set the password. Defaults to :class:`~django.contrib.auth.forms.PasswordResetForm`.