Django

Code

root/django/trunk/django/template/defaultfilters.py

Revision 10543, 27.5 kB (checked in by mtredinnick, 3 months ago)

Fixed #10675 -- Added unicode paragraph and line-sep handling to escapejs.

There were a couple of line breaking Unicode characters (\u2028 and
\u2029) that cause Javascript errors, at least in Firefox, if not
escaped. So now we do so. Based on a patch from rleland.

  • Property svn:eol-style set to native
  • Property svn:keywords set to LastChangedRevision
Line 
1 """Default variable filters."""
2
3 import re
4
5 try:
6     from decimal import Decimal, InvalidOperation, ROUND_HALF_UP
7 except ImportError:
8     from django.utils._decimal import Decimal, InvalidOperation, ROUND_HALF_UP
9
10 import random as random_module
11 try:
12     from functools import wraps
13 except ImportError:
14     from django.utils.functional import wraps  # Python 2.3, 2.4 fallback.
15
16 from django.template import Variable, Library
17 from django.conf import settings
18 from django.utils.translation import ugettext, ungettext
19 from django.utils.encoding import force_unicode, iri_to_uri
20 from django.utils.safestring import mark_safe, SafeData
21
22 register = Library()
23
24 #######################
25 # STRING DECORATOR    #
26 #######################
27
28 def stringfilter(func):
29     """
30     Decorator for filters which should only receive unicode objects. The object
31     passed as the first positional argument will be converted to a unicode
32     object.
33     """
34     def _dec(*args, **kwargs):
35         if args:
36             args = list(args)
37             args[0] = force_unicode(args[0])
38             if isinstance(args[0], SafeData) and getattr(func, 'is_safe', False):
39                 return mark_safe(func(*args, **kwargs))
40         return func(*args, **kwargs)
41
42     # Include a reference to the real function (used to check original
43     # arguments by the template parser).
44     _dec._decorated_function = getattr(func, '_decorated_function', func)
45     for attr in ('is_safe', 'needs_autoescape'):
46         if hasattr(func, attr):
47             setattr(_dec, attr, getattr(func, attr))
48     return wraps(func)(_dec)
49
50 ###################
51 # STRINGS         #
52 ###################
53
54 def addslashes(value):
55     """
56     Adds slashes before quotes. Useful for escaping strings in CSV, for
57     example. Less useful for escaping JavaScript; use the ``escapejs``
58     filter instead.
59     """
60     return value.replace('\\', '\\\\').replace('"', '\\"').replace("'", "\\'")
61 addslashes.is_safe = True
62 addslashes = stringfilter(addslashes)
63
64 def capfirst(value):
65     """Capitalizes the first character of the value."""
66     return value and value[0].upper() + value[1:]
67 capfirst.is_safe=True
68 capfirst = stringfilter(capfirst)
69
70 _base_js_escapes = (
71     ('\\', r'\x5C'),
72     ('\'', r'\x27'),
73     ('"', r'\x22'),
74     ('>', r'\x3E'),
75     ('<', r'\x3C'),
76     ('&', r'\x26'),
77     ('=', r'\x3D'),
78     ('-', r'\x2D'),
79     (';', r'\x3B'),
80     (u'\u2028', r'\u2028'),
81     (u'\u2029', r'\u2029')
82 )
83
84 # Escape every ASCII character with a value less than 32.
85 _js_escapes = (_base_js_escapes +
86                tuple([('%c' % z, '\\x%02X' % z) for z in range(32)]))
87
88 def escapejs(value):
89     """Hex encodes characters for use in JavaScript strings."""
90     for bad, good in _js_escapes:
91         value = value.replace(bad, good)
92     return value
93 escapejs = stringfilter(escapejs)
94
95 def fix_ampersands(value):
96     """Replaces ampersands with ``&amp;`` entities."""
97     from django.utils.html import fix_ampersands
98     return fix_ampersands(value)
99 fix_ampersands.is_safe=True
100 fix_ampersands = stringfilter(fix_ampersands)
101
102 # Values for testing floatformat input against infinity and NaN representations,
103 # which differ across platforms and Python versions.  Some (i.e. old Windows
104 # ones) are not recognized by Decimal but we want to return them unchanged vs.
105 # returning an empty string as we do for completley invalid input.  Note these
106 # need to be built up from values that are not inf/nan, since inf/nan values do
107 # not reload properly from .pyc files on Windows prior to some level of Python 2.5
108 # (see Python Issue757815 and Issue1080440).
109 pos_inf = 1e200 * 1e200
110 neg_inf = -1e200 * 1e200
111 nan = (1e200 * 1e200) / (1e200 * 1e200)
112 special_floats = [str(pos_inf), str(neg_inf), str(nan)]
113
114 def floatformat(text, arg=-1):
115     """
116     Displays a float to a specified number of decimal places.
117
118     If called without an argument, it displays the floating point number with
119     one decimal place -- but only if there's a decimal place to be displayed:
120
121     * num1 = 34.23234
122     * num2 = 34.00000
123     * num3 = 34.26000
124     * {{ num1|floatformat }} displays "34.2"
125     * {{ num2|floatformat }} displays "34"
126     * {{ num3|floatformat }} displays "34.3"
127
128     If arg is positive, it will always display exactly arg number of decimal
129     places:
130
131     * {{ num1|floatformat:3 }} displays "34.232"
132     * {{ num2|floatformat:3 }} displays "34.000"
133     * {{ num3|floatformat:3 }} displays "34.260"
134
135     If arg is negative, it will display arg number of decimal places -- but
136     only if there are places to be displayed:
137
138     * {{ num1|floatformat:"-3" }} displays "34.232"
139     * {{ num2|floatformat:"-3" }} displays "34"
140     * {{ num3|floatformat:"-3" }} displays "34.260"
141
142     If the input float is infinity or NaN, the (platform-dependent) string
143     representation of that value will be displayed.
144     """
145
146     try:
147         input_val = force_unicode(text)
148         d = Decimal(input_val)
149     except UnicodeEncodeError:
150         return u''
151     except InvalidOperation:
152         if input_val in special_floats:
153             return input_val
154         try:
155             d = Decimal(force_unicode(float(text)))
156         except (ValueError, InvalidOperation, TypeError, UnicodeEncodeError):
157             return u''
158     try:
159         p = int(arg)
160     except ValueError:
161         return input_val
162
163     try:
164         m = int(d) - d
165     except (OverflowError, InvalidOperation):
166         return input_val
167
168     if not m and p < 0:
169         return mark_safe(u'%d' % (int(d)))
170
171     if p == 0:
172         exp = Decimal(1)
173     else:
174         exp = Decimal('1.0') / (Decimal(10) ** abs(p))
175     try:
176         return mark_safe(u'%s' % str(d.quantize(exp, ROUND_HALF_UP)))
177     except InvalidOperation:
178         return input_val
179 floatformat.is_safe = True
180
181 def iriencode(value):
182     """Escapes an IRI value for use in a URL."""
183     return force_unicode(iri_to_uri(value))
184 iriencode.is_safe = True
185 iriencode = stringfilter(iriencode)
186
187 def linenumbers(value, autoescape=None):
188     """Displays text with line numbers."""
189     from django.utils.html import escape
190     lines = value.split(u'\n')
191     # Find the maximum width of the line count, for use with zero padding
192     # string format command
193     width = unicode(len(unicode(len(lines))))
194     if not autoescape or isinstance(value, SafeData):
195         for i, line in enumerate(lines):
196             lines[i] = (u"%0" + width  + u"d. %s") % (i + 1, line)
197     else:
198         for i, line in enumerate(lines):
199             lines[i] = (u"%0" + width  + u"d. %s") % (i + 1, escape(line))
200     return mark_safe(u'\n'.join(lines))
201 linenumbers.is_safe = True
202 linenumbers.needs_autoescape = True
203 linenumbers = stringfilter(linenumbers)
204
205 def lower(value):
206     """Converts a string into all lowercase."""
207     return value.lower()
208 lower.is_safe = True
209 lower = stringfilter(lower)
210
211 def make_list(value):
212     """
213     Returns the value turned into a list.
214
215     For an integer, it's a list of digits.
216     For a string, it's a list of characters.
217     """
218     return list(value)
219 make_list.is_safe = False
220 make_list = stringfilter(make_list)
221
222 def slugify(value):
223     """
224     Normalizes string, converts to lowercase, removes non-alpha characters,
225     and converts spaces to hyphens.
226     """
227     import unicodedata
228     value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore')
229     value = unicode(re.sub('[^\w\s-]', '', value).strip().lower())
230     return mark_safe(re.sub('[-\s]+', '-', value))
231 slugify.is_safe = True
232 slugify = stringfilter(slugify)
233
234 def stringformat(value, arg):
235     """
236     Formats the variable according to the arg, a string formatting specifier.
237
238     This specifier uses Python string formating syntax, with the exception that
239     the leading "%" is dropped.
240
241     See http://docs.python.org/lib/typesseq-strings.html for documentation
242     of Python string formatting
243     """
244     try:
245         return (u"%" + unicode(arg)) % value
246     except (ValueError, TypeError):
247         return u""
248 stringformat.is_safe = True
249
250 def title(value):
251     """Converts a string into titlecase."""
252     return re.sub("([a-z])'([A-Z])", lambda m: m.group(0).lower(), value.title())
253 title.is_safe = True
254 title = stringfilter(title)
255
256 def truncatewords(value, arg):
257     """
258     Truncates a string after a certain number of words.
259
260     Argument: Number of words to truncate after.
261     """
262     from django.utils.text import truncate_words
263     try:
264         length = int(arg)
265     except ValueError: # Invalid literal for int().
266         return value # Fail silently.
267     return truncate_words(value, length)
268 truncatewords.is_safe = True
269 truncatewords = stringfilter(truncatewords)
270
271 def truncatewords_html(value, arg):
272     """
273     Truncates HTML after a certain number of words.
274
275     Argument: Number of words to truncate after.
276     """
277     from django.utils.text import truncate_html_words
278     try:
279         length = int(arg)
280     except ValueError: # invalid literal for int()
281         return value # Fail silently.
282     return truncate_html_words(value, length)
283 truncatewords_html.is_safe = True
284 truncatewords_html = stringfilter(truncatewords_html)
285
286 def upper(value):
287     """Converts a string into all uppercase."""
288     return value.upper()
289 upper.is_safe = False
290 upper = stringfilter(upper)
291
292 def urlencode(value):
293     """Escapes a value for use in a URL."""
294     from django.utils.http import urlquote
295     return urlquote(value)
296 urlencode.is_safe = False
297 urlencode = stringfilter(urlencode)
298
299 def urlize(value, autoescape=None):
300     """Converts URLs in plain text into clickable links."""
301     from django.utils.html import urlize
302     return mark_safe(urlize(value, nofollow=True, autoescape=autoescape))
303 urlize.is_safe=True
304 urlize.needs_autoescape = True
305 urlize = stringfilter(urlize)
306
307 def urlizetrunc(value, limit, autoescape=None):
308     """
309     Converts URLs into clickable links, truncating URLs to the given character
310     limit, and adding 'rel=nofollow' attribute to discourage spamming.
311
312     Argument: Length to truncate URLs to.
313     """
314     from django.utils.html import urlize
315     return mark_safe(urlize(value, trim_url_limit=int(limit), nofollow=True,
316                             autoescape=autoescape))
317 urlizetrunc.is_safe = True
318 urlizetrunc.needs_autoescape = True
319 urlizetrunc = stringfilter(urlizetrunc)
320
321 def wordcount(value):
322     """Returns the number of words."""
323     return len(value.split())
324 wordcount.is_safe = False
325 wordcount = stringfilter(wordcount)
326
327 def wordwrap(value, arg):
328     """
329     Wraps words at specified line length.
330
331     Argument: number of characters to wrap the text at.
332     """
333     from django.utils.text import wrap
334     return wrap(value, int(arg))
335 wordwrap.is_safe = True
336 wordwrap = stringfilter(wordwrap)
337
338 def ljust(value, arg):
339     """
340     Left-aligns the value in a field of a given width.
341
342     Argument: field size.
343     """
344     return value.ljust(int(arg))
345 ljust.is_safe = True
346 ljust = stringfilter(ljust)
347
348 def rjust(value, arg):
349     """
350     Right-aligns the value in a field of a given width.
351
352     Argument: field size.
353     """
354     return value.rjust(int(arg))
355 rjust.is_safe = True
356 rjust = stringfilter(rjust)
357
358 def center(value, arg):
359     """Centers the value in a field of a given width."""
360     return value.center(int(arg))
361 center.is_safe = True
362 center = stringfilter(center)
363
364 def cut(value, arg):
365     """
366     Removes all values of arg from the given string.
367     """
368     safe = isinstance(value, SafeData)
369     value = value.replace(arg, u'')
370     if safe and arg != ';':
371         return mark_safe(value)
372     return value
373 cut = stringfilter(cut)
374
375 ###################
376 # HTML STRINGS    #
377 ###################
378
379 def escape(value):
380     """
381     Marks the value as a string that should not be auto-escaped.
382     """
383     from django.utils.safestring import mark_for_escaping
384     return mark_for_escaping(value)
385 escape.is_safe = True
386 escape = stringfilter(escape)
387
388 def force_escape(value):
389     """
390     Escapes a string's HTML. This returns a new string containing the escaped
391     characters (as opposed to "escape", which marks the content for later
392     possible escaping).
393     """
394     from django.utils.html import escape
395     return mark_safe(escape(value))
396 force_escape = stringfilter(force_escape)
397 force_escape.is_safe = True
398
399 def linebreaks(value, autoescape=None):
400     """
401     Replaces line breaks in plain text with appropriate HTML; a single
402     newline becomes an HTML line break (``<br />``) and a new line
403     followed by a blank line becomes a paragraph break (``</p>``).
404     """
405     from django.utils.html import linebreaks
406     autoescape = autoescape and not isinstance(value, SafeData)
407     return mark_safe(linebreaks(value, autoescape))
408 linebreaks.is_safe = True
409 linebreaks.needs_autoescape = True
410 linebreaks = stringfilter(linebreaks)
411
412 def linebreaksbr(value, autoescape=None):
413     """
414     Converts all newlines in a piece of plain text to HTML line breaks
415     (``<br />``).
416     """
417     if autoescape and not isinstance(value, SafeData):
418         from django.utils.html import escape
419         value = escape(value)
420     return mark_safe(value.replace('\n', '<br />'))
421 linebreaksbr.is_safe = True
422 linebreaksbr.needs_autoescape = True
423 linebreaksbr = stringfilter(linebreaksbr)
424
425 def safe(value):
426     """
427     Marks the value as a string that should not be auto-escaped.
428     """
429     return mark_safe(value)
430 safe.is_safe = True
431 safe = stringfilter(safe)
432
433 def safeseq(value):
434     """
435     A "safe" filter for sequences. Marks each element in the sequence,
436     individually, as safe, after converting them to unicode. Returns a list
437     with the results.
438     """
439     return [mark_safe(force_unicode(obj)) for obj in value]
440 safeseq.is_safe = True
441
442 def removetags(value, tags):
443     """Removes a space separated list of [X]HTML tags from the output."""
444     tags = [re.escape(tag) for tag in tags.split()]
445     tags_re = u'(%s)' % u'|'.join(tags)
446     starttag_re = re.compile(ur'<%s(/?>|(\s+[^>]*>))' % tags_re, re.U)
447     endtag_re = re.compile(u'</%s>' % tags_re)
448     value = starttag_re.sub(u'', value)
449     value = endtag_re.sub(u'', value)
450     return value
451 removetags.is_safe = True
452 removetags = stringfilter(removetags)
453
454 def striptags(value):
455     """Strips all [X]HTML tags."""
456     from django.utils.html import strip_tags
457     return strip_tags(value)
458 striptags.is_safe = True
459 striptags = stringfilter(striptags)
460
461 ###################
462 # LISTS           #
463 ###################
464
465 def dictsort(value, arg):
466     """
467     Takes a list of dicts, returns that list sorted by the property given in
468     the argument.
469     """
470     var_resolve = Variable(arg).resolve
471     decorated = [(var_resolve(item), item) for item in value]
472     decorated.sort()
473     return [item[1] for item in decorated]
474 dictsort.is_safe = False
475
476 def dictsortreversed(value, arg):
477     """
478     Takes a list of dicts, returns that list sorted in reverse order by the
479     property given in the argument.
480     """
481     var_resolve = Variable(arg).resolve
482     decorated = [(var_resolve(item), item) for item in value]
483     decorated.sort()
484     decorated.reverse()
485     return [item[1] for item in decorated]
486 dictsortreversed.is_safe = False
487
488 def first(value):
489     """Returns the first item in a list."""
490     try:
491         return value[0]
492     except IndexError:
493         return u''
494 first.is_safe = False
495
496 def join(value, arg, autoescape=None):
497     """
498     Joins a list with a string, like Python's ``str.join(list)``.
499     """
500     value = map(force_unicode, value)
501     if autoescape:
502         from django.utils.html import conditional_escape
503         value = [conditional_escape(v) for v in value]
504     try:
505         data = arg.join(value)
506     except AttributeError: # fail silently but nicely
507         return value
508     return mark_safe(data)
509 join.is_safe = True
510 join.needs_autoescape = True
511
512 def last(value):
513     "Returns the last item in a list"
514     try:
515         return value[-1]
516     except IndexError:
517         return u''
518 last.is_safe = True
519
520 def length(value):
521     """Returns the length of the value - useful for lists."""
522     try:
523         return len(value)
524     except (ValueError, TypeError):
525         return ''
526 length.is_safe = True
527
528 def length_is(value, arg):
529     """Returns a boolean of whether the value's length is the argument."""
530     try:
531         return len(value) == int(arg)
532     except (ValueError, TypeError):
533         return ''
534 length_is.is_safe = False
535
536 def random(value):
537     """Returns a random item from the list."""
538     return random_module.choice(value)
539 random.is_safe = True
540
541 def slice_(value, arg):
542     """
543     Returns a slice of the list.
544
545     Uses the same syntax as Python's list slicing; see
546     http://diveintopython.org/native_data_types/lists.html#odbchelper.list.slice
547     for an introduction.
548     """
549     try:
550         bits = []
551         for x in arg.split(u':'):
552             if len(x) == 0:
553                 bits.append(None)
554             else:
555                 bits.append(int(x))
556         return value[slice(*bits)]
557
558     except (ValueError, TypeError):
559         return value # Fail silently.
560 slice_.is_safe = True
561
562 def unordered_list(value, autoescape=None):
563     """
564     Recursively takes a self-nested list and returns an HTML unordered list --
565     WITHOUT opening and closing <ul> tags.
566
567     The list is assumed to be in the proper format. For example, if ``var``
568     contains: ``['States', ['Kansas', ['Lawrence', 'Topeka'], 'Illinois']]``,
569     then ``{{ var|unordered_list }}`` would return::
570
571         <li>States
572         <ul>
573                 <li>Kansas
574                 <ul>
575                         <li>Lawrence</li>
576                         <li>Topeka</li>
577                 </ul>
578                 </li>
579                 <li>Illinois</li>
580         </ul>
581         </li>
582     """
583     if autoescape:
584         from django.utils.html import conditional_escape
585         escaper = conditional_escape
586     else:
587         escaper = lambda x: x
588     def convert_old_style_list(list_):
589         """
590         Converts old style lists to the new easier to understand format.
591
592         The old list format looked like:
593             ['Item 1', [['Item 1.1', []], ['Item 1.2', []]]
594
595         And it is converted to:
596             ['Item 1', ['Item 1.1', 'Item 1.2]]
597         """
598         if not isinstance(list_, (tuple, list)) or len(list_) != 2:
599             return list_, False
600         first_item, second_item = list_
601         if second_item == []:
602             return [first_item], True
603         old_style_list = True
604         new_second_item = []
605         for sublist in second_item:
606             item, old_style_list = convert_old_style_list(sublist)
607             if not old_style_list:
608                 break
609             new_second_item.extend(item)
610         if old_style_list:
611             second_item = new_second_item
612         return [first_item, second_item], old_style_list
613     def _helper(list_, tabs=1):
614         indent = u'\t' * tabs
615         output = []
616
617         list_length = len(list_)
618         i = 0
619         while i < list_length:
620             title = list_[i]
621             sublist = ''
622             sublist_item = None
623             if isinstance(title, (list, tuple)):
624                 sublist_item = title
625                 title = ''
626             elif i < list_length - 1:
627                 next_item = list_[i+1]
628                 if next_item and isinstance(next_item, (list, tuple)):
629                     # The next item is a sub-list.
630                     sublist_item = next_item
631                     # We've processed the next item now too.
632                     i += 1
633             if sublist_item:
634                 sublist = _helper(sublist_item, tabs+1)
635                 sublist = '\n%s<ul>\n%s\n%s</ul>\n%s' % (indent, sublist,
636                                                          indent, indent)
637             output.append('%s<li>%s%s</li>' % (indent,
638                     escaper(force_unicode(title)), sublist))
639             i += 1
640         return '\n'.join(output)
641     value, converted = convert_old_style_list(value)
642     return mark_safe(_helper(value))
643 unordered_list.is_safe = True
644 unordered_list.needs_autoescape = True
645
646 ###################
647 # INTEGERS        #
648 ###################
649
650 def add(value, arg):
651     """Adds the arg to the value."""
652     return int(value) + int(arg)
653 add.is_safe = False
654
655 def get_digit(value, arg):
656     """
657     Given a whole number, returns the requested digit of it, where 1 is the
658     right-most digit, 2 is the second-right-most digit, etc. Returns the
659     original value for invalid input (if input or argument is not an integer,
660     or if argument is less than 1). Otherwise, output is always an integer.
661     """
662     try:
663         arg = int(arg)
664         value = int(value)
665     except ValueError:
666         return value # Fail silently for an invalid argument
667     if arg < 1:
668         return value
669     try:
670         return int(str(value)[-arg])
671     except IndexError:
672         return 0
673 get_digit.is_safe = False
674
675 ###################
676 # DATES           #
677 ###################
678
679 def date(value, arg=None):
680     """Formats a date according to the given format."""
681     from django.utils.dateformat import format
682     if not value:
683         return u''
684     if arg is None:
685         arg = settings.DATE_FORMAT
686     try:
687         return format(value, arg)
688     except AttributeError:
689         return ''
690 date.is_safe = False
691
692 def time(value, arg=None):
693     """Formats a time according to the given format."""
694     from django.utils.dateformat import time_format
695     if value in (None, u''):
696         return u''
697     if arg is None:
698         arg = settings.TIME_FORMAT
699     try:
700         return time_format(value, arg)
701     except AttributeError:
702         return ''
703 time.is_safe = False
704
705 def timesince(value, arg=None):
706     """Formats a date as the time since that date (i.e. "4 days, 6 hours")."""
707     from django.utils.timesince import timesince
708     if not value:
709         return u''
710     try:
711         if arg:
712             return timesince(value, arg)
713         return timesince(value)
714     except (ValueError, TypeError):
715         return u''
716 timesince.is_safe = False
717
718 def timeuntil(value, arg=None):
719     """Formats a date as the time until that date (i.e. "4 days, 6 hours")."""
720     from django.utils.timesince import timeuntil
721     from datetime import datetime
722     if not value:
723         return u''
724     try:
725         return timeuntil(value, arg)
726     except (ValueError, TypeError):
727         return u''
728 timeuntil.is_safe = False
729
730 ###################
731 # LOGIC           #
732 ###################
733
734 def default(value, arg):
735     """If value is unavailable, use given default."""
736     return value or arg
737 default.is_safe = False
738
739 def default_if_none(value, arg):
740     """If value is None, use given default."""
741     if value is None:
742         return arg
743     return value
744 default_if_none.is_safe = False
745
746 def divisibleby(value, arg):
747     """Returns True if the value is devisible by the argument."""
748     return int(value) % int(arg) == 0
749 divisibleby.is_safe = False
750
751 def yesno(value, arg=None):
752     """
753     Given a string mapping values for true, false and (optionally) None,
754     returns one of those strings accoding to the value:
755
756     ==========  ======================  ==================================
757     Value       Argument                Outputs
758     ==========  ======================  ==================================
759     ``True``    ``"yeah,no,maybe"``     ``yeah``
760     ``False``   ``"yeah,no,maybe"``     ``no``
761     ``None``    ``"yeah,no,maybe"``     ``maybe``
762     ``None``    ``"yeah,no"``           ``"no"`` (converts None to False
763                                         if no mapping for None is given.
764     ==========  ======================  ==================================
765     """
766     if arg is None:
767         arg = ugettext('yes,no,maybe')
768     bits = arg.split(u',')
769     if len(bits) < 2:
770         return value # Invalid arg.
771     try:
772         yes, no, maybe = bits
773     except ValueError:
774         # Unpack list of wrong size (no "maybe" value provided).
775         yes, no, maybe = bits[0], bits[1], bits[1]
776     if value is None:
777         return maybe
778     if value:
779         return yes
780     return no
781 yesno.is_safe = False
782
783 ###################
784 # MISC            #
785 ###################
786
787 def filesizeformat(bytes):
788     """
789     Formats the value like a 'human-readable' file size (i.e. 13 KB, 4.1 MB,
790     102 bytes, etc).
791     """
792     try:
793         bytes = float(bytes)
794     except TypeError:
795         return u"0 bytes"
796
797     if bytes < 1024:
798         return ungettext("%(size)d byte", "%(size)d bytes", bytes) % {'size': bytes}
799     if bytes < 1024 * 1024:
800         return ugettext("%.1f KB") % (bytes / 1024)
801     if bytes < 1024 * 1024 * 1024:
802         return ugettext("%.1f MB") % (bytes / (1024 * 1024))
803     return ugettext("%.1f GB") % (bytes / (1024 * 1024 * 1024))
804 filesizeformat.is_safe = True
805
806 def pluralize(value, arg=u's'):
807     """
808     Returns a plural suffix if the value is not 1. By default, 's' is used as
809     the suffix:
810
811     * If value is 0, vote{{ value|pluralize }} displays "0 votes".
812     * If value is 1, vote{{ value|pluralize }} displays "1 vote".
813     * If value is 2, vote{{ value|pluralize }} displays "2 votes".
814
815     If an argument is provided, that string is used instead:
816
817     * If value is 0, class{{ value|pluralize:"es" }} displays "0 classes".
818     * If value is 1, class{{ value|pluralize:"es" }} displays "1 class".
819     * If value is 2, class{{ value|pluralize:"es" }} displays "2 classes".
820
821     If the provided argument contains a comma, the text before the comma is
822     used for the singular case and the text after the comma is used for the
823     plural case:
824
825     * If value is 0, cand{{ value|pluralize:"y,ies" }} displays "0 candies".
826     * If value is 1, cand{{ value|pluralize:"y,ies" }} displays "1 candy".
827     * If value is 2, cand{{ value|pluralize:"y,ies" }} displays "2 candies".
828     """
829     if not u',' in arg:
830         arg = u',' + arg
831     bits = arg.split(u',')
832     if len(bits) > 2:
833         return u''
834     singular_suffix, plural_suffix = bits[:2]
835
836     try:
837         if int(value) != 1:
838             return plural_suffix
839     except ValueError: # Invalid string that's not a number.
840         pass
841     except TypeError: # Value isn't a string or a number; maybe it's a list?
842         try:
843             if len(value) != 1:
844                 return plural_suffix
845         except TypeError: # len() of unsized object.
846             pass
847     return singular_suffix
848 pluralize.is_safe = False
849
850 def phone2numeric(value):
851     """Takes a phone number and converts it in to its numerical equivalent."""
852     from django.utils.text import phone2numeric
853     return phone2numeric(value)
854 phone2numeric.is_safe = True
855
856 def pprint(value):
857     """A wrapper around pprint.pprint -- for debugging, really."""
858     from pprint import pformat
859     try:
860         return pformat(value)
861     except Exception, e:
862         return u"Error in formatting: %s" % force_unicode(e, errors="replace")
863 pprint.is_safe = True
864
865 # Syntax: register.filter(name of filter, callback)
866 register.filter(add)
867 register.filter(addslashes)
868 register.filter(capfirst)
869 register.filter(center)
870 register.filter(cut)
871 register.filter(date)
872 register.filter(default)
873 register.filter(default_if_none)
874 register.filter(dictsort)
875 register.filter(dictsortreversed)
876 register.filter(divisibleby)
877 register.filter(escape)
878 register.filter(escapejs)
879 register.filter(filesizeformat)
880 register.filter(first)
881 register.filter(fix_ampersands)
882 register.filter(floatformat)
883 register.filter(force_escape)
884 register.filter(get_digit)
885 register.filter(iriencode)
886 register.filter(join)
887 register.filter(last)
888 register.filter(length)
889 register.filter(length_is)
890 register.filter(linebreaks)
891 register.filter(linebreaksbr)
892 register.filter(linenumbers)
893 register.filter(ljust)
894 register.filter(lower)
895 register.filter(make_list)
896 register.filter(phone2numeric)
897 register.filter(pluralize)
898 register.filter(pprint)
899 register.filter(removetags)
900 register.filter(random)
901 register.filter(rjust)
902 register.filter(safe)
903 register.filter(safeseq)
904 register.filter('slice', slice_)
905 register.filter(slugify)
906 register.filter(stringformat)
907 register.filter(striptags)
908 register.filter(time)
909 register.filter(timesince)
910 register.filter(timeuntil)
911 register.filter(title)
912 register.filter(truncatewords)
913 register.filter(truncatewords_html)
914 register.filter(unordered_list)
915 register.filter(upper)
916 register.filter(urlencode)
917 register.filter(urlize)
918 register.filter(urlizetrunc)
919 register.filter(wordcount)
920 register.filter(wordwrap)
921 register.filter(yesno)
Note: See TracBrowser for help on using the browser.