Ticket #6667: mail_wrapping.2.diff

File mail_wrapping.2.diff, 6.9 KB (added by Chris Beaven, 17 years ago)

patch devoid of the text wrap optimisation

  • tests/regressiontests/utils/tests.py

     
    88
    99import timesince
    1010import datastructures
     11import mail
    1112
    1213# Extra tests
    1314__test__ = {
    1415    'timesince': timesince,
    1516    'datastructures': datastructures,
     17    'mail': mail,
    1618}
    1719
    1820class TestUtilsHtml(TestCase):
  • tests/regressiontests/utils/mail.py

     
     1r"""
     2>>> from django.utils.mail import wrap
     3
     4>>> wrap('1234 67 9', 100)
     5u'1234 67 9'
     6>>> wrap('1234 67 9', 9)
     7u'1234 67 9'
     8>>> wrap('1234 67 9', 8)
     9u'1234 67 \n 9'
     10
     11>>> wrap('short\na long line', 7)
     12u'short\na long \n line'
     13
     14>>> wrap('email wrapping: \nStrips spaces before lines and uses soft spacing for line breaks', 30)
     15u'email wrapping:\nStrips spaces before lines  \nand uses soft spacing for  \nline breaks'
     16
     17>>> long_word = 'l%sng' % ('0123456789'*100)
     18
     19# Long word broken at 997 (998 - 1 char for space)
     20>>> wrap(long_word) == '%s \n%s' % (long_word[:997], long_word[997:])
     21True
     22>>> out = wrap('a %s word' % long_word, 11)
     23>>> '%s...%s' % (out[:20], out[-20:])
     24u'a  \nl012345678901234...9012345 \n6789ng word'
     25
     26>>> wrap('>test me out', 10)
     27u'>test me  \n> out'
     28>>> wrap('test me >out', 10)
     29u'test me  \n >out'
     30
     31>>> wrap('> one\n>> two which will wrap\n>>> three', 15)
     32u'> one\n>> two which  \n>> will wrap\n>>> three'
     33
     34>>> wrap('preserve  whitespace good  and\n  proper\n', 9)
     35u'preserve  \n  \nwhitespace \n good   \nand\n  proper\n'
     36
     37>>> wrap('do not touch -- \nreal signatures\n-- \nThe boss', 13)
     38u'do not touch  \n--\nreal  \nsignatures\n-- \nThe boss'
     39"""
  • django/core/mail.py

     
    44
    55from django.conf import settings
    66from django.utils.encoding import smart_str, force_unicode
     7from django.utils.mail import wrap
    78from email import Charset, Encoders
    89from email.MIMEText import MIMEText
    910from email.MIMEMultipart import MIMEMultipart
     
    8687    return name, val
    8788
    8889class SafeMIMEText(MIMEText):
     90    def __init__(self, _text, _subtype='plain', *args, **kwargs):
     91        format_flowed = False
     92        if 'wrap_text' in kwargs:
     93            if kwargs.pop('wrap_text') and _subtype == 'plain':
     94                _text = wrap(_text)
     95                format_flowed = True
     96        if 'format_flowed' in kwargs:
     97            if kwargs.pop('format_flowed') and _subtype == 'plain':
     98                format_flowed = True
     99        MIMEText.__init__(self, _text, _subtype, *args, **kwargs)
     100        if format_flowed:
     101            self.set_param('format', 'flowed', 'Content-Type')
     102            self.set_param('delsp', 'yes', 'Content-Type')
     103
    89104    def __setitem__(self, name, val):
    90105        name, val = forbid_multi_line_headers(name, val)
    91106        MIMEText.__setitem__(self, name, val)
     
    190205    encoding = None     # None => use settings default
    191206
    192207    def __init__(self, subject='', body='', from_email=None, to=None, bcc=None,
    193             connection=None, attachments=None, headers=None):
     208            connection=None, attachments=None, headers=None, wrap_text=False):
    194209        """
    195210        Initialise a single email message (which can be sent to multiple
    196211        recipients).
     
    213228        self.attachments = attachments or []
    214229        self.extra_headers = headers or {}
    215230        self.connection = connection
     231        self.wrap_text = wrap_text
    216232
    217233    def get_connection(self, fail_silently=False):
    218234        if not self.connection:
     
    221237
    222238    def message(self):
    223239        encoding = self.encoding or settings.DEFAULT_CHARSET
    224         msg = SafeMIMEText(smart_str(self.body, settings.DEFAULT_CHARSET), self.content_subtype, encoding)
     240        msg = SafeMIMEText(smart_str(self.body, settings.DEFAULT_CHARSET),
     241                           self.content_subtype, encoding,
     242                           wrap_text=self.wrap_text)
    225243        if self.attachments:
    226244            body_msg = msg
    227245            msg = SafeMIMEMultipart(_subtype=self.multipart_subtype)
     
    354372    EmailMessage(settings.EMAIL_SUBJECT_PREFIX + subject, message,
    355373            settings.SERVER_EMAIL, [a[1] for a in
    356374                settings.MANAGERS]).send(fail_silently=fail_silently)
    357 
  • django/utils/mail.py

     
     1import re
     2from django.utils.encoding import force_unicode
     3from django.utils.functional import allow_lazy
     4
     5def wrap(text, width=78):
     6    """
     7    An email based word-wrap function that preserves existing line breaks and
     8    adds soft line breaks (' \n').
     9
     10    Long words are not wrapped, so the output text may have lines longer than
     11    ``width``.
     12
     13    Trailing spaces are stripped from each line so they are not confused with
     14    soft line breaks. All other white space is preserved.
     15
     16    Soft-broken lines are parsed for quotes and space-stuffed in accordance
     17    with RFC 3676. A break is forced at 998 characters (RFC 2822 2.1.1).
     18    """
     19    text = force_unicode(text)
     20    # Signature lines aren't stripped -- see RFC 3676 4.3
     21    text = re.sub(r'(?m)(?<!^--) +$', '', text)
     22    def _parse_line(line, quote, new_line):
     23        if new_line and line.startswith('>'):
     24            quote = re.match('>+', line).group()
     25        elif quote:
     26            line = '%s %s' % (quote, line)
     27        elif (line.startswith('>') or line.startswith('From ')):
     28            # Space stuff -- see RFC 3676 4.4
     29            line = ' %s' % line
     30        max_width = (line.endswith('\n') and width + 1 or width)
     31        return line, quote, max_width
     32    def _generator():
     33        for line in text.splitlines(True):   # True keeps trailing linebreaks
     34            quote = ''
     35            line, quote, max_width = _parse_line(line, quote, new_line=True)
     36            while len(line) > max_width:
     37                space = line[:max_width].rfind(' ') + 1
     38                if space == 0:
     39                    space = line.find(' ') + 1
     40                    space = min(space, 998)
     41                    if space == 0:
     42                        if len(line) > 998:
     43                            space = 998
     44                        else:
     45                            yield line
     46                            line = ''
     47                            break
     48                if space >= max_width:
     49                    space -= 1
     50                yield '%s \n' % line[:space]
     51                line = line[space:]
     52                line, quote, max_width = _parse_line(line, quote, False)
     53            if line:
     54                yield line
     55    return u''.join(_generator())
     56wrap = allow_lazy(wrap, unicode)
Back to Top