##// END OF EJS Templates
hgweb: add some in-code documentation for 'web.view'...
Pierre-Yves David -
r25565:be4dc000 default
parent child Browse files
Show More
@@ -1,402 +1,412 b''
1 # hgweb/hgweb_mod.py - Web interface for a repository.
1 # hgweb/hgweb_mod.py - Web interface for a repository.
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-2007 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005-2007 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
9 import os, re
10 from mercurial import ui, hg, hook, error, encoding, templater, util, repoview
10 from mercurial import ui, hg, hook, error, encoding, templater, util, repoview
11 from mercurial.templatefilters import websub
11 from mercurial.templatefilters import websub
12 from mercurial.i18n import _
12 from mercurial.i18n import _
13 from common import get_stat, ErrorResponse, permhooks, caching
13 from common import get_stat, ErrorResponse, permhooks, caching
14 from common import HTTP_OK, HTTP_NOT_MODIFIED, HTTP_BAD_REQUEST
14 from common import HTTP_OK, HTTP_NOT_MODIFIED, HTTP_BAD_REQUEST
15 from common import HTTP_NOT_FOUND, HTTP_SERVER_ERROR
15 from common import HTTP_NOT_FOUND, HTTP_SERVER_ERROR
16 from request import wsgirequest
16 from request import wsgirequest
17 import webcommands, protocol, webutil
17 import webcommands, protocol, webutil
18
18
19 perms = {
19 perms = {
20 'changegroup': 'pull',
20 'changegroup': 'pull',
21 'changegroupsubset': 'pull',
21 'changegroupsubset': 'pull',
22 'getbundle': 'pull',
22 'getbundle': 'pull',
23 'stream_out': 'pull',
23 'stream_out': 'pull',
24 'listkeys': 'pull',
24 'listkeys': 'pull',
25 'unbundle': 'push',
25 'unbundle': 'push',
26 'pushkey': 'push',
26 'pushkey': 'push',
27 }
27 }
28
28
29 def makebreadcrumb(url, prefix=''):
29 def makebreadcrumb(url, prefix=''):
30 '''Return a 'URL breadcrumb' list
30 '''Return a 'URL breadcrumb' list
31
31
32 A 'URL breadcrumb' is a list of URL-name pairs,
32 A 'URL breadcrumb' is a list of URL-name pairs,
33 corresponding to each of the path items on a URL.
33 corresponding to each of the path items on a URL.
34 This can be used to create path navigation entries.
34 This can be used to create path navigation entries.
35 '''
35 '''
36 if url.endswith('/'):
36 if url.endswith('/'):
37 url = url[:-1]
37 url = url[:-1]
38 if prefix:
38 if prefix:
39 url = '/' + prefix + url
39 url = '/' + prefix + url
40 relpath = url
40 relpath = url
41 if relpath.startswith('/'):
41 if relpath.startswith('/'):
42 relpath = relpath[1:]
42 relpath = relpath[1:]
43
43
44 breadcrumb = []
44 breadcrumb = []
45 urlel = url
45 urlel = url
46 pathitems = [''] + relpath.split('/')
46 pathitems = [''] + relpath.split('/')
47 for pathel in reversed(pathitems):
47 for pathel in reversed(pathitems):
48 if not pathel or not urlel:
48 if not pathel or not urlel:
49 break
49 break
50 breadcrumb.append({'url': urlel, 'name': pathel})
50 breadcrumb.append({'url': urlel, 'name': pathel})
51 urlel = os.path.dirname(urlel)
51 urlel = os.path.dirname(urlel)
52 return reversed(breadcrumb)
52 return reversed(breadcrumb)
53
53
54
54
55 class hgweb(object):
55 class hgweb(object):
56 def __init__(self, repo, name=None, baseui=None):
56 def __init__(self, repo, name=None, baseui=None):
57 if isinstance(repo, str):
57 if isinstance(repo, str):
58 if baseui:
58 if baseui:
59 u = baseui.copy()
59 u = baseui.copy()
60 else:
60 else:
61 u = ui.ui()
61 u = ui.ui()
62 r = hg.repository(u, repo)
62 r = hg.repository(u, repo)
63 else:
63 else:
64 # we trust caller to give us a private copy
64 # we trust caller to give us a private copy
65 r = repo
65 r = repo
66
66
67 r = self._getview(r)
67 r = self._getview(r)
68 r.ui.setconfig('ui', 'report_untrusted', 'off', 'hgweb')
68 r.ui.setconfig('ui', 'report_untrusted', 'off', 'hgweb')
69 r.baseui.setconfig('ui', 'report_untrusted', 'off', 'hgweb')
69 r.baseui.setconfig('ui', 'report_untrusted', 'off', 'hgweb')
70 r.ui.setconfig('ui', 'nontty', 'true', 'hgweb')
70 r.ui.setconfig('ui', 'nontty', 'true', 'hgweb')
71 r.baseui.setconfig('ui', 'nontty', 'true', 'hgweb')
71 r.baseui.setconfig('ui', 'nontty', 'true', 'hgweb')
72 # displaying bundling progress bar while serving feel wrong and may
72 # displaying bundling progress bar while serving feel wrong and may
73 # break some wsgi implementation.
73 # break some wsgi implementation.
74 r.ui.setconfig('progress', 'disable', 'true', 'hgweb')
74 r.ui.setconfig('progress', 'disable', 'true', 'hgweb')
75 r.baseui.setconfig('progress', 'disable', 'true', 'hgweb')
75 r.baseui.setconfig('progress', 'disable', 'true', 'hgweb')
76 self.repo = r
76 self.repo = r
77 hook.redirect(True)
77 hook.redirect(True)
78 self.repostate = ((-1, -1), (-1, -1))
78 self.repostate = ((-1, -1), (-1, -1))
79 self.mtime = -1
79 self.mtime = -1
80 self.reponame = name
80 self.reponame = name
81 self.archives = 'zip', 'gz', 'bz2'
81 self.archives = 'zip', 'gz', 'bz2'
82 self.stripecount = 1
82 self.stripecount = 1
83 # a repo owner may set web.templates in .hg/hgrc to get any file
83 # a repo owner may set web.templates in .hg/hgrc to get any file
84 # readable by the user running the CGI script
84 # readable by the user running the CGI script
85 self.templatepath = self.config('web', 'templates')
85 self.templatepath = self.config('web', 'templates')
86 self.websubtable = self.loadwebsub()
86 self.websubtable = self.loadwebsub()
87
87
88 # The CGI scripts are often run by a user different from the repo owner.
88 # The CGI scripts are often run by a user different from the repo owner.
89 # Trust the settings from the .hg/hgrc files by default.
89 # Trust the settings from the .hg/hgrc files by default.
90 def config(self, section, name, default=None, untrusted=True):
90 def config(self, section, name, default=None, untrusted=True):
91 return self.repo.ui.config(section, name, default,
91 return self.repo.ui.config(section, name, default,
92 untrusted=untrusted)
92 untrusted=untrusted)
93
93
94 def configbool(self, section, name, default=False, untrusted=True):
94 def configbool(self, section, name, default=False, untrusted=True):
95 return self.repo.ui.configbool(section, name, default,
95 return self.repo.ui.configbool(section, name, default,
96 untrusted=untrusted)
96 untrusted=untrusted)
97
97
98 def configlist(self, section, name, default=None, untrusted=True):
98 def configlist(self, section, name, default=None, untrusted=True):
99 return self.repo.ui.configlist(section, name, default,
99 return self.repo.ui.configlist(section, name, default,
100 untrusted=untrusted)
100 untrusted=untrusted)
101
101
102 def _getview(self, repo):
102 def _getview(self, repo):
103 """The 'web.view' config controls changeset filter to hgweb. Possible
104 values are ``served``, ``visible`` and ``all``. Default is ``served``.
105 The ``served`` filter only shows changesets that can be pulled from the
106 hgweb instance. The``visible`` filter includes secret changesets but
107 still excludes "hidden" one.
108
109 See the repoview module for details.
110
111 The option has been around undocumented since Mercurial 2.5, but no
112 user ever asked about it. So we better keep it undocumented for now."""
103 viewconfig = repo.ui.config('web', 'view', 'served',
113 viewconfig = repo.ui.config('web', 'view', 'served',
104 untrusted=True)
114 untrusted=True)
105 if viewconfig == 'all':
115 if viewconfig == 'all':
106 return repo.unfiltered()
116 return repo.unfiltered()
107 elif viewconfig in repoview.filtertable:
117 elif viewconfig in repoview.filtertable:
108 return repo.filtered(viewconfig)
118 return repo.filtered(viewconfig)
109 else:
119 else:
110 return repo.filtered('served')
120 return repo.filtered('served')
111
121
112 def refresh(self, request=None):
122 def refresh(self, request=None):
113 st = get_stat(self.repo.spath)
123 st = get_stat(self.repo.spath)
114 pst = get_stat(self.repo.spath, 'phaseroots')
124 pst = get_stat(self.repo.spath, 'phaseroots')
115 # changelog mtime and size, phaseroots mtime and size
125 # changelog mtime and size, phaseroots mtime and size
116 repostate = ((st.st_mtime, st.st_size), (pst.st_mtime, pst.st_size))
126 repostate = ((st.st_mtime, st.st_size), (pst.st_mtime, pst.st_size))
117 # we need to compare file size in addition to mtime to catch
127 # we need to compare file size in addition to mtime to catch
118 # changes made less than a second ago
128 # changes made less than a second ago
119 if repostate != self.repostate:
129 if repostate != self.repostate:
120 r = hg.repository(self.repo.baseui, self.repo.url())
130 r = hg.repository(self.repo.baseui, self.repo.url())
121 self.repo = self._getview(r)
131 self.repo = self._getview(r)
122 self.maxchanges = int(self.config("web", "maxchanges", 10))
132 self.maxchanges = int(self.config("web", "maxchanges", 10))
123 self.stripecount = int(self.config("web", "stripes", 1))
133 self.stripecount = int(self.config("web", "stripes", 1))
124 self.maxshortchanges = int(self.config("web", "maxshortchanges",
134 self.maxshortchanges = int(self.config("web", "maxshortchanges",
125 60))
135 60))
126 self.maxfiles = int(self.config("web", "maxfiles", 10))
136 self.maxfiles = int(self.config("web", "maxfiles", 10))
127 self.allowpull = self.configbool("web", "allowpull", True)
137 self.allowpull = self.configbool("web", "allowpull", True)
128 encoding.encoding = self.config("web", "encoding",
138 encoding.encoding = self.config("web", "encoding",
129 encoding.encoding)
139 encoding.encoding)
130 # update these last to avoid threads seeing empty settings
140 # update these last to avoid threads seeing empty settings
131 self.repostate = repostate
141 self.repostate = repostate
132 # mtime is needed for ETag
142 # mtime is needed for ETag
133 self.mtime = st.st_mtime
143 self.mtime = st.st_mtime
134 if request:
144 if request:
135 self.repo.ui.environ = request.env
145 self.repo.ui.environ = request.env
136
146
137 def run(self):
147 def run(self):
138 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
148 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
139 raise RuntimeError("This function is only intended to be "
149 raise RuntimeError("This function is only intended to be "
140 "called while running as a CGI script.")
150 "called while running as a CGI script.")
141 import mercurial.hgweb.wsgicgi as wsgicgi
151 import mercurial.hgweb.wsgicgi as wsgicgi
142 wsgicgi.launch(self)
152 wsgicgi.launch(self)
143
153
144 def __call__(self, env, respond):
154 def __call__(self, env, respond):
145 req = wsgirequest(env, respond)
155 req = wsgirequest(env, respond)
146 return self.run_wsgi(req)
156 return self.run_wsgi(req)
147
157
148 def run_wsgi(self, req):
158 def run_wsgi(self, req):
149
159
150 self.refresh(req)
160 self.refresh(req)
151
161
152 # work with CGI variables to create coherent structure
162 # work with CGI variables to create coherent structure
153 # use SCRIPT_NAME, PATH_INFO and QUERY_STRING as well as our REPO_NAME
163 # use SCRIPT_NAME, PATH_INFO and QUERY_STRING as well as our REPO_NAME
154
164
155 req.url = req.env['SCRIPT_NAME']
165 req.url = req.env['SCRIPT_NAME']
156 if not req.url.endswith('/'):
166 if not req.url.endswith('/'):
157 req.url += '/'
167 req.url += '/'
158 if 'REPO_NAME' in req.env:
168 if 'REPO_NAME' in req.env:
159 req.url += req.env['REPO_NAME'] + '/'
169 req.url += req.env['REPO_NAME'] + '/'
160
170
161 if 'PATH_INFO' in req.env:
171 if 'PATH_INFO' in req.env:
162 parts = req.env['PATH_INFO'].strip('/').split('/')
172 parts = req.env['PATH_INFO'].strip('/').split('/')
163 repo_parts = req.env.get('REPO_NAME', '').split('/')
173 repo_parts = req.env.get('REPO_NAME', '').split('/')
164 if parts[:len(repo_parts)] == repo_parts:
174 if parts[:len(repo_parts)] == repo_parts:
165 parts = parts[len(repo_parts):]
175 parts = parts[len(repo_parts):]
166 query = '/'.join(parts)
176 query = '/'.join(parts)
167 else:
177 else:
168 query = req.env['QUERY_STRING'].split('&', 1)[0]
178 query = req.env['QUERY_STRING'].split('&', 1)[0]
169 query = query.split(';', 1)[0]
179 query = query.split(';', 1)[0]
170
180
171 # process this if it's a protocol request
181 # process this if it's a protocol request
172 # protocol bits don't need to create any URLs
182 # protocol bits don't need to create any URLs
173 # and the clients always use the old URL structure
183 # and the clients always use the old URL structure
174
184
175 cmd = req.form.get('cmd', [''])[0]
185 cmd = req.form.get('cmd', [''])[0]
176 if protocol.iscmd(cmd):
186 if protocol.iscmd(cmd):
177 try:
187 try:
178 if query:
188 if query:
179 raise ErrorResponse(HTTP_NOT_FOUND)
189 raise ErrorResponse(HTTP_NOT_FOUND)
180 if cmd in perms:
190 if cmd in perms:
181 self.check_perm(req, perms[cmd])
191 self.check_perm(req, perms[cmd])
182 return protocol.call(self.repo, req, cmd)
192 return protocol.call(self.repo, req, cmd)
183 except ErrorResponse, inst:
193 except ErrorResponse, inst:
184 # A client that sends unbundle without 100-continue will
194 # A client that sends unbundle without 100-continue will
185 # break if we respond early.
195 # break if we respond early.
186 if (cmd == 'unbundle' and
196 if (cmd == 'unbundle' and
187 (req.env.get('HTTP_EXPECT',
197 (req.env.get('HTTP_EXPECT',
188 '').lower() != '100-continue') or
198 '').lower() != '100-continue') or
189 req.env.get('X-HgHttp2', '')):
199 req.env.get('X-HgHttp2', '')):
190 req.drain()
200 req.drain()
191 else:
201 else:
192 req.headers.append(('Connection', 'Close'))
202 req.headers.append(('Connection', 'Close'))
193 req.respond(inst, protocol.HGTYPE,
203 req.respond(inst, protocol.HGTYPE,
194 body='0\n%s\n' % inst.message)
204 body='0\n%s\n' % inst.message)
195 return ''
205 return ''
196
206
197 # translate user-visible url structure to internal structure
207 # translate user-visible url structure to internal structure
198
208
199 args = query.split('/', 2)
209 args = query.split('/', 2)
200 if 'cmd' not in req.form and args and args[0]:
210 if 'cmd' not in req.form and args and args[0]:
201
211
202 cmd = args.pop(0)
212 cmd = args.pop(0)
203 style = cmd.rfind('-')
213 style = cmd.rfind('-')
204 if style != -1:
214 if style != -1:
205 req.form['style'] = [cmd[:style]]
215 req.form['style'] = [cmd[:style]]
206 cmd = cmd[style + 1:]
216 cmd = cmd[style + 1:]
207
217
208 # avoid accepting e.g. style parameter as command
218 # avoid accepting e.g. style parameter as command
209 if util.safehasattr(webcommands, cmd):
219 if util.safehasattr(webcommands, cmd):
210 req.form['cmd'] = [cmd]
220 req.form['cmd'] = [cmd]
211
221
212 if cmd == 'static':
222 if cmd == 'static':
213 req.form['file'] = ['/'.join(args)]
223 req.form['file'] = ['/'.join(args)]
214 else:
224 else:
215 if args and args[0]:
225 if args and args[0]:
216 node = args.pop(0)
226 node = args.pop(0)
217 req.form['node'] = [node]
227 req.form['node'] = [node]
218 if args:
228 if args:
219 req.form['file'] = args
229 req.form['file'] = args
220
230
221 ua = req.env.get('HTTP_USER_AGENT', '')
231 ua = req.env.get('HTTP_USER_AGENT', '')
222 if cmd == 'rev' and 'mercurial' in ua:
232 if cmd == 'rev' and 'mercurial' in ua:
223 req.form['style'] = ['raw']
233 req.form['style'] = ['raw']
224
234
225 if cmd == 'archive':
235 if cmd == 'archive':
226 fn = req.form['node'][0]
236 fn = req.form['node'][0]
227 for type_, spec in self.archive_specs.iteritems():
237 for type_, spec in self.archive_specs.iteritems():
228 ext = spec[2]
238 ext = spec[2]
229 if fn.endswith(ext):
239 if fn.endswith(ext):
230 req.form['node'] = [fn[:-len(ext)]]
240 req.form['node'] = [fn[:-len(ext)]]
231 req.form['type'] = [type_]
241 req.form['type'] = [type_]
232
242
233 # process the web interface request
243 # process the web interface request
234
244
235 try:
245 try:
236 tmpl = self.templater(req)
246 tmpl = self.templater(req)
237 ctype = tmpl('mimetype', encoding=encoding.encoding)
247 ctype = tmpl('mimetype', encoding=encoding.encoding)
238 ctype = templater.stringify(ctype)
248 ctype = templater.stringify(ctype)
239
249
240 # check read permissions non-static content
250 # check read permissions non-static content
241 if cmd != 'static':
251 if cmd != 'static':
242 self.check_perm(req, None)
252 self.check_perm(req, None)
243
253
244 if cmd == '':
254 if cmd == '':
245 req.form['cmd'] = [tmpl.cache['default']]
255 req.form['cmd'] = [tmpl.cache['default']]
246 cmd = req.form['cmd'][0]
256 cmd = req.form['cmd'][0]
247
257
248 if self.configbool('web', 'cache', True):
258 if self.configbool('web', 'cache', True):
249 caching(self, req) # sets ETag header or raises NOT_MODIFIED
259 caching(self, req) # sets ETag header or raises NOT_MODIFIED
250 if cmd not in webcommands.__all__:
260 if cmd not in webcommands.__all__:
251 msg = 'no such method: %s' % cmd
261 msg = 'no such method: %s' % cmd
252 raise ErrorResponse(HTTP_BAD_REQUEST, msg)
262 raise ErrorResponse(HTTP_BAD_REQUEST, msg)
253 elif cmd == 'file' and 'raw' in req.form.get('style', []):
263 elif cmd == 'file' and 'raw' in req.form.get('style', []):
254 self.ctype = ctype
264 self.ctype = ctype
255 content = webcommands.rawfile(self, req, tmpl)
265 content = webcommands.rawfile(self, req, tmpl)
256 else:
266 else:
257 content = getattr(webcommands, cmd)(self, req, tmpl)
267 content = getattr(webcommands, cmd)(self, req, tmpl)
258 req.respond(HTTP_OK, ctype)
268 req.respond(HTTP_OK, ctype)
259
269
260 return content
270 return content
261
271
262 except (error.LookupError, error.RepoLookupError), err:
272 except (error.LookupError, error.RepoLookupError), err:
263 req.respond(HTTP_NOT_FOUND, ctype)
273 req.respond(HTTP_NOT_FOUND, ctype)
264 msg = str(err)
274 msg = str(err)
265 if (util.safehasattr(err, 'name') and
275 if (util.safehasattr(err, 'name') and
266 not isinstance(err, error.ManifestLookupError)):
276 not isinstance(err, error.ManifestLookupError)):
267 msg = 'revision not found: %s' % err.name
277 msg = 'revision not found: %s' % err.name
268 return tmpl('error', error=msg)
278 return tmpl('error', error=msg)
269 except (error.RepoError, error.RevlogError), inst:
279 except (error.RepoError, error.RevlogError), inst:
270 req.respond(HTTP_SERVER_ERROR, ctype)
280 req.respond(HTTP_SERVER_ERROR, ctype)
271 return tmpl('error', error=str(inst))
281 return tmpl('error', error=str(inst))
272 except ErrorResponse, inst:
282 except ErrorResponse, inst:
273 req.respond(inst, ctype)
283 req.respond(inst, ctype)
274 if inst.code == HTTP_NOT_MODIFIED:
284 if inst.code == HTTP_NOT_MODIFIED:
275 # Not allowed to return a body on a 304
285 # Not allowed to return a body on a 304
276 return ['']
286 return ['']
277 return tmpl('error', error=inst.message)
287 return tmpl('error', error=inst.message)
278
288
279 def loadwebsub(self):
289 def loadwebsub(self):
280 websubtable = []
290 websubtable = []
281 websubdefs = self.repo.ui.configitems('websub')
291 websubdefs = self.repo.ui.configitems('websub')
282 # we must maintain interhg backwards compatibility
292 # we must maintain interhg backwards compatibility
283 websubdefs += self.repo.ui.configitems('interhg')
293 websubdefs += self.repo.ui.configitems('interhg')
284 for key, pattern in websubdefs:
294 for key, pattern in websubdefs:
285 # grab the delimiter from the character after the "s"
295 # grab the delimiter from the character after the "s"
286 unesc = pattern[1]
296 unesc = pattern[1]
287 delim = re.escape(unesc)
297 delim = re.escape(unesc)
288
298
289 # identify portions of the pattern, taking care to avoid escaped
299 # identify portions of the pattern, taking care to avoid escaped
290 # delimiters. the replace format and flags are optional, but
300 # delimiters. the replace format and flags are optional, but
291 # delimiters are required.
301 # delimiters are required.
292 match = re.match(
302 match = re.match(
293 r'^s%s(.+)(?:(?<=\\\\)|(?<!\\))%s(.*)%s([ilmsux])*$'
303 r'^s%s(.+)(?:(?<=\\\\)|(?<!\\))%s(.*)%s([ilmsux])*$'
294 % (delim, delim, delim), pattern)
304 % (delim, delim, delim), pattern)
295 if not match:
305 if not match:
296 self.repo.ui.warn(_("websub: invalid pattern for %s: %s\n")
306 self.repo.ui.warn(_("websub: invalid pattern for %s: %s\n")
297 % (key, pattern))
307 % (key, pattern))
298 continue
308 continue
299
309
300 # we need to unescape the delimiter for regexp and format
310 # we need to unescape the delimiter for regexp and format
301 delim_re = re.compile(r'(?<!\\)\\%s' % delim)
311 delim_re = re.compile(r'(?<!\\)\\%s' % delim)
302 regexp = delim_re.sub(unesc, match.group(1))
312 regexp = delim_re.sub(unesc, match.group(1))
303 format = delim_re.sub(unesc, match.group(2))
313 format = delim_re.sub(unesc, match.group(2))
304
314
305 # the pattern allows for 6 regexp flags, so set them if necessary
315 # the pattern allows for 6 regexp flags, so set them if necessary
306 flagin = match.group(3)
316 flagin = match.group(3)
307 flags = 0
317 flags = 0
308 if flagin:
318 if flagin:
309 for flag in flagin.upper():
319 for flag in flagin.upper():
310 flags |= re.__dict__[flag]
320 flags |= re.__dict__[flag]
311
321
312 try:
322 try:
313 regexp = re.compile(regexp, flags)
323 regexp = re.compile(regexp, flags)
314 websubtable.append((regexp, format))
324 websubtable.append((regexp, format))
315 except re.error:
325 except re.error:
316 self.repo.ui.warn(_("websub: invalid regexp for %s: %s\n")
326 self.repo.ui.warn(_("websub: invalid regexp for %s: %s\n")
317 % (key, regexp))
327 % (key, regexp))
318 return websubtable
328 return websubtable
319
329
320 def templater(self, req):
330 def templater(self, req):
321
331
322 # determine scheme, port and server name
332 # determine scheme, port and server name
323 # this is needed to create absolute urls
333 # this is needed to create absolute urls
324
334
325 proto = req.env.get('wsgi.url_scheme')
335 proto = req.env.get('wsgi.url_scheme')
326 if proto == 'https':
336 if proto == 'https':
327 proto = 'https'
337 proto = 'https'
328 default_port = "443"
338 default_port = "443"
329 else:
339 else:
330 proto = 'http'
340 proto = 'http'
331 default_port = "80"
341 default_port = "80"
332
342
333 port = req.env["SERVER_PORT"]
343 port = req.env["SERVER_PORT"]
334 port = port != default_port and (":" + port) or ""
344 port = port != default_port and (":" + port) or ""
335 urlbase = '%s://%s%s' % (proto, req.env['SERVER_NAME'], port)
345 urlbase = '%s://%s%s' % (proto, req.env['SERVER_NAME'], port)
336 logourl = self.config("web", "logourl", "http://mercurial.selenic.com/")
346 logourl = self.config("web", "logourl", "http://mercurial.selenic.com/")
337 logoimg = self.config("web", "logoimg", "hglogo.png")
347 logoimg = self.config("web", "logoimg", "hglogo.png")
338 staticurl = self.config("web", "staticurl") or req.url + 'static/'
348 staticurl = self.config("web", "staticurl") or req.url + 'static/'
339 if not staticurl.endswith('/'):
349 if not staticurl.endswith('/'):
340 staticurl += '/'
350 staticurl += '/'
341
351
342 # some functions for the templater
352 # some functions for the templater
343
353
344 def motd(**map):
354 def motd(**map):
345 yield self.config("web", "motd", "")
355 yield self.config("web", "motd", "")
346
356
347 # figure out which style to use
357 # figure out which style to use
348
358
349 vars = {}
359 vars = {}
350 styles = (
360 styles = (
351 req.form.get('style', [None])[0],
361 req.form.get('style', [None])[0],
352 self.config('web', 'style'),
362 self.config('web', 'style'),
353 'paper',
363 'paper',
354 )
364 )
355 style, mapfile = templater.stylemap(styles, self.templatepath)
365 style, mapfile = templater.stylemap(styles, self.templatepath)
356 if style == styles[0]:
366 if style == styles[0]:
357 vars['style'] = style
367 vars['style'] = style
358
368
359 start = req.url[-1] == '?' and '&' or '?'
369 start = req.url[-1] == '?' and '&' or '?'
360 sessionvars = webutil.sessionvars(vars, start)
370 sessionvars = webutil.sessionvars(vars, start)
361
371
362 if not self.reponame:
372 if not self.reponame:
363 self.reponame = (self.config("web", "name")
373 self.reponame = (self.config("web", "name")
364 or req.env.get('REPO_NAME')
374 or req.env.get('REPO_NAME')
365 or req.url.strip('/') or self.repo.root)
375 or req.url.strip('/') or self.repo.root)
366
376
367 def websubfilter(text):
377 def websubfilter(text):
368 return websub(text, self.websubtable)
378 return websub(text, self.websubtable)
369
379
370 # create the templater
380 # create the templater
371
381
372 tmpl = templater.templater(mapfile,
382 tmpl = templater.templater(mapfile,
373 filters={"websub": websubfilter},
383 filters={"websub": websubfilter},
374 defaults={"url": req.url,
384 defaults={"url": req.url,
375 "logourl": logourl,
385 "logourl": logourl,
376 "logoimg": logoimg,
386 "logoimg": logoimg,
377 "staticurl": staticurl,
387 "staticurl": staticurl,
378 "urlbase": urlbase,
388 "urlbase": urlbase,
379 "repo": self.reponame,
389 "repo": self.reponame,
380 "encoding": encoding.encoding,
390 "encoding": encoding.encoding,
381 "motd": motd,
391 "motd": motd,
382 "sessionvars": sessionvars,
392 "sessionvars": sessionvars,
383 "pathdef": makebreadcrumb(req.url),
393 "pathdef": makebreadcrumb(req.url),
384 "style": style,
394 "style": style,
385 })
395 })
386 return tmpl
396 return tmpl
387
397
388 def archivelist(self, nodeid):
398 def archivelist(self, nodeid):
389 allowed = self.configlist("web", "allow_archive")
399 allowed = self.configlist("web", "allow_archive")
390 for i, spec in self.archive_specs.iteritems():
400 for i, spec in self.archive_specs.iteritems():
391 if i in allowed or self.configbool("web", "allow" + i):
401 if i in allowed or self.configbool("web", "allow" + i):
392 yield {"type" : i, "extension" : spec[2], "node" : nodeid}
402 yield {"type" : i, "extension" : spec[2], "node" : nodeid}
393
403
394 archive_specs = {
404 archive_specs = {
395 'bz2': ('application/x-bzip2', 'tbz2', '.tar.bz2', None),
405 'bz2': ('application/x-bzip2', 'tbz2', '.tar.bz2', None),
396 'gz': ('application/x-gzip', 'tgz', '.tar.gz', None),
406 'gz': ('application/x-gzip', 'tgz', '.tar.gz', None),
397 'zip': ('application/zip', 'zip', '.zip', None),
407 'zip': ('application/zip', 'zip', '.zip', None),
398 }
408 }
399
409
400 def check_perm(self, req, op):
410 def check_perm(self, req, op):
401 for permhook in permhooks:
411 for permhook in permhooks:
402 permhook(self, req, op)
412 permhook(self, req, op)
General Comments 0
You need to be logged in to leave comments. Login now