##// END OF EJS Templates
hgweb: web.encoding should override encoding.encoding (issue1183)
Matt Mackall -
r8859:580a79dd default
parent child Browse files
Show More
@@ -1,312 +1,313 b''
1 1 # hgweb/hgweb_mod.py - Web interface for a repository.
2 2 #
3 3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
4 4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 5 #
6 6 # This software may be used and distributed according to the terms of the
7 7 # GNU General Public License version 2, incorporated herein by reference.
8 8
9 9 import os
10 10 from mercurial import ui, hg, hook, error, encoding, templater
11 11 from common import get_mtime, ErrorResponse
12 12 from common import HTTP_OK, HTTP_BAD_REQUEST, HTTP_NOT_FOUND, HTTP_SERVER_ERROR
13 13 from common import HTTP_UNAUTHORIZED, HTTP_METHOD_NOT_ALLOWED
14 14 from request import wsgirequest
15 15 import webcommands, protocol, webutil
16 16
17 17 perms = {
18 18 'changegroup': 'pull',
19 19 'changegroupsubset': 'pull',
20 20 'unbundle': 'push',
21 21 'stream_out': 'pull',
22 22 }
23 23
24 24 class hgweb(object):
25 25 def __init__(self, repo, name=None):
26 26 if isinstance(repo, str):
27 27 u = ui.ui()
28 28 u.setconfig('ui', 'report_untrusted', 'off')
29 29 u.setconfig('ui', 'interactive', 'off')
30 30 self.repo = hg.repository(u, repo)
31 31 else:
32 32 self.repo = repo
33 33
34 34 hook.redirect(True)
35 35 self.mtime = -1
36 36 self.reponame = name
37 37 self.archives = 'zip', 'gz', 'bz2'
38 38 self.stripecount = 1
39 39 # a repo owner may set web.templates in .hg/hgrc to get any file
40 40 # readable by the user running the CGI script
41 41 self.templatepath = self.config('web', 'templates')
42 42
43 43 # The CGI scripts are often run by a user different from the repo owner.
44 44 # Trust the settings from the .hg/hgrc files by default.
45 45 def config(self, section, name, default=None, untrusted=True):
46 46 return self.repo.ui.config(section, name, default,
47 47 untrusted=untrusted)
48 48
49 49 def configbool(self, section, name, default=False, untrusted=True):
50 50 return self.repo.ui.configbool(section, name, default,
51 51 untrusted=untrusted)
52 52
53 53 def configlist(self, section, name, default=None, untrusted=True):
54 54 return self.repo.ui.configlist(section, name, default,
55 55 untrusted=untrusted)
56 56
57 57 def refresh(self):
58 58 mtime = get_mtime(self.repo.root)
59 59 if mtime != self.mtime:
60 60 self.mtime = mtime
61 61 self.repo = hg.repository(self.repo.ui, self.repo.root)
62 62 self.maxchanges = int(self.config("web", "maxchanges", 10))
63 63 self.stripecount = int(self.config("web", "stripes", 1))
64 64 self.maxshortchanges = int(self.config("web", "maxshortchanges", 60))
65 65 self.maxfiles = int(self.config("web", "maxfiles", 10))
66 66 self.allowpull = self.configbool("web", "allowpull", True)
67 self.encoding = self.config("web", "encoding", encoding.encoding)
67 encoding.encoding = self.config("web", "encoding",
68 encoding.encoding)
68 69
69 70 def run(self):
70 71 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
71 72 raise RuntimeError("This function is only intended to be "
72 73 "called while running as a CGI script.")
73 74 import mercurial.hgweb.wsgicgi as wsgicgi
74 75 wsgicgi.launch(self)
75 76
76 77 def __call__(self, env, respond):
77 78 req = wsgirequest(env, respond)
78 79 return self.run_wsgi(req)
79 80
80 81 def run_wsgi(self, req):
81 82
82 83 self.refresh()
83 84
84 85 # process this if it's a protocol request
85 86 # protocol bits don't need to create any URLs
86 87 # and the clients always use the old URL structure
87 88
88 89 cmd = req.form.get('cmd', [''])[0]
89 90 if cmd and cmd in protocol.__all__:
90 91 try:
91 92 if cmd in perms:
92 93 try:
93 94 self.check_perm(req, perms[cmd])
94 95 except ErrorResponse, inst:
95 96 if cmd == 'unbundle':
96 97 req.drain()
97 98 raise
98 99 method = getattr(protocol, cmd)
99 100 return method(self.repo, req)
100 101 except ErrorResponse, inst:
101 102 req.respond(inst, protocol.HGTYPE)
102 103 if not inst.message:
103 104 return []
104 105 return '0\n%s\n' % inst.message,
105 106
106 107 # work with CGI variables to create coherent structure
107 108 # use SCRIPT_NAME, PATH_INFO and QUERY_STRING as well as our REPO_NAME
108 109
109 110 req.url = req.env['SCRIPT_NAME']
110 111 if not req.url.endswith('/'):
111 112 req.url += '/'
112 113 if 'REPO_NAME' in req.env:
113 114 req.url += req.env['REPO_NAME'] + '/'
114 115
115 116 if 'PATH_INFO' in req.env:
116 117 parts = req.env['PATH_INFO'].strip('/').split('/')
117 118 repo_parts = req.env.get('REPO_NAME', '').split('/')
118 119 if parts[:len(repo_parts)] == repo_parts:
119 120 parts = parts[len(repo_parts):]
120 121 query = '/'.join(parts)
121 122 else:
122 123 query = req.env['QUERY_STRING'].split('&', 1)[0]
123 124 query = query.split(';', 1)[0]
124 125
125 126 # translate user-visible url structure to internal structure
126 127
127 128 args = query.split('/', 2)
128 129 if 'cmd' not in req.form and args and args[0]:
129 130
130 131 cmd = args.pop(0)
131 132 style = cmd.rfind('-')
132 133 if style != -1:
133 134 req.form['style'] = [cmd[:style]]
134 135 cmd = cmd[style+1:]
135 136
136 137 # avoid accepting e.g. style parameter as command
137 138 if hasattr(webcommands, cmd):
138 139 req.form['cmd'] = [cmd]
139 140 else:
140 141 cmd = ''
141 142
142 143 if cmd == 'static':
143 144 req.form['file'] = ['/'.join(args)]
144 145 else:
145 146 if args and args[0]:
146 147 node = args.pop(0)
147 148 req.form['node'] = [node]
148 149 if args:
149 150 req.form['file'] = args
150 151
151 152 if cmd == 'archive':
152 153 fn = req.form['node'][0]
153 154 for type_, spec in self.archive_specs.iteritems():
154 155 ext = spec[2]
155 156 if fn.endswith(ext):
156 157 req.form['node'] = [fn[:-len(ext)]]
157 158 req.form['type'] = [type_]
158 159
159 160 # process the web interface request
160 161
161 162 try:
162 163 tmpl = self.templater(req)
163 ctype = tmpl('mimetype', encoding=self.encoding)
164 ctype = tmpl('mimetype', encoding=encoding.encoding)
164 165 ctype = templater.stringify(ctype)
165 166
166 167 # check read permissions non-static content
167 168 if cmd != 'static':
168 169 self.check_perm(req, None)
169 170
170 171 if cmd == '':
171 172 req.form['cmd'] = [tmpl.cache['default']]
172 173 cmd = req.form['cmd'][0]
173 174
174 175 if cmd not in webcommands.__all__:
175 176 msg = 'no such method: %s' % cmd
176 177 raise ErrorResponse(HTTP_BAD_REQUEST, msg)
177 178 elif cmd == 'file' and 'raw' in req.form.get('style', []):
178 179 self.ctype = ctype
179 180 content = webcommands.rawfile(self, req, tmpl)
180 181 else:
181 182 content = getattr(webcommands, cmd)(self, req, tmpl)
182 183 req.respond(HTTP_OK, ctype)
183 184
184 185 return content
185 186
186 187 except error.LookupError, err:
187 188 req.respond(HTTP_NOT_FOUND, ctype)
188 189 msg = str(err)
189 190 if 'manifest' not in msg:
190 191 msg = 'revision not found: %s' % err.name
191 192 return tmpl('error', error=msg)
192 193 except (error.RepoError, error.RevlogError), inst:
193 194 req.respond(HTTP_SERVER_ERROR, ctype)
194 195 return tmpl('error', error=str(inst))
195 196 except ErrorResponse, inst:
196 197 req.respond(inst, ctype)
197 198 return tmpl('error', error=inst.message)
198 199
199 200 def templater(self, req):
200 201
201 202 # determine scheme, port and server name
202 203 # this is needed to create absolute urls
203 204
204 205 proto = req.env.get('wsgi.url_scheme')
205 206 if proto == 'https':
206 207 proto = 'https'
207 208 default_port = "443"
208 209 else:
209 210 proto = 'http'
210 211 default_port = "80"
211 212
212 213 port = req.env["SERVER_PORT"]
213 214 port = port != default_port and (":" + port) or ""
214 215 urlbase = '%s://%s%s' % (proto, req.env['SERVER_NAME'], port)
215 216 staticurl = self.config("web", "staticurl") or req.url + 'static/'
216 217 if not staticurl.endswith('/'):
217 218 staticurl += '/'
218 219
219 220 # some functions for the templater
220 221
221 222 def header(**map):
222 yield tmpl('header', encoding=self.encoding, **map)
223 yield tmpl('header', encoding=encoding.encoding, **map)
223 224
224 225 def footer(**map):
225 226 yield tmpl("footer", **map)
226 227
227 228 def motd(**map):
228 229 yield self.config("web", "motd", "")
229 230
230 231 # figure out which style to use
231 232
232 233 vars = {}
233 234 style = self.config("web", "style", "paper")
234 235 if 'style' in req.form:
235 236 style = req.form['style'][0]
236 237 vars['style'] = style
237 238
238 239 start = req.url[-1] == '?' and '&' or '?'
239 240 sessionvars = webutil.sessionvars(vars, start)
240 241 mapfile = templater.stylemap(style, self.templatepath)
241 242
242 243 if not self.reponame:
243 244 self.reponame = (self.config("web", "name")
244 245 or req.env.get('REPO_NAME')
245 246 or req.url.strip('/') or self.repo.root)
246 247
247 248 # create the templater
248 249
249 250 tmpl = templater.templater(mapfile,
250 251 defaults={"url": req.url,
251 252 "staticurl": staticurl,
252 253 "urlbase": urlbase,
253 254 "repo": self.reponame,
254 255 "header": header,
255 256 "footer": footer,
256 257 "motd": motd,
257 258 "sessionvars": sessionvars
258 259 })
259 260 return tmpl
260 261
261 262 def archivelist(self, nodeid):
262 263 allowed = self.configlist("web", "allow_archive")
263 264 for i, spec in self.archive_specs.iteritems():
264 265 if i in allowed or self.configbool("web", "allow" + i):
265 266 yield {"type" : i, "extension" : spec[2], "node" : nodeid}
266 267
267 268 archive_specs = {
268 269 'bz2': ('application/x-tar', 'tbz2', '.tar.bz2', None),
269 270 'gz': ('application/x-tar', 'tgz', '.tar.gz', None),
270 271 'zip': ('application/zip', 'zip', '.zip', None),
271 272 }
272 273
273 274 def check_perm(self, req, op):
274 275 '''Check permission for operation based on request data (including
275 276 authentication info). Return if op allowed, else raise an ErrorResponse
276 277 exception.'''
277 278
278 279 user = req.env.get('REMOTE_USER')
279 280
280 281 deny_read = self.configlist('web', 'deny_read')
281 282 if deny_read and (not user or deny_read == ['*'] or user in deny_read):
282 283 raise ErrorResponse(HTTP_UNAUTHORIZED, 'read not authorized')
283 284
284 285 allow_read = self.configlist('web', 'allow_read')
285 286 result = (not allow_read) or (allow_read == ['*'])
286 287 if not (result or user in allow_read):
287 288 raise ErrorResponse(HTTP_UNAUTHORIZED, 'read not authorized')
288 289
289 290 if op == 'pull' and not self.allowpull:
290 291 raise ErrorResponse(HTTP_UNAUTHORIZED, 'pull not authorized')
291 292 elif op == 'pull' or op is None: # op is None for interface requests
292 293 return
293 294
294 295 # enforce that you can only push using POST requests
295 296 if req.env['REQUEST_METHOD'] != 'POST':
296 297 msg = 'push requires POST request'
297 298 raise ErrorResponse(HTTP_METHOD_NOT_ALLOWED, msg)
298 299
299 300 # require ssl by default for pushing, auth info cannot be sniffed
300 301 # and replayed
301 302 scheme = req.env.get('wsgi.url_scheme')
302 303 if self.configbool('web', 'push_ssl', True) and scheme != 'https':
303 304 raise ErrorResponse(HTTP_OK, 'ssl required')
304 305
305 306 deny = self.configlist('web', 'deny_push')
306 307 if deny and (not user or deny == ['*'] or user in deny):
307 308 raise ErrorResponse(HTTP_UNAUTHORIZED, 'push not authorized')
308 309
309 310 allow = self.configlist('web', 'allow_push')
310 311 result = allow and (allow == ['*'] or user in allow)
311 312 if not result:
312 313 raise ErrorResponse(HTTP_UNAUTHORIZED, 'push not authorized')
@@ -1,329 +1,331 b''
1 1 # hgweb/hgwebdir_mod.py - Web interface for a directory of repositories.
2 2 #
3 3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
4 4 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
5 5 #
6 6 # This software may be used and distributed according to the terms of the
7 7 # GNU General Public License version 2, incorporated herein by reference.
8 8
9 9 import os, time
10 10 from mercurial.i18n import _
11 11 from mercurial import ui, hg, util, templater
12 12 from mercurial import error, encoding
13 13 from common import ErrorResponse, get_mtime, staticfile, paritygen,\
14 14 get_contact, HTTP_OK, HTTP_NOT_FOUND, HTTP_SERVER_ERROR
15 15 from hgweb_mod import hgweb
16 16 from request import wsgirequest
17 17 import webutil
18 18
19 19 def cleannames(items):
20 20 return [(util.pconvert(name).strip('/'), path) for name, path in items]
21 21
22 22 def findrepos(paths):
23 23 repos = {}
24 24 for prefix, root in cleannames(paths):
25 25 roothead, roottail = os.path.split(root)
26 26 # "foo = /bar/*" makes every subrepo of /bar/ to be
27 27 # mounted as foo/subrepo
28 28 # and "foo = /bar/**" also recurses into the subdirectories,
29 29 # remember to use it without working dir.
30 30 try:
31 31 recurse = {'*': False, '**': True}[roottail]
32 32 except KeyError:
33 33 repos[prefix] = root
34 34 continue
35 35 roothead = os.path.normpath(roothead)
36 36 for path in util.walkrepos(roothead, followsym=True, recurse=recurse):
37 37 path = os.path.normpath(path)
38 38 name = util.pconvert(path[len(roothead):]).strip('/')
39 39 if prefix:
40 40 name = prefix + '/' + name
41 41 repos[name] = path
42 42 return repos.items()
43 43
44 44 class hgwebdir(object):
45 45 refreshinterval = 20
46 46
47 47 def __init__(self, conf, baseui=None):
48 48 self.conf = conf
49 49 self.baseui = baseui
50 50 self.lastrefresh = 0
51 51 self.refresh()
52 52
53 53 def refresh(self):
54 54 if self.lastrefresh + self.refreshinterval > time.time():
55 55 return
56 56
57 57 if self.baseui:
58 58 self.ui = self.baseui.copy()
59 59 else:
60 60 self.ui = ui.ui()
61 61 self.ui.setconfig('ui', 'report_untrusted', 'off')
62 62 self.ui.setconfig('ui', 'interactive', 'off')
63 63
64 64 if not isinstance(self.conf, (dict, list, tuple)):
65 65 map = {'paths': 'hgweb-paths'}
66 66 self.ui.readconfig(self.conf, remap=map, trust=True)
67 67 paths = self.ui.configitems('hgweb-paths')
68 68 elif isinstance(self.conf, (list, tuple)):
69 69 paths = self.conf
70 70 elif isinstance(self.conf, dict):
71 71 paths = self.conf.items()
72 72
73 encoding.encoding = self.ui.config('web', 'encoding',
74 encoding.encoding)
73 75 self.motd = self.ui.config('web', 'motd')
74 76 self.style = self.ui.config('web', 'style', 'paper')
75 77 self.stripecount = self.ui.config('web', 'stripes', 1)
76 78 if self.stripecount:
77 79 self.stripecount = int(self.stripecount)
78 80 self._baseurl = self.ui.config('web', 'baseurl')
79 81
80 82 self.repos = findrepos(paths)
81 83 for prefix, root in self.ui.configitems('collections'):
82 84 prefix = util.pconvert(prefix)
83 85 for path in util.walkrepos(root, followsym=True):
84 86 repo = os.path.normpath(path)
85 87 name = util.pconvert(repo)
86 88 if name.startswith(prefix):
87 89 name = name[len(prefix):]
88 90 self.repos.append((name.lstrip('/'), repo))
89 91
90 92 self.repos.sort()
91 93 self.lastrefresh = time.time()
92 94
93 95 def run(self):
94 96 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
95 97 raise RuntimeError("This function is only intended to be "
96 98 "called while running as a CGI script.")
97 99 import mercurial.hgweb.wsgicgi as wsgicgi
98 100 wsgicgi.launch(self)
99 101
100 102 def __call__(self, env, respond):
101 103 req = wsgirequest(env, respond)
102 104 return self.run_wsgi(req)
103 105
104 106 def read_allowed(self, ui, req):
105 107 """Check allow_read and deny_read config options of a repo's ui object
106 108 to determine user permissions. By default, with neither option set (or
107 109 both empty), allow all users to read the repo. There are two ways a
108 110 user can be denied read access: (1) deny_read is not empty, and the
109 111 user is unauthenticated or deny_read contains user (or *), and (2)
110 112 allow_read is not empty and the user is not in allow_read. Return True
111 113 if user is allowed to read the repo, else return False."""
112 114
113 115 user = req.env.get('REMOTE_USER')
114 116
115 117 deny_read = ui.configlist('web', 'deny_read', untrusted=True)
116 118 if deny_read and (not user or deny_read == ['*'] or user in deny_read):
117 119 return False
118 120
119 121 allow_read = ui.configlist('web', 'allow_read', untrusted=True)
120 122 # by default, allow reading if no allow_read option has been set
121 123 if (not allow_read) or (allow_read == ['*']) or (user in allow_read):
122 124 return True
123 125
124 126 return False
125 127
126 128 def run_wsgi(self, req):
127 129 try:
128 130 try:
129 131 self.refresh()
130 132
131 133 virtual = req.env.get("PATH_INFO", "").strip('/')
132 134 tmpl = self.templater(req)
133 135 ctype = tmpl('mimetype', encoding=encoding.encoding)
134 136 ctype = templater.stringify(ctype)
135 137
136 138 # a static file
137 139 if virtual.startswith('static/') or 'static' in req.form:
138 140 if virtual.startswith('static/'):
139 141 fname = virtual[7:]
140 142 else:
141 143 fname = req.form['static'][0]
142 144 static = templater.templatepath('static')
143 145 return (staticfile(static, fname, req),)
144 146
145 147 # top-level index
146 148 elif not virtual:
147 149 req.respond(HTTP_OK, ctype)
148 150 return self.makeindex(req, tmpl)
149 151
150 152 # nested indexes and hgwebs
151 153
152 154 repos = dict(self.repos)
153 155 while virtual:
154 156 real = repos.get(virtual)
155 157 if real:
156 158 req.env['REPO_NAME'] = virtual
157 159 try:
158 160 repo = hg.repository(self.ui, real)
159 161 return hgweb(repo).run_wsgi(req)
160 162 except IOError, inst:
161 163 msg = inst.strerror
162 164 raise ErrorResponse(HTTP_SERVER_ERROR, msg)
163 165 except error.RepoError, inst:
164 166 raise ErrorResponse(HTTP_SERVER_ERROR, str(inst))
165 167
166 168 # browse subdirectories
167 169 subdir = virtual + '/'
168 170 if [r for r in repos if r.startswith(subdir)]:
169 171 req.respond(HTTP_OK, ctype)
170 172 return self.makeindex(req, tmpl, subdir)
171 173
172 174 up = virtual.rfind('/')
173 175 if up < 0:
174 176 break
175 177 virtual = virtual[:up]
176 178
177 179 # prefixes not found
178 180 req.respond(HTTP_NOT_FOUND, ctype)
179 181 return tmpl("notfound", repo=virtual)
180 182
181 183 except ErrorResponse, err:
182 184 req.respond(err, ctype)
183 185 return tmpl('error', error=err.message or '')
184 186 finally:
185 187 tmpl = None
186 188
187 189 def makeindex(self, req, tmpl, subdir=""):
188 190
189 191 def archivelist(ui, nodeid, url):
190 192 allowed = ui.configlist("web", "allow_archive", untrusted=True)
191 193 for i in [('zip', '.zip'), ('gz', '.tar.gz'), ('bz2', '.tar.bz2')]:
192 194 if i[0] in allowed or ui.configbool("web", "allow" + i[0],
193 195 untrusted=True):
194 196 yield {"type" : i[0], "extension": i[1],
195 197 "node": nodeid, "url": url}
196 198
197 199 sortdefault = 'name', False
198 200 def entries(sortcolumn="", descending=False, subdir="", **map):
199 201 rows = []
200 202 parity = paritygen(self.stripecount)
201 203 for name, path in self.repos:
202 204 if not name.startswith(subdir):
203 205 continue
204 206 name = name[len(subdir):]
205 207
206 208 u = self.ui.copy()
207 209 try:
208 210 u.readconfig(os.path.join(path, '.hg', 'hgrc'))
209 211 except Exception, e:
210 212 u.warn(_('error reading %s/.hg/hgrc: %s\n') % (path, e))
211 213 continue
212 214 def get(section, name, default=None):
213 215 return u.config(section, name, default, untrusted=True)
214 216
215 217 if u.configbool("web", "hidden", untrusted=True):
216 218 continue
217 219
218 220 if not self.read_allowed(u, req):
219 221 continue
220 222
221 223 parts = [name]
222 224 if 'PATH_INFO' in req.env:
223 225 parts.insert(0, req.env['PATH_INFO'].rstrip('/'))
224 226 if req.env['SCRIPT_NAME']:
225 227 parts.insert(0, req.env['SCRIPT_NAME'])
226 228 url = ('/'.join(parts).replace("//", "/")) + '/'
227 229
228 230 # update time with local timezone
229 231 try:
230 232 d = (get_mtime(path), util.makedate()[1])
231 233 except OSError:
232 234 continue
233 235
234 236 contact = get_contact(get)
235 237 description = get("web", "description", "")
236 238 name = get("web", "name", name)
237 239 row = dict(contact=contact or "unknown",
238 240 contact_sort=contact.upper() or "unknown",
239 241 name=name,
240 242 name_sort=name,
241 243 url=url,
242 244 description=description or "unknown",
243 245 description_sort=description.upper() or "unknown",
244 246 lastchange=d,
245 247 lastchange_sort=d[1]-d[0],
246 248 archives=archivelist(u, "tip", url))
247 249 if (not sortcolumn or (sortcolumn, descending) == sortdefault):
248 250 # fast path for unsorted output
249 251 row['parity'] = parity.next()
250 252 yield row
251 253 else:
252 254 rows.append((row["%s_sort" % sortcolumn], row))
253 255 if rows:
254 256 rows.sort()
255 257 if descending:
256 258 rows.reverse()
257 259 for key, row in rows:
258 260 row['parity'] = parity.next()
259 261 yield row
260 262
261 263 self.refresh()
262 264 sortable = ["name", "description", "contact", "lastchange"]
263 265 sortcolumn, descending = sortdefault
264 266 if 'sort' in req.form:
265 267 sortcolumn = req.form['sort'][0]
266 268 descending = sortcolumn.startswith('-')
267 269 if descending:
268 270 sortcolumn = sortcolumn[1:]
269 271 if sortcolumn not in sortable:
270 272 sortcolumn = ""
271 273
272 274 sort = [("sort_%s" % column,
273 275 "%s%s" % ((not descending and column == sortcolumn)
274 276 and "-" or "", column))
275 277 for column in sortable]
276 278
277 279 self.refresh()
278 280 if self._baseurl is not None:
279 281 req.env['SCRIPT_NAME'] = self._baseurl
280 282
281 283 return tmpl("index", entries=entries, subdir=subdir,
282 284 sortcolumn=sortcolumn, descending=descending,
283 285 **dict(sort))
284 286
285 287 def templater(self, req):
286 288
287 289 def header(**map):
288 290 yield tmpl('header', encoding=encoding.encoding, **map)
289 291
290 292 def footer(**map):
291 293 yield tmpl("footer", **map)
292 294
293 295 def motd(**map):
294 296 if self.motd is not None:
295 297 yield self.motd
296 298 else:
297 299 yield config('web', 'motd', '')
298 300
299 301 def config(section, name, default=None, untrusted=True):
300 302 return self.ui.config(section, name, default, untrusted)
301 303
302 304 if self._baseurl is not None:
303 305 req.env['SCRIPT_NAME'] = self._baseurl
304 306
305 307 url = req.env.get('SCRIPT_NAME', '')
306 308 if not url.endswith('/'):
307 309 url += '/'
308 310
309 311 vars = {}
310 312 style = self.style
311 313 if 'style' in req.form:
312 314 vars['style'] = style = req.form['style'][0]
313 315 start = url[-1] == '?' and '&' or '?'
314 316 sessionvars = webutil.sessionvars(vars, start)
315 317
316 318 staticurl = config('web', 'staticurl') or url + 'static/'
317 319 if not staticurl.endswith('/'):
318 320 staticurl += '/'
319 321
320 322 style = 'style' in req.form and req.form['style'][0] or self.style
321 323 mapfile = templater.stylemap(style)
322 324 tmpl = templater.templater(mapfile,
323 325 defaults={"header": header,
324 326 "footer": footer,
325 327 "motd": motd,
326 328 "url": url,
327 329 "staticurl": staticurl,
328 330 "sessionvars": sessionvars})
329 331 return tmpl
General Comments 0
You need to be logged in to leave comments. Login now