23 return ''.join(l) |
23 return ''.join(l) |
24 |
24 |
25 def httphdr(type): |
25 def httphdr(type): |
26 print 'Content-type: %s\n' % type |
26 print 'Content-type: %s\n' % type |
27 |
27 |
|
28 class template: |
|
29 def __init__(self, tmpl_dir): |
|
30 self.tmpl_dir = tmpl_dir |
|
31 def do_page(self, tmpl_fn, **map): |
|
32 out = [] |
|
33 txt = file(os.path.join(self.tmpl_dir, tmpl_fn)).read() |
|
34 while txt: |
|
35 m = re.search(r"#([a-zA-Z0-9]+)#", txt) |
|
36 if m: |
|
37 out.append(txt[:m.start(0)]) |
|
38 v = map.get(m.group(1), "") |
|
39 if callable(v): |
|
40 for y in v(**map): out.append(y) |
|
41 else: |
|
42 out.append(str(v)) |
|
43 txt = txt[m.end(0):] |
|
44 else: |
|
45 out.append(txt) |
|
46 txt = '' |
|
47 return ''.join(out) |
|
48 |
28 class page: |
49 class page: |
29 def __init__(self, type="text/html", title="Mercurial Web", |
50 def __init__(self, tmpl_dir = "", type="text/html", title="Mercurial Web", |
30 charset="ISO-8859-1"): |
51 charset="ISO-8859-1"): |
|
52 self.tmpl = template(tmpl_dir) |
|
53 |
31 print 'Content-type: %s; charset=%s\n' % (type, charset) |
54 print 'Content-type: %s; charset=%s\n' % (type, charset) |
32 print '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">' |
55 print self.tmpl.do_page('htmlstart.tmpl', title = title) |
33 print '<HTML>' |
|
34 print '<!-- created by hgweb 0.1 - jake@edge2.net -->' |
|
35 print '<HEAD><TITLE>%s</TITLE>' % title |
|
36 print '<style type="text/css">' |
|
37 print 'body { font-family: sans-serif; font-size: 12px; }' |
|
38 print 'table { font-size: 12px; }' |
|
39 print '.errmsg { font-size: 200%; color: red; }' |
|
40 print '.filename { font-size: 150%; color: purple; }' |
|
41 print '.manifest { font-size: 150%; color: purple; }' |
|
42 print '.filehist { font-size: 150%; color: purple; }' |
|
43 print '.plusline { color: green; }' |
|
44 print '.minusline { color: red; }' |
|
45 print '.atline { color: purple; }' |
|
46 print '</style>' |
|
47 print '</HEAD>' |
|
48 print '<BODY>' |
|
49 |
56 |
50 def endpage(self): |
57 def endpage(self): |
51 print '</BODY>' |
58 print '</BODY>' |
52 print '</HTML>' |
59 print '</HTML>' |
53 |
60 |
71 class errpage(page): |
78 class errpage(page): |
72 def __init__(self): |
79 def __init__(self): |
73 page.__init__(self, title="Mercurial Web Error Page") |
80 page.__init__(self, title="Mercurial Web Error Page") |
74 |
81 |
75 class change_list(page): |
82 class change_list(page): |
76 |
83 def __init__(self, repo, tmpl_dir, reponame, numchanges = 50): |
77 numchanges = 50 # number of changes to show |
84 page.__init__(self, tmpl_dir) |
78 |
85 self.repo = repo |
79 def __init__(self, repo, reponame): |
86 self.numchanges = numchanges |
80 page.__init__(self) |
87 print self.tmpl.do_page('changestitle.tmpl', reponame=reponame) |
81 self.repo = repo |
|
82 print '<h3>Changes For: %s</h3>' % reponame |
|
83 |
88 |
84 def content(self, hi=None): |
89 def content(self, hi=None): |
85 cl = [] |
90 cl = [] |
86 count = self.repo.changelog.count() |
91 count = self.repo.changelog.count() |
87 if not hi: |
92 if not hi: |
124 def change_table(self, nodeid, changes): |
129 def change_table(self, nodeid, changes): |
125 hn = hg.hex(nodeid) |
130 hn = hg.hex(nodeid) |
126 i = self.repo.changelog.rev(nodeid) |
131 i = self.repo.changelog.rev(nodeid) |
127 (h1, h2) = [ hg.hex(x) for x in self.repo.changelog.parents(nodeid) ] |
132 (h1, h2) = [ hg.hex(x) for x in self.repo.changelog.parents(nodeid) ] |
128 datestr = time.asctime(time.gmtime(float(changes[2].split(' ')[0]))) |
133 datestr = time.asctime(time.gmtime(float(changes[2].split(' ')[0]))) |
129 print '<table summary="" width="100%" border="1">' |
134 files = [] |
130 print '\t<tr><td valign="top" width="10%">author:</td>' + \ |
|
131 '<td valign="top" width="20%%">%s</td>' % \ |
|
132 (obfuscate(changes[1]), ) |
|
133 print '\t\t<td valign="top" width="10%">description:</td>' + \ |
|
134 '<td width="60%">' + \ |
|
135 '<a href="?cmd=chkin;nd=%s">%s</a></td></tr>' % \ |
|
136 (hn, nl2br(cgi.escape(changes[4])), ) |
|
137 print '\t<tr><td>date:</td><td>%s UTC</td>' % (datestr, ) |
|
138 print '\t\t<td valign="top">files:</td><td valign="top">' |
|
139 for f in changes[3]: |
135 for f in changes[3]: |
140 print '\t\t<a href="?cmd=file;cs=%s;fn=%s">%s</a> ' % \ |
136 files.append('<a href="?cmd=file;cs=%s;fn=%s">%s</a> ' \ |
141 (hn, f, cgi.escape(f), ) |
137 % (hn, f, cgi.escape(f))) |
142 print '\t</td></tr>' |
138 print self.tmpl.do_page('change_table.tmpl', |
143 print '\t<tr><td>revision:</td><td colspan="3">%d:<a ' % (i, ) + \ |
139 author=obfuscate(changes[1]), |
144 'href="?cmd=chkin;nd=%s">%s</a></td></tr>' % (hn, hn, ) |
140 desc=nl2br(cgi.escape(changes[4])), date=datestr, |
145 print '</table><br />' |
141 files=''.join(files), revnum=i, revnode=hn) |
146 |
142 |
147 class checkin(page): |
143 class checkin(page): |
148 def __init__(self, repo, nodestr): |
144 def __init__(self, repo, tmpl_dir, nodestr): |
149 page.__init__(self) |
145 page.__init__(self, tmpl_dir) |
150 self.repo = repo |
146 self.repo = repo |
151 self.node = hg.bin(nodestr) |
147 self.node = hg.bin(nodestr) |
152 self.nodestr = nodestr |
148 self.nodestr = nodestr |
153 print '<h3>Checkin: %s</h3>' % nodestr |
149 print '<h3>Checkin: %s</h3>' % nodestr |
154 |
150 |
158 parents = self.repo.changelog.parents(self.node) |
154 parents = self.repo.changelog.parents(self.node) |
159 (h1, h2) = [ hg.hex(x) for x in parents ] |
155 (h1, h2) = [ hg.hex(x) for x in parents ] |
160 (i1, i2) = [ self.repo.changelog.rev(x) for x in parents ] |
156 (i1, i2) = [ self.repo.changelog.rev(x) for x in parents ] |
161 datestr = time.asctime(time.gmtime(float(changes[2].split(' ')[0]))) |
157 datestr = time.asctime(time.gmtime(float(changes[2].split(' ')[0]))) |
162 mf = self.repo.manifest.read(changes[0]) |
158 mf = self.repo.manifest.read(changes[0]) |
163 print '<table summary="" width="100%" border="1">' |
159 files = [] |
164 print '\t<tr><td>revision:</td><td colspan="3">%d:' % (i, ), |
160 for f in changes[3]: |
165 print '<a href="?cmd=chkin;nd=%s">%s</a></td></tr>' % \ |
161 files.append('<a href="?cmd=file;nd=%s;fn=%s">%s</a> ' \ |
166 (self.nodestr, self.nodestr, ) |
162 % (hg.hex(mf[f]), f, cgi.escape(f))) |
167 print '\t<tr><td>parent(s):</td><td colspan="3">%d:' % (i1, ) |
163 p2link = h2 |
168 print '<a href="?cmd=chkin;nd=%s">%s</a>' % (h1, h1, ), |
|
169 if i2 != -1: |
164 if i2 != -1: |
170 print ' %d:<a href="?cmd=chkin;nd=%s">%s</a>' % \ |
165 p2link = '<a href="?cmd=chkin;nd=%s">%s</a>' % (h2, h2) |
171 (i2, h2, h2, ), |
166 |
172 else: |
167 print self.tmpl.do_page('checkin.tmpl', revnum=i, revnode=self.nodestr, |
173 print ' %d:%s' % (i2, h2, ), |
168 p1num=i1, p1node=h1, p2num=i2, p2node=h2, p2link=p2link, |
174 print '</td></tr>' |
169 mfnum=self.repo.manifest.rev(changes[0]), |
175 print '\t<tr><td>manifest:</td><td colspan="3">%d:' % \ |
170 mfnode=hg.hex(changes[0]), author=obfuscate(changes[1]), |
176 (self.repo.manifest.rev(changes[0]), ), |
171 desc=nl2br(cgi.escape(changes[4])), date=datestr) |
177 print '<a href="?cmd=mf;nd=%s">%s</a></td></tr>' % \ |
|
178 (hg.hex(changes[0]), hg.hex(changes[0]), ) |
|
179 print '\t<tr><td valign="top" width="10%">author:</td>' + \ |
|
180 '<td valign="top" width="20%%">%s</td>' % \ |
|
181 (obfuscate(changes[1]), ) |
|
182 print '\t\t<td valign="top" width="10%">description:</td>' + \ |
|
183 '<td width="60%">' + \ |
|
184 '<a href="?cmd=chkin;nd=%s">%s</a></td></tr>' % \ |
|
185 (self.nodestr, nl2br(cgi.escape(changes[4])), ) |
|
186 print '\t<tr><td>date:</td><td>%s UTC</td>' % (datestr, ) |
|
187 print '\t\t<td valign="top">files:</td><td valign="top">' |
|
188 for f in changes[3]: |
|
189 print '\t\t<a href="?cmd=file;nd=%s&fn=%s">%s</a>' % \ |
|
190 (hg.hex(mf[f]), f, cgi.escape(f), ), |
|
191 print ' ' |
|
192 print '\t</td></tr>' |
|
193 print '</table><br />' |
|
194 |
172 |
195 (c, a, d) = self.repo.diffrevs(parents[0], self.node) |
173 (c, a, d) = self.repo.diffrevs(parents[0], self.node) |
196 change = self.repo.changelog.read(parents[0]) |
174 change = self.repo.changelog.read(parents[0]) |
197 mf2 = self.repo.manifest.read(change[0]) |
175 mf2 = self.repo.manifest.read(change[0]) |
198 for f in c: |
176 for f in c: |
202 self.show_diff('', self.repo.file(f).read(mf[f]), f) |
180 self.show_diff('', self.repo.file(f).read(mf[f]), f) |
203 for f in d: |
181 for f in d: |
204 self.show_diff(self.repo.file(f).read(mf2[f]), '', f) |
182 self.show_diff(self.repo.file(f).read(mf2[f]), '', f) |
205 |
183 |
206 class filepage(page): |
184 class filepage(page): |
207 def __init__(self, repo, fn, node=None, cs=None): |
185 def __init__(self, repo, tmpl_dir, fn, node=None, cs=None): |
208 page.__init__(self) |
186 page.__init__(self, tmpl_dir) |
209 self.repo = repo |
187 self.repo = repo |
210 self.fn = fn |
188 self.fn = fn |
211 if cs: |
189 if cs: |
212 chng = self.repo.changelog.read(hg.bin(cs)) |
190 chng = self.repo.changelog.read(hg.bin(cs)) |
213 mf = self.repo.manifest.read(chng[0]) |
191 mf = self.repo.manifest.read(chng[0]) |
224 print '<pre>' |
202 print '<pre>' |
225 print cgi.escape(self.repo.file(self.fn).read(self.node)) |
203 print cgi.escape(self.repo.file(self.fn).read(self.node)) |
226 print '</pre>' |
204 print '</pre>' |
227 |
205 |
228 class mfpage(page): |
206 class mfpage(page): |
229 def __init__(self, repo, node): |
207 def __init__(self, repo, tmpl_dir, node): |
230 page.__init__(self) |
208 page.__init__(self, tmpl_dir) |
231 self.repo = repo |
209 self.repo = repo |
232 self.nodestr = node |
210 self.nodestr = node |
233 self.node = hg.bin(node) |
211 self.node = hg.bin(node) |
234 |
212 |
235 def content(self): |
213 def content(self): |
236 mf = self.repo.manifest.read(self.node) |
214 mf = self.repo.manifest.read(self.node) |
237 fns = mf.keys() |
215 fns = mf.keys() |
238 fns.sort() |
216 fns.sort() |
239 print '<div class="manifest">Manifest (%s)</div>' % self.nodestr |
217 print self.tmpl.do_page('mftitle.tmpl', node = self.nodestr) |
240 for f in fns: |
218 for f in fns: |
241 print '<a href="?cmd=file;fn=%s;nd=%s">%s</a><br />' % \ |
219 print self.tmpl.do_page('mfentry.tmpl', fn=f, node=hg.hex(mf[f])) |
242 (f, hg.hex(mf[f]), f) |
|
243 |
220 |
244 class histpage(page): |
221 class histpage(page): |
245 def __init__(self, repo, fn): |
222 def __init__(self, repo, tmpl_dir, fn): |
246 page.__init__(self) |
223 page.__init__(self, tmpl_dir) |
247 self.repo = repo |
224 self.repo = repo |
248 self.fn = fn |
225 self.fn = fn |
249 |
226 |
250 def content(self): |
227 def content(self): |
251 print '<div class="filehist">File History: %s</div>' % self.fn |
228 print '<div class="filehist">File History: %s</div>' % self.fn |
252 r = self.repo.file(self.fn) |
229 r = self.repo.file(self.fn) |
253 print '<br />' |
230 print '<br />' |
254 print '<table summary="" width="100%" align="center">' |
231 print '<table summary="" width="100%" align="center">' |
255 for i in xrange(r.count()-1, -1, -1): |
232 for i in xrange(r.count()-1, -1, -1): |
256 n = r.node(i) |
|
257 (p1, p2) = r.parents(n) |
|
258 (h, h1, h2) = map(hg.hex, (n, p1, p2)) |
|
259 (i1, i2) = map(r.rev, (p1, p2)) |
|
260 ci = r.linkrev(n) |
|
261 cn = self.repo.changelog.node(ci) |
|
262 cs = hg.hex(cn) |
|
263 changes = self.repo.changelog.read(cn) |
|
264 print '<tr><td>' |
233 print '<tr><td>' |
265 self.hist_ent(i, h, i1, h1, i2, h2, ci, cs, changes) |
234 self.hist_ent(i, r) |
266 print '</tr></td>' |
235 print '</tr></td>' |
267 print '</table>' |
236 print '</table>' |
268 |
237 |
269 def hist_ent(self, revi, revs, p1i, p1s, p2i, p2s, ci, cs, changes): |
238 def hist_ent(self, i, r): |
|
239 n = r.node(i) |
|
240 (p1, p2) = r.parents(n) |
|
241 (h, h1, h2) = map(hg.hex, (n, p1, p2)) |
|
242 (i1, i2) = map(r.rev, (p1, p2)) |
|
243 ci = r.linkrev(n) |
|
244 cn = self.repo.changelog.node(ci) |
|
245 cs = hg.hex(cn) |
|
246 changes = self.repo.changelog.read(cn) |
270 datestr = time.asctime(time.gmtime(float(changes[2].split(' ')[0]))) |
247 datestr = time.asctime(time.gmtime(float(changes[2].split(' ')[0]))) |
271 print '<table summary="" width="100%" border="1">' |
248 p2entry = '' |
272 print '\t<tr><td valign="top" width="10%">author:</td>' + \ |
249 if i2 != -1: |
273 '<td valign="top" width="20%%">%s</td>' % \ |
250 p2entry = ' %d:<a href="?cmd=file;nd=%s;fn=%s">%s</a>' \ |
274 (obfuscate(changes[1]), ) |
251 % (i2, h2, self.fn, h2 ), |
275 print '\t\t<td valign="top" width="10%">description:</td>' + \ |
252 print self.tmpl.do_page('hist_ent.tmpl', author=obfuscate(changes[1]), |
276 '<td width="60%">' + \ |
253 csnode=cs, desc=nl2br(cgi.escape(changes[4])), |
277 '<a href="?cmd=chkin;nd=%s">%s</a></td></tr>' % \ |
254 date = datestr, fn=self.fn, revnode=h, p1num = i1, |
278 (cs, nl2br(cgi.escape(changes[4])), ) |
255 p1node=h1, p2entry=p2entry) |
279 print '\t<tr><td>date:</td><td>%s UTC</td>' % (datestr, ) |
256 |
280 print '\t\t<td>revision:</td><td>%d:<a ' % (revi, ) + \ |
|
281 'href="?cmd=file;cs=%s;fn=%s">%s</a></td></tr>' % \ |
|
282 (cs, self.fn, revs ) |
|
283 print '\t<tr><td>parent(s):</td><td colspan="3">%d:' % (p1i, ) |
|
284 print '<a href="?cmd=file;nd=%s;fn=%s">%s</a>' % (p1s, self.fn, p1s, ), |
|
285 if p2i != -1: |
|
286 print ' %d:<a href="?cmd=file;nd=%s;fn=%s">%s</a>' % \ |
|
287 (p2i, p2s, self.fn, p2s ), |
|
288 print '</td></tr>' |
|
289 print '</table><br />' |
|
290 |
|
291 class hgweb: |
257 class hgweb: |
292 repo_path = "." |
258 repo_path = "." |
293 numchanges = 50 |
259 numchanges = 50 |
|
260 tmpl_dir = "templates" |
294 |
261 |
295 def __init__(self): |
262 def __init__(self): |
296 pass |
263 pass |
297 |
264 |
298 def run(self): |
265 def run(self): |
301 |
268 |
302 ui = hg.ui() |
269 ui = hg.ui() |
303 repo = hg.repository(ui, self.repo_path) |
270 repo = hg.repository(ui, self.repo_path) |
304 |
271 |
305 if not args.has_key('cmd') or args['cmd'][0] == 'changes': |
272 if not args.has_key('cmd') or args['cmd'][0] == 'changes': |
306 page = change_list(repo, 'Mercurial') |
273 page = change_list(repo, self.tmpl_dir, 'Mercurial', |
|
274 self.numchanges) |
307 hi = args.get('hi', ( repo.changelog.count(), )) |
275 hi = args.get('hi', ( repo.changelog.count(), )) |
308 page.content(hi = int(hi[0])) |
276 page.content(hi = int(hi[0])) |
309 page.endpage() |
277 page.endpage() |
310 |
278 |
311 elif args['cmd'][0] == 'chkin': |
279 elif args['cmd'][0] == 'chkin': |
312 if not args.has_key('nd'): |
280 if not args.has_key('nd'): |
313 page = errpage() |
281 page = errpage() |
314 print '<div class="errmsg">No Node!</div>' |
282 print '<div class="errmsg">No Node!</div>' |
315 else: |
283 else: |
316 page = checkin(repo, args['nd'][0]) |
284 page = checkin(repo, self.tmpl_dir, args['nd'][0]) |
317 page.content() |
285 page.content() |
318 page.endpage() |
286 page.endpage() |
319 |
287 |
320 elif args['cmd'][0] == 'file': |
288 elif args['cmd'][0] == 'file': |
321 if not (args.has_key('nd') and args.has_key('fn')) and \ |
289 if not (args.has_key('nd') and args.has_key('fn')) and \ |
322 not (args.has_key('cs') and args.has_key('fn')): |
290 not (args.has_key('cs') and args.has_key('fn')): |
323 page = errpage() |
291 page = errpage() |
324 print '<div class="errmsg">Invalid Args!</div>' |
292 print '<div class="errmsg">Invalid Args!</div>' |
325 else: |
293 else: |
326 if args.has_key('nd'): |
294 if args.has_key('nd'): |
327 page = filepage(repo, args['fn'][0], node=args['nd'][0]) |
295 page = filepage(repo, self.tmpl_dir, |
|
296 args['fn'][0], node=args['nd'][0]) |
328 else: |
297 else: |
329 page = filepage(repo, args['fn'][0], cs=args['cs'][0]) |
298 page = filepage(repo, self.tmpl_dir, |
|
299 args['fn'][0], cs=args['cs'][0]) |
330 page.content() |
300 page.content() |
331 page.endpage() |
301 page.endpage() |
332 |
302 |
333 elif args['cmd'][0] == 'mf': |
303 elif args['cmd'][0] == 'mf': |
334 if not args.has_key('nd'): |
304 if not args.has_key('nd'): |
335 page = errpage() |
305 page = errpage() |
336 print '<div class="errmsg">No Node!</div>' |
306 print '<div class="errmsg">No Node!</div>' |
337 else: |
307 else: |
338 page = mfpage(repo, args['nd'][0]) |
308 page = mfpage(repo, self.tmpl_dir, args['nd'][0]) |
339 page.content() |
309 page.content() |
340 page.endpage() |
310 page.endpage() |
341 |
311 |
342 elif args['cmd'][0] == 'hist': |
312 elif args['cmd'][0] == 'hist': |
343 if not args.has_key('fn'): |
313 if not args.has_key('fn'): |
344 page = errpage() |
314 page = errpage() |
345 print '<div class="errmsg">No Filename!</div>' |
315 print '<div class="errmsg">No Filename!</div>' |
346 else: |
316 else: |
347 page = histpage(repo, args['fn'][0]) |
317 page = histpage(repo, self.tmpl_dir, args['fn'][0]) |
348 page.content() |
318 page.content() |
349 page.endpage() |
319 page.endpage() |
350 |
320 |
351 elif args['cmd'][0] == 'branches': |
321 elif args['cmd'][0] == 'branches': |
352 httphdr("text/plain") |
322 httphdr("text/plain") |