mercurial/hgweb/hgwebdir_mod.py
changeset 2356 2db831b33e8f
parent 2355 eb08fb4d41e1
child 2358 8819fc1dcf4b
equal deleted inserted replaced
2355:eb08fb4d41e1 2356:2db831b33e8f
       
     1 # hgweb.py - web interface to a mercurial repository
       
     2 #
       
     3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
       
     4 # Copyright 2005 Matt Mackall <mpm@selenic.com>
       
     5 #
       
     6 # This software may be used and distributed according to the terms
       
     7 # of the GNU General Public License, incorporated herein by reference.
       
     8 
       
     9 import os
       
    10 from mercurial.demandload import demandload
       
    11 demandload(globals(), "ConfigParser")
       
    12 demandload(globals(), "mercurial:ui,hg,util,templater")
       
    13 demandload(globals(), "mercurial.hgweb.request:hgrequest")
       
    14 from mercurial.i18n import gettext as _
       
    15 
       
    16 # This is a stopgap
       
    17 class hgwebdir(object):
       
    18     def __init__(self, config):
       
    19         def cleannames(items):
       
    20             return [(name.strip(os.sep), path) for name, path in items]
       
    21 
       
    22         self.motd = ""
       
    23         self.repos_sorted = ('name', False)
       
    24         if isinstance(config, (list, tuple)):
       
    25             self.repos = cleannames(config)
       
    26             self.repos_sorted = ('', False)
       
    27         elif isinstance(config, dict):
       
    28             self.repos = cleannames(config.items())
       
    29             self.repos.sort()
       
    30         else:
       
    31             cp = ConfigParser.SafeConfigParser()
       
    32             cp.read(config)
       
    33             self.repos = []
       
    34             if cp.has_section('web') and cp.has_option('web', 'motd'):
       
    35                 self.motd = cp.get('web', 'motd')
       
    36             if cp.has_section('paths'):
       
    37                 self.repos.extend(cleannames(cp.items('paths')))
       
    38             if cp.has_section('collections'):
       
    39                 for prefix, root in cp.items('collections'):
       
    40                     for path in util.walkrepos(root):
       
    41                         repo = os.path.normpath(path)
       
    42                         name = repo
       
    43                         if name.startswith(prefix):
       
    44                             name = name[len(prefix):]
       
    45                         self.repos.append((name.lstrip(os.sep), repo))
       
    46             self.repos.sort()
       
    47 
       
    48     def run(self, req=hgrequest()):
       
    49         def header(**map):
       
    50             yield tmpl("header", **map)
       
    51 
       
    52         def footer(**map):
       
    53             yield tmpl("footer", motd=self.motd, **map)
       
    54 
       
    55         m = os.path.join(templater.templatepath(), "map")
       
    56         tmpl = templater.templater(m, templater.common_filters,
       
    57                                    defaults={"header": header,
       
    58                                              "footer": footer})
       
    59 
       
    60         def archivelist(ui, nodeid, url):
       
    61             for i in ['zip', 'gz', 'bz2']:
       
    62                 if ui.configbool("web", "allow" + i, False):
       
    63                     yield {"type" : i, "node": nodeid, "url": url}
       
    64 
       
    65         def entries(sortcolumn="", descending=False, **map):
       
    66             rows = []
       
    67             parity = 0
       
    68             for name, path in self.repos:
       
    69                 u = ui.ui()
       
    70                 try:
       
    71                     u.readconfig(os.path.join(path, '.hg', 'hgrc'))
       
    72                 except IOError:
       
    73                     pass
       
    74                 get = u.config
       
    75 
       
    76                 url = ('/'.join([req.env["REQUEST_URI"].split('?')[0], name])
       
    77                        .replace("//", "/"))
       
    78 
       
    79                 # update time with local timezone
       
    80                 try:
       
    81                     d = (get_mtime(path), util.makedate()[1])
       
    82                 except OSError:
       
    83                     continue
       
    84 
       
    85                 contact = (get("ui", "username") or # preferred
       
    86                            get("web", "contact") or # deprecated
       
    87                            get("web", "author", "")) # also
       
    88                 description = get("web", "description", "")
       
    89                 name = get("web", "name", name)
       
    90                 row = dict(contact=contact or "unknown",
       
    91                            contact_sort=contact.upper() or "unknown",
       
    92                            name=name,
       
    93                            name_sort=name,
       
    94                            url=url,
       
    95                            description=description or "unknown",
       
    96                            description_sort=description.upper() or "unknown",
       
    97                            lastchange=d,
       
    98                            lastchange_sort=d[1]-d[0],
       
    99                            archives=archivelist(u, "tip", url))
       
   100                 if (not sortcolumn
       
   101                     or (sortcolumn, descending) == self.repos_sorted):
       
   102                     # fast path for unsorted output
       
   103                     row['parity'] = parity
       
   104                     parity = 1 - parity
       
   105                     yield row
       
   106                 else:
       
   107                     rows.append((row["%s_sort" % sortcolumn], row))
       
   108             if rows:
       
   109                 rows.sort()
       
   110                 if descending:
       
   111                     rows.reverse()
       
   112                 for key, row in rows:
       
   113                     row['parity'] = parity
       
   114                     parity = 1 - parity
       
   115                     yield row
       
   116 
       
   117         virtual = req.env.get("PATH_INFO", "").strip('/')
       
   118         if virtual:
       
   119             real = dict(self.repos).get(virtual)
       
   120             if real:
       
   121                 try:
       
   122                     hgweb(real).run(req)
       
   123                 except IOError, inst:
       
   124                     req.write(tmpl("error", error=inst.strerror))
       
   125                 except hg.RepoError, inst:
       
   126                     req.write(tmpl("error", error=str(inst)))
       
   127             else:
       
   128                 req.write(tmpl("notfound", repo=virtual))
       
   129         else:
       
   130             if req.form.has_key('static'):
       
   131                 static = os.path.join(templater.templatepath(), "static")
       
   132                 fname = req.form['static'][0]
       
   133                 req.write(staticfile(static, fname)
       
   134                           or tmpl("error", error="%r not found" % fname))
       
   135             else:
       
   136                 sortable = ["name", "description", "contact", "lastchange"]
       
   137                 sortcolumn, descending = self.repos_sorted
       
   138                 if req.form.has_key('sort'):
       
   139                     sortcolumn = req.form['sort'][0]
       
   140                     descending = sortcolumn.startswith('-')
       
   141                     if descending:
       
   142                         sortcolumn = sortcolumn[1:]
       
   143                     if sortcolumn not in sortable:
       
   144                         sortcolumn = ""
       
   145 
       
   146                 sort = [("sort_%s" % column,
       
   147                          "%s%s" % ((not descending and column == sortcolumn)
       
   148                                    and "-" or "", column))
       
   149                         for column in sortable]
       
   150                 req.write(tmpl("index", entries=entries,
       
   151                                sortcolumn=sortcolumn, descending=descending,
       
   152                                **dict(sort)))