##// END OF EJS Templates
hgweb: forgot to centralize the req.write() calls in hgwebdir
Dirkjan Ochtman -
r5965:abe373e1 default
parent child Browse files
Show More
@@ -1,275 +1,275 b''
1 # hgweb/hgwebdir_mod.py - Web interface for a directory of repositories.
1 # hgweb/hgwebdir_mod.py - Web interface for a directory of repositories.
2 #
2 #
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
4 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
5 #
5 #
6 # This software may be used and distributed according to the terms
6 # This software may be used and distributed according to the terms
7 # of the GNU General Public License, incorporated herein by reference.
7 # of the GNU General Public License, incorporated herein by reference.
8
8
9 import os
9 import os
10 from mercurial.i18n import gettext as _
10 from mercurial.i18n import gettext as _
11 from mercurial import ui, hg, util, templater
11 from mercurial import ui, hg, util, templater
12 from common import ErrorResponse, get_mtime, staticfile, style_map, paritygen, \
12 from common import ErrorResponse, get_mtime, staticfile, style_map, paritygen, \
13 get_contact
13 get_contact
14 from hgweb_mod import hgweb
14 from hgweb_mod import hgweb
15 from request import wsgirequest
15 from request import wsgirequest
16
16
17 # This is a stopgap
17 # This is a stopgap
18 class hgwebdir(object):
18 class hgwebdir(object):
19 def __init__(self, config, parentui=None):
19 def __init__(self, config, parentui=None):
20 def cleannames(items):
20 def cleannames(items):
21 return [(util.pconvert(name).strip('/'), path)
21 return [(util.pconvert(name).strip('/'), path)
22 for name, path in items]
22 for name, path in items]
23
23
24 self.parentui = parentui or ui.ui(report_untrusted=False,
24 self.parentui = parentui or ui.ui(report_untrusted=False,
25 interactive = False)
25 interactive = False)
26 self.motd = None
26 self.motd = None
27 self.style = None
27 self.style = None
28 self.stripecount = None
28 self.stripecount = None
29 self.repos_sorted = ('name', False)
29 self.repos_sorted = ('name', False)
30 if isinstance(config, (list, tuple)):
30 if isinstance(config, (list, tuple)):
31 self.repos = cleannames(config)
31 self.repos = cleannames(config)
32 self.repos_sorted = ('', False)
32 self.repos_sorted = ('', False)
33 elif isinstance(config, dict):
33 elif isinstance(config, dict):
34 self.repos = cleannames(config.items())
34 self.repos = cleannames(config.items())
35 self.repos.sort()
35 self.repos.sort()
36 else:
36 else:
37 if isinstance(config, util.configparser):
37 if isinstance(config, util.configparser):
38 cp = config
38 cp = config
39 else:
39 else:
40 cp = util.configparser()
40 cp = util.configparser()
41 cp.read(config)
41 cp.read(config)
42 self.repos = []
42 self.repos = []
43 if cp.has_section('web'):
43 if cp.has_section('web'):
44 if cp.has_option('web', 'motd'):
44 if cp.has_option('web', 'motd'):
45 self.motd = cp.get('web', 'motd')
45 self.motd = cp.get('web', 'motd')
46 if cp.has_option('web', 'style'):
46 if cp.has_option('web', 'style'):
47 self.style = cp.get('web', 'style')
47 self.style = cp.get('web', 'style')
48 if cp.has_option('web', 'stripes'):
48 if cp.has_option('web', 'stripes'):
49 self.stripecount = int(cp.get('web', 'stripes'))
49 self.stripecount = int(cp.get('web', 'stripes'))
50 if cp.has_section('paths'):
50 if cp.has_section('paths'):
51 self.repos.extend(cleannames(cp.items('paths')))
51 self.repos.extend(cleannames(cp.items('paths')))
52 if cp.has_section('collections'):
52 if cp.has_section('collections'):
53 for prefix, root in cp.items('collections'):
53 for prefix, root in cp.items('collections'):
54 for path in util.walkrepos(root):
54 for path in util.walkrepos(root):
55 repo = os.path.normpath(path)
55 repo = os.path.normpath(path)
56 name = repo
56 name = repo
57 if name.startswith(prefix):
57 if name.startswith(prefix):
58 name = name[len(prefix):]
58 name = name[len(prefix):]
59 self.repos.append((name.lstrip(os.sep), repo))
59 self.repos.append((name.lstrip(os.sep), repo))
60 self.repos.sort()
60 self.repos.sort()
61
61
62 def run(self):
62 def run(self):
63 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
63 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
64 raise RuntimeError("This function is only intended to be called while running as a CGI script.")
64 raise RuntimeError("This function is only intended to be called while running as a CGI script.")
65 import mercurial.hgweb.wsgicgi as wsgicgi
65 import mercurial.hgweb.wsgicgi as wsgicgi
66 wsgicgi.launch(self)
66 wsgicgi.launch(self)
67
67
68 def __call__(self, env, respond):
68 def __call__(self, env, respond):
69 req = wsgirequest(env, respond)
69 req = wsgirequest(env, respond)
70 self.run_wsgi(req)
70 self.run_wsgi(req)
71 return req
71 return req
72
72
73 def run_wsgi(self, req):
73 def run_wsgi(self, req):
74
74
75 try:
75 try:
76 try:
76 try:
77
77
78 virtual = req.env.get("PATH_INFO", "").strip('/')
78 virtual = req.env.get("PATH_INFO", "").strip('/')
79
79
80 # a static file
80 # a static file
81 if virtual.startswith('static/') or 'static' in req.form:
81 if virtual.startswith('static/') or 'static' in req.form:
82 static = os.path.join(templater.templatepath(), 'static')
82 static = os.path.join(templater.templatepath(), 'static')
83 if virtual.startswith('static/'):
83 if virtual.startswith('static/'):
84 fname = virtual[7:]
84 fname = virtual[7:]
85 else:
85 else:
86 fname = req.form['static'][0]
86 fname = req.form['static'][0]
87 req.write(staticfile(static, fname, req))
87 req.write(staticfile(static, fname, req))
88 return
88 return
89
89
90 # top-level index
90 # top-level index
91 elif not virtual:
91 elif not virtual:
92 tmpl = self.templater(req)
92 tmpl = self.templater(req)
93 self.makeindex(req, tmpl)
93 req.write(self.makeindex(req, tmpl))
94 return
94 return
95
95
96 # nested indexes and hgwebs
96 # nested indexes and hgwebs
97 repos = dict(self.repos)
97 repos = dict(self.repos)
98 while virtual:
98 while virtual:
99 real = repos.get(virtual)
99 real = repos.get(virtual)
100 if real:
100 if real:
101 req.env['REPO_NAME'] = virtual
101 req.env['REPO_NAME'] = virtual
102 try:
102 try:
103 repo = hg.repository(self.parentui, real)
103 repo = hg.repository(self.parentui, real)
104 hgweb(repo).run_wsgi(req)
104 hgweb(repo).run_wsgi(req)
105 return
105 return
106 except IOError, inst:
106 except IOError, inst:
107 raise ErrorResponse(500, inst.strerror)
107 raise ErrorResponse(500, inst.strerror)
108 except hg.RepoError, inst:
108 except hg.RepoError, inst:
109 raise ErrorResponse(500, str(inst))
109 raise ErrorResponse(500, str(inst))
110
110
111 # browse subdirectories
111 # browse subdirectories
112 subdir = virtual + '/'
112 subdir = virtual + '/'
113 if [r for r in repos if r.startswith(subdir)]:
113 if [r for r in repos if r.startswith(subdir)]:
114 tmpl = self.templater(req)
114 tmpl = self.templater(req)
115 self.makeindex(req, tmpl, subdir)
115 req.write(self.makeindex(req, tmpl, subdir))
116 return
116 return
117
117
118 up = virtual.rfind('/')
118 up = virtual.rfind('/')
119 if up < 0:
119 if up < 0:
120 break
120 break
121 virtual = virtual[:up]
121 virtual = virtual[:up]
122
122
123 # prefixes not found
123 # prefixes not found
124 tmpl = self.templater(req)
124 tmpl = self.templater(req)
125 req.respond(404, tmpl("notfound", repo=virtual))
125 req.respond(404, tmpl("notfound", repo=virtual))
126
126
127 except ErrorResponse, err:
127 except ErrorResponse, err:
128 tmpl = self.templater(req)
128 tmpl = self.templater(req)
129 req.respond(err.code, tmpl('error', error=err.message or ''))
129 req.respond(err.code, tmpl('error', error=err.message or ''))
130 finally:
130 finally:
131 tmpl = None
131 tmpl = None
132
132
133 def makeindex(self, req, tmpl, subdir=""):
133 def makeindex(self, req, tmpl, subdir=""):
134
134
135 def archivelist(ui, nodeid, url):
135 def archivelist(ui, nodeid, url):
136 allowed = ui.configlist("web", "allow_archive", untrusted=True)
136 allowed = ui.configlist("web", "allow_archive", untrusted=True)
137 for i in [('zip', '.zip'), ('gz', '.tar.gz'), ('bz2', '.tar.bz2')]:
137 for i in [('zip', '.zip'), ('gz', '.tar.gz'), ('bz2', '.tar.bz2')]:
138 if i[0] in allowed or ui.configbool("web", "allow" + i[0],
138 if i[0] in allowed or ui.configbool("web", "allow" + i[0],
139 untrusted=True):
139 untrusted=True):
140 yield {"type" : i[0], "extension": i[1],
140 yield {"type" : i[0], "extension": i[1],
141 "node": nodeid, "url": url}
141 "node": nodeid, "url": url}
142
142
143 def entries(sortcolumn="", descending=False, subdir="", **map):
143 def entries(sortcolumn="", descending=False, subdir="", **map):
144 def sessionvars(**map):
144 def sessionvars(**map):
145 fields = []
145 fields = []
146 if 'style' in req.form:
146 if 'style' in req.form:
147 style = req.form['style'][0]
147 style = req.form['style'][0]
148 if style != get('web', 'style', ''):
148 if style != get('web', 'style', ''):
149 fields.append(('style', style))
149 fields.append(('style', style))
150
150
151 separator = url[-1] == '?' and ';' or '?'
151 separator = url[-1] == '?' and ';' or '?'
152 for name, value in fields:
152 for name, value in fields:
153 yield dict(name=name, value=value, separator=separator)
153 yield dict(name=name, value=value, separator=separator)
154 separator = ';'
154 separator = ';'
155
155
156 rows = []
156 rows = []
157 parity = paritygen(self.stripecount)
157 parity = paritygen(self.stripecount)
158 for name, path in self.repos:
158 for name, path in self.repos:
159 if not name.startswith(subdir):
159 if not name.startswith(subdir):
160 continue
160 continue
161 name = name[len(subdir):]
161 name = name[len(subdir):]
162
162
163 u = ui.ui(parentui=self.parentui)
163 u = ui.ui(parentui=self.parentui)
164 try:
164 try:
165 u.readconfig(os.path.join(path, '.hg', 'hgrc'))
165 u.readconfig(os.path.join(path, '.hg', 'hgrc'))
166 except Exception, e:
166 except Exception, e:
167 u.warn(_('error reading %s/.hg/hgrc: %s\n' % (path, e)))
167 u.warn(_('error reading %s/.hg/hgrc: %s\n' % (path, e)))
168 continue
168 continue
169 def get(section, name, default=None):
169 def get(section, name, default=None):
170 return u.config(section, name, default, untrusted=True)
170 return u.config(section, name, default, untrusted=True)
171
171
172 if u.configbool("web", "hidden", untrusted=True):
172 if u.configbool("web", "hidden", untrusted=True):
173 continue
173 continue
174
174
175 parts = [req.env['PATH_INFO'], name]
175 parts = [req.env['PATH_INFO'], name]
176 if req.env['SCRIPT_NAME']:
176 if req.env['SCRIPT_NAME']:
177 parts.insert(0, req.env['SCRIPT_NAME'])
177 parts.insert(0, req.env['SCRIPT_NAME'])
178 url = ('/'.join(parts).replace("//", "/")) + '/'
178 url = ('/'.join(parts).replace("//", "/")) + '/'
179
179
180 # update time with local timezone
180 # update time with local timezone
181 try:
181 try:
182 d = (get_mtime(path), util.makedate()[1])
182 d = (get_mtime(path), util.makedate()[1])
183 except OSError:
183 except OSError:
184 continue
184 continue
185
185
186 contact = get_contact(get)
186 contact = get_contact(get)
187 description = get("web", "description", "")
187 description = get("web", "description", "")
188 name = get("web", "name", name)
188 name = get("web", "name", name)
189 row = dict(contact=contact or "unknown",
189 row = dict(contact=contact or "unknown",
190 contact_sort=contact.upper() or "unknown",
190 contact_sort=contact.upper() or "unknown",
191 name=name,
191 name=name,
192 name_sort=name,
192 name_sort=name,
193 url=url,
193 url=url,
194 description=description or "unknown",
194 description=description or "unknown",
195 description_sort=description.upper() or "unknown",
195 description_sort=description.upper() or "unknown",
196 lastchange=d,
196 lastchange=d,
197 lastchange_sort=d[1]-d[0],
197 lastchange_sort=d[1]-d[0],
198 sessionvars=sessionvars,
198 sessionvars=sessionvars,
199 archives=archivelist(u, "tip", url))
199 archives=archivelist(u, "tip", url))
200 if (not sortcolumn
200 if (not sortcolumn
201 or (sortcolumn, descending) == self.repos_sorted):
201 or (sortcolumn, descending) == self.repos_sorted):
202 # fast path for unsorted output
202 # fast path for unsorted output
203 row['parity'] = parity.next()
203 row['parity'] = parity.next()
204 yield row
204 yield row
205 else:
205 else:
206 rows.append((row["%s_sort" % sortcolumn], row))
206 rows.append((row["%s_sort" % sortcolumn], row))
207 if rows:
207 if rows:
208 rows.sort()
208 rows.sort()
209 if descending:
209 if descending:
210 rows.reverse()
210 rows.reverse()
211 for key, row in rows:
211 for key, row in rows:
212 row['parity'] = parity.next()
212 row['parity'] = parity.next()
213 yield row
213 yield row
214
214
215 sortable = ["name", "description", "contact", "lastchange"]
215 sortable = ["name", "description", "contact", "lastchange"]
216 sortcolumn, descending = self.repos_sorted
216 sortcolumn, descending = self.repos_sorted
217 if 'sort' in req.form:
217 if 'sort' in req.form:
218 sortcolumn = req.form['sort'][0]
218 sortcolumn = req.form['sort'][0]
219 descending = sortcolumn.startswith('-')
219 descending = sortcolumn.startswith('-')
220 if descending:
220 if descending:
221 sortcolumn = sortcolumn[1:]
221 sortcolumn = sortcolumn[1:]
222 if sortcolumn not in sortable:
222 if sortcolumn not in sortable:
223 sortcolumn = ""
223 sortcolumn = ""
224
224
225 sort = [("sort_%s" % column,
225 sort = [("sort_%s" % column,
226 "%s%s" % ((not descending and column == sortcolumn)
226 "%s%s" % ((not descending and column == sortcolumn)
227 and "-" or "", column))
227 and "-" or "", column))
228 for column in sortable]
228 for column in sortable]
229
229
230 req.write(tmpl("index", entries=entries, subdir=subdir,
230 return tmpl("index", entries=entries, subdir=subdir,
231 sortcolumn=sortcolumn, descending=descending,
231 sortcolumn=sortcolumn, descending=descending,
232 **dict(sort)))
232 **dict(sort))
233
233
234 def templater(self, req):
234 def templater(self, req):
235
235
236 def header(**map):
236 def header(**map):
237 ctype = tmpl('mimetype', encoding=util._encoding)
237 ctype = tmpl('mimetype', encoding=util._encoding)
238 req.httphdr(templater.stringify(ctype))
238 req.httphdr(templater.stringify(ctype))
239 yield tmpl('header', encoding=util._encoding, **map)
239 yield tmpl('header', encoding=util._encoding, **map)
240
240
241 def footer(**map):
241 def footer(**map):
242 yield tmpl("footer", **map)
242 yield tmpl("footer", **map)
243
243
244 def motd(**map):
244 def motd(**map):
245 if self.motd is not None:
245 if self.motd is not None:
246 yield self.motd
246 yield self.motd
247 else:
247 else:
248 yield config('web', 'motd', '')
248 yield config('web', 'motd', '')
249
249
250 def config(section, name, default=None, untrusted=True):
250 def config(section, name, default=None, untrusted=True):
251 return self.parentui.config(section, name, default, untrusted)
251 return self.parentui.config(section, name, default, untrusted)
252
252
253 url = req.env.get('SCRIPT_NAME', '')
253 url = req.env.get('SCRIPT_NAME', '')
254 if not url.endswith('/'):
254 if not url.endswith('/'):
255 url += '/'
255 url += '/'
256
256
257 staticurl = config('web', 'staticurl') or url + 'static/'
257 staticurl = config('web', 'staticurl') or url + 'static/'
258 if not staticurl.endswith('/'):
258 if not staticurl.endswith('/'):
259 staticurl += '/'
259 staticurl += '/'
260
260
261 style = self.style
261 style = self.style
262 if style is None:
262 if style is None:
263 style = config('web', 'style', '')
263 style = config('web', 'style', '')
264 if 'style' in req.form:
264 if 'style' in req.form:
265 style = req.form['style'][0]
265 style = req.form['style'][0]
266 if self.stripecount is None:
266 if self.stripecount is None:
267 self.stripecount = int(config('web', 'stripes', 1))
267 self.stripecount = int(config('web', 'stripes', 1))
268 mapfile = style_map(templater.templatepath(), style)
268 mapfile = style_map(templater.templatepath(), style)
269 tmpl = templater.templater(mapfile, templater.common_filters,
269 tmpl = templater.templater(mapfile, templater.common_filters,
270 defaults={"header": header,
270 defaults={"header": header,
271 "footer": footer,
271 "footer": footer,
272 "motd": motd,
272 "motd": motd,
273 "url": url,
273 "url": url,
274 "staticurl": staticurl})
274 "staticurl": staticurl})
275 return tmpl
275 return tmpl
General Comments 0
You need to be logged in to leave comments. Login now