Mercurial > traipse_dev
comparison upmana/mercurial/hgweb/hgwebdir_mod.py @ 121:496dbf12a6cb alpha
Traipse Alpha 'OpenRPG' {091030-00}
Traipse is a distribution of OpenRPG that is designed to be easy to setup and go.
Traipse also makes it easy for developers to work on code without fear of
sacrifice. 'Ornery-Orc' continues the trend of 'Grumpy' and adds fixes to the
code. 'Ornery-Orc's main goal is to offer more advanced features and enhance the
productivity of the user.
Update Summary (Cleaning up for Beta):
Adds Bookmarks (Alpha) with cool Smiley Star and Plus Symbol images!
Changes made to the map for increased portability. SnowDog has changes planned in
Core, though.
Added an initial push to the BCG. Not much to see, just shows off how it is
re-writing Main code.
Fix to remote admin commands
Minor fix to texted based server, works in /System/ folder
Some Core changes to gametree to correctly disply Pretty Print, thanks David!
Fix to Splitter Nodes not being created.
Added images to Plugin Control panel for Autostart feature
Fix to massive amounts of images loading; from Core
fix to gsclient so with_statement imports
Added 'boot' command to remote admin
Prep work in Pass tool for remote admin rankings and different passwords, ei,
Server, Admin, Moderator, etc.
Remote Admin Commands more organized, more prep work.
Added Confirmation window for sent nodes.
Minor changes to allow for portability to an OpenSUSE linux OS (hopefully without
breaking)
{091028}
Made changes to gametree to start working with Element Tree, mostly from Core
Minor changes to Map to start working with Element Tree, from Core
Preliminary changes to map efficiency, from FlexiRPG
Miniatures Layer pop up box allows users to turn off Mini labels, from FlexiRPG
Changes to main.py to start working with Element Tree
{091029}
Changes made to server to start working with Element Tree.
Changes made to Meta Server Lib. Prepping test work for a multi meta network
page.
Minor bug fixed with mini to gametree
Zoom Mouse plugin added.
{091030}
Getting ready for Beta. Server needs debugging so Alpha remains bugged.
Plugin UI code cleaned. Auto start works with a graphic, pop-up asks to enable or
disable plugin.
Update Manager now has a partially working Status Bar. Status Bar captures
terminal text, so Merc out put is visible. Manifest.xml file, will be renamed, is
now much cleaner.
Debug Console has a clear button and a Report Bug button. Prep work for a Term2Win
class in Debug Console.
Known: Current Alpha fails in Windows.
author | sirebral |
---|---|
date | Fri, 30 Oct 2009 22:21:40 -0500 |
parents | |
children | 36919b8a3ef9 |
comparison
equal
deleted
inserted
replaced
120:d86e762a994f | 121:496dbf12a6cb |
---|---|
1 # hgweb/hgwebdir_mod.py - Web interface for a directory of repositories. | |
2 # | |
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net> | |
4 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com> | |
5 # | |
6 # This software may be used and distributed according to the terms of the | |
7 # GNU General Public License version 2, incorporated herein by reference. | |
8 | |
9 import os, re, time | |
10 from mercurial.i18n import _ | |
11 from mercurial import ui, hg, util, templater | |
12 from mercurial import error, encoding | |
13 from common import ErrorResponse, get_mtime, staticfile, paritygen,\ | |
14 get_contact, HTTP_OK, HTTP_NOT_FOUND, HTTP_SERVER_ERROR | |
15 from hgweb_mod import hgweb | |
16 from request import wsgirequest | |
17 import webutil | |
18 | |
19 def cleannames(items): | |
20 return [(util.pconvert(name).strip('/'), path) for name, path in items] | |
21 | |
22 def findrepos(paths): | |
23 repos = {} | |
24 for prefix, root in cleannames(paths): | |
25 roothead, roottail = os.path.split(root) | |
26 # "foo = /bar/*" makes every subrepo of /bar/ to be | |
27 # mounted as foo/subrepo | |
28 # and "foo = /bar/**" also recurses into the subdirectories, | |
29 # remember to use it without working dir. | |
30 try: | |
31 recurse = {'*': False, '**': True}[roottail] | |
32 except KeyError: | |
33 repos[prefix] = root | |
34 continue | |
35 roothead = os.path.normpath(roothead) | |
36 for path in util.walkrepos(roothead, followsym=True, recurse=recurse): | |
37 path = os.path.normpath(path) | |
38 name = util.pconvert(path[len(roothead):]).strip('/') | |
39 if prefix: | |
40 name = prefix + '/' + name | |
41 repos[name] = path | |
42 return repos.items() | |
43 | |
44 class hgwebdir(object): | |
45 refreshinterval = 20 | |
46 | |
47 def __init__(self, conf, baseui=None): | |
48 self.conf = conf | |
49 self.baseui = baseui | |
50 self.lastrefresh = 0 | |
51 self.refresh() | |
52 | |
53 def refresh(self): | |
54 if self.lastrefresh + self.refreshinterval > time.time(): | |
55 return | |
56 | |
57 if self.baseui: | |
58 self.ui = self.baseui.copy() | |
59 else: | |
60 self.ui = ui.ui() | |
61 self.ui.setconfig('ui', 'report_untrusted', 'off') | |
62 self.ui.setconfig('ui', 'interactive', 'off') | |
63 | |
64 if not isinstance(self.conf, (dict, list, tuple)): | |
65 map = {'paths': 'hgweb-paths'} | |
66 self.ui.readconfig(self.conf, remap=map, trust=True) | |
67 paths = self.ui.configitems('hgweb-paths') | |
68 elif isinstance(self.conf, (list, tuple)): | |
69 paths = self.conf | |
70 elif isinstance(self.conf, dict): | |
71 paths = self.conf.items() | |
72 | |
73 encoding.encoding = self.ui.config('web', 'encoding', | |
74 encoding.encoding) | |
75 self.motd = self.ui.config('web', 'motd') | |
76 self.style = self.ui.config('web', 'style', 'paper') | |
77 self.stripecount = self.ui.config('web', 'stripes', 1) | |
78 if self.stripecount: | |
79 self.stripecount = int(self.stripecount) | |
80 self._baseurl = self.ui.config('web', 'baseurl') | |
81 | |
82 self.repos = findrepos(paths) | |
83 for prefix, root in self.ui.configitems('collections'): | |
84 prefix = util.pconvert(prefix) | |
85 for path in util.walkrepos(root, followsym=True): | |
86 repo = os.path.normpath(path) | |
87 name = util.pconvert(repo) | |
88 if name.startswith(prefix): | |
89 name = name[len(prefix):] | |
90 self.repos.append((name.lstrip('/'), repo)) | |
91 | |
92 self.repos.sort() | |
93 self.lastrefresh = time.time() | |
94 | |
95 def run(self): | |
96 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."): | |
97 raise RuntimeError("This function is only intended to be " | |
98 "called while running as a CGI script.") | |
99 import mercurial.hgweb.wsgicgi as wsgicgi | |
100 wsgicgi.launch(self) | |
101 | |
102 def __call__(self, env, respond): | |
103 req = wsgirequest(env, respond) | |
104 return self.run_wsgi(req) | |
105 | |
106 def read_allowed(self, ui, req): | |
107 """Check allow_read and deny_read config options of a repo's ui object | |
108 to determine user permissions. By default, with neither option set (or | |
109 both empty), allow all users to read the repo. There are two ways a | |
110 user can be denied read access: (1) deny_read is not empty, and the | |
111 user is unauthenticated or deny_read contains user (or *), and (2) | |
112 allow_read is not empty and the user is not in allow_read. Return True | |
113 if user is allowed to read the repo, else return False.""" | |
114 | |
115 user = req.env.get('REMOTE_USER') | |
116 | |
117 deny_read = ui.configlist('web', 'deny_read', untrusted=True) | |
118 if deny_read and (not user or deny_read == ['*'] or user in deny_read): | |
119 return False | |
120 | |
121 allow_read = ui.configlist('web', 'allow_read', untrusted=True) | |
122 # by default, allow reading if no allow_read option has been set | |
123 if (not allow_read) or (allow_read == ['*']) or (user in allow_read): | |
124 return True | |
125 | |
126 return False | |
127 | |
128 def run_wsgi(self, req): | |
129 try: | |
130 try: | |
131 self.refresh() | |
132 | |
133 virtual = req.env.get("PATH_INFO", "").strip('/') | |
134 tmpl = self.templater(req) | |
135 ctype = tmpl('mimetype', encoding=encoding.encoding) | |
136 ctype = templater.stringify(ctype) | |
137 | |
138 # a static file | |
139 if virtual.startswith('static/') or 'static' in req.form: | |
140 if virtual.startswith('static/'): | |
141 fname = virtual[7:] | |
142 else: | |
143 fname = req.form['static'][0] | |
144 static = templater.templatepath('static') | |
145 return (staticfile(static, fname, req),) | |
146 | |
147 # top-level index | |
148 elif not virtual: | |
149 req.respond(HTTP_OK, ctype) | |
150 return self.makeindex(req, tmpl) | |
151 | |
152 # nested indexes and hgwebs | |
153 | |
154 repos = dict(self.repos) | |
155 while virtual: | |
156 real = repos.get(virtual) | |
157 if real: | |
158 req.env['REPO_NAME'] = virtual | |
159 try: | |
160 repo = hg.repository(self.ui, real) | |
161 return hgweb(repo).run_wsgi(req) | |
162 except IOError, inst: | |
163 msg = inst.strerror | |
164 raise ErrorResponse(HTTP_SERVER_ERROR, msg) | |
165 except error.RepoError, inst: | |
166 raise ErrorResponse(HTTP_SERVER_ERROR, str(inst)) | |
167 | |
168 # browse subdirectories | |
169 subdir = virtual + '/' | |
170 if [r for r in repos if r.startswith(subdir)]: | |
171 req.respond(HTTP_OK, ctype) | |
172 return self.makeindex(req, tmpl, subdir) | |
173 | |
174 up = virtual.rfind('/') | |
175 if up < 0: | |
176 break | |
177 virtual = virtual[:up] | |
178 | |
179 # prefixes not found | |
180 req.respond(HTTP_NOT_FOUND, ctype) | |
181 return tmpl("notfound", repo=virtual) | |
182 | |
183 except ErrorResponse, err: | |
184 req.respond(err, ctype) | |
185 return tmpl('error', error=err.message or '') | |
186 finally: | |
187 tmpl = None | |
188 | |
189 def makeindex(self, req, tmpl, subdir=""): | |
190 | |
191 def archivelist(ui, nodeid, url): | |
192 allowed = ui.configlist("web", "allow_archive", untrusted=True) | |
193 for i in [('zip', '.zip'), ('gz', '.tar.gz'), ('bz2', '.tar.bz2')]: | |
194 if i[0] in allowed or ui.configbool("web", "allow" + i[0], | |
195 untrusted=True): | |
196 yield {"type" : i[0], "extension": i[1], | |
197 "node": nodeid, "url": url} | |
198 | |
199 sortdefault = 'name', False | |
200 def entries(sortcolumn="", descending=False, subdir="", **map): | |
201 rows = [] | |
202 parity = paritygen(self.stripecount) | |
203 for name, path in self.repos: | |
204 if not name.startswith(subdir): | |
205 continue | |
206 name = name[len(subdir):] | |
207 | |
208 u = self.ui.copy() | |
209 try: | |
210 u.readconfig(os.path.join(path, '.hg', 'hgrc')) | |
211 except Exception, e: | |
212 u.warn(_('error reading %s/.hg/hgrc: %s\n') % (path, e)) | |
213 continue | |
214 def get(section, name, default=None): | |
215 return u.config(section, name, default, untrusted=True) | |
216 | |
217 if u.configbool("web", "hidden", untrusted=True): | |
218 continue | |
219 | |
220 if not self.read_allowed(u, req): | |
221 continue | |
222 | |
223 parts = [name] | |
224 if 'PATH_INFO' in req.env: | |
225 parts.insert(0, req.env['PATH_INFO'].rstrip('/')) | |
226 if req.env['SCRIPT_NAME']: | |
227 parts.insert(0, req.env['SCRIPT_NAME']) | |
228 m = re.match('((?:https?://)?)(.*)', '/'.join(parts)) | |
229 # squish repeated slashes out of the path component | |
230 url = m.group(1) + re.sub('/+', '/', m.group(2)) + '/' | |
231 | |
232 # update time with local timezone | |
233 try: | |
234 d = (get_mtime(path), util.makedate()[1]) | |
235 except OSError: | |
236 continue | |
237 | |
238 contact = get_contact(get) | |
239 description = get("web", "description", "") | |
240 name = get("web", "name", name) | |
241 row = dict(contact=contact or "unknown", | |
242 contact_sort=contact.upper() or "unknown", | |
243 name=name, | |
244 name_sort=name, | |
245 url=url, | |
246 description=description or "unknown", | |
247 description_sort=description.upper() or "unknown", | |
248 lastchange=d, | |
249 lastchange_sort=d[1]-d[0], | |
250 archives=archivelist(u, "tip", url)) | |
251 if (not sortcolumn or (sortcolumn, descending) == sortdefault): | |
252 # fast path for unsorted output | |
253 row['parity'] = parity.next() | |
254 yield row | |
255 else: | |
256 rows.append((row["%s_sort" % sortcolumn], row)) | |
257 if rows: | |
258 rows.sort() | |
259 if descending: | |
260 rows.reverse() | |
261 for key, row in rows: | |
262 row['parity'] = parity.next() | |
263 yield row | |
264 | |
265 self.refresh() | |
266 sortable = ["name", "description", "contact", "lastchange"] | |
267 sortcolumn, descending = sortdefault | |
268 if 'sort' in req.form: | |
269 sortcolumn = req.form['sort'][0] | |
270 descending = sortcolumn.startswith('-') | |
271 if descending: | |
272 sortcolumn = sortcolumn[1:] | |
273 if sortcolumn not in sortable: | |
274 sortcolumn = "" | |
275 | |
276 sort = [("sort_%s" % column, | |
277 "%s%s" % ((not descending and column == sortcolumn) | |
278 and "-" or "", column)) | |
279 for column in sortable] | |
280 | |
281 self.refresh() | |
282 if self._baseurl is not None: | |
283 req.env['SCRIPT_NAME'] = self._baseurl | |
284 | |
285 return tmpl("index", entries=entries, subdir=subdir, | |
286 sortcolumn=sortcolumn, descending=descending, | |
287 **dict(sort)) | |
288 | |
289 def templater(self, req): | |
290 | |
291 def header(**map): | |
292 yield tmpl('header', encoding=encoding.encoding, **map) | |
293 | |
294 def footer(**map): | |
295 yield tmpl("footer", **map) | |
296 | |
297 def motd(**map): | |
298 if self.motd is not None: | |
299 yield self.motd | |
300 else: | |
301 yield config('web', 'motd', '') | |
302 | |
303 def config(section, name, default=None, untrusted=True): | |
304 return self.ui.config(section, name, default, untrusted) | |
305 | |
306 if self._baseurl is not None: | |
307 req.env['SCRIPT_NAME'] = self._baseurl | |
308 | |
309 url = req.env.get('SCRIPT_NAME', '') | |
310 if not url.endswith('/'): | |
311 url += '/' | |
312 | |
313 vars = {} | |
314 style = self.style | |
315 if 'style' in req.form: | |
316 vars['style'] = style = req.form['style'][0] | |
317 start = url[-1] == '?' and '&' or '?' | |
318 sessionvars = webutil.sessionvars(vars, start) | |
319 | |
320 staticurl = config('web', 'staticurl') or url + 'static/' | |
321 if not staticurl.endswith('/'): | |
322 staticurl += '/' | |
323 | |
324 style = 'style' in req.form and req.form['style'][0] or self.style | |
325 mapfile = templater.stylemap(style) | |
326 tmpl = templater.templater(mapfile, | |
327 defaults={"header": header, | |
328 "footer": footer, | |
329 "motd": motd, | |
330 "url": url, | |
331 "staticurl": staticurl, | |
332 "sessionvars": sessionvars}) | |
333 return tmpl |