##// END OF EJS Templates
hgwebdir: check for repo foo before browsing subdirectories of foo/
Brendan Cully -
r4852:edd07be9 default
parent child Browse files
Show More
@@ -1,258 +1,258 b''
1 1 # hgweb/hgwebdir_mod.py - Web interface for a directory of repositories.
2 2 #
3 3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
4 4 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
5 5 #
6 6 # This software may be used and distributed according to the terms
7 7 # of the GNU General Public License, incorporated herein by reference.
8 8
9 9 from mercurial import demandimport; demandimport.enable()
10 10 import os, mimetools, cStringIO
11 11 from mercurial.i18n import gettext as _
12 12 from mercurial import ui, hg, util, templater
13 13 from common import get_mtime, staticfile, style_map, paritygen
14 14 from hgweb_mod import hgweb
15 15
16 16 # This is a stopgap
17 17 class hgwebdir(object):
18 18 def __init__(self, config, parentui=None):
19 19 def cleannames(items):
20 20 return [(name.strip(os.sep), path) for name, path in items]
21 21
22 22 self.parentui = parentui
23 23 self.motd = None
24 24 self.style = None
25 25 self.stripecount = None
26 26 self.repos_sorted = ('name', False)
27 27 if isinstance(config, (list, tuple)):
28 28 self.repos = cleannames(config)
29 29 self.repos_sorted = ('', False)
30 30 elif isinstance(config, dict):
31 31 self.repos = cleannames(config.items())
32 32 self.repos.sort()
33 33 else:
34 34 if isinstance(config, util.configparser):
35 35 cp = config
36 36 else:
37 37 cp = util.configparser()
38 38 cp.read(config)
39 39 self.repos = []
40 40 if cp.has_section('web'):
41 41 if cp.has_option('web', 'motd'):
42 42 self.motd = cp.get('web', 'motd')
43 43 if cp.has_option('web', 'style'):
44 44 self.style = cp.get('web', 'style')
45 45 if cp.has_option('web', 'stripes'):
46 46 self.stripecount = int(cp.get('web', 'stripes'))
47 47 if cp.has_section('paths'):
48 48 self.repos.extend(cleannames(cp.items('paths')))
49 49 if cp.has_section('collections'):
50 50 for prefix, root in cp.items('collections'):
51 51 for path in util.walkrepos(root):
52 52 repo = os.path.normpath(path)
53 53 name = repo
54 54 if name.startswith(prefix):
55 55 name = name[len(prefix):]
56 56 self.repos.append((name.lstrip(os.sep), repo))
57 57 self.repos.sort()
58 58
59 59 def run(self):
60 60 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
61 61 raise RuntimeError("This function is only intended to be called while running as a CGI script.")
62 62 import mercurial.hgweb.wsgicgi as wsgicgi
63 63 from request import wsgiapplication
64 64 def make_web_app():
65 65 return self
66 66 wsgicgi.launch(wsgiapplication(make_web_app))
67 67
68 68 def run_wsgi(self, req):
69 69 def header(**map):
70 70 header_file = cStringIO.StringIO(
71 71 ''.join(tmpl("header", encoding=util._encoding, **map)))
72 72 msg = mimetools.Message(header_file, 0)
73 73 req.header(msg.items())
74 74 yield header_file.read()
75 75
76 76 def footer(**map):
77 77 yield tmpl("footer", **map)
78 78
79 79 def motd(**map):
80 80 if self.motd is not None:
81 81 yield self.motd
82 82 else:
83 83 yield config('web', 'motd', '')
84 84
85 85 parentui = self.parentui or ui.ui(report_untrusted=False)
86 86
87 87 def config(section, name, default=None, untrusted=True):
88 88 return parentui.config(section, name, default, untrusted)
89 89
90 90 url = req.env['REQUEST_URI'].split('?')[0]
91 91 if not url.endswith('/'):
92 92 url += '/'
93 93 pathinfo = req.env.get('PATH_INFO', '').strip('/') + '/'
94 94 base = url[:len(url) - len(pathinfo)]
95 95 if not base.endswith('/'):
96 96 base += '/'
97 97
98 98 staticurl = config('web', 'staticurl') or base + 'static/'
99 99 if not staticurl.endswith('/'):
100 100 staticurl += '/'
101 101
102 102 style = self.style
103 103 if style is None:
104 104 style = config('web', 'style', '')
105 105 if req.form.has_key('style'):
106 106 style = req.form['style'][0]
107 107 if self.stripecount is None:
108 108 self.stripecount = int(config('web', 'stripes', 1))
109 109 mapfile = style_map(templater.templatepath(), style)
110 110 tmpl = templater.templater(mapfile, templater.common_filters,
111 111 defaults={"header": header,
112 112 "footer": footer,
113 113 "motd": motd,
114 114 "url": url,
115 115 "staticurl": staticurl})
116 116
117 117 def archivelist(ui, nodeid, url):
118 118 allowed = ui.configlist("web", "allow_archive", untrusted=True)
119 119 for i in [('zip', '.zip'), ('gz', '.tar.gz'), ('bz2', '.tar.bz2')]:
120 120 if i[0] in allowed or ui.configbool("web", "allow" + i[0],
121 121 untrusted=True):
122 122 yield {"type" : i[0], "extension": i[1],
123 123 "node": nodeid, "url": url}
124 124
125 125 def entries(sortcolumn="", descending=False, subdir="", **map):
126 126 def sessionvars(**map):
127 127 fields = []
128 128 if req.form.has_key('style'):
129 129 style = req.form['style'][0]
130 130 if style != get('web', 'style', ''):
131 131 fields.append(('style', style))
132 132
133 133 separator = url[-1] == '?' and ';' or '?'
134 134 for name, value in fields:
135 135 yield dict(name=name, value=value, separator=separator)
136 136 separator = ';'
137 137
138 138 rows = []
139 139 parity = paritygen(self.stripecount)
140 140 for name, path in self.repos:
141 141 if not name.startswith(subdir):
142 142 continue
143 143 name = name[len(subdir):]
144 144
145 145 u = ui.ui(parentui=parentui)
146 146 try:
147 147 u.readconfig(os.path.join(path, '.hg', 'hgrc'))
148 148 except IOError:
149 149 pass
150 150 def get(section, name, default=None):
151 151 return u.config(section, name, default, untrusted=True)
152 152
153 153 if u.configbool("web", "hidden", untrusted=True):
154 154 continue
155 155
156 156 url = ('/'.join([req.env["REQUEST_URI"].split('?')[0], name])
157 157 .replace("//", "/")) + '/'
158 158
159 159 # update time with local timezone
160 160 try:
161 161 d = (get_mtime(path), util.makedate()[1])
162 162 except OSError:
163 163 continue
164 164
165 165 contact = (get("ui", "username") or # preferred
166 166 get("web", "contact") or # deprecated
167 167 get("web", "author", "")) # also
168 168 description = get("web", "description", "")
169 169 name = get("web", "name", name)
170 170 row = dict(contact=contact or "unknown",
171 171 contact_sort=contact.upper() or "unknown",
172 172 name=name,
173 173 name_sort=name,
174 174 url=url,
175 175 description=description or "unknown",
176 176 description_sort=description.upper() or "unknown",
177 177 lastchange=d,
178 178 lastchange_sort=d[1]-d[0],
179 179 sessionvars=sessionvars,
180 180 archives=archivelist(u, "tip", url))
181 181 if (not sortcolumn
182 182 or (sortcolumn, descending) == self.repos_sorted):
183 183 # fast path for unsorted output
184 184 row['parity'] = parity.next()
185 185 yield row
186 186 else:
187 187 rows.append((row["%s_sort" % sortcolumn], row))
188 188 if rows:
189 189 rows.sort()
190 190 if descending:
191 191 rows.reverse()
192 192 for key, row in rows:
193 193 row['parity'] = parity.next()
194 194 yield row
195 195
196 196 def makeindex(req, subdir=""):
197 197 sortable = ["name", "description", "contact", "lastchange"]
198 198 sortcolumn, descending = self.repos_sorted
199 199 if req.form.has_key('sort'):
200 200 sortcolumn = req.form['sort'][0]
201 201 descending = sortcolumn.startswith('-')
202 202 if descending:
203 203 sortcolumn = sortcolumn[1:]
204 204 if sortcolumn not in sortable:
205 205 sortcolumn = ""
206 206
207 207 sort = [("sort_%s" % column,
208 208 "%s%s" % ((not descending and column == sortcolumn)
209 209 and "-" or "", column))
210 210 for column in sortable]
211 211 req.write(tmpl("index", entries=entries, subdir=subdir,
212 212 sortcolumn=sortcolumn, descending=descending,
213 213 **dict(sort)))
214 214
215 215 try:
216 216 virtual = req.env.get("PATH_INFO", "").strip('/')
217 217 if virtual.startswith('static/'):
218 218 static = os.path.join(templater.templatepath(), 'static')
219 219 fname = virtual[7:]
220 220 req.write(staticfile(static, fname, req) or
221 221 tmpl('error', error='%r not found' % fname))
222 222 elif virtual:
223 223 repos = dict(self.repos)
224 # browse subdirectories
225 subdir = virtual + '/'
226 if [r for r in repos if r.startswith(subdir)]:
227 makeindex(req, subdir)
228 return
229
230 224 while virtual:
231 225 real = repos.get(virtual)
232 226 if real:
233 break
227 req.env['REPO_NAME'] = virtual
228 try:
229 repo = hg.repository(parentui, real)
230 hgweb(repo).run_wsgi(req)
231 except IOError, inst:
232 req.write(tmpl("error", error=inst.strerror))
233 except hg.RepoError, inst:
234 req.write(tmpl("error", error=str(inst)))
235 return
236
237 # browse subdirectories
238 subdir = virtual + '/'
239 if [r for r in repos if r.startswith(subdir)]:
240 makeindex(req, subdir)
241 return
242
234 243 up = virtual.rfind('/')
235 244 if up < 0:
236 245 break
237 246 virtual = virtual[:up]
238 if real:
239 req.env['REPO_NAME'] = virtual
240 try:
241 repo = hg.repository(parentui, real)
242 hgweb(repo).run_wsgi(req)
243 except IOError, inst:
244 req.write(tmpl("error", error=inst.strerror))
245 except hg.RepoError, inst:
246 req.write(tmpl("error", error=str(inst)))
247 else:
248 req.write(tmpl("notfound", repo=virtual))
247
248 req.write(tmpl("notfound", repo=virtual))
249 249 else:
250 250 if req.form.has_key('static'):
251 251 static = os.path.join(templater.templatepath(), "static")
252 252 fname = req.form['static'][0]
253 253 req.write(staticfile(static, fname, req)
254 254 or tmpl("error", error="%r not found" % fname))
255 255 else:
256 256 makeindex(req)
257 257 finally:
258 258 tmpl = None
General Comments 0
You need to be logged in to leave comments. Login now