comparison mercurial/hgweb/hgwebdir_mod.py @ 2356:2db831b33e8f

Final stage of the hgweb split up. hgweb and hgwebdir now have their own modules.
author Eric Hopper <hopper@omnifarious.org>
date Wed, 31 May 2006 10:42:44 -0700
parents mercurial/hgweb/__init__.py@eb08fb4d41e1
children 8819fc1dcf4b
comparison
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)))