mirror of https://github.com/django/django.git
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
|
import re
|
||||||
from django.conf import settings
|
|
||||||
from django.utils.encoding import force_unicode
|
from django.utils.encoding import force_unicode
|
||||||
from django.utils.functional import allow_lazy
|
from django.utils.functional import allow_lazy
|
||||||
from django.utils.translation import ugettext_lazy
|
from django.utils.translation import ugettext_lazy
|
||||||
|
@ -37,24 +36,25 @@ def wrap(text, width):
|
||||||
return u''.join(_generator())
|
return u''.join(_generator())
|
||||||
wrap = allow_lazy(wrap, unicode)
|
wrap = allow_lazy(wrap, unicode)
|
||||||
|
|
||||||
def truncate_words(s, num):
|
def truncate_words(s, num, end_text='...'):
|
||||||
"Truncates a string after a certain number of words."
|
"""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)
|
s = force_unicode(s)
|
||||||
length = int(num)
|
length = int(num)
|
||||||
words = s.split()
|
words = s.split()
|
||||||
if len(words) > length:
|
if len(words) > length:
|
||||||
words = words[:length]
|
words = words[:length]
|
||||||
if not words[-1].endswith('...'):
|
if not words[-1].endswith(end_text):
|
||||||
words.append('...')
|
words.append(end_text)
|
||||||
return u' '.join(words)
|
return u' '.join(words)
|
||||||
truncate_words = allow_lazy(truncate_words, unicode)
|
truncate_words = allow_lazy(truncate_words, unicode)
|
||||||
|
|
||||||
def truncate_html_words(s, num):
|
def truncate_html_words(s, num, end_text='...'):
|
||||||
"""
|
"""Truncates html to a certain number of words (not counting tags and
|
||||||
Truncates html to a certain number of words (not counting tags and
|
|
||||||
comments). Closes opened tags if they were correctly closed in the given
|
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)
|
s = force_unicode(s)
|
||||||
length = int(num)
|
length = int(num)
|
||||||
if length <= 0:
|
if length <= 0:
|
||||||
|
@ -65,7 +65,7 @@ def truncate_html_words(s, num):
|
||||||
re_tag = re.compile(r'<(/)?([^ ]+?)(?: (/)| .*?)?>')
|
re_tag = re.compile(r'<(/)?([^ ]+?)(?: (/)| .*?)?>')
|
||||||
# Count non-HTML words and keep note of open tags
|
# Count non-HTML words and keep note of open tags
|
||||||
pos = 0
|
pos = 0
|
||||||
ellipsis_pos = 0
|
end_text_pos = 0
|
||||||
words = 0
|
words = 0
|
||||||
open_tags = []
|
open_tags = []
|
||||||
while words <= length:
|
while words <= length:
|
||||||
|
@ -78,11 +78,11 @@ def truncate_html_words(s, num):
|
||||||
# It's an actual non-HTML word
|
# It's an actual non-HTML word
|
||||||
words += 1
|
words += 1
|
||||||
if words == length:
|
if words == length:
|
||||||
ellipsis_pos = pos
|
end_text_pos = pos
|
||||||
continue
|
continue
|
||||||
# Check for tag
|
# Check for tag
|
||||||
tag = re_tag.match(m.group(0))
|
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
|
# Don't worry about non tags or tags after our truncate point
|
||||||
continue
|
continue
|
||||||
closing_tag, tagname, self_closing = tag.groups()
|
closing_tag, tagname, self_closing = tag.groups()
|
||||||
|
@ -104,7 +104,9 @@ def truncate_html_words(s, num):
|
||||||
if words <= length:
|
if words <= length:
|
||||||
# Don't try to close tags if we don't need to truncate
|
# Don't try to close tags if we don't need to truncate
|
||||||
return s
|
return s
|
||||||
out = s[:ellipsis_pos] + ' ...'
|
out = s[:end_text_pos]
|
||||||
|
if end_text:
|
||||||
|
out += ' ' + end_text
|
||||||
# Close any tags still open
|
# Close any tags still open
|
||||||
for tag in open_tags:
|
for tag in open_tags:
|
||||||
out += '</%s>' % tag
|
out += '</%s>' % tag
|
||||||
|
|
|
@ -4,7 +4,7 @@ Tests for django.utils.
|
||||||
|
|
||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
|
|
||||||
from django.utils import html, checksums
|
from django.utils import html, checksums, text
|
||||||
from django.utils.functional import SimpleLazyObject
|
from django.utils.functional import SimpleLazyObject
|
||||||
|
|
||||||
import timesince
|
import timesince
|
||||||
|
@ -244,6 +244,24 @@ class TestUtilsSimpleLazyObject(TestCase):
|
||||||
s3 = copy.deepcopy(s)
|
s3 = copy.deepcopy(s)
|
||||||
self.assertEqual(s3, complex_object())
|
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__":
|
if __name__ == "__main__":
|
||||||
import doctest
|
import doctest
|
||||||
doctest.testmod()
|
doctest.testmod()
|
||||||
|
|
Loading…
Reference in New Issue