Fixed #6799 - added an `end_text` argument to `truncate_words`/`truncate_html_words`.
This allows customizing the standard "..." end text. Yes, this is technically a feature sneaking in after the deadline, but I just couldn't bring myself to punt it again: we already used that excuse for not getting it into 1.1. Thanks to Adam Fast and Travis Cline for work on this patch. git-svn-id: http://code.djangoproject.com/svn/django/trunk@12431 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
7578981626
commit
1d078be448
|
@ -1,5 +1,4 @@
|
|||
import re
|
||||
from django.conf import settings
|
||||
from django.utils.encoding import force_unicode
|
||||
from django.utils.functional import allow_lazy
|
||||
from django.utils.translation import ugettext_lazy
|
||||
|
@ -37,24 +36,25 @@ def wrap(text, width):
|
|||
return u''.join(_generator())
|
||||
wrap = allow_lazy(wrap, unicode)
|
||||
|
||||
def truncate_words(s, num):
|
||||
"Truncates a string after a certain number of words."
|
||||
def truncate_words(s, num, end_text='...'):
|
||||
"""Truncates a string after a certain number of words. Takes an optional
|
||||
argument of what should be used to notify that the string has been
|
||||
truncated, defaults to ellipsis (...)"""
|
||||
s = force_unicode(s)
|
||||
length = int(num)
|
||||
words = s.split()
|
||||
if len(words) > length:
|
||||
words = words[:length]
|
||||
if not words[-1].endswith('...'):
|
||||
words.append('...')
|
||||
if not words[-1].endswith(end_text):
|
||||
words.append(end_text)
|
||||
return u' '.join(words)
|
||||
truncate_words = allow_lazy(truncate_words, unicode)
|
||||
|
||||
def truncate_html_words(s, num):
|
||||
"""
|
||||
Truncates html to a certain number of words (not counting tags and
|
||||
def truncate_html_words(s, num, end_text='...'):
|
||||
"""Truncates html to a certain number of words (not counting tags and
|
||||
comments). Closes opened tags if they were correctly closed in the given
|
||||
html.
|
||||
"""
|
||||
html. Takes an optional argument of what should be used to notify that the
|
||||
string has been truncated, defaults to ellipsis (...)."""
|
||||
s = force_unicode(s)
|
||||
length = int(num)
|
||||
if length <= 0:
|
||||
|
@ -65,7 +65,7 @@ def truncate_html_words(s, num):
|
|||
re_tag = re.compile(r'<(/)?([^ ]+?)(?: (/)| .*?)?>')
|
||||
# Count non-HTML words and keep note of open tags
|
||||
pos = 0
|
||||
ellipsis_pos = 0
|
||||
end_text_pos = 0
|
||||
words = 0
|
||||
open_tags = []
|
||||
while words <= length:
|
||||
|
@ -78,11 +78,11 @@ def truncate_html_words(s, num):
|
|||
# It's an actual non-HTML word
|
||||
words += 1
|
||||
if words == length:
|
||||
ellipsis_pos = pos
|
||||
end_text_pos = pos
|
||||
continue
|
||||
# Check for tag
|
||||
tag = re_tag.match(m.group(0))
|
||||
if not tag or ellipsis_pos:
|
||||
if not tag or end_text_pos:
|
||||
# Don't worry about non tags or tags after our truncate point
|
||||
continue
|
||||
closing_tag, tagname, self_closing = tag.groups()
|
||||
|
@ -104,7 +104,9 @@ def truncate_html_words(s, num):
|
|||
if words <= length:
|
||||
# Don't try to close tags if we don't need to truncate
|
||||
return s
|
||||
out = s[:ellipsis_pos] + ' ...'
|
||||
out = s[:end_text_pos]
|
||||
if end_text:
|
||||
out += ' ' + end_text
|
||||
# Close any tags still open
|
||||
for tag in open_tags:
|
||||
out += '</%s>' % tag
|
||||
|
|
|
@ -4,7 +4,7 @@ Tests for django.utils.
|
|||
|
||||
from unittest import TestCase
|
||||
|
||||
from django.utils import html, checksums
|
||||
from django.utils import html, checksums, text
|
||||
from django.utils.functional import SimpleLazyObject
|
||||
|
||||
import timesince
|
||||
|
@ -244,6 +244,24 @@ class TestUtilsSimpleLazyObject(TestCase):
|
|||
s3 = copy.deepcopy(s)
|
||||
self.assertEqual(s3, complex_object())
|
||||
|
||||
class TestUtilsText(TestCase):
|
||||
|
||||
def test_truncate_words(self):
|
||||
self.assertEqual(u'The quick brown fox jumped over the lazy dog.',
|
||||
text.truncate_words(u'The quick brown fox jumped over the lazy dog.', 10))
|
||||
self.assertEqual(u'The quick brown fox ...',
|
||||
text.truncate_words('The quick brown fox jumped over the lazy dog.', 4))
|
||||
self.assertEqual(u'The quick brown fox ....',
|
||||
text.truncate_words('The quick brown fox jumped over the lazy dog.', 4, '....'))
|
||||
self.assertEqual(u'<p><strong><em>The quick brown fox jumped over the lazy dog.</em></strong></p>',
|
||||
text.truncate_html_words('<p><strong><em>The quick brown fox jumped over the lazy dog.</em></strong></p>', 10))
|
||||
self.assertEqual(u'<p><strong><em>The quick brown fox ...</em></strong></p>',
|
||||
text.truncate_html_words('<p><strong><em>The quick brown fox jumped over the lazy dog.</em></strong></p>', 4))
|
||||
self.assertEqual(u'<p><strong><em>The quick brown fox ....</em></strong></p>',
|
||||
text.truncate_html_words('<p><strong><em>The quick brown fox jumped over the lazy dog.</em></strong></p>', 4, '....'))
|
||||
self.assertEqual(u'<p><strong><em>The quick brown fox</em></strong></p>',
|
||||
text.truncate_html_words('<p><strong><em>The quick brown fox jumped over the lazy dog.</em></strong></p>', 4, None))
|
||||
|
||||
if __name__ == "__main__":
|
||||
import doctest
|
||||
doctest.testmod()
|
||||
|
|
Loading…
Reference in New Issue