hgweb: add template filters, template style maps, and raw pages
authormpm@selenic.com
Tue, 31 May 2005 21:10:10 -0800
changeset 201 f918a6fa2572
parent 200 8450c18f2a45
child 202 e875a0cf7f3a
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-----
mercurial/hgweb.py
templates/changelog.tmpl
templates/changelogentry.tmpl
templates/changeset-raw.tmpl
templates/changeset.tmpl
templates/fileannotate.tmpl
templates/filediff-raw.tmpl
templates/filediff.tmpl
templates/filelog.tmpl
templates/filelogentry.tmpl
templates/filerevision-raw.tmpl
templates/filerevision.tmpl
templates/header-raw.tmpl
templates/manifest.tmpl
templates/map
templates/map-raw
--- a/mercurial/hgweb.py	Tue May 31 09:03:46 2005 -0800
+++ b/mercurial/hgweb.py	Tue May 31 21:10:10 2005 -0800
@@ -14,9 +14,9 @@
 from mercurial.hg import *
 
 def templatepath():
-    for f in "templates/map", "../templates/map":
+    for f in "templates", "../templates":
         p = os.path.join(os.path.dirname(__file__), f)
-        if os.path.isfile(p): return p
+        if os.path.isdir(p): return p
 
 def age(t):
     def plural(t, c):
@@ -43,7 +43,7 @@
         if n >= 2 or s == 1: return fmt(t, n)
 
 def nl2br(text):
-    return text.replace('\n', '<br/>')
+    return text.replace('\n', '<br/>\n')
 
 def obfuscate(text):
     return ''.join([ '&#%d' % ord(c) for c in text ])
@@ -67,23 +67,31 @@
         else:
             sys.stdout.write(str(thing))
 
-def template(tmpl, **map):
+def template(tmpl, filters = {}, **map):
     while tmpl:
-        m = re.search(r"#([a-zA-Z0-9]+)#", tmpl)
+        m = re.search(r"#([a-zA-Z0-9]+)((\|[a-zA-Z0-9]+)*)#", tmpl)
         if m:
             yield tmpl[:m.start(0)]
             v = map.get(m.group(1), "")
-            yield callable(v) and v() or v
+            v = callable(v) and v() or v
+
+            fl = m.group(2)
+            if fl:
+                for f in fl.split("|")[1:]:
+                    v = filters[f](v)
+                
+            yield v
             tmpl = tmpl[m.end(0):]
         else:
             yield tmpl
             return
 
 class templater:
-    def __init__(self, mapfile):
+    def __init__(self, mapfile, filters = {}):
         self.cache = {}
         self.map = {}
         self.base = os.path.dirname(mapfile)
+        self.filters = filters
         
         for l in file(mapfile):
             m = re.match(r'(\S+)\s*=\s*"(.*)"$', l)
@@ -101,20 +109,27 @@
             tmpl = self.cache[t]
         except KeyError:
             tmpl = self.cache[t] = file(self.map[t]).read()
-        return template(tmpl, **map)
+        return template(tmpl, self.filters, **map)
         
 class hgweb:
     maxchanges = 20
     maxfiles = 10
 
-    def __init__(self, path, name, templatemap = ""):
-        templatemap = templatemap or templatepath()
-
+    def __init__(self, path, name, templates = ""):
+        self.templates = templates or templatepath()
         self.reponame = name
         self.repo = repository(ui(), path)
-        self.t = templater(templatemap)
         self.viewonly = 0
 
+        self.filters = {
+            "escape": cgi.escape,
+            "age": age,
+            "date": (lambda x: time.asctime(time.gmtime(x))),
+            "addbreaks": nl2br,
+            "obfuscate": obfuscate,
+            "firstline": (lambda x: x.splitlines(1)[0]),
+            }
+
     def date(self, cs):
         return time.asctime(time.gmtime(float(cs[2].split(' ')[0])))
 
@@ -154,15 +169,14 @@
             
         def prettyprintlines(diff):
             for l in diff.splitlines(1):
-                line = cgi.escape(l)
-                if line.startswith('+'):
-                    yield self.t("difflineplus", line = line)
-                elif line.startswith('-'):
-                    yield self.t("difflineminus", line = line)
-                elif line.startswith('@'):
-                    yield self.t("difflineat", line = line)
+                if l.startswith('+'):
+                    yield self.t("difflineplus", line = l)
+                elif l.startswith('-'):
+                    yield self.t("difflineminus", line = l)
+                elif l.startswith('@'):
+                    yield self.t("difflineat", line = l)
                 else:
