##// END OF EJS Templates
hgweb: extract the path logic from updatereqenv and add doctests
Matt Mackall -
r15003:a31b8e03 default
parent child Browse files
Show More
@@ -1,376 +1,399
1 # hgweb/hgwebdir_mod.py - Web interface for a directory of repositories.
1 # hgweb/hgwebdir_mod.py - Web interface for a directory of repositories.
2 #
2 #
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
4 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 import os, re, time
9 import os, re, time
10 from mercurial.i18n import _
10 from mercurial.i18n import _
11 from mercurial import ui, hg, scmutil, util, templater
11 from mercurial import ui, hg, scmutil, util, templater
12 from mercurial import error, encoding
12 from mercurial import error, encoding
13 from common import ErrorResponse, get_mtime, staticfile, paritygen, \
13 from common import ErrorResponse, get_mtime, staticfile, paritygen, \
14 get_contact, HTTP_OK, HTTP_NOT_FOUND, HTTP_SERVER_ERROR
14 get_contact, HTTP_OK, HTTP_NOT_FOUND, HTTP_SERVER_ERROR
15 from hgweb_mod import hgweb
15 from hgweb_mod import hgweb
16 from request import wsgirequest
16 from request import wsgirequest
17 import webutil
17 import webutil
18
18
19 def cleannames(items):
19 def cleannames(items):
20 return [(util.pconvert(name).strip('/'), path) for name, path in items]
20 return [(util.pconvert(name).strip('/'), path) for name, path in items]
21
21
22 def findrepos(paths):
22 def findrepos(paths):
23 repos = []
23 repos = []
24 for prefix, root in cleannames(paths):
24 for prefix, root in cleannames(paths):
25 roothead, roottail = os.path.split(root)
25 roothead, roottail = os.path.split(root)
26 # "foo = /bar/*" makes every subrepo of /bar/ to be
26 # "foo = /bar/*" makes every subrepo of /bar/ to be
27 # mounted as foo/subrepo
27 # mounted as foo/subrepo
28 # and "foo = /bar/**" also recurses into the subdirectories,
28 # and "foo = /bar/**" also recurses into the subdirectories,
29 # remember to use it without working dir.
29 # remember to use it without working dir.
30 try:
30 try:
31 recurse = {'*': False, '**': True}[roottail]
31 recurse = {'*': False, '**': True}[roottail]
32 except KeyError:
32 except KeyError:
33 repos.append((prefix, root))
33 repos.append((prefix, root))
34 continue
34 continue
35 roothead = os.path.normpath(os.path.abspath(roothead))
35 roothead = os.path.normpath(os.path.abspath(roothead))
36 paths = scmutil.walkrepos(roothead, followsym=True, recurse=recurse)
36 paths = scmutil.walkrepos(roothead, followsym=True, recurse=recurse)
37 repos.extend(urlrepos(prefix, roothead, paths))
37 repos.extend(urlrepos(prefix, roothead, paths))
38 return repos
38 return repos
39
39
40 def urlrepos(prefix, roothead, paths):
40 def urlrepos(prefix, roothead, paths):
41 """yield url paths and filesystem paths from a list of repo paths
41 """yield url paths and filesystem paths from a list of repo paths
42
42
43 >>> conv = lambda seq: [(v, util.pconvert(p)) for v,p in seq]
43 >>> conv = lambda seq: [(v, util.pconvert(p)) for v,p in seq]
44 >>> conv(urlrepos('hg', '/opt', ['/opt/r', '/opt/r/r', '/opt']))
44 >>> conv(urlrepos('hg', '/opt', ['/opt/r', '/opt/r/r', '/opt']))
45 [('hg/r', '/opt/r'), ('hg/r/r', '/opt/r/r'), ('hg', '/opt')]
45 [('hg/r', '/opt/r'), ('hg/r/r', '/opt/r/r'), ('hg', '/opt')]
46 >>> conv(urlrepos('', '/opt', ['/opt/r', '/opt/r/r', '/opt']))
46 >>> conv(urlrepos('', '/opt', ['/opt/r', '/opt/r/r', '/opt']))
47 [('r', '/opt/r'), ('r/r', '/opt/r/r'), ('', '/opt')]
47 [('r', '/opt/r'), ('r/r', '/opt/r/r'), ('', '/opt')]
48 """
48 """
49 for path in paths:
49 for path in paths:
50 path = os.path.normpath(path)
50 path = os.path.normpath(path)
51 yield (prefix + '/' +
51 yield (prefix + '/' +
52 util.pconvert(path[len(roothead):]).lstrip('/')).strip('/'), path
52 util.pconvert(path[len(roothead):]).lstrip('/')).strip('/'), path
53
53
54 def geturlcgivars(baseurl, port):
55 """
56 Extract CGI variables from baseurl
57
58 >>> geturlcgivars("http://host.org/base", "80")
59 ('host.org', '80', '/base')
60 >>> geturlcgivars("http://host.org:8000/base", "80")
61 ('host.org', '8000', '/base')
62 >>> geturlcgivars('/base', 8000)
63 ('', '8000', '/base')
64 >>> geturlcgivars("base", '8000')
65 ('', '8000', '/base')
66 >>> geturlcgivars("http://host", '8000')
67 ('host', '8000', '/')
68 >>> geturlcgivars("http://host/", '8000')
69 ('host', '8000', '/')
70 """
71 u = util.url(baseurl)
72 name = u.host or ''
73 if u.port:
74 port = u.port
75 path = u.path or ""
76 if not path.startswith('/'):
77 path = '/' + path
78
79 return name, str(port), path
80
54 class hgwebdir(object):
81 class hgwebdir(object):
55 refreshinterval = 20
82 refreshinterval = 20
56
83
57 def __init__(self, conf, baseui=None):
84 def __init__(self, conf, baseui=None):
58 self.conf = conf
85 self.conf = conf
59 self.baseui = baseui
86 self.baseui = baseui
60 self.lastrefresh = 0
87 self.lastrefresh = 0
61 self.motd = None
88 self.motd = None
62 self.refresh()
89 self.refresh()
63
90
64 def refresh(self):
91 def refresh(self):
65 if self.lastrefresh + self.refreshinterval > time.time():
92 if self.lastrefresh + self.refreshinterval > time.time():
66 return
93 return
67
94
68 if self.baseui:
95 if self.baseui:
69 u = self.baseui.copy()
96 u = self.baseui.copy()
70 else:
97 else:
71 u = ui.ui()
98 u = ui.ui()
72 u.setconfig('ui', 'report_untrusted', 'off')
99 u.setconfig('ui', 'report_untrusted', 'off')
73 u.setconfig('ui', 'interactive', 'off')
100 u.setconfig('ui', 'interactive', 'off')
74
101
75 if not isinstance(self.conf, (dict, list, tuple)):
102 if not isinstance(self.conf, (dict, list, tuple)):
76 map = {'paths': 'hgweb-paths'}
103 map = {'paths': 'hgweb-paths'}
77 if not os.path.exists(self.conf):
104 if not os.path.exists(self.conf):
78 raise util.Abort(_('config file %s not found!') % self.conf)
105 raise util.Abort(_('config file %s not found!') % self.conf)
79 u.readconfig(self.conf, remap=map, trust=True)
106 u.readconfig(self.conf, remap=map, trust=True)
80 paths = []
107 paths = []
81 for name, ignored in u.configitems('hgweb-paths'):
108 for name, ignored in u.configitems('hgweb-paths'):
82 for path in u.configlist('hgweb-paths', name):
109 for path in u.configlist('hgweb-paths', name):
83 paths.append((name, path))
110 paths.append((name, path))
84 elif isinstance(self.conf, (list, tuple)):
111 elif isinstance(self.conf, (list, tuple)):
85 paths = self.conf
112 paths = self.conf
86 elif isinstance(self.conf, dict):
113 elif isinstance(self.conf, dict):
87 paths = self.conf.items()
114 paths = self.conf.items()
88
115
89 repos = findrepos(paths)
116 repos = findrepos(paths)
90 for prefix, root in u.configitems('collections'):
117 for prefix, root in u.configitems('collections'):
91 prefix = util.pconvert(prefix)
118 prefix = util.pconvert(prefix)
92 for path in scmutil.walkrepos(root, followsym=True):
119 for path in scmutil.walkrepos(root, followsym=True):
93 repo = os.path.normpath(path)
120 repo = os.path.normpath(path)
94 name = util.pconvert(repo)
121 name = util.pconvert(repo)
95 if name.startswith(prefix):
122 if name.startswith(prefix):
96 name = name[len(prefix):]
123 name = name[len(prefix):]
97 repos.append((name.lstrip('/'), repo))
124 repos.append((name.lstrip('/'), repo))
98
125
99 self.repos = repos
126 self.repos = repos
100 self.ui = u
127 self.ui = u
101 encoding.encoding = self.ui.config('web', 'encoding',
128 encoding.encoding = self.ui.config('web', 'encoding',
102 encoding.encoding)
129 encoding.encoding)
103 self.style = self.ui.config('web', 'style', 'paper')
130 self.style = self.ui.config('web', 'style', 'paper')
104 self.templatepath = self.ui.config('web', 'templates', None)
131 self.templatepath = self.ui.config('web', 'templates', None)
105 self.stripecount = self.ui.config('web', 'stripes', 1)
132 self.stripecount = self.ui.config('web', 'stripes', 1)
106 if self.stripecount:
133 if self.stripecount:
107 self.stripecount = int(self.stripecount)
134 self.stripecount = int(self.stripecount)
108 self._baseurl = self.ui.config('web', 'baseurl')
135 self._baseurl = self.ui.config('web', 'baseurl')
109 self.lastrefresh = time.time()
136 self.lastrefresh = time.time()
110
137
111 def run(self):
138 def run(self):
112 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
139 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
113 raise RuntimeError("This function is only intended to be "
140 raise RuntimeError("This function is only intended to be "
114 "called while running as a CGI script.")
141 "called while running as a CGI script.")
115 import mercurial.hgweb.wsgicgi as wsgicgi
142 import mercurial.hgweb.wsgicgi as wsgicgi
116 wsgicgi.launch(self)
143 wsgicgi.launch(self)
117
144
118 def __call__(self, env, respond):
145 def __call__(self, env, respond):
119 req = wsgirequest(env, respond)
146 req = wsgirequest(env, respond)
120 return self.run_wsgi(req)
147 return self.run_wsgi(req)
121
148
122 def read_allowed(self, ui, req):
149 def read_allowed(self, ui, req):
123 """Check allow_read and deny_read config options of a repo's ui object
150 """Check allow_read and deny_read config options of a repo's ui object
124 to determine user permissions. By default, with neither option set (or
151 to determine user permissions. By default, with neither option set (or
125 both empty), allow all users to read the repo. There are two ways a
152 both empty), allow all users to read the repo. There are two ways a
126 user can be denied read access: (1) deny_read is not empty, and the
153 user can be denied read access: (1) deny_read is not empty, and the
127 user is unauthenticated or deny_read contains user (or *), and (2)
154 user is unauthenticated or deny_read contains user (or *), and (2)
128 allow_read is not empty and the user is not in allow_read. Return True
155 allow_read is not empty and the user is not in allow_read. Return True
129 if user is allowed to read the repo, else return False."""
156 if user is allowed to read the repo, else return False."""
130
157
131 user = req.env.get('REMOTE_USER')
158 user = req.env.get('REMOTE_USER')
132
159
133 deny_read = ui.configlist('web', 'deny_read', untrusted=True)
160 deny_read = ui.configlist('web', 'deny_read', untrusted=True)
134 if deny_read and (not user or deny_read == ['*'] or user in deny_read):
161 if deny_read and (not user or deny_read == ['*'] or user in deny_read):
135 return False
162 return False
136
163
137 allow_read = ui.configlist('web', 'allow_read', untrusted=True)
164 allow_read = ui.configlist('web', 'allow_read', untrusted=True)
138 # by default, allow reading if no allow_read option has been set
165 # by default, allow reading if no allow_read option has been set
139 if (not allow_read) or (allow_read == ['*']) or (user in allow_read):
166 if (not allow_read) or (allow_read == ['*']) or (user in allow_read):
140 return True
167 return True
141
168
142 return False
169 return False
143
170
144 def run_wsgi(self, req):
171 def run_wsgi(self, req):
145 try:
172 try:
146 try:
173 try:
147 self.refresh()
174 self.refresh()
148
175
149 virtual = req.env.get("PATH_INFO", "").strip('/')
176 virtual = req.env.get("PATH_INFO", "").strip('/')
150 tmpl = self.templater(req)
177 tmpl = self.templater(req)
151 ctype = tmpl('mimetype', encoding=encoding.encoding)
178 ctype = tmpl('mimetype', encoding=encoding.encoding)
152 ctype = templater.stringify(ctype)
179 ctype = templater.stringify(ctype)
153
180
154 # a static file
181 # a static file
155 if virtual.startswith('static/') or 'static' in req.form:
182 if virtual.startswith('static/') or 'static' in req.form:
156 if virtual.startswith('static/'):
183 if virtual.startswith('static/'):
157 fname = virtual[7:]
184 fname = virtual[7:]
158 else:
185 else:
159 fname = req.form['static'][0]
186 fname = req.form['static'][0]
160 static = templater.templatepath('static')
187 static = templater.templatepath('static')
161 return (staticfile(static, fname, req),)
188 return (staticfile(static, fname, req),)
162
189
163 # top-level index
190 # top-level index
164 elif not virtual:
191 elif not virtual:
165 req.respond(HTTP_OK, ctype)
192 req.respond(HTTP_OK, ctype)
166 return self.makeindex(req, tmpl)
193 return self.makeindex(req, tmpl)
167
194
168 # nested indexes and hgwebs
195 # nested indexes and hgwebs
169
196
170 repos = dict(self.repos)
197 repos = dict(self.repos)
171 virtualrepo = virtual
198 virtualrepo = virtual
172 while virtualrepo:
199 while virtualrepo:
173 real = repos.get(virtualrepo)
200 real = repos.get(virtualrepo)
174 if real:
201 if real:
175 req.env['REPO_NAME'] = virtualrepo
202 req.env['REPO_NAME'] = virtualrepo
176 try:
203 try:
177 repo = hg.repository(self.ui, real)
204 repo = hg.repository(self.ui, real)
178 return hgweb(repo).run_wsgi(req)
205 return hgweb(repo).run_wsgi(req)
179 except IOError, inst:
206 except IOError, inst:
180 msg = inst.strerror
207 msg = inst.strerror
181 raise ErrorResponse(HTTP_SERVER_ERROR, msg)
208 raise ErrorResponse(HTTP_SERVER_ERROR, msg)
182 except error.RepoError, inst:
209 except error.RepoError, inst:
183 raise ErrorResponse(HTTP_SERVER_ERROR, str(inst))
210 raise ErrorResponse(HTTP_SERVER_ERROR, str(inst))
184
211
185 up = virtualrepo.rfind('/')
212 up = virtualrepo.rfind('/')
186 if up < 0:
213 if up < 0:
187 break
214 break
188 virtualrepo = virtualrepo[:up]
215 virtualrepo = virtualrepo[:up]
189
216
190 # browse subdirectories
217 # browse subdirectories
191 subdir = virtual + '/'
218 subdir = virtual + '/'
192 if [r for r in repos if r.startswith(subdir)]:
219 if [r for r in repos if r.startswith(subdir)]:
193 req.respond(HTTP_OK, ctype)
220 req.respond(HTTP_OK, ctype)
194 return self.makeindex(req, tmpl, subdir)
221 return self.makeindex(req, tmpl, subdir)
195
222
196 # prefixes not found
223 # prefixes not found
197 req.respond(HTTP_NOT_FOUND, ctype)
224 req.respond(HTTP_NOT_FOUND, ctype)
198 return tmpl("notfound", repo=virtual)
225 return tmpl("notfound", repo=virtual)
199
226
200 except ErrorResponse, err:
227 except ErrorResponse, err:
201 req.respond(err, ctype)
228 req.respond(err, ctype)
202 return tmpl('error', error=err.message or '')
229 return tmpl('error', error=err.message or '')
203 finally:
230 finally:
204 tmpl = None
231 tmpl = None
205
232
206 def makeindex(self, req, tmpl, subdir=""):
233 def makeindex(self, req, tmpl, subdir=""):
207
234
208 def archivelist(ui, nodeid, url):
235 def archivelist(ui, nodeid, url):
209 allowed = ui.configlist("web", "allow_archive", untrusted=True)
236 allowed = ui.configlist("web", "allow_archive", untrusted=True)
210 archives = []
237 archives = []
211 for i in [('zip', '.zip'), ('gz', '.tar.gz'), ('bz2', '.tar.bz2')]:
238 for i in [('zip', '.zip'), ('gz', '.tar.gz'), ('bz2', '.tar.bz2')]:
212 if i[0] in allowed or ui.configbool("web", "allow" + i[0],
239 if i[0] in allowed or ui.configbool("web", "allow" + i[0],
213 untrusted=True):
240 untrusted=True):
214 archives.append({"type" : i[0], "extension": i[1],
241 archives.append({"type" : i[0], "extension": i[1],
215 "node": nodeid, "url": url})
242 "node": nodeid, "url": url})
216 return archives
243 return archives
217
244
218 def rawentries(subdir="", **map):
245 def rawentries(subdir="", **map):
219
246
220 descend = self.ui.configbool('web', 'descend', True)
247 descend = self.ui.configbool('web', 'descend', True)
221 for name, path in self.repos:
248 for name, path in self.repos:
222
249
223 if not name.startswith(subdir):
250 if not name.startswith(subdir):
224 continue
251 continue
225 name = name[len(subdir):]
252 name = name[len(subdir):]
226 if not descend and '/' in name:
253 if not descend and '/' in name:
227 continue
254 continue
228
255
229 u = self.ui.copy()
256 u = self.ui.copy()
230 try:
257 try:
231 u.readconfig(os.path.join(path, '.hg', 'hgrc'))
258 u.readconfig(os.path.join(path, '.hg', 'hgrc'))
232 except Exception, e:
259 except Exception, e:
233 u.warn(_('error reading %s/.hg/hgrc: %s\n') % (path, e))
260 u.warn(_('error reading %s/.hg/hgrc: %s\n') % (path, e))
234 continue
261 continue
235 def get(section, name, default=None):
262 def get(section, name, default=None):
236 return u.config(section, name, default, untrusted=True)
263 return u.config(section, name, default, untrusted=True)
237
264
238 if u.configbool("web", "hidden", untrusted=True):
265 if u.configbool("web", "hidden", untrusted=True):
239 continue
266 continue
240
267
241 if not self.read_allowed(u, req):
268 if not self.read_allowed(u, req):
242 continue
269 continue
243
270
244 parts = [name]
271 parts = [name]
245 if 'PATH_INFO' in req.env:
272 if 'PATH_INFO' in req.env:
246 parts.insert(0, req.env['PATH_INFO'].rstrip('/'))
273 parts.insert(0, req.env['PATH_INFO'].rstrip('/'))
247 if req.env['SCRIPT_NAME']:
274 if req.env['SCRIPT_NAME']:
248 parts.insert(0, req.env['SCRIPT_NAME'])
275 parts.insert(0, req.env['SCRIPT_NAME'])
249 url = re.sub(r'/+', '/', '/'.join(parts) + '/')
276 url = re.sub(r'/+', '/', '/'.join(parts) + '/')
250
277
251 # update time with local timezone
278 # update time with local timezone
252 try:
279 try:
253 r = hg.repository(self.ui, path)
280 r = hg.repository(self.ui, path)
254 except IOError:
281 except IOError:
255 u.warn(_('error accessing repository at %s\n') % path)
282 u.warn(_('error accessing repository at %s\n') % path)
256 continue
283 continue
257 except error.RepoError:
284 except error.RepoError:
258 u.warn(_('error accessing repository at %s\n') % path)
285 u.warn(_('error accessing repository at %s\n') % path)
259 continue
286 continue
260 try:
287 try:
261 d = (get_mtime(r.spath), util.makedate()[1])
288 d = (get_mtime(r.spath), util.makedate()[1])
262 except OSError:
289 except OSError:
263 continue
290 continue
264
291
265 contact = get_contact(get)
292 contact = get_contact(get)
266 description = get("web", "description", "")
293 description = get("web", "description", "")
267 name = get("web", "name", name)
294 name = get("web", "name", name)
268 row = dict(contact=contact or "unknown",
295 row = dict(contact=contact or "unknown",
269 contact_sort=contact.upper() or "unknown",
296 contact_sort=contact.upper() or "unknown",
270 name=name,
297 name=name,
271 name_sort=name,
298 name_sort=name,
272 url=url,
299 url=url,
273 description=description or "unknown",
300 description=description or "unknown",
274 description_sort=description.upper() or "unknown",
301 description_sort=description.upper() or "unknown",
275 lastchange=d,
302 lastchange=d,
276 lastchange_sort=d[1]-d[0],
303 lastchange_sort=d[1]-d[0],
277 archives=archivelist(u, "tip", url))
304 archives=archivelist(u, "tip", url))
278 yield row
305 yield row
279
306
280 sortdefault = None, False
307 sortdefault = None, False
281 def entries(sortcolumn="", descending=False, subdir="", **map):
308 def entries(sortcolumn="", descending=False, subdir="", **map):
282 rows = rawentries(subdir=subdir, **map)
309 rows = rawentries(subdir=subdir, **map)
283
310
284 if sortcolumn and sortdefault != (sortcolumn, descending):
311 if sortcolumn and sortdefault != (sortcolumn, descending):
285 sortkey = '%s_sort' % sortcolumn
312 sortkey = '%s_sort' % sortcolumn
286 rows = sorted(rows, key=lambda x: x[sortkey],
313 rows = sorted(rows, key=lambda x: x[sortkey],
287 reverse=descending)
314 reverse=descending)
288 for row, parity in zip(rows, paritygen(self.stripecount)):
315 for row, parity in zip(rows, paritygen(self.stripecount)):
289 row['parity'] = parity
316 row['parity'] = parity
290 yield row
317 yield row
291
318
292 self.refresh()
319 self.refresh()
293 sortable = ["name", "description", "contact", "lastchange"]
320 sortable = ["name", "description", "contact", "lastchange"]
294 sortcolumn, descending = sortdefault
321 sortcolumn, descending = sortdefault
295 if 'sort' in req.form:
322 if 'sort' in req.form:
296 sortcolumn = req.form['sort'][0]
323 sortcolumn = req.form['sort'][0]
297 descending = sortcolumn.startswith('-')
324 descending = sortcolumn.startswith('-')
298 if descending:
325 if descending:
299 sortcolumn = sortcolumn[1:]
326 sortcolumn = sortcolumn[1:]
300 if sortcolumn not in sortable:
327 if sortcolumn not in sortable:
301 sortcolumn = ""
328 sortcolumn = ""
302
329
303 sort = [("sort_%s" % column,
330 sort = [("sort_%s" % column,
304 "%s%s" % ((not descending and column == sortcolumn)
331 "%s%s" % ((not descending and column == sortcolumn)
305 and "-" or "", column))
332 and "-" or "", column))
306 for column in sortable]
333 for column in sortable]
307
334
308 self.refresh()
335 self.refresh()
309 self.updatereqenv(req.env)
336 self.updatereqenv(req.env)
310
337
311 return tmpl("index", entries=entries, subdir=subdir,
338 return tmpl("index", entries=entries, subdir=subdir,
312 sortcolumn=sortcolumn, descending=descending,
339 sortcolumn=sortcolumn, descending=descending,
313 **dict(sort))
340 **dict(sort))
314
341
315 def templater(self, req):
342 def templater(self, req):
316
343
317 def header(**map):
344 def header(**map):
318 yield tmpl('header', encoding=encoding.encoding, **map)
345 yield tmpl('header', encoding=encoding.encoding, **map)
319
346
320 def footer(**map):
347 def footer(**map):
321 yield tmpl("footer", **map)
348 yield tmpl("footer", **map)
322
349
323 def motd(**map):
350 def motd(**map):
324 if self.motd is not None:
351 if self.motd is not None:
325 yield self.motd
352 yield self.motd
326 else:
353 else:
327 yield config('web', 'motd', '')
354 yield config('web', 'motd', '')
328
355
329 def config(section, name, default=None, untrusted=True):
356 def config(section, name, default=None, untrusted=True):
330 return self.ui.config(section, name, default, untrusted)
357 return self.ui.config(section, name, default, untrusted)
331
358
332 self.updatereqenv(req.env)
359 self.updatereqenv(req.env)
333
360
334 url = req.env.get('SCRIPT_NAME', '')
361 url = req.env.get('SCRIPT_NAME', '')
335 if not url.endswith('/'):
362 if not url.endswith('/'):
336 url += '/'
363 url += '/'
337
364
338 vars = {}
365 vars = {}
339 styles = (
366 styles = (
340 req.form.get('style', [None])[0],
367 req.form.get('style', [None])[0],
341 config('web', 'style'),
368 config('web', 'style'),
342 'paper'
369 'paper'
343 )
370 )
344 style, mapfile = templater.stylemap(styles, self.templatepath)
371 style, mapfile = templater.stylemap(styles, self.templatepath)
345 if style == styles[0]:
372 if style == styles[0]:
346 vars['style'] = style
373 vars['style'] = style
347
374
348 start = url[-1] == '?' and '&' or '?'
375 start = url[-1] == '?' and '&' or '?'
349 sessionvars = webutil.sessionvars(vars, start)
376 sessionvars = webutil.sessionvars(vars, start)
350 logourl = config('web', 'logourl', 'http://mercurial.selenic.com/')
377 logourl = config('web', 'logourl', 'http://mercurial.selenic.com/')
351 logoimg = config('web', 'logoimg', 'hglogo.png')
378 logoimg = config('web', 'logoimg', 'hglogo.png')
352 staticurl = config('web', 'staticurl') or url + 'static/'
379 staticurl = config('web', 'staticurl') or url + 'static/'
353 if not staticurl.endswith('/'):
380 if not staticurl.endswith('/'):
354 staticurl += '/'
381 staticurl += '/'
355
382
356 tmpl = templater.templater(mapfile,
383 tmpl = templater.templater(mapfile,
357 defaults={"header": header,
384 defaults={"header": header,
358 "footer": footer,
385 "footer": footer,
359 "motd": motd,
386 "motd": motd,
360 "url": url,
387 "url": url,
361 "logourl": logourl,
388 "logourl": logourl,
362 "logoimg": logoimg,
389 "logoimg": logoimg,
363 "staticurl": staticurl,
390 "staticurl": staticurl,
364 "sessionvars": sessionvars})
391 "sessionvars": sessionvars})
365 return tmpl
392 return tmpl
366
393
367 def updatereqenv(self, env):
394 def updatereqenv(self, env):
368 if self._baseurl is not None:
395 if self._baseurl is not None:
369 u = util.url(self._baseurl)
396 name, port, path = geturlcgivars(self._baseurl, env['SERVER_PORT'])
370 env['SERVER_NAME'] = u.host
397 env['SERVER_NAME'] = name
371 if u.port:
398 env['SERVER_PORT'] = port
372 env['SERVER_PORT'] = u.port
373 path = u.path or ""
374 if not path.startswith('/'):
375 path = '/' + path
376 env['SCRIPT_NAME'] = path
399 env['SCRIPT_NAME'] = path
General Comments 0
You need to be logged in to leave comments. Login now