##// END OF EJS Templates
templatefilters: unnest uescape()
Patrick Mezard -
r11891:0bedf3a2 stable
parent child Browse files
Show More
@@ -1,227 +1,227 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 util, encoding
9 import util, encoding
10
10
11 def stringify(thing):
11 def stringify(thing):
12 '''turn nested template iterator into string.'''
12 '''turn nested template iterator into string.'''
13 if hasattr(thing, '__iter__') and not isinstance(thing, str):
13 if hasattr(thing, '__iter__') and not isinstance(thing, str):
14 return "".join([stringify(t) for t in thing if t is not None])
14 return "".join([stringify(t) for t in thing if t is not None])
15 return str(thing)
15 return str(thing)
16
16
17 agescales = [("year", 3600 * 24 * 365),
17 agescales = [("year", 3600 * 24 * 365),
18 ("month", 3600 * 24 * 30),
18 ("month", 3600 * 24 * 30),
19 ("week", 3600 * 24 * 7),
19 ("week", 3600 * 24 * 7),
20 ("day", 3600 * 24),
20 ("day", 3600 * 24),
21 ("hour", 3600),
21 ("hour", 3600),
22 ("minute", 60),
22 ("minute", 60),
23 ("second", 1)]
23 ("second", 1)]
24
24
25 def age(date):
25 def age(date):
26 '''turn a (timestamp, tzoff) tuple into an age string.'''
26 '''turn a (timestamp, tzoff) tuple into an age string.'''
27
27
28 def plural(t, c):
28 def plural(t, c):
29 if c == 1:
29 if c == 1:
30 return t
30 return t
31 return t + "s"
31 return t + "s"
32 def fmt(t, c):
32 def fmt(t, c):
33 return "%d %s" % (c, plural(t, c))
33 return "%d %s" % (c, plural(t, c))
34
34
35 now = time.time()
35 now = time.time()
36 then = date[0]
36 then = date[0]
37 if then > now:
37 if then > now:
38 return 'in the future'
38 return 'in the future'
39
39
40 delta = max(1, int(now - then))
40 delta = max(1, int(now - then))
41 if delta > agescales[0][1] * 2:
41 if delta > agescales[0][1] * 2:
42 return util.shortdate(date)
42 return util.shortdate(date)
43
43
44 for t, s in agescales:
44 for t, s in agescales:
45 n = delta // s
45 n = delta // s
46 if n >= 2 or s == 1:
46 if n >= 2 or s == 1:
47 return '%s ago' % fmt(t, n)
47 return '%s ago' % fmt(t, n)
48
48
49 para_re = None
49 para_re = None
50 space_re = None
50 space_re = None
51
51
52 def fill(text, width):
52 def fill(text, width):
53 '''fill many paragraphs.'''
53 '''fill many paragraphs.'''
54 global para_re, space_re
54 global para_re, space_re
55 if para_re is None:
55 if para_re is None:
56 para_re = re.compile('(\n\n|\n\\s*[-*]\\s*)', re.M)
56 para_re = re.compile('(\n\n|\n\\s*[-*]\\s*)', re.M)
57 space_re = re.compile(r' +')
57 space_re = re.compile(r' +')
58
58
59 def findparas():
59 def findparas():
60 start = 0
60 start = 0
61 while True:
61 while True:
62 m = para_re.search(text, start)
62 m = para_re.search(text, start)
63 if not m:
63 if not m:
64 uctext = unicode(text[start:], encoding.encoding)
64 uctext = unicode(text[start:], encoding.encoding)
65 w = len(uctext)
65 w = len(uctext)
66 while 0 < w and uctext[w - 1].isspace():
66 while 0 < w and uctext[w - 1].isspace():
67 w -= 1
67 w -= 1
68 yield (uctext[:w].encode(encoding.encoding),
68 yield (uctext[:w].encode(encoding.encoding),
69 uctext[w:].encode(encoding.encoding))
69 uctext[w:].encode(encoding.encoding))
70 break
70 break
71 yield text[start:m.start(0)], m.group(1)
71 yield text[start:m.start(0)], m.group(1)
72 start = m.end(1)
72 start = m.end(1)
73
73
74 return "".join([space_re.sub(' ', util.wrap(para, width=width)) + rest
74 return "".join([space_re.sub(' ', util.wrap(para, width=width)) + rest
75 for para, rest in findparas()])
75 for para, rest in findparas()])
76
76
77 def firstline(text):
77 def firstline(text):
78 '''return the first line of text'''
78 '''return the first line of text'''
79 try:
79 try:
80 return text.splitlines(True)[0].rstrip('\r\n')
80 return text.splitlines(True)[0].rstrip('\r\n')
81 except IndexError:
81 except IndexError:
82 return ''
82 return ''
83
83
84 def nl2br(text):
84 def nl2br(text):
85 '''replace raw newlines with xhtml line breaks.'''
85 '''replace raw newlines with xhtml line breaks.'''
86 return text.replace('\n', '<br/>\n')
86 return text.replace('\n', '<br/>\n')
87
87
88 def obfuscate(text):
88 def obfuscate(text):
89 text = unicode(text, encoding.encoding, 'replace')
89 text = unicode(text, encoding.encoding, 'replace')
90 return ''.join(['&#%d;' % ord(c) for c in text])
90 return ''.join(['&#%d;' % ord(c) for c in text])
91
91
92 def domain(author):
92 def domain(author):
93 '''get domain of author, or empty string if none.'''
93 '''get domain of author, or empty string if none.'''
94 f = author.find('@')
94 f = author.find('@')
95 if f == -1:
95 if f == -1:
96 return ''
96 return ''
97 author = author[f + 1:]
97 author = author[f + 1:]
98 f = author.find('>')
98 f = author.find('>')
99 if f >= 0:
99 if f >= 0:
100 author = author[:f]
100 author = author[:f]
101 return author
101 return author
102
102
103 def person(author):
103 def person(author):
104 '''get name of author, or else username.'''
104 '''get name of author, or else username.'''
105 if not '@' in author:
105 if not '@' in author:
106 return author
106 return author
107 f = author.find('<')
107 f = author.find('<')
108 if f == -1:
108 if f == -1:
109 return util.shortuser(author)
109 return util.shortuser(author)
110 return author[:f].rstrip()
110 return author[:f].rstrip()
111
111
112 def indent(text, prefix):
112 def indent(text, prefix):
113 '''indent each non-empty line of text after first with prefix.'''
113 '''indent each non-empty line of text after first with prefix.'''
114 lines = text.splitlines()
114 lines = text.splitlines()
115 num_lines = len(lines)
115 num_lines = len(lines)
116 endswithnewline = text[-1:] == '\n'
116 endswithnewline = text[-1:] == '\n'
117 def indenter():
117 def indenter():
118 for i in xrange(num_lines):
118 for i in xrange(num_lines):
119 l = lines[i]
119 l = lines[i]
120 if i and l.strip():
120 if i and l.strip():
121 yield prefix
121 yield prefix
122 yield l
122 yield l
123 if i < num_lines - 1 or endswithnewline:
123 if i < num_lines - 1 or endswithnewline:
124 yield '\n'
124 yield '\n'
125 return "".join(indenter())
125 return "".join(indenter())
126
126
127 def permissions(flags):
127 def permissions(flags):
128 if "l" in flags:
128 if "l" in flags:
129 return "lrwxrwxrwx"
129 return "lrwxrwxrwx"
130 if "x" in flags:
130 if "x" in flags:
131 return "-rwxr-xr-x"
131 return "-rwxr-xr-x"
132 return "-rw-r--r--"
132 return "-rw-r--r--"
133
133
134 def xmlescape(text):
134 def xmlescape(text):
135 text = (text
135 text = (text
136 .replace('&', '&amp;')
136 .replace('&', '&amp;')
137 .replace('<', '&lt;')
137 .replace('<', '&lt;')
138 .replace('>', '&gt;')
138 .replace('>', '&gt;')
139 .replace('"', '&quot;')
139 .replace('"', '&quot;')
140 .replace("'", '&#39;')) # &apos; invalid in HTML
140 .replace("'", '&#39;')) # &apos; invalid in HTML
141 return re.sub('[\x00-\x08\x0B\x0C\x0E-\x1F]', ' ', text)
141 return re.sub('[\x00-\x08\x0B\x0C\x0E-\x1F]', ' ', text)
142
142
143 def uescape(c):
144 if ord(c) < 0x80:
145 return c
146 else:
147 return '\\u%04x' % ord(c)
148
143 _escapes = [
149 _escapes = [
144 ('\\', '\\\\'), ('"', '\\"'), ('\t', '\\t'), ('\n', '\\n'),
150 ('\\', '\\\\'), ('"', '\\"'), ('\t', '\\t'), ('\n', '\\n'),
145 ('\r', '\\r'), ('\f', '\\f'), ('\b', '\\b'),
151 ('\r', '\\r'), ('\f', '\\f'), ('\b', '\\b'),
146 ]
152 ]
147
153
148 def jsonescape(s):
154 def jsonescape(s):
149 for k, v in _escapes:
155 for k, v in _escapes:
150 s = s.replace(k, v)
156 s = s.replace(k, v)
151
152 def uescape(c):
153 if ord(c) < 0x80:
154 return c
155 else:
156 return '\\u%04x' % ord(c)
157 return ''.join(uescape(c) for c in s)
157 return ''.join(uescape(c) for c in s)
158
158
159 def json(obj):
159 def json(obj):
160 if obj is None or obj is False or obj is True:
160 if obj is None or obj is False or obj is True:
161 return {None: 'null', False: 'false', True: 'true'}[obj]
161 return {None: 'null', False: 'false', True: 'true'}[obj]
162 elif isinstance(obj, int) or isinstance(obj, float):
162 elif isinstance(obj, int) or isinstance(obj, float):
163 return str(obj)
163 return str(obj)
164 elif isinstance(obj, str):
164 elif isinstance(obj, str):
165 u = unicode(obj, encoding.encoding, 'replace')
165 u = unicode(obj, encoding.encoding, 'replace')
166 return '"%s"' % jsonescape(u)
166 return '"%s"' % jsonescape(u)
167 elif isinstance(obj, unicode):
167 elif isinstance(obj, unicode):
168 return '"%s"' % jsonescape(obj)
168 return '"%s"' % jsonescape(obj)
169 elif hasattr(obj, 'keys'):
169 elif hasattr(obj, 'keys'):
170 out = []
170 out = []
171 for k, v in obj.iteritems():
171 for k, v in obj.iteritems():
172 s = '%s: %s' % (json(k), json(v))
172 s = '%s: %s' % (json(k), json(v))
173 out.append(s)
173 out.append(s)
174 return '{' + ', '.join(out) + '}'
174 return '{' + ', '.join(out) + '}'
175 elif hasattr(obj, '__iter__'):
175 elif hasattr(obj, '__iter__'):
176 out = []
176 out = []
177 for i in obj:
177 for i in obj:
178 out.append(json(i))
178 out.append(json(i))
179 return '[' + ', '.join(out) + ']'
179 return '[' + ', '.join(out) + ']'
180 else:
180 else:
181 raise TypeError('cannot encode type %s' % obj.__class__.__name__)
181 raise TypeError('cannot encode type %s' % obj.__class__.__name__)
182
182
183 def stripdir(text):
183 def stripdir(text):
184 '''Treat the text as path and strip a directory level, if possible.'''
184 '''Treat the text as path and strip a directory level, if possible.'''
185 dir = os.path.dirname(text)
185 dir = os.path.dirname(text)
186 if dir == "":
186 if dir == "":
187 return os.path.basename(text)
187 return os.path.basename(text)
188 else:
188 else:
189 return dir
189 return dir
190
190
191 def nonempty(str):
191 def nonempty(str):
192 return str or "(none)"
192 return str or "(none)"
193
193
194 filters = {
194 filters = {
195 "addbreaks": nl2br,
195 "addbreaks": nl2br,
196 "basename": os.path.basename,
196 "basename": os.path.basename,
197 "stripdir": stripdir,
197 "stripdir": stripdir,
198 "age": age,
198 "age": age,
199 "date": lambda x: util.datestr(x),
199 "date": lambda x: util.datestr(x),
200 "domain": domain,
200 "domain": domain,
201 "email": util.email,
201 "email": util.email,
202 "escape": lambda x: cgi.escape(x, True),
202 "escape": lambda x: cgi.escape(x, True),
203 "fill68": lambda x: fill(x, width=68),
203 "fill68": lambda x: fill(x, width=68),
204 "fill76": lambda x: fill(x, width=76),
204 "fill76": lambda x: fill(x, width=76),
205 "firstline": firstline,
205 "firstline": firstline,
206 "tabindent": lambda x: indent(x, '\t'),
206 "tabindent": lambda x: indent(x, '\t'),
207 "hgdate": lambda x: "%d %d" % x,
207 "hgdate": lambda x: "%d %d" % x,
208 "isodate": lambda x: util.datestr(x, '%Y-%m-%d %H:%M %1%2'),
208 "isodate": lambda x: util.datestr(x, '%Y-%m-%d %H:%M %1%2'),
209 "isodatesec": lambda x: util.datestr(x, '%Y-%m-%d %H:%M:%S %1%2'),
209 "isodatesec": lambda x: util.datestr(x, '%Y-%m-%d %H:%M:%S %1%2'),
210 "json": json,
210 "json": json,
211 "jsonescape": jsonescape,
211 "jsonescape": jsonescape,
212 "localdate": lambda x: (x[0], util.makedate()[1]),
212 "localdate": lambda x: (x[0], util.makedate()[1]),
213 "nonempty": nonempty,
213 "nonempty": nonempty,
214 "obfuscate": obfuscate,
214 "obfuscate": obfuscate,
215 "permissions": permissions,
215 "permissions": permissions,
216 "person": person,
216 "person": person,
217 "rfc822date": lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S %1%2"),
217 "rfc822date": lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S %1%2"),
218 "rfc3339date": lambda x: util.datestr(x, "%Y-%m-%dT%H:%M:%S%1:%2"),
218 "rfc3339date": lambda x: util.datestr(x, "%Y-%m-%dT%H:%M:%S%1:%2"),
219 "short": lambda x: x[:12],
219 "short": lambda x: x[:12],
220 "shortdate": util.shortdate,
220 "shortdate": util.shortdate,
221 "stringify": stringify,
221 "stringify": stringify,
222 "strip": lambda x: x.strip(),
222 "strip": lambda x: x.strip(),
223 "urlescape": lambda x: urllib.quote(x),
223 "urlescape": lambda x: urllib.quote(x),
224 "user": lambda x: util.shortuser(x),
224 "user": lambda x: util.shortuser(x),
225 "stringescape": lambda x: x.encode('string_escape'),
225 "stringescape": lambda x: x.encode('string_escape'),
226 "xmlescape": xmlescape,
226 "xmlescape": xmlescape,
227 }
227 }
General Comments 0
You need to be logged in to leave comments. Login now