Mercurial > public > mercurial-scm > hg-stable
annotate mercurial/hgweb.py @ 201:f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
hgweb: add template filters, template style maps, and raw pages
Template filters:
in templates, you can now specify a chain of filters like
#desc|firstline|escape#
#desc|escape|addbreaks#
#date|age#
to specify how you'd like raw text (or whatever) to be transformed.
Template style maps:
add ;style=foo to a URL and we'll use templates/map-foo if it exists.
Raw output:
Together, these two features make it east to implement raw
downloadable files and patches. Simply link to the same page with
style=raw and present the output as unfiltered text/plain with that
template.
manifest hash: 5954a648b3d6b4e6dc2dcd1975f96b4b0178da2a
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.0 (GNU/Linux)
iD8DBQFCnUMyywK+sNU5EO8RAkKjAJ9h9JElSCbWBPUnL+koCSDxgo38AwCgrccM
0qwyKdh/fUNglICxSh3HBNA=
=Svlo
-----END PGP SIGNATURE-----
author | mpm@selenic.com |
---|---|
date | Tue, 31 May 2005 21:10:10 -0800 |
parents | c88ef31fb5c0 |
children | 9ff5a78d0c45 |
rev | line source |
---|---|
131 | 1 #!/usr/bin/env python |
2 # | |
132 | 3 # hgweb.py - 0.2 - 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net> |
131 | 4 # - web interface to a mercurial repository |
5 # | |
6 # This software may be used and distributed according to the terms | |
7 # of the GNU General Public License, incorporated herein by reference. | |
8 | |
9 # useful for debugging | |
10 import cgitb | |
11 cgitb.enable() | |
12 | |
13 import os, cgi, time, re, difflib, sys, zlib | |
138 | 14 from mercurial.hg import * |
15 | |
157
2653740d8118
Install the templates where they can be found by hgweb.py
mpm@selenic.com
parents:
156
diff
changeset
|
16 def templatepath(): |
201
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
17 for f in "templates", "../templates": |
157
2653740d8118
Install the templates where they can be found by hgweb.py
mpm@selenic.com
parents:
156
diff
changeset
|
18 p = os.path.join(os.path.dirname(__file__), f) |
201
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
19 if os.path.isdir(p): return p |
157
2653740d8118
Install the templates where they can be found by hgweb.py
mpm@selenic.com
parents:
156
diff
changeset
|
20 |
138 | 21 def age(t): |
22 def plural(t, c): | |
23 if c == 1: return t | |
24 return t + "s" | |
25 def fmt(t, c): | |
26 return "%d %s" % (c, plural(t, c)) | |
27 | |
28 now = time.time() | |
29 delta = max(1, int(now - t)) | |
30 | |
31 scales = [["second", 1], | |
32 ["minute", 60], | |
33 ["hour", 3600], | |
34 ["day", 3600 * 24], | |
35 ["week", 3600 * 24 * 7], | |
36 ["month", 3600 * 24 * 30], | |
37 ["year", 3600 * 24 * 365]] | |
38 | |
39 scales.reverse() | |
40 | |
41 for t, s in scales: | |
42 n = delta / s | |
195 | 43 if n >= 2 or s == 1: return fmt(t, n) |
131 | 44 |
45 def nl2br(text): | |
201
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
46 return text.replace('\n', '<br/>\n') |
131 | 47 |
48 def obfuscate(text): | |
138 | 49 return ''.join([ '&#%d' % ord(c) for c in text ]) |
50 | |
51 def up(p): | |
52 if p[0] != "/": p = "/" + p | |
53 if p[-1] == "/": p = p[:-1] | |
54 up = os.path.dirname(p) | |
55 if up == "/": | |
56 return "/" | |
57 return up + "/" | |
131 | 58 |
59 def httphdr(type): | |
60 print 'Content-type: %s\n' % type | |
61 | |
135 | 62 def write(*things): |
63 for thing in things: | |
64 if hasattr(thing, "__iter__"): | |
65 for part in thing: | |
66 write(part) | |
67 else: | |
68 sys.stdout.write(str(thing)) | |
69 | |
201
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
70 def template(tmpl, filters = {}, **map): |
138 | 71 while tmpl: |
201
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
72 m = re.search(r"#([a-zA-Z0-9]+)((\|[a-zA-Z0-9]+)*)#", tmpl) |
138 | 73 if m: |
74 yield tmpl[:m.start(0)] | |
75 v = map.get(m.group(1), "") | |
201
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
76 v = callable(v) and v() or v |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
77 |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
78 fl = m.group(2) |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
79 if fl: |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
80 for f in fl.split("|")[1:]: |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
81 v = filters[f](v) |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
82 |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
83 yield v |
138 | 84 tmpl = tmpl[m.end(0):] |
85 else: | |
86 yield tmpl | |
87 return | |
88 | |
89 class templater: | |
201
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
90 def __init__(self, mapfile, filters = {}): |
138 | 91 self.cache = {} |
92 self.map = {} | |
93 self.base = os.path.dirname(mapfile) | |
201
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
94 self.filters = filters |
138 | 95 |
96 for l in file(mapfile): | |
97 m = re.match(r'(\S+)\s*=\s*"(.*)"$', l) | |
133
fb84d3e71042
added template support for some hgweb output, also, template files for
jake@edge2.net
parents:
132
diff
changeset
|
98 if m: |
138 | 99 self.cache[m.group(1)] = m.group(2) |
100 else: | |
101 m = re.match(r'(\S+)\s*=\s*(\S+)', l) | |
102 if m: | |
103 self.map[m.group(1)] = os.path.join(self.base, m.group(2)) | |
133
fb84d3e71042
added template support for some hgweb output, also, template files for
jake@edge2.net
parents:
132
diff
changeset
|
104 else: |
138 | 105 raise "unknown map entry '%s'" % l |
133
fb84d3e71042
added template support for some hgweb output, also, template files for
jake@edge2.net
parents:
132
diff
changeset
|
106 |
138 | 107 def __call__(self, t, **map): |
108 try: | |
109 tmpl = self.cache[t] | |
110 except KeyError: | |
111 tmpl = self.cache[t] = file(self.map[t]).read() | |
201
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
112 return template(tmpl, self.filters, **map) |
138 | 113 |
114 class hgweb: | |
115 maxchanges = 20 | |
116 maxfiles = 10 | |
133
fb84d3e71042
added template support for some hgweb output, also, template files for
jake@edge2.net
parents:
132
diff
changeset
|
117 |
201
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
118 def __init__(self, path, name, templates = ""): |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
119 self.templates = templates or templatepath() |
138 | 120 self.reponame = name |
121 self.repo = repository(ui(), path) | |
197 | 122 self.viewonly = 0 |
131 | 123 |
201
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
124 self.filters = { |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
125 "escape": cgi.escape, |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
126 "age": age, |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
127 "date": (lambda x: time.asctime(time.gmtime(x))), |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
128 "addbreaks": nl2br, |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
129 "obfuscate": obfuscate, |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
130 "firstline": (lambda x: x.splitlines(1)[0]), |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
131 } |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
132 |
138 | 133 def date(self, cs): |
134 return time.asctime(time.gmtime(float(cs[2].split(' ')[0]))) | |
135 | |
136 def listfiles(self, files, mf): | |
137 for f in files[:self.maxfiles]: | |
138 yield self.t("filenodelink", node = hex(mf[f]), file = f) | |
139 if len(files) > self.maxfiles: | |
140 yield self.t("fileellipses") | |
141 | |
142 def listfilediffs(self, files, changeset): | |
143 for f in files[:self.maxfiles]: | |
144 yield self.t("filedifflink", node = hex(changeset), file = f) | |
145 if len(files) > self.maxfiles: | |
146 yield self.t("fileellipses") | |
147 | |
156 | 148 def parent(self, t1, node=nullid, rev=-1, **args): |
142 | 149 if node != hex(nullid): |
156 | 150 yield self.t(t1, node = node, rev = rev, **args) |
142 | 151 |
138 | 152 def diff(self, node1, node2, files): |
153 def filterfiles(list, files): | |
154 l = [ x for x in list if x in files ] | |
155 | |
156 for f in files: | |
157 if f[-1] != os.sep: f += os.sep | |
158 l += [ x for x in list if x.startswith(f) ] | |
159 return l | |
131 | 160 |
172
e9b1147db448
hgweb: alternating colors for multifile diffs
mpm@selenic.com
parents:
168
diff
changeset
|
161 parity = [0] |
e9b1147db448
hgweb: alternating colors for multifile diffs
mpm@selenic.com
parents:
168
diff
changeset
|
162 def diffblock(diff, f, fn): |
e9b1147db448
hgweb: alternating colors for multifile diffs
mpm@selenic.com
parents:
168
diff
changeset
|
163 yield self.t("diffblock", |
e9b1147db448
hgweb: alternating colors for multifile diffs
mpm@selenic.com
parents:
168
diff
changeset
|
164 lines = prettyprintlines(diff), |
e9b1147db448
hgweb: alternating colors for multifile diffs
mpm@selenic.com
parents:
168
diff
changeset
|
165 parity = parity[0], |
e9b1147db448
hgweb: alternating colors for multifile diffs
mpm@selenic.com
parents:
168
diff
changeset
|
166 file = f, |
e9b1147db448
hgweb: alternating colors for multifile diffs
mpm@selenic.com
parents:
168
diff
changeset
|
167 filenode = hex(fn)) |
e9b1147db448
hgweb: alternating colors for multifile diffs
mpm@selenic.com
parents:
168
diff
changeset
|
168 parity[0] = 1 - parity[0] |
e9b1147db448
hgweb: alternating colors for multifile diffs
mpm@selenic.com
parents:
168
diff
changeset
|
169 |
e9b1147db448
hgweb: alternating colors for multifile diffs
mpm@selenic.com
parents:
168
diff
changeset
|
170 def prettyprintlines(diff): |
138 | 171 for l in diff.splitlines(1): |
201
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
172 if l.startswith('+'): |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
173 yield self.t("difflineplus", line = l) |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
174 elif l.startswith('-'): |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
175 yield self.t("difflineminus", line = l) |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
176 elif l.startswith('@'): |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
177 yield self.t("difflineat", line = l) |
138 | 178 else: |
201
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
179 yield self.t("diffline", line = l) |
131 | 180 |
138 | 181 r = self.repo |
182 cl = r.changelog | |
183 mf = r.manifest | |
184 change1 = cl.read(node1) | |
185 change2 = cl.read(node2) | |
186 mmap1 = mf.read(change1[0]) | |
187 mmap2 = mf.read(change2[0]) | |
188 date1 = self.date(change1) | |
189 date2 = self.date(change2) | |
131 | 190 |
138 | 191 c, a, d = r.diffrevs(node1, node2) |
192 c, a, d = map(lambda x: filterfiles(x, files), (c, a, d)) | |
131 | 193 |
138 | 194 for f in c: |
195 to = r.file(f).read(mmap1[f]) | |
196 tn = r.file(f).read(mmap2[f]) | |
172
e9b1147db448
hgweb: alternating colors for multifile diffs
mpm@selenic.com
parents:
168
diff
changeset
|
197 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn) |
138 | 198 for f in a: |
199 to = "" | |
200 tn = r.file(f).read(mmap2[f]) | |
172
e9b1147db448
hgweb: alternating colors for multifile diffs
mpm@selenic.com
parents:
168
diff
changeset
|
201 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn) |
138 | 202 for f in d: |
203 to = r.file(f).read(mmap1[f]) | |
204 tn = "" | |
172
e9b1147db448
hgweb: alternating colors for multifile diffs
mpm@selenic.com
parents:
168
diff
changeset
|
205 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn) |
131 | 206 |
142 | 207 def header(self): |
208 yield self.t("header", repo = self.reponame) | |
209 | |
210 def footer(self): | |
211 yield self.t("footer", repo = self.reponame) | |
212 | |
180 | 213 def changelog(self, pos): |
138 | 214 def changenav(): |
215 def seq(factor = 1): | |
216 yield 1 * factor | |
173
8da1df932c16
hgweb: make navigation of changesets a bit nicer
mpm@selenic.com
parents:
172
diff
changeset
|
217 yield 3 * factor |
8da1df932c16
hgweb: make navigation of changesets a bit nicer
mpm@selenic.com
parents:
172
diff
changeset
|
218 #yield 5 * factor |
138 | 219 for f in seq(factor * 10): |
220 yield f | |
131 | 221 |
173
8da1df932c16
hgweb: make navigation of changesets a bit nicer
mpm@selenic.com
parents:
172
diff
changeset
|
222 l = [] |
8da1df932c16
hgweb: make navigation of changesets a bit nicer
mpm@selenic.com
parents:
172
diff
changeset
|
223 for f in seq(): |
8da1df932c16
hgweb: make navigation of changesets a bit nicer
mpm@selenic.com
parents:
172
diff
changeset
|
224 if f < self.maxchanges / 2: continue |
8da1df932c16
hgweb: make navigation of changesets a bit nicer
mpm@selenic.com
parents:
172
diff
changeset
|
225 if f > count: break |
8da1df932c16
hgweb: make navigation of changesets a bit nicer
mpm@selenic.com
parents:
172
diff
changeset
|
226 r = "%d" % f |
8da1df932c16
hgweb: make navigation of changesets a bit nicer
mpm@selenic.com
parents:
172
diff
changeset
|
227 if pos + f < count - (f/2): l.append(("+" + r, pos + f)) |
8da1df932c16
hgweb: make navigation of changesets a bit nicer
mpm@selenic.com
parents:
172
diff
changeset
|
228 if pos - f >= 0 + (f/2): l.insert(0, ("-" + r, pos - f)) |
8da1df932c16
hgweb: make navigation of changesets a bit nicer
mpm@selenic.com
parents:
172
diff
changeset
|
229 |
8da1df932c16
hgweb: make navigation of changesets a bit nicer
mpm@selenic.com
parents:
172
diff
changeset
|
230 yield self.t("naventry", rev = 0, label="(0)") |
138 | 231 |
173
8da1df932c16
hgweb: make navigation of changesets a bit nicer
mpm@selenic.com
parents:
172
diff
changeset
|
232 for label, rev in l: |
8da1df932c16
hgweb: make navigation of changesets a bit nicer
mpm@selenic.com
parents:
172
diff
changeset
|
233 yield self.t("naventry", label = label, rev = rev) |
8da1df932c16
hgweb: make navigation of changesets a bit nicer
mpm@selenic.com
parents:
172
diff
changeset
|
234 |
198
c88ef31fb5c0
hgweb: fix tip so that it doesn't need refreshing
mpm@selenic.com
parents:
197
diff
changeset
|
235 yield self.t("naventry", label="tip") |
131 | 236 |
138 | 237 def changelist(): |
142 | 238 parity = (start - end) & 1 |
138 | 239 cl = self.repo.changelog |
240 l = [] # build a list in forward order for efficiency | |
241 for i in range(start, end + 1): | |
242 n = cl.node(i) | |
243 changes = cl.read(n) | |
244 hn = hex(n) | |
245 p1, p2 = cl.parents(n) | |
246 t = float(changes[2].split(' ')[0]) | |
131 | 247 |
138 | 248 l.insert(0, self.t( |
249 'changelogentry', | |
142 | 250 parity = parity, |
201
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
251 author = changes[1], |
142 | 252 parent1 = self.parent("changelogparent", |
253 hex(p1), cl.rev(p1)), | |
254 parent2 = self.parent("changelogparent", | |
255 hex(p2), cl.rev(p2)), | |
138 | 256 p1 = hex(p1), p2 = hex(p2), |
257 p1rev = cl.rev(p1), p2rev = cl.rev(p2), | |
258 manifest = hex(changes[0]), | |
201
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
259 desc = changes[4], |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
260 date = t, |
138 | 261 files = self.listfilediffs(changes[3], n), |
262 rev = i, | |
263 node = hn)) | |
142 | 264 parity = 1 - parity |
138 | 265 |
266 yield l | |
131 | 267 |
168 | 268 cl = self.repo.changelog |
269 mf = cl.read(cl.tip())[0] | |
270 count = cl.count() | |
138 | 271 end = min(pos, count - 1) |
272 start = max(0, pos - self.maxchanges) | |
273 end = min(count - 1, start + self.maxchanges) | |
274 | |
142 | 275 yield self.t('changelog', |
276 header = self.header(), | |
277 footer = self.footer(), | |
278 repo = self.reponame, | |
279 changenav = changenav, | |
168 | 280 manifest = hex(mf), |
142 | 281 rev = pos, changesets = count, entries = changelist) |
131 | 282 |
138 | 283 def changeset(self, nodeid): |
284 n = bin(nodeid) | |
285 cl = self.repo.changelog | |
286 changes = cl.read(n) | |
287 p1, p2 = cl.parents(n) | |
288 p1rev, p2rev = cl.rev(p1), cl.rev(p2) | |
289 t = float(changes[2].split(' ')[0]) | |
290 | |
133
fb84d3e71042
added template support for some hgweb output, also, template files for
jake@edge2.net
parents:
132
diff
changeset
|
291 files = [] |
138 | 292 mf = self.repo.manifest.read(changes[0]) |
131 | 293 for f in changes[3]: |
138 | 294 files.append(self.t("filenodelink", |
295 filenode = hex(mf[f]), file = f)) | |
296 | |
297 def diff(): | |
298 yield self.diff(p1, n, changes[3]) | |
131 | 299 |
138 | 300 yield self.t('changeset', |
142 | 301 header = self.header(), |
302 footer = self.footer(), | |
303 repo = self.reponame, | |
138 | 304 diff = diff, |
305 rev = cl.rev(n), | |
306 node = nodeid, | |
142 | 307 parent1 = self.parent("changesetparent", |
308 hex(p1), cl.rev(p1)), | |
309 parent2 = self.parent("changesetparent", | |
310 hex(p2), cl.rev(p2)), | |
138 | 311 p1 = hex(p1), p2 = hex(p2), |
312 p1rev = cl.rev(p1), p2rev = cl.rev(p2), | |
313 manifest = hex(changes[0]), | |
201
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
314 author = changes[1], |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
315 desc = changes[4], |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
316 date = t, |
138 | 317 files = files) |
131 | 318 |
138 | 319 def filelog(self, f, filenode): |
320 cl = self.repo.changelog | |
321 fl = self.repo.file(f) | |
322 count = fl.count() | |
323 | |
324 def entries(): | |
325 l = [] | |
142 | 326 parity = (count - 1) & 1 |
327 | |
138 | 328 for i in range(count): |
329 | |
330 n = fl.node(i) | |
331 lr = fl.linkrev(n) | |
332 cn = cl.node(lr) | |
333 cs = cl.read(cl.node(lr)) | |
334 p1, p2 = fl.parents(n) | |
335 t = float(cs[2].split(' ')[0]) | |
133
fb84d3e71042
added template support for some hgweb output, also, template files for
jake@edge2.net
parents:
132
diff
changeset
|
336 |
138 | 337 l.insert(0, self.t("filelogentry", |
142 | 338 parity = parity, |
138 | 339 filenode = hex(n), |
340 filerev = i, | |
341 file = f, | |
342 node = hex(cn), | |
201
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
343 author = cs[1], |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
344 date = t, |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
345 desc = cs[4], |
138 | 346 p1 = hex(p1), p2 = hex(p2), |
347 p1rev = fl.rev(p1), p2rev = fl.rev(p2))) | |
142 | 348 parity = 1 - parity |
138 | 349 |
350 yield l | |
351 | |
352 yield self.t("filelog", | |
142 | 353 header = self.header(), |
354 footer = self.footer(), | |
355 repo = self.reponame, | |
138 | 356 file = f, |
357 filenode = filenode, | |
358 entries = entries) | |
131 | 359 |
138 | 360 def filerevision(self, f, node): |
361 fl = self.repo.file(f) | |
362 n = bin(node) | |
201
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
363 text = fl.read(n) |
138 | 364 changerev = fl.linkrev(n) |
365 cl = self.repo.changelog | |
366 cn = cl.node(changerev) | |
367 cs = cl.read(cn) | |
368 p1, p2 = fl.parents(n) | |
369 t = float(cs[2].split(' ')[0]) | |
370 mfn = cs[0] | |
142 | 371 |
372 def lines(): | |
373 for l, t in enumerate(text.splitlines(1)): | |
201
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
374 yield self.t("fileline", line = t, |
142 | 375 linenumber = "% 6d" % (l + 1), |
376 parity = l & 1) | |
138 | 377 |
378 yield self.t("filerevision", file = f, | |
142 | 379 header = self.header(), |
380 footer = self.footer(), | |
381 repo = self.reponame, | |
138 | 382 filenode = node, |
383 path = up(f), | |
142 | 384 text = lines(), |
138 | 385 rev = changerev, |
386 node = hex(cn), | |
387 manifest = hex(mfn), | |
201
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
388 author = cs[1], |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
389 date = t, |
142 | 390 parent1 = self.parent("filerevparent", |
156 | 391 hex(p1), fl.rev(p1), file=f), |
142 | 392 parent2 = self.parent("filerevparent", |
156 | 393 hex(p2), fl.rev(p2), file=f), |
138 | 394 p1 = hex(p1), p2 = hex(p2), |
395 p1rev = fl.rev(p1), p2rev = fl.rev(p2)) | |
396 | |
397 def fileannotate(self, f, node): | |
398 bcache = {} | |
399 ncache = {} | |
400 fl = self.repo.file(f) | |
401 n = bin(node) | |
402 changerev = fl.linkrev(n) | |
403 | |
404 cl = self.repo.changelog | |
405 cn = cl.node(changerev) | |
406 cs = cl.read(cn) | |
407 p1, p2 = fl.parents(n) | |
408 t = float(cs[2].split(' ')[0]) | |
409 mfn = cs[0] | |
131 | 410 |
138 | 411 def annotate(): |
142 | 412 parity = 1 |
413 last = None | |
138 | 414 for r, l in fl.annotate(n): |
415 try: | |
416 cnode = ncache[r] | |
417 except KeyError: | |
418 cnode = ncache[r] = self.repo.changelog.node(r) | |
419 | |
420 try: | |
421 name = bcache[r] | |
422 except KeyError: | |
423 cl = self.repo.changelog.read(cnode) | |
424 name = cl[1] | |
425 f = name.find('@') | |
426 if f >= 0: | |
427 name = name[:f] | |
428 bcache[r] = name | |
131 | 429 |
142 | 430 if last != cnode: |
431 parity = 1 - parity | |
432 last = cnode | |
433 | |
138 | 434 yield self.t("annotateline", |
142 | 435 parity = parity, |
138 | 436 node = hex(cnode), |
437 rev = r, | |
438 author = name, | |
439 file = f, | |
201
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
440 line = l) |
138 | 441 |
442 yield self.t("fileannotate", | |
142 | 443 header = self.header(), |
444 footer = self.footer(), | |
445 repo = self.reponame, | |
138 | 446 file = f, |
447 filenode = node, | |
448 annotate = annotate, | |
449 path = up(f), | |
450 rev = changerev, | |
451 node = hex(cn), | |
452 manifest = hex(mfn), | |
201
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
453 author = cs[1], |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
454 date = t, |
156 | 455 parent1 = self.parent("fileannotateparent", |
456 hex(p1), fl.rev(p1), file=f), | |
457 parent2 = self.parent("fileannotateparent", | |
458 hex(p2), fl.rev(p2), file=f), | |
138 | 459 p1 = hex(p1), p2 = hex(p2), |
460 p1rev = fl.rev(p1), p2rev = fl.rev(p2)) | |
136 | 461 |
138 | 462 def manifest(self, mnode, path): |
463 mf = self.repo.manifest.read(bin(mnode)) | |
464 rev = self.repo.manifest.rev(bin(mnode)) | |
465 node = self.repo.changelog.node(rev) | |
466 | |
467 files = {} | |
142 | 468 |
138 | 469 p = path[1:] |
470 l = len(p) | |
131 | 471 |
138 | 472 for f,n in mf.items(): |
473 if f[:l] != p: | |
474 continue | |
475 remain = f[l:] | |
476 if "/" in remain: | |
477 short = remain[:remain.find("/") + 1] # bleah | |
142 | 478 files[short] = (f, None) |
138 | 479 else: |
480 short = os.path.basename(remain) | |
481 files[short] = (f, n) | |
131 | 482 |
138 | 483 def filelist(): |
142 | 484 parity = 0 |
138 | 485 fl = files.keys() |
486 fl.sort() | |
487 for f in fl: | |
488 full, fnode = files[f] | |
142 | 489 if fnode: |
490 yield self.t("manifestfileentry", | |
491 file = full, | |
492 manifest = mnode, | |
493 filenode = hex(fnode), | |
494 parity = parity, | |
495 basename = f) | |
496 else: | |
497 yield self.t("manifestdirentry", | |
498 parity = parity, | |
499 path = os.path.join(path, f), | |
500 manifest = mnode, basename = f[:-1]) | |
501 parity = 1 - parity | |
138 | 502 |
503 yield self.t("manifest", | |
142 | 504 header = self.header(), |
505 footer = self.footer(), | |
506 repo = self.reponame, | |
138 | 507 manifest = mnode, |
508 rev = rev, | |
509 node = hex(node), | |
510 path = path, | |
511 up = up(path), | |
142 | 512 entries = filelist) |
131 | 513 |
168 | 514 def tags(self): |
515 cl = self.repo.changelog | |
516 mf = cl.read(cl.tip())[0] | |
517 | |
518 self.repo.lookup(0) # prime the cache | |
519 i = self.repo.tags.items() | |
183 | 520 n = [ (cl.rev(e[1]), e) for e in i ] # sort by revision |
521 n.sort() | |
522 n.reverse() | |
523 i = [ e[1] for e in n ] | |
168 | 524 |
525 def entries(): | |
526 parity = 0 | |
527 for k,n in i: | |
528 yield self.t("tagentry", | |
529 parity = parity, | |
530 tag = k, | |
531 node = hex(n)) | |
532 parity = 1 - parity | |
533 | |
534 yield self.t("tags", | |
535 header = self.header(), | |
536 footer = self.footer(), | |
537 repo = self.reponame, | |
538 manifest = hex(mf), | |
539 entries = entries) | |
540 | |
138 | 541 def filediff(self, file, changeset): |
542 n = bin(changeset) | |
543 cl = self.repo.changelog | |
544 p1 = cl.parents(n)[0] | |
545 cs = cl.read(n) | |
546 mf = self.repo.manifest.read(cs[0]) | |
547 | |
548 def diff(): | |
549 yield self.diff(p1, n, file) | |
131 | 550 |
138 | 551 yield self.t("filediff", |
142 | 552 header = self.header(), |
553 footer = self.footer(), | |
554 repo = self.reponame, | |
138 | 555 file = file, |
556 filenode = hex(mf[file]), | |
557 node = changeset, | |
558 rev = self.repo.changelog.rev(n), | |
559 p1 = hex(p1), | |
560 p1rev = self.repo.changelog.rev(p1), | |
561 diff = diff) | |
562 | |
563 # add tags to things | |
564 # tags -> list of changesets corresponding to tags | |
565 # find tag, changeset, file | |
131 | 566 |
132 | 567 def run(self): |
568 args = cgi.parse() | |
569 | |
201
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
570 m = os.path.join(self.templates, "map") |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
571 if args.has_key('style'): |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
572 b = os.path.basename("map-" + args['style'][0]) |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
573 p = os.path.join(self.templates, b) |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
574 if os.path.isfile(p): m = p |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
575 |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
576 self.t = templater(m, self.filters) |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
577 |
138 | 578 if not args.has_key('cmd') or args['cmd'][0] == 'changelog': |
579 hi = self.repo.changelog.count() | |
153
e8a360cd5a9f
changed pos to rev for changelog cmd, changed & to ;
jake@edge2.net
parents:
142
diff
changeset
|
580 if args.has_key('rev'): |
165 | 581 hi = args['rev'][0] |
166
39624c47060f
hgweb: don't blow up on search for unknown keys
mpm@selenic.com
parents:
165
diff
changeset
|
582 try: |
39624c47060f
hgweb: don't blow up on search for unknown keys
mpm@selenic.com
parents:
165
diff
changeset
|
583 hi = self.repo.changelog.rev(self.repo.lookup(hi)) |
39624c47060f
hgweb: don't blow up on search for unknown keys
mpm@selenic.com
parents:
165
diff
changeset
|
584 except KeyError: |
39624c47060f
hgweb: don't blow up on search for unknown keys
mpm@selenic.com
parents:
165
diff
changeset
|
585 hi = self.repo.changelog.count() |
131 | 586 |
138 | 587 write(self.changelog(hi)) |
132 | 588 |
138 | 589 elif args['cmd'][0] == 'changeset': |
590 write(self.changeset(args['node'][0])) | |
591 | |
592 elif args['cmd'][0] == 'manifest': | |
593 write(self.manifest(args['manifest'][0], args['path'][0])) | |
594 | |
168 | 595 elif args['cmd'][0] == 'tags': |
596 write(self.tags()) | |
597 | |
138 | 598 elif args['cmd'][0] == 'filediff': |
599 write(self.filediff(args['file'][0], args['node'][0])) | |
131 | 600 |
132 | 601 elif args['cmd'][0] == 'file': |
138 | 602 write(self.filerevision(args['file'][0], args['filenode'][0])) |
131 | 603 |
138 | 604 elif args['cmd'][0] == 'annotate': |
605 write(self.fileannotate(args['file'][0], args['filenode'][0])) | |
131 | 606 |
138 | 607 elif args['cmd'][0] == 'filelog': |
608 write(self.filelog(args['file'][0], args['filenode'][0])) | |
136 | 609 |
132 | 610 elif args['cmd'][0] == 'branches': |
611 httphdr("text/plain") | |
612 nodes = [] | |
613 if args.has_key('nodes'): | |
138 | 614 nodes = map(bin, args['nodes'][0].split(" ")) |
615 for b in self.repo.branches(nodes): | |
616 sys.stdout.write(" ".join(map(hex, b)) + "\n") | |
131 | 617 |
132 | 618 elif args['cmd'][0] == 'between': |
619 httphdr("text/plain") | |
620 nodes = [] | |
621 if args.has_key('pairs'): | |
138 | 622 pairs = [ map(bin, p.split("-")) |
132 | 623 for p in args['pairs'][0].split(" ") ] |
138 | 624 for b in self.repo.between(pairs): |
625 sys.stdout.write(" ".join(map(hex, b)) + "\n") | |
132 | 626 |
627 elif args['cmd'][0] == 'changegroup': | |
628 httphdr("application/hg-changegroup") | |
629 nodes = [] | |
197 | 630 if self.viewonly: |
631 return | |
632 | |
132 | 633 if args.has_key('roots'): |
138 | 634 nodes = map(bin, args['roots'][0].split(" ")) |
131 | 635 |
132 | 636 z = zlib.compressobj() |
138 | 637 for chunk in self.repo.changegroup(nodes): |
132 | 638 sys.stdout.write(z.compress(chunk)) |
639 | |
640 sys.stdout.write(z.flush()) | |
131 | 641 |
132 | 642 else: |
138 | 643 write(self.t("error")) |
131 | 644 |
158
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
645 def server(path, name, templates, address, port): |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
646 |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
647 import BaseHTTPServer |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
648 import sys, os |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
649 |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
650 class hgwebhandler(BaseHTTPServer.BaseHTTPRequestHandler): |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
651 def do_POST(self): |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
652 self.do_hgweb() |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
653 |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
654 def do_GET(self): |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
655 self.do_hgweb() |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
656 |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
657 def do_hgweb(self): |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
658 query = "" |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
659 p = self.path.find("?") |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
660 if p: |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
661 query = self.path[p + 1:] |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
662 query = query.replace('+', ' ') |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
663 |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
664 env = {} |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
665 env['GATEWAY_INTERFACE'] = 'CGI/1.1' |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
666 env['REQUEST_METHOD'] = self.command |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
667 if query: |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
668 env['QUERY_STRING'] = query |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
669 host = self.address_string() |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
670 if host != self.client_address[0]: |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
671 env['REMOTE_HOST'] = host |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
672 env['REMOTE_ADDR'] = self.client_address[0] |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
673 |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
674 if self.headers.typeheader is None: |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
675 env['CONTENT_TYPE'] = self.headers.type |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
676 else: |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
677 env['CONTENT_TYPE'] = self.headers.typeheader |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
678 length = self.headers.getheader('content-length') |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
679 if length: |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
680 env['CONTENT_LENGTH'] = length |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
681 accept = [] |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
682 for line in self.headers.getallmatchingheaders('accept'): |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
683 if line[:1] in "\t\n\r ": |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
684 accept.append(line.strip()) |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
685 else: |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
686 accept = accept + line[7:].split(',') |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
687 env['HTTP_ACCEPT'] = ','.join(accept) |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
688 |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
689 os.environ.update(env) |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
690 |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
691 save = sys.argv, sys.stdin, sys.stdout, sys.stderr |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
692 try: |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
693 sys.stdin = self.rfile |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
694 sys.stdout = self.wfile |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
695 sys.argv = ["hgweb.py"] |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
696 if '=' not in query: |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
697 sys.argv.append(query) |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
698 self.send_response(200, "Script output follows") |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
699 hg.run() |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
700 finally: |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
701 sys.argv, sys.stdin, sys.stdout, sys.stderr = save |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
702 |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
703 hg = hgweb(path, name, templates) |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
704 httpd = BaseHTTPServer.HTTPServer((address, port), hgwebhandler) |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
705 httpd.serve_forever() |