##// END OF EJS Templates
hgweb: make notfound templates more informative
hgweb: make notfound templates more informative

File last commit:

r5336:24de0275 merge default
r5560:e78c2401 default
Show More
hgwebdir_mod.py
260 lines | 10.7 KiB | text/x-python | PythonLexer
Eric Hopper
Fixing up comment headers for split up code.
r2391 # hgweb/hgwebdir_mod.py - Web interface for a directory of repositories.
Eric Hopper
Final stage of the hgweb split up....
r2356 #
# Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
Vadim Gelfer
update copyrights.
r2859 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
Eric Hopper
Final stage of the hgweb split up....
r2356 #
# This software may be used and distributed according to the terms
# of the GNU General Public License, incorporated herein by reference.
Matt Mackall
Replace demandload with new demandimport
r3877 import os, mimetools, cStringIO
Eric Hopper
Final stage of the hgweb split up....
r2356 from mercurial.i18n import gettext as _
Matt Mackall
Replace demandload with new demandimport
r3877 from mercurial import ui, hg, util, templater
Thomas Arendsen Hein
hgweb: use generator to count parity of horizontal stripes for easier reading....
r4462 from common import get_mtime, staticfile, style_map, paritygen
Matt Mackall
Replace demandload with new demandimport
r3877 from hgweb_mod import hgweb
Eric Hopper
Final stage of the hgweb split up....
r2356
# This is a stopgap
class hgwebdir(object):
Alexis S. L. Carvalho
Pass a ui from create_server to hgwebdir and a repo from hgwebdir to hgweb...
r4079 def __init__(self, config, parentui=None):
Eric Hopper
Final stage of the hgweb split up....
r2356 def cleannames(items):
Alexis S. L. Carvalho
hgwebdir: change os.sep in the name of repos to "/"
r5063 return [(util.pconvert(name.strip(os.sep)), path)
for name, path in items]
Eric Hopper
Final stage of the hgweb split up....
r2356
Alexis S. L. Carvalho
Pass a ui from create_server to hgwebdir and a repo from hgwebdir to hgweb...
r4079 self.parentui = parentui
Alexis S. L. Carvalho
hgwebdir: try to get web.style and web.motd from the ui.config system...
r4080 self.motd = None
self.style = None
Thomas Arendsen Hein
hgweb: use generator to count parity of horizontal stripes for easier reading....
r4462 self.stripecount = None
Eric Hopper
Final stage of the hgweb split up....
r2356 self.repos_sorted = ('name', False)
if isinstance(config, (list, tuple)):
self.repos = cleannames(config)
self.repos_sorted = ('', False)
elif isinstance(config, dict):
self.repos = cleannames(config.items())
self.repos.sort()
else:
Michael Gebetsroither
hgwebdir: class hgwebdir should also accept a configparser instance
r4051 if isinstance(config, util.configparser):
cp = config
else:
cp = util.configparser()
cp.read(config)
Eric Hopper
Final stage of the hgweb split up....
r2356 self.repos = []
Edouard Gomez
Add style support to hgwebdir
r3221 if cp.has_section('web'):
if cp.has_option('web', 'motd'):
self.motd = cp.get('web', 'motd')
if cp.has_option('web', 'style'):
Thomas Arendsen Hein
Whitespace/Tab cleanup
r3223 self.style = cp.get('web', 'style')
Thomas Arendsen Hein
hgweb: use generator to count parity of horizontal stripes for easier reading....
r4462 if cp.has_option('web', 'stripes'):
self.stripecount = int(cp.get('web', 'stripes'))
Eric Hopper
Final stage of the hgweb split up....
r2356 if cp.has_section('paths'):
self.repos.extend(cleannames(cp.items('paths')))
if cp.has_section('collections'):
for prefix, root in cp.items('collections'):
for path in util.walkrepos(root):
repo = os.path.normpath(path)
name = repo
if name.startswith(prefix):
name = name[len(prefix):]
self.repos.append((name.lstrip(os.sep), repo))
self.repos.sort()
Eric Hopper
Arrange for old copies of CGI scripts to still work.
r2535 def run(self):
Eric Hopper
Cleanup hgweb and hgwebdir's run method a bit.
r2538 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
Eric Hopper
Arrange for old copies of CGI scripts to still work.
r2535 raise RuntimeError("This function is only intended to be called while running as a CGI script.")
import mercurial.hgweb.wsgicgi as wsgicgi
from request import wsgiapplication
def make_web_app():
Eric Hopper
Cleanup hgweb and hgwebdir's run method a bit.
r2538 return self
Eric Hopper
Arrange for old copies of CGI scripts to still work.
r2535 wsgicgi.launch(wsgiapplication(make_web_app))
def run_wsgi(self, req):
Eric Hopper
Final stage of the hgweb split up....
r2356 def header(**map):
Thomas Arendsen Hein
Set charset encoding for hgwebdir, too....
r3882 header_file = cStringIO.StringIO(
''.join(tmpl("header", encoding=util._encoding, **map)))
Eric Hopper
Really fix http headers for web UI and issue 254....
r2514 msg = mimetools.Message(header_file, 0)
req.header(msg.items())
yield header_file.read()
Eric Hopper
Final stage of the hgweb split up....
r2356
def footer(**map):
Thomas Arendsen Hein
Fix "templater object got multiple values for keyword argument 'motd'"...
r3479 yield tmpl("footer", **map)
Eric Hopper
Final stage of the hgweb split up....
r2356
Alexis S. L. Carvalho
hgweb: make #motd# available for all templates
r3488 def motd(**map):
Alexis S. L. Carvalho
hgwebdir: try to get web.style and web.motd from the ui.config system...
r4080 if self.motd is not None:
yield self.motd
else:
yield config('web', 'motd', '')
Alexis S. L. Carvalho
hgweb: make #motd# available for all templates
r3488
Dirkjan Ochtman
Prevent WSGI apps from touching sys.stdin by setting ui.interactive to False.
r5289 parentui = self.parentui or ui.ui(report_untrusted=False,
interactive=False)
Alexis S. L. Carvalho
Pass a ui from create_server to hgwebdir and a repo from hgwebdir to hgweb...
r4079
Alexis S. L. Carvalho
hgwebdir: try to get web.style and web.motd from the ui.config system...
r4080 def config(section, name, default=None, untrusted=True):
return parentui.config(section, name, default, untrusted)
Brendan Cully
Add base URL to hgwebdir templater (fixes index page when the URL does not have a trailing /)
r3328 url = req.env['REQUEST_URI'].split('?')[0]
if not url.endswith('/'):
url += '/'
Brendan Cully
hgweb: let hgwebdir browse subdirectories
r4841 pathinfo = req.env.get('PATH_INFO', '').strip('/') + '/'
base = url[:len(url) - len(pathinfo)]
if not base.endswith('/'):
base += '/'
Brendan Cully
Add base URL to hgwebdir templater (fixes index page when the URL does not have a trailing /)
r3328
Brendan Cully
hgweb: let hgwebdir browse subdirectories
r4841 staticurl = config('web', 'staticurl') or base + 'static/'
Alexis S. L. Carvalho
hgweb: allow static files to be served directly by the HTTP server
r4084 if not staticurl.endswith('/'):
staticurl += '/'
Edouard Gomez
Add style support to hgwebdir
r3221 style = self.style
Alexis S. L. Carvalho
hgwebdir: try to get web.style and web.motd from the ui.config system...
r4080 if style is None:
style = config('web', 'style', '')
Edouard Gomez
Add style support to hgwebdir
r3221 if req.form.has_key('style'):
style = req.form['style'][0]
Thomas Arendsen Hein
hgweb: use generator to count parity of horizontal stripes for easier reading....
r4462 if self.stripecount is None:
self.stripecount = int(config('web', 'stripes', 1))
Thomas Arendsen Hein
hgweb: Search templates in templatepath/style/map, too, using a common function....
r3276 mapfile = style_map(templater.templatepath(), style)
tmpl = templater.templater(mapfile, templater.common_filters,
Eric Hopper
Final stage of the hgweb split up....
r2356 defaults={"header": header,
Brendan Cully
Add base URL to hgwebdir templater (fixes index page when the URL does not have a trailing /)
r3328 "footer": footer,
Alexis S. L. Carvalho
hgweb: make #motd# available for all templates
r3488 "motd": motd,
Alexis S. L. Carvalho
hgweb: allow static files to be served directly by the HTTP server
r4084 "url": url,
"staticurl": staticurl})
Eric Hopper
Final stage of the hgweb split up....
r2356
def archivelist(ui, nodeid, url):
Alexis S. L. Carvalho
use untrusted settings in hgwebdir
r3556 allowed = ui.configlist("web", "allow_archive", untrusted=True)
Brendan Cully
Teach hgwebdir about new interface
r3262 for i in [('zip', '.zip'), ('gz', '.tar.gz'), ('bz2', '.tar.bz2')]:
Alexis S. L. Carvalho
use untrusted settings in hgwebdir
r3556 if i[0] in allowed or ui.configbool("web", "allow" + i[0],
untrusted=True):
Brendan Cully
Teach hgwebdir about new interface
r3262 yield {"type" : i[0], "extension": i[1],
"node": nodeid, "url": url}
Eric Hopper
Final stage of the hgweb split up....
r2356
Brendan Cully
hgweb: let hgwebdir browse subdirectories
r4841 def entries(sortcolumn="", descending=False, subdir="", **map):
Thomas Arendsen Hein
Keep session variables when linking from hgwebdir's index to repositories.
r3365 def sessionvars(**map):
fields = []
if req.form.has_key('style'):
style = req.form['style'][0]
if style != get('web', 'style', ''):
fields.append(('style', style))
separator = url[-1] == '?' and ';' or '?'
for name, value in fields:
yield dict(name=name, value=value, separator=separator)
separator = ';'
Eric Hopper
Final stage of the hgweb split up....
r2356 rows = []
Thomas Arendsen Hein
hgweb: use generator to count parity of horizontal stripes for easier reading....
r4462 parity = paritygen(self.stripecount)
Eric Hopper
Final stage of the hgweb split up....
r2356 for name, path in self.repos:
Brendan Cully
hgweb: let hgwebdir browse subdirectories
r4841 if not name.startswith(subdir):
continue
Brendan Cully
hgwebdir: show only trailing part of path when browsing subdirectories
r4843 name = name[len(subdir):]
Brendan Cully
hgweb: let hgwebdir browse subdirectories
r4841
Alexis S. L. Carvalho
Pass a ui from create_server to hgwebdir and a repo from hgwebdir to hgweb...
r4079 u = ui.ui(parentui=parentui)
Eric Hopper
Final stage of the hgweb split up....
r2356 try:
u.readconfig(os.path.join(path, '.hg', 'hgrc'))
Alexis S. L. Carvalho
hgwebdir: ignore hgrc parse errors while building the index page...
r5332 except Exception, e:
u.warn(_('error reading %s/.hg/hgrc: %s\n' % (path, e)))
continue
Alexis S. L. Carvalho
use untrusted settings in hgwebdir
r3556 def get(section, name, default=None):
return u.config(section, name, default, untrusted=True)
Eric Hopper
Final stage of the hgweb split up....
r2356
Markus F.X.J. Oberhumer
Add option "hidden" to hgwebdir....
r4709 if u.configbool("web", "hidden", untrusted=True):
continue
Eric Hopper
Final stage of the hgweb split up....
r2356 url = ('/'.join([req.env["REQUEST_URI"].split('?')[0], name])
Brendan Cully
Teach hgwebdir about new interface
r3262 .replace("//", "/")) + '/'
Eric Hopper
Final stage of the hgweb split up....
r2356
# update time with local timezone
try:
d = (get_mtime(path), util.makedate()[1])
except OSError:
continue
contact = (get("ui", "username") or # preferred
get("web", "contact") or # deprecated
get("web", "author", "")) # also
description = get("web", "description", "")
name = get("web", "name", name)
row = dict(contact=contact or "unknown",
contact_sort=contact.upper() or "unknown",
name=name,
name_sort=name,
url=url,
description=description or "unknown",
description_sort=description.upper() or "unknown",
lastchange=d,
lastchange_sort=d[1]-d[0],
Thomas Arendsen Hein
Keep session variables when linking from hgwebdir's index to repositories.
r3365 sessionvars=sessionvars,
Eric Hopper
Final stage of the hgweb split up....
r2356 archives=archivelist(u, "tip", url))
if (not sortcolumn
or (sortcolumn, descending) == self.repos_sorted):
# fast path for unsorted output
Thomas Arendsen Hein
hgweb: use generator to count parity of horizontal stripes for easier reading....
r4462 row['parity'] = parity.next()
Eric Hopper
Final stage of the hgweb split up....
r2356 yield row
else:
rows.append((row["%s_sort" % sortcolumn], row))
if rows:
rows.sort()
if descending:
rows.reverse()
for key, row in rows:
Thomas Arendsen Hein
hgweb: use generator to count parity of horizontal stripes for easier reading....
r4462 row['parity'] = parity.next()
Eric Hopper
Final stage of the hgweb split up....
r2356 yield row
Brendan Cully
hgweb: let hgwebdir browse subdirectories
r4841 def makeindex(req, subdir=""):
sortable = ["name", "description", "contact", "lastchange"]
sortcolumn, descending = self.repos_sorted
if req.form.has_key('sort'):
sortcolumn = req.form['sort'][0]
descending = sortcolumn.startswith('-')
if descending:
sortcolumn = sortcolumn[1:]
if sortcolumn not in sortable:
sortcolumn = ""
sort = [("sort_%s" % column,
"%s%s" % ((not descending and column == sortcolumn)
and "-" or "", column))
for column in sortable]
req.write(tmpl("index", entries=entries, subdir=subdir,
sortcolumn=sortcolumn, descending=descending,
**dict(sort)))
Alexis S. L. Carvalho
hgwebdir: break templater -> templater circular reference...
r4244 try:
virtual = req.env.get("PATH_INFO", "").strip('/')
if virtual.startswith('static/'):
static = os.path.join(templater.templatepath(), 'static')
fname = virtual[7:]
req.write(staticfile(static, fname, req) or
tmpl('error', error='%r not found' % fname))
elif virtual:
Brendan Cully
hgweb: browse subdirectories before checking whether parent directory is also a repository
r4850 repos = dict(self.repos)
Alexis S. L. Carvalho
hgwebdir: break templater -> templater circular reference...
r4244 while virtual:
Brendan Cully
hgweb: browse subdirectories before checking whether parent directory is also a repository
r4850 real = repos.get(virtual)
Alexis S. L. Carvalho
hgwebdir: break templater -> templater circular reference...
r4244 if real:
Brendan Cully
hgwebdir: check for repo foo before browsing subdirectories of foo/
r4852 req.env['REPO_NAME'] = virtual
try:
repo = hg.repository(parentui, real)
hgweb(repo).run_wsgi(req)
except IOError, inst:
req.write(tmpl("error", error=inst.strerror))
except hg.RepoError, inst:
req.write(tmpl("error", error=str(inst)))
return
# browse subdirectories
subdir = virtual + '/'
if [r for r in repos if r.startswith(subdir)]:
makeindex(req, subdir)
return
Alexis S. L. Carvalho
hgwebdir: break templater -> templater circular reference...
r4244 up = virtual.rfind('/')
if up < 0:
break
virtual = virtual[:up]
Thomas Arendsen Hein
removed trailing whitespace
r4957
Brendan Cully
hgwebdir: check for repo foo before browsing subdirectories of foo/
r4852 req.write(tmpl("notfound", repo=virtual))
Eric Hopper
Final stage of the hgweb split up....
r2356 else:
Alexis S. L. Carvalho
hgwebdir: break templater -> templater circular reference...
r4244 if req.form.has_key('static'):
static = os.path.join(templater.templatepath(), "static")
fname = req.form['static'][0]
req.write(staticfile(static, fname, req)
or tmpl("error", error="%r not found" % fname))
else:
Brendan Cully
hgweb: let hgwebdir browse subdirectories
r4841 makeindex(req)
Alexis S. L. Carvalho
hgwebdir: break templater -> templater circular reference...
r4244 finally:
tmpl = None