hgweb.py
changeset 102 58039eddbdda
parent 61 1215bf60468f
parent 101 6da5cf0c4193
equal deleted inserted replaced
98:3dde7c87e36d 102:58039eddbdda
    22     l = []
    22     l = []
    23     for c in text:
    23     for c in text:
    24         l.append('&#%d;' % ord(c))
    24         l.append('&#%d;' % ord(c))
    25     return ''.join(l)
    25     return ''.join(l)
    26 
    26 
    27 def httphdr(type = "text/html"):
    27 def httphdr(type):
    28     print 'Content-type: %s\n' % type
    28     print 'Content-type: %s\n' % type
    29 
    29 
    30 def htmldoctype():
    30 class page:
    31     print '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">'
    31     def __init__(self, type="text/html", title="Mercurial Web", 
    32 
    32             charset="ISO-8859-1"):
    33 def htmlhead(title):
    33         print 'Content-type: %s; charset=%s\n' % (type, charset)
    34     print '<HTML>'
    34         print '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">'
    35     print '<!-- created by hgweb 0.1 - jake@edge2.net -->'
    35         print '<HTML>'
    36     print '<HEAD><TITLE>%s</TITLE></HEAD>' % (title, )
    36         print '<!-- created by hgweb 0.1 - jake@edge2.net -->'
    37     print '<style type="text/css">'
    37         print '<HEAD><TITLE>%s</TITLE>' % title
    38     print 'body { font-family: sans-serif; font-size: 12px; }'
    38         print '<style type="text/css">'
    39     print 'table { font-size: 12px; }'
    39         print 'body { font-family: sans-serif; font-size: 12px; }'
    40     print '.errmsg { font-size: 200%; color: red; }'
    40         print 'table { font-size: 12px; }'
    41     print '.filename { font-size: 150%; color: purple; }'
    41         print '.errmsg { font-size: 200%; color: red; }'
    42     print '.plusline { color: green; }'
    42         print '.filename { font-size: 150%; color: purple; }'
    43     print '.minusline { color: red; }'
    43         print '.manifest { font-size: 150%; color: purple; }'
    44     print '.atline { color: purple; }'
    44         print '.filehist { font-size: 150%; color: purple; }'
    45     print '</style>'
    45         print '.plusline { color: green; }'
    46 
    46         print '.minusline { color: red; }'
    47 def startpage(title):
    47         print '.atline { color: purple; }'
    48     httphdr()
    48         print '</style>'
    49     htmldoctype()
    49         print '</HEAD>'
    50     htmlhead(title)
    50         print '<BODY>'
    51     print '<BODY>'
    51 
    52 
    52     def endpage(self):
    53 def endpage():
    53         print '</BODY>'
    54     print '</BODY>'
    54         print '</HTML>'
    55     print '</HTML>'
    55 
    56 
    56     def show_diff(self, a, b, fn):
    57 
    57         a = a.splitlines(1)
    58 
    58         b = b.splitlines(1)
    59 def ent_change(repo, nodeid):
    59         l = difflib.unified_diff(a, b, fn, fn)
    60     changes = repo.changelog.read(nodeid)
    60         print '<pre>'
    61     hn = hg.hex(nodeid)
    61         for line in l:
    62     i = repo.changelog.rev(nodeid)
    62             line = cgi.escape(line[:-1])
    63     (h1, h2) = [ hg.hex(x) for x in repo.changelog.parents(nodeid) ]
    63             if line.startswith('+'):
    64     datestr = time.asctime(time.gmtime(float(changes[2].split(' ')[0])))
    64                 print '<span class="plusline">%s</span>' % (line, )
    65     print '<table width="100%" border="1">'
    65             elif line.startswith('-'):
    66     print '\t<tr><td valign="top" width="10%%">author:</td>' + \
    66                 print '<span class="minusline">%s</span>' % (line, )
    67             '<td valign="top" width="20%%">%s</td>' % (obfuscate(changes[1]), )
    67             elif line.startswith('@'):
    68     print '\t\t<td valign="top" width="10%%">description:</td>' + \
    68                 print '<span class="atline">%s</span>' % (line, )
    69             '<td width="60%%">' + \
    69             else:
    70             '<a href="?cmd=chkin;nd=%s">%s</a></td></tr>' % \
    70                 print line
    71             (hn, nl2br(changes[4]), )
    71         print '</pre>'
    72     print '\t<tr><td>date:</td><td>%s UTC</td>' % (datestr, )
    72 
    73     print '\t\t<td valign="top">files:</td><td valign="top">'
    73 class errpage(page):
    74     for f in changes[3]:
    74     def __init__(self):
    75         print '\t\t%s&nbsp;&nbsp;' % f
    75         page.__init__(self, title="Mercurial Web Error Page")
    76     print '\t</td></tr>'
    76 
    77 #    print '\t<tr><td>revision:</td><td colspan="3">%d:<a ' % (i, ) + \
    77 class change_list(page):
    78 #            'href="?cmd=rev;nd=%s">%s</a></td></tr>' % (hn, hn, )
    78 
    79     print '</table><br />'
    79     numchanges = 50   # number of changes to show
    80 
    80 
    81 def ent_diff(a, b, fn):
    81     def __init__(self, repo, reponame):
    82     a = a.splitlines(1)
    82         page.__init__(self)
    83     b = b.splitlines(1)
    83         self.repo = repo
    84     l = difflib.unified_diff(a, b, fn, fn)
    84         print '<h3>Changes For: %s</h3>' % reponame
    85     print '<pre>'
    85 
    86     for line in l:
    86     def content(self, hi=None):
    87         line = cgi.escape(line[:-1])
    87         cl = []
    88         if line.startswith('+'):
    88         count = self.repo.changelog.count()
    89             print '<span class="plusline">%s</span>' % (line, )
    89         if not hi:
    90         elif line.startswith('-'):
    90             hi = count
    91             print '<span class="minusline">%s</span>' % (line, )
    91         elif hi < self.numchanges:
    92         elif line.startswith('@'):
    92             hi = self.numchanges
    93             print '<span class="atline">%s</span>' % (line, )
    93 
       
    94         start = 0
       
    95         if hi - self.numchanges >= 0:
       
    96             start = hi - self.numchanges
       
    97 
       
    98         nav = "Displaying Revisions: %d-%d" % (start, hi-1)
       
    99         if start != 0:
       
   100             nav = ('<a href="?cmd=changes;hi=%d">Previous %d</a>&nbsp;&nbsp;' \
       
   101                     % (start, self.numchanges)) + nav
       
   102         if hi != count:
       
   103             if hi + self.numchanges <= count:
       
   104                 nav += '&nbsp;&nbsp;<a href="?cmd=changes;hi=%d">Next %d</a>' \
       
   105                         % (hi + self.numchanges, self.numchanges)
       
   106             else:
       
   107                 nav += '&nbsp;&nbsp;<a href="?cmd=changes">Next %d</a>' % \
       
   108                         self.numchanges
       
   109 
       
   110         print '<center>%s</center>' % nav
       
   111 
       
   112         for i in xrange(start, hi):
       
   113             n = self.repo.changelog.node(i)
       
   114             cl.append((n, self.repo.changelog.read(n)))
       
   115         cl.reverse()
       
   116 
       
   117         print '<table summary="" width="100%" align="center">'
       
   118         for n, ch in cl:
       
   119             print '<tr><td>'
       
   120             self.change_table(n, ch)
       
   121             print '</td></tr>'
       
   122         print '</table>'
       
   123 
       
   124         print '<center>%s</center>' % nav
       
   125 
       
   126     def change_table(self, nodeid, changes):
       
   127         hn = hg.hex(nodeid)
       
   128         i = self.repo.changelog.rev(nodeid)
       
   129         (h1, h2) = [ hg.hex(x) for x in self.repo.changelog.parents(nodeid) ]
       
   130         datestr = time.asctime(time.gmtime(float(changes[2].split(' ')[0])))
       
   131         print '<table summary="" width="100%" border="1">'
       
   132         print '\t<tr><td valign="top" width="10%">author:</td>' + \
       
   133                 '<td valign="top" width="20%%">%s</td>' % \
       
   134                 (obfuscate(changes[1]), )
       
   135         print '\t\t<td valign="top" width="10%">description:</td>' + \
       
   136                 '<td width="60%">' + \
       
   137                 '<a href="?cmd=chkin;nd=%s">%s</a></td></tr>' % \
       
   138                 (hn, nl2br(cgi.escape(changes[4])), )
       
   139         print '\t<tr><td>date:</td><td>%s UTC</td>' % (datestr, )
       
   140         print '\t\t<td valign="top">files:</td><td valign="top">'
       
   141         for f in changes[3]:
       
   142             print '\t\t<a href="?cmd=file;cs=%s;fn=%s">%s</a>&nbsp;&nbsp;' % \
       
   143                     (hn, f, cgi.escape(f), )
       
   144         print '\t</td></tr>'
       
   145         print '\t<tr><td>revision:</td><td colspan="3">%d:<a ' % (i, ) + \
       
   146                 'href="?cmd=chkin;nd=%s">%s</a></td></tr>' % (hn, hn, )
       
   147         print '</table><br />'
       
   148 
       
   149 class checkin(page):
       
   150     def __init__(self, repo, nodestr):
       
   151         page.__init__(self)
       
   152         self.repo = repo
       
   153         self.node = hg.bin(nodestr)
       
   154         self.nodestr = nodestr
       
   155         print '<h3>Checkin: %s</h3>' % nodestr
       
   156 
       
   157     def content(self):
       
   158         changes = self.repo.changelog.read(self.node)
       
   159         i = self.repo.changelog.rev(self.node)
       
   160         parents = self.repo.changelog.parents(self.node)
       
   161         (h1, h2) = [ hg.hex(x) for x in parents ]
       
   162         (i1, i2) = [ self.repo.changelog.rev(x) for x in parents ]
       
   163         datestr = time.asctime(time.gmtime(float(changes[2].split(' ')[0])))
       
   164         mf = self.repo.manifest.read(changes[0])
       
   165         print '<table summary="" width="100%" border="1">'
       
   166         print '\t<tr><td>revision:</td><td colspan="3">%d:' % (i, ),
       
   167         print '<a href="?cmd=chkin;nd=%s">%s</a></td></tr>' % \
       
   168                 (self.nodestr, self.nodestr, )
       
   169         print '\t<tr><td>parent(s):</td><td colspan="3">%d:' % (i1, )
       
   170         print '<a href="?cmd=chkin;nd=%s">%s</a>' % (h1, h1, ),
       
   171         if i2 != -1:
       
   172             print '&nbsp;&nbsp;%d:<a href="?cmd=chkin;nd=%s">%s</a>' % \
       
   173                     (i2, h2, h2, ),
    94         else:
   174         else:
    95             print line
   175             print '&nbsp;&nbsp;%d:%s' % (i2, h2, ),
    96     print '</pre>'
   176         print '</td></tr>'
    97 
   177         print '\t<tr><td>manifest:</td><td colspan="3">%d:' % \
    98 def ent_checkin(repo, nodeid):
   178                 (self.repo.manifest.rev(changes[0]), ),
    99     startpage("Mercurial Web")
   179         print '<a href="?cmd=mf;nd=%s">%s</a></td></tr>' % \
   100 
   180                 (hg.hex(changes[0]), hg.hex(changes[0]), )
   101     changes = repo.changelog.read(nodeid)
   181         print '\t<tr><td valign="top" width="10%">author:</td>' + \
   102     hn = hg.hex(nodeid)
   182                 '<td valign="top" width="20%%">%s</td>' % \
   103     i = repo.changelog.rev(nodeid)
   183                 (obfuscate(changes[1]), )
   104     parents = repo.changelog.parents(nodeid)
   184         print '\t\t<td valign="top" width="10%">description:</td>' + \
   105     (h1, h2) = [ hg.hex(x) for x in parents ]
   185                 '<td width="60%">' + \
   106     (i1, i2) = [ repo.changelog.rev(x) for x in parents ]
   186                 '<a href="?cmd=chkin;nd=%s">%s</a></td></tr>' % \
   107     datestr = time.asctime(time.gmtime(float(changes[2].split(' ')[0])))
   187                 (self.nodestr, nl2br(cgi.escape(changes[4])), )
   108     mf = repo.manifest.read(changes[0])
   188         print '\t<tr><td>date:</td><td>%s UTC</td>' % (datestr, )
   109     print '<table width="100%" border="1">'
   189         print '\t\t<td valign="top">files:</td><td valign="top">'
   110     print '\t<tr><td>revision:</td><td colspan="3">%d:' % (i, ),
   190         for f in changes[3]:
   111     print '<a href="?cmd=rev;nd=%s">%s</a></td></tr>' % (hn, hn, )
   191             print '\t\t<a href="?cmd=file;nd=%s&fn=%s">%s</a>' % \
   112     print '\t<tr><td>parent(s):</td><td colspan="3">%d:' % (i1, )
   192                     (hg.hex(mf[f]), f, cgi.escape(f), ),
   113     print '<a href="?cmd=rev;nd=%s">%s</a>' % (h1, h1, ),
   193             print '&nbsp;&nbsp;'
   114     if i2 != -1:
   194         print '\t</td></tr>'
   115         print '&nbsp;&nbsp;%d:<a href="?cmd=rev;nd=%s">%s</a>' % \
   195         print '</table><br />'
   116                 (i2, h2, h2, ),
   196 
   117     else:
   197         (c, a, d) = self.repo.diffrevs(parents[0], self.node)
   118         print '&nbsp;&nbsp;%d:%s' % (i2, h2, ),
   198         change = self.repo.changelog.read(parents[0])
   119     print '</td></tr>'
   199         mf2 = self.repo.manifest.read(change[0])
   120     print '\t<tr><td>manifest:</td><td colspan="3">%d:' % \
   200         for f in c:
   121             (repo.manifest.rev(changes[0]), ),
   201             self.show_diff(self.repo.file(f).read(mf2[f]), \
   122     print '<a href="?cmd=mf;nd=%s">%s</a></td></tr>' % \
   202                     self.repo.file(f).read(mf[f]), f)
   123             (hg.hex(changes[0]), hg.hex(changes[0]), )
   203         for f in a:
   124     print '\t<tr><td valign="top" width="10%%">author:</td>' + \
   204             self.show_diff('', self.repo.file(f).read(mf[f]), f)
   125             '<td valign="top" width="20%%">%s</td>' % (obfuscate(changes[1]), )
   205         for f in d:
   126     print '\t\t<td valign="top" width="10%%">description:</td>' + \
   206             self.show_diff(self.repo.file(f).read(mf2[f]), '', f)
   127             '<td width="60%%">' + \
   207 
   128             '<a href="?cmd=chkin;nd=%s">%s</a></td></tr>' % \
   208 class filepage(page):
   129             (hn, nl2br(changes[4]), )
   209     def __init__(self, repo, fn, node=None, cs=None):
   130     print '\t<tr><td>date:</td><td>%s UTC</td>' % (datestr, )
   210         page.__init__(self)
   131     print '\t\t<td valign="top">files:</td><td valign="top">'
   211         self.repo = repo
   132     for f in changes[3]:
   212         self.fn = fn
   133         print '\t\t<a href="?cmd=file;nd=%s&fn=%s">%s</a>' % \
   213         if cs: 
   134                 (hg.hex(mf[f]), f, f, ),
   214             chng = self.repo.changelog.read(hg.bin(cs))
   135         print '&nbsp;&nbsp;'
   215             mf = self.repo.manifest.read(chng[0])
   136     print '\t</td></tr>'
   216             self.node = mf[self.fn]
   137     print '</table><br />'
   217             self.nodestr = hg.hex(self.node)
   138 
   218         else:
   139     (c, a, d) = repo.diffrevs(parents[0], nodeid)
   219             self.nodestr = node
   140     change = repo.changelog.read(parents[0])
   220             self.node = hg.bin(node)
   141     mf2 = repo.manifest.read(change[0])
   221         print '<div class="filename">%s (%s)</div>' % \
   142     for f in c:
   222                 (cgi.escape(self.fn), self.nodestr, )
   143         ent_diff(repo.file(f).read(mf2[f]), repo.file(f).read(mf[f]), f)
   223         print '<a href="?cmd=hist;fn=%s">history</a><br />' % self.fn
   144     for f in a:
   224 
   145         ent_diff('', repo.file(f).read(mf[f]), f)
   225     def content(self):
   146     for f in d:
   226         print '<pre>'
   147         ent_diff(repo.file(f).read(mf2[f]), '', f)
   227         print cgi.escape(self.repo.file(self.fn).read(self.node))
   148 
   228         print '</pre>'
   149     endpage()
   229 
   150 
   230 class mfpage(page):
   151 
   231     def __init__(self, repo, node):
   152 def ent_file(repo, nodeid, fn):
   232         page.__init__(self)
   153     print '<div class="filename">%s (%s)</div>' % (fn, hg.hex(nodeid), )
   233         self.repo = repo
   154     print '<pre>'
   234         self.nodestr = node
   155     print cgi.escape(repo.file(fn).read(nodeid))
   235         self.node = hg.bin(node)
   156     print '</pre>'
   236 
   157 
   237     def content(self):
   158 def change_page():
   238         mf = self.repo.manifest.read(self.node)
   159     startpage("Mercurial Web")
   239         fns = mf.keys()
   160     print '<table width="100%" align="center">'
   240         fns.sort()
   161     for i in xrange(0, repo.changelog.count()):
   241         print '<div class="manifest">Manifest (%s)</div>' % self.nodestr
   162         n = repo.changelog.node(i)
   242         for f in fns:
   163         print '<tr><td>'
   243             print '<a href="?cmd=file;fn=%s;nd=%s">%s</a><br />' % \
   164         ent_change(repo, n)
   244                     (f, hg.hex(mf[f]), f)
   165         print '</td></th>'
   245 
   166 
   246 class histpage(page):
   167     print '</table>'
   247     def __init__(self, repo, fn):
   168     endpage()
   248         page.__init__(self)
       
   249         self.repo = repo
       
   250         self.fn = fn
       
   251 
       
   252     def content(self):
       
   253         print '<div class="filehist">File History: %s</div>' % self.fn
       
   254         r = self.repo.file(self.fn)
       
   255         print '<br />'
       
   256         print '<table summary="" width="100%" align="center">'
       
   257         for i in xrange(r.count()-1, -1, -1):
       
   258             n = r.node(i)
       
   259             (p1, p2) = r.parents(n)
       
   260             (h, h1, h2) = map(hg.hex, (n, p1, p2))
       
   261             (i1, i2) = map(r.rev, (p1, p2))
       
   262             ci = r.linkrev(n)
       
   263             cn = self.repo.changelog.node(ci)
       
   264             cs = hg.hex(cn)
       
   265             changes = self.repo.changelog.read(cn)
       
   266             print '<tr><td>'
       
   267             self.hist_ent(i, h, i1, h1, i2, h2, ci, cs, changes)
       
   268             print '</tr></td>'
       
   269         print '</table>'
       
   270 
       
   271     def hist_ent(self, revi, revs, p1i, p1s, p2i, p2s, ci, cs, changes):
       
   272         datestr = time.asctime(time.gmtime(float(changes[2].split(' ')[0])))
       
   273         print '<table summary="" width="100%" border="1">'
       
   274         print '\t<tr><td valign="top" width="10%">author:</td>' + \
       
   275                 '<td valign="top" width="20%%">%s</td>' % \
       
   276                 (obfuscate(changes[1]), )
       
   277         print '\t\t<td valign="top" width="10%">description:</td>' + \
       
   278                 '<td width="60%">' + \
       
   279                 '<a href="?cmd=chkin;nd=%s">%s</a></td></tr>' % \
       
   280                 (cs, nl2br(cgi.escape(changes[4])), )
       
   281         print '\t<tr><td>date:</td><td>%s UTC</td>' % (datestr, )
       
   282         print '\t\t<td>revision:</td><td>%d:<a ' % (revi, ) + \
       
   283                 'href="?cmd=file;cs=%s;fn=%s">%s</a></td></tr>' % \
       
   284                 (cs, self.fn, revs )
       
   285         print '\t<tr><td>parent(s):</td><td colspan="3">%d:' % (p1i, )
       
   286         print '<a href="?cmd=file;nd=%s;fn=%s">%s</a>' % (p1s, self.fn, p1s, ),
       
   287         if p2i != -1:
       
   288             print '&nbsp;&nbsp;%d:<a href="?cmd=file;nd=%s;fn=%s">%s</a>' % \
       
   289                     (p2i, p2s, self.fn, p2s ),
       
   290         print '</td></tr>'
       
   291         print '</table><br />'
   169 
   292 
   170 args = cgi.parse()
   293 args = cgi.parse()
   171 
   294 
   172 ui = hg.ui()
   295 ui = hg.ui()
   173 repo = hg.repository(ui, repo_path)
   296 repo = hg.repository(ui, repo_path)
   174 
   297 
   175 if not args.has_key('cmd'):
   298 if not args.has_key('cmd') or args['cmd'][0] == 'changes':
   176     change_page()
   299     page = change_list(repo, 'Mercurial')
       
   300     hi = args.get('hi', ( repo.changelog.count(), ))
       
   301     page.content(hi = int(hi[0]))
       
   302     page.endpage()
   177     
   303     
   178 elif args['cmd'][0] == 'chkin':
   304 elif args['cmd'][0] == 'chkin':
   179     if not args.has_key('nd'):
   305     if not args.has_key('nd'):
       
   306         page = errpage()
   180         print '<div class="errmsg">No Node!</div>'
   307         print '<div class="errmsg">No Node!</div>'
   181     else:
   308     else:
   182         ent_checkin(repo, hg.bin(args['nd'][0]))
   309         page = checkin(repo, args['nd'][0])
       
   310         page.content()
       
   311     page.endpage()
   183 
   312 
   184 elif args['cmd'][0] == 'file':
   313 elif args['cmd'][0] == 'file':
   185     startpage("Mercurial Web")
   314     if not (args.has_key('nd') and args.has_key('fn')) and \
   186 
   315             not (args.has_key('cs') and args.has_key('fn')):
       
   316         page = errpage()
       
   317         print '<div class="errmsg">Invalid Args!</div>'
       
   318     else:
       
   319         if args.has_key('nd'):
       
   320             page = filepage(repo, args['fn'][0], node=args['nd'][0])
       
   321         else:
       
   322             page = filepage(repo, args['fn'][0], cs=args['cs'][0])
       
   323         page.content()
       
   324     page.endpage()
       
   325 
       
   326 elif args['cmd'][0] == 'mf':
   187     if not args.has_key('nd'):
   327     if not args.has_key('nd'):
       
   328         page = errpage()
   188         print '<div class="errmsg">No Node!</div>'
   329         print '<div class="errmsg">No Node!</div>'
   189     elif not args.has_key('fn'):
   330     else:
       
   331         page = mfpage(repo, args['nd'][0])
       
   332         page.content()
       
   333     page.endpage()
       
   334 
       
   335 elif args['cmd'][0] == 'hist':
       
   336     if not args.has_key('fn'):
       
   337         page = errpage()
   190         print '<div class="errmsg">No Filename!</div>'
   338         print '<div class="errmsg">No Filename!</div>'
   191     else:
   339     else:
   192         ent_file(repo, hg.bin(args['nd'][0]), args['fn'][0])
   340         page = histpage(repo, args['fn'][0])
   193     endpage()
   341         page.content()
       
   342     page.endpage()
   194 
   343 
   195 elif args['cmd'][0] == 'branches':
   344 elif args['cmd'][0] == 'branches':
   196     httphdr("text/plain")
   345     httphdr("text/plain")
   197     nodes = []
   346     nodes = []
   198     if args.has_key('nodes'):
   347     if args.has_key('nodes'):
   220         sys.stdout.write(z.compress(chunk))
   369         sys.stdout.write(z.compress(chunk))
   221 
   370 
   222     sys.stdout.write(z.flush())
   371     sys.stdout.write(z.flush())
   223 
   372 
   224 else:
   373 else:
   225     startpage("Mercurial Web Error")
   374     page = errpage()
   226     print '<div class="errmsg">unknown command: ', args['cmd'][0], '</div>'
   375     print '<div class="errmsg">unknown command: %s</div>' % \
   227     endpage()
   376             cgi.escape(args['cmd'][0])
       
   377     page.endpage()