##// END OF EJS Templates
discovery: move handling of sampling special case inside sampling function...
discovery: move handling of sampling special case inside sampling function The handling of cases where the number of revisions to sample is smaller than the sample size can be moved with the sample function themselves. This simplifies main logic, preparing a coming refactoring.

File last commit:

r40760:c93d046d default
r41146:3c85a62d 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:
r = hg.repository(ui, path)
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: always use "?" when writing session vars...
r36823 sessionvars = webutil.sessionvars(vars, r'?')
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