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