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

Compare commits

..

3 Commits

Author SHA1 Message Date
cmpilato
fab88fb6c3 Retroactively "peg" the svn:externals property on these tags which
pulls in the 'templates-contrib' area.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/tags/1.1.0-beta1@2451 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-09-08 16:51:21 +00:00
cmpilato
cc1cf127a3 Tag the 1.1.0-beta1 release.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/tags/1.1.0-beta1@2037 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-11-04 21:15:53 +00:00
cmpilato
c3ba043654 Tag the 1.1.0-beta1 release.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/tags/1.1.0-beta1@2035 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-11-04 20:46:52 +00:00
92 changed files with 4506 additions and 6278 deletions

193
CHANGES
View File

@@ -1,172 +1,4 @@
Version 1.1.19 (released 22-Apr-2013)
* improve root lookup performance (issue #523)
* new 'max_filesize_kbytes' config option and handling (issue #524)
* tarball generation improvements:
- preserve Subversion symlinks in generated tarballs (issue #487)
- reduce memory usage of tarball generation logic
- fix double compression of generated tarballs (issue #525)
* file content handling improvements:
- expanded support for encoding detection and transcoding (issue #11)
- fix tab-to-space conversion bugs in markup, annotate, and diff views
- fix handling of trailing whitespace in diff view
* add support for timestamp display in ISO8601 format (issue #46)
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
* fix broken standalone.py -c and -d options handling
* add --help option to standalone.py
* 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)
* avoid parsing errors on RCS newphrases in the admin section (issue #483)
* make rlog parsing code more robust in certain error cases (issue #444)
Version 1.1.10 (released 15-Mar-2011)
* fix stack trace in Subversion revision info logic (issue #475, issue #476)
Version 1.1.9 (released 18-Feb-2011)
* vcauth universal access determinations (issue #425)
* rework svn revision info cache for performance
* make revision log "extra pages" count configurable
* fix Subversion 1.4.x revision log compatibility code regression
* display sanitized error when authzfile is malformed
* restore markup of URLs in file contents (issue #455)
* optionally display last-committed metadata in roots view (issue #457)
Version 1.1.8 (released 02-Dec-2010)
* fix slowness triggered by allow_compress=1 configuration (issue #467)
* allow use of 'fcrypt' for Windows standalone.py authn support (issue #471)
* yield more useful error on directory markup/annotate request (issue #472)
Version 1.1.7 (released 09-Sep-2010)
* display Subversion revision properties in the revision view (issue #453)
* fix exception in 'standalone.py -r REPOS' when run without a config file
* fix standalone.py server root deployments (--script-alias='')
* add Basic authentication support to standalone.py (Unix only) (issue #49)
* fix obscure "unexpected NULL parent pool" Subversion bindings error
* enable path info / link display in remote Subversion root revision view
* fix vhost name case handling inconsistency (issue #466)
* use svn:mime-type property charset param as encoding hint
* markup Subversion revision references in log messages (issue #313)
* add rudimentary support for FastCGI-based deployments (issue #464)
* fix query script WSGI deployment
* add configuration to fix query script cross-linking to ViewVC
Version 1.1.6 (released 02-Jun-2010)
* add rudimentary support for WSGI-based deployments (issue #397)
* fix exception caused by trying to HTML-escape non-string data (issue #454)
* fix incorrect RSS feed Content-Type header (issue #449)
* fix RSS <title> encoding problem (issue #451)
* allow 'svndbadmin purge' to work on missing repositories (issue #452)
Version 1.1.5 (released 29-Mar-2010)
* security fix: escape user-provided search_re input to avoid XSS attack
Version 1.1.4 (released 10-Mar-2010)
* security fix: escape user-provided query form input to avoid XSS attack
* fix standalone.py failure (when per-root options aren't used) (issue #445)
* fix annotate failure caused by ignored svn_config_dir (issue #447)
Version 1.1.3 (released 22-Dec-2009)
* security fix: add root listing support of per-root authz config
* security fix: query.py requires 'forbidden' authorizer (or none) in config
* fix URL-ification of truncated log messages (issue #3)
* fix regexp input validation (issue #426, #427, #440)
* add support for configurable tab-to-spaces conversion
* fix not-a-sequence error in diff view
* allow viewvc-install to work when templates-contrib is absent
* minor template improvements/corrections
* expose revision metadata in diff view (issue #431)
* markup file/directory item property URLs and email addresses (issue #434)
* make ViewVC cross copies in Subversion history by default
* fix bug that caused standalone.py failure under Python 1.5.2 (issue #442)
* fix support for per-vhost overrides of authorizer parameters (issue #411)
* fix root name identification in query.py interface
Version 1.1.2 (released 11-Aug-2009)
* security fix: validate the 'view' parameter to avoid XSS attack
* security fix: avoid printing illegal parameter names and values
* add optional support for character encoding detection (issue #400)
* fix username case handling in svnauthz module (issue #419)
* fix cvsdbadmin/svnadmin rebuild error on missing repos (issue #420)
* don't drop leading blank lines from colorized file contents (issue #422)
* add file.ezt template logic for optionally hiding binary file contents
Version 1.1.1 (released 03-Jun-2009)
* fix broken query form (missing required template variables) (issue #416)
* fix bug in cvsdb which caused rebuild operations to lose data (issue #417)
* fix cvsdb purge/rebuild repos lookup to error on missing repos
* fix misleading file contents view page title
Version 1.1.0 (released 13-May-2009)
Version 1.1.0 (released ??-???-????)
* add support for full content diffs (issue #153)
* make many more data dictionary items available to all views
@@ -180,7 +12,9 @@ Version 1.1.0 (released 13-May-2009)
* add support for query by log message (issues #22, #121)
* fix bug parsing 'svn blame' output with too-long author names (issue #221)
* fix default standalone.py port to be within private IANA range (issue #234)
* add unified configury of allowed views; checkout view disabled by default
* add support for integration with GNU source-highlight (issue #285)
* add unified configury of allowed views
* add support for disabling the checkout view (now the default state)
* add support for ranges of revisions to svndbadmin (issue #224)
* make the query handling more forgiving of malformatted subdirs (issue #244)
* add support for per-root configuration overrides (issue #371)
@@ -202,24 +36,7 @@ Version 1.1.0 (released 13-May-2009)
* show RSS/query links only for roots found in commits database (issue #357)
* recognize Subversion svn:mime-type property values (issue #364)
* hide CVS files when viewing tags/branches on which they don't exist
* allow hiding of errorful entries from the directory view (issue #105)
* fix directory view sorting UI
* tolerate malformed Accept-Language headers (issue #396)
* allow MIME type mapping overrides in ViewVC configuration (issue #401)
* fix exception in rev-sorted remote Subversion directory views (issue #409)
* allow setting of page sizes for log and dir views individually (issue #402)
Version 1.0.9 (released 11-Aug-2009)
* security fix: validate the 'view' parameter to avoid XSS attack
* security fix: avoid printing illegal parameter names and values
Version 1.0.8 (released 05-May-2009)
* fix directory view sorting UI
* tolerate malformed Accept-Language headers (issue #396)
* fix directory log views in revision-less Subversion repositories
* fix exception in rev-sorted remote Subversion directory views (issue #409)
* add support for hiding errorful entries from the directory view (issue #105)
Version 1.0.7 (released 14-Oct-2008)

160
INSTALL
View File

@@ -19,7 +19,7 @@ Congratulations on getting this far. :-)
For CVS Support:
* Python 1.5.2 or later (sorry, no 3.x support yet)
* Python 1.5.2 or later
(http://www.python.org/)
* RCS, Revision Control System
(http://www.cs.purdue.edu/homes/trinkle/RCS/)
@@ -30,11 +30,11 @@ Congratulations on getting this far. :-)
For Subversion Support:
* Python 2.0 or later (sorry, no 3.x support yet)
* Python 2.0 or later
(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:
@@ -139,7 +139,7 @@ installation instructions.
default_root
root_as_url_component
rcs_dir
mime_types_files
mime_types_file
There are some other options that are usually nice to change. See
viewvc.conf for more information. ViewVC provides a working,
@@ -168,145 +168,72 @@ checkin database working are below.
APACHE CONFIGURATION
--------------------
1) Locate your Apache configuration file(s).
1) Find out where the web server configuration file is kept. Typical
locations are /etc/httpd/httpd.conf, /etc/httpd/conf/httpd.conf,
and /etc/apache/httpd.conf. Depending on how apache was installed,
you may also look under /usr/local/etc or /etc/local. Use the vendor
documentation or the find utility if in doubt.
Typical locations are /etc/httpd/httpd.conf,
/etc/httpd/conf/httpd.conf, and /etc/apache/httpd.conf. Depending
on how Apache was installed, you may also look under /usr/local/etc
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.
ViewVC provides several different ways to do this. Choose one of
the following methods:
-----------------------------------
METHOD A: CGI mode via ScriptAlias
-----------------------------------
The ScriptAlias directive is very useful for pointing
Either METHOD A:
2) 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
----------------------------------------
Copy the CGI scripts from
continue with step 3).
or alternatively METHOD B:
2) 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
continue with step 3).
------------------------------------------
METHOD C: CGI mode in ExecCGI'd directory
------------------------------------------
Copy the CGI scripts from
and then there's METHOD C:
2) Copy the CGI scripts from
<VIEWVC_INSTALLATION_DIRECTORY>/bin/cgi/*.cgi
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:
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
(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"
needs to have been specified for the directory.
need to have been specified for the directory.)
------------------------------------------
METHOD D: Using mod_python (if installed)
------------------------------------------
Copy the Python scripts and .htaccess file from
continue with step 3).
or if you've got Mod_Python installed you can use METHOD D:
2) Copy the Python scripts and .htaccess file from
<VIEWVC_INSTALLATION_DIRECTORY>/bin/mod_python/
to a directory being served by Apache.
to a directory being served by apache.
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
under Apache 2.
----------------------------------------
METHOD E: Using mod_wsgi (if installed)
----------------------------------------
Copy the Python scripts file from
<VIEWVC_INSTALLATION_DIRECTORY>/bin/mod_python/
to the directory of your choosing. Modify httpd.conf with the
following directives:
continue with step 3).
WSGIScriptAlias /viewvc <VIEWVC_INSTALLATION_DIRECTORY>/bin/wsgi/viewvc.wsgi
WSGIScriptAlias /query <VIEWVC_INSTALLATION_DIRECTORY>/bin/wsgi/query.wsgi
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"
You'll probably also need the following directive because of the
not-quite-sanctioned way that ViewVC manipulates Python objects.
4) Optional: Add access control.
WSGIApplicationGroup %{GLOBAL}
Note: WSGI support in ViewVC is at this time quite rudimentary,
bordering on downright experimental. Your mileage may vary.
-----------------------------------------
METHOD F: Using mod_fcgid (if installed)
-----------------------------------------
This uses ViewVC's WSGI support (from above), but supports using FastCGI,
and is a somewhat hybrid approach of several of the above methods.
Especially if fcgi is already being used for other purposes, e.g. PHP,
also using fcgi can prevent the need for including additional modules
(e.g. mod_python or mod_wsgi) within Apache, which may help lessen Apache's
memory usage and/or help improve performance.
This depends on mod_fcgid:
http://httpd.apache.org/mod_fcgid/
as well as the fcgi server from Python's flup package:
http://pypi.python.org/pypi/flup
http://trac.saddi.com/flup
The following are some example httpd.conf fragments you can use to
support this configuration:
ScriptAlias /viewvc /usr/local/viewvc/bin/wsgi/viewvc.fcgi
ScriptAlias /query /usr/local/viewvc/bin/wsgi/query.fcgi
4) [Optional] Add access control.
In your httpd.conf you can control access to certain modules by
adding directives like this:
In your httpd.conf you can control access to certain modules by adding
directives like this:
<Location "<url to viewvc.cgi>/<modname_you_wish_to_access_ctl>">
AllowOverride None
@@ -324,14 +251,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-2008 The ViewCVS Group. All rights
reserved.</strong></p>
<p>By using ViewVC, you agree to the terms and conditions set forth
@@ -59,11 +59,6 @@
<li>March 17, 2006 &mdash; software renamed from "ViewCVS"</li>
<li>April 10, 2007 &mdash; copyright years updated</li>
<li>February 22, 2008 &mdash; copyright years updated</li>
<li>March 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
@@ -54,10 +54,7 @@ import query
server = sapi.AspServer(Server, Request, Response, Application)
try:
cfg = viewvc.load_config(CONF_PATHNAME, server)
viewvc_base_url = cfg.query.viewvc_base_url
if viewvc_base_url is None:
viewvc_base_url = "viewvc.asp"
query.main(server, cfg, viewvc_base_url)
query.main(server, cfg, "viewvc.asp")
finally:
s.close()

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
@@ -54,7 +54,4 @@ import query
server = sapi.CgiServer()
cfg = viewvc.load_config(CONF_PATHNAME, server)
viewvc_base_url = cfg.query.viewvc_base_url
if viewvc_base_url is None:
viewvc_base_url = "viewvc.cgi"
query.main(server, cfg, viewvc_base_url)
query.main(server, cfg, "viewvc.cgi")

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
@@ -177,12 +177,7 @@ if __name__ == '__main__':
if command in ('rebuild', 'purge'):
if quiet_level < 2:
print "Purging existing data for repository root `%s'" % root
try:
db.PurgeRepository(root)
except cvsdb.UnknownRepositoryError, e:
if command == 'purge':
sys.stderr.write("ERROR: " + str(e) + "\n")
sys.exit(1)
db.PurgeRepository(root)
if command in ('rebuild', 'update'):
repository = vclib.ccvs.CVSRepository(None, rootpath, None,

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-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
@@ -18,29 +18,29 @@
import os, sys, string
import popen2
import getopt
## ------------------------------------------------------------------------
## Stuff common to all schemas
##
DATABASE_SCRIPT_COMMON="""\
INTRO_TEXT = """\
This script creates the database and tables in MySQL used by the
ViewVC checkin database. You will be prompted for: database server
hostname, database user, database user password, and database name.
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.
"""
DATABASE_SCRIPT="""\
DROP DATABASE IF EXISTS <dbname>;
CREATE DATABASE <dbname>;
USE <dbname>;
"""
## ------------------------------------------------------------------------
## Version 0: The original, Bonsai-compatible schema.
##
DATABASE_SCRIPT_VERSION_0="""\
DROP TABLE IF EXISTS branches;
CREATE TABLE branches (
id mediumint(9) NOT NULL auto_increment,
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,232 +118,31 @@ CREATE TABLE tags (
KEY dirid (dirid),
KEY fileid (fileid),
KEY branchid (branchid)
) ENGINE=MyISAM;
) TYPE=MyISAM;
"""
## ------------------------------------------------------------------------
## Version 1: Adds the 'metadata' table. Adds 'descid' index to
## 'checkins' table, and renames that table to 'commits'.
##
DATABASE_SCRIPT_VERSION_1="""\
DROP TABLE IF EXISTS branches;
CREATE TABLE branches (
id mediumint(9) NOT NULL auto_increment,
branch varchar(64) binary DEFAULT '' NOT NULL,
PRIMARY KEY (id),
UNIQUE branch (branch)
) ENGINE=MyISAM;
DROP TABLE IF EXISTS commits;
CREATE TABLE commits (
type enum('Change','Add','Remove'),
ci_when datetime DEFAULT '0000-00-00 00:00:00' NOT NULL,
whoid mediumint(9) DEFAULT '0' NOT NULL,
repositoryid mediumint(9) DEFAULT '0' NOT NULL,
dirid mediumint(9) DEFAULT '0' NOT NULL,
fileid mediumint(9) DEFAULT '0' NOT NULL,
revision varchar(32) binary DEFAULT '' NOT NULL,
stickytag varchar(255) binary DEFAULT '' NOT NULL,
branchid mediumint(9) DEFAULT '0' NOT NULL,
addedlines int(11) DEFAULT '0' NOT NULL,
removedlines int(11) DEFAULT '0' NOT NULL,
descid mediumint(9),
UNIQUE repositoryid (repositoryid,dirid,fileid,revision),
KEY ci_when (ci_when),
KEY whoid (whoid),
KEY repositoryid_2 (repositoryid),
KEY dirid (dirid),
KEY fileid (fileid),
KEY branchid (branchid),
KEY descid (descid)
) ENGINE=MyISAM;
DROP TABLE IF EXISTS descs;
CREATE TABLE descs (
id mediumint(9) NOT NULL auto_increment,
description text,
hash bigint(20) DEFAULT '0' NOT NULL,
PRIMARY KEY (id),
KEY hash (hash)
) ENGINE=MyISAM;
DROP TABLE IF EXISTS dirs;
CREATE TABLE dirs (
id mediumint(9) NOT NULL auto_increment,
dir varchar(255) binary DEFAULT '' NOT NULL,
PRIMARY KEY (id),
UNIQUE dir (dir)
) ENGINE=MyISAM;
DROP TABLE IF EXISTS files;
CREATE TABLE files (
id mediumint(9) NOT NULL auto_increment,
file varchar(255) binary DEFAULT '' NOT NULL,
PRIMARY KEY (id),
UNIQUE file (file)
) ENGINE=MyISAM;
DROP TABLE IF EXISTS people;
CREATE TABLE people (
id mediumint(9) NOT NULL auto_increment,
who varchar(128) binary DEFAULT '' NOT NULL,
PRIMARY KEY (id),
UNIQUE who (who)
) ENGINE=MyISAM;
DROP TABLE IF EXISTS repositories;
CREATE TABLE repositories (
id mediumint(9) NOT NULL auto_increment,
repository varchar(64) binary DEFAULT '' NOT NULL,
PRIMARY KEY (id),
UNIQUE repository (repository)
) ENGINE=MyISAM;
DROP TABLE IF EXISTS tags;
CREATE TABLE tags (
repositoryid mediumint(9) DEFAULT '0' NOT NULL,
branchid mediumint(9) DEFAULT '0' NOT NULL,
dirid mediumint(9) DEFAULT '0' NOT NULL,
fileid mediumint(9) DEFAULT '0' NOT NULL,
revision varchar(32) binary DEFAULT '' NOT NULL,
UNIQUE repositoryid (repositoryid,dirid,fileid,branchid,revision),
KEY repositoryid_2 (repositoryid),
KEY dirid (dirid),
KEY fileid (fileid),
KEY branchid (branchid)
) ENGINE=MyISAM;
DROP TABLE IF EXISTS metadata;
CREATE TABLE metadata (
name varchar(255) binary DEFAULT '' NOT NULL,
value text,
PRIMARY KEY (name),
UNIQUE name (name)
) ENGINE=MyISAM;
INSERT INTO metadata (name, value) VALUES ('version', '1');
"""
BONSAI_COMPAT="""
WARNING: Creating Bonsai-compatible legacy database version. Some ViewVC
features may not be available, or may not perform especially well.
"""
## ------------------------------------------------------------------------
def usage_and_exit(errmsg=None):
stream = errmsg is None and sys.stdout or sys.stderr
stream.write("""\
Usage: %s [OPTIONS]
This script creates the database and tables in MySQL used by the
ViewVC checkin database. In order to operate correctly, it needs to
know the following: your database server hostname, database user,
database user password, and database name. (You will be prompted for
any of this information that you do not provide via command-line
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.
[Default: ViewVC]
--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.
--password=ARG Use ARG as the password for the MySQL connection.
--username=ARG Use ARG as the username for the MySQL connection.
--version=ARG Create the database using the schema employed by
version ARG of ViewVC. Valid values are:
[ "1.0" ]
""" % (os.path.basename(sys.argv[0])))
if errmsg is not None:
stream.write("[ERROR] %s.\n" % (errmsg))
sys.exit(1)
sys.exit(0)
if __name__ == "__main__":
try:
# Parse the command-line options, if any.
dbname = version = hostname = port = username = password = None
opts, args = getopt.getopt(sys.argv[1:], '', [ 'dbname=',
'help',
'hostname=',
'port=',
'password=',
'username=',
'version=',
])
if len(args) > 0:
usage_and_exit("Unexpected command-line parameters")
for name, value in opts:
if name == '--help':
usage_and_exit(0)
elif name == '--dbname':
dbname = value
elif name == '--hostname':
hostname = value
elif name == '--port':
port = value
elif name == '--username':
username = value
elif name == '--password':
password = value
elif name == '--version':
if value in ["1.0"]:
version = value
else:
usage_and_exit("Invalid version specified")
print INTRO_TEXT
# 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): ")
if username is None:
username = raw_input("MySQL User: ")
if password is None:
password = raw_input("MySQL Password: ")
if dbname is None:
dbname = raw_input("ViewVC Database Name [default: ViewVC]: ") or "ViewVC"
# Prompt for necessary information
host = raw_input("MySQL Hostname [default: localhost]: ") or ""
user = raw_input("MySQL User: ")
passwd = raw_input("MySQL Password: ")
dbase = raw_input("ViewVC Database Name [default: ViewVC]: ") or "ViewVC"
# Create the database.
dscript = string.replace(DATABASE_SCRIPT_COMMON, "<dbname>", dbname)
if version == "1.0":
print BONSAI_COMPAT
dscript = dscript + DATABASE_SCRIPT_VERSION_0
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)
# Create the database
dscript = string.replace(DATABASE_SCRIPT, "<dbname>", dbase)
host_option = host and "--host=%s" % (host) or ""
if sys.platform == "win32":
cmd = "mysql %s" % (cmd_args)
cmd = "mysql --user=%s --password=%s %s "\
% (user, passwd, 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" \
% (user, passwd, host_option)
pipes = popen2.Popen3(cmd)
pipes.tochild.write(dscript)
pipes.tochild.close()
@@ -351,11 +150,10 @@ if __name__ == "__main__":
status = pipes.wait()
if status:
print "[ERROR] The database did not create sucessfully."
print "[ERROR] the database did not create sucessfully."
sys.exit(1)
print "Database created successfully. Don't forget to configure the "
print "[cvsdb] section of your viewvc.conf file."
print "Database created successfully."
except KeyboardInterrupt:
pass
sys.exit(0)

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-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
@@ -65,10 +65,7 @@ cfg = viewvc.load_config(CONF_PATHNAME)
def index(req):
server = sapi.ModPythonServer(req)
try:
viewvc_base_url = cfg.query.viewvc_base_url
if viewvc_base_url is None:
viewvc_base_url = "viewvc.py"
query.main(server, cfg, viewvc_base_url)
query.main(server, cfg, "viewvc.py")
finally:
server.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

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,6 @@
#!/usr/bin/env python
# -*-python-*-
#
# Copyright (C) 2004-2013 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 +152,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 +163,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))
@@ -254,35 +241,23 @@ def main(command, repository, revs=[], verbose=0, force=0):
cfg = viewvc.load_config(CONF_PATHNAME)
db = cvsdb.ConnectDatabase(cfg)
# Purge what must be purged.
if command in ('rebuild', 'purge'):
if verbose:
print "Purging commit info for repository root `%s'" % repository
try:
db.PurgeRepository(repository)
except cvsdb.UnknownRepositoryError, e:
if command == 'purge':
sys.stderr.write("ERROR: " + str(e) + "\n")
sys.exit(1)
db.PurgeRepository(repository)
# Record what must be recorded.
if command in ('rebuild', 'update'):
if not os.path.exists(repository):
sys.stderr.write('ERROR: could not find repository %s\n'
% (repository))
sys.exit(1)
repo = SvnRepo(repository)
if command == 'rebuild' or (command == 'update' and not revs):
for rev in range(repo.rev_max+1):
handle_revision(db, command, repo, rev, verbose)
elif command == 'update':
if revs[0] is None:
revs[0] = repo.rev_max
if revs[1] is None:
revs[1] = repo.rev_max
revs.sort()
for rev in range(revs[0], revs[1]+1):
handle_revision(db, command, repo, rev, verbose, force)
repo = SvnRepo(repository)
if command == 'rebuild' or (command == 'update' and not revs):
for rev in range(repo.rev_max+1):
handle_revision(db, command, repo, rev, verbose)
elif command == 'update':
if revs[0] is None:
revs[0] = repo.rev_max
if revs[1] is None:
revs[1] = repo.rev_max
revs.sort()
for rev in range(revs[0], revs[1]+1):
handle_revision(db, command, repo, rev, verbose, force)
def _rev2int(r):
if r == 'HEAD':
@@ -300,7 +275,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
@@ -349,6 +324,12 @@ if __name__ == '__main__':
sys.stderr.write('ERROR: unknown command %s\n' % command)
usage()
repository = args[2]
if not os.path.exists(repository):
sys.stderr.write('ERROR: could not find repository %s\n' % args[2])
usage()
repository = vclib.svn.canonicalize_rootpath(repository)
revs = []
if len(sys.argv) > 3:
if command == 'rebuild':
@@ -371,7 +352,6 @@ if __name__ == '__main__':
rev = None
try:
repository = vclib.svn.canonicalize_rootpath(args[2])
repository = cvsdb.CleanRepository(os.path.abspath(repository))
main(command, repository, revs, verbose, force)
except KeyboardInterrupt:

View File

@@ -1,54 +0,0 @@
#!/usr/bin/python
# -*-python-*-
#
# Copyright (C) 1999-2013 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
# distribution or at http://viewvc.org/license-1.html.
#
# For more information, visit http://viewvc.org/
#
# -----------------------------------------------------------------------
#
# viewvc: View CVS/SVN repositories via a web browser
#
# -----------------------------------------------------------------------
#
# This is a fcgi entry point for the query ViewVC app. It's appropriate
# for use with mod_fcgid and flup. It defines a single application function
# that is a valid fcgi entry point.
#
# mod_fcgid: http://httpd.apache.org/mod_fcgid/
# flup:
# http://pypi.python.org/pypi/flup
# http://trac.saddi.com/flup
#
# -----------------------------------------------------------------------
import sys, os
LIBRARY_DIR = None
CONF_PATHNAME = None
if LIBRARY_DIR:
sys.path.insert(0, LIBRARY_DIR)
else:
sys.path.insert(0, os.path.abspath(os.path.join(sys.argv[0],
"../../../lib")))
import sapi
import viewvc
import query
from flup.server import fcgi
def application(environ, start_response):
server = sapi.WsgiServer(environ, start_response)
cfg = viewvc.load_config(CONF_PATHNAME, server)
viewvc_base_url = cfg.query.viewvc_base_url
if viewvc_base_url is None:
viewvc_base_url = "viewvc.fcgi"
query.main(server, cfg, viewvc_base_url)
return []
fcgi.WSGIServer(application).run()

View File

@@ -1,45 +0,0 @@
# -*-python-*-
#
# Copyright (C) 1999-2013 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
# distribution or at http://viewvc.org/license-1.html.
#
# For more information, visit http://viewvc.org/
#
# -----------------------------------------------------------------------
#
# viewvc: View CVS/SVN repositories via a web browser
#
# -----------------------------------------------------------------------
#
# This is a wsgi entry point for the query ViewVC app. It's appropriate
# for use with mod_wsgi. It defines a single application function that
# is a valid wsgi entry point.
#
# -----------------------------------------------------------------------
import sys, os
LIBRARY_DIR = None
CONF_PATHNAME = None
if LIBRARY_DIR:
sys.path.insert(0, LIBRARY_DIR)
else:
sys.path.insert(0, os.path.abspath(os.path.join(sys.argv[0],
"../../../lib")))
import sapi
import viewvc
import query
def application(environ, start_response):
server = sapi.WsgiServer(environ, start_response)
cfg = viewvc.load_config(CONF_PATHNAME, server)
viewvc_base_url = cfg.query.viewvc_base_url
if viewvc_base_url is None:
viewvc_base_url = "viewvc.wsgi"
query.main(server, cfg, viewvc_base_url)
return []

View File

@@ -1,50 +0,0 @@
#!/usr/bin/python
# -*-python-*-
#
# Copyright (C) 1999-2013 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
# distribution or at http://viewvc.org/license-1.html.
#
# For more information, visit http://viewvc.org/
#
# -----------------------------------------------------------------------
#
# viewvc: View CVS/SVN repositories via a web browser
#
# -----------------------------------------------------------------------
#
# This is a fcgi entry point for the main ViewVC app. It's appropriate
# for use with mod_fcgid and flup. It defines a single application function
# that is a valid fcgi entry point.
#
# mod_fcgid: http://httpd.apache.org/mod_fcgid/
# flup:
# http://pypi.python.org/pypi/flup
# http://trac.saddi.com/flup
#
# -----------------------------------------------------------------------
import sys, os
LIBRARY_DIR = None
CONF_PATHNAME = None
if LIBRARY_DIR:
sys.path.insert(0, LIBRARY_DIR)
else:
sys.path.insert(0, os.path.abspath(os.path.join(sys.argv[0],
"../../../lib")))
import sapi
import viewvc
from flup.server import fcgi
def application(environ, start_response):
server = sapi.WsgiServer(environ, start_response)
cfg = viewvc.load_config(CONF_PATHNAME, server)
viewvc.main(server, cfg)
return []
fcgi.WSGIServer(application).run()

View File

@@ -1,41 +0,0 @@
# -*-python-*-
#
# Copyright (C) 1999-2013 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
# distribution or at http://viewvc.org/license-1.html.
#
# For more information, visit http://viewvc.org/
#
# -----------------------------------------------------------------------
#
# viewvc: View CVS/SVN repositories via a web browser
#
# -----------------------------------------------------------------------
#
# This is a wsgi entry point for the main ViewVC app. It's appropriate
# for use with mod_wsgi. It defines a single application function that
# is a valid wsgi entry point.
#
# -----------------------------------------------------------------------
import sys, os
LIBRARY_DIR = None
CONF_PATHNAME = None
if LIBRARY_DIR:
sys.path.insert(0, LIBRARY_DIR)
else:
sys.path.insert(0, os.path.abspath(os.path.join(sys.argv[0],
"../../../lib")))
import sapi
import viewvc
def application(environ, start_response):
server = sapi.WsgiServer(environ, start_response)
cfg = viewvc.load_config(CONF_PATHNAME, server)
viewvc.main(server, cfg)
return []

View File

@@ -1,32 +0,0 @@
#---------------------------------------------------------------------------
#
# MIME type mapping file for ViewVC
#
# Information on ViewVC is located at the following web site:
# http://viewvc.org/
#
#---------------------------------------------------------------------------
# THE FORMAT OF THIS FILE
#
# This file contains records -- one per line -- of the following format:
#
# MIME_TYPE [EXTENSION [EXTENSION ...]]
#
# where whitespace separates the MIME_TYPE from the EXTENSION(s),
# and the EXTENSIONs from each other.
#
# For example:
#
# text/x-csh csh
# text/x-csrc c
# text/x-diff diff patch
# image/png png
# image/jpeg jpeg jpg jpe
#
# By default, this file is left empty, allowing ViewVC to continue
# consulting it first without overriding the MIME type mappings
# found in more standard mapping files (such as those provided as
# part of the operating system or web server software).
#
#

File diff suppressed because it is too large Load Diff

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>
@@ -161,6 +161,12 @@ td {
resource. Valid only when <var>pathtype</var> is <tt>file</tt>
or (for Subversion roots) <tt>dir</tt>.</td>
</tr>
<tr class="varlevel1">
<td class="varname">log_rev_href</td>
<td>String</td>
<td>Revision number of the file-revision currently being viewed, or
None.</td>
</tr>
<tr class="varlevel1">
<td class="varname">nav_path</td>
<td>List</td>
@@ -315,7 +321,7 @@ td {
<tr class="varlevel1">
<td class="varname">pathrev_hidden_values</td>
<td>List</td>
<td>Hidden field name/value pairs for the revision/tag selection form.</td>
<td>Hidden value name/value pairs for the revision/tag selection form.</td>
</tr>
<tr class="varlevel1">
<td class="varname">pathrev_clear_action</td>
@@ -325,7 +331,7 @@ td {
<tr class="varlevel1">
<td class="varname">pathrev_clear_hidden_values</td>
<td>List</td>
<td>Hidden field name/value pairs for the path revision clear button.</td>
<td>Hidden value name/value pairs for the path revision clear button.</td>
</tr>
</tbody>
</table>
@@ -444,10 +450,10 @@ td {
<tr class="varlevel1">
<td class="varname">annotation</td>
<td>String</td>
<td>Valid values are "none" (no annotations were attempted),
"annotated" (annotation was successful), "binary" (file contents
are not line-based and human-readable), and "error" (something
went wrong during annotation).</td>
<td>If set, indicates that annotations were requested. Valid values
are "annotated" (annotation was successful), "binary" (file contents
are not line-based and human-readable), and "error" (something went
wrong during annotation).</td>
</tr>
<tr class="varlevel1">
<td class="varname">author</td>
@@ -719,29 +725,19 @@ td {
<tr class="varlevel1">
<td class="varname">diff_format_hidden_values</td>
<td>List</td>
<td>Hidden field name/value pairs for the diff format selection form.</td>
<td>Hidden value name/value pairs for the diff format selection form.</td>
</tr>
<tr class="varlevel1">
<td class="varname">left</td>
<td>Container</td>
<td>Container object for grouping information about the left file.</td>
</tr>
<tr class="varlevel2">
<td class="varname">left.ago</td>
<td>String</td>
<td>Text description of the time elapsed since <var>left.date</date>.</td>
</tr>
<tr class="varlevel2">
<td class="varname">left.annotate_href</td>
<td>String</td>
<td>URL of the ViewVC annotation view for the left file.
Valid only when <var>entries.pathtype</var> is <tt>file</tt>.</td>
</tr>
<tr class="varlevel2">
<td class="varname">left.author</td>
<td>String</td>
<td>Author of the revision of the left file.</td>
</tr>
<tr class="varlevel2">
<td class="varname">left.date</td>
<td>String</td>
@@ -759,11 +755,6 @@ td {
<td>URL to download the HEAD revision of the left file as
<tt>text/plain</tt>.</td>
</tr>
<tr class="varlevel2">
<td class="varname">left.log</td>
<td>String</td>
<td>Log message of the left file revision.</td>
</tr>
<tr class="varlevel2">
<td class="varname">left.path</td>
<td>String</td>
@@ -787,11 +778,6 @@ td {
current revision. Valid only when <var>roottype</var> is
<tt>svn</tt>.</td>
</tr>
<tr class="varlevel2">
<td class="varname">left.size</td>
<td>String</td>
<td>Size of the left file revision, in bytes. Subversion only.</td>
</tr>
<tr class="varlevel2">
<td class="varname">left.tag</td>
<td>String</td>
@@ -802,11 +788,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>
@@ -818,22 +799,12 @@ td {
<td>Container</td>
<td>Container object for grouping information about the right file.</td>
</tr>
<tr class="varlevel2">
<td class="varname">right.author</td>
<td>String</td>
<td>Author of the revision of the right file.</td>
</tr>
<tr class="varlevel2">
<td class="varname">right.annotate_href</td>
<td>String</td>
<td>URL of the ViewVC annotation view for the right file.
Valid only when <var>entries.pathtype</var> is <tt>file</tt>.</td>
</tr>
<tr class="varlevel2">
<td class="varname">right.author</td>
<td>String</td>
<td>Author of the revision of the right file.</td>
</tr>
<tr class="varlevel2">
<td class="varname">right.date</td>
<td>String</td>
@@ -851,11 +822,6 @@ td {
<td>URL to download the HEAD revision of the right file as
<tt>text/plain</tt>.</td>
</tr>
<tr class="varlevel2">
<td class="varname">right.log</td>
<td>String</td>
<td>Log message of the right file revision.</td>
</tr>
<tr class="varlevel2">
<td class="varname">right.path</td>
<td>String</td>
@@ -879,11 +845,6 @@ td {
current revision. Valid only when <var>roottype</var> is
<tt>svn</tt>.</td>
</tr>
<tr class="varlevel2">
<td class="varname">right.size</td>
<td>String</td>
<td>Size of the right file revision, in bytes. Subversion only.</td>
</tr>
<tr class="varlevel2">
<td class="varname">right.tag</td>
<td>String</td>
@@ -944,7 +905,7 @@ td {
<tr class="varlevel1">
<td class="varname">dir_paging_hidden_values</td>
<td>List</td>
<td>Hidden field name/value pairs for the page selection form.</td>
<td>Hidden value name/value pairs for the page selection form.</td>
</tr>
<tr class="varlevel1">
<td class="varname">entries</td>
@@ -1126,16 +1087,23 @@ td {
<td>String</td>
<td>Current search expression, if any.</td>
</tr>
<tr class="varlevel1">
<td class="varname">search_re_form</td>
<td>Boolean</td>
<td>Indicates whether or not to display the regular expression search
form. Value depends on the whether searching is enabled in the
configuration and whether or not the current directory is
empty.</td>
</tr>
<tr class="varlevel1">
<td class="varname">search_re_action</td>
<td>String</td>
<td>Form action URL for the regular expression search form,
if searching is available.</td>
<td>Form action URL for the regular expression search form.</td>
</tr>
<tr class="varlevel1">
<td class="varname">search_re_hidden_values</td>
<td>List</td>
<td>Hidden field name/value pairs for the regular expression search form.</td>
<td>Hidden value name/value pairs for the regular expression search form.</td>
</tr>
<tr class="varlevel1">
<td class="varname">show_attic_href</td>
@@ -1281,7 +1249,7 @@ td {
<tr class="varlevel1">
<td class="varname">diff_select_hidden_values</td>
<td>List</td>
<td>Hidden field name/value pairs for the diff selection form.</td>
<td>Hidden value name/value pairs for the diff selection form.</td>
</tr>
<tr class="varlevel1">
<td class="varname">entries</td>
@@ -1562,7 +1530,7 @@ td {
<tr class="varlevel1">
<td class="varname">log_paging_hidden_values</td>
<td>List</td>
<td>Hidden field name/value pairs for the page selection form.</td>
<td>Hidden value name/value pairs for the page selection form.</td>
</tr>
<tr class="varlevel1">
<td class="varname">logsort</td>
@@ -1578,7 +1546,7 @@ td {
<tr class="varlevel1">
<td class="varname">logsort_hidden_values</td>
<td>List</td>
<td>Hidden field name/value pairs for the log sort drop down box</td>
<td>Hidden value name/value pairs for the log sort drop down box</td>
</tr>
<tr class="varlevel1">
<td class="varname">mime_type</td>
@@ -1826,14 +1794,6 @@ td {
<td>Indicates how query results are being sorted. Possible values:
<tt>date</tt>, <tt>author</tt>, and <tt>file</tt>.</td>
</tr>
<tr class="varlevel1">
<td class="varname">row_limit_reached</td>
<td>Boolean</td>
<td>Indicates whether the internal database row limit threshold (set
via the <code>cvsdb.row_limit</code>
and <code>cvsdb.rss_row_limit</code> configuration options) was
reached by the query.</td>
</tr>
<tr class="varlevel1">
<td class="varname">show_branch</td>
<td>Boolean</td>
@@ -1950,7 +1910,7 @@ td {
<tr class="varlevel1">
<td class="varname">query_hidden_values</td>
<td>List</td>
<td>Hidden field name/value pairs for query form.</td>
<td>Hidden value name/value pairs for query form.</td>
</tr>
<tr class="varlevel1">
<td class="varname">querysort</td>
@@ -2090,7 +2050,7 @@ td {
<tr class="varlevel1">
<td class="varname">jump_rev_hidden_values</td>
<td>List</td>
<td>Hidden field name/value pairs for revision jump form.</td>
<td>Hidden value name/value pairs for revision jump form.</td>
</tr>
<tr class="varlevel1">
<td class="varname">limit_changes</td>
@@ -2113,11 +2073,6 @@ td {
<td>String</td>
<td>URL for the current view but with <tt>limit_changes</tt> disabled.</td>
</tr>
<tr class="varlevel1">
<td class="varname">num_changes</td>
<td>String</td>
<td>Number of paths changed in this revision.</td>
</tr>
<tr class="varlevel1">
<td class="varname">next_href</td>
<td>String</td>
@@ -2157,38 +2112,6 @@ td {
<td>List</td>
<td>Set of configured viewable repositories.</td>
</tr>
<tr class="varlevel2">
<td class="varname">roots.ago</td>
<td>String</td>
<td>Textual description of the time since <var>roots.date</var>.</td>
</tr>
<tr class="varlevel2">
<td class="varname">roots.author</td>
<td>String</td>
<td>Username of the last modifier of the root.</td>
</tr>
<tr class="varlevel2">
<td class="varname">root.date</td>
<td>String</td>
<td>Date (in UTC if not otherwise configured) of the last
modification of the root.</td>
</tr>
<tr class="varlevel2">
<td class="varname">roots.href</td>
<td>String</td>
<td>URL of root directory view for a configured repository.</td>
</tr>
<tr class="varlevel2">
<td class="varname">roots.log</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>
@@ -2202,24 +2125,17 @@ td {
configuration can have negative security implications. Use this
token at your own risk.</td>
</tr>
<tr class="varlevel2">
<td class="varname">roots.rev</td>
<td>String</td>
<td>Youngest revision of the root.</td>
</tr>
<tr class="varlevel2">
<td class="varname">roots.short_log</td>
<td>String</td>
<td>Log message of last modification to the root, truncated to
contain no more than the number of characters specified by
the <code>short_log_len</code> configuration option.</td>
</tr>
<tr class="varlevel2">
<td class="varname">roots.type</td>
<td>String</td>
<td>Version control type of a configured repository. Valid
values: <tt>cvs</tt>, <tt>svn</tt>.</td>
</tr>
<tr class="varlevel2">
<td class="varname">roots.href</td>
<td>String</td>
<td>URL of root directory view for a configured repository.</td>
</tr>
</tbody>
</table>

View File

@@ -137,8 +137,9 @@ td {
configuration's "general" section.</li>
<li>Finally, ensure that that the new <code>authorizer</code>
option is set to either "forbidden" or "forbiddenre", depending
on which of those you were using in ViewVC 1.0.x.</li>
option is set to either "forbidden" (which is the default) or
"forbiddenre", depending on which of those you were using in
ViewVC 1.0.x.</li>
</ol>
@@ -214,9 +215,10 @@ td {
<li>options/py2html_path</li>
<li>options/use_enscript</li>
<li>options/use_highlight</li>
<li>options/use_pagesize</li>
<li>options/use_php</li>
<li>options/use_py2html</li>
<li>options/use_pygments</li>
<li>options/use_source_highlight</li>
</ul>
</div>
@@ -224,23 +226,16 @@ td {
<div class="h3">
<h3>Checkin Database</h3>
<p>ViewVC 1.1 introduces to the <code>cvsdbadmin</code>
and <code>svndbadmin</code> tools a new "purge" operation, which
allows you to remove all the information related to a given root
from your checkins database (without disturbing the information
associated with other roots). Likewise, the "rebuild" command in
those tools now implies a "purge" followed by an update.</p>
<p>As a related change, the <code>svndbadmin</code> program's
"rebuild" subcommand has had its purpose become more defined. It
no longer accepts a revision argument, and therefore can now only
be used to completely rebuild the entirety of the checkin database
information for a Subversion repository (instead of being able to
only update the information related to single Subversion revision).
For per-revision updating, use <code>svndbadmin update</code> and
<p>In ViewVC 1.1, the <code>svndbadmin</code> program's "rebuild"
subcommand has had its purpose become more defined. It no longer
accepts a revision argument, and therefore can now only be used to
completely rebuild the entirety of the checkin database information
for a Subversion repository (instead of being able to only update
the information related to single Subversion revision). For
per-revision updating, use <code>svndbadmin update</code> and
provide a revision (or revision range). And to get the previous
rebuild-a-revision effect, pass the new <code>--force</code> option
to <code>svndbadmin update</code>.</p>
rebuild-a-revision effect, pass the new <code>--force</code>
option to <code>svndbadmin update</code>.</p>
<p>In other words, where you once did this:</p>
@@ -254,19 +249,6 @@ td {
</pre>
</blockquote>
<p>To enhance the performance of the new "purge" operation, ViewVC 1.1
introduces some slight changes to the checkin database schema. If
you use the <code>make-database</code> tool to (re)create your
checkins database, it will by default employ the new database
schema. This should cause the database to be virtually unusable by
previous versions of ViewVC, and that's by design. If, however,
you need to (re)create your checkins database and you require
compatibility with previous versions of ViewVC or ViewCVS, simply
pass the "--version=1.0" option to the <code>make-database</code>
script. Note that your "purge" and "rebuild" operations could be
abysmally slow, though, as that version of the database schema is
not optimized for those operations.</p>
</div>
<div class="h3">
@@ -314,22 +296,6 @@ td {
<li>options/allow_tar</li>
</ul>
<p>ViewVC now honors the "svn:mime-type" property stored on
Subversion-versioned files as the primary source of MIME type
determination (before falling back to name-based MIME mappings and
such). However, this can negatively affect the viewability of
certain files &mdash; especially images &mdash; whose
"svn:mime-type" properties are set incorrectly, such as will happen
if Subversion itself merely determines that the file isn't
human-readable and uses the "application/octet-stream" MIME type to
record this determination. To make ViewVC <em>not</em> honor the
"svn:mime-type" property value, set the <code>svn_ignore_mimetype</code>
configuration option.</p>
<p>Speaking of MIME types, the option <code>mime_types_file</code> is
now <code>mime_types_files</code>, as it now carries multiple paths
to MIME mappings files, ordered by preference.</p>
<p>The <code>use_rcsparse</code> option was moved from the "general"
section to the "options" section.</p>
@@ -348,7 +314,7 @@ td {
all = viewvc.*
[all-options]
allowed_views = annotate, diff, markup, tar
allow_tar = 1
</pre>
</blockquote>
@@ -358,7 +324,7 @@ allowed_views = annotate, diff, markup, tar
all = viewvc.*
[vhost-all/options]
allowed_views = annotate, diff, markup, tar
allow_tar = 1
</pre>
</blockquote>
@@ -665,11 +631,6 @@ allowed_views = annotate, diff, markup, tar
<td>revision.ezt</td>
<td>now is an iterable list of objects with .name and .value attributes</td>
</tr>
<tr class="added">
<td class="varname">num_changes</td>
<td>revision.ezt</td>
<td>added</td>
</tr>
</tbody>
</table>
@@ -698,7 +659,7 @@ allowed_views = annotate, diff, markup, tar
<h3>Checkin Database</h3>
<p>ViewVC 1.0 reads and writes commit times in the MySQL database in
UTC time rather than local time. This can cause times displayed on
UTC time rather than local time. This can cause times displayed on
the query page to be a few hours off if an old database is being
used with a new version of ViewVC. The best way to fix this is to
rebuild the database with the new version of cvsdbadmin, but it
@@ -741,8 +702,7 @@ allowed_views = annotate, diff, markup, tar
<li>options/root_as_url_component</li>
<li>options/default_file_view</li>
<li>options/sort_group_dirs</li>
<li>options/dir_pagesize</li>
<li>options/log_pagesize</li>
<li>options/use_pagesize</li>
<li>options/limit_changes</li>
<li>options/use_localtime</li>
<li>options/cross_copies</li>

View File

@@ -996,7 +996,7 @@ th.caption {
<td>file query string</td>
</tr>
<tr>
<td><code>file_match=<var>FILE_MATCH</var></code></td>
<td><code>file_match=FILE_MATCH</code></td>
<td>optional</td>
<td>"exact" "like" "glob" "regex" or "notregex" determining type
of file match</td>
@@ -1007,7 +1007,7 @@ th.caption {
<td>author query string</td>
</tr>
<tr>
<td><code>who_match=<var>WHO_MATCH</var></code></td>
<td><code>who_match=WHO_MATCH</code></td>
<td>optional</td>
<td>"exact" "like" "glob" "regex" or "notregex" determining type
of author match</td>
@@ -1024,36 +1024,36 @@ th.caption {
of log message match</td>
</tr>
<tr>
<td><code>querysort=<var>SORT</var></code></td>
<td><code>querysort=SORT</code></td>
<td>optional</td>
<td>"date" "author" or "file" determining order of query results</td>
</tr>
<tr>
<td><code>date=<var>DATE</var></code></td>
<td><code>date=DATE</code></td>
<td>optional</td>
<td>"hours" "day" "week" "month" "all" or "explicit" to filter
query results by date</td>
</tr>
<tr>
<td><code>hours=<var>HOURS</var></code></td>
<td><code>hours=HOURS</code></td>
<td>optional</td>
<td>number of hours back to include results from when
<code><var>DATE</var></code> is "hours"</td>
</tr>
<tr>
<td><code>mindate=<var>MINDATE</var></code></td>
<td><code>mindate=MINDATE</code></td>
<td>optional</td>
<td>earliest date to include results from when
<code><var>DATE</var></code> is "explicit"</td>
</tr>
<tr>
<td><code>maxdate=<var>MAXDATE</var></code></td>
<td><code>maxdate=MAXDATE</code></td>
<td>optional</td>
<td>latest date to include results from when
<code><var>DATE</var></code> is "explicit"</td>
</tr>
<tr>
<td><code>limit_changes=<var>LIMIT_CHANGES</var></code></td>
<td><code>limit_changes=LIMIT_CHANGES</code></td>
<td>optional</td>
<td>maximum number of files to list per commit in query
results. Default is value of <code>limit_changes</code>
@@ -1113,7 +1113,7 @@ th.caption {
<td>branch query string</td>
</tr>
<tr>
<td><code>branch_match=<var>BRANCH_MATCH</var></code></td>
<td><code>branch_match=BRANCH_MATCH</code></td>
<td>optional</td>
<td>"exact" "like" "glob" "regex" or "notregex" determining type
of branch match</td>
@@ -1129,7 +1129,7 @@ th.caption {
<td>file query string</td>
</tr>
<tr>
<td><code>file_match=<var>FILE_MATCH</var></code></td>
<td><code>file_match=FILE_MATCH</code></td>
<td>optional</td>
<td>"exact" "like" "glob" "regex" or "notregex" determining type
of file match</td>
@@ -1140,7 +1140,7 @@ th.caption {
<td>author query string</td>
</tr>
<tr>
<td><code>who_match=<var>WHO_MATCH</var></code></td>
<td><code>who_match=WHO_MATCH</code></td>
<td>optional</td>
<td>"exact" "like" "glob" "regex" or "notregex" determining type
of author match</td>
@@ -1157,43 +1157,50 @@ th.caption {
of log message match</td>
</tr>
<tr>
<td><code>querysort=<var>SORT</var></code></td>
<td><code>querysort=SORT</code></td>
<td>optional</td>
<td>"date" "author" or "file" determining order of query results</td>
</tr>
<tr>
<td><code>date=<var>DATE</var></code></td>
<td><code>date=DATE</code></td>
<td>optional</td>
<td>"hours" "day" "week" "month" "all" or "explicit" to filter
query results by date</td>
</tr>
<tr>
<td><code>hours=<var>HOURS</var></code></td>
<td><code>hours=HOURS</code></td>
<td>optional</td>
<td>number of hours back to include results from when
<code><var>DATE</var></code> is "hours"</td>
</tr>
<tr>
<td><code>mindate=<var>MINDATE</var></code></td>
<td><code>mindate=MINDATE</code></td>
<td>optional</td>
<td>earliest date to include results from when
<code><var>DATE</var></code> is "explicit"</td>
</tr>
<tr>
<td><code>maxdate=<var>MAXDATE</var></code></td>
<td><code>maxdate=MAXDATE</code></td>
<td>optional</td>
<td>latest date to include results from when
<code><var>DATE</var></code> is "explicit"</td>
</tr>
<tr>
<td><code>format=<var>FORMAT</var></code></td>
<td><code>format=FORMAT</code></td>
<td>optional</td>
<td>"rss" or "backout" values to generate an rss feed or list of
commands to back out changes instead showing a normal query result
page</td>
</tr>
<tr>
<td><code>limit_changes=<var>LIMIT_CHANGES</var></code></td>
<td><code>limit=LIMIT</code></td>
<td>optional</td>
<td>maximum number of file-revisions to process during a
query. Default is value of <code>row_limit</code> configuration
option</td>
</tr>
<tr>
<td><code>limit_changes=LIMIT_CHANGES</code></td>
<td>optional</td>
<td>maximum number of files to list per commit in query
results. Default is value of <code>limit_changes</code>
@@ -1247,7 +1254,7 @@ th.caption {
<td><a href="#revision-param"><code>revision</code> parameter</a></td>
</tr>
<tr>
<td><code>limit_changes=<var>LIMIT_CHANGES</var></code></td>
<td><code>limit_changes=LIMIT_CHANGES</code></td>
<td>optional</td>
<td>maximum number of files to list per commit. Default is value
of <code>limit_changes</code> configuration option</td>

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
@@ -39,7 +39,7 @@ def _parse(hdr, result):
while pos < len(hdr):
name = _re_token.match(hdr, pos)
if not name:
raise AcceptLanguageParseError()
raise AcceptParseError()
a = result.item_class(string.lower(name.group(1)))
pos = name.end()
while 1:
@@ -210,7 +210,7 @@ class _LanguageSelector:
def append(self, item):
self.requested.append(item)
class AcceptLanguageParseError(Exception):
class AcceptParseError(Exception):
pass
def _test():

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*-python-*-
#
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2007 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
@@ -32,8 +32,9 @@ import os
import re
import time
import math
import cgi
import vclib
import sapi
re_includes = re.compile('\\#(\\s*)include(\\s*)"(.*?)"')
@@ -74,15 +75,14 @@ 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)
diff_url = None
if item.prev_rev:
diff_url = '%sr1=%s&amp;r2=%s' % (self.diff_url, item.prev_rev, item.rev)
thisline = link_includes(sapi.escape(item.text), self.repos,
thisline = link_includes(cgi.escape(item.text), self.repos,
self.path_parts, self.include_url)
return _item(text=thisline, line_number=item.line_number,
rev=item.rev, prev_rev=item.prev_rev,

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-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
@@ -24,150 +24,45 @@ import fnmatch
#########################################################################
#
# CONFIGURATION
# -------------
#
# There are three forms of configuration:
#
# 1. edit the viewvc.conf created by the viewvc-install(er)
# 2. as (1), but delete all unchanged entries from viewvc.conf
# 3. do not use viewvc.conf and just edit the defaults in this file
# 1) edit the viewvc.conf created by the viewvc-install(er)
# 2) as (1), but delete all unchanged entries from viewvc.conf
# 3) do not use viewvc.conf and just edit the defaults in this file
#
# Most users will want to use (1), but there are slight speed advantages
# to the other two options. Note that viewvc.conf values are a bit easier
# to work with since it is raw text, rather than python literal values.
#
#
# A WORD ABOUT OPTION LAYERING/OVERRIDES
# --------------------------------------
#
# ViewVC has three "layers" of configuration options:
#
# 1. base configuration options - very basic configuration bits
# found in sections like 'general', 'options', etc.
# 2. vhost overrides - these options overlay/override the base
# configuration on a per-vhost basis.
# 3. root overrides - these options overlay/override the base
# configuration and vhost overrides on a per-root basis.
#
# Here's a diagram of the valid overlays/overrides:
#
# PER-ROOT PER-VHOST BASE
#
# ,-----------. ,-----------.
# | vhost-*/ | | |
# | general | --> | general |
# | | | |
# `-----------' `-----------'
# ,-----------. ,-----------. ,-----------.
# | root-*/ | | vhost-*/ | | |
# | options | --> | options | --> | options |
# | | | | | |
# `-----------' `-----------' `-----------'
# ,-----------. ,-----------. ,-----------.
# | root-*/ | | vhost-*/ | | |
# | templates | --> | templates | --> | templates |
# | | | | | |
# `-----------' `-----------' `-----------'
# ,-----------. ,-----------. ,-----------.
# | root-*/ | | vhost-*/ | | |
# | utilities | --> | utilities | --> | utilities |
# | | | | | |
# `-----------' `-----------' `-----------'
# ,-----------. ,-----------.
# | vhost-*/ | | |
# | cvsdb | --> | cvsdb |
# | | | |
# `-----------' `-----------'
# ,-----------. ,-----------. ,-----------.
# | root-*/ | | vhost-*/ | | |
# | authz-* | --> | authz-* | --> | authz-* |
# | | | | | |
# `-----------' `-----------' `-----------'
# ,-----------.
# | |
# | vhosts |
# | |
# `-----------'
# ,-----------.
# | |
# | query |
# | |
# `-----------'
#
# ### TODO: Figure out what this all means for the 'kv' stuff.
#
#########################################################################
class Config:
_base_sections = (
# Base configuration sections.
'authz-*',
'cvsdb',
'general',
'options',
'query',
'templates',
'utilities',
)
_force_multi_value = (
# Configuration values with multiple, comma-separated values.
'allowed_views',
'binary_mime_types',
'custom_log_formatting',
'cvs_roots',
'kv_files',
'languages',
'mime_types_files',
'root_parents',
'svn_roots',
)
_allowed_overrides = {
# Mapping of override types to allowed overridable sections.
'vhost' : ('authz-*',
'cvsdb',
'general',
'options',
'templates',
'utilities',
),
'root' : ('authz-*',
'options',
'templates',
'utilities',
)
}
_sections = ('general', 'utilities', 'options', 'cvsdb', 'templates')
_force_multi_value = ('cvs_roots', 'svn_roots', 'languages', 'kv_files',
'root_parents', 'allowed_views')
def __init__(self):
self.root_options_overlayed = 0
for section in self._base_sections:
if section[-1] == '*':
continue
for section in self._sections:
setattr(self, section, _sub_config())
def load_config(self, pathname, vhost=None):
"""Load the configuration file at PATHNAME, applying configuration
settings there as overrides to the built-in default values. If
VHOST is provided, also process the configuration overrides
specific to that virtual host."""
def load_config(self, pathname, vhost=None, rootname=None):
self.conf_path = os.path.isfile(pathname) and pathname or None
self.base = os.path.dirname(pathname)
self.parser = ConfigParser.ConfigParser()
self.parser.optionxform = lambda x: x # don't case-normalize option names.
self.parser.read(self.conf_path or [])
for section in self.parser.sections():
if self._is_allowed_section(section, self._base_sections):
for section in self._sections:
if self.parser.has_section(section):
self._process_section(self.parser, section, section)
if vhost and self.parser.has_section('vhosts'):
self._process_vhost(self.parser, vhost)
if rootname:
self._process_root_options(self.parser, rootname)
def load_kv_files(self, language):
"""Process the key/value (kv) files specified in the
configuration, merging their values into the configuration as
dotted heirarchical items."""
kv = _sub_config()
for fname in self.general.kv_files:
@@ -180,7 +75,6 @@ class Config:
fname = string.replace(fname, '%lang%', language)
parser = ConfigParser.ConfigParser()
parser.optionxform = lambda x: x # don't case-normalize option names.
parser.read(os.path.join(self.base, fname))
for section in parser.sections():
for option in parser.options(section):
@@ -198,12 +92,10 @@ class Config:
return kv
def path(self, path):
"""Return PATH relative to the config file directory."""
"""Return path relative to the config file directory"""
return os.path.join(self.base, path)
def _process_section(self, parser, section, subcfg_name):
if not hasattr(self, subcfg_name):
setattr(self, subcfg_name, _sub_config())
sc = getattr(self, subcfg_name)
for opt in parser.options(section):
@@ -216,54 +108,25 @@ class Config:
except ValueError:
pass
### FIXME: This feels like unnecessary depth of knowledge for a
### semi-generic configuration object.
if opt == 'cvs_roots' or opt == 'svn_roots':
value = _parse_roots(opt, value)
setattr(sc, opt, value)
def _is_allowed_section(self, section, allowed_sections):
"""Return 1 iff SECTION is an allowed section, defined as being
explicitly present in the ALLOWED_SECTIONS list or present in the
form 'someprefix-*' in that list."""
for allowed_section in allowed_sections:
if allowed_section[-1] == '*':
if _startswith(section, allowed_section[:-1]):
return 1
elif allowed_section == section:
return 1
return 0
def _is_allowed_override(self, sectype, secspec, section):
"""Test if SECTION is an allowed override section for sections of
type SECTYPE ('vhosts' or 'root', currently) and type-specifier
SECSPEC (a rootname or vhostname, currently). If it is, return
the overridden base section name. If it's not an override section
at all, return None. And if it's an override section but not an
allowed one, raise IllegalOverrideSection."""
cv = '%s-%s/' % (sectype, secspec)
lcv = len(cv)
if section[:lcv] != cv:
return None
base_section = section[lcv:]
if self._is_allowed_section(base_section,
self._allowed_overrides[sectype]):
return base_section
raise IllegalOverrideSection(sectype, section)
def _process_vhost(self, parser, vhost):
# Find a vhost name for this VHOST, if any (else, we've nothing to do).
# find a vhost name for this vhost, if any (if not, we've nothing to do)
canon_vhost = self._find_canon_vhost(parser, vhost)
if not canon_vhost:
return
# Overlay any option sections associated with this vhost name.
# overlay any option sections associated with this vhost name
cv = 'vhost-%s/' % (canon_vhost)
lcv = len(cv)
for section in parser.sections():
base_section = self._is_allowed_override('vhost', canon_vhost, section)
if base_section:
if section[:lcv] == cv:
base_section = section[lcv:]
if base_section not in self._sections:
raise IllegalOverrideSection('vhost', section)
self._process_section(parser, section, base_section)
def _find_canon_vhost(self, parser, vhost):
@@ -278,29 +141,26 @@ class Config:
return None
def _process_root_options(self, parser, rootname):
rn = 'root-%s/' % (rootname)
lrn = len(rn)
for section in parser.sections():
if section[:lrn] == rn:
base_section = section[lrn:]
if base_section in self._sections:
if base_section == 'general':
raise IllegalOverrideSection('root', section)
self._process_section(parser, section, base_section)
elif _startswith(base_section, 'authz-'):
pass
else:
raise IllegalOverrideSection('root', section)
def overlay_root_options(self, rootname):
"""Overlay per-root options for ROOTNAME atop the existing option
set. This is a destructive change to the configuration."""
did_overlay = 0
"Overly per-root options atop the existing option set."
if not self.conf_path:
return
for section in self.parser.sections():
base_section = self._is_allowed_override('root', rootname, section)
if base_section:
# We can currently only deal with root overlays happening
# once, so check that we've not yet done any overlaying of
# per-root options.
assert(self.root_options_overlayed == 0)
self._process_section(self.parser, section, base_section)
did_overlay = 1
# If we actually did any overlaying, remember this fact so we
# don't do it again later.
if did_overlay:
self.root_options_overlayed = 1
self._process_root_options(self.parser, rootname)
def _get_parser_items(self, parser, section):
"""Basically implement ConfigParser.items() for pre-Python-2.3 versions."""
@@ -311,67 +171,23 @@ class Config:
for option in parser.options(section):
d[option] = parser.get(section, option)
return d.items()
def get_authorizer_and_params_hack(self, rootname):
"""Return a 2-tuple containing the name and parameters of the
authorizer configured for use with ROOTNAME.
### FIXME: This whole thing is a hack caused by our not being able
### to non-destructively overlay root options when trying to do
### something like a root listing (which might need to get
### different authorizer bits for each and every root in the list).
### Until we have a good way to do that, we expose this function,
### which assumes that base and per-vhost configuration has been
### absorbed into this object and that per-root options have *not*
### been overlayed. See issue #371."""
# We assume that per-root options have *not* been overlayed.
assert(self.root_options_overlayed == 0)
def get_authorizer_params(self, authorizer, rootname=None):
if not self.conf_path:
return None, {}
return {}
# Figure out the authorizer by searching first for a per-root
# override, then falling back to the base/vhost configuration.
authorizer = None
root_options_section = 'root-%s/options' % (rootname)
if self.parser.has_section(root_options_section) \
and self.parser.has_option(root_options_section, 'authorizer'):
authorizer = self.parser.get(root_options_section, 'authorizer')
if not authorizer:
authorizer = self.options.authorizer
# No authorizer? Get outta here.
if not authorizer:
return None, {}
# Dig up the parameters for the authorizer, starting with the
# base/vhost items, then overlaying any root-specific ones we find.
params = {}
authz_section = 'authz-%s' % (authorizer)
if hasattr(self, authz_section):
sub_config = getattr(self, authz_section)
for attr in dir(sub_config):
params[attr] = getattr(sub_config, attr)
root_authz_section = 'root-%s/authz-%s' % (rootname, authorizer)
for section in self.parser.sections():
if section == root_authz_section:
if section == authz_section:
for key, value in self._get_parser_items(self.parser, section):
params[key] = value
return authorizer, params
def get_authorizer_params(self, authorizer=None):
"""Return a dictionary of parameter names and values which belong
to the configured authorizer (or AUTHORIZER, if provided)."""
params = {}
if authorizer is None:
authorizer = self.options.authorizer
if authorizer:
authz_section = 'authz-%s' % (self.options.authorizer)
if hasattr(self, authz_section):
sub_config = getattr(self, authz_section)
for attr in dir(sub_config):
params[attr] = getattr(sub_config, attr)
if rootname:
root_authz_section = 'root-%s/authz-%s' % (rootname, authorizer)
for section in self.parser.sections():
if section == root_authz_section:
for key, value in self._get_parser_items(self.parser, section):
params[key] = value
return params
def set_defaults(self):
@@ -381,7 +197,7 @@ class Config:
self.general.svn_roots = { }
self.general.root_parents = []
self.general.default_root = ''
self.general.mime_types_files = ["mimetypes.conf"]
self.general.mime_types_file = ''
self.general.address = ''
self.general.kv_files = [ ]
self.general.languages = ['en-us']
@@ -397,17 +213,13 @@ class Config:
self.options.root_as_url_component = 1
self.options.checkout_magic = 0
self.options.allowed_views = ['annotate', 'diff', 'markup', 'roots']
self.options.authorizer = None
self.options.allowed_views = ['markup', 'annotate', 'roots']
self.options.authorizer = 'forbidden'
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
self.options.svn_config_dir = None
self.options.max_filesize_kbytes = 512
self.options.use_rcsparse = 0
self.options.sort_by = 'file'
self.options.sort_group_dirs = 1
@@ -425,22 +237,16 @@ class Config:
self.options.template_dir = "templates"
self.options.docroot = None
self.options.show_subdir_lastmod = 0
self.options.show_roots_lastmod = 0
self.options.show_logs = 1
self.options.show_log_in_markup = 1
self.options.cross_copies = 1
self.options.cross_copies = 0
self.options.use_localtime = 0
self.options.iso8601_timestamps = 0
self.options.short_log_len = 80
self.options.enable_syntax_coloration = 1
self.options.tabsize = 8
self.options.detect_encoding = 0
self.options.use_cvsgraph = 0
self.options.cvsgraph_conf = "cvsgraph.conf"
self.options.use_re_search = 0
self.options.dir_pagesize = 0
self.options.log_pagesize = 0
self.options.log_pagesextra = 3
self.options.use_pagesize = 0
self.options.limit_changes = 100
self.templates.diff = None
@@ -466,8 +272,6 @@ class Config:
self.cvsdb.rss_row_limit = 100
self.cvsdb.check_database_for_root = 0
self.query.viewvc_base_url = None
def _startswith(somestr, substr):
return somestr[:len(substr)] == substr

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
@@ -20,14 +20,6 @@ import re
import vclib
import dbi
## Current commits database schema version number.
##
## Version 0 was the original Bonsai-compatible version.
##
## Version 1 added the 'metadata' table (which holds the 'version' key)
## and renamed all the 'repository'-related stuff to be 'root'-
##
CURRENT_SCHEMA_VERSION = 1
## error
error = "cvsdb error"
@@ -38,13 +30,13 @@ error = "cvsdb error"
## complient database interface
class CheckinDatabase:
def __init__(self, host, port, user, passwd, database):
def __init__(self, host, port, user, passwd, database, row_limit):
self._host = host
self._port = port
self._user = user
self._passwd = passwd
self._database = database
self._version = None
self._row_limit = row_limit
## database lookup caches
self._get_cache = {}
@@ -56,19 +48,6 @@ class CheckinDatabase:
self._host, self._port, self._user, self._passwd, self._database)
cursor = self.db.cursor()
cursor.execute("SET AUTOCOMMIT=1")
table_list = self.GetTableList()
if 'metadata' in table_list:
version = self.GetMetadataValue("version")
if version is None:
self._version = 0
else:
self._version = int(version)
else:
self._version = 0
if self._version > CURRENT_SCHEMA_VERSION:
raise DatabaseVersionError("Database version %d is newer than the "
"last version supported by this "
"software." % (self._version))
def sql_get_id(self, table, column, value, auto_set):
sql = "SELECT id FROM %s WHERE %s=%%s" % (table, column)
@@ -168,45 +147,6 @@ class CheckinDatabase:
return list
def GetCommitsTable(self):
return self._version >= 1 and 'commits' or 'checkins'
def GetTableList(self):
sql = "SHOW TABLES"
cursor = self.db.cursor()
cursor.execute(sql)
list = []
while 1:
row = cursor.fetchone()
if row == None:
break
list.append(row[0])
return list
def GetMetadataValue(self, name):
sql = "SELECT value FROM metadata WHERE name=%s"
sql_args = (name)
cursor = self.db.cursor()
cursor.execute(sql, sql_args)
try:
(value,) = cursor.fetchone()
except TypeError:
return None
return value
def SetMetadataValue(self, name, value):
assert(self._version > 0)
sql = "REPLACE INTO metadata (name, value) VALUES (%s, %s)"
sql_args = (name, value)
cursor = self.db.cursor()
try:
cursor.execute(sql, sql_args)
except Exception, e:
raise Exception("Error setting metadata: '%s'\n"
"\tname = %s\n"
"\tvalue = %s\n"
% (str(e), name, value))
def GetBranchID(self, branch, auto_set = 1):
return self.get_id("branches", "branch", branch, auto_set)
@@ -311,8 +251,7 @@ class CheckinDatabase:
minus_count = commit.GetMinusCount() or '0'
description_id = self.GetDescriptionID(commit.GetDescription())
sql = "REPLACE INTO %s" % (self.GetCommitsTable())
sql = sql + \
sql = "REPLACE INTO checkins"\
" (type,ci_when,whoid,repositoryid,dirid,fileid,revision,"\
" stickytag,branchid,addedlines,removedlines,descid)"\
"VALUES(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)"
@@ -352,17 +291,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":
@@ -372,70 +303,59 @@ class CheckinDatabase:
return "(%s)" % (string.join(sqlList, " OR "))
def CreateSQLQueryString(self, query, detect_leftover=0):
commits_table = self.GetCommitsTable()
tableList = [(commits_table, None)]
def CreateSQLQueryString(self, query):
tableList = [("checkins", None)]
condList = []
if len(query.repository_list):
tableList.append(("repositories",
"(%s.repositoryid=repositories.id)"
% (commits_table)))
"(checkins.repositoryid=repositories.id)"))
temp = self.SQLQueryListString("repositories.repository",
query.repository_list)
condList.append(temp)
if len(query.branch_list):
tableList.append(("branches",
"(%s.branchid=branches.id)" % (commits_table)))
tableList.append(("branches", "(checkins.branchid=branches.id)"))
temp = self.SQLQueryListString("branches.branch",
query.branch_list)
condList.append(temp)
if len(query.directory_list):
tableList.append(("dirs",
"(%s.dirid=dirs.id)" % (commits_table)))
tableList.append(("dirs", "(checkins.dirid=dirs.id)"))
temp = self.SQLQueryListString("dirs.dir", query.directory_list)
condList.append(temp)
if len(query.file_list):
tableList.append(("files",
"(%s.fileid=files.id)" % (commits_table)))
tableList.append(("files", "(checkins.fileid=files.id)"))
temp = self.SQLQueryListString("files.file", query.file_list)
condList.append(temp)
if len(query.author_list):
tableList.append(("people",
"(%s.whoid=people.id)" % (commits_table)))
tableList.append(("people", "(checkins.whoid=people.id)"))
temp = self.SQLQueryListString("people.who", query.author_list)
condList.append(temp)
if len(query.comment_list):
tableList.append(("descs",
"(%s.descid=descs.id)" % (commits_table)))
tableList.append(("descs", "(checkins.descid=descs.id)"))
temp = self.SQLQueryListString("descs.description",
query.comment_list)
condList.append(temp)
if query.from_date:
temp = "(%s.ci_when>=\"%s\")" \
% (commits_table, str(query.from_date))
temp = "(checkins.ci_when>=\"%s\")" % (str(query.from_date))
condList.append(temp)
if query.to_date:
temp = "(%s.ci_when<=\"%s\")" \
% (commits_table, str(query.to_date))
temp = "(checkins.ci_when<=\"%s\")" % (str(query.to_date))
condList.append(temp)
if query.sort == "date":
order_by = "ORDER BY %s.ci_when DESC,descid" % (commits_table)
order_by = "ORDER BY checkins.ci_when DESC,descid"
elif query.sort == "author":
tableList.append(("people",
"(%s.whoid=people.id)" % (commits_table)))
tableList.append(("people", "(checkins.whoid=people.id)"))
order_by = "ORDER BY people.who,descid"
elif query.sort == "file":
tableList.append(("files",
"(%s.fileid=files.id)" % (commits_table)))
tableList.append(("files", "(checkins.fileid=files.id)"))
order_by = "ORDER BY files.file,descid"
## exclude duplicates from the table list, and split out join
@@ -453,35 +373,28 @@ class CheckinDatabase:
conditions = string.join(joinConds + condList, " AND ")
conditions = conditions and "WHERE %s" % conditions
## apply the query's row limit, if any (so we avoid really
## slamming a server with a large database)
## limit the number of rows requested or we could really slam
## a server with a large database
limit = ""
if query.limit:
if detect_leftover:
limit = "LIMIT %s" % (str(query.limit + 1))
else:
limit = "LIMIT %s" % (str(query.limit))
limit = "LIMIT %s" % (str(query.limit))
elif self._row_limit:
limit = "LIMIT %s" % (str(self._row_limit))
sql = "SELECT %s.* FROM %s %s %s %s" \
% (commits_table, tables, conditions, order_by, limit)
sql = "SELECT checkins.* FROM %s %s %s %s" % (
tables, conditions, order_by, limit)
return sql
def RunQuery(self, query):
sql = self.CreateSQLQueryString(query, 1)
sql = self.CreateSQLQueryString(query)
cursor = self.db.cursor()
cursor.execute(sql)
query.SetExecuted()
row_count = 0
while 1:
row = cursor.fetchone()
if not row:
break
row_count = row_count + 1
if query.limit and (row_count > query.limit):
query.SetLimitReached()
break
(dbType, dbCI_When, dbAuthorID, dbRepositoryID, dbDirID,
dbFileID, dbRevision, dbStickyTag, dbBranchID, dbAddedLines,
@@ -520,15 +433,8 @@ 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 "\
" repositoryid=%%s "\
" AND dirid=%%s"\
" AND fileid=%%s"\
" AND revision=%%s"\
% (self.GetCommitsTable())
sql = "SELECT * FROM checkins WHERE "\
" repositoryid=%s AND dirid=%s AND fileid=%s AND revision=%s"
sql_args = (repository_id, dir_id, file_id, commit.GetRevision())
cursor = self.db.cursor()
@@ -546,73 +452,39 @@ class CheckinDatabase:
sql = "DELETE FROM %s WHERE %s=%%s" % (table, key)
sql_args = (value, )
if keep_fkey:
sql += " AND %s NOT IN (SELECT %s FROM %s WHERE %s = %%s)" \
% (key, keep_fkey, self.GetCommitsTable(), keep_fkey)
sql_args = (value, value)
sql += " AND %s NOT IN (SELECT %s FROM checkins WHERE %s = %%s)" \
% (key, keep_fkey, keep_fkey)
sql_args = (value, value)
cursor = self.db.cursor()
cursor.execute(sql, sql_args)
def sql_purge(self, table, key, fkey, ftable):
sql = "DELETE FROM %s WHERE %s NOT IN (SELECT %s FROM %s)" \
% (table, key, fkey, ftable)
cursor = self.db.cursor()
cursor.execute(sql)
def PurgeRepository(self, repository):
rep_id = self.GetRepositoryID(repository, auto_set=0)
rep_id = self.GetRepositoryID(repository)
if not rep_id:
raise UnknownRepositoryError("Unknown repository '%s'"
% (repository))
if (self._version >= 1):
self.sql_delete('repositories', 'id', rep_id)
self.sql_purge('commits', 'repositoryid', 'id', 'repositories')
self.sql_purge('files', 'id', 'fileid', 'commits')
self.sql_purge('dirs', 'id', 'dirid', 'commits')
self.sql_purge('branches', 'id', 'branchid', 'commits')
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_args = (rep_id, )
cursor = self.db.cursor()
cursor.execute(sql, sql_args)
checkins = []
while 1:
try:
(ci_type, ci_when, who_id, repository_id,
dir_id, file_id, revision, sticky_tag, branch_id,
plus_count, minus_count, description_id) = \
cursor.fetchone()
except TypeError:
break
checkins.append([file_id, dir_id, branch_id,
description_id, who_id])
#self.sql_delete('repositories', 'id', rep_id)
self.sql_delete('checkins', 'repositoryid', rep_id)
for checkin in checkins:
self.sql_delete('files', 'id', checkin[0], 'fileid')
self.sql_delete('dirs', 'id', checkin[1], 'dirid')
self.sql_delete('branches', 'id', checkin[2], 'branchid')
self.sql_delete('descs', 'id', checkin[3], 'descid')
self.sql_delete('people', 'id', checkin[4], 'whoid')
# Reset all internal id caches. We could be choosier here,
# but let's just be as safe as possible.
self._get_cache = {}
self._get_id_cache = {}
self._desc_id_cache = {}
raise Exception, "Unknown repository '%s'" % (repository)
sql = "SELECT * FROM checkins WHERE repositoryid=%s"
sql_args = (rep_id, )
cursor = self.db.cursor()
cursor.execute(sql, sql_args)
checkins = []
while 1:
try:
(ci_type, ci_when, who_id, repository_id,
dir_id, file_id, revision, sticky_tag, branch_id,
plus_count, minus_count, description_id) = cursor.fetchone()
except TypeError:
break
checkins.append([file_id, dir_id, branch_id, description_id, who_id])
class DatabaseVersionError(Exception):
pass
class UnknownRepositoryError(Exception):
pass
#self.sql_delete('repositories', 'id', rep_id)
self.sql_delete('checkins', 'repositoryid', rep_id)
for checkin in checkins:
self.sql_delete('files', 'id', checkin[0], 'fileid')
self.sql_delete('dirs', 'id', checkin[1], 'dirid')
self.sql_delete('branches', 'id', checkin[2], 'branchid')
self.sql_delete('descs', 'id', checkin[3], 'descid')
self.sql_delete('people', 'id', checkin[4], 'whoid')
## the Commit class holds data on one commit, the representation is as
## close as possible to how it should be committed and retrieved to the
@@ -789,9 +661,8 @@ class QueryEntry:
self.data = data
self.match = match
## CheckinDatabaseQuery is an object which contains the search
## parameters for a query to the Checkin Database and -- after the
## query is executed -- the data returned by the query.
## CheckinDatabaseQueryData is a object which contains the search parameters
## for a query to the CheckinDatabase
class CheckinDatabaseQuery:
def __init__(self):
## sorting
@@ -811,8 +682,7 @@ class CheckinDatabaseQuery:
## limit on number of rows to return
self.limit = None
self.limit_reached = 0
## list of commits -- filled in by CVS query
self.commit_list = []
@@ -820,9 +690,6 @@ class CheckinDatabaseQuery:
## are added
self.commit_cb = None
## has this query been run?
self.executed = 0
def SetRepository(self, repository, match = "exact"):
self.repository_list.append(QueryEntry(repository, match))
@@ -868,20 +735,6 @@ class CheckinDatabaseQuery:
def AddCommit(self, commit):
self.commit_list.append(commit)
def SetExecuted(self):
self.executed = 1
def SetLimitReached(self):
self.limit_reached = 1
def GetLimitReached(self):
assert self.executed
return self.limit_reached
def GetCommitList(self):
assert self.executed
return self.commit_list
##
## entrypoints
@@ -900,7 +753,7 @@ def ConnectDatabase(cfg, readonly=0):
user = cfg.cvsdb.user
passwd = cfg.cvsdb.passwd
db = CheckinDatabase(cfg.cvsdb.host, cfg.cvsdb.port, user, passwd,
cfg.cvsdb.database_name)
cfg.cvsdb.database_name, cfg.cvsdb.row_limit)
db.Connect()
return db

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-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
@@ -48,17 +48,13 @@ if SHOW_TIMES:
else:
_times[which] = t
def t_dump(out):
out.write('<div>')
names = _times.keys()
names.sort()
for name in names:
out.write('%s: %.6fs<br/>\n' % (name, _times[name]))
out.write('</div>')
def dump():
for name, value in _times.items():
print '%s: %.6f<br />' % (name, value)
else:
t_start = t_end = t_dump = lambda *args: None
t_start = t_end = dump = lambda *args: None
class ViewVCException:

View File

@@ -347,7 +347,7 @@ class Template:
for_names = [ ]
if base_format:
program.append((self._cmd_format, _formatters[base_format]))
program.append((self._cmd_format, _printers[base_format]))
for i in range(len(parts)):
piece = parts[i]
@@ -405,13 +405,13 @@ class Template:
elif cmd == 'format':
if args[1][0]:
# argument is a variable reference
formatter = args[1]
printer = args[1]
else:
# argument is a string constant referring to built-in formatter
formatter = _formatters.get(args[1][1])
if not formatter:
# argument is a string constant referring to built-in printer
printer = _printers.get(args[1][1])
if not printer:
raise UnknownFormatConstantError(str(args[1:]))
program.append((self._cmd_format, formatter))
program.append((self._cmd_format, printer))
# remember the cmd, current pos, args, and a section placeholder
stack.append([cmd, len(program), args[1:], None])
@@ -460,18 +460,15 @@ class Template:
def _cmd_print(self, valrefs, ctx):
value = _get_value(valrefs[0], ctx)
args = map(lambda valref, ctx=ctx: _get_value(valref, ctx), valrefs[1:])
try:
_write_value(value, args, ctx)
except TypeError:
raise Exception("Unprintable value type for '%s'" % (str(valrefs[0][0])))
_write_value(value, args, ctx)
def _cmd_format(self, formatter, ctx):
if type(formatter) is TupleType:
formatter = _get_value(formatter, ctx)
ctx.formatters.append(formatter)
def _cmd_format(self, printer, ctx):
if type(printer) is TupleType:
printer = _get_value(printer, ctx)
ctx.printers.append(printer)
def _cmd_end_format(self, valref, ctx):
ctx.formatters.pop()
ctx.printers.pop()
def _cmd_include(self, (valref, reader), ctx):
fname = _get_value(valref, ctx)
@@ -525,8 +522,7 @@ class Template:
((valref,), unused, section) = args
list = _get_value(valref, ctx)
if isinstance(list, StringType):
raise NeedSequenceError("The value of '%s' is not a sequence"
% (valref[0]))
raise NeedSequenceError()
refname = valref[0]
ctx.for_iterators[refname] = iterator = _iter(list)
for unused in iterator:
@@ -637,23 +633,14 @@ def _get_value((refname, start, rest), ctx):
# string or a sequence
return ob
def _print_formatted(formatters, ctx, chunk):
# print chunk to ctx.fp after running it sequentially through formatters
for formatter in formatters:
chunk = formatter(chunk)
ctx.fp.write(chunk)
def _write_value(value, args, ctx):
# value is a callback function, generates its own output
if callable(value):
apply(value, [ctx] + list(args))
return
# squirrel away formatters in case one of them recursively calls
# _write_value() -- we'll use them (in reverse order) to format our
# output.
formatters = ctx.formatters[:]
formatters.reverse()
# pop printer in case it recursively calls _write_value
printer = ctx.printers.pop()
try:
# if the value has a 'read' attribute, then it is a stream: copy it
@@ -662,7 +649,7 @@ def _write_value(value, args, ctx):
chunk = value.read(16384)
if not chunk:
break
_print_formatted(formatters, ctx, chunk)
printer(ctx, chunk)
# value is a substitution pattern
elif args:
@@ -675,58 +662,21 @@ def _write_value(value, args, ctx):
piece = args[idx]
else:
piece = '<undef>'
_print_formatted(formatters, ctx, piece)
printer(ctx, piece)
# plain old value, write to output
else:
_print_formatted(formatters, ctx, value)
printer(ctx, value)
finally:
# restore our formatters
formatters.reverse()
ctx.formatters = formatters
class TemplateData:
"""A custom dictionary-like object that allows one-time definition
of keys, and only value fetches and changes, and key deletions,
thereafter.
EZT doesn't require the use of this special class -- a normal
dict-type data dictionary works fine. But use of this class will
assist those who want the data sent to their templates to have a
consistent set of keys."""
def __init__(self, initial_data={}):
self._items = initial_data
def __getitem__(self, key):
return self._items.__getitem__(key)
def __setitem__(self, key, item):
assert self._items.has_key(key)
return self._items.__setitem__(key, item)
def __delitem__(self, key):
return self._items.__delitem__(key)
def keys(self):
return self._items.keys()
def merge(self, template_data):
"""Merge the data in TemplataData instance TEMPLATA_DATA into this
instance. Avoid the temptation to use this conditionally in your
code -- it rather defeats the purpose of this class."""
assert isinstance(template_data, TemplateData)
self._items.update(template_data._items)
ctx.printers.append(printer)
class Context:
"""A container for the execution context"""
def __init__(self, fp):
self.fp = fp
self.formatters = []
self.printers = []
def write(self, value, args=()):
_write_value(value, args, self)
@@ -839,26 +789,20 @@ class BaseUnavailableError(EZTException):
class UnknownFormatConstantError(EZTException):
"""The format specifier is an unknown value."""
def _raw_formatter(s):
return s
def _raw_printer(ctx, s):
ctx.fp.write(s)
def _html_printer(ctx, s):
ctx.fp.write(cgi.escape(s))
def _html_formatter(s):
return cgi.escape(s)
def _uri_printer(ctx, s):
ctx.fp.write(urllib.quote(s))
def _xml_formatter(s):
s = s.replace('&', '&#x26;')
s = s.replace('<', '&#x3C;')
s = s.replace('>', '&#x3E;')
return s
def _uri_formatter(s):
return urllib.quote(s)
_formatters = {
FORMAT_RAW : _raw_formatter,
FORMAT_HTML : _html_formatter,
FORMAT_XML : _xml_formatter,
FORMAT_URI : _uri_formatter,
_printers = {
FORMAT_RAW : _raw_printer,
FORMAT_HTML : _html_printer,
FORMAT_XML : _html_printer,
FORMAT_URI : _uri_printer,
}
# --- standard test environment ---

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
@@ -20,7 +20,7 @@ import difflib
import sys
import re
import ezt
import sapi
import cgi
def sidebyside(fromlines, tolines, context):
"""Generate side by side diff"""
@@ -49,18 +49,18 @@ def _mdiff_split(flag, (line_number, text)):
while True:
m = _re_mdiff.search(text, pos)
if not m:
segments.append(_item(text=sapi.escape(text[pos:]), type=None))
segments.append(_item(text=cgi.escape(text[pos:]), type=None))
break
if m.start() > pos:
segments.append(_item(text=sapi.escape(text[pos:m.start()]), type=None))
segments.append(_item(text=cgi.escape(text[pos:m.start()]), type=None))
if m.group(1) == "+":
segments.append(_item(text=sapi.escape(m.group(2)), type="add"))
segments.append(_item(text=cgi.escape(m.group(2)), type="add"))
elif m.group(1) == "-":
segments.append(_item(text=sapi.escape(m.group(2)), type="remove"))
segments.append(_item(text=cgi.escape(m.group(2)), type="remove"))
elif m.group(1) == "^":
segments.append(_item(text=sapi.escape(m.group(2)), type="change"))
segments.append(_item(text=cgi.escape(m.group(2)), type="change"))
pos = m.end()
@@ -166,12 +166,12 @@ def _differ_split(row, guide):
for m in _re_differ.finditer(guide, pos):
if m.start() > pos:
segments.append(_item(text=sapi.escape(line[pos:m.start()]), type=None))
segments.append(_item(text=sapi.escape(line[m.start():m.end()]),
segments.append(_item(text=cgi.escape(line[pos:m.start()]), type=None))
segments.append(_item(text=cgi.escape(line[m.start():m.end()]),
type="change"))
pos = m.end()
segments.append(_item(text=sapi.escape(line[pos:]), type=None))
segments.append(_item(text=cgi.escape(line[pos:]), type=None))
return _item(gap=ezt.boolean(gap), type=type, segments=segments,
left_number=left_number, right_number=right_number)

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
@@ -131,6 +131,194 @@ def popen(cmd, args, mode, capture_err=1):
# crap. shouldn't be here.
sys.exit(127)
def pipe_cmds(cmds, out=None):
"""Executes a sequence of commands. The output of each command is directed to
the input of the next command. A _pipe object is returned for writing to the
first command's input. The output of the last command is directed to the
"out" file object or the standard output if "out" is None. If "out" is not an
OS file descriptor, a separate thread will be spawned to send data to its
write() method."""
if out is None:
out = sys.stdout
if sys.platform == "win32":
### FIXME: windows implementation ignores "out" argument, always
### writing last command's output to standard out
if debug.SHOW_CHILD_PROCESSES:
dbgIn = StringIO.StringIO()
hStdIn, handle = win32popen.MakeSpyPipe(1, 0, (dbgIn,))
i = 0
for cmd in cmds:
i = i + 1
dbgOut, dbgErr = StringIO.StringIO(), StringIO.StringIO()
if i < len(cmds):
nextStdIn, hStdOut = win32popen.MakeSpyPipe(1, 1, (dbgOut,))
x, hStdErr = win32popen.MakeSpyPipe(None, 1, (dbgErr,))
else:
ehandle = win32event.CreateEvent(None, 1, 0, None)
nextStdIn, hStdOut = win32popen.MakeSpyPipe(None, 1, (dbgOut, sapi.server.file()), ehandle)
x, hStdErr = win32popen.MakeSpyPipe(None, 1, (dbgErr,))
command = win32popen.CommandLine(cmd[0], cmd[1:])
phandle, pid, thandle, tid = win32popen.CreateProcess(command, hStdIn, hStdOut, hStdErr)
if debug.SHOW_CHILD_PROCESSES:
debug.Process(command, dbgIn, dbgOut, dbgErr)
dbgIn = dbgOut
hStdIn = nextStdIn
else:
hStdIn, handle = win32popen.CreatePipe(1, 0)
spool = None
i = 0
for cmd in cmds:
i = i + 1
if i < len(cmds):
nextStdIn, hStdOut = win32popen.CreatePipe(1, 1)
else:
# very last process
nextStdIn = None
if sapi.server.inheritableOut:
# send child output to standard out
hStdOut = win32popen.MakeInheritedHandle(win32popen.FileObject2File(sys.stdout),0)
ehandle = None
else:
ehandle = win32event.CreateEvent(None, 1, 0, None)
x, hStdOut = win32popen.MakeSpyPipe(None, 1, (sapi.server.file(),), ehandle)
command = win32popen.CommandLine(cmd[0], cmd[1:])
phandle, pid, thandle, tid = win32popen.CreateProcess(command, hStdIn, hStdOut, None)
hStdIn = nextStdIn
return _pipe(win32popen.File2FileObject(handle, 'wb'), phandle, ehandle)
# flush the stdio buffers since we are about to change the FD under them
sys.stdout.flush()
sys.stderr.flush()
prev_r, parent_w = os.pipe()
null = os.open('/dev/null', os.O_RDWR)
child_pids = []
for cmd in cmds[:-1]:
r, w = os.pipe()
pid = os.fork()
if not pid:
# in the child
# hook up stdin to the "read" channel
os.dup2(prev_r, 0)
# hook up stdout to the output channel
os.dup2(w, 1)
# toss errors
os.dup2(null, 2)
# close these extra descriptors
os.close(prev_r)
os.close(parent_w)
os.close(null)
os.close(r)
os.close(w)
# time to run the command
try:
os.execvp(cmd[0], cmd)
except:
pass
sys.exit(127)
# in the parent
child_pids.append(pid)
# we don't need these any more
os.close(prev_r)
os.close(w)
# the read channel of this pipe will feed into to the next command
prev_r = r
# no longer needed
os.close(null)
# done with most of the commands. set up the last command to write to "out"
if not hasattr(out, 'fileno'):
r, w = os.pipe()
pid = os.fork()
if not pid:
# in the child (the last command)
# hook up stdin to the "read" channel
os.dup2(prev_r, 0)
# hook up stdout to "out"
if hasattr(out, 'fileno'):
if out.fileno() != 1:
os.dup2(out.fileno(), 1)
out.close()
else:
# "out" can't be hooked up directly, so use a pipe and a thread
os.dup2(w, 1)
os.close(r)
os.close(w)
# close these extra descriptors
os.close(prev_r)
os.close(parent_w)
# run the last command
try:
os.execvp(cmds[-1][0], cmds[-1])
except:
pass
sys.exit(127)
child_pids.append(pid)
# not needed any more
os.close(prev_r)
if not hasattr(out, 'fileno'):
os.close(w)
thread = _copy(r, out)
thread.start()
else:
thread = None
# write into the first pipe, wait on the final process
return _pipe(os.fdopen(parent_w, 'w'), child_pids, thread=thread)
class _copy(threading.Thread):
def __init__(self, srcfd, destfile):
self.srcfd = srcfd
self.destfile = destfile
threading.Thread.__init__(self)
def run(self):
try:
while 1:
s = os.read(self.srcfd, 1024)
if not s:
break
self.destfile.write(s)
finally:
os.close(self.srcfd)
class _pipe:
"Wrapper for a file which can wait() on a child process at close time."

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
@@ -217,9 +217,8 @@ def decode_command(cmd):
else:
return "exact"
def form_to_cvsdb_query(cfg, form_data):
def form_to_cvsdb_query(form_data):
query = cvsdb.CreateCheckinQuery()
query.SetLimit(cfg.cvsdb.row_limit)
if form_data.repository:
for cmd, str in listparse_string(form_data.repository):
@@ -276,30 +275,8 @@ def prev_rev(rev):
return string.join(r, '.')
def is_forbidden(cfg, cvsroot_name, module):
'''Return 1 if MODULE in CVSROOT_NAME is forbidden; return 0 otherwise.'''
# CVSROOT_NAME might be None here if the data comes from an
# unconfigured root. This interfaces doesn't care that the root
# isn't configured, but if that's the case, it will consult only
# the base and per-vhost configuration for authorizer and
# authorizer parameters.
if cvsroot_name:
authorizer, params = cfg.get_authorizer_and_params_hack(cvsroot_name)
else:
authorizer = cfg.options.authorizer
params = cfg.get_authorizer_params()
# If CVSROOT_NAME isn't configured to use an authorizer, nothing
# is forbidden. If it's configured to use something other than
# the 'forbidden' authorizer, complain. Otherwise, check for
# forbiddenness per the PARAMS as expected.
if not authorizer:
return 0
if authorizer != 'forbidden':
raise Exception("The 'forbidden' authorizer is the only one supported "
"by this interface. The '%s' root is configured to "
"use a different one." % (cvsroot_name))
forbidden = params.get('forbidden', '')
auth_params = cfg.get_authorizer_params('forbidden', cvsroot_name)
forbidden = auth_params.get('forbidden', '')
forbidden = map(string.strip, filter(None, string.split(forbidden, ',')))
default = 0
for pat in forbidden:
@@ -313,7 +290,11 @@ def is_forbidden(cfg, cvsroot_name, module):
def build_commit(server, cfg, desc, files, cvsroots, viewvc_link):
ob = _item(num_files=len(files), files=[])
ob.log = desc and string.replace(server.escape(desc), '\n', '<br />') or ''
if desc:
ob.log = string.replace(server.escape(desc), '\n', '<br />')
else:
ob.log = '&nbsp;'
for commit in files:
repository = commit.GetRepository()
@@ -347,10 +328,9 @@ def build_commit(server, cfg, desc, files, cvsroots, viewvc_link):
except:
raise Exception, str([directory, commit.GetFile()])
## If we couldn't find the cvsroot path configured in the
## viewvc.conf file, or we don't have a VIEWVC_LINK, then
## don't make the link.
if cvsroot_name and viewvc_link:
## if we couldn't find the cvsroot path configured in the
## viewvc.conf file, then don't make the link
if cvsroot_name:
flink = '[%s] <a href="%s/%s?root=%s">%s</a>' % (
cvsroot_name, viewvc_link, urllib.quote(file),
cvsroot_name, file)
@@ -378,27 +358,23 @@ def build_commit(server, cfg, desc, files, cvsroots, viewvc_link):
return ob
def run_query(server, cfg, form_data, viewvc_link):
query = form_to_cvsdb_query(cfg, form_data)
query = form_to_cvsdb_query(form_data)
db = cvsdb.ConnectDatabaseReadOnly(cfg)
db.RunQuery(query)
commit_list = query.GetCommitList()
if not commit_list:
return [ ], 0
row_limit_reached = query.GetLimitReached()
if not query.commit_list:
return [ ]
commits = [ ]
files = [ ]
cvsroots = {}
viewvc.expand_root_parents(cfg)
rootitems = cfg.general.svn_roots.items() + cfg.general.cvs_roots.items()
for key, value in rootitems:
cvsroots[cvsdb.CleanRepository(value)] = key
current_desc = commit_list[0].GetDescription()
for commit in commit_list:
current_desc = query.commit_list[0].GetDescription()
for commit in query.commit_list:
desc = commit.GetDescription()
if current_desc == desc:
files.append(commit)
@@ -421,7 +397,7 @@ def run_query(server, cfg, form_data, viewvc_link):
return len(commit.files) > 0
commits = filter(_only_with_files, commits)
return commits, row_limit_reached
return commits
def main(server, cfg, viewvc_link):
try:
@@ -430,40 +406,45 @@ def main(server, cfg, viewvc_link):
form_data = FormData(form)
if form_data.valid:
commits, row_limit_reached = run_query(server, cfg,
form_data, viewvc_link)
commits = run_query(server, cfg, form_data, viewvc_link)
query = None
else:
commits = [ ]
row_limit_reached = 0
query = 'skipped'
docroot = cfg.options.docroot
if docroot is None and viewvc_link:
docroot = viewvc_link + '/' + viewvc.docroot_magic_path
data = ezt.TemplateData({
script_name = server.getenv('SCRIPT_NAME', '')
data = {
'cfg' : cfg,
'address' : cfg.general.address,
'vsn' : viewvc.__version__,
'repository' : server.escape(form_data.repository),
'branch' : server.escape(form_data.branch),
'directory' : server.escape(form_data.directory),
'file' : server.escape(form_data.file),
'who' : server.escape(form_data.who),
'docroot' : docroot,
'repository' : server.escape(form_data.repository, 1),
'branch' : server.escape(form_data.branch, 1),
'directory' : server.escape(form_data.directory, 1),
'file' : server.escape(form_data.file, 1),
'who' : server.escape(form_data.who, 1),
'docroot' : cfg.options.docroot is None \
and viewvc_link + '/' + viewvc.docroot_magic_path \
or cfg.options.docroot,
'sortby' : form_data.sortby,
'date' : form_data.date,
'query' : query,
'row_limit_reached' : ezt.boolean(row_limit_reached),
'commits' : commits,
'num_commits' : len(commits),
'rss_href' : None,
'hours' : form_data.hours and form_data.hours or 2,
})
}
if form_data.hours:
data['hours'] = form_data.hours
else:
data['hours'] = 2
server.header()
# generate the page
server.header()
template = viewvc.get_view_template(cfg, "query")
template.generate(server.file(), data)

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
@@ -20,27 +20,13 @@ import string
import os
import sys
import re
import cgi
# global server object. It will be either a CgiServer, a WsgiServer,
# or a proxy to an AspServer or ModPythonServer object.
# global server object. It will be either a CgiServer or a proxy to
# an AspServer or ModPythonServer object.
server = None
# Simple HTML string escaping. Note that we always escape the
# double-quote character -- ViewVC shouldn't ever need to preserve
# that character as-is, and sometimes needs to embed escaped values
# into HTML attributes.
def escape(s):
s = str(s)
s = string.replace(s, '&', '&amp;')
s = string.replace(s, '>', '&gt;')
s = string.replace(s, '<', '&lt;')
s = string.replace(s, '"', "&quot;")
return s
class Server:
def __init__(self):
self.pageGlobals = {}
@@ -48,9 +34,6 @@ class Server:
def self(self):
return self
def escape(self, s):
return escape(s)
def close(self):
pass
@@ -146,6 +129,9 @@ class CgiServer(Server):
global server
server = self
global cgi
import cgi
def addheader(self, name, value):
self.headers.append((name, value))
@@ -172,7 +158,11 @@ class CgiServer(Server):
if self.iis: url = fix_iis_url(self, url)
self.addheader('Location', url)
self.header(status='301 Moved')
sys.stdout.write('This document is located <a href="%s">here</a>.\n' % url)
print 'This document is located <a href="%s">here</a>.' % url
sys.exit(0)
def escape(self, s, quote = None):
return cgi.escape(s, quote)
def getenv(self, name, value=None):
ret = os.environ.get(name, value)
@@ -186,7 +176,7 @@ class CgiServer(Server):
def FieldStorage(fp=None, headers=None, outerboundary="",
environ=os.environ, keep_blank_values=0, strict_parsing=0):
return cgi.FieldStorage(fp, headers, outerboundary, environ,
keep_blank_values, strict_parsing)
keep_blank_values, strict_parsing)
def write(self, s):
sys.stdout.write(s)
@@ -198,63 +188,6 @@ class CgiServer(Server):
return sys.stdout
class WsgiServer(Server):
def __init__(self, environ, start_response):
Server.__init__(self)
self._environ = environ
self._start_response = start_response;
self._headers = []
self._wsgi_write = None
self.headerSent = False
global server
server = self
global cgi
import cgi
def addheader(self, name, value):
self._headers.append((name, value))
def header(self, content_type='text/html; charset=UTF-8', status=None):
if not status:
status = "200 OK"
if not self.headerSent:
self.headerSent = True
self._headers.insert(0, ("Content-Type", content_type),)
self._wsgi_write = self._start_response("%s" % status, self._headers)
def redirect(self, url):
"""Redirect client to url. This discards any data that has been queued
to be sent to the user. But there should never by any anyway.
"""
self.addheader('Location', url)
self.header(status='301 Moved')
self._wsgi_write('This document is located <a href="%s">here</a>.' % url)
def getenv(self, name, value=None):
return self._environ.get(name, value)
def params(self):
return cgi.parse(environ=self._environ, fp=self._environ["wsgi.input"])
def FieldStorage(self, fp=None, headers=None, outerboundary="",
environ=os.environ, keep_blank_values=0, strict_parsing=0):
return cgi.FieldStorage(self._environ["wsgi.input"], headers,
outerboundary, self._environ, keep_blank_values,
strict_parsing)
def write(self, s):
self._wsgi_write(s)
def flush(self):
pass
def file(self):
return File(self)
class AspServer(ThreadedServer):
def __init__(self, Server, Request, Response, Application):
ThreadedServer.__init__(self)
@@ -286,6 +219,10 @@ class AspServer(ThreadedServer):
def redirect(self, url):
self.response.Redirect(url)
sys.exit()
def escape(self, s, quote = None):
return self.server.HTMLEncode(str(s))
def getenv(self, name, value = None):
ret = self.request.ServerVariables(name)()
@@ -348,6 +285,9 @@ class ModPythonServer(ThreadedServer):
self.request = request
self.headerSent = 0
global cgi
import cgi
def addheader(self, name, value):
self.request.headers_out.add(name, value)
@@ -368,7 +308,11 @@ class ModPythonServer(ThreadedServer):
self.request.headers_out['Location'] = url
self.request.status = mod_python.apache.HTTP_MOVED_TEMPORARILY
self.request.write("You are being redirected to <a href=\"%s\">%s</a>"
% (url, url))
% (url, url))
sys.exit()
def escape(self, s, quote = None):
return cgi.escape(s, quote)
def getenv(self, name, value = None):
try:

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 2006-2013 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 2006-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
@@ -29,15 +29,7 @@ class GenericViewVCAuthorizer:
def check_root_access(self, rootname):
"""Return 1 iff the associated username is permitted to read ROOTNAME."""
pass
def check_universal_access(self, rootname):
"""Return 1 if the associated username is permitted to read every
path in the repository at every revision, 0 if the associated
username is prohibited from reading any path in the repository, or
None if no such determination can be made (perhaps because the
cost of making it is too great)."""
pass
def check_path_access(self, rootname, path_parts, pathtype, rev=None):
"""Return 1 iff the associated username is permitted to read
revision REV of the path PATH_PARTS (of type PATHTYPE) in
@@ -52,9 +44,6 @@ class ViewVCAuthorizer(GenericViewVCAuthorizer):
"""The uber-permissive authorizer."""
def check_root_access(self, rootname):
return 1
def check_universal_access(self, rootname):
return 1
def check_path_access(self, rootname, path_parts, pathtype, rev=None):
return 1

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 2006-2013 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 2006-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
@@ -23,14 +23,7 @@ class ViewVCAuthorizer(vcauth.GenericViewVCAuthorizer):
def check_root_access(self, rootname):
return 1
def check_universal_access(self, rootname):
# If there aren't any forbidden paths, we can grant universal read
# access. Otherwise, we make no claim.
if not self.forbidden:
return 1
return None
def check_path_access(self, rootname, path_parts, pathtype, rev=None):
# No path? No problem.
if not path_parts:

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 2008-2013 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 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
@@ -46,13 +46,6 @@ class ViewVCAuthorizer(vcauth.GenericViewVCAuthorizer):
def check_root_access(self, rootname):
return self._check_root_path_access(rootname)
def check_universal_access(self, rootname):
# If there aren't any forbidden regexps, we can grant universal
# read access. Otherwise, we make no claim.
if not self.forbidden:
return 1
return None
def check_path_access(self, rootname, path_parts, pathtype, rev=None):
root_path = rootname
if path_parts:

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 2006-2013 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 2006-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
@@ -22,6 +22,7 @@ class ViewVCAuthorizer(vcauth.GenericViewVCAuthorizer):
"""Subversion authz authorizer module"""
def __init__(self, username, params={}):
self.username = username
self.rootpaths = { } # {root -> { paths -> access boolean for USERNAME }}
# Get the authz file location from a passed-in parameter.
@@ -31,33 +32,15 @@ class ViewVCAuthorizer(vcauth.GenericViewVCAuthorizer):
if not os.path.exists(self.authz_file):
raise debug.ViewVCException("Configured authzfile file not found")
# 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
elif self.force_username_case == "lower":
self.username = username and string.lower(username) or username
elif not self.force_username_case:
self.username = username
else:
raise debug.ViewVCException("Invalid value for force_username_case "
"option")
def _get_paths_for_root(self, rootname):
if self.rootpaths.has_key(rootname):
return self.rootpaths[rootname]
paths_for_root = { }
# Parse the authz file, replacing ConfigParser's optionxform()
# method with something that won't futz with the case of the
# option names.
# Parse the authz file.
cp = ConfigParser()
cp.optionxform = lambda x: x
try:
cp.read(self.authz_file)
except:
raise debug.ViewVCException("Unable to parse configured authzfile file")
cp.read(self.authz_file)
# Figure out if there are any aliases for the current username
aliases = []
@@ -224,36 +207,6 @@ class ViewVCAuthorizer(vcauth.GenericViewVCAuthorizer):
paths = self._get_paths_for_root(rootname)
return (paths is not None) and 1 or 0
def check_universal_access(self, rootname):
paths = self._get_paths_for_root(rootname)
if not paths: # None or empty.
return 0
# Search the access determinations. If there's a mix, we can't
# claim a universal access determination.
found_allow = 0
found_deny = 0
for access in paths.values():
if access:
found_allow = 1
else:
found_deny = 1
if found_allow and found_deny:
return None
# We didn't find both allowances and denials, so we must have
# found one or the other. Denials only is a universal denial.
if found_deny:
return 0
# ... but allowances only is only a universal allowance if read
# access is granted to the root directory.
if found_allow and paths.has_key('/'):
return 1
# Anything else is indeterminable.
return None
def check_path_access(self, rootname, path_parts, pathtype, rev=None):
# Crawl upward from the path represented by PATH_PARTS toward to
# the root of the repository, looking for an explicitly grant or

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
@@ -76,7 +76,7 @@ class Repository:
"""
pass
def openfile(self, path_parts, rev, options):
def openfile(self, path_parts, rev):
"""Open a file object to read file contents at a given path and revision.
The return value is a 2-tuple of containg the file object and revision
@@ -86,8 +86,6 @@ class Repository:
of the repository. e.g. ["subdir1", "subdir2", "filename"]
rev is the revision of the file to check out
options is a dictionary of implementation specific options
"""
def listdir(self, path_parts, rev, options):
@@ -170,29 +168,20 @@ 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
rev is the revision of the item to return information about
Return value is a 5-tuple containing: the date, author, log
message, a list of ChangedPath items representing paths changed,
and a dictionary mapping property names to property values for
properties stored on an item.
Return value is a 4-tuple containing the date, author, log
message, and a list of ChangedPath items representing paths changed
Raise vclib.UnsupportedFeature if the version control system
doesn't support a global revision concept.
@@ -207,21 +196,7 @@ class Repository:
rev is the revision of the item to return information about
"""
def filesize(self, path_parts, rev):
"""Return the size of a versioned file's contents if it can be
obtained without a brute force measurement; -1 otherwise.
NOTE: Callers that require a filesize answer when this function
returns -1 may obtain it by measuring the data returned via
openfile().
The 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
"""
# ======================================================================
class DirEntry:

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"))
@@ -35,23 +33,6 @@ def expand_root_parent(parent_path):
return roots
def find_root_in_parent(parent_path, rootname):
"""Search PARENT_PATH for a root named ROOTNAME, returning the
canonicalized ROOTPATH of the root if found; return None if no such
root is found."""
assert os.path.isabs(parent_path)
# Is PARENT_PATH itself a CVS repository? If so, we allow ROOTNAME
# to be any subdir within it. Otherwise, we expect
# PARENT_PATH/ROOTNAME to be a CVS repository.
rootpath = os.path.join(parent_path, rootname)
if os.path.exists(os.path.join(parent_path, "CVSROOT", "config")):
return canonicalize_rootpath(rootpath)
if os.path.exists(os.path.join(rootpath, "CVSROOT", "config")):
return canonicalize_rootpath(rootpath)
return None
def CVSRepository(name, rootpath, authorizer, utilities, use_rcsparse):
rootpath = canonicalize_rootpath(rootpath)
if use_rcsparse:

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
@@ -40,11 +40,6 @@ class BaseCVSRepository(vclib.Repository):
if not vclib.check_root_access(self):
raise vclib.ReposNotFound(name)
def open(self):
# See if a universal read access determination can be made.
if self.auth and self.auth.check_universal_access(self.name) == 1:
self.auth = None
def rootname(self):
return self.name
@@ -149,11 +144,6 @@ class BaseCVSRepository(vclib.Repository):
% (string.join(path_parts, "/")))
rcsfile = self.rcsfile(path_parts, 1)
return os.access(rcsfile, os.X_OK)
def filesize(self, path_parts, rev):
if self.itemtype(path_parts, rev) != vclib.FILE: # does auth-check
raise vclib.Error("Path '%s' is not a file." % (_path_join(path_parts)))
return -1
class BinCVSRepository(BaseCVSRepository):
@@ -172,14 +162,7 @@ class BinCVSRepository(BaseCVSRepository):
return revs[-1]
return None
def openfile(self, path_parts, rev, options):
"""see vclib.Repository.openfile docstring
Option values recognized by this implementation:
cvs_oldkeywords
boolean. true to use the original keyword substitution values.
"""
def openfile(self, path_parts, rev):
if self.itemtype(path_parts, rev) != vclib.FILE: # does auth-check
raise vclib.Error("Path '%s' is not a file."
% (string.join(path_parts, "/")))
@@ -187,14 +170,12 @@ class BinCVSRepository(BaseCVSRepository):
rev_flag = '-p'
else:
rev_flag = '-p' + rev
if options.get('cvs_oldkeywords', 0):
kv_flag = '-ko'
else:
kv_flag = '-kkv'
full_name = self.rcsfile(path_parts, root=1, v=0)
used_rlog = 0
tip_rev = None # used only if we have to fallback to using rlog
fp = self.rcs_popen('co', (kv_flag, rev_flag, full_name), 'rb')
fp = self.rcs_popen('co', (rev_flag, full_name), 'rb')
try:
filename, revision = _parse_co_header(fp)
except COMissingRevision:
@@ -337,13 +318,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):
@@ -1041,16 +1022,16 @@ def _get_logs(repos, dir_path_parts, entries, view_tag, get_dirs):
file.errors.append("rlog error: %s" % msg)
continue
tag = None
if view_tag == 'MAIN' or view_tag == 'HEAD':
tag = Tag(None, default_branch)
elif taginfo.has_key(view_tag):
tag = Tag(None, taginfo[view_tag])
elif view_tag and (eof != _EOF_FILE):
# the tag wasn't found, so skip this file (unless we already
# know there's nothing left of it to read)
elif view_tag:
# the tag wasn't found, so skip this file
_skip_file(rlog)
eof = _EOF_FILE
eof = 1
else:
tag = None
# we don't care about the specific values -- just the keys and whether
# the values point to branches or revisions. this the fastest way to

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*-python-*-
#
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2007 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-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
@@ -127,9 +127,9 @@ class CCVSRepository(BaseCVSRepository):
% (string.join(path_parts2, "/")))
temp1 = tempfile.mktemp()
open(temp1, 'wb').write(self.openfile(path_parts1, rev1, {})[0].getvalue())
open(temp1, 'wb').write(self.openfile(path_parts1, rev1)[0].getvalue())
temp2 = tempfile.mktemp()
open(temp2, 'wb').write(self.openfile(path_parts2, rev2, {})[0].getvalue())
open(temp2, 'wb').write(self.openfile(path_parts2, rev2)[0].getvalue())
r1 = self.itemlog(path_parts1, rev1, vclib.SORTBY_DEFAULT, 0, 0, {})[-1]
r2 = self.itemlog(path_parts2, rev2, vclib.SORTBY_DEFAULT, 0, 0, {})[-1]
@@ -142,17 +142,17 @@ 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):
raise vclib.UnsupportedFeature
def openfile(self, path_parts, rev, options):
def openfile(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, "/")))
@@ -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-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
@@ -194,8 +194,7 @@ class _Parser:
else:
# Chew up "newphrase"
# warn("Unexpected RCS token: $token\n")
while self.ts.get() != ';':
pass
pass
else:
if f is None:
self.ts.unget(token)

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-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
@@ -19,11 +19,7 @@ import string
import common
class _TokenStream:
token_term = string.whitespace + ";:"
try:
token_term = frozenset(token_term)
except NameError:
pass
token_term = string.whitespace + ';:'
# the algorithm is about the same speed for any CHUNK_SIZE chosen.
# grab a good-sized chunk, but not too large to overwhelm memory.
@@ -48,17 +44,15 @@ class _TokenStream:
# out more complex solutions.
buf = self.buf
lbuf = len(buf)
idx = self.idx
while 1:
if idx == lbuf:
if idx == len(buf):
buf = self.rcsfile.read(self.CHUNK_SIZE)
if buf == '':
# signal EOF by returning None as the token
del self.buf # so we fail if get() is called again
return None
lbuf = len(buf)
idx = 0
if buf[idx] not in string.whitespace:
@@ -66,7 +60,7 @@ class _TokenStream:
idx = idx + 1
if buf[idx] in ';:':
if buf[idx] == ';' or buf[idx] == ':':
self.buf = buf
self.idx = idx + 1
return buf[idx]
@@ -76,18 +70,17 @@ class _TokenStream:
token = ''
while 1:
# find token characters in the current buffer
while end < lbuf and buf[end] not in self.token_term:
while end < len(buf) and buf[end] not in self.token_term:
end = end + 1
token = token + buf[idx:end]
if end < lbuf:
if end < len(buf):
# we stopped before the end, so we have a full token
idx = end
break
# we stopped at the end of the buffer, so we may have a partial token
buf = self.rcsfile.read(self.CHUNK_SIZE)
lbuf = len(buf)
idx = end = 0
self.buf = buf
@@ -101,24 +94,22 @@ class _TokenStream:
chunks = [ ]
while 1:
if idx == lbuf:
if idx == len(buf):
idx = 0
buf = self.rcsfile.read(self.CHUNK_SIZE)
if buf == '':
raise RuntimeError, 'EOF'
lbuf = len(buf)
i = string.find(buf, '@', idx)
if i == -1:
chunks.append(buf[idx:])
idx = lbuf
idx = len(buf)
continue
if i == lbuf - 1:
if i == len(buf) - 1:
chunks.append(buf[idx:i])
idx = 0
buf = '@' + self.rcsfile.read(self.CHUNK_SIZE)
if buf == '@':
raise RuntimeError, 'EOF'
lbuf = len(buf)
continue
if buf[i + 1] == '@':
chunks.append(buf[idx:i+1])

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-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
@@ -15,50 +15,17 @@
import os
import os.path
import re
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 re.search(_re_url, rootpath):
return rootpath[-1] == '/' and rootpath[:-1] or rootpath
return os.path.normpath(rootpath)
def expand_root_parent(parent_path):
@@ -68,7 +35,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)
@@ -77,20 +43,6 @@ def expand_root_parent(parent_path):
return roots
def find_root_in_parent(parent_path, rootname):
"""Search PARENT_PATH for a root named ROOTNAME, returning the
canonicalized ROOTPATH of the root if found; return None if no such
root is found."""
if not re.search(_re_url, parent_path):
assert os.path.isabs(parent_path)
rootpath = os.path.join(parent_path, rootname)
format_path = os.path.join(rootpath, "format")
if os.path.exists(format_path):
return canonicalize_rootpath(rootpath)
return None
def SubversionRepository(name, rootpath, authorizer, utilities, config_dir):
rootpath = canonicalize_rootpath(rootpath)
if re.search(_re_url, rootpath):

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
@@ -18,12 +18,10 @@ import os
import string
import re
import tempfile
import popen2
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
from svn import core, delta, client, wc, ra
@@ -54,71 +52,13 @@ 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
try:
client.svn_client_log4([url], start_rev, start_rev, end_rev,
log_limit, include_changes, 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.
def cb_convert(paths, revision, author, date, message, pool):
class svn_log_entry_t:
pass
log_entry = svn_log_entry_t()
log_entry.changed_paths = paths
log_entry.revision = revision
log_entry.revprops = { core.SVN_PROP_REVISION_LOG : message,
core.SVN_PROP_REVISION_AUTHOR : author,
core.SVN_PROP_REVISION_DATE : date,
}
cb_func(log_entry, pool)
client.svn_client_log2([url], start_rev, end_rev, log_limit,
include_changes, not cross_copies, cb_convert, ctx)
def setup_client_ctx(config_dir):
# Ensure that the configuration directory exists.
core.svn_config_ensure(config_dir)
# Fetch the configuration (and 'config' bit thereof).
cfg = core.svn_config_get_config(config_dir)
config = cfg.get(core.SVN_CONFIG_CATEGORY_CONFIG)
# Here's the compat-sensitive part: try to use
# svn_cmdline_create_auth_baton(), and fall back to making our own
# if that fails.
try:
auth_baton = core.svn_cmdline_create_auth_baton(1, None, None, config_dir,
1, 1, config, None)
except AttributeError:
auth_baton = core.svn_auth_open([
client.svn_client_get_simple_provider(),
client.svn_client_get_username_provider(),
client.svn_client_get_ssl_server_trust_file_provider(),
client.svn_client_get_ssl_client_cert_file_provider(),
client.svn_client_get_ssl_client_cert_pw_file_provider(),
])
if config_dir is not None:
core.svn_auth_set_parameter(auth_baton,
core.SVN_AUTH_PARAM_CONFIG_DIR,
config_dir)
# Create, setup, and return the client context baton.
ctx = client.svn_client_create_context()
ctx.config = cfg
ctx.auth_baton = auth_baton
return 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 = '/'
@@ -127,16 +67,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)
def add_log(self, paths, revision, author, date, message, pool):
# Changed paths have leading slashes
changed_paths = paths.keys()
changed_paths.sort(lambda a, b: _compare_paths(a, b))
@@ -156,17 +88,13 @@ 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, _datestr_to_date(date), author, message, None,
self.lockinfo, self.path[1:], None, None)
self.logs.append(entry)
if this_path:
self.path = this_path
def cat_to_tempfile(svnrepos, path, rev):
def temp_checkout(svnrepos, path, rev):
"""Check out file revision to temporary file"""
temp = tempfile.mktemp()
stream = core.svn_stream_from_aprfile(temp)
@@ -227,8 +155,21 @@ class RemoteSubversionRepository(vclib.Repository):
def open(self):
# Setup the client context baton, complete with non-prompting authstuffs.
self.ctx = setup_client_ctx(self.config_dir)
# TODO: svn_cmdline_setup_auth_baton() is mo' better (when available)
core.svn_config_ensure(self.config_dir)
self.ctx = client.svn_client_ctx_t()
self.ctx.auth_baton = core.svn_auth_open([
client.svn_client_get_simple_provider(),
client.svn_client_get_username_provider(),
client.svn_client_get_ssl_server_trust_file_provider(),
client.svn_client_get_ssl_client_cert_file_provider(),
client.svn_client_get_ssl_client_cert_pw_file_provider(),
])
self.ctx.config = core.svn_config_get_config(self.config_dir)
if self.config_dir is not None:
core.svn_auth_set_parameter(self.ctx.auth_baton,
core.SVN_AUTH_PARAM_CONFIG_DIR,
self.config_dir)
ra_callbacks = ra.svn_ra_callbacks_t()
ra_callbacks.auth_baton = self.ctx.auth_baton
self.ra_session = ra.svn_ra_open(self.rootpath, ra_callbacks, None,
@@ -236,10 +177,6 @@ class RemoteSubversionRepository(vclib.Repository):
self.youngest = ra.svn_ra_get_latest_revnum(self.ra_session)
self._dirent_cache = { }
self._revinfo_cache = { }
# See if a universal read access determination can be made.
if self.auth and self.auth.check_universal_access(self.name) == 1:
self.auth = None
def rootname(self):
return self.name
@@ -274,23 +211,25 @@ class RemoteSubversionRepository(vclib.Repository):
raise vclib.ItemNotFound(path_parts)
return pathtype
def openfile(self, path_parts, rev, options):
def openfile(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)
tmp_file = tempfile.mktemp()
stream = core.svn_stream_from_aprfile(tmp_file)
### rev here should be the last history revision of the URL
fp = SelfCleanFP(cat_to_tempfile(self, path, rev))
lh_rev, c_rev = self._get_last_history_rev(path_parts, rev)
return fp, lh_rev
client.svn_client_cat(core.Stream(stream), url, _rev2optrev(rev), self.ctx)
core.svn_stream_close(stream)
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]
@@ -298,9 +237,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):
@@ -311,14 +249,12 @@ 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.
entry.date, entry.author, entry.log, revprops, changes = \
self._revinfo(dirent.created_rev)
entry.rev = str(dirent.created_rev)
dirent = dirents[entry.name]
entry.date, entry.author, entry.log, changes = \
self.revinfo(dirent.created_rev)
entry.rev = dirent.created_rev
entry.size = dirent.size
entry.lockinfo = None
if locks.has_key(entry.name):
@@ -331,51 +267,29 @@ 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,
cross_copies, lc.add_log, self.ctx)
client.svn_client_log2([url], _rev2optrev(rev), _rev2optrev(1),
log_limit, 1, not 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()
@@ -395,25 +309,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,
@@ -421,27 +323,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 cached_info[0], cached_info[1], cached_info[2], cached_info[3]
def rawdiff(self, path_parts1, rev1, path_parts2, rev2, type, options={}):
p1 = self._getpath(path_parts1)
@@ -456,17 +352,16 @@ 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, changes = self.revinfo(rev)
return date
try:
temp1 = cat_to_tempfile(self, p1, r1)
temp2 = cat_to_tempfile(self, p2, r2)
temp1 = temp_checkout(self, p1, r1)
temp2 = temp_checkout(self, p2, r2)
info1 = p1, _date_from_rev(r1), r1
info2 = p2, _date_from_rev(r2), r2
return vclib._diff_fp(temp1, temp2, info1, info2, self.diff_cmd, args)
except core.SubversionException, e:
_fix_subversion_exception(e)
if e.apr_err == vclib.svn.core.SVN_ERR_FS_NOT_FOUND:
raise vclib.InvalidRevision
raise
@@ -475,15 +370,6 @@ class RemoteSubversionRepository(vclib.Repository):
props = self.itemprops(path_parts, rev) # does authz-check
return props.has_key(core.SVN_PROP_EXECUTABLE)
def filesize(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)
dirents, locks = self._get_dirents(self._getpath(path_parts[:-1]), rev)
dirent = dirents.get(path_parts[-1], None)
return dirent.size
def _getpath(self, path_parts):
return string.join(path_parts, '/')
@@ -491,11 +377,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)
@@ -504,139 +387,58 @@ 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, message, 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)
def _log_cb(changed_paths, revision, author,
datestr, message, pool, retval=revs):
date = _datestr_to_date(datestr)
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.
try:
changed_paths = log_entry.changed_paths2
paths = (changed_paths or {}).keys()
except:
changed_paths = log_entry.changed_paths
paths = (changed_paths or {}).keys()
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.
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.
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.
text_modified = props_modified = 0
if hasattr(change, 'text_modified'):
if change.text_modified == core.svn_tristate_true:
text_modified = 1
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?"
change = changed_paths[path]
action = action_map.get(change.action, vclib.MODIFIED)
### Wrong, diddily wrong wrong wrong. Can you say,
### "Manufacturing data left and right because it hurts to
### figure out the right stuff?"
if change.copyfrom_path and change.copyfrom_rev:
is_copy = 1
base_path = change.copyfrom_path
@@ -649,61 +451,31 @@ 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))
base_rev, action, is_copy, 0, 0))
found_readable = 1
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
message = None
if not found_readable:
author = None
date = None
revs.append([date, author, message, 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)
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
client.svn_client_log([self.rootpath], optrev, optrev,
1, 0, _log_cb, self.ctx)
return revs[0][0], revs[0][1], revs[0][2], revs[0][3]
##--- custom --##
@@ -714,7 +486,6 @@ class RemoteSubversionRepository(vclib.Repository):
try:
results = ra.get_locations(self.ra_session, path, rev, [old_rev])
except core.SubversionException, e:
_fix_subversion_exception(e)
if e.apr_err == core.SVN_ERR_FS_NOT_FOUND:
raise vclib.ItemNotFound(path)
raise
@@ -722,16 +493,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
@@ -766,33 +544,3 @@ class RemoteSubversionRepository(vclib.Repository):
else:
peg_revision = mid
return peg_revision, path
def get_symlink_target(self, path_parts, rev):
"""Return the target of the symbolic link versioned at PATH_PARTS
in REV, or None if that object is not a symlink."""
path = self._getpath(path_parts)
path_type = self.itemtype(path_parts, rev) # does auth-check
rev = self._getrev(rev)
url = self._geturl(path)
# Symlinks must be files with the svn:special property set on them
# and with file contents which read "link SOME_PATH".
if path_type != vclib.FILE:
return None
pairs = client.svn_client_proplist2(url, _rev2optrev(rev),
_rev2optrev(rev), 0, self.ctx)
props = pairs and pairs[0][1] or {}
if not props.has_key(core.SVN_PROP_SPECIAL):
return None
pathspec = ''
### FIXME: We're being a touch sloppy here, first by grabbing the
### whole file and then by checking only the first line
### of it.
fp = SelfCleanFP(cat_to_tempfile(self, path, rev))
pathspec = fp.readline()
fp.close()
if pathspec[:5] != 'link ':
return None
return pathspec[5:]

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
@@ -15,14 +15,15 @@
import vclib
import os
import os.path
import stat
import string
import cStringIO
import signal
import shutil
import time
import tempfile
import popen
import re
import urllib
from svn import fs, repos, core, client, delta
@@ -30,27 +31,7 @@ from svn import fs, repos, core, client, delta
if (core.SVN_VER_MAJOR, core.SVN_VER_MINOR, core.SVN_VER_PATCH) < (1, 3, 1):
raise Exception, "Version requirement not met (needs 1.3.1 or better)"
### Pre-1.5 Subversion doesn't have SVN_ERR_CEASE_INVOCATION
try:
_SVN_ERR_CEASE_INVOCATION = core.SVN_ERR_CEASE_INVOCATION
except:
_SVN_ERR_CEASE_INVOCATION = core.SVN_ERR_CANCELLED
### Pre-1.5 SubversionException's might not have the .msg and .apr_err members
def _fix_subversion_exception(e):
if not hasattr(e, 'apr_err'):
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,37 +97,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)
# Given a dictionary REVPROPS of revision properties, pull special
# ones out of them and return a 4-tuple containing the log message,
# the author, the date (converted from the date string property), and
# a dictionary of any/all other revprops.
def _split_revprops(revprops):
if not revprops:
return None, None, None, {}
special_props = []
for prop in core.SVN_PROP_REVISION_LOG, \
core.SVN_PROP_REVISION_AUTHOR, \
core.SVN_PROP_REVISION_DATE:
if revprops.has_key(prop):
special_props.append(revprops[prop])
del(revprops[prop])
else:
special_props.append(None)
msg, author, datestr = tuple(special_props)
date = _datestr_to_date(datestr)
return msg, author, date, revprops
return 'file://' + string.join([rootpath, path], "/")
def _datestr_to_date(datestr):
@@ -212,11 +167,60 @@ class NodeHistory:
return
self.histories.append([revision, _cleanup_path(path)])
if self.limit and len(self.histories) == self.limit:
raise core.SubversionException("", _SVN_ERR_CEASE_INVOCATION)
raise core.SubversionException("", core.SVN_ERR_CEASE_INVOCATION)
def __getitem__(self, idx):
return self.histories[idx]
def _get_history(svnrepos, path, rev, path_type, limit=0, options={}):
rev_paths = []
fsroot = svnrepos._getroot(rev)
show_all_logs = options.get('svn_show_all_dir_logs', 0)
if not show_all_logs:
# See if the path is a file or directory.
kind = fs.check_path(fsroot, path)
if kind is core.svn_node_file:
show_all_logs = 1
# Instantiate a NodeHistory collector object, and use it to collect
# history items for PATH@REV.
history = NodeHistory(svnrepos.fs_ptr, show_all_logs, limit)
try:
repos.svn_repos_history(svnrepos.fs_ptr, path, history.add_history,
1, rev, options.get('svn_cross_copies', 0))
except core.SubversionException, e:
if e.apr_err != core.SVN_ERR_CEASE_INVOCATION:
raise
# Now, iterate over those history items, checking for changes of
# location, pruning as necessitated by authz rules.
for hist_rev, hist_path in history:
path_parts = _path_parts(hist_path)
if not vclib.check_path_access(svnrepos, path_parts, path_type, hist_rev):
break
rev_paths.append([hist_rev, hist_path])
return rev_paths
def _log_helper(svnrepos, path, rev, lockinfo):
rev_root = fs.revision_root(svnrepos.fs_ptr, rev)
# Was this path@rev the target of a copy?
copyfrom_rev, copyfrom_path = fs.copied_from(rev_root, path)
# Assemble our LogEntry
date, author, msg, changes = svnrepos.revinfo(rev)
if fs.is_file(rev_root, path):
size = fs.file_length(rev_root, path)
else:
size = None
entry = Revision(rev, date, author, msg, size, lockinfo, path,
copyfrom_path and _cleanup_path(copyfrom_path),
copyfrom_rev)
return entry
def _get_last_history_rev(fsroot, path):
history = fs.node_history(fsroot, path)
history = fs.history_prev(history, 0)
@@ -295,15 +299,14 @@ class FileContentsPipe:
class BlameSource:
def __init__(self, local_url, rev, first_rev, include_text, config_dir):
def __init__(self, local_url, rev, first_rev):
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)
ctx.config = core.svn_config_get_config(config_dir)
ctx = client.ctx_t()
core.svn_config_ensure(None)
ctx.config = core.svn_config_get_config(None)
ctx.auth_baton = core.svn_auth_open([])
try:
### TODO: Is this use of FIRST_REV always what we want? Should we
@@ -311,7 +314,6 @@ class BlameSource:
client.blame2(local_url, _rev2optrev(rev), _rev2optrev(first_rev),
_rev2optrev(rev), self._blame_cb, ctx)
except core.SubversionException, e:
_fix_subversion_exception(e)
if e.apr_err == core.SVN_ERR_CLIENT_IS_BINARY_FILE:
raise vclib.NonTextualFileContents
raise
@@ -320,8 +322,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))
@@ -360,7 +360,7 @@ class LocalSubversionRepository(vclib.Repository):
self.auth = authorizer
self.svn_client_path = utilities.svn or 'svn'
self.diff_cmd = utilities.diff or 'diff'
self.config_dir = config_dir or None
self.config_dir = config_dir
# See if this repository is even viewable, authz-wise.
if not vclib.check_root_access(self):
@@ -390,10 +390,6 @@ class LocalSubversionRepository(vclib.Repository):
self._fsroots = {}
self._revinfo_cache = {}
# See if a universal read access determination can be made.
if self.auth and self.auth.check_universal_access(self.name) == 1:
self.auth = None
def rootname(self):
return self.name
@@ -409,14 +405,19 @@ class LocalSubversionRepository(vclib.Repository):
def itemtype(self, path_parts, rev):
rev = self._getrev(rev)
basepath = self._getpath(path_parts)
pathtype = self._gettype(basepath, rev)
if pathtype is None:
kind = fs.check_path(self._getroot(rev), basepath)
pathtype = None
if kind == core.svn_node_dir:
pathtype = vclib.DIR
elif kind == core.svn_node_file:
pathtype = vclib.FILE
else:
raise vclib.ItemNotFound(path_parts)
if not vclib.check_path_access(self, path_parts, pathtype, rev):
raise vclib.ItemNotFound(path_parts)
return pathtype
def openfile(self, path_parts, rev, options):
def openfile(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)
@@ -455,7 +456,7 @@ class LocalSubversionRepository(vclib.Repository):
continue
path = self._getpath(entry_path_parts)
entry_rev = _get_last_history_rev(fsroot, path)
date, author, msg, revprops, changes = self._revinfo(entry_rev)
date, author, msg, changes = self.revinfo(entry_rev)
entry.rev = str(entry_rev)
entry.date = date
entry.author = author
@@ -504,19 +505,20 @@ class LocalSubversionRepository(vclib.Repository):
# 'limit' parameter here as numeric cut-off for the depth of our
# history search.
if options.get('svn_latest_log', 0):
revision = self._log_helper(path, rev, lockinfo)
revision = _log_helper(self, path, rev, lockinfo)
if revision:
revision.prev = None
revs.append(revision)
else:
history = self._get_history(path, rev, path_type, first + limit, options)
history = _get_history(self, path, rev, path_type,
first + limit, options)
if len(history) < first:
history = []
if limit:
history = history[first:first+limit]
for hist_rev, hist_path in history:
revision = self._log_helper(hist_path, hist_rev, lockinfo)
revision = _log_helper(self, hist_path, hist_rev, lockinfo)
if revision:
# If we have unreadable copyfrom data, obscure it.
if revision.copy_path is not None:
@@ -537,23 +539,114 @@ 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:
raise vclib.Error("Path '%s' is not a file." % path)
rev = self._getrev(rev)
fsroot = self._getroot(rev)
history = self._get_history(path, rev, path_type, 0,
{'svn_cross_copies': 1})
history = _get_history(self, path, rev, path_type, 0,
{'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)
return source, youngest_rev
def _revinfo_raw(self, rev):
fsroot = self._getroot(rev)
# Get the changes for the revision
editor = repos.ChangeCollector(self.fs_ptr, fsroot)
e_ptr, e_baton = delta.make_editor(editor)
repos.svn_repos_replay(fsroot, e_ptr, e_baton)
changes = editor.get_changes()
changedpaths = {}
# Now get the revision property info. Would use
# editor.get_root_props(), but something is broken there...
revprops = fs.revision_proplist(self.fs_ptr, rev)
msg = revprops.get(core.SVN_PROP_REVISION_LOG)
author = revprops.get(core.SVN_PROP_REVISION_AUTHOR)
datestr = revprops.get(core.SVN_PROP_REVISION_DATE)
# Copy the Subversion changes into a new hash, converting them into
# ChangedPath objects.
found_readable = found_unreadable = 0
for path in changes.keys():
change = changes[path]
if change.path:
change.path = _cleanup_path(change.path)
if change.base_path:
change.base_path = _cleanup_path(change.base_path)
is_copy = 0
if not hasattr(change, 'action'): # new to subversion 1.4.0
action = vclib.MODIFIED
if not change.path:
action = vclib.DELETED
elif change.added:
action = vclib.ADDED
replace_check_path = path
if change.base_path and change.base_rev:
replace_check_path = change.base_path
if changedpaths.has_key(replace_check_path) \
and changedpaths[replace_check_path].action == vclib.DELETED:
action = vclib.REPLACED
else:
if change.action == repos.CHANGE_ACTION_ADD:
action = vclib.ADDED
elif change.action == repos.CHANGE_ACTION_DELETE:
action = vclib.DELETED
elif change.action == repos.CHANGE_ACTION_REPLACE:
action = vclib.REPLACED
else:
action = vclib.MODIFIED
if (action == vclib.ADDED or action == vclib.REPLACED) \
and change.base_path \
and change.base_rev:
is_copy = 1
if change.item_kind == core.svn_node_dir:
pathtype = vclib.DIR
elif change.item_kind == core.svn_node_file:
pathtype = vclib.FILE
else:
pathtype = None
parts = _path_parts(path)
if vclib.check_path_access(self, parts, pathtype, rev):
if is_copy and change.base_path and (change.base_path != path):
parts = _path_parts(change.base_path)
if not vclib.check_path_access(self, parts, pathtype, change.base_rev):
is_copy = 0
change.base_path = None
change.base_rev = None
changedpaths[path] = SVNChangedPath(path, rev, pathtype,
change.base_path,
change.base_rev, action,
is_copy, change.text_changed,
change.prop_changes)
found_readable = 1
else:
found_unreadable = 1
# Return our tuple, auth-filtered: date, author, msg, changes
if found_unreadable:
msg = None
if not found_readable:
author = None
datestr = None
date = _datestr_to_date(datestr)
return date, author, msg, changedpaths.values()
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 cached_info[0], cached_info[1], cached_info[2], cached_info[3]
def rawdiff(self, path_parts1, rev1, path_parts2, rev2, type, options={}):
p1 = self._getpath(path_parts1)
@@ -568,7 +661,7 @@ class LocalSubversionRepository(vclib.Repository):
args = vclib._diff_args(type, options)
def _date_from_rev(rev):
date, author, msg, revprops, changes = self._revinfo(rev)
date, author, msg, changes = self.revinfo(rev)
return date
try:
@@ -578,7 +671,6 @@ class LocalSubversionRepository(vclib.Repository):
info2 = p2, _date_from_rev(r2), r2
return vclib._diff_fp(temp1, temp2, info1, info2, self.diff_cmd, args)
except core.SubversionException, e:
_fix_subversion_exception(e)
if e.apr_err == core.SVN_ERR_FS_NOT_FOUND:
raise vclib.InvalidRevision
raise
@@ -586,253 +678,6 @@ class LocalSubversionRepository(vclib.Repository):
def isexecutable(self, path_parts, rev):
props = self.itemprops(path_parts, rev) # does authz-check
return props.has_key(core.SVN_PROP_EXECUTABLE)
def filesize(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)
fsroot = self._getroot(self._getrev(rev))
return fs.file_length(fsroot, path)
##--- helpers ---##
def _revinfo(self, rev, include_changed_paths=0):
"""Internal-use, cache-friendly revision information harvester."""
def _get_changed_paths(fsroot):
"""Return a 3-tuple: found_readable, found_unreadable, changed_paths."""
editor = repos.ChangeCollector(self.fs_ptr, fsroot)
e_ptr, e_baton = delta.make_editor(editor)
repos.svn_repos_replay(fsroot, e_ptr, e_baton)
changedpaths = {}
changes = editor.get_changes()
# Copy the Subversion changes into a new hash, checking
# authorization and converting them into ChangedPath objects.
found_readable = found_unreadable = 0
for path in changes.keys():
change = changes[path]
if change.path:
change.path = _cleanup_path(change.path)
if change.base_path:
change.base_path = _cleanup_path(change.base_path)
is_copy = 0
if not hasattr(change, 'action'): # new to subversion 1.4.0
action = vclib.MODIFIED
if not change.path:
action = vclib.DELETED
elif change.added:
action = vclib.ADDED
replace_check_path = path
if change.base_path and change.base_rev:
replace_check_path = change.base_path
if changedpaths.has_key(replace_check_path) \
and changedpaths[replace_check_path].action == vclib.DELETED:
action = vclib.REPLACED
else:
if change.action == repos.CHANGE_ACTION_ADD:
action = vclib.ADDED
elif change.action == repos.CHANGE_ACTION_DELETE:
action = vclib.DELETED
elif change.action == repos.CHANGE_ACTION_REPLACE:
action = vclib.REPLACED
else:
action = vclib.MODIFIED
if (action == vclib.ADDED or action == vclib.REPLACED) \
and change.base_path \
and change.base_rev:
is_copy = 1
if change.item_kind == core.svn_node_dir:
pathtype = vclib.DIR
elif change.item_kind == core.svn_node_file:
pathtype = vclib.FILE
else:
pathtype = None
parts = _path_parts(path)
if vclib.check_path_access(self, parts, pathtype, rev):
if is_copy and change.base_path and (change.base_path != path):
parts = _path_parts(change.base_path)
if not vclib.check_path_access(self, parts, pathtype,
change.base_rev):
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,
is_copy, change.text_changed,
change.prop_changes)
found_readable = 1
else:
found_unreadable = 1
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
return copyfrom_path, copyfrom_rev
def _simple_auth_check(fsroot):
"""Return a 2-tuple: found_readable, found_unreadable."""
found_unreadable = found_readable = 0
if hasattr(fs, 'paths_changed2'):
changes = fs.paths_changed2(fsroot)
else:
changes = fs.paths_changed(fsroot)
paths = changes.keys()
for path in paths:
change = changes[path]
pathtype = None
if hasattr(change, 'node_kind'):
if change.node_kind == core.svn_node_file:
pathtype = vclib.FILE
elif change.node_kind == core.svn_node_dir:
pathtype = vclib.DIR
parts = _path_parts(path)
if pathtype is None:
# Figure out the pathtype so we can query the authz subsystem.
if change.change_kind == fs.path_change_delete:
# Deletions are annoying, because they might be underneath
# copies (make their previous location non-trivial).
prev_parts = parts
prev_rev = rev - 1
parent_parts = parts[:-1]
while parent_parts:
parent_path = '/' + self._getpath(parent_parts)
parent_change = changes.get(parent_path)
if not (parent_change and \
(parent_change.change_kind == fs.path_change_add or
parent_change.change_kind == fs.path_change_replace)):
del(parent_parts[-1])
continue
copyfrom_path, copyfrom_rev = \
_get_change_copyinfo(fsroot, parent_path, parent_change)
if copyfrom_path:
prev_rev = copyfrom_rev
prev_parts = _path_parts(copyfrom_path) + \
parts[len(parent_parts):]
break
del(parent_parts[-1])
pathtype = self._gettype(self._getpath(prev_parts), prev_rev)
else:
pathtype = self._gettype(self._getpath(parts), rev)
if vclib.check_path_access(self, parts, pathtype, rev):
found_readable = 1
copyfrom_path, copyfrom_rev = \
_get_change_copyinfo(fsroot, path, change)
if copyfrom_path and copyfrom_path != path:
parts = _path_parts(copyfrom_path)
if not vclib.check_path_access(self, parts, pathtype,
copyfrom_rev):
found_unreadable = 1
else:
found_unreadable = 1
if found_readable and found_unreadable:
break
return found_readable, found_unreadable
def _revinfo_helper(rev, include_changed_paths):
# Get the revision property info. (Would use
# editor.get_root_props(), but something is broken there...)
revprops = fs.revision_proplist(self.fs_ptr, rev)
msg, author, date, revprops = _split_revprops(revprops)
# Optimization: If our caller doesn't care about the changed
# paths, and we don't need them to do authz determinations, let's
# get outta here.
if self.auth is None and not include_changed_paths:
return date, author, msg, revprops, None
# If we get here, then we either need the changed paths because we
# were asked for them, or we need them to do authorization checks.
#
# If we only need them for authorization checks, though, we
# won't bother generating fully populated ChangedPath items (the
# cost is too great).
fsroot = self._getroot(rev)
if include_changed_paths:
found_readable, found_unreadable, changedpaths = \
_get_changed_paths(fsroot)
else:
changedpaths = None
found_readable, found_unreadable = _simple_auth_check(fsroot)
# Filter our metadata where necessary, and return the requested data.
if found_unreadable:
msg = None
if not found_readable:
author = None
date = None
return date, author, msg, revprops, changedpaths
# 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 = _revinfo_helper(rev, include_changed_paths)
self._revinfo_cache[rev] = cached_info
return tuple(cached_info)
def _log_helper(self, path, rev, lockinfo):
rev_root = fs.revision_root(self.fs_ptr, rev)
copyfrom_rev, copyfrom_path = fs.copied_from(rev_root, path)
date, author, msg, revprops, changes = self._revinfo(rev)
if fs.is_file(rev_root, path):
size = fs.file_length(rev_root, path)
else:
size = None
return Revision(rev, date, author, msg, size, lockinfo, path,
copyfrom_path and _cleanup_path(copyfrom_path),
copyfrom_rev)
def _get_history(self, path, rev, path_type, limit=0, options={}):
if self.youngest == 0:
return []
rev_paths = []
fsroot = self._getroot(rev)
show_all_logs = options.get('svn_show_all_dir_logs', 0)
if not show_all_logs:
# See if the path is a file or directory.
kind = fs.check_path(fsroot, path)
if kind is core.svn_node_file:
show_all_logs = 1
# Instantiate a NodeHistory collector object, and use it to collect
# history items for PATH@REV.
history = NodeHistory(self.fs_ptr, show_all_logs, limit)
try:
repos.svn_repos_history(self.fs_ptr, path, history.add_history,
1, rev, options.get('svn_cross_copies', 0))
except core.SubversionException, e:
_fix_subversion_exception(e)
if e.apr_err != _SVN_ERR_CEASE_INVOCATION:
raise
# Now, iterate over those history items, checking for changes of
# location, pruning as necessitated by authz rules.
for hist_rev, hist_path in history:
path_parts = _path_parts(hist_path)
if not vclib.check_path_access(self, path_parts, path_type, hist_rev):
break
rev_paths.append([hist_rev, hist_path])
return rev_paths
def _getpath(self, path_parts):
return string.join(path_parts, '/')
@@ -841,11 +686,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)
@@ -858,20 +700,7 @@ class LocalSubversionRepository(vclib.Repository):
r = self._fsroots[rev] = fs.revision_root(self.fs_ptr, rev)
return r
def _gettype(self, path, rev):
# Similar to itemtype(), but without the authz check. Returns
# None for missing paths.
try:
kind = fs.check_path(self._getroot(rev), path)
except:
return None
if kind == core.svn_node_dir:
return vclib.DIR
if kind == core.svn_node_file:
return vclib.FILE
return None
##--- custom ---##
##--- custom --##
def get_youngest_revision(self):
return self.youngest
@@ -881,7 +710,6 @@ class LocalSubversionRepository(vclib.Repository):
results = repos.svn_repos_trace_node_locations(self.fs_ptr, path,
rev, [old_rev], _allow_all)
except core.SubversionException, e:
_fix_subversion_exception(e)
if e.apr_err == core.SVN_ERR_FS_NOT_FOUND:
raise vclib.ItemNotFound(path)
raise
@@ -931,7 +759,6 @@ class LocalSubversionRepository(vclib.Repository):
try:
mid_id = fs.node_id(self._getroot(mid), path)
except core.SubversionException, e:
_fix_subversion_exception(e)
if e.apr_err == core.SVN_ERR_FS_NOT_FOUND:
cmp = -1
else:
@@ -949,32 +776,3 @@ class LocalSubversionRepository(vclib.Repository):
return peg_revision, path
finally:
pass
def get_symlink_target(self, path_parts, rev):
"""Return the target of the symbolic link versioned at PATH_PARTS
in REV, or None if that object is not a symlink."""
path = self._getpath(path_parts)
rev = self._getrev(rev)
path_type = self.itemtype(path_parts, rev) # does auth-check
fsroot = self._getroot(rev)
# Symlinks must be files with the svn:special property set on them
# and with file contents which read "link SOME_PATH".
if path_type != vclib.FILE:
return None
props = fs.node_proplist(fsroot, path)
if not props.has_key(core.SVN_PROP_SPECIAL):
return None
pathspec = ''
### FIXME: We're being a touch sloppy here, only checking the first line
### of the file.
stream = fs.file_contents(fsroot, path)
try:
pathspec, eof = core.svn_stream_readline(stream, '\n')
finally:
core.svn_stream_close(stream)
if pathspec[:5] != 'link ':
return None
return pathspec[5:]

File diff suppressed because it is too large Load Diff

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

@@ -27,9 +27,7 @@ numbers, and not literal):
3. Verify that copyright years are correct in both the license-1.html
file and the source code.
4. Update and commit the 'CHANGES' file, using any available crystal
balls or other forward-looking devices to take a stab at the
release date.
4. Update and commit the 'CHANGES' file.
5. Test, test, test! There is no automatic testsuite available. So
just run with permuting different `viewvc.conf' settings... and
@@ -40,86 +38,50 @@ numbers, and not literal):
should exactly reflect what you wish to distribute and dub "the
release".
7. Update your release branch working copy to HEAD.
svn up
8. Edit the file 'lib/viewvc.py' and remove the "-dev" suffix from
7. Edit the file 'lib/viewvc.py' and remove the "-dev" suffix from
__version__. The remainder should be of the form "X.Y.Z", where X,
Y, and Z are positive integers.
Y, and Z are positive integers. Do NOT commit this change.
*** Do NOT commit this change. ***
8. Update your working copy to HEAD, and tag the release:
9. "Peg" the contributed templates externals definition to the
current HEAD revision:
svn update
svn cp -m "Tag the X.Y.Z final release." . \
http://viewvc.tigris.org/svn/viewvc/tags/X.Y.Z
svn pedit svn:externals .
(squeeze "-rBASE_REV", where BASE_REV is the current HEAD revision
number, between 'templates-contrib' and the target URL).
9. Go into an empty directory and run the 'make-release' script:
*** Do NOT commit this change. ***
tools/make-release viewvc-X.Y.Z X.Y.Z
10. Tag the release:
svn cp -m "Tag the X.Y.Z final release." . ^/tags/X.Y.Z
This will create a copy of the release branch, plus your local
modifications to the svn:externals property and lib/viewvc.py
file, to the tag location.
11. Revert the changes in your working copy.
svn revert -R .
12. Go into an empty directory and run the 'make-release' script:
tools/make-release viewvc-X.Y.Z tags/X.Y.Z
13. Verify the archive files:
10. Verify the archive files:
- do they have a LICENSE.html file?
- do they have necessary include documentation?
- do they *not* have unnecessary stuff?
- do they install and work correctly?
14. Upload the created archive files (tar.gz and zip) into the Files
11. Upload the created archive files (tar.gz and zip) into the Files
and Documents section of the Tigris.org project, and modify the
CHECKSUMS document there accordingly:
CHECKSUMS document there accordingly. Also, drop a copy of the
archive files into the root directory of the viewvc.org website
(unversioned).
http://viewvc.tigris.org/servlets/ProjectDocumentList?folderID=6004
12. 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.
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.
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:
13. Edit the file 'lib/viewvc.py' again, re-adding the "-dev" suffix
and incrementing the patch number assigned to the __version__
variable, and add a new empty block in the branch's CHANGES file,
and commit:
svn ci -m "Begin a new release cycle."
17. Edit the Issue Tracker configuration options, adding a new Version
14. Edit the Issue Tracker configuration options, adding a new Version
for the just-released one, and a new Milestone for the next patch
(and possibly, minor or major) release. (For the Milestone sort
key, use a packed integer XXYYZZ: 1.0.3 == 10003, 2.11.4 == 21104.)
http://viewvc.tigris.org/issues/editversions.cgi?component=viewvc&action=add
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.
15. Write an announcement explaining all the cool new features and
post it to the announce@ list, to the project's News area, and to
other places interested in this sort of stuff, such as Freshmeat
(http://www.freshmeat.net).

View File

@@ -21,13 +21,13 @@
<th style="width:6%;"></th>
<th style="width:47%; vertical-align:top;">
[is left.path right.path][else][left.path][end]
revision [if-any left_view_href]<a href="[left_view_href]">[end][left.rev][if-any left_view_href]</a>[end][if-any left.author] by <em>[left.author]</em>[end],
revision [if-any left_view_href]<a href="[left_view_href]">[end][left.rev][if-any left_view_href]</a>[end],
[left.date]
[if-any left.tag]<br />Tag: [left.tag][end]
</th>
<th style="width:47%; vertical-align:top;">
[is left.path right.path][else][right.path][end]
revision [if-any right_view_href]<a href="[right_view_href]">[end][right.rev][if-any right_view_href]</a>[end][if-any right.author] by <em>[right.author]</em>[end],
revision [if-any right_view_href]<a href="[right_view_href]">[end][right.rev][if-any right_view_href]</a>[end],
[right.date]
[if-any right.tag]<br />Tag: [right.tag][end]
</th>
@@ -49,7 +49,7 @@
[else]
[is changes.type "add"]
<tr>
<td class="vc_diff_line_number" id="l[changes.line_number]">[if-any right.annotate_href]<a href="[right.annotate_href]#l[changes.line_number]">[changes.line_number]</a>[else][changes.line_number][end]</td>
<td id="l[changes.line_number]">[if-any right.annotate_href]<a href="[right.annotate_href]#l[changes.line_number]">[changes.line_number]</a>[else][changes.line_number][end]</td>
<td class="vc_diff_empty">&nbsp;</td>
<td class="vc_diff_add">&nbsp;[changes.right]</td>
</tr>
@@ -64,7 +64,7 @@
[is changes.type "change"]
<tr>
[if-any changes.have_right]
<td class="vc_diff_line_number" id="l[changes.line_number]">[if-any right.annotate_href]<a href="[right.annotate_href]#l[changes.line_number]">[changes.line_number]</a>[else][changes.line_number][end]</td>
<td id="l[changes.line_number]">[if-any right.annotate_href]<a href="[right.annotate_href]#l[changes.line_number]">[changes.line_number]</a>[else][changes.line_number][end]</td>
[else]
<td></td>
[end]
@@ -112,7 +112,7 @@
</tr>
[else]
<tr>
<td class="vc_diff_line_number" id="l[changes.line_number]">[if-any right.annotate_href]<a href="[right.annotate_href]#l[changes.line_number]">[changes.line_number]</a>[else][changes.line_number][end]</td>
<td id="l[changes.line_number]">[if-any right.annotate_href]<a href="[right.annotate_href]#l[changes.line_number]">[changes.line_number]</a>[else][changes.line_number][end]</td>
<td class="vc_diff_nochange">&nbsp;[changes.left]</td>
<td class="vc_diff_nochange">&nbsp;[changes.right]</td>
</tr>

View File

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

View File

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

View File

@@ -94,6 +94,9 @@ form { margin: 0; }
.vc_properties {
margin: 1em 0;
}
.vc_properties h2 {
font-size: 115%;
}
/*** File Content Markup Styles ***/
@@ -226,8 +229,6 @@ form { margin: 0; }
font-family: sans-serif;
font-size: smaller;
}
.vc_diff_line_number {
}
.vc_raw_diff {
background-color: #cccccc;
font-size: smaller;
@@ -269,14 +270,3 @@ table.vc_idiff tbody th {
.vc_query_form {
background-color: #e6e6e6;
}
/*** Warning! ***/
.vc_warning {
border-width: 1px 2px 2px 2px;
border-color: black;
border-style: solid;
background-color: red;
color: white;
padding: 0.5em;
}

View File

@@ -1,19 +1,5 @@
[# ------------------------------------------------------------------------- ]
[# CUSTOMIZE ME: To avoid displaying "binary garbage" -- the contents of ]
[# files with non-human-readable file formats -- change the value of the ]
[# hide_binary_garbage variable below to 1. ]
[# ------------------------------------------------------------------------- ]
[define hide_binary_garbage]0[end]
[# ------------------------------------------------------------------------- ]
[# 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]Annotate of /[where][end]
[define help_href][docroot]/help_rootview.html[end]
[# end]
@@ -22,7 +8,7 @@
<hr />
<div class="vc_summary">
Revision [if-any revision_href]<a href="[revision_href]"><strong>[rev]</strong></a>[else]<strong>[rev]</strong>[end] -
([is annotation "annotated"]<a href="[view_href]"><strong>hide annotations</strong></a>[else]<a href="[annotate_href]"><strong>show annotations</strong></a>[end])
([if-any annotation][is annotation "annotated"]<a href="[view_href]"><strong>hide annotations</strong></a>[end][else]<a href="[annotate_href]"><strong>show annotations</strong></a>[end])
[if-any download_href](<a href="[download_href]"><strong>download</strong></a>)[end]
[if-any download_text_href](<a href="[download_text_href]"><strong>as text</strong></a>)[end]
@@ -57,12 +43,14 @@ Revision [if-any revision_href]<a href="[revision_href]"><strong>[rev]</strong><
[if-any lockinfo]
<br />Lock status: <img src="[docroot]/images/lock.png" alt="Locked" width="16" height="16" /> [lockinfo]
[end]
[if-any annotation]
[is annotation "binary"]
<br /><strong>Unable to calculate annotation data on binary file contents.</strong>
[end]
[is annotation "error"]
<br /><strong>Error occurred while calculating annotation data.</strong>
[end]
[end]
[is state "dead"]
<br /><strong><em>FILE REMOVED</em></strong>
[end]
@@ -71,14 +59,6 @@ Revision [if-any revision_href]<a href="[revision_href]"><strong>[rev]</strong><
[end]
</div>
[if-any prefer_markup][define hide_binary_garbage]0[end][end]
[if-any image_src_href][define hide_binary_garbage]0[end][end]
[is hide_binary_garbage "1"]
<p><strong>This file's contents are not viewable.
[if-any download_href]Please <a href="[download_href]">download</a>
this version of the file in order to view it.[end]</strong></p>
[else]
[define last_rev]0[end]
[define rowclass]vc_row_even[end]
@@ -121,7 +101,6 @@ Revision [if-any revision_href]<a href="[revision_href]"><strong>[rev]</strong><
</div>
[end]
[end]
[end]
[include "include/props.ezt"]
[include "include/footer.ezt"]

View File

@@ -32,7 +32,7 @@
<td>[include "pathrev_form.ezt"]</td>
</tr>
[if-any search_re_action]
[if-any search_re_form]
<tr>
<td>Filter files by content:</td>
<td><form method="get" action="[search_re_action]" style="display: inline;">
@@ -63,7 +63,7 @@
</table>
[is picklist_len "0"]
[is cfg.options.use_pagesize "0"]
[else]
[is picklist_len "1"]
[else]

View File

@@ -2,7 +2,7 @@
[is pathtype "file"]
<a href="[up_href]"><img src="[docroot]/images/back_small.png" class="vc_icon" alt="Parent Directory" /> Parent Directory</a>
[if-any log_href]
| <a href="[log_href]"><img src="[docroot]/images/log.png" class="vc_icon" alt="Revision Log" /> Revision Log</a>
| <a href="[log_href][if-any log_href_rev]#rev[log_href_rev][end]"><img src="[docroot]/images/log.png" class="vc_icon" alt="Revision Log" /> Revision Log</a>
[end]
[if-any graph_href]
| <a href="[graph_href]"><img src="[docroot]/images/cvsgraph_16x16.png" class="vc_icon" alt="View Revision Graph" /> Revision Graph</a>

View File

@@ -5,7 +5,7 @@
<table>
<tr>
<td>[if-any cfg.general.address]<address><a href="mailto:[cfg.general.address]">[cfg.general.address]</a></address>[else]&nbsp;[end]</td>
<td style="text-align: right;">[if-any help_href]<strong><a href="[help_href]">ViewVC Help</a></strong>[else]&nbsp;[end]</td>
<td style="text-align: right;"><strong><a href="[help_href]">ViewVC Help</a></strong></td>
</tr>
<tr>
<td>Powered by <a href="http://viewvc.tigris.org/">ViewVC [vsn]</a></td>

View File

@@ -5,7 +5,7 @@
<head>
<title>[if-any rootname][[][rootname]][else]ViewVC[end] [page_title]</title>
<meta name="generator" content="ViewVC [vsn]" />
<link rel="shortcut icon" href="[docroot]/images/favicon.ico" />
<link rel="shortcut icon" href="[docroot]/images/favicon.ico" type="image/x-icon" />
<link rel="stylesheet" href="[docroot]/styles.css" type="text/css" />
[if-any rss_href]<link rel="alternate" type="application/rss+xml" title="RSS [[][rootname]][where]" href="[rss_href]" />[end]
</head>

View File

@@ -1,4 +1,4 @@
[is picklist_len "0"]
[is cfg.options.use_pagesize "0"]
[else]
[is picklist_len "1"]
[else]
@@ -17,4 +17,6 @@
</select>
</form>
[end]
[end]
[end]

View File

@@ -4,13 +4,13 @@
<!-- ViewVC :: http://www.viewvc.org/ -->
<head>
<title>Checkin Database Query</title>
[if-any docroot]<link rel="stylesheet" href="[docroot]/styles.css" type="text/css" />[end]
<link rel="stylesheet" href="[docroot]/styles.css" type="text/css" />
</head>
<body>
[# setup page definitions]
[define help_href][if-any docroot][docroot]/help_query.html[end][end]
[define help_href][docroot]/help_query.html[end]
[# end]
<p>
@@ -158,15 +158,7 @@
[is query "skipped"]
[else]
<p><strong>[num_commits]</strong> matches found.</p>
[if-any row_limit_reached]
<p class="vc_warning">WARNING: These query results have been
artificially limited by an administrative threshold value and do
<em>not</em> represent the entirety of the data set which matches
the query. Consider modifying your query to be more specific</a>,
using your version control tool's query capabilities, or asking
your administrator to raise the database response size
threshold.</p>
[end]
[if-any commits]
<table cellspacing="0" cellpadding="2">
<thead>
@@ -210,21 +202,21 @@
</td>
[# uncommment, if you want a separate Description column:
{if-index commits.files first}
{if-index commits.files first{
<td style="vertical-align:top;" rowspan="{commits.num_files}">
{if-any commits.log}{commits.log}{else}&nbsp;{end}
{commits.log}
</td>
{end}
(substitute brackets for the braces)
]
</tr>
[# and also take the following out in the "Description column" case:]
[# and also take the following out in the "Description column"-case:]
[if-index commits.files last]
<tr class="vc_row_[if-index commits even]even[else]odd[end]">
<td>&nbsp;</td>
<td colspan="5"><strong>Log:</strong><br />
<pre class="vc_log">[if-any commits.log][commits.log][else]&nbsp;[end]</pre></td>
<pre class="vc_log">[commits.log]</pre></td>
</tr>
[end]
[# ---]

View File

@@ -7,15 +7,6 @@
<p><strong>[english_query]</strong></p>
[# <!-- {sql} --> ]
[if-any row_limit_reached]
<p class="vc_warning">WARNING: These query results have been
artificially limited by an administrative threshold value and do
<em>not</em> represent the entirety of the data set which matches
the query. Consider <a href="[queryform_href]">modifying your
query to be more specific</a>, using your version control tool's
query capabilities, or asking your administrator to raise the
database response size threshold.</p>
[end]
<p><a href="[queryform_href]">Modify query</a></p>
<p><a href="[backout_href]">Show commands which could be used to back out these changes</a></p>

View File

@@ -6,7 +6,6 @@
[include "include/header.ezt" "revision"]
<hr />
<form method="get" action="[jump_rev_action]">
<table cellspacing="1" cellpadding="2" style="width: auto;">
<tr align="left">
@@ -29,11 +28,6 @@
<td>[if-any date][date][else]<em>(unknown date)</em>[end]
[if-any ago]<em>([ago] ago)</em>[end]</td>
</tr>
<tr align="left">
<th>Changed paths:</th>
<td><strong>[num_changes]</strong>
[if-any more_changes](showing only [limit_changes]; <a href="[more_changes_href]">show all</a>)[end][if-any first_changes](<a href="[first_changes_href]">show only first [first_changes]</a>)[end]</td>
</tr>
<tr align="left">
<th>Log Message:</th>
<td><pre class="vc_log">[log]</pre></td>
@@ -43,7 +37,18 @@
<hr />
<h2>Changed paths</h2>
<p><strong>Changed paths:</strong></p>
[if-any more_changes]
<div>
Only [limit_changes] changes shown,
<a href="[more_changes_href]">display [more_changes] more changes...</a>
</div>
[end]
[if-any first_changes]
<div><a href="[first_changes_href]">Show only first [first_changes] changes...</div>
[end]
<table cellspacing="1" cellpadding="2">
<thead>
@@ -70,13 +75,7 @@
<td colspan="5">No changed paths.</td>
</tr>
[end]
[if-any more_changes]
<tr>
<td colspan="5">[[]<a href="[more_changes_href]">...</a>]</td>
</tr>
[end]
</tbody>
</table>
[include "include/props.ezt"]
[include "include/footer.ezt"]

View File

@@ -9,12 +9,6 @@
<thead>
<tr>
<th class="vc_header_sort">Name</th>
[is cfg.options.show_roots_lastmod "1"]
<th class="vc_header">Revision</th>
<th class="vc_header">Age</th>
<th class="vc_header">Author</th>
<th class="vc_header">Log</th>
[end]
</tr>
</thead>
@@ -26,12 +20,6 @@
<img src="[docroot]/images/dir.png" alt="" class="vc_icon" />
[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.ago]</td>
<td style="width:20">&nbsp;[roots.author]</td>
<td style="width:20">&nbsp;[roots.short_log]</td>
[end]
</tr>
[end]
</tbody>

View File

@@ -7,11 +7,11 @@
<description>[is roottype "svn"]Subversion[else]CVS[end] commits to the[if-any where] [where] directory of the[end] [rootname] repository</description>
[for commits]<item>
<title>[if-any commits.rev][commits.rev]: [end][[commits.author]] [format "xml"][commits.short_log][end]</title>
<title>[if-any commits.rev][commits.rev]: [end][[commits.author]] [commits.short_log]</title>
[if-any commits.rss_url]<link>[commits.rss_url]</link>[end]
<author>[commits.author]</author>
<pubDate>[if-any commits.rss_date][commits.rss_date][else](unknown date)[end]</pubDate>
<description>&#x3C;pre&#x3E;[format "xml"][format "html"][commits.log][end][end]&#x3C;/pre&#x3E;</description>
<description>&lt;pre&gt;[format "xml"][commits.log][end]&lt;/pre&gt;</description>
</item>[end]
</channel>
</rss>

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
@@ -17,18 +17,17 @@
### Validate input
if test $# != 2 && test $# != 1; then
echo "Usage: $0 TARGET-DIRECTORY [BRANCH]"
echo "Usage: $0 TARGET-DIRECTORY [TAGNAME]"
echo ""
echo "If BRANCH (i.e. \"tags/1.1.0\" or \"branches/1.0.x\") is not provided,"
echo "the release will be rolled from trunk."
echo "If TAGNAME is not provided, the release will be rolled from trunk."
exit 1
fi
TARGET=${1}
if test $# = 1; then
if test $# == 1; then
ROOT=trunk
else
ROOT=${2}
ROOT=tags/${2}
fi
if test -e ${TARGET}; then
@@ -37,8 +36,7 @@ if test -e ${TARGET}; then
fi
### Grab an export from the Subversion repository.
EXPORT_URL="http://viewvc.tigris.org/svn/viewvc/${ROOT}"
echo "Exporting '${EXPORT_URL}' into '${TARGET}'"
echo "Exporting into:" ${TARGET}
for PLATFORM in unix windows; do
if test ${PLATFORM} = windows; then
@@ -50,7 +48,7 @@ for PLATFORM in unix windows; do
echo "Beginning build for ${PLATFORM}:"
echo " Exporting source code..."
svn export --quiet ${EOL} ${EXPORT_URL} ${TARGET}
svn export --quiet ${EOL} http://viewvc.tigris.org/svn/viewvc/${ROOT} ${TARGET}
### Various shifting, cleanup.

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-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
@@ -49,10 +49,6 @@ CLEAN_MODE = None
FILE_INFO_LIST = [
("bin/cgi/viewvc.cgi", "bin/cgi/viewvc.cgi", 0755, 1, 0, 0),
("bin/cgi/query.cgi", "bin/cgi/query.cgi", 0755, 1, 0, 0),
("bin/wsgi/viewvc.wsgi", "bin/wsgi/viewvc.wsgi", 0755, 1, 0, 0),
("bin/wsgi/viewvc.fcgi", "bin/wsgi/viewvc.fcgi", 0755, 1, 0, 0),
("bin/wsgi/query.wsgi", "bin/wsgi/query.wsgi", 0755, 1, 0, 0),
("bin/wsgi/query.fcgi", "bin/wsgi/query.fcgi", 0755, 1, 0, 0),
("bin/mod_python/viewvc.py", "bin/mod_python/viewvc.py", 0755, 1, 0, 0),
("bin/mod_python/query.py", "bin/mod_python/query.py", 0755, 1, 0, 0),
("bin/mod_python/handler.py", "bin/mod_python/handler.py", 0755, 1, 0, 0),
@@ -62,12 +58,10 @@ FILE_INFO_LIST = [
("bin/cvsdbadmin", "bin/cvsdbadmin", 0755, 1, 0, 0),
("bin/svndbadmin", "bin/svndbadmin", 0755, 1, 0, 0),
("bin/make-database", "bin/make-database", 0755, 1, 0, 0),
("conf/viewvc.conf.dist", "viewvc.conf.dist", 0644, 0, 0, 0),
("conf/viewvc.conf.dist", "viewvc.conf", 0644, 0, 1, 0),
("conf/cvsgraph.conf.dist", "cvsgraph.conf.dist", 0644, 0, 0, 0),
("conf/cvsgraph.conf.dist", "cvsgraph.conf", 0644, 0, 1, 0),
("conf/mimetypes.conf.dist", "mimetypes.conf.dist", 0644, 0, 0, 0),
("conf/mimetypes.conf.dist", "mimetypes.conf", 0644, 0, 1, 0),
("viewvc.conf.dist", "viewvc.conf.dist", 0644, 0, 0, 0),
("viewvc.conf.dist", "viewvc.conf", 0644, 0, 1, 0),
("cvsgraph.conf.dist", "cvsgraph.conf.dist", 0644, 0, 0, 0),
("cvsgraph.conf.dist", "cvsgraph.conf", 0644, 0, 1, 0),
]
if sys.platform == "win32":
FILE_INFO_LIST.extend([
@@ -79,12 +73,11 @@ if sys.platform == "win32":
## List of directories for installation.
## type (source path,
## destination path,
## boolean -- optional item?,
## boolean -- prompt before replacing?)
TREE_LIST = [
("lib", "lib", 0, 0),
("templates", "templates", 0, 1),
("templates-contrib", "templates-contrib", 1, 1),
("lib", "lib", 0),
("templates", "templates", 1),
("templates-contrib", "templates-contrib", 1),
]
@@ -209,8 +202,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 """
@@ -282,22 +273,18 @@ LEGEND
py_compile.compile(destdir_path, destdir_path + "c" , dst_path)
def install_tree(src_path, dst_path, is_optional, prompt_replace):
def install_tree(src_path, dst_path, prompt_replace):
"""Install a tree whose source is at SRC_PATH (which is relative
to the ViewVC source directory) into the location DST_PATH (which
is relative both to the global ROOT_DIR and DESTDIR settings). If
PROMPT_REPLACE is set (and is not overridden by global setting
CLEAN_MODE), prompt the user for how to deal with already existing
files that differ from the to-be-installed version. If
IS_OPTIONAL is set, don't fuss about a missing source item."""
files that differ from the to-be-installed version."""
orig_src_path = src_path
orig_dst_path = dst_path
src_path = _actual_src_path(src_path)
dst_path = os.path.join(ROOT_DIR, string.replace(dst_path, '/', os.sep))
if not os.path.isdir(src_path):
print " skipping %s" % (dst_path)
return
destdir_path = os.path.join(DESTDIR + dst_path)
# Get a list of items in the directory.
@@ -317,7 +304,7 @@ def install_tree(src_path, dst_path, is_optional, prompt_replace):
# If the item is a subdirectory, recurse. Otherwise, install the file.
if os.path.isdir(os.path.join(src_path, fname)):
install_tree(orig_src_child, orig_dst_child, 0, prompt_replace)
install_tree(orig_src_child, orig_dst_child, prompt_replace)
else:
set_paths = 0
compile_it = fname[-3:] == '.py'

784
viewvc.conf.dist Normal file
View File

@@ -0,0 +1,784 @@
#---------------------------------------------------------------------------
#
# Configuration file for ViewVC
#
# Information on ViewVC is located at the following web site:
# http://viewvc.org/
#
#---------------------------------------------------------------------------
# THE FORMAT OF THIS CONFIGURATION FILE
#
# This file is delineated by sections, specified in [brackets]. Within
# each section, are a number of configuration settings. These settings
# take the form of: name = value. Values may be continued on the
# following line by indenting the continued line.
#
# WARNING: Indentation *always* means continuation. Name=value lines
# should always start in column zero.
#
# Comments should always start in column zero, and are identified
# with "#".
#
# Certain configuration settings may have multiple values. These should
# be separated by a comma. The settings where this is allowed are noted
# below. Any other setting that requires special syntax is noted at that
# setting.
#
#
# SOME TERMINOLOGY USED HEREIN
#
# "root" - This is a CVS or Subversion repository. For Subversion, the
# meaning is pretty clear, as the virtual, versioned directory tree
# stored inside a Subversion repository looks nothing like the actual
# tree visible with shell utilities that holds the repository. For
# CVS, this is more confusing, because CVS's repository layout mimics
# (actually, defines) the layout of the stuff housed in the repository.
# But a CVS repository can be identified by the presence of a CVSROOT
# subdirectory in its root directory.
#
# "module" - A module is a top-level subdirectory of a root, usually
# associated with the concept of a single "project" among many housed
# within a single repository.
#
#
# BASIC VIEWVC CONFIGURATION HINTS
#
# While ViewVC has quite a few configuration options, you generally
# only need to change a small subset of them to get your ViewVC
# installation working properly. Here are some options that we
# recommend you pay attention to. Of course, don't try to change the
# options here -- do so in the relevant section of the configuration
# file below.
#
# For correct operation, you will probably need to change the following
# configuration variables:
#
# cvs_roots (for CVS)
# svn_roots (for Subversion)
# root_parents (for CVS or Subversion)
# default_root
# root_as_url_component
# rcs_dir
# mime_types_file
# the many options in the [utilities] section
#
# It is usually desirable to change the following variables:
#
# address
# forbidden
#
# To optimize delivery of ViewVC static files:
#
# docroot
#
# To customize the display of ViewVC for your site:
#
# template_dir
# the [templates] override section
#
#---------------------------------------------------------------------------
[general]
#
# This setting specifies each of the CVS roots on your system and assigns
# names to them. Each root should be given by a "name: path" value. Multiple
# roots should be separated by commas and can be placed on separate lines.
#
cvs_roots = cvs: /home/cvsroot
#
# This setting 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. Multiple roots should be separated by
# commas and can be placed on separate lines.
#
#svn_roots = svn: /home/svnrepos
# The 'root_parents' setting specifies a list of directories in which
# any number of repositories may reside. Rather than force you to add
# a new entry to 'cvs_roots' or 'svn_roots' each time you create a new
# repository, ViewVC rewards you for organising all your repositories
# under a few parent directories by allowing you to simply specifiy
# just those parent directories. ViewVC will then notice each
# repository in that directory as a new root whose name is the
# subdirectory of the parent path in which that repository lives.
#
# You can specify multiple parent paths separated by commas or new lines.
#
# Caution: these names can, of course, clash with names you have
# defined in your cvs_roots or svn_roots configuration items. If this
# occurs, you can either rename the offending repository on disk, or
# grant new names to the clashing item in cvs_roots or svn_roots.
# Each parent path is processed sequentially, so repositories under
# later parent paths may override earlier ones.
#
#root_parents = /home/svn-repositories : svn,
# /home/cvs-repositories : cvs
# This is the name of the default root. Valid names include those
# explicitly listed in the cvs_roots and svn_roots configuration
# options, as well as those implicitly indicated by virtue of being
# the basenames of repositories found in the root_parents option
# locations.
#
# Note: This setting is ignored when root_as_url_component is enabled.
default_root = cvs
#
# This is a pathname to a MIME types file to help viewvc to guess the
# correct MIME type on checkout. If you are having problems with the
# default guess on the MIME type, then uncomment this option and point
# it at a MIME type file.
#
# For example, you can use the mime.types provided by Apache here:
#mime_types_file = /usr/local/apache2/conf/mime.types
# The address of the local repository maintainer. (This option is
# provided only as a convenience for ViewVC installations which are
# using the default template set, where the value of this option will
# be displayed in the footer of every ViewVC page.)
address =
#
# This option provides a mechanism for custom key/value pairs to be
# available to templates. These are stored in key/value (KV) files.
#
# The paths of the KV files are listed here, specified either as
# absolute paths or relative to this configuration file. The files
# use the same format as this configuration file, containing one or
# more user-defined sections, and user-defined options in those
# sections. ViewVC makes these options available to template authors
# as:
#
# kv.SECTION.OPTION
#
# Note that an option name can be dotted. For example:
#
# [my_images]
# logos.small = /images/small-logo.png
# logos.big = /images/big-logo.png
#
# Templates can use these with a directive like: [kv.my_images.logos.small]
#
# Note that section names which are common to multiple KV files will
# be merged. If two files have a [my_images] section, then the
# options in those two like-named sections will be merged together.
# If two files have the same option name in a section, then one will
# overwrite the other (and which one "wins" is unspecified).
#
# To further categorize the KV files, and how the values are provided to
# the templates, a KV file name may be annotated with an additional level
# of dotted naming. For example:
#
# kv_files = [asf]kv/images.conf
#
# Assuming the same section as above, the template would refer to an image
# using [kv.asf.my_images.logos.small]
#
# Lastly, it is possible to use %lang% in the filenames to specify a
# substitution of the selected language-tag.
#
# Example:
# kv_files = kv/file1.conf, kv/file2.conf, [i18n]kv/%lang%_data.conf
#
kv_files =
#
# This option is a comma-separated list of language-tag values
# available to ViewVC. The first language-tag listed is the default
# language, and will be used if an Accept-Language header is not
# present in the request, or none of the user's requested languages
# are available. If there are ties on the selection of a language,
# then the first to appear in the list is chosen.
#
# Some examples:
#
# languages = en-us, de
# languages = en-us, en-gb, de
# languages = de, fr, en-us
#
languages = en-us
#---------------------------------------------------------------------------
[utilities]
# ViewVC uses (sometimes optionally) various third-party programs to do some
# of the heavy lifting. Generally, it will attempt to execute those utility
# programs in such a way that if they are found in ViewVC's executable
# search path ($PATH, %PATH%, etc.) all is well. But sometimes these tools
# aren't installed in the executable search path, so here's where you can
# tell ViewVC where to find them.
#
# NOTE: Options with a "_dir" suffix are for configuring the directories
# in which certain programs live; otherwise, the option value should
# point to the actual program.
# RCS utilities, used for viewing CVS repositories
rcs_dir =
# rcs_dir = /usr/bin/
# ViewVC can use CVSNT (www.cvsnt.org) instead of the RCS utilities to
# retrieve information from CVS repositories. To enable use of CVSNT,
# set the "cvsnt" value to the path of the CVSNT executable. (If CVSNT
# is on the standard path, you can also set it to the name of the
# CVSNT executable). By default "cvsnt" is set to "cvs" on Windows and
# is not set on other platforms.
cvsnt =
# cvsnt =
# cvsnt = cvs
# cvsnt = K:\Program Files\cvsnt\cvs.exe
# cvsnt = = /usr/bin/cvs
# Subversion command-line client, used for viewing Subversion repositories
svn =
# svn = /usr/bin/svn
# GNU diff, used for showing file version differences
diff =
# diff = /usr/bin/diff
# CvsGraph, a graphical CVS version graph generator (see options.use_cvsgraph)
cvsgraph =
# cvsgraph = /usr/local/bin/cvsgraph
#---------------------------------------------------------------------------
[options]
# root_as_url_component: Interpret the first path component in the URL
# after the script location as the root to use. This is an
# alternative to using the "root=" query key. If ViewVC is configured
# with multiple repositories, this results in more natural looking
# ViewVC URLs.
# Note: Enabling this option will break backwards compatibility with
# any old ViewCVS URL which doesn't have an explicit "root" parameter.
root_as_url_component = 1
# checkout_magic: Use checkout links with magic /*checkout*/ prefixes so
# checked out HTML pages can have working links to other repository files
# Note: This option is DEPRECATED and should not be used in new ViewVC
# installations. Setting "default_file_view = co" achieves the same effect
checkout_magic = 0
# 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).
# Possible values: "tar", "annotate", "co", "markup", "roots"
allowed_views = markup, annotate, roots
# 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
# easily accomplished by placing it in ViewVC's lib/vcauth/ directory)
# and which implements a ViewVCAuthorizer class (as a subclass of
# vcauth.GenericViewVCAuthorizer). You can provide custom parameters
# to the authorizer module by defining configuration sections named
# authz-MODULENAME and adding the parameter keys and values there.
#
# ViewVC provides the following modules:
# svnauthz - based on Subversion authz files
# forbidden - simple path glob matches against top-level root directories
# forbiddenre - root and path matches against regular expressions
#
# NOTE: Only one authorizer may be in use for a given ViewVC request.
# It doesn't matter if you configure the parameters of multiple
# authorizer plugins -- only the authorizer whose name is configured
# here (or effectively configured here via vhost configuration) will
# be activated.
authorizer = forbidden
# hide_cvsroot: Don't show the CVSROOT directory
# 1 Hide CVSROOT directory
# 0 Show CVSROOT directory
# NOTE: Someday this option may be removed in favor of letting
# individual authorizer plugin hide the CVSROOT.
hide_cvsroot = 1
# mangle_email_addresses: Mangle email addresses in marked-up output.
# There are various levels of mangling available:
# 0 - No mangling; markup un-mangled email addresses as hyperlinks
# 1 - Obfuscation (using entity encoding); no hyperlinking
# 2 - Data-dropping address truncation; no hyperlinking
# Note: this will not effect the display of versioned file contents, only
# addresses that appear in version control metadata (e.g. log messages).
mangle_email_addresses = 0
# 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
# ViewCVS URLs, but "co" has the advantage that it allows ViewVC to serve
# static HTML pages directly from a repository with working links
# to other repository files
# Note: Changing this option may break compatibility with existing
# bookmarked URLs.
# Also note: If you choose one of the "co" or "markup" views, be sure
# to enable it (via the allowed_views option)
default_file_view = log
# http_expiration_time: Expiration time (in seconds) for cacheable
# pages served by ViewVC. Note that in most cases, a cache aware
# client will only revalidate the page after it expires (using the
# If-Modified-Since and/or If-None-Match headers) and that browsers
# will also revalidate the page when the reload button is pressed.
# Set to 0 to disable the transmission of these caching headers.
http_expiration_time = 600
# generate_etags: Generate Etag headers for relevant pages to assist
# in browser caching.
# 1 Generate Etags
# 0 Don't generate Etags
generate_etags = 1
# svn_config_dir: Path of the Subversion runtime configuration
# directory ViewVC should consult for various things, including cached
# remote authentication credentials. If unset, Subversion will use
# the default location(s) ($HOME/.subversion, etc.)
svn_config_dir =
# use the rcsparse Python module to retrieve CVS repository
# information instead of invoking rcs utilities [EXPERIMENTAL]
use_rcsparse = 0
# sort_by: File sort order
# file Sort by filename
# rev Sort by revision number
# date Sort by commit date
# author Sort by author
# log Sort by log message
sort_by = file
# sort_group_dirs: Group directories when sorting
# 1 Group directories together
# 0 No grouping -- sort directories as any other item would be sorted
sort_group_dirs = 1
# hide_attic: Hide or show the contents of the Attic subdirectory
# 1 Hide dead files inside Attic subdir
# 0 Show the files which are inside the Attic subdir
hide_attic = 1
# hide_errorful_entries: Hide or show errorful directory entries
# (perhaps due to not being readable, or some other rlog parsing
# error, etc.)
# 1 Hide errorful entries from the directory display
# 0 Show errorful entries (with their errors) in the directory display
hide_errorful_entries = 0
# log_sort: Sort order for log messages
# date Sort revisions by date
# rev Sort revision by revision number
# none Use the version control system's ordering
log_sort = date
# diff_format: Default diff format
# h Human readable
# u Unified diff
# c Context diff
# s Side by side
# l Long human readable (more context)
# f Full human readable (entire file)
diff_format = h
# hr_breakable: Diff view line breaks
# 1 lines break at spaces
# 0 no line breaking
# Or, use a positive integer > 1 to cut lines after that many characters
hr_breakable = 1
# give out function names in human readable diffs
# this just makes sense if we have C-files, otherwise
# diff's heuristic doesn't work well ..
# ( '-p' option to diff)
hr_funout = 0
# ignore whitespaces for human readable diffs
# (indendation and stuff ..)
# ( '-w' option to diff)
hr_ignore_white = 0
# ignore diffs which are caused by
# keyword-substitution like $Id - Stuff
# ( '-kk' option to rcsdiff)
hr_ignore_keyword_subst = 1
# Enable highlighting of intraline changes in human readable diffs
# this feature is experimental and currently requires python 2.4
hr_intraline = 0
# allow compression with gzip of output if the Browser accepts it
# (HTTP_ACCEPT_ENCODING contains "gzip")
#allow_compress = 1
# The directory which contains the EZT templates used by ViewVC to
# customize the display of the various output views. ViewVC looks in
# this directory for files with names that match the name of the view
# ("log", "directory", etc.) plus the ".ezt" extension. If specified
# as a relative path, it is relative to the directory where this config
# file resides; absolute paths may be used as well.
#
# If %lang% occurs in the pathname, then the selected language will be
# substituted.
#
# See also the [templates] configuration section, where you can
# override templates on a per-view basis.
#
template_dir = templates
# Web path to a directory that contains ViewVC static files
# (stylesheets, images, etc.) If set, static files will get
# downloaded directory from this location. If unset, static files
# will be served by the ViewVC script (at a likely performance
# penalty, and from the "docroot" subdirectory of the directory
# specified by the "template_dir" option).
#docroot = /docroot
# Show last changelog message for CVS subdirectories
# NOTE: The current implementation makes many assumptions and may show
# the incorrect file at some times. The main assumption is that the
# last modified file has the newest filedate. But some CVS operations
# touches the file without even when a new version is not checked in,
# and TAG based browsing essentially puts this out of order, unless
# the last checkin was on the same tag as you are viewing. Enable
# this if you like the feature, but don't rely on correct results.
#
# ** WARNING: Enabling this will currently leak unauthorized path names **
show_subdir_lastmod = 0
# Show the most recent log entry in directory listings.
show_logs = 1
# Show log when viewing file contents
show_log_in_markup = 1
# Cross filesystem copies when traversing Subversion file revision histories.
cross_copies = 1
# Display dates as UTC or in local time zone
use_localtime = 0
#use_localtime = 1
### CONFIGURATION DEFAULTS ###
###
### Defaults for configuration variables that shouldn't need
### to be configured..
# the length to which the most recent log entry should be truncated when
# shown in the directory view
short_log_len = 80
# should we colorize known file content syntaxes? (requires Pygments module)
enable_syntax_coloration = 1
# Use CvsGraph. See http://www.akhphd.au.dk/~bertho/cvsgraph/ for
# documentation and download.
use_cvsgraph = 0
#use_cvsgraph = 1
# Location of the customized cvsgraph configuration file.
cvsgraph_conf = cvsgraph.conf
#
# Set to enable regular expression search of all files in a directory
#
# WARNING:
#
# Enabling this option can consume HUGE amounts of server time. A
# "checkout" must be performed on *each* file in a directory, and
# the result needs to be searched for a match against the regular
# expression.
#
#
# SECURITY WARNING: Denial Of Service
#
# Since a user can enter the regular expression, it is possible for
# them to enter an expression with many alternatives and a lot of
# backtracking. Executing that search over thousands of lines over
# dozens of files can easily tie up a server for a long period of
# time.
#
# This option should only be used on sites with trusted users. It is
# highly inadvisable to use this on a public site.
#
use_re_search = 0
# use_re_search = 1
#
# Split directories and logs into pages.
# Allows ViewVC to present discrete pages to the users instead of the
# entire log or directory.
# Set use_pagesize to the number of entries you want displayed on a page.
#
use_pagesize = 0
# use_pagesize = 20
# Limit number of changed paths shown per commit in the Subversion revision
# view and in query results. This is not a hard limit (the UI provides
# options to show all changed paths), but it prevents ViewVC from generating
# enormous and hard to read pages by default when they happen to contain
# import or merge commits affecting hundreds or thousands of files.
# Set to 0 to disable the limit.
limit_changes = 100
#---------------------------------------------------------------------------
[templates]
# You can override the templates used by various ViewVC views in this
# section. By default, ViewVC will look for templates in the
# directory specified by the "template_dir" configuration option (see
# the documentation for that option for details). But if you want to
# use a different template for a particular view, simply uncomment the
# appropriate option below and specify the currect location of the EZT
# template file you wish to use for that view.
#
# Templates are specified relative to the configured template
# directory (see the "template_dir" option), but absolute paths may
# also be used as well.
#
# If %lang% occurs in the pathname, then the selected language will be
# substituted.
#
# Note: the selected language is defined by the "languages" item in the
# [general] section, and based on the request's Accept-Language
# header.
#
#diff = diff.ezt
#directory = directory.ezt
### an alternative directory view
#directory = dir_new.ezt
#error = error.ezt
#file = file.ezt
#graph = graph.ezt
#log = log.ezt
### a table-based alternative log view
#log = log_table.ezt
#query = query.ezt
#query_form = query_form.ezt
#query_results = query_results.ezt
#revision = revision.ezt
#roots = roots.ezt
#---------------------------------------------------------------------------
[cvsdb]
# Set to 1 to enable the database integration feature, 0 otherwise.
enabled = 0
# Database hostname and port.
#host = localhost
#port = 3306
# ViewVC database name.
#database_name = ViewVC
# Username and password of user with read/write privileges to the ViewVC
# database.
#user =
#passwd =
# Username and password of user with read privileges to the ViewVC
# database.
#readonly_user =
#readonly_passwd =
# Limit the number of rows returned by a given query to this number.
#row_limit = 1000
# Limit the number of rows returned by a given query made as part an
# RSS feed request to this number. (Keeping in mind that RSS readers
# tend to poll regularly for new data, you might want to keep this set
# to a conservative number.)
#rss_row_limit = 100
# Check if the repository is found in the database before showing
# the query link and RSS feeds. Set to 1 to enable check.
#
# WARNING: Enabling this check adds the cost of a database connection
# and query to most ViewVC requests. If all your roots are represented
# in the commits database, or if you don't care about the creation of
# RSS and query links that might lead ultimately to error pages for
# certain of your roots, or if you simply don't want to add this extra
# cost to your ViewVC requests, leave this disabled.
#check_database_for_root = 0
#---------------------------------------------------------------------------
[vhosts]
# Virtual hosts are individual logical servers accessible via
# different hostnames, but which are all really the same physical
# computer. For example, you might have your web server configured to
# accept incoming traffic for both http://www.yourdomain.com/ and
# http://viewvc.yourdomain.com/. Users pointing their web browsers at
# each of those two URLs might see entirely different content via one
# URL versus the other, but all that content actually lives on the
# same computer, is served up via the same web server, and so
# on. It just *looks* like its coming from multiple servers.
#
# ViewVC allows you to customize its configuration options for
# individual virtual hosts. You might, for example, wish to expose
# all of your Subversion repositories at http://svn.yourdomain.com/viewvc/
# and all your CVS ones at http://cvs.yourdomain.com/viewvc/, with no
# cross-exposure. Using ViewVC's virtual host (vhost) configuration
# support, you can do this. Simply create two vhost configurations
# (one for each of your hostnames), then configure the cvs_roots
# option only for the vhost associated with cvs.yourdomain.com, and
# configure the svn_roots option only for the vhost associated with
# svn.yourdomain.com.
#
# This section is a freeform configuration section, where you create
# both the option names and their values. The names of the options
# are then treated as canonical names of virtual hosts, and their
# values are defined to be comma-delimited lists of hostname globs
# against which incoming ViewVC requests will be matched to figure out
# which vhost they apply to.
#
# After you've named and defined your vhosts, you may then create new
# configuration sections whose names are of the form
# vhost-VHOSTNAME/CONFIGSECTION. VHOSTNAME here is the canonical name
# of one of the virtual hosts you defined under the [vhosts] section.
# Inside those configuration sections, you override the standard
# ViewVC options typically found in the base configuration section
# named CONFIGSECTION ("general", "option", etc.)
#
# Here is an example:
#
# [vhosts]
# libs = libs.yourdomain.*, *.yourlibs.*
# gui = guiproject.yourdomain.*
#
# [vhost-libs/general]
# cvs_roots =
# svn_roots = svnroot: /var/svn/libs-repos
# default_root = svnroot
#
# [vhost-libs/options]
# show_logs = 1
#
# [vhost-gui/general]
# cvs_roots = cvsroot: /var/cvs/guiproject
# svn_roots =
# default_root = cvsroot
#
#---------------------------------------------------------------------------
# ViewVC recognizes per-root configuration overrides, too. To
# override the value of a configuration parameter only for a single
# root, create a configuration section whose names is of the form
# root-ROOTNAME/CONFIGSECTION. ROOTNAME here is the name of the root
# as defined explicitly in cvs_roots or svn_roots or implicitly as the
# basename of a root path in root_parents. Options found in this new
# configuration section override for this one root the corresponding
# options found in the base configuration section CONFIGSECTION
# ("options", "authz-*", etc.)
#
# Here is an example showing how to enable Subversion authz-based
# authorization for only the single root named "svnroot":
#
# [root-svnroot/options]
# authorizer = svnauthz
#
# [root-svnroot/authz-svnauthz]
# authzfile = /path/to/authzfile
#
#---------------------------------------------------------------------------
[authz-forbidden]
# The "forbidden" authorizer forbids access to repository modules,
# defined to be top-level subdirectories in a repository. You can use
# a simple list of modules, or something more complex:
#
# *) The "!" can be used before a module to explicitly state that it
# is NOT forbidden. Whenever this form is seen, then all modules will
# be forbidden unless one of the "!" modules match.
#
# *) Shell-style "glob" expressions may be used. "*" will match any
# sequence of zero or more characters, "?" will match any single
# character, "[seq]" will match any character in seq, and "[!seq]"
# will match any character not in seq.
#
# *) Tests are performed in sequence. The first match will terminate the
# testing. This allows for more complex allow/deny patterns.
#
# Tests are case-sensitive.
#
# NOTE: Again, this is for the hiding of modules within repositories, *not*
# for the hiding of repositories (roots) themselves.
#
# Some examples:
#
# Disallow "example" but allow all others:
# forbidden = example
#
# Disallow "example1" and "example2" but allow all others:
# forbidden = example1, example2
#
# Allow *only* "example1" and "example2":
# forbidden = !example1, !example2
#
# Forbid modules starting with "x":
# forbidden = x*
#
# Allow modules starting with "x" but no others:
# forbidden = !x*
#
# Allow "xml", forbid other modules starting with "x", and allow the rest:
# forbidden = !xml, x*, !*
#
forbidden =
#---------------------------------------------------------------------------
[authz-forbiddenre]
# The "forbiddenre" authorizer forbids access to repositories and
# repository paths by comparing a list of regular expressions
# (separated by commas) against paths consisting of the repository (or
# root) name plus the path of the versioned file or directory to be
# tested. For example, to see if the user is authorized to see the
# path "/trunk/www/index.html" in the repository whose root name is
# "svnrepos", this authorizer will check the path
# "svnrepos/trunk/www/index.html" against the list of forbidden
# regular expressions. Directory paths will be terminated by a forward
# slash.
#
# Like the "forbidden" authorizer...
#
# *) The "!" can be used before a module to explicitly state that it
# is NOT forbidden. Whenever this form is seen, then all modules will
# be forbidden unless one of the "!" modules match.
#
# *) Tests are performed in sequence. The first match will terminate the
# testing. This allows for more complex allow/deny patterns.
#
# Unlike the "forbidden" authorizer, you can can use this to hide roots, too.
#
# Some examples:
#
# Disallow files named "PRIVATE", but allow all others:
# forbiddenre = /PRIVATE$
#
# Disallow the "hidden" repository, allowing all others:
# forbiddenre = ^hidden(/|$)
#
# Allow only the "example1" and "example2" roots and the paths inside them,
# disallowing all others (which can be done in multiple ways):
# forbiddenre = !^example1(/|$), !^example2(/|$)/
# forbiddenre = !^example[12](/|$)
#
# Only allow visibility of HTML files and the directories that hold them:
# forbiddenre = !^([^/]+|.*(/|\.html))$
#
forbiddenre =
#---------------------------------------------------------------------------
[authz-svnauthz]
# The "svnauthz" authorizer uses a Subversion authz configuration file
# to determine access to repository paths. This option specifies the
# location of that file using an absolute path.
#
authzfile =
#---------------------------------------------------------------------------

9
viewvc.org/.htaccess Normal file
View File

@@ -0,0 +1,9 @@
# Old documents.
RedirectPermanent /upgrading.html http://viewvc.tigris.org/nonav/source/browse/*checkout*/viewvc/trunk/docs/upgrading-howto.html
RedirectPermanent /template-authoring-guide.html http://viewvc.tigris.org/nonav/source/browse/*checkout*/viewvc/trunk/docs/template-authoring-guide.html
RedirectPermanent /url-reference.html http://viewvc.tigris.org/nonav/source/browse/*checkout*/viewvc/trunk/docs/url-reference.html
# The license-1.html file.
RedirectPermanent /license-1.html http://viewvc.tigris.org/nonav/source/browse/*checkout*/viewvc/trunk/LICENSE.html

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

@@ -0,0 +1,93 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>ViewVC: Contact</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<link rel="stylesheet" type="text/css" href="./styles.css"/>
<link rel="shortcut icon" href="./favicon.ico" type="image/x-icon" />
</head>
<body>
<div id="title">
<a href="http://www.viewvc.org/"><img
src="./images/title.jpg" alt="ViewVC: Repository Browsing"/></a>
</div>
<div id="menu">
<p><a href="./index.html">Home</a> |
<a href="http://viewvc.tigris.org/">Project Page</a> |
<a href="./download.html">Download</a> |
<a href="./contributing.html">Contributing</a> |
<a href="./faq.html">FAQ</a> |
<a href="http://viewvc.tigris.org/nonav/source/browse/*checkout*/viewvc/trunk/LICENSE.html">License</a> |
<a href="./contact.html">Contact</a> |
<a href="./who.html">About</a>
</p>
</div>
<table id="pagetable">
<tr>
<td id="pagecolumn1">
<h4>On this page:</h4>
<ul id="bookmarks">
<li><a href="#sec-contacting-us">Contacting Us</a></li>
</ul>
<p><a href="http://validator.w3.org/check?uri=referer"><img
src="http://www.w3.org/Icons/valid-xhtml10"
alt="Valid XHTML 1.0 Strict" height="31" width="88" /></a>
</p>
</td>
<td id="pagecolumn2">
<div class="section">
<h2 id="sec-contacting-us">Contacting Us</h2>
<div class="section-body">
<p>ViewVC is an <a href="http://www.opensource.org/">Open Source</a>
project, and all <a href="./contributing.html">contributions</a>
are welcome. Please send any comments, questions, or suggestions
to the <a
href="mailto:&#117;&#115;&#101;&#114;&#115;&#64;&#118;&#105;&#101;&#119;&#118;&#99;&#46;&#116;&#105;&#103;&#114;&#105;&#115;&#46;&#111;&#114;&#103;">ViewVC
users mailing list</a>. There is also a <a
href="mailto:&#100;&#101;&#118;&#64;&#118;&#105;&#101;&#119;&#118;&#99;&#46;&#116;&#105;&#103;&#114;&#105;&#115;&#46;&#111;&#114;&#103;">mailing
list specifically for ViewVC developers</a>. You can subscribe to
these lists (and other project lists), as well view the list
archives, <a
href="http://viewvc.tigris.org/servlets/ProjectMailingListList"
>here</a>. If you are having problems with ViewVC, consider
checking our <a href="./faq.html">Frequently Asked Questions</a>
page and list of <a
href="http://viewvc.tigris.org/issues/buglist.cgi?component=viewvc&amp;issue_status=UNCONFIRMED&amp;issue_status=NEW&amp;issue_status=STARTED&amp;issue_status=REOPENED"
>open issues</a> before mail our users@ list &mdash; perhaps the
information you need is already available in one of those
places.</p>
<p>If you prefer interaction that's a little more real-time, use your
favorite IRC client to pop into <tt><a
href="irc://irc.freenode.net/viewvc">#viewvc</a></tt> on
irc.freenode.net.</p>
<p>So, to summarize:</p>
<ul>
<li>To discuss ViewVC installation, configuration, and behavior: &#117;&#115;&#101;&#114;&#115;&#64;&#118;&#105;&#101;&#119;&#118;&#99;&#46;&#116;&#105;&#103;&#114;&#105;&#115;&#46;&#111;&#114;&#103;</li>
<li>To discuss the development of ViewVC itself or submit patches: &#100;&#101;&#118;&#64;&#118;&#105;&#101;&#119;&#118;&#99;&#46;&#116;&#105;&#103;&#114;&#105;&#115;&#46;&#111;&#114;&#103;</li>
<li>To discussion anything ViewVC-related in real time: irc.freenode.net, #viewvc</li>
</ul>
</div> <!-- section-body -->
</div> <!-- section -->
</td>
</tr>
</table>
</body>
</html>

View File

@@ -0,0 +1,241 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>ViewVC: Contributing</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<link rel="stylesheet" type="text/css" href="./styles.css"/>
<link rel="shortcut icon" href="./favicon.ico" type="image/x-icon" />
</head>
<body>
<div id="title">
<a href="http://www.viewvc.org/"><img
src="./images/title.jpg" alt="ViewVC: Repository Browsing"/></a>
</div>
<div id="menu">
<p><a href="./index.html">Home</a> |
<a href="http://viewvc.tigris.org/">Project Page</a> |
<a href="./download.html">Download</a> |
<a href="./contributing.html">Contributing</a> |
<a href="./faq.html">FAQ</a> |
<a href="http://viewvc.tigris.org/nonav/source/browse/*checkout*/viewvc/trunk/LICENSE.html">License</a> |
<a href="./contact.html">Contact</a> |
<a href="./who.html">About</a>
</p>
</div>
<table id="pagetable">
<tr>
<td id="pagecolumn1">
<h4>On this page:</h4>
<ul id="bookmarks">
<li><a href="#sec-getting-started">Getting Started</a></li>
<li><a href="#sec-testing">Testing and Reporting</a></li>
<li><a href="#sec-coding-style">Coding Style</a></li>
<li><a href="#sec-patches">Submitting Patches</a></li>
<li><a href="#sec-security">Security</a></li>
<li><a href="#sec-adding-features">Adding Features</a></li>
<li><a href="#sec-templates">Hacking on Templates</a></li>
</ul>
<p><a href="http://validator.w3.org/check?uri=referer"><img
src="http://www.w3.org/Icons/valid-xhtml10"
alt="Valid XHTML 1.0 Strict" height="31" width="88" /></a>
</p>
</td>
<td id="pagecolumn2">
<div class="section">
<h2 id="sec-getting-started">Getting Started</h2>
<div class="section-body">
<p>Some basic knowledge about <a
href="http://www.python.org">Python</a> and development tools like
<code>diff</code> is required. Your best bet is to start with a
fresh source code snapshot, which you may obtain from our
Subversion repository (see instructions <a
href="./download.html#sec-subversion">here</a>).</p>
<p>Version control history can be obtained using Subversion clients,
but is also browsable using <a
href="http://viewvc.tigris.org/source/browse/viewvc/trunk/"
>tigris.org's integrated ViewVC tool</a>.</p>
</div> <!-- section-body -->
</div> <!-- section -->
<div class="section">
<h2 id="sec-testing">Testing and Reporting</h2>
<div class="section-body">
<p>Testing usability and the installation process on different
platforms is also a valuable contribution. Please report your
results back to us developers. Bandwidth is getting cheaper daily,
so don't be afraid &mdash; in fact, feel encouraged &mdash; to dump
as much detail about the problems you are seeing as possible into
your bug reports. Here are some things you definitely should
try to include:</p>
<ul>
<li>What version of ViewVC you are using (if you are using a source
snapshot, tell us the date of that snapshot).</li>
<li>What operating system your ViewVC is running on.</li>
<li>What version of Python you are using.</li>
<li>Whether you are running ViewVC standalone, or as a CGI program
under a web server (and if so, what web server).</li>
<li>The URL of your ViewVC instantiation, if it is public.
Sometimes, letting developers see the problem for themselves can
save everyone alot of time.</li>
</ul>
</div> <!-- section-body -->
</div> <!-- section -->
<div class="section">
<h2 id="sec-coding-style">Coding Style</h2>
<div class="section-body">
<p>Unlike its predecessor, CvsWeb, ViewVC is written in Python, so it
doesn't suffer from the "unmaintainable code effect" that hits most
Perl projects sooner or later:</p>
<blockquote>
<p>&quot;[Perl] combines all the worst aspects of C and Lisp: a
billion different sublanguages in one monolithic executable. It
combines the power of C with the readability of
PostScript.&quot; &mdash;&nbsp;Jamie&nbsp;Zawinski
</p>
</blockquote>
<p>Of course, a symphony of insanity can be composed in any language,
so we do try to stick to some basic guiding principles. Maintain
whatever style is present in the code being modified. New code can
use anything sane (which generally means <a
href="http://www.python.org/dev/peps/pep-0008/">PEP&nbsp;8</a>).
Our only real peeve is if someone writes a function call as:
<code>some_func&nbsp;(args)</code> &mdash; that space between the
function name and opening parenthesis is Huge Badness. Oh, and we
do <strong>not</strong> use Subversion keywords (such as
<code>$</code><code>Id$</code>) within the source.</p>
<p>Otherwise&hellip; <em>shrug</em>.</p>
</div> <!-- section-body -->
</div> <!-- section -->
<div class="section">
<h2 id="sec-patches">Submitting Patches</h2>
<div class="section-body">
<p>Nothing speaks more loudly when bugs or features are the topic than
a patch. And quite frankly, sometimes if you want something done,
you gotta do it yourself. So, patches are always welcome. If you
aren't sure what exactly a &quot;patch&quot; is, or don't know how
to generate one, but you've got code contributions to make, please
don't hesitate to ask questions on the mailing lists. Patch
generation and application are pretty easy thing to get the hang of,
and drastically simplify code submission and review.</p>
<p>Please use the <a
href="http://viewvc.tigris.org/servlets/ProjectIssues">Issue
Tracker</a> to submit your patches. Unified contextual diffs
against the latest development snapshot are preferred.</p>
<p>If you have commit access, then you should know what you're doing.
Just make changes directly. Subscribing to the <a
href="http://viewvc.tigris.org/servlets/ProjectMailingListList"
><tt>dev@</tt> developer mailing list</a> is recommended in any
case.</p>
</div> <!-- section-body -->
</div> <!-- section -->
<div class="section">
<h2 id="sec-security">Security</h2>
<div class="section-body">
<p>Since ViewVC is used on the Internet, security is a major concern.
If you need to pass data from the request into an external program,
please don't use <code>os.system()</code> or
<code>os.popen()</code>. Please use the module
<code>lib/popen.py</code> that is included in the ViewVC
distribution instead.</p>
</div> <!-- section-body -->
</div> <!-- section -->
<div class="section">
<h2 id="sec-adding-features">Adding Features</h2>
<div class="section-body">
<p>If you need a new configuration option think carefully, into which
section it belongs. Try to keep the content of
<code>cgi/viewvc.conf.dist</code> file and the library module
<code>lib/config.py</code> in sync.</p>
<p>Because ViewVC is a Web-based application, people will have ViewVC
URLs hyperlinked from other sites, embedded in emails, bookmarked
in their browsers, etc. It is very important to ensure that those
URLs continue to retrieve the information they were intended to
retrieve even if ViewVC is upgraded on the hosting server. In
other words, as new features require modifications to the <a
href="./url-reference.html">ViewVC URL schema</a>, make sure those
modifications preserve the existing functionality of all ViewVC
URLs.</p>
<p>The library subdirectory contains a module <code>debug.py</code>,
which you may find useful for performance testing.</p>
<p>If a new file or module is added, a new line in the installer
program <code>viewvc-install</code> is required.</p>
</div> <!-- section-body -->
</div> <!-- section -->
<div class="section">
<h2 id="sec-templates">Hacking on Templates</h2>
<div class="section-body">
<p>The library module <code>ezt.py</code> contains a module docstring
which describes the directives used in the HTML templates used by
ViewVC. The templates themselves can be found in the
<code>templates</code> subdirectory. We're currently developing a
how-to guide for <a href="./template-authoring-guide.html">ViewVC
template customization</a>.</p>
</div> <!-- section-body -->
</div> <!-- section -->
</td>
</tr>
</table>
</body>
</html>

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

@@ -0,0 +1,90 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>ViewVC: Download</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<link rel="stylesheet" type="text/css" href="./styles.css"/>
<link rel="shortcut icon" href="./favicon.ico" type="image/x-icon" />
</head>
<body>
<div id="title">
<a href="http://www.viewvc.org/"><img
src="./images/title.jpg" alt="ViewVC: Repository Browsing"/></a>
</div>
<div id="menu">
<p><a href="./index.html">Home</a> |
<a href="http://viewvc.tigris.org/">Project Page</a> |
<a href="./download.html">Download</a> |
<a href="./contributing.html">Contributing</a> |
<a href="./faq.html">FAQ</a> |
<a href="http://viewvc.tigris.org/nonav/source/browse/*checkout*/viewvc/trunk/LICENSE.html">License</a> |
<a href="./contact.html">Contact</a> |
<a href="./who.html">About</a>
</p>
</div>
<table id="pagetable">
<tr>
<td id="pagecolumn1">
<h4>On this page:</h4>
<ul id="bookmarks">
<li><a href="#sec-download">Downloading</a></li>
<li><a href="#sec-subversion">Subversion</a></li>
</ul>
<p><a href="http://validator.w3.org/check?uri=referer"><img
src="http://www.w3.org/Icons/valid-xhtml10"
alt="Valid XHTML 1.0 Strict" height="31" width="88" /></a>
</p>
</td>
<td id="pagecolumn2">
<div class="section">
<h2 id="sec-download">Downloading</h2>
<div class="section-body">
<p>You can download the latest release of ViewVC (and the older ViewVC
and ViewCVS releases, too) from our <a
href="http://viewvc.tigris.org/servlets/ProjectDocumentList?folderID=6004"
>Documents &amp; Files</a> area. For information about what has
changed in each release, see the <a
href="http://viewvc.tigris.org/source/browse/viewvc/trunk/CHANGES?rev=HEAD"
>CHANGES</a> file.</p>
<p>We are also making <a href="./nightly/">nightly snapshots</a>
available in tar.gz and zip formats.</p>
</div> <!-- section-body -->
</div> <!-- section -->
<div class="section">
<h2 id="sec-subversion">Subversion</h2>
<div class="section-body">
<p>The source code for ViewVC is maintained in a Subversion repository
at Tigris.org. You can checkout the trunk of our development tree
from <a href="http://viewvc.tigris.org/svn/viewvc/trunk/"
>http://viewvc.tigris.org/svn/viewvc/trunk/</a>. You'll
need to provide your Tigris.org username and password when so
prompted, or, if you don't have a Tigris.org account, use "guest"
as the username (with no, or an empty, password).</p>
</div> <!-- section-body -->
</div> <!-- section -->
</td>
</tr>
</table>
</body>
</html>

359
viewvc.org/faq.html Normal file
View File

@@ -0,0 +1,359 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>ViewVC: Frequently Asked Questions (FAQ)</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<link rel="stylesheet" type="text/css" href="./styles.css"/>
<link rel="shortcut icon" href="./favicon.ico" type="image/x-icon" />
</head>
<body>
<div id="title">
<a href="http://www.viewvc.org/"><img
src="./images/title.jpg" alt="ViewVC: Frequently Asked Questions (FAQ)"/></a>
</div>
<div id="menu">
<p><a href="./index.html">Home</a> |
<a href="http://viewvc.tigris.org/">Project Page</a> |
<a href="./download.html">Download</a> |
<a href="./contributing.html">Contributing</a> |
<a href="./faq.html">FAQ</a> |
<a href="http://viewvc.tigris.org/nonav/source/browse/*checkout*/viewvc/trunk/LICENSE.html">License</a> |
<a href="./contact.html">Contact</a> |
<a href="./who.html">About</a>
</p>
</div>
<table id="pagetable">
<tr>
<td id="pagecolumn1">
<h4>On this page:</h4>
<ul id="bookmarks">
<li><a href="#faq-q">Questions</a></li>
<li><a href="#faq-a">Answers</a></li>
</ul>
<p><a href="http://validator.w3.org/check?uri=referer"><img
src="http://www.w3.org/Icons/valid-xhtml10"
alt="Valid XHTML 1.0 Strict" height="31" width="88" /></a>
</p>
</td>
<td id="pagecolumn2">
<div class="section">
<!-- ##################################################################### -->
<h2 id="faq-q">Questions</h2>
<!-- ##################################################################### -->
<div class="section-body">
<!-- #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# -->
<h3 class="faq-section" id="faq-q-general">General Usage</h3>
<!-- #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# -->
<ul>
<li><a href="#installation">Where does the installation documentation,
if any, live?</a></li>
<li><a href="#authz-support">Does ViewVC support path-based
authorization, such as Subversion's authz-file mechanism?</a></li>
<li><a href="#missing-tmpdir">What causes "Error: OSError: [Errno 2] No such file
or directory: '/tmp/tmpGc-Ztj'"?</a></li>
<li><a href="#standalone-only">Why does <em>SOME-FEATURE</em>
work under standalone.py, but not under Apache (or IIS or
&hellip;)?</a></li>
<li><a href="#rss-support">How do I enable ViewVC's RSS feed
capabilities?</a></li>
</ul>
<!-- #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# -->
<h3 class="faq-section" id="faq-q-cvs">CVS Browsing</h3>
<!-- #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# -->
<ul>
<li><a href="#rlog-output-ended-early">What causes "Error: Rlog output ended
early. Expected RCS file "/opt/cvs/project/file,v""?</a></li>
<li><a href="#comalformedoutput">What causes "Error: COMalformedOutput: Unable to
find filename in co output stream"?</a></li>
<li><a href="#error-during-rlog">What causes "Error: error during
rlog: 0x100"?</a></li>
<li><a href="#missing-files">Why do my directories have no files in them?</a></li>
<li><a href="#none-match">ViewVC doesn't show files I'm looking for,
and instead displays the message "NOTE: There are N files, but
none match the current selection criteria". How can I fix this?</a></li>
</ul>
<!-- #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# -->
<h3 class="faq-section" id="faq-q-svn">Subversion Browsing</h3>
<!-- #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# -->
<ul>
<li><a href="#no-module-named-svn">What causes "Error: ImportError: No module named
svn"?</a></li>
<li><a href="#remote-svn-access">Can I use ViewVC with remote
Subversion repositories?</a></li>
</ul>
<!-- #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# -->
</div>
</div>
<div class="section">
<!-- ##################################################################### -->
<h2 id="faq-a">Answers</h2>
<!-- ##################################################################### -->
<div class="section-body">
<!-- #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# -->
<h3 class="faq-section" id="faq-a-general">General Usage</h3>
<!-- #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# -->
<div id="installation">
<p class="faq-atitle">Where does the installation documentation, if
any, live?</p>
<p>ViewVC's installation how-to documentation lives in the INSTALL
file located in the root of the ViewVC source code distribution.
The most recent version of this document (which may cover
unreleased ViewVC versions) can be found
at <a href="http://viewvc.tigris.org/source/browse/*checkout*/viewvc/trunk/INSTALL"
>http://viewvc.tigris.org/source/browse/*checkout*/viewvc/trunk/INSTALL</a>.
If you are upgrading an existing ViewVC instance, you'll also want
to read the upgrade documentation, found
at <a href="http://viewvc.tigris.org/source/browse/*checkout*/viewvc/trunk/docs/upgrading-howto.html"
>http://viewvc.tigris.org/source/browse/*checkout*/viewvc/trunk/docs/upgrading-howto.html</a>.</p>
</div>
<div id="authz-support">
<p class="faq-atitle">Does ViewVC support path-based authorization,
such as Subversion's authz-file mechanism?</p>
<p>There are no released versions of ViewVC which have this support
built-in. The feature request is being tracking in <a
href="http://viewvc.tigris.org/issues/show_bug.cgi?id=268">issue
#268</a>, and we're working to complete it for release in ViewVC
1.1.</p>
<p>In the meantime, there are some available workarounds. For
example, if you are running ViewVC under Apache, you can use
regular Apache configuration directives to authz-protect areas of
your repository &mdash; see the <a
href="http://httpd.apache.org/docs/2.2/mod/core.html#location"
>Location</a> and <a
href="http://httpd.apache.org/docs/2.2/mod/core.html#locationmatch"
>LocationMatch</a> directives, as well as the <a
href="http://httpd.apache.org/docs/2.2/howto/auth.html"
>Authentication, Authorization and Access Control</a> portions of
the Apache HTTP Server documentation. This works best when ViewVC
is only serving up a single repository, or when the
<code>root_as_url_component</code> option is enabled otherwise.
And you might have to disable the checkout view, or at least turn
off the use of <code>checkout_magic</code> (see
<code>viewvc.conf</code> for details).</p>
</div>
<div id="missing-tmpdir">
<p class="faq-atitle">What causes "Error: OSError: [Errno 2] No such
file or directory: '/tmp/tmpGc-Ztj'"?</p>
<p>This is tracked in <a
href="http://viewvc.tigris.org/issues/show_bug.cgi?id=282">issue
#282</a>. While we haven't figured out how to make the error
message more graceful or helpful, the basic problem seems to
generally boil down to a misconfiguration of ViewVC. Make sure
your helper applications are in the program search path
(<code>$PATH</code>, e.g.) on your ViewVC server, or accurately
specified in <code>viewvc.conf</code>.</p>
</div>
<div id="standalone-only">
<p class="faq-atitle">Why does <em>SOME-FEATURE</em> work under
standalone.py, but not under Apache (or IIS or &hellip;)?</p>
<p>Most of the time folks run standalone.py, they do so as a regular
system user, from a shell running with any and all the
environmental customizations present in their shell startup
scripts. But most web server packages (like Apache) run as a
different, often underprivileged, user, whose environment is
stripped down to just the system-wide default state. This can
cause problems for ViewVC's various helper applications, which
might reside in a location that's included as part of your (and
thus standalone.py's) <code>$PATH</code>, but not that of the user
as whom the web server package runs. The solution might be as
simple explicitly configuring the paths of the helper applications
in <code>viewvc.conf</code>, or as complex as expanding the program
search path used by your web server software.</p>
</div>
<div id="rss-support">
<p class="faq-atitle">How do I enable ViewVC's RSS feed capabilities?</p>
<p>ViewVC generates its RSS feeds from repository data mirrored in a
database as part of its MySQL integration. Follow the steps in
ViewVC's <code>INSTALL</code> file to setup and configure the MySQL
integration and begin mirroring your commit metadata in the
database &mdash; the RSS feed feature will then be enabled with no
additional configuration.</p>
</div>
<!-- #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# -->
<h3 class="faq-section" id="faq-a-cvs">CVS Browsing</h3>
<!-- #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# -->
<div id="rlog-output-ended-early">
<p class="faq-atitle">What causes "Error: Rlog output ended
early. Expected RCS file "/opt/cvs/project/file,v""?</p>
<p>This error generally occurs when ViewVC is unable to locate the
rlog tool, one of several in the RCS toolchain. If the system
doesn't have the RCS toolchain installed, you'll need to install
it. If the RCS toolchain is installed, but ViewVC simply can't
locate the RCS programs, either ensure that the programs are
in the system <code>$PATH</code>, or specify their location in
the <code>viewvc.conf</code> file &mdash; the option is
<code>utilities/rcs_dir</code> as of ViewVC 1.1,
<code>general/rcs_path</code> in earlier versions.</p>
</div>
<div id="comalformedoutput">
<p class="faq-atitle">What causes "Error: COMalformedOutput: Unable to
find filename in co output stream"?</p>
<p>This is another symptom of the basic problem described <a
href="#rlog-output-ended-early">here</a>.</p>
</div>
<div id="error-during-rlog">
<p class="faq-atitle">What causes "Error: error during rlog:
0x100"?</p>
<p>### TODO ###</p>
</div>
<div id="missing-files">
<p class="faq-atitle">Why do my directories have no files in them?</p>
<p>There are a few reasons why this can happen. Here are some of the
most common ones:</p>
<ul>
<li>Some folks mistakenly point ViewVC's configuration bits to their
CVS working copies. But ViewVC isn't a working copy browser
&mdash; it's a <em>repository</em> browser. If you don't know
the difference, here's a tip that might help: CVS repositories
are directories trees filled with files that end with
"<code>,v</code>". If your directory isn't filled with "comma
vee" files, it probably is <em>not</em> a CVS repository.</li>
<li>&hellip; <!-- TODO --></li>
</ul>
</div>
<div id="none-match">
<p class="faq-atitle">ViewVC doesn't show files I'm looking for, and
instead displays the message "NOTE: There are N files, but none
match the current selection criteria". How can I fix this?</p>
<p>From time to time, CVS and CVSNT add support for new keywords to
the RCS backend used to house repository data. If you're running a
version of ViewVC or ViewCVS that hasn't yet learned to handle
those new keywords, you might get the error you're seeing. The
solution is to try to get versions of CVS/CVSNT and ViewVC which
are better aligned, which generally means upgrading ViewVC (which
is probably less disruptive than downgrading your version control
system).</p>
</div>
<!-- #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# -->
<h3 class="faq-section" id="faq-a-svn">Subversion Browsing</h3>
<!-- #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# -->
<div id="no-module-named-svn">
<p class="faq-atitle">What causes "Error: ImportError: No module named
svn"?</p>
<p>ViewVC uses Subversion's Python bindings to interact with and pull
information out of your Subversion repositories. These bindings
are not, however, generally provided as part of the ViewVC
distribution &mdash; you have to install them yourself some other
way. (For more information, contact the <a
href="http://subversion.tigris.org">Subversion</a> community.) The
error you see is Python being asked to import the Subversion Python
bindings and being unable to do so, typically because the bindings
modules aren't found in the Python library search path.</p>
</div>
<div id="remote-svn-access">
<p class="faq-atitle">Can I use ViewVC with remote Subversion
repositories?</p>
<p>ViewVC prefers to have direct access to the repository (and in
fact, <em>must</em> have that for CVS repositories), but there does
exist experimental support for remote Subversion repositories.
Enabling this is a fairly simple &mdash; just use the URL of the
repository where you would use its path in the
<code>svn_roots</code> configuration options (sorry, you can't use
<code>root_parents</code> for remote repositories because
Subversion doesn't expose a repository-listing repository access
API).</p>
<p>What you should expect:</p>
<ul>
<li><p>Near parity with local-access Subversion in terms of
functionality.</p></li>
<li><p>Known shortcomings:</p>
<ul>
<li>file sizes aren't reported everywhere</li>
<li>the "revision" view lacks/botches some information</li>
<li>she ain't the fastest horse on the track&hellip;</li>
</ul>
</li>
<li><p>Lack of configury for dealing with the intricacies of remote
access. If the auth credentials and such you need to access
the remote repository aren't cached in
<code>~VIEWVC_USER/.subversion</code> (where
<code>VIEWVC_USER</code> is the system user as whom ViewVC
runs), stuff won't work.</p></li>
</ul>
<!-- #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# -->
</div>
</div>
</div> <!-- section -->
</td>
</tr>
</table>
</body>
</html>

BIN
viewvc.org/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

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

Binary file not shown.

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

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

View File

@@ -0,0 +1,219 @@
#!/usr/bin/env python
import sys
import os
import time
import urllib
import shutil
import getopt
import tempfile
class BuildError(Exception):
def __init__(self, message):
self.message = message
def __str__(self):
return self.message
def make_release(branch, export_dir, publish_dir, root_url, username, password):
# Export the requested ViewVC source tree.
cmd = "svn export --quiet '%s/%s' %s " \
"--username '%s' --password '%s' --non-interactive --force" \
% (root_url, urllib.quote(branch), export_dir, username, password)
sys.stdout.write("Running: %s\n" % (cmd))
os.system(cmd)
# Get the version number from ViewVC itself.
sys.path.insert(0, os.path.join(export_dir, 'lib'))
try:
import viewvc
except ImportError:
raise BuildError("Unable to import viewvc module; export failed?")
version = viewvc.__version__
del sys.modules['viewvc']
del viewvc
del sys.path[0]
# Now, use ViewVC tools to make the distribution archives.
localtime = time.localtime()
date = "%4d%02d%02d" % (localtime[0], localtime[1], localtime[2])
distversion = "viewvc-%s" % (version)
distname = "%s-%s" % (distversion, date)
gzip_name = distname + '.tar.gz'
zip_name = distname + '.zip'
curdir = os.getcwd()
try:
os.chdir(os.path.join(export_dir, 'tools'))
os.system('./make-release %s' % (distname))
finally:
os.chdir(curdir)
# Finally, return the locations of the archive files we've built.
gzip_file = os.path.join(export_dir, 'tools', gzip_name)
if not os.path.exists(gzip_file):
gzip_file = None
zip_file = os.path.join(export_dir, 'tools', zip_name)
if not os.path.exists(zip_file):
zip_file = None
# Remove superceded archives.
if gzip_file or zip_file:
dirents = os.listdir(publish_dir)
for dirent in dirents:
if dirent.startswith(distversion) \
and ((dirent.endswith('.tar.gz') and gzip_file) \
or (dirent.endswith('.zip') and zip_file)):
os.unlink(dirent)
# Install the new archives.
if gzip_file:
os.rename(gzip_file, os.path.join(publish_dir, gzip_name))
if zip_file:
os.rename(zip_file, os.path.join(publish_dir, zip_name))
# Return our archive names.
return gzip_file and gzip_name or None, zip_file and zip_name or None
def publish_releases(branches, publish_dir, root_url, username, password):
new_index_contents = get_html_index_header()
for branch in branches:
sys.stdout.write("Beginning build for branch '%s'.\n" % (branch))
export_dir = None
try:
export_dir = tempfile.mkdtemp("", "viewvc-nightly-")
gzip_file, zip_file = make_release(branch, export_dir, publish_dir,
root_url, username, password)
new_index_contents = new_index_contents + """
<p>Build of %s:</p>
<ul>
<li><a href="%s">%s</a></li>
<li><a href="%s">%s</a></li>
</ul>
""" % (branch, urllib.quote(gzip_file), gzip_file,
urllib.quote(zip_file), zip_file)
finally:
if export_dir:
sys.stdout.write("Removing temporary directory '%s'.\n" % (export_dir))
shutil.rmtree(export_dir)
sys.stdout.write("Finished build for branch '%s'.\n" % (branch))
new_index_contents = new_index_contents + get_html_index_footer()
open(os.path.join(publish_dir, 'index.html'), 'w').write(new_index_contents)
def get_html_index_header():
return """
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>ViewVC: Nightly Snapshots</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<link rel="stylesheet" type="text/css" href="../styles.css"/>
<link rel="shortcut icon" href="../favicon.ico" type="image/x-icon" />
</head>
<body>
<div id="title">
<a href="http://www.viewvc.org/"><img
src="../images/title.jpg" alt="ViewVC: Repository Browsing"/></a>
</div>
<div id="menu">
<p><a href="../index.html">Home</a> |
<a href="http://viewvc.tigris.org/">Project Page</a> |
<a href="../download.html">Download</a> |
<a href="../contributing.html">Contributing</a> |
<a href="./faq.html">FAQ</a> |
<a href="http://viewvc.tigris.org/nonav/source/browse/*checkout*/viewvc/trunk/LICENSE.html">License</a> |
<a href="../contact.html">Contact</a> |
<a href="../who.html">About</a>
</p>
</div>
<table id="pagetable">
<tr>
<td id="pagecolumn1">
<h4>On this page:</h4>
<ul id="bookmarks">
<li><a href="%s">Snapshots</a></li>
</ul>
<p><a href="http://validator.w3.org/check?uri=referer"><img
src="http://www.w3.org/Icons/valid-xhtml10"
alt="Valid XHTML 1.0 Strict" height="31" width="88" /></a>
</p>
</td>
<td id="pagecolumn2">
<div class="section">
<h2 id="sec-snapshots">Snapshots</h2>
<div class="section-body">
""" % ("#sec-snapshots")
def get_html_index_footer():
return """
</div> <!-- section-body -->
</div> <!-- section -->
</td>
</tr>
</table>
</body>
</html>
"""
def usage_and_exit(errmsg=None):
stream = errmsg and sys.stderr or sys.stdout
progname = os.path.basename(sys.argv[0])
stream.write("""%s -- nightly ViewVC build generation
Usage: %s [OPTIONS] PUBLISH-DIR BRANCH ...
Build an archive from one or more BRANCH in the ViewVC source
repository, dropping them into PUBLISH-DIR alongside an index.html
file that name and links to them.
Options:
--help (-h, -?) : Show this usage message
--username : Username used for source export
--password : Password used for source export
--root-url : Alternative source repository root URL
""" % (progname, progname))
if errmsg:
stream.write("ERROR: %s\n" % (errmsg))
sys.exit(errmsg and 1 or 0)
if __name__ == "__main__":
root_url = "http://viewvc.tigris.org/svn/viewvc"
username = password = ""
opts, args = getopt.getopt(sys.argv[1:], 'h?',
['help', 'username=', 'password=', 'root-url='])
for name, value in opts:
if name == '-h' or name == '-?' or name == '--help':
usage_and_exit()
elif name == '--username':
username = value
elif name == '--password':
password = value
elif name == '--root-url':
root_url = value
argc = len(args)
if argc < 2:
usage_and_exit("Not enough arguments")
try:
publish_releases(args[1:], args[0], root_url, username, password)
except BuildError, e:
sys.stderr.write(str(e) + "\n")
sys.exit(1)

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

@@ -0,0 +1,169 @@
body {
background-color: #b2d388;
color: black;
font-family: trebuchet ms, arial, sans-serif;
margin: 0;
}
#title {
background-color: #739c3e;
height: 70px;
}
img {
border: none;
}
hr {
width: 95%;
height: 1px;
}
address {
font-size: 60%;
font-style: normal;
}
h2 {
background-color: #94bd5e;
padding: 2px 0.5em 2px 0.5em;
margin: 0;
border-bottom: dotted 1px rgb(180,193,205);
font-size: 110%;
}
h3 {
margin: 0;
font-size: 100%;
font-weight: bold;
border-bottom: dotted 1px rgb(180,193,205);
}
h4 {
padding: 2px 0.5em 2px 0.5em;
margin: 0;
font-size: 90%;
font-weight: bold;
}
p, dl {
padding: 0.5em;
margin: 0;
font-size: 90%;
}
ul, ol {
font-size: 90%;
padding: 0.5em;
margin: 0 0 0 0.25in;
}
code {
font-size: 105%;
font-style: italic;
font-family: monospace;
}
blockquote {
text-align: justify;
font-style: italic;
margin: 0 0.5in 0 0.5in;
padding: 5px;
border: 1px dotted black;
}
.section {
background: white;
border-color: rgb(24,24,24);
border-width: 1px;
border-style: solid;
padding: 0;
margin-bottom: 0.5em;
}
.section-body {
padding: 0 1em;
}
.section p, .section dl, .section ul, .section ol {
padding: 0.5em 0;
}
#menu {
background: black;
color: white;
}
#menu a, #menu a:visited {
background: black;
color: white;
text-decoration: none;
}
#menu a:hover {
background: black;
color: white;
text-decoration: underline;
}
#submenu {
background: rgb(90%,97%,99%);
font-size: 80%;
}
#pagetable tr {
vertical-align: top;
}
#pagetable {
width: 100%;
}
#pagecolumn1 {
width: 175px;
padding: 1em 0 0 0;
}
#pagecolumn1 a, #pagecolumn1 a:visited {
color: rgb(0,0,164);
text-decoration: none;
}
#pagecolumn1 a:hover {
text-decoration: underline;
}
#pagecolumn2 {
padding: 1em 2em;
}
#pagecolumn2 a, #pagecolumn2 a:visited {
color: rgb(0,0,164);
text-decoration: underline;
}
#pagecolumn2 a:hover {
background-color: rgb(180,193,205);
text-decoration: underline;
}
#bookmarks {
padding-left: 0.5em;
font-size: 80%;
white-space: nowrap;
}
.notice {
border: 1px solid black;
margin-bottom: 0.5em;
background: yellow;
}
.faq-section {
margin-top: 1em;
}
.faq-atitle {
font-weight: bold;
}

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

@@ -0,0 +1,181 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>ViewVC: About</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<link rel="stylesheet" type="text/css" href="./styles.css"/>
<link rel="shortcut icon" href="./favicon.ico" type="image/x-icon" />
</head>
<body>
<div id="title">
<a href="http://www.viewvc.org/"><img
src="./images/title.jpg" alt="ViewVC: Repository Browsing"/></a>
</div>
<div id="menu">
<p><a href="./index.html">Home</a> |
<a href="http://viewvc.tigris.org/">Project Page</a> |
<a href="./download.html">Download</a> |
<a href="./contributing.html">Contributing</a> |
<a href="./faq.html">FAQ</a> |
<a href="http://viewvc.tigris.org/nonav/source/browse/*checkout*/viewvc/trunk/LICENSE.html">License</a> |
<a href="./contact.html">Contact</a> |
<a href="./who.html">About</a>
</p>
</div>
<table id="pagetable">
<tr>
<td id="pagecolumn1">
<h4>On this page:</h4>
<ul id="bookmarks">
<li><a href="#sec-history">The History of ViewVC</a></li>
<li><a href="#sec-viewcvs-group">The ViewCVS Group</a></li>
<li><a href="#sec-site-credits">About This Site</a></li>
</ul>
<p><a href="http://validator.w3.org/check?uri=referer"><img
src="http://www.w3.org/Icons/valid-xhtml10"
alt="Valid XHTML 1.0 Strict" height="31" width="88" /></a>
</p>
</td>
<td id="pagecolumn2">
<div class="section">
<h2 id="sec-history">The History of ViewVC</h2>
<div class="section-body">
<p>The ViewVC software was inspired by early versions of cvsweb
(originally written by Bill Fenner, further developed by Henner
Zeller, and now maintained as <a
href="http://www.freebsd.org/projects/cvsweb.html">CVSweb</a> by
the FreeBSD community). Greg Stein wanted to make some changes and
updates, but cvsweb was implemented in Perl. He wrote:</p>
<blockquote>
<p>&quot;While I can manage some Perl, cvsweb was rather
unmaintainable for me. So I undertook the task to convert the
software to Python. As a result, I've actually been able to go
<strong>way</strong> beyond the simple changes that I had
envisioned.&quot;
</p>
</blockquote>
<p>So ViewVC started out as just a port of the cvsweb script,
originally called ViewCVS. Along the way, it has had numerous
cleanups and other modifications, a process simplified by the
elegance of the <a href="http://www.python.org/">Python</a>
language.</p>
<p>In 2001, the ViewCVS project was moved to <a
href="http://www.sourceforge.net">SourceForge</a>, a popular
software collaboration environment. There the project continued to
mature, releasing several stable-yet-pre-1.0 versions. In 2002,
C. Michael Pilato began implementing support for Subversion in
ViewCVS, taking advantage of a version control abstraction layer
begun by Lucas Bruand. Along the way, Russell Yanofsky delivered
large improvements to that abstraction, and to ViewCVS as whole.
ViewCVS was well on its way to releasing a 1.0 version.</p>
<p>Of course, now that ViewCVS could browse Subversion repositories as
easily as CVS ones, the ViewCVS name seemed inappropriate. Also,
the active ViewCVS developers at the time were growing frustrated
with SourceForge as a project hosting environment &mdash;
Subversion wasn't yet available as an option for version control,
the CVS service was always down at just the wrong moment, the bug
trackers were painful to use, and so on. So in late 2005, the
decision was made to rename the project to ViewVC, to convert the
project's CVS data to Subversion, and to move the project and its
version controlled and open issue data to <a
href="http://www.tigris.org">Tigris.org</a>.</p>
<p>Today, ViewVC is being developed at <a
href="http://viewvc.tigris.org">http://viewvc.tigris.org</a> by a
small community of folks.</p>
</div> <!-- section-body -->
</div> <!-- section -->
<div class="section">
<h2 id="sec-viewcvs-group">The ViewCVS Group</h2>
<div class="section-body">
<p>The ViewCVS Group is an informal group of people working on and
developing the ViewVC package. The current set of members are
listed below with some of their notable contributions:</p>
<dl>
<dt><a href="http://www.lyra.org/greg/">Greg Stein</a></dt>
<dd>original python port of Henner Zeller's cvsweb, secure popen
implementation, configuration file implementation, rcsparse
module, and EZT template engine</dd>
<dt>Jay Painter</dt>
<dd>CVSdb query engine</dd>
<dt>Tanaka Akira</dt>
<dd>enscript colorization and tarball generation</dd>
<dt>Tim Cera</dt>
<dd>CvsGraph support, log_table template, regular expression search,
and paging capability</dd>
<dt>Peter Funk</dt>
<dd>standalone server, blimp logo, and numerous improvements to
ViewVC's interfaces and documentation</dd>
<dt>Lucas Bruand</dt>
<dd>C++ RCS parser (tparse) and vclib module for supporting new
version control systems</dd>
<dt><a href="http://www.cmichaelpilato.com/">C. Michael Pilato</a></dt>
<dd>Subversion support, root_as_url alternative URL scheme,
templatization work, website design, documentation</dd>
<dt>Russell Yanofsky</dt>
<dd>Windows support and the sapi module for supporting multiple web
server interfaces, sweeping abstraction and UI improvements</dd>
<dt>James Henstridge</dt>
<dd>integrated query interface, support for querying Subversion
repositories, caching support, CSS formatting, and the EZT
&quot;define&quot; directive</dd>
</dl>
</div> <!-- section-body -->
</div> <!-- section -->
<div class="section">
<h2 id="sec-site-credits">About This Site</h2>
<div class="section-body">
<p>The ViewVC website was designed by <a
href="http://www.cmichaelpilato.com/">C. Michael Pilato</a>. All
HTML was hand-edited in Emacs, and the little splashes of graphical
goodness owe their existence to Open Office and the Gimp. Textual
content for the site is mostly the work of Greg Stein, but has been
tweaked through the ages by various ViewVC contributors.</p>
</div> <!-- section-body -->
</div> <!-- section -->
</td>
</tr>
</table>
</body>
</html>