mercurial/hgweb.py
changeset 133 fb84d3e71042
parent 132 210eeb6f5197
child 134 13d609f8d830
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")