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