mirror of
https://github.com/vitalif/viewvc-4intranet
synced 2019-04-16 04:14:59 +03:00
Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
0045fedeb6 | ||
![]() |
a390adef78 |
48
CHANGES
48
CHANGES
@@ -1,51 +1,3 @@
|
||||
Version 1.1.11 (released 17-May-2011)
|
||||
|
||||
* security fix: remove user-reachable override of cvsdb row limit
|
||||
* fix broken standalone.py -c and -d options handling
|
||||
* add --help option to standalone.py
|
||||
* fix stack trace when asked to checkout a directory (issue #478)
|
||||
* improve memory usage and speed of revision log markup (issue #477)
|
||||
* fix broken annotation view in CVS keyword-bearing files (issue #479)
|
||||
* warn users when query results are incomplete (issue #443)
|
||||
* avoid parsing errors on RCS newphrases in the admin section (issue #483)
|
||||
* make rlog parsing code more robust in certain error cases (issue #444)
|
||||
|
||||
Version 1.1.10 (released 15-Mar-2011)
|
||||
|
||||
* fix stack trace in Subversion revision info logic (issue #475, issue #476)
|
||||
|
||||
Version 1.1.9 (released 18-Feb-2011)
|
||||
|
||||
* vcauth universal access determinations (issue #425)
|
||||
* rework svn revision info cache for performance
|
||||
* make revision log "extra pages" count configurable
|
||||
* fix Subversion 1.4.x revision log compatibility code regression
|
||||
* display sanitized error when authzfile is malformed
|
||||
* 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)
|
||||
|
31
INSTALL
31
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)
|
||||
@@ -251,33 +251,6 @@ APACHE CONFIGURATION
|
||||
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-2010 The ViewCVS Group. All rights
|
||||
reserved.</strong></p>
|
||||
|
||||
<p>By using ViewVC, you agree to the terms and conditions set forth
|
||||
@@ -61,7 +61,6 @@
|
||||
<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()
|
||||
|
||||
|
1311
bin/standalone.py
1311
bin/standalone.py
File diff suppressed because it is too large
Load Diff
@@ -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,6 +1,6 @@
|
||||
# -*-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
|
||||
@@ -16,7 +16,7 @@
|
||||
#
|
||||
# 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.
|
||||
# is a walid wsgi entry point.
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
@@ -38,8 +38,5 @@ 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)
|
||||
query.main(server, cfg, "viewvc.wsgi")
|
||||
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()
|
@@ -16,7 +16,7 @@
|
||||
#
|
||||
# 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.
|
||||
# is a walid wsgi entry point.
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
|
@@ -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
|
||||
@@ -797,14 +749,6 @@
|
||||
## row_limit: Maximum number of rows returned by a given normal query
|
||||
## to the database.
|
||||
##
|
||||
## NOTE: This limits the amount of data provided to ViewVC by the
|
||||
## database. It is from this already-reduced data set that ViewVC
|
||||
## builds the query response it presents to the user, which may or may
|
||||
## not include still more limiting via the query form's 'limit'
|
||||
## parameter. In other words, there is no value which the user can use
|
||||
## in the query form's 'limit' parameter which will cause more data to
|
||||
## be returned by the database for ViewVC to process.
|
||||
##
|
||||
#row_limit = 1000
|
||||
|
||||
## rss_row_limit: Maximum number of rows returned by a given query to
|
||||
@@ -812,9 +756,6 @@
|
||||
## that RSS readers tend to poll regularly for new data, you might want
|
||||
## to keep this set to a conservative number.)
|
||||
##
|
||||
## See also the `NOTE' for the 'row_limit' option, which applies here
|
||||
## as well.
|
||||
##
|
||||
#rss_row_limit = 100
|
||||
|
||||
## check_database_for_root: Check if the repository is found in the
|
||||
@@ -1056,30 +997,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 =
|
||||
|
||||
##---------------------------------------------------------------------------
|
||||
|
@@ -1821,14 +1821,6 @@ td {
|
||||
<td>Indicates how query results are being sorted. Possible values:
|
||||
<tt>date</tt>, <tt>author</tt>, and <tt>file</tt>.</td>
|
||||
</tr>
|
||||
<tr class="varlevel1">
|
||||
<td class="varname">row_limit_reached</td>
|
||||
<td>Boolean</td>
|
||||
<td>Indicates whether the internal database row limit threshold (set
|
||||
via the <code>cvsdb.row_limit</code>
|
||||
and <code>cvsdb.rss_row_limit</code> configuration options) was
|
||||
reached by the query.</td>
|
||||
</tr>
|
||||
<tr class="varlevel1">
|
||||
<td class="varname">show_branch</td>
|
||||
<td>Boolean</td>
|
||||
@@ -2152,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>
|
||||
@@ -2191,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>
|
||||
|
||||
|
@@ -1192,6 +1192,13 @@ th.caption {
|
||||
commands to back out changes instead showing a normal query result
|
||||
page</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>limit=<var>LIMIT</var></code></td>
|
||||
<td>optional</td>
|
||||
<td>maximum number of file-revisions to process during a
|
||||
query. Default is value of <code>row_limit</code> configuration
|
||||
option</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>limit_changes=<var>LIMIT_CHANGES</var></code></td>
|
||||
<td>optional</td>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2011 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2010 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
@@ -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)
|
||||
|
||||
@@ -286,7 +280,8 @@ class Config:
|
||||
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
|
||||
@@ -327,7 +322,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 +415,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 +428,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 +453,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
|
||||
|
||||
|
67
lib/cvsdb.py
67
lib/cvsdb.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
|
||||
@@ -38,12 +38,13 @@ error = "cvsdb error"
|
||||
## complient database interface
|
||||
|
||||
class CheckinDatabase:
|
||||
def __init__(self, host, port, user, passwd, database):
|
||||
def __init__(self, host, port, user, passwd, database, row_limit):
|
||||
self._host = host
|
||||
self._port = port
|
||||
self._user = user
|
||||
self._passwd = passwd
|
||||
self._database = database
|
||||
self._row_limit = row_limit
|
||||
self._version = None
|
||||
|
||||
## database lookup caches
|
||||
@@ -168,9 +169,6 @@ class CheckinDatabase:
|
||||
|
||||
return list
|
||||
|
||||
def GetCommitsTable(self):
|
||||
return self._version >= 1 and 'commits' or 'checkins'
|
||||
|
||||
def GetTableList(self):
|
||||
sql = "SHOW TABLES"
|
||||
cursor = self.db.cursor()
|
||||
@@ -311,7 +309,8 @@ class CheckinDatabase:
|
||||
minus_count = commit.GetMinusCount() or '0'
|
||||
description_id = self.GetDescriptionID(commit.GetDescription())
|
||||
|
||||
sql = "REPLACE INTO %s" % (self.GetCommitsTable())
|
||||
commits_table = self._version >= 1 and 'commits' or 'checkins'
|
||||
sql = "REPLACE INTO %s" % (commits_table)
|
||||
sql = sql + \
|
||||
" (type,ci_when,whoid,repositoryid,dirid,fileid,revision,"\
|
||||
" stickytag,branchid,addedlines,removedlines,descid)"\
|
||||
@@ -364,8 +363,8 @@ class CheckinDatabase:
|
||||
|
||||
return "(%s)" % (string.join(sqlList, " OR "))
|
||||
|
||||
def CreateSQLQueryString(self, query, detect_leftover=0):
|
||||
commits_table = self.GetCommitsTable()
|
||||
def CreateSQLQueryString(self, query):
|
||||
commits_table = self._version >= 1 and 'commits' or 'checkins'
|
||||
tableList = [(commits_table, None)]
|
||||
condList = []
|
||||
|
||||
@@ -445,14 +444,13 @@ class CheckinDatabase:
|
||||
conditions = string.join(joinConds + condList, " AND ")
|
||||
conditions = conditions and "WHERE %s" % conditions
|
||||
|
||||
## apply the query's row limit, if any (so we avoid really
|
||||
## slamming a server with a large database)
|
||||
## limit the number of rows requested or we could really slam
|
||||
## a server with a large database
|
||||
limit = ""
|
||||
if query.limit:
|
||||
if detect_leftover:
|
||||
limit = "LIMIT %s" % (str(query.limit + 1))
|
||||
else:
|
||||
limit = "LIMIT %s" % (str(query.limit))
|
||||
limit = "LIMIT %s" % (str(query.limit))
|
||||
elif self._row_limit:
|
||||
limit = "LIMIT %s" % (str(self._row_limit))
|
||||
|
||||
sql = "SELECT %s.* FROM %s %s %s %s" \
|
||||
% (commits_table, tables, conditions, order_by, limit)
|
||||
@@ -460,20 +458,14 @@ class CheckinDatabase:
|
||||
return sql
|
||||
|
||||
def RunQuery(self, query):
|
||||
sql = self.CreateSQLQueryString(query, 1)
|
||||
sql = self.CreateSQLQueryString(query)
|
||||
cursor = self.db.cursor()
|
||||
cursor.execute(sql)
|
||||
query.SetExecuted()
|
||||
row_count = 0
|
||||
|
||||
while 1:
|
||||
row = cursor.fetchone()
|
||||
if not row:
|
||||
break
|
||||
row_count = row_count + 1
|
||||
if query.limit and (row_count > query.limit):
|
||||
query.SetLimitReached()
|
||||
break
|
||||
|
||||
(dbType, dbCI_When, dbAuthorID, dbRepositoryID, dbDirID,
|
||||
dbFileID, dbRevision, dbStickyTag, dbBranchID, dbAddedLines,
|
||||
@@ -512,12 +504,13 @@ class CheckinDatabase:
|
||||
if file_id == None:
|
||||
return None
|
||||
|
||||
commits_table = self._version >= 1 and 'commits' or 'checkins'
|
||||
sql = "SELECT * FROM %s WHERE "\
|
||||
" repositoryid=%%s "\
|
||||
" AND dirid=%%s"\
|
||||
" AND fileid=%%s"\
|
||||
" AND revision=%%s"\
|
||||
% (self.GetCommitsTable())
|
||||
% (commits_table)
|
||||
sql_args = (repository_id, dir_id, file_id, commit.GetRevision())
|
||||
|
||||
cursor = self.db.cursor()
|
||||
@@ -534,9 +527,10 @@ class CheckinDatabase:
|
||||
def sql_delete(self, table, key, value, keep_fkey = None):
|
||||
sql = "DELETE FROM %s WHERE %s=%%s" % (table, key)
|
||||
sql_args = (value, )
|
||||
commits_table = self._version >= 1 and 'commits' or 'checkins'
|
||||
if keep_fkey:
|
||||
sql += " AND %s NOT IN (SELECT %s FROM %s WHERE %s = %%s)" \
|
||||
% (key, keep_fkey, self.GetCommitsTable(), keep_fkey)
|
||||
% (key, keep_fkey, commits_table, keep_fkey)
|
||||
sql_args = (value, value)
|
||||
cursor = self.db.cursor()
|
||||
cursor.execute(sql, sql_args)
|
||||
@@ -775,9 +769,8 @@ class QueryEntry:
|
||||
self.data = data
|
||||
self.match = match
|
||||
|
||||
## CheckinDatabaseQuery is an object which contains the search
|
||||
## parameters for a query to the Checkin Database and -- after the
|
||||
## query is executed -- the data returned by the query.
|
||||
## CheckinDatabaseQueryData is a object which contains the search parameters
|
||||
## for a query to the CheckinDatabase
|
||||
class CheckinDatabaseQuery:
|
||||
def __init__(self):
|
||||
## sorting
|
||||
@@ -797,8 +790,7 @@ class CheckinDatabaseQuery:
|
||||
|
||||
## limit on number of rows to return
|
||||
self.limit = None
|
||||
self.limit_reached = 0
|
||||
|
||||
|
||||
## list of commits -- filled in by CVS query
|
||||
self.commit_list = []
|
||||
|
||||
@@ -806,9 +798,6 @@ class CheckinDatabaseQuery:
|
||||
## are added
|
||||
self.commit_cb = None
|
||||
|
||||
## has this query been run?
|
||||
self.executed = 0
|
||||
|
||||
def SetRepository(self, repository, match = "exact"):
|
||||
self.repository_list.append(QueryEntry(repository, match))
|
||||
|
||||
@@ -854,20 +843,6 @@ class CheckinDatabaseQuery:
|
||||
def AddCommit(self, commit):
|
||||
self.commit_list.append(commit)
|
||||
|
||||
def SetExecuted(self):
|
||||
self.executed = 1
|
||||
|
||||
def SetLimitReached(self):
|
||||
self.limit_reached = 1
|
||||
|
||||
def GetLimitReached(self):
|
||||
assert self.executed
|
||||
return self.limit_reached
|
||||
|
||||
def GetCommitList(self):
|
||||
assert self.executed
|
||||
return self.commit_list
|
||||
|
||||
|
||||
##
|
||||
## entrypoints
|
||||
@@ -886,7 +861,7 @@ def ConnectDatabase(cfg, readonly=0):
|
||||
user = cfg.cvsdb.user
|
||||
passwd = cfg.cvsdb.passwd
|
||||
db = CheckinDatabase(cfg.cvsdb.host, cfg.cvsdb.port, user, passwd,
|
||||
cfg.cvsdb.database_name)
|
||||
cfg.cvsdb.database_name, cfg.cvsdb.row_limit)
|
||||
db.Connect()
|
||||
return db
|
||||
|
||||
|
46
lib/query.py
46
lib/query.py
@@ -217,9 +217,8 @@ def decode_command(cmd):
|
||||
else:
|
||||
return "exact"
|
||||
|
||||
def form_to_cvsdb_query(cfg, form_data):
|
||||
def form_to_cvsdb_query(form_data):
|
||||
query = cvsdb.CreateCheckinQuery()
|
||||
query.SetLimit(cfg.cvsdb.row_limit)
|
||||
|
||||
if form_data.repository:
|
||||
for cmd, str in listparse_string(form_data.repository):
|
||||
@@ -313,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()
|
||||
@@ -347,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)
|
||||
@@ -378,15 +380,12 @@ def build_commit(server, cfg, desc, files, cvsroots, viewvc_link):
|
||||
return ob
|
||||
|
||||
def run_query(server, cfg, form_data, viewvc_link):
|
||||
query = form_to_cvsdb_query(cfg, form_data)
|
||||
query = form_to_cvsdb_query(form_data)
|
||||
db = cvsdb.ConnectDatabaseReadOnly(cfg)
|
||||
db.RunQuery(query)
|
||||
|
||||
commit_list = query.GetCommitList()
|
||||
if not commit_list:
|
||||
return [ ], 0
|
||||
|
||||
row_limit_reached = query.GetLimitReached()
|
||||
if not query.commit_list:
|
||||
return [ ]
|
||||
|
||||
commits = [ ]
|
||||
files = [ ]
|
||||
@@ -397,8 +396,8 @@ def run_query(server, cfg, form_data, viewvc_link):
|
||||
for key, value in rootitems:
|
||||
cvsroots[cvsdb.CleanRepository(value)] = key
|
||||
|
||||
current_desc = commit_list[0].GetDescription()
|
||||
for commit in commit_list:
|
||||
current_desc = query.commit_list[0].GetDescription()
|
||||
for commit in query.commit_list:
|
||||
desc = commit.GetDescription()
|
||||
if current_desc == desc:
|
||||
files.append(commit)
|
||||
@@ -421,7 +420,7 @@ def run_query(server, cfg, form_data, viewvc_link):
|
||||
return len(commit.files) > 0
|
||||
commits = filter(_only_with_files, commits)
|
||||
|
||||
return commits, row_limit_reached
|
||||
return commits
|
||||
|
||||
def main(server, cfg, viewvc_link):
|
||||
try:
|
||||
@@ -430,18 +429,12 @@ def main(server, cfg, viewvc_link):
|
||||
form_data = FormData(form)
|
||||
|
||||
if form_data.valid:
|
||||
commits, row_limit_reached = run_query(server, cfg,
|
||||
form_data, viewvc_link)
|
||||
commits = run_query(server, cfg, form_data, viewvc_link)
|
||||
query = None
|
||||
else:
|
||||
commits = [ ]
|
||||
row_limit_reached = 0
|
||||
query = 'skipped'
|
||||
|
||||
docroot = cfg.options.docroot
|
||||
if docroot is None and viewvc_link:
|
||||
docroot = viewvc_link + '/' + viewvc.docroot_magic_path
|
||||
|
||||
data = ezt.TemplateData({
|
||||
'cfg' : cfg,
|
||||
'address' : cfg.general.address,
|
||||
@@ -451,11 +444,14 @@ def main(server, cfg, viewvc_link):
|
||||
'directory' : server.escape(form_data.directory),
|
||||
'file' : server.escape(form_data.file),
|
||||
'who' : server.escape(form_data.who),
|
||||
'docroot' : docroot,
|
||||
'docroot' : cfg.options.docroot is None \
|
||||
and viewvc_link + '/' + viewvc.docroot_magic_path \
|
||||
or cfg.options.docroot,
|
||||
|
||||
'sortby' : form_data.sortby,
|
||||
'date' : form_data.date,
|
||||
|
||||
'query' : query,
|
||||
'row_limit_reached' : ezt.boolean(row_limit_reached),
|
||||
'commits' : commits,
|
||||
'num_commits' : len(commits),
|
||||
'rss_href' : None,
|
||||
|
@@ -186,7 +186,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)
|
||||
@@ -244,7 +244,7 @@ class WsgiServer(Server):
|
||||
|
||||
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,
|
||||
return cgi.FieldStorage(self._environ["wsgi.input"], self._headers,
|
||||
outerboundary, self._environ, keep_blank_values,
|
||||
strict_parsing)
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 2006-2010 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 2006-2008 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
@@ -29,15 +29,7 @@ class GenericViewVCAuthorizer:
|
||||
def check_root_access(self, rootname):
|
||||
"""Return 1 iff the associated username is permitted to read ROOTNAME."""
|
||||
pass
|
||||
|
||||
def check_universal_access(self, rootname):
|
||||
"""Return 1 if the associated username is permitted to read every
|
||||
path in the repository at every revision, 0 if the associated
|
||||
username is prohibited from reading any path in the repository, or
|
||||
None if no such determination can be made (perhaps because the
|
||||
cost of making it is too great)."""
|
||||
pass
|
||||
|
||||
|
||||
def check_path_access(self, rootname, path_parts, pathtype, rev=None):
|
||||
"""Return 1 iff the associated username is permitted to read
|
||||
revision REV of the path PATH_PARTS (of type PATHTYPE) in
|
||||
@@ -52,9 +44,6 @@ class ViewVCAuthorizer(GenericViewVCAuthorizer):
|
||||
"""The uber-permissive authorizer."""
|
||||
def check_root_access(self, rootname):
|
||||
return 1
|
||||
|
||||
def check_universal_access(self, rootname):
|
||||
return 1
|
||||
|
||||
def check_path_access(self, rootname, path_parts, pathtype, rev=None):
|
||||
return 1
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 2006-2010 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 2006-2008 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
@@ -23,14 +23,7 @@ class ViewVCAuthorizer(vcauth.GenericViewVCAuthorizer):
|
||||
|
||||
def check_root_access(self, rootname):
|
||||
return 1
|
||||
|
||||
def check_universal_access(self, rootname):
|
||||
# If there aren't any forbidden paths, we can grant universal read
|
||||
# access. Otherwise, we make no claim.
|
||||
if not self.forbidden:
|
||||
return 1
|
||||
return None
|
||||
|
||||
|
||||
def check_path_access(self, rootname, path_parts, pathtype, rev=None):
|
||||
# No path? No problem.
|
||||
if not path_parts:
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 2008-2010 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 2008 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
@@ -46,13 +46,6 @@ class ViewVCAuthorizer(vcauth.GenericViewVCAuthorizer):
|
||||
def check_root_access(self, rootname):
|
||||
return self._check_root_path_access(rootname)
|
||||
|
||||
def check_universal_access(self, rootname):
|
||||
# If there aren't any forbidden regexps, we can grant universal
|
||||
# read access. Otherwise, we make no claim.
|
||||
if not self.forbidden:
|
||||
return 1
|
||||
return None
|
||||
|
||||
def check_path_access(self, rootname, path_parts, pathtype, rev=None):
|
||||
root_path = rootname
|
||||
if path_parts:
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
@@ -76,7 +76,7 @@ class Repository:
|
||||
"""
|
||||
pass
|
||||
|
||||
def openfile(self, path_parts, rev, options):
|
||||
def openfile(self, path_parts, rev):
|
||||
"""Open a file object to read file contents at a given path and revision.
|
||||
|
||||
The return value is a 2-tuple of containg the file object and revision
|
||||
@@ -86,8 +86,6 @@ class Repository:
|
||||
of the repository. e.g. ["subdir1", "subdir2", "filename"]
|
||||
|
||||
rev is the revision of the file to check out
|
||||
|
||||
options is a dictionary of implementation specific options
|
||||
"""
|
||||
|
||||
def listdir(self, path_parts, rev, options):
|
||||
@@ -182,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.
|
||||
|
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -167,14 +162,7 @@ class BinCVSRepository(BaseCVSRepository):
|
||||
return revs[-1]
|
||||
return None
|
||||
|
||||
def openfile(self, path_parts, rev, options):
|
||||
"""see vclib.Repository.openfile docstring
|
||||
|
||||
Option values recognized by this implementation:
|
||||
|
||||
cvs_oldkeywords
|
||||
boolean. true to use the original keyword substitution values.
|
||||
"""
|
||||
def openfile(self, path_parts, rev):
|
||||
if self.itemtype(path_parts, rev) != vclib.FILE: # does auth-check
|
||||
raise vclib.Error("Path '%s' is not a file."
|
||||
% (string.join(path_parts, "/")))
|
||||
@@ -182,14 +170,12 @@ class BinCVSRepository(BaseCVSRepository):
|
||||
rev_flag = '-p'
|
||||
else:
|
||||
rev_flag = '-p' + rev
|
||||
if options.get('cvs_oldkeywords', 0):
|
||||
kv_flag = '-ko'
|
||||
else:
|
||||
kv_flag = '-kkv'
|
||||
full_name = self.rcsfile(path_parts, root=1, v=0)
|
||||
|
||||
used_rlog = 0
|
||||
tip_rev = None # used only if we have to fallback to using rlog
|
||||
fp = self.rcs_popen('co', (kv_flag, rev_flag, full_name), 'rb')
|
||||
|
||||
fp = self.rcs_popen('co', (rev_flag, full_name), 'rb')
|
||||
try:
|
||||
filename, revision = _parse_co_header(fp)
|
||||
except COMissingRevision:
|
||||
@@ -1036,16 +1022,16 @@ def _get_logs(repos, dir_path_parts, entries, view_tag, get_dirs):
|
||||
file.errors.append("rlog error: %s" % msg)
|
||||
continue
|
||||
|
||||
tag = None
|
||||
if view_tag == 'MAIN' or view_tag == 'HEAD':
|
||||
tag = Tag(None, default_branch)
|
||||
elif taginfo.has_key(view_tag):
|
||||
tag = Tag(None, taginfo[view_tag])
|
||||
elif view_tag and (eof != _EOF_FILE):
|
||||
# the tag wasn't found, so skip this file (unless we already
|
||||
# know there's nothing left of it to read)
|
||||
elif view_tag:
|
||||
# the tag wasn't found, so skip this file
|
||||
_skip_file(rlog)
|
||||
eof = _EOF_FILE
|
||||
eof = 1
|
||||
else:
|
||||
tag = None
|
||||
|
||||
# we don't care about the specific values -- just the keys and whether
|
||||
# the values point to branches or revisions. this the fastest way to
|
||||
|
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2011 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
|
||||
|
@@ -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
|
||||
@@ -127,9 +127,9 @@ class CCVSRepository(BaseCVSRepository):
|
||||
% (string.join(path_parts2, "/")))
|
||||
|
||||
temp1 = tempfile.mktemp()
|
||||
open(temp1, 'wb').write(self.openfile(path_parts1, rev1, {})[0].getvalue())
|
||||
open(temp1, 'wb').write(self.openfile(path_parts1, rev1)[0].getvalue())
|
||||
temp2 = tempfile.mktemp()
|
||||
open(temp2, 'wb').write(self.openfile(path_parts2, rev2, {})[0].getvalue())
|
||||
open(temp2, 'wb').write(self.openfile(path_parts2, rev2)[0].getvalue())
|
||||
|
||||
r1 = self.itemlog(path_parts1, rev1, vclib.SORTBY_DEFAULT, 0, 0, {})[-1]
|
||||
r2 = self.itemlog(path_parts2, rev2, vclib.SORTBY_DEFAULT, 0, 0, {})[-1]
|
||||
@@ -152,7 +152,7 @@ class CCVSRepository(BaseCVSRepository):
|
||||
def revinfo(self, rev):
|
||||
raise vclib.UnsupportedFeature
|
||||
|
||||
def openfile(self, path_parts, rev, options):
|
||||
def openfile(self, path_parts, rev=None):
|
||||
if self.itemtype(path_parts, rev) != vclib.FILE: # does auth-check
|
||||
raise vclib.Error("Path '%s' is not a file."
|
||||
% (string.join(path_parts, "/")))
|
||||
|
@@ -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
|
||||
@@ -194,8 +194,7 @@ class _Parser:
|
||||
else:
|
||||
# Chew up "newphrase"
|
||||
# warn("Unexpected RCS token: $token\n")
|
||||
while self.ts.get() != ';':
|
||||
pass
|
||||
pass
|
||||
else:
|
||||
if f is None:
|
||||
self.ts.unget(token)
|
||||
|
@@ -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
|
||||
@@ -241,7 +211,7 @@ class RemoteSubversionRepository(vclib.Repository):
|
||||
raise vclib.ItemNotFound(path_parts)
|
||||
return pathtype
|
||||
|
||||
def openfile(self, path_parts, rev, options):
|
||||
def openfile(self, path_parts, rev):
|
||||
path = self._getpath(path_parts)
|
||||
if self.itemtype(path_parts, rev) != vclib.FILE: # does auth-check
|
||||
raise vclib.Error("Path '%s' is not a file." % path)
|
||||
@@ -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-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
|
||||
@@ -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)
|
||||
@@ -287,7 +319,7 @@ class BlameSource:
|
||||
self.first_rev = first_rev
|
||||
self.blame_data = []
|
||||
|
||||
ctx = client.svn_client_create_context()
|
||||
ctx = client.ctx_t()
|
||||
core.svn_config_ensure(config_dir)
|
||||
ctx.config = core.svn_config_get_config(config_dir)
|
||||
ctx.auth_baton = core.svn_auth_open([])
|
||||
@@ -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
|
||||
|
||||
@@ -393,14 +421,19 @@ class LocalSubversionRepository(vclib.Repository):
|
||||
def itemtype(self, path_parts, rev):
|
||||
rev = self._getrev(rev)
|
||||
basepath = self._getpath(path_parts)
|
||||
pathtype = self._gettype(basepath, rev)
|
||||
if pathtype is None:
|
||||
kind = fs.check_path(self._getroot(rev), basepath)
|
||||
pathtype = None
|
||||
if kind == core.svn_node_dir:
|
||||
pathtype = vclib.DIR
|
||||
elif kind == core.svn_node_file:
|
||||
pathtype = vclib.FILE
|
||||
else:
|
||||
raise vclib.ItemNotFound(path_parts)
|
||||
if not vclib.check_path_access(self, path_parts, pathtype, rev):
|
||||
raise vclib.ItemNotFound(path_parts)
|
||||
return pathtype
|
||||
|
||||
def openfile(self, path_parts, rev, options):
|
||||
def openfile(self, path_parts, rev):
|
||||
path = self._getpath(path_parts)
|
||||
if self.itemtype(path_parts, rev) != vclib.FILE: # does auth-check
|
||||
raise vclib.Error("Path '%s' is not a file." % path)
|
||||
@@ -439,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
|
||||
@@ -488,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:
|
||||
@@ -528,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)
|
||||
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)
|
||||
@@ -630,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
|
||||
@@ -643,108 +656,24 @@ class LocalSubversionRepository(vclib.Repository):
|
||||
found_readable = 1
|
||||
else:
|
||||
found_unreadable = 1
|
||||
return found_readable, found_unreadable, changedpaths.values()
|
||||
|
||||
def _get_change_copyinfo(fsroot, path, change):
|
||||
if hasattr(change, 'copyfrom_known') and change.copyfrom_known:
|
||||
copyfrom_path = change.copyfrom_path
|
||||
copyfrom_rev = change.copyfrom_rev
|
||||
else:
|
||||
copyfrom_rev, copyfrom_path = fs.copied_from(fsroot, path)
|
||||
return copyfrom_path, copyfrom_rev
|
||||
|
||||
def _simple_auth_check(fsroot):
|
||||
"""Return a 2-tuple: found_readable, found_unreadable."""
|
||||
found_unreadable = found_readable = 0
|
||||
if hasattr(fs, 'paths_changed2'):
|
||||
changes = fs.paths_changed2(fsroot)
|
||||
else:
|
||||
changes = fs.paths_changed(fsroot)
|
||||
paths = changes.keys()
|
||||
for path in paths:
|
||||
change = changes[path]
|
||||
pathtype = None
|
||||
if hasattr(change, 'node_kind'):
|
||||
if change.node_kind == core.svn_node_file:
|
||||
pathtype = vclib.FILE
|
||||
elif change.node_kind == core.svn_node_dir:
|
||||
pathtype = vclib.DIR
|
||||
parts = _path_parts(path)
|
||||
if pathtype is None:
|
||||
# Figure out the pathtype so we can query the authz subsystem.
|
||||
if change.change_kind == fs.path_change_delete:
|
||||
# Deletions are annoying, because they might be underneath
|
||||
# copies (make their previous location non-trivial).
|
||||
prev_parts = parts
|
||||
prev_rev = rev - 1
|
||||
parent_parts = parts[:-1]
|
||||
while parent_parts:
|
||||
parent_path = '/' + self._getpath(parent_parts)
|
||||
parent_change = changes.get(parent_path)
|
||||
if not (parent_change and \
|
||||
(parent_change.change_kind == fs.path_change_add or
|
||||
parent_change.change_kind == fs.path_change_replace)):
|
||||
del(parent_parts[-1])
|
||||
continue
|
||||
copyfrom_path, copyfrom_rev = \
|
||||
_get_change_copyinfo(fsroot, parent_path, parent_change)
|
||||
if copyfrom_path:
|
||||
prev_rev = copyfrom_rev
|
||||
prev_parts = _path_parts(copyfrom_path) + \
|
||||
parts[len(parent_parts):]
|
||||
break
|
||||
del(parent_parts[-1])
|
||||
pathtype = self._gettype(self._getpath(prev_parts), prev_rev)
|
||||
else:
|
||||
pathtype = self._gettype(self._getpath(parts), rev)
|
||||
if vclib.check_path_access(self, parts, pathtype, rev):
|
||||
found_readable = 1
|
||||
copyfrom_path, copyfrom_rev = \
|
||||
_get_change_copyinfo(fsroot, path, change)
|
||||
if copyfrom_path and copyfrom_path != path:
|
||||
parts = _path_parts(copyfrom_path)
|
||||
if not vclib.check_path_access(self, parts, pathtype,
|
||||
copyfrom_rev):
|
||||
found_unreadable = 1
|
||||
else:
|
||||
found_unreadable = 1
|
||||
if found_readable and found_unreadable:
|
||||
break
|
||||
return found_readable, found_unreadable
|
||||
|
||||
def _revinfo_helper(rev, include_changed_paths):
|
||||
# Get the revision property info. (Would use
|
||||
# editor.get_root_props(), but something is broken there...)
|
||||
revprops = fs.revision_proplist(self.fs_ptr, rev)
|
||||
msg, author, date, revprops = _split_revprops(revprops)
|
||||
|
||||
# Optimization: If our caller doesn't care about the changed
|
||||
# paths, and we don't need them to do authz determinations, let's
|
||||
# get outta here.
|
||||
if self.auth is None and not include_changed_paths:
|
||||
return date, author, msg, revprops, None
|
||||
|
||||
# If we get here, then we either need the changed paths because we
|
||||
# were asked for them, or we need them to do authorization checks.
|
||||
#
|
||||
# If we only need them for authorization checks, though, we
|
||||
# won't bother generating fully populated ChangedPath items (the
|
||||
# cost is too great).
|
||||
fsroot = self._getroot(rev)
|
||||
if include_changed_paths:
|
||||
found_readable, found_unreadable, changedpaths = \
|
||||
_get_changed_paths(fsroot)
|
||||
else:
|
||||
changedpaths = None
|
||||
found_readable, found_unreadable = _simple_auth_check(fsroot)
|
||||
# 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
|
||||
@@ -752,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, '/')
|
||||
@@ -823,20 +742,7 @@ class LocalSubversionRepository(vclib.Repository):
|
||||
r = self._fsroots[rev] = fs.revision_root(self.fs_ptr, rev)
|
||||
return r
|
||||
|
||||
def _gettype(self, path, rev):
|
||||
# Similar to itemtype(), but without the authz check. Returns
|
||||
# None for missing paths.
|
||||
try:
|
||||
kind = fs.check_path(self._getroot(rev), path)
|
||||
except:
|
||||
return None
|
||||
if kind == core.svn_node_dir:
|
||||
return vclib.DIR
|
||||
if kind == core.svn_node_file:
|
||||
return vclib.FILE
|
||||
return None
|
||||
|
||||
##--- custom ---##
|
||||
##--- custom --##
|
||||
|
||||
def get_youngest_revision(self):
|
||||
return self.youngest
|
||||
|
387
lib/viewvc.py
387
lib/viewvc.py
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2011 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2010 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
@@ -14,7 +14,7 @@
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
__version__ = '1.1.11'
|
||||
__version__ = '1.1.6'
|
||||
|
||||
# this comes from our library; measure the startup time
|
||||
import debug
|
||||
@@ -79,6 +79,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 +273,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.
|
||||
@@ -703,6 +706,7 @@ _legal_params = {
|
||||
'mindate' : _re_validate_datetime,
|
||||
'maxdate' : _re_validate_datetime,
|
||||
'format' : _re_validate_alpha,
|
||||
'limit' : _re_validate_number,
|
||||
|
||||
# for redirect_pathrev
|
||||
'orig_path' : None,
|
||||
@@ -892,37 +896,22 @@ 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):
|
||||
@@ -1096,10 +1085,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.
|
||||
|
||||
@@ -1130,7 +1117,7 @@ class ViewVCHtmlFormatter:
|
||||
linkified email address, with no more than MAXLEN characters
|
||||
in the non-HTML-tag bits. If MAXLEN is 0, there is no maximum.
|
||||
- the number of non-HTML-tag characters returned.
|
||||
"""
|
||||
"""
|
||||
s = mobj.group(0)
|
||||
trunc_s = maxlen and s[:maxlen] or s
|
||||
return '<a href="mailto:%s">%s</a>' % (urllib.quote(s),
|
||||
@@ -1165,25 +1152,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
|
||||
@@ -1231,69 +1199,54 @@ class ViewVCHtmlFormatter:
|
||||
|
||||
def _tokenize_text(self, s):
|
||||
tokens = []
|
||||
# We could just have a "while s:" here instead of "for line: while
|
||||
# line:", but for really large log messages with heavy
|
||||
# tokenization, the cost in both performance and memory
|
||||
# consumption of the approach taken was atrocious.
|
||||
for line in string.split(string.replace(s, '\r\n', '\n'), '\n'):
|
||||
line = line + '\n'
|
||||
while line:
|
||||
best_match = best_conv = best_userdata = None
|
||||
for test in self._formatters:
|
||||
match = test[0].search(line)
|
||||
# If we find and match and (a) its our first one, or (b) it
|
||||
# matches text earlier than our previous best match, or (c) it
|
||||
# matches text at the same location as our previous best match
|
||||
# but extends to cover more text than that match, then this is
|
||||
# our new best match.
|
||||
#
|
||||
# Implied here is that when multiple formatters match exactly
|
||||
# the same text, the first formatter in the registration list wins.
|
||||
if match \
|
||||
and ((best_match is None) \
|
||||
or (match.start() < best_match.start())
|
||||
or ((match.start() == best_match.start()) \
|
||||
and (match.end() > best_match.end()))):
|
||||
best_match = match
|
||||
best_conv = test[1]
|
||||
best_userdata = test[2]
|
||||
# If we found a match...
|
||||
if best_match:
|
||||
# ... add any non-matching stuff first, then the matching bit.
|
||||
start = best_match.start()
|
||||
end = best_match.end()
|
||||
if start > 0:
|
||||
tokens.append(_item(match=line[:start],
|
||||
converter=self.format_text,
|
||||
userdata=None))
|
||||
tokens.append(_item(match=best_match,
|
||||
converter=best_conv,
|
||||
userdata=best_userdata))
|
||||
line = line[end:]
|
||||
else:
|
||||
# Otherwise, just add the rest of the string.
|
||||
tokens.append(_item(match=line,
|
||||
while s:
|
||||
best_match = best_conv = best_userdata = None
|
||||
for test in self._formatters:
|
||||
match = test[0].search(s)
|
||||
# If we find and match and (a) its our first one, or (b) it
|
||||
# matches text earlier than our previous best match, or (c) it
|
||||
# matches text at the same location as our previous best match
|
||||
# but extends to cover more text than that match, then this is
|
||||
# our new best match.
|
||||
#
|
||||
# Implied here is that when multiple formatters match exactly
|
||||
# the same text, the first formatter in the registration list wins.
|
||||
if match \
|
||||
and ((best_match is None) \
|
||||
or (match.start() < best_match.start())
|
||||
or ((match.start() == best_match.start()) \
|
||||
and (match.end() > best_match.end()))):
|
||||
best_match = match
|
||||
best_conv = test[1]
|
||||
best_userdata = test[2]
|
||||
# If we found a match...
|
||||
if best_match:
|
||||
# ... add any non-matching stuff first, then the matching bit.
|
||||
start = best_match.start()
|
||||
end = best_match.end()
|
||||
if start > 0:
|
||||
tokens.append(_item(match=s[:start],
|
||||
converter=self.format_text,
|
||||
userdata=None))
|
||||
line = ''
|
||||
tokens.append(_item(match=best_match,
|
||||
converter=best_conv,
|
||||
userdata=best_userdata))
|
||||
s = s[end:]
|
||||
else:
|
||||
# Otherwise, just add the rest of the string.
|
||||
tokens.append(_item(match=s,
|
||||
converter=self.format_text,
|
||||
userdata=None))
|
||||
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:
|
||||
@@ -1532,23 +1485,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
|
||||
@@ -1574,14 +1511,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,
|
||||
@@ -1610,7 +1546,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:
|
||||
@@ -1622,7 +1557,6 @@ def markup_stream_pygments(request, cfg, blame_data, fp, filename,
|
||||
break
|
||||
line_no = line_no + 1
|
||||
line = sapi.escape(string.expandtabs(line, cfg.options.tabsize))
|
||||
line = markup_escaped_urls(line)
|
||||
item = vclib.Annotation(line, line_no, None, None, None, None)
|
||||
item.diff_href = None
|
||||
lines.append(item)
|
||||
@@ -1640,7 +1574,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:
|
||||
@@ -1688,8 +1621,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:
|
||||
@@ -1705,33 +1639,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
|
||||
@@ -1739,12 +1660,12 @@ 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) \
|
||||
and 'co' in cfg.options.allowed_views:
|
||||
fp, revision = request.repos.openfile(path, rev, {})
|
||||
fp, revision = request.repos.openfile(path, rev)
|
||||
fp.close()
|
||||
if check_freshness(request, None, revision, weak=1):
|
||||
return
|
||||
@@ -1767,12 +1688,12 @@ def markup_or_annotate(request, is_annotate):
|
||||
except:
|
||||
annotation = 'error'
|
||||
|
||||
fp, revision = request.repos.openfile(path, rev, {'cvs_oldkeywords' : 1})
|
||||
fp, revision = request.repos.openfile(path, rev)
|
||||
if check_freshness(request, None, revision, weak=1):
|
||||
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)
|
||||
@@ -1806,7 +1727,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:
|
||||
@@ -1839,18 +1760,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):
|
||||
@@ -1925,16 +1840,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)
|
||||
@@ -2035,8 +1943,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)
|
||||
@@ -2093,9 +2001,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
|
||||
@@ -2259,11 +2166,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):
|
||||
@@ -2367,11 +2273,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?
|
||||
@@ -2392,9 +2296,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)
|
||||
|
||||
@@ -2414,7 +2318,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
|
||||
@@ -2655,8 +2559,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)
|
||||
|
||||
@@ -2667,20 +2570,16 @@ def view_checkout(request):
|
||||
if 'co' not in cfg.options.allowed_views:
|
||||
raise debug.ViewVCException('Checkout view is disabled',
|
||||
'403 Forbidden')
|
||||
if request.pathtype != vclib.FILE:
|
||||
raise debug.ViewVCException('Unsupported feature: checkout view on '
|
||||
'directory', '400 Bad Request')
|
||||
|
||||
|
||||
path, rev = _orig_path(request)
|
||||
fp, revision = request.repos.openfile(path, rev, {})
|
||||
fp, revision = request.repos.openfile(path, rev)
|
||||
|
||||
# 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()
|
||||
|
||||
@@ -2758,7 +2657,7 @@ def search_file(repos, path_parts, rev, search_re):
|
||||
|
||||
# Read in each line of a checked-out file, and then use re.search to
|
||||
# search line.
|
||||
fp = repos.openfile(path_parts, rev, {})[0]
|
||||
fp = repos.openfile(path_parts, rev)[0]
|
||||
matches = 0
|
||||
while 1:
|
||||
line = fp.readline()
|
||||
@@ -2800,6 +2699,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':
|
||||
@@ -2810,8 +2710,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):
|
||||
@@ -3218,13 +3117,13 @@ def view_diff(request):
|
||||
if (cfg.options.hr_intraline and idiff
|
||||
and ((human_readable and idiff.sidebyside)
|
||||
or (not human_readable and diff_type == vclib.UNIFIED))):
|
||||
f1 = request.repos.openfile(p1, rev1, {})[0]
|
||||
f1 = request.repos.openfile(p1, rev1)[0]
|
||||
try:
|
||||
lines_left = f1.readlines()
|
||||
finally:
|
||||
f1.close()
|
||||
|
||||
f2 = request.repos.openfile(p2, rev2, {})[0]
|
||||
f2 = request.repos.openfile(p2, rev2)[0]
|
||||
try:
|
||||
lines_right = f2.readlines()
|
||||
finally:
|
||||
@@ -3265,7 +3164,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,
|
||||
@@ -3281,7 +3180,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,
|
||||
@@ -3437,7 +3336,7 @@ def generate_tarball(out, request, reldir, stack, dir_mtime=None):
|
||||
|
||||
### FIXME: Read the whole file into memory? Bad... better to do
|
||||
### 2 passes.
|
||||
fp = request.repos.openfile(rep_path + [file.name], request.pathrev, {})[0]
|
||||
fp = request.repos.openfile(rep_path + [file.name], request.pathrev)[0]
|
||||
contents = fp.read()
|
||||
fp.close()
|
||||
|
||||
@@ -3502,7 +3401,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")
|
||||
@@ -3521,29 +3420,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)
|
||||
@@ -3658,8 +3537,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,
|
||||
@@ -3967,7 +3845,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.
|
||||
@@ -4016,8 +3894,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)
|
||||
@@ -4086,6 +3964,7 @@ def view_query(request):
|
||||
mindate = request.query_dict.get('mindate', '')
|
||||
maxdate = request.query_dict.get('maxdate', '')
|
||||
format = request.query_dict.get('format')
|
||||
limit = int(request.query_dict.get('limit', 0))
|
||||
limit_changes = int(request.query_dict.get('limit_changes',
|
||||
cfg.options.limit_changes))
|
||||
|
||||
@@ -4155,32 +4034,29 @@ def view_query(request):
|
||||
query.SetFromDateObject(mindate)
|
||||
if maxdate is not None:
|
||||
query.SetToDateObject(maxdate)
|
||||
|
||||
# Set the admin-defined (via configuration) row limits. This is to avoid
|
||||
# slamming the database server with a monster query.
|
||||
if format == 'rss':
|
||||
if limit:
|
||||
query.SetLimit(limit)
|
||||
elif format == 'rss':
|
||||
query.SetLimit(cfg.cvsdb.rss_row_limit)
|
||||
else:
|
||||
query.SetLimit(cfg.cvsdb.row_limit)
|
||||
|
||||
# run the query
|
||||
db.RunQuery(query)
|
||||
commit_list = query.GetCommitList()
|
||||
row_limit_reached = query.GetLimitReached()
|
||||
|
||||
|
||||
sql = request.server.escape(db.CreateSQLQueryString(query))
|
||||
|
||||
# gather commits
|
||||
commits = []
|
||||
plus_count = 0
|
||||
minus_count = 0
|
||||
mod_time = -1
|
||||
if commit_list:
|
||||
if query.commit_list:
|
||||
files = []
|
||||
limited_files = 0
|
||||
current_desc = commit_list[0].GetDescriptionID()
|
||||
current_rev = commit_list[0].GetRevision()
|
||||
current_desc = query.commit_list[0].GetDescriptionID()
|
||||
current_rev = query.commit_list[0].GetRevision()
|
||||
dir_strip = _path_join(repos_dir)
|
||||
|
||||
for commit in commit_list:
|
||||
for commit in query.commit_list:
|
||||
commit_desc = commit.GetDescriptionID()
|
||||
commit_rev = commit.GetRevision()
|
||||
|
||||
@@ -4249,7 +4125,7 @@ def view_query(request):
|
||||
|
||||
data = common_template_data(request)
|
||||
data.merge(ezt.TemplateData({
|
||||
'sql': request.server.escape(db.CreateSQLQueryString(query)),
|
||||
'sql': sql,
|
||||
'english_query': english_query(request),
|
||||
'queryform_href': request.get_url(view_func=view_queryform, escape=1),
|
||||
'backout_href': backout_href,
|
||||
@@ -4258,7 +4134,6 @@ def view_query(request):
|
||||
'show_branch': show_branch,
|
||||
'querysort': querysort,
|
||||
'commits': commits,
|
||||
'row_limit_reached' : ezt.boolean(row_limit_reached),
|
||||
'limit_changes': limit_changes,
|
||||
'limit_changes_href': limit_changes_href,
|
||||
'rss_link_href': request.get_url(view_func=view_query,
|
||||
@@ -4301,26 +4176,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():
|
||||
@@ -4330,7 +4190,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
|
||||
|
||||
@@ -4342,8 +4202,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]))
|
||||
@@ -4358,9 +4218,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,76 +38,48 @@ 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 up && svn cp -m "Tag the X.Y.Z final release." . ^/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:
|
||||
CHECKSUMS document there accordingly. Also, drop a copy of the
|
||||
archive files into the root directory of the viewvc.org website
|
||||
(unversioned).
|
||||
|
||||
http://viewvc.tigris.org/servlets/ProjectDocumentList?folderID=6004
|
||||
|
||||
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.)
|
||||
|
||||
http://viewvc.tigris.org/issues/editversions.cgi?component=viewvc&action=add
|
||||
http://viewvc.tigris.org/issues/editmilestones.cgi?component=viewvc&action=add
|
||||
|
||||
18. Send to the announce@ list a message explaining all the cool new
|
||||
features, 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 ***/
|
||||
@@ -269,14 +272,3 @@ table.vc_idiff tbody th {
|
||||
.vc_query_form {
|
||||
background-color: #e6e6e6;
|
||||
}
|
||||
|
||||
|
||||
/*** Warning! ***/
|
||||
.vc_warning {
|
||||
border-width: 1px 2px 2px 2px;
|
||||
border-color: black;
|
||||
border-style: solid;
|
||||
background-color: red;
|
||||
color: white;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
@@ -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>
|
||||
|
@@ -4,13 +4,13 @@
|
||||
<!-- ViewVC :: http://www.viewvc.org/ -->
|
||||
<head>
|
||||
<title>Checkin Database Query</title>
|
||||
[if-any docroot]<link rel="stylesheet" href="[docroot]/styles.css" type="text/css" />[end]
|
||||
<link rel="stylesheet" href="[docroot]/styles.css" type="text/css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
[# setup page definitions]
|
||||
[define help_href][if-any docroot][docroot]/help_query.html[end][end]
|
||||
[define help_href][docroot]/help_query.html[end]
|
||||
[# end]
|
||||
|
||||
<p>
|
||||
@@ -158,15 +158,7 @@
|
||||
[is query "skipped"]
|
||||
[else]
|
||||
<p><strong>[num_commits]</strong> matches found.</p>
|
||||
[if-any row_limit_reached]
|
||||
<p class="vc_warning">WARNING: These query results have been
|
||||
artificially limited by an administrative threshold value and do
|
||||
<em>not</em> represent the entirety of the data set which matches
|
||||
the query. Consider modifying your query to be more specific</a>,
|
||||
using your version control tool's query capabilities, or asking
|
||||
your administrator to raise the database response size
|
||||
threshold.</p>
|
||||
[end]
|
||||
|
||||
[if-any commits]
|
||||
<table cellspacing="0" cellpadding="2">
|
||||
<thead>
|
||||
@@ -210,21 +202,21 @@
|
||||
</td>
|
||||
|
||||
[# uncommment, if you want a separate Description column:
|
||||
{if-index commits.files first}
|
||||
{if-index commits.files first{
|
||||
<td style="vertical-align:top;" rowspan="{commits.num_files}">
|
||||
{if-any commits.log}{commits.log}{else} {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]
|
||||
[# ---]
|
||||
|
@@ -7,15 +7,6 @@
|
||||
|
||||
<p><strong>[english_query]</strong></p>
|
||||
[# <!-- {sql} --> ]
|
||||
[if-any row_limit_reached]
|
||||
<p class="vc_warning">WARNING: These query results have been
|
||||
artificially limited by an administrative threshold value and do
|
||||
<em>not</em> represent the entirety of the data set which matches
|
||||
the query. Consider <a href="[queryform_href]">modifying your
|
||||
query to be more specific</a>, using your version control tool's
|
||||
query capabilities, or asking your administrator to raise the
|
||||
database response size threshold.</p>
|
||||
[end]
|
||||
<p><a href="[queryform_href]">Modify query</a></p>
|
||||
<p><a href="[backout_href]">Show commands which could be used to back out these changes</a></p>
|
||||
|
||||
|
@@ -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>
|
||||
|
@@ -50,9 +50,7 @@ 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