-                    yield self.t("diffline", line = line)
+                    yield self.t("diffline", line = l)
 
         r = self.repo
         cl = r.changelog
@@ -234,9 +248,7 @@
                 l.insert(0, self.t(
                     'changelogentry',
                     parity = parity,
-                    author = obfuscate(changes[1]),
-                    shortdesc = cgi.escape(changes[4].splitlines()[0]),
-                    age = age(t),
+                    author = changes[1],
                     parent1 = self.parent("changelogparent",
                                           hex(p1), cl.rev(p1)),
                     parent2 = self.parent("changelogparent",
@@ -244,8 +256,8 @@
                     p1 = hex(p1), p2 = hex(p2),
                     p1rev = cl.rev(p1), p2rev = cl.rev(p2),
                     manifest = hex(changes[0]),
-                    desc = nl2br(cgi.escape(changes[4])),
-                    date = time.asctime(time.gmtime(t)),
+                    desc = changes[4],
+                    date = t,
                     files = self.listfilediffs(changes[3], n),
                     rev = i,
                     node = hn))
@@ -292,7 +304,6 @@
                      diff = diff,
                      rev = cl.rev(n),
                      node = nodeid,
-                     shortdesc = cgi.escape(changes[4].splitlines()[0]),
                      parent1 = self.parent("changesetparent",
                                            hex(p1), cl.rev(p1)),
                      parent2 = self.parent("changesetparent",
@@ -300,9 +311,9 @@
                      p1 = hex(p1), p2 = hex(p2),
                      p1rev = cl.rev(p1), p2rev = cl.rev(p2),
                      manifest = hex(changes[0]),
-                     author = obfuscate(changes[1]),
-                     desc = nl2br(cgi.escape(changes[4])),
-                     date = time.asctime(time.gmtime(t)),
+                     author = changes[1],
+                     desc = changes[4],
+                     date = t,
                      files = files)
 
     def filelog(self, f, filenode):
@@ -329,10 +340,9 @@
                                    filerev = i,
                                    file = f,
                                    node = hex(cn),
-                                   author = obfuscate(cs[1]),
-                                   age = age(t),
-                                   date = time.asctime(time.gmtime(t)),
-                                   shortdesc = cgi.escape(cs[4].splitlines()[0]),
+                                   author = cs[1],
+                                   date = t,
+                                   desc = cs[4],
                                    p1 = hex(p1), p2 = hex(p2),
                                    p1rev = fl.rev(p1), p2rev = fl.rev(p2)))
                 parity = 1 - parity
@@ -350,7 +360,7 @@
     def filerevision(self, f, node):
         fl = self.repo.file(f)
         n = bin(node)
-        text = cgi.escape(fl.read(n))
+        text = fl.read(n)
         changerev = fl.linkrev(n)
         cl = self.repo.changelog
         cn = cl.node(changerev)
@@ -361,8 +371,7 @@
 
         def lines():
             for l, t in enumerate(text.splitlines(1)):
