PageRenderTime 50ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/Django-1.4.3/build/lib.linux-i686-2.7/django/template/defaultfilters.py

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