Ticket #10863: 10863-2-html-error-email.diff
File 10863-2-html-error-email.diff, 10.4 KB (added by , 14 years ago) |
---|
-
django/core/mail/__init__.py
# HG changeset patch # User Brodie Rao <brodie@bitheap.org> # Date 1291452498 -39600 # Node ID 876fd3c1a04fb41f27fc552f0171a6d5573caa47 # Parent e08464eca04c6a8ce8c09dc029e8b287a5291903 Added support for sending Django error emails in HTML using the debug exception template diff --git a/django/core/mail/__init__.py b/django/core/mail/__init__.py
a b def send_mass_mail(datatuple, fail_silen 83 83 return connection.send_messages(messages) 84 84 85 85 86 def mail_admins(subject, message, fail_silently=False, connection=None): 86 def mail_admins(subject, message, fail_silently=False, connection=None, 87 html_message=None): 87 88 """Sends a message to the admins, as defined by the ADMINS setting.""" 88 89 if not settings.ADMINS: 89 90 return 90 EmailMessage(u'%s%s' % (settings.EMAIL_SUBJECT_PREFIX, subject), message, 91 settings.SERVER_EMAIL, [a[1] for a in settings.ADMINS], 92 connection=connection).send(fail_silently=fail_silently) 91 mail = EmailMultiAlternatives(u'%s%s' % (settings.EMAIL_SUBJECT_PREFIX, subject), 92 message, settings.SERVER_EMAIL, [a[1] for a in settings.ADMINS], 93 connection=connection) 94 if html_message: 95 mail.attach_alternative(html_message, 'text/html') 96 mail.send(fail_silently=fail_silently) 93 97 94 98 95 def mail_managers(subject, message, fail_silently=False, connection=None): 99 def mail_managers(subject, message, fail_silently=False, connection=None, 100 html_message=None): 96 101 """Sends a message to the managers, as defined by the MANAGERS setting.""" 97 102 if not settings.MANAGERS: 98 103 return 99 EmailMessage(u'%s%s' % (settings.EMAIL_SUBJECT_PREFIX, subject), message, 100 settings.SERVER_EMAIL, [a[1] for a in settings.MANAGERS], 101 connection=connection).send(fail_silently=fail_silently) 104 mail = EmailMultiAlternatives(u'%s%s' % (settings.EMAIL_SUBJECT_PREFIX, subject), 105 message, settings.SERVER_EMAIL, [a[1] for a in settings.ADMINS], 106 connection=connection) 107 if html_message: 108 mail.attach_alternative(html_message, 'text/html') 109 mail.send(fail_silently=fail_silently) 102 110 103 111 104 112 class SMTPConnection(_SMTPConnection): -
django/utils/log.py
diff --git a/django/utils/log.py b/django/utils/log.py
a b class AdminEmailHandler(logging.Handler) 56 56 def emit(self, record): 57 57 import traceback 58 58 from django.conf import settings 59 from django.views.debug import ExceptionReporter 59 60 60 61 try: 61 62 if sys.version_info < (2,5): … … class AdminEmailHandler(logging.Handler) 75 76 request_repr = repr(request) 76 77 except: 77 78 subject = 'Error: Unknown URL' 79 request = None 78 80 request_repr = "Request repr() unavailable" 79 81 80 82 if record.exc_info: 83 exc_info = record.exc_info 81 84 stack_trace = '\n'.join(traceback.format_exception(*record.exc_info)) 82 85 else: 86 exc_info = () 83 87 stack_trace = 'No stack trace available' 84 88 85 89 message = "%s\n\n%s" % (stack_trace, request_repr) 86 mail.mail_admins(subject, message, fail_silently=True) 90 reporter = ExceptionReporter(request, *exc_info, is_email=True) 91 html_message = reporter.get_traceback_html() 92 mail.mail_admins(subject, message, fail_silently=True, 93 html_message=html_message) -
django/views/debug.py
diff --git a/django/views/debug.py b/django/views/debug.py
a b class ExceptionReporter: 62 62 """ 63 63 A class to organize and coordinate reporting on exceptions. 64 64 """ 65 def __init__(self, request, exc_type, exc_value, tb ):65 def __init__(self, request, exc_type, exc_value, tb, is_email=False): 66 66 self.request = request 67 67 self.exc_type = exc_type 68 68 self.exc_value = exc_value 69 69 self.tb = tb 70 self.is_email = is_email 70 71 71 72 self.template_info = None 72 73 self.template_does_not_exist = False … … class ExceptionReporter: 118 119 from django import get_version 119 120 t = Template(TECHNICAL_500_TEMPLATE, name='Technical 500 template') 120 121 c = Context({ 122 'is_email': self.is_email, 121 123 'exception_type': self.exc_type.__name__, 122 124 'exception_value': smart_unicode(self.exc_value, errors='replace'), 123 125 'unicode_hint': unicode_hint, … … TECHNICAL_500_TEMPLATE = """ 353 355 span.commands a:link {color:#5E5694;} 354 356 pre.exception_value { font-family: sans-serif; color: #666; font-size: 1.5em; margin: 10px 0 10px 0; } 355 357 </style> 358 {% if not is_email %} 356 359 <script type="text/javascript"> 357 360 //<!-- 358 361 function getElementsByClassName(oElm, strTagName, strClassName){ … … TECHNICAL_500_TEMPLATE = """ 408 411 } 409 412 //--> 410 413 </script> 414 {% endif %} 411 415 </head> 412 416 <body> 413 417 <div id="summary"> 414 <h1>{{ exception_type }} at {{ request.path_info|escape }}</h1>418 <h1>{{ exception_type }}{% if request %} at {{ request.path_info|escape }}{% endif %}</h1> 415 419 <pre class="exception_value">{{ exception_value|force_escape }}</pre> 416 420 <table class="meta"> 417 421 <tr> … … TECHNICAL_500_TEMPLATE = """ 498 502 </div> 499 503 {% endif %} 500 504 <div id="traceback"> 501 <h2>Traceback <span class="commands"> <a href="#" onclick="return switchPastebinFriendly(this);">Switch to copy-and-paste view</a></span></h2>505 <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> 502 506 {% autoescape off %} 503 507 <div id="browserTraceback"> 504 508 <ul class="traceback"> … … TECHNICAL_500_TEMPLATE = """ 545 549 </div> 546 550 {% endautoescape %} 547 551 <form action="http://dpaste.com/" name="pasteform" id="pasteform" method="post"> 552 {% if not is_email %} 548 553 <div id="pastebinTraceback" class="pastebin"> 549 554 <input type="hidden" name="language" value="PythonConsole"> 550 <input type="hidden" name="title" value="{{ exception_type|escape }} at {{ request.path_info|escape }}">555 <input type="hidden" name="title" value="{{ exception_type|escape }}{% if request %} at {{ request.path_info|escape }}{% endif %}"> 551 556 <input type="hidden" name="source" value="Django Dpaste Agent"> 552 557 <input type="hidden" name="poster" value="Django"> 553 558 <textarea name="content" id="traceback_area" cols="140" rows="25"> 554 559 Environment: 555 560 561 {% if request %} 556 562 Request Method: {{ request.META.REQUEST_METHOD }} 557 563 Request URL: {{ request.build_absolute_uri|escape }} 564 {% endif %} 558 565 Django Version: {{ django_version_info }} 559 566 Python Version: {{ sys_version_info }} 560 567 Installed Applications: … … Traceback: 581 588 {% for frame in frames %}File "{{ frame.filename|escape }}" in {{ frame.function|escape }} 582 589 {% if frame.context_line %} {{ frame.lineno }}. {{ frame.context_line|escape }}{% endif %} 583 590 {% endfor %} 584 Exception Type: {{ exception_type|escape }} at {{ request.path_info|escape }}591 Exception Type: {{ exception_type|escape }}{% if request %} at {{ request.path_info|escape }}{% endif %} 585 592 Exception Value: {{ exception_value|force_escape }} 586 593 </textarea> 587 594 <br><br> … … Exception Value: {{ exception_value|forc 589 596 </div> 590 597 </form> 591 598 </div> 599 {% endif %} 592 600 593 601 <div id="requestinfo"> 594 602 <h2>Request information</h2> 595 603 604 {% if request %} 596 605 <h3 id="get-info">GET</h3> 597 606 {% if request.GET %} 598 607 <table class="req"> … … Exception Value: {{ exception_value|forc 698 707 {% endfor %} 699 708 </tbody> 700 709 </table> 710 {% endif %} 701 711 702 712 <h3 id="settings-info">Settings</h3> 703 713 <h4>Using settings module <code>{{ settings.SETTINGS_MODULE }}</code></h4> -
docs/topics/email.txt
diff --git a/docs/topics/email.txt b/docs/topics/email.txt
a b a single connection for all of its messa 109 109 mail_admins() 110 110 ============= 111 111 112 .. function:: mail_admins(subject, message, fail_silently=False, connection=None )112 .. function:: mail_admins(subject, message, fail_silently=False, connection=None, html_message=None) 113 113 114 114 ``django.core.mail.mail_admins()`` is a shortcut for sending an e-mail to the 115 115 site admins, as defined in the :setting:`ADMINS` setting. … … The "From:" header of the e-mail will be 122 122 123 123 This method exists for convenience and readability. 124 124 125 .. versionchanged:: 1.3 126 127 If ``html_message`` is provided, the resulting e-mail will be a 128 multipart/alternative e-mail with ``message`` as the "text/plain" 129 content type and ``html_message`` as the "text/html" content type. 130 125 131 mail_managers() 126 132 =============== 127 133 128 .. function:: mail_managers(subject, message, fail_silently=False, connection=None )134 .. function:: mail_managers(subject, message, fail_silently=False, connection=None, html_message=None) 129 135 130 136 ``django.core.mail.mail_managers()`` is just like ``mail_admins()``, except it 131 137 sends an e-mail to the site managers, as defined in the :setting:`MANAGERS` -
tests/regressiontests/mail/tests.py
diff --git a/tests/regressiontests/mail/tests.py b/tests/regressiontests/mail/tests.py
a b class MailTests(TestCase): 364 364 settings.ADMINS = old_admins 365 365 settings.MANAGERS = old_managers 366 366 367 def test_html_mail(self): 368 """Test html_message argument to mail_admins and mail_managers""" 369 old_admins = settings.ADMINS 370 old_managers = settings.MANAGERS 371 settings.ADMINS = settings.MANAGERS = [('nobody','nobody@example.com')] 372 373 mail.outbox = [] 374 mail_managers('Subject', 'Content', html_message='HTML Content') 375 self.assertEqual(len(mail.outbox), 1) 376 message = mail.outbox[0] 377 self.assertEqual(message.subject, '[Django] Subject') 378 self.assertEqual(message.body, 'Content') 379 self.assertEqual(message.alternatives, [('HTML Content', 'text/html')]) 380 381 mail.outbox = [] 382 mail_admins('Subject', 'Content', html_message='HTML Content') 383 self.assertEqual(len(mail.outbox), 1) 384 message = mail.outbox[0] 385 self.assertEqual(message.subject, '[Django] Subject') 386 self.assertEqual(message.body, 'Content') 387 self.assertEqual(message.alternatives, [('HTML Content', 'text/html')]) 388 389 settings.ADMINS = old_admins 390 settings.MANAGERS = old_managers 391 367 392 def test_idn_validation(self): 368 393 """Test internationalized email adresses""" 369 394 # Regression for #14301.