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