comparison mercurial/hgweb.py @ 133:fb84d3e71042

added template support for some hgweb output, also, template files for the parts that have been converted
author jake@edge2.net
date Sat, 21 May 2005 16:13:42 -0700
parents 210eeb6f5197
children 13d609f8d830
comparison
equal deleted inserted replaced
132:210eeb6f5197 133:fb84d3e71042
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>&nbsp;&nbsp;' % \ 136 files.append('<a href="?cmd=file;cs=%s;fn=%s">%s</a>&nbsp;&nbsp;' \
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>&nbsp;&nbsp;' \
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 '&nbsp;&nbsp;%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 '&nbsp;&nbsp;%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 '&nbsp;&nbsp;'
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 = '&nbsp;&nbsp;%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 '&nbsp;&nbsp;%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")