##// END OF EJS Templates
templatefilters: unroll handling of None/False/True...
Yuya Nishihara -
r31780:8d9eafe0 default
parent child Browse files
Show More
@@ -1,431 +1,435 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 or obj is False or obj is True:
222 if obj is None:
223 return {None: 'null', False: 'false', True: 'true'}[obj]
223 return 'null'
224 elif obj is False:
225 return 'false'
226 elif obj is True:
227 return 'true'
224 elif isinstance(obj, (int, long, float)):
228 elif isinstance(obj, (int, long, float)):
225 return str(obj)
229 return str(obj)
226 elif isinstance(obj, str):
230 elif isinstance(obj, str):
227 return '"%s"' % encoding.jsonescape(obj, paranoid=True)
231 return '"%s"' % encoding.jsonescape(obj, paranoid=True)
228 elif util.safehasattr(obj, 'keys'):
232 elif util.safehasattr(obj, 'keys'):
229 out = []
233 out = []
230 for k, v in sorted(obj.iteritems()):
234 for k, v in sorted(obj.iteritems()):
231 s = '%s: %s' % (json(k), json(v))
235 s = '%s: %s' % (json(k), json(v))
232 out.append(s)
236 out.append(s)
233 return '{' + ', '.join(out) + '}'
237 return '{' + ', '.join(out) + '}'
234 elif util.safehasattr(obj, '__iter__'):
238 elif util.safehasattr(obj, '__iter__'):
235 out = []
239 out = []
236 for i in obj:
240 for i in obj:
237 out.append(json(i))
241 out.append(json(i))
238 return '[' + ', '.join(out) + ']'
242 return '[' + ', '.join(out) + ']'
239 else:
243 else:
240 raise TypeError('cannot encode type %s' % obj.__class__.__name__)
244 raise TypeError('cannot encode type %s' % obj.__class__.__name__)
241
245
242 @templatefilter('lower')
246 @templatefilter('lower')
243 def lower(text):
247 def lower(text):
244 """Any text. Converts the text to lowercase."""
248 """Any text. Converts the text to lowercase."""
245 return encoding.lower(text)
249 return encoding.lower(text)
246
250
247 @templatefilter('nonempty')
251 @templatefilter('nonempty')
248 def nonempty(str):
252 def nonempty(str):
249 """Any text. Returns '(none)' if the string is empty."""
253 """Any text. Returns '(none)' if the string is empty."""
250 return str or "(none)"
254 return str or "(none)"
251
255
252 @templatefilter('obfuscate')
256 @templatefilter('obfuscate')
253 def obfuscate(text):
257 def obfuscate(text):
254 """Any text. Returns the input text rendered as a sequence of
258 """Any text. Returns the input text rendered as a sequence of
255 XML entities.
259 XML entities.
256 """
260 """
257 text = unicode(text, encoding.encoding, 'replace')
261 text = unicode(text, encoding.encoding, 'replace')
258 return ''.join(['&#%d;' % ord(c) for c in text])
262 return ''.join(['&#%d;' % ord(c) for c in text])
259
263
260 @templatefilter('permissions')
264 @templatefilter('permissions')
261 def permissions(flags):
265 def permissions(flags):
262 if "l" in flags:
266 if "l" in flags:
263 return "lrwxrwxrwx"
267 return "lrwxrwxrwx"
264 if "x" in flags:
268 if "x" in flags:
265 return "-rwxr-xr-x"
269 return "-rwxr-xr-x"
266 return "-rw-r--r--"
270 return "-rw-r--r--"
267
271
268 @templatefilter('person')
272 @templatefilter('person')
269 def person(author):
273 def person(author):
270 """Any text. Returns the name before an email address,
274 """Any text. Returns the name before an email address,
271 interpreting it as per RFC 5322.
275 interpreting it as per RFC 5322.
272
276
273 >>> person('foo@bar')
277 >>> person('foo@bar')
274 'foo'
278 'foo'
275 >>> person('Foo Bar <foo@bar>')
279 >>> person('Foo Bar <foo@bar>')
276 'Foo Bar'
280 'Foo Bar'
277 >>> person('"Foo Bar" <foo@bar>')
281 >>> person('"Foo Bar" <foo@bar>')
278 'Foo Bar'
282 'Foo Bar'
279 >>> person('"Foo \"buz\" Bar" <foo@bar>')
283 >>> person('"Foo \"buz\" Bar" <foo@bar>')
280 'Foo "buz" Bar'
284 'Foo "buz" Bar'
281 >>> # The following are invalid, but do exist in real-life
285 >>> # The following are invalid, but do exist in real-life
282 ...
286 ...
283 >>> person('Foo "buz" Bar <foo@bar>')
287 >>> person('Foo "buz" Bar <foo@bar>')
284 'Foo "buz" Bar'
288 'Foo "buz" Bar'
285 >>> person('"Foo Bar <foo@bar>')
289 >>> person('"Foo Bar <foo@bar>')
286 'Foo Bar'
290 'Foo Bar'
287 """
291 """
288 if '@' not in author:
292 if '@' not in author:
289 return author
293 return author
290 f = author.find('<')
294 f = author.find('<')
291 if f != -1:
295 if f != -1:
292 return author[:f].strip(' "').replace('\\"', '"')
296 return author[:f].strip(' "').replace('\\"', '"')
293 f = author.find('@')
297 f = author.find('@')
294 return author[:f].replace('.', ' ')
298 return author[:f].replace('.', ' ')
295
299
296 @templatefilter('revescape')
300 @templatefilter('revescape')
297 def revescape(text):
301 def revescape(text):
298 """Any text. Escapes all "special" characters, except @.
302 """Any text. Escapes all "special" characters, except @.
299 Forward slashes are escaped twice to prevent web servers from prematurely
303 Forward slashes are escaped twice to prevent web servers from prematurely
300 unescaping them. For example, "@foo bar/baz" becomes "@foo%20bar%252Fbaz".
304 unescaping them. For example, "@foo bar/baz" becomes "@foo%20bar%252Fbaz".
301 """
305 """
302 return urlreq.quote(text, safe='/@').replace('/', '%252F')
306 return urlreq.quote(text, safe='/@').replace('/', '%252F')
303
307
304 @templatefilter('rfc3339date')
308 @templatefilter('rfc3339date')
305 def rfc3339date(text):
309 def rfc3339date(text):
306 """Date. Returns a date using the Internet date format
310 """Date. Returns a date using the Internet date format
307 specified in RFC 3339: "2009-08-18T13:00:13+02:00".
311 specified in RFC 3339: "2009-08-18T13:00:13+02:00".
308 """
312 """
309 return util.datestr(text, "%Y-%m-%dT%H:%M:%S%1:%2")
313 return util.datestr(text, "%Y-%m-%dT%H:%M:%S%1:%2")
310
314
311 @templatefilter('rfc822date')
315 @templatefilter('rfc822date')
312 def rfc822date(text):
316 def rfc822date(text):
313 """Date. Returns a date using the same format used in email
317 """Date. Returns a date using the same format used in email
314 headers: "Tue, 18 Aug 2009 13:00:13 +0200".
318 headers: "Tue, 18 Aug 2009 13:00:13 +0200".
315 """
319 """
316 return util.datestr(text, "%a, %d %b %Y %H:%M:%S %1%2")
320 return util.datestr(text, "%a, %d %b %Y %H:%M:%S %1%2")
317
321
318 @templatefilter('short')
322 @templatefilter('short')
319 def short(text):
323 def short(text):
320 """Changeset hash. Returns the short form of a changeset hash,
324 """Changeset hash. Returns the short form of a changeset hash,
321 i.e. a 12 hexadecimal digit string.
325 i.e. a 12 hexadecimal digit string.
322 """
326 """
323 return text[:12]
327 return text[:12]
324
328
325 @templatefilter('shortbisect')
329 @templatefilter('shortbisect')
326 def shortbisect(text):
330 def shortbisect(text):
327 """Any text. Treats `text` as a bisection status, and
331 """Any text. Treats `text` as a bisection status, and
328 returns a single-character representing the status (G: good, B: bad,
332 returns a single-character representing the status (G: good, B: bad,
329 S: skipped, U: untested, I: ignored). Returns single space if `text`
333 S: skipped, U: untested, I: ignored). Returns single space if `text`
330 is not a valid bisection status.
334 is not a valid bisection status.
331 """
335 """
332 return hbisect.shortlabel(text) or ' '
336 return hbisect.shortlabel(text) or ' '
333
337
334 @templatefilter('shortdate')
338 @templatefilter('shortdate')
335 def shortdate(text):
339 def shortdate(text):
336 """Date. Returns a date like "2006-09-18"."""
340 """Date. Returns a date like "2006-09-18"."""
337 return util.shortdate(text)
341 return util.shortdate(text)
338
342
339 @templatefilter('splitlines')
343 @templatefilter('splitlines')
340 def splitlines(text):
344 def splitlines(text):
341 """Any text. Split text into a list of lines."""
345 """Any text. Split text into a list of lines."""
342 return templatekw.showlist('line', text.splitlines(), 'lines')
346 return templatekw.showlist('line', text.splitlines(), 'lines')
343
347
344 @templatefilter('stringescape')
348 @templatefilter('stringescape')
345 def stringescape(text):
349 def stringescape(text):
346 return util.escapestr(text)
350 return util.escapestr(text)
347
351
348 @templatefilter('stringify')
352 @templatefilter('stringify')
349 def stringify(thing):
353 def stringify(thing):
350 """Any type. Turns the value into text by converting values into
354 """Any type. Turns the value into text by converting values into
351 text and concatenating them.
355 text and concatenating them.
352 """
356 """
353 if util.safehasattr(thing, '__iter__') and not isinstance(thing, str):
357 if util.safehasattr(thing, '__iter__') and not isinstance(thing, str):
354 return "".join([stringify(t) for t in thing if t is not None])
358 return "".join([stringify(t) for t in thing if t is not None])
355 if thing is None:
359 if thing is None:
356 return ""
360 return ""
357 return str(thing)
361 return str(thing)
358
362
359 @templatefilter('stripdir')
363 @templatefilter('stripdir')
360 def stripdir(text):
364 def stripdir(text):
361 """Treat the text as path and strip a directory level, if
365 """Treat the text as path and strip a directory level, if
362 possible. For example, "foo" and "foo/bar" becomes "foo".
366 possible. For example, "foo" and "foo/bar" becomes "foo".
363 """
367 """
364 dir = os.path.dirname(text)
368 dir = os.path.dirname(text)
365 if dir == "":
369 if dir == "":
366 return os.path.basename(text)
370 return os.path.basename(text)
367 else:
371 else:
368 return dir
372 return dir
369
373
370 @templatefilter('tabindent')
374 @templatefilter('tabindent')
371 def tabindent(text):
375 def tabindent(text):
372 """Any text. Returns the text, with every non-empty line
376 """Any text. Returns the text, with every non-empty line
373 except the first starting with a tab character.
377 except the first starting with a tab character.
374 """
378 """
375 return indent(text, '\t')
379 return indent(text, '\t')
376
380
377 @templatefilter('upper')
381 @templatefilter('upper')
378 def upper(text):
382 def upper(text):
379 """Any text. Converts the text to uppercase."""
383 """Any text. Converts the text to uppercase."""
380 return encoding.upper(text)
384 return encoding.upper(text)
381
385
382 @templatefilter('urlescape')
386 @templatefilter('urlescape')
383 def urlescape(text):
387 def urlescape(text):
384 """Any text. Escapes all "special" characters. For example,
388 """Any text. Escapes all "special" characters. For example,
385 "foo bar" becomes "foo%20bar".
389 "foo bar" becomes "foo%20bar".
386 """
390 """
387 return urlreq.quote(text)
391 return urlreq.quote(text)
388
392
389 @templatefilter('user')
393 @templatefilter('user')
390 def userfilter(text):
394 def userfilter(text):
391 """Any text. Returns a short representation of a user name or email
395 """Any text. Returns a short representation of a user name or email
392 address."""
396 address."""
393 return util.shortuser(text)
397 return util.shortuser(text)
394
398
395 @templatefilter('emailuser')
399 @templatefilter('emailuser')
396 def emailuser(text):
400 def emailuser(text):
397 """Any text. Returns the user portion of an email address."""
401 """Any text. Returns the user portion of an email address."""
398 return util.emailuser(text)
402 return util.emailuser(text)
399
403
400 @templatefilter('utf8')
404 @templatefilter('utf8')
401 def utf8(text):
405 def utf8(text):
402 """Any text. Converts from the local character encoding to UTF-8."""
406 """Any text. Converts from the local character encoding to UTF-8."""
403 return encoding.fromlocal(text)
407 return encoding.fromlocal(text)
404
408
405 @templatefilter('xmlescape')
409 @templatefilter('xmlescape')
406 def xmlescape(text):
410 def xmlescape(text):
407 text = (text
411 text = (text
408 .replace('&', '&amp;')
412 .replace('&', '&amp;')
409 .replace('<', '&lt;')
413 .replace('<', '&lt;')
410 .replace('>', '&gt;')
414 .replace('>', '&gt;')
411 .replace('"', '&quot;')
415 .replace('"', '&quot;')
412 .replace("'", '&#39;')) # &apos; invalid in HTML
416 .replace("'", '&#39;')) # &apos; invalid in HTML
413 return re.sub('[\x00-\x08\x0B\x0C\x0E-\x1F]', ' ', text)
417 return re.sub('[\x00-\x08\x0B\x0C\x0E-\x1F]', ' ', text)
414
418
415 def websub(text, websubtable):
419 def websub(text, websubtable):
416 """:websub: Any text. Only applies to hgweb. Applies the regular
420 """:websub: Any text. Only applies to hgweb. Applies the regular
417 expression replacements defined in the websub section.
421 expression replacements defined in the websub section.
418 """
422 """
419 if websubtable:
423 if websubtable:
420 for regexp, format in websubtable:
424 for regexp, format in websubtable:
421 text = regexp.sub(format, text)
425 text = regexp.sub(format, text)
422 return text
426 return text
423
427
424 def loadfilter(ui, extname, registrarobj):
428 def loadfilter(ui, extname, registrarobj):
425 """Load template filter from specified registrarobj
429 """Load template filter from specified registrarobj
426 """
430 """
427 for name, func in registrarobj._table.iteritems():
431 for name, func in registrarobj._table.iteritems():
428 filters[name] = func
432 filters[name] = func
429
433
430 # tell hggettext to extract docstrings from these functions:
434 # tell hggettext to extract docstrings from these functions:
431 i18nfunctions = filters.values()
435 i18nfunctions = filters.values()
General Comments 0
You need to be logged in to leave comments. Login now