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