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