##// END OF EJS Templates
templatefilters: split out jsonescape() function
Dirkjan Ochtman -
r8014:6a77ba18 default
parent child Browse files
Show More
@@ -1,186 +1,190 b''
1 # template-filters.py - common template expansion filters
1 # template-filters.py - common template expansion filters
2 #
2 #
3 # Copyright 2005-2008 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2008 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 import cgi, re, os, time, urllib, textwrap
8 import cgi, re, os, time, urllib, textwrap
9 import util, templater, encoding
9 import util, templater, encoding
10
10
11 agescales = [("second", 1),
11 agescales = [("second", 1),
12 ("minute", 60),
12 ("minute", 60),
13 ("hour", 3600),
13 ("hour", 3600),
14 ("day", 3600 * 24),
14 ("day", 3600 * 24),
15 ("week", 3600 * 24 * 7),
15 ("week", 3600 * 24 * 7),
16 ("month", 3600 * 24 * 30),
16 ("month", 3600 * 24 * 30),
17 ("year", 3600 * 24 * 365)]
17 ("year", 3600 * 24 * 365)]
18
18
19 agescales.reverse()
19 agescales.reverse()
20
20
21 def age(date):
21 def age(date):
22 '''turn a (timestamp, tzoff) tuple into an age string.'''
22 '''turn a (timestamp, tzoff) tuple into an age string.'''
23
23
24 def plural(t, c):
24 def plural(t, c):
25 if c == 1:
25 if c == 1:
26 return t
26 return t
27 return t + "s"
27 return t + "s"
28 def fmt(t, c):
28 def fmt(t, c):
29 return "%d %s" % (c, plural(t, c))
29 return "%d %s" % (c, plural(t, c))
30
30
31 now = time.time()
31 now = time.time()
32 then = date[0]
32 then = date[0]
33 if then > now:
33 if then > now:
34 return 'in the future'
34 return 'in the future'
35
35
36 delta = max(1, int(now - then))
36 delta = max(1, int(now - then))
37 for t, s in agescales:
37 for t, s in agescales:
38 n = delta / s
38 n = delta / s
39 if n >= 2 or s == 1:
39 if n >= 2 or s == 1:
40 return fmt(t, n)
40 return fmt(t, n)
41
41
42 para_re = None
42 para_re = None
43 space_re = None
43 space_re = None
44
44
45 def fill(text, width):
45 def fill(text, width):
46 '''fill many paragraphs.'''
46 '''fill many paragraphs.'''
47 global para_re, space_re
47 global para_re, space_re
48 if para_re is None:
48 if para_re is None:
49 para_re = re.compile('(\n\n|\n\\s*[-*]\\s*)', re.M)
49 para_re = re.compile('(\n\n|\n\\s*[-*]\\s*)', re.M)
50 space_re = re.compile(r' +')
50 space_re = re.compile(r' +')
51
51
52 def findparas():
52 def findparas():
53 start = 0
53 start = 0
54 while True:
54 while True:
55 m = para_re.search(text, start)
55 m = para_re.search(text, start)
56 if not m:
56 if not m:
57 w = len(text)
57 w = len(text)
58 while w > start and text[w-1].isspace(): w -= 1
58 while w > start and text[w-1].isspace(): w -= 1
59 yield text[start:w], text[w:]
59 yield text[start:w], text[w:]
60 break
60 break
61 yield text[start:m.start(0)], m.group(1)
61 yield text[start:m.start(0)], m.group(1)
62 start = m.end(1)
62 start = m.end(1)
63
63
64 return "".join([space_re.sub(' ', textwrap.fill(para, width)) + rest
64 return "".join([space_re.sub(' ', textwrap.fill(para, width)) + rest
65 for para, rest in findparas()])
65 for para, rest in findparas()])
66
66
67 def firstline(text):
67 def firstline(text):
68 '''return the first line of text'''
68 '''return the first line of text'''
69 try:
69 try:
70 return text.splitlines(1)[0].rstrip('\r\n')
70 return text.splitlines(1)[0].rstrip('\r\n')
71 except IndexError:
71 except IndexError:
72 return ''
72 return ''
73
73
74 def nl2br(text):
74 def nl2br(text):
75 '''replace raw newlines with xhtml line breaks.'''
75 '''replace raw newlines with xhtml line breaks.'''
76 return text.replace('\n', '<br/>\n')
76 return text.replace('\n', '<br/>\n')
77
77
78 def obfuscate(text):
78 def obfuscate(text):
79 text = unicode(text, encoding.encoding, 'replace')
79 text = unicode(text, encoding.encoding, 'replace')
80 return ''.join(['&#%d;' % ord(c) for c in text])
80 return ''.join(['&#%d;' % ord(c) for c in text])
81
81
82 def domain(author):
82 def domain(author):
83 '''get domain of author, or empty string if none.'''
83 '''get domain of author, or empty string if none.'''
84 f = author.find('@')
84 f = author.find('@')
85 if f == -1: return ''
85 if f == -1: return ''
86 author = author[f+1:]
86 author = author[f+1:]
87 f = author.find('>')
87 f = author.find('>')
88 if f >= 0: author = author[:f]
88 if f >= 0: author = author[:f]
89 return author
89 return author
90
90
91 def person(author):
91 def person(author):
92 '''get name of author, or else username.'''
92 '''get name of author, or else username.'''
93 f = author.find('<')
93 f = author.find('<')
94 if f == -1: return util.shortuser(author)
94 if f == -1: return util.shortuser(author)
95 return author[:f].rstrip()
95 return author[:f].rstrip()
96
96
97 def indent(text, prefix):
97 def indent(text, prefix):
98 '''indent each non-empty line of text after first with prefix.'''
98 '''indent each non-empty line of text after first with prefix.'''
99 lines = text.splitlines()
99 lines = text.splitlines()
100 num_lines = len(lines)
100 num_lines = len(lines)
101 def indenter():
101 def indenter():
102 for i in xrange(num_lines):
102 for i in xrange(num_lines):
103 l = lines[i]
103 l = lines[i]
104 if i and l.strip():
104 if i and l.strip():
105 yield prefix
105 yield prefix
106 yield l
106 yield l
107 if i < num_lines - 1 or text.endswith('\n'):
107 if i < num_lines - 1 or text.endswith('\n'):
108 yield '\n'
108 yield '\n'
109 return "".join(indenter())
109 return "".join(indenter())
110
110
111 def permissions(flags):
111 def permissions(flags):
112 if "l" in flags:
112 if "l" in flags:
113 return "lrwxrwxrwx"
113 return "lrwxrwxrwx"
114 if "x" in flags:
114 if "x" in flags:
115 return "-rwxr-xr-x"
115 return "-rwxr-xr-x"
116 return "-rw-r--r--"
116 return "-rw-r--r--"
117
117
118 def xmlescape(text):
118 def xmlescape(text):
119 text = (text
119 text = (text
120 .replace('&', '&amp;')
120 .replace('&', '&amp;')
121 .replace('<', '&lt;')
121 .replace('<', '&lt;')
122 .replace('>', '&gt;')
122 .replace('>', '&gt;')
123 .replace('"', '&quot;')
123 .replace('"', '&quot;')
124 .replace("'", '&#39;')) # &apos; invalid in HTML
124 .replace("'", '&#39;')) # &apos; invalid in HTML
125 return re.sub('[\x00-\x08\x0B\x0C\x0E-\x1F]', ' ', text)
125 return re.sub('[\x00-\x08\x0B\x0C\x0E-\x1F]', ' ', text)
126
126
127 _escapes = [
127 _escapes = [
128 ('\\', '\\\\'), ('"', '\\"'), ('\t', '\\t'), ('\n', '\\n'),
128 ('\\', '\\\\'), ('"', '\\"'), ('\t', '\\t'), ('\n', '\\n'),
129 ('\r', '\\r'), ('\f', '\\f'), ('\b', '\\b'),
129 ('\r', '\\r'), ('\f', '\\f'), ('\b', '\\b'),
130 ]
130 ]
131
131
132 def jsonescape(s):
133 for k, v in _escapes:
134 s = s.replace(k, v)
135 return s
136
132 def json(obj):
137 def json(obj):
133 if obj is None or obj is False or obj is True:
138 if obj is None or obj is False or obj is True:
134 return {None: 'null', False: 'false', True: 'true'}[obj]
139 return {None: 'null', False: 'false', True: 'true'}[obj]
135 elif isinstance(obj, int) or isinstance(obj, float):
140 elif isinstance(obj, int) or isinstance(obj, float):
136 return str(obj)
141 return str(obj)
137 elif isinstance(obj, str):
142 elif isinstance(obj, str):
138 for k, v in _escapes:
143 return '"%s"' % jsonescape(obj)
139 obj = obj.replace(k, v)
140 return '"%s"' % obj
141 elif isinstance(obj, unicode):
144 elif isinstance(obj, unicode):
142 return json(obj.encode('utf-8'))
145 return json(obj.encode('utf-8'))
143 elif hasattr(obj, 'keys'):
146 elif hasattr(obj, 'keys'):
144 out = []
147 out = []
145 for k, v in obj.iteritems():
148 for k, v in obj.iteritems():
146 s = '%s: %s' % (json(k), json(v))
149 s = '%s: %s' % (json(k), json(v))
147 out.append(s)
150 out.append(s)
148 return '{' + ', '.join(out) + '}'
151 return '{' + ', '.join(out) + '}'
149 elif hasattr(obj, '__iter__'):
152 elif hasattr(obj, '__iter__'):
150 out = []
153 out = []
151 for i in obj:
154 for i in obj:
152 out.append(json(i))
155 out.append(json(i))
153 return '[' + ', '.join(out) + ']'
156 return '[' + ', '.join(out) + ']'
154 else:
157 else:
155 raise TypeError('cannot encode type %s' % obj.__class__.__name__)
158 raise TypeError('cannot encode type %s' % obj.__class__.__name__)
156
159
157 filters = {
160 filters = {
158 "addbreaks": nl2br,
161 "addbreaks": nl2br,
159 "basename": os.path.basename,
162 "basename": os.path.basename,
160 "age": age,
163 "age": age,
161 "date": lambda x: util.datestr(x),
164 "date": lambda x: util.datestr(x),
162 "domain": domain,
165 "domain": domain,
163 "email": util.email,
166 "email": util.email,
164 "escape": lambda x: cgi.escape(x, True),
167 "escape": lambda x: cgi.escape(x, True),
165 "fill68": lambda x: fill(x, width=68),
168 "fill68": lambda x: fill(x, width=68),
166 "fill76": lambda x: fill(x, width=76),
169 "fill76": lambda x: fill(x, width=76),
167 "firstline": firstline,
170 "firstline": firstline,
168 "tabindent": lambda x: indent(x, '\t'),
171 "tabindent": lambda x: indent(x, '\t'),
169 "hgdate": lambda x: "%d %d" % x,
172 "hgdate": lambda x: "%d %d" % x,
170 "isodate": lambda x: util.datestr(x, '%Y-%m-%d %H:%M %1%2'),
173 "isodate": lambda x: util.datestr(x, '%Y-%m-%d %H:%M %1%2'),
171 "isodatesec": lambda x: util.datestr(x, '%Y-%m-%d %H:%M:%S %1%2'),
174 "isodatesec": lambda x: util.datestr(x, '%Y-%m-%d %H:%M:%S %1%2'),
175 "json": json,
176 "jsonescape": jsonescape,
172 "obfuscate": obfuscate,
177 "obfuscate": obfuscate,
173 "permissions": permissions,
178 "permissions": permissions,
174 "person": person,
179 "person": person,
175 "rfc822date": lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S %1%2"),
180 "rfc822date": lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S %1%2"),
176 "rfc3339date": lambda x: util.datestr(x, "%Y-%m-%dT%H:%M:%S%1:%2"),
181 "rfc3339date": lambda x: util.datestr(x, "%Y-%m-%dT%H:%M:%S%1:%2"),
177 "short": lambda x: x[:12],
182 "short": lambda x: x[:12],
178 "shortdate": util.shortdate,
183 "shortdate": util.shortdate,
179 "stringify": templater.stringify,
184 "stringify": templater.stringify,
180 "strip": lambda x: x.strip(),
185 "strip": lambda x: x.strip(),
181 "urlescape": lambda x: urllib.quote(x),
186 "urlescape": lambda x: urllib.quote(x),
182 "user": lambda x: util.shortuser(x),
187 "user": lambda x: util.shortuser(x),
183 "stringescape": lambda x: x.encode('string_escape'),
188 "stringescape": lambda x: x.encode('string_escape'),
184 "xmlescape": xmlescape,
189 "xmlescape": xmlescape,
185 "json": json,
186 }
190 }
General Comments 0
You need to be logged in to leave comments. Login now