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