Ticket #10863: 10863-2-html-error-email.diff

File 10863-2-html-error-email.diff, 10.4 KB (added by brodie, 5 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 
    8383    return connection.send_messages(messages)
    8484
    8585
    86 def mail_admins(subject, message, fail_silently=False, connection=None):
     86def mail_admins(subject, message, fail_silently=False, connection=None,
     87                html_message=None):
    8788    """Sends a message to the admins, as defined by the ADMINS setting."""
    8889    if not settings.ADMINS:
    8990        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)
    9397
    9498
    95 def mail_managers(subject, message, fail_silently=False, connection=None):
     99def mail_managers(subject, message, fail_silently=False, connection=None,
     100                  html_message=None):
    96101    """Sends a message to the managers, as defined by the MANAGERS setting."""
    97102    if not settings.MANAGERS:
    98103        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)
    102110
    103111
    104112class SMTPConnection(_SMTPConnection):
  • django/utils/log.py

    diff --git a/django/utils/log.py b/django/utils/log.py
    a b class AdminEmailHandler(logging.Handler) 
    5656    def emit(self, record):
    5757        import traceback
    5858        from django.conf import settings
     59        from django.views.debug import ExceptionReporter
    5960
    6061        try:
    6162            if sys.version_info < (2,5):
    class AdminEmailHandler(logging.Handler) 
    7576            request_repr = repr(request)
    7677        except:
    7778            subject = 'Error: Unknown URL'
     79            request = None
    7880            request_repr = "Request repr() unavailable"
    7981
    8082        if record.exc_info:
     83            exc_info = record.exc_info
    8184            stack_trace = '\n'.join(traceback.format_exception(*record.exc_info))
    8285        else:
     86            exc_info = ()
    8387            stack_trace = 'No stack trace available'
    8488
    8589        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: 
    6262    """
    6363    A class to organize and coordinate reporting on exceptions.
    6464    """
    65     def __init__(self, request, exc_type, exc_value, tb):
     65    def __init__(self, request, exc_type, exc_value, tb, is_email=False):
    6666        self.request = request
    6767        self.exc_type = exc_type
    6868        self.exc_value = exc_value
    6969        self.tb = tb
     70        self.is_email = is_email
    7071
    7172        self.template_info = None
    7273        self.template_does_not_exist = False
    class ExceptionReporter: 
    118119        from django import get_version
    119120        t = Template(TECHNICAL_500_TEMPLATE, name='Technical 500 template')
    120121        c = Context({
     122            'is_email': self.is_email,
    121123            'exception_type': self.exc_type.__name__,
    122124            'exception_value': smart_unicode(self.exc_value, errors='replace'),
    123125            'unicode_hint': unicode_hint,
    TECHNICAL_500_TEMPLATE = """ 
    353355    span.commands a:link {color:#5E5694;}
    354356    pre.exception_value { font-family: sans-serif; color: #666; font-size: 1.5em; margin: 10px 0 10px 0; }
    355357  </style>
     358  {% if not is_email %}
    356359  <script type="text/javascript">
    357360  //<!--
    358361    function getElementsByClassName(oElm, strTagName, strClassName){
    TECHNICAL_500_TEMPLATE = """ 
    408411    }
    409412    //-->
    410413  </script>
     414  {% endif %}
    411415</head>
    412416<body>
    413417<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>
    415419  <pre class="exception_value">{{ exception_value|force_escape }}</pre>
    416420  <table class="meta">
    417421    <tr>
    TECHNICAL_500_TEMPLATE = """ 
    498502</div>
    499503{% endif %}
    500504<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>
    502506  {% autoescape off %}
    503507  <div id="browserTraceback">
    504508    <ul class="traceback">
    TECHNICAL_500_TEMPLATE = """ 
    545549  </div>
    546550  {% endautoescape %}
    547551  <form action="http://dpaste.com/" name="pasteform" id="pasteform" method="post">
     552{% if not is_email %}
    548553  <div id="pastebinTraceback" class="pastebin">
    549554    <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 %}">
    551556    <input type="hidden" name="source" value="Django Dpaste Agent">
    552557    <input type="hidden" name="poster" value="Django">
    553558    <textarea name="content" id="traceback_area" cols="140" rows="25">
    554559Environment:
    555560
     561{% if request %}
    556562Request Method: {{ request.META.REQUEST_METHOD }}
    557563Request URL: {{ request.build_absolute_uri|escape }}
     564{% endif %}
    558565Django Version: {{ django_version_info }}
    559566Python Version: {{ sys_version_info }}
    560567Installed Applications:
    Traceback: 
    581588{% for frame in frames %}File "{{ frame.filename|escape }}" in {{ frame.function|escape }}
    582589{% if frame.context_line %}  {{ frame.lineno }}. {{ frame.context_line|escape }}{% endif %}
    583590{% endfor %}
    584 Exception Type: {{ exception_type|escape }} at {{ request.path_info|escape }}
     591Exception Type: {{ exception_type|escape }}{% if request %} at {{ request.path_info|escape }}{% endif %}
    585592Exception Value: {{ exception_value|force_escape }}
    586593</textarea>
    587594  <br><br>
    Exception Value: {{ exception_value|forc 
    589596  </div>
    590597</form>
    591598</div>
     599{% endif %}
    592600
    593601<div id="requestinfo">
    594602  <h2>Request information</h2>
    595603
     604{% if request %}
    596605  <h3 id="get-info">GET</h3>
    597606  {% if request.GET %}
    598607    <table class="req">
    Exception Value: {{ exception_value|forc 
    698707      {% endfor %}
    699708    </tbody>
    700709  </table>
     710{% endif %}
    701711
    702712  <h3 id="settings-info">Settings</h3>
    703713  <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 
    109109mail_admins()
    110110=============
    111111
    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)
    113113
    114114``django.core.mail.mail_admins()`` is a shortcut for sending an e-mail to the
    115115site admins, as defined in the :setting:`ADMINS` setting.
    The "From:" header of the e-mail will be 
    122122
    123123This method exists for convenience and readability.
    124124
     125.. versionchanged:: 1.3
     126
     127If ``html_message`` is provided, the resulting e-mail will be a
     128multipart/alternative e-mail with ``message`` as the "text/plain"
     129content type and ``html_message`` as the "text/html" content type.
     130
    125131mail_managers()
    126132===============
    127133
    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)
    129135
    130136``django.core.mail.mail_managers()`` is just like ``mail_admins()``, except it
    131137sends 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): 
    364364        settings.ADMINS = old_admins
    365365        settings.MANAGERS = old_managers
    366366
     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
    367392    def test_idn_validation(self):
    368393        """Test internationalized email adresses"""
    369394        # Regression for #14301.
Back to Top