##// 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 from . import (
17 17 encoding,
18 18 hbisect,
19 19 node,
20 registrar,
20 21 templatekw,
21 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 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 36 every line except the last.
27 37 """
28 38 return text.replace('\n', '<br/>\n')
@@ -35,8 +45,9 agescales = [("year", 3600 * 24 * 365, '
35 45 ("minute", 60, 'm'),
36 46 ("second", 1, 's')]
37 47
48 @templatefilter('age')
38 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 51 given date/time and the current date/time.
41 52 """
42 53
@@ -69,20 +80,23 def age(date, abbrev=False):
69 80 return '%s from now' % fmt(t, n, a)
70 81 return '%s ago' % fmt(t, n, a)
71 82
83 @templatefilter('basename')
72 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 86 component of the path after splitting by the path separator
75 87 (ignoring trailing separators). For example, "foo/bar/baz" becomes
76 88 "baz" and "foo/bar//" becomes "bar".
77 89 """
78 90 return os.path.basename(path)
79 91
92 @templatefilter('count')
80 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 95 return len(i)
83 96
97 @templatefilter('domain')
84 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 100 address, and extracts just the domain component. Example: ``User
87 101 <user@example.com>`` becomes ``example.com``.
88 102 """
@@ -95,15 +109,17 def domain(author):
95 109 author = author[:f]
96 110 return author
97 111
112 @templatefilter('email')
98 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 115 address. Example: ``User <user@example.com>`` becomes
101 116 ``user@example.com``.
102 117 """
103 118 return util.email(text)
104 119
120 @templatefilter('escape')
105 121 def escape(text):
106 """:escape: Any text. Replaces the special XML/XHTML characters "&", "<"
122 """Any text. Replaces the special XML/XHTML characters "&", "<"
107 123 and ">" with XML entities, and filters out NUL characters.
108 124 """
109 125 return cgi.escape(text.replace('\0', ''), True)
@@ -137,41 +153,48 def fill(text, width, initindent='', han
137 153 width, initindent, hangindent) + rest
138 154 for para, rest in findparas()])
139 155
156 @templatefilter('fill68')
140 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 159 return fill(text, 68)
143 160
161 @templatefilter('fill76')
144 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 164 return fill(text, 76)
147 165
166 @templatefilter('firstline')
148 167 def firstline(text):
149 """:firstline: Any text. Returns the first line of text."""
168 """Any text. Returns the first line of text."""
150 169 try:
151 170 return text.splitlines(True)[0].rstrip('\r\n')
152 171 except IndexError:
153 172 return ''
154 173
174 @templatefilter('hex')
155 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 177 its long hexadecimal representation.
158 178 """
159 179 return node.hex(text)
160 180
181 @templatefilter('hgdate')
161 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 184 25200" (Unix timestamp, timezone offset).
164 185 """
165 186 return "%d %d" % text
166 187
188 @templatefilter('isodate')
167 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 191 +0200".
170 192 """
171 193 return util.datestr(text, '%Y-%m-%d %H:%M %1%2')
172 194
195 @templatefilter('isodatesec')
173 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 198 seconds: "2009-08-18 13:00:13 +0200". See also the rfc3339date
176 199 filter.
177 200 """
@@ -192,6 +215,7 def indent(text, prefix):
192 215 yield '\n'
193 216 return "".join(indenter())
194 217
218 @templatefilter('json')
195 219 def json(obj):
196 220 if obj is None or obj is False or obj is True:
197 221 return {None: 'null', False: 'false', True: 'true'}[obj]
@@ -215,21 +239,25 def json(obj):
215 239 else:
216 240 raise TypeError('cannot encode type %s' % obj.__class__.__name__)
217 241
242 @templatefilter('lower')
218 243 def lower(text):
219 """:lower: Any text. Converts the text to lowercase."""
244 """Any text. Converts the text to lowercase."""
220 245 return encoding.lower(text)
221 246
247 @templatefilter('nonempty')
222 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 250 return str or "(none)"
225 251
252 @templatefilter('obfuscate')
226 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 255 XML entities.
229 256 """
230 257 text = unicode(text, encoding.encoding, 'replace')
231 258 return ''.join(['&#%d;' % ord(c) for c in text])
232 259
260 @templatefilter('permissions')
233 261 def permissions(flags):
234 262 if "l" in flags:
235 263 return "lrwxrwxrwx"
@@ -237,8 +265,9 def permissions(flags):
237 265 return "-rwxr-xr-x"
238 266 return "-rw-r--r--"
239 267
268 @templatefilter('person')
240 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 271 interpreting it as per RFC 5322.
243 272
244 273 >>> person('foo@bar')
@@ -264,52 +293,61 def person(author):
264 293 f = author.find('@')
265 294 return author[:f].replace('.', ' ')
266 295
296 @templatefilter('revescape')
267 297 def revescape(text):
268 """:revescape: Any text. Escapes all "special" characters, except @.
298 """Any text. Escapes all "special" characters, except @.
269 299 Forward slashes are escaped twice to prevent web servers from prematurely
270 300 unescaping them. For example, "@foo bar/baz" becomes "@foo%20bar%252Fbaz".
271 301 """
272 302 return urllib.quote(text, safe='/@').replace('/', '%252F')
273 303
304 @templatefilter('rfc3339date')
274 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 307 specified in RFC 3339: "2009-08-18T13:00:13+02:00".
277 308 """
278 309 return util.datestr(text, "%Y-%m-%dT%H:%M:%S%1:%2")
279 310
311 @templatefilter('rfc822date')
280 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 314 headers: "Tue, 18 Aug 2009 13:00:13 +0200".
283 315 """
284 316 return util.datestr(text, "%a, %d %b %Y %H:%M:%S %1%2")
285 317
318 @templatefilter('short')
286 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 321 i.e. a 12 hexadecimal digit string.
289 322 """
290 323 return text[:12]
291 324
325 @templatefilter('shortbisect')
292 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 328 returns a single-character representing the status (G: good, B: bad,
295 329 S: skipped, U: untested, I: ignored). Returns single space if `text`
296 330 is not a valid bisection status.
297 331 """
298 332 return hbisect.shortlabel(text) or ' '
299 333
334 @templatefilter('shortdate')
300 335 def shortdate(text):
301 """:shortdate: Date. Returns a date like "2006-09-18"."""
336 """Date. Returns a date like "2006-09-18"."""
302 337 return util.shortdate(text)
303 338
339 @templatefilter('splitlines')
304 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 342 return templatekw.showlist('line', text.splitlines(), 'lines')
307 343
344 @templatefilter('stringescape')
308 345 def stringescape(text):
309 346 return text.encode('string_escape')
310 347
348 @templatefilter('stringify')
311 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 351 text and concatenating them.
314 352 """
315 353 if util.safehasattr(thing, '__iter__') and not isinstance(thing, str):
@@ -318,8 +356,9 def stringify(thing):
318 356 return ""
319 357 return str(thing)
320 358
359 @templatefilter('stripdir')
321 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 362 possible. For example, "foo" and "foo/bar" becomes "foo".
324 363 """
325 364 dir = os.path.dirname(text)
@@ -328,35 +367,42 def stripdir(text):
328 367 else:
329 368 return dir
330 369
370 @templatefilter('tabindent')
331 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 373 except the first starting with a tab character.
334 374 """
335 375 return indent(text, '\t')
336 376
377 @templatefilter('upper')
337 378 def upper(text):
338 """:upper: Any text. Converts the text to uppercase."""
379 """Any text. Converts the text to uppercase."""
339 380 return encoding.upper(text)
340 381
382 @templatefilter('urlescape')
341 383 def urlescape(text):
342 """:urlescape: Any text. Escapes all "special" characters. For example,
384 """Any text. Escapes all "special" characters. For example,
343 385 "foo bar" becomes "foo%20bar".
344 386 """
345 387 return urllib.quote(text)
346 388
389 @templatefilter('user')
347 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 392 address."""
350 393 return util.shortuser(text)
351 394
395 @templatefilter('emailuser')
352 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 398 return util.emailuser(text)
355 399
400 @templatefilter('utf8')
356 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 403 return encoding.fromlocal(text)
359 404
405 @templatefilter('xmlescape')
360 406 def xmlescape(text):
361 407 text = (text
362 408 .replace('&', '&amp;')
@@ -366,46 +412,6 def xmlescape(text):
366 412 .replace("'", '&#39;')) # &apos; invalid in HTML
367 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 415 def websub(text, websubtable):
410 416 """:websub: Any text. Only applies to hgweb. Applies the regular
411 417 expression replacements defined in the websub section.
@@ -253,7 +253,7
253 253 mercurial/subrepo.py: error importing: <ImportError> No module named 'cStringIO' (error at cmdutil.py:*) (glob)
254 254 mercurial/tagmerge.py: error importing: <ImportError> No module named 'cStringIO' (error at parsers.py:*) (glob)
255 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 257 mercurial/templatekw.py: error importing: <ImportError> No module named 'cStringIO' (error at patch.py:*) (glob)
258 258 mercurial/templater.py: error importing: <ImportError> No module named 'cStringIO' (error at parsers.py:*) (glob)
259 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