Ticket #5025: 5025.diff

File 5025.diff, 6.7 KB (added by Chris Beaven, 13 years ago)
  • django/template/defaultfilters.py

    diff --git a/django/template/defaultfilters.py b/django/template/defaultfilters.py
    index 60fa59e..8f7b1b7 100644
    a b def title(value):  
    239239title.is_safe = True
    240240title = stringfilter(title)
    241241
     242def truncatechars(value, arg):
     243    """
     244    Truncates a string after a certain number of characters.
     245   
     246    Argument: Number of characters to truncate after.
     247    """
     248    from django.utils.text import truncate_chars
     249    try:
     250        length = int(arg)
     251    except ValueError: # Invalid literal for int().
     252        return value # Fail silently.
     253    return truncate_chars(value, length)
     254truncatechars.is_safe = True
     255truncatechars = stringfilter(truncatechars)
     256
    242257def truncatewords(value, arg):
    243258    """
    244259    Truncates a string after a certain number of words.
  • django/utils/text.py

    diff --git a/django/utils/text.py b/django/utils/text.py
    index 00c999c..a618a96 100644
    a b  
    11import re
     2import unicodedata
    23from django.utils.encoding import force_unicode
    34from django.utils.functional import allow_lazy
    45from django.utils.translation import ugettext_lazy, ugettext as _
    def wrap(text, width):  
    3637    return u''.join(_generator())
    3738wrap = allow_lazy(wrap, unicode)
    3839
     40def truncate_chars(s, num, end_text=ugettext_lazy('...')):
     41    """Truncates a string to be no longer than the specified number of
     42    characters. Takes an optional argument of what should be used to notify
     43    that the string has been truncated, defaulting to a translatable string of
     44    an ellipsis (...)
     45    """
     46    s = unicodedata.normalize('NFC', force_unicode(s))
     47    length = int(num)
     48    end_text = unicodedata.normalize('NFC', force_unicode(end_text))
     49
     50    # Calculate the length to truncate to (max length - end_text length)
     51    truncate_len = length
     52    for char in end_text:
     53        if not unicodedata.combining(char):
     54            truncate_len -= 1
     55            if truncate_len == 0:
     56                break
     57
     58    s_len = 0
     59    end_index = None
     60    for i, char in enumerate(s):
     61        if unicodedata.combining(char):
     62            # Don't consider combining characters as adding to the string
     63            # length
     64            continue
     65        s_len += 1
     66        if end_index is None and s_len > truncate_len:
     67            end_index = i
     68        if s_len > length:
     69            # Return the truncated string
     70            return u'%s%s' % (s[:end_index or 0], end_text)
     71
     72    # Return the original string since no truncation was necessary
     73    return s
     74truncate_chars = allow_lazy(truncate_chars, unicode)
     75
    3976def truncate_words(s, num, end_text='...'):
    4077    """Truncates a string after a certain number of words. Takes an optional
    4178    argument of what should be used to notify that the string has been
  • docs/ref/templates/builtins.txt

    diff --git a/docs/ref/templates/builtins.txt b/docs/ref/templates/builtins.txt
    index 29bed25..efddf02 100644
    a b For example::  
    20242024
    20252025If ``value`` is ``"my first post"``, the output will be ``"My First Post"``.
    20262026
     2027.. templatefilter:: truncatechars
     2028
     2029truncatechars
     2030~~~~~~~~~~~~~
     2031
     2032Truncates a string if it is longer than the specified number of characters.
     2033Truncated strings will end with a translatable ellipsis sequence ("...").
     2034
     2035**Argument:** Number of characters to truncate to
     2036
     2037For example::
     2038
     2039    {{ value|truncatechars:9 }}
     2040
     2041If ``value`` is ``"Joel is a slug"``, the output will be ``"Joel i..."``.
     2042
    20272043.. templatefilter:: truncatewords
    20282044
    20292045truncatewords
  • docs/releases/1.4.txt

    diff --git a/docs/releases/1.4.txt b/docs/releases/1.4.txt
    index 496a4c9..d57cc49 100644
    a b A new helper function,  
    6060``template.Library`` to ease the creation of template tags that store some
    6161data in a specified context variable.
    6262
     63``truncatechars`` template filter
     64~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     65
     66Added a filter which truncates a string to be no longer than the specified
     67number of characters. Truncated strings end with a translatable ellipsis
     68sequence ("...").
     69
    6370.. _backwards-incompatible-changes-1.4:
    6471
    6572Backwards incompatible changes in 1.4
  • tests/regressiontests/utils/text.py

    diff --git a/tests/regressiontests/utils/text.py b/tests/regressiontests/utils/text.py
    index f565d87..3f0a189 100644
    a b  
     1# -*- coding: utf-8 -*-
    12import unittest
    23
    34from django.utils import text
    45
    56class TestUtilsText(unittest.TestCase):
    67
     8    def test_truncate_chars(self):
     9        self.assertEqual(u'The quick brown fox jumped over the lazy dog.',
     10            text.truncate_chars(u'The quick brown fox jumped over the lazy dog.', 100))
     11        self.assertEqual(u'The quick brown fox ...',
     12            text.truncate_chars('The quick brown fox jumped over the lazy dog.', 23))
     13        self.assertEqual(u'The quick brown fo.....',
     14            text.truncate_chars('The quick brown fox jumped over the lazy dog.', 23, '.....'))
     15       
     16        # Ensure that we normalize our unicode data first
     17        nfc = u'o\xfco\xfco\xfco\xfc'
     18        nfd = u'ou\u0308ou\u0308ou\u0308ou\u0308'
     19        self.assertEqual(u'oüoüoüoü', text.truncate_chars(nfc, 8))
     20        self.assertEqual(u'oüoüoüoü', text.truncate_chars(nfd, 8))
     21        self.assertEqual(u'oü...', text.truncate_chars(nfc, 5))
     22        self.assertEqual(u'oü...', text.truncate_chars(nfd, 5))
     23       
     24        # Ensure the final length is calculated correctly when there are
     25        # combining characters with no precomposed form, and that combining
     26        # characters are not split up.
     27        self.assertEqual(u'-B\u030A...',
     28            text.truncate_chars(u'-B\u030AB\u030A----8', 5))
     29        self.assertEqual(u'-B\u030AB\u030A-...',
     30            text.truncate_chars(u'-B\u030AB\u030A----8', 7))
     31        self.assertEqual(u'-B\u030AB\u030A----8',
     32            text.truncate_chars(u'-B\u030AB\u030A----8', 8))
     33
     34        # Ensure the length of the end text is correctly calculated when it
     35        # contains combining characters with no precomposed form.
     36        self.assertEqual(u'---B\u030A',
     37            text.truncate_chars(u'-----', 4, end_text=u'B\u030A'))
     38        self.assertEqual(u'-----',
     39            text.truncate_chars(u'-----', 5, end_text=u'B\u030A'))
     40
     41        # Make a best effort to shorten to the desired length, but requesting
     42        # a length shorter than the ellipsis shouldn't break
     43        self.assertEqual(u'...', text.truncate_chars(u'asdf', 1))
     44
    745    def test_truncate_words(self):
    846        self.assertEqual(u'The quick brown fox jumped over the lazy dog.',
    947            text.truncate_words(u'The quick brown fox jumped over the lazy dog.', 10))
Back to Top