mirror of
https://github.com/vitalif/viewvc-4intranet
synced 2019-04-16 04:14:59 +03:00
Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
79e6d32043 | ||
![]() |
a6c01c2f5b |
50
CHANGES
50
CHANGES
@@ -1,53 +1,3 @@
|
||||
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
|
||||
* handle file:/// Subversion rootpaths as local roots (issue #446)
|
||||
* restore markup of URLs in file contents (issue #455)
|
||||
* optionally display last-committed metadata in roots view (issue #457)
|
||||
|
||||
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
|
||||
|
54
INSTALL
54
INSTALL
@@ -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,7 +30,7 @@ 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)
|
||||
@@ -213,9 +213,9 @@ APACHE CONFIGURATION
|
||||
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)
|
||||
@@ -232,52 +232,6 @@ APACHE CONFIGURATION
|
||||
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:
|
||||
|
||||
WSGIScriptAlias /viewvc <VIEWVC_INSTALLATION_DIRECTORY>/bin/wsgi/viewvc.wsgi
|
||||
WSGIScriptAlias /query <VIEWVC_INSTALLATION_DIRECTORY>/bin/wsgi/query.wsgi
|
||||
|
||||
You'll probably also need the following directive because of the
|
||||
not-quite-sanctioned way that ViewVC manipulates Python objects.
|
||||
|
||||
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
|
||||
|
||||
3) Restart Apache.
|
||||
|
||||
The commands to do this vary. "httpd -k restart" and "apache -k
|
||||
|
@@ -15,7 +15,7 @@
|
||||
|
||||
<blockquote>
|
||||
|
||||
<p><strong>Copyright © 1999-2011 The ViewCVS Group. All rights
|
||||
<p><strong>Copyright © 1999-2009 The ViewCVS Group. All rights
|
||||
reserved.</strong></p>
|
||||
|
||||
<p>By using ViewVC, you agree to the terms and conditions set forth
|
||||
@@ -60,8 +60,6 @@
|
||||
<li>April 10, 2007 — copyright years updated</li>
|
||||
<li>February 22, 2008 — copyright years updated</li>
|
||||
<li>March 18, 2009 — copyright years updated</li>
|
||||
<li>March 29, 2010 — copyright years updated</li>
|
||||
<li>February 18, 2011 — copyright years updated</li>
|
||||
</ul>
|
||||
|
||||
</body>
|
||||
|
@@ -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()
|
||||
|
||||
|
@@ -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")
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2010 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
|
||||
@@ -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()
|
||||
|
||||
|
1228
bin/standalone.py
1228
bin/standalone.py
File diff suppressed because it is too large
Load Diff
@@ -242,7 +242,6 @@ 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
|
||||
@@ -253,24 +252,18 @@ def main(command, repository, revs=[], verbose=0, force=0):
|
||||
sys.stderr.write("ERROR: " + str(e) + "\n")
|
||||
sys.exit(1)
|
||||
|
||||
# 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':
|
||||
@@ -337,6 +330,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':
|
||||
@@ -359,7 +358,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:
|
||||
|
@@ -1,54 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2010 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
# 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()
|
@@ -1,45 +0,0 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2010 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
# 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 []
|
@@ -1,50 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2010 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
# 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()
|
@@ -1,41 +0,0 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2009 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
# 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 []
|
@@ -106,47 +106,27 @@
|
||||
##
|
||||
#svn_roots =
|
||||
|
||||
## root_parents: Specifies a list of directories under which any
|
||||
## number of repositories may reside. You can specify multiple root
|
||||
## parents separated by commas or new lines, each of which is of the
|
||||
## form "path: type" (where type is either "cvs" or "svn").
|
||||
## root_parents: 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.
|
||||
##
|
||||
## 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 organizing all your repositories under a few parent
|
||||
## directories by allowing you to simply tell it about those parent
|
||||
## directories. ViewVC will then notice each repository of the
|
||||
## specified type in that directory as a root whose name is the
|
||||
## subdirectory in which that repository lives.
|
||||
## You can specify multiple parent paths separated by commas or new lines.
|
||||
##
|
||||
## For example, if you have three Subversion repositories located at
|
||||
## /opt/svn/projects, /opt/svn/websites, and /opt/svn/devstuff, you
|
||||
## could list them individually in svn_roots like so:
|
||||
##
|
||||
## svn_roots = projects: /opt/svn/projects,
|
||||
## websites: /opt/svn/websites,
|
||||
## devstuff: /opt/svn/devstuff
|
||||
##
|
||||
## or you could instead use the root_parents configuration option:
|
||||
##
|
||||
## root_parents = /opt/svn: svn
|
||||
##
|
||||
## The benefit of this latter approach is that, as you add new
|
||||
## repositories to your /opt/svn directory, they automatically become
|
||||
## available for display in ViewVC without additional configuration.
|
||||
##
|
||||
## WARNING: the root names derived for repositories configured via the
|
||||
## root_parents option 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 the
|
||||
## names of repositories under later parent paths may override earlier
|
||||
## ones.
|
||||
## WARNING: 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.
|
||||
##
|
||||
## Example:
|
||||
## root_parents = /opt/svn: svn,
|
||||
## /opt/cvs: cvs
|
||||
## root_parents = /opt/svn : svn,
|
||||
## /opt/cvs : cvs
|
||||
##
|
||||
#root_parents =
|
||||
|
||||
@@ -572,14 +552,6 @@
|
||||
##
|
||||
#show_subdir_lastmod = 0
|
||||
|
||||
## show_roots_lastmod: In the root listing view, show the most recent
|
||||
## modifications made to the root. (Subversion roots only.)
|
||||
##
|
||||
## NOTE: Enabling this feature will significantly reduce the
|
||||
## performance of the root listing view.
|
||||
##
|
||||
#show_roots_lastmod = 0
|
||||
|
||||
## show_logs: Show the most recent log entry in directory listings.
|
||||
##
|
||||
#show_logs = 1
|
||||
@@ -657,26 +629,6 @@
|
||||
##
|
||||
#log_pagesize = 0
|
||||
|
||||
## log_pagesextra: Maximum number of extra pages (based on
|
||||
## log_pagesize) of revision log data to fetch and present to the user
|
||||
## as additional options for display. Revision log information
|
||||
## "beyond" this window is still accessible, but must be navigated to
|
||||
## in multiple steps.
|
||||
##
|
||||
## Example:
|
||||
## log_pagesize = 100
|
||||
## log_pagesextra = 3
|
||||
##
|
||||
## For a versioned file with 1000 revisions, the above settings would
|
||||
## present to the user the first 100 of those 1000 revisions, with
|
||||
## links to three additional pages (the 200-299th revisions, 300-399th
|
||||
## revisions, and 400-499th revisions) plus a link to the 500th
|
||||
## revision. Following these links slides the display "window",
|
||||
## showing the requested set of revisions plus links to three
|
||||
## additional pages beyond those, and so on.
|
||||
##
|
||||
#log_pagesextra = 3
|
||||
|
||||
## limit_changes: Maximum 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
|
||||
@@ -906,10 +858,6 @@
|
||||
## utilities
|
||||
## authz-*
|
||||
##
|
||||
## WARNING: Do not use per-root overrides if your ViewVC instance is
|
||||
## served via the standalone.py server option! Doing so could cause
|
||||
## ViewVC to be unable to function properly (or at all).
|
||||
##
|
||||
## Here is an example showing how to enable Subversion authz-based
|
||||
## authorization for only the single root named "svnroot":
|
||||
##
|
||||
@@ -924,15 +872,8 @@
|
||||
[authz-forbidden]
|
||||
|
||||
## The "forbidden" authorizer forbids access to repository modules,
|
||||
## defined to be top-level subdirectories in a repository.
|
||||
##
|
||||
## NOTE: The options in this section apply only when the 'authorizer'
|
||||
## option (in the [options] section) is set to 'forbidden'.
|
||||
|
||||
## forbidden: A comma-delimited list of patterns which match modules
|
||||
## that ViewVC should hide from users.
|
||||
##
|
||||
## You can use a simple list of modules, or something more complex:
|
||||
## 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
|
||||
@@ -987,12 +928,6 @@
|
||||
## regular expressions. Directory paths will be terminated by a forward
|
||||
## slash.
|
||||
##
|
||||
## NOTE: The options in this section apply only when the 'authorizer'
|
||||
## option (in the [options] section) is set to 'forbiddenre'.
|
||||
|
||||
## forbiddenre: A comma-delimited list of regular expressions which
|
||||
## match paths that ViewVC should hide from users.
|
||||
##
|
||||
## Like the "forbidden" authorizer...
|
||||
##
|
||||
## *) The "!" can be used before a module to explicitly state that it
|
||||
@@ -1027,9 +962,6 @@
|
||||
|
||||
## The "svnauthz" authorizer uses a Subversion authz configuration file
|
||||
## to determine access to repository paths.
|
||||
##
|
||||
## NOTE: The options in this section apply only when the 'authorizer'
|
||||
## option (in the [options] section) is set to 'svnauthz'.
|
||||
|
||||
## authzfile: Specifies the location of the authorization rules file
|
||||
## (using an absolute path).
|
||||
@@ -1045,30 +977,3 @@
|
||||
#force_username_case =
|
||||
|
||||
##---------------------------------------------------------------------------
|
||||
[query]
|
||||
|
||||
## The configuration items in this section are used exclusively by the
|
||||
## 'query' script, a separate script from ViewVC itself that ships
|
||||
## with ViewVC and allows for queries into the ViewVC commits
|
||||
## database. If you aren't using this separate script (which was made
|
||||
## largely irrelevant by the introduction of an integrated "query"
|
||||
## view in ViewVC itself, or aren't using the ViewVC commits database
|
||||
## functionality at all, you can ignore these configurations items
|
||||
## altogether.
|
||||
|
||||
## viewvc_base_url: Base URL at which ViewVC may be accessed on this
|
||||
## server. The default value for this option is determined at
|
||||
## run-time by the various front-ends to the query script.
|
||||
##
|
||||
## Examples:
|
||||
## viewvc_base_url = /viewvc.py
|
||||
## viewvc_base_url = /viewvc.wsgi
|
||||
## viewvc_base_url = /cgi-bin/viewvc
|
||||
## viewvc_base_url = viewvc
|
||||
##
|
||||
## To disable cross-linking between the query script and ViewVC,
|
||||
## uncomment this option and leave its value empty.
|
||||
##
|
||||
#viewvc_base_url =
|
||||
|
||||
##---------------------------------------------------------------------------
|
||||
|
@@ -2144,32 +2144,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.name</td>
|
||||
<td>String</td>
|
||||
@@ -2183,24 +2157,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>
|
||||
|
||||
|
@@ -348,7 +348,7 @@ td {
|
||||
all = viewvc.*
|
||||
|
||||
[all-options]
|
||||
allowed_views = annotate, diff, markup, tar
|
||||
allow_tar = 1
|
||||
</pre>
|
||||
</blockquote>
|
||||
|
||||
@@ -358,7 +358,7 @@ allowed_views = annotate, diff, markup, tar
|
||||
all = viewvc.*
|
||||
|
||||
[vhost-all/options]
|
||||
allowed_views = annotate, diff, markup, tar
|
||||
allow_tar = 1
|
||||
</pre>
|
||||
</blockquote>
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2010 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2008 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*)"(.*?)"')
|
||||
|
||||
@@ -81,7 +82,7 @@ class HTMLBlameSource:
|
||||
diff_url = None
|
||||
if item.prev_rev:
|
||||
diff_url = '%sr1=%s&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,
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2011 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2009 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
@@ -88,11 +88,6 @@ import fnmatch
|
||||
# | vhosts |
|
||||
# | |
|
||||
# `-----------'
|
||||
# ,-----------.
|
||||
# | |
|
||||
# | query |
|
||||
# | |
|
||||
# `-----------'
|
||||
#
|
||||
# ### TODO: Figure out what this all means for the 'kv' stuff.
|
||||
#
|
||||
@@ -105,7 +100,6 @@ class Config:
|
||||
'cvsdb',
|
||||
'general',
|
||||
'options',
|
||||
'query',
|
||||
'templates',
|
||||
'utilities',
|
||||
)
|
||||
@@ -151,11 +145,11 @@ class Config:
|
||||
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):
|
||||
if self._is_allowed_section(self.parser, section,
|
||||
self._base_sections):
|
||||
self._process_section(self.parser, section, section)
|
||||
|
||||
if vhost and self.parser.has_section('vhosts'):
|
||||
@@ -178,7 +172,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):
|
||||
@@ -221,7 +214,7 @@ class Config:
|
||||
|
||||
setattr(sc, opt, value)
|
||||
|
||||
def _is_allowed_section(self, section, allowed_sections):
|
||||
def _is_allowed_section(self, parser, 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."""
|
||||
@@ -234,7 +227,7 @@ class Config:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
def _is_allowed_override(self, sectype, secspec, section):
|
||||
def _is_allowed_override(self, parser, 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
|
||||
@@ -247,7 +240,7 @@ class Config:
|
||||
if section[:lcv] != cv:
|
||||
return None
|
||||
base_section = section[lcv:]
|
||||
if self._is_allowed_section(base_section,
|
||||
if self._is_allowed_section(parser, base_section,
|
||||
self._allowed_overrides[sectype]):
|
||||
return base_section
|
||||
raise IllegalOverrideSection(sectype, section)
|
||||
@@ -260,7 +253,8 @@ class Config:
|
||||
|
||||
# Overlay any option sections associated with this vhost name.
|
||||
for section in parser.sections():
|
||||
base_section = self._is_allowed_override('vhost', canon_vhost, section)
|
||||
base_section = self._is_allowed_override(parser, 'vhost',
|
||||
canon_vhost, section)
|
||||
if base_section:
|
||||
self._process_section(parser, section, base_section)
|
||||
|
||||
@@ -280,25 +274,18 @@ class Config:
|
||||
"""Overlay per-root options for ROOTNAME atop the existing option
|
||||
set. This is a destructive change to the configuration."""
|
||||
|
||||
did_overlay = 0
|
||||
# We can only deal with this happening once!
|
||||
assert(self.root_options_overlayed == 0)
|
||||
self.root_options_overlayed = 1
|
||||
|
||||
if not self.conf_path:
|
||||
return
|
||||
|
||||
for section in self.parser.sections():
|
||||
base_section = self._is_allowed_override('root', rootname, section)
|
||||
base_section = self._is_allowed_override(self.parser, '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
|
||||
|
||||
def _get_parser_items(self, parser, section):
|
||||
"""Basically implement ConfigParser.items() for pre-Python-2.3 versions."""
|
||||
@@ -327,7 +314,7 @@ class Config:
|
||||
assert(self.root_options_overlayed == 0)
|
||||
|
||||
if not self.conf_path:
|
||||
return None, {}
|
||||
return None
|
||||
|
||||
# Figure out the authorizer by searching first for a per-root
|
||||
# override, then falling back to the base/vhost configuration.
|
||||
@@ -420,7 +407,6 @@ 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
|
||||
@@ -434,7 +420,6 @@ class Config:
|
||||
self.options.use_re_search = 0
|
||||
self.options.dir_pagesize = 0
|
||||
self.options.log_pagesize = 0
|
||||
self.options.log_pagesextra = 3
|
||||
self.options.limit_changes = 100
|
||||
|
||||
self.templates.diff = None
|
||||
@@ -460,8 +445,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
|
||||
|
||||
|
77
lib/ezt.py
77
lib/ezt.py
@@ -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])
|
||||
@@ -465,13 +465,13 @@ class Template:
|
||||
except TypeError:
|
||||
raise Exception("Unprintable value type for '%s'" % (str(valrefs[0][0])))
|
||||
|
||||
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)
|
||||
@@ -637,23 +637,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 +653,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,16 +666,14 @@ 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
|
||||
ctx.printers.append(printer)
|
||||
|
||||
|
||||
class TemplateData:
|
||||
@@ -726,7 +715,7 @@ 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 +828,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('&', '&')
|
||||
s = s.replace('<', '<')
|
||||
s = s.replace('>', '>')
|
||||
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 ---
|
||||
|
20
lib/idiff.py
20
lib/idiff.py
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2010 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)
|
||||
|
35
lib/query.py
35
lib/query.py
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2010 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2009 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
@@ -312,7 +312,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 = ' '
|
||||
|
||||
for commit in files:
|
||||
repository = commit.GetRepository()
|
||||
@@ -346,10 +350,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)
|
||||
@@ -432,22 +435,22 @@ def main(server, cfg, viewvc_link):
|
||||
commits = [ ]
|
||||
query = 'skipped'
|
||||
|
||||
docroot = cfg.options.docroot
|
||||
if docroot is None and viewvc_link:
|
||||
docroot = viewvc_link + '/' + viewvc.docroot_magic_path
|
||||
|
||||
data = ezt.TemplateData({
|
||||
'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,
|
||||
'commits' : commits,
|
||||
'num_commits' : len(commits),
|
||||
|
100
lib/sapi.py
100
lib/sapi.py
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2010 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, '&', '&')
|
||||
s = string.replace(s, '>', '>')
|
||||
s = string.replace(s, '<', '<')
|
||||
s = string.replace(s, '"', """)
|
||||
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))
|
||||
|
||||
@@ -174,6 +160,9 @@ class CgiServer(Server):
|
||||
self.header(status='301 Moved')
|
||||
sys.stdout.write('This document is located <a href="%s">here</a>.\n' % url)
|
||||
|
||||
def escape(self, s, quote = None):
|
||||
return cgi.escape(s, quote)
|
||||
|
||||
def getenv(self, name, value=None):
|
||||
ret = os.environ.get(name, value)
|
||||
if self.iis and name == 'PATH_INFO' and ret:
|
||||
@@ -186,7 +175,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,66 +187,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 escape(self, s, quote = None):
|
||||
return cgi.escape(s, quote)
|
||||
|
||||
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)
|
||||
@@ -290,6 +219,9 @@ class AspServer(ThreadedServer):
|
||||
def redirect(self, url):
|
||||
self.response.Redirect(url)
|
||||
|
||||
def escape(self, s, quote = None):
|
||||
return self.server.HTMLEncode(str(s))
|
||||
|
||||
def getenv(self, name, value = None):
|
||||
ret = self.request.ServerVariables(name)()
|
||||
if not type(ret) is types.UnicodeType:
|
||||
@@ -351,6 +283,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)
|
||||
|
||||
@@ -373,6 +308,9 @@ class ModPythonServer(ThreadedServer):
|
||||
self.request.write("You are being redirected to <a href=\"%s\">%s</a>"
|
||||
% (url, url))
|
||||
|
||||
def escape(self, s, quote = None):
|
||||
return cgi.escape(s, quote)
|
||||
|
||||
def getenv(self, name, value = None):
|
||||
try:
|
||||
return self.request.subprocess_env[name]
|
||||
|
@@ -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
|
||||
|
@@ -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:
|
||||
|
@@ -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:
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 2006-2011 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
|
||||
@@ -54,10 +54,7 @@ class ViewVCAuthorizer(vcauth.GenericViewVCAuthorizer):
|
||||
# option names.
|
||||
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 +221,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
|
||||
|
@@ -180,10 +180,8 @@ class Repository:
|
||||
|
||||
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.
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -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])
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2011 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,7 +15,6 @@
|
||||
import os
|
||||
import os.path
|
||||
import re
|
||||
import urllib
|
||||
|
||||
_re_url = re.compile('^(http|https|file|svn|svn\+[^:]+)://')
|
||||
|
||||
@@ -24,20 +23,8 @@ def canonicalize_rootpath(rootpath):
|
||||
import svn.core
|
||||
return svn.core.svn_path_canonicalize(rootpath)
|
||||
except:
|
||||
if os.name == 'posix':
|
||||
rootpath_lower = rootpath.lower()
|
||||
if rootpath_lower in ['file://localhost',
|
||||
'file://localhost/',
|
||||
'file://',
|
||||
'file:///'
|
||||
]:
|
||||
return '/'
|
||||
if rootpath_lower.startswith('file://localhost/'):
|
||||
return os.path.normpath(urllib.unquote(rootpath[16:]))
|
||||
elif rootpath_lower.startswith('file:///'):
|
||||
return os.path.normpath(urllib.unquote(rootpath[7:]))
|
||||
if re.search(_re_url, rootpath):
|
||||
return rootpath.rstrip('/')
|
||||
return rootpath[-1] == '/' and rootpath[:-1] or rootpath
|
||||
return os.path.normpath(rootpath)
|
||||
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2011 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2009 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
@@ -18,9 +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
|
||||
from svn_repos import Revision, SVNChangedPath, _datestr_to_date, _compare_paths, _path_parts, _cleanup_path, _rev2optrev, _fix_subversion_exception
|
||||
from svn import core, delta, client, wc, ra
|
||||
|
||||
|
||||
@@ -51,29 +52,6 @@ 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, cross_copies,
|
||||
cb_func, ctx):
|
||||
try:
|
||||
client.svn_client_log4([url], start_rev, start_rev, end_rev,
|
||||
log_limit, 1, not cross_copies, 0, None,
|
||||
cb_func, ctx)
|
||||
except AttributeError:
|
||||
# Wrap old svn_log_message_receiver_t interface with a
|
||||
# svn_log_entry_t one.
|
||||
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,
|
||||
1, not cross_copies, cb_convert, ctx)
|
||||
|
||||
### END COMPATABILITY CODE ###
|
||||
|
||||
|
||||
@@ -90,11 +68,7 @@ class LogCollector:
|
||||
self.show_all_logs = show_all_logs
|
||||
self.lockinfo = lockinfo
|
||||
|
||||
def add_log(self, log_entry, pool):
|
||||
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))
|
||||
@@ -114,8 +88,8 @@ class LogCollector:
|
||||
if change.copyfrom_path:
|
||||
this_path = change.copyfrom_path + self.path[len(changed_path):]
|
||||
if self.show_all_logs or this_path:
|
||||
entry = Revision(revision, date, author, msg, None, self.lockinfo,
|
||||
self.path[1:], None, None)
|
||||
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
|
||||
@@ -183,7 +157,7 @@ class RemoteSubversionRepository(vclib.Repository):
|
||||
# Setup the client context baton, complete with non-prompting authstuffs.
|
||||
# TODO: svn_cmdline_setup_auth_baton() is mo' better (when available)
|
||||
core.svn_config_ensure(self.config_dir)
|
||||
self.ctx = client.svn_client_create_context()
|
||||
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(),
|
||||
@@ -203,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
|
||||
@@ -282,7 +252,7 @@ class RemoteSubversionRepository(vclib.Repository):
|
||||
if not vclib.check_path_access(self, entry_path_parts, entry.kind, rev):
|
||||
continue
|
||||
dirent = dirents[entry.name]
|
||||
entry.date, entry.author, entry.log, revprops, changes = \
|
||||
entry.date, entry.author, entry.log, changes = \
|
||||
self.revinfo(dirent.created_rev)
|
||||
entry.rev = str(dirent.created_rev)
|
||||
entry.size = dirent.size
|
||||
@@ -313,8 +283,9 @@ class RemoteSubversionRepository(vclib.Repository):
|
||||
log_limit = 0
|
||||
if limit:
|
||||
log_limit = first + limit
|
||||
client_log(url, _rev2optrev(rev), _rev2optrev(1), log_limit,
|
||||
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
|
||||
@@ -366,7 +337,7 @@ class RemoteSubversionRepository(vclib.Repository):
|
||||
if not cached_info:
|
||||
cached_info = self._revinfo_raw(rev)
|
||||
self._revinfo_cache[rev] = cached_info
|
||||
return tuple(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)
|
||||
@@ -381,7 +352,7 @@ class RemoteSubversionRepository(vclib.Repository):
|
||||
args = vclib._diff_args(type, options)
|
||||
|
||||
def _date_from_rev(rev):
|
||||
date, author, msg, revprops, changes = self.revinfo(rev)
|
||||
date, author, msg, changes = self.revinfo(rev)
|
||||
return date
|
||||
|
||||
try:
|
||||
@@ -446,54 +417,29 @@ class RemoteSubversionRepository(vclib.Repository):
|
||||
return revisions[0]
|
||||
|
||||
def _revinfo_raw(self, rev):
|
||||
# return 5-tuple (date, author, msg, revprops, changes)
|
||||
# return 5-tuple (date, author, message, changes)
|
||||
optrev = _rev2optrev(rev)
|
||||
revs = []
|
||||
|
||||
def _log_cb(log_entry, pool, retval=revs):
|
||||
### 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.sort(lambda a, b: _compare_paths(a, b))
|
||||
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,
|
||||
}
|
||||
paths = (changed_paths or {}).keys()
|
||||
paths.sort(lambda a, b: _compare_paths(a, b))
|
||||
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
|
||||
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?"
|
||||
action = action_map.get(change.action, vclib.MODIFIED)
|
||||
if change.copyfrom_path and change.copyfrom_rev:
|
||||
is_copy = 1
|
||||
base_path = change.copyfrom_path
|
||||
@@ -516,21 +462,21 @@ class RemoteSubversionRepository(vclib.Repository):
|
||||
base_path = None
|
||||
base_rev = None
|
||||
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 found_unreadable:
|
||||
msg = None
|
||||
message = None
|
||||
if not found_readable:
|
||||
author = None
|
||||
date = None
|
||||
revs.append([date, author, msg, revprops, changes])
|
||||
revs.append([date, author, message, changes])
|
||||
|
||||
client_log(self.rootpath, optrev, optrev, 1, 0, _log_cb, self.ctx)
|
||||
return tuple(revs[0])
|
||||
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 --##
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2011 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2009 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
@@ -115,27 +115,6 @@ def _rootpath2url(rootpath, path):
|
||||
return 'file://' + string.join([rootpath, path], "/")
|
||||
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
def _datestr_to_date(datestr):
|
||||
try:
|
||||
return core.svn_time_from_cstring(datestr) / 1000000
|
||||
@@ -204,6 +183,59 @@ class NodeHistory:
|
||||
def __getitem__(self, idx):
|
||||
return self.histories[idx]
|
||||
|
||||
|
||||
def _get_history(svnrepos, path, rev, path_type, limit=0, options={}):
|
||||
if svnrepos.youngest == 0:
|
||||
return []
|
||||
|
||||
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:
|
||||
_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(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)
|
||||
@@ -282,14 +314,14 @@ class FileContentsPipe:
|
||||
|
||||
|
||||
class BlameSource:
|
||||
def __init__(self, local_url, rev, first_rev, config_dir):
|
||||
def __init__(self, local_url, rev, first_rev):
|
||||
self.idx = -1
|
||||
self.first_rev = first_rev
|
||||
self.blame_data = []
|
||||
|
||||
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
|
||||
@@ -344,7 +376,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):
|
||||
@@ -374,10 +406,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
|
||||
|
||||
@@ -444,7 +472,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
|
||||
@@ -493,19 +521,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:
|
||||
@@ -533,56 +562,36 @@ class LocalSubversionRepository(vclib.Repository):
|
||||
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, self.config_dir)
|
||||
youngest_rev, oldest_rev)
|
||||
return source, youngest_rev
|
||||
|
||||
def revinfo(self, rev):
|
||||
return self._revinfo(rev, 1)
|
||||
|
||||
def rawdiff(self, path_parts1, rev1, path_parts2, rev2, type, options={}):
|
||||
p1 = self._getpath(path_parts1)
|
||||
p2 = self._getpath(path_parts2)
|
||||
r1 = self._getrev(rev1)
|
||||
r2 = self._getrev(rev2)
|
||||
if not vclib.check_path_access(self, path_parts1, vclib.FILE, rev1):
|
||||
raise vclib.ItemNotFound(path_parts1)
|
||||
if not vclib.check_path_access(self, path_parts2, vclib.FILE, rev2):
|
||||
raise vclib.ItemNotFound(path_parts2)
|
||||
|
||||
args = vclib._diff_args(type, options)
|
||||
|
||||
def _date_from_rev(rev):
|
||||
date, author, msg, revprops, changes = self._revinfo(rev)
|
||||
return date
|
||||
|
||||
try:
|
||||
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 == core.SVN_ERR_FS_NOT_FOUND:
|
||||
raise vclib.InvalidRevision
|
||||
raise
|
||||
|
||||
def isexecutable(self, path_parts, rev):
|
||||
props = self.itemprops(path_parts, rev) # does authz-check
|
||||
return props.has_key(core.SVN_PROP_EXECUTABLE)
|
||||
|
||||
##--- 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."""
|
||||
|
||||
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 = revprops.get(core.SVN_PROP_REVISION_LOG)
|
||||
author = revprops.get(core.SVN_PROP_REVISION_AUTHOR)
|
||||
datestr = revprops.get(core.SVN_PROP_REVISION_DATE)
|
||||
date = _datestr_to_date(datestr)
|
||||
|
||||
# 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, 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.
|
||||
# Either way, we need 'em, so let's get 'em.
|
||||
fsroot = self._getroot(rev)
|
||||
editor = repos.ChangeCollector(self.fs_ptr, fsroot)
|
||||
e_ptr, e_baton = delta.make_editor(editor)
|
||||
repos.svn_repos_replay(fsroot, e_ptr, e_baton)
|
||||
@@ -635,8 +644,7 @@ class LocalSubversionRepository(vclib.Repository):
|
||||
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):
|
||||
if not vclib.check_path_access(self, parts, pathtype, change.base_rev):
|
||||
is_copy = 0
|
||||
change.base_path = None
|
||||
change.base_rev = None
|
||||
@@ -648,76 +656,24 @@ class LocalSubversionRepository(vclib.Repository):
|
||||
found_readable = 1
|
||||
else:
|
||||
found_unreadable = 1
|
||||
return found_readable, found_unreadable, changedpaths.values()
|
||||
|
||||
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 vclib.check_path_access(self, parts, pathtype, rev):
|
||||
found_readable = 1
|
||||
if hasattr(change, 'copyfrom_path'):
|
||||
copyfrom_path = change.copyfrom_path
|
||||
copyfrom_rev = change.copyfrom_rev
|
||||
else:
|
||||
copyfrom_rev, copyfrom_path = fs.copied_from(fsroot, path)
|
||||
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)
|
||||
# If our caller doesn't care about changed paths, we must be
|
||||
# here for authz reasons only. That means the minute we've
|
||||
# found both a readable and an unreadable path, we can bail out.
|
||||
if (not include_changed_paths) and found_readable and found_unreadable:
|
||||
return date, author, None, None
|
||||
|
||||
# Filter our metadata where necessary, and return the requested data.
|
||||
# Okay, we've process all our paths. Let's filter our metadata,
|
||||
# and return the requested data.
|
||||
if found_unreadable:
|
||||
msg = None
|
||||
if not found_readable:
|
||||
author = None
|
||||
date = None
|
||||
return date, author, msg, revprops, changedpaths
|
||||
if include_changed_paths:
|
||||
return date, author, msg, changedpaths.values()
|
||||
else:
|
||||
return date, author, msg, None
|
||||
|
||||
# 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
|
||||
@@ -725,55 +681,45 @@ class LocalSubversionRepository(vclib.Repository):
|
||||
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):
|
||||
or (include_changed_paths and cached_info[3] is None):
|
||||
cached_info = _revinfo_helper(rev, include_changed_paths)
|
||||
self._revinfo_cache[rev] = cached_info
|
||||
return tuple(cached_info)
|
||||
return cached_info[0], cached_info[1], cached_info[2], cached_info[3]
|
||||
|
||||
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 revinfo(self, rev):
|
||||
return self._revinfo(rev, 1)
|
||||
|
||||
def rawdiff(self, path_parts1, rev1, path_parts2, rev2, type, options={}):
|
||||
p1 = self._getpath(path_parts1)
|
||||
p2 = self._getpath(path_parts2)
|
||||
r1 = self._getrev(rev1)
|
||||
r2 = self._getrev(rev2)
|
||||
if not vclib.check_path_access(self, path_parts1, vclib.FILE, rev1):
|
||||
raise vclib.ItemNotFound(path_parts1)
|
||||
if not vclib.check_path_access(self, path_parts2, vclib.FILE, rev2):
|
||||
raise vclib.ItemNotFound(path_parts2)
|
||||
|
||||
args = vclib._diff_args(type, options)
|
||||
|
||||
def _get_history(self, path, rev, path_type, limit=0, options={}):
|
||||
if self.youngest == 0:
|
||||
return []
|
||||
def _date_from_rev(rev):
|
||||
date, author, msg, changes = self._revinfo(rev)
|
||||
return date
|
||||
|
||||
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))
|
||||
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 != _SVN_ERR_CEASE_INVOCATION:
|
||||
raise
|
||||
if e.apr_err == core.SVN_ERR_FS_NOT_FOUND:
|
||||
raise vclib.InvalidRevision
|
||||
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 isexecutable(self, path_parts, rev):
|
||||
props = self.itemprops(path_parts, rev) # does authz-check
|
||||
return props.has_key(core.SVN_PROP_EXECUTABLE)
|
||||
|
||||
def _getpath(self, path_parts):
|
||||
return string.join(path_parts, '/')
|
||||
@@ -796,7 +742,7 @@ class LocalSubversionRepository(vclib.Repository):
|
||||
r = self._fsroots[rev] = fs.revision_root(self.fs_ptr, rev)
|
||||
return r
|
||||
|
||||
##--- custom ---##
|
||||
##--- custom --##
|
||||
|
||||
def get_youngest_revision(self):
|
||||
return self.youngest
|
||||
|
361
lib/viewvc.py
361
lib/viewvc.py
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2011 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2009 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
@@ -14,7 +14,7 @@
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
__version__ = '1.1.9'
|
||||
__version__ = '1.1.3'
|
||||
|
||||
# this comes from our library; measure the startup time
|
||||
import debug
|
||||
@@ -24,6 +24,7 @@ debug.t_start('imports')
|
||||
# standard modules that we know are in the path or builtin
|
||||
import sys
|
||||
import os
|
||||
import cgi
|
||||
import gzip
|
||||
import mimetypes
|
||||
import re
|
||||
@@ -79,6 +80,10 @@ _sticky_vars = [
|
||||
'limit_changes',
|
||||
]
|
||||
|
||||
# number of extra pages of information on either side of the current
|
||||
# page to fetch (see dir_pagesize/log_pagesize configuration option)
|
||||
EXTRA_PAGES = 3
|
||||
|
||||
# for reading/writing between a couple descriptors
|
||||
CHUNK_SIZE = 8192
|
||||
|
||||
@@ -269,8 +274,7 @@ class Request:
|
||||
self.roottype = 'cvs'
|
||||
else:
|
||||
raise debug.ViewVCException(
|
||||
'The root "%s" has an unknown type ("%s"). Expected "cvs" or "svn".'
|
||||
% (self.rootname, type),
|
||||
'The root "%s" has an unknown type (%s).' % (self.rootname, type),
|
||||
"500 Internal Server Error")
|
||||
|
||||
# If this is using an old-style 'rev' parameter, redirect to new hotness.
|
||||
@@ -428,8 +432,7 @@ class Request:
|
||||
action = self.server.escape(urllib.quote(url, _URL_SAFE_CHARS))
|
||||
hidden_values = []
|
||||
for name, value in params.items():
|
||||
hidden_values.append(_item(name=self.server.escape(name),
|
||||
value=self.server.escape(value)))
|
||||
hidden_values.append(_item(name=name, value=value))
|
||||
return action, hidden_values
|
||||
|
||||
def get_link(self, view_func=None, where=None, pathtype=None, params=None):
|
||||
@@ -893,41 +896,26 @@ def get_view_template(cfg, view_name, language="en"):
|
||||
|
||||
return template
|
||||
|
||||
def get_writeready_server_file(request, content_type=None, encoding=None,
|
||||
content_length=None):
|
||||
def get_writeready_server_file(request, content_type=None):
|
||||
"""Return a file handle to a response body stream, after outputting
|
||||
any queued special headers (on REQUEST.server) and (optionally) a
|
||||
'Content-Type' header whose value is CONTENT_TYPE and character set
|
||||
is ENCODING.
|
||||
|
||||
If CONTENT_LENGTH is provided and compression is not in use, also
|
||||
generate a 'Content-Length' header for this response.
|
||||
|
||||
After this function is called, it is too late to add new headers to
|
||||
the response."""
|
||||
|
||||
'Content-Type' header whose value is CONTENT_TYPE. After this is
|
||||
called, it is too late to add new headers to the response."""
|
||||
if request.gzip_compress_level:
|
||||
request.server.addheader('Content-Encoding', 'gzip')
|
||||
elif content_length is not None:
|
||||
request.server.addheader('Content-Length', content_length)
|
||||
|
||||
if content_type and encoding:
|
||||
request.server.header("%s; charset=%s" % (content_type, encoding))
|
||||
elif content_type:
|
||||
if content_type:
|
||||
request.server.header(content_type)
|
||||
else:
|
||||
request.server.header()
|
||||
|
||||
if request.gzip_compress_level:
|
||||
fp = gzip.GzipFile('', 'wb', request.gzip_compress_level,
|
||||
request.server.file())
|
||||
else:
|
||||
fp = request.server.file()
|
||||
|
||||
return fp
|
||||
|
||||
def generate_page(request, view_name, data, content_type=None):
|
||||
server_fp = get_writeready_server_file(request, content_type)
|
||||
server_fp = get_writeready_server_file(request)
|
||||
template = get_view_template(request.cfg, view_name, request.language)
|
||||
template.generate(server_fp, data)
|
||||
|
||||
@@ -1087,6 +1075,9 @@ def get_file_view_info(request, where, rev=None, mime_type=None, pathrev=-1):
|
||||
revision_href=revision_href,
|
||||
prefer_markup=ezt.boolean(prefer_markup))
|
||||
|
||||
def htmlify(html):
|
||||
return html and cgi.escape(html) or html
|
||||
|
||||
|
||||
# Matches URLs
|
||||
_re_rewrite_url = re.compile('((http|https|ftp|file|svn|svn\+ssh)'
|
||||
@@ -1097,10 +1088,8 @@ _re_rewrite_url = re.compile('((http|https|ftp|file|svn|svn\+ssh)'
|
||||
_re_rewrite_email = re.compile('([-a-zA-Z0-9_.\+]+)@'
|
||||
'(([-a-zA-Z0-9]+\.)+[A-Za-z]{2,4})')
|
||||
|
||||
# Matches revision references
|
||||
_re_rewrite_svnrevref = re.compile(r'\b(r|rev #?|revision #?)([0-9]+)\b')
|
||||
|
||||
class ViewVCHtmlFormatter:
|
||||
class HtmlFormatter:
|
||||
"""Format a string as HTML-encoded output with customizable markup
|
||||
rules, for example turning strings that look like URLs into anchor links.
|
||||
|
||||
@@ -1121,8 +1110,8 @@ class ViewVCHtmlFormatter:
|
||||
"""
|
||||
s = mobj.group(0)
|
||||
trunc_s = maxlen and s[:maxlen] or s
|
||||
return '<a href="%s">%s</a>' % (sapi.escape(s),
|
||||
sapi.escape(trunc_s)), \
|
||||
return '<a href="%s">%s</a>' % (cgi.escape(s),
|
||||
cgi.escape(trunc_s)), \
|
||||
len(trunc_s)
|
||||
|
||||
def format_email(self, mobj, userdata, maxlen=0):
|
||||
@@ -1166,25 +1155,6 @@ class ViewVCHtmlFormatter:
|
||||
trunc_s = mobj.group(1)[:maxlen]
|
||||
return self._entity_encode(trunc_s), len(trunc_s)
|
||||
|
||||
def format_svnrevref(self, mobj, userdata, maxlen=0):
|
||||
"""Return a 2-tuple containing:
|
||||
- the text represented by MatchObject MOBJ, formatted as an
|
||||
linkified URL to a ViewVC Subversion revision view, with no
|
||||
more than MAXLEN characters in the non-HTML-tag portions.
|
||||
If MAXLEN is 0, there is no maximum.
|
||||
- the number of characters returned.
|
||||
|
||||
USERDATA is a function that accepts a revision reference
|
||||
and returns a URL to that revision.
|
||||
"""
|
||||
s = mobj.group(0)
|
||||
revref = mobj.group(2)
|
||||
trunc_s = maxlen and s[:maxlen] or s
|
||||
revref_url = userdata(revref)
|
||||
return '<a href="%s">%s</a>' % (sapi.escape(revref_url),
|
||||
sapi.escape(trunc_s)), \
|
||||
len(trunc_s)
|
||||
|
||||
def format_text(self, s, unused, maxlen=0):
|
||||
"""Return a 2-tuple containing:
|
||||
- the text S, HTML-escaped, containing no more than MAXLEN
|
||||
@@ -1192,7 +1162,7 @@ class ViewVCHtmlFormatter:
|
||||
- the number of characters returned.
|
||||
"""
|
||||
trunc_s = maxlen and s[:maxlen] or s
|
||||
return sapi.escape(trunc_s), len(trunc_s)
|
||||
return cgi.escape(trunc_s), len(trunc_s)
|
||||
|
||||
def add_formatter(self, regexp, conv, userdata=None):
|
||||
"""Register a formatter which finds instances of strings matching
|
||||
@@ -1273,22 +1243,13 @@ class ViewVCHtmlFormatter:
|
||||
s = ''
|
||||
return tokens
|
||||
|
||||
|
||||
def format_log(request, log, maxlen=0, htmlize=1):
|
||||
def format_log(log, cfg, maxlen=0, htmlize=1):
|
||||
if not log:
|
||||
return log
|
||||
|
||||
cfg = request.cfg
|
||||
if htmlize:
|
||||
lf = ViewVCHtmlFormatter()
|
||||
lf = HtmlFormatter()
|
||||
lf.add_formatter(_re_rewrite_url, lf.format_url)
|
||||
if request.roottype == 'svn':
|
||||
def revision_to_url(rev):
|
||||
return request.get_url(view_func=view_revision,
|
||||
params={'revision': rev},
|
||||
escape=1)
|
||||
lf.add_formatter(_re_rewrite_svnrevref, lf.format_svnrevref,
|
||||
revision_to_url)
|
||||
if cfg.options.mangle_email_addresses == 2:
|
||||
lf.add_formatter(_re_rewrite_email, lf.format_email_truncated)
|
||||
elif cfg.options.mangle_email_addresses == 1:
|
||||
@@ -1506,7 +1467,7 @@ def copy_stream(src, dst, htmlize=0):
|
||||
if not chunk:
|
||||
break
|
||||
if htmlize:
|
||||
chunk = sapi.escape(chunk)
|
||||
chunk = htmlify(chunk)
|
||||
dst.write(chunk)
|
||||
|
||||
class MarkupPipeWrapper:
|
||||
@@ -1527,23 +1488,7 @@ class MarkupPipeWrapper:
|
||||
if self.posttext:
|
||||
ctx.fp.write(self.posttext)
|
||||
|
||||
_re_rewrite_escaped_url = re.compile('((http|https|ftp|file|svn|svn\+ssh)'
|
||||
'(://[-a-zA-Z0-9%.~:_/]+)'
|
||||
'((\?|\&amp;|\&|\&)'
|
||||
'([-a-zA-Z0-9%.~:_]+)=([-a-zA-Z0-9%.~:_])+)*'
|
||||
'(#([-a-zA-Z0-9%.~:_]+)?)?)')
|
||||
|
||||
def markup_escaped_urls(s):
|
||||
# Return a copy of S with all URL references -- which are expected
|
||||
# to be already HTML-escaped -- wrapped in <a href=""></a>.
|
||||
def _url_repl(match_obj):
|
||||
url = match_obj.group(0)
|
||||
unescaped_url = string.replace(url, "&amp;", "&")
|
||||
return "<a href=\"%s\">%s</a>" % (unescaped_url, url)
|
||||
return re.sub(_re_rewrite_escaped_url, _url_repl, s)
|
||||
|
||||
def markup_stream_pygments(request, cfg, blame_data, fp, filename,
|
||||
mime_type, encoding):
|
||||
def markup_stream_pygments(request, cfg, blame_data, fp, filename, mime_type):
|
||||
# Determine if we should use Pygments to highlight our output.
|
||||
# Reasons not to include a) being told not to by the configuration,
|
||||
# b) not being able to import the Pygments modules, and c) Pygments
|
||||
@@ -1551,7 +1496,7 @@ def markup_stream_pygments(request, cfg, blame_data, fp, filename,
|
||||
blame_source = []
|
||||
if blame_data:
|
||||
for i in blame_data:
|
||||
i.text = sapi.escape(i.text)
|
||||
i.text = cgi.escape(i.text)
|
||||
i.diff_href = None
|
||||
if i.prev_rev:
|
||||
i.diff_href = request.get_url(view_func=view_diff,
|
||||
@@ -1569,14 +1514,13 @@ def markup_stream_pygments(request, cfg, blame_data, fp, filename,
|
||||
get_lexer_by_name, \
|
||||
get_lexer_for_mimetype, \
|
||||
get_lexer_for_filename
|
||||
if not encoding:
|
||||
encoding = 'guess'
|
||||
if cfg.options.detect_encoding:
|
||||
try:
|
||||
import chardet
|
||||
encoding = 'chardet'
|
||||
except (SyntaxError, ImportError):
|
||||
pass
|
||||
encoding = 'guess'
|
||||
if cfg.options.detect_encoding:
|
||||
try:
|
||||
import chardet
|
||||
encoding = 'chardet'
|
||||
except (SyntaxError, ImportError):
|
||||
pass
|
||||
try:
|
||||
lexer = get_lexer_for_mimetype(mime_type,
|
||||
encoding=encoding,
|
||||
@@ -1605,7 +1549,6 @@ def markup_stream_pygments(request, cfg, blame_data, fp, filename,
|
||||
def __getitem__(self, idx):
|
||||
item = self.blame_source.__getitem__(idx)
|
||||
item.text = string.expandtabs(item.text, self.tabsize)
|
||||
item.text = markup_escaped_urls(item.text)
|
||||
return item
|
||||
return BlameSourceTabsizeWrapper(blame_source, cfg.options.tabsize)
|
||||
else:
|
||||
@@ -1616,8 +1559,7 @@ def markup_stream_pygments(request, cfg, blame_data, fp, filename,
|
||||
if not line:
|
||||
break
|
||||
line_no = line_no + 1
|
||||
line = sapi.escape(string.expandtabs(line, cfg.options.tabsize))
|
||||
line = markup_escaped_urls(line)
|
||||
line = cgi.escape(string.expandtabs(line, cfg.options.tabsize))
|
||||
item = vclib.Annotation(line, line_no, None, None, None, None)
|
||||
item.diff_href = None
|
||||
lines.append(item)
|
||||
@@ -1635,7 +1577,6 @@ def markup_stream_pygments(request, cfg, blame_data, fp, filename,
|
||||
self.line_no = 0
|
||||
def write(self, buf):
|
||||
### FIXME: Don't bank on write() being called once per line
|
||||
buf = markup_escaped_urls(buf)
|
||||
if self.has_blame_data:
|
||||
self.blame_data[self.line_no].text = buf
|
||||
else:
|
||||
@@ -1683,8 +1624,9 @@ def get_itemprops(request, path_parts, rev):
|
||||
propnames = itemprops.keys()
|
||||
propnames.sort()
|
||||
props = []
|
||||
has_binary_props = 0
|
||||
for name in propnames:
|
||||
value = format_log(request, itemprops[name])
|
||||
value = format_log(itemprops[name], request.cfg)
|
||||
undisplayable = ezt.boolean(0)
|
||||
# skip non-utf8 property names
|
||||
try:
|
||||
@@ -1700,33 +1642,20 @@ def get_itemprops(request, path_parts, rev):
|
||||
props.append(_item(name=name, value=value, undisplayable=undisplayable))
|
||||
return props
|
||||
|
||||
def parse_mime_type(mime_type):
|
||||
mime_parts = map(lambda x: x.strip(), string.split(mime_type, ';'))
|
||||
type_subtype = mime_parts[0].lower()
|
||||
parameters = {}
|
||||
for part in mime_parts[1:]:
|
||||
name, value = string.split(part, '=', 1)
|
||||
parameters[name] = value
|
||||
return type_subtype, parameters
|
||||
|
||||
def calculate_mime_type(request, path_parts, rev):
|
||||
"""Return a 2-tuple carrying the MIME content type and character
|
||||
encoding for the file represented by PATH_PARTS in REV. Use REQUEST
|
||||
for repository access as necessary."""
|
||||
mime_type = None
|
||||
if not path_parts:
|
||||
return None, None
|
||||
mime_type = encoding = None
|
||||
return None
|
||||
if request.roottype == 'svn' \
|
||||
and (not request.cfg.options.svn_ignore_mimetype):
|
||||
try:
|
||||
itemprops = request.repos.itemprops(path_parts, rev)
|
||||
mime_type = itemprops.get('svn:mime-type')
|
||||
if mime_type:
|
||||
mime_type, parameters = parse_mime_type(mime_type)
|
||||
return mime_type, parameters.get('charset')
|
||||
return mime_type
|
||||
except:
|
||||
pass
|
||||
return guess_mime(path_parts[-1]), None
|
||||
return guess_mime(path_parts[-1])
|
||||
|
||||
def markup_or_annotate(request, is_annotate):
|
||||
cfg = request.cfg
|
||||
@@ -1734,7 +1663,7 @@ def markup_or_annotate(request, is_annotate):
|
||||
lines = fp = image_src_href = None
|
||||
annotation = 'none'
|
||||
revision = None
|
||||
mime_type, encoding = calculate_mime_type(request, path, rev)
|
||||
mime_type = calculate_mime_type(request, path, rev)
|
||||
|
||||
# Is this a viewable image type?
|
||||
if is_viewable_image(mime_type) \
|
||||
@@ -1767,7 +1696,7 @@ def markup_or_annotate(request, is_annotate):
|
||||
fp.close()
|
||||
return
|
||||
lines = markup_stream_pygments(request, cfg, blame_source, fp,
|
||||
path[-1], mime_type, encoding)
|
||||
path[-1], mime_type)
|
||||
fp.close()
|
||||
|
||||
data = common_template_data(request, revision)
|
||||
@@ -1801,7 +1730,7 @@ def markup_or_annotate(request, is_annotate):
|
||||
data['date'] = make_time_string(entry.date, cfg)
|
||||
data['author'] = entry.author
|
||||
data['changed'] = entry.changed
|
||||
data['log'] = format_log(request, entry.log)
|
||||
data['log'] = format_log(entry.log, cfg)
|
||||
data['size'] = entry.size
|
||||
|
||||
if entry.date is not None:
|
||||
@@ -1834,18 +1763,12 @@ def view_markup(request):
|
||||
if 'markup' not in request.cfg.options.allowed_views:
|
||||
raise debug.ViewVCException('Markup view is disabled',
|
||||
'403 Forbidden')
|
||||
if request.pathtype != vclib.FILE:
|
||||
raise debug.ViewVCException('Unsupported feature: markup view on '
|
||||
'directory', '400 Bad Request')
|
||||
markup_or_annotate(request, 0)
|
||||
|
||||
def view_annotate(request):
|
||||
if 'annotate' not in request.cfg.options.allowed_views:
|
||||
raise debug.ViewVCException('Annotation view is disabled',
|
||||
'403 Forbidden')
|
||||
if request.pathtype != vclib.FILE:
|
||||
raise debug.ViewVCException('Unsupported feature: annotate view on '
|
||||
'directory', '400 Bad Request')
|
||||
markup_or_annotate(request, 1)
|
||||
|
||||
def revcmp(rev1, rev2):
|
||||
@@ -1920,16 +1843,9 @@ def view_roots(request):
|
||||
href = request.get_url(view_func=view_directory,
|
||||
where='', pathtype=vclib.DIR,
|
||||
params={'root': rootname}, escape=1)
|
||||
lastmod = allroots[rootname][2]
|
||||
roots.append(_item(name=request.server.escape(rootname),
|
||||
type=allroots[rootname][1],
|
||||
path=allroots[rootname][0],
|
||||
author=lastmod and lastmod.author or None,
|
||||
ago=lastmod and lastmod.ago or None,
|
||||
date=lastmod and lastmod.date or None,
|
||||
log=lastmod and lastmod.log or None,
|
||||
short_log=lastmod and lastmod.short_log or None,
|
||||
rev=lastmod and lastmod.rev or None,
|
||||
href=href))
|
||||
|
||||
data = common_template_data(request)
|
||||
@@ -2030,8 +1946,8 @@ def view_directory(request):
|
||||
row.date = make_time_string(file.date, cfg)
|
||||
row.ago = html_time(request, file.date)
|
||||
if cfg.options.show_logs:
|
||||
row.log = format_log(request, file.log)
|
||||
row.short_log = format_log(request, file.log,
|
||||
row.log = format_log(file.log, cfg)
|
||||
row.short_log = format_log(file.log, cfg,
|
||||
maxlen=cfg.options.short_log_len)
|
||||
row.lockinfo = file.lockinfo
|
||||
row.anchor = request.server.escape(file.name)
|
||||
@@ -2088,9 +2004,8 @@ def view_directory(request):
|
||||
if request.roottype == 'svn':
|
||||
row.size = file.size
|
||||
|
||||
row.mime_type, encoding = calculate_mime_type(request,
|
||||
_path_parts(file_where),
|
||||
file.rev)
|
||||
row.mime_type = calculate_mime_type(request, _path_parts(file_where),
|
||||
file.rev)
|
||||
fvi = get_file_view_info(request, file_where, file.rev, row.mime_type)
|
||||
row.view_href = fvi.view_href
|
||||
row.download_href = fvi.download_href
|
||||
@@ -2119,7 +2034,7 @@ def view_directory(request):
|
||||
'entries' : rows,
|
||||
'sortby' : sortby,
|
||||
'sortdir' : sortdir,
|
||||
'search_re' : request.server.escape(search_re),
|
||||
'search_re' : htmlify(search_re),
|
||||
'dir_pagestart' : None,
|
||||
'sortby_file_href' : request.get_url(params={'sortby': 'file',
|
||||
'sortdir': None},
|
||||
@@ -2254,11 +2169,10 @@ def paging(data, key, pagestart, local_name, pagesize):
|
||||
# Slice
|
||||
return data[key][pagestart:pageend]
|
||||
|
||||
def paging_sws(data, key, pagestart, local_name, pagesize,
|
||||
extra_pages, offset):
|
||||
def paging_sws(data, key, pagestart, local_name, pagesize, offset):
|
||||
"""Implement sliding window-style paging."""
|
||||
# Create the picklist
|
||||
last_requested = pagestart + (extra_pages * pagesize)
|
||||
last_requested = pagestart + (EXTRA_PAGES * pagesize)
|
||||
picklist = data['picklist'] = []
|
||||
has_more = ezt.boolean(0)
|
||||
for i in range(0, len(data[key]), pagesize):
|
||||
@@ -2362,11 +2276,9 @@ def view_log(request):
|
||||
if request.roottype == 'cvs':
|
||||
raise debug.ViewVCException('Unsupported feature: log view on CVS '
|
||||
'directory', '400 Bad Request')
|
||||
mime_type = encoding = None
|
||||
mime_type = None
|
||||
else:
|
||||
mime_type, encoding = calculate_mime_type(request,
|
||||
request.path_parts,
|
||||
request.pathrev)
|
||||
mime_type = calculate_mime_type(request, request.path_parts, request.pathrev)
|
||||
|
||||
options = {}
|
||||
options['svn_show_all_dir_logs'] = 1 ### someday make this optional?
|
||||
@@ -2387,9 +2299,9 @@ def view_log(request):
|
||||
first = last = 0
|
||||
if cfg.options.log_pagesize:
|
||||
log_pagestart = int(request.query_dict.get('log_pagestart', 0))
|
||||
total = cfg.options.log_pagesextra * cfg.options.log_pagesize
|
||||
first = log_pagestart - min(log_pagestart, total)
|
||||
last = log_pagestart + (total + cfg.options.log_pagesize) + 1
|
||||
first = log_pagestart - min(log_pagestart,
|
||||
(EXTRA_PAGES * cfg.options.log_pagesize))
|
||||
last = log_pagestart + ((EXTRA_PAGES + 1) * cfg.options.log_pagesize) + 1
|
||||
show_revs = request.repos.itemlog(request.path_parts, request.pathrev,
|
||||
sortby, first, last - first, options)
|
||||
|
||||
@@ -2409,7 +2321,7 @@ def view_log(request):
|
||||
entry.ago = None
|
||||
if rev.date is not None:
|
||||
entry.ago = html_time(request, rev.date, 1)
|
||||
entry.log = format_log(request, rev.log or '')
|
||||
entry.log = format_log(rev.log or '', cfg)
|
||||
entry.size = rev.size
|
||||
entry.lockinfo = rev.lockinfo
|
||||
entry.branch_point = None
|
||||
@@ -2650,8 +2562,7 @@ def view_log(request):
|
||||
request.get_form(params={'log_pagestart': None})
|
||||
data['log_pagestart'] = int(request.query_dict.get('log_pagestart',0))
|
||||
data['entries'] = paging_sws(data, 'entries', data['log_pagestart'],
|
||||
'rev', cfg.options.log_pagesize,
|
||||
cfg.options.log_pagesextra, first)
|
||||
'rev', cfg.options.log_pagesize, first)
|
||||
|
||||
generate_page(request, "log", data)
|
||||
|
||||
@@ -2668,11 +2579,10 @@ def view_checkout(request):
|
||||
|
||||
# The revision number acts as a strong validator.
|
||||
if not check_freshness(request, None, revision):
|
||||
mime_type, encoding = calculate_mime_type(request, path, rev)
|
||||
mime_type = request.query_dict.get('content-type') \
|
||||
or mime_type \
|
||||
or calculate_mime_type(request, path, rev) \
|
||||
or 'text/plain'
|
||||
server_fp = get_writeready_server_file(request, mime_type, encoding)
|
||||
server_fp = get_writeready_server_file(request, mime_type)
|
||||
copy_stream(fp, server_fp)
|
||||
fp.close()
|
||||
|
||||
@@ -2792,6 +2702,7 @@ def view_doc(request):
|
||||
raise debug.ViewVCException('Static file "%s" not available (%s)'
|
||||
% (document, str(v)), '404 Not Found')
|
||||
|
||||
request.server.addheader('Content-Length', content_length)
|
||||
if document[-3:] == 'png':
|
||||
mime_type = 'image/png'
|
||||
elif document[-3:] == 'jpg':
|
||||
@@ -2802,8 +2713,7 @@ def view_doc(request):
|
||||
mime_type = 'text/css'
|
||||
else: # assume HTML:
|
||||
mime_type = None
|
||||
copy_stream(fp, get_writeready_server_file(request, mime_type,
|
||||
content_length=content_length))
|
||||
copy_stream(fp, get_writeready_server_file(request, mime_type))
|
||||
fp.close()
|
||||
|
||||
def rcsdiff_date_reformat(date_str, cfg):
|
||||
@@ -2856,7 +2766,7 @@ class DiffSource:
|
||||
hr_breakable = self.cfg.options.hr_breakable
|
||||
|
||||
# in the code below, "\x01" will be our stand-in for "&". We don't want
|
||||
# to insert "&" because it would get escaped by sapi.escape(). Similarly,
|
||||
# to insert "&" because it would get escaped by htmlify(). Similarly,
|
||||
# we use "\x02" as a stand-in for "<br>"
|
||||
|
||||
if hr_breakable > 1 and len(text) > hr_breakable:
|
||||
@@ -2866,7 +2776,7 @@ class DiffSource:
|
||||
text = string.replace(text, ' ', ' \x01nbsp;')
|
||||
else:
|
||||
text = string.replace(text, ' ', '\x01nbsp;')
|
||||
text = sapi.escape(text)
|
||||
text = htmlify(text)
|
||||
text = string.replace(text, '\x01', '&')
|
||||
text = string.replace(text, '\x02',
|
||||
'<span style="color:red">\</span><br />')
|
||||
@@ -3247,7 +3157,7 @@ def view_diff(request):
|
||||
else:
|
||||
changes = DiffSource(fp, cfg)
|
||||
else:
|
||||
raw_diff_fp = MarkupPipeWrapper(fp, request.server.escape(headers), None, 1)
|
||||
raw_diff_fp = MarkupPipeWrapper(fp, htmlify(headers), None, 1)
|
||||
|
||||
no_format_params = request.query_dict.copy()
|
||||
no_format_params['diff_format'] = None
|
||||
@@ -3257,7 +3167,7 @@ def view_diff(request):
|
||||
fvi = get_file_view_info(request, path_left, rev1)
|
||||
left = _item(date=make_time_string(log_entry1.date, cfg),
|
||||
author=log_entry1.author,
|
||||
log=format_log(request, log_entry1.log),
|
||||
log=format_log(log_entry1.log, cfg),
|
||||
size=log_entry1.size,
|
||||
ago=ago1,
|
||||
path=path_left,
|
||||
@@ -3273,7 +3183,7 @@ def view_diff(request):
|
||||
fvi = get_file_view_info(request, path_right, rev2)
|
||||
right = _item(date=make_time_string(log_entry2.date, cfg),
|
||||
author=log_entry2.author,
|
||||
log=format_log(request, log_entry2.log),
|
||||
log=format_log(log_entry2.log, cfg),
|
||||
size=log_entry2.size,
|
||||
ago=ago2,
|
||||
path=path_right,
|
||||
@@ -3494,7 +3404,7 @@ def download_tarball(request):
|
||||
|
||||
|
||||
def view_revision(request):
|
||||
if request.roottype != "svn":
|
||||
if request.roottype == "cvs":
|
||||
raise debug.ViewVCException("Revision view not supported for CVS "
|
||||
"repositories at this time.",
|
||||
"400 Bad Request")
|
||||
@@ -3513,29 +3423,9 @@ def view_revision(request):
|
||||
return
|
||||
|
||||
# Fetch the revision information.
|
||||
date, author, msg, revprops, changes = request.repos.revinfo(rev)
|
||||
date, author, msg, changes = request.repos.revinfo(rev)
|
||||
date_str = make_time_string(date, cfg)
|
||||
|
||||
# Fix up the revprops list (rather like get_itemprops()).
|
||||
propnames = revprops.keys()
|
||||
propnames.sort()
|
||||
props = []
|
||||
for name in propnames:
|
||||
value = format_log(request, revprops[name])
|
||||
undisplayable = ezt.boolean(0)
|
||||
# skip non-utf8 property names
|
||||
try:
|
||||
unicode(name, 'utf8')
|
||||
except:
|
||||
continue
|
||||
# note non-utf8 property values
|
||||
try:
|
||||
unicode(value, 'utf8')
|
||||
except:
|
||||
value = None
|
||||
undisplayable = ezt.boolean(1)
|
||||
props.append(_item(name=name, value=value, undisplayable=undisplayable))
|
||||
|
||||
# Sort the changes list by path.
|
||||
def changes_sort_by_path(a, b):
|
||||
return cmp(a.path_parts, b.path_parts)
|
||||
@@ -3650,8 +3540,7 @@ def view_revision(request):
|
||||
'rev' : str(rev),
|
||||
'author' : author,
|
||||
'date' : date_str,
|
||||
'log' : format_log(request, msg),
|
||||
'properties' : props,
|
||||
'log' : format_log(msg, cfg),
|
||||
'ago' : date is not None and html_time(request, date, 1) or None,
|
||||
'changes' : changes,
|
||||
'prev_href' : prev_rev_href,
|
||||
@@ -3664,11 +3553,6 @@ def view_revision(request):
|
||||
'first_changes_href': first_changes_href,
|
||||
'jump_rev_action' : jump_rev_action,
|
||||
'jump_rev_hidden_values' : jump_rev_hidden_values,
|
||||
'revision_href' : request.get_url(view_func=view_revision,
|
||||
where=None,
|
||||
pathtype=None,
|
||||
params={'revision': str(rev)},
|
||||
escape=1),
|
||||
}))
|
||||
if rev == youngest_rev:
|
||||
request.server.addheader("Cache-control", "no-store")
|
||||
@@ -3737,26 +3621,23 @@ def view_queryform(request):
|
||||
limit_changes = \
|
||||
int(request.query_dict.get('limit_changes',
|
||||
request.cfg.options.limit_changes))
|
||||
|
||||
def escaped_query_dict_get(itemname, itemdefault=''):
|
||||
return request.server.escape(request.query_dict.get(itemname, itemdefault))
|
||||
|
||||
|
||||
data = common_template_data(request)
|
||||
data.merge(ezt.TemplateData({
|
||||
'branch' : escaped_query_dict_get('branch', ''),
|
||||
'branch_match' : escaped_query_dict_get('branch_match', 'exact'),
|
||||
'dir' : escaped_query_dict_get('dir', ''),
|
||||
'file' : escaped_query_dict_get('file', ''),
|
||||
'file_match' : escaped_query_dict_get('file_match', 'exact'),
|
||||
'who' : escaped_query_dict_get('who', ''),
|
||||
'who_match' : escaped_query_dict_get('who_match', 'exact'),
|
||||
'comment' : escaped_query_dict_get('comment', ''),
|
||||
'comment_match' : escaped_query_dict_get('comment_match', 'exact'),
|
||||
'querysort' : escaped_query_dict_get('querysort', 'date'),
|
||||
'date' : escaped_query_dict_get('date', 'hours'),
|
||||
'hours' : escaped_query_dict_get('hours', '2'),
|
||||
'mindate' : escaped_query_dict_get('mindate', ''),
|
||||
'maxdate' : escaped_query_dict_get('maxdate', ''),
|
||||
'branch' : request.query_dict.get('branch', ''),
|
||||
'branch_match' : request.query_dict.get('branch_match', 'exact'),
|
||||
'dir' : request.query_dict.get('dir', ''),
|
||||
'file' : request.query_dict.get('file', ''),
|
||||
'file_match' : request.query_dict.get('file_match', 'exact'),
|
||||
'who' : request.query_dict.get('who', ''),
|
||||
'who_match' : request.query_dict.get('who_match', 'exact'),
|
||||
'comment' : request.query_dict.get('comment', ''),
|
||||
'comment_match' : request.query_dict.get('comment_match', 'exact'),
|
||||
'querysort' : request.query_dict.get('querysort', 'date'),
|
||||
'date' : request.query_dict.get('date', 'hours'),
|
||||
'hours' : request.query_dict.get('hours', '2'),
|
||||
'mindate' : request.query_dict.get('mindate', ''),
|
||||
'maxdate' : request.query_dict.get('maxdate', ''),
|
||||
'query_action' : query_action,
|
||||
'query_hidden_values' : query_hidden_values,
|
||||
'limit_changes' : limit_changes,
|
||||
@@ -3820,7 +3701,7 @@ def english_query(request):
|
||||
ret.append('on all branches ')
|
||||
comment = request.query_dict.get('comment', '')
|
||||
if comment:
|
||||
ret.append('with comment <i>%s</i> ' % request.server.escape(comment))
|
||||
ret.append('with comment <i>%s</i> ' % htmlify(comment))
|
||||
if who:
|
||||
ret.append('by <em>%s</em> ' % request.server.escape(who))
|
||||
date = request.query_dict.get('date', 'hours')
|
||||
@@ -3959,7 +3840,7 @@ def build_commit(request, files, max_files, dir_strip, format):
|
||||
diff_href = request.get_url(view_func=view_diff,
|
||||
where=where, pathtype=vclib.FILE,
|
||||
params=diff_href_params, escape=1)
|
||||
mime_type, encoding = calculate_mime_type(request, path_parts, exam_rev)
|
||||
mime_type = calculate_mime_type(request, path_parts, exam_rev)
|
||||
prefer_markup = ezt.boolean(default_view(mime_type, cfg) == view_markup)
|
||||
|
||||
# Update plus/minus line change count.
|
||||
@@ -4008,8 +3889,8 @@ def build_commit(request, files, max_files, dir_strip, format):
|
||||
commit.log = None
|
||||
commit.short_log = None
|
||||
else:
|
||||
commit.log = format_log(request, desc, 0, format != 'rss')
|
||||
commit.short_log = format_log(request, desc, cfg.options.short_log_len,
|
||||
commit.log = format_log(desc, cfg, 0, format != 'rss')
|
||||
commit.short_log = format_log(desc, cfg, cfg.options.short_log_len,
|
||||
format != 'rss')
|
||||
commit.author = request.server.escape(author)
|
||||
commit.rss_date = make_rss_time_string(date, request.cfg)
|
||||
@@ -4027,29 +3908,27 @@ def build_commit(request, files, max_files, dir_strip, format):
|
||||
return commit
|
||||
|
||||
def query_backout(request, commits):
|
||||
server_fp = get_writeready_server_file(request, 'text/plain')
|
||||
if not commits:
|
||||
server_fp.write("""\
|
||||
# No changes were selected by the query.
|
||||
# There is nothing to back out.
|
||||
""")
|
||||
request.server.header('text/plain')
|
||||
if commits:
|
||||
print '# This page can be saved as a shell script and executed.'
|
||||
print '# It should be run at the top of your work area. It will update'
|
||||
print '# your working copy to back out the changes selected by the'
|
||||
print '# query.'
|
||||
print
|
||||
else:
|
||||
print '# No changes were selected by the query.'
|
||||
print '# There is nothing to back out.'
|
||||
return
|
||||
server_fp.write("""\
|
||||
# This page can be saved as a shell script and executed.
|
||||
# It should be run at the top of your work area. It will update
|
||||
# your working copy to back out the changes selected by the
|
||||
# query.
|
||||
""")
|
||||
for commit in commits:
|
||||
for fileinfo in commit.files:
|
||||
if request.roottype == 'cvs':
|
||||
server_fp.write('cvs update -j %s -j %s %s/%s\n'
|
||||
% (fileinfo.rev, prev_rev(fileinfo.rev),
|
||||
fileinfo.dir, fileinfo.file))
|
||||
print 'cvs update -j %s -j %s %s/%s' \
|
||||
% (fileinfo.rev, prev_rev(fileinfo.rev),
|
||||
fileinfo.dir, fileinfo.file)
|
||||
elif request.roottype == 'svn':
|
||||
server_fp.write('svn merge -r %s:%s %s/%s\n'
|
||||
% (fileinfo.rev, prev_rev(fileinfo.rev),
|
||||
fileinfo.dir, fileinfo.file))
|
||||
print 'svn merge -r %s:%s %s/%s' \
|
||||
% (fileinfo.rev, prev_rev(fileinfo.rev),
|
||||
fileinfo.dir, fileinfo.file)
|
||||
|
||||
def view_query(request):
|
||||
if not is_query_supported(request):
|
||||
@@ -4290,26 +4169,11 @@ def list_roots(request):
|
||||
for root in cfg.general.svn_roots.keys():
|
||||
auth = setup_authorizer(cfg, request.username, root)
|
||||
try:
|
||||
repos = vclib.svn.SubversionRepository(root, cfg.general.svn_roots[root],
|
||||
auth, cfg.utilities,
|
||||
cfg.options.svn_config_dir)
|
||||
lastmod = None
|
||||
if cfg.options.show_roots_lastmod:
|
||||
try:
|
||||
repos.open()
|
||||
youngest_rev = repos.youngest
|
||||
date, author, msg, revprops, changes = repos.revinfo(youngest_rev)
|
||||
date_str = make_time_string(date, cfg)
|
||||
ago = html_time(request, date)
|
||||
log = format_log(request, msg)
|
||||
short_log = format_log(request, msg, maxlen=cfg.options.short_log_len)
|
||||
lastmod = _item(ago=ago, author=author, date=date_str, log=log,
|
||||
short_log=short_log, rev=str(youngest_rev))
|
||||
except:
|
||||
lastmod = None
|
||||
vclib.svn.SubversionRepository(root, cfg.general.svn_roots[root], auth,
|
||||
cfg.utilities, cfg.options.svn_config_dir)
|
||||
except vclib.ReposNotFound:
|
||||
continue
|
||||
allroots[root] = [cfg.general.svn_roots[root], 'svn', lastmod]
|
||||
allroots[root] = [cfg.general.svn_roots[root], 'svn']
|
||||
|
||||
# Add the viewable CVS roots
|
||||
for root in cfg.general.cvs_roots.keys():
|
||||
@@ -4319,7 +4183,7 @@ def list_roots(request):
|
||||
cfg.utilities, cfg.options.use_rcsparse)
|
||||
except vclib.ReposNotFound:
|
||||
continue
|
||||
allroots[root] = [cfg.general.cvs_roots[root], 'cvs', None]
|
||||
allroots[root] = [cfg.general.cvs_roots[root], 'cvs']
|
||||
|
||||
return allroots
|
||||
|
||||
@@ -4331,8 +4195,8 @@ def expand_root_parents(cfg):
|
||||
pos = string.rfind(pp, ':')
|
||||
if pos < 0:
|
||||
raise debug.ViewVCException(
|
||||
'The path "%s" in "root_parents" does not include a '
|
||||
'repository type. Expected "cvs" or "svn".' % (pp))
|
||||
"The path '%s' in 'root_parents' does not include a "
|
||||
"repository type." % (pp))
|
||||
|
||||
repo_type = string.strip(pp[pos+1:])
|
||||
pp = os.path.normpath(string.strip(pp[:pos]))
|
||||
@@ -4347,9 +4211,8 @@ def expand_root_parents(cfg):
|
||||
cfg.general.svn_roots.update(roots)
|
||||
else:
|
||||
raise debug.ViewVCException(
|
||||
'The path "%s" in "root_parents" has an unrecognized '
|
||||
'repository type ("%s"). Expected "cvs" or "svn".'
|
||||
% (pp, repo_type))
|
||||
"The path '%s' in 'root_parents' has an unrecognized "
|
||||
"repository type." % (pp))
|
||||
|
||||
def find_root_in_parents(cfg, rootname, roottype):
|
||||
"""Return the rootpath for configured ROOTNAME of ROOTTYPE."""
|
||||
|
@@ -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,70 +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).
|
||||
|
||||
*** Do NOT commit this change. ***
|
||||
|
||||
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:
|
||||
9. 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. Also, drop a copy of the
|
||||
archive files into the root directory of the viewvc.org website
|
||||
(unversioned).
|
||||
|
||||
15. On trunk, update the websites (both the viewvc.org/ and www/ ones)
|
||||
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.
|
||||
|
||||
16. Edit the file 'lib/viewvc.py' again, incrementing the patch number
|
||||
assigned to the __version__ variable. Add a new empty block in
|
||||
the branch's CHANGES file. Commit your changes:
|
||||
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.)
|
||||
|
||||
18. Send to the announce@ list a message explaining all the cool new
|
||||
features, and post similar announcements to other places interested
|
||||
in this sort of stuff, such as Freshmeat (http://www.freshmeat.net).
|
||||
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).
|
||||
|
@@ -94,6 +94,9 @@ form { margin: 0; }
|
||||
.vc_properties {
|
||||
margin: 1em 0;
|
||||
}
|
||||
.vc_properties h2 {
|
||||
font-size: 115%;
|
||||
}
|
||||
|
||||
|
||||
/*** File Content Markup Styles ***/
|
||||
|
@@ -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] [end]</td>
|
||||
<td style="text-align: right;">[if-any help_href]<strong><a href="[help_href]">ViewVC Help</a></strong>[else] [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>
|
||||
|
@@ -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>
|
||||
|
@@ -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>
|
||||
@@ -202,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} {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> </td>
|
||||
<td colspan="5"><strong>Log:</strong><br />
|
||||
<pre class="vc_log">[if-any commits.log][commits.log][else] [end]</pre></td>
|
||||
<pre class="vc_log">[commits.log]</pre></td>
|
||||
</tr>
|
||||
[end]
|
||||
[# ---]
|
||||
|
@@ -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">
|
||||
@@ -43,7 +42,7 @@
|
||||
|
||||
<hr />
|
||||
|
||||
<h2>Changed paths</h2>
|
||||
<p><strong>Changed paths:</strong></p>
|
||||
|
||||
<table cellspacing="1" cellpadding="2">
|
||||
<thead>
|
||||
@@ -78,5 +77,4 @@
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
[include "include/props.ezt"]
|
||||
[include "include/footer.ezt"]
|
||||
|
@@ -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"> [roots.rev]</td>
|
||||
<td style="width:20"> [roots.ago]</td>
|
||||
<td style="width:20"> [roots.author]</td>
|
||||
<td style="width:20"> [roots.short_log]</td>
|
||||
[end]
|
||||
</tr>
|
||||
[end]
|
||||
</tbody>
|
||||
|
@@ -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><pre>[format "xml"][format "html"][commits.log][end][end]</pre></description>
|
||||
<description><pre>[format "xml"][commits.log][end]</pre></description>
|
||||
</item>[end]
|
||||
</channel>
|
||||
</rss>
|
||||
|
@@ -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),
|
||||
|
Reference in New Issue
Block a user