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': |