Ticket #6667: mail_wrapping.2.diff
File mail_wrapping.2.diff, 6.9 KB (added by , 17 years ago) |
---|
-
tests/regressiontests/utils/tests.py
8 8 9 9 import timesince 10 10 import datastructures 11 import mail 11 12 12 13 # Extra tests 13 14 __test__ = { 14 15 'timesince': timesince, 15 16 'datastructures': datastructures, 17 'mail': mail, 16 18 } 17 19 18 20 class TestUtilsHtml(TestCase): -
tests/regressiontests/utils/mail.py
1 r""" 2 >>> from django.utils.mail import wrap 3 4 >>> wrap('1234 67 9', 100) 5 u'1234 67 9' 6 >>> wrap('1234 67 9', 9) 7 u'1234 67 9' 8 >>> wrap('1234 67 9', 8) 9 u'1234 67 \n 9' 10 11 >>> wrap('short\na long line', 7) 12 u'short\na long \n line' 13 14 >>> wrap('email wrapping: \nStrips spaces before lines and uses soft spacing for line breaks', 30) 15 u'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:]) 21 True 22 >>> out = wrap('a %s word' % long_word, 11) 23 >>> '%s...%s' % (out[:20], out[-20:]) 24 u'a \nl012345678901234...9012345 \n6789ng word' 25 26 >>> wrap('>test me out', 10) 27 u'>test me \n> out' 28 >>> wrap('test me >out', 10) 29 u'test me \n >out' 30 31 >>> wrap('> one\n>> two which will wrap\n>>> three', 15) 32 u'> one\n>> two which \n>> will wrap\n>>> three' 33 34 >>> wrap('preserve whitespace good and\n proper\n', 9) 35 u'preserve \n \nwhitespace \n good \nand\n proper\n' 36 37 >>> wrap('do not touch -- \nreal signatures\n-- \nThe boss', 13) 38 u'do not touch \n--\nreal \nsignatures\n-- \nThe boss' 39 """ -
django/core/mail.py
4 4 5 5 from django.conf import settings 6 6 from django.utils.encoding import smart_str, force_unicode 7 from django.utils.mail import wrap 7 8 from email import Charset, Encoders 8 9 from email.MIMEText import MIMEText 9 10 from email.MIMEMultipart import MIMEMultipart … … 86 87 return name, val 87 88 88 89 class 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 89 104 def __setitem__(self, name, val): 90 105 name, val = forbid_multi_line_headers(name, val) 91 106 MIMEText.__setitem__(self, name, val) … … 190 205 encoding = None # None => use settings default 191 206 192 207 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): 194 209 """ 195 210 Initialise a single email message (which can be sent to multiple 196 211 recipients). … … 213 228 self.attachments = attachments or [] 214 229 self.extra_headers = headers or {} 215 230 self.connection = connection 231 self.wrap_text = wrap_text 216 232 217 233 def get_connection(self, fail_silently=False): 218 234 if not self.connection: … … 221 237 222 238 def message(self): 223 239 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) 225 243 if self.attachments: 226 244 body_msg = msg 227 245 msg = SafeMIMEMultipart(_subtype=self.multipart_subtype) … … 354 372 EmailMessage(settings.EMAIL_SUBJECT_PREFIX + subject, message, 355 373 settings.SERVER_EMAIL, [a[1] for a in 356 374 settings.MANAGERS]).send(fail_silently=fail_silently) 357 -
django/utils/mail.py
1 import re 2 from django.utils.encoding import force_unicode 3 from django.utils.functional import allow_lazy 4 5 def 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()) 56 wrap = allow_lazy(wrap, unicode)