File viewvc-buglink.patch of Package viewvc
Index: conf/viewvc.conf.dist
===================================================================
--- conf/viewvc.conf.dist.orig
+++ conf/viewvc.conf.dist
@@ -343,6 +343,21 @@
##---------------------------------------------------------------------------
[options]
+## The 'buglink_base' value is a string that can be used to form a URL
+## by appending a bug number. If viewvc sees something that looks
+## like a bug number in a log message (eg. "bug 12345" or "#12345"), it
+## will be displayed as a link to the bug in your bug tracking system.
+##
+## For a Bugzilla installation, you probably want to set this to
+## something like "http://hostname/show_bug.cgi?id=". For the Debian
+## bug tracker, you might use
+## "http://hostname/cgi-bin/bugreport.cgi?bug=".
+##
+## If 'buglink_base' is not set, then bug tracker links won't be
+## generated.
+##
+#buglink_base = http://example.com/show_bug.cgi?id=
+
## root_as_url_component: Interpret the first path component in the URL
## after the script location as the root to use. This is an
## alternative to using the "root=" query key. If ViewVC is configured
Index: lib/config.py
===================================================================
--- lib/config.py.orig
+++ lib/config.py
@@ -52,7 +52,7 @@ import fnmatch
# Here's a diagram of the valid overlays/overrides:
#
# PER-ROOT PER-VHOST BASE
-#
+#
# ,-----------. ,-----------.
# | vhost-*/ | | |
# | general | --> | general |
@@ -150,13 +150,13 @@ class Config:
settings there as overrides to the built-in default values. If
VHOST is provided, also process the configuration overrides
specific to that virtual host."""
-
+
self.conf_path = os.path.isfile(pathname) and pathname or None
self.base = os.path.dirname(pathname)
self.parser = ConfigParser.ConfigParser()
self.parser.optionxform = lambda x: x # don't case-normalize option names.
self.parser.read(self.conf_path or [])
-
+
for section in self.parser.sections():
if self._is_allowed_section(section, self._base_sections):
self._process_section(self.parser, section, section)
@@ -168,7 +168,7 @@ class Config:
"""Process the key/value (kv) files specified in the
configuration, merging their values into the configuration as
dotted heirarchical items."""
-
+
kv = _sub_config()
for fname in self.general.kv_files:
@@ -228,7 +228,7 @@ class Config:
"""Return 1 iff SECTION is an allowed section, defined as being
explicitly present in the ALLOWED_SECTIONS list or present in the
form 'someprefix-*' in that list."""
-
+
for allowed_section in allowed_sections:
if allowed_section[-1] == '*':
if _startswith(section, allowed_section[:-1]):
@@ -284,7 +284,7 @@ class Config:
set. This is a destructive change to the configuration."""
did_overlay = 0
-
+
if not self.conf_path:
return
@@ -374,7 +374,7 @@ class Config:
for attr in dir(sub_config):
params[attr] = getattr(sub_config, attr)
return params
-
+
def set_defaults(self):
"Set some default values in the configuration."
@@ -445,6 +445,7 @@ class Config:
self.options.log_pagesextra = 3
self.options.limit_changes = 100
self.options.stacktraces = 0
+ self.options.buglink_base = None
self.templates.diff = None
self.templates.directory = None
@@ -464,13 +465,13 @@ class Config:
self.cvsdb.user = ''
self.cvsdb.passwd = ''
self.cvsdb.readonly_user = ''
- self.cvsdb.readonly_passwd = ''
+ self.cvsdb.readonly_passwd = ''
self.cvsdb.row_limit = 1000
self.cvsdb.rss_row_limit = 100
self.cvsdb.check_database_for_root = 0
self.query.viewvc_base_url = None
-
+
def _startswith(somestr, substr):
return somestr[:len(substr)] == substr
@@ -494,7 +495,7 @@ class IllegalOverrideSection(ViewVCConfi
def __str__(self):
return "malformed configuration: illegal %s override section: %s" \
% (self.override_type, self.section_name)
-
+
class MalformedRoot(ViewVCConfigurationError):
def __init__(self, config_name, value_given):
Exception.__init__(self, config_name, value_given)
Index: lib/viewvc.py
===================================================================
--- lib/viewvc.py.orig
+++ lib/viewvc.py
@@ -154,7 +154,7 @@ class Request:
for name, values in self.server.params().items():
# we only care about the first value
value = values[0]
-
+
# patch up old queries that use 'cvsroot' to look like they used 'root'
if name == 'cvsroot':
name = 'root'
@@ -172,12 +172,12 @@ class Request:
# validate the parameter
_validate_param(name, value)
-
+
# if we're here, then the parameter is okay
self.query_dict[name] = value
# Resolve the view parameter into a handler function.
- self.view_func = _views.get(self.query_dict.get('view', None),
+ self.view_func = _views.get(self.query_dict.get('view', None),
self.view_func)
# Process PATH_INFO component of query string
@@ -239,7 +239,7 @@ class Request:
if roottype:
# Overlay root-specific options.
cfg.overlay_root_options(self.rootname)
-
+
# Setup an Authorizer for this rootname and username
debug.t_start('setup-authorizer')
self.auth = setup_authorizer(cfg, self.username)
@@ -290,7 +290,7 @@ class Request:
'The root "%s" has an unknown type ("%s"). Expected "cvs" or "svn".'
% (self.rootname, type),
"500 Internal Server Error")
-
+
# If this is using an old-style 'rev' parameter, redirect to new hotness.
# Subversion URLs will now use 'pathrev'; CVS ones use 'revision'.
if self.repos and self.query_dict.has_key('rev'):
@@ -356,7 +356,7 @@ class Request:
needs_redirect = 1
if self.view_func is None:
- # view parameter is not set, try looking at pathtype and the
+ # view parameter is not set, try looking at pathtype and the
# other parameters
if not self.rootname:
self.view_func = view_roots
@@ -374,7 +374,7 @@ class Request:
elif self.query_dict.has_key('graph'):
if not self.query_dict.has_key('makeimage'):
self.view_func = view_cvsgraph
- else:
+ else:
self.view_func = view_cvsgraph_image
elif self.query_dict.has_key('revision') \
or cfg.options.default_file_view != "log":
@@ -394,7 +394,7 @@ class Request:
self.where = ''
self.path_parts = []
self.pathtype = None
-
+
# if we have a directory and the request didn't end in "/", then redirect
# so that it does.
if (self.pathtype == vclib.DIR and path_info[-1:] != '/'
@@ -454,7 +454,7 @@ class Request:
def get_link(self, view_func=None, where=None, pathtype=None, params=None):
"""Constructs a link pointing to another ViewVC page. All arguments
- correspond to members of the Request object. If they are set to
+ correspond to members of the Request object. If they are set to
None they take values from the current page. Return value is a base
URL and a dictionary of parameters"""
@@ -467,7 +467,7 @@ class Request:
params = self.query_dict.copy()
else:
params = params.copy()
-
+
# must specify both where and pathtype or neither
assert (where is None) == (pathtype is None)
@@ -481,7 +481,7 @@ class Request:
pathtype = self.pathtype
# no need to add sticky variables for views with no links
- sticky_vars = not (view_func is view_checkout
+ sticky_vars = not (view_func is view_checkout
or view_func is download_tarball)
# The logic used to construct the URL is an inverse of the
@@ -518,7 +518,7 @@ class Request:
# no need to specify default root
if rootname == cfg.general.default_root:
- del params['root']
+ del params['root']
# add 'pathrev' value to parameter list
if (self.pathrev is not None
@@ -606,7 +606,7 @@ def _normalize_path(path):
because we output the script name in links and web browsers
interpret //viewvc.cgi/ as http://viewvc.cgi/
"""
-
+
i = 0
for c in path:
if c != '/':
@@ -687,7 +687,7 @@ _legal_params = {
'search' : _validate_regex,
'p1' : None,
'p2' : None,
-
+
'hideattic' : _re_validate_boolint,
'limit_changes' : _re_validate_number,
'sortby' : _re_validate_alpha,
@@ -787,7 +787,7 @@ def _orig_path(request, rev_param='revis
# *checkout*/circle.jpg?pathrev=3
# *checkout*/square.jpg?revision=3
# *checkout*/square.jpg?revision=3&pathrev=4
- #
+ #
# Note that the following:
#
# *checkout*/circle.jpg?rev=3
@@ -799,7 +799,7 @@ def _orig_path(request, rev_param='revis
#
rev = request.query_dict.get(rev_param, request.pathrev)
path = request.query_dict.get(path_param, request.where)
-
+
if rev is not None and hasattr(request.repos, '_getrev'):
try:
pathrev = request.repos._getrev(request.pathrev)
@@ -813,7 +813,7 @@ def setup_authorizer(cfg, username, root
"""Setup the authorizer. If ROOTNAME is provided, assume that
per-root options have not been overlayed. Otherwise, assume they
have (and fetch the authorizer for the configured root)."""
-
+
if rootname is None:
authorizer = cfg.options.authorizer
params = cfg.get_authorizer_params()
@@ -853,7 +853,7 @@ def check_freshness(request, mtime=None,
# See if we are supposed to disable etags (for debugging, usually)
if not cfg.options.generate_etags:
return 0
-
+
request_etag = request_mtime = None
if etag is not None:
if weak:
@@ -937,7 +937,7 @@ def get_writeready_server_file(request,
request.server.addheader('Content-Encoding', 'gzip')
elif content_length is not None:
request.server.addheader('Content-Length', content_length)
-
+
if content_type and encoding:
request.server.header("%s; charset=%s" % (content_type, encoding))
elif content_type:
@@ -950,9 +950,9 @@ def get_writeready_server_file(request,
request.server.file())
else:
fp = request.server.file()
-
+
return fp
-
+
def generate_page(request, view_name, data, content_type=None):
server_fp = get_writeready_server_file(request, content_type)
template = get_view_template(request.cfg, view_name, request.language)
@@ -1040,7 +1040,7 @@ def default_view(mime_type, cfg):
# very useful marked up. If the mime type is totally unknown (happens when
# we encounter an unrecognized file extension) we also view it through
# the markup page since that's better than sending it text/plain.
- if ('markup' in cfg.options.allowed_views and
+ if ('markup' in cfg.options.allowed_views and
(is_viewable_image(mime_type) or is_text(mime_type))):
return view_markup
return view_checkout
@@ -1053,7 +1053,7 @@ def is_binary_file_mime_type(mime_type,
if fnmatch.fnmatch(mime_type, pattern):
return True
return False
-
+
def get_file_view_info(request, where, rev=None, mime_type=None, pathrev=-1):
"""Return an object holding common hrefs and a viewability flag used
for various views of FILENAME at revision REV whose MIME type is
@@ -1068,9 +1068,9 @@ def get_file_view_info(request, where, r
prefer_markup
is_viewable_image
is_binary
-
+
"""
-
+
rev = rev and str(rev) or None
mime_type = mime_type or guess_mime(where)
if pathrev == -1: # cheesy default value, since we need to preserve None
@@ -1167,7 +1167,11 @@ class ViewVCHtmlFormatterTokens:
return out, out_len, 1
return out, out_len, 0
-
+
+# Matches bug numbers
+_re_rewrite_bug = re.compile(r'((?:\bbug[\s:#+]|[^&]#|^#)\s*(\d\d+))', re.I)
+_re_buglink_prefix = ""
+
class ViewVCHtmlFormatter:
"""Format a string as HTML-encoded output with customizable markup
rules, for example turning strings that look like URLs into anchor links.
@@ -1176,10 +1180,23 @@ class ViewVCHtmlFormatter:
interface, there is a good chance that there are consumers outside
of ViewVC itself that make use of these things.
"""
-
+
def __init__(self):
self._formatters = []
+ def format_bugzilla(self, mobj, userdata, maxlen=0):
+ """Return a 2-tuple containing:
+ - the text represented by MatchObject MOBJ, formatted as
+ linkified URL, with no more than MAXLEN characters in the
+ non-HTML-tag bits. If MAXLEN is 0, there is no maximum.
+ - the number of non-HTML-tag characters returned.
+ """
+ s = mobj.group(0)
+ trunc_s = maxlen and s[:maxlen] or s
+ return '<a href="%s%s">%s</a>' % (_re_buglink_prefix, urllib.quote(mobj.group(2)),
+ sapi.escape(trunc_s)), \
+ len(trunc_s)
+
def format_url(self, mobj, userdata, maxlen=0):
"""Return a 2-tuple containing:
- the text represented by MatchObject MOBJ, formatted as
@@ -1212,7 +1229,7 @@ class ViewVCHtmlFormatter:
entity-encoded email address, with no more than MAXLEN characters
in the non-HTML-tag bits. If MAXLEN is 0, there is no maximum.
- the number of non-HTML-tag characters returned.
- """
+ """
s = mobj.group(0)
trunc_s = maxlen and s[:maxlen] or s
return self._entity_encode(trunc_s), len(trunc_s)
@@ -1282,10 +1299,10 @@ class ViewVCHtmlFormatter:
- the text S, HTML-escaped, containing no more than MAXLEN
characters. If MAXLEN is 0, there is no maximum.
- the number of characters returned.
- """
+ """
trunc_s = maxlen and s[:maxlen] or s
return sapi.escape(trunc_s), len(trunc_s)
-
+
def add_formatter(self, regexp, conv, userdata=None):
"""Register a formatter which finds instances of strings matching
REGEXP, and using the function CONV and USERDATA to format them.
@@ -1375,11 +1392,11 @@ class LogFormatter:
def get(self, maxlen=0, htmlize=1):
cfg = self.request.cfg
-
+
# Prefer the cache.
if self.cache.has_key((maxlen, htmlize)):
return self.cache[(maxlen, htmlize)]
-
+
# If we are HTML-izing...
if htmlize:
# ...and we don't yet have ViewVCHtmlFormatter() object tokens...
@@ -1399,6 +1416,11 @@ class LogFormatter:
lf.add_formatter(_re_rewrite_svnrevref, lf.format_svnrevref,
revision_to_url)
+ if cfg.options.buglink_base is not None:
+ global _re_buglink_prefix
+ _re_buglink_prefix = cfg.options.buglink_base
+ lf.add_formatter(_re_rewrite_bug, lf.format_bugzilla)
+
# Rewrite email addresses.
if cfg.options.mangle_email_addresses == 2:
lf.add_formatter(_re_rewrite_email, lf.format_email_truncated)
@@ -1409,7 +1431,7 @@ class LogFormatter:
# Add custom rewrite handling per configuration.
for rule in cfg.options.custom_log_formatting:
- rule = rule.replace('\\:', '\x01')
+ rule = rule.replace('\\:', '\x01')
regexp, format = map(lambda x: x.strip(), rule.split(':', 1))
regexp = regexp.replace('\x01', ':')
format = format.replace('\x01', ':')
@@ -1496,7 +1518,7 @@ def html_time(request, secs, extended=0)
def common_template_data(request, revision=None, mime_type=None):
"""Return a ezt.TemplateData instance with data dictionary items
common to most ViewVC views."""
-
+
cfg = request.cfg
# Initialize data dictionary members (sorted alphanumerically)
@@ -1587,7 +1609,7 @@ def common_template_data(request, revisi
data['view_href'] = request.get_url(view_func=view_directory,
params={}, escape=1)
if 'tar' in cfg.options.allowed_views:
- data['tarball_href'] = request.get_url(view_func=download_tarball,
+ data['tarball_href'] = request.get_url(view_func=download_tarball,
params={},
escape=1)
if request.roottype == 'svn':
@@ -1633,7 +1655,7 @@ def retry_read(src, reqlen=CHUNK_SIZE):
time.sleep(1)
continue
return chunk
-
+
def copy_stream(src, dst, htmlize=0):
while 1:
chunk = retry_read(src)
@@ -1682,7 +1704,7 @@ def detect_encoding(text_block):
Python module. (Currently, this is used only when syntax
highlighting is not enabled/available; otherwise, Pygments does this
work for us.)"""
-
+
# Does the TEXT_BLOCK start with a BOM?
for bom, encoding in [('\xef\xbb\xbf', 'utf-8'),
('\xff\xfe', 'utf-16'),
@@ -1711,7 +1733,7 @@ def detect_encoding(text_block):
# By default ... we have no idea.
return None
-
+
def transcode_text(text, encoding=None):
"""If ENCODING is provided and not 'utf-8', transcode TEXT from
ENCODING to UTF-8."""
@@ -1735,7 +1757,7 @@ def markup_stream(request, cfg, blame_da
apply syntax coloration to the file contents, and use the
HTML-marked-up results as the text in the return vclib.Annotation
objects."""
-
+
# Nothing to mark up? So be it.
if not file_lines:
return []
@@ -1792,7 +1814,7 @@ def markup_stream(request, cfg, blame_da
stripnl=False)
except ClassNotFound:
pygments_lexer = None
-
+
# If we aren't highlighting, just return an amalgamation of the
# BLAME_DATA (if any) and the FILE_LINES.
if not pygments_lexer:
@@ -1933,7 +1955,7 @@ def parse_mime_type(mime_type):
name, value = string.split(part, '=', 1)
parameters[name] = value
return type_subtype, parameters
-
+
def calculate_mime_type(request, path_parts, rev):
"""Return a 2-tuple carrying the MIME content type and character
encoding for the file represented by PATH_PARTS in REV. Use REQUEST
@@ -1961,7 +1983,7 @@ def assert_viewable_filesize(cfg, filesi
'disallowed by configuration'
% (cfg.options.max_filesize_kbytes),
'403 Forbidden')
-
+
def markup_or_annotate(request, is_annotate):
cfg = request.cfg
path, rev = _orig_path(request, is_annotate and 'annotate' or 'revision')
@@ -1974,7 +1996,7 @@ def markup_or_annotate(request, is_annot
if is_binary_file_mime_type(mime_type, cfg):
raise debug.ViewVCException('Display of binary file content disabled '
'by configuration', '403 Forbidden')
-
+
# Is this a viewable image type?
if is_viewable_image(mime_type) \
and 'co' in cfg.options.allowed_views:
@@ -2128,9 +2150,9 @@ def markup_or_annotate(request, is_annot
pathtype=vclib.FILE,
params={'pathrev': revision},
escape=1)
-
+
generate_page(request, "file", data)
-
+
def view_markup(request):
if 'markup' not in request.cfg.options.allowed_views:
raise debug.ViewVCException('Markup view is disabled',
@@ -2213,7 +2235,7 @@ def view_roots(request):
if 'roots' not in request.cfg.options.allowed_views:
raise debug.ViewVCException('Root listing view is disabled',
'403 Forbidden')
-
+
# add in the roots for the selection
roots = []
expand_root_parents(request.cfg)
@@ -2247,7 +2269,7 @@ def view_roots(request):
data = common_template_data(request)
data.merge(ezt.TemplateData({
'roots' : roots,
- 'roots_shown' : len(roots),
+ 'roots_shown' : len(roots),
}))
generate_page(request, "roots", data)
@@ -2269,7 +2291,7 @@ def view_directory(request):
# List current directory
options = {}
if request.roottype == 'cvs':
- hideattic = int(request.query_dict.get('hideattic',
+ hideattic = int(request.query_dict.get('hideattic',
cfg.options.hide_attic))
options["cvs_subdirs"] = (cfg.options.show_subdir_lastmod and
cfg.options.show_logs)
@@ -2321,7 +2343,7 @@ def view_directory(request):
rows = [ ]
dirs_displayed = files_displayed = 0
num_dead = 0
-
+
# set some values to be used inside loop
where = request.where
where_prefix = where and where + '/'
@@ -2361,7 +2383,7 @@ def view_directory(request):
and is_cvsroot_path(request.roottype,
request.path_parts + [file.name]):
continue
-
+
dirs_displayed += 1
row.view_href = request.get_url(view_func=view_directory,
@@ -2387,7 +2409,7 @@ def view_directory(request):
pathtype=vclib.DIR,
params={},
escape=1)
-
+
elif file.kind == vclib.FILE:
if searchstr is not None:
if request.roottype == 'cvs' and (file.errors or file.dead):
@@ -2399,11 +2421,11 @@ def view_directory(request):
num_dead = num_dead + 1
if hideattic:
continue
-
+
files_displayed += 1
file_where = where_prefix + file.name
- if request.roottype == 'svn':
+ if request.roottype == 'svn':
row.size = file.size
row.mime_type, encoding = calculate_mime_type(request,
@@ -2512,7 +2534,7 @@ def view_directory(request):
branch_tags.sort(icmp)
branch_tags.reverse()
data['branch_tags']= branch_tags
-
+
data['attic_showing'] = ezt.boolean(not hideattic)
data['show_attic_href'] = request.get_url(params={'hideattic': 0},
escape=1)
@@ -2590,7 +2612,7 @@ def paging_sws(data, key, pagestart, loc
try:
pick.end = getattr(data[key][i+pagesize-1], local_name)
except IndexError:
- pick.end = getattr(data[key][-1], local_name)
+ pick.end = getattr(data[key][-1], local_name)
picklist.append(pick)
if pick.count >= last_requested:
pick.more = ezt.boolean(1)
@@ -2645,9 +2667,9 @@ def redirect_pathrev(request):
new_pathrev = request.query_dict.get('pathrev') or None
path = request.query_dict.get('orig_path', '')
pathtype = request.query_dict.get('orig_pathtype')
- pathrev = request.query_dict.get('orig_pathrev')
+ pathrev = request.query_dict.get('orig_pathrev')
view = _views.get(request.query_dict.get('orig_view'))
-
+
youngest = request.repos.get_youngest_revision()
# go out of the way to allow revision numbers higher than youngest
@@ -2669,7 +2691,7 @@ def redirect_pathrev(request):
if new_pathrev is None and pathrev == youngest:
pathrev = None
- request.server.redirect(request.get_url(view_func=view,
+ request.server.redirect(request.get_url(view_func=view,
where=path,
pathtype=pathtype,
params={'pathrev': pathrev}))
@@ -2751,7 +2773,7 @@ def view_log(request):
entry.diff_to_prev_href = None
entry.diff_to_branch_href = None
entry.diff_to_main_href = None
-
+
if request.roottype == 'cvs':
prev = rev.prev or rev.parent
entry.prev = prev and prev.string
@@ -2944,7 +2966,7 @@ def view_log(request):
data['tag_annotate_href']= fvi.annotate_href
data['tag_prefer_markup']= fvi.prefer_markup
else:
- data['head_view_href'] = request.get_url(view_func=view_directory,
+ data['head_view_href'] = request.get_url(view_func=view_directory,
params={}, escape=1)
taginfo = options.get('cvs_tags', {})
@@ -2985,7 +3007,7 @@ def view_log(request):
def view_checkout(request):
cfg = request.cfg
-
+
if 'co' not in cfg.options.allowed_views:
raise debug.ViewVCException('Checkout view is disabled',
'403 Forbidden')
@@ -3024,7 +3046,7 @@ def view_cvsgraph_image(request):
("-c", cfg.path(cfg.options.cvsgraph_conf),
"-r", request.repos.rootpath,
rcsfile), 'rb', 0)
-
+
copy_stream(fp, get_writeready_server_file(request, 'image/png'))
fp.close()
@@ -3054,7 +3076,7 @@ def view_cvsgraph(request):
"-x", "x",
"-3", request.get_url(view_func=view_log, params={},
escape=1),
- "-4", request.get_url(view_func=view,
+ "-4", request.get_url(view_func=view,
params={'revision': None},
escape=1, partial=1),
"-5", request.get_url(view_func=view_diff,
@@ -3155,7 +3177,7 @@ class DiffSource:
self.save_line = None
self.line_number = None
self.prev_line_number = None
-
+
# keep track of where we are during an iteration
self.idx = -1
self.last = None
@@ -3186,11 +3208,11 @@ class DiffSource:
if self.cfg.options.tabsize > 0:
text = string.expandtabs(text, self.cfg.options.tabsize)
hr_breakable = self.cfg.options.hr_breakable
-
+
# in the code below, "\x01" will be our stand-in for "&". We don't want
# to insert "&" because it would get escaped by sapi.escape(). Similarly,
# we use "\x02" as a stand-in for "<br>"
-
+
if hr_breakable > 1 and len(text) > hr_breakable:
text = re.sub('(' + ('.' * hr_breakable) + ')', '\\1\x02', text)
if hr_breakable:
@@ -3203,7 +3225,7 @@ class DiffSource:
text = string.replace(text, '\x02',
'<span style="color:red">\</span><br />')
return text
-
+
def _get_row(self):
if self.state[:5] == 'flush':
item = self._flush_row()
@@ -3243,7 +3265,7 @@ class DiffSource:
line_info_left=match.group(1),
line_info_right=match.group(2),
line_info_extra=self._format_text(match.group(3)))
-
+
if line[0] == '\\':
# \ No newline at end of file
@@ -3254,7 +3276,7 @@ class DiffSource:
diff_code = line[0]
output = self._format_text(line[1:])
-
+
if diff_code == '+':
if self.state == 'dump':
self.line_number = self.line_number + 1
@@ -3353,7 +3375,7 @@ def diff_parse_headers(fp, diff_type, pa
elif line[:3] == 'Bin':
flag = _RCSDIFF_IS_BINARY
parsing = 0
- elif (string.find(line, 'not found') != -1 or
+ elif (string.find(line, 'not found') != -1 or
string.find(line, 'illegal option') != -1):
flag = _RCSDIFF_ERROR
parsing = 0
@@ -3411,7 +3433,7 @@ def setup_diff(request):
else:
rev1 = r1[:idx]
sym1 = r1[idx+1:]
-
+
if r2 == 'text':
rev2 = query_dict.get('tr2', None)
if not rev2:
@@ -3433,7 +3455,7 @@ def setup_diff(request):
except vclib.InvalidRevision:
raise debug.ViewVCException('Invalid revision(s) passed to diff',
'400 Bad Request')
-
+
p1 = _get_diff_path_parts(request, 'p1', rev1, request.pathrev)
p2 = _get_diff_path_parts(request, 'p2', rev2, request.pathrev)
@@ -3476,7 +3498,7 @@ def view_patch(request):
else:
raise debug.ViewVCException('Diff format %s not understood'
% format, '400 Bad Request')
-
+
try:
fp = request.repos.rawdiff(p1, rev1, p2, rev2, diff_type)
except vclib.InvalidRevision:
@@ -3503,7 +3525,7 @@ def view_diff(request):
cfg = request.cfg
query_dict = request.query_dict
p1, p2, rev1, rev2, sym1, sym2 = setup_diff(request)
-
+
mime_type1, encoding1 = calculate_mime_type(request, p1, rev1)
mime_type2, encoding2 = calculate_mime_type(request, p2, rev2)
if is_binary_file_mime_type(mime_type1, cfg) or \
@@ -3524,7 +3546,7 @@ def view_diff(request):
and html_time(request, log_entry1.date, 1) or None
ago2 = log_entry2.date is not None \
and html_time(request, log_entry2.date, 2) or None
-
+
diff_type = None
diff_options = {}
human_readable = 0
@@ -3579,7 +3601,7 @@ def view_diff(request):
else:
unified = idiff.unified(lines_left, lines_right,
diff_options.get("context", 2))
- else:
+ else:
fp = request.repos.rawdiff(p1, rev1, p2, rev2, diff_type, diff_options)
except vclib.InvalidRevision:
raise debug.ViewVCException('Invalid path(s) or revision(s) passed '
@@ -3622,7 +3644,7 @@ def view_diff(request):
annotate_href=fvi.annotate_href,
revision_href=fvi.revision_href,
prefer_markup=fvi.prefer_markup)
-
+
fvi = get_file_view_info(request, path_right, rev2)
right = _item(date=make_time_string(log_entry2.date, cfg),
author=log_entry2.author,
@@ -3744,7 +3766,7 @@ def generate_tarball(out, request, reldi
tar_dir = tar_dir + _path_join(reldir) + '/'
cvs = request.roottype == 'cvs'
-
+
# If our caller doesn't dictate a datestamp to use for the current
# directory, its datestamps will be the youngest of the datestamps
# of versioned items in that subdirectory. We'll be ignoring dead
@@ -3822,7 +3844,7 @@ def generate_tarball(out, request, reldi
# Write the tarball header...
generate_tarball_header(out, tar_dir + file.name, filesize, mode,
file.date is not None and file.date or 0)
-
+
# ...the file's contents ...
fp = request.repos.openfile(rep_path + [file.name], request.pathrev, {})[0]
while 1:
@@ -3852,7 +3874,7 @@ def generate_tarball(out, request, reldi
def download_tarball(request):
cfg = request.cfg
-
+
if 'tar' not in request.cfg.options.allowed_views:
raise debug.ViewVCException('Tarball generation is disabled',
'403 Forbidden')
@@ -3863,7 +3885,7 @@ def download_tarball(request):
# our own gzip stream wrapper.
if debug.TARFILE_PATH:
fp = open(debug.TARFILE_PATH, 'w')
- else:
+ else:
tarfile = request.rootname
if request.path_parts:
tarfile = "%s-%s" % (tarfile, request.path_parts[-1])
@@ -3904,7 +3926,7 @@ def view_revision(request):
except vclib.InvalidRevision:
raise debug.ViewVCException('Invalid revision', '404 Not Found')
youngest_rev = request.repos.get_youngest_revision()
-
+
# The revision number acts as a weak validator (but we tell browsers
# not to cache the youngest revision).
if rev != youngest_rev and check_freshness(request, None, str(rev), weak=1):
@@ -3934,7 +3956,7 @@ def view_revision(request):
value = None
undisplayable = ezt.boolean(1)
props.append(_item(name=name, value=value, undisplayable=undisplayable))
-
+
# Sort the changes list by path.
def changes_sort_by_path(a, b):
return cmp(a.path_parts, b.path_parts)
@@ -4002,14 +4024,14 @@ def view_revision(request):
if change.pathtype is vclib.FILE and change.text_changed:
change.diff_href = request.get_url(view_func=view_diff,
- where=path,
+ where=path,
pathtype=change.pathtype,
params={'pathrev' : str(rev),
'r1' : str(rev),
'r2' : str(change.base_rev),
},
escape=1)
-
+
# use same variable names as the log template
change.path = _path_join(change.path_parts)
@@ -4099,7 +4121,7 @@ def is_querydb_nonempty_for_root(request
def validate_query_args(request):
# Do some additional input validation of query form arguments beyond
# what is offered by the CGI param validation loop in Request.run_viewvc().
-
+
for arg_base in ['branch', 'file', 'comment', 'who']:
# First, make sure the the XXX_match args have valid values:
arg_match = arg_base + '_match'
@@ -4122,7 +4144,7 @@ def validate_query_args(request):
'An illegal value was provided for the "%s" parameter.'
% (arg_base),
'400 Bad Request')
-
+
def view_queryform(request):
if not is_query_supported(request):
raise debug.ViewVCException('Can not query project root "%s" at "%s".'
@@ -4131,7 +4153,7 @@ def view_queryform(request):
# Do some more precise input validation.
validate_query_args(request)
-
+
query_action, query_hidden_values = \
request.get_form(view_func=view_query, params={'limit_changes': None})
limit_changes = \
@@ -4140,7 +4162,7 @@ def view_queryform(request):
def escaped_query_dict_get(itemname, itemdefault=''):
return request.server.escape(request.query_dict.get(itemname, itemdefault))
-
+
data = common_template_data(request)
data.merge(ezt.TemplateData({
'branch' : escaped_query_dict_get('branch', ''),
@@ -4167,7 +4189,7 @@ def view_queryform(request):
def parse_date(datestr):
"""Parse a date string from the query form."""
-
+
match = re.match(r'^(\d\d\d\d)-(\d\d)-(\d\d)(?:\ +'
'(\d\d):(\d\d)(?::(\d\d))?)?$', datestr)
if match:
@@ -4279,7 +4301,7 @@ def build_commit(request, files, max_fil
plus_count = 0
minus_count = 0
found_unreadable = 0
-
+
for f in files:
dirname = f.GetDirectory()
filename = f.GetFile()
@@ -4310,7 +4332,7 @@ def build_commit(request, files, max_fil
and is_cvsroot_path(request.roottype, path_parts):
found_unreadable = 1
continue
-
+
# We have to do a rare authz check here because this data comes
# from the CVSdb, not from the vclib providers.
#
@@ -4328,12 +4350,12 @@ def build_commit(request, files, max_fil
if not readable:
found_unreadable = 1
continue
-
+
if request.roottype == 'svn':
params = { 'pathrev': exam_rev }
else:
- params = { 'revision': exam_rev, 'pathrev': f.GetBranch() or None }
-
+ params = { 'revision': exam_rev, 'pathrev': f.GetBranch() or None }
+
dir_href = request.get_url(view_func=view_directory,
where=dirname, pathtype=vclib.DIR,
params=params, escape=1)
@@ -4367,7 +4389,7 @@ def build_commit(request, files, max_fil
minus = int(f.GetMinusCount())
plus_count = plus_count + plus
minus_count = minus_count + minus
-
+
num_allowed = num_allowed + 1
if max_files and num_allowed > max_files:
continue
@@ -4560,7 +4582,7 @@ def view_query(request):
db.RunQuery(query)
commit_list = query.GetCommitList()
row_limit_reached = query.GetLimitReached()
-
+
# gather commits
commits = []
plus_count = 0
@@ -4580,7 +4602,7 @@ def view_query(request):
# base modification time on the newest commit
if commit.GetTime() > mod_time:
mod_time = commit.GetTime()
-
+
# For CVS, group commits with the same commit message.
# For Subversion, group them only if they have the same revision number
if request.roottype == 'cvs':
@@ -4605,7 +4627,7 @@ def view_query(request):
limited_files = 0
current_desc = commit_desc
current_rev = commit_rev
-
+
# we need to tack on our last commit grouping, if any
commit_item = build_commit(request, files, limit_changes,
dir_strip, format)
@@ -4614,7 +4636,7 @@ def view_query(request):
plus_count = plus_count + commit_item.plus
minus_count = minus_count + commit_item.minus
commits.append(commit_item)
-
+
# only show the branch column if we are querying all branches
# or doing a non-exact branch match on a CVS repository.
show_branch = ezt.boolean(request.roottype == 'cvs' and
@@ -4689,7 +4711,7 @@ for code, view in _views.items():
def list_roots(request):
cfg = request.cfg
allroots = { }
-
+
# Add the viewable Subversion roots
for root in cfg.general.svn_roots.keys():
auth = setup_authorizer(cfg, request.username, root)
@@ -4725,12 +4747,12 @@ def list_roots(request):
except vclib.ReposNotFound:
continue
allroots[root] = [cfg.general.cvs_roots[root], 'cvs', None]
-
+
return allroots
def expand_root_parents(cfg):
"""Expand the configured root parents into individual roots."""
-
+
# Each item in root_parents is a "directory : repo_type" string.
for pp in cfg.general.root_parents:
pos = string.rfind(pp, ':')
@@ -4762,7 +4784,7 @@ def find_root_in_parents(cfg, rootname,
# Easy out: caller wants rootname "CVSROOT", and we're hiding those.
if rootname == 'CVSROOT' and cfg.options.hide_cvsroot:
return None
-
+
for pp in cfg.general.root_parents:
pos = string.rfind(pp, ':')
if pos < 0:
@@ -4771,7 +4793,7 @@ def find_root_in_parents(cfg, rootname,
if repo_type != roottype:
continue
pp = os.path.normpath(string.strip(pp[:pos]))
-
+
rootpath = None
if roottype == 'cvs':
rootpath = vclib.ccvs.find_root_in_parent(pp, rootname)
@@ -4797,7 +4819,7 @@ def locate_root(cfg, rootname):
cfg.general.svn_roots[rootname] = path_in_parent
return 'svn', path_in_parent
return None, None
-
+
def load_config(pathname=None, server=None):
"""Load the ViewVC configuration file. SERVER is the server object
that will be using this configuration. Consult the environment for
@@ -4805,7 +4827,7 @@ def load_config(pathname=None, server=No
legacy name) and, if set, use its value as the path of the
configuration file; otherwise, use PATHNAME (if provided). Failing
all else, use a hardcoded default configuration path."""
-
+
debug.t_start('load-config')
# See if the environment contains overrides to the configuration