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 |
""" |
|
|
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 |
""" |
|
|
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 |
""" |
|
|
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 |
""" |
|
|
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 |
""" |
|
|
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 |
""" |
|
|
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 |
""" |
|
|
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 |
""" |
|
|
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 |
""" |
|
|
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 |
""" |
|
|
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 |
""" |
|
|
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 |
""" |
|
|
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 |
""" |
|
|
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 |
""" |
|
|
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 |
""" |
|
|
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 |
""" |
|
|
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 |
""" |
|
|
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 |
""" |
|
|
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 |
""" |
|
|
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 |
""" |
|
|
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 |
""" |
|
|
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 |
""" |
|
|
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 |
""" |
|
|
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 |
""" |
|
|
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 |
""" |
|
|
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 |
""" |
|
|
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 |
""" |
|
|
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 |
""" |
|
|
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 |
""" |
|
|
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 |
""" |
|
|
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 |
""" |
|
|
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 |
""" |
|
|
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 |
""" |
|
|
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('&', '&') |
@@ -366,46 +412,6 def xmlescape(text): | |||
|
366 | 412 | .replace("'", ''')) # ' 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 pa |
|
|
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