##// END OF EJS Templates
lookup: optimize '.'...
lookup: optimize '.' Remove error messages as behavior is documented in hg help revs

File last commit:

r6460:a63aed91 merge default
r6736:369ddc9c default
Show More
hgweb_mod.py
379 lines | 14.2 KiB | text/x-python | PythonLexer
Eric Hopper
Fixing up comment headers for split up code.
r2391 # hgweb/hgweb_mod.py - Web interface for a repository.
Eric Hopper
Final stage of the hgweb split up....
r2356 #
# Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
Thomas Arendsen Hein
Updated copyright notices and add "and others" to "hg version"
r4635 # Copyright 2005-2007 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.
Dirkjan Ochtman
hgweb: refactor hgweb code
r6393 import os, mimetypes
from mercurial.node import hex, nullid
Joel Rosdahl
Avoid importing mercurial.node/mercurial.repo stuff from mercurial.hg
r6217 from mercurial.repo import RepoError
Dirkjan Ochtman
hgweb: refactor hgweb code
r6393 from mercurial import mdiff, ui, hg, util, patch, hook
Dirkjan Ochtman
hgweb: use bundletypes from mercurial.changegroup
r6152 from mercurial import revlog, templater, templatefilters, changegroup
Dirkjan Ochtman
hgweb: refactor hgweb code
r6393 from common import get_mtime, style_map, paritygen, countgen, ErrorResponse
Dirkjan Ochtman
hgweb: explicit response status
r5993 from common import HTTP_OK, HTTP_BAD_REQUEST, HTTP_NOT_FOUND, HTTP_SERVER_ERROR
Dirkjan Ochtman
Less indirection in the WSGI web interface. This simplifies some code, and makes it more compliant with WSGI.
r5566 from request import wsgirequest
Dirkjan Ochtman
hgweb: separate out utility functions
r6392 import webcommands, protocol, webutil
Eric Hopper
Final stage of the hgweb split up....
r2356
Dirkjan Ochtman
hgweb: extract constant to global level
r5597 shortcuts = {
'cl': [('cmd', ['changelog']), ('rev', None)],
'sl': [('cmd', ['shortlog']), ('rev', None)],
'cs': [('cmd', ['changeset']), ('node', None)],
'f': [('cmd', ['file']), ('filenode', None)],
'fl': [('cmd', ['filelog']), ('filenode', None)],
'fd': [('cmd', ['filediff']), ('node', None)],
'fa': [('cmd', ['annotate']), ('filenode', None)],
'mf': [('cmd', ['manifest']), ('manifest', None)],
'ca': [('cmd', ['archive']), ('node', None)],
'tags': [('cmd', ['tags'])],
'tip': [('cmd', ['changeset']), ('node', ['tip'])],
'static': [('cmd', ['static']), ('file', None)]
}
Eric Hopper
Final stage of the hgweb split up....
r2356
class hgweb(object):
Thomas Arendsen Hein
Backed out changeset b913d3aacddc (see issue971/msg5317)
r6141 def __init__(self, repo, name=None):
Christian Ebert
Use isinstance instead of type == type
r4874 if isinstance(repo, str):
Thomas Arendsen Hein
Backed out changeset b913d3aacddc (see issue971/msg5317)
r6141 parentui = ui.ui(report_untrusted=False, interactive=False)
Dirkjan Ochtman
Prevent WSGI apps from touching sys.stdin by setting ui.interactive to False.
r5289 self.repo = hg.repository(parentui, repo)
Eric Hopper
Final stage of the hgweb split up....
r2356 else:
self.repo = repo
Matt Mackall
hook: redirect stdout to stderr for ssh and http servers
r5833 hook.redirect(True)
Eric Hopper
Final stage of the hgweb split up....
r2356 self.mtime = -1
self.reponame = name
self.archives = 'zip', 'gz', 'bz2'
Frank Kingswood
hgweb: Configurable zebra stripes...
r2666 self.stripecount = 1
Dirkjan Ochtman
hgweb: use bundletypes from mercurial.changegroup
r6152 self._capabilities = None
Alexis S. L. Carvalho
use untrusted settings in hgweb...
r3555 # a repo owner may set web.templates in .hg/hgrc to get any file
# readable by the user running the CGI script
self.templatepath = self.config("web", "templates",
templater.templatepath(),
untrusted=False)
# The CGI scripts are often run by a user different from the repo owner.
# Trust the settings from the .hg/hgrc files by default.
def config(self, section, name, default=None, untrusted=True):
return self.repo.ui.config(section, name, default,
untrusted=untrusted)
def configbool(self, section, name, default=False, untrusted=True):
return self.repo.ui.configbool(section, name, default,
untrusted=untrusted)
def configlist(self, section, name, default=None, untrusted=True):
return self.repo.ui.configlist(section, name, default,
untrusted=untrusted)
Eric Hopper
Final stage of the hgweb split up....
r2356
def refresh(self):
mtime = get_mtime(self.repo.root)
if mtime != self.mtime:
self.mtime = mtime
self.repo = hg.repository(self.repo.ui, self.repo.root)
Alexis S. L. Carvalho
use untrusted settings in hgweb...
r3555 self.maxchanges = int(self.config("web", "maxchanges", 10))
self.stripecount = int(self.config("web", "stripes", 1))
self.maxshortchanges = int(self.config("web", "maxshortchanges", 60))
self.maxfiles = int(self.config("web", "maxfiles", 10))
self.allowpull = self.configbool("web", "allowpull", True)
OHASHI Hideya <ohachige at gmail.com>
Enable to select encoding in hgrc web section...
r4690 self.encoding = self.config("web", "encoding", util._encoding)
Dirkjan Ochtman
hgweb: use bundletypes from mercurial.changegroup
r6152 self._capabilities = None
def capabilities(self):
if self._capabilities is not None:
return self._capabilities
caps = ['lookup', 'changegroupsubset']
if self.configbool('server', 'uncompressed'):
caps.append('stream=%d' % self.repo.changelog.version)
if changegroup.bundlepriority:
caps.append('unbundle=%s' % ','.join(changegroup.bundlepriority))
self._capabilities = caps
return caps
Eric Hopper
Final stage of the hgweb split up....
r2356
Dirkjan Ochtman
split out hgweb commands into a separate file, move some code around
r5591 def run(self):
if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
raise RuntimeError("This function is only intended to be called while running as a CGI script.")
import mercurial.hgweb.wsgicgi as wsgicgi
wsgicgi.launch(self)
def __call__(self, env, respond):
req = wsgirequest(env, respond)
self.run_wsgi(req)
return req
def run_wsgi(self, req):
Dirkjan Ochtman
hgweb: get rid of some nested functions
r5596 self.refresh()
Dirkjan Ochtman
split out hgweb commands into a separate file, move some code around
r5591
Dirkjan Ochtman
hgweb: get rid of some nested functions
r5596 # expand form shortcuts
Dirkjan Ochtman
split out hgweb commands into a separate file, move some code around
r5591
Dirkjan Ochtman
hgweb: get rid of some nested functions
r5596 for k in shortcuts.iterkeys():
if k in req.form:
for name, value in shortcuts[k]:
if value is None:
value = req.form[k]
req.form[name] = value
del req.form[k]
Dirkjan Ochtman
split out hgweb commands into a separate file, move some code around
r5591
Dirkjan Ochtman
hgweb: get rid of some nested functions
r5596 # work with CGI variables to create coherent structure
# use SCRIPT_NAME, PATH_INFO and QUERY_STRING as well as our REPO_NAME
Dirkjan Ochtman
split out hgweb commands into a separate file, move some code around
r5591
Dirkjan Ochtman
hgweb: get rid of some nested functions
r5596 req.url = req.env['SCRIPT_NAME']
if not req.url.endswith('/'):
req.url += '/'
Christian Ebert
Prefer i in d over d.has_key(i)
r5915 if 'REPO_NAME' in req.env:
Dirkjan Ochtman
hgweb: get rid of some nested functions
r5596 req.url += req.env['REPO_NAME'] + '/'
Dirkjan Ochtman
split out hgweb commands into a separate file, move some code around
r5591
Dirkjan Ochtman
hgweb: make hgwebdir work in the absence of PATH_INFO...
r6459 if 'PATH_INFO' in req.env:
parts = req.env['PATH_INFO'].strip('/').split('/')
Dirkjan Ochtman
hgweb: get rid of some nested functions
r5596 repo_parts = req.env.get('REPO_NAME', '').split('/')
if parts[:len(repo_parts)] == repo_parts:
parts = parts[len(repo_parts):]
query = '/'.join(parts)
else:
query = req.env['QUERY_STRING'].split('&', 1)[0]
query = query.split(';', 1)[0]
Dirkjan Ochtman
split out hgweb commands into a separate file, move some code around
r5591
Dirkjan Ochtman
hgweb: get rid of some nested functions
r5596 # translate user-visible url structure to internal structure
args = query.split('/', 2)
if 'cmd' not in req.form and args and args[0]:
Dirkjan Ochtman
split out hgweb commands into a separate file, move some code around
r5591
cmd = args.pop(0)
style = cmd.rfind('-')
if style != -1:
req.form['style'] = [cmd[:style]]
cmd = cmd[style+1:]
Dirkjan Ochtman
hgweb: get rid of some nested functions
r5596
Dirkjan Ochtman
split out hgweb commands into a separate file, move some code around
r5591 # avoid accepting e.g. style parameter as command
Dirkjan Ochtman
separate the wire protocol commands from the user interface commands
r5598 if hasattr(webcommands, cmd) or hasattr(protocol, cmd):
Dirkjan Ochtman
split out hgweb commands into a separate file, move some code around
r5591 req.form['cmd'] = [cmd]
if args and args[0]:
node = args.pop(0)
req.form['node'] = [node]
if args:
req.form['file'] = args
if cmd == 'static':
req.form['file'] = req.form['node']
elif cmd == 'archive':
fn = req.form['node'][0]
for type_, spec in self.archive_specs.iteritems():
ext = spec[2]
if fn.endswith(ext):
req.form['node'] = [fn[:-len(ext)]]
req.form['type'] = [type_]
Dirkjan Ochtman
hgweb: separate protocol calls from interface calls (issue996)...
r6149 # process this if it's a protocol request
cmd = req.form.get('cmd', [''])[0]
if cmd in protocol.__all__:
method = getattr(protocol, cmd)
method(self, req)
return
# process the web interface request
Dirkjan Ochtman
hgweb: split out templater definition
r5599
try:
Dirkjan Ochtman
hgweb: separate protocol calls from interface calls (issue996)...
r6149 tmpl = self.templater(req)
Dirkjan Ochtman
Backed out changeset d2bb66a8a435 (temporary template compatibility)
r6391 ctype = tmpl('mimetype', encoding=self.encoding)
ctype = templater.stringify(ctype)
Dirkjan Ochtman
hgweb: separate protocol calls from interface calls (issue996)...
r6149
if cmd == '':
req.form['cmd'] = [tmpl.cache['default']]
cmd = req.form['cmd'][0]
Dirkjan Ochtman
hgweb: fast path for sending raw files
r5890
Dirkjan Ochtman
hgweb: separate protocol calls from interface calls (issue996)...
r6149 if cmd not in webcommands.__all__:
Dirkjan Ochtman
hgweb: better error messages
r6368 msg = 'no such method: %s' % cmd
Dirkjan Ochtman
hgweb: separate protocol calls from interface calls (issue996)...
r6149 raise ErrorResponse(HTTP_BAD_REQUEST, msg)
elif cmd == 'file' and 'raw' in req.form.get('style', []):
self.ctype = ctype
content = webcommands.rawfile(self, req, tmpl)
else:
content = getattr(webcommands, cmd)(self, req, tmpl)
req.respond(HTTP_OK, ctype)
Dirkjan Ochtman
hgweb: fast path for sending raw files
r5890
Dirkjan Ochtman
hgweb: separate protocol calls from interface calls (issue996)...
r6149 req.write(content)
del tmpl
Dirkjan Ochtman
hgweb: explicitly pass around the templater
r5600
except revlog.LookupError, err:
Dirkjan Ochtman
hgweb: explicit response status
r5993 req.respond(HTTP_NOT_FOUND, ctype)
Dirkjan Ochtman
hgweb: fix breakage in python < 2.5 introduced in 2c370f08c486
r6374 msg = str(err)
if 'manifest' not in msg:
Dirkjan Ochtman
hgweb: better error messages
r6368 msg = 'revision not found: %s' % err.name
req.write(tmpl('error', error=msg))
Joel Rosdahl
Avoid importing mercurial.node/mercurial.repo stuff from mercurial.hg
r6217 except (RepoError, revlog.RevlogError), inst:
Dirkjan Ochtman
hgweb: explicit response status
r5993 req.respond(HTTP_SERVER_ERROR, ctype)
req.write(tmpl('error', error=str(inst)))
Dirkjan Ochtman
hgweb: explicitly pass around the templater
r5600 except ErrorResponse, inst:
Dirkjan Ochtman
hgweb: explicit response status
r5993 req.respond(inst.code, ctype)
req.write(tmpl('error', error=inst.message))
Dirkjan Ochtman
hgweb: split out templater definition
r5599
def templater(self, req):
Dirkjan Ochtman
hgweb: get rid of some nested functions
r5596 # determine scheme, port and server name
# this is needed to create absolute urls
Dirkjan Ochtman
split out hgweb commands into a separate file, move some code around
r5591
proto = req.env.get('wsgi.url_scheme')
if proto == 'https':
proto = 'https'
default_port = "443"
else:
proto = 'http'
default_port = "80"
port = req.env["SERVER_PORT"]
port = port != default_port and (":" + port) or ""
urlbase = '%s://%s%s' % (proto, req.env['SERVER_NAME'], port)
staticurl = self.config("web", "staticurl") or req.url + 'static/'
if not staticurl.endswith('/'):
staticurl += '/'
Dirkjan Ochtman
hgweb: get rid of some nested functions
r5596 # some functions for the templater
def header(**map):
Dirkjan Ochtman
Backed out changeset d2bb66a8a435 (temporary template compatibility)
r6391 yield tmpl('header', encoding=self.encoding, **map)
Dirkjan Ochtman
hgweb: get rid of some nested functions
r5596
def footer(**map):
Dirkjan Ochtman
hgweb: explicitly pass around the templater
r5600 yield tmpl("footer", **map)
Dirkjan Ochtman
hgweb: get rid of some nested functions
r5596
def motd(**map):
yield self.config("web", "motd", "")
def sessionvars(**map):
fields = []
Christian Ebert
Prefer i in d over d.has_key(i)
r5915 if 'style' in req.form:
Dirkjan Ochtman
hgweb: get rid of some nested functions
r5596 style = req.form['style'][0]
if style != self.config('web', 'style', ''):
fields.append(('style', style))
separator = req.url[-1] == '?' and ';' or '?'
for name, value in fields:
yield dict(name=name, value=value, separator=separator)
separator = ';'
Dirkjan Ochtman
hgweb: split out templater definition
r5599 # figure out which style to use
Dirkjan Ochtman
hgweb: get rid of some nested functions
r5596 style = self.config("web", "style", "")
Christian Ebert
Prefer i in d over d.has_key(i)
r5915 if 'style' in req.form:
Dirkjan Ochtman
hgweb: get rid of some nested functions
r5596 style = req.form['style'][0]
mapfile = style_map(self.templatepath, style)
Dirkjan Ochtman
split out hgweb commands into a separate file, move some code around
r5591 if not self.reponame:
self.reponame = (self.config("web", "name")
or req.env.get('REPO_NAME')
or req.url.strip('/') or self.repo.root)
Dirkjan Ochtman
hgweb: split out templater definition
r5599 # create the templater
Matt Mackall
templates: move filters to their own module...
r5976 tmpl = templater.templater(mapfile, templatefilters.filters,
Dirkjan Ochtman
hgweb: explicitly pass around the templater
r5600 defaults={"url": req.url,
"staticurl": staticurl,
"urlbase": urlbase,
"repo": self.reponame,
"header": header,
"footer": footer,
"motd": motd,
"sessionvars": sessionvars
})
return tmpl
Dirkjan Ochtman
split out hgweb commands into a separate file, move some code around
r5591
Eric Hopper
Final stage of the hgweb split up....
r2356 def archivelist(self, nodeid):
Alexis S. L. Carvalho
use untrusted settings in hgweb...
r3555 allowed = self.configlist("web", "allow_archive")
Brendan Cully
hgweb: accept NewWebInterface URLs
r3260 for i, spec in self.archive_specs.iteritems():
Alexis S. L. Carvalho
use untrusted settings in hgweb...
r3555 if i in allowed or self.configbool("web", "allow" + i):
Brendan Cully
hgweb: accept NewWebInterface URLs
r3260 yield {"type" : i, "extension" : spec[2], "node" : nodeid}
Eric Hopper
Final stage of the hgweb split up....
r2356
Dirkjan Ochtman
hgweb: explicitly pass around the templater
r5600 def listfilediffs(self, tmpl, files, changeset):
Eric Hopper
Final stage of the hgweb split up....
r2356 for f in files[:self.maxfiles]:
Dirkjan Ochtman
hgweb: explicitly pass around the templater
r5600 yield tmpl("filedifflink", node=hex(changeset), file=f)
Eric Hopper
Final stage of the hgweb split up....
r2356 if len(files) > self.maxfiles:
Dirkjan Ochtman
hgweb: explicitly pass around the templater
r5600 yield tmpl("fileellipses")
Eric Hopper
Final stage of the hgweb split up....
r2356
Dirkjan Ochtman
hgweb: explicitly pass around the templater
r5600 def diff(self, tmpl, node1, node2, files):
Eric Hopper
Final stage of the hgweb split up....
r2356 def filterfiles(filters, files):
l = [x for x in files if x in filters]
for t in filters:
if t and t[-1] != os.sep:
t += os.sep
l += [x for x in files if x.startswith(t)]
return l
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 def diffblock(diff, f, fn):
Dirkjan Ochtman
hgweb: explicitly pass around the templater
r5600 yield tmpl("diffblock",
lines=prettyprintlines(diff),
parity=parity.next(),
file=f,
filenode=hex(fn or nullid))
Eric Hopper
Final stage of the hgweb split up....
r2356
Edward Lee
Add line anchors to annotate, changeset, diff, file views for hgweb
r6122 blockcount = countgen()
Eric Hopper
Final stage of the hgweb split up....
r2356 def prettyprintlines(diff):
Edward Lee
Add line anchors to annotate, changeset, diff, file views for hgweb
r6122 blockno = blockcount.next()
for lineno, l in enumerate(diff.splitlines(1)):
if blockno == 0:
lineno = lineno + 1
else:
lineno = "%d.%d" % (blockno, lineno + 1)
Eric Hopper
Final stage of the hgweb split up....
r2356 if l.startswith('+'):
Thomas Arendsen Hein
merged Edward Lee's line anchors patch
r6123 ltype = "difflineplus"
Eric Hopper
Final stage of the hgweb split up....
r2356 elif l.startswith('-'):
Thomas Arendsen Hein
merged Edward Lee's line anchors patch
r6123 ltype = "difflineminus"
Eric Hopper
Final stage of the hgweb split up....
r2356 elif l.startswith('@'):
Thomas Arendsen Hein
merged Edward Lee's line anchors patch
r6123 ltype = "difflineat"
Eric Hopper
Final stage of the hgweb split up....
r2356 else:
Thomas Arendsen Hein
merged Edward Lee's line anchors patch
r6123 ltype = "diffline"
yield tmpl(ltype,
line=l,
lineid="l%s" % lineno,
linenumber="% 8s" % lineno)
Eric Hopper
Final stage of the hgweb split up....
r2356
r = self.repo
Benoit Boissinot
hgweb: use contexts, fix coding style
r3973 c1 = r.changectx(node1)
c2 = r.changectx(node2)
date1 = util.datestr(c1.date())
date2 = util.datestr(c2.date())
Eric Hopper
Final stage of the hgweb split up....
r2356
Giorgos Keramidas
hgweb: repo.changes() is now called repo.status()
r2876 modified, added, removed, deleted, unknown = r.status(node1, node2)[:5]
Eric Hopper
Final stage of the hgweb split up....
r2356 if files:
modified, added, removed = map(lambda x: filterfiles(files, x),
(modified, added, removed))
Alexis S. L. Carvalho
use untrusted settings in hgweb...
r3555 diffopts = patch.diffopts(self.repo.ui, untrusted=True)
Eric Hopper
Final stage of the hgweb split up....
r2356 for f in modified:
Benoit Boissinot
hgweb: use contexts, fix coding style
r3973 to = c1.filectx(f).data()
tn = c2.filectx(f).data()
Rocco Rutte
hgweb_mod: update unidiff() calls and finish e5eedd74e70f job
r5486 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f, f,
Vadim Gelfer
refactor text diff/patch code....
r2874 opts=diffopts), f, tn)
Eric Hopper
Final stage of the hgweb split up....
r2356 for f in added:
to = None
Benoit Boissinot
hgweb: use contexts, fix coding style
r3973 tn = c2.filectx(f).data()
Rocco Rutte
hgweb_mod: update unidiff() calls and finish e5eedd74e70f job
r5486 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f, f,
Vadim Gelfer
refactor text diff/patch code....
r2874 opts=diffopts), f, tn)
Eric Hopper
Final stage of the hgweb split up....
r2356 for f in removed:
Benoit Boissinot
hgweb: use contexts, fix coding style
r3973 to = c1.filectx(f).data()
Eric Hopper
Final stage of the hgweb split up....
r2356 tn = None
Rocco Rutte
hgweb_mod: update unidiff() calls and finish e5eedd74e70f job
r5486 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f, f,
Vadim Gelfer
refactor text diff/patch code....
r2874 opts=diffopts), f, tn)
Eric Hopper
Final stage of the hgweb split up....
r2356
archive_specs = {
Thomas Arendsen Hein
Fix automatic decompression of tarballs with Firefox....
r2361 'bz2': ('application/x-tar', 'tbz2', '.tar.bz2', None),
'gz': ('application/x-tar', 'tgz', '.tar.gz', None),
Eric Hopper
Final stage of the hgweb split up....
r2356 'zip': ('application/zip', 'zip', '.zip', None),
}
Vadim Gelfer
push over http: server side authorization support....
r2466 def check_perm(self, req, op, default):
'''check permission for operation based on user auth.
return true if op allowed, else false.
default is policy to use if no config given.'''
user = req.env.get('REMOTE_USER')
Alexis S. L. Carvalho
use untrusted settings in hgweb...
r3555 deny = self.configlist('web', 'deny_' + op)
Vadim Gelfer
push over http: server side authorization support....
r2466 if deny and (not user or deny == ['*'] or user in deny):
return False
Alexis S. L. Carvalho
use untrusted settings in hgweb...
r3555 allow = self.configlist('web', 'allow_' + op)
Vadim Gelfer
push over http: server side authorization support....
r2466 return (allow and (allow == ['*'] or user in allow)) or default