1 | from django.template import Library, Node, TemplateSyntaxError
|
---|
2 | from django.template import VariableNode, FILTER_SEPARATOR
|
---|
3 | from django.utils.html import escape
|
---|
4 |
|
---|
5 | register = Library()
|
---|
6 |
|
---|
7 | FINALIZED_FILTER_NAME = 'finalized'
|
---|
8 |
|
---|
9 | class FinalFilterNode(Node):
|
---|
10 | def __init__(self, nodelist, filters):
|
---|
11 | self.nodelist = nodelist
|
---|
12 | self.filters = filters
|
---|
13 | self.parsed = False
|
---|
14 |
|
---|
15 | def render(self, context):
|
---|
16 | self.parse_nodes()
|
---|
17 | return self.nodelist.render(context)
|
---|
18 |
|
---|
19 | def parse_nodes(self):
|
---|
20 | if self.parsed:
|
---|
21 | # Don't ever parse twice.
|
---|
22 | return
|
---|
23 | nodelist = self.nodelist
|
---|
24 | # Parse inner FinalFilterNodes first (they may contain a
|
---|
25 | # finalize filter).
|
---|
26 | for node in nodelist.get_nodes_by_type(FinalFilterNode):
|
---|
27 | node.parse_nodes()
|
---|
28 | # Parse child nodes.
|
---|
29 | for node in nodelist.get_nodes_by_type(VariableNode):
|
---|
30 | node_filters = node.filter_expression.filters
|
---|
31 | filter_funcs = [filter[0] for filter in node_filters]
|
---|
32 | if finalized not in filter_funcs:
|
---|
33 | # Ignore the node if it has the finalized functions in
|
---|
34 | # its filters.
|
---|
35 | for filter in self.filters:
|
---|
36 | if filter[0] not in filter_funcs:
|
---|
37 | # Don't double-up on filters which have already
|
---|
38 | # been applied to this VariableNode.
|
---|
39 | node_filters.append(filter)
|
---|
40 | self.parsed = True
|
---|
41 |
|
---|
42 | def finalfilter(parser, token):
|
---|
43 | """
|
---|
44 | Add common filters to all variable nodes within this block tag.
|
---|
45 | Use the `finalized` filter in a variable node to skip adding the filters
|
---|
46 | to that node. If a common filter has already been explicitly added to a
|
---|
47 | variable node, it will *not* be added again.
|
---|
48 |
|
---|
49 | Filters can also be piped through each other, and they can have
|
---|
50 | arguments -- just like in variable syntax.
|
---|
51 |
|
---|
52 | Note: This tag adds the filter(s) at render time so this happens across
|
---|
53 | all extended block tags.
|
---|
54 |
|
---|
55 | Example usage::
|
---|
56 |
|
---|
57 | {% finalfilter escape %}
|
---|
58 | {{ html_menu|finalized }}
|
---|
59 | <h2>{{ object.company }}</h2>
|
---|
60 | <p>
|
---|
61 | <a href="{{ object.url }}">
|
---|
62 | {{ object.first_name }} {{ object.last_name }}
|
---|
63 | </a>
|
---|
64 | </p>
|
---|
65 | {% endfinalfilter %}
|
---|
66 |
|
---|
67 | This example would add the escape filter to the end of each variable node
|
---|
68 | except for `html_menu`.
|
---|
69 | """
|
---|
70 | nodelist = parser.parse(('endfinalfilter',))
|
---|
71 | parser.delete_first_token()
|
---|
72 |
|
---|
73 | try:
|
---|
74 | _, rest = token.contents.split(None, 1)
|
---|
75 | except ValueError:
|
---|
76 | raise TemplateSyntaxError, "'finalfilter' requires at least one filter"
|
---|
77 |
|
---|
78 | # Build the final filters list.
|
---|
79 | filter_expression = parser.compile_filter('var%s%s' % (FILTER_SEPARATOR, rest))
|
---|
80 | filters = filter_expression.filters
|
---|
81 |
|
---|
82 | return FinalFilterNode(nodelist, filters)
|
---|
83 | finalfilter = register.tag(finalfilter)
|
---|
84 |
|
---|
85 | def finalized(value):
|
---|
86 | """
|
---|
87 | Used to cancel the effect of {% finalfilter %}. Has no other effect.
|
---|
88 | """
|
---|
89 | return value
|
---|
90 | register.filter(FINALIZED_FILTER_NAME, finalized)
|
---|
91 |
|
---|
92 | def better_escape(value):
|
---|
93 | """
|
---|
94 | HTML escape the given text with ampersands, quotes and carets encoded.
|
---|
95 | Alternately, if a list is given, each item of the list is html escaped
|
---|
96 | (lists are escaped recursively).
|
---|
97 |
|
---|
98 | Useful for this sort of thing::
|
---|
99 |
|
---|
100 | {{ names_list|escape|join:'<br />' }}
|
---|
101 | """
|
---|
102 | if isinstance(value, (list, tuple)):
|
---|
103 | return map(better_escape, value)
|
---|
104 | else:
|
---|
105 | return escape(value)
|
---|
106 | # It'd be nice to override the default escape filter, but that causes
|
---|
107 | # problems with double-escaping when escape is used in templates which
|
---|
108 | # use {% extends %} without {% load filtertags %}.
|
---|
109 | # Best solution is if this is implemented in core :)
|
---|
110 | register.filter('better_escape', better_escape)
|
---|