##// END OF EJS Templates
templatefilters: use list comprehension in json()...
Yuya Nishihara -
r31781:47925b63 default
parent child Browse files
Show More
@@ -1,435 +1,431 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 of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import cgi
10 import cgi
11 import os
11 import os
12 import re
12 import re
13 import time
13 import time
14
14
15 from . import (
15 from . import (
16 encoding,
16 encoding,
17 hbisect,
17 hbisect,
18 node,
18 node,
19 registrar,
19 registrar,
20 templatekw,
20 templatekw,
21 util,
21 util,
22 )
22 )
23
23
24 urlerr = util.urlerr
24 urlerr = util.urlerr
25 urlreq = util.urlreq
25 urlreq = util.urlreq
26
26
27 # filters are callables like:
27 # filters are callables like:
28 # fn(obj)
28 # fn(obj)
29 # with:
29 # with:
30 # obj - object to be filtered (text, date, list and so on)
30 # obj - object to be filtered (text, date, list and so on)
31 filters = {}
31 filters = {}
32
32
33 templatefilter = registrar.templatefilter(filters)
33 templatefilter = registrar.templatefilter(filters)
34
34
35 @templatefilter('addbreaks')
35 @templatefilter('addbreaks')
36 def addbreaks(text):
36 def addbreaks(text):
37 """Any text. Add an XHTML "<br />" tag before the end of
37 """Any text. Add an XHTML "<br />" tag before the end of
38 every line except the last.
38 every line except the last.
39 """
39 """
40 return text.replace('\n', '<br/>\n')
40 return text.replace('\n', '<br/>\n')
41
41
42 agescales = [("year", 3600 * 24 * 365, 'Y'),
42 agescales = [("year", 3600 * 24 * 365, 'Y'),
43 ("month", 3600 * 24 * 30, 'M'),
43 ("month", 3600 * 24 * 30, 'M'),
44 ("week", 3600 * 24 * 7, 'W'),
44 ("week", 3600 * 24 * 7, 'W'),
45 ("day", 3600 * 24, 'd'),
45 ("day", 3600 * 24, 'd'),
46 ("hour", 3600, 'h'),
46 ("hour", 3600, 'h'),
47 ("minute", 60, 'm'),
47 ("minute", 60, 'm'),
48 ("second", 1, 's')]
48 ("second", 1, 's')]
49
49
50 @templatefilter('age')
50 @templatefilter('age')
51 def age(date, abbrev=False):
51 def age(date, abbrev=False):
52 """Date. Returns a human-readable date/time difference between the
52 """Date. Returns a human-readable date/time difference between the
53 given date/time and the current date/time.
53 given date/time and the current date/time.
54 """
54 """
55
55
56 def plural(t, c):
56 def plural(t, c):
57 if c == 1:
57 if c == 1:
58 return t
58 return t
59 return t + "s"
59 return t + "s"
60 def fmt(t, c, a):
60 def fmt(t, c, a):
61 if abbrev:
61 if abbrev:
62 return "%d%s" % (c, a)
62 return "%d%s" % (c, a)
63 return "%d %s" % (c, plural(t, c))
63 return "%d %s" % (c, plural(t, c))
64
64
65 now = time.time()
65 now = time.time()
66 then = date[0]
66 then = date[0]
67 future = False
67 future = False
68 if then > now:
68 if then > now:
69 future = True
69 future = True
70 delta = max(1, int(then - now))
70 delta = max(1, int(then - now))
71 if delta > agescales[0][1] * 30:
71 if delta > agescales[0][1] * 30:
72 return 'in the distant future'
72 return 'in the distant future'
73 else:
73 else:
74 delta = max(1, int(now - then))
74 delta = max(1, int(now - then))
75 if delta > agescales[0][1] * 2:
75 if delta > agescales[0][1] * 2:
76 return util.shortdate(date)
76 return util.shortdate(date)
77
77
78 for t, s, a in agescales:
78 for t, s, a in agescales:
79 n = delta // s
79 n = delta // s
80 if n >= 2 or s == 1:
80 if n >= 2 or s == 1:
81 if future:
81 if future:
82 return '%s from now' % fmt(t, n, a)
82 return '%s from now' % fmt(t, n, a)
83 return '%s ago' % fmt(t, n, a)
83 return '%s ago' % fmt(t, n, a)
84
84
85 @templatefilter('basename')
85 @templatefilter('basename')
86 def basename(path):
86 def basename(path):
87 """Any text. Treats the text as a path, and returns the last
87 """Any text. Treats the text as a path, and returns the last
88 component of the path after splitting by the path separator
88 component of the path after splitting by the path separator
89 (ignoring trailing separators). For example, "foo/bar/baz" becomes
89 (ignoring trailing separators). For example, "foo/bar/baz" becomes
90 "baz" and "foo/bar//" becomes "bar".
90 "baz" and "foo/bar//" becomes "bar".
91 """
91 """
92 return os.path.basename(path)
92 return os.path.basename(path)
93
93
94 @templatefilter('count')
94 @templatefilter('count')
95 def count(i):
95 def count(i):
96 """List or text. Returns the length as an integer."""
96 """List or text. Returns the length as an integer."""
97 return len(i)
97 return len(i)
98
98
99 @templatefilter('domain')
99 @templatefilter('domain')
100 def domain(author):
100 def domain(author):
101 """Any text. Finds the first string that looks like an email
101 """Any text. Finds the first string that looks like an email
102 address, and extracts just the domain component. Example: ``User
102 address, and extracts just the domain component. Example: ``User
103 <user@example.com>`` becomes ``example.com``.
103 <user@example.com>`` becomes ``example.com``.
104 """
104 """
105 f = author.find('@')
105 f = author.find('@')
106 if f == -1:
106 if f == -1:
107 return ''
107 return ''
108 author = author[f + 1:]
108 author = author[f + 1:]
109 f = author.find('>')
109 f = author.find('>')
110 if f >= 0:
110 if f >= 0:
111 author = author[:f]
111 author = author[:f]
112 return author
112 return author
113
113
114 @templatefilter('email')
114 @templatefilter('email')
115 def email(text):
115 def email(text):
116 """Any text. Extracts the first string that looks like an email
116 """Any text. Extracts the first string that looks like an email
117 address. Example: ``User <user@example.com>`` becomes
117 address. Example: ``User <user@example.com>`` becomes
118 ``user@example.com``.
118 ``user@example.com``.
119 """
119 """
120 return util.email(text)
120 return util.email(text)
121
121
122 @templatefilter('escape')
122 @templatefilter('escape')
123 def escape(text):
123 def escape(text):
124 """Any text. Replaces the special XML/XHTML characters "&", "<"
124 """Any text. Replaces the special XML/XHTML characters "&", "<"
125 and ">" with XML entities, and filters out NUL characters.
125 and ">" with XML entities, and filters out NUL characters.
126 """
126 """
127 return cgi.escape(text.replace('\0', ''), True)
127 return cgi.escape(text.replace('\0', ''), True)
128
128
129 para_re = None
129 para_re = None
130 space_re = None
130 space_re = None
131
131
132 def fill(text, width, initindent='', hangindent=''):
132 def fill(text, width, initindent='', hangindent=''):
133 '''fill many paragraphs with optional indentation.'''
133 '''fill many paragraphs with optional indentation.'''
134 global para_re, space_re
134 global para_re, space_re
135 if para_re is None:
135 if para_re is None:
136 para_re = re.compile('(\n\n|\n\\s*[-*]\\s*)', re.M)
136 para_re = re.compile('(\n\n|\n\\s*[-*]\\s*)', re.M)
137 space_re = re.compile(r' +')
137 space_re = re.compile(r' +')
138
138
139 def findparas():
139 def findparas():
140 start = 0
140 start = 0
141 while True:
141 while True:
142 m = para_re.search(text, start)
142 m = para_re.search(text, start)
143 if not m:
143 if not m:
144 uctext = unicode(text[start:], encoding.encoding)
144 uctext = unicode(text[start:], encoding.encoding)
145 w = len(uctext)
145 w = len(uctext)
146 while 0 < w and uctext[w - 1].isspace():
146 while 0 < w and uctext[w - 1].isspace():
147 w -= 1
147 w -= 1
148 yield (uctext[:w].encode(encoding.encoding),
148 yield (uctext[:w].encode(encoding.encoding),
149 uctext[w:].encode(encoding.encoding))
149 uctext[w:].encode(encoding.encoding))
150 break
150 break
151 yield text[start:m.start(0)], m.group(1)
151 yield text[start:m.start(0)], m.group(1)
152 start = m.end(1)
152 start = m.end(1)
153
153
154 return "".join([util.wrap(space_re.sub(' ', util.wrap(para, width)),
154 return "".join([util.wrap(space_re.sub(' ', util.wrap(para, width)),
155 width, initindent, hangindent) + rest
155 width, initindent, hangindent) + rest
156 for para, rest in findparas()])
156 for para, rest in findparas()])
157
157
158 @templatefilter('fill68')
158 @templatefilter('fill68')
159 def fill68(text):
159 def fill68(text):
160 """Any text. Wraps the text to fit in 68 columns."""
160 """Any text. Wraps the text to fit in 68 columns."""
161 return fill(text, 68)
161 return fill(text, 68)
162
162
163 @templatefilter('fill76')
163 @templatefilter('fill76')
164 def fill76(text):
164 def fill76(text):
165 """Any text. Wraps the text to fit in 76 columns."""
165 """Any text. Wraps the text to fit in 76 columns."""
166 return fill(text, 76)
166 return fill(text, 76)
167
167
168 @templatefilter('firstline')
168 @templatefilter('firstline')
169 def firstline(text):
169 def firstline(text):
170 """Any text. Returns the first line of text."""
170 """Any text. Returns the first line of text."""
171 try:
171 try:
172 return text.splitlines(True)[0].rstrip('\r\n')
172 return text.splitlines(True)[0].rstrip('\r\n')
173 except IndexError:
173 except IndexError:
174 return ''
174 return ''
175
175
176 @templatefilter('hex')
176 @templatefilter('hex')
177 def hexfilter(text):
177 def hexfilter(text):
178 """Any text. Convert a binary Mercurial node identifier into
178 """Any text. Convert a binary Mercurial node identifier into
179 its long hexadecimal representation.
179 its long hexadecimal representation.
180 """
180 """
181 return node.hex(text)
181 return node.hex(text)
182
182
183 @templatefilter('hgdate')
183 @templatefilter('hgdate')
184 def hgdate(text):
184 def hgdate(text):
185 """Date. Returns the date as a pair of numbers: "1157407993
185 """Date. Returns the date as a pair of numbers: "1157407993
186 25200" (Unix timestamp, timezone offset).
186 25200" (Unix timestamp, timezone offset).
187 """
187 """
188 return "%d %d" % text
188 return "%d %d" % text
189
189
190 @templatefilter('isodate')
190 @templatefilter('isodate')
191 def isodate(text):
191 def isodate(text):
192 """Date. Returns the date in ISO 8601 format: "2009-08-18 13:00
192 """Date. Returns the date in ISO 8601 format: "2009-08-18 13:00
193 +0200".
193 +0200".
194 """
194 """
195 return util.datestr(text, '%Y-%m-%d %H:%M %1%2')
195 return util.datestr(text, '%Y-%m-%d %H:%M %1%2')
196
196
197 @templatefilter('isodatesec')
197 @templatefilter('isodatesec')
198 def isodatesec(text):
198 def isodatesec(text):
199 """Date. Returns the date in ISO 8601 format, including
199 """Date. Returns the date in ISO 8601 format, including
200 seconds: "2009-08-18 13:00:13 +0200". See also the rfc3339date
200 seconds: "2009-08-18 13:00:13 +0200". See also the rfc3339date
201 filter.
201 filter.
202 """
202 """
203 return util.datestr(text, '%Y-%m-%d %H:%M:%S %1%2')
203 return util.datestr(text, '%Y-%m-%d %H:%M:%S %1%2')
204
204
205 def indent(text, prefix):
205 def indent(text, prefix):
206 '''indent each non-empty line of text after first with prefix.'''
206 '''indent each non-empty line of text after first with prefix.'''
207 lines = text.splitlines()
207 lines = text.splitlines()
208 num_lines = len(lines)
208 num_lines = len(lines)
209 endswithnewline = text[-1:] == '\n'
209 endswithnewline = text[-1:] == '\n'
210 def indenter():
210 def indenter():
211 for i in xrange(num_lines):
211 for i in xrange(num_lines):
212 l = lines[i]
212 l = lines[i]
213 if i and l.strip():
213 if i and l.strip():
214 yield prefix
214 yield prefix
215 yield l
215 yield l
216 if i < num_lines - 1 or endswithnewline:
216 if i < num_lines - 1 or endswithnewline:
217 yield '\n'
217 yield '\n'
218 return "".join(indenter())
218 return "".join(indenter())
219
219
220 @templatefilter('json')
220 @templatefilter('json')
221 def json(obj):
221 def json(obj):
222 if obj is None:
222 if obj is None:
223 return 'null'
223 return 'null'
224 elif obj is False:
224 elif obj is False:
225 return 'false'
225 return 'false'
226 elif obj is True:
226 elif obj is True:
227 return 'true'
227 return 'true'
228 elif isinstance(obj, (int, long, float)):
228 elif isinstance(obj, (int, long, float)):
229 return str(obj)
229 return str(obj)
230 elif isinstance(obj, str):
230 elif isinstance(obj, str):
231 return '"%s"' % encoding.jsonescape(obj, paranoid=True)
231 return '"%s"' % encoding.jsonescape(obj, paranoid=True)
232 elif util.safehasattr(obj, 'keys'):
232 elif util.safehasattr(obj, 'keys'):
233 out = []
233 out = ['%s: %s' % (json(k), json(v))
234 for k, v in sorted(obj.iteritems()):
234 for k, v in sorted(obj.iteritems())]
235 s = '%s: %s' % (json(k), json(v))
236 out.append(s)
237 return '{' + ', '.join(out) + '}'
235 return '{' + ', '.join(out) + '}'
238 elif util.safehasattr(obj, '__iter__'):
236 elif util.safehasattr(obj, '__iter__'):
239 out = []
237 out = [json(i) for i in obj]
240 for i in obj:
241 out.append(json(i))
242 return '[' + ', '.join(out) + ']'
238 return '[' + ', '.join(out) + ']'
243 else:
239 else:
244 raise TypeError('cannot encode type %s' % obj.__class__.__name__)
240 raise TypeError('cannot encode type %s' % obj.__class__.__name__)
245
241
246 @templatefilter('lower')
242 @templatefilter('lower')
247 def lower(text):
243 def lower(text):
248 """Any text. Converts the text to lowercase."""
244 """Any text. Converts the text to lowercase."""
249 return encoding.lower(text)
245 return encoding.lower(text)
250
246
251 @templatefilter('nonempty')
247 @templatefilter('nonempty')
252 def nonempty(str):
248 def nonempty(str):
253 """Any text. Returns '(none)' if the string is empty."""
249 """Any text. Returns '(none)' if the string is empty."""
254 return str or "(none)"
250 return str or "(none)"
255
251
256 @templatefilter('obfuscate')
252 @templatefilter('obfuscate')
257 def obfuscate(text):
253 def obfuscate(text):
258 """Any text. Returns the input text rendered as a sequence of
254 """Any text. Returns the input text rendered as a sequence of
259 XML entities.
255 XML entities.
260 """
256 """
261 text = unicode(text, encoding.encoding, 'replace')
257 text = unicode(text, encoding.encoding, 'replace')
262 return ''.join(['&#%d;' % ord(c) for c in text])
258 return ''.join(['&#%d;' % ord(c) for c in text])
263
259
264 @templatefilter('permissions')
260 @templatefilter('permissions')
265 def permissions(flags):
261 def permissions(flags):
266 if "l" in flags:
262 if "l" in flags:
267 return "lrwxrwxrwx"
263 return "lrwxrwxrwx"
268 if "x" in flags:
264 if "x" in flags:
269 return "-rwxr-xr-x"
265 return "-rwxr-xr-x"
270 return "-rw-r--r--"
266 return "-rw-r--r--"
271
267
272 @templatefilter('person')
268 @templatefilter('person')
273 def person(author):
269 def person(author):
274 """Any text. Returns the name before an email address,
270 """Any text. Returns the name before an email address,
275 interpreting it as per RFC 5322.
271 interpreting it as per RFC 5322.
276
272
277 >>> person('foo@bar')
273 >>> person('foo@bar')
278 'foo'
274 'foo'
279 >>> person('Foo Bar <foo@bar>')
275 >>> person('Foo Bar <foo@bar>')
280 'Foo Bar'
276 'Foo Bar'
281 >>> person('"Foo Bar" <foo@bar>')
277 >>> person('"Foo Bar" <foo@bar>')
282 'Foo Bar'
278 'Foo Bar'
283 >>> person('"Foo \"buz\" Bar" <foo@bar>')
279 >>> person('"Foo \"buz\" Bar" <foo@bar>')
284 'Foo "buz" Bar'
280 'Foo "buz" Bar'
285 >>> # The following are invalid, but do exist in real-life
281 >>> # The following are invalid, but do exist in real-life
286 ...
282 ...
287 >>> person('Foo "buz" Bar <foo@bar>')
283 >>> person('Foo "buz" Bar <foo@bar>')
288 'Foo "buz" Bar'
284 'Foo "buz" Bar'
289 >>> person('"Foo Bar <foo@bar>')
285 >>> person('"Foo Bar <foo@bar>')
290 'Foo Bar'
286 'Foo Bar'
291 """
287 """
292 if '@' not in author:
288 if '@' not in author:
293 return author
289 return author
294 f = author.find('<')
290 f = author.find('<')
295 if f != -1:
291 if f != -1:
296 return author[:f].strip(' "').replace('\\"', '"')
292 return author[:f].strip(' "').replace('\\"', '"')
297 f = author.find('@')
293 f = author.find('@')
298 return author[:f].replace('.', ' ')
294 return author[:f].replace('.', ' ')
299
295
300 @templatefilter('revescape')
296 @templatefilter('revescape')
301 def revescape(text):
297 def revescape(text):
302 """Any text. Escapes all "special" characters, except @.
298 """Any text. Escapes all "special" characters, except @.
303 Forward slashes are escaped twice to prevent web servers from prematurely
299 Forward slashes are escaped twice to prevent web servers from prematurely
304 unescaping them. For example, "@foo bar/baz" becomes "@foo%20bar%252Fbaz".
300 unescaping them. For example, "@foo bar/baz" becomes "@foo%20bar%252Fbaz".
305 """
301 """
306 return urlreq.quote(text, safe='/@').replace('/', '%252F')
302 return urlreq.quote(text, safe='/@').replace('/', '%252F')
307
303
308 @templatefilter('rfc3339date')
304 @templatefilter('rfc3339date')
309 def rfc3339date(text):
305 def rfc3339date(text):
310 """Date. Returns a date using the Internet date format
306 """Date. Returns a date using the Internet date format
311 specified in RFC 3339: "2009-08-18T13:00:13+02:00".
307 specified in RFC 3339: "2009-08-18T13:00:13+02:00".
312 """
308 """
313 return util.datestr(text, "%Y-%m-%dT%H:%M:%S%1:%2")
309 return util.datestr(text, "%Y-%m-%dT%H:%M:%S%1:%2")
314
310
315 @templatefilter('rfc822date')
311 @templatefilter('rfc822date')
316 def rfc822date(text):
312 def rfc822date(text):
317 """Date. Returns a date using the same format used in email
313 """Date. Returns a date using the same format used in email
318 headers: "Tue, 18 Aug 2009 13:00:13 +0200".
314 headers: "Tue, 18 Aug 2009 13:00:13 +0200".
319 """
315 """
320 return util.datestr(text, "%a, %d %b %Y %H:%M:%S %1%2")
316 return util.datestr(text, "%a, %d %b %Y %H:%M:%S %1%2")
321
317
322 @templatefilter('short')
318 @templatefilter('short')
323 def short(text):
319 def short(text):
324 """Changeset hash. Returns the short form of a changeset hash,
320 """Changeset hash. Returns the short form of a changeset hash,
325 i.e. a 12 hexadecimal digit string.
321 i.e. a 12 hexadecimal digit string.
326 """
322 """
327 return text[:12]
323 return text[:12]
328
324
329 @templatefilter('shortbisect')
325 @templatefilter('shortbisect')
330 def shortbisect(text):
326 def shortbisect(text):
331 """Any text. Treats `text` as a bisection status, and
327 """Any text. Treats `text` as a bisection status, and
332 returns a single-character representing the status (G: good, B: bad,
328 returns a single-character representing the status (G: good, B: bad,
333 S: skipped, U: untested, I: ignored). Returns single space if `text`
329 S: skipped, U: untested, I: ignored). Returns single space if `text`
334 is not a valid bisection status.
330 is not a valid bisection status.
335 """
331 """
336 return hbisect.shortlabel(text) or ' '
332 return hbisect.shortlabel(text) or ' '
337
333
338 @templatefilter('shortdate')
334 @templatefilter('shortdate')
339 def shortdate(text):
335 def shortdate(text):
340 """Date. Returns a date like "2006-09-18"."""
336 """Date. Returns a date like "2006-09-18"."""
341 return util.shortdate(text)
337 return util.shortdate(text)
342
338
343 @templatefilter('splitlines')
339 @templatefilter('splitlines')
344 def splitlines(text):
340 def splitlines(text):
345 """Any text. Split text into a list of lines."""
341 """Any text. Split text into a list of lines."""
346 return templatekw.showlist('line', text.splitlines(), 'lines')
342 return templatekw.showlist('line', text.splitlines(), 'lines')
347
343
348 @templatefilter('stringescape')
344 @templatefilter('stringescape')
349 def stringescape(text):
345 def stringescape(text):
350 return util.escapestr(text)
346 return util.escapestr(text)
351
347
352 @templatefilter('stringify')
348 @templatefilter('stringify')
353 def stringify(thing):
349 def stringify(thing):
354 """Any type. Turns the value into text by converting values into
350 """Any type. Turns the value into text by converting values into
355 text and concatenating them.
351 text and concatenating them.
356 """
352 """
357 if util.safehasattr(thing, '__iter__') and not isinstance(thing, str):
353 if util.safehasattr(thing, '__iter__') and not isinstance(thing, str):
358 return "".join([stringify(t) for t in thing if t is not None])
354 return "".join([stringify(t) for t in thing if t is not None])
359 if thing is None:
355 if thing is None:
360 return ""
356 return ""
361 return str(thing)
357 return str(thing)
362
358
363 @templatefilter('stripdir')
359 @templatefilter('stripdir')
364 def stripdir(text):
360 def stripdir(text):
365 """Treat the text as path and strip a directory level, if
361 """Treat the text as path and strip a directory level, if
366 possible. For example, "foo" and "foo/bar" becomes "foo".
362 possible. For example, "foo" and "foo/bar" becomes "foo".
367 """
363 """
368 dir = os.path.dirname(text)
364 dir = os.path.dirname(text)
369 if dir == "":
365 if dir == "":
370 return os.path.basename(text)
366 return os.path.basename(text)
371 else:
367 else:
372 return dir
368 return dir
373
369
374 @templatefilter('tabindent')
370 @templatefilter('tabindent')
375 def tabindent(text):
371 def tabindent(text):
376 """Any text. Returns the text, with every non-empty line
372 """Any text. Returns the text, with every non-empty line
377 except the first starting with a tab character.
373 except the first starting with a tab character.
378 """
374 """
379 return indent(text, '\t')
375 return indent(text, '\t')
380
376
381 @templatefilter('upper')
377 @templatefilter('upper')
382 def upper(text):
378 def upper(text):
383 """Any text. Converts the text to uppercase."""
379 """Any text. Converts the text to uppercase."""
384 return encoding.upper(text)
380 return encoding.upper(text)
385
381
386 @templatefilter('urlescape')
382 @templatefilter('urlescape')
387 def urlescape(text):
383 def urlescape(text):
388 """Any text. Escapes all "special" characters. For example,
384 """Any text. Escapes all "special" characters. For example,
389 "foo bar" becomes "foo%20bar".
385 "foo bar" becomes "foo%20bar".
390 """
386 """
391 return urlreq.quote(text)
387 return urlreq.quote(text)
392
388
393 @templatefilter('user')
389 @templatefilter('user')
394 def userfilter(text):
390 def userfilter(text):
395 """Any text. Returns a short representation of a user name or email
391 """Any text. Returns a short representation of a user name or email
396 address."""
392 address."""
397 return util.shortuser(text)
393 return util.shortuser(text)
398
394
399 @templatefilter('emailuser')
395 @templatefilter('emailuser')
400 def emailuser(text):
396 def emailuser(text):
401 """Any text. Returns the user portion of an email address."""
397 """Any text. Returns the user portion of an email address."""
402 return util.emailuser(text)
398 return util.emailuser(text)
403
399
404 @templatefilter('utf8')
400 @templatefilter('utf8')
405 def utf8(text):
401 def utf8(text):
406 """Any text. Converts from the local character encoding to UTF-8."""
402 """Any text. Converts from the local character encoding to UTF-8."""
407 return encoding.fromlocal(text)
403 return encoding.fromlocal(text)
408
404
409 @templatefilter('xmlescape')
405 @templatefilter('xmlescape')
410 def xmlescape(text):
406 def xmlescape(text):
411 text = (text
407 text = (text
412 .replace('&', '&amp;')
408 .replace('&', '&amp;')
413 .replace('<', '&lt;')
409 .replace('<', '&lt;')
414 .replace('>', '&gt;')
410 .replace('>', '&gt;')
415 .replace('"', '&quot;')
411 .replace('"', '&quot;')
416 .replace("'", '&#39;')) # &apos; invalid in HTML
412 .replace("'", '&#39;')) # &apos; invalid in HTML
417 return re.sub('[\x00-\x08\x0B\x0C\x0E-\x1F]', ' ', text)
413 return re.sub('[\x00-\x08\x0B\x0C\x0E-\x1F]', ' ', text)
418
414
419 def websub(text, websubtable):
415 def websub(text, websubtable):
420 """:websub: Any text. Only applies to hgweb. Applies the regular
416 """:websub: Any text. Only applies to hgweb. Applies the regular
421 expression replacements defined in the websub section.
417 expression replacements defined in the websub section.
422 """
418 """
423 if websubtable:
419 if websubtable:
424 for regexp, format in websubtable:
420 for regexp, format in websubtable:
425 text = regexp.sub(format, text)
421 text = regexp.sub(format, text)
426 return text
422 return text
427
423
428 def loadfilter(ui, extname, registrarobj):
424 def loadfilter(ui, extname, registrarobj):
429 """Load template filter from specified registrarobj
425 """Load template filter from specified registrarobj
430 """
426 """
431 for name, func in registrarobj._table.iteritems():
427 for name, func in registrarobj._table.iteritems():
432 filters[name] = func
428 filters[name] = func
433
429
434 # tell hggettext to extract docstrings from these functions:
430 # tell hggettext to extract docstrings from these functions:
435 i18nfunctions = filters.values()
431 i18nfunctions = filters.values()
General Comments 0
You need to be logged in to leave comments. Login now