Ticket #6667: mail_wrapping.diff
File mail_wrapping.diff, 9.8 KB (added by , 17 years ago) |
---|
-
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) -
django/utils/text.py
10 10 11 11 def wrap(text, width): 12 12 """ 13 A word-wrap function that preserves existing line breaks and most spaces in 14 the text. Expects that existing line breaks are posix newlines. 13 A word-wrap function that preserves existing line breaks. Expects that 14 existing line breaks are posix newlines. 15 16 All white space is preserved except added line breaks consume the space on 17 which they break the line. 18 19 Long words are not wrapped, so the output text may have lines longer than 20 ``width``. 15 21 """ 16 22 text = force_unicode(text) 17 23 def _generator(): 18 it = iter(text.split(' ')) 19 word = it.next() 20 yield word 21 pos = len(word) - word.rfind('\n') - 1 22 for word in it: 23 if "\n" in word: 24 lines = word.split('\n') 25 else: 26 lines = (word,) 27 pos += len(lines[0]) + 1 28 if pos > width: 29 yield '\n' 30 pos = len(lines[-1]) 31 else: 32 yield ' ' 33 if len(lines) > 1: 34 pos = len(lines[-1]) 35 yield word 24 for line in text.splitlines(True): # True keeps trailing linebreaks 25 quote = '' 26 max_width = (line.endswith('\n') and width + 1 or width) 27 while len(line) > max_width: 28 space = line[:max_width+1].rfind(' ') + 1 29 if space == 0: 30 space = line.find(' ') + 1 31 if space == 0: 32 yield line 33 line = '' 34 break 35 yield '%s\n' % line[:space-1] 36 line = line[space:] 37 max_width = (line.endswith('\n') and width + 1 or width) 38 if line: 39 yield line 36 40 return u''.join(_generator()) 37 41 wrap = allow_lazy(wrap, unicode) 38 42 -
tests/regressiontests/utils/tests.py
8 8 9 9 import timesince 10 10 import datastructures 11 import text 12 import mail 11 13 12 14 # Extra tests 13 15 __test__ = { 14 16 'timesince': timesince, 15 17 'datastructures': datastructures, 18 'text': text, 19 'mail': mail, 16 20 } 17 21 18 22 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 """ -
tests/regressiontests/utils/text.py
1 r""" 2 >>> from django.utils.text 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\n9' 10 11 >>> wrap('short\na long line', 7) 12 u'short\na long\nline' 13 14 >>> wrap('do-not-break-long-words please? ok', 8) 15 u'do-not-break-long-words\nplease?\nok' 16 17 >>> long_word = 'l%sng' % ('0123456789'*100) 18 >>> wrap(long_word, 50) == long_word 19 True 20 >>> out = wrap('a %s word' % long_word, 10) 21 >>> '%s...%s' % (out[:20], out[-20:]) 22 u'a\nl01234567890123456...7890123456789ng\nword' 23 """