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