Show More
@@ -0,0 +1,155 b'' | |||
|
1 | # template-filters.py - common template expansion filters | |
|
2 | # | |
|
3 | # Copyright 2005-2008 Matt Mackall <mpm@selenic.com> | |
|
4 | # | |
|
5 | # This software may be used and distributed according to the terms | |
|
6 | # of the GNU General Public License, incorporated herein by reference. | |
|
7 | ||
|
8 | import cgi, re, os, time, urllib, textwrap | |
|
9 | import util, templater | |
|
10 | ||
|
11 | agescales = [("second", 1), | |
|
12 | ("minute", 60), | |
|
13 | ("hour", 3600), | |
|
14 | ("day", 3600 * 24), | |
|
15 | ("week", 3600 * 24 * 7), | |
|
16 | ("month", 3600 * 24 * 30), | |
|
17 | ("year", 3600 * 24 * 365)] | |
|
18 | ||
|
19 | agescales.reverse() | |
|
20 | ||
|
21 | def age(date): | |
|
22 | '''turn a (timestamp, tzoff) tuple into an age string.''' | |
|
23 | ||
|
24 | def plural(t, c): | |
|
25 | if c == 1: | |
|
26 | return t | |
|
27 | return t + "s" | |
|
28 | def fmt(t, c): | |
|
29 | return "%d %s" % (c, plural(t, c)) | |
|
30 | ||
|
31 | now = time.time() | |
|
32 | then = date[0] | |
|
33 | delta = max(1, int(now - then)) | |
|
34 | ||
|
35 | for t, s in agescales: | |
|
36 | n = delta / s | |
|
37 | if n >= 2 or s == 1: | |
|
38 | return fmt(t, n) | |
|
39 | ||
|
40 | para_re = None | |
|
41 | space_re = None | |
|
42 | ||
|
43 | def fill(text, width): | |
|
44 | '''fill many paragraphs.''' | |
|
45 | global para_re, space_re | |
|
46 | if para_re is None: | |
|
47 | para_re = re.compile('(\n\n|\n\\s*[-*]\\s*)', re.M) | |
|
48 | space_re = re.compile(r' +') | |
|
49 | ||
|
50 | def findparas(): | |
|
51 | start = 0 | |
|
52 | while True: | |
|
53 | m = para_re.search(text, start) | |
|
54 | if not m: | |
|
55 | w = len(text) | |
|
56 | while w > start and text[w-1].isspace(): w -= 1 | |
|
57 | yield text[start:w], text[w:] | |
|
58 | break | |
|
59 | yield text[start:m.start(0)], m.group(1) | |
|
60 | start = m.end(1) | |
|
61 | ||
|
62 | return "".join([space_re.sub(' ', textwrap.fill(para, width)) + rest | |
|
63 | for para, rest in findparas()]) | |
|
64 | ||
|
65 | def firstline(text): | |
|
66 | '''return the first line of text''' | |
|
67 | try: | |
|
68 | return text.splitlines(1)[0].rstrip('\r\n') | |
|
69 | except IndexError: | |
|
70 | return '' | |
|
71 | ||
|
72 | def isodate(date): | |
|
73 | '''turn a (timestamp, tzoff) tuple into an iso 8631 date and time.''' | |
|
74 | return util.datestr(date, format='%Y-%m-%d %H:%M') | |
|
75 | ||
|
76 | def hgdate(date): | |
|
77 | '''turn a (timestamp, tzoff) tuple into an hg cset timestamp.''' | |
|
78 | return "%d %d" % date | |
|
79 | ||
|
80 | def nl2br(text): | |
|
81 | '''replace raw newlines with xhtml line breaks.''' | |
|
82 | return text.replace('\n', '<br/>\n') | |
|
83 | ||
|
84 | def obfuscate(text): | |
|
85 | text = unicode(text, util._encoding, 'replace') | |
|
86 | return ''.join(['&#%d;' % ord(c) for c in text]) | |
|
87 | ||
|
88 | def domain(author): | |
|
89 | '''get domain of author, or empty string if none.''' | |
|
90 | f = author.find('@') | |
|
91 | if f == -1: return '' | |
|
92 | author = author[f+1:] | |
|
93 | f = author.find('>') | |
|
94 | if f >= 0: author = author[:f] | |
|
95 | return author | |
|
96 | ||
|
97 | def person(author): | |
|
98 | '''get name of author, or else username.''' | |
|
99 | f = author.find('<') | |
|
100 | if f == -1: return util.shortuser(author) | |
|
101 | return author[:f].rstrip() | |
|
102 | ||
|
103 | def shortdate(date): | |
|
104 | '''turn (timestamp, tzoff) tuple into iso 8631 date.''' | |
|
105 | return util.datestr(date, format='%Y-%m-%d', timezone=False) | |
|
106 | ||
|
107 | def indent(text, prefix): | |
|
108 | '''indent each non-empty line of text after first with prefix.''' | |
|
109 | lines = text.splitlines() | |
|
110 | num_lines = len(lines) | |
|
111 | def indenter(): | |
|
112 | for i in xrange(num_lines): | |
|
113 | l = lines[i] | |
|
114 | if i and l.strip(): | |
|
115 | yield prefix | |
|
116 | yield l | |
|
117 | if i < num_lines - 1 or text.endswith('\n'): | |
|
118 | yield '\n' | |
|
119 | return "".join(indenter()) | |
|
120 | ||
|
121 | def permissions(flags): | |
|
122 | if "l" in flags: | |
|
123 | return "lrwxrwxrwx" | |
|
124 | if "x" in flags: | |
|
125 | return "-rwxr-xr-x" | |
|
126 | return "-rw-r--r--" | |
|
127 | ||
|
128 | filters = { | |
|
129 | "addbreaks": nl2br, | |
|
130 | "basename": os.path.basename, | |
|
131 | "age": age, | |
|
132 | "date": lambda x: util.datestr(x), | |
|
133 | "domain": domain, | |
|
134 | "email": util.email, | |
|
135 | "escape": lambda x: cgi.escape(x, True), | |
|
136 | "fill68": lambda x: fill(x, width=68), | |
|
137 | "fill76": lambda x: fill(x, width=76), | |
|
138 | "firstline": firstline, | |
|
139 | "tabindent": lambda x: indent(x, '\t'), | |
|
140 | "hgdate": hgdate, | |
|
141 | "isodate": isodate, | |
|
142 | "obfuscate": obfuscate, | |
|
143 | "permissions": permissions, | |
|
144 | "person": person, | |
|
145 | "rfc822date": lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S"), | |
|
146 | "rfc3339date": lambda x: util.datestr(x, "%Y-%m-%dT%H:%M:%S", True, "%+03d:%02d"), | |
|
147 | "short": lambda x: x[:12], | |
|
148 | "shortdate": shortdate, | |
|
149 | "stringify": templater.stringify, | |
|
150 | "strip": lambda x: x.strip(), | |
|
151 | "urlescape": lambda x: urllib.quote(x), | |
|
152 | "user": lambda x: util.shortuser(x), | |
|
153 | "stringescape": lambda x: x.encode('string_escape'), | |
|
154 | } | |
|
155 |
@@ -12,7 +12,7 b'' | |||
|
12 | 12 | # <alias email> <actual email> |
|
13 | 13 | |
|
14 | 14 | from mercurial.i18n import gettext as _ |
|
15 | from mercurial import hg, mdiff, cmdutil, ui, util, templater, node | |
|
15 | from mercurial import hg, mdiff, cmdutil, ui, util, templatefilters, node | |
|
16 | 16 | import os, sys |
|
17 | 17 | |
|
18 | 18 | def get_tty_width(): |
@@ -27,9 +27,9 b'' | |||
|
27 | 27 | |
|
28 | 28 | import re |
|
29 | 29 | from mercurial.hgweb import hgweb_mod |
|
30 | from mercurial import templater | |
|
30 | from mercurial import templatefilters | |
|
31 | 31 | |
|
32 |
orig_escape = templater. |
|
|
32 | orig_escape = templatefilters.filters["escape"] | |
|
33 | 33 | |
|
34 | 34 | interhg_table = [] |
|
35 | 35 | |
@@ -39,7 +39,7 b' def interhg_escape(x):' | |||
|
39 | 39 | escstr = regexp.sub(format, escstr) |
|
40 | 40 | return escstr |
|
41 | 41 | |
|
42 |
templater. |
|
|
42 | templatefilters.filters["escape"] = interhg_escape | |
|
43 | 43 | |
|
44 | 44 | orig_refresh = hgweb_mod.hgweb.refresh |
|
45 | 45 |
@@ -78,8 +78,8 b" like CVS' $Log$, are not supported. A ke" | |||
|
78 | 78 | "Log = {desc}" expands to the first line of the changeset description. |
|
79 | 79 | ''' |
|
80 | 80 | |
|
81 | from mercurial import commands, cmdutil, context, dispatch, filelog | |
|
82 |
from mercurial import patch, localrepo, |
|
|
81 | from mercurial import commands, cmdutil, context, dispatch, filelog, revlog | |
|
82 | from mercurial import patch, localrepo, templater, templatefilters, util | |
|
83 | 83 | from mercurial.node import * |
|
84 | 84 | from mercurial.i18n import _ |
|
85 | 85 | import re, shutil, sys, tempfile, time |
@@ -130,7 +130,7 b' class kwtemplater(object):' | |||
|
130 | 130 | kwpat = r'\$(%s)(: [^$\n\r]*? )??\$' % '|'.join(escaped) |
|
131 | 131 | self.re_kw = re.compile(kwpat) |
|
132 | 132 | |
|
133 |
templater. |
|
|
133 | templatefilters.filters['utcdate'] = utcdate | |
|
134 | 134 | self.ct = cmdutil.changeset_templater(self.ui, self.repo, |
|
135 | 135 | False, '', False) |
|
136 | 136 | |
@@ -149,7 +149,8 b' class kwtemplater(object):' | |||
|
149 | 149 | self.ct.use_template(self.templates[kw]) |
|
150 | 150 | self.ui.pushbuffer() |
|
151 | 151 | self.ct.show(changenode=fnode, root=self.repo.root, file=self.path) |
|
152 |
return '$%s: %s $' % (kw, templater.firstline( |
|
|
152 | return '$%s: %s $' % (kw, templatefilters.firstline( | |
|
153 | self.ui.popbuffer())) | |
|
153 | 154 | |
|
154 | 155 | return subfunc(kwsub, data) |
|
155 | 156 |
@@ -8,7 +8,7 b'' | |||
|
8 | 8 | from node import * |
|
9 | 9 | from i18n import _ |
|
10 | 10 | import os, sys, bisect, stat |
|
11 | import mdiff, bdiff, util, templater, patch, errno | |
|
11 | import mdiff, bdiff, util, templater, templatefilters, patch, errno | |
|
12 | 12 | |
|
13 | 13 | revrangesep = ':' |
|
14 | 14 | |
@@ -673,7 +673,7 b' class changeset_templater(changeset_prin' | |||
|
673 | 673 | |
|
674 | 674 | def __init__(self, ui, repo, patch, mapfile, buffered): |
|
675 | 675 | changeset_printer.__init__(self, ui, repo, patch, buffered) |
|
676 |
filters = templater. |
|
|
676 | filters = templatefilters.filters.copy() | |
|
677 | 677 | filters['formatnode'] = (ui.debugflag and (lambda x: x) |
|
678 | 678 | or (lambda x: x[:12])) |
|
679 | 679 | self.t = templater.templater(mapfile, filters, |
@@ -9,7 +9,7 b'' | |||
|
9 | 9 | import os, mimetypes, re |
|
10 | 10 | from mercurial.node import * |
|
11 | 11 | from mercurial import mdiff, ui, hg, util, archival, patch, hook |
|
12 | from mercurial import revlog, templater | |
|
12 | from mercurial import revlog, templater, templatefilters | |
|
13 | 13 | from common import ErrorResponse, get_mtime, style_map, paritygen, get_contact |
|
14 | 14 | from request import wsgirequest |
|
15 | 15 | import webcommands, protocol |
@@ -288,7 +288,7 b' class hgweb(object):' | |||
|
288 | 288 | |
|
289 | 289 | # create the templater |
|
290 | 290 | |
|
291 |
tmpl = templater.templater(mapfile, templater. |
|
|
291 | tmpl = templater.templater(mapfile, templatefilters.filters, | |
|
292 | 292 | defaults={"url": req.url, |
|
293 | 293 | "staticurl": staticurl, |
|
294 | 294 | "urlbase": urlbase, |
@@ -8,8 +8,8 b'' | |||
|
8 | 8 | |
|
9 | 9 | import os |
|
10 | 10 | from mercurial.i18n import gettext as _ |
|
11 | from mercurial import ui, hg, util, templater | |
|
12 |
from common import ErrorResponse, get_mtime, staticfile, style_map, paritygen, |
|
|
11 | from mercurial import ui, hg, util, templater, templatefilters | |
|
12 | from common import ErrorResponse, get_mtime, staticfile, style_map, paritygen,\ | |
|
13 | 13 | get_contact |
|
14 | 14 | from hgweb_mod import hgweb |
|
15 | 15 | from request import wsgirequest |
@@ -266,7 +266,7 b' class hgwebdir(object):' | |||
|
266 | 266 | if self.stripecount is None: |
|
267 | 267 | self.stripecount = int(config('web', 'stripes', 1)) |
|
268 | 268 | mapfile = style_map(templater.templatepath(), style) |
|
269 |
tmpl = templater.templater(mapfile, templater. |
|
|
269 | tmpl = templater.templater(mapfile, templatefilters.filters, | |
|
270 | 270 | defaults={"header": header, |
|
271 | 271 | "footer": footer, |
|
272 | 272 | "motd": motd, |
@@ -6,7 +6,7 b'' | |||
|
6 | 6 | # of the GNU General Public License, incorporated herein by reference. |
|
7 | 7 | |
|
8 | 8 | from i18n import _ |
|
9 | import cgi, re, sys, os, time, urllib, util, textwrap | |
|
9 | import re, sys, os | |
|
10 | 10 | |
|
11 | 11 | def parsestring(s, quoted=True): |
|
12 | 12 | '''parse a string using simple c-like syntax. |
@@ -122,157 +122,6 b' class templater(object):' | |||
|
122 | 122 | v = self.filters[f](v) |
|
123 | 123 | yield v |
|
124 | 124 | |
|
125 | agescales = [("second", 1), | |
|
126 | ("minute", 60), | |
|
127 | ("hour", 3600), | |
|
128 | ("day", 3600 * 24), | |
|
129 | ("week", 3600 * 24 * 7), | |
|
130 | ("month", 3600 * 24 * 30), | |
|
131 | ("year", 3600 * 24 * 365)] | |
|
132 | ||
|
133 | agescales.reverse() | |
|
134 | ||
|
135 | def age(date): | |
|
136 | '''turn a (timestamp, tzoff) tuple into an age string.''' | |
|
137 | ||
|
138 | def plural(t, c): | |
|
139 | if c == 1: | |
|
140 | return t | |
|
141 | return t + "s" | |
|
142 | def fmt(t, c): | |
|
143 | return "%d %s" % (c, plural(t, c)) | |
|
144 | ||
|
145 | now = time.time() | |
|
146 | then = date[0] | |
|
147 | delta = max(1, int(now - then)) | |
|
148 | ||
|
149 | for t, s in agescales: | |
|
150 | n = delta / s | |
|
151 | if n >= 2 or s == 1: | |
|
152 | return fmt(t, n) | |
|
153 | ||
|
154 | def stringify(thing): | |
|
155 | '''turn nested template iterator into string.''' | |
|
156 | if hasattr(thing, '__iter__'): | |
|
157 | return "".join([stringify(t) for t in thing if t is not None]) | |
|
158 | return str(thing) | |
|
159 | ||
|
160 | para_re = None | |
|
161 | space_re = None | |
|
162 | ||
|
163 | def fill(text, width): | |
|
164 | '''fill many paragraphs.''' | |
|
165 | global para_re, space_re | |
|
166 | if para_re is None: | |
|
167 | para_re = re.compile('(\n\n|\n\\s*[-*]\\s*)', re.M) | |
|
168 | space_re = re.compile(r' +') | |
|
169 | ||
|
170 | def findparas(): | |
|
171 | start = 0 | |
|
172 | while True: | |
|
173 | m = para_re.search(text, start) | |
|
174 | if not m: | |
|
175 | w = len(text) | |
|
176 | while w > start and text[w-1].isspace(): w -= 1 | |
|
177 | yield text[start:w], text[w:] | |
|
178 | break | |
|
179 | yield text[start:m.start(0)], m.group(1) | |
|
180 | start = m.end(1) | |
|
181 | ||
|
182 | return "".join([space_re.sub(' ', textwrap.fill(para, width)) + rest | |
|
183 | for para, rest in findparas()]) | |
|
184 | ||
|
185 | def firstline(text): | |
|
186 | '''return the first line of text''' | |
|
187 | try: | |
|
188 | return text.splitlines(1)[0].rstrip('\r\n') | |
|
189 | except IndexError: | |
|
190 | return '' | |
|
191 | ||
|
192 | def isodate(date): | |
|
193 | '''turn a (timestamp, tzoff) tuple into an iso 8631 date and time.''' | |
|
194 | return util.datestr(date, format='%Y-%m-%d %H:%M') | |
|
195 | ||
|
196 | def hgdate(date): | |
|
197 | '''turn a (timestamp, tzoff) tuple into an hg cset timestamp.''' | |
|
198 | return "%d %d" % date | |
|
199 | ||
|
200 | def nl2br(text): | |
|
201 | '''replace raw newlines with xhtml line breaks.''' | |
|
202 | return text.replace('\n', '<br/>\n') | |
|
203 | ||
|
204 | def obfuscate(text): | |
|
205 | text = unicode(text, util._encoding, 'replace') | |
|
206 | return ''.join(['&#%d;' % ord(c) for c in text]) | |
|
207 | ||
|
208 | def domain(author): | |
|
209 | '''get domain of author, or empty string if none.''' | |
|
210 | f = author.find('@') | |
|
211 | if f == -1: return '' | |
|
212 | author = author[f+1:] | |
|
213 | f = author.find('>') | |
|
214 | if f >= 0: author = author[:f] | |
|
215 | return author | |
|
216 | ||
|
217 | def person(author): | |
|
218 | '''get name of author, or else username.''' | |
|
219 | f = author.find('<') | |
|
220 | if f == -1: return util.shortuser(author) | |
|
221 | return author[:f].rstrip() | |
|
222 | ||
|
223 | def shortdate(date): | |
|
224 | '''turn (timestamp, tzoff) tuple into iso 8631 date.''' | |
|
225 | return util.datestr(date, format='%Y-%m-%d', timezone=False) | |
|
226 | ||
|
227 | def indent(text, prefix): | |
|
228 | '''indent each non-empty line of text after first with prefix.''' | |
|
229 | lines = text.splitlines() | |
|
230 | num_lines = len(lines) | |
|
231 | def indenter(): | |
|
232 | for i in xrange(num_lines): | |
|
233 | l = lines[i] | |
|
234 | if i and l.strip(): | |
|
235 | yield prefix | |
|
236 | yield l | |
|
237 | if i < num_lines - 1 or text.endswith('\n'): | |
|
238 | yield '\n' | |
|
239 | return "".join(indenter()) | |
|
240 | ||
|
241 | def permissions(flags): | |
|
242 | if "l" in flags: | |
|
243 | return "lrwxrwxrwx" | |
|
244 | if "x" in flags: | |
|
245 | return "-rwxr-xr-x" | |
|
246 | return "-rw-r--r--" | |
|
247 | ||
|
248 | common_filters = { | |
|
249 | "addbreaks": nl2br, | |
|
250 | "basename": os.path.basename, | |
|
251 | "age": age, | |
|
252 | "date": lambda x: util.datestr(x), | |
|
253 | "domain": domain, | |
|
254 | "email": util.email, | |
|
255 | "escape": lambda x: cgi.escape(x, True), | |
|
256 | "fill68": lambda x: fill(x, width=68), | |
|
257 | "fill76": lambda x: fill(x, width=76), | |
|
258 | "firstline": firstline, | |
|
259 | "tabindent": lambda x: indent(x, '\t'), | |
|
260 | "hgdate": hgdate, | |
|
261 | "isodate": isodate, | |
|
262 | "obfuscate": obfuscate, | |
|
263 | "permissions": permissions, | |
|
264 | "person": person, | |
|
265 | "rfc822date": lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S"), | |
|
266 | "rfc3339date": lambda x: util.datestr(x, "%Y-%m-%dT%H:%M:%S", True, "%+03d:%02d"), | |
|
267 | "short": lambda x: x[:12], | |
|
268 | "shortdate": shortdate, | |
|
269 | "stringify": stringify, | |
|
270 | "strip": lambda x: x.strip(), | |
|
271 | "urlescape": lambda x: urllib.quote(x), | |
|
272 | "user": lambda x: util.shortuser(x), | |
|
273 | "stringescape": lambda x: x.encode('string_escape'), | |
|
274 | } | |
|
275 | ||
|
276 | 125 | def templatepath(name=None): |
|
277 | 126 | '''return location of template file or directory (if no name). |
|
278 | 127 | returns None if not found.''' |
@@ -289,3 +138,9 b' def templatepath(name=None):' | |||
|
289 | 138 | if (name and os.path.exists(p)) or os.path.isdir(p): |
|
290 | 139 | return os.path.normpath(p) |
|
291 | 140 | |
|
141 | def stringify(thing): | |
|
142 | '''turn nested template iterator into string.''' | |
|
143 | if hasattr(thing, '__iter__'): | |
|
144 | return "".join([stringify(t) for t in thing if t is not None]) | |
|
145 | return str(thing) | |
|
146 |
General Comments 0
You need to be logged in to leave comments.
Login now