mercurial/hgweb.py
changeset 1896 f8f818a04f5b
parent 1796 a373881fdf2a
child 1897 58b6784cf9f1
equal deleted inserted replaced
1800:414e81ae971f 1896:f8f818a04f5b
     4 # Copyright 2005 Matt Mackall <mpm@selenic.com>
     4 # Copyright 2005 Matt Mackall <mpm@selenic.com>
     5 #
     5 #
     6 # This software may be used and distributed according to the terms
     6 # This software may be used and distributed according to the terms
     7 # of the GNU General Public License, incorporated herein by reference.
     7 # of the GNU General Public License, incorporated herein by reference.
     8 
     8 
     9 import os, cgi, sys, urllib
     9 import os, cgi, sys
    10 import mimetypes
    10 import mimetypes
    11 from demandload import demandload
    11 from demandload import demandload
    12 demandload(globals(), "mdiff time re socket zlib errno ui hg ConfigParser")
    12 demandload(globals(), "mdiff time re socket zlib errno ui hg ConfigParser")
    13 demandload(globals(), "zipfile tempfile StringIO tarfile BaseHTTPServer util")
    13 demandload(globals(), "zipfile tempfile StringIO tarfile BaseHTTPServer util")
    14 demandload(globals(), "mimetypes")
    14 demandload(globals(), "mimetypes templater")
    15 from node import *
    15 from node import *
    16 from i18n import gettext as _
    16 from i18n import gettext as _
    17 
    17 
    18 def templatepath():
    18 def templatepath():
    19     for f in "templates", "../templates":
    19     for f in "templates", "../templates":
    20         p = os.path.join(os.path.dirname(__file__), f)
    20         p = os.path.join(os.path.dirname(__file__), f)
    21         if os.path.isdir(p):
    21         if os.path.isdir(p):
    22             return os.path.normpath(p)
    22             return os.path.normpath(p)
    23 
       
    24 def age(x):
       
    25     def plural(t, c):
       
    26         if c == 1:
       
    27             return t
       
    28         return t + "s"
       
    29     def fmt(t, c):
       
    30         return "%d %s" % (c, plural(t, c))
       
    31 
       
    32     now = time.time()
       
    33     then = x[0]
       
    34     delta = max(1, int(now - then))
       
    35 
       
    36     scales = [["second", 1],
       
    37               ["minute", 60],
       
    38               ["hour", 3600],
       
    39               ["day", 3600 * 24],
       
    40               ["week", 3600 * 24 * 7],
       
    41               ["month", 3600 * 24 * 30],
       
    42               ["year", 3600 * 24 * 365]]
       
    43 
       
    44     scales.reverse()
       
    45 
       
    46     for t, s in scales:
       
    47         n = delta / s
       
    48         if n >= 2 or s == 1:
       
    49             return fmt(t, n)
       
    50 
       
    51 def nl2br(text):
       
    52     return text.replace('\n', '<br/>\n')
       
    53 
       
    54 def obfuscate(text):
       
    55     return ''.join(['&#%d;' % ord(c) for c in text])
       
    56 
    23 
    57 def up(p):
    24 def up(p):
    58     if p[0] != "/":
    25     if p[0] != "/":
    59         p = "/" + p
    26         p = "/" + p
    60     if p[-1] == "/":
    27     if p[-1] == "/":
   129         if file:
    96         if file:
   130             headers.append(('Content-disposition', 'attachment; filename=%s' % file))
    97             headers.append(('Content-disposition', 'attachment; filename=%s' % file))
   131         if size > 0:
    98         if size > 0:
   132             headers.append(('Content-length', str(size)))
    99             headers.append(('Content-length', str(size)))
   133         self.header(headers)
   100         self.header(headers)
   134 
       
   135 class templater(object):
       
   136     def __init__(self, mapfile, filters={}, defaults={}):
       
   137         self.cache = {}
       
   138         self.map = {}
       
   139         self.base = os.path.dirname(mapfile)
       
   140         self.filters = filters
       
   141         self.defaults = defaults
       
   142 
       
   143         for l in file(mapfile):
       
   144             m = re.match(r'(\S+)\s*=\s*"(.*)"$', l)
       
   145             if m:
       
   146                 self.cache[m.group(1)] = m.group(2)
       
   147             else:
       
   148                 m = re.match(r'(\S+)\s*=\s*(\S+)', l)
       
   149                 if m:
       
   150                     self.map[m.group(1)] = os.path.join(self.base, m.group(2))
       
   151                 else:
       
   152                     raise LookupError(_("unknown map entry '%s'") % l)
       
   153 
       
   154     def __call__(self, t, **map):
       
   155         m = self.defaults.copy()
       
   156         m.update(map)
       
   157         try:
       
   158             tmpl = self.cache[t]
       
   159         except KeyError:
       
   160             tmpl = self.cache[t] = file(self.map[t]).read()
       
   161         return self.template(tmpl, self.filters, **m)
       
   162 
       
   163     def template(self, tmpl, filters={}, **map):
       
   164         while tmpl:
       
   165             m = re.search(r"#([a-zA-Z0-9]+)((%[a-zA-Z0-9]+)*)((\|[a-zA-Z0-9]+)*)#", tmpl)
       
   166             if m:
       
   167                 yield tmpl[:m.start(0)]
       
   168                 v = map.get(m.group(1), "")
       
   169                 v = callable(v) and v(**map) or v
       
   170 
       
   171                 format = m.group(2)
       
   172                 fl = m.group(4)
       
   173 
       
   174                 if format:
       
   175                     q = v.__iter__
       
   176                     for i in q():
       
   177                         lm = map.copy()
       
   178                         lm.update(i)
       
   179                         yield self(format[1:], **lm)
       
   180 
       
   181                     v = ""
       
   182 
       
   183                 elif fl:
       
   184                     for f in fl.split("|")[1:]:
       
   185                         v = filters[f](v)
       
   186 
       
   187                 yield v
       
   188                 tmpl = tmpl[m.end(0):]
       
   189             else:
       
   190                 yield tmpl
       
   191                 return
       
   192 
       
   193 common_filters = {
       
   194     "escape": lambda x: cgi.escape(x, True),
       
   195     "urlescape": urllib.quote,
       
   196     "strip": lambda x: x.strip(),
       
   197     "age": age,
       
   198     "date": lambda x: util.datestr(x),
       
   199     "addbreaks": nl2br,
       
   200     "obfuscate": obfuscate,
       
   201     "short": (lambda x: x[:12]),
       
   202     "firstline": (lambda x: x.splitlines(1)[0]),
       
   203     "permissions": (lambda x: x and "-rwxr-xr-x" or "-rw-r--r--"),
       
   204     "rfc822date": lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S"),
       
   205     }
       
   206 
   101 
   207 class hgweb(object):
   102 class hgweb(object):
   208     def __init__(self, repo, name=None):
   103     def __init__(self, repo, name=None):
   209         if type(repo) == type(""):
   104         if type(repo) == type(""):
   210             self.repo = hg.repository(ui.ui(), repo)
   105             self.repo = hg.repository(ui.ui(), repo)
   908         url = "http://%s%s%s" % (req.env["SERVER_NAME"], port, uri)
   803         url = "http://%s%s%s" % (req.env["SERVER_NAME"], port, uri)
   909         if not self.reponame:
   804         if not self.reponame:
   910             self.reponame = (self.repo.ui.config("web", "name")
   805             self.reponame = (self.repo.ui.config("web", "name")
   911                              or uri.strip('/') or self.repo.root)
   806                              or uri.strip('/') or self.repo.root)
   912 
   807 
   913         self.t = templater(m, common_filters,
   808         self.t = templater.templater(m, templater.common_filters,
   914                            {"url": url,
   809                                      {"url": url,
   915                             "repo": self.reponame,
   810                                       "repo": self.reponame,
   916                             "header": header,
   811                                       "header": header,
   917                             "footer": footer,
   812                                       "footer": footer,
   918                            })
   813                                       })
   919 
   814 
   920         if not req.form.has_key('cmd'):
   815         if not req.form.has_key('cmd'):
   921             req.form['cmd'] = [self.t.cache['default'],]
   816             req.form['cmd'] = [self.t.cache['default'],]
   922 
   817 
   923         if req.form['cmd'][0] == 'changelog':
   818         if req.form['cmd'][0] == 'changelog':