comparison mercurial/hgweb.py @ 1896:f8f818a04f5b

move hgweb template code out to templater
author Vadim Gelfer <vadim.gelfer@gmail.com>
date Sun, 26 Feb 2006 12:59:28 -0800
parents a373881fdf2a
children 58b6784cf9f1
comparison
equal deleted inserted replaced
1800:414e81ae971f 1896:f8f818a04f5b
4 # Copyright 2005 Matt Mackall <mpm@selenic.com> 4 # Copyright 2005 Matt Mackall <mpm@selenic.com>
5 # 5 #
6 # This software may be used and distributed according to the terms 6 # This software may be used and distributed according to the terms
7 # of the GNU General Public License, incorporated herein by reference. 7 # of the GNU General Public License, incorporated herein by reference.
8 8
9 import os, cgi, sys, urllib 9 import os, cgi, sys
10 import mimetypes 10 import mimetypes
11 from demandload import demandload 11 from demandload import demandload
12 demandload(globals(), "mdiff time re socket zlib errno ui hg ConfigParser") 12 demandload(globals(), "mdiff time re socket zlib errno ui hg ConfigParser")
13 demandload(globals(), "zipfile tempfile StringIO tarfile BaseHTTPServer util") 13 demandload(globals(), "zipfile tempfile StringIO tarfile BaseHTTPServer util")
14 demandload(globals(), "mimetypes") 14 demandload(globals(), "mimetypes templater")
15 from node import * 15 from node import *
16 from i18n import gettext as _ 16 from i18n import gettext as _
17 17
18 def templatepath(): 18 def templatepath():
19 for f in "templates", "../templates": 19 for f in "templates", "../templates":
20 p = os.path.join(os.path.dirname(__file__), f) 20 p = os.path.join(os.path.dirname(__file__), f)
21 if os.path.isdir(p): 21 if os.path.isdir(p):
22 return os.path.normpath(p) 22 return os.path.normpath(p)
23
24 def age(x):
25 def plural(t, c):
26 if c == 1:
27 return t
28 return t + "s"
29 def fmt(t, c):
30 return "%d %s" % (c, plural(t, c))
31
32 now = time.time()
33 then = x[0]
34 delta = max(1, int(now - then))
35
36 scales = [["second", 1],
37 ["minute", 60],
38 ["hour", 3600],
39 ["day", 3600 * 24],
40 ["week", 3600 * 24 * 7],
41 ["month", 3600 * 24 * 30],
42 ["year", 3600 * 24 * 365]]
43
44 scales.reverse()
45
46 for t, s in scales:
47 n = delta / s
48 if n >= 2 or s == 1:
49 return fmt(t, n)
50
51 def nl2br(text):
52 return text.replace('\n', '<br/>\n')
53
54 def obfuscate(text):
55 return ''.join(['&#%d;' % ord(c) for c in text])
56 23
57 def up(p): 24 def up(p):
58 if p[0] != "/": 25 if p[0] != "/":
59 p = "/" + p 26 p = "/" + p
60 if p[-1] == "/": 27 if p[-1] == "/":
129 if file: 96 if file:
130 headers.append(('Content-disposition', 'attachment; filename=%s' % file)) 97 headers.append(('Content-disposition', 'attachment; filename=%s' % file))
131 if size > 0: 98 if size > 0:
132 headers.append(('Content-length', str(size))) 99 headers.append(('Content-length', str(size)))
133 self.header(headers) 100 self.header(headers)
134
135 class templater(object):
136 def __init__(self, mapfile, filters={}, defaults={}):
137 self.cache = {}
138 self.map = {}
139 self.base = os.path.dirname(mapfile)
140 self.filters = filters
141 self.defaults = defaults
142
143 for l in file(mapfile):
144 m = re.match(r'(\S+)\s*=\s*"(.*)"$', l)
145 if m:
146 self.cache[m.group(1)] = m.group(2)
147 else:
148 m = re.match(r'(\S+)\s*=\s*(\S+)', l)
149 if m:
150 self.map[m.group(1)] = os.path.join(self.base, m.group(2))
151 else:
152 raise LookupError(_("unknown map entry '%s'") % l)
153
154 def __call__(self, t, **map):
155 m = self.defaults.copy()
156 m.update(map)
157 try:
158 tmpl = self.cache[t]
159 except KeyError:
160 tmpl = self.cache[t] = file(self.map[t]).read()
161 return self.template(tmpl, self.filters, **m)
162
163 def template(self, tmpl, filters={}, **map):
164 while tmpl:
165 m = re.search(r"#([a-zA-Z0-9]+)((%[a-zA-Z0-9]+)*)((\|[a-zA-Z0-9]+)*)#", tmpl)
166 if m:
167 yield tmpl[:m.start(0)]
168 v = map.get(m.group(1), "")
169 v = callable(v) and v(**map) or v
170
171 format = m.group(2)
172 fl = m.group(4)
173
174 if format:
175 q = v.__iter__
176 for i in q():
177 lm = map.copy()
178 lm.update(i)
179 yield self(format[1:], **lm)
180
181 v = ""
182
183 elif fl:
184 for f in fl.split("|")[1:]:
185 v = filters[f](v)
186
187 yield v
188 tmpl = tmpl[m.end(0):]
189 else:
190 yield tmpl
191 return
192
193 common_filters = {
194 "escape": lambda x: cgi.escape(x, True),
195 "urlescape": urllib.quote,
196 "strip": lambda x: x.strip(),
197 "age": age,
198 "date": lambda x: util.datestr(x),
199 "addbreaks": nl2br,
200 "obfuscate": obfuscate,
201 "short": (lambda x: x[:12]),
202 "firstline": (lambda x: x.splitlines(1)[0]),
203 "permissions": (lambda x: x and "-rwxr-xr-x" or "-rw-r--r--"),
204 "rfc822date": lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S"),
205 }
206 101
207 class hgweb(object): 102 class hgweb(object):
208 def __init__(self, repo, name=None): 103 def __init__(self, repo, name=None):
209 if type(repo) == type(""): 104 if type(repo) == type(""):
210 self.repo = hg.repository(ui.ui(), repo) 105 self.repo = hg.repository(ui.ui(), repo)
908 url = "http://%s%s%s" % (req.env["SERVER_NAME"], port, uri) 803 url = "http://%s%s%s" % (req.env["SERVER_NAME"], port, uri)
909 if not self.reponame: 804 if not self.reponame:
910 self.reponame = (self.repo.ui.config("web", "name") 805 self.reponame = (self.repo.ui.config("web", "name")
911 or uri.strip('/') or self.repo.root) 806 or uri.strip('/') or self.repo.root)
912 807
913 self.t = templater(m, common_filters, 808 self.t = templater.templater(m, templater.common_filters,
914 {"url": url, 809 {"url": url,
915 "repo": self.reponame, 810 "repo": self.reponame,
916 "header": header, 811 "header": header,
917 "footer": footer, 812 "footer": footer,
918 }) 813 })
919 814
920 if not req.form.has_key('cmd'): 815 if not req.form.has_key('cmd'):
921 req.form['cmd'] = [self.t.cache['default'],] 816 req.form['cmd'] = [self.t.cache['default'],]
922 817
923 if req.form['cmd'][0] == 'changelog': 818 if req.form['cmd'][0] == 'changelog':