##// END OF EJS Templates
debugsparse: abort if the repository is not sparse instead of ui.status()...
debugsparse: abort if the repository is not sparse instead of ui.status() This is similar to what narrow extension does. Differential Revision: https://phab.mercurial-scm.org/D6149

File last commit:

r41401:876494fd default
r42155:f9344d04 default
Show More
hgwebdir_mod.py
540 lines | 18.9 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 #
Martin Geisler
updated license to be explicit about GPL version 2
r8225 # This software may be used and distributed according to the terms of the
Matt Mackall
Update license to GPLv2+
r10263 # GNU General Public License version 2 or any later version.
Eric Hopper
Final stage of the hgweb split up....
r2356
Yuya Nishihara
hgweb: use absolute_import
r27046 from __future__ import absolute_import
Gregory Szorc
hgweb: garbage collect on every request...
r36929 import gc
Yuya Nishihara
hgweb: use absolute_import
r27046 import os
import time
from ..i18n import _
from .common import (
ErrorResponse,
HTTP_SERVER_ERROR,
Gregory Szorc
hgweb: support Content Security Policy...
r30766 cspvalues,
Yuya Nishihara
hgweb: use absolute_import
r27046 get_contact,
get_mtime,
ismember,
paritygen,
staticfile,
Gregory Szorc
hgweb: port to new response API...
r36923 statusmessage,
Yuya Nishihara
hgweb: use absolute_import
r27046 )
from .. import (
Boris Feld
configitems: register the 'web.refreshinterval' config
r34241 configitems,
Yuya Nishihara
hgweb: use absolute_import
r27046 encoding,
error,
Yuya Nishihara
hgweb: load globally-enabled extensions explicitly...
r40759 extensions,
Yuya Nishihara
hgweb: use absolute_import
r27046 hg,
Gregory Szorc
hgweb: profile HTTP requests...
r29787 profiling,
Yuya Nishihara
py3: remove use of str() in hgwebdir...
r34354 pycompat,
Yuya Nishihara
hgweb: use registrar to add "motd" template keyword...
r38964 registrar,
Yuya Nishihara
hgweb: use absolute_import
r27046 scmutil,
templater,
Yuya Nishihara
hgwebdir: wrap {entries} with mappinggenerator...
r37526 templateutil,
Yuya Nishihara
hgweb: use absolute_import
r27046 ui as uimod,
util,
)
from . import (
hgweb_mod,
Gregory Szorc
hgweb: rename req to wsgireq...
r36822 request as requestmod,
Yuya Nishihara
hgweb: use absolute_import
r27046 webutil,
wsgicgi,
)
Boris Feld
util: extract all date-related utils in utils/dateutil module...
r36625 from ..utils import dateutil
Eric Hopper
Final stage of the hgweb split up....
r2356
Dirkjan Ochtman
hgweb: some cleanups in hgwebdir, remove double defaults...
r8215 def cleannames(items):
return [(util.pconvert(name).strip('/'), path) for name, path in items]
Eric Hopper
Final stage of the hgweb split up....
r2356
Jeremy Whitlock
hgweb: make hgwebdir handle dict/list paths the same as config paths...
r8529 def findrepos(paths):
Dirkjan Ochtman
hgweb: use a tuple-list instead of dictionary for append-only store
r9723 repos = []
Jeremy Whitlock
hgweb: make hgwebdir handle dict/list paths the same as config paths...
r8529 for prefix, root in cleannames(paths):
roothead, roottail = os.path.split(root)
Mads Kiilerich
help: improve hgweb help...
r17104 # "foo = /bar/*" or "foo = /bar/**" lets every repo /bar/N in or below
# /bar/ be served as as foo/N .
# '*' will not search inside dirs with .hg (except .hg/patches),
# '**' will search inside dirs with .hg (and thus also find subrepos).
Jeremy Whitlock
hgweb: make hgwebdir handle dict/list paths the same as config paths...
r8529 try:
recurse = {'*': False, '**': True}[roottail]
except KeyError:
Dirkjan Ochtman
hgweb: use a tuple-list instead of dictionary for append-only store
r9723 repos.append((prefix, root))
Jeremy Whitlock
hgweb: make hgwebdir handle dict/list paths the same as config paths...
r8529 continue
Mads Kiilerich
hgwebdir: allow pure relative globs in paths...
r11677 roothead = os.path.normpath(os.path.abspath(roothead))
Adrian Buehlmann
move walkrepos from util to scmutil
r13975 paths = scmutil.walkrepos(roothead, followsym=True, recurse=recurse)
Mads Kiilerich
hgweb: doctest of url creation from wildcard expansion
r13402 repos.extend(urlrepos(prefix, roothead, paths))
Dirkjan Ochtman
hgweb: use a tuple-list instead of dictionary for append-only store
r9723 return repos
Jeremy Whitlock
hgweb: make hgwebdir handle dict/list paths the same as config paths...
r8529
Mads Kiilerich
hgweb: doctest of url creation from wildcard expansion
r13402 def urlrepos(prefix, roothead, paths):
"""yield url paths and filesystem paths from a list of repo paths
Patrick Mezard
test-doctest: handle unix/windows path discrepancies
r13538 >>> conv = lambda seq: [(v, util.pconvert(p)) for v,p in seq]
Yuya Nishihara
doctest: bulk-replace string literals with b'' for Python 3...
r34133 >>> conv(urlrepos(b'hg', b'/opt', [b'/opt/r', b'/opt/r/r', b'/opt']))
Mads Kiilerich
hgweb: make paths wildcards expanding in a repo root match repo correctly...
r13403 [('hg/r', '/opt/r'), ('hg/r/r', '/opt/r/r'), ('hg', '/opt')]
Yuya Nishihara
doctest: bulk-replace string literals with b'' for Python 3...
r34133 >>> conv(urlrepos(b'', b'/opt', [b'/opt/r', b'/opt/r/r', b'/opt']))
Mads Kiilerich
hgweb: doctest of url creation from wildcard expansion
r13402 [('r', '/opt/r'), ('r/r', '/opt/r/r'), ('', '/opt')]
"""
for path in paths:
path = os.path.normpath(path)
Mads Kiilerich
hgweb: make paths wildcards expanding in a repo root match repo correctly...
r13403 yield (prefix + '/' +
util.pconvert(path[len(roothead):]).lstrip('/')).strip('/'), path
Mads Kiilerich
hgweb: doctest of url creation from wildcard expansion
r13402
Gregory Szorc
hgweb: move readallowed to a standalone function...
r36906 def readallowed(ui, req):
"""Check allow_read and deny_read config options of a repo's ui object
to determine user permissions. By default, with neither option set (or
both empty), allow all users to read the repo. There are two ways a
user can be denied read access: (1) deny_read is not empty, and the
user is unauthenticated or deny_read contains user (or *), and (2)
allow_read is not empty and the user is not in allow_read. Return True
if user is allowed to read the repo, else return False."""
user = req.remoteuser
deny_read = ui.configlist('web', 'deny_read', untrusted=True)
if deny_read and (not user or ismember(ui, user, deny_read)):
return False
allow_read = ui.configlist('web', 'allow_read', untrusted=True)
# by default, allow reading if no allow_read option has been set
if not allow_read or ismember(ui, user, allow_read):
return True
return False
Gregory Szorc
hgweb: don't pass wsgireq to makeindex and other functions...
r36920 def rawindexentries(ui, repos, req, subdir=''):
Gregory Szorc
hgweb: move rawentries() to a standalone function...
r36908 descend = ui.configbool('web', 'descend')
collapse = ui.configbool('web', 'collapse')
seenrepos = set()
seendirs = set()
for name, path in repos:
if not name.startswith(subdir):
continue
name = name[len(subdir):]
directory = False
if '/' in name:
if not descend:
continue
nameparts = name.split('/')
rootname = nameparts[0]
if not collapse:
pass
elif rootname in seendirs:
continue
elif rootname in seenrepos:
pass
else:
directory = True
name = rootname
# redefine the path to refer to the directory
discarded = '/'.join(nameparts[1:])
# remove name parts plus accompanying slash
path = path[:-len(discarded) - 1]
try:
Martin von Zweigbergk
cleanup: delete lots of unused local variables...
r41401 hg.repository(ui, path)
Gregory Szorc
hgweb: move rawentries() to a standalone function...
r36908 directory = False
except (IOError, error.RepoError):
pass
Gregory Szorc
hgweb: rewrite path generation for index entries...
r36918 parts = [
Gregory Szorc
hgweb: don't pass wsgireq to makeindex and other functions...
r36920 req.apppath.strip('/'),
Gregory Szorc
hgweb: rewrite path generation for index entries...
r36918 subdir.strip('/'),
name.strip('/'),
]
url = '/' + '/'.join(p for p in parts if p) + '/'
Matt Mackall
hgweb: extract the path logic from updatereqenv and add doctests
r15003
Gregory Szorc
hgweb: move rawentries() to a standalone function...
r36908 # show either a directory entry or a repository
if directory:
# get the directory's time information
try:
d = (get_mtime(path), dateutil.makedate()[1])
except OSError:
continue
# add '/' to the name to make it obvious that
# the entry is a directory, not a regular repository
row = {'contact': "",
'contact_sort': "",
'name': name + '/',
'name_sort': name,
'url': url,
'description': "",
'description_sort': "",
'lastchange': d,
'lastchange_sort': d[1] - d[0],
Yuya Nishihara
hgweb: wrap {archives} with mappinglist...
r37533 'archives': templateutil.mappinglist([]),
Gregory Szorc
hgweb: move rawentries() to a standalone function...
r36908 'isdirectory': True,
Yuya Nishihara
hgweb: wrap {labels} by hybridlist()...
r37528 'labels': templateutil.hybridlist([], name='label'),
Gregory Szorc
hgweb: move rawentries() to a standalone function...
r36908 }
seendirs.add(name)
yield row
continue
u = ui.copy()
try:
u.readconfig(os.path.join(path, '.hg', 'hgrc'))
except Exception as e:
u.warn(_('error reading %s/.hg/hgrc: %s\n') % (path, e))
continue
def get(section, name, default=uimod._unset):
return u.config(section, name, default, untrusted=True)
if u.configbool("web", "hidden", untrusted=True):
continue
if not readallowed(u, req):
continue
Matt Mackall
hgweb: extract the path logic from updatereqenv and add doctests
r15003
Gregory Szorc
hgweb: move rawentries() to a standalone function...
r36908 # update time with local timezone
try:
r = hg.repository(ui, path)
except IOError:
u.warn(_('error accessing repository at %s\n') % path)
continue
except error.RepoError:
u.warn(_('error accessing repository at %s\n') % path)
continue
try:
d = (get_mtime(r.spath), dateutil.makedate()[1])
except OSError:
continue
contact = get_contact(get)
description = get("web", "description")
seenrepos.add(name)
name = get("web", "name", name)
Yuya Nishihara
hgweb: wrap {labels} by hybridlist()...
r37528 labels = u.configlist('web', 'labels', untrusted=True)
Gregory Szorc
hgweb: move rawentries() to a standalone function...
r36908 row = {'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],
Yuya Nishihara
hgweb: move archivelist() of hgwebdir to webutil
r37531 'archives': webutil.archivelist(u, "tip", url),
Gregory Szorc
hgweb: move rawentries() to a standalone function...
r36908 'isdirectory': None,
Yuya Nishihara
hgweb: wrap {labels} by hybridlist()...
r37528 'labels': templateutil.hybridlist(labels, name='label'),
Gregory Szorc
hgweb: move rawentries() to a standalone function...
r36908 }
yield row
Yuya Nishihara
hgwebdir: wrap {entries} with mappinggenerator...
r37526 def _indexentriesgen(context, ui, repos, req, stripecount, sortcolumn,
descending, subdir):
Gregory Szorc
hgweb: don't pass wsgireq to makeindex and other functions...
r36920 rows = rawindexentries(ui, repos, req, subdir=subdir)
Gregory Szorc
hgweb: extract entries() to standalone function...
r36909
sortdefault = None, False
if sortcolumn and sortdefault != (sortcolumn, descending):
sortkey = '%s_sort' % sortcolumn
rows = sorted(rows, key=lambda x: x[sortkey],
reverse=descending)
for row, parity in zip(rows, paritygen(stripecount)):
row['parity'] = parity
yield row
Matt Mackall
hgweb: extract the path logic from updatereqenv and add doctests
r15003
Yuya Nishihara
hgwebdir: wrap {entries} with mappinggenerator...
r37526 def indexentries(ui, repos, req, stripecount, sortcolumn='',
descending=False, subdir=''):
args = (ui, repos, req, stripecount, sortcolumn, descending, subdir)
return templateutil.mappinggenerator(_indexentriesgen, args=args)
Eric Hopper
Final stage of the hgweb split up....
r2356 class hgwebdir(object):
Gregory Szorc
hgweb: add some documentation...
r26132 """HTTP server for multiple repositories.
Given a configuration, different repositories will be served depending
on the request path.
Instances are typically used as WSGI applications.
"""
Matt Mackall
hgweb: kill parentui references
r8191 def __init__(self, conf, baseui=None):
Bryan O'Sullivan
hgwebdir: refresh configuration periodically...
r8371 self.conf = conf
self.baseui = baseui
Gregory Szorc
hgweb: make refresh interval configurable...
r26072 self.ui = None
Bryan O'Sullivan
hgwebdir: refresh configuration periodically...
r8371 self.lastrefresh = 0
Thomas Arendsen Hein
Do not overwrite motd attribute of hgwebdir instances on refresh....
r9903 self.motd = None
Bryan O'Sullivan
hgwebdir: refresh configuration periodically...
r8371 self.refresh()
Yuya Nishihara
hgweb: load globally-enabled extensions explicitly...
r40759 if not baseui:
# set up environment for new ui
extensions.loadall(self.ui)
Yuya Nishihara
extensions: add "uipopulate" hook, called per instance, not per process...
r40760 extensions.populateui(self.ui)
Eric Hopper
Final stage of the hgweb split up....
r2356
Bryan O'Sullivan
hgwebdir: refresh configuration periodically...
r8371 def refresh(self):
Gregory Szorc
hgweb: make refresh interval configurable...
r26072 if self.ui:
Boris Feld
configitems: register the 'web.refreshinterval' config
r34241 refreshinterval = self.ui.configint('web', 'refreshinterval')
else:
item = configitems.coreitems['web']['refreshinterval']
refreshinterval = item.default
Gregory Szorc
hgweb: make refresh interval configurable...
r26072
# refreshinterval <= 0 means to always refresh.
if (refreshinterval > 0 and
self.lastrefresh + refreshinterval > time.time()):
Bryan O'Sullivan
hgwebdir: refresh configuration periodically...
r8371 return
if self.baseui:
Matt Mackall
hgweb: fix race in refreshing repo list (issue2188)
r11239 u = self.baseui.copy()
Eric Hopper
Final stage of the hgweb split up....
r2356 else:
Yuya Nishihara
ui: factor out ui.load() to create a ui without loading configs (API)...
r30559 u = uimod.ui.load()
Mads Kiilerich
config: set a 'source' in most cases where config don't come from file but code...
r20790 u.setconfig('ui', 'report_untrusted', 'off', 'hgwebdir')
u.setconfig('ui', 'nontty', 'true', 'hgwebdir')
Pierre-Yves David
hgewb: disable progress when serving (issue4582)...
r25488 # displaying bundling progress bar while serving feels wrong and may
# break some wsgi implementations.
u.setconfig('progress', 'disable', 'true', 'hgweb')
Matt Mackall
ui: refactor option setting...
r8136
Jeremy Whitlock
hgweb: make hgwebdir handle dict/list paths the same as config paths...
r8529 if not isinstance(self.conf, (dict, list, tuple)):
map = {'paths': 'hgweb-paths'}
Matt Mackall
hgweb: abort if config file isn't found
r13214 if not os.path.exists(self.conf):
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_('config file %s not found!') % self.conf)
Matt Mackall
hgweb: fix race in refreshing repo list (issue2188)
r11239 u.readconfig(self.conf, remap=map, trust=True)
timeless
hgweb: support multiple directories for the same path...
r13667 paths = []
for name, ignored in u.configitems('hgweb-paths'):
for path in u.configlist('hgweb-paths', name):
paths.append((name, path))
Jeremy Whitlock
hgweb: make hgwebdir handle dict/list paths the same as config paths...
r8529 elif isinstance(self.conf, (list, tuple)):
paths = self.conf
elif isinstance(self.conf, dict):
paths = self.conf.items()
Yuya Nishihara
extensions: add "uipopulate" hook, called per instance, not per process...
r40760 extensions.populateui(u)
Alexander Solovyov
hgwebdir: read --webdir-conf as actual configuration to ui (issue1586)...
r8345
Matt Mackall
hgweb: fix race in refreshing repo list (issue2188)
r11239 repos = findrepos(paths)
for prefix, root in u.configitems('collections'):
prefix = util.pconvert(prefix)
Adrian Buehlmann
move walkrepos from util to scmutil
r13975 for path in scmutil.walkrepos(root, followsym=True):
Matt Mackall
hgweb: fix race in refreshing repo list (issue2188)
r11239 repo = os.path.normpath(path)
name = util.pconvert(repo)
if name.startswith(prefix):
name = name[len(prefix):]
repos.append((name.lstrip('/'), repo))
self.repos = repos
self.ui = u
Boris Feld
configitems: register the 'web.encoding' config
r34236 encoding.encoding = self.ui.config('web', 'encoding')
Boris Feld
configitems: register the 'web.style' config
r34243 self.style = self.ui.config('web', 'style')
Boris Feld
hgwebdir: read 'web.template' untrusted...
r34245 self.templatepath = self.ui.config('web', 'templates', untrusted=False)
Boris Feld
configitems: register the 'web.stripes' config
r34242 self.stripecount = self.ui.config('web', 'stripes')
Dirkjan Ochtman
hgweb: extract config values after reading webdir-config
r8621 if self.stripecount:
self.stripecount = int(self.stripecount)
Boris Feld
configitems: register the 'web.prefix' config
r34240 prefix = self.ui.config('web', 'prefix')
Angel Ezquerra
hgwebdir: use web.prefix when creating url breadcrumbs (issue3790)...
r18515 if prefix.startswith('/'):
prefix = prefix[1:]
if prefix.endswith('/'):
prefix = prefix[:-1]
self.prefix = prefix
Bryan O'Sullivan
hgwebdir: refresh configuration periodically...
r8371 self.lastrefresh = time.time()
Eric Hopper
Final stage of the hgweb split up....
r2356
Eric Hopper
Arrange for old copies of CGI scripts to still work.
r2535 def run(self):
Pulkit Goyal
py3: replace os.environ with encoding.environ (part 3 of 5)
r30636 if not encoding.environ.get('GATEWAY_INTERFACE',
'').startswith("CGI/1."):
Martin Geisler
wrap string literals in error messages
r8663 raise RuntimeError("This function is only intended to be "
"called while running as a CGI script.")
Dirkjan Ochtman
Less indirection in the WSGI web interface. This simplifies some code, and makes it more compliant with WSGI.
r5566 wsgicgi.launch(self)
def __call__(self, env, respond):
Gregory Szorc
hgweb: support constructing URLs from an alternate base URL...
r36916 baseurl = self.ui.config('web', 'baseurl')
Gregory Szorc
hgweb: remove wsgirequest (API)...
r36928 req = requestmod.parserequestfromenv(env, altbaseurl=baseurl)
res = requestmod.wsgiresponse(req, respond)
Mark Edgington
hgweb: support for deny_read/allow_read options...
r7336
Gregory Szorc
hgweb: remove wsgirequest (API)...
r36928 return self.run_wsgi(req, res)
Mark Edgington
hgweb: support for deny_read/allow_read options...
r7336
Gregory Szorc
hgweb: remove wsgirequest (API)...
r36928 def run_wsgi(self, req, res):
profile: drop maybeprofile...
r32788 profile = self.ui.configbool('profiling', 'enabled')
with profiling.profile(self.ui, enabled=profile):
Gregory Szorc
hgweb: garbage collect on every request...
r36929 try:
Augie Fackler
merge with stable
r36996 for r in self._runwsgi(req, res):
Gregory Szorc
hgweb: garbage collect on every request...
r36929 yield r
finally:
# There are known cycles in localrepository that prevent
# those objects (and tons of held references) from being
# collected through normal refcounting. We mitigate those
# leaks by performing an explicit GC on every request.
# TODO remove this once leaks are fixed.
# TODO only run this on requests that create localrepository
# instances instead of every request.
gc.collect()
Gregory Szorc
hgweb: abstract call to hgwebdir wsgi function...
r29786
Gregory Szorc
hgweb: remove wsgirequest (API)...
r36928 def _runwsgi(self, req, res):
Dirkjan Ochtman
hgwebdir: split out makeindex function, facilitate test failure diagnosis
r5601 try:
Matt Mackall
hgweb: use try/except/finally
r25083 self.refresh()
Dirkjan Ochtman
hgwebdir: refactor inner loop
r5603
Gregory Szorc
hgweb: support Content Security Policy...
r30766 csp, nonce = cspvalues(self.ui)
if csp:
Gregory Szorc
hgweb: port static file handling to new response API...
r36889 res.headers['Content-Security-Policy'] = csp
Gregory Szorc
hgweb: support Content Security Policy...
r30766
Gregory Szorc
hgweb: replace PATH_INFO with dispatchpath...
r36919 virtual = req.dispatchpath.strip('/')
Gregory Szorc
hgweb: support Content Security Policy...
r30766 tmpl = self.templater(req, nonce)
Yuya Nishihara
templater: factor out helper that renders named template as string...
r37004 ctype = tmpl.render('mimetype', {'encoding': encoding.encoding})
Thomas Arendsen Hein
Removed tabs and trailing whitespace in python files
r5760
Gregory Szorc
hgweb: port static file handling to new response API...
r36889 # Global defaults. These can be overridden by any handler.
res.status = '200 Script output follows'
res.headers['Content-Type'] = ctype
Thomas Arendsen Hein
Removed tabs and trailing whitespace in python files
r5760
Matt Mackall
hgweb: use try/except/finally
r25083 # a static file
Gregory Szorc
hgweb: perform all parameter lookup via qsparams...
r36881 if virtual.startswith('static/') or 'static' in req.qsparams:
Matt Mackall
hgweb: use try/except/finally
r25083 if virtual.startswith('static/'):
fname = virtual[7:]
else:
Gregory Szorc
hgweb: perform all parameter lookup via qsparams...
r36881 fname = req.qsparams['static']
Yuya Nishihara
hgweb: register web.static to the config table...
r39829 static = self.ui.config("web", "static", untrusted=False)
Matt Mackall
hgweb: use try/except/finally
r25083 if not static:
tp = self.templatepath or templater.templatepaths()
if isinstance(tp, str):
tp = [tp]
static = [os.path.join(p, 'static') for p in tp]
Gregory Szorc
hgweb: port static file handling to new response API...
r36889
staticfile(static, fname, res)
return res.sendresponse()
Dirkjan Ochtman
hgwebdir: refactor inner loop
r5603
Matt Mackall
hgweb: use try/except/finally
r25083 # top-level index
Matt Harbison
hgwebdir: add support for explicit index files...
r31482
repos = dict(self.repos)
Matt Harbison
hgwebdir: allow a repository to be hosted at "/"...
r32004 if (not virtual or virtual == 'index') and virtual not in repos:
Gregory Szorc
hgweb: use modern response type for index generation...
r36921 return self.makeindex(req, res, tmpl)
Dirkjan Ochtman
hgwebdir: split out makeindex function, facilitate test failure diagnosis
r5601
Matt Mackall
hgweb: use try/except/finally
r25083 # nested indexes and hgwebs
Thomas Arendsen Hein
Removed trailing spaces from everything except test output
r6210
Matt Harbison
hgwebdir: add support for explicit index files...
r31482 if virtual.endswith('/index') and virtual not in repos:
subdir = virtual[:-len('index')]
if any(r.startswith(subdir) for r in repos):
Gregory Szorc
hgweb: use modern response type for index generation...
r36921 return self.makeindex(req, res, tmpl, subdir)
Matt Harbison
hgwebdir: add support for explicit index files...
r31482
Matt Harbison
hgwebdir: allow a repository to be hosted at "/"...
r32004 def _virtualdirs():
# Check the full virtual path, each parent, and the root ('')
if virtual != '':
yield virtual
for p in util.finddirs(virtual):
yield p
yield ''
for virtualrepo in _virtualdirs():
Matt Mackall
hgweb: use try/except/finally
r25083 real = repos.get(virtualrepo)
if real:
Gregory Szorc
hgweb: refactor repository name URL parsing...
r36913 # Re-parse the WSGI environment to take into account our
# repository path component.
Augie Fackler
hgwebdir: un-bytes the env dict before re-parsing env...
r37730 uenv = req.rawenv
if pycompat.ispy3:
uenv = {k.decode('latin1'): v for k, v in
uenv.iteritems()}
Gregory Szorc
hgweb: remove wsgirequest (API)...
r36928 req = requestmod.parserequestfromenv(
Augie Fackler
hgwebdir: un-bytes the env dict before re-parsing env...
r37730 uenv, reponame=virtualrepo,
Gregory Szorc
hgweb: reuse body file object when hgwebdir calls hgweb (issue5851)...
r37836 altbaseurl=self.ui.config('web', 'baseurl'),
# Reuse wrapped body file object otherwise state
# tracking can get confused.
bodyfh=req.bodyfh)
Matt Mackall
hgweb: use try/except/finally
r25083 try:
# ensure caller gets private copy of ui
repo = hg.repository(self.ui.copy(), real)
Gregory Szorc
hgweb: remove wsgirequest (API)...
r36928 return hgweb_mod.hgweb(repo).run_wsgi(req, res)
Gregory Szorc
global: mass rewrite to use modern exception syntax...
r25660 except IOError as inst:
Augie Fackler
python3: wrap all uses of <exception>.strerror with strtolocal...
r34024 msg = encoding.strtolocal(inst.strerror)
Matt Mackall
hgweb: use try/except/finally
r25083 raise ErrorResponse(HTTP_SERVER_ERROR, msg)
Gregory Szorc
global: mass rewrite to use modern exception syntax...
r25660 except error.RepoError as inst:
Yuya Nishihara
py3: remove use of str() in hgwebdir...
r34354 raise ErrorResponse(HTTP_SERVER_ERROR, bytes(inst))
Dirkjan Ochtman
hgwebdir: split out makeindex function, facilitate test failure diagnosis
r5601
Matt Mackall
hgweb: use try/except/finally
r25083 # browse subdirectories
subdir = virtual + '/'
if [r for r in repos if r.startswith(subdir)]:
Gregory Szorc
hgweb: use modern response type for index generation...
r36921 return self.makeindex(req, res, tmpl, subdir)
Dirkjan Ochtman
hgwebdir: refactor inner loop
r5603
Matt Mackall
hgweb: use try/except/finally
r25083 # prefixes not found
Gregory Szorc
hgweb: port to new response API...
r36923 res.status = '404 Not Found'
Yuya Nishihara
templater: use named function to expand template against mapping dict (API)...
r37037 res.setbodygen(tmpl.generate('notfound', {'repo': virtual}))
Gregory Szorc
hgweb: port to new response API...
r36923 return res.sendresponse()
Thomas Arendsen Hein
Removed tabs and trailing whitespace in python files
r5760
Gregory Szorc
hgweb: port to new response API...
r36923 except ErrorResponse as e:
res.status = statusmessage(e.code, pycompat.bytestr(e))
Yuya Nishihara
templater: use named function to expand template against mapping dict (API)...
r37037 res.setbodygen(tmpl.generate('error', {'error': e.message or ''}))
Gregory Szorc
hgweb: port to new response API...
r36923 return res.sendresponse()
Dirkjan Ochtman
hgwebdir: split out makeindex function, facilitate test failure diagnosis
r5601 finally:
tmpl = None
Gregory Szorc
hgweb: use modern response type for index generation...
r36921 def makeindex(self, req, res, tmpl, subdir=""):
Bryan O'Sullivan
hgwebdir: refresh configuration periodically...
r8371 self.refresh()
Dirkjan Ochtman
hgwebdir: split out makeindex function, facilitate test failure diagnosis
r5601 sortable = ["name", "description", "contact", "lastchange"]
Gregory Szorc
hgweb: extract entries() to standalone function...
r36909 sortcolumn, descending = None, False
Gregory Szorc
hgweb: remove some use of wsgireq in hgwebdir...
r36905 if 'sort' in req.qsparams:
sortcolumn = req.qsparams['sort']
Dirkjan Ochtman
hgwebdir: split out makeindex function, facilitate test failure diagnosis
r5601 descending = sortcolumn.startswith('-')
if descending:
sortcolumn = sortcolumn[1:]
if sortcolumn not in sortable:
sortcolumn = ""
Brendan Cully
hgweb: let hgwebdir browse subdirectories
r4841
Dirkjan Ochtman
hgwebdir: split out makeindex function, facilitate test failure diagnosis
r5601 sort = [("sort_%s" % column,
"%s%s" % ((not descending and column == sortcolumn)
and "-" or "", column))
for column in sortable]
Dirkjan Ochtman
hgweb: move HTTP content types out of header templates...
r5928
Bryan O'Sullivan
hgwebdir: refresh configuration periodically...
r8371 self.refresh()
Brendan Cully
Support web.baseurl in hgwebdir, overriding SCRIPT_NAME
r6221
Gregory Szorc
hgweb: don't pass wsgireq to makeindex and other functions...
r36920 entries = indexentries(self.ui, self.repos, req,
Gregory Szorc
hgweb: extract entries() to standalone function...
r36909 self.stripecount, sortcolumn=sortcolumn,
descending=descending, subdir=subdir)
Brendan Cully
Support web.baseurl in hgwebdir, overriding SCRIPT_NAME
r6221
Yuya Nishihara
templater: use named function to expand template against mapping dict (API)...
r37037 mapping = {
'entries': entries,
'subdir': subdir,
'pathdef': hgweb_mod.makebreadcrumb('/' + subdir, self.prefix),
'sortcolumn': sortcolumn,
'descending': descending,
}
mapping.update(sort)
res.setbodygen(tmpl.generate('index', mapping))
Gregory Szorc
hgweb: use modern response type for index generation...
r36921 return res.sendresponse()
Dirkjan Ochtman
hgwebdir: split out templater creation
r5602
Gregory Szorc
hgweb: support Content Security Policy...
r30766 def templater(self, req, nonce):
Dirkjan Ochtman
hgwebdir: split out templater creation
r5602
Boris Feld
web: use '_unset' default value for proxy config method...
r34223 def config(section, name, default=uimod._unset, untrusted=True):
Matt Mackall
hgweb: kill parentui references
r8191 return self.ui.config(section, name, default, untrusted)
Dirkjan Ochtman
hgwebdir: split out templater creation
r5602
Dirkjan Ochtman
hgweb: use new sessionvars code in hgwebdir, too
r8216 vars = {}
Augie Fackler
hgweb: extract function for loading style from request context...
r34516 styles, (style, mapfile) = hgweb_mod.getstyle(req, config,
self.templatepath)
Dirkjan Ochtman
hgweb: don't choke when an inexistent style is requested (issue1901)
r9842 if style == styles[0]:
vars['style'] = style
Matt Mackall
many, many trivial check-code fixups
r10282
Gregory Szorc
hgweb: don't use raw string for session vars...
r41349 sessionvars = webutil.sessionvars(vars, '?')
Boris Feld
configitems: register the 'web.logourl' config
r34613 logourl = config('web', 'logourl')
Boris Feld
configitems: register the 'web.logoimg' config
r34612 logoimg = config('web', 'logoimg')
Gregory Szorc
hgweb: construct static URL like hgweb does...
r36911 staticurl = (config('web', 'staticurl')
Cédric Krier
hgweb: strip trailing '/' in apppath before appending '/static/' (issue5943)...
r38768 or req.apppath.rstrip('/') + '/static/')
Dirkjan Ochtman
hgwebdir: split out templater creation
r5602 if not staticurl.endswith('/'):
staticurl += '/'
Yuya Nishihara
templater: separate function to create templater from map file (API)...
r28954 defaults = {
"encoding": encoding.encoding,
Gregory Szorc
hgweb: pass modern request type into templater()...
r36922 "url": req.apppath + '/',
Yuya Nishihara
templater: separate function to create templater from map file (API)...
r28954 "logourl": logourl,
"logoimg": logoimg,
"staticurl": staticurl,
"sessionvars": sessionvars,
"style": style,
Gregory Szorc
hgweb: support Content Security Policy...
r30766 "nonce": nonce,
Yuya Nishihara
templater: separate function to create templater from map file (API)...
r28954 }
Yuya Nishihara
hgweb: use registrar to add "motd" template keyword...
r38964 templatekeyword = registrar.templatekeyword(defaults)
@templatekeyword('motd', requires=())
def motd(context, mapping):
if self.motd is not None:
yield self.motd
else:
yield config('web', 'motd')
Yuya Nishihara
templater: separate function to create templater from map file (API)...
r28954 tmpl = templater.templater.frommapfile(mapfile, defaults=defaults)
Dirkjan Ochtman
hgwebdir: split out templater creation
r5602 return tmpl