##// END OF EJS Templates
hgwebdir: ignore hgrc parse errors while building the index page...
Alexis S. L. Carvalho -
r5332:b0bfe087 default
parent child Browse files
Show More
@@ -1,242 +1,243
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 from mercurial import demandimport; demandimport.enable()
9 from mercurial import demandimport; demandimport.enable()
10 import os, mimetools, cStringIO
10 import os, mimetools, cStringIO
11 from mercurial.i18n import gettext as _
11 from mercurial.i18n import gettext as _
12 from mercurial import ui, hg, util, templater
12 from mercurial import ui, hg, util, templater
13 from common import get_mtime, staticfile, style_map, paritygen
13 from common import get_mtime, staticfile, style_map, paritygen
14 from hgweb_mod import hgweb
14 from hgweb_mod import hgweb
15
15
16 # This is a stopgap
16 # This is a stopgap
17 class hgwebdir(object):
17 class hgwebdir(object):
18 def __init__(self, config, parentui=None):
18 def __init__(self, config, parentui=None):
19 def cleannames(items):
19 def cleannames(items):
20 return [(util.pconvert(name.strip(os.sep)), path)
20 return [(util.pconvert(name.strip(os.sep)), path)
21 for name, path in items]
21 for name, path in items]
22
22
23 self.parentui = parentui
23 self.parentui = parentui
24 self.motd = None
24 self.motd = None
25 self.style = None
25 self.style = None
26 self.stripecount = None
26 self.stripecount = None
27 self.repos_sorted = ('name', False)
27 self.repos_sorted = ('name', False)
28 if isinstance(config, (list, tuple)):
28 if isinstance(config, (list, tuple)):
29 self.repos = cleannames(config)
29 self.repos = cleannames(config)
30 self.repos_sorted = ('', False)
30 self.repos_sorted = ('', False)
31 elif isinstance(config, dict):
31 elif isinstance(config, dict):
32 self.repos = cleannames(config.items())
32 self.repos = cleannames(config.items())
33 self.repos.sort()
33 self.repos.sort()
34 else:
34 else:
35 if isinstance(config, util.configparser):
35 if isinstance(config, util.configparser):
36 cp = config
36 cp = config
37 else:
37 else:
38 cp = util.configparser()
38 cp = util.configparser()
39 cp.read(config)
39 cp.read(config)
40 self.repos = []
40 self.repos = []
41 if cp.has_section('web'):
41 if cp.has_section('web'):
42 if cp.has_option('web', 'motd'):
42 if cp.has_option('web', 'motd'):
43 self.motd = cp.get('web', 'motd')
43 self.motd = cp.get('web', 'motd')
44 if cp.has_option('web', 'style'):
44 if cp.has_option('web', 'style'):
45 self.style = cp.get('web', 'style')
45 self.style = cp.get('web', 'style')
46 if cp.has_option('web', 'stripes'):
46 if cp.has_option('web', 'stripes'):
47 self.stripecount = int(cp.get('web', 'stripes'))
47 self.stripecount = int(cp.get('web', 'stripes'))
48 if cp.has_section('paths'):
48 if cp.has_section('paths'):
49 self.repos.extend(cleannames(cp.items('paths')))
49 self.repos.extend(cleannames(cp.items('paths')))
50 if cp.has_section('collections'):
50 if cp.has_section('collections'):
51 for prefix, root in cp.items('collections'):
51 for prefix, root in cp.items('collections'):
52 for path in util.walkrepos(root):
52 for path in util.walkrepos(root):
53 repo = os.path.normpath(path)
53 repo = os.path.normpath(path)
54 name = repo
54 name = repo
55 if name.startswith(prefix):
55 if name.startswith(prefix):
56 name = name[len(prefix):]
56 name = name[len(prefix):]
57 self.repos.append((name.lstrip(os.sep), repo))
57 self.repos.append((name.lstrip(os.sep), repo))
58 self.repos.sort()
58 self.repos.sort()
59
59
60 def run(self):
60 def run(self):
61 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
61 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
62 raise RuntimeError("This function is only intended to be called while running as a CGI script.")
62 raise RuntimeError("This function is only intended to be called while running as a CGI script.")
63 import mercurial.hgweb.wsgicgi as wsgicgi
63 import mercurial.hgweb.wsgicgi as wsgicgi
64 from request import wsgiapplication
64 from request import wsgiapplication
65 def make_web_app():
65 def make_web_app():
66 return self
66 return self
67 wsgicgi.launch(wsgiapplication(make_web_app))
67 wsgicgi.launch(wsgiapplication(make_web_app))
68
68
69 def run_wsgi(self, req):
69 def run_wsgi(self, req):
70 def header(**map):
70 def header(**map):
71 header_file = cStringIO.StringIO(
71 header_file = cStringIO.StringIO(
72 ''.join(tmpl("header", encoding=util._encoding, **map)))
72 ''.join(tmpl("header", encoding=util._encoding, **map)))
73 msg = mimetools.Message(header_file, 0)
73 msg = mimetools.Message(header_file, 0)
74 req.header(msg.items())
74 req.header(msg.items())
75 yield header_file.read()
75 yield header_file.read()
76
76
77 def footer(**map):
77 def footer(**map):
78 yield tmpl("footer", **map)
78 yield tmpl("footer", **map)
79
79
80 def motd(**map):
80 def motd(**map):
81 if self.motd is not None:
81 if self.motd is not None:
82 yield self.motd
82 yield self.motd
83 else:
83 else:
84 yield config('web', 'motd', '')
84 yield config('web', 'motd', '')
85
85
86 parentui = self.parentui or ui.ui(report_untrusted=False,
86 parentui = self.parentui or ui.ui(report_untrusted=False,
87 interactive=False)
87 interactive=False)
88
88
89 def config(section, name, default=None, untrusted=True):
89 def config(section, name, default=None, untrusted=True):
90 return parentui.config(section, name, default, untrusted)
90 return parentui.config(section, name, default, untrusted)
91
91
92 url = req.env['REQUEST_URI'].split('?')[0]
92 url = req.env['REQUEST_URI'].split('?')[0]
93 if not url.endswith('/'):
93 if not url.endswith('/'):
94 url += '/'
94 url += '/'
95
95
96 staticurl = config('web', 'staticurl') or url + 'static/'
96 staticurl = config('web', 'staticurl') or url + 'static/'
97 if not staticurl.endswith('/'):
97 if not staticurl.endswith('/'):
98 staticurl += '/'
98 staticurl += '/'
99
99
100 style = self.style
100 style = self.style
101 if style is None:
101 if style is None:
102 style = config('web', 'style', '')
102 style = config('web', 'style', '')
103 if req.form.has_key('style'):
103 if req.form.has_key('style'):
104 style = req.form['style'][0]
104 style = req.form['style'][0]
105 if self.stripecount is None:
105 if self.stripecount is None:
106 self.stripecount = int(config('web', 'stripes', 1))
106 self.stripecount = int(config('web', 'stripes', 1))
107 mapfile = style_map(templater.templatepath(), style)
107 mapfile = style_map(templater.templatepath(), style)
108 tmpl = templater.templater(mapfile, templater.common_filters,
108 tmpl = templater.templater(mapfile, templater.common_filters,
109 defaults={"header": header,
109 defaults={"header": header,
110 "footer": footer,
110 "footer": footer,
111 "motd": motd,
111 "motd": motd,
112 "url": url,
112 "url": url,
113 "staticurl": staticurl})
113 "staticurl": staticurl})
114
114
115 def archivelist(ui, nodeid, url):
115 def archivelist(ui, nodeid, url):
116 allowed = ui.configlist("web", "allow_archive", untrusted=True)
116 allowed = ui.configlist("web", "allow_archive", untrusted=True)
117 for i in [('zip', '.zip'), ('gz', '.tar.gz'), ('bz2', '.tar.bz2')]:
117 for i in [('zip', '.zip'), ('gz', '.tar.gz'), ('bz2', '.tar.bz2')]:
118 if i[0] in allowed or ui.configbool("web", "allow" + i[0],
118 if i[0] in allowed or ui.configbool("web", "allow" + i[0],
119 untrusted=True):
119 untrusted=True):
120 yield {"type" : i[0], "extension": i[1],
120 yield {"type" : i[0], "extension": i[1],
121 "node": nodeid, "url": url}
121 "node": nodeid, "url": url}
122
122
123 def entries(sortcolumn="", descending=False, **map):
123 def entries(sortcolumn="", descending=False, **map):
124 def sessionvars(**map):
124 def sessionvars(**map):
125 fields = []
125 fields = []
126 if req.form.has_key('style'):
126 if req.form.has_key('style'):
127 style = req.form['style'][0]
127 style = req.form['style'][0]
128 if style != get('web', 'style', ''):
128 if style != get('web', 'style', ''):
129 fields.append(('style', style))
129 fields.append(('style', style))
130
130
131 separator = url[-1] == '?' and ';' or '?'
131 separator = url[-1] == '?' and ';' or '?'
132 for name, value in fields:
132 for name, value in fields:
133 yield dict(name=name, value=value, separator=separator)
133 yield dict(name=name, value=value, separator=separator)
134 separator = ';'
134 separator = ';'
135
135
136 rows = []
136 rows = []
137 parity = paritygen(self.stripecount)
137 parity = paritygen(self.stripecount)
138 for name, path in self.repos:
138 for name, path in self.repos:
139 u = ui.ui(parentui=parentui)
139 u = ui.ui(parentui=parentui)
140 try:
140 try:
141 u.readconfig(os.path.join(path, '.hg', 'hgrc'))
141 u.readconfig(os.path.join(path, '.hg', 'hgrc'))
142 except IOError:
142 except Exception, e:
143 pass
143 u.warn(_('error reading %s/.hg/hgrc: %s\n' % (path, e)))
144 continue
144 def get(section, name, default=None):
145 def get(section, name, default=None):
145 return u.config(section, name, default, untrusted=True)
146 return u.config(section, name, default, untrusted=True)
146
147
147 if u.configbool("web", "hidden", untrusted=True):
148 if u.configbool("web", "hidden", untrusted=True):
148 continue
149 continue
149
150
150 url = ('/'.join([req.env["REQUEST_URI"].split('?')[0], name])
151 url = ('/'.join([req.env["REQUEST_URI"].split('?')[0], name])
151 .replace("//", "/")) + '/'
152 .replace("//", "/")) + '/'
152
153
153 # update time with local timezone
154 # update time with local timezone
154 try:
155 try:
155 d = (get_mtime(path), util.makedate()[1])
156 d = (get_mtime(path), util.makedate()[1])
156 except OSError:
157 except OSError:
157 continue
158 continue
158
159
159 contact = (get("ui", "username") or # preferred
160 contact = (get("ui", "username") or # preferred
160 get("web", "contact") or # deprecated
161 get("web", "contact") or # deprecated
161 get("web", "author", "")) # also
162 get("web", "author", "")) # also
162 description = get("web", "description", "")
163 description = get("web", "description", "")
163 name = get("web", "name", name)
164 name = get("web", "name", name)
164 row = dict(contact=contact or "unknown",
165 row = dict(contact=contact or "unknown",
165 contact_sort=contact.upper() or "unknown",
166 contact_sort=contact.upper() or "unknown",
166 name=name,
167 name=name,
167 name_sort=name,
168 name_sort=name,
168 url=url,
169 url=url,
169 description=description or "unknown",
170 description=description or "unknown",
170 description_sort=description.upper() or "unknown",
171 description_sort=description.upper() or "unknown",
171 lastchange=d,
172 lastchange=d,
172 lastchange_sort=d[1]-d[0],
173 lastchange_sort=d[1]-d[0],
173 sessionvars=sessionvars,
174 sessionvars=sessionvars,
174 archives=archivelist(u, "tip", url))
175 archives=archivelist(u, "tip", url))
175 if (not sortcolumn
176 if (not sortcolumn
176 or (sortcolumn, descending) == self.repos_sorted):
177 or (sortcolumn, descending) == self.repos_sorted):
177 # fast path for unsorted output
178 # fast path for unsorted output
178 row['parity'] = parity.next()
179 row['parity'] = parity.next()
179 yield row
180 yield row
180 else:
181 else:
181 rows.append((row["%s_sort" % sortcolumn], row))
182 rows.append((row["%s_sort" % sortcolumn], row))
182 if rows:
183 if rows:
183 rows.sort()
184 rows.sort()
184 if descending:
185 if descending:
185 rows.reverse()
186 rows.reverse()
186 for key, row in rows:
187 for key, row in rows:
187 row['parity'] = parity.next()
188 row['parity'] = parity.next()
188 yield row
189 yield row
189
190
190 try:
191 try:
191 virtual = req.env.get("PATH_INFO", "").strip('/')
192 virtual = req.env.get("PATH_INFO", "").strip('/')
192 if virtual.startswith('static/'):
193 if virtual.startswith('static/'):
193 static = os.path.join(templater.templatepath(), 'static')
194 static = os.path.join(templater.templatepath(), 'static')
194 fname = virtual[7:]
195 fname = virtual[7:]
195 req.write(staticfile(static, fname, req) or
196 req.write(staticfile(static, fname, req) or
196 tmpl('error', error='%r not found' % fname))
197 tmpl('error', error='%r not found' % fname))
197 elif virtual:
198 elif virtual:
198 while virtual:
199 while virtual:
199 real = dict(self.repos).get(virtual)
200 real = dict(self.repos).get(virtual)
200 if real:
201 if real:
201 break
202 break
202 up = virtual.rfind('/')
203 up = virtual.rfind('/')
203 if up < 0:
204 if up < 0:
204 break
205 break
205 virtual = virtual[:up]
206 virtual = virtual[:up]
206 if real:
207 if real:
207 req.env['REPO_NAME'] = virtual
208 req.env['REPO_NAME'] = virtual
208 try:
209 try:
209 repo = hg.repository(parentui, real)
210 repo = hg.repository(parentui, real)
210 hgweb(repo).run_wsgi(req)
211 hgweb(repo).run_wsgi(req)
211 except IOError, inst:
212 except IOError, inst:
212 req.write(tmpl("error", error=inst.strerror))
213 req.write(tmpl("error", error=inst.strerror))
213 except hg.RepoError, inst:
214 except hg.RepoError, inst:
214 req.write(tmpl("error", error=str(inst)))
215 req.write(tmpl("error", error=str(inst)))
215 else:
216 else:
216 req.write(tmpl("notfound", repo=virtual))
217 req.write(tmpl("notfound", repo=virtual))
217 else:
218 else:
218 if req.form.has_key('static'):
219 if req.form.has_key('static'):
219 static = os.path.join(templater.templatepath(), "static")
220 static = os.path.join(templater.templatepath(), "static")
220 fname = req.form['static'][0]
221 fname = req.form['static'][0]
221 req.write(staticfile(static, fname, req)
222 req.write(staticfile(static, fname, req)
222 or tmpl("error", error="%r not found" % fname))
223 or tmpl("error", error="%r not found" % fname))
223 else:
224 else:
224 sortable = ["name", "description", "contact", "lastchange"]
225 sortable = ["name", "description", "contact", "lastchange"]
225 sortcolumn, descending = self.repos_sorted
226 sortcolumn, descending = self.repos_sorted
226 if req.form.has_key('sort'):
227 if req.form.has_key('sort'):
227 sortcolumn = req.form['sort'][0]
228 sortcolumn = req.form['sort'][0]
228 descending = sortcolumn.startswith('-')
229 descending = sortcolumn.startswith('-')
229 if descending:
230 if descending:
230 sortcolumn = sortcolumn[1:]
231 sortcolumn = sortcolumn[1:]
231 if sortcolumn not in sortable:
232 if sortcolumn not in sortable:
232 sortcolumn = ""
233 sortcolumn = ""
233
234
234 sort = [("sort_%s" % column,
235 sort = [("sort_%s" % column,
235 "%s%s" % ((not descending and column == sortcolumn)
236 "%s%s" % ((not descending and column == sortcolumn)
236 and "-" or "", column))
237 and "-" or "", column))
237 for column in sortable]
238 for column in sortable]
238 req.write(tmpl("index", entries=entries,
239 req.write(tmpl("index", entries=entries,
239 sortcolumn=sortcolumn, descending=descending,
240 sortcolumn=sortcolumn, descending=descending,
240 **dict(sort)))
241 **dict(sort)))
241 finally:
242 finally:
242 tmpl = None
243 tmpl = None
General Comments 0
You need to be logged in to leave comments. Login now