Fixed #10863 -- Added HTML support to mail_managers() and mail_admins(), and used this to provide more and prettier detail in error emails. Thanks to boxed for the suggestion, and to Rob Hudson and Brodie Rao for their work on the patch.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@14844 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
b407de3bc5
commit
29c4a578af
|
@ -83,22 +83,30 @@ def send_mass_mail(datatuple, fail_silently=False, auth_user=None,
|
||||||
return connection.send_messages(messages)
|
return connection.send_messages(messages)
|
||||||
|
|
||||||
|
|
||||||
def mail_admins(subject, message, fail_silently=False, connection=None):
|
def mail_admins(subject, message, fail_silently=False, connection=None,
|
||||||
|
html_message=None):
|
||||||
"""Sends a message to the admins, as defined by the ADMINS setting."""
|
"""Sends a message to the admins, as defined by the ADMINS setting."""
|
||||||
if not settings.ADMINS:
|
if not settings.ADMINS:
|
||||||
return
|
return
|
||||||
EmailMessage(u'%s%s' % (settings.EMAIL_SUBJECT_PREFIX, subject), message,
|
mail = EmailMultiAlternatives(u'%s%s' % (settings.EMAIL_SUBJECT_PREFIX, subject),
|
||||||
settings.SERVER_EMAIL, [a[1] for a in settings.ADMINS],
|
message, settings.SERVER_EMAIL, [a[1] for a in settings.ADMINS],
|
||||||
connection=connection).send(fail_silently=fail_silently)
|
connection=connection)
|
||||||
|
if html_message:
|
||||||
|
mail.attach_alternative(html_message, 'text/html')
|
||||||
|
mail.send(fail_silently=fail_silently)
|
||||||
|
|
||||||
|
|
||||||
def mail_managers(subject, message, fail_silently=False, connection=None):
|
def mail_managers(subject, message, fail_silently=False, connection=None,
|
||||||
|
html_message=None):
|
||||||
"""Sends a message to the managers, as defined by the MANAGERS setting."""
|
"""Sends a message to the managers, as defined by the MANAGERS setting."""
|
||||||
if not settings.MANAGERS:
|
if not settings.MANAGERS:
|
||||||
return
|
return
|
||||||
EmailMessage(u'%s%s' % (settings.EMAIL_SUBJECT_PREFIX, subject), message,
|
mail = EmailMultiAlternatives(u'%s%s' % (settings.EMAIL_SUBJECT_PREFIX, subject),
|
||||||
settings.SERVER_EMAIL, [a[1] for a in settings.MANAGERS],
|
message, settings.SERVER_EMAIL, [a[1] for a in settings.MANAGERS],
|
||||||
connection=connection).send(fail_silently=fail_silently)
|
connection=connection)
|
||||||
|
if html_message:
|
||||||
|
mail.attach_alternative(html_message, 'text/html')
|
||||||
|
mail.send(fail_silently=fail_silently)
|
||||||
|
|
||||||
|
|
||||||
class SMTPConnection(_SMTPConnection):
|
class SMTPConnection(_SMTPConnection):
|
||||||
|
|
|
@ -56,6 +56,7 @@ class AdminEmailHandler(logging.Handler):
|
||||||
def emit(self, record):
|
def emit(self, record):
|
||||||
import traceback
|
import traceback
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.views.debug import ExceptionReporter
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if sys.version_info < (2,5):
|
if sys.version_info < (2,5):
|
||||||
|
@ -75,12 +76,18 @@ class AdminEmailHandler(logging.Handler):
|
||||||
request_repr = repr(request)
|
request_repr = repr(request)
|
||||||
except:
|
except:
|
||||||
subject = 'Error: Unknown URL'
|
subject = 'Error: Unknown URL'
|
||||||
|
request = None
|
||||||
request_repr = "Request repr() unavailable"
|
request_repr = "Request repr() unavailable"
|
||||||
|
|
||||||
if record.exc_info:
|
if record.exc_info:
|
||||||
|
exc_info = record.exc_info
|
||||||
stack_trace = '\n'.join(traceback.format_exception(*record.exc_info))
|
stack_trace = '\n'.join(traceback.format_exception(*record.exc_info))
|
||||||
else:
|
else:
|
||||||
|
exc_info = ()
|
||||||
stack_trace = 'No stack trace available'
|
stack_trace = 'No stack trace available'
|
||||||
|
|
||||||
message = "%s\n\n%s" % (stack_trace, request_repr)
|
message = "%s\n\n%s" % (stack_trace, request_repr)
|
||||||
mail.mail_admins(subject, message, fail_silently=True)
|
reporter = ExceptionReporter(request, *exc_info, is_email=True)
|
||||||
|
html_message = reporter.get_traceback_html()
|
||||||
|
mail.mail_admins(subject, message, fail_silently=True,
|
||||||
|
html_message=html_message)
|
||||||
|
|
|
@ -62,11 +62,12 @@ class ExceptionReporter:
|
||||||
"""
|
"""
|
||||||
A class to organize and coordinate reporting on exceptions.
|
A class to organize and coordinate reporting on exceptions.
|
||||||
"""
|
"""
|
||||||
def __init__(self, request, exc_type, exc_value, tb):
|
def __init__(self, request, exc_type, exc_value, tb, is_email=False):
|
||||||
self.request = request
|
self.request = request
|
||||||
self.exc_type = exc_type
|
self.exc_type = exc_type
|
||||||
self.exc_value = exc_value
|
self.exc_value = exc_value
|
||||||
self.tb = tb
|
self.tb = tb
|
||||||
|
self.is_email = is_email
|
||||||
|
|
||||||
self.template_info = None
|
self.template_info = None
|
||||||
self.template_does_not_exist = False
|
self.template_does_not_exist = False
|
||||||
|
@ -118,6 +119,7 @@ class ExceptionReporter:
|
||||||
from django import get_version
|
from django import get_version
|
||||||
t = Template(TECHNICAL_500_TEMPLATE, name='Technical 500 template')
|
t = Template(TECHNICAL_500_TEMPLATE, name='Technical 500 template')
|
||||||
c = Context({
|
c = Context({
|
||||||
|
'is_email': self.is_email,
|
||||||
'exception_type': self.exc_type.__name__,
|
'exception_type': self.exc_type.__name__,
|
||||||
'exception_value': smart_unicode(self.exc_value, errors='replace'),
|
'exception_value': smart_unicode(self.exc_value, errors='replace'),
|
||||||
'unicode_hint': unicode_hint,
|
'unicode_hint': unicode_hint,
|
||||||
|
@ -324,7 +326,7 @@ TECHNICAL_500_TEMPLATE = """
|
||||||
table.vars { margin:5px 0 2px 40px; }
|
table.vars { margin:5px 0 2px 40px; }
|
||||||
table.vars td, table.req td { font-family:monospace; }
|
table.vars td, table.req td { font-family:monospace; }
|
||||||
table td.code { width:100%; }
|
table td.code { width:100%; }
|
||||||
table td.code div { overflow:hidden; }
|
table td.code pre { overflow:hidden; }
|
||||||
table.source th { color:#666; }
|
table.source th { color:#666; }
|
||||||
table.source td { font-family:monospace; white-space:pre; border-bottom:1px solid #eee; }
|
table.source td { font-family:monospace; white-space:pre; border-bottom:1px solid #eee; }
|
||||||
ul.traceback { list-style-type:none; }
|
ul.traceback { list-style-type:none; }
|
||||||
|
@ -353,6 +355,7 @@ TECHNICAL_500_TEMPLATE = """
|
||||||
span.commands a:link {color:#5E5694;}
|
span.commands a:link {color:#5E5694;}
|
||||||
pre.exception_value { font-family: sans-serif; color: #666; font-size: 1.5em; margin: 10px 0 10px 0; }
|
pre.exception_value { font-family: sans-serif; color: #666; font-size: 1.5em; margin: 10px 0 10px 0; }
|
||||||
</style>
|
</style>
|
||||||
|
{% if not is_email %}
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
//<!--
|
//<!--
|
||||||
function getElementsByClassName(oElm, strTagName, strClassName){
|
function getElementsByClassName(oElm, strTagName, strClassName){
|
||||||
|
@ -408,10 +411,11 @@ TECHNICAL_500_TEMPLATE = """
|
||||||
}
|
}
|
||||||
//-->
|
//-->
|
||||||
</script>
|
</script>
|
||||||
|
{% endif %}
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="summary">
|
<div id="summary">
|
||||||
<h1>{{ exception_type }} at {{ request.path_info|escape }}</h1>
|
<h1>{{ exception_type }}{% if request %} at {{ request.path_info|escape }}{% endif %}</h1>
|
||||||
<pre class="exception_value">{{ exception_value|force_escape }}</pre>
|
<pre class="exception_value">{{ exception_value|force_escape }}</pre>
|
||||||
<table class="meta">
|
<table class="meta">
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -448,7 +452,7 @@ TECHNICAL_500_TEMPLATE = """
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Python Path:</th>
|
<th>Python Path:</th>
|
||||||
<td>{{ sys_path }}</td>
|
<td><pre>{{ sys_path|pprint }}</pre></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Server time:</th>
|
<th>Server time:</th>
|
||||||
|
@ -498,7 +502,7 @@ TECHNICAL_500_TEMPLATE = """
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div id="traceback">
|
<div id="traceback">
|
||||||
<h2>Traceback <span class="commands"><a href="#" onclick="return switchPastebinFriendly(this);">Switch to copy-and-paste view</a></span></h2>
|
<h2>Traceback <span class="commands">{% if not is_email %}<a href="#" onclick="return switchPastebinFriendly(this);">Switch to copy-and-paste view</a></span>{% endif %}</h2>
|
||||||
{% autoescape off %}
|
{% autoescape off %}
|
||||||
<div id="browserTraceback">
|
<div id="browserTraceback">
|
||||||
<ul class="traceback">
|
<ul class="traceback">
|
||||||
|
@ -508,19 +512,23 @@ TECHNICAL_500_TEMPLATE = """
|
||||||
|
|
||||||
{% if frame.context_line %}
|
{% if frame.context_line %}
|
||||||
<div class="context" id="c{{ frame.id }}">
|
<div class="context" id="c{{ frame.id }}">
|
||||||
{% if frame.pre_context %}
|
{% if frame.pre_context and not is_email %}
|
||||||
<ol start="{{ frame.pre_context_lineno }}" class="pre-context" id="pre{{ frame.id }}">{% for line in frame.pre_context %}<li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')">{{ line|escape }}</li>{% endfor %}</ol>
|
<ol start="{{ frame.pre_context_lineno }}" class="pre-context" id="pre{{ frame.id }}">{% for line in frame.pre_context %}<li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')"><pre>{{ line|escape }}</pre></li>{% endfor %}</ol>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<ol start="{{ frame.lineno }}" class="context-line"><li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')">{{ frame.context_line|escape }} <span>...</span></li></ol>
|
<ol start="{{ frame.lineno }}" class="context-line"><li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')"><pre>{{ frame.context_line|escape }}</pre>{% if not is_email %} <span>...</span>{% endif %}</li></ol>
|
||||||
{% if frame.post_context %}
|
{% if frame.post_context and not is_email %}
|
||||||
<ol start='{{ frame.lineno|add:"1" }}' class="post-context" id="post{{ frame.id }}">{% for line in frame.post_context %}<li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')">{{ line|escape }}</li>{% endfor %}</ol>
|
<ol start='{{ frame.lineno|add:"1" }}' class="post-context" id="post{{ frame.id }}">{% for line in frame.post_context %}<li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')"><pre>{{ line|escape }}</pre></li>{% endfor %}</ol>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if frame.vars %}
|
{% if frame.vars %}
|
||||||
<div class="commands">
|
<div class="commands">
|
||||||
<a href="#" onclick="return varToggle(this, '{{ frame.id }}')"><span>▶</span> Local vars</a>
|
{% if is_email %}
|
||||||
|
<h2>Local Vars</h2>
|
||||||
|
{% else %}
|
||||||
|
<a href="#" onclick="return varToggle(this, '{{ frame.id }}')"><span>▶</span> Local vars</a>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<table class="vars" id="v{{ frame.id }}">
|
<table class="vars" id="v{{ frame.id }}">
|
||||||
<thead>
|
<thead>
|
||||||
|
@ -533,7 +541,7 @@ TECHNICAL_500_TEMPLATE = """
|
||||||
{% for var in frame.vars|dictsort:"0" %}
|
{% for var in frame.vars|dictsort:"0" %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ var.0|force_escape }}</td>
|
<td>{{ var.0|force_escape }}</td>
|
||||||
<td class="code"><div>{{ var.1|pprint|force_escape }}</div></td>
|
<td class="code"><pre>{{ var.1|pprint|force_escape }}</pre></td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
@ -545,16 +553,19 @@ TECHNICAL_500_TEMPLATE = """
|
||||||
</div>
|
</div>
|
||||||
{% endautoescape %}
|
{% endautoescape %}
|
||||||
<form action="http://dpaste.com/" name="pasteform" id="pasteform" method="post">
|
<form action="http://dpaste.com/" name="pasteform" id="pasteform" method="post">
|
||||||
|
{% if not is_email %}
|
||||||
<div id="pastebinTraceback" class="pastebin">
|
<div id="pastebinTraceback" class="pastebin">
|
||||||
<input type="hidden" name="language" value="PythonConsole">
|
<input type="hidden" name="language" value="PythonConsole">
|
||||||
<input type="hidden" name="title" value="{{ exception_type|escape }} at {{ request.path_info|escape }}">
|
<input type="hidden" name="title" value="{{ exception_type|escape }}{% if request %} at {{ request.path_info|escape }}{% endif %}">
|
||||||
<input type="hidden" name="source" value="Django Dpaste Agent">
|
<input type="hidden" name="source" value="Django Dpaste Agent">
|
||||||
<input type="hidden" name="poster" value="Django">
|
<input type="hidden" name="poster" value="Django">
|
||||||
<textarea name="content" id="traceback_area" cols="140" rows="25">
|
<textarea name="content" id="traceback_area" cols="140" rows="25">
|
||||||
Environment:
|
Environment:
|
||||||
|
|
||||||
|
{% if request %}
|
||||||
Request Method: {{ request.META.REQUEST_METHOD }}
|
Request Method: {{ request.META.REQUEST_METHOD }}
|
||||||
Request URL: {{ request.build_absolute_uri|escape }}
|
Request URL: {{ request.build_absolute_uri|escape }}
|
||||||
|
{% endif %}
|
||||||
Django Version: {{ django_version_info }}
|
Django Version: {{ django_version_info }}
|
||||||
Python Version: {{ sys_version_info }}
|
Python Version: {{ sys_version_info }}
|
||||||
Installed Applications:
|
Installed Applications:
|
||||||
|
@ -581,7 +592,7 @@ Traceback:
|
||||||
{% for frame in frames %}File "{{ frame.filename|escape }}" in {{ frame.function|escape }}
|
{% for frame in frames %}File "{{ frame.filename|escape }}" in {{ frame.function|escape }}
|
||||||
{% if frame.context_line %} {{ frame.lineno }}. {{ frame.context_line|escape }}{% endif %}
|
{% if frame.context_line %} {{ frame.lineno }}. {{ frame.context_line|escape }}{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
Exception Type: {{ exception_type|escape }} at {{ request.path_info|escape }}
|
Exception Type: {{ exception_type|escape }}{% if request %} at {{ request.path_info|escape }}{% endif %}
|
||||||
Exception Value: {{ exception_value|force_escape }}
|
Exception Value: {{ exception_value|force_escape }}
|
||||||
</textarea>
|
</textarea>
|
||||||
<br><br>
|
<br><br>
|
||||||
|
@ -589,10 +600,12 @@ Exception Value: {{ exception_value|force_escape }}
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<div id="requestinfo">
|
<div id="requestinfo">
|
||||||
<h2>Request information</h2>
|
<h2>Request information</h2>
|
||||||
|
|
||||||
|
{% if request %}
|
||||||
<h3 id="get-info">GET</h3>
|
<h3 id="get-info">GET</h3>
|
||||||
{% if request.GET %}
|
{% if request.GET %}
|
||||||
<table class="req">
|
<table class="req">
|
||||||
|
@ -606,7 +619,7 @@ Exception Value: {{ exception_value|force_escape }}
|
||||||
{% for var in request.GET.items %}
|
{% for var in request.GET.items %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ var.0 }}</td>
|
<td>{{ var.0 }}</td>
|
||||||
<td class="code"><div>{{ var.1|pprint }}</div></td>
|
<td class="code"><pre>{{ var.1|pprint }}</pre></td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
@ -628,7 +641,7 @@ Exception Value: {{ exception_value|force_escape }}
|
||||||
{% for var in request.POST.items %}
|
{% for var in request.POST.items %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ var.0 }}</td>
|
<td>{{ var.0 }}</td>
|
||||||
<td class="code"><div>{{ var.1|pprint }}</div></td>
|
<td class="code"><pre>{{ var.1|pprint }}</pre></td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
@ -649,7 +662,7 @@ Exception Value: {{ exception_value|force_escape }}
|
||||||
{% for var in request.FILES.items %}
|
{% for var in request.FILES.items %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ var.0 }}</td>
|
<td>{{ var.0 }}</td>
|
||||||
<td class="code"><div>{{ var.1|pprint }}</div></td>
|
<td class="code"><pre>{{ var.1|pprint }}</pre></td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
@ -672,7 +685,7 @@ Exception Value: {{ exception_value|force_escape }}
|
||||||
{% for var in request.COOKIES.items %}
|
{% for var in request.COOKIES.items %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ var.0 }}</td>
|
<td>{{ var.0 }}</td>
|
||||||
<td class="code"><div>{{ var.1|pprint }}</div></td>
|
<td class="code"><pre>{{ var.1|pprint }}</pre></td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
@ -693,11 +706,12 @@ Exception Value: {{ exception_value|force_escape }}
|
||||||
{% for var in request.META.items|dictsort:"0" %}
|
{% for var in request.META.items|dictsort:"0" %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ var.0 }}</td>
|
<td>{{ var.0 }}</td>
|
||||||
<td class="code"><div>{{ var.1|pprint }}</div></td>
|
<td class="code"><pre>{{ var.1|pprint }}</pre></td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<h3 id="settings-info">Settings</h3>
|
<h3 id="settings-info">Settings</h3>
|
||||||
<h4>Using settings module <code>{{ settings.SETTINGS_MODULE }}</code></h4>
|
<h4>Using settings module <code>{{ settings.SETTINGS_MODULE }}</code></h4>
|
||||||
|
@ -712,7 +726,7 @@ Exception Value: {{ exception_value|force_escape }}
|
||||||
{% for var in settings.items|dictsort:"0" %}
|
{% for var in settings.items|dictsort:"0" %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ var.0 }}</td>
|
<td>{{ var.0 }}</td>
|
||||||
<td class="code"><div>{{ var.1|pprint }}</div></td>
|
<td class="code"><pre>{{ var.1|pprint }}</pre></td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
|
@ -163,6 +163,12 @@ requests. These include:
|
||||||
|
|
||||||
* Support for _HTTPOnly cookies.
|
* Support for _HTTPOnly cookies.
|
||||||
|
|
||||||
|
* mail_admins() and mail_managers() now support easily attaching
|
||||||
|
HTML content to messages.
|
||||||
|
|
||||||
|
* Error emails now include more of the detail and formatting of
|
||||||
|
the debug server error page.
|
||||||
|
|
||||||
.. _HTTPOnly: http://www.owasp.org/index.php/HTTPOnly
|
.. _HTTPOnly: http://www.owasp.org/index.php/HTTPOnly
|
||||||
|
|
||||||
.. _backwards-incompatible-changes-1.3:
|
.. _backwards-incompatible-changes-1.3:
|
||||||
|
|
|
@ -109,7 +109,7 @@ a single connection for all of its messages. This makes
|
||||||
mail_admins()
|
mail_admins()
|
||||||
=============
|
=============
|
||||||
|
|
||||||
.. function:: mail_admins(subject, message, fail_silently=False, connection=None)
|
.. function:: mail_admins(subject, message, fail_silently=False, connection=None, html_message=None)
|
||||||
|
|
||||||
``django.core.mail.mail_admins()`` is a shortcut for sending an e-mail to the
|
``django.core.mail.mail_admins()`` is a shortcut for sending an e-mail to the
|
||||||
site admins, as defined in the :setting:`ADMINS` setting.
|
site admins, as defined in the :setting:`ADMINS` setting.
|
||||||
|
@ -122,10 +122,16 @@ The "From:" header of the e-mail will be the value of the
|
||||||
|
|
||||||
This method exists for convenience and readability.
|
This method exists for convenience and readability.
|
||||||
|
|
||||||
|
.. versionchanged:: 1.3
|
||||||
|
|
||||||
|
If ``html_message`` is provided, the resulting e-mail will be a
|
||||||
|
multipart/alternative e-mail with ``message`` as the "text/plain"
|
||||||
|
content type and ``html_message`` as the "text/html" content type.
|
||||||
|
|
||||||
mail_managers()
|
mail_managers()
|
||||||
===============
|
===============
|
||||||
|
|
||||||
.. function:: mail_managers(subject, message, fail_silently=False, connection=None)
|
.. function:: mail_managers(subject, message, fail_silently=False, connection=None, html_message=None)
|
||||||
|
|
||||||
``django.core.mail.mail_managers()`` is just like ``mail_admins()``, except it
|
``django.core.mail.mail_managers()`` is just like ``mail_admins()``, except it
|
||||||
sends an e-mail to the site managers, as defined in the :setting:`MANAGERS`
|
sends an e-mail to the site managers, as defined in the :setting:`MANAGERS`
|
||||||
|
|
|
@ -232,7 +232,7 @@ class MailTests(TestCase):
|
||||||
self.assertEqual(len(mail.outbox), 2)
|
self.assertEqual(len(mail.outbox), 2)
|
||||||
self.assertEqual(mail.outbox[0].subject, 'Subject')
|
self.assertEqual(mail.outbox[0].subject, 'Subject')
|
||||||
self.assertEqual(mail.outbox[1].subject, 'Subject 2')
|
self.assertEqual(mail.outbox[1].subject, 'Subject 2')
|
||||||
|
|
||||||
# Make sure that multiple locmem connections share mail.outbox
|
# Make sure that multiple locmem connections share mail.outbox
|
||||||
mail.outbox = []
|
mail.outbox = []
|
||||||
connection2 = locmem.EmailBackend()
|
connection2 = locmem.EmailBackend()
|
||||||
|
@ -364,6 +364,36 @@ class MailTests(TestCase):
|
||||||
settings.ADMINS = old_admins
|
settings.ADMINS = old_admins
|
||||||
settings.MANAGERS = old_managers
|
settings.MANAGERS = old_managers
|
||||||
|
|
||||||
|
def test_html_mail_admins(self):
|
||||||
|
"""Test html_message argument to mail_admins and mail_managers"""
|
||||||
|
old_admins = settings.ADMINS
|
||||||
|
settings.ADMINS = [('nobody','nobody@example.com')]
|
||||||
|
|
||||||
|
mail.outbox = []
|
||||||
|
mail_admins('Subject', 'Content', html_message='HTML Content')
|
||||||
|
self.assertEqual(len(mail.outbox), 1)
|
||||||
|
message = mail.outbox[0]
|
||||||
|
self.assertEqual(message.subject, '[Django] Subject')
|
||||||
|
self.assertEqual(message.body, 'Content')
|
||||||
|
self.assertEqual(message.alternatives, [('HTML Content', 'text/html')])
|
||||||
|
|
||||||
|
settings.ADMINS = old_admins
|
||||||
|
|
||||||
|
def test_html_mail_managers(self):
|
||||||
|
"""Test html_message argument to mail_admins and mail_managers"""
|
||||||
|
old_managers = settings.MANAGERS
|
||||||
|
settings.MANAGERS = [('nobody','nobody@example.com')]
|
||||||
|
|
||||||
|
mail.outbox = []
|
||||||
|
mail_managers('Subject', 'Content', html_message='HTML Content')
|
||||||
|
self.assertEqual(len(mail.outbox), 1)
|
||||||
|
message = mail.outbox[0]
|
||||||
|
self.assertEqual(message.subject, '[Django] Subject')
|
||||||
|
self.assertEqual(message.body, 'Content')
|
||||||
|
self.assertEqual(message.alternatives, [('HTML Content', 'text/html')])
|
||||||
|
|
||||||
|
settings.MANAGERS = old_managers
|
||||||
|
|
||||||
def test_idn_validation(self):
|
def test_idn_validation(self):
|
||||||
"""Test internationalized email adresses"""
|
"""Test internationalized email adresses"""
|
||||||
# Regression for #14301.
|
# Regression for #14301.
|
||||||
|
|
Loading…
Reference in New Issue