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