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