1
0
mirror of https://github.com/vitalif/viewvc-4intranet synced 2019-04-16 04:14:59 +03:00

Compare commits

..

1 Commits

Author SHA1 Message Date
cmpilato
8b0bcade3b Tag the 1.0.4 final release.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/tags/1.0.4@1582 8cb11bc2-c004-0410-86c3-e597b4017df7
2007-04-10 06:16:01 +00:00
71 changed files with 1624 additions and 1076 deletions

63
CHANGES
View File

@@ -1,66 +1,3 @@
Version 1.0.13 (released 24-Oct-2012)
* security fix: escape "extra" diff info to avoid XSS attack (issue #515)
* security fix: remove user-reachable override of cvsdb row limit
* fix obscure "unexpected NULL parent pool" Subversion bindings error
* fix svndbadmin failure on deleted paths under Subversion 1.7 (issue #499)
Version 1.0.12 (released 02-Jun-2010)
* fix exception caused by trying to HTML-escape non-string data (issue #454)
Version 1.0.11 (released 29-Mar-2010)
* security fix: escape user-provided search_re input to avoid XSS attack
Version 1.0.10 (released 10-Mar-2010)
* security fix: escape user-provided query form input to avoid XSS attack
* fix errors viewing remote Subversion paths with URI-unsafe characters
* fix regexp input validation (issue #426, #427, #440)
Version 1.0.9 (released 11-Aug-2009)
* security fix: validate the 'view' parameter to avoid XSS attack
* security fix: avoid printing illegal parameter names and values
Version 1.0.8 (released 05-May-2009)
* fix directory view sorting UI
* tolerate malformed Accept-Language headers (issue #396)
* fix directory log views in revision-less Subversion repositories
* fix exception in rev-sorted remote Subversion directory views (issue #409)
Version 1.0.7 (released 14-Oct-2008)
* fix regression in the 'as text' download view (issue #373)
Version 1.0.6 (released 16-Sep-2008)
* security fix: ignore arbitrary user-provided MIME types (issue #354)
* fix bug in regexp search filter when used with sticky tag (issue #346)
* fix bug in handling of certain 'co' output (issue #348)
* fix regexp search filter template bug
* fix annotate code syntax error
* fix mod_python import cycle (issue #369)
Version 1.0.5 (released 28-Feb-2008)
* security fix: omit commits of all-forbidden files from query results
* security fix: disallow direct URL navigation to hidden CVSROOT folder
* security fix: strip forbidden paths from revision view
* security fix: don't traverse log history thru forbidden locations
* security fix: honor forbiddenness via diff view path parameters
* new 'forbiddenre' regexp-based path authorization feature
* fix root name conflict resolution inconsistencies (issue #287)
* fix an oversight in the CVS 1.12.9 loginfo-handler support
* fix RSS feed content type to be more specific (issue #306)
* fix entity escaping problems in RSS feed data (issue #238)
* fix bug in tarball generation for remote Subversion repositories
* fix query interface file-count-limiting logic
* fix query results plus/minus count to ignore forbidden files
* fix blame error caused by 'svn' unable to create runtime config dir
Version 1.0.4 (released 10-Apr-2007) Version 1.0.4 (released 10-Apr-2007)
* fix some markup bugs in query views (issue #266) * fix some markup bugs in query views (issue #266)

12
INSTALL
View File

@@ -34,7 +34,7 @@ Congratulations on getting this far. :-)
(http://www.python.org/) (http://www.python.org/)
* Subversion, Version Control System, 1.2.0 or later * Subversion, Version Control System, 1.2.0 or later
(binary installation and Python bindings) (binary installation and Python bindings)
(http://subversion.apache.org/) (http://subversion.tigris.org/)
Optional: Optional:
@@ -260,16 +260,6 @@ or if you've got Mod_Python installed you can use METHOD D:
http://<server_name>/viewvc/~checkout~/<module_name> http://<server_name>/viewvc/~checkout~/<module_name>
http://<server_name>/viewvc/<module_name>.tar.gz?view=tar http://<server_name>/viewvc/<module_name>.tar.gz?view=tar
5) Optional: Protect your ViewVC instance from server-whacking webcrawlers.
As ViewVC is a web-based application which each page containing various
links to other pages and views, you can expect your server's performance
to suffer if a webcrawler finds your ViewVC instance and begins
traversing those links. We highly recommend that you add your ViewVC
location to a site-wide robots.txt file. Visit the Wikipedia page
for Robots.txt (http://en.wikipedia.org/wiki/Robots.txt) for more
information.
UPGRADING VIEWVC UPGRADING VIEWVC
----------------- -----------------

View File

@@ -3,7 +3,7 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -3,7 +3,7 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC
@@ -77,8 +77,6 @@ def Cvs1Dot12ArgParse(args):
if args[1] == '- New directory': if args[1] == '- New directory':
return None, None return None, None
elif args[1] == '- Imported sources':
return None, None
else: else:
directory = args.pop(0) directory = args.pop(0)
files = [] files = []

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC
@@ -42,23 +42,9 @@ if LIBRARY_DIR:
sys.path.insert(0, LIBRARY_DIR) sys.path.insert(0, LIBRARY_DIR)
import sapi import sapi
import imp import viewvc
import query
# Import real ViewVC module reload(query) # need reload because initial import loads this stub file
fp, pathname, description = imp.find_module('viewvc', [LIBRARY_DIR])
try:
viewvc = imp.load_module('viewvc', fp, pathname, description)
finally:
if fp:
fp.close()
# Import real ViewVC Query modules
fp, pathname, description = imp.find_module('query', [LIBRARY_DIR])
try:
query = imp.load_module('query', fp, pathname, description)
finally:
if fp:
fp.close()
cfg = viewvc.load_config(CONF_PATHNAME) cfg = viewvc.load_config(CONF_PATHNAME)

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC
@@ -42,15 +42,9 @@ if LIBRARY_DIR:
sys.path.insert(0, LIBRARY_DIR) sys.path.insert(0, LIBRARY_DIR)
import sapi import sapi
import imp import viewvc
reload(viewvc) # need reload because initial import loads this stub file
# Import real ViewVC module
fp, pathname, description = imp.find_module('viewvc', [LIBRARY_DIR])
try:
viewvc = imp.load_module('viewvc', fp, pathname, description)
finally:
if fp:
fp.close()
def index(req): def index(req):
server = sapi.ModPythonServer(req) server = sapi.ModPythonServer(req)

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,7 +1,6 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 2004 James Henstridge # Copyright (C) 2004 James Henstridge
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
@@ -155,7 +154,7 @@ class SvnRev:
fsroot = self._get_root_for_rev(rev) fsroot = self._get_root_for_rev(rev)
# find changes in the revision # find changes in the revision
editor = svn.repos.ChangeCollector(repo.fs, fsroot, pool) editor = svn.repos.RevisionChangeCollector(repo.fs, rev, pool)
e_ptr, e_baton = svn.delta.make_editor(editor, pool) e_ptr, e_baton = svn.delta.make_editor(editor, pool)
svn.repos.svn_repos_replay(fsroot, e_ptr, e_baton, pool) svn.repos.svn_repos_replay(fsroot, e_ptr, e_baton, pool)
@@ -169,35 +168,22 @@ class SvnRev:
if change.item_kind != svn.core.svn_node_file: continue if change.item_kind != svn.core.svn_node_file: continue
# deal with the change types we handle # deal with the change types we handle
action = None
base_root = None base_root = None
base_path = change.base_path
if change.base_path: if change.base_path:
base_root = self._get_root_for_rev(change.base_rev) base_root = self._get_root_for_rev(change.base_rev)
# figure out what kind of change this is, and get a diff if not change.path:
# object for it. note that prior to 1.4 Subversion's
# bindings didn't give us change.action, but that's okay
# because back then deleted paths always had a change.path
# of None.
if hasattr(change, 'action') \
and change.action == svn.repos.CHANGE_ACTION_DELETE:
action = 'remove'
elif not change.path:
action = 'remove' action = 'remove'
elif change.added: elif change.added:
action = 'add' action = 'add'
else: else:
action = 'change' action = 'change'
if action == 'remove': diffobj = svn.fs.FileDiff(base_root and base_root or None,
diffobj = svn.fs.FileDiff(base_root, base_path, None, None, base_root and change.base_path or None,
change.path and fsroot or None,
change.path and change.path or None,
subpool, []) subpool, [])
else:
diffobj = svn.fs.FileDiff(base_root, base_path,
fsroot, change.path,
subpool, [])
diff_fp = diffobj.get_pipe() diff_fp = diffobj.get_pipe()
plus, minus = _get_diff_counts(diff_fp) plus, minus = _get_diff_counts(diff_fp)
self.changes.append((path, action, plus, minus)) self.changes.append((path, action, plus, minus))

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC
@@ -39,7 +39,7 @@ def _parse(hdr, result):
while pos < len(hdr): while pos < len(hdr):
name = _re_token.match(hdr, pos) name = _re_token.match(hdr, pos)
if not name: if not name:
raise AcceptLanguageParseError() raise AcceptParseError()
a = result.item_class(string.lower(name.group(1))) a = result.item_class(string.lower(name.group(1)))
pos = name.end() pos = name.end()
while 1: while 1:
@@ -210,7 +210,7 @@ class _LanguageSelector:
def append(self, item): def append(self, item):
self.requested.append(item) self.requested.append(item)
class AcceptLanguageParseError(Exception): class AcceptParseError(Exception):
pass pass
def _test(): def _test():

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 2000 Curt Hagenlocher <curt@hagenlocher.org> # Copyright (C) 2000 Curt Hagenlocher <curt@hagenlocher.org>
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
@@ -31,8 +31,10 @@ import os
import re import re
import time import time
import math import math
import cgi
import vclib import vclib
import sapi import vclib.ccvs.blame
re_includes = re.compile('\\#(\\s*)include(\\s*)"(.*?)"') re_includes = re.compile('\\#(\\s*)include(\\s*)"(.*?)"')
@@ -80,7 +82,7 @@ class HTMLBlameSource:
diff_url = None diff_url = None
if item.prev_rev: if item.prev_rev:
diff_url = '%sr1=%s&amp;r2=%s' % (self.diff_url, item.prev_rev, item.rev) diff_url = '%sr1=%s&amp;r2=%s' % (self.diff_url, item.prev_rev, item.rev)
thisline = link_includes(sapi.escape(item.text), self.repos, thisline = link_includes(cgi.escape(item.text), self.repos,
self.path_parts, self.include_url) self.path_parts, self.include_url)
return _item(text=thisline, line_number=item.line_number, return _item(text=thisline, line_number=item.line_number,
rev=item.rev, prev_rev=item.prev_rev, rev=item.rev, prev_rev=item.prev_rev,
@@ -98,7 +100,6 @@ class _item:
def make_html(root, rcs_path): def make_html(root, rcs_path):
import vclib.ccvs.blame
bs = vclib.ccvs.blame.BlameSource(os.path.join(root, rcs_path)) bs = vclib.ccvs.blame.BlameSource(os.path.join(root, rcs_path))
count = bs.num_lines count = bs.num_lines

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC
@@ -19,8 +19,6 @@ import os
import string import string
import ConfigParser import ConfigParser
import fnmatch import fnmatch
import re
import vclib
######################################################################### #########################################################################
@@ -41,7 +39,7 @@ import vclib
class Config: class Config:
_sections = ('general', 'options', 'cvsdb', 'templates') _sections = ('general', 'options', 'cvsdb', 'templates')
_force_multi_value = ('cvs_roots', 'forbidden', 'forbiddenre', _force_multi_value = ('cvs_roots', 'forbidden',
'svn_roots', 'languages', 'kv_files', 'svn_roots', 'languages', 'kv_files',
'root_parents') 'root_parents')
@@ -153,8 +151,7 @@ class Config:
self.general.svn_path = '' self.general.svn_path = ''
self.general.mime_types_file = '' self.general.mime_types_file = ''
self.general.address = '<a href="mailto:user@insert.your.domain.here">No admin address has been configured</a>' self.general.address = '<a href="mailto:user@insert.your.domain.here">No admin address has been configured</a>'
self.general.forbidden = [] self.general.forbidden = ()
self.general.forbiddenre = []
self.general.kv_files = [ ] self.general.kv_files = [ ]
self.general.languages = ['en-us'] self.general.languages = ['en-us']
@@ -225,52 +222,9 @@ class Config:
self.options.http_expiration_time = 600 self.options.http_expiration_time = 600
self.options.generate_etags = 1 self.options.generate_etags = 1
def is_forbidden(self, root, path_parts, pathtype): def is_forbidden(self, module):
# If we don't have a root and path to check, get outta here. if not module:
if not (root and path_parts):
return 0 return 0
# Give precedence to the new 'forbiddenre' stuff first.
if self.general.forbiddenre:
# Join the root and path-parts together into one path-like thing.
root_and_path = string.join([root] + path_parts, "/")
if pathtype == vclib.DIR:
root_and_path = root_and_path + '/'
# If we still have a list of strings, replace those suckers with
# lists of (compiled_regex, negation_flag)
if type(self.general.forbiddenre[0]) == type(""):
for i in range(len(self.general.forbiddenre)):
pat = self.general.forbiddenre[i]
if pat[0] == '!':
self.general.forbiddenre[i] = (re.compile(pat[1:]), 1)
else:
self.general.forbiddenre[i] = (re.compile(pat), 0)
# Do the forbiddenness test.
default = 0
for (pat, negated) in self.general.forbiddenre:
match = pat.search(root_and_path)
if negated:
default = 1
if match:
return 0
elif match:
return 1
return default
# If no 'forbiddenre' is in use, we check 'forbidden', which only
# looks at the top-most directory.
elif self.general.forbidden:
# A root and a single non-directory path component? That's not
# a module.
if len(path_parts) == 1 and pathtype != vclib.DIR:
return 0
# Do the forbiddenness test.
module = path_parts[0]
default = 0 default = 0
for pat in self.general.forbidden: for pat in self.general.forbidden:
if pat[0] == '!': if pat[0] == '!':
@@ -281,9 +235,6 @@ class Config:
return 1 return 1
return default return default
# No forbiddenness configuration? Just allow it.
else:
return 0
def _parse_roots(config_name, config_value): def _parse_roots(config_name, config_value):
roots = { } roots = { }

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC
@@ -29,12 +29,13 @@ error = "cvsdb error"
## complient database interface ## complient database interface
class CheckinDatabase: class CheckinDatabase:
def __init__(self, host, port, user, passwd, database): def __init__(self, host, port, user, passwd, database, row_limit):
self._host = host self._host = host
self._port = port self._port = port
self._user = user self._user = user
self._passwd = passwd self._passwd = passwd
self._database = database self._database = database
self._row_limit = row_limit
## database lookup caches ## database lookup caches
self._get_cache = {} self._get_cache = {}
@@ -348,11 +349,13 @@ class CheckinDatabase:
conditions = string.join(joinConds + condList, " AND ") conditions = string.join(joinConds + condList, " AND ")
conditions = conditions and "WHERE %s" % conditions conditions = conditions and "WHERE %s" % conditions
## apply the query's row limit, if any (so we avoid really ## limit the number of rows requested or we could really slam
## slamming a server with a large database) ## a server with a large database
limit = "" limit = ""
if query.limit: if query.limit:
limit = "LIMIT %s" % (str(query.limit)) limit = "LIMIT %s" % (str(query.limit))
elif self._row_limit:
limit = "LIMIT %s" % (str(self._row_limit))
sql = "SELECT checkins.* FROM %s %s %s %s" % ( sql = "SELECT checkins.* FROM %s %s %s %s" % (
tables, conditions, order_by, limit) tables, conditions, order_by, limit)
@@ -591,8 +594,8 @@ class QueryEntry:
self.data = data self.data = data
self.match = match self.match = match
## CheckinDatabaseQuery is an object which contains the search ## CheckinDatabaseQueryData is a object which contains the search parameters
## parameters for a query to the Checkin Database ## for a query to the CheckinDatabase
class CheckinDatabaseQuery: class CheckinDatabaseQuery:
def __init__(self): def __init__(self):
## sorting ## sorting
@@ -679,7 +682,7 @@ def ConnectDatabase(cfg, readonly=0):
user = cfg.cvsdb.user user = cfg.cvsdb.user
passwd = cfg.cvsdb.passwd passwd = cfg.cvsdb.passwd
db = CheckinDatabase(cfg.cvsdb.host, cfg.cvsdb.port, user, passwd, db = CheckinDatabase(cfg.cvsdb.host, cfg.cvsdb.port, user, passwd,
cfg.cvsdb.database_name) cfg.cvsdb.database_name, cfg.cvsdb.row_limit)
db.Connect() db.Connect()
return db return db

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC
@@ -38,17 +38,13 @@ if SHOW_TIMES:
else: else:
_times[which] = t _times[which] = t
def t_dump(out): def dump():
out.write('<div>') for name, value in _times.items():
names = _times.keys() print '%s: %.6f<br />' % (name, value)
names.sort()
for name in names:
out.write('%s: %.6fs<br/>\n' % (name, _times[name]))
out.write('</div>')
else: else:
t_start = t_end = t_dump = lambda *args: None t_start = t_end = dump = lambda *args: None
class ViewVCException: class ViewVCException:

View File

@@ -200,7 +200,7 @@ Directives
equivalent to "[CALLBACK QUAL_NAME]" equivalent to "[CALLBACK QUAL_NAME]"
""" """
# #
# Copyright (C) 2001-2007 Greg Stein. All Rights Reserved. # Copyright (C) 2001-2005 Greg Stein. All Rights Reserved.
# #
# Redistribution and use in source and binary forms, with or without # Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are # modification, are permitted provided that the following conditions are

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC
@@ -20,7 +20,7 @@ import difflib
import sys import sys
import re import re
import ezt import ezt
import sapi import cgi
def sidebyside(fromlines, tolines, context): def sidebyside(fromlines, tolines, context):
"""Generate side by side diff""" """Generate side by side diff"""
@@ -49,18 +49,18 @@ def _mdiff_split(flag, (line_number, text)):
while True: while True:
m = _re_mdiff.search(text, pos) m = _re_mdiff.search(text, pos)
if not m: if not m:
segments.append(_item(text=sapi.escape(text[pos:]), type=None)) segments.append(_item(text=cgi.escape(text[pos:]), type=None))
break break
if m.start() > pos: if m.start() > pos:
segments.append(_item(text=sapi.escape(text[pos:m.start()]), type=None)) segments.append(_item(text=cgi.escape(text[pos:m.start()]), type=None))
if m.group(1) == "+": if m.group(1) == "+":
segments.append(_item(text=sapi.escape(m.group(2)), type="add")) segments.append(_item(text=cgi.escape(m.group(2)), type="add"))
elif m.group(1) == "-": elif m.group(1) == "-":
segments.append(_item(text=sapi.escape(m.group(2)), type="remove")) segments.append(_item(text=cgi.escape(m.group(2)), type="remove"))
elif m.group(1) == "^": elif m.group(1) == "^":
segments.append(_item(text=sapi.escape(m.group(2)), type="change")) segments.append(_item(text=cgi.escape(m.group(2)), type="change"))
pos = m.end() pos = m.end()
@@ -166,12 +166,12 @@ def _differ_split(row, guide):
for m in _re_differ.finditer(guide, pos): for m in _re_differ.finditer(guide, pos):
if m.start() > pos: if m.start() > pos:
segments.append(_item(text=sapi.escape(line[pos:m.start()]), type=None)) segments.append(_item(text=cgi.escape(line[pos:m.start()]), type=None))
segments.append(_item(text=sapi.escape(line[m.start():m.end()]), segments.append(_item(text=cgi.escape(line[m.start():m.end()]),
type="change")) type="change"))
pos = m.end() pos = m.end()
segments.append(_item(text=sapi.escape(line[pos:]), type=None)) segments.append(_item(text=cgi.escape(line[pos:]), type=None))
return _item(gap=ezt.boolean(gap), type=type, segments=segments, return _item(gap=ezt.boolean(gap), type=type, segments=segments,
left_number=left_number, right_number=right_number) left_number=left_number, right_number=right_number)

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC
@@ -25,7 +25,6 @@ import time
import cvsdb import cvsdb
import viewvc import viewvc
import vclib
import ezt import ezt
import debug import debug
import urllib import urllib
@@ -217,9 +216,8 @@ def decode_command(cmd):
else: else:
return "exact" return "exact"
def form_to_cvsdb_query(cfg, form_data): def form_to_cvsdb_query(form_data):
query = cvsdb.CreateCheckinQuery() query = cvsdb.CreateCheckinQuery()
query.SetLimit(cfg.cvsdb.row_limit)
if form_data.repository: if form_data.repository:
for cmd, str in listparse_string(form_data.repository): for cmd, str in listparse_string(form_data.repository):
@@ -284,8 +282,10 @@ def build_commit(server, cfg, desc, files, cvsroots, viewvc_link):
ob.log = '&nbsp;' ob.log = '&nbsp;'
for commit in files: for commit in files:
parts = filter(None, string.split(commit.GetDirectory(), '/')) dir_parts = filter(None, string.split(commit.GetDirectory(), '/'))
if parts and cfg.options.hide_cvsroot and parts[0] == 'CVSROOT': if dir_parts \
and ((dir_parts[0] == 'CVSROOT' and cfg.options.hide_cvsroot) \
or cfg.is_forbidden(dir_parts[0])):
continue continue
ctime = commit.GetTime() ctime = commit.GetTime()
@@ -304,11 +304,6 @@ def build_commit(server, cfg, desc, files, cvsroots, viewvc_link):
file = (directory and directory + "/") + commit.GetFile() file = (directory and directory + "/") + commit.GetFile()
cvsroot_name = cvsroots.get(repository) cvsroot_name = cvsroots.get(repository)
## skip forbidden files
if cfg.is_forbidden(cvsroot_name,
filter(None, string.split(file, "/")), vclib.FILE):
continue
## if we couldn't find the cvsroot path configured in the ## if we couldn't find the cvsroot path configured in the
## viewvc.conf file, then don't make the link ## viewvc.conf file, then don't make the link
if cvsroot_name: if cvsroot_name:
@@ -339,7 +334,7 @@ def build_commit(server, cfg, desc, files, cvsroots, viewvc_link):
return ob return ob
def run_query(server, cfg, form_data, viewvc_link): def run_query(server, cfg, form_data, viewvc_link):
query = form_to_cvsdb_query(cfg, form_data) query = form_to_cvsdb_query(form_data)
db = cvsdb.ConnectDatabaseReadOnly(cfg) db = cvsdb.ConnectDatabaseReadOnly(cfg)
db.RunQuery(query) db.RunQuery(query)
@@ -350,7 +345,7 @@ def run_query(server, cfg, form_data, viewvc_link):
files = [ ] files = [ ]
cvsroots = {} cvsroots = {}
rootitems = cfg.general.svn_roots.items() + cfg.general.cvs_roots.items() rootitems = cfg.general.cvs_roots.items() + cfg.general.svn_roots.items()
for key, value in rootitems: for key, value in rootitems:
cvsroots[cvsdb.CleanRepository(value)] = key cvsroots[cvsdb.CleanRepository(value)] = key
@@ -371,13 +366,6 @@ def run_query(server, cfg, form_data, viewvc_link):
commits.append(build_commit(server, cfg, current_desc, files, commits.append(build_commit(server, cfg, current_desc, files,
cvsroots, viewvc_link)) cvsroots, viewvc_link))
# Strip out commits that don't have any files attached to them. The
# files probably aren't present because they've been blocked via
# forbiddenness.
def _only_with_files(commit):
return len(commit.files) > 0
commits = filter(_only_with_files, commits)
return commits return commits
def main(server, cfg, viewvc_link): def main(server, cfg, viewvc_link):
@@ -399,11 +387,12 @@ def main(server, cfg, viewvc_link):
'cfg' : cfg, 'cfg' : cfg,
'address' : cfg.general.address, 'address' : cfg.general.address,
'vsn' : viewvc.__version__, 'vsn' : viewvc.__version__,
'repository' : server.escape(form_data.repository),
'branch' : server.escape(form_data.branch), 'repository' : server.escape(form_data.repository, 1),
'directory' : server.escape(form_data.directory), 'branch' : server.escape(form_data.branch, 1),
'file' : server.escape(form_data.file), 'directory' : server.escape(form_data.directory, 1),
'who' : server.escape(form_data.who), 'file' : server.escape(form_data.file, 1),
'who' : server.escape(form_data.who, 1),
'docroot' : cfg.options.docroot is None \ 'docroot' : cfg.options.docroot is None \
and viewvc_link + '/' + viewvc.docroot_magic_path \ and viewvc_link + '/' + viewvc.docroot_magic_path \
or cfg.options.docroot, or cfg.options.docroot,

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC
@@ -20,7 +20,6 @@ import string
import os import os
import sys import sys
import re import re
import cgi
# global server object. It will be either a CgiServer or a proxy to # global server object. It will be either a CgiServer or a proxy to
@@ -28,19 +27,6 @@ import cgi
server = None server = None
# Simple HTML string escaping. Note that we always escape the
# double-quote character -- ViewVC shouldn't ever need to preserve
# that character as-is, and sometimes needs to embed escaped values
# into HTML attributes.
def escape(s):
s = str(s)
s = string.replace(s, '&', '&amp;')
s = string.replace(s, '>', '&gt;')
s = string.replace(s, '<', '&lt;')
s = string.replace(s, '"', "&quot;")
return s
class Server: class Server:
def __init__(self): def __init__(self):
self.pageGlobals = {} self.pageGlobals = {}
@@ -48,9 +34,6 @@ class Server:
def self(self): def self(self):
return self return self
def escape(self, s):
return escape(s)
def close(self): def close(self):
pass pass
@@ -146,6 +129,9 @@ class CgiServer(Server):
global server global server
server = self server = self
global cgi
import cgi
def addheader(self, name, value): def addheader(self, name, value):
self.headers.append((name, value)) self.headers.append((name, value))
@@ -175,6 +161,9 @@ class CgiServer(Server):
print 'This document is located <a href="%s">here</a>.' % url print 'This document is located <a href="%s">here</a>.' % url
sys.exit(0) sys.exit(0)
def escape(self, s, quote = None):
return cgi.escape(s, quote)
def getenv(self, name, value=None): def getenv(self, name, value=None):
ret = os.environ.get(name, value) ret = os.environ.get(name, value)
if self.iis and name == 'PATH_INFO' and ret: if self.iis and name == 'PATH_INFO' and ret:
@@ -232,6 +221,9 @@ class AspServer(ThreadedServer):
self.response.Redirect(url) self.response.Redirect(url)
sys.exit() sys.exit()
def escape(self, s, quote = None):
return self.server.HTMLEncode(str(s))
def getenv(self, name, value = None): def getenv(self, name, value = None):
ret = self.request.ServerVariables(name)() ret = self.request.ServerVariables(name)()
if not type(ret) is types.UnicodeType: if not type(ret) is types.UnicodeType:
@@ -293,6 +285,9 @@ class ModPythonServer(ThreadedServer):
self.request = request self.request = request
self.headerSent = 0 self.headerSent = 0
global cgi
import cgi
def addheader(self, name, value): def addheader(self, name, value):
self.request.headers_out.add(name, value) self.request.headers_out.add(name, value)
@@ -316,6 +311,9 @@ class ModPythonServer(ThreadedServer):
% (url, url)) % (url, url))
sys.exit() sys.exit()
def escape(self, s, quote = None):
return cgi.escape(s, quote)
def getenv(self, name, value = None): def getenv(self, name, value = None):
try: try:
return self.request.subprocess_env[name] return self.request.subprocess_env[name]

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC
@@ -534,13 +534,10 @@ def _parse_co_header(fp):
raise COMalformedOutput, "Unable to find filename in co output stream" raise COMalformedOutput, "Unable to find filename in co output stream"
filename = match.group(1) filename = match.group(1)
# look through subsequent lines for a revision. we might encounter # look for a revision in the second line.
# some ignorable or problematic lines along the way.
while 1:
line = fp.readline() line = fp.readline()
if not line: if not line:
break raise COMalformedOutput, "Missing second line from co output stream"
# look for a revision.
match = _re_co_revision.match(line) match = _re_co_revision.match(line)
if match: if match:
return filename, match.group(1) return filename, match.group(1)
@@ -549,10 +546,19 @@ def _parse_co_header(fp):
elif _re_co_warning.match(line): elif _re_co_warning.match(line):
pass pass
else: else:
break
raise COMalformedOutput, "Unable to find revision in co output stream" raise COMalformedOutput, "Unable to find revision in co output stream"
# if we get here, the second line wasn't a revision, but it was a
# warning we can ignore. look for a revision in the third line.
line = fp.readline()
if not line:
raise COMalformedOutput, "Missing third line from co output stream"
match = _re_co_revision.match(line)
if match:
return filename, match.group(1)
raise COMalformedOutput, "Unable to find revision in co output stream"
# if your rlog doesn't use 77 '=' characters, then this must change # if your rlog doesn't use 77 '=' characters, then this must change
LOG_END_MARKER = '=' * 77 + '\n' LOG_END_MARKER = '=' * 77 + '\n'
ENTRY_END_MARKER = '-' * 28 + '\n' ENTRY_END_MARKER = '-' * 28 + '\n'

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 2000 Curt Hagenlocher <curt@hagenlocher.org> # Copyright (C) 2000 Curt Hagenlocher <curt@hagenlocher.org>
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC
@@ -25,7 +25,7 @@ _tt = TextTools
_idchar_list = map(chr, range(33, 127)) + map(chr, range(160, 256)) _idchar_list = map(chr, range(33, 127)) + map(chr, range(160, 256))
_idchar_list.remove('$') _idchar_list.remove('$')
_idchar_list.remove(',') _idchar_list.remove(',')
#_idchar_list.remove('.') # leave as part of 'num' symbol #_idchar_list.remove('.') leave as part of 'num' symbol
_idchar_list.remove(':') _idchar_list.remove(':')
_idchar_list.remove(';') _idchar_list.remove(';')
_idchar_list.remove('@') _idchar_list.remove('@')

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC
@@ -15,11 +15,9 @@
import vclib import vclib
import os import os
import os.path import os.path
import stat
import string import string
import cStringIO import cStringIO
import signal import signal
import shutil
import time import time
import tempfile import tempfile
import popen import popen
@@ -239,9 +237,6 @@ class NodeHistory:
def _get_history(svnrepos, full_name, rev, options={}): def _get_history(svnrepos, full_name, rev, options={}):
if svnrepos.youngest == 0:
return {}
fsroot = svnrepos._getroot(rev) fsroot = svnrepos._getroot(rev)
show_all_logs = options.get('svn_show_all_dir_logs', 0) show_all_logs = options.get('svn_show_all_dir_logs', 0)
if not show_all_logs: if not show_all_logs:
@@ -504,36 +499,13 @@ class BlameSource:
if os.sep != '/': if os.sep != '/':
rootpath = string.replace(rootpath, os.sep, '/') rootpath = string.replace(rootpath, os.sep, '/')
# Make a read-only temporary directory for Subversion to use as
# its runtime config dir. (Read-only because that will prevent
# Subversion from fleshing out all the default runtime config
# contents.)
self.config_dir = self._mkdtemp()
os.chmod(self.config_dir, stat.S_IRUSR | stat.S_IXUSR)
url = 'file://' + string.join([rootpath, fs_path], "/") url = 'file://' + string.join([rootpath, fs_path], "/")
fp = popen.popen(svn_client_path, fp = popen.popen(svn_client_path,
("blame", ('blame', "-r%d" % int(rev), "--non-interactive",
"-r%d" % int(rev),
"--non-interactive",
"--config-dir", self.config_dir,
"%s@%d" % (url, int(rev))), "%s@%d" % (url, int(rev))),
'rb', 1) 'rb', 1)
self.fp = fp self.fp = fp
def _mkdtemp(self):
### FIXME: When we require Python 2.3, this can go away.
for i in range(10):
dir = tempfile.mktemp()
try:
os.mkdir(dir, 0700)
return dir
except OSError, e:
if e.errno == errno.EEXIST:
continue # try again
raise
raise IOError, (errno.EEXIST, "No usable temporary directory name found")
def __getitem__(self, idx): def __getitem__(self, idx):
if idx == self.idx: if idx == self.idx:
return self.last return self.last
@@ -558,13 +530,6 @@ class BlameSource:
self.idx = idx self.idx = idx
return item return item
def __del__(self):
try:
if self.config_dir:
shutil.rmtree(self.config_dir)
except:
pass
class BlameSequencingError(Exception): class BlameSequencingError(Exception):
pass pass

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC
@@ -20,7 +20,6 @@ import re
import tempfile import tempfile
import popen2 import popen2
import time import time
import urllib
from vclib.svn import Revision, ChangedPath, _datestr_to_date, _compare_paths, _cleanup_path from vclib.svn import Revision, ChangedPath, _datestr_to_date, _compare_paths, _cleanup_path
from svn import core, delta, client, wc, ra from svn import core, delta, client, wc, ra
@@ -101,12 +100,8 @@ def created_rev(svnrepos, full_name, rev):
kind = ra.svn_ra_check_path(svnrepos.ra_session, full_name, rev, kind = ra.svn_ra_check_path(svnrepos.ra_session, full_name, rev,
svnrepos.pool) svnrepos.pool)
if kind == core.svn_node_dir: if kind == core.svn_node_dir:
retval = ra.svn_ra_get_dir(svnrepos.ra_session, full_name, props = ra.svn_ra_get_dir(svnrepos.ra_session, full_name,
rev, svnrepos.pool) rev, svnrepos.pool)
if type(retval) == type([]) and len(retval) == 3:
props = retval[2]
else: # compat with older (broken) bindings
props = retval
return int(props[core.SVN_PROP_ENTRY_COMMITTED_REV]) return int(props[core.SVN_PROP_ENTRY_COMMITTED_REV])
return core.SVN_INVALID_REVNUM return core.SVN_INVALID_REVNUM
@@ -181,14 +176,12 @@ class LogCollector:
# Changed paths have leading slashes # Changed paths have leading slashes
changed_paths = paths.keys() changed_paths = paths.keys()
changed_paths.sort(lambda a, b: _compare_paths(a, b)) changed_paths.sort(lambda a, b: _compare_paths(a, b))
copyfrom_path = copyfrom_rev = this_path = None this_path = None
if self.path in changed_paths: if self.path in changed_paths:
this_path = self.path this_path = self.path
change = paths[self.path] change = paths[self.path]
if change.copyfrom_path: if change.copyfrom_path:
this_path = change.copyfrom_path this_path = change.copyfrom_path
copyfrom_path = change.copyfrom_path[1:]
copyfrom_rev = change.copyfrom_rev
for changed_path in changed_paths: for changed_path in changed_paths:
if changed_path != self.path: if changed_path != self.path:
# If a parent of our path was copied, our "next previous" # If a parent of our path was copied, our "next previous"
@@ -201,7 +194,7 @@ class LogCollector:
if self.show_all_logs or this_path: if self.show_all_logs or this_path:
date = _datestr_to_date(date, pool) date = _datestr_to_date(date, pool)
entry = Revision(revision, date, author, message, None, entry = Revision(revision, date, author, message, None,
self.path[1:], copyfrom_path, copyfrom_rev) self.path[1:], None, None)
self.logs.append(entry) self.logs.append(entry)
if this_path: if this_path:
self.path = this_path self.path = this_path
@@ -221,7 +214,7 @@ def get_logs(svnrepos, full_name, rev, files):
rev, author, date, log, changes = \ rev, author, date, log, changes = \
_get_rev_details(svnrepos, entry.created_rev, subpool) _get_rev_details(svnrepos, entry.created_rev, subpool)
rev_info_cache[entry.created_rev] = rev, author, date, log rev_info_cache[entry.created_rev] = rev, author, date, log
file.rev = str(rev) file.rev = rev
file.author = author file.author = author
file.date = _datestr_to_date(date, subpool) file.date = _datestr_to_date(date, subpool)
file.log = log file.log = log
@@ -235,7 +228,7 @@ def temp_checkout(svnrepos, path, rev, pool):
"""Check out file revision to temporary file""" """Check out file revision to temporary file"""
temp = tempfile.mktemp() temp = tempfile.mktemp()
stream = core.svn_stream_from_aprfile(temp, pool) stream = core.svn_stream_from_aprfile(temp, pool)
url = svnrepos._geturl(path) url = svnrepos.rootpath + (path and '/' + path)
client.svn_client_cat(core.Stream(stream), url, _rev2optrev(rev), client.svn_client_cat(core.Stream(stream), url, _rev2optrev(rev),
svnrepos.ctx, pool) svnrepos.ctx, pool)
core.svn_stream_close(stream) core.svn_stream_close(stream)
@@ -247,7 +240,7 @@ class SelfCleanFP:
self._path = path self._path = path
self._eof = 0 self._eof = 0
def read(self, len=None): def read(self, len):
if len: if len:
chunk = self._fp.read(len) chunk = self._fp.read(len)
else: else:
@@ -286,7 +279,7 @@ class SubversionRepository(vclib.Repository):
self.rootpath = rootpath self.rootpath = rootpath
# Setup the client context baton, complete with non-prompting authstuffs. # Setup the client context baton, complete with non-prompting authstuffs.
ctx = client.svn_client_create_context() ctx = client.svn_client_ctx_t()
providers = [] providers = []
providers.append(client.svn_client_get_simple_provider(pool)) providers.append(client.svn_client_get_simple_provider(pool))
providers.append(client.svn_client_get_username_provider(pool)) providers.append(client.svn_client_get_username_provider(pool))
@@ -324,10 +317,10 @@ class SubversionRepository(vclib.Repository):
raise vclib.ItemNotFound(path_parts) raise vclib.ItemNotFound(path_parts)
def openfile(self, path_parts, rev): def openfile(self, path_parts, rev):
path = self._getpath(path_parts)
rev = self._getrev(rev) rev = self._getrev(rev)
url = self._geturl(path) url = self.rootpath
if len(path_parts):
url = self.rootpath + '/' + self._getpath(path_parts)
tmp_file = tempfile.mktemp() tmp_file = tempfile.mktemp()
stream = core.svn_stream_from_aprfile(tmp_file, self.pool) stream = core.svn_stream_from_aprfile(tmp_file, self.pool)
### rev here should be the last history revision of the URL ### rev here should be the last history revision of the URL
@@ -354,15 +347,18 @@ class SubversionRepository(vclib.Repository):
get_logs(self, self._getpath(path_parts), self._getrev(rev), entries) get_logs(self, self._getpath(path_parts), self._getrev(rev), entries)
def itemlog(self, path_parts, rev, options): def itemlog(self, path_parts, rev, options):
path = self._getpath(path_parts) full_name = self._getpath(path_parts)
rev = self._getrev(rev) rev = self._getrev(rev)
url = self._geturl(path)
# It's okay if we're told to not show all logs on a file -- all # It's okay if we're told to not show all logs on a file -- all
# the revisions should match correctly anyway. # the revisions should match correctly anyway.
lc = LogCollector(path, options.get('svn_show_all_dir_logs', 0)) lc = LogCollector(full_name, options.get('svn_show_all_dir_logs', 0))
dir_url = self.rootpath
if full_name:
dir_url = dir_url + '/' + full_name
cross_copies = options.get('svn_cross_copies', 0) cross_copies = options.get('svn_cross_copies', 0)
client.svn_client_log([url], _rev2optrev(rev), _rev2optrev(1), client.svn_client_log([dir_url], _rev2optrev(rev), _rev2optrev(1),
1, not cross_copies, lc.add_log, 1, not cross_copies, lc.add_log,
self.ctx, self.pool) self.ctx, self.pool)
revs = lc.logs revs = lc.logs
@@ -377,7 +373,7 @@ class SubversionRepository(vclib.Repository):
def annotate(self, path_parts, rev): def annotate(self, path_parts, rev):
path = self._getpath(path_parts) path = self._getpath(path_parts)
rev = self._getrev(rev) rev = self._getrev(rev)
url = self._geturl(path) url = self.rootpath + (path and '/' + path)
blame_data = [] blame_data = []
@@ -427,17 +423,13 @@ class SubversionRepository(vclib.Repository):
raise vclib.InvalidRevision(rev) raise vclib.InvalidRevision(rev)
return rev return rev
def _geturl(self, path=None):
if not path:
return self.rootpath
return self.rootpath + '/' + urllib.quote(path, "/*~")
def _get_dirents(self, path, rev): def _get_dirents(self, path, rev):
dir_url = self._geturl(path)
if path: if path:
key = str(rev) + '/' + path key = str(rev) + '/' + path
dir_url = self.rootpath + '/' + path
else: else:
key = str(rev) key = str(rev)
dir_url = self.rootpath
dirents = self._dirent_cache.get(key) dirents = self._dirent_cache.get(key)
if dirents: if dirents:
return dirents return dirents

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC
@@ -14,7 +14,7 @@
# #
# ----------------------------------------------------------------------- # -----------------------------------------------------------------------
__version__ = '1.0.13' __version__ = '1.0.4'
# this comes from our library; measure the startup time # this comes from our library; measure the startup time
import debug import debug
@@ -25,6 +25,7 @@ debug.t_start('imports')
import sys import sys
import os import os
import sapi import sapi
import cgi
import string import string
import urllib import urllib
import mimetypes import mimetypes
@@ -111,10 +112,7 @@ class Request:
# process the Accept-Language: header # process the Accept-Language: header
hal = server.getenv('HTTP_ACCEPT_LANGUAGE','') hal = server.getenv('HTTP_ACCEPT_LANGUAGE','')
try:
self.lang_selector = accept.language(hal) self.lang_selector = accept.language(hal)
except accept.AcceptLanguageParseError:
self.lang_selector = accept.language('en')
self.language = self.lang_selector.select_from(cfg.general.languages) self.language = self.lang_selector.select_from(cfg.general.languages)
# load the key/value files, given the selected language # load the key/value files, given the selected language
@@ -149,9 +147,6 @@ class Request:
# Process the query params # Process the query params
for name, values in self.server.params().items(): 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' # patch up old queries that use 'cvsroot' to look like they used 'root'
if name == 'cvsroot': if name == 'cvsroot':
name = 'root' name = 'root'
@@ -163,12 +158,12 @@ class Request:
needs_redirect = 1 needs_redirect = 1
# validate the parameter # validate the parameter
_validate_param(name, value) _validate_param(name, values[0])
# if we're here, then the parameter is okay # if we're here, then the parameter is okay
self.query_dict[name] = value self.query_dict[name] = values[0]
# Resolve the view parameter into a handler function. # handle view parameter
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) self.view_func)
@@ -288,19 +283,13 @@ class Request:
needs_redirect = 1 needs_redirect = 1
if self.repos and self.view_func is not redirect_pathrev: if self.repos and self.view_func is not redirect_pathrev:
# If this is an intended-to-be-hidden CVSROOT path, complain.
if cfg.options.hide_cvsroot \
and is_cvsroot_path(self.roottype, path_parts):
raise debug.ViewVCException('%s: unknown location'
% self.where, '404 Not Found')
# Make sure path exists # Make sure path exists
self.pathrev = pathrev = self.query_dict.get('pathrev') self.pathrev = pathrev = self.query_dict.get('pathrev')
self.pathtype = _repos_pathtype(self.repos, path_parts, pathrev) self.pathtype = _repos_pathtype(self.repos, path_parts, pathrev)
if self.pathtype is None: if self.pathtype is None:
# Path doesn't exist, see if it could be an old-style ViewVC URL # path doesn't exist, see if it could be an old-style ViewVC URL
# with a fake suffix. # with a fake suffix
result = _strip_suffix('.diff', path_parts, pathrev, vclib.FILE, \ result = _strip_suffix('.diff', path_parts, pathrev, vclib.FILE, \
self.repos, view_diff) or \ self.repos, view_diff) or \
_strip_suffix('.tar.gz', path_parts, pathrev, vclib.DIR, \ _strip_suffix('.tar.gz', path_parts, pathrev, vclib.DIR, \
@@ -338,10 +327,10 @@ class Request:
self.where = _path_join(attic_parts) self.where = _path_join(attic_parts)
needs_redirect = 1 needs_redirect = 1
# If this is a forbidden location, stop now # If this is a forbidden directory, stop now
if cfg.is_forbidden(self.rootname, self.path_parts, self.pathtype): if self.path_parts and self.pathtype == vclib.DIR \
raise debug.ViewVCException('%s: unknown location' \ and cfg.is_forbidden(self.path_parts[0]):
% _path_join(self.path_parts), raise debug.ViewVCException('%s: unknown location' % path_parts[0],
'404 Not Found') '404 Not Found')
if self.view_func is None: if self.view_func is None:
@@ -601,48 +590,34 @@ def _validate_param(name, value):
this function throws an exception. Otherwise, it simply returns None. this function throws an exception. Otherwise, it simply returns None.
""" """
# First things first -- check that we have a legal parameter name.
try: try:
validator = _legal_params[name] validator = _legal_params[name]
except KeyError: except KeyError:
raise debug.ViewVCException( raise debug.ViewVCException(
'An illegal parameter name was provided.', 'An illegal parameter name ("%s") was passed.' % name,
'400 Bad Request') '400 Bad Request')
# Is there a validator? Is it a regex or a function? Validate if
# we can, returning without incident on valid input.
if validator is None: if validator is None:
return return
elif hasattr(validator, 'match'):
if validator.match(value): # is the validator a regex?
return if hasattr(validator, 'match'):
else: if not validator.match(value):
if validator(value): raise debug.ViewVCException(
'An illegal value ("%s") was passed as a parameter.' %
value, '400 Bad Request')
return return
# If we get here, the input value isn't valid. # the validator must be a function
raise debug.ViewVCException( validator(value)
'An illegal value was provided for the "%s" parameter.' % (name),
'400 Bad Request')
def _validate_regex(value): def _validate_regex(value):
# hmm. there isn't anything that we can do here.
### we need to watch the flow of these parameters through the system ### we need to watch the flow of these parameters through the system
### to ensure they don't hit the page unescaped. otherwise, these ### to ensure they don't hit the page unescaped. otherwise, these
### parameters could constitute a CSS attack. ### parameters could constitute a CSS attack.
try: pass
re.compile(value)
return True
except:
return None
def _validate_view(value):
# Return true iff VALUE is one of our allowed views.
return _views.has_key(value)
def _validate_mimetype(value):
# For security purposes, we only allow mimetypes from a predefined set
# thereof.
return value in (viewcvs_mime_type, alt_mime_type, 'text/plain')
# obvious things here. note that we don't need uppercase for alpha. # obvious things here. note that we don't need uppercase for alpha.
_re_validate_alpha = re.compile('^[a-z]+$') _re_validate_alpha = re.compile('^[a-z]+$')
@@ -651,13 +626,17 @@ _re_validate_number = re.compile('^[0-9]+$')
# when comparing two revs, we sometimes construct REV:SYMBOL, so ':' is needed # when comparing two revs, we sometimes construct REV:SYMBOL, so ':' is needed
_re_validate_revnum = re.compile('^[-_.a-zA-Z0-9:~\\[\\]/]*$') _re_validate_revnum = re.compile('^[-_.a-zA-Z0-9:~\\[\\]/]*$')
# it appears that RFC 2045 also says these chars are legal: !#$%&'*+^{|}~`
# but woah... I'll just leave them out for now
_re_validate_mimetype = re.compile('^[-_.a-zA-Z0-9/]+$')
# date time values # date time values
_re_validate_datetime = re.compile(r'^(\d\d\d\d-\d\d-\d\d(\s+\d\d:\d\d(:\d\d)?)?)?$') _re_validate_datetime = re.compile(r'^(\d\d\d\d-\d\d-\d\d(\s+\d\d:\d\d(:\d\d)?)?)?$')
# the legal query parameters and their validation functions # the legal query parameters and their validation functions
_legal_params = { _legal_params = {
'root' : None, 'root' : None,
'view' : _validate_view, 'view' : None,
'search' : _validate_regex, 'search' : _validate_regex,
'p1' : None, 'p1' : None,
'p2' : None, 'p2' : None,
@@ -683,7 +662,7 @@ _legal_params = {
'tr2' : _re_validate_revnum, 'tr2' : _re_validate_revnum,
'rev' : _re_validate_revnum, 'rev' : _re_validate_revnum,
'revision' : _re_validate_revnum, 'revision' : _re_validate_revnum,
'content-type' : _validate_mimetype, 'content-type' : _re_validate_mimetype,
# for query # for query
'branch' : _validate_regex, 'branch' : _validate_regex,
@@ -699,6 +678,7 @@ _legal_params = {
'mindate' : _re_validate_datetime, 'mindate' : _re_validate_datetime,
'maxdate' : _re_validate_datetime, 'maxdate' : _re_validate_datetime,
'format' : _re_validate_alpha, 'format' : _re_validate_alpha,
'limit' : _re_validate_number,
# for redirect_pathrev # for redirect_pathrev
'orig_path' : None, 'orig_path' : None,
@@ -926,9 +906,6 @@ def is_viewable_image(mime_type):
def is_text(mime_type): def is_text(mime_type):
return not mime_type or mime_type[:5] == 'text/' return not mime_type or mime_type[:5] == 'text/'
def is_cvsroot_path(roottype, path_parts):
return roottype == 'cvs' and path_parts and path_parts[0] == 'CVSROOT'
def is_plain_text(mime_type): def is_plain_text(mime_type):
return not mime_type or mime_type == 'text/plain' return not mime_type or mime_type == 'text/plain'
@@ -997,18 +974,13 @@ def get_file_view_info(request, where, rev=None, mime_type=None, pathrev=-1):
_re_rewrite_url = re.compile('((http|https|ftp|file|svn|svn\+ssh)(://[-a-zA-Z0-9%.~:_/]+)((\?|\&amp;)([-a-zA-Z0-9%.~:_]+)=([-a-zA-Z0-9%.~:_])+)*(#([-a-zA-Z0-9%.~:_]+)?)?)') _re_rewrite_url = re.compile('((http|https|ftp|file|svn|svn\+ssh)(://[-a-zA-Z0-9%.~:_/]+)((\?|\&amp;)([-a-zA-Z0-9%.~:_]+)=([-a-zA-Z0-9%.~:_])+)*(#([-a-zA-Z0-9%.~:_]+)?)?)')
_re_rewrite_email = re.compile('([-a-zA-Z0-9_.\+]+)@(([-a-zA-Z0-9]+\.)+[A-Za-z]{2,4})') _re_rewrite_email = re.compile('([-a-zA-Z0-9_.\+]+)@(([-a-zA-Z0-9]+\.)+[A-Za-z]{2,4})')
def htmlify(html): def htmlify(html):
html = sapi.escape(html) html = cgi.escape(html)
html = re.sub(_re_rewrite_url, r'<a href="\1">\1</a>', html) html = re.sub(_re_rewrite_url, r'<a href="\1">\1</a>', html)
html = re.sub(_re_rewrite_email, r'<a href="mailto:\1&#64;\2">\1&#64;\2</a>', html) html = re.sub(_re_rewrite_email, r'<a href="mailto:\1&#64;\2">\1&#64;\2</a>', html)
return html return html
def format_log(log, cfg, htmlize=1): def format_log(log, cfg):
if not log:
return log
if htmlize:
s = htmlify(log[:cfg.options.short_log_len]) s = htmlify(log[:cfg.options.short_log_len])
else:
s = sapi.escape(log[:cfg.options.short_log_len])
if len(log) > cfg.options.short_log_len: if len(log) > cfg.options.short_log_len:
s = s + '...' s = s + '...'
return s return s
@@ -1332,7 +1304,7 @@ def markup_stream_python(fp, cfg):
### It doesn't escape stuff quite right, nor does it munge URLs and ### It doesn't escape stuff quite right, nor does it munge URLs and
### mailtos as well as we do. ### mailtos as well as we do.
html = sapi.escape(fp.read()) html = cgi.escape(fp.read())
pp = py2html.PrettyPrint(PyFontify.fontify, "rawhtml", "color") pp = py2html.PrettyPrint(PyFontify.fontify, "rawhtml", "color")
pp.set_mode_rawhtml_color() pp.set_mode_rawhtml_color()
html = pp.fontify(html) html = pp.fontify(html)
@@ -1485,7 +1457,7 @@ def prepare_hidden_values(params):
hidden_values = [] hidden_values = []
for name, value in params.items(): for name, value in params.items():
hidden_values.append('<input type="hidden" name="%s" value="%s" />' % hidden_values.append('<input type="hidden" name="%s" value="%s" />' %
(sapi.escape(name), sapi.escape(value))) (name, value))
return string.join(hidden_values, '') return string.join(hidden_values, '')
def sort_file_data(file_data, roottype, sortdir, sortby, group_dirs): def sort_file_data(file_data, roottype, sortdir, sortby, group_dirs):
@@ -1565,9 +1537,16 @@ def view_directory(request):
cfg.options.hide_attic)) cfg.options.hide_attic))
options["cvs_subdirs"] = (cfg.options.show_subdir_lastmod and options["cvs_subdirs"] = (cfg.options.show_subdir_lastmod and
cfg.options.show_logs) cfg.options.show_logs)
file_data = request.repos.listdir(request.path_parts, request.pathrev, file_data = request.repos.listdir(request.path_parts, request.pathrev,
options) options)
# Filter file list if a regex is specified
search_re = request.query_dict.get('search', '')
if cfg.options.use_re_search and search_re:
file_data = search_files(request.repos, request.path_parts, request.pathrev,
file_data, search_re)
# Retrieve log messages, authors, revision numbers, timestamps # Retrieve log messages, authors, revision numbers, timestamps
request.repos.dirlogs(request.path_parts, request.pathrev, file_data, options) request.repos.dirlogs(request.path_parts, request.pathrev, file_data, options)
@@ -1577,12 +1556,6 @@ def view_directory(request):
sort_file_data(file_data, request.roottype, sortdir, sortby, sort_file_data(file_data, request.roottype, sortdir, sortby,
cfg.options.sort_group_dirs) cfg.options.sort_group_dirs)
# If a regex is specified, build a compiled form thereof for filtering
searchstr = None
search_re = request.query_dict.get('search', '')
if cfg.options.use_re_search and search_re:
searchstr = re.compile(search_re)
# loop through entries creating rows and changing these values # loop through entries creating rows and changing these values
rows = [ ] rows = [ ]
num_displayed = 0 num_displayed = 0
@@ -1614,14 +1587,13 @@ def view_directory(request):
(file.kind == vclib.DIR and 'dir') (file.kind == vclib.DIR and 'dir')
row.errors = file.errors row.errors = file.errors
if cfg.is_forbidden(request.rootname, request.path_parts + [file.name], if file.kind == vclib.DIR:
file.kind):
if (where == '') and (cfg.is_forbidden(file.name)):
continue continue
if file.kind == vclib.DIR: if (request.roottype == 'cvs' and cfg.options.hide_cvsroot
if cfg.options.hide_cvsroot \ and where == '' and file.name == 'CVSROOT'):
and is_cvsroot_path(request.roottype,
request.path_parts + [file.name]):
continue continue
row.view_href = request.get_url(view_func=view_directory, row.view_href = request.get_url(view_func=view_directory,
@@ -1649,17 +1621,10 @@ def view_directory(request):
escape=1) escape=1)
elif file.kind == vclib.FILE: elif file.kind == vclib.FILE:
if searchstr is not None:
if request.roottype == 'cvs' and (file.errors or file.dead):
continue
if not search_file(request.repos, request.path_parts + [file.name],
request.pathrev, searchstr):
continue
if request.roottype == 'cvs' and file.dead: if request.roottype == 'cvs' and file.dead:
num_dead = num_dead + 1 num_dead = num_dead + 1
if hideattic: if hideattic:
continue continue
num_displayed = num_displayed + 1 num_displayed = num_displayed + 1
file_where = where_prefix + file.name file_where = where_prefix + file.name
@@ -1732,9 +1697,6 @@ def view_directory(request):
data['sortby_%s_href' % sortby] = request.get_url(params={'sortdir': data['sortby_%s_href' % sortby] = request.get_url(params={'sortdir':
revsortdir}, revsortdir},
escape=1) escape=1)
# CVS doesn't support sorting by rev
if request.roottype == "cvs":
data['sortby_rev_href'] = None
# set cvs-specific fields # set cvs-specific fields
if request.roottype == 'cvs': if request.roottype == 'cvs':
@@ -1929,12 +1891,10 @@ def view_log(request):
# selected revision # selected revision
selected_rev = request.query_dict.get('r1') selected_rev = request.query_dict.get('r1')
paths_forbidden = {}
entries = [ ] entries = [ ]
name_printed = { } name_printed = { }
cvs = request.roottype == 'cvs' cvs = request.roottype == 'cvs'
for rev in show_revs: for rev in show_revs:
last_one = 0
entry = _item() entry = _item()
entry.rev = rev.string entry.rev = rev.string
entry.state = (cvs and rev.dead and 'dead') entry.state = (cvs and rev.dead and 'dead')
@@ -1999,27 +1959,8 @@ def view_log(request):
entry.vendor_branch = None entry.vendor_branch = None
if rev.filename != request.where: if rev.filename != request.where:
entry.orig_path = rev.filename entry.orig_path = rev.filename
# If this path has been copied, check the copy source for
# forbiddenness. If it's forbidden, we'll a) pretend this is a
# regular add (instead of a copy), and b) stop traversing history.
if rev.copy_path:
if not paths_forbidden.has_key(rev.copy_path):
paths_forbidden[rev.copy_path] = \
cfg.is_forbidden(request.rootname,
_path_parts(rev.copy_path), pathtype)
if paths_forbidden[rev.copy_path]:
entry.prev = None
last_one = 1
else:
entry.copy_path = rev.copy_path entry.copy_path = rev.copy_path
entry.copy_rev = rev.copy_rev entry.copy_rev = rev.copy_rev
entry.copy_href = request.get_url(view_func=view_log,
where=rev.copy_path,
pathtype=vclib.FILE,
params={'pathrev': rev.copy_rev},
escape=1)
if entry.orig_path: if entry.orig_path:
entry.orig_href = request.get_url(view_func=view_log, entry.orig_href = request.get_url(view_func=view_log,
@@ -2028,6 +1969,14 @@ def view_log(request):
params={'pathrev': rev.string}, params={'pathrev': rev.string},
escape=1) escape=1)
if rev.copy_path:
entry.copy_href = request.get_url(view_func=view_log,
where=rev.copy_path,
pathtype=vclib.FILE,
params={'pathrev': rev.copy_rev},
escape=1)
# view/download links # view/download links
if pathtype is vclib.FILE: if pathtype is vclib.FILE:
entry.view_href, entry.download_href, entry.download_text_href, \ entry.view_href, entry.download_href, entry.download_text_href, \
@@ -2088,8 +2037,6 @@ def view_log(request):
if entry.copy_path: if entry.copy_path:
entry.copy_path = request.server.escape(entry.copy_path) entry.copy_path = request.server.escape(entry.copy_path)
entries.append(entry) entries.append(entry)
if last_one:
break
data = common_template_data(request) data = common_template_data(request)
data.update({ data.update({
@@ -2295,23 +2242,58 @@ def view_cvsgraph(request):
request.server.header() request.server.header()
generate_page(request, "graph", data) generate_page(request, "graph", data)
def search_file(repos, path_parts, rev, search_re): def search_files(repos, path_parts, rev, files, search_re):
"""Return 1 iff the contents of the file at PATH_PARTS in REPOS as """ Search files in a directory for a regular expression.
of revision REV matches regular expression SEARCH_RE."""
# Read in each line of a checked-out file, and then use re.search to Does a check-out of each file in the directory. Only checks for
# search line. the first match.
fp = repos.openfile(path_parts, rev)[0] """
matches = 0
# Pass in search regular expression. We check out
# each file and look for the regular expression. We then return the data
# for all files that match the regex.
# Compile to make sure we do this as fast as possible.
searchstr = re.compile(search_re)
# Will become list of files that have at least one match.
# new_file_list also includes directories.
new_file_list = [ ]
# Loop on every file (and directory)
for file in files:
# Is this a directory? If so, append name to new_file_list
# and move to next file.
if file.kind != vclib.FILE:
new_file_list.append(file)
continue
# Only files at this point
# Shouldn't search binary files, or should we?
# Should allow all text mime types to pass.
if not is_text(guess_mime(file.name)):
continue
# Only text files at this point
# Assign contents of checked out file to fp.
fp = repos.openfile(path_parts + [file.name], rev)[0]
# Read in each line, use re.search to search line.
# If successful, add file to new_file_list and break.
while 1: while 1:
line = fp.readline() line = fp.readline()
if not line: if not line:
break break
if search_re.search(line): if searchstr.search(line):
matches = 1 new_file_list.append(file)
# close down the pipe (and wait for the child to terminate)
fp.close() fp.close()
break break
return matches
return new_file_list
def view_doc(request): def view_doc(request):
"""Serve ViewVC static content locally. """Serve ViewVC static content locally.
@@ -2457,7 +2439,7 @@ class DiffSource:
return _item(type='header', return _item(type='header',
line_info_left=match.group(1), line_info_left=match.group(1),
line_info_right=match.group(2), line_info_right=match.group(2),
line_info_extra=spaced_html_text(match.group(3), self.cfg)) line_info_extra=match.group(3))
if line[0] == '\\': if line[0] == '\\':
# \ No newline at end of file # \ No newline at end of file
@@ -2580,9 +2562,12 @@ def diff_parse_headers(fp, diff_type, rev1, rev2, sym1=None, sym2=None):
return date1, date2, flag, string.join(header_lines, '') return date1, date2, flag, string.join(header_lines, '')
def _get_svn_location(request, base_rev, rev): def _get_diff_path_parts(request, query_key, rev, base_rev):
repos = request.repos if request.query_dict.has_key(query_key):
parts = _path_parts(request.query_dict[query_key])
elif request.roottype == 'svn':
try: try:
repos = request.repos
parts = _path_parts(vclib.svn.get_location(repos, request.where, parts = _path_parts(vclib.svn.get_location(repos, request.where,
repos._getrev(base_rev), repos._getrev(base_rev),
repos._getrev(rev))) repos._getrev(rev)))
@@ -2592,17 +2577,6 @@ def _get_svn_location(request, base_rev, rev):
except vclib.ItemNotFound: except vclib.ItemNotFound:
raise debug.ViewVCException('Invalid path(s) or revision(s) passed ' raise debug.ViewVCException('Invalid path(s) or revision(s) passed '
'to diff', '400 Bad Request') 'to diff', '400 Bad Request')
if request.cfg.is_forbidden(request.rootname, parts, vclib.FILE):
raise debug.ViewVCException('Invalid path(s) or revision(s) passed '
'to diff', '400 Bad Request')
return parts
def _get_diff_path_parts(request, query_key, rev, base_rev):
if request.query_dict.has_key(query_key):
parts = _path_parts(request.query_dict[query_key])
elif request.roottype == 'svn':
parts = _get_svn_location(request, base_rev, rev)
else: else:
parts = request.path_parts parts = request.path_parts
return parts return parts
@@ -2922,11 +2896,6 @@ def generate_tarball(out, request, reldir, stack, dir_mtime=None):
if cvs and (file.rev is None or file.dead): if cvs and (file.rev is None or file.dead):
continue continue
# Skip forbidden files.
if request.cfg.is_forbidden(request.rootname, rep_path + [file.name],
file.kind):
continue
# If we get here, we've seen at least one valid file in the # If we get here, we've seen at least one valid file in the
# current directory. For CVS, we need to make sure there are # current directory. For CVS, we need to make sure there are
# directory parents to contain it, so we flush the stack. # directory parents to contain it, so we flush the stack.
@@ -2957,14 +2926,11 @@ def generate_tarball(out, request, reldir, stack, dir_mtime=None):
if file.errors or file.kind != vclib.DIR: if file.errors or file.kind != vclib.DIR:
continue continue
# Skip hidden directories (top-level only). # Skip forbidden/hidden directories (top-level only).
if request.cfg.options.hide_cvsroot \ if not rep_path:
and is_cvsroot_path(request.roottype, rep_path + [file.name]): if (request.cfg.is_forbidden(file.name)
continue or (cvs and request.cfg.options.hide_cvsroot
and file.name == 'CVSROOT')):
# Skip forbidden subdirs.
if request.cfg.is_forbidden(request.rootname, rep_path + [file.name],
file.kind):
continue continue
mtime = request.roottype == 'svn' and file.date or None mtime = request.roottype == 'svn' and file.date or None
@@ -2993,9 +2959,8 @@ def download_tarball(request):
def view_revision(request): def view_revision(request):
if request.roottype == "cvs": if request.roottype == "cvs":
raise debug.ViewVCException("Revision view not supported for CVS " raise ViewVCException("Revision view not supported for CVS repositories "
"repositories at this time.", "at this time.", "400 Bad Request")
"400 Bad Request")
data = common_template_data(request) data = common_template_data(request)
query_dict = request.query_dict query_dict = request.query_dict
@@ -3011,14 +2976,6 @@ def view_revision(request):
if check_freshness(request, None, str(rev), weak=1): if check_freshness(request, None, str(rev), weak=1):
return return
# Strip forbidden changed paths (we allow forbidden copyfrom-paths
# to leak through, though).
def _only_allowed(change):
return not request.cfg.is_forbidden(request.rootname,
_path_parts(change.filename),
change.pathtype)
changes = filter(_only_allowed, changes)
# Handle limit_changes parameter # Handle limit_changes parameter
cfg_limit_changes = request.cfg.options.limit_changes cfg_limit_changes = request.cfg.options.limit_changes
limit_changes = int(query_dict.get('limit_changes', cfg_limit_changes)) limit_changes = int(query_dict.get('limit_changes', cfg_limit_changes))
@@ -3044,22 +3001,8 @@ def view_revision(request):
pathtype = (change.pathtype == vclib.FILE and 'file') \ pathtype = (change.pathtype == vclib.FILE and 'file') \
or (change.pathtype == vclib.DIR and 'dir') \ or (change.pathtype == vclib.DIR and 'dir') \
or None or None
if (change.action == 'added' or change.action == 'replaced') \
# If this is an add or a replacement, we'll verify that copyfrom and not change.is_copy:
# paths are readable (if this is a copy), and if not claim this
# isn't a copy after all. And if it ain't a copy (now or "after
# all"), we'll clear the text_mods and prop_mods flags.
if (change.action == 'added' or change.action == 'replaced'):
if change.is_copy \
and request.cfg.is_forbidden(request.rootname,
_path_parts(change.base_path),
change.pathtype):
change.is_copy = 0
if change.action == 'added':
change.base_path = None
change.base_rev = None
if not change.is_copy:
change.text_mods = 0 change.text_mods = 0
change.prop_mods = 0 change.prop_mods = 0
@@ -3166,22 +3109,19 @@ def view_queryform(request):
data['query_action'], data['query_hidden_values'] = \ data['query_action'], data['query_hidden_values'] = \
request.get_form(view_func=view_query, params={'limit_changes': None}) request.get_form(view_func=view_query, params={'limit_changes': None})
def escaped_query_dict_get(itemname, itemdefault=''):
return request.server.escape(request.query_dict.get(itemname, itemdefault))
# default values ... # default values ...
data['branch'] = escaped_query_dict_get('branch', '') data['branch'] = request.query_dict.get('branch', '')
data['branch_match'] = escaped_query_dict_get('branch_match', 'exact') data['branch_match'] = request.query_dict.get('branch_match', 'exact')
data['dir'] = escaped_query_dict_get('dir', '') data['dir'] = request.query_dict.get('dir', '')
data['file'] = escaped_query_dict_get('file', '') data['file'] = request.query_dict.get('file', '')
data['file_match'] = escaped_query_dict_get('file_match', 'exact') data['file_match'] = request.query_dict.get('file_match', 'exact')
data['who'] = escaped_query_dict_get('who', '') data['who'] = request.query_dict.get('who', '')
data['who_match'] = escaped_query_dict_get('who_match', 'exact') data['who_match'] = request.query_dict.get('who_match', 'exact')
data['querysort'] = escaped_query_dict_get('querysort', 'date') data['querysort'] = request.query_dict.get('querysort', 'date')
data['date'] = escaped_query_dict_get('date', 'hours') data['date'] = request.query_dict.get('date', 'hours')
data['hours'] = escaped_query_dict_get('hours', '2') data['hours'] = request.query_dict.get('hours', '2')
data['mindate'] = escaped_query_dict_get('mindate', '') data['mindate'] = request.query_dict.get('mindate', '')
data['maxdate'] = escaped_query_dict_get('maxdate', '') data['maxdate'] = request.query_dict.get('maxdate', '')
data['limit_changes'] = int(request.query_dict.get('limit_changes', data['limit_changes'] = int(request.query_dict.get('limit_changes',
request.cfg.options.limit_changes)) request.cfg.options.limit_changes))
@@ -3279,23 +3219,27 @@ def prev_rev(rev):
r = r[:-2] r = r[:-2]
return string.join(r, '.') return string.join(r, '.')
def build_commit(request, files, max_files, dir_strip, format): def build_commit(request, files, limited_files, dir_strip):
"""Return a commit object build from the information in FILES, or commit = _item(num_files=len(files), files=[])
None if no allowed files are present in the set. DIR_STRIP is the commit.limited_files = ezt.boolean(limited_files)
path prefix to remove from the commit object's set of files. If
MAX_FILES is non-zero, it is used to limit the number of files
returned in the commit object. FORMAT is the requested output
format of the query request."""
author = files[0].GetAuthor()
date = files[0].GetTime()
desc = files[0].GetDescription() desc = files[0].GetDescription()
commit_rev = files[0].GetRevision() commit.log = htmlify(desc)
commit.short_log = format_log(desc, request.cfg)
commit.author = request.server.escape(files[0].GetAuthor())
commit.rss_date = make_rss_time_string(files[0].GetTime(), request.cfg)
if request.roottype == 'svn':
commit.rev = files[0].GetRevision()
commit.rss_url = '%s://%s%s' % \
(request.server.getenv("HTTPS") == "on" and "https" or "http",
request.server.getenv("HTTP_HOST"),
request.get_url(view_func=view_revision,
params={'revision': commit.rev},
escape=1))
else:
commit.rev = None
commit.rss_url = None
len_strip = len(dir_strip) len_strip = len(dir_strip)
commit_files = []
num_allowed = 0
plus_count = 0
minus_count = 0
for f in files: for f in files:
commit_time = f.GetTime() commit_time = f.GetTime()
@@ -3313,15 +3257,7 @@ def build_commit(request, files, max_files, dir_strip, format):
assert dirname[:len_strip] == dir_strip assert dirname[:len_strip] == dir_strip
assert len(dirname) == len_strip or dirname[len(dir_strip)] == '/' assert len(dirname) == len_strip or dirname[len(dir_strip)] == '/'
dirname = dirname[len_strip+1:] dirname = dirname[len_strip+1:]
where = dirname and ("%s/%s" % (dirname, filename)) or filename filename = dirname and ("%s/%s" % (dirname, filename)) or filename
# skip files in forbidden or hidden modules
path_parts = _path_parts(where)
if request.cfg.is_forbidden(request.rootname, path_parts, vclib.FILE):
continue
if request.cfg.options.hide_cvsroot \
and is_cvsroot_path(request.roottype, path_parts):
continue
# In CVS, we can actually look at deleted revisions; in Subversion # In CVS, we can actually look at deleted revisions; in Subversion
# we can't -- we'll look at the previous revision instead. # we can't -- we'll look at the previous revision instead.
@@ -3337,14 +3273,14 @@ def build_commit(request, files, max_files, dir_strip, format):
where=dirname, pathtype=vclib.DIR, where=dirname, pathtype=vclib.DIR,
params=params, escape=1) params=params, escape=1)
log_href = request.get_url(view_func=view_log, log_href = request.get_url(view_func=view_log,
where=where, pathtype=vclib.FILE, where=filename, pathtype=vclib.FILE,
params=params, escape=1) params=params, escape=1)
diff_href = view_href = download_href = None diff_href = view_href = download_href = None
view_href = request.get_url(view_func=view_markup, view_href = request.get_url(view_func=view_markup,
where=where, pathtype=vclib.FILE, where=filename, pathtype=vclib.FILE,
params=params, escape=1) params=params, escape=1)
download_href = request.get_url(view_func=view_checkout, download_href = request.get_url(view_func=view_checkout,
where=where, pathtype=vclib.FILE, where=filename, pathtype=vclib.FILE,
params=params, escape=1) params=params, escape=1)
if change_type == 'Change': if change_type == 'Change':
diff_href_params = params.copy() diff_href_params = params.copy()
@@ -3354,29 +3290,27 @@ def build_commit(request, files, max_files, dir_strip, format):
'diff_format': None 'diff_format': None
}) })
diff_href = request.get_url(view_func=view_diff, diff_href = request.get_url(view_func=view_diff,
where=where, pathtype=vclib.FILE, where=filename, pathtype=vclib.FILE,
params=diff_href_params, escape=1) params=diff_href_params, escape=1)
prefer_markup = ezt.boolean(default_view(guess_mime(filename), prefer_markup = ezt.boolean(default_view(guess_mime(filename),
request.cfg) == view_markup) request.cfg) == view_markup)
# Update plus/minus line change count. # skip files in forbidden or hidden modules
plus = int(f.GetPlusCount()) dir_parts = filter(None, string.split(dirname, '/'))
minus = int(f.GetMinusCount()) if dir_parts \
plus_count = plus_count + plus and ((dir_parts[0] == 'CVSROOT'
minus_count = minus_count + minus and request.cfg.options.hide_cvsroot) \
or request.cfg.is_forbidden(dir_parts[0])):
num_allowed = num_allowed + 1
if max_files and num_allowed > max_files:
continue continue
commit_files.append(_item(date=commit_time, commit.files.append(_item(date=commit_time,
dir=request.server.escape(dirname), dir=request.server.escape(dirname),
file=request.server.escape(filename), file=request.server.escape(f.GetFile()),
author=request.server.escape(f.GetAuthor()), author=request.server.escape(f.GetAuthor()),
rev=rev, rev=rev,
branch=f.GetBranch(), branch=f.GetBranch(),
plus=plus, plus=int(f.GetPlusCount()),
minus=minus, minus=int(f.GetMinusCount()),
type=change_type, type=change_type,
dir_href=dir_href, dir_href=dir_href,
log_href=log_href, log_href=log_href,
@@ -3384,56 +3318,30 @@ def build_commit(request, files, max_files, dir_strip, format):
download_href=download_href, download_href=download_href,
prefer_markup=prefer_markup, prefer_markup=prefer_markup,
diff_href=diff_href)) diff_href=diff_href))
# No files survived forbiddenness checks? Let's just pretend this
# little commit didn't happen, shall we?
if not len(commit_files):
return None
commit = _item(num_files=len(commit_files), files=commit_files,
plus=plus_count, minus=minus_count)
commit.limited_files = ezt.boolean(num_allowed > len(commit_files))
commit.log = htmlify(desc)
commit.short_log = format_log(desc, request.cfg, format != 'rss')
commit.author = request.server.escape(author)
commit.rss_date = make_rss_time_string(date, request.cfg)
if request.roottype == 'svn':
commit.rev = commit_rev
commit.rss_url = '%s://%s%s' % \
(request.server.getenv("HTTPS") == "on" and "https" or "http",
request.server.getenv("HTTP_HOST"),
request.get_url(view_func=view_revision,
params={'revision': commit.rev},
escape=1))
else:
commit.rev = None
commit.rss_url = None
return commit return commit
def query_backout(request, commits): def query_backout(request, commits):
server_fp = get_writeready_server_file(request, 'text/plain') request.server.header('text/plain')
if not commits: if commits:
server_fp.write("""\ print '# This page can be saved as a shell script and executed.'
# No changes were selected by the query. print '# It should be run at the top of your work area. It will update'
# There is nothing to back out. print '# your working copy to back out the changes selected by the'
""") print '# query.'
print
else:
print '# No changes were selected by the query.'
print '# There is nothing to back out.'
return return
server_fp.write("""\
# This page can be saved as a shell script and executed.
# It should be run at the top of your work area. It will update
# your working copy to back out the changes selected by the
# query.
""")
for commit in commits: for commit in commits:
for fileinfo in commit.files: for fileinfo in commit.files:
if request.roottype == 'cvs': if request.roottype == 'cvs':
server_fp.write('cvs update -j %s -j %s %s/%s\n' print 'cvs update -j %s -j %s %s/%s' \
% (fileinfo.rev, prev_rev(fileinfo.rev), % (fileinfo.rev, prev_rev(fileinfo.rev),
fileinfo.dir, fileinfo.file)) fileinfo.dir, fileinfo.file)
elif request.roottype == 'svn': elif request.roottype == 'svn':
server_fp.write('svn merge -r %s:%s %s/%s\n' print 'svn merge -r %s:%s %s/%s' \
% (fileinfo.rev, prev_rev(fileinfo.rev), % (fileinfo.rev, prev_rev(fileinfo.rev),
fileinfo.dir, fileinfo.file)) fileinfo.dir, fileinfo.file)
def view_query(request): def view_query(request):
if not is_query_supported(request): if not is_query_supported(request):
@@ -3455,6 +3363,7 @@ def view_query(request):
mindate = request.query_dict.get('mindate', '') mindate = request.query_dict.get('mindate', '')
maxdate = request.query_dict.get('maxdate', '') maxdate = request.query_dict.get('maxdate', '')
format = request.query_dict.get('format') format = request.query_dict.get('format')
limit = int(request.query_dict.get('limit', 0))
limit_changes = int(request.query_dict.get('limit_changes', limit_changes = int(request.query_dict.get('limit_changes',
request.cfg.options.limit_changes)) request.cfg.options.limit_changes))
@@ -3521,17 +3430,16 @@ def view_query(request):
query.SetFromDateObject(mindate) query.SetFromDateObject(mindate)
if maxdate is not None: if maxdate is not None:
query.SetToDateObject(maxdate) query.SetToDateObject(maxdate)
if limit:
# Set the admin-defined (via configuration) row limits. This is to avoid query.SetLimit(limit)
# slamming the database server with a monster query. elif format == 'rss':
if format == 'rss':
query.SetLimit(request.cfg.cvsdb.rss_row_limit) query.SetLimit(request.cfg.cvsdb.rss_row_limit)
else:
query.SetLimit(request.cfg.cvsdb.row_limit)
# run the query # run the query
db.RunQuery(query) db.RunQuery(query)
sql = request.server.escape(db.CreateSQLQueryString(query))
# gather commits # gather commits
commits = [] commits = []
plus_count = 0 plus_count = 0
@@ -3543,48 +3451,45 @@ def view_query(request):
current_desc = query.commit_list[0].GetDescriptionID() current_desc = query.commit_list[0].GetDescriptionID()
current_rev = query.commit_list[0].GetRevision() current_rev = query.commit_list[0].GetRevision()
dir_strip = _path_join(repos_dir) dir_strip = _path_join(repos_dir)
for commit in query.commit_list: for commit in query.commit_list:
commit_desc = commit.GetDescriptionID() # base modification time on the newest commit ...
commit_rev = commit.GetRevision() if commit.GetTime() > mod_time: mod_time = commit.GetTime()
# form plus/minus totals
# base modification time on the newest commit plus_count = plus_count + int(commit.GetPlusCount())
if commit.GetTime() > mod_time: minus_count = minus_count + int(commit.GetMinusCount())
mod_time = commit.GetTime() # group commits with the same commit message ...
desc = commit.GetDescriptionID()
# For CVS, group commits with the same commit message. # For CVS, group commits with the same commit message.
# For Subversion, group them only if they have the same revision number # For Subversion, group them only if they have the same revision number
if request.roottype == 'cvs': if request.roottype == 'cvs':
if current_desc == commit_desc: if current_desc == desc:
if not limit_changes or len(files) < limit_changes:
files.append(commit) files.append(commit)
else:
limited_files = 1
continue continue
else: else:
if current_rev == commit_rev: if current_rev == commit.GetRevision():
if not limit_changes or len(files) < limit_changes:
files.append(commit) files.append(commit)
else:
limited_files = 1
continue continue
# append this grouping # if our current group has any allowed files, append a commit
commit_item = build_commit(request, files, limit_changes, # with those files.
dir_strip, format) if len(files):
if commit_item: commits.append(build_commit(request, files, limited_files, dir_strip))
# update running plus/minus totals
plus_count = plus_count + commit_item.plus
minus_count = minus_count + commit_item.minus
commits.append(commit_item)
files = [ commit ] files = [ commit ]
limited_files = 0 limited_files = 0
current_desc = commit_desc current_desc = desc
current_rev = commit_rev current_rev = commit.GetRevision()
# we need to tack on our last commit grouping, if any # we need to tack on our last commit grouping, but, again, only if
commit_item = build_commit(request, files, limit_changes, # it has allowed files.
dir_strip, format) if len(files):
if commit_item: commits.append(build_commit(request, files, limited_files, dir_strip))
# update running plus/minus totals
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 # only show the branch column if we are querying all branches
# or doing a non-exact branch match on a CVS repository. # or doing a non-exact branch match on a CVS repository.
@@ -3615,7 +3520,7 @@ def view_query(request):
data = common_template_data(request) data = common_template_data(request)
data.update({ data.update({
'sql': request.server.escape(db.CreateSQLQueryString(query)), 'sql': sql,
'english_query': english_query(request), 'english_query': english_query(request),
'queryform_href': queryform_href, 'queryform_href': queryform_href,
'backout_href': backout_href, 'backout_href': backout_href,
@@ -3629,7 +3534,7 @@ def view_query(request):
}) })
if format == 'rss': if format == 'rss':
request.server.header("application/rss+xml") request.server.header("text/xml")
generate_page(request, "rss", data) generate_page(request, "rss", data)
else: else:
request.server.header() request.server.header()
@@ -3659,10 +3564,10 @@ for code, view in _views.items():
def list_roots(cfg): def list_roots(cfg):
allroots = { } allroots = { }
for root in cfg.general.svn_roots.keys():
allroots[root] = [cfg.general.svn_roots[root], 'svn']
for root in cfg.general.cvs_roots.keys(): for root in cfg.general.cvs_roots.keys():
allroots[root] = [cfg.general.cvs_roots[root], 'cvs'] allroots[root] = [cfg.general.cvs_roots[root], 'cvs']
for root in cfg.general.svn_roots.keys():
allroots[root] = [cfg.general.svn_roots[root], 'svn']
return allroots return allroots
def load_config(pathname=None, server=None): def load_config(pathname=None, server=None):
@@ -3731,9 +3636,9 @@ def view_error(server, cfg):
exc_dict = debug.GetExceptionData() exc_dict = debug.GetExceptionData()
status = exc_dict['status'] status = exc_dict['status']
if exc_dict['msg']: if exc_dict['msg']:
exc_dict['msg'] = server.escape(exc_dict['msg']) exc_dict['msg'] = htmlify(exc_dict['msg'])
if exc_dict['stacktrace']: if exc_dict['stacktrace']:
exc_dict['stacktrace'] = server.escape(exc_dict['stacktrace']) exc_dict['stacktrace'] = htmlify(exc_dict['stacktrace'])
handled = 0 handled = 0
# use the configured error template if possible # use the configured error template if possible
@@ -3765,7 +3670,7 @@ def main(server, cfg):
finally: finally:
debug.t_end('main') debug.t_end('main')
debug.t_dump(server.file()) debug.dump()
debug.DumpChildren(server) debug.DumpChildren(server)

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,53 +0,0 @@
PREFACE
-------
This file will go away soon after release 0.8. Please use the SourceForge
tracker to resubmit any of the items listed below, if you think, it is
still an issue:
http://sourceforge.net/tracker/?group_id=18760
Before reporting please check, whether someone else has already done this.
Working patches increase the chance to be included into the next release.
-- PeFu / October 2001
TODO ITEMS
----------
*) add Tamminen Eero's comments on how to make Linux directly execute
the Python script. From email on Feb 19.
[ add other examples, such as my /bin/sh hack or the teeny CGI stub
importing the bulk hack ]
*) insert rcs_path into PATH before calling "rcsdiff". rcsdiff might
use "co" and needs to find it on the path.
*) show the "locked" flag (attach it to the LogEntry objects).
Idea from Russell Gordon <russell@hoopscotch.dhs.org>
*) committing with a specific revision number:
http://mailman.lyra.org/pipermail/viewcvs/2000q1/000008.html
*) add capability similar to cvs2cl.pl:
http://mailman.lyra.org/pipermail/viewcvs/2000q2/000050.html
suggestion from Chris Meyer <cmeyer@gatan.com>.
*) add a tree view of the directory structure (and files?)
*) include a ConfigParser.py to help older Python installations
*) add a check for the rcs programs/paths to viewvc-install. clarify the
dependency on RCS in the docs.
*) have a "check" mode that verifies binaries are available on rcs_path
-> alternately (probably?): use rcsparse rather than external tools
KNOWN BUGS
----------
*) time.timezone seems to not be available on some 1.5.2 installs.
I was unable to verify this. On RedHat and SuSE Linux this bug
is non existant.
*) With old repositories containing many branches, tags or thousands
or revisions, the cvsgraph feature becomes unusable (see INSTALL).
ViewVC can't do much about this, but it might be possible to
investigate the number of branches, tags and revision in advance
and disable the cvsgraph links, if the numbers exceed a certain
treshold.

View File

@@ -1,82 +0,0 @@
RELEASE MANAGEMENT
ViewVC rolls releases from release branches associate with each minor
version of the software. For example, the 1.1.0 is rolled from the
1.1.x branch. The same is true for the 1.1.1, 1.1.2, ... releases.
There is a script, `tools/make-release', which creates a release
directory and the various archive files that we distribute. All other
steps required to get a ViewVC release out of the door require manual
execution (currently by C. Michael Pilato). Those steps are as
follows:
Checkout a working copy of the release branch for the release you
intend to roll, and in that working copy, perform the following steps
(X, Y, and Z below represent integral major, minor, and patch version
numbers, and not literal):
1. Review any open bug reports:
http://viewvc.tigris.org/servlets/ProjectIssues
2. Add a new subsection to the file 'docs/upgrading.html' describing
all user visible changes for users of previous releases of ViewVC.
Commit any modifications. NOTE: This step should not be necessary
for patch releases.
3. Verify that copyright years are correct in both the LICENSE.html
file and the source code.
4. Update and commit the 'CHANGES' file.
5. Test, test, test! There is no automatic testsuite available. So
just run with permuting different `viewvc.conf' settings... and
pray. Fix what needs fixin', keeping the CHANGES file in sync
with the branch.
6. At this point, the source code committed to the release branch
should exactly reflect what you wish to distribute and dub "the
release".
7. Edit the file 'lib/viewvc.py' and remove the "-dev" suffix from
__version__. The remainder should be of the form "X.Y.Z", where X,
Y, and Z are positive integers. Do NOT commit this change.
8. Update your working copy to HEAD, and tag the release:
svn up && svn cp -m "Tag the X.Y.Z final release." . ^/tags/X.Y.Z
9. Go into an empty directory and run the 'make-release' script:
tools/make-release viewvc-X.Y.Z tags/X.Y.Z
10. Verify the archive files:
- do they have a LICENSE.html file?
- do they have necessary include documentation?
- do they *not* have unnecessary stuff?
- do they install and work correctly?
11. Upload the created archive files (tar.gz and zip) into the Files
and Documents section of the Tigris.org project, and modify the
CHECKSUMS document there accordingly. Also, drop a copy of the
archive files into the root directory of the viewvc.org website
(unversioned).
12. Update the websites (both the viewvc.org/ and www/ ones) to refer
to the new release files.
13. Edit the file 'lib/viewvc.py' again, re-adding the "-dev" suffix
and incrementing the patch number assigned to the __version__
variable, and commit:
svn ci -m "Begin a new release cycle."
14. Edit the Issue Tracker configuration options, adding a new Version
for the just-released one, and a new Milestone for the next patch
(and possibly, minor or major) release. (For the Milestone sort
key, use a packed integer XXYYZZ: 1.0.3 == 10003, 2.11.4 == 21104.)
15. Send to the announce@ list a message explaining all the cool new
features, and post similar announcements to other places interested
in this sort of stuff, such as Freshmeat (http://www.freshmeat.net).

View File

@@ -4,30 +4,22 @@
<thead> <thead>
<tr> <tr>
<th class="vc_header[is sortby "file"]_sort[end]" colspan="2"> <th class="vc_header[is sortby "file"]_sort[end]" colspan="2">
[if-any sortby_file_href]<a href="[sortby_file_href]#dirlist">File</a>[else]File[end] <a href="[sortby_file_href]#dirlist">File
[is sortby "file"] [is sortby "file"]
<img class="vc_sortarrow" alt="[is sortdir "down"](rev)[end]" <img class="vc_sortarrow" alt="[is sortdir "down"](rev)[end]"
width="13" height="13" width="13" height="13"
src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" /> src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" />
[end] [end]
</a>
</th> </th>
[if-any sortby_rev_href]
<th class="vc_header[is sortby "rev"]_sort[end]"> <th class="vc_header[is sortby "rev"]_sort[end]">
<a href="[sortby_rev_href]#dirlist">Last Change</a> <a href="[sortby_rev_href]#dirlist">Last Change
[is sortby "rev"] [is sortby "rev"]
<img class="vc_sortarrow" alt="[is sortdir "down"](rev)[end]" <img class="vc_sortarrow" alt="[is sortdir "down"](rev)[end]"
width="13" height="13" width="13" height="13"
src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" /> src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" />
[end] [end]
[else] </a>
<th class="vc_header[is sortby "date"]_sort[end]">
[if-any sortby_date_href]<a href="[sortby_date_href]#dirlist">Last Change</a>[else]Last Change[end]
[is sortby "date"]
<img class="vc_sortarrow" alt="[is sortdir "down"](date)[end]"
width="13" height="13"
src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" />
[end]
[end]
</th> </th>
</tr> </tr>
</thead> </thead>

View File

@@ -4,45 +4,50 @@
<thead> <thead>
<tr> <tr>
<th class="vc_header[is sortby "file"]_sort[end]" colspan="2"> <th class="vc_header[is sortby "file"]_sort[end]" colspan="2">
[if-any sortby_file_href]<a href="[sortby_file_href]#dirlist">File</a>[else]File[end] <a href="[sortby_file_href]#dirlist">File
[is sortby "file"] [is sortby "file"]
<img class="vc_sortarrow" alt="[is sortdir "down"](rev)[end]" <img class="vc_sortarrow" alt="[is sortdir "down"](rev)[end]"
width="13" height="13" width="13" height="13"
src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" /> src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" />
[end] [end]
</a>
</th> </th>
<th class="vc_header[is sortby "rev"]_sort[end]"> <th class="vc_header[is sortby "rev"]_sort[end]">
[if-any sortby_rev_href]<a href="[sortby_rev_href]#dirlist">Rev.</a>[else]Rev.[end] <a href="[sortby_rev_href]#dirlist">Rev.
[is sortby "rev"] [is sortby "rev"]
<img class="vc_sortarrow" alt="[is sortdir "down"](rev)[end]" <img class="vc_sortarrow" alt="[is sortdir "down"](rev)[end]"
width="13" height="13" width="13" height="13"
src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" /> src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" />
[end] [end]
</a>
</th> </th>
<th class="vc_header[is sortby "date"]_sort[end]"> <th class="vc_header[is sortby "date"]_sort[end]">
[if-any sortby_date_href]<a href="[sortby_date_href]#dirlist">Age</a>[else]Age[end] <a href="[sortby_date_href]#dirlist">Age
[is sortby "date"] [is sortby "date"]
<img class="vc_sortarrow" alt="[is sortdir "down"](rev)[end]" <img class="vc_sortarrow" alt="[is sortdir "down"](rev)[end]"
width="13" height="13" width="13" height="13"
src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" /> src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" />
[end] [end]
</a>
</th> </th>
<th class="vc_header[is sortby "author"]_sort[end]"> <th class="vc_header[is sortby "author"]_sort[end]">
[if-any sortby_author_href]<a href="[sortby_author_href]#dirlist">Author</a>[else]Author[end] <a href="[sortby_author_href]#dirlist">Author
[is sortby "author"] [is sortby "author"]
<img class="vc_sortarrow" alt="[is sortdir "down"](rev)[end]" <img class="vc_sortarrow" alt="[is sortdir "down"](rev)[end]"
width="13" height="13" width="13" height="13"
src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" /> src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" />
[end] [end]
</a>
</th> </th>
[is cfg.options.show_logs "1"] [is cfg.options.show_logs "1"]
<th class="vc_header[is sortby "log"]_sort[end]"> <th class="vc_header[is sortby "log"]_sort[end]">
[if-any sortby_log_href]<a href="[sortby_log_href]#dirlist">Last log entry</a>[else]Last log entry[end] <a href="[sortby_log_href]#dirlist">Last log entry
[is sortby "log"] [is sortby "log"]
<img class="vc_sortarrow" alt="[is sortdir "down"](rev)[end]" <img class="vc_sortarrow" alt="[is sortdir "down"](rev)[end]"
width="13" height="13" width="13" height="13"
src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" /> src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" />
[end] [end]
</a>
</th> </th>
[end] [end]
</tr> </tr>

View File

@@ -9,43 +9,16 @@
<h3>An Exception Has Occurred</h3> <h3>An Exception Has Occurred</h3>
[if-any msg] [if-any msg]
<p>[msg]</p> <p><pre>[msg]</pre></p>
[end] [end]
[if-any status] [if-any status]
<h4>HTTP Response Status</h4> <h4>HTTP Response Status</h4>
<p><pre>[status]</pre></p> <p><pre>[status]</pre></p>
<hr /> <hr />
[end] [end]
[if-any msg][else]
<h4>Python Traceback</h4> <h4>Python Traceback</h4>
<p><pre> <p><pre>
[stacktrace] [stacktrace]
</pre></p> </pre></p>
[end]
[# Here follows a bunch of space characters, present to ensure that
our error message is larger than 512 bytes so that IE's "Friendly
Error Message" won't show. For more information, see
http://oreillynet.com/onjava/blog/2002/09/internet_explorer_subverts_err.html]
</body> </body>
</html> </html>

View File

@@ -1,24 +1,34 @@
[if-any search_re_form] [if-any search_re_form]
<hr /> <hr />
[# this table holds the selectors on the left, and reset on the right ]
<table class="auto">
<tr>
<td>Show files containing the regular expression:</td>
<td>
<form method="get" action="[search_re_action]">
<div> <div>
Show files containing the regular expression:
<form method="get" action="[search_re_action]" style="display: inline;">
<div style="display: inline;">
[search_re_hidden_values] [search_re_hidden_values]
<input type="text" name="search" value="[search_re]" /> <input type="text" name="search" value="[search_re]" />
<input type="submit" value="Show" /> <input type="submit" value="Show" />
</div> </div>
</form> </form>
</td>
</tr>
[if-any search_re] [if-any search_re]
<form method="get" action="[search_re_action]" style="display: inline;"> <tr>
<div style="display: inline;"> <td>&nbsp;</td>
[search_re_hidden_values] <td>
<form method="get" action="[search_tag_action]">
<div>
[search_tag_hidden_values]
<input type="submit" value="Show all files" /> <input type="submit" value="Show all files" />
</div> </div>
</form> </form>
</td>
</tr>
[end] [end]
</table>
[end] [end]
</div>
[# if you want to disable tarball generation remove the following: ] [# if you want to disable tarball generation remove the following: ]
[if-any tarball_href] [if-any tarball_href]

View File

@@ -9,8 +9,8 @@
<title>[if-any commits.rev][commits.rev]: [end][[commits.author]] [commits.short_log]</title> <title>[if-any commits.rev][commits.rev]: [end][[commits.author]] [commits.short_log]</title>
[if-any commits.rss_url]<link>[commits.rss_url]</link>[end] [if-any commits.rss_url]<link>[commits.rss_url]</link>[end]
<author>[commits.author]</author> <author>[commits.author]</author>
<pubDate>[if-any commits.rss_date][commits.rss_date][else](unknown date)[end]</pubDate> <pubDate>[commits.rss_date]</pubDate>
<description>&lt;pre&gt;[format "xml"][commits.log][end]&lt;/pre&gt;</description> <description>[commits.log]</description>
</item>[end] </item>[end]
</channel> </channel>
</rss> </rss>

View File

@@ -1,6 +1,6 @@
#!/bin/sh #!/bin/sh
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC
@@ -17,18 +17,17 @@
### Validate input ### Validate input
if test $# != 2 && test $# != 1; then if test $# != 2 && test $# != 1; then
echo "Usage: $0 TARGET-DIRECTORY [BRANCH]" echo "Usage: $0 TARGET-DIRECTORY [TAGNAME]"
echo "" echo ""
echo "If BRANCH (i.e. \"tags/1.1.0\" or \"branches/1.0.x\") is not provided," echo "If TAGNAME is not provided, the release will be rolled from trunk."
echo "the release will be rolled from trunk."
exit 1 exit 1
fi fi
TARGET=${1} TARGET=${1}
if test $# = 1; then if test $# == 1; then
ROOT=trunk ROOT=trunk
else else
ROOT=${2} ROOT=tags/${2}
fi fi
if test -e ${TARGET}; then if test -e ${TARGET}; then
@@ -37,8 +36,7 @@ if test -e ${TARGET}; then
fi fi
### Grab an export from the Subversion repository. ### Grab an export from the Subversion repository.
EXPORT_URL="http://viewvc.tigris.org/svn/viewvc/${ROOT}" echo "Exporting into:" ${TARGET}
echo "Exporting '${EXPORT_URL}' into '${TARGET}'"
for PLATFORM in unix windows; do for PLATFORM in unix windows; do
if test ${PLATFORM} = windows; then if test ${PLATFORM} = windows; then
@@ -47,44 +45,30 @@ for PLATFORM in unix windows; do
EOL="--native-eol LF" EOL="--native-eol LF"
fi fi
echo "Beginning build for ${PLATFORM}:" svn export ${EOL} http://viewvc.tigris.org/svn/viewvc/${ROOT} ${TARGET}
echo " Exporting source code..."
svn export --quiet ${EOL} ${EXPORT_URL} ${TARGET}
### Various shifting, cleanup. ### Various shifting, cleanup.
# Documentation is now also distributed together with the release, but
# we still copy the license file to its traditional place (it is small
# and many files still contain comments refering to this location):
# Remove some not useful directories # Remove some not useful directories
for JUNK in elemx \ rm -r ${TARGET}/{elemx,tests,tools,tparse,viewcvs.sourceforge.net,www}
notes \
tests \
tools \
tparse \
viewcvs.sourceforge.net \
viewvc.org \
www; do
if [ -d ${TARGET}/${JUNK} ]; then
echo " Removing ${TARGET}/${JUNK}..."
rm -r ${TARGET}/${JUNK}
fi
done
# Make sure permissions are reasonable: # Make sure permissions are reasonable:
echo " Normalizing permissions..."
find ${TARGET} -print | xargs chmod uoa+r find ${TARGET} -print | xargs chmod uoa+r
find ${TARGET} -type d -print | xargs chmod uoa+x find ${TARGET} -type d -print | xargs chmod uoa+x
if test ${PLATFORM} = windows; then if test ${PLATFORM} = windows; then
# Create also a ZIP file for those poor souls :-) still using Windows: # Create also a ZIP file for those poor souls :-) still using Windows:
echo " Creating ZIP archive..."
zip -qor9 ${TARGET}.zip ${TARGET} zip -qor9 ${TARGET}.zip ${TARGET}
else else
# Cut the tarball: # Cut the tarball:
echo " Creating tarball archive..."
tar cf - ${TARGET} | gzip -9 > ${TARGET}.tar.gz tar cf - ${TARGET} | gzip -9 > ${TARGET}.tar.gz
fi fi
# remove target directory # remove target directory
rm -r ${TARGET} rm -r ${TARGET}
done done
echo "Done." echo 'Done.'

View File

@@ -1,5 +1,5 @@
/* /*
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,5 +1,5 @@
/* /*
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,5 +1,5 @@
/* /*
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,5 +1,5 @@
/* /*
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -0,0 +1,2 @@
*.tar.gz
*.zip

View File

@@ -0,0 +1 @@
RedirectMatch ^(.*)$ http://www.viewvc.org/$1

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- Mode: python -*- # -*- Mode: python -*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC
@@ -77,7 +77,6 @@ if sys.platform == "win32":
TREE_LIST = [ TREE_LIST = [
("lib", "lib", 0), ("lib", "lib", 0),
("templates", "templates", 1), ("templates", "templates", 1),
("templates-contrib", "templates-contrib", 1),
] ]

View File

@@ -142,9 +142,8 @@ use_rcsparse = 0
address = <a href="mailto:cvs-admin@insert.your.domain.here">No admin address has been configured</a> address = <a href="mailto:cvs-admin@insert.your.domain.here">No admin address has been configured</a>
# #
# This should contain a list of modules (that is, top-level directories within # This should contain a list of modules in the repository that should not be
# repositories) that should not be displayed (by default or by explicit path # displayed (by default or by explicit path specification).
# specification).
# #
# This configuration can be a simple list of modules, or it can get quite # This configuration can be a simple list of modules, or it can get quite
# complex: # complex:
@@ -163,9 +162,8 @@ address = <a href="mailto:cvs-admin@insert.your.domain.here">No admin address ha
# #
# Tests are case-sensitive. # Tests are case-sensitive.
# #
# NOTE: This is for the hiding of modules within repositories, *not* forbidden =
# for the hiding of repositories (roots) themselves.
#
# Some examples: # Some examples:
# #
# Disallow "example" but allow all others: # Disallow "example" but allow all others:
@@ -186,40 +184,6 @@ address = <a href="mailto:cvs-admin@insert.your.domain.here">No admin address ha
# Allow "xml", forbid other modules starting with "x", and allow the rest: # Allow "xml", forbid other modules starting with "x", and allow the rest:
# forbidden = !xml, x*, !* # forbidden = !xml, x*, !*
# #
forbidden =
#
# This is similar to 'forbidden', but differs in some key ways:
#
# *) Rather than shell-style "glob" expressions, the values in this
# list are regular expressions. You can still prepend a ! character
# to each regular expression to invert its meaning, though.
#
# *) It compares not against modules only, but against paths consisting
# of the repository (or root) name plus the path of the versioned file
# or directory to be tested. For example, to see if the user is
# authorized to see the path "/trunk/www/index.html" in the repository
# whose root name is "svnrepos", this authorizer will test the path
# "svnrepos/trunk/www/index.html" against the list of forbidden regular
# expressions. Directory paths will be terminated by a forward slash.
#
# NOTE: Use of this configuration option will *disable* any configuration of
# the 'forbidden' option -- they cannot be used simultaneously.
#
# Some examples:
#
# Disallow files named "PRIVATE", but allow all others:
# forbiddenre = /PRIVATE$
#
# Allow only the "example1" and "example2" roots and the paths inside them,
# disallowing all others (which can be done in multiple ways):
# forbiddenre = !^example1(/|$), !^example2(/|$)/
# forbiddenre = !^example[12](/|$)
#
# Only allow visibility of HTML files and the directories that hold them:
# forbiddenre = !^.*(/|\.html)$
#
forbiddenre =
# #
# This option provides a mechanism for custom key/value pairs to be # This option provides a mechanism for custom key/value pairs to be

2
viewvc.org/.cvsignore Normal file
View File

@@ -0,0 +1,2 @@
*.tar.gz
*.zip

68
viewvc.org/contact.html Normal file
View File

@@ -0,0 +1,68 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<title>ViewVC: Contact</title>
<link rel="stylesheet" type="text/css" href="./styles.css"/>
</head>
<body>
<div id="title">
<a href="http://www.viewvc.org/"><img
src="./images/title.jpg" alt="ViewVC: Repository Browsing"/></a>
</div>
<div id="menu">
<p><a href="./index.html">Home</a> |
<a href="http://viewvc.tigris.org/">Project Page</a> |
<a href="./download.html">Download</a> |
<a href="./upgrading.html">Upgrading</a> |
<a href="./contributing.html">Contributing</a> |
<a href="./license-1.html">License</a> |
<a href="./contact.html">Contact</a> |
<a href="./who.html">About</a>
</p>
</div>
<table id="pagetable">
<tr>
<td id="pagecolumn1">
<h4>On this page:</h4>
<ul id="bookmarks">
<li><a href="#sec-contacting-us">Contacting Us</a></li>
</ul>
<hr/>
<address><a href="mailto:&#117&#115&#101&#114&#115&#64&#118&#105&#101&#119&#118&#99&#46&#116&#105&#103&#114&#105&#115&#46&#111&#114&#103">ViewVC Users Group</a></address>
</td>
<td id="pagecolumn2">
<div class="section">
<h2 id="sec-contacting-us">Contacting Us</h2>
<p>Please send any comments, questions, or suggestions to the <a
href="mailto:&#117&#115&#101&#114&#115&#64&#118&#105&#101&#119&#118&#99&#46&#116&#105&#103&#114&#105&#115&#46&#111&#114&#103">ViewVC users mailing
list</a>. There is also a <a
href="mailto:&#100&#101&#118&#64&#118&#105&#101&#119&#118&#99&#46&#116&#105&#103&#114&#105&#115&#46&#111&#114&#103">mailing list specifically for
ViewVC developers</a>. You can subscribe to these lists, as well
view the list archives, (and other
project lists) <a
href="http://viewvc.tigris.org/servlets/ProjectMailingListList"
>here</a>.</p>
<p>ViewVC is an <a href="http://www.opensource.org/">Open
Source</a> project, and all <a href="./contributing.html">contributions</a>
are welcome.</p>
</div>
</td>
</tr>
</table>
</body>
</html>

View File

@@ -0,0 +1,276 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<title>ViewVC: Contributing</title>
<link rel="stylesheet" type="text/css" href="./styles.css"/>
</head>
<body>
<div id="title">
<a href="http://www.viewvc.org/"><img
src="./images/title.jpg" alt="ViewVC: Repository Browsing"/></a>
</div>
<div id="menu">
<p><a href="./index.html">Home</a> |
<a href="http://viewvc.tigris.org/">Project Page</a> |
<a href="./download.html">Download</a> |
<a href="./upgrading.html">Upgrading</a> |
<a href="./contributing.html">Contributing</a> |
<a href="./license-1.html">License</a> |
<a href="./contact.html">Contact</a> |
<a href="./who.html">About</a>
</p>
</div>
<table id="pagetable">
<tr>
<td id="pagecolumn1">
<h4>On this page:</h4>
<ul id="bookmarks">
<li><a href="#sec-getting-started">Getting Started</a></li>
<li><a href="#sec-testing">Testing and Reporting</a></li>
<li><a href="#sec-coding-style">Coding Style</a></li>
<li><a href="#sec-patches">Submitting Patches</a></li>
<li><a href="#sec-security">Security</a></li>
<li><a href="#sec-adding-features">Adding Features</a></li>
<li><a href="#sec-templates">Hacking on Templates</a></li>
<li><a href="#sec-releasing">Release Management</a></li>
</ul>
<hr/>
<address><a href="mailto:&#117&#115&#101&#114&#115&#64&#118&#105&#101&#119&#118&#99&#46&#116&#105&#103&#114&#105&#115&#46&#111&#114&#103">ViewVC Users Group</a></address>
</td>
<td id="pagecolumn2">
<div class="section">
<h2 id="sec-getting-started">Getting Started</h2>
<p>Some basic knowledge about <a
href="http://www.python.org">Python</a> and development tools like
<code>diff</code> is required. Your best bet is to start with a
fresh source code snapshot, which you may obtain from our
Subversion repository (see instructions <a
href="./download.html#sec-subversion">here</a>).</p>
<p>Version control history can be obtained using Subversion clients,
but is also browsable using <a
href="http://viewvc.tigris.org/source/browse/viewvc/trunk/"
>tigris.org's integrated ViewVC tool</a>.</p>
</div>
<div class="section">
<h2 id="sec-testing">Testing and Reporting</h2>
<p>Testing usability and the installation process on different
platforms is also a valuable contribution. Please report your
results back to us developers. Bandwidth is getting cheaper daily,
so don't be afraid &mdash; in fact, feel encouraged &mdash; to dump
as much detail about the problems you are seeing as possible into
your bug reports. Here are some things you definitely should
try to include:</p>
<ul>
<li>What version of ViewVC you are using (if you are using a source
snapshot, tell us the date of that snapshot).</li>
<li>What operating system your ViewVC is running on.</li>
<li>What version of Python you are using.</li>
<li>Whether you are running ViewVC standalone, or as a CGI program
under a web server (and if so, what web server).</li>
<li>The URL of your ViewVC instantiation, if it is public.
Sometimes, letting developers see the problem for themselves can
save everyone alot of time.</li>
</ul>
</div>
<div class="section">
<h2 id="sec-coding-style">Coding Style</h2>
<p>Unlike its predecessor, CvsWeb, ViewVC is written in Python, so it
doesn't suffer from the "unmaintainable code effect" that hits most
Perl projects sooner or later:</p>
<blockquote><em>&quot;[Perl] combines all the worst aspects of C and Lisp: a
billion different sublanguages in one monolithic executable. It
combines the power of C with the readability of PostScript.&quot;</em>
&mdash;&nbsp;Jamie&nbsp;Zawinski
</blockquote>
<p>Of course, a symphony of insanity can be composed in any language,
so we do try to stick to some basic guiding principles. Maintain
whatever style is present in the code being modified. New code can
use anything sane (which generally means <a
href="http://python.sourceforge.net/peps/pep-0008.html">PEP&nbsp;8</a>).
Our only real peeve is if someone writes a function call as:
<code>some_func&nbsp;(args)</code> &mdash; that space between the
function name and opening parenthesis is Huge Badness. Oh, and we
do <strong>not</strong> use Subversion keywords (such as
<code>$</code><code>Id$</code>) within the source.</p>
<p>Otherwise&hellip; <em>shrug</em>.</p>
</div>
<div class="section">
<h2 id="sec-patches">Submitting Patches</h2>
<p>Nothing speaks more loudly when bugs or features are the topic than
a patch. And quite frankly, sometimes if you want something done,
you gotta do it yourself. So, patches are always welcome. If you
aren't sure what exactly a &quot;patch&quot; is, or don't know how
to generate one, but you've got code contributions to make, please
don't hesitate to ask questions on the mailing lists. Patch
generation and application are pretty easy thing to get the hang of,
and drastically simplify code submission and review.</p>
<p>Please use the <a
href="http://viewvc.tigris.org/servlets/ProjectIssues">Issue
Tracker</a> to submit your patches. Unified contextual diffs
against the latest development snapshot are preferred.</p>
<p>If you have commit access, then you should know what you're doing.
Just make changes directly. Subscribing to the <a
href="http://viewvc.tigris.org/servlets/ProjectMailingListList"
><tt>dev@</tt> developer mailing list</a> is recommended in any
case.</p>
</div>
<div class="section">
<h2 id="sec-security">Security</h2>
<p>Since ViewVC is used on the Internet, security is a major concern.
If you need to pass data from the request into an external program,
please don't use <code>os.system()</code> or
<code>os.popen()</code>. Please use the module
<code>lib/popen.py</code> that is included in the ViewVC
distribution instead.</p>
</div>
<div class="section">
<h2 id="sec-adding-features">Adding Features</h2>
<p>If you need a new configuration option think carefully, into which
section it belongs. Try to keep the content of
<code>cgi/viewvc.conf.dist</code> file and the library module
<code>lib/config.py</code> in sync.</p>
<p>Because ViewVC is a Web-based application, people will have ViewVC
URLs hyperlinked from other sites, embedded in emails, bookmarked
in their browsers, etc. It is very important to ensure that those
URLs continue to retrieve the information they were intended to
retrieve even if ViewVC is upgraded on the hosting server. In
other words, as new features require modifications to the <a
href="./url-reference.html">ViewVC URL schema</a>, make sure those
modifications preserve the existing functionality of all ViewVC
URLs.</p>
<p>The library subdirectory contains a module <code>debug.py</code>,
which you may find useful for performance testing.</p>
<p>If a new file or module is added, a new line in the installer
program <code>viewvc-install</code> is required.</p>
</div>
<div class="section">
<h2 id="sec-templates">Hacking on Templates</h2>
<p>The library module <code>ezt.py</code> contains a module docstring
which describes the directives used in the HTML templates used by
ViewVC. The templates themselves can be found in the
<code>templates</code> subdirectory. We're currently developing a
how-to guide for <a href="./template-authoring-guide.html">ViewVC
template customization</a>.</p>
</div>
<div class="section">
<h2 id="sec-releasing">Release Management</h2>
<p>There is a script, <code>tools/make-release</code>, which creates a
release directory and the various archive files that we distribute.
All other steps required to get a ViewVC release out of the door
require manual execution (currently by C. Michael Pilato). Those
steps are as follows:</p>
<ol>
<li>Add a new subsection to the file
<code>website/upgrading.html</code> describing all user visible
changes for users of previous releases of ViewVC.</li>
<li>Update the <code>CHANGES</code> file</li>
<li>Update the <code>website/index.html</code> file to refer to the
new X.Y files. (there are three links to update)</li>
<li>Edit the file <code>lib/viewvc.py</code> and remove the
<tt>"-dev"</tt> suffix from <code>__version__</code>. The
remainder should be of the form X.Y.Z, where X, Y, and Z are
positive integers.</li>
<li>Ensure all of the above changes have been committed.</li>
<li>Test, Test, Test! At the time of this writing (1.0-dev) there
is no automatic testsuite available. So just run with
permuting different <code>viewvc.conf</code> settings&hellip;
and pray.</li>
<li>Review any open <a
href="http://viewvc.tigris.org/servlets/ProjectIssues">bug
reports.</a></li>
<li>Use <code>svn cp</code> to tag the release to
<code>/tags/<i>X</i>.<i>Y</i>.<i>Z</i></code>, where
<i>X</i>, <i>Y</i>, and <i>Z</i> should be replaced by the
release number from above. If a developer is willing to
volunteer as a bug fix patch release manager, it is now
possible to start here at this point with a feature-frozen
branch using <code>svn cp</code> to copy the tag to
<code>/branches/<i>X</i>.<i>Y</i>.x</code>.</li>
<li>Go into an empty directory and run <code>tools/make-release
viewvc-<i>X.Y.Z</i> <i>X.Y.Z</i></code>. This step requires
read access to the Subversion source code repository.</li>
<li>Upload the created archive files (tar.gz and zip) into the
Files and Documents section of the Tigris.org project.</li>
<li>Edit the file <code>lib/viewvc.py</code> again and this time
increment the <code>__version__</code> for the next release
cycle, again append the <code>-dev</code> to the version and
again <code>svn commit -m "Begin a new release cycle."
lib/viewvc.py</code>.</li>
<li>Write an announcement explaining all the cool new features and
post it <a
href="mailto:&#97&#110&#110&#111&#117&#110&#99&#101&#64&#118&#105&#101&#119&#118&#99&#46&#116&#105&#103&#114&#105&#115&#46&#111&#114&#103">&#97&#110&#110&#111&#117&#110&#99&#101&#64&#118&#105&#101&#119&#118&#99&#46&#116&#105&#103&#114&#105&#115&#46&#111&#114&#103</a>,
to the project's News area, and to other places interested in
this sort of stuff (such as <a
href="http://www.freshmeat.net">Freshmeat</a>).</p>
</ol>
</div>
</td>
</tr>
</table>
</body>
</html>

79
viewvc.org/download.html Normal file
View File

@@ -0,0 +1,79 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<title>ViewVC: Download</title>
<link rel="stylesheet" type="text/css" href="./styles.css"/>
</head>
<body>
<div id="title">
<a href="http://www.viewvc.org/"><img
src="./images/title.jpg" alt="ViewVC: Repository Browsing"/></a>
</div>
<div id="menu">
<p><a href="./index.html">Home</a> |
<a href="http://viewvc.tigris.org/">Project Page</a> |
<a href="./download.html">Download</a> |
<a href="./upgrading.html">Upgrading</a> |
<a href="./contributing.html">Contributing</a> |
<a href="./license-1.html">License</a> |
<a href="./contact.html">Contact</a> |
<a href="./who.html">About</a>
</p>
</div>
<table id="pagetable">
<tr>
<td id="pagecolumn1">
<h4>On this page:</h4>
<ul id="bookmarks">
<li><a href="#sec-download">Downloading</a></li>
<li><a href="#sec-subversion">Subversion</a></li>
</ul>
<hr/>
<address><a href="mailto:&#117&#115&#101&#114&#115&#64&#118&#105&#101&#119&#118&#99&#46&#116&#105&#103&#114&#105&#115&#46&#111&#114&#103">ViewVC Users Group</a></address>
</td>
<td id="pagecolumn2">
<div class="section">
<h2 id="sec-download">Downloading</h2>
<p>You can download the ViewVC (and the older ViewCVS releases, too)
from our <a
href="http://viewvc.tigris.org/servlets/ProjectDocumentList?folderID=6004"
>File and Documents</a> area. For information about what has
changed in each release, see the <a
href="http://viewvc.tigris.org/source/browse/viewvc/trunk/CHANGES?rev=HEAD"
>CHANGES</a> file.</p>
<p>We are also making <a href="./nightly/">nightly snapshots</a>
available in tar.gz and zip formats.</p>
</div>
<div class="section">
<h2 id="sec-subversion">Subversion</h2>
<p>The source code for ViewVC is maintained in a Subversion repository
at Tigris.org. You can checkout the trunk of our development tree
from <a href="http://viewvc.tigris.org/svn/viewvc/trunk/"
><tt>http://viewvc.tigris.org/svn/viewvc/trunk/</tt></a>. You'll
need to provide your Tigris.org username and password when so
prompted, or, if you don't have a Tigris.org account, use "guest"
as both the username and password.</p>
</div>
</td>
</tr>
</table>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
viewvc.org/images/title.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
viewvc.org/images/title.xcf Normal file

Binary file not shown.

192
viewvc.org/index.html Normal file
View File

@@ -0,0 +1,192 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<title>ViewVC: Repository Browsing</title>
<link rel="stylesheet" type="text/css" href="./styles.css"/>
</head>
<body>
<div id="title">
<a href="http://www.viewvc.org/"><img
src="./images/title.jpg" alt="ViewVC: Repository Browsing"/></a>
</div>
<div id="menu">
<p><a href="./index.html">Home</a> |
<a href="http://viewvc.tigris.org/">Project Page</a> |
<a href="./download.html">Download</a> |
<a href="./upgrading.html">Upgrading</a> |
<a href="./contributing.html">Contributing</a> |
<a href="./license-1.html">License</a> |
<a href="./contact.html">Contact</a> |
<a href="./who.html">About</a>
</p>
</div>
<table id="pagetable">
<tr>
<td id="pagecolumn1">
<h4>On this page:</h4>
<ul id="bookmarks">
<li><a href="#sec-what-is-viewvc">What Is ViewVC?</a></li>
<li><a href="#sec-requirements">Requirements</a></li>
<li><a href="#sec-future">Future Plans</a></li>
</ul>
<hr/>
<address><a href="mailto:&#117&#115&#101&#114&#115&#64&#118&#105&#101&#119&#118&#99&#46&#116&#105&#103&#114&#105&#115&#46&#111&#114&#103">ViewVC Users Group</a></address>
</td>
<td id="pagecolumn2">
<div class="section">
<h2 id="sec-what-is-viewvc">What Is ViewVC?</h2>
<p>ViewVC is a browser interface for CVS and Subversion version
control repositories. It generates templatized HTML to present
navigable directory, revision, and change log listings. It can
display specific versions of files as well as diffs between those
versions. Basically, ViewVC provides the bulk of the report-like
functionality you expect out of your version control tool, but much
more prettily than the average textual command-line program
output.</p>
<p>Here are some of the additional features of ViewVC:</p>
<ul>
<li>Support for filesystem-accessible CVS and Subversion repositories.</li>
<li>Individually configurable virtual host support.</li>
<li>Line-based annotation/blame display.</li>
<li>Revision graph capabilities (via integration with <a
href="http://www.akhphd.au.dk/~bertho/cvsgraph/">CvsGraph</a>)
(<em>CVS only</em>).</li>
<li>Syntax highlighting support (via integration with <a
href="http://www.codento.com/people/mtr/genscript/">GNU
enscript</a> or
<a href="http://www.andre-simon.de/">Highlight</a>).</li>
<li><a href="http://www.mozilla.org/projects/bonsai/">Bonsai</a>-like
repository query facilities.</li>
<li>Template-driven output generation.</li>
<li>Colorized, side-by-side differences.</li>
<li>Tarball generation (by tag/branch for CVS, by revision for
Subversion).</li>
<li>I18N support based on the Accept-Language request header.</li>
<li>Ability to run either as CGI script or as a standalone
server.</li>
<li>Regexp-based file searching.</li>
<li>INI-like configuration file (as opposed to requiring actual code
tweaks).</li>
</ul>
<p>For a complete list of changes present in each release, see
ViewVC's <a
href="http://viewvc.tigris.org/source/browse/viewvc/trunk/CHANGES?rev=HEAD"
>CHANGES</a> file.</p>
</div>
<div class="section">
<h2 id="sec-requirements">Requirements</h2>
<p>The only hard software requirement for running ViewVC is <a
href="http://www.python.org/">Python 1.5.2</a> or later. All other
requirements depend on what you want to do with the tool.</p>
<p>If you plan to use ViewVC with CVS repositories, you need the
following things:</p>
<ul>
<li><a href="http://www.cs.purdue.edu/homes/trinkle/RCS/">RCS</a>
(Revision Control System)</li>
<li><a href="http://www.gnu.org/software/diffutils/diffutils.html">GNU
diff</a></li>
<li>Read-only, physical access to a CVS repository.</li>
</ul>
<p>For use with Subversion repositories, you need these things:</p>
<ul>
<li><a href="http://subversion.tigris.org/">Subversion</a> 1.2 or
later and its SWIG Python bindings.</li>
<li><a href="http://www.gnu.org/software/diffutils/diffutils.html">GNU
diff</a></li>
<li>Physical access to a Subversion repository (though there is
limited, use-at-your-risk support for remote access, too).</li>
</ul>
<p>ViewVC integrates with additional pieces of software to provide
certain bits of optional functionality:</p>
<ul>
<li><a href="http://www.mysql.com/">MySQL</a> &mdash; Needed to use
the commit database query functionality.</li>
<li><a href="http://www.codento.com/people/mtr/genscript/">GNU
enscript</a> &mdash; Needed for syntax highlighting in versioned
file contents displays</li>
<li><a href="http://www.akhphd.au.dk/~bertho/cvsgraph/">CvsGraph</a>
&mdash; Needed for version graph displays.</li>
<li><a href="http://httpd.apache.org/">Apache HTTP Server</a>, or
another server capable of running CGI programs &mdash; unless
you just want ViewVC to run in standalone server mode.</li>
</ul>
</div>
<div class="section">
<h2 id="sec-future">Future Plans</h2>
<p>ViewVC is an Open Source project. So any future development
depends on the <a href="./contributing.html">contributions</a> that
will be made by its user community. Certainly working patches have
a greater chance to become realized quickly than feature requests,
but please don't hesitate to submit your suggestions to our <a
href="http://viewvc.tigris.org/servlets/ProjectIssues">issue
tracker</a>.</p>
<p>Some things we're thinking about include:</p>
<ul>
<li>UI streamlining/simplification.</li>
<li>Integration with CVS and Subversion commit mail scripts.</li>
<li>Integration with an indexer such as LXR.</li>
</ul>
</div>
</td>
</tr>
</table>
</body>
</html>

View File

@@ -1,21 +1,63 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html> <html>
<head> <head>
<title>ViewVC: License v1</title> <title>ViewVC: License v1</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <link rel="stylesheet" type="text/css" href="./styles.css"/>
</head> </head>
<body> <body>
<div id="title">
<a href="http://www.viewvc.org/"><img
src="./images/title.jpg" alt="ViewVC: Repository Browsing"/></a>
</div>
<div id="menu">
<p><a href="./index.html">Home</a> |
<a href="http://viewvc.tigris.org/">Project Page</a> |
<a href="./download.html">Download</a> |
<a href="./upgrading.html">Upgrading</a> |
<a href="./contributing.html">Contributing</a> |
<a href="./license-1.html">License</a> |
<a href="./contact.html">Contact</a> |
<a href="./who.html">About</a>
</p>
</div>
<table id="pagetable">
<tr>
<td id="pagecolumn1">
<h4>On this page:</h4>
<ul id="bookmarks">
<li><a href="#sec-license">License</a></li>
</ul>
<hr/>
<address><a href="mailto:&#117&#115&#101&#114&#115&#64&#118&#105&#101&#119&#118&#99&#46&#116&#105&#103&#114&#105&#115&#46&#111&#114&#103">ViewVC Users Group</a></address>
</td>
<td id="pagecolumn2">
<div class="section">
<h2 id="sec-license">License</h2>
<p>The following text constitutes the license agreement for the <a <p>The following text constitutes the license agreement for the <a
href="http://www.viewvc.org/">ViewVC</a> software (formerly known href="./index.html">ViewVC</a> software (formerly known as
as ViewCVS). It is an agreement between <a ViewCVS). It is an agreement between <a
href="http://www.viewvc.org/who.html#sec-viewcvs-group">The ViewCVS href="./who.html#sec-viewcvs-group">The ViewCVS Group</a> and the
Group</a> and the users of ViewVC.</p> users of ViewVC.</p>
<blockquote> <p style="font-size: 80%;"><em>Note: the copyright years were updated
on May 12, 2001 and September 5, 2002. No other changes were made
to the license.</em></p>
<p><strong>Copyright &copy; 1999-2012 The ViewCVS Group. All rights <hr/>
<p><strong>Copyright &copy; 1999-2002 The ViewCVS Group. All rights
reserved.</strong></p> reserved.</strong></p>
<p>By using ViewVC, you agree to the terms and conditions set forth <p>By using ViewVC, you agree to the terms and conditions set forth
@@ -48,21 +90,10 @@
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.</p> SUCH DAMAGE.</p>
</blockquote> </div>
<hr />
<p>The following changes have occured to this license over time:</p>
<ul>
<li>May 12, 2001 &mdash; copyright years updated</li>
<li>September 5, 2002 &mdash; copyright years updated</li>
<li>March 17, 2006 &mdash; software renamed from "ViewCVS"</li>
<li>April 10, 2007 &mdash; copyright years updated</li>
<li>February 22, 2008 &mdash; copyright years updated</li>
<li>March 29, 2010 &mdash; copyright years updated</li>
<li>February 18, 2011 &mdash; copyright years updated</li>
<li>January 23, 2012 &mdash; copyright years updated</li>
</ul>
</td>
</tr>
</table>
</body> </body>
</html> </html>

151
viewvc.org/styles.css Normal file
View File

@@ -0,0 +1,151 @@
body {
background-color: rgb(180,193,205);
background-image: url('./images/bg-grad.jpg');
background-repeat: repeat-x;
color: black;
font-family: arial, sans-serif;
font-size: 90%;
margin: 0;
}
#title {
background-color: rgb(100,128,150);
height: 60px;
}
img {
border: none;
}
hr {
width: 95%;
height: 1px;
}
address {
text-align: center;
font-size: 60%;
font-style: normal;
}
h2 {
background-color: rgb(204,213,221);
padding: 2px 0.5em 2px 0.5em;
margin: 0;
border-bottom: dotted 1px rgb(180,193,205);
font-size: 110%;
}
h3 {
padding: 2px 0.5em 2px 0.5em;
margin: 0;
font-size: 100%;
font-weight: bold;
}
h4 {
padding: 2px 0.5em 2px 0.5em;
margin: 0;
font-size: 90%;
font-weight: bold;
}
p, dl, ul, ol {
padding: 0.5em;
margin: 0;
font-size: 90%;
}
ul, ol {
padding: 0.5em;
margin: 0 0 0 0.25in;
}
li {
font-size: 90%;
}
code {
font-size: 105%;
font-style: italic;
font-family: monospace;
}
blockquote {
padding: 0.5em;
margin: 0 0.25in 0 0.25in;
font-size: 80%;
}
.section {
background: white;
border-color: rgb(24,24,24);
border-width: 1px 2px 2px 1px;
border-style: solid;
padding: 0;
margin-bottom: 0.5em;
}
#menu {
background: black;
color: white;
}
#menu a, #menu a:visited {
background: black;
color: white;
text-decoration: none;
}
#menu a:hover {
background: black;
color: white;
text-decoration: underline;
}
#submenu {
background: rgb(90%,97%,99%);
font-size: 80%;
}
#pagetable tr {
vertical-align: top;
}
#pagetable {
width: 100%;
}
#pagecolumn1 {
width: 175px;
padding: 0;
}
#pagecolumn1 a, #pagecolumn1 a:visited {
color: rgb(0,0,164);
text-decoration: none;
}
#pagecolumn1 a:hover {
text-decoration: underline;
}
#pagecolumn2 {
padding: 0;
}
#pagecolumn2 a, #pagecolumn2 a:visited {
color: rgb(0,0,164);
text-decoration: underline;
}
#pagecolumn2 a:hover {
background-color: rgb(180,193,205);
text-decoration: underline;
}
#bookmarks {
padding-left: 0.25in;
font-size: 80%;
white-space: nowrap;
}

View File

@@ -1586,20 +1586,10 @@ td {
<td>Boolean</td> <td>Boolean</td>
<td>True if files list was cut short due to <tt>limit_changes</tt>.</td> <td>True if files list was cut short due to <tt>limit_changes</tt>.</td>
</tr> </tr>
<tr class="varlevel2">
<td class="varname">commits.minus</td>
<td>String</td>
<td>Total number of lines removed from files in this commit.</td>
</tr>
<tr class="varlevel2"> <tr class="varlevel2">
<td class="varname">commits.num_files</td> <td class="varname">commits.num_files</td>
<td>String</td> <td>String</td>
<td>Total number of files in the <var>commits.files</var> list.</td> <td>Number of files in the <var>commits.files</var> list.</td>
</tr>
<tr class="varlevel2">
<td class="varname">commits.plus</td>
<td>String</td>
<td>Number of lines added to files in this commit.</td>
</tr> </tr>
<tr class="varlevel2"> <tr class="varlevel2">
<td class="varname">commits.rev</td> <td class="varname">commits.rev</td>
@@ -1640,14 +1630,12 @@ td {
<tr class="varlevel1"> <tr class="varlevel1">
<td class="varname">minus_count</td> <td class="varname">minus_count</td>
<td>String</td> <td>String</td>
<td>Total number of lines removed from all files across all returned <td>Total number of lines removed in the commit (over all files).</td>
commits.</td>
</tr> </tr>
<tr class="varlevel1"> <tr class="varlevel1">
<td class="varname">plus_count</td> <td class="varname">plus_count</td>
<td>String</td> <td>String</td>
<td>Total number of lines added to all files across all returned <td>Total number of lines added in the commit (over all files).</td>
commits.</td>
</tr> </tr>
<tr class="varlevel1"> <tr class="varlevel1">
<td class="varname">queryform_href</td> <td class="varname">queryform_href</td>

View File

@@ -89,7 +89,7 @@ th.caption {
<li><a href="#compat-tarball">'<code>tarball=1</code>' Parameter &rArr; '<code>view=tar</code>'</a></li> <li><a href="#compat-tarball">'<code>tarball=1</code>' Parameter &rArr; '<code>view=tar</code>'</a></li>
<li><a href="#compat-graph">'<code>graph=1</code>' Parameter &rArr; '<code>view=graph</code>'</a></li> <li><a href="#compat-graph">'<code>graph=1</code>' Parameter &rArr; '<code>view=graph</code>'</a></li>
<li><a href="#compat-makeimage">'<code>graph=1&makeimage=1</code>' Parameters &rArr; '<code>view=graphimg</code>'</a></li> <li><a href="#compat-makeimage">'<code>graph=1&makeimage=1</code>' Parameters &rArr; '<code>view=graphimg</code>'</a></li>
<li><a href="#compat-content_type">'<code>content-type=text/vnd.viewcvs-markup</code>' and '<code>content-type=text/x-cvsweb-markup</code>' Parameters&rArr; '<code>view=markup</code>' <li><a href="#compat-content_type">'<code>content_type=text/vnd.viewcvs-markup</code>' and '<code>content_type=text/x-cvsweb-markup</code>' Parameters&rArr; '<code>view=markup</code>'
<li><a href="#compat-attic">'<code>Attic/FILE</code>' Paths &rArr; '<code>FILE</code>'</a></li> <li><a href="#compat-attic">'<code>Attic/FILE</code>' Paths &rArr; '<code>FILE</code>'</a></li>
</ul> </ul>
</div> </div>
@@ -284,6 +284,11 @@ th.caption {
<td>depends</td> <td>depends</td>
<td><a href="#view-param"><code>view</code> parameter</a>, not needed if the <code>default_file_view</code> configuration variable is set to <code>co</code>, since that makes the checkout view the default view for file paths. Also not needed if the <code>/*checkout*</code> magic prefix or the <code>revision</code> parameter is present. <td><a href="#view-param"><code>view</code> parameter</a>, not needed if the <code>default_file_view</code> configuration variable is set to <code>co</code>, since that makes the checkout view the default view for file paths. Also not needed if the <code>/*checkout*</code> magic prefix or the <code>revision</code> parameter is present.
</tr> </tr>
<tr>
<td><code>content-type=<var>TYPE</var></code></td>
<td>optional</td>
<td>MIME type to send with checked out file, default is a guess based on file extension</td>
</tr>
<tr> <tr>
<td><code>revision=<var>REVISION</var></code></td> <td><code>revision=<var>REVISION</var></code></td>
<td>optional</td> <td>optional</td>
@@ -867,7 +872,7 @@ th.caption {
<td>file query string</td> <td>file query string</td>
</tr> </tr>
<tr> <tr>
<td><code>file_match=<var>FILE_MATCH</var></code></td> <td><code>file_match=FILE_MATCH</code></td>
<td>optional</td> <td>optional</td>
<td>"exact" "like" "glob" "regex" or "notregex" determining type of file match</td> <td>"exact" "like" "glob" "regex" or "notregex" determining type of file match</td>
</tr> </tr>
@@ -877,37 +882,37 @@ th.caption {
<td>author query string</td> <td>author query string</td>
</tr> </tr>
<tr> <tr>
<td><code>who_match=<var>WHO_MATCH</var></code></td> <td><code>who_match=WHO_MATCH</code></td>
<td>optional</td> <td>optional</td>
<td>"exact" "like" "glob" "regex" or "notregex" determining type of author match</td> <td>"exact" "like" "glob" "regex" or "notregex" determining type of author match</td>
</tr> </tr>
<tr> <tr>
<td><code>querysort=<var>SORT</var></code></td> <td><code>querysort=SORT</code></td>
<td>optional</td> <td>optional</td>
<td>"date" "author" or "file" determining order of query results</td> <td>"date" "author" or "file" determining order of query results</td>
</tr> </tr>
<tr> <tr>
<td><code>date=<var>DATE</var></code></td> <td><code>date=DATE</code></td>
<td>optional</td> <td>optional</td>
<td>"hours" "day" "week" "month" "all" or "explicit" to filter query results by date</td> <td>"hours" "day" "week" "month" "all" or "explicit" to filter query results by date</td>
</tr> </tr>
<tr> <tr>
<td><code>hours=<var>HOURS</var></code></td> <td><code>hours=HOURS</code></td>
<td>optional</td> <td>optional</td>
<td>number of hours back to include results from when <code><var>DATE</var></code> is "hours"</td> <td>number of hours back to include results from when <code><var>DATE</var></code> is "hours"</td>
</tr> </tr>
<tr> <tr>
<td><code>mindate=<var>MINDATE</var></code></td> <td><code>mindate=MINDATE</code></td>
<td>optional</td> <td>optional</td>
<td>earliest date to include results from when <code><var>DATE</var></code> is "explicit"</td> <td>earliest date to include results from when <code><var>DATE</var></code> is "explicit"</td>
</tr> </tr>
<tr> <tr>
<td><code>maxdate=<var>MAXDATE</var></code></td> <td><code>maxdate=MAXDATE</code></td>
<td>optional</td> <td>optional</td>
<td>latest date to include results from when <code><var>DATE</var></code> is "explicit"</td> <td>latest date to include results from when <code><var>DATE</var></code> is "explicit"</td>
</tr> </tr>
<tr> <tr>
<td><code>limit_changes=<var>LIMIT_CHANGES</var></code></td> <td><code>limit_changes=LIMIT_CHANGES</code></td>
<td>optional</td> <td>optional</td>
<td>maximum number of files to list per commit in query results. Default is value of <code>limit_changes</code> configuration option</td> <td>maximum number of files to list per commit in query results. Default is value of <code>limit_changes</code> configuration option</td>
</tr> </tr>
@@ -965,7 +970,7 @@ th.caption {
<td>branch query string</td> <td>branch query string</td>
</tr> </tr>
<tr> <tr>
<td><code>branch_match=<var>BRANCH_MATCH</var></code></td> <td><code>branch_match=BRANCH_MATCH</code></td>
<td>optional</td> <td>optional</td>
<td>"exact" "like" "glob" "regex" or "notregex" determining type of branch match</td> <td>"exact" "like" "glob" "regex" or "notregex" determining type of branch match</td>
</tr> </tr>
@@ -980,7 +985,7 @@ th.caption {
<td>file query string</td> <td>file query string</td>
</tr> </tr>
<tr> <tr>
<td><code>file_match=<var>FILE_MATCH</var></code></td> <td><code>file_match=FILE_MATCH</code></td>
<td>optional</td> <td>optional</td>
<td>"exact" "like" "glob" "regex" or "notregex" determining type of file match</td> <td>"exact" "like" "glob" "regex" or "notregex" determining type of file match</td>
</tr> </tr>
@@ -990,42 +995,47 @@ th.caption {
<td>author query string</td> <td>author query string</td>
</tr> </tr>
<tr> <tr>
<td><code>who_match=<var>WHO_MATCH</var></code></td> <td><code>who_match=WHO_MATCH</code></td>
<td>optional</td> <td>optional</td>
<td>"exact" "like" "glob" "regex" or "notregex" determining type of author match</td> <td>"exact" "like" "glob" "regex" or "notregex" determining type of author match</td>
</tr> </tr>
<tr> <tr>
<td><code>querysort=<var>SORT</var></code></td> <td><code>querysort=SORT</code></td>
<td>optional</td> <td>optional</td>
<td>"date" "author" or "file" determining order of query results</td> <td>"date" "author" or "file" determining order of query results</td>
</tr> </tr>
<tr> <tr>
<td><code>date=<var>DATE</var></code></td> <td><code>date=DATE</code></td>
<td>optional</td> <td>optional</td>
<td>"hours" "day" "week" "month" "all" or "explicit" to filter query results by date</td> <td>"hours" "day" "week" "month" "all" or "explicit" to filter query results by date</td>
</tr> </tr>
<tr> <tr>
<td><code>hours=<var>HOURS</var></code></td> <td><code>hours=HOURS</code></td>
<td>optional</td> <td>optional</td>
<td>number of hours back to include results from when <code><var>DATE</var></code> is "hours"</td> <td>number of hours back to include results from when <code><var>DATE</var></code> is "hours"</td>
</tr> </tr>
<tr> <tr>
<td><code>mindate=<var>MINDATE</var></code></td> <td><code>mindate=MINDATE</code></td>
<td>optional</td> <td>optional</td>
<td>earliest date to include results from when <code><var>DATE</var></code> is "explicit"</td> <td>earliest date to include results from when <code><var>DATE</var></code> is "explicit"</td>
</tr> </tr>
<tr> <tr>
<td><code>maxdate=<var>MAXDATE</var></code></td> <td><code>maxdate=MAXDATE</code></td>
<td>optional</td> <td>optional</td>
<td>latest date to include results from when <code><var>DATE</var></code> is "explicit"</td> <td>latest date to include results from when <code><var>DATE</var></code> is "explicit"</td>
</tr> </tr>
<tr> <tr>
<td><code>format=<var>FORMAT</var></code></td> <td><code>format=FORMAT</code></td>
<td>optional</td> <td>optional</td>
<td>"rss" or "backout" values to generate an rss feed or list of commands to back out changes instead showing a normal query result page</td> <td>"rss" or "backout" values to generate an rss feed or list of commands to back out changes instead showing a normal query result page</td>
</tr> </tr>
<tr> <tr>
<td><code>limit_changes=<var>LIMIT_CHANGES</var></code></td> <td><code>limit=LIMIT</code></td>
<td>optional</td>
<td>maximum number of file-revisions to process during a query. Default is value of <code>row_limit</code> configuration option</td>
</tr>
<tr>
<td><code>limit_changes=LIMIT_CHANGES</code></td>
<td>optional</td> <td>optional</td>
<td>maximum number of files to list per commit in query results. Default is value of <code>limit_changes</code> configuration option</td> <td>maximum number of files to list per commit in query results. Default is value of <code>limit_changes</code> configuration option</td>
</tr> </tr>
@@ -1077,7 +1087,7 @@ th.caption {
<td><a href="#revision-param"><code>revision</code> parameter</a></td> <td><a href="#revision-param"><code>revision</code> parameter</a></td>
</tr> </tr>
<tr> <tr>
<td><code>limit_changes=<var>LIMIT_CHANGES</var></code></td> <td><code>limit_changes=LIMIT_CHANGES</code></td>
<td>optional</td> <td>optional</td>
<td>maximum number of files to list per commit. Default is value of <code>limit_changes</code> configuration option</td> <td>maximum number of files to list per commit. Default is value of <code>limit_changes</code> configuration option</td>
</tr> </tr>
@@ -1223,8 +1233,8 @@ th.caption {
<h3 id="compat-makeimage">'<code>graph=1&amp;makeimage=1</code>' Parameters &rArr; '<code>view=graphimg</code>'</h3> <h3 id="compat-makeimage">'<code>graph=1&amp;makeimage=1</code>' Parameters &rArr; '<code>view=graphimg</code>'</h3>
<p>A <code>graph=1&amp;makeimage=1</code> parameter is treated like a <code>view=graph</code> parameter. There is currently no redirect when it is encountered, but there could be one in the future.</p> <p>A <code>graph=1&amp;makeimage=1</code> parameter is treated like a <code>view=graph</code> parameter. There is currently no redirect when it is encountered, but there could be one in the future.</p>
<h3 id="compat-content_type">'<code>content-type=text/vnd.viewcvs-markup</code>' and '<code>content-type=text/x-cvsweb-markup</code>' Parameters&rArr; '<code>view=markup</code>'; other values ignored</h3> <h3 id="compat-content_type">'<code>content_type=text/vnd.viewcvs-markup</code>' and '<code>content_type=text/x-cvsweb-markup</code>' Parameters&rArr; '<code>view=markup</code>'</h3>
<p><code>content-type=text/vnd.viewcvs-markup</code> and <code>content-type=text/x-cvsweb-markup</code> parameters are treated like a <code>view=markup</code> parameter. There is currently no redirect when it is encountered, but there could be one in the future. Other values of the <code>content-type</code> parameter, which were used to dictate the MIME type of files displayed in the checkout/download view prior to ViewVC 1.0.6, are ignored.</p> <p><code>content_type=text/vnd.viewcvs-markup</code> and <code>content_type=text/x-cvsweb-markup</code> parameters are treated like a <code>view=markup</code> parameter. There is currently no redirect when it is encountered, but there could be one in the future.</p>
<h3 id="compat-attic">'<code>Attic/FILE</code>' Paths &rArr; '<code>FILE</code>'</h3> <h3 id="compat-attic">'<code>Attic/FILE</code>' Paths &rArr; '<code>FILE</code>'</h3>
<p>When ViewVC encounters an invalid repository path whose last or second-to-last component is named <code>Attic</code>, and stripping the component yields a valid path, it will redirect to a URL with that path.</p> <p>When ViewVC encounters an invalid repository path whose last or second-to-last component is named <code>Attic</code>, and stripping the component yields a valid path, it will redirect to a URL with that path.</p>

159
viewvc.org/who.html Normal file
View File

@@ -0,0 +1,159 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<title>ViewVC: About</title>
<link rel="stylesheet" type="text/css" href="./styles.css"/>
</head>
<body>
<div id="title">
<a href="http://www.viewvc.org/"><img
src="./images/title.jpg" alt="ViewVC: Repository Browsing"/></a>
</div>
<div id="menu">
<p><a href="./index.html">Home</a> |
<a href="http://viewvc.tigris.org/">Project Page</a> |
<a href="./download.html">Download</a> |
<a href="./upgrading.html">Upgrading</a> |
<a href="./contributing.html">Contributing</a> |
<a href="./license-1.html">License</a> |
<a href="./contact.html">Contact</a> |
<a href="./who.html">About</a>
</p>
</div>
<table id="pagetable">
<tr>
<td id="pagecolumn1">
<h4>On this page:</h4>
<ul id="bookmarks">
<li><a href="#sec-history">The History of ViewVC</a></li>
<li><a href="#sec-viewcvs-group">The ViewCVS Group</a></li>
<li><a href="#sec-site-credits">About This Site</a></li>
</ul>
<hr/>
<address><a href="mailto:&#117&#115&#101&#114&#115&#64&#118&#105&#101&#119&#118&#99&#46&#116&#105&#103&#114&#105&#115&#46&#111&#114&#103">ViewVC Users Group</a></address>
</td>
<td id="pagecolumn2">
<div class="section">
<h2 id="sec-history">The History of ViewVC</h2>
<p>The ViewVC software was inspired by <a
href="http://people.freebsd.org/~fenner/cvsweb/">cvsweb</a>
(originally written by Bill Fenner and then further developed by <a
href="mailto:zeller@think.de">Henner Zeller</a>). Greg Stein
wanted to make some changes and updates, but cvsweb was implemented
in Perl. He wrote:</p>
<blockquote style="font-style: italic;">&quot;While I can manage some
Perl, cvsweb was rather unmaintainable for me. So I undertook the
task to convert the software to Python. As a result, I've actually
been able to go <em>way</em> beyond the simple changes that I had
envisioned.&quot;</blockquote>
<p>So ViewVC started out as just a port of the cvsweb script,
originally called ViewCVS. Along the way, it has had numerous
cleanups and other modifications, a process simplified by the
elegance of the <a href="http://www.python.org/">Python</a>
language.</p>
<p>In 2001, the ViewCVS project was moved to <a
href="http://www.sourceforge.net">SourceForge</a>, a popular
software collaboration environment. There the project continued to
mature, releasing several stable-yet-pre-1.0 versions. In 2002,
C. Michael Pilato began implementing support for Subversion in
ViewCVS, building atop the beginnings of a version control
abstraction layer begun by Lucas Bruand. Along the way, Russell
Yanofsky delivered large improvements to that abstraction, and to
ViewCVS as whole. ViewCVS was well on its way to releasing a 1.0
version.</p>
<p>Of course, now that ViewCVS could browse Subversion repositories as
easily as CVS ones, the ViewCVS name seemed inappropriate. Also,
SourceForge's lack of support for Subversion (which was already
well past its 1.0 release, and becoming hugely popular) in its
project version control offerings was annoying ViewCVS primary
developers. So in late 2005, the decision was made to rename the
project to ViewVC, to convert the project's CVS data to Subversion,
and to move the project and its Subversion data to <a
href="http://www.tigris.org">Tigris.org</a>.</p>
<p>Today, ViewVC is being developed at <a
href="http://viewvc.tigris.org">http://viewvc.tigris.org</a> by a
small community of folks</a>.</p>
</div>
<div class="section">
<h2 id="sec-viewcvs-group">The ViewCVS Group</h2>
<p>The ViewCVS Group is an informal group of people working on and
developing the ViewVC package. The current set of members are
listed below with some of their notable contributions:</p>
<dl>
<dt><a href="http://www.lyra.org/greg/">Greg Stein</a></dt>
<dd>original python port of Henner Zeller's cvsweb, secure popen
implementation, configuration file implementation, rcsparse
module, and EZT template engine</dd>
<dt>Jay Painter</dt>
<dd>CVSdb query engine</dd>
<dt>Tanaka Akira</dt>
<dd>enscript colorization and tarball generation
</dd>
<dt>Tim Cera</dt>
<dd>CvsGraph support, log_table template, regular expression search,
and paging capability</dd>
<dt>Peter Funk</dt>
<dd>standalone server, blimp logo, and numerous improvements to
ViewVC's interfaces and documentation</dd>
<dt>Lucas Bruand</dt>
<dd>C++ RCS parser (tparse) and vclib module for supporting new
version control systems</dd>
<dt><a href="http://www.cmichaelpilato.com/">C. Michael Pilato</a></dt>
<dd>Subversion support, root_as_url alternative URL scheme,
templatization work, website design, documentation</dd>
<dt>Russell Yanofsky</dt>
<dd>Windows support and the sapi module for supporting multiple web
server interfaces, sweeping abstraction and UI improvements</dd>
<dt>James Henstridge</dt>
<dd>integrated query interface, support for querying Subversion
repositories, caching support, CSS formatting, and the EZT
&quot;define&quot; directive</dd>
</dl>
</div>
<div class="section">
<h2 id="sec-site-credits">About This Site</h2>
<p>The ViewVC website was designed by <a
href="http://www.cmichaelpilato.com/">C. Michael Pilato</a>. All
HTML was hand-edited in Emacs, and the little splashes of graphical
goodness owe their existence to Adobe PhotoShop. Textual content
for the site is mostly the work of Greg Stein, but has been tweaked
through the ages by various ViewVC contributors.</p>
</div>
</td>
</tr>
</table>
</body>
</html>

70
www/index.html Normal file
View File

@@ -0,0 +1,70 @@
<html>
<head>
<title>ViewVC - Version Control Repository Browser</title>
<!-- Custom stylations to hide the obnoxious project info -->
<style type="text/css">
#projecthome .axial { display: none; }
#apphead h1 { display: none; }
#longdescription { border: none; }
#longdescription h2 { display: none; }
#customcontent h2 { display: block; }
</style>
<!-- End custom stylations -->
</head>
<body>
<div class="app" id="customcontent">
<h1>ViewVC &mdash; Web-based Version Control Repository Browsing</h1>
<div class="h2">
<h2>Latest Release</h2>
<p>The most recent release of ViewVC is: <strong>1.0.0-rc1</strong></p>
</div>
<div class="h2">
<h2>What Is ViewVC?</h2>
<p>ViewVC is a browser interface for CVS and Subversion version
control repositories. It generates templatized HTML to present
navigable directory, revision, and change log listings. It can
display specific versions of files as well as diffs between those
versions. Basically, ViewVC provides the bulk of the report-like
functionality you expect out of your version control tool, but much
more prettily than the average textual command-line program
output.</p>
<p>Here are some of the additional features of ViewVC:</p>
<ul>
<li>Support for filesystem-accessible CVS and Subversion repositories</li>
<li>Individually configurable virtual host support</li>
<li>Line-based annotation/blame display</li>
<li>Revision graph capabilities (<em>CVS only</em>)</li>
<li>Syntax highlighting support</li>
<li>Commit metadata query facilities</li>
<li>Template-driven output generation</li>
<li>Colorized, side-by-side differences</li>
<li>Tarball generation (by tag/branch for CVS, by revision for
Subversion)</li>
<li>Localization support based on the Accept-Language request header</li>
<li>Ability to run either as CGI script or as a standalone
server</li>
<li>Regexp-based file searching</li>
<li>INI-like configuration file (as opposed to requiring actual code
tweaks)</li>
</ul>
<p>For a complete list of changes present in each release, see
ViewVC's <a
href="http://viewvc.tigris.org/source/browse/viewvc/trunk/CHANGES?rev=HEAD"
>CHANGES</a> file.</p>
</div>
</div>
</body>
</html>

30
www/project_tools.html Normal file
View File

@@ -0,0 +1,30 @@
<!-- Overrides the left-nav tool bar on viewvc.tigris.org, a feature
specific to tigris.org's CEE branding. See www/overrides/ in the
look.tigris.org project for details. -->
<!-- dd --><ul>
<li><a href="http://www.viewvc.org/"
>Project website</a></li>
<!-- #################################################### --></ul></dd><dd><ul>
<li><a href="http://viewvc.tigris.org/servlets/ProjectMemberList"
>Membership</a></li>
<!-- #################################################### --></ul></dd><dd><ul>
<li><a href="http://viewvc.tigris.org/servlets/ProjectNewsList"
>Announcements</a></li>
<!-- li><a href="http://viewvc.tigris.org/servlets/ProjectForumView"
>Discussion forums</a></li -->
<li><a href="http://viewvc.tigris.org/servlets/ProjectMailingListList"
>Mailing lists</a></li>
<li><a href="http://viewvc.tigris.org/servlets/ProjectDocumentList"
>Documents &amp; files</a></li>
<li><a href="http://viewvc.tigris.org/source/browse/viewvc/"
>Subversion</a></li>
<li><a href="http://viewvc.tigris.org/issues/buglist.cgi?component=viewvc&amp;issue_status=UNCONFIRMED&amp;issue_status=NEW&amp;issue_status=STARTED&amp;issue_status=REOPENED"
>Issue tracker</a></li>
</ul><!-- /dd -->