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