-                yield self.t("fileline",
-                             line = t,
+                yield self.t("fileline", line = t,
                              linenumber = "% 6d" % (l + 1),
                              parity = l & 1)
         
@@ -376,10 +385,8 @@
                      rev = changerev,
                      node = hex(cn),
                      manifest = hex(mfn),
-                     author = obfuscate(cs[1]),
-                     age = age(t),
-                     date = time.asctime(time.gmtime(t)),
-                     shortdesc = cgi.escape(cs[4].splitlines()[0]),
+                     author = cs[1],
+                     date = t,
                      parent1 = self.parent("filerevparent",
                                            hex(p1), fl.rev(p1), file=f),
                      parent2 = self.parent("filerevparent",
@@ -387,7 +394,6 @@
                      p1 = hex(p1), p2 = hex(p2),
                      p1rev = fl.rev(p1), p2rev = fl.rev(p2))
 
-
     def fileannotate(self, f, node):
         bcache = {}
         ncache = {}
@@ -431,7 +437,7 @@
                              rev = r,
                              author = name,
                              file = f,
-                             line = cgi.escape(l))
+                             line = l)
 
         yield self.t("fileannotate",
                      header = self.header(),
@@ -444,10 +450,8 @@
                      rev = changerev,
                      node = hex(cn),
                      manifest = hex(mfn),
-                     author = obfuscate(cs[1]),
-                     age = age(t),
-                     date = time.asctime(time.gmtime(t)),
-                     shortdesc = cgi.escape(cs[4].splitlines()[0]),
+                     author = cs[1],
+                     date = t,
                      parent1 = self.parent("fileannotateparent",
                                            hex(p1), fl.rev(p1), file=f),
                      parent2 = self.parent("fileannotateparent",
@@ -563,6 +567,14 @@
     def run(self):
         args = cgi.parse()
 
+        m = os.path.join(self.templates, "map")
+        if args.has_key('style'):
+            b = os.path.basename("map-" + args['style'][0])
+            p = os.path.join(self.templates, b)
+            if os.path.isfile(p): m = p
+            
+        self.t = templater(m, self.filters)
+
         if not args.has_key('cmd') or args['cmd'][0] == 'changelog':
             hi = self.repo.changelog.count()
             if args.has_key('rev'):
--- a/templates/changelog.tmpl	Tue May 31 09:03:46 2005 -0800
+++ b/templates/changelog.tmpl	Tue May 31 21:10:10 2005 -0800
@@ -1,5 +1,5 @@
 #header#
-<title>#repo#: changelog</title>
+<title>#repo|escape#: changelog</title>
 </head>
 <body>
 
@@ -8,7 +8,7 @@
 <a href="?cmd=manifest;manifest=#manifest#;path=/">manifest</a>
 </div>
 
-<h2>changelog for #repo#</h2>
+<h2>changelog for #repo|escape#</h2>
 
 <form>
 search:
--- a/templates/changelogentry.tmpl	Tue May 31 09:03:46 2005 -0800
+++ b/templates/changelogentry.tmpl	Tue May 31 21:10:10 2005 -0800
@@ -1,8 +1,8 @@
 <div class="parity#parity#">
 <table width="100%" cellpadding="0" cellspacing="0">
 <tr>
- <td align="right" width="15%"><b>#age# ago:&nbsp;</b></td>
- <td><b>#shortdesc#</b></td</tr>
+ <td align="right" width="15%"><b>#date|age# ago:&nbsp;</b></td>
+ <td><b>#desc|firstline|escape#</b></td</tr>
 <tr>
  <td align="right">changeset #rev#:&nbsp;</td>
  <td><a href="?cmd=changeset;node=#node#">#node#</a></td></tr>
@@ -13,7 +13,7 @@
  <td>#author#</td></tr>
 <tr>
  <td align="right">date:&nbsp;</td>
- <td>#date#</td></tr>
+ <td>#date|date#</td></tr>
 <tr>
  <td align="right" valign="top"><a href="?cmd=manifest;manifest=#manifest#;path=/">files</a>:&nbsp;</td>
  <td>#files#</td></tr>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/changeset-raw.tmpl	Tue May 31 21:10:10 2005 -0800
@@ -0,0 +1,10 @@
+#header#
+# HG changeset patch
+# User #author#
+# Date #date|date#
+# Node ID #node#
+#parent1#
+#parent2#
+#desc#
+
+#diff#
--- a/templates/changeset.tmpl	Tue May 31 09:03:46 2005 -0800
+++ b/templates/changeset.tmpl	Tue May 31 21:10:10 2005 -0800
@@ -1,5 +1,5 @@
 #header#
-<title>#repo#: changeset #node#</title>
+<title>#repo|escape#: changeset #node#</title>
 </head>
 <body>
 
@@ -7,9 +7,10 @@
 <a href="?cmd=changelog;rev=#rev#">changelog</a>
 <a href="?cmd=tags">tags</a>
 <a href="?cmd=manifest;manifest=#manifest#;path=/">manifest</a>
+<a href="?cmd=changeset;node=#node#;style=raw">raw</a>
 </div>
 
-<h2>changeset: #shortdesc#</h2>
+<h2>changeset: #desc|escape|firstline#</h2>
 
 <table>
 <tr>
@@ -22,16 +23,16 @@
  <td><a href="?cmd=manifest;manifest=#manifest#;path=/">#manifest#</a></td></tr>
 <tr>
  <td class="metatag">author:</td>
- <td>#author#</td></tr>
+ <td>#author|obfuscate#</td></tr>
 <tr>
  <td class="metatag">date:</td>
- <td>#date#</td></tr>
+ <td>#date|date# (#date|age# ago)</td></tr>
 <tr>
  <td class="metatag" valign="top">files:</td>
  <td>#files#</td></tr>
 <tr>
  <td class="metatag" valign="top">description:</td>
- <td>#desc#</td></tr>
+ <td>#desc|escape|addbreaks#</td></tr>
 </table>
 
 <pre>
--- a/templates/fileannotate.tmpl	Tue May 31 09:03:46 2005 -0800
+++ b/templates/fileannotate.tmpl	Tue May 31 21:10:10 2005 -0800
@@ -1,5 +1,5 @@
 #header#
-<title>#repo#: #file# annotate</title>
+<title>#repo|escape#: #file# annotate</title>
 </head>
 <body>
 
@@ -25,10 +25,10 @@
  <td><a href="?cmd=manifest;manifest=#manifest#;path=/">#manifest#</a></td></tr>
 <tr>
  <td class="metatag">author:</td>
- <td>#author#</td></tr>
+ <td>#author|obfuscate#</td></tr>
 <tr>
  <td class="metatag">date:</td>
- <td>#date#</td></tr>
+ <td>#date|date# (#date|age#) ago)</td></tr>
 </table>
 
 <br/>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/filediff-raw.tmpl	Tue May 31 21:10:10 2005 -0800
@@ -0,0 +1,5 @@
+#header#
+#diff#
+#footer#
+
+
--- a/templates/filediff.tmpl	Tue May 31 09:03:46 2005 -0800
+++ b/templates/filediff.tmpl	Tue May 31 21:10:10 2005 -0800
@@ -1,5 +1,5 @@
 #header#
-<title>#repo#: #file# diff</title>
+<title>#repo|escape#: #file# diff</title>
 </head>
 <body>
 
@@ -10,6 +10,7 @@
 <a href="?cmd=file;file=#file#;filenode=#filenode#">file</a>
 <a href="?cmd=filelog;file=#file#;filenode=#filenode#">revisions</a>
 <a href="?cmd=annotate;file=#file#;filenode=#filenode#">annotate</a>
+<a href="?cmd=filediff;file=#file#;node=#node#;style=raw">raw</a>
 </div>
 
 <h2>#file#</h2>
--- a/templates/filelog.tmpl	Tue May 31 09:03:46 2005 -0800
+++ b/templates/filelog.tmpl	Tue May 31 21:10:10 2005 -0800
@@ -1,5 +1,5 @@
 #header#
-<title>#repo#: #file# history</title>
+<title>#repo|escape#: #file# history</title>
 </head>
 <body>
 
--- a/templates/filelogentry.tmpl	Tue May 31 09:03:46 2005 -0800
+++ b/templates/filelogentry.tmpl	Tue May 31 21:10:10 2005 -0800
@@ -1,7 +1,7 @@
 <table class="parity#parity#" width="100%" cellspacing="0" cellpadding="0">
 <tr>
- <td align="right" width="15%"><b>#age# ago:&nbsp;</b></td>
- <td><b><a href="?cmd=changeset;node=#node#">#shortdesc#</a></b></td</tr>
+ <td align="right" width="15%"><b>#date|age# ago:&nbsp;</b></td>
+ <td><b><a href="?cmd=changeset;node=#node#">#desc|firstline|escape#</a></b></td</tr>
 <tr>
  <td align="right">revision #filerev#:&nbsp;</td>
  <td><a href="?cmd=file;file=#file#;filenode=#filenode#">#filenode#</a>
@@ -10,10 +10,10 @@
 </td></tr>
 <tr>
  <td align="right">author:&nbsp;</td>
- <td>#author#</td></tr>
+ <td>#author|obfuscate#</td></tr>
 <tr>
  <td align="right">date:&nbsp;</td>
- <td>#date#</td></tr>
+ <td>#date|date# (#date|age# ago)</td></tr>
 </table>
 
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/filerevision-raw.tmpl	Tue May 31 21:10:10 2005 -0800
@@ -0,0 +1,3 @@
+#header#
+#text#
+#footer#
--- a/templates/filerevision.tmpl	Tue May 31 09:03:46 2005 -0800
+++ b/templates/filerevision.tmpl	Tue May 31 21:10:10 2005 -0800
@@ -1,5 +1,5 @@
 #header#
-<title>#repo#:#file#</title>
+<title>#repo|escape#:#file#</title>
 </head>
 <body>
 
@@ -10,6 +10,7 @@
 <a href="?cmd=manifest;manifest=#manifest#;path=#path#">manifest</a>
 <a href="?cmd=filelog;file=#file#;filenode=#filenode#">revisions</a>
 <a href="?cmd=annotate;file=#file#;filenode=#filenode#">annotate</a>
+<a href="?cmd=file;file=#file#;filenode=#filenode#;style=raw">raw</a>
 </div>
 
 <h2>#file# (revision #filenode#)</h2>
@@ -25,10 +26,10 @@
  <td><a href="?cmd=manifest;manifest=#manifest#;path=/">#manifest#</a></td></tr>
 <tr>
  <td class="metatag">author:</td>
- <td>#author#</td></tr>
+ <td>#author|obfuscate#</td></tr>
 <tr>
  <td class="metatag">date:</td>
- <td>#date#</td></tr>
+ <td>#date|date# (#date|age# ago)</td></tr>
 </table>
 
 <pre>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/header-raw.tmpl	Tue May 31 21:10:10 2005 -0800
@@ -0,0 +1,1 @@
+Content-type: text/plain
--- a/templates/manifest.tmpl	Tue May 31 09:03:46 2005 -0800
+++ b/templates/manifest.tmpl	Tue May 31 21:10:10 2005 -0800
@@ -1,5 +1,5 @@
 #header#
-<title>#repo#: manifest #manifest#</title>
+<title>#repo|escape#: manifest #manifest#</title>
 </head>
 <body>
 
--- a/templates/map	Tue May 31 09:03:46 2005 -0800
+++ b/templates/map	Tue May 31 21:10:10 2005 -0800
@@ -14,13 +14,13 @@
 fileannotate = fileannotate.tmpl
 filediff = filediff.tmpl
 filelog = filelog.tmpl
-fileline = "<div class="parity#parity#"><span class="lineno">#linenumber# </span>#line#</div>"
+fileline = "<div class="parity#parity#"><span class="lineno">#linenumber# </span>#line|escape#</div>"
 filelogentry = filelogentry.tmpl
-annotateline = "<tr class="parity#parity#"><td class="annotate"><a href="?cmd=changeset;node=#node#">#author#@#rev#</a></td><td><pre>#line#</pre></td></tr>"
-difflineplus = "<span class="plusline">#line#</span>"
-difflineminus = "<span class="minusline">#line#</span>"
-difflineat = "<span class="atline">#line#</span>"
-diffline = "#line#"
+annotateline = "<tr class="parity#parity#"><td class="annotate"><a href="?cmd=changeset;node=#node#">#author#@#rev#</a></td><td><pre>#line|escape#</pre></td></tr>"
+difflineplus = "<span class="plusline">#line|escape#</span>"
+difflineminus = "<span class="minusline">#line|escape#</span>"
+difflineat = "<span class="atline">#line|escape#</span>"
+diffline = "#line|escape#"
 changelogparent = "<tr><td align="right">parent:&nbsp;</td><td><a href="?cmd=changeset;node=#node#">#node#</a></td></tr>"
 changesetparent = "<tr><td class="metatag">parent:</td><td><a href="?cmd=changeset;node=#node#">#node#</a></td></tr>"
 filerevparent = "<tr><td class="metatag">parent:</td><td><a href="?cmd=file;file=#file#;filenode=#node#">#node#</a></td></tr>"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/map-raw	Tue May 31 21:10:10 2005 -0800
@@ -0,0 +1,14 @@
+header = header-raw.tmpl
+footer = ""
+changeset = changeset-raw.tmpl
+annotateline = "<tr class="parity#parity#"><td class="annotate"><a href="?cmd=changeset;node=#node#">#author#@#rev#</a></td><td><pre>#line#</pre></td></tr>"
+difflineplus = "#line#"
+difflineminus = "#line#"
+difflineat = "#line#"
+diffline = "#line#"
+changesetparent = "# parent: #node#"
+filenodelink = "#file#"
+filerevision = filerevision-raw.tmpl
+fileline = "#line#"
+diffblock = "#lines#"
+filediff = filediff-raw.tmpl