Fixed #17431 -- Added send_mail() method to PasswordResetForm.
Credits for the initial patch go to ejucovy; big thanks to Tim Graham for the review.
This commit is contained in:
parent
d8f19bb3b6
commit
a00b78b1e2
|
@ -3,6 +3,7 @@ from __future__ import unicode_literals
|
|||
from collections import OrderedDict
|
||||
|
||||
from django import forms
|
||||
from django.core.mail import EmailMultiAlternatives
|
||||
from django.forms.utils import flatatt
|
||||
from django.template import loader
|
||||
from django.utils.encoding import force_bytes
|
||||
|
@ -230,6 +231,23 @@ class AuthenticationForm(forms.Form):
|
|||
class PasswordResetForm(forms.Form):
|
||||
email = forms.EmailField(label=_("Email"), max_length=254)
|
||||
|
||||
def send_mail(self, subject_template_name, email_template_name,
|
||||
context, from_email, to_email, html_email_template_name=None):
|
||||
"""
|
||||
Sends a django.core.mail.EmailMultiAlternatives to `to_email`.
|
||||
"""
|
||||
subject = loader.render_to_string(subject_template_name, context)
|
||||
# Email subject *must not* contain newlines
|
||||
subject = ''.join(subject.splitlines())
|
||||
body = loader.render_to_string(email_template_name, context)
|
||||
|
||||
email_message = EmailMultiAlternatives(subject, body, from_email, [to_email])
|
||||
if html_email_template_name is not None:
|
||||
html_email = loader.render_to_string(html_email_template_name, context)
|
||||
email_message.attach_alternative(html_email, 'text/html')
|
||||
|
||||
email_message.send()
|
||||
|
||||
def save(self, domain_override=None,
|
||||
subject_template_name='registration/password_reset_subject.txt',
|
||||
email_template_name='registration/password_reset_email.html',
|
||||
|
@ -239,7 +257,6 @@ class PasswordResetForm(forms.Form):
|
|||
Generates a one-use only link for resetting password and sends to the
|
||||
user.
|
||||
"""
|
||||
from django.core.mail import send_mail
|
||||
UserModel = get_user_model()
|
||||
email = self.cleaned_data["email"]
|
||||
active_users = UserModel._default_manager.filter(
|
||||
|
@ -255,7 +272,7 @@ class PasswordResetForm(forms.Form):
|
|||
domain = current_site.domain
|
||||
else:
|
||||
site_name = domain = domain_override
|
||||
c = {
|
||||
context = {
|
||||
'email': user.email,
|
||||
'domain': domain,
|
||||
'site_name': site_name,
|
||||
|
@ -264,16 +281,10 @@ class PasswordResetForm(forms.Form):
|
|||
'token': token_generator.make_token(user),
|
||||
'protocol': 'https' if use_https else 'http',
|
||||
}
|
||||
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)
|
||||
|
||||
if html_email_template_name:
|
||||
html_email = loader.render_to_string(html_email_template_name, c)
|
||||
else:
|
||||
html_email = None
|
||||
send_mail(subject, email, from_email, [user.email], html_message=html_email)
|
||||
self.send_mail(subject_template_name, email_template_name,
|
||||
context, from_email, user.email,
|
||||
html_email_template_name=html_email_template_name)
|
||||
|
||||
|
||||
class SetPasswordForm(forms.Form):
|
||||
|
|
|
@ -11,6 +11,7 @@ from django.contrib.auth.forms import (UserCreationForm, AuthenticationForm,
|
|||
ReadOnlyPasswordHashField, ReadOnlyPasswordHashWidget)
|
||||
from django.contrib.auth.tests.utils import skipIfCustomUser
|
||||
from django.core import mail
|
||||
from django.core.mail import EmailMultiAlternatives
|
||||
from django.forms.fields import Field, CharField
|
||||
from django.test import TestCase, override_settings
|
||||
from django.utils.encoding import force_text
|
||||
|
@ -416,6 +417,35 @@ class PasswordResetFormTest(TestCase):
|
|||
self.assertEqual(len(mail.outbox), 1)
|
||||
self.assertEqual(mail.outbox[0].subject, 'Custom password reset on example.com')
|
||||
|
||||
def test_custom_email_constructor(self):
|
||||
template_path = os.path.join(os.path.dirname(__file__), 'templates')
|
||||
with self.settings(TEMPLATE_DIRS=(template_path,)):
|
||||
data = {'email': 'testclient@example.com'}
|
||||
|
||||
class CustomEmailPasswordResetForm(PasswordResetForm):
|
||||
def send_mail(self, subject_template_name, email_template_name,
|
||||
context, from_email, to_email,
|
||||
html_email_template_name=None):
|
||||
EmailMultiAlternatives(
|
||||
"Forgot your password?",
|
||||
"Sorry to hear you forgot your password.",
|
||||
None, [to_email],
|
||||
['site_monitor@example.com'],
|
||||
headers={'Reply-To': 'webmaster@example.com'},
|
||||
alternatives=[("Really sorry to hear you forgot your password.",
|
||||
"text/html")]).send()
|
||||
|
||||
form = CustomEmailPasswordResetForm(data)
|
||||
self.assertTrue(form.is_valid())
|
||||
# Since we're not providing a request object, we must provide a
|
||||
# domain_override to prevent the save operation from failing in the
|
||||
# potential case where contrib.sites is not installed. Refs #16412.
|
||||
form.save(domain_override='example.com')
|
||||
self.assertEqual(len(mail.outbox), 1)
|
||||
self.assertEqual(mail.outbox[0].subject, 'Forgot your password?')
|
||||
self.assertEqual(mail.outbox[0].bcc, ['site_monitor@example.com'])
|
||||
self.assertEqual(mail.outbox[0].content_subtype, "plain")
|
||||
|
||||
def test_preserve_username_case(self):
|
||||
"""
|
||||
Preserve the case of the user name (before the @ in the email address)
|
||||
|
|
|
@ -41,6 +41,9 @@ Minor features
|
|||
:meth:`~django.contrib.auth.models.User.has_perm`
|
||||
and :meth:`~django.contrib.auth.models.User.has_module_perms`
|
||||
to short-circuit permission checking.
|
||||
* :class:`~django.contrib.auth.forms.PasswordResetForm` now
|
||||
has a method :meth:`~django.contrib.auth.forms.PasswordResetForm.send_email`
|
||||
that can be overridden to customize the mail to be sent.
|
||||
|
||||
:mod:`django.contrib.formtools`
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
|
@ -1205,6 +1205,26 @@ provides several built-in forms located in :mod:`django.contrib.auth.forms`:
|
|||
A form for generating and emailing a one-time use link to reset a
|
||||
user's password.
|
||||
|
||||
.. method:: send_email(subject_template_name, email_template_name, context, from_email, to_email, [html_email_template_name=None])
|
||||
|
||||
.. versionadded:: 1.8
|
||||
|
||||
Uses the arguments to send an ``EmailMultiAlternatives``.
|
||||
Can be overridden to customize how the email is sent to the user.
|
||||
|
||||
:param subject_template_name: the template for the subject.
|
||||
:param email_template_name: the template for the email body.
|
||||
:param context: context passed to the ``subject_template``, ``email_template``,
|
||||
and ``html_email_template`` (if it is not ``None``).
|
||||
:param from_email: the sender's email.
|
||||
:param to_email: the email of the requester.
|
||||
:param html_email_template_name: the template for the HTML body;
|
||||
defaults to ``None``, in which case a plain text email is sent.
|
||||
|
||||
By default, ``save()`` populates the ``context`` with the
|
||||
same variables that :func:`~django.contrib.auth.views.password_reset`
|
||||
passes to its email context.
|
||||
|
||||
.. class:: SetPasswordForm
|
||||
|
||||
A form that lets a user change his/her password without entering the old
|
||||
|
|
Loading…
Reference in New Issue