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
1f399bbd6d Tag the 1.1.11 final release.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/tags/1.1.11@2573 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-05-17 12:27:03 +00:00
60 changed files with 418 additions and 1081 deletions

58
CHANGES
View File

@@ -1,58 +1,3 @@
Version 1.1.18 (released 28-Feb-2013)
* fix exception raised by BDB-backed SVN repositories (issue #519)
* hide revision-less files when rcsparse is in use
* include branchpoints in branch views using rcsparse (issue #347)
* miscellaneous cvsdb improvements:
- add --port option to make-database (issue #521)
- explicitly name columns in queries (issue #522)
- update MySQL syntax to avoid discontinued "TYPE=" terms
Version 1.1.17 (released 25-Oct-2012)
* fix exception caused by uninitialized variable usage (issue #516)
Version 1.1.16 (released 24-Oct-2012)
* security fix: escape "extra" diff info to avoid XSS attack (issue #515)
* add 'binary_mime_types' configuration option and handling (issue #510)
* fix 'select for diffs' persistence across log pages (issue #512)
* remove lock status and filesize check on directories in remote SVN views
* fix bogus 'Annotation of' page title for non-annotated view (issue #514)
Version 1.1.15 (released 22-Jun-2012)
* security fix: complete authz support for remote SVN views (issue #353)
* security fix: log msg leak in SVN revision view with unreadable copy source
* fix several instances of incorrect information in remote SVN views
* increase performance of some revision metadata lookups in remote SVN views
* fix RSS feed regression introduced in 1.1.14
Version 1.1.14 (released 12-Jun-2012)
* fix annotation of svn files with non-URI-safe paths (issue #504)
* handle file:/// Subversion rootpaths as local roots (issue #446)
* fix bug caused by trying to case-normalize anon usernames (issue #505)
* speed up log handling by reusing tokenization results (issue #506)
* add support for custom revision log markup rules (issue #246)
Version 1.1.13 (released 23-Jan-2012)
* fix svndbadmin failure on deleted paths under Subversion 1.7 (issue #499)
* fix annotation of files in svn roots with non-URI-safe paths
* fix stray annotation warning in markup display of images
* more gracefully handle attempts to display binary content (issue #501)
Version 1.1.12 (released 03-Nov-2011)
* fix path display in patch and certain diff views (issue #485)
* fix broken cvsdb glob searching (issue 486)
* allow svn revision specifiers to have leading r's (issue #441, #448)
* allow environmental override of configuration location (issue #494)
* fix exception HTML-escaping non-string data under WSGI (issue #454)
* add links to root logs from roots view (issue #470)
* use Pygments lexer-guessing functionality (issue #495)
Version 1.1.11 (released 17-May-2011)
* security fix: remove user-reachable override of cvsdb row limit
@@ -61,7 +6,7 @@ Version 1.1.11 (released 17-May-2011)
* fix stack trace when asked to checkout a directory (issue #478)
* improve memory usage and speed of revision log markup (issue #477)
* fix broken annotation view in CVS keyword-bearing files (issue #479)
* warn users when query results are incomplete (issue #433)
* warn users when query results are incomplete (issue #443)
* avoid parsing errors on RCS newphrases in the admin section (issue #483)
* make rlog parsing code more robust in certain error cases (issue #444)
@@ -76,6 +21,7 @@ Version 1.1.9 (released 18-Feb-2011)
* make revision log "extra pages" count configurable
* fix Subversion 1.4.x revision log compatibility code regression
* display sanitized error when authzfile is malformed
* handle file:/// Subversion rootpaths as local roots (issue #446)
* restore markup of URLs in file contents (issue #455)
* optionally display last-committed metadata in roots view (issue #457)

55
INSTALL
View File

@@ -34,7 +34,7 @@ Congratulations on getting this far. :-)
(http://www.python.org/)
* Subversion, Version Control System, 1.3.1 or later
(binary installation and Python bindings)
(http://subversion.apache.org/)
(http://subversion.tigris.org/)
Optional:
@@ -176,24 +176,7 @@ APACHE CONFIGURATION
or /etc/local. Use the vendor documentation or the find utility if
in doubt.
2) Depending on how your Apache configuration is setup by default, you
might need to explicitly allow high-level access to the ViewVC
install location.
<Directory <VIEWVC_INSTALLATION_DIRECTORY>>
Order allow,deny
Allow from all
</Directory>
For example, if ViewVC is installed in /usr/local/viewvc-1.0 on
your system:
<Directory /usr/local/viewvc-1.0>
Order allow,deny
Allow from all
</Directory>
3) Configure Apache to expose ViewVC to users at the URL of your choice.
2) Configure Apache to expose ViewVC to users at the URL of your choice.
ViewVC provides several different ways to do this. Choose one of
the following methods:
@@ -204,13 +187,13 @@ APACHE CONFIGURATION
The ScriptAlias directive is very useful for pointing
directly to the viewvc.cgi script. Simply insert a line containing
ScriptAlias /viewvc <VIEWVC_INSTALLATION_DIRECTORY>/bin/cgi/viewvc.cgi
ScriptAlias /viewvc <VIEWVC_INSTALLATION_DIRECTORY>/bin/cgi/viewvc.cgi
into your httpd.conf file. Choose the location in httpd.conf where
also the other ScriptAlias lines reside. Some examples:
ScriptAlias /viewvc /usr/local/viewvc-1.0/bin/cgi/viewvc.cgi
ScriptAlias /query /usr/local/viewvc-1.0/bin/cgi/query.cgi
ScriptAlias /viewvc /usr/local/viewvc-1.0/bin/cgi/viewvc.cgi
ScriptAlias /query /usr/local/viewvc-1.0/bin/cgi/query.cgi
----------------------------------------
METHOD B: CGI mode in cgi-bin directory
@@ -218,10 +201,6 @@ APACHE CONFIGURATION
Copy the CGI scripts from
<VIEWVC_INSTALLATION_DIRECTORY>/bin/cgi/*.cgi
to the /cgi-bin/ directory configured in your httpd.conf file.
You can override configuration file location using:
SetEnv VIEWVC_CONF_PATHNAME /etc/viewvc.conf
------------------------------------------
METHOD C: CGI mode in ExecCGI'd directory
@@ -231,8 +210,8 @@ APACHE CONFIGURATION
to the directory of your choosing in the Document Root adding the following
Apache directives for the directory in httpd.conf or an .htaccess file:
Options +ExecCGI
AddHandler cgi-script .cgi
Options +ExecCGI
AddHandler cgi-script .cgi
Note: For this to work mod_cgi has to be loaded. And for the .htaccess file
to be effective, "AllowOverride All" or "AllowOverride Options FileInfo"
@@ -248,10 +227,6 @@ APACHE CONFIGURATION
In httpd.conf, make sure that "AllowOverride All" or at least
"AllowOverride FileInfo Options" are enabled for the directory
you copied the files to.
You can override configuration file location using:
SetEnv VIEWVC_CONF_PATHNAME /etc/viewvc.conf
Note: If you are using Mod_Python under Apache 1.3 the tarball generation
feature may not work because it uses multithreading. This works fine
@@ -303,6 +278,13 @@ APACHE CONFIGURATION
ScriptAlias /viewvc /usr/local/viewvc/bin/wsgi/viewvc.fcgi
ScriptAlias /query /usr/local/viewvc/bin/wsgi/query.fcgi
3) Restart Apache.
The commands to do this vary. "httpd -k restart" and "apache -k
restart" are two common variants. On RedHat Linux it is done using
the command "/sbin/service httpd restart" and on SuSE Linux it is
done with "rcapache restart". Other systems use "apachectl restart".
4) [Optional] Add access control.
In your httpd.conf you can control access to certain modules by
@@ -324,14 +306,7 @@ APACHE CONFIGURATION
http://<server_name>/viewvc/~checkout~/<module_name>
http://<server_name>/viewvc/<module_name>.tar.gz?view=tar
5) Restart Apache.
The commands to do this vary. "httpd -k restart" and "apache -k
restart" are two common variants. On RedHat Linux it is done using
the command "/sbin/service httpd restart" and on SuSE Linux it is
done with "rcapache restart". Other systems use "apachectl restart".
6) Optional: Protect your ViewVC instance from server-whacking webcrawlers.
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

View File

@@ -15,7 +15,7 @@
<blockquote>
<p><strong>Copyright &copy; 1999-2013 The ViewCVS Group. All rights
<p><strong>Copyright &copy; 1999-2011 The ViewCVS Group. All rights
reserved.</strong></p>
<p>By using ViewVC, you agree to the terms and conditions set forth
@@ -62,8 +62,6 @@
<li>March 18, 2009 &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>
<li>January 04, 2013 &mdash; copyright years updated</li>
</ul>
</body>

View File

@@ -3,7 +3,7 @@
# -*-python-*-
#
# Copyright (C) 1999-2013 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
# the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -3,7 +3,7 @@
# -*-python-*-
#
# Copyright (C) 1999-2013 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
# 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
# -*-python-*-
#
# Copyright (C) 1999-2013 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
# 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
# -*-python-*-
#
# Copyright (C) 1999-2013 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
# 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
# -*-python-*-
#
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
#
# 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

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*-python-*-
#
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
#
# 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

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*-python-*-
#
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2009 The ViewCVS Group. All Rights Reserved.
#
# 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
@@ -40,7 +40,7 @@ CREATE TABLE branches (
branch varchar(64) binary DEFAULT '' NOT NULL,
PRIMARY KEY (id),
UNIQUE branch (branch)
) ENGINE=MyISAM;
) TYPE=MyISAM;
DROP TABLE IF EXISTS checkins;
CREATE TABLE checkins (
@@ -63,7 +63,7 @@ CREATE TABLE checkins (
KEY dirid (dirid),
KEY fileid (fileid),
KEY branchid (branchid)
) ENGINE=MyISAM;
) TYPE=MyISAM;
DROP TABLE IF EXISTS descs;
CREATE TABLE descs (
@@ -72,7 +72,7 @@ CREATE TABLE descs (
hash bigint(20) DEFAULT '0' NOT NULL,
PRIMARY KEY (id),
KEY hash (hash)
) ENGINE=MyISAM;
) TYPE=MyISAM;
DROP TABLE IF EXISTS dirs;
CREATE TABLE dirs (
@@ -80,7 +80,7 @@ CREATE TABLE dirs (
dir varchar(255) binary DEFAULT '' NOT NULL,
PRIMARY KEY (id),
UNIQUE dir (dir)
) ENGINE=MyISAM;
) TYPE=MyISAM;
DROP TABLE IF EXISTS files;
CREATE TABLE files (
@@ -88,7 +88,7 @@ CREATE TABLE files (
file varchar(255) binary DEFAULT '' NOT NULL,
PRIMARY KEY (id),
UNIQUE file (file)
) ENGINE=MyISAM;
) TYPE=MyISAM;
DROP TABLE IF EXISTS people;
CREATE TABLE people (
@@ -96,7 +96,7 @@ CREATE TABLE people (
who varchar(128) binary DEFAULT '' NOT NULL,
PRIMARY KEY (id),
UNIQUE who (who)
) ENGINE=MyISAM;
) TYPE=MyISAM;
DROP TABLE IF EXISTS repositories;
CREATE TABLE repositories (
@@ -104,7 +104,7 @@ CREATE TABLE repositories (
repository varchar(64) binary DEFAULT '' NOT NULL,
PRIMARY KEY (id),
UNIQUE repository (repository)
) ENGINE=MyISAM;
) TYPE=MyISAM;
DROP TABLE IF EXISTS tags;
CREATE TABLE tags (
@@ -118,7 +118,7 @@ CREATE TABLE tags (
KEY dirid (dirid),
KEY fileid (fileid),
KEY branchid (branchid)
) ENGINE=MyISAM;
) TYPE=MyISAM;
"""
## ------------------------------------------------------------------------
@@ -132,7 +132,7 @@ CREATE TABLE branches (
branch varchar(64) binary DEFAULT '' NOT NULL,
PRIMARY KEY (id),
UNIQUE branch (branch)
) ENGINE=MyISAM;
) TYPE=MyISAM;
DROP TABLE IF EXISTS commits;
CREATE TABLE commits (
@@ -156,7 +156,7 @@ CREATE TABLE commits (
KEY fileid (fileid),
KEY branchid (branchid),
KEY descid (descid)
) ENGINE=MyISAM;
) TYPE=MyISAM;
DROP TABLE IF EXISTS descs;
CREATE TABLE descs (
@@ -165,7 +165,7 @@ CREATE TABLE descs (
hash bigint(20) DEFAULT '0' NOT NULL,
PRIMARY KEY (id),
KEY hash (hash)
) ENGINE=MyISAM;
) TYPE=MyISAM;
DROP TABLE IF EXISTS dirs;
CREATE TABLE dirs (
@@ -173,7 +173,7 @@ CREATE TABLE dirs (
dir varchar(255) binary DEFAULT '' NOT NULL,
PRIMARY KEY (id),
UNIQUE dir (dir)
) ENGINE=MyISAM;
) TYPE=MyISAM;
DROP TABLE IF EXISTS files;
CREATE TABLE files (
@@ -181,7 +181,7 @@ CREATE TABLE files (
file varchar(255) binary DEFAULT '' NOT NULL,
PRIMARY KEY (id),
UNIQUE file (file)
) ENGINE=MyISAM;
) TYPE=MyISAM;
DROP TABLE IF EXISTS people;
CREATE TABLE people (
@@ -189,7 +189,7 @@ CREATE TABLE people (
who varchar(128) binary DEFAULT '' NOT NULL,
PRIMARY KEY (id),
UNIQUE who (who)
) ENGINE=MyISAM;
) TYPE=MyISAM;
DROP TABLE IF EXISTS repositories;
CREATE TABLE repositories (
@@ -197,7 +197,7 @@ CREATE TABLE repositories (
repository varchar(64) binary DEFAULT '' NOT NULL,
PRIMARY KEY (id),
UNIQUE repository (repository)
) ENGINE=MyISAM;
) TYPE=MyISAM;
DROP TABLE IF EXISTS tags;
CREATE TABLE tags (
@@ -211,7 +211,7 @@ CREATE TABLE tags (
KEY dirid (dirid),
KEY fileid (fileid),
KEY branchid (branchid)
) ENGINE=MyISAM;
) TYPE=MyISAM;
DROP TABLE IF EXISTS metadata;
CREATE TABLE metadata (
@@ -219,7 +219,7 @@ CREATE TABLE metadata (
value text,
PRIMARY KEY (name),
UNIQUE name (name)
) ENGINE=MyISAM;
) TYPE=MyISAM;
INSERT INTO metadata (name, value) VALUES ('version', '1');
"""
@@ -245,10 +245,6 @@ options.) This script will use the 'mysql' program to create the
database for you. You will then need to set the appropriate
parameters in the [cvsdb] section of your viewvc.conf file.
NOTE: If a hostname or port is supplied at the command line or during
interactive prompting, this script will pass '--protocol=TCP' to
'mysql'.
Options:
--dbname=ARG Use ARG as the ViewVC database name to create.
@@ -257,8 +253,7 @@ Options:
--help Show this usage message.
--hostname=ARG Use ARG as the hostname for the MySQL connection.
--port=ARG Use ARG as the port for the MySQL connection.
[Default: localhost]
--password=ARG Use ARG as the password for the MySQL connection.
@@ -278,11 +273,10 @@ Options:
if __name__ == "__main__":
try:
# Parse the command-line options, if any.
dbname = version = hostname = port = username = password = None
dbname = version = hostname = username = password = None
opts, args = getopt.getopt(sys.argv[1:], '', [ 'dbname=',
'help',
'hostname=',
'port=',
'password=',
'username=',
'version=',
@@ -296,8 +290,6 @@ if __name__ == "__main__":
dbname = value
elif name == '--hostname':
hostname = value
elif name == '--port':
port = value
elif name == '--username':
username = value
elif name == '--password':
@@ -310,9 +302,7 @@ if __name__ == "__main__":
# Prompt for information not provided via command-line options.
if hostname is None:
hostname = raw_input("MySQL Hostname (leave blank for default): ")
if port is None:
port = raw_input("MySQL Port (leave blank for default): ")
hostname = raw_input("MySQL Hostname [default: localhost]: ") or ""
if username is None:
username = raw_input("MySQL User: ")
if password is None:
@@ -320,7 +310,7 @@ if __name__ == "__main__":
if dbname is None:
dbname = raw_input("ViewVC Database Name [default: ViewVC]: ") or "ViewVC"
# Create the database.
# Create the database
dscript = string.replace(DATABASE_SCRIPT_COMMON, "<dbname>", dbname)
if version == "1.0":
print BONSAI_COMPAT
@@ -328,22 +318,16 @@ if __name__ == "__main__":
else:
dscript = dscript + DATABASE_SCRIPT_VERSION_1
# Calculate command arguments.
cmd_args = "--user=%s --password=%s" % (username, password)
if hostname or port:
cmd_args = cmd_args + " --protocol=TCP"
if hostname:
cmd_args = cmd_args + " --host=%s" % (hostname)
if port:
cmd_args = cmd_args + " --port=%s" % (port)
host_option = hostname and "--host=%s" % (hostname) or ""
if sys.platform == "win32":
cmd = "mysql %s" % (cmd_args)
cmd = "mysql --user=%s --password=%s %s "\
% (username, password, host_option)
mysql = os.popen(cmd, "w") # popen2.Popen3 is not provided on windows
mysql.write(dscript)
status = mysql.close()
else:
cmd = "{ mysql %s ; } 2>&1" % (cmd_args)
cmd = "{ mysql --user=%s --password=%s %s ; } 2>&1" \
% (username, password, host_option)
pipes = popen2.Popen3(cmd)
pipes.tochild.write(dscript)
pipes.tochild.close()

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2013 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
# the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2010 The ViewCVS Group. All Rights Reserved.
#
# 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

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
#
# 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

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*-python-*-
#
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2010 The ViewCVS Group. All Rights Reserved.
#
# 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

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*-python-*-
#
# Copyright (C) 2004-2013 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 2004-2008 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 2004-2007 James Henstridge
#
# By using this file, you agree to the terms and conditions set forth in
@@ -153,7 +153,7 @@ class SvnRev:
fsroot = self._get_root_for_rev(rev)
# find changes in the revision
editor = svn.repos.ChangeCollector(repo.fs, fsroot)
editor = svn.repos.RevisionChangeCollector(repo.fs, rev)
e_ptr, e_baton = svn.delta.make_editor(editor)
svn.repos.svn_repos_replay(fsroot, e_ptr, e_baton)
@@ -164,33 +164,21 @@ class SvnRev:
continue
# deal with the change types we handle
action = None
base_root = None
base_path = change.base_path
if change.base_path:
base_root = self._get_root_for_rev(change.base_rev)
# figure out what kind of change this is, and get a diff
# 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:
if not change.path:
action = 'remove'
elif change.added:
action = 'add'
else:
action = 'change'
if action == 'remove':
diffobj = svn.fs.FileDiff(base_root, base_path, None, None)
else:
diffobj = svn.fs.FileDiff(base_root, base_path,
fsroot, change.path)
diffobj = svn.fs.FileDiff(base_root and base_root or None,
base_root and change.base_path or None,
change.path and fsroot or None,
change.path and change.path or None)
diff_fp = diffobj.get_pipe()
plus, minus = _get_diff_counts(diff_fp)
self.changes.append((path, action, plus, minus))
@@ -300,7 +288,7 @@ def usage():
located at REPOS-PATH.
Usage: 1. %s [-v] rebuild REPOS-PATH
2. %s [-v] update REPOS-PATH [REV[:REV2]] [--force]
2. %s [-v] update REPOS-PATH [REV:[REV2]] [--force]
3. %s [-v] purge REPOS-PATH
1. Rebuild the commit database information for the repository located

View File

@@ -1,7 +1,7 @@
#!/usr/bin/python
# -*-python-*-
#
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2010 The ViewCVS Group. All Rights Reserved.
#
# 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

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2010 The ViewCVS Group. All Rights Reserved.
#
# 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

View File

@@ -1,7 +1,7 @@
#!/usr/bin/python
# -*-python-*-
#
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2010 The ViewCVS Group. All Rights Reserved.
#
# 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

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2009 The ViewCVS Group. All Rights Reserved.
#
# 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

View File

@@ -86,8 +86,8 @@
## cvs_roots: Specifies each of the CVS roots on your system and
## assigns names to them. Each root should be given by a "name: path"
## value (where the path is an absolute filesystem path). Multiple roots
## should be separated by commas and can be placed on separate lines.
## value. Multiple roots should be separated by commas and can be
## placed on separate lines.
##
## Example:
## cvs_roots = cvsroot: /opt/cvs/repos1,
@@ -97,13 +97,8 @@
## svn_roots: Specifies each of the Subversion roots (repositories) on
## your system and assigns names to them. Each root should be given by
## a "name: path" value (where the path is an absolute filesystem path).
## Multiple roots should be separated by commas and can be placed on
## separate lines.
##
## NOTE: ViewVC offers *experimental* support for displaying remote
## Subversion repositories. Simply use the repository's URL instead
## of a local filesystem path when defining the root.
## a "name: path" value. Multiple roots should be separated by commas
## and can be placed on separate lines.
##
## Example:
## svn_roots = svnrepos: /opt/svn/,
@@ -114,8 +109,7 @@
## root_parents: Specifies a list of directories under which any
## number of repositories may reside. You can specify multiple root
## parents separated by commas or new lines, each of which is of the
## form "path: type" (where the type is either "cvs" or "svn", and
## the path is an absolute filesystem path).
## form "path: type" (where type is either "cvs" or "svn").
##
## Rather than force you to add a new entry to 'cvs_roots' or
## 'svn_roots' each time you create a new repository, ViewVC rewards
@@ -351,64 +345,10 @@
## allowed_views: List the ViewVC views which are enabled. Views not
## in this comma-delited list will not be served (or, will return an
## error on attempted access).
##
## Valid items for this list include: "annotate", "co", "diff", "markup",
## "roots", "tar".
##
## ----------+---------------------------------------------------------
## VIEW | DESCRIPTION
## ----------+---------------------------------------------------------
## annotate | The 'annotate' view shows the contents of a single
## | revision of a versioned file in exactly the same way as
## | the markup view, but with additional line-by-line
## | change attribution (the revision number, author, etc.
## | the most recent edit to that line of text as of the
## | displayed version).
## ----------+---------------------------------------------------------
## co | The 'co' (aka "checkout" or "download") view isn't
## | really a branded view at all, but allows for direct
## | downloading of the contents of a single revision of a
## | versioned file.
## ----------+---------------------------------------------------------
## diff | The 'diff' view displays line-based differences between
## | two revisions of a versioned file in a variety of
## | different user-selectable formats.
## ----------+---------------------------------------------------------
## markup | The 'markup' view shows the contents of a single
## | revision of a versioned file, with syntax highlighting
## | where possible and enabled. It can also optionally
## | show change log information for that revision of the
## | file.
## ----------+---------------------------------------------------------
## roots | The 'roots' view is a simple listing of the various
## | repositories which ViewVC has been configured to serve
## | to users.
## ----------+---------------------------------------------------------
## tar | The 'tar' view isn't a branded view, but generates
## | a GNU Tar archive file containing a single versioned
## | directory and its contents (recursively).
## ----------+---------------------------------------------------------
## Possible values: "annotate", "co", "diff", "markup", "roots", "tar"
##
#allowed_views = annotate, diff, markup, roots
## Comma-delimited list of MIME content types (with support for fnmatch-
## style glob characters) which are considered not-human-readable and for
## which ViewVC will neither generate links to, nor support the direct
## display of, non-checkout views which carry the file's content (the
## 'markup', 'annotate', 'diff', and 'patch' views).
##
## NOTE: Handling of this option is given priority over ViewVC's
## longstanding support for showing web-friendly file formats -- even
## binary ones such as "image/jpeg" and "image/gif" -- in the 'markup'
## view. Thus, if you add "image/*" to this list, 'markup'-view
## display of JPEG, GIF, and PNG images will be disabled.
##
## Example:
## binary_mime_types = application/octet-stream, image/*, application/pdf,
## application/vnd*, application/msword, audio/*
#
#binary_mime_types =
## authorizer: The name of the ViewVC authorizer plugin to use when
## authorizing access to repository contents. This value must be the
## name of a Python module addressable as vcauth.MODULENAME (most
@@ -451,26 +391,6 @@
##
#mangle_email_addresses = 0
## custom_log_formatting: Specifies mappings of regular expressions to
## substitution format strings used to URL-ize strings found in
## revision log messages. Multiple mappings (specified as
## REGEXP:FORMATSTRING) may be defined, separated by commas.
##
## NOTE: Due to a limitation of the configuration format, commas may
## not be used in the regular expression portion of each mapping.
## Commas "in the raw" may not be used in the format string portion,
## either, but you can probably use the URI-encoded form of the comma
## ("%2C") instead with no ill side-effects. If you must specify a
## colon character in either the regular expression or the format
## string, escape it with a preceding backslash ("\:").
##
## Example:
## custom_log_formatting =
## artf[0-9]+ : http://example.com/tracker?id=\0,
## issue ([0-9]+) : http://example.com/bug?id=\1&opts=full%2csortby=id
##
#custom_log_formatting =
## default_file_view: "log", "co", or "markup"
## Controls whether the default view for file URLs is a checkout view or
## a log view. "log" is the default for backwards compatibility with old
@@ -579,7 +499,7 @@
## (Only works well for C source files, otherwise diff's heuristic falls short.)
## ('-p' option to diff)
##
#hr_funout = 1
#hr_funout = 0
## hr_ignore_white: Ignore whitespace (indendation and stuff) for human
## readable diffs.
@@ -683,13 +603,7 @@
#short_log_len = 80
## enable_syntax_coloration: Should we colorize known file content
## syntaxes?
##
## NOTE: This feature requires the Pygments Python module
## (http://pygments.org) and works only when ViewVC can determine the
## MIME content type of the file whose contents it wishes to colorize.
## Use the 'mime_types_files' configuration option to specify MIME
## type mapping files useful for making that determination.
## syntaxes? [Requires Pygments Python module]
##
#enable_syntax_coloration = 1

View File

@@ -1,6 +1,6 @@
<html>
<head>
<title>ViewVC 1.1 Template Authoring Guide</title>
<title>ViewVC 1.0 Template Authoring Guide</title>
<style>
body {
background-color: rgb(180,193,205);
@@ -38,13 +38,13 @@ td {
</head>
<body>
<h1>ViewVC 1.1 Template Authoring Guide</h1>
<h1>ViewVC 1.0 Template Authoring Guide</h1>
<div class="h2">
<h2 id="introduction">Introduction</h2>
<p>This document represents an (unfinished) attempt at providing
instructions for how to customize ViewVC's HTML output via
documentation for how to customize ViewVC 1.0-dev's HTML output via
modification of its templates.</p>
</div>
@@ -802,11 +802,6 @@ td {
<td>String</td>
<td>This is a URL for the markup view of the left file.</td>
</tr>
<tr class="varlevel1">
<td class="varname">patch_href</td>
<td>String</td>
<td>URL of the patch view for the file.</td>
</tr>
<tr class="varlevel1">
<td class="varname">raw_diff</td>
<td>String</td>
@@ -2183,12 +2178,6 @@ td {
<td>String</td>
<td>Log message of last modification to the root.</td>
</tr>
<tr class="varlevel2">
<td class="varname">roots.log_href</td>
<td>String</td>
<td>URL of log revision view for the top-most (root) directory of
the root (repository).</td>
</tr>
<tr class="varlevel2">
<td class="varname">roots.name</td>
<td>String</td>

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2009 The ViewCVS Group. All Rights Reserved.
#
# 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

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*-python-*-
#
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2010 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 2000 Curt Hagenlocher <curt@hagenlocher.org>
#
# By using this file, you agree to the terms and conditions set forth in
@@ -74,8 +74,7 @@ class HTMLBlameSource:
self.path_parts = path_parts
self.diff_url = diff_url
self.include_url = include_url
self.annotation, self.revision = self.repos.annotate(path_parts, opt_rev,
True)
self.annotation, self.revision = self.repos.annotate(path_parts, opt_rev)
def __getitem__(self, idx):
item = self.annotation.__getitem__(idx)

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2007 The ViewCVS Group. All Rights Reserved.
#
# 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

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2011 The ViewCVS Group. All Rights Reserved.
#
# 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
@@ -112,8 +112,6 @@ class Config:
_force_multi_value = (
# Configuration values with multiple, comma-separated values.
'allowed_views',
'binary_mime_types',
'custom_log_formatting',
'cvs_roots',
'kv_files',
'languages',
@@ -400,9 +398,7 @@ class Config:
self.options.allowed_views = ['annotate', 'diff', 'markup', 'roots']
self.options.authorizer = None
self.options.mangle_email_addresses = 0
self.options.custom_log_formatting = []
self.options.default_file_view = "log"
self.options.binary_mime_types = []
self.options.http_expiration_time = 600
self.options.generate_etags = 1
self.options.svn_ignore_mimetype = 0

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2011 The ViewCVS Group. All Rights Reserved.
#
# 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
@@ -352,17 +352,9 @@ class CheckinDatabase:
match = " LIKE "
elif query_entry.match == "glob":
match = " REGEXP "
# Use fnmatch to translate the glob into a regular
# expression. Sadly, we have to account for the fact
# that in Python 2.6, fnmatch.translate() started
# sticking '\Z(?ms)' at the end of the regular
# expression instead of just '$', and doesn't prepend
# the '^'.
# use fnmatch to translate the glob into a regexp
data = fnmatch.translate(data)
if data[0] != '^':
data = '^' + data
if data[-7:] == '\Z(?ms)':
data = data[:-7] + '$'
if data[0] != '^': data = '^' + data
elif query_entry.match == "regex":
match = " REGEXP "
elif query_entry.match == "notregex":
@@ -520,10 +512,7 @@ class CheckinDatabase:
if file_id == None:
return None
sql = "SELECT type, ci_when, whoid, repositoryid, dirid, fileid, " \
"revision, stickytag, branchid, addedlines, removedlines, " \
"descid "\
" FROM %s WHERE "\
sql = "SELECT * FROM %s WHERE "\
" repositoryid=%%s "\
" AND dirid=%%s"\
" AND fileid=%%s"\
@@ -573,10 +562,7 @@ class CheckinDatabase:
self.sql_purge('descs', 'id', 'descid', 'commits')
self.sql_purge('people', 'id', 'whoid', 'commits')
else:
sql = "SELECT type, ci_when, whoid, repositoryid, dirid, " \
"fileid, revision, stickytag, branchid, addedlines, " \
"removedlines, descid "\
" FROM checkins WHERE repositoryid=%s"
sql = "SELECT * FROM checkins WHERE repositoryid=%s"
sql_args = (rep_id, )
cursor = self.db.cursor()
cursor.execute(sql, sql_args)

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2013 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
# the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
#
# 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

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2010 The ViewCVS Group. All Rights Reserved.
#
# 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

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2009 The ViewCVS Group. All Rights Reserved.
#
# 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

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*-python-*-
#
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2010 The ViewCVS Group. All Rights Reserved.
#
# 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

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2010 The ViewCVS Group. All Rights Reserved.
#
# 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
@@ -233,6 +233,9 @@ class WsgiServer(Server):
self.header(status='301 Moved')
self._wsgi_write('This document is located <a href="%s">here</a>.' % url)
def escape(self, s, quote = None):
return cgi.escape(s, quote)
def getenv(self, name, value=None):
return self._environ.get(name, value)

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 2006-2013 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 2006-2010 The ViewCVS Group. All Rights Reserved.
#
# 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

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 2006-2013 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 2006-2010 The ViewCVS Group. All Rights Reserved.
#
# 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

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 2008-2013 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 2008-2010 The ViewCVS Group. All Rights Reserved.
#
# 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

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 2006-2013 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 2006-2011 The ViewCVS Group. All Rights Reserved.
#
# 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
@@ -34,9 +34,9 @@ class ViewVCAuthorizer(vcauth.GenericViewVCAuthorizer):
# See if the admin wants us to do case normalization of usernames.
self.force_username_case = params.get('force_username_case')
if self.force_username_case == "upper":
self.username = username and string.upper(username) or username
self.username = username.upper()
elif self.force_username_case == "lower":
self.username = username and string.lower(username) or username
self.username = username.lower()
elif not self.force_username_case:
self.username = username
else:

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2011 The ViewCVS Group. All Rights Reserved.
#
# 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
@@ -170,19 +170,12 @@ class Repository:
Return value is a python file object
"""
def annotate(self, path_parts, rev, include_text=False):
"""Return a list of Annotation object, sorted by their
"line_number" components, which describe the lines of given
version of a file.
def annotate(self, path_parts, rev):
"""Return a list of annotate file content lines and a revision.
The file path is specified as a list of components, relative to
the root of the repository. e.g. ["subdir1", "subdir2", "filename"]
rev is the revision of the item to return information about.
If include_text is true, populate the Annotation objects' "text"
members with the corresponding line of file content; otherwise,
leave that member set to None."""
The result is a list of Annotation objects, sorted by their
line_number components.
"""
def revinfo(self, rev):
"""Return information about a global revision

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
#
# 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
@@ -14,7 +14,6 @@ import os.path
def canonicalize_rootpath(rootpath):
assert os.path.isabs(rootpath)
return os.path.normpath(rootpath)
@@ -23,7 +22,6 @@ def expand_root_parent(parent_path):
# "CVSROOT/config" is added the set of returned roots. Or, if the
# PARENT_PATH itself contains a child "CVSROOT/config", then all its
# subdirectories are returned as roots.
assert os.path.isabs(parent_path)
roots = {}
subpaths = os.listdir(parent_path)
cvsroot = os.path.exists(os.path.join(parent_path, "CVSROOT", "config"))

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2011 The ViewCVS Group. All Rights Reserved.
#
# 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
@@ -332,13 +332,13 @@ class BinCVSRepository(BaseCVSRepository):
args = rcs_args
return popen.popen(cmd, args, mode, capture_err)
def annotate(self, path_parts, rev=None, include_text=False):
def annotate(self, path_parts, rev=None):
if self.itemtype(path_parts, rev) != vclib.FILE: # does auth-check
raise vclib.Error("Path '%s' is not a file."
% (string.join(path_parts, "/")))
from vclib.ccvs import blame
source = blame.BlameSource(self.rcsfile(path_parts, 1), rev, include_text)
source = blame.BlameSource(self.rcsfile(path_parts, 1), rev)
return source, source.revision
def revinfo(self, rev):

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*-python-*-
#
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2011 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 2000 Curt Hagenlocher <curt@hagenlocher.org>
#
# By using this file, you agree to the terms and conditions set forth in
@@ -414,7 +414,7 @@ class CVSParser(rcsparse.Sink):
class BlameSource:
def __init__(self, rcs_file, opt_rev=None, include_text=False):
def __init__(self, rcs_file, opt_rev=None):
# Parse the CVS file
parser = CVSParser()
revision = parser.parse_cvs_file(rcs_file, opt_rev)
@@ -428,7 +428,6 @@ class BlameSource:
self.lines = lines
self.num_lines = count
self.parser = parser
self.include_text = include_text
# keep track of where we are during an iteration
self.idx = -1
@@ -448,8 +447,6 @@ class BlameSource:
line_number = idx + 1
author = self.parser.revision_author[rev]
thisline = self.lines[idx]
if not self.include_text:
thisline = None
### TODO: Put a real date in here.
item = vclib.Annotation(thisline, line_number, rev, prev_rev, author, None)
self.last = item

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2011 The ViewCVS Group. All Rights Reserved.
#
# 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
@@ -142,11 +142,11 @@ class CCVSRepository(BaseCVSRepository):
return vclib._diff_fp(temp1, temp2, info1, info2,
self.utilities.diff or 'diff', diff_args)
def annotate(self, path_parts, rev=None, include_text=False):
def annotate(self, path_parts, rev=None):
if self.itemtype(path_parts, rev) != vclib.FILE: # does auth-check
raise vclib.Error("Path '%s' is not a file."
% (string.join(path_parts, "/")))
source = blame.BlameSource(self.rcsfile(path_parts, 1), rev, include_text)
source = blame.BlameSource(self.rcsfile(path_parts, 1), rev)
return source, source.revision
def revinfo(self, rev):
@@ -200,7 +200,6 @@ class InfoSink(MatchingSink):
self.matching_rev = None
self.perfect_match = 0
self.lockinfo = { }
self.saw_revision = False
def define_tag(self, name, revision):
MatchingSink.define_tag(self, name, revision)
@@ -214,17 +213,10 @@ class InfoSink(MatchingSink):
self.entry.absent = 1
raise rcsparse.RCSStopParser
def parse_completed(self):
if not self.saw_revision:
#self.entry.errors.append("No revisions exist on %s" % (view_tag or "MAIN"))
self.entry.absent = 1
def set_locker(self, rev, locker):
self.lockinfo[rev] = locker
def define_revision(self, revision, date, author, state, branches, next):
self.saw_revision = True
if self.perfect_match:
return
@@ -232,18 +224,14 @@ class InfoSink(MatchingSink):
rev = Revision(revision, date, author, state == "dead")
rev.lockinfo = self.lockinfo.get(revision)
# perfect match if revision number matches tag number or if
# revision is on trunk and tag points to trunk. imperfect match
# if tag refers to a branch and either a) this revision is the
# highest revision so far found on that branch, or b) this
# revision is the branchpoint.
# perfect match if revision number matches tag number or if revision is on
# trunk and tag points to trunk. imperfect match if tag refers to a branch
# and this revision is the highest revision so far found on that branch
perfect = ((rev.number == tag.number) or
(not tag.number and len(rev.number) == 2))
if perfect or (tag.is_branch and \
((tag.number == rev.number[:-1] and
(not self.matching_rev or
rev.number > self.matching_rev.number)) or
(rev.number == tag.number[:-1]))):
if perfect or (tag.is_branch and tag.number == rev.number[:-1] and
(not self.matching_rev or
rev.number > self.matching_rev.number)):
self.matching_rev = rev
self.perfect_match = perfect

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2013 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
# the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2011 The ViewCVS Group. All Rights Reserved.
#
# 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

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2013 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
# the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
#
# 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

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
#
# 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

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2011 The ViewCVS Group. All Rights Reserved.
#
# 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
@@ -19,46 +19,26 @@ import urllib
_re_url = re.compile('^(http|https|file|svn|svn\+[^:]+)://')
def _canonicalize_path(path):
import svn.core
try:
return svn.core.svn_path_canonicalize(path)
except AttributeError: # svn_path_canonicalize() appeared in 1.4.0 bindings
# There's so much more that we *could* do here, but if we're
# here at all its because there's a really old Subversion in
# place, and those older Subversion versions cared quite a bit
# less about the specifics of path canonicalization.
if re.search(_re_url, path):
return path.rstrip('/')
else:
return os.path.normpath(path)
def canonicalize_rootpath(rootpath):
# Try to canonicalize the rootpath using Subversion semantics.
rootpath = _canonicalize_path(rootpath)
# ViewVC's support for local repositories is more complete and more
# performant than its support for remote ones, so if we're on a
# Unix-y system and we have a file:/// URL, convert it to a local
# path instead.
if os.name == 'posix':
rootpath_lower = rootpath.lower()
if rootpath_lower in ['file://localhost',
'file://localhost/',
'file://',
'file:///'
]:
return '/'
if rootpath_lower.startswith('file://localhost/'):
rootpath = os.path.normpath(urllib.unquote(rootpath[16:]))
elif rootpath_lower.startswith('file:///'):
rootpath = os.path.normpath(urllib.unquote(rootpath[7:]))
# Ensure that we have an absolute path (or URL), and return.
if not re.search(_re_url, rootpath):
assert os.path.isabs(rootpath)
return rootpath
try:
import svn.core
return svn.core.svn_path_canonicalize(rootpath)
except:
if os.name == 'posix':
rootpath_lower = rootpath.lower()
if rootpath_lower in ['file://localhost',
'file://localhost/',
'file://',
'file:///'
]:
return '/'
if rootpath_lower.startswith('file://localhost/'):
return os.path.normpath(urllib.unquote(rootpath[16:]))
elif rootpath_lower.startswith('file:///'):
return os.path.normpath(urllib.unquote(rootpath[7:]))
if re.search(_re_url, rootpath):
return rootpath.rstrip('/')
return os.path.normpath(rootpath)
def expand_root_parent(parent_path):
@@ -68,7 +48,6 @@ def expand_root_parent(parent_path):
else:
# Any subdirectories of PARENT_PATH which themselves have a child
# "format" are returned as roots.
assert os.path.isabs(parent_path)
subpaths = os.listdir(parent_path)
for rootname in subpaths:
rootpath = os.path.join(parent_path, rootname)

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2011 The ViewCVS Group. All Rights Reserved.
#
# 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
@@ -20,10 +20,7 @@ import re
import tempfile
import time
import urllib
from svn_repos import Revision, SVNChangedPath, _datestr_to_date, \
_compare_paths, _path_parts, _cleanup_path, \
_rev2optrev, _fix_subversion_exception, \
_split_revprops, _canonicalize_path
from svn_repos import Revision, SVNChangedPath, _datestr_to_date, _compare_paths, _path_parts, _cleanup_path, _rev2optrev, _fix_subversion_exception, _split_revprops
from svn import core, delta, client, wc, ra
@@ -54,14 +51,12 @@ def get_directory_props(ra_session, path, rev):
props = ra.svn_ra_get_dir(ra_session, path, rev)
return props
def client_log(url, start_rev, end_rev, log_limit, include_changes,
cross_copies, cb_func, ctx):
include_changes = include_changes and 1 or 0
cross_copies = cross_copies and 1 or 0
def client_log(url, start_rev, end_rev, log_limit, cross_copies,
cb_func, ctx):
try:
client.svn_client_log4([url], start_rev, start_rev, end_rev,
log_limit, include_changes, not cross_copies,
0, None, cb_func, ctx)
log_limit, 1, not cross_copies, 0, None,
cb_func, ctx)
except AttributeError:
# Wrap old svn_log_message_receiver_t interface with a
# svn_log_entry_t one.
@@ -77,14 +72,15 @@ def client_log(url, start_rev, end_rev, log_limit, include_changes,
}
cb_func(log_entry, pool)
client.svn_client_log2([url], start_rev, end_rev, log_limit,
include_changes, not cross_copies, cb_convert, ctx)
1, not cross_copies, cb_convert, ctx)
### END COMPATABILITY CODE ###
class LogCollector:
### TODO: Make this thing authz-aware
def __init__(self, path, show_all_logs, lockinfo, access_check_func):
def __init__(self, path, show_all_logs, lockinfo):
# This class uses leading slashes for paths internally
if not path:
self.path = '/'
@@ -93,12 +89,8 @@ class LogCollector:
self.logs = []
self.show_all_logs = show_all_logs
self.lockinfo = lockinfo
self.access_check_func = access_check_func
self.done = False
def add_log(self, log_entry, pool):
if self.done:
return
paths = log_entry.changed_paths
revision = log_entry.revision
msg, author, date, revprops = _split_revprops(log_entry.revprops)
@@ -122,13 +114,9 @@ class LogCollector:
if change.copyfrom_path:
this_path = change.copyfrom_path + self.path[len(changed_path):]
if self.show_all_logs or this_path:
if self.access_check_func is None \
or self.access_check_func(self.path[1:], revision):
entry = Revision(revision, date, author, msg, None, self.lockinfo,
self.path[1:], None, None)
self.logs.append(entry)
else:
self.done = True
entry = Revision(revision, date, author, msg, None, self.lockinfo,
self.path[1:], None, None)
self.logs.append(entry)
if this_path:
self.path = this_path
@@ -264,15 +252,14 @@ class RemoteSubversionRepository(vclib.Repository):
### rev here should be the last history revision of the URL
client.svn_client_cat(core.Stream(stream), url, _rev2optrev(rev), self.ctx)
core.svn_stream_close(stream)
lh_rev, c_rev = self._get_last_history_rev(path_parts, rev)
return SelfCleanFP(tmp_file), lh_rev
return SelfCleanFP(tmp_file), self._get_last_history_rev(path_parts, rev)
def listdir(self, path_parts, rev, options):
path = self._getpath(path_parts)
if self.itemtype(path_parts, rev) != vclib.DIR: # does auth-check
raise vclib.Error("Path '%s' is not a directory." % path)
rev = self._getrev(rev)
entries = []
entries = [ ]
dirents, locks = self._get_dirents(path, rev)
for name in dirents.keys():
entry = dirents[name]
@@ -280,9 +267,8 @@ class RemoteSubversionRepository(vclib.Repository):
kind = vclib.DIR
elif entry.kind == core.svn_node_file:
kind = vclib.FILE
else:
kind = None
entries.append(vclib.DirEntry(name, kind))
if vclib.check_path_access(self, path_parts + [name], kind, rev):
entries.append(vclib.DirEntry(name, kind))
return entries
def dirlogs(self, path_parts, rev, entries, options):
@@ -293,13 +279,11 @@ class RemoteSubversionRepository(vclib.Repository):
dirents, locks = self._get_dirents(path, rev)
for entry in entries:
entry_path_parts = path_parts + [entry.name]
dirent = dirents.get(entry.name, None)
# dirents is authz-sanitized, so ensure the entry is found therein.
if dirent is None:
if not vclib.check_path_access(self, entry_path_parts, entry.kind, rev):
continue
# Get authz-sanitized revision metadata.
dirent = dirents[entry.name]
entry.date, entry.author, entry.log, revprops, changes = \
self._revinfo(dirent.created_rev)
self.revinfo(dirent.created_rev)
entry.rev = str(dirent.created_rev)
entry.size = dirent.size
entry.lockinfo = None
@@ -313,51 +297,28 @@ class RemoteSubversionRepository(vclib.Repository):
rev = self._getrev(rev)
url = self._geturl(path)
# If this is a file, fetch the lock status and size (as of REV)
# for this item.
lockinfo = size_in_rev = None
if path_type == vclib.FILE:
basename = path_parts[-1]
list_url = self._geturl(self._getpath(path_parts[:-1]))
dirents, locks = list_directory(list_url, _rev2optrev(rev),
_rev2optrev(rev), 0, self.ctx)
if locks.has_key(basename):
lockinfo = locks[basename].owner
if dirents.has_key(basename):
size_in_rev = dirents[basename].size
# Special handling for the 'svn_latest_log' scenario.
### FIXME: Don't like this hack. We should just introduce
### something more direct in the vclib API.
if options.get('svn_latest_log', 0):
dir_lh_rev, dir_c_rev = self._get_last_history_rev(path_parts, rev)
date, author, log, revprops, changes = self._revinfo(dir_lh_rev)
return [vclib.Revision(dir_lh_rev, str(dir_lh_rev), date, author,
None, log, size_in_rev, lockinfo)]
# Use ls3 to fetch the lock status for this item.
lockinfo = None
basename = path_parts and path_parts[-1] or ""
dirents, locks = list_directory(url, _rev2optrev(rev),
_rev2optrev(rev), 0, self.ctx)
if locks.has_key(basename):
lockinfo = locks[basename].owner
def _access_checker(check_path, check_rev):
return vclib.check_path_access(self, _path_parts(check_path),
path_type, check_rev)
# It's okay if we're told to not show all logs on a file -- all
# the revisions should match correctly anyway.
lc = LogCollector(path, options.get('svn_show_all_dir_logs', 0),
lockinfo, _access_checker)
lc = LogCollector(path, options.get('svn_show_all_dir_logs', 0), lockinfo)
cross_copies = options.get('svn_cross_copies', 0)
log_limit = 0
if limit:
log_limit = first + limit
client_log(url, _rev2optrev(rev), _rev2optrev(1), log_limit, 1,
client_log(url, _rev2optrev(rev), _rev2optrev(1), log_limit,
cross_copies, lc.add_log, self.ctx)
revs = lc.logs
revs.sort()
prev = None
for rev in revs:
# Swap out revision info with stuff from the cache (which is
# authz-sanitized).
rev.date, rev.author, rev.log, revprops, changes \
= self._revinfo(rev.number)
rev.prev = prev
prev = rev
revs.reverse()
@@ -377,25 +338,13 @@ class RemoteSubversionRepository(vclib.Repository):
_rev2optrev(rev), 0, self.ctx)
return pairs and pairs[0][1] or {}
def annotate(self, path_parts, rev, include_text=False):
def annotate(self, path_parts, rev):
path = self._getpath(path_parts)
if self.itemtype(path_parts, rev) != vclib.FILE: # does auth-check
raise vclib.Error("Path '%s' is not a file." % path)
rev = self._getrev(rev)
url = self._geturl(path)
# Examine logs for the file to determine the oldest revision we are
# permitted to see.
log_options = {
'svn_cross_copies' : 1,
'svn_show_all_dir_logs' : 1,
}
revs = self.itemlog(path_parts, rev, vclib.SORTBY_REV, 0, 0, log_options)
oldest_rev = revs[-1].number
# Now calculate the annotation data. Note that we'll not
# inherently trust the provided author and date, because authz
# rules might necessitate that we strip that information out.
blame_data = []
def _blame_cb(line_no, revision, author, date,
@@ -403,27 +352,21 @@ class RemoteSubversionRepository(vclib.Repository):
prev_rev = None
if revision > 1:
prev_rev = revision - 1
# If we have an invalid revision, clear the date and author
# values. Otherwise, if we have authz filtering to do, use the
# revinfo cache to do so.
if revision < 0:
date = author = None
elif self.auth:
date, author, msg, revprops, changes = self._revinfo(revision)
# Strip text if the caller doesn't want it.
if not include_text:
line = None
blame_data.append(vclib.Annotation(line, line_no + 1, revision, prev_rev,
author, date))
blame_data.append(vclib.Annotation(line, line_no+1, revision, prev_rev,
author, None))
client.blame2(url, _rev2optrev(rev), _rev2optrev(oldest_rev),
_rev2optrev(rev), _blame_cb, self.ctx)
client.svn_client_blame(url, _rev2optrev(1), _rev2optrev(rev),
_blame_cb, self.ctx)
return blame_data, rev
def revinfo(self, rev):
return self._revinfo(rev, 1)
rev = self._getrev(rev)
cached_info = self._revinfo_cache.get(rev)
if not cached_info:
cached_info = self._revinfo_raw(rev)
self._revinfo_cache[rev] = cached_info
return tuple(cached_info)
def rawdiff(self, path_parts1, rev1, path_parts2, rev2, type, options={}):
p1 = self._getpath(path_parts1)
@@ -438,7 +381,7 @@ class RemoteSubversionRepository(vclib.Repository):
args = vclib._diff_args(type, options)
def _date_from_rev(rev):
date, author, msg, revprops, changes = self._revinfo(rev)
date, author, msg, revprops, changes = self.revinfo(rev)
return date
try:
@@ -464,11 +407,8 @@ class RemoteSubversionRepository(vclib.Repository):
if rev is None or rev == 'HEAD':
return self.youngest
try:
if type(rev) == type(''):
while rev[0] == 'r':
rev = rev[1:]
rev = int(rev)
except:
except ValueError:
raise vclib.InvalidRevision(rev)
if (rev < 0) or (rev > self.youngest):
raise vclib.InvalidRevision(rev)
@@ -477,99 +417,42 @@ class RemoteSubversionRepository(vclib.Repository):
def _geturl(self, path=None):
if not path:
return self.rootpath
path = self.rootpath + '/' + urllib.quote(path)
return _canonicalize_path(path)
return self.rootpath + '/' + urllib.quote(path, "/*~")
def _get_dirents(self, path, rev):
"""Return a 2-type of dirents and locks, possibly reading/writing
from a local cache of that information. This functions performs
authz checks, stripping out unreadable dirents."""
from a local cache of that information."""
dir_url = self._geturl(path)
path_parts = _path_parts(path)
if path:
key = str(rev) + '/' + path
else:
key = str(rev)
# Ensure that the cache gets filled...
dirents_locks = self._dirent_cache.get(key)
if not dirents_locks:
tmp_dirents, locks = list_directory(dir_url, _rev2optrev(rev),
_rev2optrev(rev), 0, self.ctx)
dirents = {}
for name, dirent in tmp_dirents.items():
dirent_parts = path_parts + [name]
kind = dirent.kind
if (kind == core.svn_node_dir or kind == core.svn_node_file) \
and vclib.check_path_access(self, dirent_parts,
kind == core.svn_node_dir \
and vclib.DIR or vclib.FILE, rev):
lh_rev, c_rev = self._get_last_history_rev(dirent_parts, rev)
dirent.created_rev = lh_rev
dirents[name] = dirent
dirents, locks = list_directory(dir_url, _rev2optrev(rev),
_rev2optrev(rev), 0, self.ctx)
dirents_locks = [dirents, locks]
self._dirent_cache[key] = dirents_locks
# ...then return the goodies from the cache.
return dirents_locks[0], dirents_locks[1]
def _get_last_history_rev(self, path_parts, rev):
"""Return the a 2-tuple which contains:
- the last interesting revision equal to or older than REV in
the history of PATH_PARTS.
- the created_rev of of PATH_PARTS as of REV."""
path = self._getpath(path_parts)
url = self._geturl(self._getpath(path_parts))
optrev = _rev2optrev(rev)
# Get the last-changed-rev.
revisions = []
def _info_cb(path, info, pool, retval=revisions):
revisions.append(info.last_changed_rev)
client.svn_client_info(url, optrev, optrev, _info_cb, 0, self.ctx)
last_changed_rev = revisions[0]
# Now, this object might not have been directly edited since the
# last-changed-rev, but it might have been the child of a copy.
# To determine this, we'll run a potentially no-op log between
# LAST_CHANGED_REV and REV.
lc = LogCollector(path, 1, None, None)
client_log(url, optrev, _rev2optrev(last_changed_rev), 1, 1, 0,
lc.add_log, self.ctx)
revs = lc.logs
if revs:
revs.sort()
return revs[0].number, last_changed_rev
else:
return last_changed_rev, last_changed_rev
return revisions[0]
def _revinfo_fetch(self, rev, include_changed_paths=0):
need_changes = include_changed_paths or self.auth
def _revinfo_raw(self, rev):
# return 5-tuple (date, author, msg, revprops, changes)
optrev = _rev2optrev(rev)
revs = []
def _log_cb(log_entry, pool, retval=revs):
# If Subversion happens to call us more than once, we choose not
# to care.
if retval:
return
revision = log_entry.revision
msg, author, date, revprops = _split_revprops(log_entry.revprops)
action_map = { 'D' : vclib.DELETED,
'A' : vclib.ADDED,
'R' : vclib.REPLACED,
'M' : vclib.MODIFIED,
}
# Easy out: if we won't use the changed-path info, just return a
# changes-less tuple.
if not need_changes:
return revs.append([date, author, msg, revprops, None])
# Subversion 1.5 and earlier didn't offer the 'changed_paths2'
# hash, and in Subversion 1.6, it's offered but broken.
### Subversion 1.5 and earlier didn't offer the 'changed_paths2'
### hash, and in Subversion 1.6, it's offered but broken.
try:
changed_paths = log_entry.changed_paths2
paths = (changed_paths or {}).keys()
@@ -577,27 +460,29 @@ class RemoteSubversionRepository(vclib.Repository):
changed_paths = log_entry.changed_paths
paths = (changed_paths or {}).keys()
paths.sort(lambda a, b: _compare_paths(a, b))
# If we get this far, our caller needs changed-paths, or we need
# them for authz-related sanitization.
revision = log_entry.revision
msg, author, date, revprops = _split_revprops(log_entry.revprops)
action_map = { 'D' : vclib.DELETED,
'A' : vclib.ADDED,
'R' : vclib.REPLACED,
'M' : vclib.MODIFIED,
}
changes = []
found_readable = found_unreadable = 0
for path in paths:
change = changed_paths[path]
# svn_log_changed_path_t (which we might get instead of the
# svn_log_changed_path2_t we'd prefer) doesn't have the
# 'node_kind' member.
### svn_log_changed_path_t (which we might get instead of the
### svn_log_changed_path2_t we'd prefer) doesn't have the
### 'node_kind' member.
pathtype = None
if hasattr(change, 'node_kind'):
if change.node_kind == core.svn_node_dir:
pathtype = vclib.DIR
elif change.node_kind == core.svn_node_file:
pathtype = vclib.FILE
# svn_log_changed_path2_t only has the 'text_modified' and
# 'props_modified' bits in Subversion 1.7 and beyond. And
# svn_log_changed_path_t is without.
### svn_log_changed_path2_t only has the 'text_modified' and
### 'props_modified' bits in Subversion 1.7 and beyond. And
### svn_log_changed_path_t is without.
text_modified = props_modified = 0
if hasattr(change, 'text_modified'):
if change.text_modified == core.svn_tristate_true:
@@ -605,10 +490,9 @@ class RemoteSubversionRepository(vclib.Repository):
if hasattr(change, 'props_modified'):
if change.props_modified == core.svn_tristate_true:
props_modified = 1
# Wrong, diddily wrong wrong wrong. Can you say,
# "Manufacturing data left and right because it hurts to
# figure out the right stuff?"
### Wrong, diddily wrong wrong wrong. Can you say,
### "Manufacturing data left and right because it hurts to
### figure out the right stuff?"
action = action_map.get(change.action, vclib.MODIFIED)
if change.copyfrom_path and change.copyfrom_rev:
is_copy = 1
@@ -622,16 +506,15 @@ class RemoteSubversionRepository(vclib.Repository):
base_path = path
base_rev = revision - 1
# Check authz rules (sadly, we have to lie about the path type)
### Check authz rules (we lie about the path type)
parts = _path_parts(path)
if vclib.check_path_access(self, parts, vclib.FILE, revision):
if is_copy and base_path and (base_path != path):
parts = _path_parts(base_path)
if not vclib.check_path_access(self, parts, vclib.FILE, base_rev):
if vclib.check_path_access(self, parts, vclib.FILE, base_rev):
is_copy = 0
base_path = None
base_rev = None
found_unreadable = 1
changes.append(SVNChangedPath(path, revision, pathtype, base_path,
base_rev, action, is_copy,
text_modified, props_modified))
@@ -639,45 +522,16 @@ class RemoteSubversionRepository(vclib.Repository):
else:
found_unreadable = 1
# If our caller doesn't want changed-path stuff, and we have
# the info we need to make an authz determination already,
# quit this loop and get on with it.
if (not include_changed_paths) and found_unreadable and found_readable:
break
# Filter unreadable information.
if found_unreadable:
msg = None
if not found_readable:
author = None
date = None
revs.append([date, author, msg, revprops, changes])
# Drop unrequested changes.
if not include_changed_paths:
changes = None
# Add this revision information to the "return" array.
retval.append([date, author, msg, revprops, changes])
optrev = _rev2optrev(rev)
client_log(self.rootpath, optrev, optrev, 1, need_changes, 0,
_log_cb, self.ctx)
client_log(self.rootpath, optrev, optrev, 1, 0, _log_cb, self.ctx)
return tuple(revs[0])
def _revinfo(self, rev, include_changed_paths=0):
"""Internal-use, cache-friendly revision information harvester."""
# Consult the revinfo cache first. If we don't have cached info,
# or our caller wants changed paths and we don't have those for
# this revision, go do the real work.
rev = self._getrev(rev)
cached_info = self._revinfo_cache.get(rev)
if not cached_info \
or (include_changed_paths and cached_info[4] is None):
cached_info = self._revinfo_fetch(rev, include_changed_paths)
self._revinfo_cache[rev] = cached_info
return cached_info
##--- custom --##
def get_youngest_revision(self):
@@ -695,16 +549,23 @@ class RemoteSubversionRepository(vclib.Repository):
old_path = results[old_rev]
except KeyError:
raise vclib.ItemNotFound(path)
old_path = _cleanup_path(old_path)
old_path_parts = _path_parts(old_path)
# Check access (lying about path types)
if not vclib.check_path_access(self, old_path_parts, vclib.FILE, old_rev):
raise vclib.ItemNotFound(path)
return old_path
return _cleanup_path(old_path)
def created_rev(self, path, rev):
lh_rev, c_rev = self._get_last_history_rev(_path_parts(path), rev)
return lh_rev
# NOTE: We can't use svn_client_propget here because the
# interfaces in that layer strip out the properties not meant for
# human consumption (such as svn:entry:committed-rev, which we are
# using here to get the created revision of PATH@REV).
kind = ra.svn_ra_check_path(self.ra_session, path, rev)
if kind == core.svn_node_none:
raise vclib.ItemNotFound(_path_parts(path))
elif kind == core.svn_node_dir:
props = get_directory_props(self.ra_session, path, rev)
elif kind == core.svn_node_file:
fetched_rev, props = ra.svn_ra_get_file(self.ra_session, path, rev, None)
return int(props.get(core.SVN_PROP_ENTRY_COMMITTED_REV,
SVN_INVALID_REVNUM))
def last_rev(self, path, peg_revision, limit_revision=None):
"""Given PATH, known to exist in PEG_REVISION, find the youngest

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2011 The ViewCVS Group. All Rights Reserved.
#
# 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
@@ -22,7 +22,6 @@ import time
import tempfile
import popen
import re
import urllib
from svn import fs, repos, core, client, delta
@@ -43,14 +42,7 @@ def _fix_subversion_exception(e):
e.apr_err = e[1]
if not hasattr(e, 'message'):
e.message = e[0]
### Pre-1.4 Subversion doesn't have svn_path_canonicalize()
def _canonicalize_path(path):
try:
return core.svn_path_canonicalize(path)
except AttributeError:
return path
def _allow_all(root, path, pool):
"""Generic authz_read_func that permits access to all paths"""
return 1
@@ -116,16 +108,11 @@ def _rev2optrev(rev):
def _rootpath2url(rootpath, path):
rootpath = os.path.abspath(rootpath)
drive, rootpath = os.path.splitdrive(rootpath)
if rootpath and rootpath[0] != '/':
rootpath = '/' + rootpath
if os.sep != '/':
rootpath = string.replace(rootpath, os.sep, '/')
rootpath = urllib.quote(rootpath)
path = urllib.quote(path)
if drive:
url = 'file:///' + drive + rootpath + '/' + path
else:
url = 'file://' + rootpath + '/' + path
return _canonicalize_path(url)
return 'file://' + string.join([rootpath, path], "/")
# Given a dictionary REVPROPS of revision properties, pull special
@@ -295,11 +282,10 @@ class FileContentsPipe:
class BlameSource:
def __init__(self, local_url, rev, first_rev, include_text, config_dir):
def __init__(self, local_url, rev, first_rev, config_dir):
self.idx = -1
self.first_rev = first_rev
self.blame_data = []
self.include_text = include_text
ctx = client.svn_client_create_context()
core.svn_config_ensure(config_dir)
@@ -320,8 +306,6 @@ class BlameSource:
prev_rev = None
if rev > self.first_rev:
prev_rev = rev - 1
if not self.include_text:
text = None
self.blame_data.append(vclib.Annotation(text, line_no + 1, rev,
prev_rev, author, None))
@@ -537,7 +521,7 @@ class LocalSubversionRepository(vclib.Repository):
fsroot = self._getroot(rev)
return fs.node_proplist(fsroot, path)
def annotate(self, path_parts, rev, include_text=False):
def annotate(self, path_parts, rev):
path = self._getpath(path_parts)
path_type = self.itemtype(path_parts, rev) # does auth-check
if path_type != vclib.FILE:
@@ -548,8 +532,8 @@ class LocalSubversionRepository(vclib.Repository):
{'svn_cross_copies': 1})
youngest_rev, youngest_path = history[0]
oldest_rev, oldest_path = history[-1]
source = BlameSource(_rootpath2url(self.rootpath, path), youngest_rev,
oldest_rev, include_text, self.config_dir)
source = BlameSource(_rootpath2url(self.rootpath, path),
youngest_rev, oldest_rev, self.config_dir)
return source, youngest_rev
def revinfo(self, rev):
@@ -651,7 +635,6 @@ class LocalSubversionRepository(vclib.Repository):
is_copy = 0
change.base_path = None
change.base_rev = None
found_unreadable = 1
changedpaths[path] = SVNChangedPath(path, rev, pathtype,
change.base_path,
change.base_rev, action,
@@ -663,19 +646,11 @@ class LocalSubversionRepository(vclib.Repository):
return found_readable, found_unreadable, changedpaths.values()
def _get_change_copyinfo(fsroot, path, change):
# If we know the copyfrom info, return it...
if hasattr(change, 'copyfrom_known') and change.copyfrom_known:
copyfrom_path = change.copyfrom_path
copyfrom_rev = change.copyfrom_rev
# ...otherwise, if this change could be a copy (that is, it
# contains an add action), query the copyfrom info ...
elif (change.change_kind == fs.path_change_add or
change.change_kind == fs.path_change_replace):
copyfrom_rev, copyfrom_path = fs.copied_from(fsroot, path)
# ...else, there's no copyfrom info.
else:
copyfrom_rev = core.SVN_INVALID_REVNUM
copyfrom_path = None
copyfrom_rev, copyfrom_path = fs.copied_from(fsroot, path)
return copyfrom_path, copyfrom_rev
def _simple_auth_check(fsroot):
@@ -834,11 +809,8 @@ class LocalSubversionRepository(vclib.Repository):
if rev is None or rev == 'HEAD':
return self.youngest
try:
if type(rev) == type(''):
while rev[0] == 'r':
rev = rev[1:]
rev = int(rev)
except:
except ValueError:
raise vclib.InvalidRevision(rev)
if (rev < 0) or (rev > self.youngest):
raise vclib.InvalidRevision(rev)

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2011 The ViewCVS Group. All Rights Reserved.
#
# 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
@@ -14,7 +14,7 @@
#
# -----------------------------------------------------------------------
__version__ = '1.1.18'
__version__ = '1.1.11'
# this comes from our library; measure the startup time
import debug
@@ -24,7 +24,6 @@ debug.t_start('imports')
# standard modules that we know are in the path or builtin
import sys
import os
import fnmatch
import gzip
import mimetypes
import re
@@ -1018,15 +1017,6 @@ def default_view(mime_type, cfg):
return view_markup
return view_checkout
def is_binary_file_mime_type(mime_type, cfg):
"""Return True iff MIME_TYPE is set and matches one of the binary
file mime type patterns in CFG."""
if mime_type:
for pattern in cfg.options.binary_mime_types:
if fnmatch.fnmatch(mime_type, pattern):
return True
return False
def get_file_view_info(request, where, rev=None, mime_type=None, pathrev=-1):
"""Return an object holding common hrefs and a viewability flag used
for various views of FILENAME at revision REV whose MIME type is
@@ -1087,12 +1077,7 @@ def get_file_view_info(request, where, rev=None, mime_type=None, pathrev=-1):
params={'revision': rev},
escape=1)
is_binary_file = is_binary_file_mime_type(mime_type, request.cfg)
if is_binary_file:
download_text_href = annotate_href = view_href = None
prefer_markup = False
else:
prefer_markup = default_view(mime_type, request.cfg) == view_markup
prefer_markup = default_view(mime_type, request.cfg) == view_markup
return _item(view_href=view_href,
download_href=download_href,
@@ -1114,29 +1099,6 @@ _re_rewrite_email = re.compile('([-a-zA-Z0-9_.\+]+)@'
# Matches revision references
_re_rewrite_svnrevref = re.compile(r'\b(r|rev #?|revision #?)([0-9]+)\b')
class ViewVCHtmlFormatterTokens:
def __init__(self, tokens):
self.tokens = tokens
def get_result(self, maxlen=0):
"""Format the tokens per the registered set of formatters, and
limited to MAXLEN visible characters (or unlimited if MAXLEN is
0). Return a 3-tuple containing the formatted result string, the
number of visible characters in the result string, and a boolean
flag indicating whether or not S was truncated."""
out = ''
out_len = 0
for token in self.tokens:
chunk, chunk_len = token.converter(token.match, token.userdata, maxlen)
out = out + chunk
out_len = out_len + chunk_len
if maxlen:
maxlen = maxlen - chunk_len
if maxlen <= 0:
return out, out_len, 1
return out, out_len, 0
class ViewVCHtmlFormatter:
"""Format a string as HTML-encoded output with customizable markup
rules, for example turning strings that look like URLs into anchor links.
@@ -1222,30 +1184,6 @@ class ViewVCHtmlFormatter:
sapi.escape(trunc_s)), \
len(trunc_s)
def format_custom_url(self, mobj, userdata, maxlen=0):
"""Return a 2-tuple containing:
- the text represented by MatchObject MOBJ, formatted as an
linkified URL created by substituting match groups 0-9 into
USERDATA (which is a format string that uses \N to
represent the substitution locations) and with no more than
MAXLEN characters in the non-HTML-tag portions. If MAXLEN
is 0, there is no maximum.
- the number of characters returned.
"""
format = userdata
text = mobj.group(0)
url = format
for i in range(9):
try:
repl = mobj.group(i)
except:
repl = ''
url = url.replace('\%d' % (i), repl)
trunc_s = maxlen and text[:maxlen] or text
return '<a href="%s">%s</a>' % (sapi.escape(url),
sapi.escape(trunc_s)), \
len(trunc_s)
def format_text(self, s, unused, maxlen=0):
"""Return a 2-tuple containing:
- the text S, HTML-escaped, containing no more than MAXLEN
@@ -1278,14 +1216,20 @@ class ViewVCHtmlFormatter:
"""
out = ''
out_len = 0
tokens = self.tokenize_text(s)
return tokens.get_result()
for token in self._tokenize_text(s):
chunk, chunk_len = token.converter(token.match, token.userdata, maxlen)
out = out + chunk
out_len = out_len + chunk_len
if maxlen:
maxlen = maxlen - chunk_len
if maxlen <= 0:
return out, out_len, 1
return out, out_len, 0
def tokenize_text(self, s):
"""Return a ViewVCHtmlFormatterTokens object containing the tokens
created when parsing the string S. Callers can use that object's
get_result() function to retrieve HTML-formatted text.
"""
def _entity_encode(self, s):
return string.join(map(lambda x: '&#%d;' % (ord(x)), s), '')
def _tokenize_text(self, s):
tokens = []
# We could just have a "while s:" here instead of "for line: while
# line:", but for really large log messages with heavy
@@ -1332,80 +1276,36 @@ class ViewVCHtmlFormatter:
converter=self.format_text,
userdata=None))
line = ''
return ViewVCHtmlFormatterTokens(tokens)
def _entity_encode(self, s):
return string.join(map(lambda x: '&#%d;' % (ord(x)), s), '')
return tokens
class LogFormatter:
def __init__(self, request, log):
self.request = request
self.log = log or ''
self.tokens = None
self.cache = {} # (maxlen, htmlize) => resulting_log
def format_log(request, log, maxlen=0, htmlize=1):
if not log:
return log
def get(self, maxlen=0, htmlize=1):
cfg = self.request.cfg
# Prefer the cache.
if self.cache.has_key((maxlen, htmlize)):
return self.cache[(maxlen, htmlize)]
# If we are HTML-izing...
if htmlize:
# ...and we don't yet have ViewVCHtmlFormatter() object tokens...
if not self.tokens:
# ... then get them.
lf = ViewVCHtmlFormatter()
# Rewrite URLs.
lf.add_formatter(_re_rewrite_url, lf.format_url)
# Rewrite Subversion revision references.
if self.request.roottype == 'svn':
def revision_to_url(rev):
return self.request.get_url(view_func=view_revision,
params={'revision': rev},
escape=1)
lf.add_formatter(_re_rewrite_svnrevref, lf.format_svnrevref,
revision_to_url)
# Rewrite email addresses.
if cfg.options.mangle_email_addresses == 2:
lf.add_formatter(_re_rewrite_email, lf.format_email_truncated)
elif cfg.options.mangle_email_addresses == 1:
lf.add_formatter(_re_rewrite_email, lf.format_email_obfuscated)
else:
lf.add_formatter(_re_rewrite_email, lf.format_email)
# Add custom rewrite handling per configuration.
for rule in cfg.options.custom_log_formatting:
rule = rule.replace('\\:', '\x01')
regexp, format = map(lambda x: x.strip(), rule.split(':', 1))
regexp = regexp.replace('\x01', ':')
format = format.replace('\x01', ':')
lf.add_formatter(re.compile(regexp), lf.format_custom_url, format)
# Tokenize the log message.
self.tokens = lf.tokenize_text(self.log)
# Use our formatter to ... you know ... format.
log, log_len, truncated = self.tokens.get_result(maxlen)
result_log = log + (truncated and '&hellip;' or '')
# But if we're not HTML-izing...
cfg = request.cfg
if htmlize:
lf = ViewVCHtmlFormatter()
lf.add_formatter(_re_rewrite_url, lf.format_url)
if request.roottype == 'svn':
def revision_to_url(rev):
return request.get_url(view_func=view_revision,
params={'revision': rev},
escape=1)
lf.add_formatter(_re_rewrite_svnrevref, lf.format_svnrevref,
revision_to_url)
if cfg.options.mangle_email_addresses == 2:
lf.add_formatter(_re_rewrite_email, lf.format_email_truncated)
elif cfg.options.mangle_email_addresses == 1:
lf.add_formatter(_re_rewrite_email, lf.format_email_obfuscated)
else:
# ...then do much more simplistic transformations as necessary.
log = self.log
if cfg.options.mangle_email_addresses == 2:
log = re.sub(_re_rewrite_email, r'\1@...', log)
result_log = maxlen and log[:maxlen] or log
# In either case, populate the cache and return the results.
self.cache[(maxlen, htmlize)] = result_log
return result_log
lf.add_formatter(_re_rewrite_email, lf.format_email)
log, log_len, truncated = lf.get_result(log, maxlen)
return log + (truncated and '&hellip;' or '')
else:
if cfg.options.mangle_email_addresses == 2:
log = re.sub(_re_rewrite_email, r'\1@...', log)
return maxlen and log[:maxlen] or log
_time_desc = {
1 : 'second',
@@ -1647,36 +1547,33 @@ def markup_escaped_urls(s):
return "<a href=\"%s\">%s</a>" % (unescaped_url, url)
return re.sub(_re_rewrite_escaped_url, _url_repl, s)
def markup_stream(request, cfg, blame_data, file_lines, filename,
mime_type, encoding, colorize):
"""Return the contents of a versioned file as a list of
vclib.Annotation objects, each representing one line of the file's
contents. Use BLAME_DATA as the annotation information for the file
if provided. Use FILE_LINES as the lines of file content text
themselves. MIME_TYPE is the MIME content type of the file;
ENCODING is its character encoding. If COLORIZE is true, attempt to
apply syntax coloration to the file contents, and use the
HTML-marked-up results as the text in the return vclib.Annotation
objects."""
# Nothing to mark up? So be it.
if not file_lines:
return []
# Determine if we should (and can) use Pygments to highlight our
# output. Reasons not to include a) being told not to by the
# configuration, b) not being able to import the Pygments modules,
# and c) Pygments not having a lexer for our file's format.
pygments_lexer = None
if colorize:
def markup_stream_pygments(request, cfg, blame_data, fp, filename,
mime_type, encoding):
# Determine if we should use Pygments to highlight our output.
# Reasons not to include a) being told not to by the configuration,
# b) not being able to import the Pygments modules, and c) Pygments
# not having a lexer for our file's format.
blame_source = []
if blame_data:
for i in blame_data:
i.text = sapi.escape(i.text)
i.diff_href = None
if i.prev_rev:
i.diff_href = request.get_url(view_func=view_diff,
params={'r1': i.prev_rev,
'r2': i.rev},
escape=1, partial=1)
blame_source.append(i)
blame_data = blame_source
lexer = None
use_pygments = cfg.options.enable_syntax_coloration
try:
from pygments import highlight
from pygments.formatters import HtmlFormatter
from pygments.lexers import ClassNotFound, \
get_lexer_by_name, \
get_lexer_for_mimetype, \
get_lexer_for_filename, \
guess_lexer
get_lexer_for_filename
if not encoding:
encoding = 'guess'
if cfg.options.detect_encoding:
@@ -1685,50 +1582,50 @@ def markup_stream(request, cfg, blame_data, file_lines, filename,
encoding = 'chardet'
except (SyntaxError, ImportError):
pass
# First, see if there's a Pygments lexer associated with MIME_TYPE.
if mime_type:
try:
lexer = get_lexer_for_mimetype(mime_type,
encoding=encoding,
tabsize=cfg.options.tabsize,
stripnl=False)
except ClassNotFound:
try:
pygments_lexer = get_lexer_for_mimetype(mime_type,
encoding=encoding,
tabsize=cfg.options.tabsize,
stripnl=False)
lexer = get_lexer_for_filename(filename,
encoding=encoding,
tabsize=cfg.options.tabsize,
stripnl=False)
except ClassNotFound:
pygments_lexer = None
use_pygments = 0
except ImportError:
use_pygments = 0
# If we've no lexer thus far, try to find one based on the FILENAME.
if not pygments_lexer:
try:
pygments_lexer = get_lexer_for_filename(filename,
encoding=encoding,
tabsize=cfg.options.tabsize,
stripnl=False)
except ClassNotFound:
pygments_lexer = None
# Still no lexer? If we've reason to believe this is a text
# file, try to guess the lexer based on the file's content.
if not pygments_lexer and is_text(mime_type) and file_lines:
try:
pygments_lexer = guess_lexer(file_lines[0])
except ClassNotFound:
pygments_lexer = None
# If we aren't highlighting, just return an amalgamation of the
# BLAME_DATA (if any) and the FILE_LINES.
if not pygments_lexer:
lines = []
for i in range(len(file_lines)):
line = file_lines[i].rstrip('\n\r')
line = sapi.escape(string.expandtabs(line, cfg.options.tabsize))
line = markup_escaped_urls(line)
if blame_data:
blame_item = blame_data[i]
blame_item.text = line
else:
blame_item = vclib.Annotation(line, i + 1, None, None, None, None)
blame_item.diff_href = None
lines.append(blame_item)
# If we aren't going to be highlighting anything, just return the
# BLAME_SOURCE. If there's no blame_source, we'll generate a fake
# one from the file contents we fetch with PATH and REV.
if not use_pygments:
if blame_source:
class BlameSourceTabsizeWrapper:
def __init__(self, blame_source, tabsize):
self.blame_source = blame_source
self.tabsize = cfg.options.tabsize
def __getitem__(self, idx):
item = self.blame_source.__getitem__(idx)
item.text = string.expandtabs(item.text, self.tabsize)
item.text = markup_escaped_urls(item.text)
return item
return BlameSourceTabsizeWrapper(blame_source, cfg.options.tabsize)
else:
lines = []
line_no = 0
while 1:
line = fp.readline()
if not line:
break
line_no = line_no + 1
line = sapi.escape(string.expandtabs(line, cfg.options.tabsize))
line = markup_escaped_urls(line)
item = vclib.Annotation(line, line_no, None, None, None, None)
item.diff_href = None
lines.append(item)
return lines
# If we get here, we're highlighting something.
@@ -1743,7 +1640,7 @@ def markup_stream(request, cfg, blame_data, file_lines, filename,
self.line_no = 0
def write(self, buf):
### FIXME: Don't bank on write() being called once per line
buf = markup_escaped_urls(buf.rstrip('\n\r'))
buf = markup_escaped_urls(buf)
if self.has_blame_data:
self.blame_data[self.line_no].text = buf
else:
@@ -1752,9 +1649,8 @@ def markup_stream(request, cfg, blame_data, file_lines, filename,
item.diff_href = None
self.blame_data.append(item)
self.line_no = self.line_no + 1
ps = PygmentsSink(blame_data)
highlight(string.join(file_lines, ''), pygments_lexer,
ps = PygmentsSink(blame_source)
highlight(fp.read(), lexer,
HtmlFormatter(nowrap=True,
classprefix="pygments-",
encoding='utf-8'), ps)
@@ -1793,8 +1689,7 @@ def get_itemprops(request, path_parts, rev):
propnames.sort()
props = []
for name in propnames:
lf = LogFormatter(request, itemprops[name])
value = lf.get(maxlen=0, htmlize=1)
value = format_log(request, itemprops[name])
undisplayable = ezt.boolean(0)
# skip non-utf8 property names
try:
@@ -1846,11 +1741,6 @@ def markup_or_annotate(request, is_annotate):
revision = None
mime_type, encoding = calculate_mime_type(request, path, rev)
# Is this display blocked by 'binary_mime_types' configuration?
if is_binary_file_mime_type(mime_type, cfg):
raise debug.ViewVCException('Display of binary file content disabled '
'by configuration', '403 Forbidden')
# Is this a viewable image type?
if is_viewable_image(mime_type) \
and 'co' in cfg.options.allowed_views:
@@ -1858,71 +1748,34 @@ def markup_or_annotate(request, is_annotate):
fp.close()
if check_freshness(request, None, revision, weak=1):
return
if is_annotate:
annotation = 'binary'
annotation = 'binary'
image_src_href = request.get_url(view_func=view_checkout,
params={'revision': rev}, escape=1)
# Not a viewable image.
else:
blame_data = None
# If this was an annotation request, try to annotate this file.
# If something goes wrong, that's okay -- we'll gracefully revert
# to a plain markup display.
blame_source = None
if is_annotate:
# Try to annotate this file, but don't croak if we fail.
try:
blame_source, revision = request.repos.annotate(path, rev, False)
blame_source, revision = request.repos.annotate(path, rev)
annotation = 'annotated'
if check_freshness(request, None, revision, weak=1):
return
# Create BLAME_DATA list from BLAME_SOURCE, adding diff_href
# items to each relevant "line".
blame_data = []
for item in blame_source:
item.diff_href = None
if item.prev_rev:
item.diff_href = request.get_url(view_func=view_diff,
params={'r1': item.prev_rev,
'r2': item.rev},
escape=1, partial=1)
blame_data.append(item)
annotation = 'annotated'
except vclib.NonTextualFileContents:
annotation = 'binary'
except:
annotation = 'error'
# Grab the file contents.
fp, revision = request.repos.openfile(path, rev, {'cvs_oldkeywords' : 1})
if check_freshness(request, None, revision, weak=1):
fp.close()
return
file_lines = fp.readlines()
lines = markup_stream_pygments(request, cfg, blame_source, fp,
path[-1], mime_type, encoding)
fp.close()
# Do we have a differing number of file content lines and
# annotation items? That's no good. Call it an error and don't
# bother attempting the annotation display.
if blame_data and (len(file_lines) != len(blame_data)):
annotation = 'error'
blame_data = None
# Try to markup the file contents/annotation. If we get an error
# and we were colorizing the stream, try once more without the
# colorization enabled.
colorize = cfg.options.enable_syntax_coloration
try:
lines = markup_stream(request, cfg, blame_data, file_lines,
path[-1], mime_type, encoding, colorize)
except:
if colorize:
lines = markup_stream(request, cfg, blame_data, file_lines,
path[-1], mime_type, encoding, False)
else:
raise debug.ViewVCException('Error displaying file contents',
'500 Internal Server Error')
data = common_template_data(request, revision, mime_type)
data = common_template_data(request, revision)
data.merge(ezt.TemplateData({
'mime_type' : mime_type,
'log' : None,
@@ -1946,19 +1799,14 @@ def markup_or_annotate(request, is_annotate):
}))
if cfg.options.show_log_in_markup:
options = {
'svn_latest_log': 1, ### FIXME: Use of this magical value is uncool.
'svn_cross_copies': 1,
}
options = {'svn_latest_log': 1} ### FIXME: No longer needed?
revs = request.repos.itemlog(path, revision, vclib.SORTBY_REV,
0, 1, options)
entry = revs[-1]
lf = LogFormatter(request, entry.log)
data['date'] = make_time_string(entry.date, cfg)
data['author'] = entry.author
data['changed'] = entry.changed
data['log'] = lf.get(maxlen=0, htmlize=1)
data['log'] = format_log(request, entry.log)
data['size'] = entry.size
if entry.date is not None:
@@ -2074,27 +1922,20 @@ def view_roots(request):
rootnames = allroots.keys()
rootnames.sort(icmp)
for rootname in rootnames:
root_path, root_type, lastmod = allroots[rootname]
href = request.get_url(view_func=view_directory,
where='', pathtype=vclib.DIR,
params={'root': rootname}, escape=1)
if root_type == vclib.SVN:
log_href = request.get_url(view_func=view_log,
where='', pathtype=vclib.DIR,
params={'root': rootname}, escape=1)
else:
log_href = None
lastmod = allroots[rootname][2]
roots.append(_item(name=request.server.escape(rootname),
type=root_type,
path=root_path,
type=allroots[rootname][1],
path=allroots[rootname][0],
author=lastmod and lastmod.author or None,
ago=lastmod and lastmod.ago or None,
date=lastmod and lastmod.date or None,
log=lastmod and lastmod.log or None,
short_log=lastmod and lastmod.short_log or None,
rev=lastmod and lastmod.rev or None,
href=href,
log_href=log_href))
href=href))
data = common_template_data(request)
data.merge(ezt.TemplateData({
@@ -2194,11 +2035,9 @@ def view_directory(request):
row.date = make_time_string(file.date, cfg)
row.ago = html_time(request, file.date)
if cfg.options.show_logs:
debug.t_start("dirview_logformat")
lf = LogFormatter(request, file.log)
row.log = lf.get(maxlen=0, htmlize=1)
row.short_log = lf.get(maxlen=cfg.options.short_log_len, htmlize=1)
debug.t_end("dirview_logformat")
row.log = format_log(request, file.log)
row.short_log = format_log(request, file.log,
maxlen=cfg.options.short_log_len)
row.lockinfo = file.lockinfo
row.anchor = request.server.escape(file.name)
row.name = request.server.escape(file.name)
@@ -2551,7 +2390,6 @@ def view_log(request):
sortby = vclib.SORTBY_DEFAULT
first = last = 0
log_pagestart = None
if cfg.options.log_pagesize:
log_pagestart = int(request.query_dict.get('log_pagestart', 0))
total = cfg.options.log_pagesextra * cfg.options.log_pagesize
@@ -2576,6 +2414,7 @@ def view_log(request):
entry.ago = None
if rev.date is not None:
entry.ago = html_time(request, rev.date, 1)
entry.log = format_log(request, rev.log or '')
entry.size = rev.size
entry.lockinfo = rev.lockinfo
entry.branch_point = None
@@ -2583,9 +2422,6 @@ def view_log(request):
entry.orig_path = None
entry.copy_path = None
lf = LogFormatter(request, rev.log or '')
entry.log = lf.get(maxlen=0, htmlize=1)
entry.view_href = None
entry.download_href = None
entry.download_text_href = None
@@ -2675,8 +2511,7 @@ def view_log(request):
if selected_rev != entry.rev:
entry.sel_for_diff_href = \
request.get_url(view_func=view_log,
params={'r1': entry.rev,
'log_pagestart': log_pagestart},
params={'r1': entry.rev},
escape=1)
if entry.prev is not None:
entry.diff_to_prev_href = \
@@ -2817,9 +2652,7 @@ def view_log(request):
if cfg.options.log_pagesize:
data['log_paging_action'], data['log_paging_hidden_values'] = \
request.get_form(params={'log_pagestart': None,
'r1': selected_rev,
})
request.get_form(params={'log_pagestart': None})
data['log_pagestart'] = int(request.query_dict.get('log_pagestart',0))
data['entries'] = paging_sws(data, 'entries', data['log_pagestart'],
'rev', cfg.options.log_pagesize,
@@ -3085,7 +2918,7 @@ class DiffSource:
return _item(type='header',
line_info_left=match.group(1),
line_info_right=match.group(2),
line_info_extra=self._format_text(match.group(3)))
line_info_extra=match.group(3))
if line[0] == '\\':
# \ No newline at end of file
@@ -3153,8 +2986,7 @@ class DiffSource:
class DiffSequencingError(Exception):
pass
def diff_parse_headers(fp, diff_type, path1, path2, rev1, rev2,
sym1=None, sym2=None):
def diff_parse_headers(fp, diff_type, rev1, rev2, sym1=None, sym2=None):
date1 = date2 = log_rev1 = log_rev2 = flag = None
header_lines = []
@@ -3183,15 +3015,15 @@ def diff_parse_headers(fp, diff_type, path1, path2, rev1, rev2,
if match:
date1 = match.group(1)
log_rev1 = match.group(2)
line = '%s%s\t%s\t%s%s\n' % (f1, path1, date1, log_rev1,
sym1 and ' ' + sym1 or '')
if sym1:
line = line[:-1] + ' %s\n' % sym1
elif line[:len(f2)] == f2:
match = _re_extract_rev.match(line)
if match:
date2 = match.group(1)
log_rev2 = match.group(2)
line = '%s%s\t%s\t%s%s\n' % (f2, path2, date2, log_rev2,
sym2 and ' ' + sym2 or '')
if sym2:
line = line[:-1] + ' %s\n' % sym2
parsing = 0
elif line[:3] == 'Bin':
flag = _RCSDIFF_IS_BINARY
@@ -3300,13 +3132,6 @@ def view_patch(request):
query_dict = request.query_dict
p1, p2, rev1, rev2, sym1, sym2 = setup_diff(request)
mime_type1, encoding1 = calculate_mime_type(request, p1, rev1)
mime_type2, encoding2 = calculate_mime_type(request, p2, rev2)
if is_binary_file_mime_type(mime_type1, cfg) or \
is_binary_file_mime_type(mime_type2, cfg):
raise debug.ViewVCException('Display of binary file content disabled '
'by configuration', '403 Forbidden')
# In the absence of a format dictation in the CGI params, we'll let
# use the configured diff format, allowing 'c' to mean 'c' and
# anything else to mean 'u'.
@@ -3326,11 +3151,8 @@ def view_patch(request):
raise debug.ViewVCException('Invalid path(s) or revision(s) passed '
'to diff', '400 Bad Request')
path_left = _path_join(p1)
path_right = _path_join(p2)
date1, date2, flag, headers = diff_parse_headers(fp, diff_type,
path_left, path_right,
rev1, rev2, sym1, sym2)
date1, date2, flag, headers = diff_parse_headers(fp, diff_type, rev1, rev2,
sym1, sym2)
server_fp = get_writeready_server_file(request, 'text/plain')
server_fp.write(headers)
@@ -3347,13 +3169,6 @@ def view_diff(request):
query_dict = request.query_dict
p1, p2, rev1, rev2, sym1, sym2 = setup_diff(request)
mime_type1, encoding1 = calculate_mime_type(request, p1, rev1)
mime_type2, encoding2 = calculate_mime_type(request, p2, rev2)
if is_binary_file_mime_type(mime_type1, cfg) or \
is_binary_file_mime_type(mime_type2, cfg):
raise debug.ViewVCException('Display of binary file content disabled '
'by configuration', '403 Forbidden')
# since templates are in use and subversion allows changes to the dates,
# we can't provide a strong etag
if check_freshness(request, None, '%s-%s' % (rev1, rev2), weak=1):
@@ -3433,7 +3248,6 @@ def view_diff(request):
changes = []
if fp:
date1, date2, flag, headers = diff_parse_headers(fp, diff_type,
path_left, path_right,
rev1, rev2, sym1, sym2)
if human_readable:
if flag is not None:
@@ -3451,8 +3265,7 @@ def view_diff(request):
fvi = get_file_view_info(request, path_left, rev1)
left = _item(date=make_time_string(log_entry1.date, cfg),
author=log_entry1.author,
log=LogFormatter(request,
log_entry1.log).get(maxlen=0, htmlize=1),
log=format_log(request, log_entry1.log),
size=log_entry1.size,
ago=ago1,
path=path_left,
@@ -3468,8 +3281,7 @@ def view_diff(request):
fvi = get_file_view_info(request, path_right, rev2)
right = _item(date=make_time_string(log_entry2.date, cfg),
author=log_entry2.author,
log=LogFormatter(request,
log_entry2.log).get(maxlen=0, htmlize=1),
log=format_log(request, log_entry2.log),
size=log_entry2.size,
ago=ago2,
path=path_right,
@@ -3717,8 +3529,7 @@ def view_revision(request):
propnames.sort()
props = []
for name in propnames:
lf = LogFormatter(request, revprops[name])
value = lf.get(maxlen=0, htmlize=1)
value = format_log(request, revprops[name])
undisplayable = ezt.boolean(0)
# skip non-utf8 property names
try:
@@ -3841,14 +3652,13 @@ def view_revision(request):
escape=1)
jump_rev_action, jump_rev_hidden_values = \
request.get_form(params={'revision': None})
lf = LogFormatter(request, msg)
data = common_template_data(request)
data.merge(ezt.TemplateData({
'rev' : str(rev),
'author' : author,
'date' : date_str,
'log' : lf.get(maxlen=0, htmlize=1),
'log' : format_log(request, msg),
'properties' : props,
'ago' : date is not None and html_time(request, date, 1) or None,
'changes' : changes,
@@ -4206,10 +4016,9 @@ def build_commit(request, files, max_files, dir_strip, format):
commit.log = None
commit.short_log = None
else:
lf = LogFormatter(request, desc)
htmlize = (format != 'rss')
commit.log = lf.get(maxlen=0, htmlize=htmlize)
commit.short_log = lf.get(maxlen=cfg.options.short_log_len, htmlize=htmlize)
commit.log = format_log(request, desc, 0, format != 'rss')
commit.short_log = format_log(request, desc, cfg.options.short_log_len,
format != 'rss')
commit.author = request.server.escape(author)
commit.rss_date = make_rss_time_string(date, request.cfg)
if request.roottype == 'svn':
@@ -4503,9 +4312,8 @@ def list_roots(request):
date, author, msg, revprops, changes = repos.revinfo(youngest_rev)
date_str = make_time_string(date, cfg)
ago = html_time(request, date)
lf = LogFormatter(request, msg)
log = lf.get(maxlen=0, htmlize=1)
short_log = lf.get(maxlen=cfg.options.short_log_len, htmlize=1)
log = format_log(request, msg)
short_log = format_log(request, msg, maxlen=cfg.options.short_log_len)
lastmod = _item(ago=ago, author=author, date=date_str, log=log,
short_log=short_log, rev=str(youngest_rev))
except:
@@ -4597,35 +4405,14 @@ def locate_root(cfg, rootname):
return None, None
def load_config(pathname=None, server=None):
"""Load the ViewVC configuration file. SERVER is the server object
that will be using this configuration. Consult the environment for
the variable VIEWVC_CONF_PATHNAME and VIEWCVS_CONF_PATHNAME (its
legacy name) and, if set, use its value as the path of the
configuration file; otherwise, use PATHNAME (if provided). Failing
all else, use a hardcoded default configuration path."""
debug.t_start('load-config')
# See if the environment contains overrides to the configuration
# path. If we have a SERVER object, consult its environment; use
# the OS environment otherwise.
env_pathname = None
if server is not None:
env_pathname = (server.getenv("VIEWVC_CONF_PATHNAME")
or server.getenv("VIEWCVS_CONF_PATHNAME"))
else:
env_pathname = (os.environ.get("VIEWVC_CONF_PATHNAME")
or os.environ.get("VIEWCVS_CONF_PATHNAME"))
if pathname is None:
pathname = (os.environ.get("VIEWVC_CONF_PATHNAME")
or os.environ.get("VIEWCVS_CONF_PATHNAME")
or os.path.join(os.path.dirname(os.path.dirname(__file__)),
"viewvc.conf"))
# Try to find the configuration pathname by searching these ordered
# locations: the environment, the passed-in PATHNAME, the hard-coded
# default.
pathname = (env_pathname
or pathname
or os.path.join(os.path.dirname(os.path.dirname(__file__)),
"viewvc.conf"))
# Load the configuration!
cfg = config.Config()
cfg.set_defaults()
cfg.load_config(pathname, server and server.getenv("HTTP_HOST"))

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2007 The ViewCVS Group. All Rights Reserved.
#
# 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

View File

@@ -92,15 +92,13 @@ numbers, and not literal):
Also, drop a copy of the archive files into the root directory of
the viewvc.org website (unversioned).
15. Update the Tigris.org website (^/trunk/www/index.html) to refer to
the new release files and commit.
15. On trunk, update the websites (both the viewvc.org/ and www/ ones)
to refer to the new release files, and copy the CHANGES for the
new release into trunk's CHANGES file.
svn ci -m "Bump latest advertised release."
16. Back on the release branch, edit the file 'lib/viewvc.py' again,
incrementing the patch number assigned to the __version__
variable. Add a new empty block in the branch's CHANGES file.
Commit your changes:
16. Edit the file 'lib/viewvc.py' again, incrementing the patch number
assigned to the __version__ variable. Add a new empty block in
the branch's CHANGES file. Commit your changes:
svn ci -m "Begin a new release cycle."
@@ -113,13 +111,5 @@ numbers, and not literal):
http://viewvc.tigris.org/issues/editmilestones.cgi?component=viewvc&action=add
18. Send to the announce@ list a message explaining all the cool new
features.
http://viewvc.tigris.org/ds/viewForumSummary.do?dsForumId=4253
19. Post a new release notification at Freecode.
https://freecode.com/projects/viewvc/releases/new
20. Merge CHANGES for this release into the CHANGES file for newer
release lines and commit.
features, and post similar announcements to other places interested
in this sort of stuff, such as Freshmeat (http://www.freshmeat.net).

View File

@@ -9,11 +9,7 @@
[# ------------------------------------------------------------------------- ]
[# setup page definitions]
[is annotation "annotated"]
[define page_title]Annotation of /[where][end]
[else]
[define page_title]Contents of /[where][end]
[end]
[define page_title]Contents of /[where][end]
[define help_href][docroot]/help_rootview.html[end]
[# end]

View File

@@ -27,7 +27,7 @@
[roots.name]</a>
</td>
[is cfg.options.show_roots_lastmod "1"]
<td style="width:20">&nbsp;[if-any roots.log_href]<a href="[roots.log_href]">[roots.rev]</a>[else][roots.rev][end]</td>
<td style="width:20">&nbsp;[roots.rev]</td>
<td style="width:20">&nbsp;[roots.ago]</td>
<td style="width:20">&nbsp;[roots.author]</td>
<td style="width:20">&nbsp;[roots.short_log]</td>

View File

@@ -1,6 +1,6 @@
#!/bin/sh
#
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2007 The ViewCVS Group. All Rights Reserved.
#
# 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

View File

@@ -1,5 +1,5 @@
/*
# Copyright (C) 1999-2013 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
# the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,5 +1,5 @@
/*
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2007 The ViewCVS Group. All Rights Reserved.
#
# 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

View File

@@ -1,5 +1,5 @@
/*
# Copyright (C) 1999-2013 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
# the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,5 +1,5 @@
/*
# Copyright (C) 1999-2013 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
# 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
# -*- Mode: python -*-
#
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2009 The ViewCVS Group. All Rights Reserved.
#
# 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
@@ -209,8 +209,6 @@ def install_file(src_path, dst_path, mode, subst_path_vars,
temp = raw_input("Do you want to [O]verwrite, [D]o "
"not overwrite, or [V]iew "
"differences? ")
if len(temp) == 0:
continue
temp = string.lower(temp[0])
if temp == "v" and ext not in BINARY_FILE_EXTS:
print """