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