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