mirror of
https://github.com/vitalif/viewvc-4intranet
synced 2019-04-16 04:14:59 +03:00
Compare commits
233 Commits
custishack
...
1.1.14
Author | SHA1 | Date | |
---|---|---|---|
![]() |
d228de45d7 | ||
![]() |
5a51470cbc | ||
![]() |
acc0783468 | ||
![]() |
c91325d40a | ||
![]() |
ca1bd67b5d | ||
![]() |
f1e7ef42d6 | ||
![]() |
8b7eae7f14 | ||
![]() |
6f3d9a3a00 | ||
![]() |
1d7307c09b | ||
![]() |
22d1e72c66 | ||
![]() |
0e7d4061ed | ||
![]() |
56dd2dcf28 | ||
![]() |
5932f24a68 | ||
![]() |
161421a20f | ||
![]() |
2a297c5361 | ||
![]() |
f43117b10f | ||
![]() |
a025237f7e | ||
![]() |
228db2fadb | ||
![]() |
0cd26cc79f | ||
![]() |
b808d5d1e8 | ||
![]() |
f843c054b7 | ||
![]() |
cf06a971e8 | ||
![]() |
bc8f3bdd4f | ||
![]() |
a6fcab67b0 | ||
![]() |
4069208316 | ||
![]() |
cfaa30b40f | ||
![]() |
97c5a82b7b | ||
![]() |
21bd391d62 | ||
![]() |
9d1476ef1d | ||
![]() |
151fcd05e6 | ||
![]() |
22400ddcfb | ||
![]() |
ae55530edc | ||
![]() |
112700a12c | ||
![]() |
cecffecf39 | ||
![]() |
bdac8697fe | ||
![]() |
3d9404b67d | ||
![]() |
7c50f55153 | ||
![]() |
368e4dc360 | ||
![]() |
964d8bb5f9 | ||
![]() |
846e8e46c5 | ||
![]() |
75c719bcde | ||
![]() |
fc8793bf15 | ||
![]() |
65935c40fd | ||
![]() |
a30d0b44cc | ||
![]() |
fded8462d2 | ||
![]() |
b1095ac763 | ||
![]() |
e54399a169 | ||
![]() |
4cc0db75be | ||
![]() |
74a9cbb2a0 | ||
![]() |
390e337a8e | ||
![]() |
ecdac77d5f | ||
![]() |
79158c2ee7 | ||
![]() |
96fdfbdbcf | ||
![]() |
361538da21 | ||
![]() |
6250d4134b | ||
![]() |
8589949521 | ||
![]() |
f12e262fa5 | ||
![]() |
f379070697 | ||
![]() |
d47fc0ff3b | ||
![]() |
f2b6f0ba86 | ||
![]() |
ddbe150be1 | ||
![]() |
6b5ed7c857 | ||
![]() |
391f7d8237 | ||
![]() |
141cf5ff10 | ||
![]() |
bfe148eb45 | ||
![]() |
6fb28f2198 | ||
![]() |
02cc53d34b | ||
![]() |
5928918da4 | ||
![]() |
2e9f84427c | ||
![]() |
df599031a0 | ||
![]() |
6311c93298 | ||
![]() |
b982cccbcd | ||
![]() |
33b8224714 | ||
![]() |
2f05f570b1 | ||
![]() |
d737657e1d | ||
![]() |
3d5294635e | ||
![]() |
75c3fc2346 | ||
![]() |
80bce159af | ||
![]() |
7ff9b84ee1 | ||
![]() |
b6acd3c114 | ||
![]() |
fa5d40caa3 | ||
![]() |
a9cbd4c6a6 | ||
![]() |
882cdaa46b | ||
![]() |
58e64cb28d | ||
![]() |
779e67653b | ||
![]() |
0381a772b7 | ||
![]() |
048b2c8033 | ||
![]() |
e354ab302c | ||
![]() |
5f931c6cf1 | ||
![]() |
576837351a | ||
![]() |
6084ccf877 | ||
![]() |
514da91629 | ||
![]() |
d2c8bb9878 | ||
![]() |
fe274e9b9c | ||
![]() |
6603a67a91 | ||
![]() |
b50de1c92e | ||
![]() |
dfd8dc82f6 | ||
![]() |
1a942d1062 | ||
![]() |
289a78eb99 | ||
![]() |
2897763020 | ||
![]() |
47886423e5 | ||
![]() |
0890821839 | ||
![]() |
2903389d3e | ||
![]() |
5bdea369e8 | ||
![]() |
3699e8e0c7 | ||
![]() |
55c0b30bc0 | ||
![]() |
6fd321529a | ||
![]() |
531877db4a | ||
![]() |
51df003d8e | ||
![]() |
7d05859d7e | ||
![]() |
2e48cd2ba1 | ||
![]() |
267b61f347 | ||
![]() |
7f53d5cc3c | ||
![]() |
af591ed9f7 | ||
![]() |
17492a3856 | ||
![]() |
e990ff9f2f | ||
![]() |
d832930c8d | ||
![]() |
8a0c66c7cf | ||
![]() |
292c2d7ce0 | ||
![]() |
16937af30f | ||
![]() |
d067d49afa | ||
![]() |
132a01a88f | ||
![]() |
4db5103ff1 | ||
![]() |
41943f67c5 | ||
![]() |
1f37623526 | ||
![]() |
7e92babad8 | ||
![]() |
97e45101d9 | ||
![]() |
62d5a3b649 | ||
![]() |
c96c585b39 | ||
![]() |
136063e5a7 | ||
![]() |
f59db1597d | ||
![]() |
c6c3f55c2c | ||
![]() |
8063d46a53 | ||
![]() |
fbc7737465 | ||
![]() |
23f3f3ad55 | ||
![]() |
0efc53a373 | ||
![]() |
0cfe027753 | ||
![]() |
df3b75cdb0 | ||
![]() |
24dd18bf0f | ||
![]() |
bbf27f4afd | ||
![]() |
d4ac97de3c | ||
![]() |
3039b1f43b | ||
![]() |
13597f89cd | ||
![]() |
870ae9aecc | ||
![]() |
c94b0709ec | ||
![]() |
5632e63bcb | ||
![]() |
ae7b1103f6 | ||
![]() |
2882178be7 | ||
![]() |
bfc59256ca | ||
![]() |
3abe695c3c | ||
![]() |
624dfdf0f8 | ||
![]() |
28bf22f279 | ||
![]() |
830a48f88c | ||
![]() |
a23543d763 | ||
![]() |
586a183d5c | ||
![]() |
7ae02a5887 | ||
![]() |
4020fd2e5f | ||
![]() |
af871d59ce | ||
![]() |
b8d36c8c14 | ||
![]() |
4b5721a3d7 | ||
![]() |
10235b8fdf | ||
![]() |
45332f577a | ||
![]() |
148fd7b462 | ||
![]() |
6f66313b51 | ||
![]() |
cf32af4816 | ||
![]() |
df3addacdb | ||
![]() |
a1428600b7 | ||
![]() |
4a4a3b1d61 | ||
![]() |
8c65f1b2c5 | ||
![]() |
55046f2261 | ||
![]() |
b6cedd7c1a | ||
![]() |
771d6736fb | ||
![]() |
455157e413 | ||
![]() |
c875582cfe | ||
![]() |
c3d896d5a8 | ||
![]() |
236069b068 | ||
![]() |
6de96c4fc6 | ||
![]() |
e45c2fcf6e | ||
![]() |
8c620c8c1a | ||
![]() |
c6d6dc4bdf | ||
![]() |
355fac5da1 | ||
![]() |
77d0c3dd06 | ||
![]() |
d95cb540f5 | ||
![]() |
98b757de23 | ||
![]() |
efb811d20c | ||
![]() |
a57e6b7054 | ||
![]() |
fe52bbb079 | ||
![]() |
76d6b541c3 | ||
![]() |
269a9ca864 | ||
![]() |
a5aafe3172 | ||
![]() |
d30bd89c42 | ||
![]() |
d76ce85625 | ||
![]() |
b2b247f417 | ||
![]() |
3ab2ec665b | ||
![]() |
2806f0e9a2 | ||
![]() |
8b426f6fb3 | ||
![]() |
19ee8b32df | ||
![]() |
a5389019fa | ||
![]() |
7f272fbe04 | ||
![]() |
89edf0ac04 | ||
![]() |
71fbec8871 | ||
![]() |
504b1e1a8a | ||
![]() |
cf9381e0ee | ||
![]() |
3907172131 | ||
![]() |
c34752ce92 | ||
![]() |
40cc23f12a | ||
![]() |
9b7fea99d4 | ||
![]() |
85949a6464 | ||
![]() |
d5291a6b98 | ||
![]() |
9e41d171c9 | ||
![]() |
da54e91dea | ||
![]() |
41b2ae2c7a | ||
![]() |
1815220abe | ||
![]() |
a080d8eef4 | ||
![]() |
8c4af42d56 | ||
![]() |
7f1d0952fc | ||
![]() |
222aa4ec1f | ||
![]() |
c03bfd4b89 | ||
![]() |
6fa8cdf117 | ||
![]() |
c4c777ffe6 | ||
![]() |
3680903ed4 | ||
![]() |
6fe61a11bb | ||
![]() |
437975652f | ||
![]() |
7ff75bec7b | ||
![]() |
ea03f20f46 | ||
![]() |
a5df515d79 | ||
![]() |
fabe8e8a66 | ||
![]() |
f01fcc4ec4 | ||
![]() |
05e99f4c7b | ||
![]() |
d8aff4e58f | ||
![]() |
e8d3d6ad89 | ||
![]() |
1317088e3c | ||
![]() |
fe81ee5969 |
106
CHANGES
106
CHANGES
@@ -1,91 +1,10 @@
|
||||
4Intra.net/CUSTIS improvements
|
||||
|
||||
* Support for full-text search over file contents, including binary
|
||||
documents like *.doc and so on using Sphinx Search and Apache Tika
|
||||
server. Patched Tika with fixes for #TIKA709 and #TIKA964 is highly
|
||||
recommended:
|
||||
http://wiki.4intra.net/public/tika-app-1.2-fix-TIKA709-TIKA964.jar
|
||||
(SHA1 efef722a5e2322f7c2616d096552a48134dc5faa)
|
||||
* Access right checks in query results.
|
||||
* Access right checks for repository root directories.
|
||||
* New query parameters: repository, repo type, revision number.
|
||||
* Authorizer for CVSnt ACLs.
|
||||
* InnoDB, additional database indexes and some search query
|
||||
optimisations.
|
||||
* Support for specifying path to MySQL UNIX socket.
|
||||
* Asynchronous hook examples for updating SVN and CVS repos.
|
||||
* Slightly more correct charset guessing, especially for Russian.
|
||||
* Support for diffing added/removed files.
|
||||
* File lists in RSS feeds for 'classic' template.
|
||||
* Path configuration via a single 'viewvcinstallpath.py' file,
|
||||
not via editing multiple bin/* files.
|
||||
* Link to repository list instead of viewvc.org from the logo
|
||||
* "rcsfile service" support used to workaround command execution
|
||||
problems (forks) from Apache mod_python.
|
||||
|
||||
Version 1.2.0 (released ??-???-????)
|
||||
|
||||
* bumped minimum support Python version to 2.4
|
||||
* implemented support for property diffs (issue #383)
|
||||
* allow user-configurable cvsgraph display (issue #336)
|
||||
* allow rNNNN syntax for Subversion revision numbers (issue #441)
|
||||
|
||||
Version 1.1.20 (released 24-Apr-2013)
|
||||
|
||||
* fix tab-to-space handling regression in markup view
|
||||
* fix regression in root lookup handling (issue #526)
|
||||
|
||||
Version 1.1.19 (released 22-Apr-2013)
|
||||
|
||||
* improve root lookup performance (issue #523)
|
||||
* new 'max_filesize_kbytes' config option and handling (issue #524)
|
||||
* tarball generation improvements:
|
||||
- preserve Subversion symlinks in generated tarballs (issue #487)
|
||||
- reduce memory usage of tarball generation logic
|
||||
- fix double compression of generated tarballs (issue #525)
|
||||
* file content handling improvements:
|
||||
- expanded support for encoding detection and transcoding (issue #11)
|
||||
- fix tab-to-space conversion bugs in markup, annotate, and diff views
|
||||
- fix handling of trailing whitespace in diff view
|
||||
* add support for timestamp display in ISO8601 format (issue #46)
|
||||
|
||||
Version 1.1.18 (released 28-Feb-2013)
|
||||
|
||||
* fix exception raised by BDB-backed SVN repositories (issue #519)
|
||||
* hide revision-less files when rcsparse is in use
|
||||
* include branchpoints in branch views using rcsparse (issue #347)
|
||||
* miscellaneous cvsdb improvements:
|
||||
- add --port option to make-database (issue #521)
|
||||
- explicitly name columns in queries (issue #522)
|
||||
- update MySQL syntax to avoid discontinued "TYPE=" terms
|
||||
|
||||
Version 1.1.17 (released 25-Oct-2012)
|
||||
|
||||
* fix exception caused by uninitialized variable usage (issue #516)
|
||||
|
||||
Version 1.1.16 (released 24-Oct-2012)
|
||||
|
||||
* security fix: escape "extra" diff info to avoid XSS attack (issue #515)
|
||||
* add 'binary_mime_types' configuration option and handling (issue #510)
|
||||
* fix 'select for diffs' persistence across log pages (issue #512)
|
||||
* remove lock status and filesize check on directories in remote SVN views
|
||||
* fix bogus 'Annotation of' page title for non-annotated view (issue #514)
|
||||
|
||||
Version 1.1.15 (released 22-Jun-2012)
|
||||
|
||||
* security fix: complete authz support for remote SVN views (issue #353)
|
||||
* security fix: log msg leak in SVN revision view with unreadable copy source
|
||||
* fix several instances of incorrect information in remote SVN views
|
||||
* increase performance of some revision metadata lookups in remote SVN views
|
||||
* fix RSS feed regression introduced in 1.1.14
|
||||
|
||||
Version 1.1.14 (released 12-Jun-2012)
|
||||
|
||||
* fix annotation of svn files with non-URI-safe paths (issue #504)
|
||||
* handle file:/// Subversion rootpaths as local roots (issue #446)
|
||||
* fix bug caused by trying to case-normalize anon usernames (issue #505)
|
||||
* speed up log handling by reusing tokenization results (issue #506)
|
||||
* add support for custom revision log markup rules (issue #246)
|
||||
* add support for custom review log markup rules (issue #429)
|
||||
|
||||
Version 1.1.13 (released 23-Jan-2012)
|
||||
|
||||
@@ -141,7 +60,7 @@ 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 rudimentary Basic authentication support to standalone.py (issue #49)
|
||||
* 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)
|
||||
@@ -246,27 +165,6 @@ Version 1.1.0 (released 13-May-2009)
|
||||
* fix exception in rev-sorted remote Subversion directory views (issue #409)
|
||||
* allow setting of page sizes for log and dir views individually (issue #402)
|
||||
|
||||
Version 1.0.13 (released 24-Oct-2012)
|
||||
|
||||
* security fix: escape "extra" diff info to avoid XSS attack (issue #515)
|
||||
* security fix: remove user-reachable override of cvsdb row limit
|
||||
* fix obscure "unexpected NULL parent pool" Subversion bindings error
|
||||
* fix svndbadmin failure on deleted paths under Subversion 1.7 (issue #499)
|
||||
|
||||
Version 1.0.12 (released 02-Jun-2010)
|
||||
|
||||
* fix exception caused by trying to HTML-escape non-string data (issue #454)
|
||||
|
||||
Version 1.0.11 (released 29-Mar-2010)
|
||||
|
||||
* security fix: escape user-provided search_re input to avoid XSS attack
|
||||
|
||||
Version 1.0.10 (released 10-Mar-2010)
|
||||
|
||||
* security fix: escape user-provided query form input to avoid XSS attack
|
||||
* fix errors viewing remote Subversion paths with URI-unsafe characters
|
||||
* fix regexp input validation (issue #426, #427, #440)
|
||||
|
||||
Version 1.0.9 (released 11-Aug-2009)
|
||||
|
||||
* security fix: validate the 'view' parameter to avoid XSS attack
|
||||
|
57
INSTALL
57
INSTALL
@@ -17,13 +17,10 @@ Congratulations on getting this far. :-)
|
||||
|
||||
Required Software And Configuration Needed To Run ViewVC:
|
||||
|
||||
In General:
|
||||
|
||||
* Python 2, version 2.4 or later (sorry, no 3.x support yet)
|
||||
(http://www.python.org/)
|
||||
|
||||
For CVS Support:
|
||||
|
||||
* Python 1.5.2 or later (sorry, no 3.x support yet)
|
||||
(http://www.python.org/)
|
||||
* RCS, Revision Control System
|
||||
(http://www.cs.purdue.edu/homes/trinkle/RCS/)
|
||||
* GNU-diff to replace diff implementations without the -u option
|
||||
@@ -33,6 +30,8 @@ Congratulations on getting this far. :-)
|
||||
|
||||
For Subversion Support:
|
||||
|
||||
* Python 2.0 or later (sorry, no 3.x support yet)
|
||||
(http://www.python.org/)
|
||||
* Subversion, Version Control System, 1.3.1 or later
|
||||
(binary installation and Python bindings)
|
||||
(http://subversion.apache.org/)
|
||||
@@ -262,7 +261,7 @@ APACHE CONFIGURATION
|
||||
METHOD E: Using mod_wsgi (if installed)
|
||||
----------------------------------------
|
||||
Copy the Python scripts file from
|
||||
<VIEWVC_INSTALLATION_DIRECTORY>/bin/wsgi/
|
||||
<VIEWVC_INSTALLATION_DIRECTORY>/bin/mod_python/
|
||||
to the directory of your choosing. Modify httpd.conf with the
|
||||
following directives:
|
||||
|
||||
@@ -304,47 +303,7 @@ APACHE CONFIGURATION
|
||||
ScriptAlias /viewvc /usr/local/viewvc/bin/wsgi/viewvc.fcgi
|
||||
ScriptAlias /query /usr/local/viewvc/bin/wsgi/query.fcgi
|
||||
|
||||
4) [Optional] Provide direct access to icons, stylesheets, etc.
|
||||
|
||||
ViewVC's HTML templates reference various stylesheets and icons
|
||||
provided by ViewVC itself. By default, ViewVC generates URLs to
|
||||
those artifacts which point back into ViewVC (using a magic
|
||||
syntax); ViewVC in turn handles such magic URL requests by
|
||||
streaming back the contents of the requested icon or stylesheet
|
||||
file. While this simplifies the configuration and initial
|
||||
deployment of ViewVC, it's not the most efficient approach to
|
||||
deliver what is essentially static content.
|
||||
|
||||
To improve performance, consider carving out a URL space in your
|
||||
webserver's configuration solely for this static content and
|
||||
instruct ViewVC to use that space when generating URLs for that
|
||||
content. For example, you might add an Alias such as the following
|
||||
to your httpd.conf:
|
||||
|
||||
Alias /viewvc-docroot /usr/local/viewvc/templates/default/docroot
|
||||
|
||||
And then, in viewvc.conf, set the 'docroot' option to the same
|
||||
location:
|
||||
|
||||
docroot = /viewvc-docroot
|
||||
|
||||
WARNING: As always when using Alias directives, be careful that you
|
||||
have them in the correct order. For example, if you use an
|
||||
ordering such as the following, Apache will hand requests for your
|
||||
static documents off to ViewVC as if they were versioned resources:
|
||||
|
||||
ScriptAlias /viewvc /usr/local/viewvc/bin/wsgi/viewvc.fcgi
|
||||
Alias /viewvc/static /usr/local/viewvc/templates/default/docroot
|
||||
|
||||
The correct order would be:
|
||||
|
||||
Alias /viewvc/static /usr/local/viewvc/templates/default/docroot
|
||||
ScriptAlias /viewvc /usr/local/viewvc/bin/wsgi/viewvc.fcgi
|
||||
|
||||
(That said, it's best to avoid such namespace nesting altogether if
|
||||
you can.)
|
||||
|
||||
5) [Optional] Add access control.
|
||||
4) [Optional] Add access control.
|
||||
|
||||
In your httpd.conf you can control access to certain modules by
|
||||
adding directives like this:
|
||||
@@ -365,14 +324,14 @@ APACHE CONFIGURATION
|
||||
http://<server_name>/viewvc/~checkout~/<module_name>
|
||||
http://<server_name>/viewvc/<module_name>.tar.gz?view=tar
|
||||
|
||||
6) Restart Apache.
|
||||
5) Restart Apache.
|
||||
|
||||
The commands to do this vary. "httpd -k restart" and "apache -k
|
||||
restart" are two common variants. On RedHat Linux it is done using
|
||||
the command "/sbin/service httpd restart" and on SuSE Linux it is
|
||||
done with "rcapache restart". Other systems use "apachectl restart".
|
||||
|
||||
7) [Optional] Protect your ViewVC instance from server-whacking webcrawlers.
|
||||
6) Optional: Protect your ViewVC instance from server-whacking webcrawlers.
|
||||
|
||||
As ViewVC is a web-based application which each page containing various
|
||||
links to other pages and views, you can expect your server's performance
|
||||
|
@@ -15,7 +15,7 @@
|
||||
|
||||
<blockquote>
|
||||
|
||||
<p><strong>Copyright © 1999-2013 The ViewCVS Group. All rights
|
||||
<p><strong>Copyright © 1999-2012 The ViewCVS Group. All rights
|
||||
reserved.</strong></p>
|
||||
|
||||
<p>By using ViewVC, you agree to the terms and conditions set forth
|
||||
@@ -63,7 +63,6 @@
|
||||
<li>March 29, 2010 — copyright years updated</li>
|
||||
<li>February 18, 2011 — copyright years updated</li>
|
||||
<li>January 23, 2012 — copyright years updated</li>
|
||||
<li>January 04, 2013 — copyright years updated</li>
|
||||
</ul>
|
||||
|
||||
</body>
|
||||
|
3
README
3
README
@@ -1,9 +1,6 @@
|
||||
ViewVC -- Viewing the content of CVS/SVN repositories with a Webbrowser.
|
||||
|
||||
This is the 4Intra.net patched version with some extra features.
|
||||
|
||||
Please read the file INSTALL for more information.
|
||||
|
||||
And see windows/README for more information on running ViewVC on
|
||||
Microsoft Windows.
|
||||
|
||||
|
@@ -3,7 +3,7 @@
|
||||
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
@@ -24,15 +24,15 @@
|
||||
#
|
||||
|
||||
#########################################################################
|
||||
#
|
||||
# INSTALL-TIME CONFIGURATION
|
||||
#
|
||||
# These values will be set during the installation process. During
|
||||
# development, there will be no 'viewvcinstallpath.py'
|
||||
# development, they will remain None.
|
||||
#
|
||||
|
||||
import viewvcinstallpath
|
||||
LIBRARY_DIR = viewvcinstallpath.LIBRARY_DIR
|
||||
CONF_PATHNAME = viewvcinstallpath.CONF_PATHNAME
|
||||
LIBRARY_DIR = None
|
||||
CONF_PATHNAME = None
|
||||
|
||||
#########################################################################
|
||||
#
|
||||
|
@@ -3,7 +3,7 @@
|
||||
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
@@ -24,15 +24,15 @@
|
||||
#
|
||||
|
||||
#########################################################################
|
||||
#
|
||||
# INSTALL-TIME CONFIGURATION
|
||||
#
|
||||
# These values will be set during the installation process. During
|
||||
# development, there will be no 'viewvcinstallpath.py'
|
||||
# development, they will remain None.
|
||||
#
|
||||
|
||||
import viewvcinstallpath
|
||||
LIBRARY_DIR = viewvcinstallpath.LIBRARY_DIR
|
||||
CONF_PATHNAME = viewvcinstallpath.CONF_PATHNAME
|
||||
LIBRARY_DIR = None
|
||||
CONF_PATHNAME = None
|
||||
|
||||
#########################################################################
|
||||
#
|
||||
|
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
@@ -22,19 +22,19 @@
|
||||
#
|
||||
|
||||
#########################################################################
|
||||
#
|
||||
# INSTALL-TIME CONFIGURATION
|
||||
#
|
||||
# These values will be set during the installation process. During
|
||||
# development, there will be no 'viewvcinstallpath.py'
|
||||
# development, they will remain None.
|
||||
#
|
||||
|
||||
import viewvcinstallpath
|
||||
LIBRARY_DIR = viewvcinstallpath.LIBRARY_DIR
|
||||
CONF_PATHNAME = viewvcinstallpath.CONF_PATHNAME
|
||||
LIBRARY_DIR = None
|
||||
CONF_PATHNAME = None
|
||||
|
||||
#########################################################################
|
||||
#
|
||||
# Adjust sys.path to include our library directory.
|
||||
# Adjust sys.path to include our library directory
|
||||
#
|
||||
|
||||
import sys
|
||||
@@ -47,20 +47,6 @@ else:
|
||||
"../../../lib")))
|
||||
|
||||
#########################################################################
|
||||
#
|
||||
# If admins want nicer processes, here's the place to get them.
|
||||
#
|
||||
|
||||
#try:
|
||||
# os.nice(20) # bump the nice level of this process
|
||||
#except:
|
||||
# pass
|
||||
|
||||
|
||||
#########################################################################
|
||||
#
|
||||
# Go do the work.
|
||||
#
|
||||
|
||||
import sapi
|
||||
import viewvc
|
||||
|
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
@@ -22,19 +22,19 @@
|
||||
#
|
||||
|
||||
#########################################################################
|
||||
#
|
||||
# INSTALL-TIME CONFIGURATION
|
||||
#
|
||||
# These values will be set during the installation process. During
|
||||
# development, there will be no 'viewvcinstallpath.py'
|
||||
# development, they will remain None.
|
||||
#
|
||||
|
||||
import viewvcinstallpath
|
||||
LIBRARY_DIR = viewvcinstallpath.LIBRARY_DIR
|
||||
CONF_PATHNAME = viewvcinstallpath.CONF_PATHNAME
|
||||
LIBRARY_DIR = None
|
||||
CONF_PATHNAME = None
|
||||
|
||||
#########################################################################
|
||||
#
|
||||
# Adjust sys.path to include our library directory.
|
||||
# Adjust sys.path to include our library directory
|
||||
#
|
||||
|
||||
import sys
|
||||
@@ -47,21 +47,12 @@ else:
|
||||
"../../../lib")))
|
||||
|
||||
#########################################################################
|
||||
#
|
||||
# If admins want nicer processes, here's the place to get them.
|
||||
#
|
||||
|
||||
#try:
|
||||
# os.nice(20) # bump the nice level of this process
|
||||
#except:
|
||||
# pass
|
||||
|
||||
### add code for checking the load average
|
||||
|
||||
#########################################################################
|
||||
#
|
||||
# Go do the work.
|
||||
#
|
||||
|
||||
# go do the work
|
||||
import sapi
|
||||
import viewvc
|
||||
|
||||
|
@@ -1,216 +0,0 @@
|
||||
#!/bin/sh
|
||||
# ViewVC installation script for CustIS
|
||||
|
||||
if [ -f custis-install-config ]; then
|
||||
. custis-install-config
|
||||
else
|
||||
cat >custis-install-config <<EOF
|
||||
CVS_ASYNC=
|
||||
SVN_ASYNC=
|
||||
CVS_USER=www-data
|
||||
CVS_GROUP=cvs.users
|
||||
CVSROOTS=
|
||||
SVNROOT=
|
||||
SVN_FORBIDDENRE=
|
||||
|
||||
DB_HOST=
|
||||
DB_PORT=
|
||||
DB_SOCKET=/var/run/mysqld/mysqld.sock
|
||||
DB=
|
||||
DB_USER=
|
||||
DB_PASSWD=
|
||||
|
||||
VIEWVC_DIR=
|
||||
VIEWVC_URI=/
|
||||
VIEWVC_URI_SLASH=/
|
||||
VIEWVC_STATIC_URI=/static
|
||||
http_proxy=
|
||||
no_proxy=
|
||||
EOF
|
||||
echo Empty 'custis-install-config' initialized, please edit it before installation.
|
||||
exit
|
||||
fi
|
||||
|
||||
if [ ! "$DB" -o ! "$VIEWVC_DIR" ]; then
|
||||
echo Please set up 'custis-install-config' before installation.
|
||||
exit
|
||||
fi
|
||||
|
||||
################################################################################
|
||||
|
||||
export http_proxy
|
||||
export no_proxy
|
||||
|
||||
DEPS="python libapache2-mod-python rcs diff cvsnt subversion subversion-tools python-setuptools python-subversion python-mysqldb cvsgraph"
|
||||
|
||||
echo "*** Installing dependency packages: $DEPS"
|
||||
apt-get install $DEPS
|
||||
|
||||
echo "*** Installing Pygments for Python"
|
||||
wget http://pypi.python.org/packages/source/P/Pygments/Pygments-0.11.1.tar.gz
|
||||
tar -zxf Pygments-0.11.1.tar.gz
|
||||
cd Pygments-0.11.1
|
||||
python setup.py install
|
||||
cd ..
|
||||
|
||||
echo "*** Installing ViewVC into $VIEWVC_DIR"
|
||||
if [ ! -e $VIEWVC_DIR/viewvc.conf ]; then
|
||||
../viewvc-install <<EOF
|
||||
$VIEWVC_DIR
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
EOF
|
||||
fi
|
||||
|
||||
echo "*** Writing ViewVC configuration into $VIEWVC_DIR/viewvc.conf"
|
||||
for CVSROOT in $CVSROOTS; do
|
||||
if [ "$vccvsroots" ]; then
|
||||
vccvsroots="$vccvsroots, "
|
||||
fi
|
||||
vcrootname=`basename $CVSROOT`
|
||||
vccvsroots="$vccvsroots$vcrootname: $CVSROOT"
|
||||
done;
|
||||
cat >"$VIEWVC_DIR/viewvc.conf" <<EOF
|
||||
[general]
|
||||
cvs_roots = $vccvsroots
|
||||
root_parents = $SVNROOT : svn
|
||||
cvsnt_exe_path = /usr/bin/cvsnt
|
||||
mime_types_file = /etc/mime.types
|
||||
address = Admin address: stas [gav-gav] custis [ru]
|
||||
kv_files =
|
||||
languages = en-us, ru-ru
|
||||
|
||||
[utilities]
|
||||
rcs_dir = /usr/bin
|
||||
cvsnt = /usr/bin/cvsnt
|
||||
svn = /usr/bin/svn
|
||||
diff = /usr/bin/diff
|
||||
cvsgraph = /usr/bin/cvsgraph
|
||||
|
||||
[options]
|
||||
allowed_views = markup, annotate, roots
|
||||
authorizer = forbiddenre
|
||||
checkout_magic = 0
|
||||
cross_copies = 1
|
||||
cvsgraph_conf = $VIEWVC_DIR/cvsgraph.conf
|
||||
default_file_view = log
|
||||
diff_format = h
|
||||
docroot = $VIEWVC_STATIC_URI
|
||||
enable_syntax_coloration = 1
|
||||
generate_etags = 1
|
||||
hide_attic = 1
|
||||
hide_cvsroot = 1
|
||||
hide_errorful_entries = 0
|
||||
hr_breakable = 1
|
||||
hr_funout = 0
|
||||
hr_ignore_keyword_subst = 1
|
||||
hr_ignore_white = 1
|
||||
hr_intraline = 0
|
||||
http_expiration_time = 600
|
||||
limit_changes = 100
|
||||
log_sort = date
|
||||
mangle_email_addresses = 1
|
||||
root_as_url_component = 1
|
||||
short_log_len = 80
|
||||
show_log_in_markup = 1
|
||||
show_logs = 1
|
||||
show_subdir_lastmod = 0
|
||||
sort_by = file
|
||||
sort_group_dirs = 1
|
||||
svn_config_dir =
|
||||
template_dir = templates
|
||||
use_cvsgraph = 1
|
||||
use_localtime = 1
|
||||
use_pagesize = 0
|
||||
use_rcsparse = 0
|
||||
use_re_search = 0
|
||||
|
||||
[templates]
|
||||
|
||||
[cvsdb]
|
||||
enabled = 1
|
||||
host = $DB_HOST
|
||||
socket = $DB_SOCKET
|
||||
database_name = $DB
|
||||
user = $DB_USER
|
||||
passwd = $DB_PASSWD
|
||||
readonly_user = $DB_USER
|
||||
readonly_passwd = $DB_PASSWD
|
||||
|
||||
[vhosts]
|
||||
|
||||
[authz-forbidden]
|
||||
forbidden =
|
||||
|
||||
[authz-forbiddenre]
|
||||
forbiddenre = $SVN_FORBIDDENRE
|
||||
|
||||
[authz-svnauthz]
|
||||
authzfile =
|
||||
EOF
|
||||
|
||||
echo "*** Initializing database: $DB using $DB_USER@$DB_HOST"
|
||||
$VIEWVC_DIR/bin/make-database <<EOF
|
||||
$DB_HOST
|
||||
$DB_USER
|
||||
$DB_PASSWD
|
||||
$DB
|
||||
EOF
|
||||
|
||||
echo "*** Configuring Apache"
|
||||
cat >/etc/apache2/sites-available/viewvc <<EOF
|
||||
<VirtualHost *:80>
|
||||
ServerName viewvc.office.custis.ru
|
||||
ServerAlias viewvc
|
||||
ServerAdmin sysadmins@custis.ru
|
||||
ServerSignature Off
|
||||
ErrorLog /var/log/apache2/viewvc-error.log
|
||||
TransferLog /var/log/apache2/viewvc-access.log
|
||||
LogLevel warn
|
||||
# ViewVC at $VIEWVC_URI installed at $VIEWVC_DIR
|
||||
DocumentRoot $VIEWVC_DIR/bin/mod_python
|
||||
Alias $VIEWVC_STATIC_URI $VIEWVC_DIR/templates/docroot
|
||||
Alias $VIEWVC_URI $VIEWVC_DIR/bin/mod_python/
|
||||
<Location ~ ^${VIEWVC_URI_SLASH}*(\?.*)?$>
|
||||
Options -Indexes
|
||||
RewriteEngine On
|
||||
RewriteRule .* ${VIEWVC_URI_SLASH}viewvc.py%{REQUEST_URI} [R,L]
|
||||
</Location>
|
||||
<Directory $VIEWVC_DIR/bin/mod_python>
|
||||
Options +ExecCGI
|
||||
AddHandler python-program .py
|
||||
PythonHandler handler
|
||||
PythonDebug Off
|
||||
</Directory>
|
||||
</VirtualHost>
|
||||
EOF
|
||||
a2enmod python
|
||||
a2enmod mod_python
|
||||
a2enmod rewrite
|
||||
a2ensite viewvc
|
||||
|
||||
echo "*** Restarting Apache"
|
||||
apache2ctl stop
|
||||
sleep 1
|
||||
apache2ctl start
|
||||
|
||||
echo "*** Building commit database for CVS"
|
||||
for CVSROOT in $CVSROOTS; do
|
||||
$VIEWVC_DIR/bin/cvsdbadmin rebuild $CVSROOT
|
||||
done;
|
||||
|
||||
echo "*** Building commit database for Subversion repositories"
|
||||
for i in `ls $SVNROOT`; do
|
||||
if [ -d "$SVNROOT/$i" ]; then
|
||||
$VIEWVC_DIR/bin/svndbadmin -v rebuild "$SVNROOT/$i"
|
||||
fi
|
||||
done;
|
||||
|
||||
# setup hooks for CVS
|
||||
./setup-cvs-hooks "$CVSROOTS" "$VIEWVC_DIR" "$CVS_USER" "$CVS_GROUP" "$CVS_ASYNC"
|
||||
|
||||
# setup hooks for Subversion
|
||||
./setup-svn-hooks "$SVNROOT" "$VIEWVC_DIR" "$SVN_ASYNC"
|
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/python
|
||||
#!/usr/bin/env python
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 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,15 +18,15 @@
|
||||
#
|
||||
|
||||
#########################################################################
|
||||
#
|
||||
# INSTALL-TIME CONFIGURATION
|
||||
#
|
||||
# These values will be set during the installation process. During
|
||||
# development, there will be no 'viewvcinstallpath.py'
|
||||
# development, they will remain None.
|
||||
#
|
||||
|
||||
import viewvcinstallpath
|
||||
LIBRARY_DIR = viewvcinstallpath.LIBRARY_DIR
|
||||
CONF_PATHNAME = viewvcinstallpath.CONF_PATHNAME
|
||||
LIBRARY_DIR = None
|
||||
CONF_PATHNAME = None
|
||||
|
||||
# Adjust sys.path to include our library directory
|
||||
import sys
|
||||
@@ -40,28 +40,23 @@ else:
|
||||
#########################################################################
|
||||
|
||||
import os
|
||||
import string
|
||||
import cvsdb
|
||||
import viewvc
|
||||
import vclib.ccvs
|
||||
from stat import *
|
||||
|
||||
|
||||
def UpdateFile(db, repository, path, update, latest_checkin, quiet_level, encoding = None):
|
||||
|
||||
def UpdateFile(db, repository, path, update, quiet_level):
|
||||
try:
|
||||
if update:
|
||||
mtime = os.stat(repository.rcsfile(path, 1))[ST_MTIME]
|
||||
if mtime < latest_checkin:
|
||||
return
|
||||
commit_list = cvsdb.GetUnrecordedCommitList(repository, path, db)
|
||||
else:
|
||||
commit_list = cvsdb.GetCommitListFromRCSFile(repository, path)
|
||||
except cvsdb.error, e:
|
||||
print '[ERROR] %s' % (e)
|
||||
return
|
||||
except vclib.ItemNotFound, e:
|
||||
return
|
||||
|
||||
file = '/'.join(path)
|
||||
file = string.join(path, "/")
|
||||
printing = 0
|
||||
if update:
|
||||
if quiet_level < 1 or (quiet_level < 2 and len(commit_list)):
|
||||
@@ -74,8 +69,6 @@ def UpdateFile(db, repository, path, update, latest_checkin, quiet_level, encodi
|
||||
|
||||
## add the commits into the database
|
||||
for commit in commit_list:
|
||||
if encoding:
|
||||
commit.SetFile(commit.GetFile().decode(encoding).encode('utf-8'))
|
||||
db.AddCommit(commit)
|
||||
if printing:
|
||||
sys.stdout.write('.')
|
||||
@@ -84,8 +77,7 @@ def UpdateFile(db, repository, path, update, latest_checkin, quiet_level, encodi
|
||||
print
|
||||
|
||||
|
||||
def RecurseUpdate(db, repository, directory, update, latest_checkin,
|
||||
quiet_level, encoding = None):
|
||||
def RecurseUpdate(db, repository, directory, update, quiet_level):
|
||||
for entry in repository.listdir(directory, None, {}):
|
||||
path = directory + [entry.name]
|
||||
|
||||
@@ -93,14 +85,11 @@ def RecurseUpdate(db, repository, directory, update, latest_checkin,
|
||||
continue
|
||||
|
||||
if entry.kind is vclib.DIR:
|
||||
RecurseUpdate(db, repository, path, update, latest_checkin,
|
||||
quiet_level, encoding)
|
||||
RecurseUpdate(db, repository, path, update, quiet_level)
|
||||
continue
|
||||
|
||||
if entry.kind is vclib.FILE:
|
||||
UpdateFile(db, repository, path, update, latest_checkin,
|
||||
quiet_level, encoding)
|
||||
|
||||
UpdateFile(db, repository, path, update, quiet_level)
|
||||
|
||||
def RootPath(path, quiet_level):
|
||||
"""Break os path into cvs root path and other parts"""
|
||||
@@ -189,7 +178,7 @@ if __name__ == '__main__':
|
||||
if quiet_level < 2:
|
||||
print "Purging existing data for repository root `%s'" % root
|
||||
try:
|
||||
db.PurgeRepository(root, len(path_parts) and '/'.join(path_parts) or '')
|
||||
db.PurgeRepository(root)
|
||||
except cvsdb.UnknownRepositoryError, e:
|
||||
if command == 'purge':
|
||||
sys.stderr.write("ERROR: " + str(e) + "\n")
|
||||
@@ -197,15 +186,11 @@ if __name__ == '__main__':
|
||||
|
||||
if command in ('rebuild', 'update'):
|
||||
repository = vclib.ccvs.CVSRepository(None, rootpath, None,
|
||||
cfg.utilities, 0, cfg.guesser())
|
||||
latest_checkin = db.GetLatestCheckinTime(repository)
|
||||
if latest_checkin is None:
|
||||
command = 'rebuild'
|
||||
cfg.utilities, 0)
|
||||
RecurseUpdate(db, repository, path_parts,
|
||||
command == 'update', latest_checkin, quiet_level,
|
||||
cfg.options.cvs_ondisk_charset)
|
||||
command == 'update', quiet_level)
|
||||
except KeyboardInterrupt:
|
||||
print
|
||||
print '** break **'
|
||||
|
||||
|
||||
sys.exit(0)
|
||||
|
@@ -1,57 +0,0 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
use strict;
|
||||
my ($type, $path, $branch, $user, $perm);
|
||||
|
||||
my $R = ''; # -R for recursive
|
||||
my $lvl = {
|
||||
r => 1,
|
||||
t => 2,
|
||||
w => 3,
|
||||
c => 4,
|
||||
a => 4,
|
||||
p => 5,
|
||||
};
|
||||
|
||||
while(<>)
|
||||
{
|
||||
chomp;
|
||||
next if /^\s*#/so;
|
||||
($type, $path, $branch, $user, $perm) = split /:/, $_;
|
||||
($user, $perm) = split /!/, $user;
|
||||
next unless $perm;
|
||||
$perm = [ sort { $lvl->{$b} cmp $lvl->{$a} } split //, $perm ];
|
||||
$perm = $perm->[0];
|
||||
if ($perm eq 't')
|
||||
{
|
||||
$perm = 'read,tag,nowrite,nocreate,nocontrol';
|
||||
}
|
||||
elsif ($perm eq 'r')
|
||||
{
|
||||
$perm = 'read,notag,nowrite,nocreate,nocontrol';
|
||||
}
|
||||
elsif ($perm eq 'w')
|
||||
{
|
||||
$perm = 'read,tag,write,nocreate,nocontrol';
|
||||
}
|
||||
elsif ($perm eq 'c' || $perm eq 'a')
|
||||
{
|
||||
$perm = 'read,tag,write,create,nocontrol';
|
||||
}
|
||||
elsif ($perm eq 'p')
|
||||
{
|
||||
$perm = 'read,tag,write,create,control';
|
||||
}
|
||||
print "cvs rchacl$R -a $perm";
|
||||
print " -u '$user'" if $user ne 'ALL';
|
||||
print " -r '$branch'" if $branch ne 'ALL';
|
||||
if ($path ne 'ALL')
|
||||
{
|
||||
print " '$path'";
|
||||
}
|
||||
else
|
||||
{
|
||||
print ' `ls $CVSROOT | grep -v '."'#cvs'`";
|
||||
}
|
||||
print "\n";
|
||||
}
|
@@ -1,21 +0,0 @@
|
||||
#!/usr/bin/perl
|
||||
# Very simple inetd/xinetd "cvsnt rcsfile" service
|
||||
|
||||
# Useful for ViewVC as there is an unpleasant non-stable bug, probably somewhere
|
||||
# inside mod_python or Apache, which SOMETIMES causes cvsnt subprocesses forked
|
||||
# from mod_python to die. This gives empty diff outputs or different errors like
|
||||
# "Error: Rlog output ended early. Expected RCS file ..."
|
||||
# This script removes forking from mod_python code and solves the issue.
|
||||
# Additional profit of this script is that you can probably browse REMOTE cvs
|
||||
# repositories, if you expose this service to ViewVC, although it is not tested.
|
||||
|
||||
# USAGE: (local) create an inetd service with this script as server listening
|
||||
# on some port of 127.0.0.1 and put "rcsfile_socket = 127.0.0.1:port"
|
||||
# into "utilities" section of viewvc.conf
|
||||
|
||||
$args = <STDIN>;
|
||||
$args =~ s/\s+$//so;
|
||||
@args = $args =~ /\'([^\']+)\'/giso;
|
||||
|
||||
# We don't execute shell, so this is mostly safe, not a backdoor :)
|
||||
exec('/usr/bin/cvsnt', 'rcsfile', @args);
|
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
@@ -17,15 +17,15 @@
|
||||
#
|
||||
|
||||
#########################################################################
|
||||
#
|
||||
# INSTALL-TIME CONFIGURATION
|
||||
#
|
||||
# These values will be set during the installation process. During
|
||||
# development, there will be no 'viewvcinstallpath.py'
|
||||
# development, they will remain None.
|
||||
#
|
||||
|
||||
import viewvcinstallpath
|
||||
LIBRARY_DIR = viewvcinstallpath.LIBRARY_DIR
|
||||
CONF_PATHNAME = viewvcinstallpath.CONF_PATHNAME
|
||||
LIBRARY_DIR = None
|
||||
CONF_PATHNAME = None
|
||||
|
||||
# Adjust sys.path to include our library directory
|
||||
import sys
|
||||
@@ -39,6 +39,7 @@ else:
|
||||
#########################################################################
|
||||
|
||||
import os
|
||||
import string
|
||||
import getopt
|
||||
import re
|
||||
import cvsdb
|
||||
@@ -151,11 +152,11 @@ def FindLongestDirectory(s, repository):
|
||||
and a file name, either of which may contain spaces. Returns the longest
|
||||
possible directory name that actually exists"""
|
||||
|
||||
parts = s.split()
|
||||
parts = string.split(s, " ")
|
||||
|
||||
for i in range(len(parts)-1, 0, -1):
|
||||
directory = ' '.join(parts[:i])
|
||||
filename = ' '.join(parts[i:])
|
||||
directory = string.join(parts[:i])
|
||||
filename = string.join(parts[i:])
|
||||
if os.path.isdir(os.path.join(repository, directory)):
|
||||
return directory, filename
|
||||
|
||||
@@ -226,7 +227,7 @@ def ProcessLoginfo(rootpath, directory, files):
|
||||
cfg.utilities, 0)
|
||||
|
||||
# split up the directory components
|
||||
dirpath = filter(None, os.path.normpath(directory).split(os.sep))
|
||||
dirpath = filter(None, string.split(os.path.normpath(directory), os.sep))
|
||||
|
||||
## build a list of Commit objects
|
||||
commit_list = []
|
||||
@@ -278,7 +279,7 @@ if __name__ == '__main__':
|
||||
else:
|
||||
# if there are no arguments, read version information from
|
||||
# first line of input like old versions of ViewCVS did
|
||||
arg = sys.stdin.readline().rstrip()
|
||||
arg = string.rstrip(sys.stdin.readline())
|
||||
|
||||
if len(sys.argv) > 2:
|
||||
# if there is a second argument it indicates which parser
|
||||
|
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2009 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 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,10 @@ CREATE TABLE branches (
|
||||
branch varchar(64) binary DEFAULT '' NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE branch (branch)
|
||||
) TYPE=InnoDB;
|
||||
) TYPE=MyISAM;
|
||||
|
||||
DROP TABLE IF EXISTS checkins;
|
||||
CREATE TABLE checkins (
|
||||
id int NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
type enum('Change','Add','Remove'),
|
||||
ci_when datetime DEFAULT '0000-00-00 00:00:00' NOT NULL,
|
||||
whoid mediumint(9) DEFAULT '0' NOT NULL,
|
||||
@@ -58,14 +57,13 @@ CREATE TABLE checkins (
|
||||
removedlines int(11) DEFAULT '0' NOT NULL,
|
||||
descid mediumint(9),
|
||||
UNIQUE repositoryid (repositoryid,dirid,fileid,revision),
|
||||
KEY repositoryid_when (repositoryid,ci_when),
|
||||
KEY ci_when (ci_when),
|
||||
KEY whoid (whoid,ci_when),
|
||||
KEY whoid (whoid),
|
||||
KEY repositoryid_2 (repositoryid),
|
||||
KEY dirid (dirid),
|
||||
KEY fileid (fileid),
|
||||
KEY branchid (branchid),
|
||||
KEY descid (descid)
|
||||
) TYPE=InnoDB;
|
||||
KEY branchid (branchid)
|
||||
) TYPE=MyISAM;
|
||||
|
||||
DROP TABLE IF EXISTS descs;
|
||||
CREATE TABLE descs (
|
||||
@@ -73,8 +71,7 @@ CREATE TABLE descs (
|
||||
description text,
|
||||
hash bigint(20) DEFAULT '0' NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
KEY hash (hash),
|
||||
FULLTEXT KEY description (description)
|
||||
KEY hash (hash)
|
||||
) TYPE=MyISAM;
|
||||
|
||||
DROP TABLE IF EXISTS dirs;
|
||||
@@ -83,7 +80,7 @@ CREATE TABLE dirs (
|
||||
dir varchar(255) binary DEFAULT '' NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE dir (dir)
|
||||
) TYPE=InnoDB;
|
||||
) TYPE=MyISAM;
|
||||
|
||||
DROP TABLE IF EXISTS files;
|
||||
CREATE TABLE files (
|
||||
@@ -91,7 +88,7 @@ CREATE TABLE files (
|
||||
file varchar(255) binary DEFAULT '' NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE file (file)
|
||||
) TYPE=InnoDB;
|
||||
) TYPE=MyISAM;
|
||||
|
||||
DROP TABLE IF EXISTS people;
|
||||
CREATE TABLE people (
|
||||
@@ -99,7 +96,7 @@ CREATE TABLE people (
|
||||
who varchar(128) binary DEFAULT '' NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE who (who)
|
||||
) TYPE=InnoDB;
|
||||
) TYPE=MyISAM;
|
||||
|
||||
DROP TABLE IF EXISTS repositories;
|
||||
CREATE TABLE repositories (
|
||||
@@ -107,7 +104,7 @@ CREATE TABLE repositories (
|
||||
repository varchar(64) binary DEFAULT '' NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE repository (repository)
|
||||
) TYPE=InnoDB;
|
||||
) TYPE=MyISAM;
|
||||
|
||||
DROP TABLE IF EXISTS tags;
|
||||
CREATE TABLE tags (
|
||||
@@ -121,12 +118,6 @@ CREATE TABLE tags (
|
||||
KEY dirid (dirid),
|
||||
KEY fileid (fileid),
|
||||
KEY branchid (branchid)
|
||||
) TYPE=InnoDB;
|
||||
|
||||
DROP TABLE IF EXISTS contents;
|
||||
CREATE TABLE contents (
|
||||
id int NOT NULL PRIMARY KEY,
|
||||
content MEDIUMTEXT NOT NULL DEFAULT ''
|
||||
) TYPE=MyISAM;
|
||||
"""
|
||||
|
||||
@@ -141,11 +132,10 @@ CREATE TABLE branches (
|
||||
branch varchar(64) binary DEFAULT '' NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE branch (branch)
|
||||
) TYPE=InnoDB;
|
||||
) TYPE=MyISAM;
|
||||
|
||||
DROP TABLE IF EXISTS commits;
|
||||
CREATE TABLE commits (
|
||||
id int NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
type enum('Change','Add','Remove'),
|
||||
ci_when datetime DEFAULT '0000-00-00 00:00:00' NOT NULL,
|
||||
whoid mediumint(9) DEFAULT '0' NOT NULL,
|
||||
@@ -159,14 +149,14 @@ CREATE TABLE commits (
|
||||
removedlines int(11) DEFAULT '0' NOT NULL,
|
||||
descid mediumint(9),
|
||||
UNIQUE repositoryid (repositoryid,dirid,fileid,revision),
|
||||
KEY repositoryid_when (repositoryid,ci_when),
|
||||
KEY ci_when (ci_when),
|
||||
KEY whoid (whoid,ci_when),
|
||||
KEY whoid (whoid),
|
||||
KEY repositoryid_2 (repositoryid),
|
||||
KEY dirid (dirid),
|
||||
KEY fileid (fileid),
|
||||
KEY branchid (branchid),
|
||||
KEY descid (descid)
|
||||
) TYPE=InnoDB;
|
||||
) TYPE=MyISAM;
|
||||
|
||||
DROP TABLE IF EXISTS descs;
|
||||
CREATE TABLE descs (
|
||||
@@ -174,8 +164,7 @@ CREATE TABLE descs (
|
||||
description text,
|
||||
hash bigint(20) DEFAULT '0' NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
KEY hash (hash),
|
||||
FULLTEXT KEY description (description)
|
||||
KEY hash (hash)
|
||||
) TYPE=MyISAM;
|
||||
|
||||
DROP TABLE IF EXISTS dirs;
|
||||
@@ -184,7 +173,7 @@ CREATE TABLE dirs (
|
||||
dir varchar(255) binary DEFAULT '' NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE dir (dir)
|
||||
) TYPE=InnoDB;
|
||||
) TYPE=MyISAM;
|
||||
|
||||
DROP TABLE IF EXISTS files;
|
||||
CREATE TABLE files (
|
||||
@@ -192,7 +181,7 @@ CREATE TABLE files (
|
||||
file varchar(255) binary DEFAULT '' NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE file (file)
|
||||
) TYPE=InnoDB;
|
||||
) TYPE=MyISAM;
|
||||
|
||||
DROP TABLE IF EXISTS people;
|
||||
CREATE TABLE people (
|
||||
@@ -200,7 +189,7 @@ CREATE TABLE people (
|
||||
who varchar(128) binary DEFAULT '' NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE who (who)
|
||||
) TYPE=InnoDB;
|
||||
) TYPE=MyISAM;
|
||||
|
||||
DROP TABLE IF EXISTS repositories;
|
||||
CREATE TABLE repositories (
|
||||
@@ -208,7 +197,7 @@ CREATE TABLE repositories (
|
||||
repository varchar(64) binary DEFAULT '' NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE repository (repository)
|
||||
) TYPE=InnoDB;
|
||||
) TYPE=MyISAM;
|
||||
|
||||
DROP TABLE IF EXISTS tags;
|
||||
CREATE TABLE tags (
|
||||
@@ -222,7 +211,7 @@ CREATE TABLE tags (
|
||||
KEY dirid (dirid),
|
||||
KEY fileid (fileid),
|
||||
KEY branchid (branchid)
|
||||
) TYPE=InnoDB;
|
||||
) TYPE=MyISAM;
|
||||
|
||||
DROP TABLE IF EXISTS metadata;
|
||||
CREATE TABLE metadata (
|
||||
@@ -230,14 +219,8 @@ CREATE TABLE metadata (
|
||||
value text,
|
||||
PRIMARY KEY (name),
|
||||
UNIQUE name (name)
|
||||
) TYPE=InnoDB;
|
||||
INSERT INTO metadata (name, value) VALUES ('version', '1');
|
||||
|
||||
DROP TABLE IF EXISTS contents;
|
||||
CREATE TABLE contents (
|
||||
id int NOT NULL PRIMARY KEY,
|
||||
content MEDIUMTEXT NOT NULL DEFAULT ''
|
||||
) TYPE=MyISAM;
|
||||
INSERT INTO metadata (name, value) VALUES ('version', '1');
|
||||
"""
|
||||
|
||||
BONSAI_COMPAT="""
|
||||
@@ -268,7 +251,7 @@ Options:
|
||||
[Default: ViewVC]
|
||||
|
||||
--help Show this usage message.
|
||||
|
||||
|
||||
--hostname=ARG Use ARG as the hostname for the MySQL connection.
|
||||
[Default: localhost]
|
||||
|
||||
@@ -279,7 +262,7 @@ Options:
|
||||
--version=ARG Create the database using the schema employed by
|
||||
version ARG of ViewVC. Valid values are:
|
||||
[ "1.0" ]
|
||||
|
||||
|
||||
""" % (os.path.basename(sys.argv[0])))
|
||||
if errmsg is not None:
|
||||
stream.write("[ERROR] %s.\n" % (errmsg))
|
||||
|
@@ -1,3 +1,3 @@
|
||||
AddHandler python-program .py
|
||||
PythonHandler handler
|
||||
PythonDebug Off
|
||||
PythonDebug On
|
||||
|
8
bin/mod_python/handler.py
Executable file → Normal file
8
bin/mod_python/handler.py
Executable file → Normal file
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
@@ -20,8 +20,10 @@ import os.path
|
||||
def handler(req):
|
||||
path, module_name = os.path.split(req.filename)
|
||||
module_name, module_ext = os.path.splitext(module_name)
|
||||
# Let it be 500 Internal Server Error in case of import error
|
||||
module = apache.import_module(module_name, path=[path])
|
||||
try:
|
||||
module = apache.import_module(module_name, path=[path])
|
||||
except ImportError:
|
||||
raise apache.SERVER_RETURN, apache.HTTP_NOT_FOUND
|
||||
|
||||
req.add_common_vars()
|
||||
module.index(req)
|
||||
|
11
bin/mod_python/query.py
Executable file → Normal file
11
bin/mod_python/query.py
Executable file → Normal file
@@ -1,7 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
@@ -22,15 +21,15 @@
|
||||
#
|
||||
|
||||
#########################################################################
|
||||
#
|
||||
# INSTALL-TIME CONFIGURATION
|
||||
#
|
||||
# These values will be set during the installation process. During
|
||||
# development, there will be no 'viewvcinstallpath.py'
|
||||
# development, they will remain None.
|
||||
#
|
||||
|
||||
import viewvcinstallpath
|
||||
LIBRARY_DIR = viewvcinstallpath.LIBRARY_DIR
|
||||
CONF_PATHNAME = viewvcinstallpath.CONF_PATHNAME
|
||||
LIBRARY_DIR = None
|
||||
CONF_PATHNAME = None
|
||||
|
||||
#########################################################################
|
||||
#
|
||||
|
18
bin/mod_python/viewvc.py
Executable file → Normal file
18
bin/mod_python/viewvc.py
Executable file → Normal file
@@ -1,7 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
@@ -22,15 +21,15 @@
|
||||
#
|
||||
|
||||
#########################################################################
|
||||
#
|
||||
# INSTALL-TIME CONFIGURATION
|
||||
#
|
||||
# These values will be set during the installation process. During
|
||||
# development, there will be no 'viewvcinstallpath.py'
|
||||
# development, they will remain None.
|
||||
#
|
||||
|
||||
import viewvcinstallpath
|
||||
LIBRARY_DIR = viewvcinstallpath.LIBRARY_DIR
|
||||
CONF_PATHNAME = viewvcinstallpath.CONF_PATHNAME
|
||||
LIBRARY_DIR = None
|
||||
CONF_PATHNAME = None
|
||||
|
||||
#########################################################################
|
||||
#
|
||||
@@ -44,13 +43,6 @@ if LIBRARY_DIR:
|
||||
|
||||
import sapi
|
||||
import imp
|
||||
import signal
|
||||
|
||||
# Totally ignore SIGPIPE - needed for overcoming popen() errors
|
||||
def sigpipe(signum, frame):
|
||||
pass
|
||||
|
||||
signal.signal(signal.SIGPIPE, sigpipe)
|
||||
|
||||
# Import real ViewVC module
|
||||
fp, pathname, description = imp.find_module('viewvc', [LIBRARY_DIR])
|
||||
|
@@ -1,57 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
CVSROOTS=$1
|
||||
VIEWVC_DIR=$2
|
||||
CVS_USER=$3
|
||||
CVS_GROUP=$4
|
||||
CVS_ASYNC=$5
|
||||
|
||||
if [ ! "$CVSROOTS" -o ! "$VIEWVC_DIR" -o ! "$CVS_USER" -o ! "$CVS_GROUP" ]; then
|
||||
echo "USAGE: $0 <CVS_ROOTS> <VIEWVC_DIR> <CVS_USER> <CVS_GROUP> [CVS_ASYNC]"
|
||||
exit
|
||||
fi
|
||||
|
||||
echo "*** Setting up commit hooks for CVS repositories (job=$CVS_ASYNC)"
|
||||
chgrp $CVS_GROUP $VIEWVC_DIR
|
||||
chmod 775 $VIEWVC_DIR
|
||||
chmod 1777 .
|
||||
for CVSROOT in $CVSROOTS; do
|
||||
vcrootname=`basename $CVSROOT`
|
||||
if [ "$CVS_ASYNC" ]; then
|
||||
VHOOK="touch $VIEWVC_DIR/.cvs-updated-$vcrootname"
|
||||
else
|
||||
VHOOK="$VIEWVC_DIR/bin/cvsdbadmin update $CVSROOT >/dev/null"
|
||||
fi;
|
||||
su $CVS_USER -s /bin/sh <<EOSH
|
||||
CVSROOT=$CVSROOT cvs co CVSROOT
|
||||
if [ $? -eq 0 ]; then
|
||||
cd CVSROOT
|
||||
mv loginfo loginfo.bak
|
||||
grep -v '/.cvs-updated' <loginfo.bak | grep -v '/bin/cvsdbadmin update' >loginfo
|
||||
cat >>loginfo <<EOF
|
||||
ALL $VHOOK
|
||||
EOF
|
||||
cvs ci -m 'CVS commit hook for ViewVC' loginfo
|
||||
cd ..
|
||||
rm -Rf CVSROOT
|
||||
fi
|
||||
EOSH
|
||||
if [ "$CVS_ASYNC" ]; then
|
||||
cat >$VIEWVC_DIR/cvshook-$vcrootname <<EOF
|
||||
#!/bin/sh
|
||||
if [ -e "$VIEWVC_DIR/.cvs-updated-$vcrootname" ]; then
|
||||
rm "$VIEWVC_DIR/.cvs-updated-$vcrootname"
|
||||
$VIEWVC_DIR/bin/cvsdbadmin update $CVSROOT >/dev/null
|
||||
fi
|
||||
EOF
|
||||
chmod 755 $VIEWVC_DIR/cvshook-$vcrootname
|
||||
cat >/etc/cron.d/viewvc-cvs-$vcrootname <<EOF
|
||||
*/5 * * * * root $VIEWVC_DIR/cvshook-$vcrootname
|
||||
EOF
|
||||
fi
|
||||
done;
|
||||
|
||||
if [ "$CVS_ASYNC" ]; then
|
||||
echo "*** Reloading cron daemon"
|
||||
/etc/init.d/cron reload
|
||||
fi
|
@@ -1,36 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
SVNROOT=$1
|
||||
VIEWVC_DIR=$2
|
||||
SVN_ASYNC=$3
|
||||
|
||||
if [ ! "$SVNROOT" -o ! "$VIEWVC_DIR" ]; then
|
||||
echo "USAGE: $0 <SVNROOT> <VIEWVC_DIR>"
|
||||
exit
|
||||
fi
|
||||
|
||||
echo "*** Setting up commit hooks for Subversion"
|
||||
for i in `ls $SVNROOT`; do
|
||||
if [ -d "$SVNROOT/$i" ]; then
|
||||
if [ "$SVN_ASYNC" ]; then
|
||||
cat >"$SVNROOT/$i/hooks/post-commit.tmp" <<EOF
|
||||
#!/bin/sh
|
||||
echo "\$1 \$2" >> $VIEWVC_DIR/.svn-updated
|
||||
EOF
|
||||
else
|
||||
cat >"$SVNROOT/$i/hooks/post-commit.tmp" <<EOF
|
||||
#!/bin/sh
|
||||
$VIEWVC_DIR/bin/svndbadmin update \$1 \$2
|
||||
EOF
|
||||
fi
|
||||
chmod 755 "$SVNROOT/$i/hooks/post-commit.tmp"
|
||||
mv "$SVNROOT/$i/hooks/post-commit.tmp" "$SVNROOT/$i/hooks/post-commit"
|
||||
fi
|
||||
done;
|
||||
|
||||
if [ "$SVN_ASYNC" ]; then
|
||||
cat >/etc/cron.d/viewvc-svn <<EOF
|
||||
*/10 * * * * root $VIEWVC_DIR/bin/svnupdate-async.sh "$VIEWVC_DIR"
|
||||
EOF
|
||||
/etc/init.d/cron reload
|
||||
fi
|
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 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
|
||||
@@ -10,26 +10,27 @@
|
||||
# For more information, visit http://viewvc.org/
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
# This program originally written by Peter Funk <pf@artcom-gmbh.de>, with
|
||||
# contributions by Ka-Ping Yee.
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
"""Run "standalone.py -p <port>" to start an HTTP server on a given port
|
||||
on the local machine to generate ViewVC web pages.
|
||||
"""
|
||||
|
||||
#
|
||||
__author__ = "Peter Funk <pf@artcom-gmbh.de>"
|
||||
__date__ = "11 November 2001"
|
||||
__version__ = "$Revision$"
|
||||
__credits__ = """Guido van Rossum, for an excellent programming language.
|
||||
Greg Stein, for writing ViewCVS in the first place.
|
||||
Ka-Ping Yee, for the GUI code and the framework stolen from pydoc.py.
|
||||
"""
|
||||
|
||||
# INSTALL-TIME CONFIGURATION
|
||||
#
|
||||
# These values will be set during the installation process. During
|
||||
# development, there will be no 'viewvcinstallpath.py'
|
||||
# development, they will remain None.
|
||||
#
|
||||
|
||||
import viewvcinstallpath
|
||||
LIBRARY_DIR = viewvcinstallpath.LIBRARY_DIR
|
||||
CONF_PATHNAME = viewvcinstallpath.CONF_PATHNAME
|
||||
LIBRARY_DIR = None
|
||||
CONF_PATHNAME = None
|
||||
|
||||
import sys
|
||||
import os
|
||||
@@ -50,6 +51,7 @@ else:
|
||||
|
||||
import sapi
|
||||
import viewvc
|
||||
import compat; compat.for_standalone()
|
||||
|
||||
|
||||
# The 'crypt' module is only available on Unix platforms. We'll try
|
||||
@@ -96,7 +98,7 @@ class StandaloneServer(sapi.CgiServer):
|
||||
statusCode = 200
|
||||
statusText = 'OK'
|
||||
else:
|
||||
p = status.find(' ')
|
||||
p = string.find(status, ' ')
|
||||
if p < 0:
|
||||
statusCode = int(status)
|
||||
statusText = ''
|
||||
@@ -195,7 +197,7 @@ class ViewVCHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
|
||||
try:
|
||||
lines = open(htpasswd_file, 'r').readlines()
|
||||
for line in lines:
|
||||
file_user, file_pass = line.rstrip().split(':', 1)
|
||||
file_user, file_pass = string.split(line.rstrip(), ':', 1)
|
||||
if username == file_user:
|
||||
return _check_passwd(password, file_pass)
|
||||
except:
|
||||
@@ -219,10 +221,10 @@ class ViewVCHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
|
||||
if not authn:
|
||||
raise AuthenticationException()
|
||||
try:
|
||||
kind, data = authn.split(' ', 1)
|
||||
kind, data = string.split(authn, ' ', 1)
|
||||
if kind == 'Basic':
|
||||
data = base64.b64decode(data)
|
||||
username, password = data.split(':', 1)
|
||||
username, password = string.split(data, ':', 1)
|
||||
except:
|
||||
raise AuthenticationException()
|
||||
if not self.validate_password(options.htpasswd_file, username, password):
|
||||
@@ -236,7 +238,7 @@ class ViewVCHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
|
||||
|
||||
viewvc_url = self.server.url[:-1] + scriptname
|
||||
rest = self.path[len(scriptname):]
|
||||
i = rest.rfind('?')
|
||||
i = string.rfind(rest, '?')
|
||||
if i >= 0:
|
||||
rest, query = rest[:i], rest[i+1:]
|
||||
else:
|
||||
@@ -281,10 +283,10 @@ class ViewVCHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
|
||||
accept = []
|
||||
for line in self.headers.getallmatchingheaders('accept'):
|
||||
if line[:1] in string.whitespace:
|
||||
accept.append(line.strip())
|
||||
accept.append(string.strip(line))
|
||||
else:
|
||||
accept = accept + line[7:].split(',')
|
||||
env['HTTP_ACCEPT'] = ','.join(accept)
|
||||
accept = accept + string.split(line[7:], ',')
|
||||
env['HTTP_ACCEPT'] = string.joinfields(accept, ',')
|
||||
ua = self.headers.getheader('user-agent')
|
||||
if ua:
|
||||
env['HTTP_USER_AGENT'] = ua
|
||||
@@ -337,7 +339,6 @@ class ViewVCHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
|
||||
else:
|
||||
self.log_error("ViewVC exited ok")
|
||||
|
||||
|
||||
class ViewVCHTTPServer(BaseHTTPServer.HTTPServer):
|
||||
"""Customized HTTP server for ViewVC."""
|
||||
|
||||
@@ -365,7 +366,6 @@ class ViewVCHTTPServer(BaseHTTPServer.HTTPServer):
|
||||
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
BaseHTTPServer.HTTPServer.server_bind(self)
|
||||
|
||||
|
||||
def serve(host, port, callback=None):
|
||||
"""Start an HTTP server for HOST on PORT. Call CALLBACK function
|
||||
when the server is ready to serve."""
|
||||
@@ -406,7 +406,7 @@ def serve(host, port, callback=None):
|
||||
line = fp.readline()
|
||||
if not line:
|
||||
break
|
||||
if line.find("Concurrent Versions System (CVSNT)") >= 0:
|
||||
if string.find(line, "Concurrent Versions System (CVSNT)") >= 0:
|
||||
cvsnt_works = 1
|
||||
while fp.read(4096):
|
||||
pass
|
||||
@@ -423,11 +423,288 @@ def serve(host, port, callback=None):
|
||||
pass
|
||||
print 'server stopped'
|
||||
|
||||
|
||||
def handle_config(config_file):
|
||||
global cfg
|
||||
cfg = viewvc.load_config(config_file or CONF_PATHNAME)
|
||||
|
||||
# --- graphical interface: --------------------------------------------------
|
||||
|
||||
def nogui(missing_module):
|
||||
sys.stderr.write("""\
|
||||
Sorry! Your Python was compiled without the %s module enabled.
|
||||
I'm unable to run the GUI part. Please omit the '-g' and '--gui' options,
|
||||
or install another Python interpreter.
|
||||
""" % (missing_module))
|
||||
raise SystemExit, 1
|
||||
|
||||
def gui(host, port):
|
||||
"""Graphical interface (starts web server and pops up a control window)."""
|
||||
class GUI:
|
||||
def __init__(self, window, host, port):
|
||||
self.window = window
|
||||
self.server = None
|
||||
self.scanner = None
|
||||
|
||||
try:
|
||||
import Tkinter
|
||||
except ImportError:
|
||||
nogui("Tkinter")
|
||||
|
||||
self.server_frm = Tkinter.Frame(window)
|
||||
self.title_lbl = Tkinter.Label(self.server_frm,
|
||||
text='Starting server...\n ')
|
||||
self.open_btn = Tkinter.Button(self.server_frm,
|
||||
text='open browser',
|
||||
command=self.open,
|
||||
state='disabled')
|
||||
self.quit_btn = Tkinter.Button(self.server_frm,
|
||||
text='quit serving',
|
||||
command=self.quit,
|
||||
state='disabled')
|
||||
|
||||
self.window.title('ViewVC standalone')
|
||||
self.window.protocol('WM_DELETE_WINDOW', self.quit)
|
||||
self.title_lbl.pack(side='top', fill='x')
|
||||
self.open_btn.pack(side='left', fill='x', expand=1)
|
||||
self.quit_btn.pack(side='right', fill='x', expand=1)
|
||||
|
||||
# Early loading of configuration here. Used to
|
||||
# allow tinkering with configuration settings through the gui:
|
||||
handle_config(options.config_file)
|
||||
if not LIBRARY_DIR:
|
||||
cfg.options.cvsgraph_conf = "../cgi/cvsgraph.conf.dist"
|
||||
|
||||
self.options_frm = Tkinter.Frame(window)
|
||||
|
||||
# cvsgraph toggle:
|
||||
self.cvsgraph_ivar = Tkinter.IntVar()
|
||||
self.cvsgraph_ivar.set(cfg.options.use_cvsgraph)
|
||||
self.cvsgraph_toggle = \
|
||||
Tkinter.Checkbutton(self.options_frm,
|
||||
text="enable cvsgraph (needs binary)",
|
||||
var=self.cvsgraph_ivar,
|
||||
command=self.toggle_use_cvsgraph)
|
||||
self.cvsgraph_toggle.pack(side='top', anchor='w')
|
||||
|
||||
# show_subdir_lastmod toggle:
|
||||
self.subdirmod_ivar = Tkinter.IntVar()
|
||||
self.subdirmod_ivar.set(cfg.options.show_subdir_lastmod)
|
||||
self.subdirmod_toggle = \
|
||||
Tkinter.Checkbutton(self.options_frm,
|
||||
text="show subdir last mod (dir view)",
|
||||
var=self.subdirmod_ivar,
|
||||
command=self.toggle_subdirmod)
|
||||
self.subdirmod_toggle.pack(side='top', anchor='w')
|
||||
|
||||
# use_re_search toggle:
|
||||
self.useresearch_ivar = Tkinter.IntVar()
|
||||
self.useresearch_ivar.set(cfg.options.use_re_search)
|
||||
self.useresearch_toggle = \
|
||||
Tkinter.Checkbutton(self.options_frm,
|
||||
text="allow regular expr search",
|
||||
var=self.useresearch_ivar,
|
||||
command=self.toggle_useresearch)
|
||||
self.useresearch_toggle.pack(side='top', anchor='w')
|
||||
|
||||
# use_localtime toggle:
|
||||
self.use_localtime_ivar = Tkinter.IntVar()
|
||||
self.use_localtime_ivar.set(cfg.options.use_localtime)
|
||||
self.use_localtime_toggle = \
|
||||
Tkinter.Checkbutton(self.options_frm,
|
||||
text="use localtime (instead of UTC)",
|
||||
var=self.use_localtime_ivar,
|
||||
command=self.toggle_use_localtime)
|
||||
self.use_localtime_toggle.pack(side='top', anchor='w')
|
||||
|
||||
# log_pagesize integer var:
|
||||
self.log_pagesize_lbl = \
|
||||
Tkinter.Label(self.options_frm,
|
||||
text='number of items per log page (0 disables):')
|
||||
self.log_pagesize_lbl.pack(side='top', anchor='w')
|
||||
self.log_pagesize_ivar = Tkinter.IntVar()
|
||||
self.log_pagesize_ivar.set(cfg.options.log_pagesize)
|
||||
self.log_pagesize_entry = \
|
||||
Tkinter.Entry(self.options_frm,
|
||||
width=10,
|
||||
textvariable=self.log_pagesize_ivar)
|
||||
self.log_pagesize_entry.bind('<Return>', self.set_log_pagesize)
|
||||
self.log_pagesize_entry.pack(side='top', anchor='w')
|
||||
|
||||
# dir_pagesize integer var:
|
||||
self.dir_pagesize_lbl = \
|
||||
Tkinter.Label(self.options_frm,
|
||||
text='number of items per dir page (0 disables):')
|
||||
self.dir_pagesize_lbl.pack(side='top', anchor='w')
|
||||
self.dir_pagesize_ivar = Tkinter.IntVar()
|
||||
self.dir_pagesize_ivar.set(cfg.options.dir_pagesize)
|
||||
self.dir_pagesize_entry = \
|
||||
Tkinter.Entry(self.options_frm,
|
||||
width=10,
|
||||
textvariable=self.dir_pagesize_ivar)
|
||||
self.dir_pagesize_entry.bind('<Return>', self.set_dir_pagesize)
|
||||
self.dir_pagesize_entry.pack(side='top', anchor='w')
|
||||
|
||||
# directory view template:
|
||||
self.dirtemplate_lbl = \
|
||||
Tkinter.Label(self.options_frm,
|
||||
text='Choose HTML Template for the Directory pages:')
|
||||
self.dirtemplate_lbl.pack(side='top', anchor='w')
|
||||
self.dirtemplate_svar = Tkinter.StringVar()
|
||||
self.dirtemplate_svar.set(cfg.templates.directory)
|
||||
self.dirtemplate_entry = \
|
||||
Tkinter.Entry(self.options_frm,
|
||||
width=40,
|
||||
textvariable=self.dirtemplate_svar)
|
||||
self.dirtemplate_entry.bind('<Return>', self.set_templates_directory)
|
||||
self.dirtemplate_entry.pack(side='top', anchor='w')
|
||||
self.templates_dir = \
|
||||
Tkinter.Radiobutton(self.options_frm,
|
||||
text="directory.ezt",
|
||||
value="templates/directory.ezt",
|
||||
var=self.dirtemplate_svar,
|
||||
command=self.set_templates_directory)
|
||||
self.templates_dir.pack(side='top', anchor='w')
|
||||
self.templates_dir_alt = \
|
||||
Tkinter.Radiobutton(self.options_frm,
|
||||
text="dir_alternate.ezt",
|
||||
value="templates/dir_alternate.ezt",
|
||||
var=self.dirtemplate_svar,
|
||||
command=self.set_templates_directory)
|
||||
self.templates_dir_alt.pack(side='top', anchor='w')
|
||||
|
||||
# log view template:
|
||||
self.logtemplate_lbl = \
|
||||
Tkinter.Label(self.options_frm,
|
||||
text='Choose HTML Template for the Log pages:')
|
||||
self.logtemplate_lbl.pack(side='top', anchor='w')
|
||||
self.logtemplate_svar = Tkinter.StringVar()
|
||||
self.logtemplate_svar.set(cfg.templates.log)
|
||||
self.logtemplate_entry = \
|
||||
Tkinter.Entry(self.options_frm,
|
||||
width=40,
|
||||
textvariable=self.logtemplate_svar)
|
||||
self.logtemplate_entry.bind('<Return>', self.set_templates_log)
|
||||
self.logtemplate_entry.pack(side='top', anchor='w')
|
||||
self.templates_log = \
|
||||
Tkinter.Radiobutton(self.options_frm,
|
||||
text="log.ezt",
|
||||
value="templates/log.ezt",
|
||||
var=self.logtemplate_svar,
|
||||
command=self.set_templates_log)
|
||||
self.templates_log.pack(side='top', anchor='w')
|
||||
self.templates_log_table = \
|
||||
Tkinter.Radiobutton(self.options_frm,
|
||||
text="log_table.ezt",
|
||||
value="templates/log_table.ezt",
|
||||
var=self.logtemplate_svar,
|
||||
command=self.set_templates_log)
|
||||
self.templates_log_table.pack(side='top', anchor='w')
|
||||
|
||||
# query view template:
|
||||
self.querytemplate_lbl = \
|
||||
Tkinter.Label(self.options_frm,
|
||||
text='Template for the database query page:')
|
||||
self.querytemplate_lbl.pack(side='top', anchor='w')
|
||||
self.querytemplate_svar = Tkinter.StringVar()
|
||||
self.querytemplate_svar.set(cfg.templates.query)
|
||||
self.querytemplate_entry = \
|
||||
Tkinter.Entry(self.options_frm,
|
||||
width=40,
|
||||
textvariable=self.querytemplate_svar)
|
||||
self.querytemplate_entry.bind('<Return>', self.set_templates_query)
|
||||
self.querytemplate_entry.pack(side='top', anchor='w')
|
||||
self.templates_query = \
|
||||
Tkinter.Radiobutton(self.options_frm,
|
||||
text="query.ezt",
|
||||
value="templates/query.ezt",
|
||||
var=self.querytemplate_svar,
|
||||
command=self.set_templates_query)
|
||||
self.templates_query.pack(side='top', anchor='w')
|
||||
|
||||
# pack and set window manager hints:
|
||||
self.server_frm.pack(side='top', fill='x')
|
||||
self.options_frm.pack(side='top', fill='x')
|
||||
|
||||
self.window.update()
|
||||
self.minwidth = self.window.winfo_width()
|
||||
self.minheight = self.window.winfo_height()
|
||||
self.expanded = 0
|
||||
self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
|
||||
self.window.wm_minsize(self.minwidth, self.minheight)
|
||||
|
||||
try:
|
||||
import threading
|
||||
except ImportError:
|
||||
nogui("thread")
|
||||
threading.Thread(target=serve, args=(host, port, self.ready)).start()
|
||||
|
||||
def toggle_use_cvsgraph(self, event=None):
|
||||
cfg.options.use_cvsgraph = self.cvsgraph_ivar.get()
|
||||
|
||||
def toggle_use_localtime(self, event=None):
|
||||
cfg.options.use_localtime = self.use_localtime_ivar.get()
|
||||
|
||||
def toggle_subdirmod(self, event=None):
|
||||
cfg.options.show_subdir_lastmod = self.subdirmod_ivar.get()
|
||||
|
||||
def toggle_useresearch(self, event=None):
|
||||
cfg.options.use_re_search = self.useresearch_ivar.get()
|
||||
|
||||
def set_log_pagesize(self, event=None):
|
||||
cfg.options.log_pagesize = self.log_pagesize_ivar.get()
|
||||
|
||||
def set_dir_pagesize(self, event=None):
|
||||
cfg.options.dir_pagesize = self.dir_pagesize_ivar.get()
|
||||
|
||||
def set_templates_log(self, event=None):
|
||||
cfg.templates.log = self.logtemplate_svar.get()
|
||||
|
||||
def set_templates_directory(self, event=None):
|
||||
cfg.templates.directory = self.dirtemplate_svar.get()
|
||||
|
||||
def set_templates_query(self, event=None):
|
||||
cfg.templates.query = self.querytemplate_svar.get()
|
||||
|
||||
def ready(self, server):
|
||||
"""used as callback parameter to the serve() function"""
|
||||
self.server = server
|
||||
self.title_lbl.config(text='ViewVC standalone server at\n' + server.url)
|
||||
self.open_btn.config(state='normal')
|
||||
self.quit_btn.config(state='normal')
|
||||
|
||||
def open(self, event=None, url=None):
|
||||
"""opens a browser window on the local machine"""
|
||||
url = url or self.server.url
|
||||
try:
|
||||
import webbrowser
|
||||
webbrowser.open(url)
|
||||
except ImportError: # pre-webbrowser.py compatibility
|
||||
if sys.platform == 'win32':
|
||||
os.system('start "%s"' % url)
|
||||
elif sys.platform == 'mac':
|
||||
try:
|
||||
import ic
|
||||
ic.launchurl(url)
|
||||
except ImportError: pass
|
||||
else:
|
||||
rc = os.system('netscape -remote "openURL(%s)" &' % url)
|
||||
if rc:
|
||||
os.system('netscape "%s" &' % url)
|
||||
|
||||
def quit(self, event=None):
|
||||
if self.server:
|
||||
self.server.quit = 1
|
||||
self.window.quit()
|
||||
|
||||
import Tkinter
|
||||
try:
|
||||
gui = GUI(Tkinter.Tk(), host, port)
|
||||
Tkinter.mainloop()
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
|
||||
# --- command-line interface: ----------------------------------------------
|
||||
|
||||
|
||||
def usage():
|
||||
clean_options = Options()
|
||||
@@ -444,10 +721,13 @@ Options:
|
||||
--config-file=FILE (-c) Read configuration options from FILE. If not
|
||||
specified, ViewVC will look for a configuration
|
||||
file in its installation tree, falling back to
|
||||
built-in default values.
|
||||
built-in default values. (Not valid in GUI mode.)
|
||||
|
||||
--daemon (-d) Background the server process.
|
||||
|
||||
--gui (-g) Pop up a graphical configuration interface.
|
||||
Requires a valid X11 display connection.
|
||||
|
||||
--help Show this usage message and exit.
|
||||
|
||||
--host=HOSTNAME (-h) Listen on HOSTNAME. Required for access from a
|
||||
@@ -483,15 +763,17 @@ def main(argv):
|
||||
"""Command-line interface (looks at argv to decide what to do)."""
|
||||
import getopt
|
||||
|
||||
short_opts = ''.join(['c:',
|
||||
'd',
|
||||
'h:',
|
||||
'p:',
|
||||
'r:',
|
||||
's:',
|
||||
])
|
||||
short_opts = string.join(['c:',
|
||||
'd',
|
||||
'g',
|
||||
'h:',
|
||||
'p:',
|
||||
'r:',
|
||||
's:',
|
||||
], '')
|
||||
long_opts = ['daemon',
|
||||
'config-file=',
|
||||
'gui',
|
||||
'help',
|
||||
'host=',
|
||||
'htpasswd-file=',
|
||||
@@ -500,7 +782,8 @@ def main(argv):
|
||||
'script-alias=',
|
||||
]
|
||||
|
||||
opt_daemon = False
|
||||
opt_daemon = 0
|
||||
opt_gui = 0
|
||||
opt_host = None
|
||||
opt_port = None
|
||||
opt_htpasswd_file = None
|
||||
@@ -514,6 +797,8 @@ def main(argv):
|
||||
for opt, val in opts:
|
||||
if opt in ['--help']:
|
||||
usage()
|
||||
elif opt in ['-g', '--gui']:
|
||||
opt_gui = 1
|
||||
elif opt in ['-r', '--repository']: # may be used more than once
|
||||
opt_repositories.append(val)
|
||||
elif opt in ['-d', '--daemon']:
|
||||
@@ -561,7 +846,10 @@ def main(argv):
|
||||
if opt_host is not None:
|
||||
options.host = opt_host
|
||||
if opt_script_alias is not None:
|
||||
options.script_alias = '/'.join(filter(None, opt_script_alias.split('/')))
|
||||
options.script_alias = string.join(filter(None,
|
||||
string.split(opt_script_alias,
|
||||
'/')),
|
||||
'/')
|
||||
for repository in opt_repositories:
|
||||
if not options.repositories.has_key('Development'):
|
||||
rootname = 'Development'
|
||||
@@ -578,9 +866,12 @@ def main(argv):
|
||||
sys.exit()
|
||||
|
||||
# Finaly, start the server.
|
||||
def ready(server):
|
||||
print 'server ready at %s%s' % (server.url, options.script_alias)
|
||||
serve(options.host, options.port, ready)
|
||||
if opt_gui:
|
||||
gui(options.host, options.port)
|
||||
else:
|
||||
def ready(server):
|
||||
print 'server ready at %s%s' % (server.url, options.script_alias)
|
||||
serve(options.host, options.port, ready)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
237
bin/svndbadmin
237
bin/svndbadmin
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 2004-2013 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 2004-2012 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 2004-2007 James Henstridge
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
@@ -35,15 +35,15 @@
|
||||
#
|
||||
|
||||
#########################################################################
|
||||
#
|
||||
# INSTALL-TIME CONFIGURATION
|
||||
#
|
||||
# These values will be set during the installation process. During
|
||||
# development, there will be no 'viewvcinstallpath.py'
|
||||
# development, they will remain None.
|
||||
#
|
||||
|
||||
import viewvcinstallpath
|
||||
LIBRARY_DIR = viewvcinstallpath.LIBRARY_DIR
|
||||
CONF_PATHNAME = viewvcinstallpath.CONF_PATHNAME
|
||||
LIBRARY_DIR = None
|
||||
CONF_PATHNAME = None
|
||||
|
||||
# Adjust sys.path to include our library directory
|
||||
import sys
|
||||
@@ -58,11 +58,7 @@ else:
|
||||
|
||||
import os
|
||||
import string
|
||||
import socket
|
||||
import select
|
||||
import re
|
||||
import mimetypes
|
||||
import time
|
||||
|
||||
import svn.core
|
||||
import svn.repos
|
||||
@@ -72,21 +68,14 @@ import svn.delta
|
||||
import cvsdb
|
||||
import viewvc
|
||||
import vclib
|
||||
from viewvcmagic import ContentMagic
|
||||
|
||||
class SvnRepo:
|
||||
"""Class used to manage a connection to a SVN repository."""
|
||||
def __init__(self, path, index_content = None, tika_client = None, guesser = None,
|
||||
svn_ignore_mimetype = False, verbose = False):
|
||||
def __init__(self, path):
|
||||
self.path = path
|
||||
self.repo = svn.repos.svn_repos_open(path)
|
||||
self.fs = svn.repos.svn_repos_fs(self.repo)
|
||||
self.rev_max = svn.fs.youngest_rev(self.fs)
|
||||
self.index_content = index_content
|
||||
self.tika_client = tika_client
|
||||
self.guesser = guesser
|
||||
self.verbose = verbose
|
||||
self.svn_ignore_mimetype = svn_ignore_mimetype
|
||||
def __getitem__(self, rev):
|
||||
if rev is None:
|
||||
rev = self.rev_max
|
||||
@@ -96,36 +85,7 @@ class SvnRepo:
|
||||
rev = SvnRev(self, rev)
|
||||
return rev
|
||||
|
||||
_re_diff_change_command = re.compile('^(\d+)(?:,(\d+))?([acd])(\d+)(?:,(\d+))?')
|
||||
|
||||
class StupidBufferedReader:
|
||||
def __init__(self, fp, buffer = 262144):
|
||||
self.fp = fp
|
||||
self.bufsize = buffer
|
||||
self.buffer = ''
|
||||
self.eof = False
|
||||
def __iter__(self):
|
||||
return self
|
||||
def next(self):
|
||||
if self.eof:
|
||||
raise StopIteration
|
||||
return self.readline()
|
||||
def readline(self):
|
||||
if self.eof:
|
||||
return ''
|
||||
p = self.buffer.find('\n')
|
||||
while p < 0:
|
||||
b = self.fp.read(self.bufsize)
|
||||
if not len(b):
|
||||
r = self.buffer
|
||||
self.buffer = ''
|
||||
self.eof = True
|
||||
return r
|
||||
self.buffer = self.buffer + b
|
||||
p = self.buffer.find('\n')
|
||||
r = self.buffer[0:p+1]
|
||||
self.buffer = self.buffer[p+1:]
|
||||
return r
|
||||
_re_diff_change_command = re.compile('(\d+)(?:,(\d+))?([acd])(\d+)(?:,(\d+))?')
|
||||
|
||||
def _get_diff_counts(diff_fp):
|
||||
"""Calculate the plus/minus counts by parsing the output of a
|
||||
@@ -138,7 +98,8 @@ def _get_diff_counts(diff_fp):
|
||||
GNU diff manual."""
|
||||
|
||||
plus, minus = 0, 0
|
||||
for line in diff_fp:
|
||||
line = diff_fp.readline()
|
||||
while line:
|
||||
match = re.match(_re_diff_change_command, line)
|
||||
if match:
|
||||
# size of first range
|
||||
@@ -164,79 +125,9 @@ def _get_diff_counts(diff_fp):
|
||||
# RdL - remove range R of file1, which would have been
|
||||
# at line L of file2
|
||||
minus = minus + count1
|
||||
line = diff_fp.readline()
|
||||
return plus, minus
|
||||
|
||||
class TikaClient:
|
||||
# Create tika client
|
||||
def __init__(self, tika_server, mime_types, verbose):
|
||||
self.tika_server = tika_server
|
||||
self.mime_types = mime_types
|
||||
self.verbose = verbose
|
||||
self.addr = tika_server.split(':')
|
||||
# Split address
|
||||
if len(self.addr) != 2:
|
||||
raise Exception('tika_server value is incorrect: \''+tika_server+'\', please use \'host:port\' format')
|
||||
self.addr = (self.addr[0], int(self.addr[1]))
|
||||
# Build regexp for MIME types
|
||||
m = re.split('\s+', mime_types.strip())
|
||||
self.mime_regexp = re.compile('|'.join('^'+re.escape(i).replace('\\*', '.*')+'$' for i in m))
|
||||
|
||||
# Extract text content from file using Tika which runs in server mode
|
||||
def get_text(self, filename, mime_type, log_filename):
|
||||
if not self.mime_regexp.match(mime_type):
|
||||
# Tika can't handle this mime type, return nothing
|
||||
return ''
|
||||
fd = None
|
||||
s = None
|
||||
text = ''
|
||||
fsize = 0
|
||||
try:
|
||||
# Read original file
|
||||
fd = open(filename, 'rb')
|
||||
data = fd.read()
|
||||
fsize = len(data)
|
||||
if not fsize:
|
||||
return ''
|
||||
# Connect to Tika
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
s.connect(self.addr)
|
||||
s.setblocking(0)
|
||||
sockfd = s.fileno()
|
||||
# Tika is somewhat delicate about network IO, so:
|
||||
# Read and write using poll(2) system call
|
||||
p = select.poll()
|
||||
p.register(sockfd)
|
||||
while 1:
|
||||
fds = p.poll()
|
||||
if not fds:
|
||||
break
|
||||
(pollfd, event) = fds[0]
|
||||
if event & select.POLLIN:
|
||||
# Exception or empty data means EOF...
|
||||
try: part = os.read(sockfd, 65536)
|
||||
except: break
|
||||
if not part: break
|
||||
text += part
|
||||
if event & select.POLLOUT:
|
||||
if not len(data):
|
||||
# Shutdown output and forget about POLLOUT
|
||||
s.shutdown(socket.SHUT_WR)
|
||||
p.modify(sockfd, select.POLLIN)
|
||||
else:
|
||||
# Write and consume some data
|
||||
l = os.write(sockfd, data)
|
||||
data = data[l:]
|
||||
if len(text) == 0:
|
||||
raise Exception('Empty response from Tika server')
|
||||
if self.verbose:
|
||||
print "Extracted %d bytes from %s (%s) of size %d" % (len(text), log_filename, mime_type, fsize)
|
||||
except Exception, e:
|
||||
if self.verbose:
|
||||
print "Error extracting text from %s (%s) of size %d: %s" % (log_filename, mime_type, fsize, str(e))
|
||||
finally:
|
||||
if fd: fd.close()
|
||||
if s: s.close()
|
||||
return text
|
||||
|
||||
class SvnRev:
|
||||
"""Class used to hold information about a particular revision of
|
||||
@@ -260,14 +151,13 @@ class SvnRev:
|
||||
|
||||
# get a root for the current revisions
|
||||
fsroot = self._get_root_for_rev(rev)
|
||||
|
||||
|
||||
# find changes in the revision
|
||||
editor = svn.repos.ChangeCollector(repo.fs, fsroot)
|
||||
e_ptr, e_baton = svn.delta.make_editor(editor)
|
||||
svn.repos.svn_repos_replay(fsroot, e_ptr, e_baton)
|
||||
|
||||
self.changes = []
|
||||
changes_hash = {}
|
||||
for path, change in editor.changes.items():
|
||||
# skip non-file changes
|
||||
if change.item_kind != svn.core.svn_node_file:
|
||||
@@ -296,61 +186,14 @@ class SvnRev:
|
||||
action = 'change'
|
||||
|
||||
if action == 'remove':
|
||||
diffobj = svn.fs.FileDiff(base_root, change.base_path, None, None, None, ['-b', '-B'])
|
||||
diffobj = svn.fs.FileDiff(base_root, base_path, None, None)
|
||||
else:
|
||||
diffobj = svn.fs.FileDiff(base_root, change.base_path, fsroot, change.path, None, ['-b', '-B'])
|
||||
diffobj = svn.fs.FileDiff(base_root, base_path,
|
||||
fsroot, change.path)
|
||||
|
||||
diff_fp = diffobj.get_pipe()
|
||||
diff_fp = StupidBufferedReader(diff_fp)
|
||||
plus, minus = _get_diff_counts(diff_fp)
|
||||
|
||||
# CustIS Bug 50473: a workaround for svnlib behaviour in file movements (FILE1 -> FILE2 + FILE1 -> null)
|
||||
if change.base_path:
|
||||
if not change.path and change.base_path in changes_hash:
|
||||
minus = 0
|
||||
elif change.path:
|
||||
changes_hash[change.base_path] = change.path
|
||||
|
||||
content = ''
|
||||
mime = ''
|
||||
# need to check if binary file's content changed when copying,
|
||||
# if not, don't extract it, just get it from previous revision later
|
||||
if repo.index_content and action != 'remove' and change.path and (not change.base_path
|
||||
or svn.fs.contents_changed(
|
||||
base_root and base_root or None,
|
||||
base_root and change.base_path or None,
|
||||
fsroot, change.path
|
||||
)):
|
||||
props = svn.fs.node_proplist(fsroot, change.path)
|
||||
if not repo.svn_ignore_mimetype:
|
||||
mime = props.get('svn:mime-type', None)
|
||||
else:
|
||||
mime = None
|
||||
mime = repo.guesser.guess_mime(
|
||||
mime,
|
||||
os.path.basename(change.path),
|
||||
diffobj.tempfile2
|
||||
)
|
||||
# Read and guess charset by ourselves for text files
|
||||
if mime and mime.startswith('text/') or (mime.startswith('application/') and mime.endswith('xml')):
|
||||
try:
|
||||
fd = open(diffobj.tempfile2, 'rb')
|
||||
content = fd.read()
|
||||
fd.close()
|
||||
except: pass
|
||||
# Guess charset
|
||||
if content:
|
||||
content, charset = repo.guesser.guess_charset(content)
|
||||
if charset:
|
||||
content = content.encode('utf-8')
|
||||
if repo.verbose:
|
||||
print 'Guessed %s for %s' % (charset, change.path)
|
||||
elif repo.verbose:
|
||||
print 'Failed to guess charset for %s, not indexing' % (change.path, )
|
||||
# Try to extract content using Tika from binary documents
|
||||
elif repo.tika_client:
|
||||
content = repo.tika_client.get_text(diffobj.tempfile2, mime, change.path)
|
||||
self.changes.append((path, action, plus, minus, content, mime))
|
||||
self.changes.append((path, action, plus, minus))
|
||||
|
||||
def _get_root_for_rev(self, rev):
|
||||
"""Fetch a revision root from a cache of such, or a fresh root
|
||||
@@ -371,7 +214,7 @@ def handle_revision(db, command, repo, rev, verbose, force=0):
|
||||
if verbose: print "skipped (no changes)."
|
||||
return
|
||||
|
||||
for (path, action, plus, minus, content, mime) in revision.changes:
|
||||
for (path, action, plus, minus) in revision.changes:
|
||||
directory, file = os.path.split(path)
|
||||
commit = cvsdb.CreateCommit()
|
||||
commit.SetRepository(repo.path)
|
||||
@@ -384,8 +227,6 @@ def handle_revision(db, command, repo, rev, verbose, force=0):
|
||||
commit.SetPlusCount(plus)
|
||||
commit.SetMinusCount(minus)
|
||||
commit.SetBranch(None)
|
||||
commit.SetContent(content)
|
||||
commit.SetMimeType(mime)
|
||||
|
||||
if action == 'add':
|
||||
commit.SetTypeAdd()
|
||||
@@ -413,7 +254,6 @@ def main(command, repository, revs=[], verbose=0, force=0):
|
||||
cfg = viewvc.load_config(CONF_PATHNAME)
|
||||
db = cvsdb.ConnectDatabase(cfg)
|
||||
|
||||
repository = os.path.realpath(repository)
|
||||
# Purge what must be purged.
|
||||
if command in ('rebuild', 'purge'):
|
||||
if verbose:
|
||||
@@ -425,29 +265,24 @@ def main(command, repository, revs=[], verbose=0, force=0):
|
||||
sys.stderr.write("ERROR: " + str(e) + "\n")
|
||||
sys.exit(1)
|
||||
|
||||
tika_client = None
|
||||
if cfg.utilities.tika_server:
|
||||
tika_client = TikaClient(cfg.utilities.tika_server, cfg.utilities.tika_mime_types, verbose)
|
||||
repo = SvnRepo(
|
||||
path = repository,
|
||||
index_content = cfg.cvsdb.index_content,
|
||||
tika_client = tika_client,
|
||||
guesser = cfg.guesser(),
|
||||
svn_ignore_mimetype = cfg.options.svn_ignore_mimetype,
|
||||
verbose = verbose,
|
||||
)
|
||||
# Record what must be recorded.
|
||||
if command == 'rebuild' or (command == 'update' and not revs):
|
||||
for rev in range(repo.rev_max+1):
|
||||
handle_revision(db, command, repo, rev, verbose, force)
|
||||
elif command == 'update':
|
||||
if revs[0] is None:
|
||||
revs[0] = repo.rev_max
|
||||
if revs[1] is None:
|
||||
revs[1] = repo.rev_max
|
||||
revs.sort()
|
||||
for rev in range(revs[0], revs[1]+1):
|
||||
handle_revision(db, command, repo, rev, verbose, force)
|
||||
if command in ('rebuild', 'update'):
|
||||
if not os.path.exists(repository):
|
||||
sys.stderr.write('ERROR: could not find repository %s\n'
|
||||
% (repository))
|
||||
sys.exit(1)
|
||||
repo = SvnRepo(repository)
|
||||
if command == 'rebuild' or (command == 'update' and not revs):
|
||||
for rev in range(repo.rev_max+1):
|
||||
handle_revision(db, command, repo, rev, verbose)
|
||||
elif command == 'update':
|
||||
if revs[0] is None:
|
||||
revs[0] = repo.rev_max
|
||||
if revs[1] is None:
|
||||
revs[1] = repo.rev_max
|
||||
revs.sort()
|
||||
for rev in range(revs[0], revs[1]+1):
|
||||
handle_revision(db, command, repo, rev, verbose, force)
|
||||
|
||||
def _rev2int(r):
|
||||
if r == 'HEAD':
|
||||
@@ -480,7 +315,7 @@ Usage: 1. %s [-v] rebuild REPOS-PATH
|
||||
the database. If a range is specified, the revisions will be
|
||||
processed in ascending order, and you may specify "HEAD" to
|
||||
indicate "the youngest revision currently in the repository".
|
||||
|
||||
|
||||
3. Purge information specific to the repository located at REPOS-PATH
|
||||
from the database.
|
||||
|
||||
@@ -505,11 +340,11 @@ if __name__ == '__main__':
|
||||
del args[index]
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
|
||||
if len(args) < 3:
|
||||
usage()
|
||||
|
||||
command = args[1].lower()
|
||||
command = string.lower(args[1])
|
||||
if command not in ('rebuild', 'update', 'purge'):
|
||||
sys.stderr.write('ERROR: unknown command %s\n' % command)
|
||||
usage()
|
||||
|
@@ -1,50 +0,0 @@
|
||||
#!/usr/bin/perl
|
||||
# Скрипт для обновления SVN репозиториев svndbadmin-ом
|
||||
# Берёт номера ревизий и имена репозиториев из перечисленных файлов или STDIN,
|
||||
# группирует их по номерам и выводит список команд, необходимых для обновления
|
||||
|
||||
use strict;
|
||||
|
||||
# первый аргумент - путь к svndbadmin
|
||||
my $svndbadmin = shift @ARGV
|
||||
|| die "USAGE: $0 <path_to_svndbadmin> FILES...";
|
||||
|
||||
# считываем названия репозиториев и номера ревизий из файла
|
||||
my $tou = {};
|
||||
my ($repos, $rev);
|
||||
while (<>)
|
||||
{
|
||||
s/^\s+//so;
|
||||
s/\s+$//so;
|
||||
($repos, $rev) = split /\s+/, $_;
|
||||
$tou->{$repos}->{$rev} = 1;
|
||||
}
|
||||
|
||||
# превращаем номера ревизий в диапазоны ревизий
|
||||
my ($i, $j, $r, $nr);
|
||||
foreach $repos (keys %$tou)
|
||||
{
|
||||
$rev = [ sort keys %{$tou->{$repos}} ];
|
||||
$nr = [];
|
||||
$j = 0;
|
||||
for $i (1..@$rev)
|
||||
{
|
||||
if ($i > $#$rev || $rev->[$i]-$rev->[$j] > $i-$j)
|
||||
{
|
||||
$r = $rev->[$j];
|
||||
$r .= ":".$rev->[$i-1] if $i-$j > 1;
|
||||
push @$nr, $r;
|
||||
$j = $i;
|
||||
}
|
||||
}
|
||||
$tou->{$repos} = $nr;
|
||||
}
|
||||
|
||||
# выводим список команд для выполнения
|
||||
foreach $repos (keys %$tou)
|
||||
{
|
||||
foreach (@{$tou->{$repos}})
|
||||
{
|
||||
print "$svndbadmin update $repos $_\n";
|
||||
}
|
||||
}
|
@@ -1,8 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
VIEWVC_DIR=$1
|
||||
|
||||
test -f "$VIEWVC_DIR/.svn-updating" -o ! -f "$VIEWVC_DIR/.svn-updated" && exit 0
|
||||
mv "$VIEWVC_DIR/.svn-updated" "$VIEWVC_DIR/.svn-updating"
|
||||
"$VIEWVC_DIR/bin/svnupdate-async" "$VIEWVC_DIR/bin/svndbadmin" "$VIEWVC_DIR/.svn-updating" | sh &> /tmp/svnupdate-async.log
|
||||
rm "$VIEWVC_DIR/.svn-updating"
|
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/python
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 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
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 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
|
||||
|
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/python
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 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
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 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
|
||||
|
@@ -1,41 +0,0 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
# distribution or at http://viewvc.org/license-1.html.
|
||||
#
|
||||
# For more information, visit http://viewvc.org/
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
# viewvc: View CVS/SVN repositories via a web browser
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
# This is a wsgi entry point for the main ViewVC app. It's appropriate
|
||||
# for use with mod_wsgi. It defines a single application function that
|
||||
# is a valid wsgi entry point.
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
import sys, os
|
||||
|
||||
import viewvcinstallpath
|
||||
LIBRARY_DIR = viewvcinstallpath.LIBRARY_DIR
|
||||
CONF_PATHNAME = viewvcinstallpath.CONF_PATHNAME
|
||||
|
||||
if LIBRARY_DIR:
|
||||
sys.path.insert(0, LIBRARY_DIR)
|
||||
else:
|
||||
sys.path.insert(0, os.path.abspath(os.path.join(
|
||||
os.path.dirname(__file__), "../../lib")))
|
||||
import sapi
|
||||
import viewvc
|
||||
|
||||
def application(environ, start_response):
|
||||
server = sapi.WsgiServer(environ, start_response)
|
||||
cfg = viewvc.load_config(CONF_PATHNAME, server)
|
||||
viewvc.main(server, cfg)
|
||||
return []
|
@@ -1,6 +1,6 @@
|
||||
##---------------------------------------------------------------------------
|
||||
##
|
||||
## Configuration file for ViewVC (4IntraNet patched version)
|
||||
## Configuration file for ViewVC
|
||||
##
|
||||
## Information on ViewVC is located at the following web site:
|
||||
## http://viewvc.org/
|
||||
@@ -301,20 +301,13 @@
|
||||
##
|
||||
#cvsnt =
|
||||
|
||||
# ViewVC can use an inetd service instead of local "cvsnt rcsfile"
|
||||
# This is recommended for all UNIX installations which use cvsnt,
|
||||
# as there is an unpleasant non-stable bug, probably somewhere
|
||||
# inside mod_python or Apache, which SOMETIMES causes cvsnt subprocesses forked
|
||||
# from mod_python to die. This gives empty diff outputs or different errors like
|
||||
# "Error: Rlog output ended early. Expected RCS file ..."
|
||||
# This script removes forking from mod_python code and solves the issue.
|
||||
# Additional profit of this script is that you can probably browse REMOTE cvs
|
||||
# repositories, if you expose this service to ViewVC, although it is not tested.
|
||||
|
||||
# See also bin/cvsnt-rcsfile-inetd.pl
|
||||
|
||||
#rcsfile_socket = host:port
|
||||
# Example: rcsfile_socket = 127.0.0.1:8071
|
||||
## svn: Location of the Subversion command-line client, used for
|
||||
## viewing Subversion repositories.
|
||||
##
|
||||
## Example:
|
||||
## svn = /usr/bin/svn
|
||||
##
|
||||
#svn =
|
||||
|
||||
## diff: Location of the GNU diff program, used for showing file
|
||||
## version differences.
|
||||
@@ -332,32 +325,6 @@
|
||||
##
|
||||
#cvsgraph =
|
||||
|
||||
# Apache Tika TCP server host and port, used to extract text from binary documents
|
||||
# Note that as of 2011-09-12, Tika 0.9 has a bug which leads to hangups on processing
|
||||
# M$Word documents in server mode. So you must use the fixed version, downloaded from:
|
||||
# http://wiki.4intra.net/public/tika-app-0.9-fix-TIKA709.jar
|
||||
# (mirror) http://code.google.com/p/mediawiki4intranet/downloads/detail?name=tika-app-0.9-fix-TIKA709.jar
|
||||
# Or apply the patch by yourself and rebuild Tika from source, see patch here:
|
||||
# https://issues.apache.org/jira/browse/TIKA-709
|
||||
# Tika server should be started with command 'java -jar tika-app-0.9.jar -p PORT -t -eutf-8'
|
||||
|
||||
#tika_server = host:port
|
||||
# Example: tika_server = 127.0.0.1:8072
|
||||
|
||||
# This lists MIME types that can be processed by Tika
|
||||
# You may change it if your Tika is newer than 0.9 and supports more formats
|
||||
# (note) *+xml examples: xhtml+xml, rss+xml, atom+xml, docbook+xml, rdf+xml
|
||||
tika_mime_types =
|
||||
text/*
|
||||
application/*+xml
|
||||
application/xml
|
||||
application/vnd.oasis.opendocument.*
|
||||
application/vnd.openxmlformats
|
||||
application/vnd.ms-*
|
||||
application/msaccess
|
||||
application/msword
|
||||
application/pdf
|
||||
application/rtf
|
||||
|
||||
##---------------------------------------------------------------------------
|
||||
[options]
|
||||
@@ -424,24 +391,6 @@ tika_mime_types =
|
||||
##
|
||||
#allowed_views = annotate, diff, markup, roots
|
||||
|
||||
## Comma-delimited list of MIME content types (with support for fnmatch-
|
||||
## style glob characters) which are considered not-human-readable and for
|
||||
## which ViewVC will neither generate links to, nor support the direct
|
||||
## display of, non-checkout views which carry the file's content (the
|
||||
## 'markup', 'annotate', 'diff', and 'patch' views).
|
||||
##
|
||||
## NOTE: Handling of this option is given priority over ViewVC's
|
||||
## longstanding support for showing web-friendly file formats -- even
|
||||
## binary ones such as "image/jpeg" and "image/gif" -- in the 'markup'
|
||||
## view. Thus, if you add "image/*" to this list, 'markup'-view
|
||||
## display of JPEG, GIF, and PNG images will be disabled.
|
||||
##
|
||||
## Example:
|
||||
## binary_mime_types = application/octet-stream, image/*, application/pdf,
|
||||
## application/vnd*, application/msword, audio/*
|
||||
#
|
||||
#binary_mime_types =
|
||||
|
||||
## authorizer: The name of the ViewVC authorizer plugin to use when
|
||||
## authorizing access to repository contents. This value must be the
|
||||
## name of a Python module addressable as vcauth.MODULENAME (most
|
||||
@@ -543,15 +492,6 @@ tika_mime_types =
|
||||
##
|
||||
#svn_ignore_mimetype = 0
|
||||
|
||||
## max_filesize_kbytes: Limit ViewVC's processing of file contents in
|
||||
## "markup" and "annotate" views to only those files which are smaller
|
||||
## than this setting, expressed in kilobytes. Set to 0 to disable
|
||||
## this safeguard.
|
||||
##
|
||||
## NOTE: The "co" and "tar" views are unaffected by this setting.
|
||||
##
|
||||
#max_filesize_kbytes = 512
|
||||
|
||||
## svn_config_dir: Path of the Subversion runtime configuration
|
||||
## directory ViewVC should consult for various things, including cached
|
||||
## remote authentication credentials. If unset, Subversion will use
|
||||
@@ -621,7 +561,7 @@ tika_mime_types =
|
||||
## (Only works well for C source files, otherwise diff's heuristic falls short.)
|
||||
## ('-p' option to diff)
|
||||
##
|
||||
#hr_funout = 1
|
||||
#hr_funout = 0
|
||||
|
||||
## hr_ignore_white: Ignore whitespace (indendation and stuff) for human
|
||||
## readable diffs.
|
||||
@@ -662,12 +602,7 @@ tika_mime_types =
|
||||
## SEE ALSO: the [templates] configuration section, where you can
|
||||
## override templates on a per-view basis.
|
||||
##
|
||||
## Example:
|
||||
## template_dir = templates/classic
|
||||
## template_dir = templates/default
|
||||
## template_dir = templates-contrib/custom/templates
|
||||
#
|
||||
#template_dir = templates/default
|
||||
#template_dir = templates
|
||||
|
||||
## docroot: Web path to a directory that contains ViewVC static files
|
||||
## (stylesheets, images, etc.) If set, static files will get
|
||||
@@ -724,10 +659,6 @@ tika_mime_types =
|
||||
##
|
||||
#use_localtime = 0
|
||||
|
||||
## iso8601_dates: Display timestamps using a standard ISO-8601 format.
|
||||
##
|
||||
#iso8601_timestamps = 0
|
||||
|
||||
## short_log_len: The length (in characters) to which the most recent
|
||||
## log entry should be truncated when shown in the directory view.
|
||||
##
|
||||
@@ -744,17 +675,17 @@ tika_mime_types =
|
||||
##
|
||||
#enable_syntax_coloration = 1
|
||||
|
||||
## tabsize: The number of spaces into which horizontal tab characters
|
||||
## are converted when viewing file contents. Set to 0 to preserve
|
||||
## tab characters.
|
||||
## tabsize: The number of spaces into which tabstops are converted
|
||||
## when viewing file contents.
|
||||
##
|
||||
#tabsize = 8
|
||||
|
||||
## detect_encoding: Should we attempt to detect versioned file
|
||||
## character encodings? [Requires 'chardet' module, and is currently
|
||||
## used only for the 'markup' and 'annotate' views.]
|
||||
## used only by the syntax coloration logic -- if enabled -- for the
|
||||
## 'markup' and 'annotate' views; see 'enable_syntax_coloration'.]
|
||||
##
|
||||
detect_encoding = 1
|
||||
#detect_encoding = 0
|
||||
|
||||
## use_cvsgraph: Use CvsGraph to offer visual graphs of CVS revision history.
|
||||
##
|
||||
@@ -766,28 +697,6 @@ detect_encoding = 1
|
||||
##
|
||||
#cvsgraph_conf = cvsgraph.conf
|
||||
|
||||
## allowed_cvsgraph_useropts: A list of settings used for cvsgraph's
|
||||
## user-modifiable behavior which can be changed in the graph display.
|
||||
## This value is a comma-delimited list of features, taken from the
|
||||
## following set:
|
||||
## invert - Display graph upside down
|
||||
## branchbox - Add a branchbox at the tip of each branch
|
||||
## show - Display user-selected classes of revisions
|
||||
## rotate - Draw the tree left-to-right or top-to-bottom [*]
|
||||
## limittags - Allow limit on the maximum number of tags displayed
|
||||
##
|
||||
## If this option is left unset, users will not be permitted to modify
|
||||
## the graph display behavior.
|
||||
##
|
||||
## [*] WARNING: The 'rotate' option is known to cause some browsers to
|
||||
## crash due, presumably, to the display of excessively wide images.
|
||||
##
|
||||
## Example:
|
||||
## allowed_cvsgraph_useropts = limittags, show
|
||||
##
|
||||
##
|
||||
#allowed_cvsgraph_useropts =
|
||||
|
||||
## use_re_search: Enable regular expression search of files in a directory.
|
||||
##
|
||||
## WARNING: Enabling this option can consume HUGE amounts of server
|
||||
@@ -846,17 +755,6 @@ detect_encoding = 1
|
||||
##
|
||||
#limit_changes = 100
|
||||
|
||||
# You can also use primitive charset guessing instead of chardet (options.detect_encoding)
|
||||
# Just set this to the list of possible charsets in your repository.
|
||||
# ViewVC will simply try to decode content using each of them, and pick
|
||||
# the first which succeeds. UTF-8 is always tried automatically.
|
||||
#encodings = cp1251:iso-8859-1
|
||||
|
||||
# Sadly this is also required - for back-links from query results to files
|
||||
# in CVS, because it doesn't recode file names to UTF-8 as Subversion does.
|
||||
# Just set to cp1251 if you work with your CVS from Windowz.
|
||||
#cvs_ondisk_charset = cp1251
|
||||
|
||||
##---------------------------------------------------------------------------
|
||||
[templates]
|
||||
|
||||
@@ -940,10 +838,6 @@ detect_encoding = 1
|
||||
##
|
||||
#port = 3306
|
||||
|
||||
## socket: Database listening socket.
|
||||
## On Debian Linux, enable this:
|
||||
#socket = /var/run/mysqld/mysqld.sock
|
||||
|
||||
## database_name: ViewVC database name.
|
||||
##
|
||||
#database_name = ViewVC
|
||||
@@ -968,51 +862,6 @@ detect_encoding = 1
|
||||
##
|
||||
#readonly_passwd =
|
||||
|
||||
# ViewVC can use Sphinx (http://sphinxsearch.com) full-text search engine
|
||||
# to index file contents with full history and then search over them.
|
||||
# Also, Apache Tika console application can be used in TCP server mode to
|
||||
# add support for indexing binary documents (M$Word, PDF and etc).
|
||||
# See tika_server in [utilities].
|
||||
# Requires Sphinx >= 0.9.9 with a real-time updatable SphinxQL index.
|
||||
# Index must be created in sphinx.conf by hand and have the following fields:
|
||||
# rt_field = content
|
||||
# rt_attr_string = mimetype
|
||||
# rt_attr_timestamp = ci_when
|
||||
# rt_attr_uint = whoid
|
||||
# rt_attr_uint = repositoryid
|
||||
# rt_attr_uint = dirid
|
||||
# rt_attr_uint = fileid
|
||||
# rt_attr_uint = revision
|
||||
# rt_attr_uint = branchid
|
||||
|
||||
# Set to 1 to enable indexing of file contents using Sphinx and Tika
|
||||
index_content = 0
|
||||
|
||||
# Set to limit stored text file content size (max. 4MB - Sphinx limit)
|
||||
#content_max_size = 4194304
|
||||
|
||||
# Sphinx connection parameters:
|
||||
#sphinx_host =
|
||||
#sphinx_port =
|
||||
#sphinx_socket = /var/run/sphinxql.sock
|
||||
#sphinx_index = viewvc
|
||||
|
||||
## Parameters for snippet (excerpt) display
|
||||
|
||||
# Enable snippets? ViewVC indexes binary file contents in MySQL when enabled.
|
||||
enable_snippets = 1
|
||||
|
||||
# Regexp for MIME types in which \n means "newline"
|
||||
sphinx_preformatted_mime = text/(?!html|xml).*
|
||||
|
||||
# Sphinx snippet options - see http://sphinxsearch.com/docs/current.html#api-func-buildexcerpts
|
||||
sphinx_snippet_options =
|
||||
around: 15
|
||||
limit: 200
|
||||
before_match: <span style="color:red">
|
||||
after_match: </span>
|
||||
chunk_separator: ... \n
|
||||
|
||||
## row_limit: Maximum number of rows returned by a given normal query
|
||||
## to the database.
|
||||
##
|
||||
|
@@ -1,213 +0,0 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
|
||||
<html>
|
||||
<head>
|
||||
<title>ViewVC: 1.1.0 Release Notes</title>
|
||||
<style>
|
||||
.h2, .h3 {
|
||||
padding: 0.25em 0em;
|
||||
background: white;
|
||||
}
|
||||
.warning {
|
||||
font-style: italic;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<h1>ViewVC 1.1.0 Release Notes</h1>
|
||||
|
||||
<div class="h2">
|
||||
<h2 id="introduction">Introduction</h2>
|
||||
|
||||
<p>ViewVC 1.1.0 is the superset of all previous ViewVC releases.</p>
|
||||
|
||||
</div> <!-- h2 -->
|
||||
|
||||
<div class="h2">
|
||||
<h2 id="compatibility">Compatibility</h2>
|
||||
|
||||
<p>Each ViewVC release strives to maintain URL stability with previous
|
||||
releases, and 1.1.0 is no exception. All URLs considered valid for
|
||||
previous ViewVC releases should continue to work correctly in this
|
||||
release, though possibly only via the use of HTTP redirects
|
||||
(generated by ViewVC itself).</p>
|
||||
|
||||
<p>The commits database functionality has changed in ViewVC 1.1.0 in
|
||||
way that breaks compatibility with prior ViewVC releases, but only
|
||||
for new database instantiations. ViewVC 1.1.0 will continue to
|
||||
understand (for both read and write operations) the previous
|
||||
schema, so you are not required to rebuild your commits database
|
||||
for ViewVC 1.1.0 compatibility. By default, new commits databases
|
||||
created using the 1.1.0 version of the <code>make-database</code>
|
||||
script will use a new database schema that is unreadable by
|
||||
previous ViewVC versions. However, if you need a database which
|
||||
can co-exist with a previous ViewVC version, you can use
|
||||
the <code>--version=1.0</code> option
|
||||
to <code>make-database</code>.</p>
|
||||
|
||||
<p>The ViewVC configuration files and template language have changed
|
||||
dramatically. See the file <code>docs/upgrading-howto.html</code>
|
||||
in the release for information on porting existing versions of
|
||||
those items for use with ViewVC 1.1.0.</p>
|
||||
|
||||
</div> <!-- h2 -->
|
||||
|
||||
<div class="h2">
|
||||
<h2 id="compatibility">Features and Fixes</h2>
|
||||
|
||||
<div class="h3">
|
||||
<h3 id="">Extensible path-based authorization w/ Subversion authz support</h3>
|
||||
|
||||
<p>In a nutshell, ViewVC is now able to do path-based authorization.
|
||||
ViewVC 1.0 has a configuration option for naming 'forbidden'
|
||||
modules, but it is really limited — it basically just makes a
|
||||
universal decision about which top-level directories in every
|
||||
hosted repository should be hidden by ViewVC. People want
|
||||
more, and specifically requested that ViewVC learn how to honor
|
||||
Subversion's authz files and semantics. So, along with some other
|
||||
types of authorization approaches, that's what ViewVC 1.1 can now
|
||||
do. If you are using mod_authz_svn with Apache today, or
|
||||
svnserve's built-in authorization support, then you can now point
|
||||
ViewVC to the same authz configuration file and have it honor the
|
||||
access rules you've defined for your repositories.</p>
|
||||
|
||||
<p>Note that ViewVC does <strong>not</strong> handle authentication,
|
||||
though. You'll need to configure your web server to demand login
|
||||
credentials from users, which the web server itself can then hand
|
||||
off to ViewVC for application against the authorization rules
|
||||
you've defined.</p>
|
||||
|
||||
<p class="warning">WARNING: The root listing view does not consult the
|
||||
authorization subsystem when deciding what roots to display to a
|
||||
given user. If you need to protect your root names, consider
|
||||
disabling it by removing <code>roots</code> from the set of views
|
||||
listed in the <code>allowed_views</code> configuration option.
|
||||
<strong>UPDATE: This was fixed in ViewVC 1.1.3.</strong></p>
|
||||
|
||||
<p class="warning">WARNING: Support for path-based authorization is
|
||||
incomplete in the experimental version control backend modules,
|
||||
including the one that permits display of remote Subversion
|
||||
repositories. <strong>UPDATE: This was fixed in ViewVC
|
||||
1.1.15.</strong></p>
|
||||
|
||||
</div> <!-- h3 -->
|
||||
|
||||
<div class="h3">
|
||||
<h3 id="">Subversion versioned properties display</h3>
|
||||
|
||||
<p>ViewVC 1.1 displays the properties that Subversion lets you store
|
||||
on files and directories
|
||||
(<code>svn:mime-type</code>, <code>svn:mergeinfo</code>,
|
||||
<code>svn:ignore</code>, etc.). Directory properties are shown by
|
||||
default at the bottom of that directory's entry listing. File
|
||||
properties are displayed at the bottom of that file's
|
||||
markup/annotate view.</p>
|
||||
|
||||
</div> <!-- h3 -->
|
||||
|
||||
<div class="h3">
|
||||
<h3 id="">Unified markup and annotation views</h3>
|
||||
|
||||
<p>The "markup" and "annotate" views in ViewVC now have a unified look
|
||||
and feel (driven by a single EZT template). Both views support
|
||||
syntax highlighting and Subversion file property display.</p>
|
||||
|
||||
</div> <!-- h3 -->
|
||||
|
||||
<div class="h3">
|
||||
<h3 id="">Unified, hassle-free Pygments-based syntax highlighting</h3>
|
||||
|
||||
<p>ViewVC 1.0 does syntax highlighting by working with GNU enscript, or
|
||||
highlight, or php, or py2html — all these external tools just
|
||||
to accomplish a single task. But they all do things in slightly
|
||||
different ways. And if you configure them wrongly, you get strange
|
||||
errors. <a href="http://www.pygments.org/">Pygments</a> (which is
|
||||
also used by <a href="http://trac.edgewall.org/">Trac</a> for
|
||||
syntax highlighting) is a Python package that requires no
|
||||
configuration, is easier to use inside ViewVC, and so on. So
|
||||
ViewVC 1.1 drops support for all those various old integrations,
|
||||
and just uses Pygments for everything now. This change was about
|
||||
developer and administrator sanity. There will be complaints, to
|
||||
be sure, about how various color schemes differ and what file types
|
||||
now are and aren't understood by the syntax highlighting engine,
|
||||
but this change should vastly simplify the discussions of such
|
||||
things.</p>
|
||||
|
||||
</div> <!-- h3 -->
|
||||
|
||||
<div class="h3">
|
||||
<h3 id="">Better MIME detection and handling</h3>
|
||||
|
||||
<p>ViewVC typically consults a MIME types file to determine what kind
|
||||
of file a given document is, based on its filename extension
|
||||
(<code>.jpg</code> = <code>image/jpeg</code>, …). But
|
||||
Subversion lets you dictate a file's MIME type using
|
||||
the <code>svn:mime-type</code> property. ViewVC now recognizes and
|
||||
honors that property as the preferred source of a file's MIME type.
|
||||
This can be disabled in the configuration, though, which might be
|
||||
desirable if many of your Subversion-versioned files carry the
|
||||
generic <code>application/octet-stream</code> MIME type that
|
||||
Subversion uses by default for non-human-readable files.</p>
|
||||
|
||||
<p>Also, ViewVC now allows you to specify multiple MIME type mapping
|
||||
files that you'd like it to consult when determine the MIME type of
|
||||
files based on their extensions. This allows administrators to
|
||||
easily define their own custom mappings for ViewVC's benefit
|
||||
without potentially affecting the mappings used by other site
|
||||
services.</p>
|
||||
|
||||
</div> <!-- h3 -->
|
||||
|
||||
<div class="h3">
|
||||
<h3 id="">Support for full content diffs</h3>
|
||||
|
||||
<p>ViewVC 1.1 expands the previously existing options of "colored
|
||||
diff" and "long colored diff" with a new "full colored diff", which
|
||||
shows the full contents of the changed file (instead of only the 3
|
||||
or 15 lines of context shown via the older diff display types).</p>
|
||||
|
||||
</div> <!-- h3 -->
|
||||
|
||||
<div class="h3">
|
||||
<h3 id="">Support for per-root configuration overrides</h3>
|
||||
|
||||
<p>In ViewVC 1.1, you can setup configuration option overrides on a
|
||||
per-root (per-repository) basis (if you need/care to do so). See
|
||||
the comments in the <code>viewvc.conf.dist</code> file for more on
|
||||
how to do this.</p>
|
||||
|
||||
</div> <!-- h3 -->
|
||||
|
||||
<div class="h3">
|
||||
<h3 id="">Optional email address obfuscation/mangling</h3>
|
||||
|
||||
<p>ViewVC can, when displaying revision metadata, munge strings that
|
||||
look like email addresses to protect them from screen-scraping
|
||||
spammers. For example, a log message that says, "Patch by:
|
||||
cmpilato@red-bean.com" can optionally be displayed by ViewVC using
|
||||
HTML entity encoding for the characters (a trick that causes no
|
||||
visible change to the output, but that might confuse
|
||||
unsophisticated spam bot crawlers) or as "Patch by: cmpilato@..."
|
||||
(which isn't a complete email address at all, but might be enough
|
||||
information for the human reading the log message to know who to
|
||||
blame for the patch).</p>
|
||||
|
||||
</div> <!-- h3 -->
|
||||
|
||||
<div class="h3">
|
||||
<h3 id="">Pagination improvements</h3>
|
||||
|
||||
<p>The way that ViewVC splits directory and log views across pages has
|
||||
been reworked. The old way was "Fetch all the information you can
|
||||
find, then display only one page's worth." The new way is "Fetch
|
||||
only what you need to display the page requested, plus a little bit
|
||||
of border information." This provides a large performance
|
||||
enhancement for the default sort orderings.</p>
|
||||
|
||||
</div> <!-- h3 -->
|
||||
|
||||
</div> <!-- h2 -->
|
||||
|
||||
</body>
|
||||
</html>
|
@@ -1,49 +0,0 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
|
||||
<html>
|
||||
<head>
|
||||
<title>ViewVC: 1.2.0 Release Notes</title>
|
||||
<style>
|
||||
.h2, .h3 {
|
||||
padding: 0.25em 0em;
|
||||
background: white;
|
||||
}
|
||||
.warning {
|
||||
font-style: italic;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<h1>ViewVC 1.2.0 Release Notes</h1>
|
||||
|
||||
<div class="h2">
|
||||
<h2 id="introduction">Introduction</h2>
|
||||
|
||||
<p>ViewVC 1.2.0 is the superset of all previous ViewVC releases.</p>
|
||||
|
||||
</div> <!-- h2 -->
|
||||
|
||||
<div class="h2">
|
||||
<h2 id="compatibility">Compatibility</h2>
|
||||
|
||||
<p>Each ViewVC release strives to maintain URL stability with previous
|
||||
releases, and 1.2.0 is no exception. All URLs considered valid for
|
||||
previous ViewVC releases should continue to work correctly in this
|
||||
release, though possibly only via the use of HTTP redirects
|
||||
(generated by ViewVC itself).</p>
|
||||
|
||||
</div> <!-- h2 -->
|
||||
|
||||
<div class="h2">
|
||||
<h2 id="compatibility">Features and Fixes</h2>
|
||||
|
||||
<div class="h3">
|
||||
<h3 id=""></h3>
|
||||
|
||||
</div> <!-- h3 -->
|
||||
|
||||
</div> <!-- h2 -->
|
||||
|
||||
</body>
|
||||
</html>
|
@@ -1,6 +1,6 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>ViewVC 1.2 Template Authoring Guide</title>
|
||||
<title>ViewVC 1.1 Template Authoring Guide</title>
|
||||
<style>
|
||||
body {
|
||||
background-color: rgb(180,193,205);
|
||||
@@ -29,20 +29,16 @@ td {
|
||||
.varlevel1 { background: rgb(65%,85%,65%); }
|
||||
.varlevel2 { background: rgb(70%,90%,70%); }
|
||||
.varlevel3 { background: rgb(75%,95%,75%); }
|
||||
.varlevel4 { background: rgb(80%,100%,80%); }
|
||||
.varlevel5 { background: rgb(85%,100%,85%); }
|
||||
.varname { font-family: monospace; }
|
||||
.varlevel1 .varname { padding-left: 0; }
|
||||
.varlevel2 .varname { padding-left: 2em; }
|
||||
.varlevel3 .varname { padding-left: 4em; }
|
||||
.varlevel4 .varname { padding-left: 6em; }
|
||||
.varlevel5 .varname { padding-left: 8em; }
|
||||
.toc-list { font-size: 90%; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1>ViewVC 1.2 Template Authoring Guide</h1>
|
||||
<h1>ViewVC 1.1 Template Authoring Guide</h1>
|
||||
|
||||
<div class="h2">
|
||||
<h2 id="introduction">Introduction</h2>
|
||||
@@ -602,45 +598,6 @@ td {
|
||||
<td colspan="3">Includes all variables from the
|
||||
<a href="#variables-common">COMMON</a> variable set</td>
|
||||
</tr>
|
||||
<tr class="varlevel1">
|
||||
<td class="varname">gbbox</td>
|
||||
<td>Boolean</td>
|
||||
<td>Toggle generation of a branch box at the tip of all branches in
|
||||
the revision graph.</td>
|
||||
</tr>
|
||||
<tr class="varlevel1">
|
||||
<td class="varname">gflip</td>
|
||||
<td>Boolean</td>
|
||||
<td>Toggle the direction of the revision graph.</td>
|
||||
</tr>
|
||||
<tr class="varlevel1">
|
||||
<td class="varname">gleft</td>
|
||||
<td>Boolean</td>
|
||||
<td>Toggle the orientation of the revision graph.</td>
|
||||
</tr>
|
||||
<tr class="varlevel1">
|
||||
<td class="varname">gmaxtag</td>
|
||||
<td>String</td>
|
||||
<td>Number of tags per revision to display in the revision graph.</td>
|
||||
</tr>
|
||||
<tr class="varlevel1">
|
||||
<td class="varname">graph_action</td>
|
||||
<td>String</td>
|
||||
<td>Form action URL for the graph customization form.</td>
|
||||
</tr>
|
||||
<tr class="varlevel1">
|
||||
<td class="varname">graph_hidden_values</td>
|
||||
<td>String</td>
|
||||
<td>Hidden value name/value pairs for the graph customization form.</td>
|
||||
</tr>
|
||||
<tr class="varlevel1">
|
||||
<td class="varname">gshow</td>
|
||||
<td>String</td>
|
||||
<td>Classes of revisions to show in the revision graph. Valid values
|
||||
are <tt>all</tt> (all revision), <tt>inittagged</tt> (initial
|
||||
revision(s) and tagged revisions), and <tt>tagged</tt> (tagged
|
||||
revisions only).</td>
|
||||
</tr>
|
||||
<tr class="varlevel1">
|
||||
<td class="varname">imagemap</td>
|
||||
<td>String</td>
|
||||
@@ -653,37 +610,6 @@ td {
|
||||
<td>URL of the ViewVC revision graph image for the current
|
||||
resource.</td>
|
||||
</tr>
|
||||
<tr class="varlevel1">
|
||||
<td class="varname">opt_gbbox</td>
|
||||
<td>Boolean</td>
|
||||
<td>Specifies whether the user is allowed to toggle the generation
|
||||
of branch boxes at the tip of all branches in the revision
|
||||
graph.</td>
|
||||
</tr>
|
||||
<tr class="varlevel1">
|
||||
<td class="varname">opt_gflip</td>
|
||||
<td>Boolean</td>
|
||||
<td>Specifies whether the user is allowed to toggle the direction
|
||||
of the revision graph.</td>
|
||||
</tr>
|
||||
<tr class="varlevel1">
|
||||
<td class="varname">opt_gleft</td>
|
||||
<td>Boolean</td>
|
||||
<td>Specifies whether the user is allowed to toggle the orientation
|
||||
of the revision graph.</td>
|
||||
</tr>
|
||||
<tr class="varlevel1">
|
||||
<td class="varname">opt_gmaxtag</td>
|
||||
<td>Boolean</td>
|
||||
<td>Specifies whether the user is allowed to configure the maximum
|
||||
number of tags per revision show in the revision graph.</td>
|
||||
</tr>
|
||||
<tr class="varlevel1">
|
||||
<td class="varname">opt_gshow</td>
|
||||
<td>Boolean</td>
|
||||
<td>Specifies whether the user is allowed to configure which
|
||||
classes of revisions are shown in the revision graph.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -704,64 +630,30 @@ td {
|
||||
<a href="#variables-common">COMMON</a> variable set</td>
|
||||
</tr>
|
||||
<tr class="varlevel1">
|
||||
<td class="varname">diffs</td>
|
||||
<td class="varname">changes</td>
|
||||
<td>List</td>
|
||||
<td>List of all blocks of differences between the two sides, including content
|
||||
and property differences.</td>
|
||||
<td>Set of objects which contain information about a single line of
|
||||
file difference data. Valid only when <var>diff_format</var> is
|
||||
<tt>h</tt> or <tt>l</tt>.</td>
|
||||
</tr>
|
||||
<tr class="varlevel2">
|
||||
<td class="varname">diffs.diff_block_format</td>
|
||||
<td>String</td>
|
||||
<td>Indicates the type of this block. One of the <tt>anchor</tt> (no display,
|
||||
create an anchor), <tt>raw</tt> (non-colored diff, display as produced),
|
||||
<tt>sidebyside-1</tt> (traditional side-by-side diff),
|
||||
<tt>sidebyside-2</tt> (newer side-by-side diff with intraline changes),
|
||||
<tt>unified</tt> (colored unified diff).</td>
|
||||
</tr>
|
||||
<tr class="varlevel2">
|
||||
<td class="varname">diffs.anchor</td>
|
||||
<td>String</td>
|
||||
<td>If <var>diffs.diff_block_format</var> is <tt>anchor</tt>, this variable specifies
|
||||
the anchor name.</td>
|
||||
</tr>
|
||||
<tr class="varlevel2">
|
||||
<td class="varname">diffs.changes</td>
|
||||
<td>List/Container</td>
|
||||
<td>Set of objects which contain information about a change in a single
|
||||
object (file or property). Not present if <var>diffs.diff_block_format</var> is
|
||||
<tt>anchor</tt>, otherwise has different format depending on
|
||||
<var>diffs.diff_block_format</var> (applicable as indicated in brackets below).</td>
|
||||
</tr>
|
||||
<tr class="varlevel3">
|
||||
<td class="varname">diffs.changes.raw</td>
|
||||
<td>String</td>
|
||||
<td>[raw] Diff text. Valid only if <var>diffs.changes.type</var> is
|
||||
<tt>raw</tt>.</td>
|
||||
</tr>
|
||||
<tr class="varlevel3">
|
||||
<td class="varname">diffs.changes.type</td>
|
||||
<td>String</td>
|
||||
<td>[raw] The type of change. Values: <tt>binary-diff</tt>,
|
||||
<tt>error</tt>, <tt>no-changes</tt>, <tt>raw</tt>.</td>
|
||||
</tr>
|
||||
<tr class="varlevel3">
|
||||
<td class="varname">diffs.changes.have_left</td>
|
||||
<td class="varname">changes.have_left</td>
|
||||
<td>Boolean</td>
|
||||
<td>[sidebyside-1] Specifies whether the left file has a line of content relevant
|
||||
<td>Specifies whether the left file has a line of content relevant
|
||||
to the difference data line. Valid only when
|
||||
<var>changes.type</var> is <tt>change</tt>.</td>
|
||||
</tr>
|
||||
<tr class="varlevel3">
|
||||
<td class="varname">diffs.changes.have_right</td>
|
||||
<tr class="varlevel2">
|
||||
<td class="varname">changes.have_right</td>
|
||||
<td>Boolean</td>
|
||||
<td>[sidebyside-1] Specifies whether the right file has a line of content relevant
|
||||
<td>Specifies whether the right file has a line of content relevant
|
||||
to the difference data line. Valid only when
|
||||
<var>changes.type</var> is <tt>change</tt>.</td>
|
||||
</tr>
|
||||
<tr class="varlevel3">
|
||||
<td class="varname">diffs.changes.left</td>
|
||||
<tr class="varlevel2">
|
||||
<td class="varname">changes.left</td>
|
||||
<td>String</td>
|
||||
<td>[sidebyside-1] Textual contents of the relevant line in the left file. Valid
|
||||
<td>Textual contents of the relevant line in the left file. Valid
|
||||
only when <var>changes.type</var> is <tt>change</tt>,
|
||||
<tt>context</tt>, or <tt>remove</tt>. When
|
||||
<var>changes.type</var> is <tt>change</tt>, valid only when
|
||||
@@ -769,10 +661,10 @@ td {
|
||||
between missing lines and empty lines, which EZT does not
|
||||
support).</td>
|
||||
</tr>
|
||||
<tr class="varlevel3">
|
||||
<td class="varname">diffs.changes.right</td>
|
||||
<tr class="varlevel2">
|
||||
<td class="varname">changes.right</td>
|
||||
<td>String</td>
|
||||
<td>[sidebyside-1] Textual contents of the relevant line in the right file. Valid
|
||||
<td>Textual contents of the relevant line in the right file. Valid
|
||||
only when <var>changes.type</var> is <tt>add</tt>, <tt>change</tt>,
|
||||
or <tt>context</tt>. When
|
||||
<var>changes.type</var> is <tt>change</tt>, valid only when
|
||||
@@ -780,264 +672,40 @@ td {
|
||||
between missing lines and empty lines, which EZT does not
|
||||
support).</td>
|
||||
</tr>
|
||||
<tr class="varlevel3">
|
||||
<td class="varname">diffs.changes.line_info_extra</td>
|
||||
<tr class="varlevel2">
|
||||
<td class="varname">changes.line_info_extra</td>
|
||||
<td>String</td>
|
||||
<td>[sidebyside-1] Additional line information for the current difference hunk.
|
||||
<td>Additional line information for the current difference hunk.
|
||||
Valid only when <var>changes.type</var> is <tt>header</tt>.</td>
|
||||
</tr>
|
||||
<tr class="varlevel3">
|
||||
<td class="varname">diffs.changes.line_info_left</td>
|
||||
<tr class="varlevel2">
|
||||
<td class="varname">changes.line_info_left</td>
|
||||
<td>String</td>
|
||||
<td>[sidebyside-1] First line number represented by the current hunk in the left
|
||||
<td>First line number represented by the current hunk in the left
|
||||
file. Valid only when <var>changes.type</var> is <tt>header</tt>.</td>
|
||||
</tr>
|
||||
<tr class="varlevel3">
|
||||
<td class="varname">diffs.changes.line_info_right</td>
|
||||
<tr class="varlevel2">
|
||||
<td class="varname">changes.line_info_right</td>
|
||||
<td>String</td>
|
||||
<td>[sidebyside-1] First line number represented by the current hunk in the right
|
||||
<td>First line number represented by the current hunk in the right
|
||||
file. Valid only when <var>changes.type</var> is <tt>header</tt>.</td>
|
||||
</tr>
|
||||
<tr class="varlevel3">
|
||||
<td class="varname">diffs.changes.line_number</td>
|
||||
<tr class="varlevel2">
|
||||
<td class="varname">changes.line_number</td>
|
||||
<td>String</td>
|
||||
<td>[sidebyside-1] Line number (1-based) of the line.</td>
|
||||
<td>Line number (1-based) of the line.</td>
|
||||
</tr>
|
||||
<tr class="varlevel3">
|
||||
<td class="varname">diffs.changes.type</td>
|
||||
<tr class="varlevel2">
|
||||
<td class="varname">changes.type</td>
|
||||
<td>String</td>
|
||||
<td>[sidebyside-1] The type of change. Values: <tt>add</tt>, <tt>binary-diff</tt>,
|
||||
<tt>change</tt>, <tt>context</tt>, <tt>error</tt>, <tt>header</tt>,
|
||||
<td>The type of change. Value values: <tt>add</tt>,
|
||||
<tt>change</tt>, <tt>context</tt>, <tt>header</tt>,
|
||||
<tt>no-changes</tt>, <tt>remove</tt>.</td>
|
||||
</tr>
|
||||
<tr class="varlevel3">
|
||||
<td class="varname">diffs.changes.columns</td>
|
||||
<td>List</td>
|
||||
<td>[sidebyside-2] List of two columns for left and right parts of the diff.</td>
|
||||
</tr>
|
||||
<tr class="varlevel4">
|
||||
<td class="varname">diffs.changes.columns.line_number</td>
|
||||
<td>String</td>
|
||||
<td>[sidebyside-2] Line number in the left/right column.</td>
|
||||
</tr>
|
||||
<tr class="varlevel4">
|
||||
<td class="varname">diffs.changes.columns.segments</td>
|
||||
<td>List</td>
|
||||
<td>[sidebyside-2] Left/right line, broken into change segments.</td>
|
||||
</tr>
|
||||
<tr class="varlevel5">
|
||||
<td class="varname">diffs.changes.columns.segments.text</td>
|
||||
<td>String</td>
|
||||
<td>[sidebyside-2] Text of this segment.</td>
|
||||
</tr>
|
||||
<tr class="varlevel5">
|
||||
<td class="varname">diffs.changes.columns.segments.type</td>
|
||||
<td>String</td>
|
||||
<td>[sidebyside-2] Not set if the segment is the same in both left and right sides;
|
||||
otherwise, one of the <tt>add</tt>, <tt>remove</tt> or <tt>change</tt>.</td>
|
||||
</tr>
|
||||
<tr class="varlevel3">
|
||||
<td class="varname">diffs.changes.gap</td>
|
||||
<td>Boolean</td>
|
||||
<td>[sidebyside-2] If true, indicates that change blocks are non-contiguous
|
||||
and that the template should display some sort of ellipsis before the
|
||||
current block.</td>
|
||||
</tr>
|
||||
<tr class="varlevel3">
|
||||
<td class="varname">diffs.changes.type</td>
|
||||
<td>String</td>
|
||||
<td>[sidebyside-2] The type of change. Values: <tt>binary-diff</tt>,
|
||||
<tt>error</tt>, <tt>intraline</tt>, <tt>no-changes</tt>.</td>
|
||||
</tr>
|
||||
<tr class="varlevel3">
|
||||
<td class="varname">diffs.changes.segments</td>
|
||||
<td>List</td>
|
||||
<td>[unified] Left/right line, broken into change segments.</td>
|
||||
</tr>
|
||||
<tr class="varlevel4">
|
||||
<td class="varname">diffs.changes.segments.text</td>
|
||||
<td>String</td>
|
||||
<td>[unified] Text of this segment.</td>
|
||||
</tr>
|
||||
<tr class="varlevel4">
|
||||
<td class="varname">diffs.changes.segments.type</td>
|
||||
<td>String</td>
|
||||
<td>[unified] Not set if the segment is the same in both left and right sides;
|
||||
otherwise, one of the <tt>add</tt>, <tt>remove</tt> or <tt>change</tt>.</td>
|
||||
</tr>
|
||||
<tr class="varlevel3">
|
||||
<td class="varname">diffs.changes.type</td>
|
||||
<td>String</td>
|
||||
<td>[unified] The type of change. Values: <tt>add</tt>, <tt>binary-diff</tt>,
|
||||
<tt>error</tt>, <tt>no-changes</tt>, <tt>remove</tt> or empty string
|
||||
if the line was not changed (context line).</td>
|
||||
</tr>
|
||||
<tr class="varlevel2">
|
||||
<td class="varname">diffs.left</td>
|
||||
<td>Container</td>
|
||||
<td>Container object for grouping information about the left file.</td>
|
||||
</tr>
|
||||
<tr class="varlevel3">
|
||||
<td class="varname">diffs.left.ago</td>
|
||||
<td>String</td>
|
||||
<td>Text description of the time elapsed since <var>left.date</date>.</td>
|
||||
</tr>
|
||||
<tr class="varlevel3">
|
||||
<td class="varname">diffs.left.annotate_href</td>
|
||||
<td>String</td>
|
||||
<td>URL of the ViewVC annotation view for the left file.
|
||||
Valid only when <var>entries.pathtype</var> is <tt>file</tt>.</td>
|
||||
</tr>
|
||||
<tr class="varlevel3">
|
||||
<td class="varname">diffs.left.author</td>
|
||||
<td>String</td>
|
||||
<td>Author of the revision of the left file.</td>
|
||||
</tr>
|
||||
<tr class="varlevel3">
|
||||
<td class="varname">diffs.left.date</td>
|
||||
<td>String</td>
|
||||
<td>Date (in UTC if not otherwise configured) in which the left file
|
||||
revision was created.</td>
|
||||
</tr>
|
||||
<tr class="varlevel3">
|
||||
<td class="varname">diffs.left.download_href</td>
|
||||
<td>String</td>
|
||||
<td>URL to download the HEAD revision of the left file.</td>
|
||||
</tr>
|
||||
<tr class="varlevel3">
|
||||
<td class="varname">diffs.left.download_text_href</td>
|
||||
<td>String</td>
|
||||
<td>URL to download the HEAD revision of the left file as
|
||||
<tt>text/plain</tt>.</td>
|
||||
</tr>
|
||||
<tr class="varlevel3">
|
||||
<td class="varname">diffs.left.log</td>
|
||||
<td>String</td>
|
||||
<td>Log message of the left file revision.</td>
|
||||
</tr>
|
||||
<tr class="varlevel3">
|
||||
<td class="varname">diffs.left.path</td>
|
||||
<td>String</td>
|
||||
<td>Path of the left file.</td>
|
||||
</tr>
|
||||
<tr class="varlevel3">
|
||||
<td class="varname">diffs.left.prefer_markup</td>
|
||||
<td>Boolean</td>
|
||||
<td>Indicates whether to make the default file link a link to the markup
|
||||
page instead of the checkout page.</td>
|
||||
</tr>
|
||||
<tr class="varlevel3">
|
||||
<td class="varname">diffs.left.rev</td>
|
||||
<td>String</td>
|
||||
<td>Revision of the left file.</td>
|
||||
</tr>
|
||||
<tr class="varlevel3">
|
||||
<td class="varname">diffs.left.revision_href</td>
|
||||
<td>String</td>
|
||||
<td>URL of the Subversion revision view for the left file's
|
||||
current revision. Valid only when <var>roottype</var> is
|
||||
<tt>svn</tt>.</td>
|
||||
</tr>
|
||||
<tr class="varlevel3">
|
||||
<td class="varname">diffs.left.size</td>
|
||||
<td>String</td>
|
||||
<td>Size of the left file revision, in bytes. Subversion only.</td>
|
||||
</tr>
|
||||
<tr class="varlevel3">
|
||||
<td class="varname">diffs.left.tag</td>
|
||||
<td>String</td>
|
||||
<td>Tag of the left file.</td>
|
||||
</tr>
|
||||
<tr class="varlevel3">
|
||||
<td class="varname">diffs.left.view_href</td>
|
||||
<td>String</td>
|
||||
<td>This is a URL for the markup view of the left file.</td>
|
||||
</tr>
|
||||
<tr class="varlevel2">
|
||||
<td class="varname">diffs.right</td>
|
||||
<td>Container</td>
|
||||
<td>Container object for grouping information about the right file.</td>
|
||||
</tr>
|
||||
<tr class="varlevel3">
|
||||
<td class="varname">diffs.right.ago</td>
|
||||
<td>String</td>
|
||||
<td>Text description of the time elapsed since <var>right.date</var>.</td>
|
||||
</tr>
|
||||
<tr class="varlevel3">
|
||||
<td class="varname">diffs.right.annotate_href</td>
|
||||
<td>String</td>
|
||||
<td>URL of the ViewVC annotation view for the right file.
|
||||
Valid only when <var>entries.pathtype</var> is <tt>file</tt>.</td>
|
||||
</tr>
|
||||
<tr class="varlevel3">
|
||||
<td class="varname">diffs.right.author</td>
|
||||
<td>String</td>
|
||||
<td>Author of the revision of the right file.</td>
|
||||
</tr>
|
||||
<tr class="varlevel3">
|
||||
<td class="varname">diffs.right.date</td>
|
||||
<td>String</td>
|
||||
<td>Date (in UTC if not otherwise configured) in which the right file
|
||||
revision was created.</td>
|
||||
</tr>
|
||||
<tr class="varlevel3">
|
||||
<td class="varname">diffs.right.download_href</td>
|
||||
<td>String</td>
|
||||
<td>URL to download the HEAD revision of the right file.</td>
|
||||
</tr>
|
||||
<tr class="varlevel3">
|
||||
<td class="varname">diffs.right.download_text_href</td>
|
||||
<td>String</td>
|
||||
<td>URL to download the HEAD revision of the right file as
|
||||
<tt>text/plain</tt>.</td>
|
||||
</tr>
|
||||
<tr class="varlevel3">
|
||||
<td class="varname">diffs.right.log</td>
|
||||
<td>String</td>
|
||||
<td>Log message of the right file revision.</td>
|
||||
</tr>
|
||||
<tr class="varlevel3">
|
||||
<td class="varname">diffs.right.path</td>
|
||||
<td>String</td>
|
||||
<td>Path of the right file.</td>
|
||||
</tr>
|
||||
<tr class="varlevel3">
|
||||
<td class="varname">diffs.right.prefer_markup</td>
|
||||
<td>Boolean</td>
|
||||
<td>Indicates whether to make the default file link a link to the markup
|
||||
page instead of the checkout page.</td>
|
||||
</tr>
|
||||
<tr class="varlevel3">
|
||||
<td class="varname">diffs.right.rev</td>
|
||||
<td>String</td>
|
||||
<td>Revision of the right file.</td>
|
||||
</tr>
|
||||
<tr class="varlevel3">
|
||||
<td class="varname">diffs.right.revision_href</td>
|
||||
<td>String</td>
|
||||
<td>URL of the Subversion revision view for the right file's
|
||||
current revision. Valid only when <var>roottype</var> is
|
||||
<tt>svn</tt>.</td>
|
||||
</tr>
|
||||
<tr class="varlevel3">
|
||||
<td class="varname">diffs.right.size</td>
|
||||
<td>String</td>
|
||||
<td>Size of the right file revision, in bytes. Subversion only.</td>
|
||||
</tr>
|
||||
<tr class="varlevel3">
|
||||
<td class="varname">diffs.right.tag</td>
|
||||
<td>String</td>
|
||||
<td>Tag of the right file.</td>
|
||||
</tr>
|
||||
<tr class="varlevel3">
|
||||
<td class="varname">diffs.right.view_href</td>
|
||||
<td>String</td>
|
||||
<td>This is a URL for the markup view of the right file.</td>
|
||||
</tr>
|
||||
<tr class="varlevel1">
|
||||
<td class="varname">diff_format</td>
|
||||
<td>String</td>
|
||||
<td>Difference display format: Valid values are <tt>c</tt>
|
||||
<td>Difference dislay format: Valid values are <tt>c</tt>
|
||||
(context), <tt>f</tt> (full human-readable),
|
||||
<tt>h</tt> (human-readable, or colored), <tt>l</tt> (long
|
||||
human-readable), <tt>s</tt> (side-by-side), <tt>u</tt>
|
||||
@@ -1054,15 +722,178 @@ td {
|
||||
<td>Hidden field name/value pairs for the diff format selection form.</td>
|
||||
</tr>
|
||||
<tr class="varlevel1">
|
||||
<td class="varname">hide_legend</td>
|
||||
<td class="varname">left</td>
|
||||
<td>Container</td>
|
||||
<td>Container object for grouping information about the left file.</td>
|
||||
</tr>
|
||||
<tr class="varlevel2">
|
||||
<td class="varname">left.ago</td>
|
||||
<td>String</td>
|
||||
<td>Text description of the time elapsed since <var>left.date</date>.</td>
|
||||
</tr>
|
||||
<tr class="varlevel2">
|
||||
<td class="varname">left.annotate_href</td>
|
||||
<td>String</td>
|
||||
<td>URL of the ViewVC annotation view for the left file.
|
||||
Valid only when <var>entries.pathtype</var> is <tt>file</tt>.</td>
|
||||
</tr>
|
||||
<tr class="varlevel2">
|
||||
<td class="varname">left.author</td>
|
||||
<td>String</td>
|
||||
<td>Author of the revision of the left file.</td>
|
||||
</tr>
|
||||
<tr class="varlevel2">
|
||||
<td class="varname">left.date</td>
|
||||
<td>String</td>
|
||||
<td>Date (in UTC if not otherwise configured) in which the left file
|
||||
revision was created.</td>
|
||||
</tr>
|
||||
<tr class="varlevel2">
|
||||
<td class="varname">left.download_href</td>
|
||||
<td>String</td>
|
||||
<td>URL to download the HEAD revision of the left file.</td>
|
||||
</tr>
|
||||
<tr class="varlevel2">
|
||||
<td class="varname">left.download_text_href</td>
|
||||
<td>String</td>
|
||||
<td>URL to download the HEAD revision of the left file as
|
||||
<tt>text/plain</tt>.</td>
|
||||
</tr>
|
||||
<tr class="varlevel2">
|
||||
<td class="varname">left.log</td>
|
||||
<td>String</td>
|
||||
<td>Log message of the left file revision.</td>
|
||||
</tr>
|
||||
<tr class="varlevel2">
|
||||
<td class="varname">left.path</td>
|
||||
<td>String</td>
|
||||
<td>Path of the left file.</td>
|
||||
</tr>
|
||||
<tr class="varlevel2">
|
||||
<td class="varname">left.prefer_markup</td>
|
||||
<td>Boolean</td>
|
||||
<td>Indicates whether the display format requires displaying a legend</td>
|
||||
<td>Indicates whether to make the default file link a link to the markup
|
||||
page instead of the checkout page.</td>
|
||||
</tr>
|
||||
<tr class="varlevel2">
|
||||
<td class="varname">left.rev</td>
|
||||
<td>String</td>
|
||||
<td>Revision of the left file.</td>
|
||||
</tr>
|
||||
<tr class="varlevel2">
|
||||
<td class="varname">left.revision_href</td>
|
||||
<td>String</td>
|
||||
<td>URL of the Subversion revision view for the left file's
|
||||
current revision. Valid only when <var>roottype</var> is
|
||||
<tt>svn</tt>.</td>
|
||||
</tr>
|
||||
<tr class="varlevel2">
|
||||
<td class="varname">left.size</td>
|
||||
<td>String</td>
|
||||
<td>Size of the left file revision, in bytes. Subversion only.</td>
|
||||
</tr>
|
||||
<tr class="varlevel2">
|
||||
<td class="varname">left.tag</td>
|
||||
<td>String</td>
|
||||
<td>Tag of the left file.</td>
|
||||
</tr>
|
||||
<tr class="varlevel2">
|
||||
<td class="varname">left.view_href</td>
|
||||
<td>String</td>
|
||||
<td>This is a URL for the markup view of the left file.</td>
|
||||
</tr>
|
||||
<tr class="varlevel1">
|
||||
<td class="varname">patch_href</td>
|
||||
<td>String</td>
|
||||
<td>URL of the patch view for the file.</td>
|
||||
</tr>
|
||||
<tr class="varlevel1">
|
||||
<td class="varname">raw_diff</td>
|
||||
<td>String</td>
|
||||
<td>Raw difference text. Valid only when <var>diff_format</var> is
|
||||
<tt>c</tt>, <tt>s</tt>, or <tt>u</tt>.</td>
|
||||
</tr>
|
||||
<tr class="varlevel1">
|
||||
<td class="varname">right</td>
|
||||
<td>Container</td>
|
||||
<td>Container object for grouping information about the right file.</td>
|
||||
</tr>
|
||||
<tr class="varlevel2">
|
||||
<td class="varname">right.author</td>
|
||||
<td>String</td>
|
||||
<td>Author of the revision of the right file.</td>
|
||||
</tr>
|
||||
<tr class="varlevel2">
|
||||
<td class="varname">right.annotate_href</td>
|
||||
<td>String</td>
|
||||
<td>URL of the ViewVC annotation view for the right file.
|
||||
Valid only when <var>entries.pathtype</var> is <tt>file</tt>.</td>
|
||||
</tr>
|
||||
<tr class="varlevel2">
|
||||
<td class="varname">right.author</td>
|
||||
<td>String</td>
|
||||
<td>Author of the revision of the right file.</td>
|
||||
</tr>
|
||||
<tr class="varlevel2">
|
||||
<td class="varname">right.date</td>
|
||||
<td>String</td>
|
||||
<td>Date (in UTC if not otherwise configured) in which the right file
|
||||
revision was created.</td>
|
||||
</tr>
|
||||
<tr class="varlevel2">
|
||||
<td class="varname">right.download_href</td>
|
||||
<td>String</td>
|
||||
<td>URL to download the HEAD revision of the right file.</td>
|
||||
</tr>
|
||||
<tr class="varlevel2">
|
||||
<td class="varname">right.download_text_href</td>
|
||||
<td>String</td>
|
||||
<td>URL to download the HEAD revision of the right file as
|
||||
<tt>text/plain</tt>.</td>
|
||||
</tr>
|
||||
<tr class="varlevel2">
|
||||
<td class="varname">right.log</td>
|
||||
<td>String</td>
|
||||
<td>Log message of the right file revision.</td>
|
||||
</tr>
|
||||
<tr class="varlevel2">
|
||||
<td class="varname">right.path</td>
|
||||
<td>String</td>
|
||||
<td>Path of the right file.</td>
|
||||
</tr>
|
||||
<tr class="varlevel2">
|
||||
<td class="varname">right.prefer_markup</td>
|
||||
<td>Boolean</td>
|
||||
<td>Indicates whether to make the default file link a link to the markup
|
||||
page instead of the checkout page.</td>
|
||||
</tr>
|
||||
<tr class="varlevel2">
|
||||
<td class="varname">right.rev</td>
|
||||
<td>String</td>
|
||||
<td>Revision of the right file.</td>
|
||||
</tr>
|
||||
<tr class="varlevel2">
|
||||
<td class="varname">right.revision_href</td>
|
||||
<td>String</td>
|
||||
<td>URL of the Subversion revision view for the right file's
|
||||
current revision. Valid only when <var>roottype</var> is
|
||||
<tt>svn</tt>.</td>
|
||||
</tr>
|
||||
<tr class="varlevel2">
|
||||
<td class="varname">right.size</td>
|
||||
<td>String</td>
|
||||
<td>Size of the right file revision, in bytes. Subversion only.</td>
|
||||
</tr>
|
||||
<tr class="varlevel2">
|
||||
<td class="varname">right.tag</td>
|
||||
<td>String</td>
|
||||
<td>Tag of the right file.</td>
|
||||
</tr>
|
||||
<tr class="varlevel2">
|
||||
<td class="varname">right.view_href</td>
|
||||
<td>String</td>
|
||||
<td>This is a URL for the markup view of the right file.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
@@ -28,12 +28,12 @@ td {
|
||||
.h3 { border-width: 1px 0 0 0; }
|
||||
.toc-list { font-size: 90%; }
|
||||
.varname { font-family: monospace; }
|
||||
.added { background: rgb(60%,90%,60%); }
|
||||
.added { background: rgb(50%,75%,25%); }
|
||||
.unchanged { background: rgb(75%,75%,75%); }
|
||||
.renamed { background: rgb(80%,60%,80%); }
|
||||
.changed { background: rgb(100%,100%,50%); }
|
||||
.replaced { background: rgb(100%,80%,40%); }
|
||||
.removed { background: rgb(100%,70%,70%); }
|
||||
.renamed { background: rgb(75%,50%,75%); }
|
||||
.changed { background: rgb(100%,100%,25%); }
|
||||
.replaced { background: rgb(100%,75%,0%); }
|
||||
.removed { background: rgb(100%,25%,25%); }
|
||||
</style>
|
||||
</head>
|
||||
|
||||
@@ -67,21 +67,12 @@ td {
|
||||
<h2 id="toc">Table of Contents</h2>
|
||||
<ul class="toc-list">
|
||||
<li><a href="#introduction">Introduction</a></li>
|
||||
<li><a href="#sec-from-1-1">Upgrading From ViewVC 1.1</a></li>
|
||||
<li><a href="#sec-from-1-0">Upgrading From ViewVC 1.0</a></li>
|
||||
<li><a href="#sec-from-0-9">Upgrading From ViewCVS 0.9</a></li>
|
||||
<li><a href="#sec-from-0-8">Upgrading From ViewCVS 0.8</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="h2">
|
||||
<h2 id="sec-from-1-0">Upgrading From ViewVC 1.1</h2>
|
||||
|
||||
<p>This section discusses how to upgrade ViewVC 1.1.x to ViewVC 1.2.x.</p>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div class="h2">
|
||||
<h2 id="sec-from-1-0">Upgrading From ViewVC 1.0</h2>
|
||||
|
||||
@@ -146,9 +137,8 @@ td {
|
||||
configuration's "general" section.</li>
|
||||
|
||||
<li>Finally, ensure that that the new <code>authorizer</code>
|
||||
option is set to either "forbidden" (which is the default) or
|
||||
"forbiddenre", depending on which of those you were using in
|
||||
ViewVC 1.0.x.</li>
|
||||
option is set to either "forbidden" or "forbiddenre", depending
|
||||
on which of those you were using in ViewVC 1.0.x.</li>
|
||||
|
||||
</ol>
|
||||
|
||||
@@ -224,10 +214,9 @@ td {
|
||||
<li>options/py2html_path</li>
|
||||
<li>options/use_enscript</li>
|
||||
<li>options/use_highlight</li>
|
||||
<li>options/use_pagesize</li>
|
||||
<li>options/use_php</li>
|
||||
<li>options/use_py2html</li>
|
||||
<li>options/use_pygments</li>
|
||||
<li>options/use_source_highlight</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
@@ -235,16 +224,23 @@ td {
|
||||
<div class="h3">
|
||||
<h3>Checkin Database</h3>
|
||||
|
||||
<p>In ViewVC 1.1, the <code>svndbadmin</code> program's "rebuild"
|
||||
subcommand has had its purpose become more defined. It no longer
|
||||
accepts a revision argument, and therefore can now only be used to
|
||||
completely rebuild the entirety of the checkin database information
|
||||
for a Subversion repository (instead of being able to only update
|
||||
the information related to single Subversion revision). For
|
||||
per-revision updating, use <code>svndbadmin update</code> and
|
||||
<p>ViewVC 1.1 introduces to the <code>cvsdbadmin</code>
|
||||
and <code>svndbadmin</code> tools a new "purge" operation, which
|
||||
allows you to remove all the information related to a given root
|
||||
from your checkins database (without disturbing the information
|
||||
associated with other roots). Likewise, the "rebuild" command in
|
||||
those tools now implies a "purge" followed by an update.</p>
|
||||
|
||||
<p>As a related change, the <code>svndbadmin</code> program's
|
||||
"rebuild" subcommand has had its purpose become more defined. It
|
||||
no longer accepts a revision argument, and therefore can now only
|
||||
be used to completely rebuild the entirety of the checkin database
|
||||
information for a Subversion repository (instead of being able to
|
||||
only update the information related to single Subversion revision).
|
||||
For per-revision updating, use <code>svndbadmin update</code> and
|
||||
provide a revision (or revision range). And to get the previous
|
||||
rebuild-a-revision effect, pass the new <code>--force</code>
|
||||
option to <code>svndbadmin update</code>.</p>
|
||||
rebuild-a-revision effect, pass the new <code>--force</code> option
|
||||
to <code>svndbadmin update</code>.</p>
|
||||
|
||||
<p>In other words, where you once did this:</p>
|
||||
|
||||
@@ -258,6 +254,19 @@ td {
|
||||
</pre>
|
||||
</blockquote>
|
||||
|
||||
<p>To enhance the performance of the new "purge" operation, ViewVC 1.1
|
||||
introduces some slight changes to the checkin database schema. If
|
||||
you use the <code>make-database</code> tool to (re)create your
|
||||
checkins database, it will by default employ the new database
|
||||
schema. This should cause the database to be virtually unusable by
|
||||
previous versions of ViewVC, and that's by design. If, however,
|
||||
you need to (re)create your checkins database and you require
|
||||
compatibility with previous versions of ViewVC or ViewCVS, simply
|
||||
pass the "--version=1.0" option to the <code>make-database</code>
|
||||
script. Note that your "purge" and "rebuild" operations could be
|
||||
abysmally slow, though, as that version of the database schema is
|
||||
not optimized for those operations.</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="h3">
|
||||
@@ -305,6 +314,22 @@ td {
|
||||
<li>options/allow_tar</li>
|
||||
</ul>
|
||||
|
||||
<p>ViewVC now honors the "svn:mime-type" property stored on
|
||||
Subversion-versioned files as the primary source of MIME type
|
||||
determination (before falling back to name-based MIME mappings and
|
||||
such). However, this can negatively affect the viewability of
|
||||
certain files — especially images — whose
|
||||
"svn:mime-type" properties are set incorrectly, such as will happen
|
||||
if Subversion itself merely determines that the file isn't
|
||||
human-readable and uses the "application/octet-stream" MIME type to
|
||||
record this determination. To make ViewVC <em>not</em> honor the
|
||||
"svn:mime-type" property value, set the <code>svn_ignore_mimetype</code>
|
||||
configuration option.</p>
|
||||
|
||||
<p>Speaking of MIME types, the option <code>mime_types_file</code> is
|
||||
now <code>mime_types_files</code>, as it now carries multiple paths
|
||||
to MIME mappings files, ordered by preference.</p>
|
||||
|
||||
<p>The <code>use_rcsparse</code> option was moved from the "general"
|
||||
section to the "options" section.</p>
|
||||
|
||||
@@ -640,6 +665,11 @@ allowed_views = annotate, diff, markup, tar
|
||||
<td>revision.ezt</td>
|
||||
<td>now is an iterable list of objects with .name and .value attributes</td>
|
||||
</tr>
|
||||
<tr class="added">
|
||||
<td class="varname">num_changes</td>
|
||||
<td>revision.ezt</td>
|
||||
<td>added</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -668,7 +698,7 @@ allowed_views = annotate, diff, markup, tar
|
||||
<h3>Checkin Database</h3>
|
||||
|
||||
<p>ViewVC 1.0 reads and writes commit times in the MySQL database in
|
||||
UTC time rather than local time. This can cause times displayed on
|
||||
UTC time rather than local time. This can cause times displayed on
|
||||
the query page to be a few hours off if an old database is being
|
||||
used with a new version of ViewVC. The best way to fix this is to
|
||||
rebuild the database with the new version of cvsdbadmin, but it
|
||||
@@ -711,7 +741,8 @@ allowed_views = annotate, diff, markup, tar
|
||||
<li>options/root_as_url_component</li>
|
||||
<li>options/default_file_view</li>
|
||||
<li>options/sort_group_dirs</li>
|
||||
<li>options/use_pagesize</li>
|
||||
<li>options/dir_pagesize</li>
|
||||
<li>options/log_pagesize</li>
|
||||
<li>options/limit_changes</li>
|
||||
<li>options/use_localtime</li>
|
||||
<li>options/cross_copies</li>
|
||||
@@ -1572,8 +1603,13 @@ allowed_views = annotate, diff, markup, tar
|
||||
<div class="h2">
|
||||
<h2 id="sec-from-0-8">Upgrading From ViewCVS 0.8</h2>
|
||||
|
||||
<p>This section discusses how to upgrade ViewCVS 0.8 to ViewCVS 0.9.x.</p>
|
||||
<p>This section discusses how to upgrade ViewCVS 0.8 to version
|
||||
0.9 or a later version of the software.</p>
|
||||
|
||||
<p><strong>NOTE:</strong> these changes will bring you up to the
|
||||
requirements of version 0.9. You must also follow the directions
|
||||
for <a href="#sec-from-0-9">upgrading from 0.9</a>.</p>
|
||||
|
||||
<div class="h3">
|
||||
<h3>Configuration Options</h3>
|
||||
|
||||
@@ -1583,50 +1619,92 @@ allowed_views = annotate, diff, markup, tar
|
||||
options, then you will need to make corresponding changes in the
|
||||
templates.</p>
|
||||
|
||||
<dl>
|
||||
<dt>Colors: <code>diff_heading</code>, <code>diff_empty</code>,
|
||||
<code>diff_remove</code>, <code>diff_change</code>,
|
||||
<code>diff_add</code>, and <code>diff_dark_change</code></dt>
|
||||
<dd>These options have been incorporated into the
|
||||
<code>diff.ezt</code> template.</dd>
|
||||
<dl>
|
||||
<dt>
|
||||
Colors:
|
||||
<strong>diff_heading</strong>,
|
||||
<strong>diff_empty</strong>,
|
||||
<strong>diff_remove</strong>,
|
||||
<strong>diff_change</strong>,
|
||||
<strong>diff_add</strong>,
|
||||
and <strong>diff_dark_change</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
These options have been incorporated into the
|
||||
<code>diff.ezt</code> template.
|
||||
|
||||
<dt><code>markup_log</code></dt>
|
||||
<dd>This option has been incorporated into the
|
||||
<code>markup.ezt</code> template.</dd>
|
||||
<p></p>
|
||||
</dd>
|
||||
|
||||
<dt>Colors: <code>nav_header</code> and
|
||||
<code>alt_background</code></dt>
|
||||
<dd>These options have been incorporated into the
|
||||
<code>header.ezt</code> template.</dd>
|
||||
<dt><strong>markup_log</strong></dt>
|
||||
<dd>
|
||||
This option has been incorporated into the
|
||||
<code>markup.ezt</code> template.
|
||||
|
||||
<dt>Images: <code>back_icon</code>, <code>dir_icon</code>,
|
||||
and <code>file_icon</code></dt>
|
||||
<dd>These options have been incorporated into the
|
||||
<code>directory.ezt</code>, <code>header.ezt</code>,
|
||||
<code>log.ezt</code>, <code>log_table.ezt</code>, and
|
||||
<code>query.ezt</code> templates.</dd>
|
||||
<p></p>
|
||||
</dd>
|
||||
|
||||
<dt><code>use_java_script</code>
|
||||
and <code>open_extern_window</code></dt>
|
||||
<dd>The templates now use JavaScript in all applicable places, and
|
||||
open external windows for most downloading and viewing of
|
||||
files. If you wish to not use JavaScript and/or external
|
||||
windows, then remove the feature(s) from the templates.</dd>
|
||||
<dt>Colors: <strong>nav_header</strong>
|
||||
and <strong>alt_background</strong></dt>
|
||||
<dd>
|
||||
These options have been incorporated into the
|
||||
<code>header.ezt</code> template.
|
||||
|
||||
<dt><code>show_author</code></dt>
|
||||
<dd>Changing this option would be quite strange and rare. If you
|
||||
do not want to show the author for the revisions, then you
|
||||
should remove it from the various templates.</dd>
|
||||
<p></p>
|
||||
</dd>
|
||||
|
||||
<dt><code>hide_non_readable</code></dt>
|
||||
<dd>This option was never used, so it has been removed.</dd>
|
||||
<dt>
|
||||
Images:
|
||||
<strong>back_icon</strong>,
|
||||
<strong>dir_icon</strong>,
|
||||
and <strong>file_icon</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
These options have been incorporated into the
|
||||
<code>directory.ezt</code>, <code>header.ezt</code>,
|
||||
<code>log.ezt</code>, <code>log_table.ezt</code>, and
|
||||
<code>query.ezt</code> templates.
|
||||
|
||||
<dt><code>flip_links_in_dirview</code></dt>
|
||||
<dd>This option is no longer available. If you want the links in
|
||||
your directory view flipped, then you may use the
|
||||
<code>dir_alternate.ezt</code> template.</dd>
|
||||
<p></p>
|
||||
</dd>
|
||||
|
||||
</dl>
|
||||
<dt><strong>use_java_script</strong>
|
||||
and <strong>open_extern_window</strong></dt>
|
||||
<dd>
|
||||
The templates now use JavaScript in all applicable places,
|
||||
and open external windows for most downloading and viewing
|
||||
of files. If you wish to not use JavaScript and/or external
|
||||
windows, then remove the feature(s) from the templates.
|
||||
|
||||
<p></p>
|
||||
</dd>
|
||||
|
||||
<dt><strong>show_author</strong></dt>
|
||||
<dd>
|
||||
Changing this option would be quite strange and rare. If you
|
||||
do not want to show the author for the revisions, then you
|
||||
should remove it from the various templates.
|
||||
|
||||
<p></p>
|
||||
</dd>
|
||||
|
||||
<dt><strong>hide_non_readable</strong></dt>
|
||||
<dd>
|
||||
This option was never used, so it has been removed.
|
||||
|
||||
<p></p>
|
||||
</dd>
|
||||
|
||||
<dt><strong>flip_links_in_dirview</strong></dt>
|
||||
<dd>
|
||||
This option is no longer available. If you want the links in
|
||||
your directory view flipped, then you may use the
|
||||
<code>dir_alternate.ezt</code> template.
|
||||
|
||||
<p></p>
|
||||
</dd>
|
||||
|
||||
</dl>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -1637,65 +1715,53 @@ allowed_views = annotate, diff, markup, tar
|
||||
removed in 0.9. If you have custom templates that refer to these
|
||||
variables, then you will need to modify your templates.</p>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Variable</th>
|
||||
<th>Location</th>
|
||||
<th>Changes</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="removed">
|
||||
<td class="varname">headers</td>
|
||||
<td>directory.ezt</td>
|
||||
<td>removed; headers are now listed explicitly in the template, rather
|
||||
than made available through a list.</td>
|
||||
</tr>
|
||||
<tr class="removed">
|
||||
<td class="varname">rows.cols</td>
|
||||
<td>directory.ezt</td>
|
||||
<td>removed; was used in conjunction with the <var>headers</var>
|
||||
variable to control the column displays. This is now controlled
|
||||
explicitly within the templates.</td>
|
||||
</tr>
|
||||
<tr class="removed">
|
||||
<td class="varname">rows.span</td>
|
||||
<td>directory.ezt</td>
|
||||
<td>removed; was used in conjunction with the <var>headers</var>
|
||||
variable to control the column displays. This is now controlled
|
||||
explicitly within the templates.</td>
|
||||
</tr>
|
||||
<tr class="removed">
|
||||
<td class="varname">rev_in_front</td>
|
||||
<td>directory.ezt</td>
|
||||
<td>removed; was used to indicate that revision links should be used in
|
||||
the first column, rather than in their standard place in the
|
||||
second column. Changing the links should now be done in the
|
||||
template, rather than according to this variable. You may want
|
||||
to look at the <code>dir_alternate.ezt</code> template, which
|
||||
has the revision in front.</dd>
|
||||
</tr>
|
||||
<tr class="removed">
|
||||
<td class="varname">rows.attic</td>
|
||||
<td>directory.ezt</td>
|
||||
<td>removed; used to manage the hide and showing of the
|
||||
contents of the <code>Attic/</code> subdirectory. Several new
|
||||
variables were introduced which can be used to replace this
|
||||
functionality: <var>show_attic_href</var>,
|
||||
<var>hide_attic_href</var>, and <var>rows.state</var>.</td>
|
||||
</tr>
|
||||
<tr class="removed">
|
||||
<td class="varname">rows.hide_attic_href</td>
|
||||
<td>directory.ezt</td>
|
||||
<td>removed; used to manage the hide and showing of the
|
||||
contents of the <code>Attic/</code> subdirectory. Several new
|
||||
variables were introduced which can be used to replace this
|
||||
functionality: <var>show_attic_href</var>,
|
||||
<var>hide_attic_href</var>, and <var>rows.state</var>.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<dl>
|
||||
<dt><code>directory.ezt</code>: <var>headers</var></dt>
|
||||
<dd>
|
||||
The headers are now listed explicitly in the template,
|
||||
rather than made available through a list.
|
||||
<p></p>
|
||||
</dd>
|
||||
|
||||
<dt>
|
||||
<code>directory.ezt</code>:
|
||||
<var>rows.cols</var>,
|
||||
and <var>rows.span</var>
|
||||
</dt>
|
||||
<dd>
|
||||
These variables were used in conjunction with the
|
||||
<var>headers</var> variable to control the column
|
||||
displays. This is now controlled explicitly within the
|
||||
templates.
|
||||
<p></p>
|
||||
</dd>
|
||||
|
||||
<dt><code>directory.ezt</code>:
|
||||
<var>rev_in_front</var></dt>
|
||||
<dd>
|
||||
This was used to indicate that revision links should
|
||||
be used in the first column, rather than in their
|
||||
standard place in the second column. Changing the
|
||||
links should now be done in the template, rather than
|
||||
according to this variable. You may want to look at
|
||||
the <code>dir_alternate.ezt</code> template, which has
|
||||
the revision in front.
|
||||
<p></p>
|
||||
</dd>
|
||||
|
||||
<dt><code>directory.ezt</code>:
|
||||
<var>rows.attic</var>
|
||||
and <var>rows.hide_attic_href</var></dt>
|
||||
<dd>
|
||||
These variable were used to manage the hide and
|
||||
showing of the contents of the <code>Attic/</code>
|
||||
subdirectory. Several new variables were introduced
|
||||
which can be used to replace this functionality:
|
||||
<var>show_attic_href</var>,
|
||||
<var>hide_attic_href</var>, and <var>rows.state</var>.
|
||||
<p></p>
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -654,35 +654,6 @@ th.caption {
|
||||
<td>depends</td>
|
||||
<td><a href="#root-param"><code>root</code> parameter</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>gflip=<var>GFLIP</var></code></td>
|
||||
<td>optional</td>
|
||||
<td>"1" if the revisions in the graph should run
|
||||
youngest-to-oldest; "0" for the reverse</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>gbbox=<var>GBBOX</var></code></td>
|
||||
<td>optional</td>
|
||||
<td>"1" if the revision graph should contain branch boxes at the
|
||||
tip of each branch; "0" otherwise</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>gleft=<var>GLEFT</var></code></td>
|
||||
<td>optional</td>
|
||||
<td>"1" if the revision graph should be orientated left-to-right;
|
||||
"0" otherwise</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>gmaxtag=<var>GMAXTAG</var></code></td>
|
||||
<td>optional</td>
|
||||
<td>maximum number of per-revision tags to show in the revision graph</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>gshow=<var>GSHOW</var></code></td>
|
||||
<td>optional</td>
|
||||
<td>"all", "inittagged", or "tagged" — user-selected classes
|
||||
of revision to show in the graph</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<h3 id="graphimg-view">Graph Image View</h3>
|
||||
@@ -729,35 +700,6 @@ th.caption {
|
||||
<td>depends</td>
|
||||
<td><a href="#root-param"><code>root</code> parameter</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>gflip=<var>GFLIP</var></code></td>
|
||||
<td>optional</td>
|
||||
<td>"1" if the revisions in the graph should run
|
||||
youngest-to-oldest; "0" for the reverse</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>gbbox=<var>GBBOX</var></code></td>
|
||||
<td>optional</td>
|
||||
<td>"1" if the revision graph should contain branch boxes at the
|
||||
tip of each branch; "0" otherwise</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>gleft=<var>GLEFT</var></code></td>
|
||||
<td>optional</td>
|
||||
<td>"1" if the revision graph should be orientated left-to-right;
|
||||
"0" otherwise</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>gmaxtag=<var>GMAXTAG</var></code></td>
|
||||
<td>optional</td>
|
||||
<td>maximum number of per-revision tags to show in the revision graph</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>gshow=<var>GSHOW</var></code></td>
|
||||
<td>optional</td>
|
||||
<td>"all", "inittagged", or "tagged" — user-selected classes
|
||||
of revision to show in the graph</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<h3 id="log-view">Log View</h3>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 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,6 +15,7 @@
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
import re
|
||||
import string
|
||||
|
||||
|
||||
def language(hdr):
|
||||
@@ -39,7 +40,7 @@ def _parse(hdr, result):
|
||||
name = _re_token.match(hdr, pos)
|
||||
if not name:
|
||||
raise AcceptLanguageParseError()
|
||||
a = result.item_class(name.group(1).lower())
|
||||
a = result.item_class(string.lower(name.group(1)))
|
||||
pos = name.end()
|
||||
while 1:
|
||||
# are we looking at a parameter?
|
||||
@@ -55,7 +56,7 @@ def _parse(hdr, result):
|
||||
# the "=" was probably missing
|
||||
continue
|
||||
|
||||
pname = match.group(1).lower()
|
||||
pname = string.lower(match.group(1))
|
||||
if pname == 'q' or pname == 'qs':
|
||||
try:
|
||||
a.quality = float(match.group(2))
|
||||
@@ -69,7 +70,7 @@ def _parse(hdr, result):
|
||||
# bad float literal
|
||||
pass
|
||||
elif pname == 'charset':
|
||||
a.charset = match.group(2).lower()
|
||||
a.charset = string.lower(match.group(2))
|
||||
|
||||
result.append(a)
|
||||
if hdr[pos:pos+1] == ',':
|
||||
|
18
lib/blame.py
18
lib/blame.py
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 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
|
||||
@@ -27,12 +27,11 @@
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
import sys
|
||||
import string
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
import math
|
||||
|
||||
from common import _item
|
||||
import vclib
|
||||
import sapi
|
||||
|
||||
@@ -43,7 +42,7 @@ def link_includes(text, repos, path_parts, include_url):
|
||||
if match:
|
||||
incfile = match.group(3)
|
||||
include_path_parts = path_parts[:-1]
|
||||
for part in filter(None, incfile.split('/')):
|
||||
for part in filter(None, string.split(incfile, '/')):
|
||||
if part == "..":
|
||||
if not include_path_parts:
|
||||
# nothing left to pop; don't bother marking up this include.
|
||||
@@ -55,14 +54,14 @@ def link_includes(text, repos, path_parts, include_url):
|
||||
include_path = None
|
||||
try:
|
||||
if repos.itemtype(include_path_parts, None) == vclib.FILE:
|
||||
include_path = '/'.join(include_path_parts)
|
||||
include_path = string.join(include_path_parts, '/')
|
||||
except vclib.ItemNotFound:
|
||||
pass
|
||||
|
||||
if include_path:
|
||||
return '#%sinclude%s<a href="%s">"%s"</a>' % \
|
||||
(match.group(1), match.group(2),
|
||||
include_url.replace('/WHERE/', include_path), incfile)
|
||||
string.replace(include_url, '/WHERE/', include_path), incfile)
|
||||
|
||||
return text
|
||||
|
||||
@@ -95,6 +94,11 @@ def blame(repos, path_parts, diff_url, include_url, opt_rev=None):
|
||||
return source, source.revision
|
||||
|
||||
|
||||
class _item:
|
||||
def __init__(self, **kw):
|
||||
vars(self).update(kw)
|
||||
|
||||
|
||||
def make_html(root, rcs_path):
|
||||
import vclib.ccvs.blame
|
||||
bs = vclib.ccvs.blame.BlameSource(os.path.join(root, rcs_path))
|
||||
@@ -132,7 +136,7 @@ def make_html(root, rcs_path):
|
||||
sys.stdout.write('<td> </td><td> </td>')
|
||||
rev_count = rev_count + 1
|
||||
|
||||
sys.stdout.write('<td%s>%s</td></tr>\n' % (align % 'left', thisline.rstrip() or ' '))
|
||||
sys.stdout.write('<td%s>%s</td></tr>\n' % (align % 'left', string.rstrip(thisline) or ' '))
|
||||
sys.stdout.write('</table>\n')
|
||||
|
||||
|
||||
|
@@ -1,60 +0,0 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
# distribution or at http://viewvc.org/license-1.html.
|
||||
#
|
||||
# For more information, visit http://viewvc.org/
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
# common: common definitions for the viewvc library
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
# Special type indicators for diff header processing and idiff return codes
|
||||
_RCSDIFF_IS_BINARY = 'binary-diff'
|
||||
_RCSDIFF_ERROR = 'error'
|
||||
_RCSDIFF_NO_CHANGES = "no-changes"
|
||||
|
||||
|
||||
class _item:
|
||||
def __init__(self, **kw):
|
||||
vars(self).update(kw)
|
||||
|
||||
|
||||
class TemplateData:
|
||||
"""A custom dictionary-like object that allows one-time definition
|
||||
of keys, and only value fetches and changes, and key deletions,
|
||||
thereafter.
|
||||
|
||||
EZT doesn't require the use of this special class -- a normal
|
||||
dict-type data dictionary works fine. But use of this class will
|
||||
assist those who want the data sent to their templates to have a
|
||||
consistent set of keys."""
|
||||
|
||||
def __init__(self, initial_data={}):
|
||||
self._items = initial_data
|
||||
|
||||
def __getitem__(self, key):
|
||||
return self._items.__getitem__(key)
|
||||
|
||||
def __setitem__(self, key, item):
|
||||
assert self._items.has_key(key)
|
||||
return self._items.__setitem__(key, item)
|
||||
|
||||
def __delitem__(self, key):
|
||||
return self._items.__delitem__(key)
|
||||
|
||||
def keys(self):
|
||||
return self._items.keys()
|
||||
|
||||
def merge(self, template_data):
|
||||
"""Merge the data in TemplataData instance TEMPLATA_DATA into this
|
||||
instance. Avoid the temptation to use this conditionally in your
|
||||
code -- it rather defeats the purpose of this class."""
|
||||
|
||||
assert isinstance(template_data, TemplateData)
|
||||
self._items.update(template_data._items)
|
180
lib/compat.py
Normal file
180
lib/compat.py
Normal file
@@ -0,0 +1,180 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2012 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/
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
# compat.py: compatibility functions for operation across Python 1.5.x to 2.2.x
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
import urllib
|
||||
import string
|
||||
import time
|
||||
import calendar
|
||||
import re
|
||||
import os
|
||||
import rfc822
|
||||
import tempfile
|
||||
import errno
|
||||
|
||||
#
|
||||
# urllib.urlencode() is new to Python 1.5.2
|
||||
#
|
||||
try:
|
||||
urlencode = urllib.urlencode
|
||||
except AttributeError:
|
||||
def urlencode(dict):
|
||||
"Encode a dictionary as application/x-url-form-encoded."
|
||||
if not dict:
|
||||
return ''
|
||||
quote = urllib.quote_plus
|
||||
keyvalue = [ ]
|
||||
for key, value in dict.items():
|
||||
keyvalue.append(quote(key) + '=' + quote(str(value)))
|
||||
return string.join(keyvalue, '&')
|
||||
|
||||
#
|
||||
# time.strptime() is new to Python 1.5.2
|
||||
#
|
||||
if hasattr(time, 'strptime'):
|
||||
def cvs_strptime(timestr):
|
||||
'Parse a CVS-style date/time value.'
|
||||
return time.strptime(timestr, '%Y/%m/%d %H:%M:%S')[:-1] + (0,)
|
||||
else:
|
||||
_re_rev_date = re.compile('([0-9]{4})/([0-9][0-9])/([0-9][0-9]) '
|
||||
'([0-9][0-9]):([0-9][0-9]):([0-9][0-9])')
|
||||
def cvs_strptime(timestr):
|
||||
'Parse a CVS-style date/time value.'
|
||||
match = _re_rev_date.match(timestr)
|
||||
if match:
|
||||
return tuple(map(int, match.groups())) + (0, 1, 0)
|
||||
else:
|
||||
raise ValueError('date is not in cvs format')
|
||||
|
||||
#
|
||||
# os.makedirs() is new to Python 1.5.2
|
||||
#
|
||||
try:
|
||||
makedirs = os.makedirs
|
||||
except AttributeError:
|
||||
def makedirs(path, mode=0777):
|
||||
head, tail = os.path.split(path)
|
||||
if head and tail and not os.path.exists(head):
|
||||
makedirs(head, mode)
|
||||
os.mkdir(path, mode)
|
||||
|
||||
#
|
||||
# rfc822.formatdate() is new to Python 1.6
|
||||
#
|
||||
try:
|
||||
formatdate = rfc822.formatdate
|
||||
except AttributeError:
|
||||
def formatdate(timeval):
|
||||
if timeval is None:
|
||||
timeval = time.time()
|
||||
timeval = time.gmtime(timeval)
|
||||
return "%s, %02d %s %04d %02d:%02d:%02d GMT" % (
|
||||
["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"][timeval[6]],
|
||||
timeval[2],
|
||||
["Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
||||
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"][timeval[1]-1],
|
||||
timeval[0], timeval[3], timeval[4], timeval[5])
|
||||
|
||||
#
|
||||
# calendar.timegm() is new to Python 2.x and
|
||||
# calendar.leapdays() was wrong in Python 1.5.2
|
||||
#
|
||||
try:
|
||||
timegm = calendar.timegm
|
||||
except AttributeError:
|
||||
def leapdays(year1, year2):
|
||||
"""Return number of leap years in range [year1, year2).
|
||||
Assume year1 <= year2."""
|
||||
year1 = year1 - 1
|
||||
year2 = year2 - 1
|
||||
return (year2/4 - year1/4) - (year2/100 -
|
||||
year1/100) + (year2/400 - year1/400)
|
||||
|
||||
EPOCH = 1970
|
||||
def timegm(tuple):
|
||||
"""Unrelated but handy function to calculate Unix timestamp from GMT."""
|
||||
year, month, day, hour, minute, second = tuple[:6]
|
||||
# assert year >= EPOCH
|
||||
# assert 1 <= month <= 12
|
||||
days = 365*(year-EPOCH) + leapdays(EPOCH, year)
|
||||
for i in range(1, month):
|
||||
days = days + calendar.mdays[i]
|
||||
if month > 2 and calendar.isleap(year):
|
||||
days = days + 1
|
||||
days = days + day - 1
|
||||
hours = days*24 + hour
|
||||
minutes = hours*60 + minute
|
||||
seconds = minutes*60 + second
|
||||
return seconds
|
||||
|
||||
#
|
||||
# tempfile.mkdtemp() is new to Python 2.3
|
||||
#
|
||||
try:
|
||||
mkdtemp = tempfile.mkdtemp
|
||||
except AttributeError:
|
||||
def mkdtemp(suffix="", prefix="tmp", dir=None):
|
||||
# mktemp() only took a single suffix argument until Python 2.3.
|
||||
# We'll do the best we can.
|
||||
oldtmpdir = os.environ.get('TMPDIR')
|
||||
try:
|
||||
for i in range(10):
|
||||
if dir:
|
||||
os.environ['TMPDIR'] = dir
|
||||
dir = tempfile.mktemp(suffix)
|
||||
if prefix:
|
||||
parent, base = os.path.split(dir)
|
||||
dir = os.path.join(parent, prefix + base)
|
||||
try:
|
||||
os.mkdir(dir, 0700)
|
||||
return dir
|
||||
except OSError, e:
|
||||
if e.errno == errno.EEXIST:
|
||||
continue # try again
|
||||
raise
|
||||
finally:
|
||||
if oldtmpdir:
|
||||
os.environ['TMPDIR'] = oldtmpdir
|
||||
elif os.environ.has_key('TMPDIR'):
|
||||
del(os.environ['TMPDIR'])
|
||||
|
||||
raise IOError, (errno.EEXIST, "No usable temporary directory name found")
|
||||
|
||||
#
|
||||
# the following stuff is *ONLY* needed for standalone.py.
|
||||
# For that reason I've encapsulated it into a function.
|
||||
#
|
||||
|
||||
def for_standalone():
|
||||
import SocketServer
|
||||
if not hasattr(SocketServer.TCPServer, "close_request"):
|
||||
#
|
||||
# method close_request() was missing until Python 2.1
|
||||
#
|
||||
class TCPServer(SocketServer.TCPServer):
|
||||
def process_request(self, request, client_address):
|
||||
"""Call finish_request.
|
||||
|
||||
Overridden by ForkingMixIn and ThreadingMixIn.
|
||||
|
||||
"""
|
||||
self.finish_request(request, client_address)
|
||||
self.close_request(request)
|
||||
|
||||
def close_request(self, request):
|
||||
"""Called to clean up an individual request."""
|
||||
request.close()
|
||||
|
||||
SocketServer.TCPServer = TCPServer
|
162
lib/config.py
162
lib/config.py
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 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,15 +16,10 @@
|
||||
|
||||
import sys
|
||||
import os
|
||||
import string
|
||||
import ConfigParser
|
||||
import fnmatch
|
||||
import vclib
|
||||
import vclib.ccvs
|
||||
import vclib.svn
|
||||
import cvsdb
|
||||
import viewvc
|
||||
import copy
|
||||
from viewvcmagic import ContentMagic
|
||||
|
||||
|
||||
#########################################################################
|
||||
#
|
||||
@@ -117,7 +112,6 @@ class Config:
|
||||
_force_multi_value = (
|
||||
# Configuration values with multiple, comma-separated values.
|
||||
'allowed_views',
|
||||
'binary_mime_types',
|
||||
'custom_log_formatting',
|
||||
'cvs_roots',
|
||||
'kv_files',
|
||||
@@ -143,9 +137,6 @@ class Config:
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self.__guesser = None
|
||||
self.__root_configs = {}
|
||||
self.__parent = None
|
||||
self.root_options_overlayed = 0
|
||||
for section in self._base_sections:
|
||||
if section[-1] == '*':
|
||||
@@ -180,12 +171,12 @@ class Config:
|
||||
|
||||
for fname in self.general.kv_files:
|
||||
if fname[0] == '[':
|
||||
idx = fname.index(']')
|
||||
parts = fname[1:idx].split('.')
|
||||
fname = fname[idx+1:].strip()
|
||||
idx = string.index(fname, ']')
|
||||
parts = string.split(fname[1:idx], '.')
|
||||
fname = string.strip(fname[idx+1:])
|
||||
else:
|
||||
parts = [ ]
|
||||
fname = fname.replace('%lang%', language)
|
||||
fname = string.replace(fname, '%lang%', language)
|
||||
|
||||
parser = ConfigParser.ConfigParser()
|
||||
parser.optionxform = lambda x: x # don't case-normalize option names.
|
||||
@@ -217,7 +208,7 @@ class Config:
|
||||
for opt in parser.options(section):
|
||||
value = parser.get(section, opt)
|
||||
if opt in self._force_multi_value:
|
||||
value = map(lambda x: x.strip(), filter(None, value.split(',')))
|
||||
value = map(string.strip, filter(None, string.split(value, ',')))
|
||||
else:
|
||||
try:
|
||||
value = int(value)
|
||||
@@ -275,45 +266,17 @@ class Config:
|
||||
self._process_section(parser, section, base_section)
|
||||
|
||||
def _find_canon_vhost(self, parser, vhost):
|
||||
vhost = vhost.lower().split(':')[0] # lower-case, no port
|
||||
vhost = string.split(string.lower(vhost), ':')[0] # lower-case, no port
|
||||
for canon_vhost in parser.options('vhosts'):
|
||||
value = parser.get('vhosts', canon_vhost)
|
||||
patterns = map(lambda x: x.lower().strip(),
|
||||
filter(None, value.split(',')))
|
||||
patterns = map(string.lower, map(string.strip,
|
||||
filter(None, string.split(value, ','))))
|
||||
for pat in patterns:
|
||||
if fnmatch.fnmatchcase(vhost, pat):
|
||||
return canon_vhost
|
||||
|
||||
return None
|
||||
|
||||
def get_root_config(self, rootname):
|
||||
"""Get configuration object with per-root overrides for 'rootname'"""
|
||||
if self.__parent:
|
||||
return self.__parent.get_root_config(rootname)
|
||||
elif rootname in self.__root_configs:
|
||||
return self.__root_configs[rootname]
|
||||
__guesser = self.__guesser
|
||||
__root_configs = self.__root_configs
|
||||
parser = self.parser
|
||||
self.parser = None
|
||||
self.__guesser = None
|
||||
self.__root_configs = None
|
||||
sub = copy.deepcopy(self)
|
||||
sub.parser = parser
|
||||
self.parser = parser
|
||||
self.__guesser = __guesser
|
||||
self.__root_configs = __root_configs
|
||||
self.__root_configs[rootname] = sub
|
||||
sub.__parent = self
|
||||
sub.overlay_root_options(rootname)
|
||||
return sub
|
||||
|
||||
def get_parent_config(self):
|
||||
"""Get the parent non-overridden config."""
|
||||
if self.__parent:
|
||||
return self.__parent
|
||||
return self
|
||||
|
||||
def overlay_root_options(self, rootname):
|
||||
"""Overlay per-root options for ROOTNAME atop the existing option
|
||||
set. This is a destructive change to the configuration."""
|
||||
@@ -348,6 +311,54 @@ class Config:
|
||||
d[option] = parser.get(section, option)
|
||||
return d.items()
|
||||
|
||||
def get_authorizer_and_params_hack(self, rootname):
|
||||
"""Return a 2-tuple containing the name and parameters of the
|
||||
authorizer configured for use with ROOTNAME.
|
||||
|
||||
### FIXME: This whole thing is a hack caused by our not being able
|
||||
### to non-destructively overlay root options when trying to do
|
||||
### something like a root listing (which might need to get
|
||||
### different authorizer bits for each and every root in the list).
|
||||
### Until we have a good way to do that, we expose this function,
|
||||
### which assumes that base and per-vhost configuration has been
|
||||
### absorbed into this object and that per-root options have *not*
|
||||
### been overlayed. See issue #371."""
|
||||
|
||||
# We assume that per-root options have *not* been overlayed.
|
||||
assert(self.root_options_overlayed == 0)
|
||||
|
||||
if not self.conf_path:
|
||||
return None, {}
|
||||
|
||||
# Figure out the authorizer by searching first for a per-root
|
||||
# override, then falling back to the base/vhost configuration.
|
||||
authorizer = None
|
||||
root_options_section = 'root-%s/options' % (rootname)
|
||||
if self.parser.has_section(root_options_section) \
|
||||
and self.parser.has_option(root_options_section, 'authorizer'):
|
||||
authorizer = self.parser.get(root_options_section, 'authorizer')
|
||||
if not authorizer:
|
||||
authorizer = self.options.authorizer
|
||||
|
||||
# No authorizer? Get outta here.
|
||||
if not authorizer:
|
||||
return None, {}
|
||||
|
||||
# Dig up the parameters for the authorizer, starting with the
|
||||
# base/vhost items, then overlaying any root-specific ones we find.
|
||||
params = {}
|
||||
authz_section = 'authz-%s' % (authorizer)
|
||||
if hasattr(self, authz_section):
|
||||
sub_config = getattr(self, authz_section)
|
||||
for attr in dir(sub_config):
|
||||
params[attr] = getattr(sub_config, attr)
|
||||
root_authz_section = 'root-%s/authz-%s' % (rootname, authorizer)
|
||||
for section in self.parser.sections():
|
||||
if section == root_authz_section:
|
||||
for key, value in self._get_parser_items(self.parser, section):
|
||||
params[key] = value
|
||||
return authorizer, params
|
||||
|
||||
def get_authorizer_params(self, authorizer=None):
|
||||
"""Return a dictionary of parameter names and values which belong
|
||||
to the configured authorizer (or AUTHORIZER, if provided)."""
|
||||
@@ -360,14 +371,8 @@ class Config:
|
||||
sub_config = getattr(self, authz_section)
|
||||
for attr in dir(sub_config):
|
||||
params[attr] = getattr(sub_config, attr)
|
||||
params['__config'] = self
|
||||
return params
|
||||
|
||||
def guesser(self):
|
||||
if not self.__guesser:
|
||||
self.__guesser = ContentMagic(self.options.encodings)
|
||||
return self.__guesser
|
||||
|
||||
|
||||
def set_defaults(self):
|
||||
"Set some default values in the configuration."
|
||||
|
||||
@@ -385,12 +390,9 @@ class Config:
|
||||
self.utilities.cvsnt = 'cvs'
|
||||
else:
|
||||
self.utilities.cvsnt = None
|
||||
self.utilities.rcsfile_socket = ''
|
||||
self.utilities.svn = ''
|
||||
self.utilities.diff = ''
|
||||
self.utilities.cvsgraph = ''
|
||||
self.utilities.tika_server = ''
|
||||
self.utilities.tika_mime_types = ''
|
||||
|
||||
self.options.root_as_url_component = 1
|
||||
self.options.checkout_magic = 0
|
||||
@@ -399,12 +401,10 @@ class Config:
|
||||
self.options.mangle_email_addresses = 0
|
||||
self.options.custom_log_formatting = []
|
||||
self.options.default_file_view = "log"
|
||||
self.options.binary_mime_types = []
|
||||
self.options.http_expiration_time = 600
|
||||
self.options.generate_etags = 1
|
||||
self.options.svn_ignore_mimetype = 0
|
||||
self.options.svn_config_dir = None
|
||||
self.options.max_filesize_kbytes = 512
|
||||
self.options.use_rcsparse = 0
|
||||
self.options.sort_by = 'file'
|
||||
self.options.sort_group_dirs = 1
|
||||
@@ -419,7 +419,7 @@ class Config:
|
||||
self.options.hr_ignore_keyword_subst = 1
|
||||
self.options.hr_intraline = 0
|
||||
self.options.allow_compress = 0
|
||||
self.options.template_dir = "templates/default"
|
||||
self.options.template_dir = "templates"
|
||||
self.options.docroot = None
|
||||
self.options.show_subdir_lastmod = 0
|
||||
self.options.show_roots_lastmod = 0
|
||||
@@ -427,21 +427,17 @@ class Config:
|
||||
self.options.show_log_in_markup = 1
|
||||
self.options.cross_copies = 1
|
||||
self.options.use_localtime = 0
|
||||
self.options.iso8601_timestamps = 0
|
||||
self.options.short_log_len = 80
|
||||
self.options.enable_syntax_coloration = 1
|
||||
self.options.tabsize = 8
|
||||
self.options.detect_encoding = 0
|
||||
self.options.use_cvsgraph = 0
|
||||
self.options.cvsgraph_conf = "cvsgraph.conf"
|
||||
self.options.allowed_cvsgraph_useropts = []
|
||||
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.options.cvs_ondisk_charset = 'cp1251'
|
||||
self.options.encodings = 'cp1251:iso-8859-1'
|
||||
|
||||
self.templates.diff = None
|
||||
self.templates.directory = None
|
||||
@@ -455,33 +451,16 @@ class Config:
|
||||
self.templates.roots = None
|
||||
|
||||
self.cvsdb.enabled = 0
|
||||
self.cvsdb.index_content = 0
|
||||
self.cvsdb.enable_snippets = 1
|
||||
self.cvsdb.content_max_size = 0
|
||||
self.cvsdb.host = ''
|
||||
self.cvsdb.port = 3306
|
||||
self.cvsdb.socket = ''
|
||||
self.cvsdb.database_name = ''
|
||||
self.cvsdb.user = ''
|
||||
self.cvsdb.passwd = ''
|
||||
self.cvsdb.readonly_user = ''
|
||||
self.cvsdb.readonly_passwd = ''
|
||||
self.cvsdb.readonly_passwd = ''
|
||||
self.cvsdb.row_limit = 1000
|
||||
self.cvsdb.rss_row_limit = 100
|
||||
self.cvsdb.check_database_for_root = 0
|
||||
self.cvsdb.fulltext_min_relevance = 0.2
|
||||
|
||||
self.cvsdb.sphinx_host = ''
|
||||
self.cvsdb.sphinx_port = 3307
|
||||
self.cvsdb.sphinx_socket = ''
|
||||
self.cvsdb.sphinx_index = ''
|
||||
self.cvsdb.sphinx_preformatted_mime = 'text/(?!html|xml).*'
|
||||
self.cvsdb.sphinx_snippet_options = \
|
||||
'around: 15\n'\
|
||||
'limit: 200\n'\
|
||||
'before_match: <span style="color:red">\n'\
|
||||
'after_match: </span>\n'\
|
||||
'chunk_separator: ... \n\n'
|
||||
|
||||
self.query.viewvc_base_url = None
|
||||
|
||||
@@ -491,11 +470,11 @@ def _startswith(somestr, substr):
|
||||
def _parse_roots(config_name, config_value):
|
||||
roots = { }
|
||||
for root in config_value:
|
||||
try:
|
||||
name, path = root.split(':', 1)
|
||||
except:
|
||||
pos = string.find(root, ':')
|
||||
if pos < 0:
|
||||
raise MalformedRoot(config_name, root)
|
||||
roots[name.strip()] = path.strip()
|
||||
name, path = map(string.strip, (root[:pos], root[pos+1:]))
|
||||
roots[name] = path
|
||||
return roots
|
||||
|
||||
class ViewVCConfigurationError(Exception):
|
||||
@@ -508,7 +487,7 @@ class IllegalOverrideSection(ViewVCConfigurationError):
|
||||
def __str__(self):
|
||||
return "malformed configuration: illegal %s override section: %s" \
|
||||
% (self.override_type, self.section_name)
|
||||
|
||||
|
||||
class MalformedRoot(ViewVCConfigurationError):
|
||||
def __init__(self, config_name, value_given):
|
||||
Exception.__init__(self, config_name, value_given)
|
||||
@@ -521,3 +500,10 @@ class MalformedRoot(ViewVCConfigurationError):
|
||||
|
||||
class _sub_config:
|
||||
pass
|
||||
|
||||
if not hasattr(sys, 'hexversion'):
|
||||
# Python 1.5 or 1.5.1. fix the syntax for ConfigParser options.
|
||||
import regex
|
||||
ConfigParser.option_cre = regex.compile('^\([-A-Za-z0-9._]+\)\(:\|['
|
||||
+ string.whitespace
|
||||
+ ']*=\)\(.*\)$')
|
||||
|
807
lib/cvsdb.py
807
lib/cvsdb.py
File diff suppressed because it is too large
Load Diff
18
lib/dbi.py
18
lib/dbi.py
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 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 @@ import sys
|
||||
import time
|
||||
import types
|
||||
import re
|
||||
import calendar
|
||||
import compat
|
||||
import MySQLdb
|
||||
|
||||
# set to 1 to store commit times in UTC, or 0 to use the ViewVC machine's
|
||||
@@ -55,17 +55,9 @@ def TicksFromDateTime(datetime):
|
||||
t = datetime.tuple()
|
||||
|
||||
if utc_time:
|
||||
return calendar.timegm(t)
|
||||
return compat.timegm(t)
|
||||
else:
|
||||
return time.mktime(t[:8] + (-1,))
|
||||
|
||||
def connect(host, port, socket, user, passwd, db, charset = 'utf8'):
|
||||
return MySQLdb.connect(
|
||||
host = host,
|
||||
port = port,
|
||||
unix_socket = socket,
|
||||
user = user,
|
||||
passwd = passwd,
|
||||
db = db,
|
||||
charset = charset,
|
||||
use_unicode = charset == 'utf8')
|
||||
def connect(host, port, user, passwd, db):
|
||||
return MySQLdb.connect(host=host, port=port, user=user, passwd=passwd, db=db)
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 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
|
||||
|
45
lib/ezt.py
45
lib/ezt.py
@@ -687,6 +687,41 @@ def _write_value(value, args, ctx):
|
||||
ctx.formatters = formatters
|
||||
|
||||
|
||||
class TemplateData:
|
||||
"""A custom dictionary-like object that allows one-time definition
|
||||
of keys, and only value fetches and changes, and key deletions,
|
||||
thereafter.
|
||||
|
||||
EZT doesn't require the use of this special class -- a normal
|
||||
dict-type data dictionary works fine. But use of this class will
|
||||
assist those who want the data sent to their templates to have a
|
||||
consistent set of keys."""
|
||||
|
||||
def __init__(self, initial_data={}):
|
||||
self._items = initial_data
|
||||
|
||||
def __getitem__(self, key):
|
||||
return self._items.__getitem__(key)
|
||||
|
||||
def __setitem__(self, key, item):
|
||||
assert self._items.has_key(key)
|
||||
return self._items.__setitem__(key, item)
|
||||
|
||||
def __delitem__(self, key):
|
||||
return self._items.__delitem__(key)
|
||||
|
||||
def keys(self):
|
||||
return self._items.keys()
|
||||
|
||||
def merge(self, template_data):
|
||||
"""Merge the data in TemplataData instance TEMPLATA_DATA into this
|
||||
instance. Avoid the temptation to use this conditionally in your
|
||||
code -- it rather defeats the purpose of this class."""
|
||||
|
||||
assert isinstance(template_data, TemplateData)
|
||||
self._items.update(template_data._items)
|
||||
|
||||
|
||||
class Context:
|
||||
"""A container for the execution context"""
|
||||
def __init__(self, fp):
|
||||
@@ -805,26 +840,18 @@ class UnknownFormatConstantError(EZTException):
|
||||
"""The format specifier is an unknown value."""
|
||||
|
||||
def _raw_formatter(s):
|
||||
try: s = s.encode('utf-8')
|
||||
except: pass
|
||||
return s
|
||||
|
||||
def _html_formatter(s):
|
||||
try: s = s.encode('utf-8')
|
||||
except: pass
|
||||
return cgi.escape(s)
|
||||
|
||||
def _xml_formatter(s):
|
||||
try: s = s.encode('utf-8')
|
||||
except: pass
|
||||
s = s.replace('&', '&')
|
||||
s = s.replace('<', '<')
|
||||
s = s.replace('>', '>')
|
||||
return s
|
||||
|
||||
def _uri_formatter(s):
|
||||
try: s = s.encode('utf-8')
|
||||
except: pass
|
||||
return urllib.quote(s)
|
||||
|
||||
_formatters = {
|
||||
@@ -849,7 +876,7 @@ def test_parse():
|
||||
['', '["a \\"b[foo]" c.d f]', None, '']
|
||||
|
||||
def _test(argv):
|
||||
import doctest, ezt
|
||||
import doctest, ezt
|
||||
verbose = "-v" in argv
|
||||
return doctest.testmod(ezt, verbose=verbose)
|
||||
|
||||
|
@@ -1,283 +0,0 @@
|
||||
# -*-python-*-
|
||||
# -----------------------------------------------------------------------
|
||||
# Simple Global Authentication client for ViewVC
|
||||
# License: GPLv2+
|
||||
# Author: Vitaliy Filippov
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
# USAGE:
|
||||
#
|
||||
# import globalauth
|
||||
# c = globalauth.GlobalAuthClient()
|
||||
# c.auth(server)
|
||||
# user_name = c.user_name
|
||||
# user_url = c.user_url
|
||||
#
|
||||
# auth() will call sys.exit() when it needs to stop request processing
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import struct
|
||||
import cgi
|
||||
import binascii
|
||||
import time
|
||||
import datetime
|
||||
import urllib
|
||||
import urllib2
|
||||
import anyjson
|
||||
import random
|
||||
import Cookie
|
||||
|
||||
import ga_config
|
||||
|
||||
class FileCache:
|
||||
|
||||
def __init__(self, dir):
|
||||
self.dir = dir
|
||||
if not os.path.isdir(dir):
|
||||
os.mkdir(dir)
|
||||
|
||||
def fn(self, key):
|
||||
key = re.sub('([^a-zA-Z0-9_\-]+)', lambda x: binascii.hexlify(x.group(1)), key)
|
||||
return self.dir+'/'+key
|
||||
|
||||
def clean(self):
|
||||
t = time.time()
|
||||
for fn in os.listdir(self.dir):
|
||||
if t > os.stat(self.dir+'/'+fn).st_mtime:
|
||||
os.unlink(self.dir+'/'+fn)
|
||||
|
||||
def set(self, key, value, expire = 86400):
|
||||
fn = self.fn(key)
|
||||
try:
|
||||
f = open(fn,'w')
|
||||
if not expire:
|
||||
expire = 86400
|
||||
expire = time.time()+expire
|
||||
f.write(value)
|
||||
f.close()
|
||||
os.chmod(fn, 0600)
|
||||
os.utime(fn, (expire, expire))
|
||||
except:
|
||||
raise
|
||||
return 1
|
||||
|
||||
def get(self, key):
|
||||
fn = self.fn(key)
|
||||
try:
|
||||
f = open(fn,'r')
|
||||
value = f.read()
|
||||
f.close()
|
||||
if time.time() > os.stat(fn).st_mtime:
|
||||
os.unlink(fn)
|
||||
return ''
|
||||
return value
|
||||
except:
|
||||
pass
|
||||
return ''
|
||||
|
||||
def delete(self, key):
|
||||
fn = self.fn(key)
|
||||
try:
|
||||
os.unlink(fn)
|
||||
except:
|
||||
pass
|
||||
|
||||
class GlobalAuthClient:
|
||||
|
||||
wd = { 0 : 'Mon', 1 : 'Tue', 2 : 'Wed', 3 : 'Thu', 4 : 'Fri', 5 : 'Sat', 6 : 'Sun' }
|
||||
ms = { 1 : 'Jan', 2 : 'Feb', 3 : 'Mar', 4 : 'Apr', 5 : 'May', 6 : 'Jun', 7 : 'Jul', 8 : 'Aug', 9 : 'Sep', 10 : 'Oct', 11 : 'Nov', 12 : 'Dec' }
|
||||
|
||||
def __init__(self, server):
|
||||
|
||||
self.server = server
|
||||
self.v = {}
|
||||
for name, values in server.params().items():
|
||||
self.v[name] = values[0]
|
||||
fs = server.FieldStorage()
|
||||
for name in fs:
|
||||
self.v[name] = fs[name].value
|
||||
|
||||
self.cookies = Cookie.SimpleCookie()
|
||||
# '' default value is needed here - else we die here under WSGI without any exception O_o
|
||||
self.cookies.load(self.server.getenv('HTTP_COOKIE', ''))
|
||||
self.user_name = ''
|
||||
self.user_url = ''
|
||||
|
||||
if not ga_config.gac.get('globalauth_server', '') and not ga_config.gac.get('fof_sudo_server', ''):
|
||||
raise Exception('ga_config.gac must contain at least globalauth_server="URL" or fof_sudo_server="URL"')
|
||||
|
||||
self.gac = {
|
||||
'cookie_name' : 'simple_global_auth',
|
||||
'cookie_expire' : 86400*7,
|
||||
'cookie_path' : '/',
|
||||
'cookie_domain' : '',
|
||||
'globalauth_server' : '',
|
||||
'cache_dir' : os.path.abspath(os.path.dirname(__file__))+'/cache',
|
||||
'cut_email_at' : 0,
|
||||
'ga_always_require' : 0,
|
||||
'fof_sudo_server' : '',
|
||||
'fof_sudo_cookie' : 'fof_sudo_id',
|
||||
'gc_probability' : 20,
|
||||
}
|
||||
|
||||
for i in self.gac:
|
||||
if ga_config.gac.get(i, None) is not None:
|
||||
self.gac[i] = ga_config.gac[i]
|
||||
|
||||
self.cache = FileCache(self.gac['cache_dir'])
|
||||
|
||||
def auth(self):
|
||||
if self.gac['fof_sudo_server']:
|
||||
self.auth_fof_sudo()
|
||||
if not self.user_name and self.gac['globalauth_server']:
|
||||
self.auth_ga()
|
||||
|
||||
def auth_ga(self):
|
||||
i = random.randint(1, self.gac['gc_probability'])
|
||||
if i == 1:
|
||||
self.cache.clean()
|
||||
r_id = self.cookies.get(self.gac['cookie_name'], '')
|
||||
if r_id:
|
||||
r_id = r_id.value
|
||||
ga_id = self.v.get('ga_id', '')
|
||||
if self.v.get('ga_client', ''):
|
||||
self.ga_client(r_id, ga_id)
|
||||
return
|
||||
r_data = ''
|
||||
if r_id == 'nologin':
|
||||
r_data = 'nologin'
|
||||
elif r_id != '':
|
||||
r_data = self.cache.get('D'+r_id)
|
||||
if r_data != 'nologin':
|
||||
try: r_data = anyjson.deserialize(r_data)
|
||||
except: r_data = ''
|
||||
is_browser = re.match('opera|firefox|chrome|safari', self.server.getenv('HTTP_USER_AGENT'), re.I)
|
||||
if not r_data and (is_browser or self.gac['ga_always_require']) or self.v.get('ga_require', None):
|
||||
self.ga_begin()
|
||||
elif r_data and r_data != 'nologin':
|
||||
self.set_user(r_data)
|
||||
|
||||
def ga_client(self, r_id, ga_id):
|
||||
ga_key = self.v.get('ga_key', '')
|
||||
if ga_key and ga_key == self.cache.get('K'+ga_id):
|
||||
# Server-to-server request
|
||||
self.cache.delete('K'+ga_id)
|
||||
data = ''
|
||||
if self.v.get('ga_nologin','') != '':
|
||||
data = 'nologin'
|
||||
else:
|
||||
try: data = anyjson.deserialize(self.v.get('ga_data',''))
|
||||
except: raise
|
||||
if data != '':
|
||||
if data != 'nologin':
|
||||
data = anyjson.serialize(data)
|
||||
self.cache.set('D'+ga_id, data)
|
||||
self.server.header('text/plain')
|
||||
self.server.write('1')
|
||||
sys.exit()
|
||||
elif ga_key == '' and r_id != ga_id:
|
||||
# User redirect with different key
|
||||
d = self.cache.get('D'+ga_id)
|
||||
if d != 'nologin' and d != '':
|
||||
try: d = anyjson.deserialize(d)
|
||||
except: d = ''
|
||||
if d != '':
|
||||
self.setcookie(ga_id)
|
||||
self.redirect(self.clean_uri())
|
||||
self.server.header('text/plain', status=404)
|
||||
self.server.write('GlobalAuth key doesn\'t match')
|
||||
sys.exit()
|
||||
|
||||
def ga_begin(self):
|
||||
ga_id = binascii.hexlify(os.urandom(16))
|
||||
ga_key = binascii.hexlify(os.urandom(16))
|
||||
url = self.add_param(self.gac['globalauth_server'], '')
|
||||
try:
|
||||
resp = urllib2.urlopen(url+'ga_id='+urllib2.quote(ga_id)+'&ga_key='+urllib2.quote(ga_key))
|
||||
resp.read()
|
||||
if resp.code != 200:
|
||||
raise Exception(resp)
|
||||
except:
|
||||
self.setcookie('nologin')
|
||||
self.redirect(self.clean_uri())
|
||||
return_uri = 'http://'+self.server.getenv('HTTP_HOST')+self.server.getenv('REQUEST_URI')
|
||||
return_uri = self.add_param(return_uri, 'ga_client=1')
|
||||
self.cache.set('K'+ga_id, ga_key)
|
||||
url = url+'ga_id='+urllib2.quote(ga_id)+'&ga_url='+urllib2.quote(return_uri)
|
||||
if self.v.get('ga_require', '') == '' and not self.gac['ga_always_require']:
|
||||
url = url+'&ga_check=1'
|
||||
self.redirect(url)
|
||||
|
||||
def add_param(self, url, param):
|
||||
if url.find('?') != -1:
|
||||
url = url+'&'
|
||||
else:
|
||||
url = url+'?'
|
||||
return url+param
|
||||
|
||||
def auth_fof_sudo(self):
|
||||
sudo_id = self.cookies.get(self.gac['fof_sudo_cookie'], '')
|
||||
if sudo_id:
|
||||
sudo_id = sudo_id.value
|
||||
if sudo_id != '':
|
||||
url = self.gac['fof_sudo_server']
|
||||
if url.find('?') != -1:
|
||||
url = url+'&'
|
||||
else:
|
||||
url = url+'?'
|
||||
try:
|
||||
resp = urllib2.urlopen(url+'id='+urllib2.quote(sudo_id))
|
||||
d = resp.read()
|
||||
if resp.code != 200:
|
||||
raise Exception(resp)
|
||||
d = anyjson.deserialize(d)
|
||||
self.set_user(d)
|
||||
except:
|
||||
pass
|
||||
|
||||
def log(self, s):
|
||||
sys.stderr.write(s+"\n")
|
||||
sys.stderr.flush()
|
||||
|
||||
def redirect(self, url):
|
||||
self.server.addheader('Location', url)
|
||||
self.server.header(status='302 Moved Temporarily')
|
||||
self.server.write('This document is located <a href="%s">here</a>.' % url)
|
||||
sys.exit()
|
||||
|
||||
def setcookie(self, value):
|
||||
dom = self.gac['cookie_domain']
|
||||
if not dom:
|
||||
dom = self.server.getenv('HTTP_HOST')
|
||||
exp = ''
|
||||
if self.gac['cookie_expire'] > 0:
|
||||
tm = int(time.time()+self.gac['cookie_expire'])
|
||||
tm = datetime.datetime.utcfromtimestamp(tm)
|
||||
tm = "%s, %02d-%s-%04d %02d:%02d:%02d GMT" % (self.wd[tm.weekday()], tm.day, self.ms[tm.month], tm.year, tm.hour, tm.minute, tm.second)
|
||||
exp = '; expires='+tm
|
||||
self.server.addheader('Set-Cookie', "%s=%s; path=%s; domain=%s%s" % (self.gac['cookie_name'], value, self.gac['cookie_path'], dom, exp))
|
||||
|
||||
def clean_uri(self):
|
||||
uriargs = self.v.copy()
|
||||
for i in [ 'ga_id', 'ga_res', 'ga_key', 'ga_client', 'ga_nologin', 'ga_require' ]:
|
||||
uriargs.pop(i, None)
|
||||
uri = self.server.getenv('REQUEST_URI')
|
||||
p = uri.find('?')
|
||||
if p != -1:
|
||||
uri = uri[0:p]
|
||||
uri = 'http://'+self.server.getenv('HTTP_HOST')+uri+'?'+urllib.urlencode(uriargs)
|
||||
return uri
|
||||
|
||||
def set_user(self, r_data):
|
||||
r_email = r_data.get('user_email', '').encode('utf-8')
|
||||
r_url = r_data.get('user_url', '').encode('utf-8')
|
||||
if self.gac['cut_email_at']:
|
||||
p = r_email.find('@')
|
||||
if p != -1:
|
||||
r_email = r_email[0:p]
|
||||
self.user_name = r_email
|
||||
self.user_url = r_url
|
21
lib/idiff.py
21
lib/idiff.py
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
@@ -19,8 +19,6 @@ from __future__ import generators
|
||||
import difflib
|
||||
import sys
|
||||
import re
|
||||
|
||||
from common import _item, _RCSDIFF_NO_CHANGES
|
||||
import ezt
|
||||
import sapi
|
||||
|
||||
@@ -31,7 +29,6 @@ def sidebyside(fromlines, tolines, context):
|
||||
line_strip = lambda line: line.rstrip("\n")
|
||||
fromlines = map(line_strip, fromlines)
|
||||
tolines = map(line_strip, tolines)
|
||||
had_changes = 0
|
||||
|
||||
gap = False
|
||||
for fromdata, todata, flag in difflib._mdiff(fromlines, tolines, context):
|
||||
@@ -40,11 +37,8 @@ def sidebyside(fromlines, tolines, context):
|
||||
else:
|
||||
from_item = _mdiff_split(flag, fromdata)
|
||||
to_item = _mdiff_split(flag, todata)
|
||||
had_changes = 1
|
||||
yield _item(gap=ezt.boolean(gap), columns=(from_item, to_item), type="intraline")
|
||||
yield _item(gap=ezt.boolean(gap), columns=(from_item, to_item))
|
||||
gap = False
|
||||
if not had_changes:
|
||||
yield _item(type=_RCSDIFF_NO_CHANGES)
|
||||
|
||||
_re_mdiff = re.compile("\0([+-^])(.*?)\1")
|
||||
|
||||
@@ -77,26 +71,19 @@ def unified(fromlines, tolines, context):
|
||||
|
||||
diff = difflib.Differ().compare(fromlines, tolines)
|
||||
lastrow = None
|
||||
had_changes = 0
|
||||
|
||||
for row in _trim_context(diff, context):
|
||||
if row[0].startswith("? "):
|
||||
had_changes = 1
|
||||
yield _differ_split(lastrow, row[0])
|
||||
lastrow = None
|
||||
else:
|
||||
if lastrow:
|
||||
had_changes = 1
|
||||
yield _differ_split(lastrow, None)
|
||||
lastrow = row
|
||||
|
||||
if lastrow:
|
||||
had_changes = 1
|
||||
yield _differ_split(lastrow, None)
|
||||
|
||||
if not had_changes:
|
||||
yield _item(type=_RCSDIFF_NO_CHANGES)
|
||||
|
||||
def _trim_context(lines, context_size):
|
||||
"""Trim context lines that don't surround changes from Differ results
|
||||
|
||||
@@ -189,6 +176,10 @@ def _differ_split(row, guide):
|
||||
return _item(gap=ezt.boolean(gap), type=type, segments=segments,
|
||||
left_number=left_number, right_number=right_number)
|
||||
|
||||
class _item:
|
||||
def __init__(self, **kw):
|
||||
vars(self).update(kw)
|
||||
|
||||
try:
|
||||
### Using difflib._mdiff function here was the easiest way of obtaining
|
||||
### intraline diffs for use in ViewVC, but it doesn't exist prior to
|
||||
|
11
lib/popen.py
11
lib/popen.py
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 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,6 +23,7 @@ import os
|
||||
import sys
|
||||
import sapi
|
||||
import threading
|
||||
import string
|
||||
|
||||
if sys.platform == "win32":
|
||||
import win32popen
|
||||
@@ -35,7 +36,7 @@ def popen(cmd, args, mode, capture_err=1):
|
||||
if sys.platform == "win32":
|
||||
command = win32popen.CommandLine(cmd, args)
|
||||
|
||||
if mode.find('r') >= 0:
|
||||
if string.find(mode, 'r') >= 0:
|
||||
hStdIn = None
|
||||
|
||||
if debug.SHOW_CHILD_PROCESSES:
|
||||
@@ -84,7 +85,7 @@ def popen(cmd, args, mode, capture_err=1):
|
||||
# in the parent
|
||||
|
||||
# close the descriptor that we don't need and return the other one.
|
||||
if mode.find('r') >= 0:
|
||||
if string.find(mode, 'r') >= 0:
|
||||
os.close(w)
|
||||
return _pipe(os.fdopen(r, mode), pid)
|
||||
os.close(r)
|
||||
@@ -95,7 +96,7 @@ def popen(cmd, args, mode, capture_err=1):
|
||||
# we'll need /dev/null for the discarded I/O
|
||||
null = os.open('/dev/null', os.O_RDWR)
|
||||
|
||||
if mode.find('r') >= 0:
|
||||
if string.find(mode, 'r') >= 0:
|
||||
# hook stdout/stderr to the "write" channel
|
||||
os.dup2(w, 1)
|
||||
# "close" stdin; the child shouldn't use it
|
||||
@@ -124,7 +125,7 @@ def popen(cmd, args, mode, capture_err=1):
|
||||
os.execvp(cmd, (cmd,) + tuple(args))
|
||||
except:
|
||||
# aid debugging, if the os.execvp above fails for some reason:
|
||||
print "<h2>exec failed:</h2><pre>", cmd, ' '.join(args), "</pre>"
|
||||
print "<h2>exec failed:</h2><pre>", cmd, string.join(args), "</pre>"
|
||||
raise
|
||||
|
||||
# crap. shouldn't be here.
|
||||
|
@@ -1,154 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
pygments.lexers.sp4
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Lexer for CustIS m4-preprocessed PL/SQL.
|
||||
|
||||
:copyright: 2009+ by Vitaliy Filippov <vitalif@mail.ru>.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
,
|
||||
'Sp4Lexer': ('pygments.lexers.sp4', 'SP4', ('sp4',), ('*.sp4'), ('text/x-sp4',))
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
from pygments.lexer import Lexer, RegexLexer, include, bygroups, using, this, \
|
||||
do_insertions
|
||||
from pygments.token import Error, Punctuation, \
|
||||
Text, Comment, Operator, Keyword, Name, String, Number, Generic
|
||||
from pygments.util import shebang_matches
|
||||
|
||||
|
||||
__all__ = ['Sp4Lexer']
|
||||
|
||||
line_re = re.compile('.*?\n')
|
||||
|
||||
|
||||
class Sp4Lexer(RegexLexer):
|
||||
name = 'SP4'
|
||||
aliases = ['sp4']
|
||||
filenames = ['*.sp4']
|
||||
mimetypes = ['text/x-sp4']
|
||||
|
||||
flags = re.IGNORECASE
|
||||
tokens = {
|
||||
'root': [
|
||||
(r'\s+', Text),
|
||||
(r'--.*?\n', Comment.Single),
|
||||
(r'/\*', Comment.Multiline, 'multiline-comments'),
|
||||
(r'(ABORT|ABS|ABSOLUTE|ACCESS|ADA|ADD|ADMIN|AFTER|AGGREGATE|'
|
||||
r'ALIAS|ALL|ALLOCATE|ALTER|ANALYSE|ANALYZE|AND|ANY|ARE|AS|'
|
||||
r'ASC|ASENSITIVE|ASSERTION|ASSIGNMENT|ASYMMETRIC|AT|ATOMIC|'
|
||||
r'AUTHORIZATION|AVG|BACKWARD|BEFORE|BEGIN|BETWEEN|BITVAR|'
|
||||
r'BIT_LENGTH|BOTH|BREADTH|BY|C|CACHE|CALL|CALLED|CARDINALITY|'
|
||||
r'CASCADE|CASCADED|CASE|CAST|CATALOG|CATALOG_NAME|CHAIN|'
|
||||
r'CHARACTERISTICS|CHARACTER_LENGTH|CHARACTER_SET_CATALOG|'
|
||||
r'CHARACTER_SET_NAME|CHARACTER_SET_SCHEMA|CHAR_LENGTH|CHECK|'
|
||||
r'CHECKED|CHECKPOINT|CLASS|CLASS_ORIGIN|CLOB|CLOSE|CLUSTER|'
|
||||
r'COALSECE|COBOL|COLLATE|COLLATION|COLLATION_CATALOG|'
|
||||
r'COLLATION_NAME|COLLATION_SCHEMA|COLUMN|COLUMN_NAME|'
|
||||
r'COMMAND_FUNCTION|COMMAND_FUNCTION_CODE|COMMENT|COMMIT|'
|
||||
r'COMMITTED|COMPLETION|CONDITION_NUMBER|CONNECT|CONNECTION|'
|
||||
r'CONNECTION_NAME|CONSTRAINT|CONSTRAINTS|CONSTRAINT_CATALOG|'
|
||||
r'CONSTRAINT_NAME|CONSTRAINT_SCHEMA|CONSTRUCTOR|CONTAINS|'
|
||||
r'CONTINUE|CONVERSION|CONVERT|COPY|CORRESPONTING|COUNT|'
|
||||
r'CREATE|CREATEDB|CREATEUSER|CROSS|CUBE|CURRENT|CURRENT_DATE|'
|
||||
r'CURRENT_PATH|CURRENT_ROLE|CURRENT_TIME|CURRENT_TIMESTAMP|'
|
||||
r'CURRENT_USER|CURSOR|CURSOR_NAME|CYCLE|DATA|DATABASE|'
|
||||
r'DATETIME_INTERVAL_CODE|DATETIME_INTERVAL_PRECISION|DAY|'
|
||||
r'DEALLOCATE|DECLARE|DEFAULT|DEFAULTS|DEFERRABLE|DEFERRED|'
|
||||
r'DEFINED|DEFINER|DELETE|DELIMITER|DELIMITERS|DEREF|DESC|'
|
||||
r'DESCRIBE|DESCRIPTOR|DESTROY|DESTRUCTOR|DETERMINISTIC|'
|
||||
r'DIAGNOSTICS|DICTIONARY|DISCONNECT|DISPATCH|DISTINCT|DO|'
|
||||
r'DOMAIN|DROP|DYNAMIC|DYNAMIC_FUNCTION|DYNAMIC_FUNCTION_CODE|'
|
||||
r'EACH|ELSE|ENCODING|ENCRYPTED|END|END-EXEC|EQUALS|ESCAPE|EVERY|'
|
||||
r'EXCEPT|ESCEPTION|EXCLUDING|EXCLUSIVE|EXEC|EXECUTE|EXISTING|'
|
||||
r'EXISTS|EXPLAIN|EXTERNAL|EXTRACT|FALSE|FETCH|FINAL|FIRST|FOR|'
|
||||
r'FORCE|FOREIGN|FORTRAN|FORWARD|FOUND|FREE|FREEZE|FROM|FULL|'
|
||||
r'FUNCTION|G|GENERAL|GENERATED|GET|GLOBAL|GO|GOTO|GRANT|GRANTED|'
|
||||
r'GROUP|GROUPING|HANDLER|HAVING|HIERARCHY|HOLD|HOST|IDENTITY|'
|
||||
r'IGNORE|ILIKE|IMMEDIATE|IMMUTABLE|IMPLEMENTATION|IMPLICIT|IN|'
|
||||
r'INCLUDING|INCREMENT|INDEX|INDITCATOR|INFIX|INHERITS|INITIALIZE|'
|
||||
r'INITIALLY|INNER|INOUT|INPUT|INSENSITIVE|INSERT|INSTANTIABLE|'
|
||||
r'INSTEAD|INTERSECT|INTO|INVOKER|IS|ISNULL|ISOLATION|ITERATE|JOIN|'
|
||||
r'K|KEY|KEY_MEMBER|KEY_TYPE|LANCOMPILER|LANGUAGE|LARGE|LAST|'
|
||||
r'LATERAL|LEADING|LEFT|LENGTH|LESS|LEVEL|LIKE|LILMIT|LISTEN|LOAD|'
|
||||
r'LOCAL|LOCALTIME|LOCALTIMESTAMP|LOCATION|LOCATOR|LOCK|LOWER|M|'
|
||||
r'MAP|MATCH|MAX|MAXVALUE|MESSAGE_LENGTH|MESSAGE_OCTET_LENGTH|'
|
||||
r'MESSAGE_TEXT|METHOD|MIN|MINUTE|MINVALUE|MOD|MODE|MODIFIES|'
|
||||
r'MODIFY|MONTH|MORE|MOVE|MUMPS|NAMES|NATIONAL|NATURAL|NCHAR|'
|
||||
r'NCLOB|NEW|NEXT|NO|NOCREATEDB|NOCREATEUSER|NONE|NOT|NOTHING|'
|
||||
r'NOTIFY|NOTNULL|NULL|NULLABLE|NULLIF|OBJECT|OCTET_LENGTH|OF|OFF|'
|
||||
r'OFFSET|OIDS|OLD|ON|ONLY|OPEN|OPERATION|OPERATOR|OPTION|OPTIONS|'
|
||||
r'OR|ORDER|ORDINALITY|OUT|OUTER|OUTPUT|OVERLAPS|OVERLAY|OVERRIDING|'
|
||||
r'OWNER|PAD|PARAMETER|PARAMETERS|PARAMETER_MODE|PARAMATER_NAME|'
|
||||
r'PARAMATER_ORDINAL_POSITION|PARAMETER_SPECIFIC_CATALOG|'
|
||||
r'PARAMETER_SPECIFIC_NAME|PARAMATER_SPECIFIC_SCHEMA|PARTIAL|'
|
||||
r'PASCAL|PENDANT|PLACING|PLI|POSITION|POSTFIX|PRECISION|PREFIX|'
|
||||
r'PREORDER|PREPARE|PRESERVE|PRIMARY|PRIOR|PRIVILEGES|PROCEDURAL|'
|
||||
r'PROCEDURE|PUBLIC|READ|READS|RECHECK|RECURSIVE|REF|REFERENCES|'
|
||||
r'REFERENCING|REINDEX|RELATIVE|RENAME|REPEATABLE|REPLACE|RESET|'
|
||||
r'RESTART|RESTRICT|RESULT|RETURN|RETURNED_LENGTH|'
|
||||
r'RETURNED_OCTET_LENGTH|RETURNED_SQLSTATE|RETURNS|REVOKE|RIGHT|'
|
||||
r'ROLE|ROLLBACK|ROLLUP|ROUTINE|ROUTINE_CATALOG|ROUTINE_NAME|'
|
||||
r'ROUTINE_SCHEMA|ROW|ROWS|ROW_COUNT|RULE|SAVE_POINT|SCALE|SCHEMA|'
|
||||
r'SCHEMA_NAME|SCOPE|SCROLL|SEARCH|SECOND|SECURITY|SELECT|SELF|'
|
||||
r'SENSITIVE|SERIALIZABLE|SERVER_NAME|SESSION|SESSION_USER|SET|'
|
||||
r'SETOF|SETS|SHARE|SHOW|SIMILAR|SIMPLE|SIZE|SOME|SOURCE|SPACE|'
|
||||
r'SPECIFIC|SPECIFICTYPE|SPECIFIC_NAME|SQL|SQLCODE|SQLERROR|'
|
||||
r'SQLEXCEPTION|SQLSTATE|SQLWARNINIG|STABLE|START|STATE|STATEMENT|'
|
||||
r'STATIC|STATISTICS|STDIN|STDOUT|STORAGE|STRICT|STRUCTURE|STYPE|'
|
||||
r'SUBCLASS_ORIGIN|SUBLIST|SUBSTRING|SUM|SYMMETRIC|SYSID|SYSTEM|'
|
||||
r'SYSTEM_USER|TABLE|TABLE_NAME| TEMP|TEMPLATE|TEMPORARY|TERMINATE|'
|
||||
r'THAN|THEN|TIMESTAMP|TIMEZONE_HOUR|TIMEZONE_MINUTE|TO|TOAST|'
|
||||
r'TRAILING|TRANSATION|TRANSACTIONS_COMMITTED|'
|
||||
r'TRANSACTIONS_ROLLED_BACK|TRANSATION_ACTIVE|TRANSFORM|'
|
||||
r'TRANSFORMS|TRANSLATE|TRANSLATION|TREAT|TRIGGER|TRIGGER_CATALOG|'
|
||||
r'TRIGGER_NAME|TRIGGER_SCHEMA|TRIM|TRUE|TRUNCATE|TRUSTED|TYPE|'
|
||||
r'UNCOMMITTED|UNDER|UNENCRYPTED|UNION|UNIQUE|UNKNOWN|UNLISTEN|'
|
||||
r'UNNAMED|UNNEST|UNTIL|UPDATE|UPPER|USAGE|USER|'
|
||||
r'USER_DEFINED_TYPE_CATALOG|USER_DEFINED_TYPE_NAME|'
|
||||
r'USER_DEFINED_TYPE_SCHEMA|USING|VACUUM|VALID|VALIDATOR|VALUES|'
|
||||
r'VARIABLE|VERBOSE|VERSION|VIEW|VOLATILE|WHEN|WHENEVER|WHERE|'
|
||||
r'WITH|WITHOUT|WORK|WRITE|YEAR|ZONE|BINARY_INTEGER|BODY|BREAK|'
|
||||
r'CONSTANT|DEFINITION|DELETING|DLOB|DUMMY|ELSIF|ERRLVL|ERROR|'
|
||||
r'EXCEPTION|EXIT|IF|INSERTING|ISOPEN|LOOP|NO_DATA_FOUND|NOTFOUND|'
|
||||
r'OTHERS|PACKAGE|PRAGMA|RAISE|RECORD|RESTRICT_REFERENCES|RETURNING|'
|
||||
r'RNDS|RNPS|ROWCOUNT|ROWNUM|ROWTYPE|SAVEPOINT|SQLERRM|TOO_MANY_ROWS|'
|
||||
r'UPDATING|WHILE|WNDS|WNPS|BULK|CHR|COLLECT|GREATEST|INSTR|LEAST|'
|
||||
r'LENGTHB|NOCOPY|NOWAIT|NVL|RPAD|SUBST|SUBSTB|SUBSTR|SYSDATE|'
|
||||
r'TABLESPACE|TO_CHAR|TO_DATE|TO_NUMBER|TRUNC)\b', Keyword),
|
||||
(r'(ARRAY|BIGINT|BINARY|BIT|BLOB|BOOLEAN|CHAR|CHARACTER|DATE|'
|
||||
r'DEC|DECIMAL|FLOAT|INT|INTEGER|INTERVAL|NUMBER|NUMERIC|REAL|'
|
||||
r'SERIAL|SMALLINT|VARCHAR|VARCHAR2|VARYING|INT8|SERIAL8|TEXT)\b',
|
||||
Name.Builtin),
|
||||
(r'(_5date_args|_actual_area|_ASSERT|_doc_code|_doc_var|_doc_var_list|'
|
||||
r'_enforce|_ent|_enum|_enums_define|_enums_member_define|_field|'
|
||||
r'_fields|_first|_FMT|_in_list|_index|_index_unique|_indexes'
|
||||
r'_MAX_STRLEN|_MID_STRLEN|_param|_package|_pk|_plsql_param|_plsql_return|'
|
||||
r'_private_function|_private_procedure|_private_type|_private_var|'
|
||||
r'_protected_function|_protected_procedure|_protected_type|_protected_var|'
|
||||
r'_public_function|_public_procedure|_public_type|_public_var|_quote|_RAISE|'
|
||||
r'_RAISEW|_reference|_references|_request_arg|_request_define|'
|
||||
r'_request_res|_STR_CONST|_substr|_table|_tail|builtin|cdr|changecom|'
|
||||
r'changequote|changeword|debugfile|decr|define|divert|dummy_list|dumpdef|'
|
||||
r'errprint|eval|foreach|forloop|GetNth|ifelse|include|incr|index|indir|'
|
||||
r'len|lowcase|maketemp|Nth|patsubst|popdef|pushdef|regexp|'
|
||||
r'remove_prefix|shift|sinclude|Size|syscmd|TRACE|traceoff|traceon|'
|
||||
r'translit|undefine|undivert|upcase)\b',
|
||||
Name.Function),
|
||||
(r'[+*/<>=~!@#%^&|`?^-]', Operator),
|
||||
(r'[0-9]+', Number.Integer),
|
||||
(r"'(''|[^'\\]|\\\'|\\\\)*'", String.Single),
|
||||
(r'"(""|[^"\\]|\\\"|\\\\)*"', String.Symbol),
|
||||
(r'[a-zA-Z_][a-zA-Z0-9_]*', Name),
|
||||
(r'[;:()\[\],\.]', Punctuation)
|
||||
],
|
||||
'multiline-comments': [
|
||||
(r'/\*', Comment.Multiline, 'multiline-comments'),
|
||||
(r'\*/', Comment.Multiline, '#pop'),
|
||||
(r'[^/\*]+', Comment.Multiline),
|
||||
(r'[/*]', Comment.Multiline)
|
||||
]
|
||||
}
|
71
lib/query.py
71
lib/query.py
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 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,7 +23,6 @@ import sys
|
||||
import string
|
||||
import time
|
||||
|
||||
from common import _item, TemplateData
|
||||
import cvsdb
|
||||
import viewvc
|
||||
import ezt
|
||||
@@ -41,7 +40,6 @@ class FormData:
|
||||
self.file = ""
|
||||
self.who = ""
|
||||
self.sortby = ""
|
||||
self.textquery = ""
|
||||
self.date = ""
|
||||
self.hours = 0
|
||||
|
||||
@@ -49,7 +47,7 @@ class FormData:
|
||||
|
||||
def decode_thyself(self, form):
|
||||
try:
|
||||
self.textquery = string.strip(form["textquery"].value)
|
||||
self.repository = string.strip(form["repository"].value)
|
||||
except KeyError:
|
||||
pass
|
||||
except TypeError:
|
||||
@@ -58,16 +56,7 @@ class FormData:
|
||||
self.valid = 1
|
||||
|
||||
try:
|
||||
self.repository = form["repository"].value.strip()
|
||||
except KeyError:
|
||||
pass
|
||||
except TypeError:
|
||||
pass
|
||||
else:
|
||||
self.valid = 1
|
||||
|
||||
try:
|
||||
self.branch = form["branch"].value.strip()
|
||||
self.branch = string.strip(form["branch"].value)
|
||||
except KeyError:
|
||||
pass
|
||||
except TypeError:
|
||||
@@ -76,7 +65,7 @@ class FormData:
|
||||
self.valid = 1
|
||||
|
||||
try:
|
||||
self.directory = form["directory"].value.strip()
|
||||
self.directory = string.strip(form["directory"].value)
|
||||
except KeyError:
|
||||
pass
|
||||
except TypeError:
|
||||
@@ -85,7 +74,7 @@ class FormData:
|
||||
self.valid = 1
|
||||
|
||||
try:
|
||||
self.file = form["file"].value.strip()
|
||||
self.file = string.strip(form["file"].value)
|
||||
except KeyError:
|
||||
pass
|
||||
except TypeError:
|
||||
@@ -94,7 +83,7 @@ class FormData:
|
||||
self.valid = 1
|
||||
|
||||
try:
|
||||
self.who = form["who"].value.strip()
|
||||
self.who = string.strip(form["who"].value)
|
||||
except KeyError:
|
||||
pass
|
||||
except TypeError:
|
||||
@@ -103,14 +92,14 @@ class FormData:
|
||||
self.valid = 1
|
||||
|
||||
try:
|
||||
self.sortby = form["sortby"].value.strip()
|
||||
self.sortby = string.strip(form["sortby"].value)
|
||||
except KeyError:
|
||||
pass
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
try:
|
||||
self.date = form["date"].value.strip()
|
||||
self.date = string.strip(form["date"].value)
|
||||
except KeyError:
|
||||
pass
|
||||
except TypeError:
|
||||
@@ -169,7 +158,7 @@ def listparse_string(str):
|
||||
## command; add the command and start over
|
||||
elif c == ",":
|
||||
## strip ending whitespace on un-quoted data
|
||||
temp = temp.rstrip()
|
||||
temp = string.rstrip(temp)
|
||||
return_list.append( ("", temp) )
|
||||
temp = ""
|
||||
state = "eat leading whitespace"
|
||||
@@ -257,15 +246,10 @@ def form_to_cvsdb_query(cfg, form_data):
|
||||
cmd = decode_command(cmd)
|
||||
query.SetAuthor(str, cmd)
|
||||
|
||||
if form_data.textquery:
|
||||
query.SetTextQuery(form_data.textquery)
|
||||
|
||||
if form_data.sortby == "author":
|
||||
query.SetSortMethod("author")
|
||||
elif form_data.sortby == "file":
|
||||
query.SetSortMethod("file")
|
||||
elif form_data.sortby == "relevance" and form_data.textquery:
|
||||
query.SetSortMethod("relevance")
|
||||
else:
|
||||
query.SetSortMethod("date")
|
||||
|
||||
@@ -283,13 +267,13 @@ def form_to_cvsdb_query(cfg, form_data):
|
||||
|
||||
def prev_rev(rev):
|
||||
'''Returns a string representing the previous revision of the argument.'''
|
||||
r = rev.split('.')
|
||||
r = string.split(rev, '.')
|
||||
# decrement final revision component
|
||||
r[-1] = str(int(r[-1]) - 1)
|
||||
# prune if we pass the beginning of the branch
|
||||
if len(r) > 2 and r[-1] == '0':
|
||||
r = r[:-2]
|
||||
return '.'.join(r)
|
||||
return string.join(r, '.')
|
||||
|
||||
def is_forbidden(cfg, cvsroot_name, module):
|
||||
'''Return 1 if MODULE in CVSROOT_NAME is forbidden; return 0 otherwise.'''
|
||||
@@ -300,10 +284,11 @@ def is_forbidden(cfg, cvsroot_name, module):
|
||||
# the base and per-vhost configuration for authorizer and
|
||||
# authorizer parameters.
|
||||
if cvsroot_name:
|
||||
cfg = cfg.get_root_config(cvsroot_name)
|
||||
authorizer = cfg.options.authorizer
|
||||
params = cfg.get_authorizer_params()
|
||||
|
||||
authorizer, params = cfg.get_authorizer_and_params_hack(cvsroot_name)
|
||||
else:
|
||||
authorizer = cfg.options.authorizer
|
||||
params = cfg.get_authorizer_params()
|
||||
|
||||
# If CVSROOT_NAME isn't configured to use an authorizer, nothing
|
||||
# is forbidden. If it's configured to use something other than
|
||||
# the 'forbidden' authorizer, complain. Otherwise, check for
|
||||
@@ -315,7 +300,7 @@ def is_forbidden(cfg, cvsroot_name, module):
|
||||
"by this interface. The '%s' root is configured to "
|
||||
"use a different one." % (cvsroot_name))
|
||||
forbidden = params.get('forbidden', '')
|
||||
forbidden = map(lambda x: x.strip(), filter(None, forbidden.split(',')))
|
||||
forbidden = map(string.strip, filter(None, string.split(forbidden, ',')))
|
||||
default = 0
|
||||
for pat in forbidden:
|
||||
if pat[0] == '!':
|
||||
@@ -328,7 +313,7 @@ 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 server.escape(desc).replace('\n', '<br />') or ' '
|
||||
ob.log = desc and string.replace(server.escape(desc), '\n', '<br />') or ''
|
||||
|
||||
for commit in files:
|
||||
repository = commit.GetRepository()
|
||||
@@ -337,7 +322,7 @@ def build_commit(server, cfg, desc, files, cvsroots, viewvc_link):
|
||||
|
||||
## find the module name (if any)
|
||||
try:
|
||||
module = filter(None, directory.split('/'))[0]
|
||||
module = filter(None, string.split(directory, '/'))[0]
|
||||
except IndexError:
|
||||
module = None
|
||||
|
||||
@@ -379,9 +364,6 @@ def build_commit(server, cfg, desc, files, cvsroots, viewvc_link):
|
||||
flink = '[%s] %s' % (repository, file)
|
||||
dlink = None
|
||||
|
||||
ob.relevance = commit.GetRelevance()
|
||||
ob.plus += int(commit.GetPlusCount())
|
||||
ob.minus += int(commit.GetMinusCount())
|
||||
ob.files.append(_item(date=ctime,
|
||||
author=commit.GetAuthor(),
|
||||
link=flink,
|
||||
@@ -395,8 +377,9 @@ def build_commit(server, cfg, desc, files, cvsroots, viewvc_link):
|
||||
|
||||
return ob
|
||||
|
||||
def run_query(server, cfg, db, form_data, viewvc_link):
|
||||
def run_query(server, cfg, form_data, viewvc_link):
|
||||
query = form_to_cvsdb_query(cfg, form_data)
|
||||
db = cvsdb.ConnectDatabaseReadOnly(cfg)
|
||||
db.RunQuery(query)
|
||||
|
||||
commit_list = query.GetCommitList()
|
||||
@@ -446,9 +429,9 @@ def main(server, cfg, viewvc_link):
|
||||
form = server.FieldStorage()
|
||||
form_data = FormData(form)
|
||||
|
||||
db = cvsdb.ConnectDatabaseReadOnly(cfg, None)
|
||||
if form_data.valid:
|
||||
commits, row_limit_reached = run_query(server, cfg, db, form_data, viewvc_link)
|
||||
commits, row_limit_reached = run_query(server, cfg,
|
||||
form_data, viewvc_link)
|
||||
query = None
|
||||
else:
|
||||
commits = [ ]
|
||||
@@ -459,11 +442,10 @@ def main(server, cfg, viewvc_link):
|
||||
if docroot is None and viewvc_link:
|
||||
docroot = viewvc_link + '/' + viewvc.docroot_magic_path
|
||||
|
||||
data = TemplateData({
|
||||
data = ezt.TemplateData({
|
||||
'cfg' : cfg,
|
||||
'address' : cfg.general.address,
|
||||
'vsn' : viewvc.__version__,
|
||||
'textquery' : server.escape(form_data.textquery),
|
||||
'repository' : server.escape(form_data.repository),
|
||||
'branch' : server.escape(form_data.branch),
|
||||
'directory' : server.escape(form_data.directory),
|
||||
@@ -477,7 +459,6 @@ def main(server, cfg, viewvc_link):
|
||||
'commits' : commits,
|
||||
'num_commits' : len(commits),
|
||||
'rss_href' : None,
|
||||
'repositories' : db.GetRepositoryList(),
|
||||
'hours' : form_data.hours and form_data.hours or 2,
|
||||
})
|
||||
|
||||
@@ -492,3 +473,7 @@ def main(server, cfg, viewvc_link):
|
||||
exc_info = debug.GetExceptionData()
|
||||
server.header(status=exc_info['status'])
|
||||
debug.PrintException(server, exc_info)
|
||||
|
||||
class _item:
|
||||
def __init__(self, **kw):
|
||||
vars(self).update(kw)
|
||||
|
16
lib/sapi.py
16
lib/sapi.py
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 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,6 +16,7 @@
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
import types
|
||||
import string
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
@@ -32,13 +33,11 @@ server = None
|
||||
# that character as-is, and sometimes needs to embed escaped values
|
||||
# into HTML attributes.
|
||||
def escape(s):
|
||||
try: s = s.encode('utf8')
|
||||
except: pass
|
||||
s = str(s)
|
||||
s = s.replace('&', '&')
|
||||
s = s.replace('>', '>')
|
||||
s = s.replace('<', '<')
|
||||
s = s.replace('"', """)
|
||||
s = string.replace(s, '&', '&')
|
||||
s = string.replace(s, '>', '>')
|
||||
s = string.replace(s, '<', '<')
|
||||
s = string.replace(s, '"', """)
|
||||
return s
|
||||
|
||||
|
||||
@@ -212,6 +211,9 @@ class WsgiServer(Server):
|
||||
global server
|
||||
server = self
|
||||
|
||||
global cgi
|
||||
import cgi
|
||||
|
||||
def addheader(self, name, value):
|
||||
self._headers.append((name, value))
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 2006-2013 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 2006-2012 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
|
||||
@@ -12,6 +12,10 @@
|
||||
|
||||
"""Generic API for implementing authorization checks employed by ViewVC."""
|
||||
|
||||
import string
|
||||
import vclib
|
||||
|
||||
|
||||
class GenericViewVCAuthorizer:
|
||||
"""Abstract class encapsulating version control authorization routines."""
|
||||
|
||||
@@ -41,7 +45,7 @@ class GenericViewVCAuthorizer:
|
||||
pass
|
||||
|
||||
|
||||
|
||||
|
||||
##############################################################################
|
||||
|
||||
class ViewVCAuthorizer(GenericViewVCAuthorizer):
|
||||
|
@@ -1,92 +0,0 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 2009 Vitaliy Filippov.
|
||||
#
|
||||
# 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/
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
import vcauth
|
||||
import vclib
|
||||
import string
|
||||
from xml.dom.minidom import parse
|
||||
|
||||
class ViewVCAuthorizer(vcauth.GenericViewVCAuthorizer):
|
||||
"""An authorizer which uses CVSnt access control lists (which are in form
|
||||
of XML files in CVS/ subdirectories in the repository)."""
|
||||
|
||||
def __init__(self, username, params={}):
|
||||
self.username = username
|
||||
self.params = params
|
||||
self.cfg = params['__config']
|
||||
self.default = params.get('default', 0)
|
||||
self.cached = {}
|
||||
self.xmlcache = {}
|
||||
|
||||
def dom_rights(self, doc, rightname, filename, username):
|
||||
result = None
|
||||
if filename:
|
||||
for node in doc.getElementsByTagName('file'):
|
||||
if node.getAttribute('name') == filename:
|
||||
for acl in node.getElementsByTagName('acl'):
|
||||
if not acl.getAttribute('branch'):
|
||||
u = acl.getAttribute('user')
|
||||
if result is None and (not u or u == 'anonymous') or u == username:
|
||||
for r in acl.getElementsByTag(rightname):
|
||||
result = not r.getAttribute('deny')
|
||||
break
|
||||
if result is None:
|
||||
for node in doc.getElementsByTagName('directory'):
|
||||
for acl in node.getElementsByTagName('acl'):
|
||||
if not acl.getAttribute('branch'):
|
||||
u = acl.getAttribute('user')
|
||||
if result is None and (not u or u == 'anonymous') or u == username:
|
||||
for r in acl.getElementsByTag(rightname):
|
||||
result = not r.getAttribute('deny')
|
||||
return result
|
||||
|
||||
def check(self, rootname, path_parts, filename):
|
||||
d = self.cfg.general.cvs_roots.get(rootname,None)
|
||||
if not d:
|
||||
return self.default
|
||||
i = len(path_parts)
|
||||
r = None
|
||||
while i >= 0:
|
||||
try:
|
||||
xml = d
|
||||
if len(path_parts):
|
||||
xml = xml + '/' + string.join(path_parts, '/')
|
||||
xml = xml + '/CVS/fileattr.xml'
|
||||
if self.cached.get(xml, None) is not None:
|
||||
return self.cached.get(xml, None)
|
||||
doc = self.xmlcache.get(xml, None)
|
||||
if doc is None:
|
||||
doc = parse(xml)
|
||||
self.xmlcache[xml] = doc
|
||||
r = self.dom_rights(doc, 'read', filename, self.username)
|
||||
if r is not None:
|
||||
self.cached[xml] = r
|
||||
return r
|
||||
raise Exception(None)
|
||||
except:
|
||||
if len(path_parts) > 0:
|
||||
path_parts = path_parts[:-1]
|
||||
filename = ''
|
||||
i = i-1
|
||||
return self.default
|
||||
|
||||
def check_root_access(self, rootname):
|
||||
return self.check(rootname, [], '')
|
||||
|
||||
def check_path_access(self, rootname, path_parts, pathtype, rev=None):
|
||||
if not path_parts:
|
||||
return self.check(rootname, [], '')
|
||||
if pathtype == vclib.DIR:
|
||||
return self.check(rootname, path_parts, '')
|
||||
f = path_parts[-1]
|
||||
path_parts = path_parts[:-1]
|
||||
return self.check(rootname, path_parts, f)
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 2006-2013 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 2006-2012 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
|
||||
@@ -12,13 +12,14 @@
|
||||
import vcauth
|
||||
import vclib
|
||||
import fnmatch
|
||||
import string
|
||||
|
||||
class ViewVCAuthorizer(vcauth.GenericViewVCAuthorizer):
|
||||
"""A simple top-level module authorizer."""
|
||||
def __init__(self, username, params={}):
|
||||
forbidden = params.get('forbidden', '')
|
||||
self.forbidden = map(lambda x: x.strip(),
|
||||
filter(None, forbidden.split(',')))
|
||||
self.forbidden = map(string.strip,
|
||||
filter(None, string.split(forbidden, ',')))
|
||||
|
||||
def check_root_access(self, rootname):
|
||||
return 1
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 2008-2013 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 2008-2012 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
|
||||
@@ -12,6 +12,7 @@
|
||||
import vcauth
|
||||
import vclib
|
||||
import fnmatch
|
||||
import string
|
||||
import re
|
||||
|
||||
|
||||
@@ -28,8 +29,8 @@ class ViewVCAuthorizer(vcauth.GenericViewVCAuthorizer):
|
||||
"""A simple regular-expression-based authorizer."""
|
||||
def __init__(self, username, params={}):
|
||||
forbidden = params.get('forbiddenre', '')
|
||||
self.forbidden = map(lambda x: _split_regexp(x.strip()),
|
||||
filter(None, forbidden.split(',')))
|
||||
self.forbidden = map(lambda x: _split_regexp(string.strip(x)),
|
||||
filter(None, string.split(forbidden, ',')))
|
||||
|
||||
def _check_root_path_access(self, root_path):
|
||||
default = 1
|
||||
@@ -55,7 +56,7 @@ class ViewVCAuthorizer(vcauth.GenericViewVCAuthorizer):
|
||||
def check_path_access(self, rootname, path_parts, pathtype, rev=None):
|
||||
root_path = rootname
|
||||
if path_parts:
|
||||
root_path = root_path + '/' + '/'.join(path_parts)
|
||||
root_path = root_path + '/' + string.join(path_parts, '/')
|
||||
if pathtype == vclib.DIR:
|
||||
root_path = root_path + '/'
|
||||
else:
|
||||
|
@@ -1,62 +0,0 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 2009 Vitaliy Filippov.
|
||||
#
|
||||
# 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/
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
import vcauth
|
||||
import vclib
|
||||
import string
|
||||
import grp
|
||||
import re
|
||||
|
||||
class ViewVCAuthorizer(vcauth.GenericViewVCAuthorizer):
|
||||
"""A simple authorizer checking system group membership for every
|
||||
repository and every user."""
|
||||
|
||||
def __init__(self, username, params={}):
|
||||
self.username = username
|
||||
self.params = params
|
||||
self.cfg = params['__config']
|
||||
self.cached = {}
|
||||
self.grp = {}
|
||||
self.byroot = {}
|
||||
self.fmt = map(lambda l: l.strip(), params.get('group_name_format', 'svn.%s|svn.%s.ro').split('|'))
|
||||
byr = params.get('by_root', '')
|
||||
for i in byr.split(','):
|
||||
if i.find(':') < 0:
|
||||
continue
|
||||
(root, auth) = i.split(':', 2)
|
||||
self.byroot[root.strip()] = map(lambda l: l.strip(), auth.split('|'))
|
||||
|
||||
def check_root_access(self, rootname):
|
||||
r = self.cached.get(rootname, None)
|
||||
if r is not None:
|
||||
return r
|
||||
grent = self.grp.get(rootname, None)
|
||||
if grent is None:
|
||||
grent = map(lambda grn: self.getgrnam(grn, rootname), self.byroot.get(rootname, self.fmt))
|
||||
self.grp[rootname] = grent
|
||||
r = 0
|
||||
for i in grent:
|
||||
if i and i.gr_mem and len(i.gr_mem) and self.username in i.gr_mem:
|
||||
r = 1
|
||||
break
|
||||
self.cached[rootname] = r
|
||||
return r
|
||||
|
||||
def getgrnam(self, grn, rootname):
|
||||
try:
|
||||
r = grp.getgrnam(grn.replace('%s', re.sub('[^\w\.\-]+', '', rootname)))
|
||||
except KeyError:
|
||||
r = None
|
||||
return r
|
||||
|
||||
def check_path_access(self, rootname, path_parts, pathtype, rev=None):
|
||||
return self.check_root_access(rootname)
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 2006-2013 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 2006-2012 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
|
||||
@@ -12,6 +12,7 @@
|
||||
# (c) 2006 Sergey Lapin <slapin@dataart.com>
|
||||
|
||||
import vcauth
|
||||
import string
|
||||
import os.path
|
||||
import debug
|
||||
|
||||
@@ -33,9 +34,9 @@ class ViewVCAuthorizer(vcauth.GenericViewVCAuthorizer):
|
||||
# See if the admin wants us to do case normalization of usernames.
|
||||
self.force_username_case = params.get('force_username_case')
|
||||
if self.force_username_case == "upper":
|
||||
self.username = username and username.upper() or username
|
||||
self.username = username and string.upper(username) or username
|
||||
elif self.force_username_case == "lower":
|
||||
self.username = username and username.lower() or username
|
||||
self.username = username and string.lower(username) or username
|
||||
elif not self.force_username_case:
|
||||
self.username = username
|
||||
else:
|
||||
@@ -96,9 +97,9 @@ class ViewVCAuthorizer(vcauth.GenericViewVCAuthorizer):
|
||||
all_groups.append(groupname)
|
||||
group_member = 0
|
||||
groupname = groupname.strip()
|
||||
entries = cp.get('groups', groupname).split(',')
|
||||
entries = string.split(cp.get('groups', groupname), ',')
|
||||
for entry in entries:
|
||||
entry = entry.strip()
|
||||
entry = string.strip(entry)
|
||||
if entry == self.username:
|
||||
group_member = 1
|
||||
break
|
||||
@@ -139,13 +140,13 @@ class ViewVCAuthorizer(vcauth.GenericViewVCAuthorizer):
|
||||
# Figure if this path is explicitly allowed or denied to USERNAME.
|
||||
allow = deny = 0
|
||||
for user in cp.options(section):
|
||||
user = user.strip()
|
||||
user = string.strip(user)
|
||||
if _userspec_matches_user(user):
|
||||
# See if the 'r' permission is among the ones granted to
|
||||
# USER. If so, we can stop looking. (Entry order is not
|
||||
# relevant -- we'll use the most permissive entry, meaning
|
||||
# one 'allow' is all we need.)
|
||||
allow = cp.get(section, user).find('r') != -1
|
||||
allow = string.find(cp.get(section, user), 'r') != -1
|
||||
deny = not allow
|
||||
if allow:
|
||||
break
|
||||
@@ -174,7 +175,7 @@ class ViewVCAuthorizer(vcauth.GenericViewVCAuthorizer):
|
||||
if section.find(':') == -1:
|
||||
path = section
|
||||
else:
|
||||
name, path = section.split(':', 1)
|
||||
name, path = string.split(section, ':', 1)
|
||||
if name == rootname:
|
||||
root_sections.append(section)
|
||||
continue
|
||||
@@ -186,14 +187,14 @@ class ViewVCAuthorizer(vcauth.GenericViewVCAuthorizer):
|
||||
# USERNAME, record it.
|
||||
if allow or deny:
|
||||
if path != '/':
|
||||
path = '/' + '/'.join(filter(None, path.split('/')))
|
||||
path = '/' + string.join(filter(None, string.split(path, '/')), '/')
|
||||
paths_for_root[path] = allow
|
||||
|
||||
# Okay. Superimpose those root-specific values now.
|
||||
for section in root_sections:
|
||||
|
||||
# Get the path again.
|
||||
name, path = section.split(':', 1)
|
||||
name, path = string.split(section, ':', 1)
|
||||
|
||||
# Check for a specific access determination.
|
||||
allow, deny = _process_access_section(section)
|
||||
@@ -202,7 +203,7 @@ class ViewVCAuthorizer(vcauth.GenericViewVCAuthorizer):
|
||||
# USERNAME, record it.
|
||||
if allow or deny:
|
||||
if path != '/':
|
||||
path = '/' + '/'.join(filter(None, path.split('/')))
|
||||
path = '/' + string.join(filter(None, string.split(path, '/')), '/')
|
||||
paths_for_root[path] = allow
|
||||
|
||||
# If the root isn't readable, there's no point in caring about all
|
||||
@@ -262,7 +263,7 @@ class ViewVCAuthorizer(vcauth.GenericViewVCAuthorizer):
|
||||
return 0
|
||||
parts = path_parts[:]
|
||||
while parts:
|
||||
path = '/' + '/'.join(parts)
|
||||
path = '/' + string.join(parts, '/')
|
||||
if paths.has_key(path):
|
||||
return paths[path]
|
||||
del parts[-1]
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 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,6 +14,7 @@
|
||||
such as CVS.
|
||||
"""
|
||||
|
||||
import string
|
||||
import types
|
||||
|
||||
|
||||
@@ -206,21 +207,7 @@ class Repository:
|
||||
|
||||
rev is the revision of the item to return information about
|
||||
"""
|
||||
|
||||
def filesize(self, path_parts, rev):
|
||||
"""Return the size of a versioned file's contents if it can be
|
||||
obtained without a brute force measurement; -1 otherwise.
|
||||
|
||||
NOTE: Callers that require a filesize answer when this function
|
||||
returns -1 may obtain it by measuring the data returned via
|
||||
openfile().
|
||||
|
||||
The path is specified as a list of components, relative to the root
|
||||
of the repository. e.g. ["subdir1", "subdir2", "filename"]
|
||||
|
||||
rev is the revision of the item to return information about
|
||||
"""
|
||||
|
||||
|
||||
# ======================================================================
|
||||
class DirEntry:
|
||||
@@ -326,7 +313,7 @@ class ItemNotFound(Error):
|
||||
# use '/' rather than os.sep because this is for user consumption, and
|
||||
# it was defined using URL separators
|
||||
if type(path) in (types.TupleType, types.ListType):
|
||||
path = '/'.join(path)
|
||||
path = string.join(path, '/')
|
||||
Error.__init__(self, path)
|
||||
|
||||
class InvalidRevision(Error):
|
||||
@@ -342,7 +329,7 @@ class NonTextualFileContents(Error):
|
||||
# ======================================================================
|
||||
# Implementation code used by multiple vclib modules
|
||||
|
||||
import tempfile
|
||||
import popen
|
||||
import os
|
||||
import time
|
||||
|
||||
@@ -352,7 +339,7 @@ def _diff_args(type, options):
|
||||
if type == CONTEXT:
|
||||
if options.has_key('context'):
|
||||
if options['context'] is None:
|
||||
args.append('--context=%i' % 0x01ffffff)
|
||||
args.append('--context=-1')
|
||||
else:
|
||||
args.append('--context=%i' % options['context'])
|
||||
else:
|
||||
@@ -360,7 +347,7 @@ def _diff_args(type, options):
|
||||
elif type == UNIFIED:
|
||||
if options.has_key('context'):
|
||||
if options['context'] is None:
|
||||
args.append('--unified=%i' % 0x01ffffff)
|
||||
args.append('--unified=-1')
|
||||
else:
|
||||
args.append('--unified=%i' % options['context'])
|
||||
else:
|
||||
@@ -386,15 +373,11 @@ class _diff_fp:
|
||||
def __init__(self, temp1, temp2, info1=None, info2=None, diff_cmd='diff', diff_opts=[]):
|
||||
self.temp1 = temp1
|
||||
self.temp2 = temp2
|
||||
self.temp3 = tempfile.mktemp()
|
||||
args = diff_opts[:]
|
||||
if info1 and info2:
|
||||
args.extend(["-L", self._label(info1), "-L", self._label(info2)])
|
||||
args.extend([temp1, temp2])
|
||||
args.insert(0, diff_cmd)
|
||||
os.system("'"+"' '".join(args)+"' > '"+self.temp3+"' 2> '"+self.temp3+"'")
|
||||
self.fp = open(self.temp3, 'rb')
|
||||
self.fp.seek(0)
|
||||
self.fp = popen.popen(diff_cmd, args, "r")
|
||||
|
||||
def read(self, bytes):
|
||||
return self.fp.read(bytes)
|
||||
@@ -402,9 +385,6 @@ class _diff_fp:
|
||||
def readline(self):
|
||||
return self.fp.readline()
|
||||
|
||||
def readlines(self):
|
||||
return self.fp.readlines()
|
||||
|
||||
def close(self):
|
||||
try:
|
||||
if self.fp:
|
||||
@@ -412,17 +392,13 @@ class _diff_fp:
|
||||
self.fp = None
|
||||
finally:
|
||||
try:
|
||||
if self.temp1 and self.temp1 != '/dev/null':
|
||||
if self.temp1:
|
||||
os.remove(self.temp1)
|
||||
self.temp1 = None
|
||||
self.temp1 = None
|
||||
finally:
|
||||
if self.temp2 and self.temp2 != '/dev/null':
|
||||
if self.temp2:
|
||||
os.remove(self.temp2)
|
||||
self.temp2 = None
|
||||
try:
|
||||
os.remove(self.temp3)
|
||||
finally:
|
||||
self.temp3 = None
|
||||
self.temp2 = None
|
||||
|
||||
def __del__(self):
|
||||
self.close()
|
||||
@@ -440,7 +416,7 @@ def check_root_access(repos):
|
||||
if not auth:
|
||||
return 1
|
||||
return auth.check_root_access(repos.rootname())
|
||||
|
||||
|
||||
def check_path_access(repos, path_parts, pathtype=None, rev=None):
|
||||
"""Return 1 iff the associated username is permitted to read
|
||||
revision REV of the path PATH_PARTS (of type PATHTYPE) in repository
|
||||
@@ -451,7 +427,5 @@ def check_path_access(repos, path_parts, pathtype=None, rev=None):
|
||||
return 1
|
||||
if not pathtype:
|
||||
pathtype = repos.itemtype(path_parts, rev)
|
||||
if not path_parts:
|
||||
return auth.check_root_access(repos.rootname())
|
||||
return auth.check_path_access(repos.rootname(), path_parts, pathtype, rev)
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 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
|
||||
@@ -11,11 +11,6 @@
|
||||
# -----------------------------------------------------------------------
|
||||
import os
|
||||
import os.path
|
||||
import time
|
||||
|
||||
|
||||
def cvs_strptime(timestr):
|
||||
return time.strptime(timestr, '%Y/%m/%d %H:%M:%S')[:-1] + (0,)
|
||||
|
||||
|
||||
def canonicalize_rootpath(rootpath):
|
||||
@@ -23,10 +18,6 @@ def canonicalize_rootpath(rootpath):
|
||||
return os.path.normpath(rootpath)
|
||||
|
||||
|
||||
def _is_cvsroot(path):
|
||||
return os.path.exists(os.path.join(path, "CVSROOT", "config"))
|
||||
|
||||
|
||||
def expand_root_parent(parent_path):
|
||||
# Each subdirectory of PARENT_PATH that contains a child
|
||||
# "CVSROOT/config" is added the set of returned roots. Or, if the
|
||||
@@ -35,33 +26,20 @@ def expand_root_parent(parent_path):
|
||||
assert os.path.isabs(parent_path)
|
||||
roots = {}
|
||||
subpaths = os.listdir(parent_path)
|
||||
cvsroot = os.path.exists(os.path.join(parent_path, "CVSROOT", "config"))
|
||||
for rootname in subpaths:
|
||||
rootpath = os.path.join(parent_path, rootname)
|
||||
if _is_cvsroot(parent_path) or _is_cvsroot(rootpath):
|
||||
if cvsroot \
|
||||
or (os.path.exists(os.path.join(rootpath, "CVSROOT", "config"))):
|
||||
roots[rootname] = canonicalize_rootpath(rootpath)
|
||||
return roots
|
||||
|
||||
|
||||
def find_root_in_parent(parent_path, rootname):
|
||||
"""Search PARENT_PATH for a root named ROOTNAME, returning the
|
||||
canonicalized ROOTPATH of the root if found; return None if no such
|
||||
root is found."""
|
||||
# Is PARENT_PATH itself a CVS repository? If so, we allow ROOTNAME
|
||||
# to be any subdir within it. Otherwise, we expect
|
||||
# PARENT_PATH/ROOTNAME to be a CVS repository.
|
||||
assert os.path.isabs(parent_path)
|
||||
rootpath = os.path.join(parent_path, rootname)
|
||||
if (_is_cvsroot(parent_path) and os.path.exists(rootpath)) \
|
||||
or _is_cvsroot(rootpath):
|
||||
return canonicalize_rootpath(rootpath)
|
||||
return None
|
||||
|
||||
|
||||
def CVSRepository(name, rootpath, authorizer, utilities, use_rcsparse, charset_guesser = None):
|
||||
def CVSRepository(name, rootpath, authorizer, utilities, use_rcsparse):
|
||||
rootpath = canonicalize_rootpath(rootpath)
|
||||
if use_rcsparse:
|
||||
import ccvs
|
||||
return ccvs.CCVSRepository(name, rootpath, authorizer, utilities, charset_guesser)
|
||||
return ccvs.CCVSRepository(name, rootpath, authorizer, utilities)
|
||||
else:
|
||||
import bincvs
|
||||
return bincvs.BinCVSRepository(name, rootpath, authorizer, utilities, charset_guesser)
|
||||
return bincvs.BinCVSRepository(name, rootpath, authorizer, utilities)
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
@@ -18,29 +18,23 @@ import os
|
||||
import os.path
|
||||
import sys
|
||||
import stat
|
||||
import string
|
||||
import re
|
||||
import time
|
||||
import cvsdb
|
||||
import socket
|
||||
import calendar
|
||||
|
||||
# ViewVC libs
|
||||
import compat
|
||||
import popen
|
||||
import vclib.ccvs
|
||||
|
||||
def _path_join(path_parts):
|
||||
return '/'.join(path_parts)
|
||||
|
||||
class BaseCVSRepository(vclib.Repository):
|
||||
def __init__(self, name, rootpath, authorizer, utilities, charset_guesser = None):
|
||||
def __init__(self, name, rootpath, authorizer, utilities):
|
||||
if not os.path.isdir(rootpath):
|
||||
raise vclib.ReposNotFound(name)
|
||||
|
||||
raise vclib.ReposNotFound(name)
|
||||
|
||||
self.name = name
|
||||
self.rootpath = rootpath
|
||||
self.auth = authorizer
|
||||
self.utilities = utilities
|
||||
self.guesser = charset_guesser
|
||||
|
||||
# See if this repository is even viewable, authz-wise.
|
||||
if not vclib.check_root_access(self):
|
||||
@@ -62,7 +56,7 @@ class BaseCVSRepository(vclib.Repository):
|
||||
|
||||
def authorizer(self):
|
||||
return self.auth
|
||||
|
||||
|
||||
def itemtype(self, path_parts, rev):
|
||||
basepath = self._getpath(path_parts)
|
||||
kind = None
|
||||
@@ -83,11 +77,11 @@ class BaseCVSRepository(vclib.Repository):
|
||||
def itemprops(self, path_parts, rev):
|
||||
self.itemtype(path_parts, rev) # does auth-check
|
||||
return {} # CVS doesn't support properties
|
||||
|
||||
|
||||
def listdir(self, path_parts, rev, options):
|
||||
if self.itemtype(path_parts, rev) != vclib.DIR: # does auth-check
|
||||
raise vclib.Error("Path '%s' is not a directory."
|
||||
% (_path_join(path_parts)))
|
||||
% (string.join(path_parts, "/")))
|
||||
|
||||
# Only RCS files (*,v) and subdirs are returned.
|
||||
data = [ ]
|
||||
@@ -124,7 +118,7 @@ class BaseCVSRepository(vclib.Repository):
|
||||
data.append(CVSDirEntry(name, kind, errors, 1))
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def _getpath(self, path_parts):
|
||||
return apply(os.path.join, (self.rootpath,) + tuple(path_parts))
|
||||
|
||||
@@ -144,21 +138,17 @@ class BaseCVSRepository(vclib.Repository):
|
||||
if root:
|
||||
ret = ret_file
|
||||
else:
|
||||
ret = _path_join(ret_parts)
|
||||
ret = string.join(ret_parts, "/")
|
||||
if v:
|
||||
ret = ret + ",v"
|
||||
return ret
|
||||
|
||||
def isexecutable(self, path_parts, rev):
|
||||
if self.itemtype(path_parts, rev) != vclib.FILE: # does auth-check
|
||||
raise vclib.Error("Path '%s' is not a file." % (_path_join(path_parts)))
|
||||
raise vclib.Error("Path '%s' is not a file."
|
||||
% (string.join(path_parts, "/")))
|
||||
rcsfile = self.rcsfile(path_parts, 1)
|
||||
return os.access(rcsfile, os.X_OK)
|
||||
|
||||
def filesize(self, path_parts, rev):
|
||||
if self.itemtype(path_parts, rev) != vclib.FILE: # does auth-check
|
||||
raise vclib.Error("Path '%s' is not a file." % (_path_join(path_parts)))
|
||||
return -1
|
||||
|
||||
|
||||
class BinCVSRepository(BaseCVSRepository):
|
||||
@@ -169,7 +159,7 @@ class BinCVSRepository(BaseCVSRepository):
|
||||
filename, default_branch, tags, lockinfo, msg, eof = _parse_log_header(fp)
|
||||
revs = []
|
||||
while not eof:
|
||||
revision, eof = _parse_log_entry(fp, self.guesser)
|
||||
revision, eof = _parse_log_entry(fp)
|
||||
if revision:
|
||||
revs.append(revision)
|
||||
revs = _file_log(revs, tags, lockinfo, default_branch, rev)
|
||||
@@ -186,7 +176,8 @@ class BinCVSRepository(BaseCVSRepository):
|
||||
boolean. true to use the original keyword substitution values.
|
||||
"""
|
||||
if self.itemtype(path_parts, rev) != vclib.FILE: # does auth-check
|
||||
raise vclib.Error("Path '%s' is not a file." % (_path_join(path_parts)))
|
||||
raise vclib.Error("Path '%s' is not a file."
|
||||
% (string.join(path_parts, "/")))
|
||||
if not rev or rev == 'HEAD' or rev == 'MAIN':
|
||||
rev_flag = '-p'
|
||||
else:
|
||||
@@ -212,14 +203,14 @@ class BinCVSRepository(BaseCVSRepository):
|
||||
used_rlog = 1
|
||||
if not tip_rev:
|
||||
raise vclib.Error("Unable to find valid revision")
|
||||
fp = self.rcs_popen('co', ('-p' + tip_rev.string, full_name), 'rb')
|
||||
fp = self.rcs_popen('co', ('-p' + tip_rev.string, full_name), 'rb')
|
||||
filename, revision = _parse_co_header(fp)
|
||||
|
||||
|
||||
if filename is None:
|
||||
# CVSNT's co exits without any output if a dead revision is requested.
|
||||
# Bug at http://www.cvsnt.org/cgi-bin/bugzilla/show_bug.cgi?id=190
|
||||
# As a workaround, we invoke rlog to find the first non-dead revision
|
||||
# that precedes it and check out that revision instead. Of course,
|
||||
# that precedes it and check out that revision instead. Of course,
|
||||
# if we've already invoked rlog above, we just reuse its output.
|
||||
if not used_rlog:
|
||||
tip_rev = self._get_tip_revision(full_name + ',v', rev)
|
||||
@@ -228,7 +219,7 @@ class BinCVSRepository(BaseCVSRepository):
|
||||
raise vclib.Error(
|
||||
'Could not find non-dead revision preceding "%s"' % rev)
|
||||
fp = self.rcs_popen('co', ('-p' + tip_rev.undead.string,
|
||||
full_name), 'rb')
|
||||
full_name), 'rb')
|
||||
filename, revision = _parse_co_header(fp)
|
||||
|
||||
if filename is None:
|
||||
@@ -260,14 +251,14 @@ class BinCVSRepository(BaseCVSRepository):
|
||||
"""
|
||||
if self.itemtype(path_parts, rev) != vclib.DIR: # does auth-check
|
||||
raise vclib.Error("Path '%s' is not a directory."
|
||||
% (_path_join(path_parts)))
|
||||
% (string.join(path_parts, "/")))
|
||||
|
||||
subdirs = options.get('cvs_subdirs', 0)
|
||||
entries_to_fetch = []
|
||||
for entry in entries:
|
||||
if vclib.check_path_access(self, path_parts + [entry.name], None, rev):
|
||||
entries_to_fetch.append(entry)
|
||||
alltags = _get_logs(self, path_parts, entries_to_fetch, rev, subdirs, self.guesser)
|
||||
alltags = _get_logs(self, path_parts, entries_to_fetch, rev, subdirs)
|
||||
branches = options['cvs_branches'] = []
|
||||
tags = options['cvs_tags'] = []
|
||||
for name, rev in alltags.items():
|
||||
@@ -297,7 +288,8 @@ class BinCVSRepository(BaseCVSRepository):
|
||||
"""
|
||||
|
||||
if self.itemtype(path_parts, rev) != vclib.FILE: # does auth-check
|
||||
raise vclib.Error("Path '%s' is not a file." % (_path_join(path_parts)))
|
||||
raise vclib.Error("Path '%s' is not a file."
|
||||
% (string.join(path_parts, "/")))
|
||||
|
||||
# Invoke rlog
|
||||
rcsfile = self.rcsfile(path_parts, 1)
|
||||
@@ -312,7 +304,7 @@ class BinCVSRepository(BaseCVSRepository):
|
||||
# Retrieve revision objects
|
||||
revs = []
|
||||
while not eof:
|
||||
revision, eof = _parse_log_entry(fp, self.guesser)
|
||||
revision, eof = _parse_log_entry(fp)
|
||||
if revision:
|
||||
revs.append(revision)
|
||||
|
||||
@@ -331,17 +323,7 @@ class BinCVSRepository(BaseCVSRepository):
|
||||
return filtered_revs
|
||||
|
||||
def rcs_popen(self, rcs_cmd, rcs_args, mode, capture_err=1):
|
||||
a = []
|
||||
if self.utilities.rcsfile_socket:
|
||||
a = self.utilities.rcsfile_socket.split(':')
|
||||
if len(a) == 2:
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
s.connect((a[0], int(a[1])))
|
||||
s = s.makefile()
|
||||
s.write('\''+rcs_cmd+'\' \''+'\' \''.join(rcs_args)+"\'\x0d\x0a")
|
||||
s.flush()
|
||||
return s
|
||||
elif self.utilities.cvsnt:
|
||||
if self.utilities.cvsnt:
|
||||
cmd = self.utilities.cvsnt
|
||||
args = ['rcsfile', rcs_cmd]
|
||||
args.extend(list(rcs_args))
|
||||
@@ -352,15 +334,16 @@ class BinCVSRepository(BaseCVSRepository):
|
||||
|
||||
def annotate(self, path_parts, rev=None, include_text=False):
|
||||
if self.itemtype(path_parts, rev) != vclib.FILE: # does auth-check
|
||||
raise vclib.Error("Path '%s' is not a file." % (_path_join(path_parts)))
|
||||
raise vclib.Error("Path '%s' is not a file."
|
||||
% (string.join(path_parts, "/")))
|
||||
|
||||
from vclib.ccvs import blame
|
||||
source = blame.BlameSource(self.rcsfile(path_parts, 1), rev, self.guesser, include_text)
|
||||
source = blame.BlameSource(self.rcsfile(path_parts, 1), rev, include_text)
|
||||
return source, source.revision
|
||||
|
||||
def revinfo(self, rev):
|
||||
raise vclib.UnsupportedFeature
|
||||
|
||||
|
||||
def rawdiff(self, path_parts1, rev1, path_parts2, rev2, type, options={}):
|
||||
"""see vclib.Repository.rawdiff docstring
|
||||
|
||||
@@ -368,16 +351,12 @@ class BinCVSRepository(BaseCVSRepository):
|
||||
|
||||
ignore_keyword_subst - boolean, ignore keyword substitution
|
||||
"""
|
||||
if not path_parts1:
|
||||
path_parts1 = path_parts2
|
||||
rev1 = '1.0'
|
||||
if not path_parts2:
|
||||
path_parts2 = path_parts1
|
||||
rev2 = '1.0'
|
||||
if self.itemtype(path_parts1, rev1) != vclib.FILE: # does auth-check
|
||||
raise vclib.Error("Path '%s' is not a file." % (_path_join(path_parts1)))
|
||||
raise vclib.Error("Path '%s' is not a file."
|
||||
% (string.join(path_parts1, "/")))
|
||||
if self.itemtype(path_parts2, rev2) != vclib.FILE: # does auth-check
|
||||
raise vclib.Error("Path '%s' is not a file." % (_path_join(path_parts2)))
|
||||
raise vclib.Error("Path '%s' is not a file."
|
||||
% (string.join(path_parts2, "/")))
|
||||
|
||||
args = vclib._diff_args(type, options)
|
||||
if options.get('ignore_keyword_subst', 0):
|
||||
@@ -386,7 +365,8 @@ class BinCVSRepository(BaseCVSRepository):
|
||||
rcsfile = self.rcsfile(path_parts1, 1)
|
||||
if path_parts1 != path_parts2:
|
||||
raise NotImplementedError, "cannot diff across paths in cvs"
|
||||
args.extend(['-N', '-r' + rev1, '-r' + rev2, rcsfile])
|
||||
args.extend(['-r' + rev1, '-r' + rev2, rcsfile])
|
||||
|
||||
fp = self.rcs_popen('rcsdiff', args, 'rt')
|
||||
|
||||
# Eat up the non-GNU-diff-y headers.
|
||||
@@ -395,6 +375,7 @@ class BinCVSRepository(BaseCVSRepository):
|
||||
if not line or line[0:5] == 'diff ':
|
||||
break
|
||||
return fp
|
||||
|
||||
|
||||
class CVSDirEntry(vclib.DirEntry):
|
||||
def __init__(self, name, kind, errors, in_attic, absent=0):
|
||||
@@ -456,9 +437,9 @@ def _match_revs_tags(revlist, taglist):
|
||||
example: if revision is 1.2.3.4, parent is 1.2
|
||||
|
||||
"undead"
|
||||
If the revision is dead, then this is a reference to the first
|
||||
If the revision is dead, then this is a reference to the first
|
||||
previous revision which isn't dead, otherwise it's a reference
|
||||
to itself. If all the previous revisions are dead it's None.
|
||||
to itself. If all the previous revisions are dead it's None.
|
||||
|
||||
"branch_number"
|
||||
tuple representing branch number or empty tuple if on trunk
|
||||
@@ -583,7 +564,7 @@ def _remove_tag(tag):
|
||||
|
||||
def _revision_tuple(revision_string):
|
||||
"""convert a revision number into a tuple of integers"""
|
||||
t = tuple(map(int, revision_string.split('.')))
|
||||
t = tuple(map(int, string.split(revision_string, '.')))
|
||||
if len(t) % 2 == 0:
|
||||
return t
|
||||
raise ValueError
|
||||
@@ -591,7 +572,7 @@ def _revision_tuple(revision_string):
|
||||
def _tag_tuple(revision_string):
|
||||
"""convert a revision number or branch number into a tuple of integers"""
|
||||
if revision_string:
|
||||
t = map(int, revision_string.split('.'))
|
||||
t = map(int, string.split(revision_string, '.'))
|
||||
l = len(t)
|
||||
if l == 1:
|
||||
return ()
|
||||
@@ -670,7 +651,7 @@ def _parse_co_header(fp):
|
||||
pass
|
||||
else:
|
||||
break
|
||||
|
||||
|
||||
raise COMalformedOutput, "Unable to find revision in co output stream"
|
||||
|
||||
# if your rlog doesn't use 77 '=' characters, then this must change
|
||||
@@ -691,7 +672,7 @@ _EOF_ERROR = 'error message found' # rlog issued an error
|
||||
# ^rlog\: (.*)(?:\:\d+)?\: (.*)$
|
||||
#
|
||||
# But for some reason the windows version of rlog omits the "rlog: " prefix
|
||||
# for the first error message when the standard error stream has been
|
||||
# for the first error message when the standard error stream has been
|
||||
# redirected to a file or pipe. (the prefix is present in subsequent errors
|
||||
# and when rlog is run from the console). So the expression below is more
|
||||
# complicated
|
||||
@@ -720,7 +701,7 @@ def _parse_log_header(fp):
|
||||
Returns: filename, default branch, tag dictionary, lock dictionary,
|
||||
rlog error message, and eof flag
|
||||
"""
|
||||
|
||||
|
||||
filename = head = branch = msg = ""
|
||||
taginfo = { } # tag name => number
|
||||
lockinfo = { } # revision => locker
|
||||
@@ -736,7 +717,7 @@ def _parse_log_header(fp):
|
||||
|
||||
if state == 1:
|
||||
if line[0] == '\t':
|
||||
[ tag, rev ] = map(lambda x: x.strip(), line.split(':'))
|
||||
[ tag, rev ] = map(string.strip, string.split(line, ':'))
|
||||
taginfo[tag] = rev
|
||||
else:
|
||||
# oops. this line isn't tag info. stop parsing tags.
|
||||
@@ -744,12 +725,12 @@ def _parse_log_header(fp):
|
||||
|
||||
if state == 2:
|
||||
if line[0] == '\t':
|
||||
[ locker, rev ] = map(lambda x: x.strip(), line.split(':'))
|
||||
[ locker, rev ] = map(string.strip, string.split(line, ':'))
|
||||
lockinfo[rev] = locker
|
||||
else:
|
||||
# oops. this line isn't lock info. stop parsing tags.
|
||||
state = 0
|
||||
|
||||
|
||||
if state == 0:
|
||||
if line[:9] == 'RCS file:':
|
||||
filename = line[10:-1]
|
||||
@@ -800,7 +781,7 @@ _re_log_info = re.compile(r'^date:\s+([^;]+);'
|
||||
r'(\s+commitid:\s+([a-zA-Z0-9]+))?\n$')
|
||||
### _re_rev should be updated to extract the "locked" flag
|
||||
_re_rev = re.compile(r'^revision\s+([0-9.]+).*')
|
||||
def _parse_log_entry(fp, guesser):
|
||||
def _parse_log_entry(fp):
|
||||
"""Parse a single log entry.
|
||||
|
||||
On entry, fp should point to the first line of the entry (the "revision"
|
||||
@@ -853,7 +834,7 @@ def _parse_log_entry(fp, guesser):
|
||||
return None, eof
|
||||
|
||||
# parse out a time tuple for the local time
|
||||
tm = vclib.ccvs.cvs_strptime(match.group(1))
|
||||
tm = compat.cvs_strptime(match.group(1))
|
||||
|
||||
# rlog seems to assume that two-digit years are 1900-based (so, "04"
|
||||
# comes out as "1904", not "2004").
|
||||
@@ -864,10 +845,7 @@ def _parse_log_entry(fp, guesser):
|
||||
tm[0] = tm[0] + 100
|
||||
if tm[0] < EPOCH:
|
||||
raise ValueError, 'invalid year'
|
||||
date = calendar.timegm(tm)
|
||||
|
||||
if guesser:
|
||||
log = guesser.utf8(log)
|
||||
date = compat.timegm(tm)
|
||||
|
||||
return Revision(rev, date,
|
||||
# author, state, lines changed
|
||||
@@ -920,7 +898,7 @@ def _file_log(revs, taginfo, lockinfo, cur_branch, filter):
|
||||
except ValueError:
|
||||
view_tag = None
|
||||
else:
|
||||
tags.append(view_tag)
|
||||
tags.append(view_tag)
|
||||
|
||||
# Match up tags and revisions
|
||||
_match_revs_tags(revs, tags)
|
||||
@@ -928,13 +906,13 @@ def _file_log(revs, taginfo, lockinfo, cur_branch, filter):
|
||||
# Match up lockinfo and revision
|
||||
for rev in revs:
|
||||
rev.lockinfo = lockinfo.get(rev.string)
|
||||
|
||||
|
||||
# Add artificial ViewVC tag HEAD, which acts like a non-branch tag pointing
|
||||
# at the latest revision on the MAIN branch. The HEAD revision doesn't have
|
||||
# anything to do with the "head" revision number specified in the RCS file
|
||||
# and in rlog output. HEAD refers to the revision that the CVS and RCS co
|
||||
# commands will check out by default, whereas the "head" field just refers
|
||||
# to the highest revision on the trunk.
|
||||
# to the highest revision on the trunk.
|
||||
taginfo['HEAD'] = _add_tag('HEAD', taginfo['MAIN'].co_rev)
|
||||
|
||||
# Determine what revisions to return
|
||||
@@ -972,10 +950,10 @@ def _file_log(revs, taginfo, lockinfo, cur_branch, filter):
|
||||
_remove_tag(view_tag)
|
||||
else:
|
||||
filtered_revs = revs
|
||||
|
||||
|
||||
return filtered_revs
|
||||
|
||||
def _get_logs(repos, dir_path_parts, entries, view_tag, get_dirs, guesser):
|
||||
def _get_logs(repos, dir_path_parts, entries, view_tag, get_dirs):
|
||||
alltags = { # all the tags seen in the files of this dir
|
||||
'MAIN' : '',
|
||||
'HEAD' : '1.1'
|
||||
@@ -1022,7 +1000,7 @@ def _get_logs(repos, dir_path_parts, entries, view_tag, get_dirs, guesser):
|
||||
= _parse_log_header(rlog)
|
||||
|
||||
if eof == _EOF_LOG:
|
||||
# the rlog output ended early. this can happen on errors that rlog
|
||||
# the rlog output ended early. this can happen on errors that rlog
|
||||
# thinks are so serious that it stops parsing the current file and
|
||||
# refuses to parse any of the files that come after it. one of the
|
||||
# errors that triggers this obnoxious behavior looks like:
|
||||
@@ -1070,8 +1048,8 @@ def _get_logs(repos, dir_path_parts, entries, view_tag, get_dirs, guesser):
|
||||
eof = _EOF_FILE
|
||||
|
||||
# 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
|
||||
# merge the set of keys and keep values that allow us to make the
|
||||
# the values point to branches or revisions. this the fastest way to
|
||||
# merge the set of keys and keep values that allow us to make the
|
||||
# distinction between branch tags and normal tags
|
||||
alltags.update(taginfo)
|
||||
|
||||
@@ -1080,7 +1058,7 @@ def _get_logs(repos, dir_path_parts, entries, view_tag, get_dirs, guesser):
|
||||
while not eof:
|
||||
|
||||
# fetch one of the log entries
|
||||
entry, eof = _parse_log_entry(rlog, guesser)
|
||||
entry, eof = _parse_log_entry(rlog)
|
||||
|
||||
if not entry:
|
||||
# parsing error
|
||||
@@ -1116,7 +1094,7 @@ def _get_logs(repos, dir_path_parts, entries, view_tag, get_dirs, guesser):
|
||||
file.dead = 0
|
||||
#file.errors.append("No revisions exist on %s" % (view_tag or "MAIN"))
|
||||
file.absent = 1
|
||||
|
||||
|
||||
# done with this file now, skip the rest of this file's revisions
|
||||
if not eof:
|
||||
_skip_file(rlog)
|
||||
@@ -1229,7 +1207,7 @@ def _newest_file(dirpath):
|
||||
newest_time = 0
|
||||
|
||||
### FIXME: This sucker is leaking unauthorized paths! ###
|
||||
|
||||
|
||||
for subfile in os.listdir(dirpath):
|
||||
### filter CVS locks? stale NFS handles?
|
||||
if subfile[-2:] != ',v':
|
||||
|
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 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
|
||||
@@ -26,12 +26,12 @@
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
import string
|
||||
import re
|
||||
import time
|
||||
import math
|
||||
import rcsparse
|
||||
import vclib
|
||||
import cvsdb
|
||||
|
||||
class CVSParser(rcsparse.Sink):
|
||||
# Precompiled regular expressions
|
||||
@@ -100,7 +100,7 @@ class CVSParser(rcsparse.Sink):
|
||||
|
||||
# Split deltatext specified by rev to each line.
|
||||
def deltatext_split(self, rev):
|
||||
lines = self.revision_deltatext[rev].split('\n')
|
||||
lines = string.split(self.revision_deltatext[rev], '\n')
|
||||
if lines[-1] == '':
|
||||
del lines[-1]
|
||||
return lines
|
||||
@@ -139,16 +139,16 @@ class CVSParser(rcsparse.Sink):
|
||||
adjust = adjust + 1
|
||||
elif dmatch:
|
||||
# "d" - Delete command
|
||||
start_line = int(dmatch.group(1))
|
||||
count = int(dmatch.group(2))
|
||||
start_line = string.atoi(dmatch.group(1))
|
||||
count = string.atoi(dmatch.group(2))
|
||||
begin = start_line + adjust - 1
|
||||
del text[begin:begin + count]
|
||||
adjust = adjust - count
|
||||
lines_removed_now = lines_removed_now + count
|
||||
elif amatch:
|
||||
# "a" - Add command
|
||||
start_line = int(amatch.group(1))
|
||||
count = int(amatch.group(2))
|
||||
start_line = string.atoi(amatch.group(1))
|
||||
count = string.atoi(amatch.group(2))
|
||||
add_lines_remaining = count
|
||||
lines_added_now = lines_added_now + count
|
||||
else:
|
||||
@@ -311,13 +311,13 @@ class CVSParser(rcsparse.Sink):
|
||||
skip = skip - 1
|
||||
elif dmatch:
|
||||
# "d" - Delete command
|
||||
start_line = int(dmatch.group(1))
|
||||
count = int(dmatch.group(2))
|
||||
start_line = string.atoi(dmatch.group(1))
|
||||
count = string.atoi(dmatch.group(2))
|
||||
line_count = line_count - count
|
||||
elif amatch:
|
||||
# "a" - Add command
|
||||
start_line = int(amatch.group(1))
|
||||
count = int(amatch.group(2))
|
||||
start_line = string.atoi(amatch.group(1))
|
||||
count = string.atoi(amatch.group(2))
|
||||
skip = count
|
||||
line_count = line_count + count
|
||||
else:
|
||||
@@ -359,8 +359,8 @@ class CVSParser(rcsparse.Sink):
|
||||
dmatch = self.d_command.match(command)
|
||||
amatch = self.a_command.match(command)
|
||||
if dmatch:
|
||||
start_line = int(dmatch.group(1))
|
||||
count = int(dmatch.group(2))
|
||||
start_line = string.atoi(dmatch.group(1))
|
||||
count = string.atoi(dmatch.group(2))
|
||||
temp = []
|
||||
while count > 0:
|
||||
temp.append(revision)
|
||||
@@ -368,8 +368,8 @@ class CVSParser(rcsparse.Sink):
|
||||
self.revision_map = (self.revision_map[:start_line - 1] +
|
||||
temp + self.revision_map[start_line - 1:])
|
||||
elif amatch:
|
||||
start_line = int(amatch.group(1))
|
||||
count = int(amatch.group(2))
|
||||
start_line = string.atoi(amatch.group(1))
|
||||
count = string.atoi(amatch.group(2))
|
||||
del self.revision_map[start_line:start_line + count]
|
||||
skip = count
|
||||
else:
|
||||
@@ -388,15 +388,15 @@ class CVSParser(rcsparse.Sink):
|
||||
dmatch = self.d_command.match(command)
|
||||
amatch = self.a_command.match(command)
|
||||
if dmatch:
|
||||
start_line = int(dmatch.group(1))
|
||||
count = int(dmatch.group(2))
|
||||
start_line = string.atoi(dmatch.group(1))
|
||||
count = string.atoi(dmatch.group(2))
|
||||
adj_begin = start_line + adjust - 1
|
||||
adj_end = start_line + adjust - 1 + count
|
||||
del self.revision_map[adj_begin:adj_end]
|
||||
adjust = adjust - count
|
||||
elif amatch:
|
||||
start_line = int(amatch.group(1))
|
||||
count = int(amatch.group(2))
|
||||
start_line = string.atoi(amatch.group(1))
|
||||
count = string.atoi(amatch.group(2))
|
||||
skip = count
|
||||
temp = []
|
||||
while count > 0:
|
||||
@@ -414,7 +414,7 @@ class CVSParser(rcsparse.Sink):
|
||||
|
||||
|
||||
class BlameSource:
|
||||
def __init__(self, rcs_file, opt_rev=None, charset_guesser=None, include_text=False):
|
||||
def __init__(self, rcs_file, opt_rev=None, include_text=False):
|
||||
# Parse the CVS file
|
||||
parser = CVSParser()
|
||||
revision = parser.parse_cvs_file(rcs_file, opt_rev)
|
||||
@@ -428,7 +428,6 @@ class BlameSource:
|
||||
self.lines = lines
|
||||
self.num_lines = count
|
||||
self.parser = parser
|
||||
self.guesser = charset_guesser
|
||||
self.include_text = include_text
|
||||
|
||||
# keep track of where we are during an iteration
|
||||
@@ -448,14 +447,9 @@ class BlameSource:
|
||||
prev_rev = self.parser.prev_revision.get(rev)
|
||||
line_number = idx + 1
|
||||
author = self.parser.revision_author[rev]
|
||||
|
||||
thisline = self.lines[idx]
|
||||
if not self.include_text:
|
||||
thisline = None
|
||||
elif self.guesser:
|
||||
thisline = self.guesser.utf8(self.lines[idx])
|
||||
else:
|
||||
thisline = self.lines[idx]
|
||||
|
||||
### TODO: Put a real date in here.
|
||||
item = vclib.Annotation(thisline, line_number, rev, prev_rev, author, None)
|
||||
self.last = item
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 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
|
||||
@@ -11,6 +11,7 @@
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
import os
|
||||
import string
|
||||
import re
|
||||
import cStringIO
|
||||
import tempfile
|
||||
@@ -18,12 +19,10 @@ import tempfile
|
||||
import vclib
|
||||
import rcsparse
|
||||
import blame
|
||||
import cvsdb
|
||||
|
||||
### The functionality shared with bincvs should probably be moved to a
|
||||
### separate module
|
||||
from bincvs import BaseCVSRepository, Revision, Tag, _file_log, _log_path, _logsort_date_cmp, _logsort_rev_cmp, _path_join
|
||||
|
||||
from bincvs import BaseCVSRepository, Revision, Tag, _file_log, _log_path, _logsort_date_cmp, _logsort_rev_cmp
|
||||
|
||||
class CCVSRepository(BaseCVSRepository):
|
||||
def dirlogs(self, path_parts, rev, entries, options):
|
||||
@@ -45,7 +44,7 @@ class CCVSRepository(BaseCVSRepository):
|
||||
"""
|
||||
if self.itemtype(path_parts, rev) != vclib.DIR: # does auth-check
|
||||
raise vclib.Error("Path '%s' is not a directory."
|
||||
% (part2path(path_parts)))
|
||||
% (string.join(path_parts, "/")))
|
||||
entries_to_fetch = []
|
||||
for entry in entries:
|
||||
if vclib.check_path_access(self, path_parts + [entry.name], None, rev):
|
||||
@@ -67,8 +66,6 @@ class CCVSRepository(BaseCVSRepository):
|
||||
entry.path = path
|
||||
try:
|
||||
rcsparse.parse(open(path, 'rb'), InfoSink(entry, rev, alltags))
|
||||
if self.guesser:
|
||||
entry.log = self.guesser.utf8(entry.log)
|
||||
except IOError, e:
|
||||
entry.errors.append("rcsparse error: %s" % e)
|
||||
except RuntimeError, e:
|
||||
@@ -97,7 +94,8 @@ class CCVSRepository(BaseCVSRepository):
|
||||
dictionary of Tag objects for all tags encountered
|
||||
"""
|
||||
if self.itemtype(path_parts, rev) != vclib.FILE: # does auth-check
|
||||
raise vclib.Error("Path '%s' is not a file." % (_path_join(path_parts)))
|
||||
raise vclib.Error("Path '%s' is not a file."
|
||||
% (string.join(path_parts, "/")))
|
||||
|
||||
path = self.rcsfile(path_parts, 1)
|
||||
sink = TreeSink()
|
||||
@@ -121,30 +119,23 @@ class CCVSRepository(BaseCVSRepository):
|
||||
return filtered_revs
|
||||
|
||||
def rawdiff(self, path_parts1, rev1, path_parts2, rev2, type, options={}):
|
||||
if path_parts1 and self.itemtype(path_parts1, rev1) != vclib.FILE: # does auth-check
|
||||
raise vclib.Error("Path '%s' is not a file." % (_path_join(path_parts1)))
|
||||
if path_parts2 and self.itemtype(path_parts2, rev2) != vclib.FILE: # does auth-check
|
||||
raise vclib.Error("Path '%s' is not a file." % (_path_join(path_parts2)))
|
||||
if not path_parts1 and not path_parts2:
|
||||
raise vclib.Error("Nothing to diff.")
|
||||
if self.itemtype(path_parts1, rev1) != vclib.FILE: # does auth-check
|
||||
raise vclib.Error("Path '%s' is not a file."
|
||||
% (string.join(path_parts1, "/")))
|
||||
if self.itemtype(path_parts2, rev2) != vclib.FILE: # does auth-check
|
||||
raise vclib.Error("Path '%s' is not a file."
|
||||
% (string.join(path_parts2, "/")))
|
||||
|
||||
if path_parts1:
|
||||
temp1 = tempfile.mktemp()
|
||||
open(temp1, 'wb').write(self.openfile(path_parts1, rev1, {})[0].getvalue())
|
||||
r1 = self.itemlog(path_parts1, rev1, vclib.SORTBY_DEFAULT, 0, 0, {})[-1]
|
||||
info1 = (self.rcsfile(path_parts1, root=1, v=0), r1.date, r1.string)
|
||||
else:
|
||||
temp1 = '/dev/null'
|
||||
info1 = ('/dev/null', '', '')
|
||||
temp1 = tempfile.mktemp()
|
||||
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())
|
||||
|
||||
if path_parts2:
|
||||
temp2 = tempfile.mktemp()
|
||||
open(temp2, 'wb').write(self.openfile(path_parts2, rev2, {})[0].getvalue())
|
||||
r2 = self.itemlog(path_parts2, rev2, vclib.SORTBY_DEFAULT, 0, 0, {})[-1]
|
||||
info2 = (self.rcsfile(path_parts2, root=1, v=0), r2.date, r2.string)
|
||||
else:
|
||||
temp2 = '/dev/null'
|
||||
info2 = ('/dev/null', '', '')
|
||||
r1 = self.itemlog(path_parts1, rev1, vclib.SORTBY_DEFAULT, 0, 0, {})[-1]
|
||||
r2 = self.itemlog(path_parts2, rev2, vclib.SORTBY_DEFAULT, 0, 0, {})[-1]
|
||||
|
||||
info1 = (self.rcsfile(path_parts1, root=1, v=0), r1.date, r1.string)
|
||||
info2 = (self.rcsfile(path_parts2, root=1, v=0), r2.date, r2.string)
|
||||
|
||||
diff_args = vclib._diff_args(type, options)
|
||||
|
||||
@@ -153,8 +144,9 @@ class CCVSRepository(BaseCVSRepository):
|
||||
|
||||
def annotate(self, path_parts, rev=None, include_text=False):
|
||||
if self.itemtype(path_parts, rev) != vclib.FILE: # does auth-check
|
||||
raise vclib.Error("Path '%s' is not a file." % (_path_join(path_parts)))
|
||||
source = blame.BlameSource(self.rcsfile(path_parts, 1), rev, self.guesser, include_text)
|
||||
raise vclib.Error("Path '%s' is not a file."
|
||||
% (string.join(path_parts, "/")))
|
||||
source = blame.BlameSource(self.rcsfile(path_parts, 1), rev, include_text)
|
||||
return source, source.revision
|
||||
|
||||
def revinfo(self, rev):
|
||||
@@ -162,12 +154,13 @@ class CCVSRepository(BaseCVSRepository):
|
||||
|
||||
def openfile(self, path_parts, rev, options):
|
||||
if self.itemtype(path_parts, rev) != vclib.FILE: # does auth-check
|
||||
raise vclib.Error("Path '%s' is not a file." % (_path_join(path_parts)))
|
||||
raise vclib.Error("Path '%s' is not a file."
|
||||
% (string.join(path_parts, "/")))
|
||||
path = self.rcsfile(path_parts, 1)
|
||||
sink = COSink(rev)
|
||||
rcsparse.parse(open(path, 'rb'), sink)
|
||||
revision = sink.last and sink.last.string
|
||||
return cStringIO.StringIO('\n'.join(sink.sstext.text)), revision
|
||||
return cStringIO.StringIO(string.join(sink.sstext.text, "\n")), revision
|
||||
|
||||
class MatchingSink(rcsparse.Sink):
|
||||
"""Superclass for sinks that search for revisions based on tag or number"""
|
||||
@@ -207,7 +200,6 @@ class InfoSink(MatchingSink):
|
||||
self.matching_rev = None
|
||||
self.perfect_match = 0
|
||||
self.lockinfo = { }
|
||||
self.saw_revision = False
|
||||
|
||||
def define_tag(self, name, revision):
|
||||
MatchingSink.define_tag(self, name, revision)
|
||||
@@ -221,17 +213,10 @@ class InfoSink(MatchingSink):
|
||||
self.entry.absent = 1
|
||||
raise rcsparse.RCSStopParser
|
||||
|
||||
def parse_completed(self):
|
||||
if not self.saw_revision:
|
||||
#self.entry.errors.append("No revisions exist on %s" % (view_tag or "MAIN"))
|
||||
self.entry.absent = 1
|
||||
|
||||
def set_locker(self, rev, locker):
|
||||
self.lockinfo[rev] = locker
|
||||
|
||||
def define_revision(self, revision, date, author, state, branches, next):
|
||||
self.saw_revision = True
|
||||
|
||||
if self.perfect_match:
|
||||
return
|
||||
|
||||
@@ -239,18 +224,14 @@ class InfoSink(MatchingSink):
|
||||
rev = Revision(revision, date, author, state == "dead")
|
||||
rev.lockinfo = self.lockinfo.get(revision)
|
||||
|
||||
# perfect match if revision number matches tag number or if
|
||||
# revision is on trunk and tag points to trunk. imperfect match
|
||||
# if tag refers to a branch and either a) this revision is the
|
||||
# highest revision so far found on that branch, or b) this
|
||||
# revision is the branchpoint.
|
||||
# perfect match if revision number matches tag number or if revision is on
|
||||
# trunk and tag points to trunk. imperfect match if tag refers to a branch
|
||||
# and this revision is the highest revision so far found on that branch
|
||||
perfect = ((rev.number == tag.number) or
|
||||
(not tag.number and len(rev.number) == 2))
|
||||
if perfect or (tag.is_branch and \
|
||||
((tag.number == rev.number[:-1] and
|
||||
(not self.matching_rev or
|
||||
rev.number > self.matching_rev.number)) or
|
||||
(rev.number == tag.number[:-1]))):
|
||||
if perfect or (tag.is_branch and tag.number == rev.number[:-1] and
|
||||
(not self.matching_rev or
|
||||
rev.number > self.matching_rev.number)):
|
||||
self.matching_rev = rev
|
||||
self.perfect_match = perfect
|
||||
|
||||
@@ -306,18 +287,18 @@ class TreeSink(rcsparse.Sink):
|
||||
deled = 0
|
||||
if self.head != revision:
|
||||
changed = 1
|
||||
lines = text.split('\n')
|
||||
lines = string.split(text, '\n')
|
||||
idx = 0
|
||||
while idx < len(lines):
|
||||
command = lines[idx]
|
||||
dmatch = self.d_command.match(command)
|
||||
idx = idx + 1
|
||||
if dmatch:
|
||||
deled = deled + int(dmatch.group(2))
|
||||
deled = deled + string.atoi(dmatch.group(2))
|
||||
else:
|
||||
amatch = self.a_command.match(command)
|
||||
if amatch:
|
||||
count = int(amatch.group(2))
|
||||
count = string.atoi(amatch.group(2))
|
||||
added = added + count
|
||||
idx = idx + count
|
||||
elif command:
|
||||
@@ -333,12 +314,12 @@ class StreamText:
|
||||
a_command = re.compile('^a(\d+)\\s(\\d+)')
|
||||
|
||||
def __init__(self, text):
|
||||
self.text = text.split('\n')
|
||||
self.text = string.split(text, "\n")
|
||||
|
||||
def command(self, cmd):
|
||||
adjust = 0
|
||||
add_lines_remaining = 0
|
||||
diffs = cmd.split('\n')
|
||||
diffs = string.split(cmd, "\n")
|
||||
if diffs[-1] == "":
|
||||
del diffs[-1]
|
||||
if len(diffs) == 0:
|
||||
@@ -356,22 +337,22 @@ class StreamText:
|
||||
amatch = self.a_command.match(command)
|
||||
if dmatch:
|
||||
# "d" - Delete command
|
||||
start_line = int(dmatch.group(1))
|
||||
count = int(dmatch.group(2))
|
||||
start_line = string.atoi(dmatch.group(1))
|
||||
count = string.atoi(dmatch.group(2))
|
||||
begin = start_line + adjust - 1
|
||||
del self.text[begin:begin + count]
|
||||
adjust = adjust - count
|
||||
elif amatch:
|
||||
# "a" - Add command
|
||||
start_line = int(amatch.group(1))
|
||||
count = int(amatch.group(2))
|
||||
start_line = string.atoi(amatch.group(1))
|
||||
count = string.atoi(amatch.group(2))
|
||||
add_lines_remaining = count
|
||||
else:
|
||||
raise RuntimeError, 'Error parsing diff commands'
|
||||
|
||||
def secondnextdot(s, start):
|
||||
# find the position the second dot after the start index.
|
||||
return s.find('.', s.find('.', start) + 1)
|
||||
return string.find(s, '.', string.find(s, '.', start) + 1)
|
||||
|
||||
|
||||
class COSink(MatchingSink):
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 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
|
||||
@@ -10,17 +10,8 @@
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
"""This package provides parsing tools for RCS files.
|
||||
"""This package provides parsing tools for RCS files."""
|
||||
|
||||
To use this package, first create a subclass of Sink. This should
|
||||
declare all the callback methods you care about. Create an instance
|
||||
of your class, and open() the RCS file you want to read. Then call
|
||||
parse() to parse the file.
|
||||
"""
|
||||
|
||||
# Make the "Sink" class and the various exception classes visible in this
|
||||
# scope. That way, applications never need to import any of the
|
||||
# sub-packages.
|
||||
from common import *
|
||||
|
||||
try:
|
||||
@@ -32,13 +23,4 @@ except ImportError:
|
||||
from default import Parser
|
||||
|
||||
def parse(file, sink):
|
||||
"""Parse an RCS file.
|
||||
|
||||
Parameters: FILE is the file object to parse. (I.e. an object of the
|
||||
built-in Python type "file", usually created using Python's built-in
|
||||
"open()" function). It should be opened in binary mode.
|
||||
SINK is an instance of (some subclass of) Sink. It's methods will be
|
||||
called as the file is parsed; see the definition of Sink for the
|
||||
details.
|
||||
"""
|
||||
return Parser().parse(file, sink)
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 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,199 +16,52 @@ import calendar
|
||||
import string
|
||||
|
||||
class Sink:
|
||||
"""Interface to be implemented by clients. The RCS parser calls this as
|
||||
it parses the RCS file.
|
||||
|
||||
All these methods have stub implementations that do nothing, so you only
|
||||
have to override the callbacks that you care about.
|
||||
"""
|
||||
def set_head_revision(self, revision):
|
||||
"""Reports the head revision for this RCS file.
|
||||
|
||||
This is the value of the 'head' header in the admin section of the RCS
|
||||
file. This function can only be called before admin_completed().
|
||||
|
||||
Parameter: REVISION is a string containing a revision number. This is
|
||||
an actual revision number, not a branch number.
|
||||
"""
|
||||
pass
|
||||
|
||||
def set_principal_branch(self, branch_name):
|
||||
"""Reports the principal branch for this RCS file. This is only called
|
||||
if the principal branch is not trunk.
|
||||
|
||||
This is the value of the 'branch' header in the admin section of the RCS
|
||||
file. This function can only be called before admin_completed().
|
||||
|
||||
Parameter: BRANCH_NAME is a string containing a branch number. If this
|
||||
function is called, the parameter is typically "1.1.1", indicating the
|
||||
vendor branch.
|
||||
"""
|
||||
pass
|
||||
|
||||
def set_access(self, accessors):
|
||||
"""Reports the access control list for this RCS file. This function is
|
||||
only called if the ACL is set. If this function is not called then
|
||||
there is no ACL and all users are allowed access.
|
||||
|
||||
This is the value of the 'access' header in the admin section of the RCS
|
||||
file. This function can only be called before admin_completed().
|
||||
|
||||
Parameter: ACCESSORS is a list of strings. Each string is a username.
|
||||
The user is allowed access if and only if their username is in the list,
|
||||
OR the user owns the RCS file on disk, OR the user is root.
|
||||
|
||||
Note that CVS typically doesn't use this field.
|
||||
"""
|
||||
pass
|
||||
|
||||
def define_tag(self, name, revision):
|
||||
"""Reports a tag or branch definition. This function will be called
|
||||
once for each tag or branch.
|
||||
|
||||
This is taken from the 'symbols' header in the admin section of the RCS
|
||||
file. This function can only be called before admin_completed().
|
||||
|
||||
Parameters: NAME is a string containing the tag or branch name.
|
||||
REVISION is a string containing a revision number. This may be
|
||||
an actual revision number (for a tag) or a branch number.
|
||||
|
||||
The revision number consists of a number of decimal components separated
|
||||
by dots. There are three common forms. If there are an odd number of
|
||||
components, it's a branch. Otherwise, if the next-to-last component is
|
||||
zero, it's a branch (and the next-to-last component is an artifact of
|
||||
CVS and should not be shown to the user). Otherwise, it's a tag.
|
||||
|
||||
This function is called in the order that the tags appear in the RCS
|
||||
file header. For CVS, this appears to be in reverse chronological
|
||||
order of tag/branch creation.
|
||||
"""
|
||||
pass
|
||||
|
||||
def set_locker(self, revision, locker):
|
||||
"""Reports a lock on this RCS file. This function will be called once
|
||||
for each lock.
|
||||
|
||||
This is taken from the 'locks' header in the admin section of the RCS
|
||||
file. This function can only be called before admin_completed().
|
||||
|
||||
Parameters: REVISION is a string containing a revision number. This is
|
||||
an actual revision number, not a branch number.
|
||||
LOCKER is a string containing a username.
|
||||
"""
|
||||
pass
|
||||
|
||||
def set_locking(self, mode):
|
||||
"""Signals strict locking mode. This function will be called if and
|
||||
only if the RCS file is in strict locking mode.
|
||||
"""Used to signal locking mode.
|
||||
|
||||
This is taken from the 'strict' header in the admin section of the RCS
|
||||
file. This function can only be called before admin_completed().
|
||||
Called with mode argument 'strict' if strict locking
|
||||
Not called when no locking used."""
|
||||
|
||||
Parameters: MODE is always the string 'strict'.
|
||||
"""
|
||||
pass
|
||||
|
||||
def set_comment(self, comment):
|
||||
"""Reports the comment for this RCS file.
|
||||
|
||||
This is the value of the 'comment' header in the admin section of the
|
||||
RCS file. This function can only be called before admin_completed().
|
||||
|
||||
Parameter: COMMENT is a string containing the comment. This may be
|
||||
multi-line.
|
||||
|
||||
This field does not seem to be used by CVS.
|
||||
"""
|
||||
pass
|
||||
|
||||
def set_expansion(self, mode):
|
||||
"""Reports the keyword expansion mode for this RCS file.
|
||||
|
||||
This is the value of the 'expand' header in the admin section of the
|
||||
RCS file. This function can only be called before admin_completed().
|
||||
|
||||
Parameter: MODE is a string containing the keyword expansion mode.
|
||||
Possible values include 'o' and 'b', amongst others.
|
||||
"""
|
||||
pass
|
||||
|
||||
def admin_completed(self):
|
||||
"""Reports that the initial RCS header has been parsed. This function is
|
||||
called exactly once.
|
||||
"""
|
||||
pass
|
||||
|
||||
def define_revision(self, revision, timestamp, author, state,
|
||||
branches, next):
|
||||
"""Reports metadata about a single revision.
|
||||
|
||||
This function is called for each revision. It is called later than
|
||||
admin_completed() and earlier than tree_completed().
|
||||
|
||||
Parameter: REVISION is a revision number, as a string. This is an
|
||||
actual revision number, not a branch number.
|
||||
TIMESTAMP is the date and time that the revision was created, as an
|
||||
integer number of seconds since the epoch. (I.e. "UNIX time" format).
|
||||
AUTHOR is the author name, as a string.
|
||||
STATE is the state of the revision, as a string. Common values are
|
||||
"Exp" and "dead".
|
||||
BRANCHES is a list of strings, with each string being an actual
|
||||
revision number (not a branch number). For each branch which is based
|
||||
on this revision and has commits, the revision number of the first
|
||||
branch commit is listed here.
|
||||
NEXT is either None or a string representing an actual revision number
|
||||
(not a branch number).
|
||||
|
||||
When on trunk, NEXT points to what humans might consider to be the
|
||||
'previous' revision number. For example, 1.3's NEXT is 1.2.
|
||||
However, on a branch, NEXT really does point to what humans would
|
||||
consider to be the 'next' revision number. For example, 1.1.2.1's
|
||||
NEXT would be 1.1.2.2.
|
||||
In other words, NEXT always means "where to find the next deltatext
|
||||
that you need this revision to retrieve".
|
||||
"""
|
||||
pass
|
||||
|
||||
def tree_completed(self):
|
||||
"""Reports that the RCS revision tree has been parsed. This function is
|
||||
called exactly once. This function will be called later than
|
||||
admin_completed().
|
||||
"""
|
||||
pass
|
||||
|
||||
def set_description(self, description):
|
||||
"""Reports the description from the RCS file. This is set using the
|
||||
"-m" flag to "cvs add". However, many CVS users don't use that option,
|
||||
so this is often empty.
|
||||
|
||||
This function is called once, after tree_completed().
|
||||
|
||||
Parameter: DESCRIPTION is a string containing the description. This may
|
||||
be multi-line.
|
||||
"""
|
||||
pass
|
||||
|
||||
def set_revision_info(self, revision, log, text):
|
||||
"""Reports the log message and contents of a CVS revision.
|
||||
|
||||
This function is called for each revision. It is called later than
|
||||
set_description().
|
||||
|
||||
Parameters: REVISION is a string containing the actual revision number.
|
||||
LOG is a string containing the log message. This may be multi-line.
|
||||
TEXT is the contents of the file in this revision, either as full-text or
|
||||
as a diff. This is usually multi-line, and often quite large and/or
|
||||
binary.
|
||||
"""
|
||||
pass
|
||||
|
||||
def parse_completed(self):
|
||||
"""Reports that parsing an RCS file is complete.
|
||||
|
||||
This function is called once. After it is called, no more calls will be
|
||||
made via this interface.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
@@ -356,7 +209,7 @@ class _Parser:
|
||||
date = self.ts.get()
|
||||
self.ts.match(';')
|
||||
|
||||
# Convert date into standard UNIX time format (seconds since epoch)
|
||||
# Convert date into timestamp
|
||||
date_fields = string.split(date, '.')
|
||||
# According to rcsfile(5): the year "contains just the last two
|
||||
# digits of the year for years from 1900 through 1999, and all the
|
||||
@@ -366,11 +219,8 @@ class _Parser:
|
||||
date_fields = map(string.atoi, date_fields)
|
||||
EPOCH = 1970
|
||||
if date_fields[0] < EPOCH:
|
||||
raise ValueError, 'invalid year for revision %s' % (revision,)
|
||||
try:
|
||||
timestamp = calendar.timegm(tuple(date_fields) + (0, 0, 0,))
|
||||
except ValueError, e:
|
||||
raise ValueError, 'invalid date for revision %s: %s' % (revision, e,)
|
||||
raise ValueError, 'invalid year'
|
||||
timestamp = calendar.timegm(tuple(date_fields) + (0, 0, 0,))
|
||||
|
||||
# Parse author
|
||||
### NOTE: authors containing whitespace are violations of the
|
||||
@@ -406,7 +256,6 @@ class _Parser:
|
||||
# group 15;
|
||||
# permissions 644;
|
||||
# hardlinks @configure.in@;
|
||||
# commitid mLiHw3bulRjnTDGr;
|
||||
# this is "newphrase" in RCSFILE(5). we just want to skip over these.
|
||||
while 1:
|
||||
token = self.ts.get()
|
||||
@@ -450,15 +299,6 @@ class _Parser:
|
||||
self.sink.set_revision_info(revision, log, text)
|
||||
|
||||
def parse(self, file, sink):
|
||||
"""Parse an RCS file.
|
||||
|
||||
Parameters: FILE is the file object to parse. (I.e. an object of the
|
||||
built-in Python type "file", usually created using Python's built-in
|
||||
"open()" function).
|
||||
SINK is an instance of (some subclass of) Sink. It's methods will be
|
||||
called as the file is parsed; see the definition of Sink for the
|
||||
details.
|
||||
"""
|
||||
self.ts = self.stream_class(file)
|
||||
self.sink = sink
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 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
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 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
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 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
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 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
|
||||
@@ -77,20 +77,6 @@ def expand_root_parent(parent_path):
|
||||
return roots
|
||||
|
||||
|
||||
def find_root_in_parent(parent_path, rootname):
|
||||
"""Search PARENT_PATH for a root named ROOTNAME, returning the
|
||||
canonicalized ROOTPATH of the root if found; return None if no such
|
||||
root is found."""
|
||||
|
||||
if not re.search(_re_url, parent_path):
|
||||
assert os.path.isabs(parent_path)
|
||||
rootpath = os.path.join(parent_path, rootname)
|
||||
format_path = os.path.join(rootpath, "format")
|
||||
if os.path.exists(format_path):
|
||||
return canonicalize_rootpath(rootpath)
|
||||
return None
|
||||
|
||||
|
||||
def SubversionRepository(name, rootpath, authorizer, utilities, config_dir):
|
||||
rootpath = canonicalize_rootpath(rootpath)
|
||||
if re.search(_re_url, rootpath):
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 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,6 +15,7 @@
|
||||
import vclib
|
||||
import sys
|
||||
import os
|
||||
import string
|
||||
import re
|
||||
import tempfile
|
||||
import time
|
||||
@@ -53,14 +54,12 @@ def get_directory_props(ra_session, path, rev):
|
||||
props = ra.svn_ra_get_dir(ra_session, path, rev)
|
||||
return props
|
||||
|
||||
def client_log(url, start_rev, end_rev, log_limit, include_changes,
|
||||
cross_copies, cb_func, ctx):
|
||||
include_changes = include_changes and 1 or 0
|
||||
cross_copies = cross_copies and 1 or 0
|
||||
def client_log(url, start_rev, end_rev, log_limit, cross_copies,
|
||||
cb_func, ctx):
|
||||
try:
|
||||
client.svn_client_log4([url], start_rev, start_rev, end_rev,
|
||||
log_limit, include_changes, not cross_copies,
|
||||
0, None, cb_func, ctx)
|
||||
log_limit, 1, not cross_copies, 0, None,
|
||||
cb_func, ctx)
|
||||
except AttributeError:
|
||||
# Wrap old svn_log_message_receiver_t interface with a
|
||||
# svn_log_entry_t one.
|
||||
@@ -76,48 +75,15 @@ def client_log(url, start_rev, end_rev, log_limit, include_changes,
|
||||
}
|
||||
cb_func(log_entry, pool)
|
||||
client.svn_client_log2([url], start_rev, end_rev, log_limit,
|
||||
include_changes, not cross_copies, cb_convert, ctx)
|
||||
|
||||
|
||||
def setup_client_ctx(config_dir):
|
||||
# Ensure that the configuration directory exists.
|
||||
core.svn_config_ensure(config_dir)
|
||||
|
||||
# Fetch the configuration (and 'config' bit thereof).
|
||||
cfg = core.svn_config_get_config(config_dir)
|
||||
config = cfg.get(core.SVN_CONFIG_CATEGORY_CONFIG)
|
||||
|
||||
# Here's the compat-sensitive part: try to use
|
||||
# svn_cmdline_create_auth_baton(), and fall back to making our own
|
||||
# if that fails.
|
||||
try:
|
||||
auth_baton = core.svn_cmdline_create_auth_baton(1, None, None, config_dir,
|
||||
1, 1, config, None)
|
||||
except AttributeError:
|
||||
auth_baton = core.svn_auth_open([
|
||||
client.svn_client_get_simple_provider(),
|
||||
client.svn_client_get_username_provider(),
|
||||
client.svn_client_get_ssl_server_trust_file_provider(),
|
||||
client.svn_client_get_ssl_client_cert_file_provider(),
|
||||
client.svn_client_get_ssl_client_cert_pw_file_provider(),
|
||||
])
|
||||
if config_dir is not None:
|
||||
core.svn_auth_set_parameter(auth_baton,
|
||||
core.SVN_AUTH_PARAM_CONFIG_DIR,
|
||||
config_dir)
|
||||
|
||||
# Create, setup, and return the client context baton.
|
||||
ctx = client.svn_client_create_context()
|
||||
ctx.config = cfg
|
||||
ctx.auth_baton = auth_baton
|
||||
return ctx
|
||||
1, not cross_copies, cb_convert, ctx)
|
||||
|
||||
### END COMPATABILITY CODE ###
|
||||
|
||||
|
||||
class LogCollector:
|
||||
### TODO: Make this thing authz-aware
|
||||
|
||||
def __init__(self, path, show_all_logs, lockinfo, access_check_func):
|
||||
def __init__(self, path, show_all_logs, lockinfo):
|
||||
# This class uses leading slashes for paths internally
|
||||
if not path:
|
||||
self.path = '/'
|
||||
@@ -126,12 +92,8 @@ class LogCollector:
|
||||
self.logs = []
|
||||
self.show_all_logs = show_all_logs
|
||||
self.lockinfo = lockinfo
|
||||
self.access_check_func = access_check_func
|
||||
self.done = False
|
||||
|
||||
def add_log(self, log_entry, pool):
|
||||
if self.done:
|
||||
return
|
||||
paths = log_entry.changed_paths
|
||||
revision = log_entry.revision
|
||||
msg, author, date, revprops = _split_revprops(log_entry.revprops)
|
||||
@@ -149,23 +111,19 @@ class LogCollector:
|
||||
if changed_path != self.path:
|
||||
# If a parent of our path was copied, our "next previous"
|
||||
# (huh?) path will exist elsewhere (under the copy source).
|
||||
if (self.path.rfind(changed_path) == 0) and \
|
||||
if (string.rfind(self.path, changed_path) == 0) and \
|
||||
self.path[len(changed_path)] == '/':
|
||||
change = paths[changed_path]
|
||||
if change.copyfrom_path:
|
||||
this_path = change.copyfrom_path + self.path[len(changed_path):]
|
||||
if self.show_all_logs or this_path:
|
||||
if self.access_check_func is None \
|
||||
or self.access_check_func(self.path[1:], revision):
|
||||
entry = Revision(revision, date, author, msg, None, self.lockinfo,
|
||||
self.path[1:], None, None)
|
||||
self.logs.append(entry)
|
||||
else:
|
||||
self.done = True
|
||||
entry = Revision(revision, date, author, msg, None, self.lockinfo,
|
||||
self.path[1:], None, None)
|
||||
self.logs.append(entry)
|
||||
if this_path:
|
||||
self.path = this_path
|
||||
|
||||
def cat_to_tempfile(svnrepos, path, rev):
|
||||
def temp_checkout(svnrepos, path, rev):
|
||||
"""Check out file revision to temporary file"""
|
||||
temp = tempfile.mktemp()
|
||||
stream = core.svn_stream_from_aprfile(temp)
|
||||
@@ -226,8 +184,21 @@ class RemoteSubversionRepository(vclib.Repository):
|
||||
|
||||
def open(self):
|
||||
# Setup the client context baton, complete with non-prompting authstuffs.
|
||||
self.ctx = setup_client_ctx(self.config_dir)
|
||||
|
||||
# TODO: svn_cmdline_setup_auth_baton() is mo' better (when available)
|
||||
core.svn_config_ensure(self.config_dir)
|
||||
self.ctx = client.svn_client_create_context()
|
||||
self.ctx.auth_baton = core.svn_auth_open([
|
||||
client.svn_client_get_simple_provider(),
|
||||
client.svn_client_get_username_provider(),
|
||||
client.svn_client_get_ssl_server_trust_file_provider(),
|
||||
client.svn_client_get_ssl_client_cert_file_provider(),
|
||||
client.svn_client_get_ssl_client_cert_pw_file_provider(),
|
||||
])
|
||||
self.ctx.config = core.svn_config_get_config(self.config_dir)
|
||||
if self.config_dir is not None:
|
||||
core.svn_auth_set_parameter(self.ctx.auth_baton,
|
||||
core.SVN_AUTH_PARAM_CONFIG_DIR,
|
||||
self.config_dir)
|
||||
ra_callbacks = ra.svn_ra_callbacks_t()
|
||||
ra_callbacks.auth_baton = self.ctx.auth_baton
|
||||
self.ra_session = ra.svn_ra_open(self.rootpath, ra_callbacks, None,
|
||||
@@ -279,17 +250,19 @@ class RemoteSubversionRepository(vclib.Repository):
|
||||
raise vclib.Error("Path '%s' is not a file." % path)
|
||||
rev = self._getrev(rev)
|
||||
url = self._geturl(path)
|
||||
tmp_file = tempfile.mktemp()
|
||||
stream = core.svn_stream_from_aprfile(tmp_file)
|
||||
### rev here should be the last history revision of the URL
|
||||
fp = SelfCleanFP(cat_to_tempfile(self, path, rev))
|
||||
lh_rev, c_rev = self._get_last_history_rev(path_parts, rev)
|
||||
return fp, lh_rev
|
||||
client.svn_client_cat(core.Stream(stream), url, _rev2optrev(rev), self.ctx)
|
||||
core.svn_stream_close(stream)
|
||||
return SelfCleanFP(tmp_file), self._get_last_history_rev(path_parts, rev)
|
||||
|
||||
def listdir(self, path_parts, rev, options):
|
||||
path = self._getpath(path_parts)
|
||||
if self.itemtype(path_parts, rev) != vclib.DIR: # does auth-check
|
||||
raise vclib.Error("Path '%s' is not a directory." % path)
|
||||
rev = self._getrev(rev)
|
||||
entries = []
|
||||
entries = [ ]
|
||||
dirents, locks = self._get_dirents(path, rev)
|
||||
for name in dirents.keys():
|
||||
entry = dirents[name]
|
||||
@@ -297,9 +270,8 @@ class RemoteSubversionRepository(vclib.Repository):
|
||||
kind = vclib.DIR
|
||||
elif entry.kind == core.svn_node_file:
|
||||
kind = vclib.FILE
|
||||
else:
|
||||
kind = None
|
||||
entries.append(vclib.DirEntry(name, kind))
|
||||
if vclib.check_path_access(self, path_parts + [name], kind, rev):
|
||||
entries.append(vclib.DirEntry(name, kind))
|
||||
return entries
|
||||
|
||||
def dirlogs(self, path_parts, rev, entries, options):
|
||||
@@ -310,13 +282,11 @@ class RemoteSubversionRepository(vclib.Repository):
|
||||
dirents, locks = self._get_dirents(path, rev)
|
||||
for entry in entries:
|
||||
entry_path_parts = path_parts + [entry.name]
|
||||
dirent = dirents.get(entry.name, None)
|
||||
# dirents is authz-sanitized, so ensure the entry is found therein.
|
||||
if dirent is None:
|
||||
if not vclib.check_path_access(self, entry_path_parts, entry.kind, rev):
|
||||
continue
|
||||
# Get authz-sanitized revision metadata.
|
||||
dirent = dirents[entry.name]
|
||||
entry.date, entry.author, entry.log, revprops, changes = \
|
||||
self._revinfo(dirent.created_rev)
|
||||
self.revinfo(dirent.created_rev)
|
||||
entry.rev = str(dirent.created_rev)
|
||||
entry.size = dirent.size
|
||||
entry.lockinfo = None
|
||||
@@ -330,51 +300,28 @@ class RemoteSubversionRepository(vclib.Repository):
|
||||
rev = self._getrev(rev)
|
||||
url = self._geturl(path)
|
||||
|
||||
# If this is a file, fetch the lock status and size (as of REV)
|
||||
# for this item.
|
||||
lockinfo = size_in_rev = None
|
||||
if path_type == vclib.FILE:
|
||||
basename = path_parts[-1]
|
||||
list_url = self._geturl(self._getpath(path_parts[:-1]))
|
||||
dirents, locks = list_directory(list_url, _rev2optrev(rev),
|
||||
_rev2optrev(rev), 0, self.ctx)
|
||||
if locks.has_key(basename):
|
||||
lockinfo = locks[basename].owner
|
||||
if dirents.has_key(basename):
|
||||
size_in_rev = dirents[basename].size
|
||||
|
||||
# Special handling for the 'svn_latest_log' scenario.
|
||||
### FIXME: Don't like this hack. We should just introduce
|
||||
### something more direct in the vclib API.
|
||||
if options.get('svn_latest_log', 0):
|
||||
dir_lh_rev, dir_c_rev = self._get_last_history_rev(path_parts, rev)
|
||||
date, author, log, revprops, changes = self._revinfo(dir_lh_rev)
|
||||
return [vclib.Revision(dir_lh_rev, str(dir_lh_rev), date, author,
|
||||
None, log, size_in_rev, lockinfo)]
|
||||
# Use ls3 to fetch the lock status for this item.
|
||||
lockinfo = None
|
||||
basename = path_parts and path_parts[-1] or ""
|
||||
dirents, locks = list_directory(url, _rev2optrev(rev),
|
||||
_rev2optrev(rev), 0, self.ctx)
|
||||
if locks.has_key(basename):
|
||||
lockinfo = locks[basename].owner
|
||||
|
||||
def _access_checker(check_path, check_rev):
|
||||
return vclib.check_path_access(self, _path_parts(check_path),
|
||||
path_type, check_rev)
|
||||
|
||||
# It's okay if we're told to not show all logs on a file -- all
|
||||
# the revisions should match correctly anyway.
|
||||
lc = LogCollector(path, options.get('svn_show_all_dir_logs', 0),
|
||||
lockinfo, _access_checker)
|
||||
lc = LogCollector(path, options.get('svn_show_all_dir_logs', 0), lockinfo)
|
||||
|
||||
cross_copies = options.get('svn_cross_copies', 0)
|
||||
log_limit = 0
|
||||
if limit:
|
||||
log_limit = first + limit
|
||||
client_log(url, _rev2optrev(rev), _rev2optrev(1), log_limit, 1,
|
||||
client_log(url, _rev2optrev(rev), _rev2optrev(1), log_limit,
|
||||
cross_copies, lc.add_log, self.ctx)
|
||||
revs = lc.logs
|
||||
revs.sort()
|
||||
prev = None
|
||||
for rev in revs:
|
||||
# Swap out revision info with stuff from the cache (which is
|
||||
# authz-sanitized).
|
||||
rev.date, rev.author, rev.log, revprops, changes \
|
||||
= self._revinfo(rev.number)
|
||||
rev.prev = prev
|
||||
prev = rev
|
||||
revs.reverse()
|
||||
@@ -401,18 +348,6 @@ class RemoteSubversionRepository(vclib.Repository):
|
||||
rev = self._getrev(rev)
|
||||
url = self._geturl(path)
|
||||
|
||||
# Examine logs for the file to determine the oldest revision we are
|
||||
# permitted to see.
|
||||
log_options = {
|
||||
'svn_cross_copies' : 1,
|
||||
'svn_show_all_dir_logs' : 1,
|
||||
}
|
||||
revs = self.itemlog(path_parts, rev, vclib.SORTBY_REV, 0, 0, log_options)
|
||||
oldest_rev = revs[-1].number
|
||||
|
||||
# Now calculate the annotation data. Note that we'll not
|
||||
# inherently trust the provided author and date, because authz
|
||||
# rules might necessitate that we strip that information out.
|
||||
blame_data = []
|
||||
|
||||
def _blame_cb(line_no, revision, author, date,
|
||||
@@ -420,86 +355,57 @@ class RemoteSubversionRepository(vclib.Repository):
|
||||
prev_rev = None
|
||||
if revision > 1:
|
||||
prev_rev = revision - 1
|
||||
|
||||
# If we have an invalid revision, clear the date and author
|
||||
# values. Otherwise, if we have authz filtering to do, use the
|
||||
# revinfo cache to do so.
|
||||
if revision < 0:
|
||||
date = author = None
|
||||
elif self.auth:
|
||||
date, author, msg, revprops, changes = self._revinfo(revision)
|
||||
|
||||
# Strip text if the caller doesn't want it.
|
||||
if not include_text:
|
||||
line = None
|
||||
blame_data.append(vclib.Annotation(line, line_no + 1, revision, prev_rev,
|
||||
author, date))
|
||||
author, None))
|
||||
|
||||
client.blame2(url, _rev2optrev(rev), _rev2optrev(oldest_rev),
|
||||
_rev2optrev(rev), _blame_cb, self.ctx)
|
||||
client.svn_client_blame(url, _rev2optrev(1), _rev2optrev(rev),
|
||||
_blame_cb, self.ctx)
|
||||
return blame_data, rev
|
||||
|
||||
def revinfo(self, rev):
|
||||
return self._revinfo(rev, 1)
|
||||
rev = self._getrev(rev)
|
||||
cached_info = self._revinfo_cache.get(rev)
|
||||
if not cached_info:
|
||||
cached_info = self._revinfo_raw(rev)
|
||||
self._revinfo_cache[rev] = cached_info
|
||||
return tuple(cached_info)
|
||||
|
||||
def rawdiff(self, path_parts1, rev1, path_parts2, rev2, type, options={}):
|
||||
|
||||
if path_parts1 is not None:
|
||||
p1 = self._getpath(path_parts1)
|
||||
r1 = self._getrev(rev1)
|
||||
if not vclib.check_path_access(self, path_parts1, vclib.FILE, rev1):
|
||||
raise vclib.ItemNotFound(path_parts1)
|
||||
else:
|
||||
p1 = None
|
||||
|
||||
if path_parts2 is not None:
|
||||
if not p1:
|
||||
raise vclib.ItemNotFound(parh_parts2)
|
||||
p2 = self._getpath(path_parts2)
|
||||
r2 = self._getrev(rev2)
|
||||
if not vclib.check_path_access(self, path_parts2, vclib.FILE, rev2):
|
||||
raise vclib.ItemNotFound(path_parts2)
|
||||
else:
|
||||
p2 = None
|
||||
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)
|
||||
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
|
||||
if p1:
|
||||
temp1 = cat_to_tempfile(self, p1, r1)
|
||||
else:
|
||||
temp1 = '/dev/null'
|
||||
if p2:
|
||||
temp2 = cat_to_tempfile(self, p2, r2)
|
||||
else:
|
||||
temp2 = '/dev/null'
|
||||
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:
|
||||
if e.apr_err == vclib.svn.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)
|
||||
|
||||
def filesize(self, path_parts, rev):
|
||||
path = self._getpath(path_parts)
|
||||
if self.itemtype(path_parts, rev) != vclib.FILE: # does auth-check
|
||||
raise vclib.Error("Path '%s' is not a file." % path)
|
||||
rev = self._getrev(rev)
|
||||
dirents, locks = self._get_dirents(self._getpath(path_parts[:-1]), rev)
|
||||
dirent = dirents.get(path_parts[-1], None)
|
||||
return dirent.size
|
||||
|
||||
def _getpath(self, path_parts):
|
||||
return '/'.join(path_parts)
|
||||
return string.join(path_parts, '/')
|
||||
|
||||
def _getrev(self, rev):
|
||||
if rev is None or rev == 'HEAD':
|
||||
@@ -523,94 +429,38 @@ class RemoteSubversionRepository(vclib.Repository):
|
||||
|
||||
def _get_dirents(self, path, rev):
|
||||
"""Return a 2-type of dirents and locks, possibly reading/writing
|
||||
from a local cache of that information. This functions performs
|
||||
authz checks, stripping out unreadable dirents."""
|
||||
from a local cache of that information."""
|
||||
|
||||
dir_url = self._geturl(path)
|
||||
path_parts = _path_parts(path)
|
||||
if path:
|
||||
key = str(rev) + '/' + path
|
||||
else:
|
||||
key = str(rev)
|
||||
|
||||
# Ensure that the cache gets filled...
|
||||
dirents_locks = self._dirent_cache.get(key)
|
||||
if not dirents_locks:
|
||||
tmp_dirents, locks = list_directory(dir_url, _rev2optrev(rev),
|
||||
_rev2optrev(rev), 0, self.ctx)
|
||||
dirents = {}
|
||||
for name, dirent in tmp_dirents.items():
|
||||
dirent_parts = path_parts + [name]
|
||||
kind = dirent.kind
|
||||
if (kind == core.svn_node_dir or kind == core.svn_node_file) \
|
||||
and vclib.check_path_access(self, dirent_parts,
|
||||
kind == core.svn_node_dir \
|
||||
and vclib.DIR or vclib.FILE, rev):
|
||||
lh_rev, c_rev = self._get_last_history_rev(dirent_parts, rev)
|
||||
dirent.created_rev = lh_rev
|
||||
dirents[name] = dirent
|
||||
dirents, locks = list_directory(dir_url, _rev2optrev(rev),
|
||||
_rev2optrev(rev), 0, self.ctx)
|
||||
dirents_locks = [dirents, locks]
|
||||
self._dirent_cache[key] = dirents_locks
|
||||
|
||||
# ...then return the goodies from the cache.
|
||||
return dirents_locks[0], dirents_locks[1]
|
||||
|
||||
def _get_last_history_rev(self, path_parts, rev):
|
||||
"""Return the a 2-tuple which contains:
|
||||
- the last interesting revision equal to or older than REV in
|
||||
the history of PATH_PARTS.
|
||||
- the created_rev of of PATH_PARTS as of REV."""
|
||||
|
||||
path = self._getpath(path_parts)
|
||||
url = self._geturl(self._getpath(path_parts))
|
||||
optrev = _rev2optrev(rev)
|
||||
|
||||
# Get the last-changed-rev.
|
||||
revisions = []
|
||||
def _info_cb(path, info, pool, retval=revisions):
|
||||
revisions.append(info.last_changed_rev)
|
||||
client.svn_client_info(url, optrev, optrev, _info_cb, 0, self.ctx)
|
||||
last_changed_rev = revisions[0]
|
||||
|
||||
# Now, this object might not have been directly edited since the
|
||||
# last-changed-rev, but it might have been the child of a copy.
|
||||
# To determine this, we'll run a potentially no-op log between
|
||||
# LAST_CHANGED_REV and REV.
|
||||
lc = LogCollector(path, 1, None, None)
|
||||
client_log(url, optrev, _rev2optrev(last_changed_rev), 1, 1, 0,
|
||||
lc.add_log, self.ctx)
|
||||
revs = lc.logs
|
||||
if revs:
|
||||
revs.sort()
|
||||
return revs[0].number, last_changed_rev
|
||||
else:
|
||||
return last_changed_rev, last_changed_rev
|
||||
return revisions[0]
|
||||
|
||||
def _revinfo_fetch(self, rev, include_changed_paths=0):
|
||||
need_changes = include_changed_paths or self.auth
|
||||
def _revinfo_raw(self, rev):
|
||||
# return 5-tuple (date, author, msg, revprops, changes)
|
||||
optrev = _rev2optrev(rev)
|
||||
revs = []
|
||||
|
||||
|
||||
def _log_cb(log_entry, pool, retval=revs):
|
||||
# If Subversion happens to call us more than once, we choose not
|
||||
# to care.
|
||||
if retval:
|
||||
return
|
||||
|
||||
revision = log_entry.revision
|
||||
msg, author, date, revprops = _split_revprops(log_entry.revprops)
|
||||
action_map = { 'D' : vclib.DELETED,
|
||||
'A' : vclib.ADDED,
|
||||
'R' : vclib.REPLACED,
|
||||
'M' : vclib.MODIFIED,
|
||||
}
|
||||
|
||||
# Easy out: if we won't use the changed-path info, just return a
|
||||
# changes-less tuple.
|
||||
if not need_changes:
|
||||
return revs.append([date, author, msg, revprops, None])
|
||||
|
||||
# Subversion 1.5 and earlier didn't offer the 'changed_paths2'
|
||||
# hash, and in Subversion 1.6, it's offered but broken.
|
||||
### Subversion 1.5 and earlier didn't offer the 'changed_paths2'
|
||||
### hash, and in Subversion 1.6, it's offered but broken.
|
||||
try:
|
||||
changed_paths = log_entry.changed_paths2
|
||||
paths = (changed_paths or {}).keys()
|
||||
@@ -618,27 +468,29 @@ class RemoteSubversionRepository(vclib.Repository):
|
||||
changed_paths = log_entry.changed_paths
|
||||
paths = (changed_paths or {}).keys()
|
||||
paths.sort(lambda a, b: _compare_paths(a, b))
|
||||
|
||||
# If we get this far, our caller needs changed-paths, or we need
|
||||
# them for authz-related sanitization.
|
||||
revision = log_entry.revision
|
||||
msg, author, date, revprops = _split_revprops(log_entry.revprops)
|
||||
action_map = { 'D' : vclib.DELETED,
|
||||
'A' : vclib.ADDED,
|
||||
'R' : vclib.REPLACED,
|
||||
'M' : vclib.MODIFIED,
|
||||
}
|
||||
changes = []
|
||||
found_readable = found_unreadable = 0
|
||||
for path in paths:
|
||||
change = changed_paths[path]
|
||||
|
||||
# svn_log_changed_path_t (which we might get instead of the
|
||||
# svn_log_changed_path2_t we'd prefer) doesn't have the
|
||||
# 'node_kind' member.
|
||||
### svn_log_changed_path_t (which we might get instead of the
|
||||
### svn_log_changed_path2_t we'd prefer) doesn't have the
|
||||
### 'node_kind' member.
|
||||
pathtype = None
|
||||
if hasattr(change, 'node_kind'):
|
||||
if change.node_kind == core.svn_node_dir:
|
||||
pathtype = vclib.DIR
|
||||
elif change.node_kind == core.svn_node_file:
|
||||
pathtype = vclib.FILE
|
||||
|
||||
# svn_log_changed_path2_t only has the 'text_modified' and
|
||||
# 'props_modified' bits in Subversion 1.7 and beyond. And
|
||||
# svn_log_changed_path_t is without.
|
||||
### svn_log_changed_path2_t only has the 'text_modified' and
|
||||
### 'props_modified' bits in Subversion 1.7 and beyond. And
|
||||
### svn_log_changed_path_t is without.
|
||||
text_modified = props_modified = 0
|
||||
if hasattr(change, 'text_modified'):
|
||||
if change.text_modified == core.svn_tristate_true:
|
||||
@@ -646,10 +498,9 @@ class RemoteSubversionRepository(vclib.Repository):
|
||||
if hasattr(change, 'props_modified'):
|
||||
if change.props_modified == core.svn_tristate_true:
|
||||
props_modified = 1
|
||||
|
||||
# Wrong, diddily wrong wrong wrong. Can you say,
|
||||
# "Manufacturing data left and right because it hurts to
|
||||
# figure out the right stuff?"
|
||||
### Wrong, diddily wrong wrong wrong. Can you say,
|
||||
### "Manufacturing data left and right because it hurts to
|
||||
### figure out the right stuff?"
|
||||
action = action_map.get(change.action, vclib.MODIFIED)
|
||||
if change.copyfrom_path and change.copyfrom_rev:
|
||||
is_copy = 1
|
||||
@@ -663,16 +514,15 @@ class RemoteSubversionRepository(vclib.Repository):
|
||||
base_path = path
|
||||
base_rev = revision - 1
|
||||
|
||||
# Check authz rules (sadly, we have to lie about the path type)
|
||||
### Check authz rules (we lie about the path type)
|
||||
parts = _path_parts(path)
|
||||
if vclib.check_path_access(self, parts, vclib.FILE, revision):
|
||||
if is_copy and base_path and (base_path != path):
|
||||
parts = _path_parts(base_path)
|
||||
if not vclib.check_path_access(self, parts, vclib.FILE, base_rev):
|
||||
if vclib.check_path_access(self, parts, vclib.FILE, base_rev):
|
||||
is_copy = 0
|
||||
base_path = None
|
||||
base_rev = None
|
||||
found_unreadable = 1
|
||||
changes.append(SVNChangedPath(path, revision, pathtype, base_path,
|
||||
base_rev, action, is_copy,
|
||||
text_modified, props_modified))
|
||||
@@ -680,45 +530,16 @@ class RemoteSubversionRepository(vclib.Repository):
|
||||
else:
|
||||
found_unreadable = 1
|
||||
|
||||
# If our caller doesn't want changed-path stuff, and we have
|
||||
# the info we need to make an authz determination already,
|
||||
# quit this loop and get on with it.
|
||||
if (not include_changed_paths) and found_unreadable and found_readable:
|
||||
break
|
||||
|
||||
# Filter unreadable information.
|
||||
if found_unreadable:
|
||||
msg = None
|
||||
if not found_readable:
|
||||
author = None
|
||||
date = None
|
||||
revs.append([date, author, msg, revprops, changes])
|
||||
|
||||
# Drop unrequested changes.
|
||||
if not include_changed_paths:
|
||||
changes = None
|
||||
|
||||
# Add this revision information to the "return" array.
|
||||
retval.append([date, author, msg, revprops, changes])
|
||||
|
||||
optrev = _rev2optrev(rev)
|
||||
client_log(self.rootpath, optrev, optrev, 1, need_changes, 0,
|
||||
_log_cb, self.ctx)
|
||||
client_log(self.rootpath, optrev, optrev, 1, 0, _log_cb, self.ctx)
|
||||
return tuple(revs[0])
|
||||
|
||||
def _revinfo(self, rev, include_changed_paths=0):
|
||||
"""Internal-use, cache-friendly revision information harvester."""
|
||||
|
||||
# Consult the revinfo cache first. If we don't have cached info,
|
||||
# or our caller wants changed paths and we don't have those for
|
||||
# this revision, go do the real work.
|
||||
rev = self._getrev(rev)
|
||||
cached_info = self._revinfo_cache.get(rev)
|
||||
if not cached_info \
|
||||
or (include_changed_paths and cached_info[4] is None):
|
||||
cached_info = self._revinfo_fetch(rev, include_changed_paths)
|
||||
self._revinfo_cache[rev] = cached_info
|
||||
return cached_info
|
||||
|
||||
##--- custom --##
|
||||
|
||||
def get_youngest_revision(self):
|
||||
@@ -736,16 +557,23 @@ class RemoteSubversionRepository(vclib.Repository):
|
||||
old_path = results[old_rev]
|
||||
except KeyError:
|
||||
raise vclib.ItemNotFound(path)
|
||||
old_path = _cleanup_path(old_path)
|
||||
old_path_parts = _path_parts(old_path)
|
||||
# Check access (lying about path types)
|
||||
if not vclib.check_path_access(self, old_path_parts, vclib.FILE, old_rev):
|
||||
raise vclib.ItemNotFound(path)
|
||||
return old_path
|
||||
|
||||
return _cleanup_path(old_path)
|
||||
|
||||
def created_rev(self, path, rev):
|
||||
lh_rev, c_rev = self._get_last_history_rev(_path_parts(path), rev)
|
||||
return lh_rev
|
||||
# NOTE: We can't use svn_client_propget here because the
|
||||
# interfaces in that layer strip out the properties not meant for
|
||||
# human consumption (such as svn:entry:committed-rev, which we are
|
||||
# using here to get the created revision of PATH@REV).
|
||||
kind = ra.svn_ra_check_path(self.ra_session, path, rev)
|
||||
if kind == core.svn_node_none:
|
||||
raise vclib.ItemNotFound(_path_parts(path))
|
||||
elif kind == core.svn_node_dir:
|
||||
props = get_directory_props(self.ra_session, path, rev)
|
||||
elif kind == core.svn_node_file:
|
||||
fetched_rev, props = ra.svn_ra_get_file(self.ra_session, path, rev, None)
|
||||
return int(props.get(core.SVN_PROP_ENTRY_COMMITTED_REV,
|
||||
SVN_INVALID_REVNUM))
|
||||
|
||||
def last_rev(self, path, peg_revision, limit_revision=None):
|
||||
"""Given PATH, known to exist in PEG_REVISION, find the youngest
|
||||
@@ -780,33 +608,3 @@ class RemoteSubversionRepository(vclib.Repository):
|
||||
else:
|
||||
peg_revision = mid
|
||||
return peg_revision, path
|
||||
|
||||
def get_symlink_target(self, path_parts, rev):
|
||||
"""Return the target of the symbolic link versioned at PATH_PARTS
|
||||
in REV, or None if that object is not a symlink."""
|
||||
|
||||
path = self._getpath(path_parts)
|
||||
path_type = self.itemtype(path_parts, rev) # does auth-check
|
||||
rev = self._getrev(rev)
|
||||
url = self._geturl(path)
|
||||
|
||||
# Symlinks must be files with the svn:special property set on them
|
||||
# and with file contents which read "link SOME_PATH".
|
||||
if path_type != vclib.FILE:
|
||||
return None
|
||||
pairs = client.svn_client_proplist2(url, _rev2optrev(rev),
|
||||
_rev2optrev(rev), 0, self.ctx)
|
||||
props = pairs and pairs[0][1] or {}
|
||||
if not props.has_key(core.SVN_PROP_SPECIAL):
|
||||
return None
|
||||
pathspec = ''
|
||||
### FIXME: We're being a touch sloppy here, first by grabbing the
|
||||
### whole file and then by checking only the first line
|
||||
### of it.
|
||||
fp = SelfCleanFP(cat_to_tempfile(self, path, rev))
|
||||
pathspec = fp.readline()
|
||||
fp.close()
|
||||
if pathspec[:5] != 'link ':
|
||||
return None
|
||||
return pathspec[5:]
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 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,13 +15,15 @@
|
||||
import vclib
|
||||
import os
|
||||
import os.path
|
||||
import string
|
||||
import cStringIO
|
||||
import signal
|
||||
import time
|
||||
import tempfile
|
||||
import popen
|
||||
import re
|
||||
import urllib
|
||||
from svn import fs, repos, core, client, delta, diff
|
||||
from svn import fs, repos, core, client, delta
|
||||
|
||||
|
||||
### Require Subversion 1.3.1 or better.
|
||||
@@ -55,12 +57,12 @@ def _allow_all(root, path, pool):
|
||||
|
||||
|
||||
def _path_parts(path):
|
||||
return filter(None, path.split('/'))
|
||||
return filter(None, string.split(path, '/'))
|
||||
|
||||
|
||||
def _cleanup_path(path):
|
||||
"""Return a cleaned-up Subversion filesystem path"""
|
||||
return '/'.join(_path_parts(path))
|
||||
return string.join(_path_parts(path), '/')
|
||||
|
||||
|
||||
def _fs_path_join(base, relative):
|
||||
@@ -116,7 +118,7 @@ def _rootpath2url(rootpath, path):
|
||||
rootpath = os.path.abspath(rootpath)
|
||||
drive, rootpath = os.path.splitdrive(rootpath)
|
||||
if os.sep != '/':
|
||||
rootpath = rootpath.replace(os.sep, '/')
|
||||
rootpath = string.replace(rootpath, os.sep, '/')
|
||||
rootpath = urllib.quote(rootpath)
|
||||
path = urllib.quote(path)
|
||||
if drive:
|
||||
@@ -197,7 +199,7 @@ class NodeHistory:
|
||||
test_path = path
|
||||
found = 0
|
||||
while 1:
|
||||
off = test_path.rfind('/')
|
||||
off = string.rfind(test_path, '/')
|
||||
if off < 0:
|
||||
break
|
||||
test_path = test_path[0:off]
|
||||
@@ -306,8 +308,8 @@ class BlameSource:
|
||||
try:
|
||||
### TODO: Is this use of FIRST_REV always what we want? Should we
|
||||
### pass 1 here instead and do filtering later?
|
||||
client.blame3(local_url, _rev2optrev(rev), _rev2optrev(first_rev),
|
||||
_rev2optrev(rev), diff.svn_diff_file_options_t(), True, self._blame_cb, ctx)
|
||||
client.blame2(local_url, _rev2optrev(rev), _rev2optrev(first_rev),
|
||||
_rev2optrev(rev), self._blame_cb, ctx)
|
||||
except core.SubversionException, e:
|
||||
_fix_subversion_exception(e)
|
||||
if e.apr_err == core.SVN_ERR_CLIENT_IS_BINARY_FILE:
|
||||
@@ -356,6 +358,7 @@ class LocalSubversionRepository(vclib.Repository):
|
||||
self.rootpath = rootpath
|
||||
self.name = name
|
||||
self.auth = authorizer
|
||||
self.svn_client_path = utilities.svn or 'svn'
|
||||
self.diff_cmd = utilities.diff or 'diff'
|
||||
self.config_dir = config_dir or None
|
||||
|
||||
@@ -364,6 +367,22 @@ class LocalSubversionRepository(vclib.Repository):
|
||||
raise vclib.ReposNotFound(name)
|
||||
|
||||
def open(self):
|
||||
# Register a handler for SIGTERM so we can have a chance to
|
||||
# cleanup. If ViewVC takes too long to start generating CGI
|
||||
# output, Apache will grow impatient and SIGTERM it. While we
|
||||
# don't mind getting told to bail, we want to gracefully close the
|
||||
# repository before we bail.
|
||||
def _sigterm_handler(signum, frame, self=self):
|
||||
sys.exit(-1)
|
||||
try:
|
||||
signal.signal(signal.SIGTERM, _sigterm_handler)
|
||||
except ValueError:
|
||||
# This is probably "ValueError: signal only works in main
|
||||
# thread", which will get thrown by the likes of mod_python
|
||||
# when trying to install a signal handler from a thread that
|
||||
# isn't the main one. We'll just not care.
|
||||
pass
|
||||
|
||||
# Open the repository and init some other variables.
|
||||
self.repos = repos.svn_repos_open(self.rootpath)
|
||||
self.fs_ptr = repos.svn_repos_fs(self.repos)
|
||||
@@ -537,24 +556,15 @@ class LocalSubversionRepository(vclib.Repository):
|
||||
return self._revinfo(rev, 1)
|
||||
|
||||
def rawdiff(self, path_parts1, rev1, path_parts2, rev2, type, options={}):
|
||||
if path_parts1:
|
||||
p1 = self._getpath(path_parts1)
|
||||
r1 = self._getrev(rev1)
|
||||
if not vclib.check_path_access(self, path_parts1, vclib.FILE, rev1):
|
||||
raise vclib.ItemNotFound(path_parts1)
|
||||
else:
|
||||
p1 = None
|
||||
|
||||
if path_parts2:
|
||||
p2 = self._getpath(path_parts2)
|
||||
r2 = self._getrev(rev2)
|
||||
if not vclib.check_path_access(self, path_parts2, vclib.FILE, rev2):
|
||||
raise vclib.ItemNotFound(path_parts2)
|
||||
else:
|
||||
if not p1:
|
||||
raise vclib.ItemNotFound(path_parts2)
|
||||
p2 = None
|
||||
|
||||
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):
|
||||
@@ -562,37 +572,21 @@ class LocalSubversionRepository(vclib.Repository):
|
||||
return date
|
||||
|
||||
try:
|
||||
if p1:
|
||||
temp1 = temp_checkout(self, p1, r1)
|
||||
info1 = p1, _date_from_rev(r1), r1
|
||||
else:
|
||||
temp1 = '/dev/null'
|
||||
info1 = '/dev/null', _date_from_rev(rev1), rev1
|
||||
if p2:
|
||||
temp2 = temp_checkout(self, p2, r2)
|
||||
info2 = p2, _date_from_rev(r2), r2
|
||||
else:
|
||||
temp2 = '/dev/null'
|
||||
info2 = '/dev/null', _date_from_rev(rev2), rev2
|
||||
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
|
||||
if e.apr_err != _SVN_ERR_CEASE_INVOCATION:
|
||||
raise
|
||||
raise
|
||||
|
||||
def isexecutable(self, path_parts, rev):
|
||||
props = self.itemprops(path_parts, rev) # does authz-check
|
||||
return props.has_key(core.SVN_PROP_EXECUTABLE)
|
||||
|
||||
def filesize(self, path_parts, rev):
|
||||
path = self._getpath(path_parts)
|
||||
if self.itemtype(path_parts, rev) != vclib.FILE: # does auth-check
|
||||
raise vclib.Error("Path '%s' is not a file." % path)
|
||||
fsroot = self._getroot(self._getrev(rev))
|
||||
return fs.file_length(fsroot, path)
|
||||
|
||||
##--- helpers ---##
|
||||
|
||||
def _revinfo(self, rev, include_changed_paths=0):
|
||||
@@ -657,7 +651,6 @@ class LocalSubversionRepository(vclib.Repository):
|
||||
is_copy = 0
|
||||
change.base_path = None
|
||||
change.base_rev = None
|
||||
found_unreadable = 1
|
||||
changedpaths[path] = SVNChangedPath(path, rev, pathtype,
|
||||
change.base_path,
|
||||
change.base_rev, action,
|
||||
@@ -669,19 +662,11 @@ class LocalSubversionRepository(vclib.Repository):
|
||||
return found_readable, found_unreadable, changedpaths.values()
|
||||
|
||||
def _get_change_copyinfo(fsroot, path, change):
|
||||
# If we know the copyfrom info, return it...
|
||||
if hasattr(change, 'copyfrom_known') and change.copyfrom_known:
|
||||
copyfrom_path = change.copyfrom_path
|
||||
copyfrom_rev = change.copyfrom_rev
|
||||
# ...otherwise, if this change could be a copy (that is, it
|
||||
# contains an add action), query the copyfrom info ...
|
||||
elif (change.change_kind == fs.path_change_add or
|
||||
change.change_kind == fs.path_change_replace):
|
||||
copyfrom_rev, copyfrom_path = fs.copied_from(fsroot, path)
|
||||
# ...else, there's no copyfrom info.
|
||||
else:
|
||||
copyfrom_rev = core.SVN_INVALID_REVNUM
|
||||
copyfrom_path = None
|
||||
copyfrom_rev, copyfrom_path = fs.copied_from(fsroot, path)
|
||||
return copyfrom_path, copyfrom_rev
|
||||
|
||||
def _simple_auth_check(fsroot):
|
||||
@@ -834,7 +819,7 @@ class LocalSubversionRepository(vclib.Repository):
|
||||
return rev_paths
|
||||
|
||||
def _getpath(self, path_parts):
|
||||
return '/'.join(path_parts)
|
||||
return string.join(path_parts, '/')
|
||||
|
||||
def _getrev(self, rev):
|
||||
if rev is None or rev == 'HEAD':
|
||||
@@ -920,7 +905,7 @@ class LocalSubversionRepository(vclib.Repository):
|
||||
while history:
|
||||
path, peg_revision = fs.history_location(history)
|
||||
if peg_revision <= limit_revision:
|
||||
return peg_revision, _cleanup_path(path)
|
||||
return max(peg_revision, limit_revision), _cleanup_path(path)
|
||||
history = fs.history_prev(history, 1)
|
||||
return peg_revision, _cleanup_path(path)
|
||||
else:
|
||||
@@ -948,32 +933,3 @@ class LocalSubversionRepository(vclib.Repository):
|
||||
return peg_revision, path
|
||||
finally:
|
||||
pass
|
||||
|
||||
def get_symlink_target(self, path_parts, rev):
|
||||
"""Return the target of the symbolic link versioned at PATH_PARTS
|
||||
in REV, or None if that object is not a symlink."""
|
||||
|
||||
path = self._getpath(path_parts)
|
||||
rev = self._getrev(rev)
|
||||
path_type = self.itemtype(path_parts, rev) # does auth-check
|
||||
fsroot = self._getroot(rev)
|
||||
|
||||
# Symlinks must be files with the svn:special property set on them
|
||||
# and with file contents which read "link SOME_PATH".
|
||||
if path_type != vclib.FILE:
|
||||
return None
|
||||
props = fs.node_proplist(fsroot, path)
|
||||
if not props.has_key(core.SVN_PROP_SPECIAL):
|
||||
return None
|
||||
pathspec = ''
|
||||
### FIXME: We're being a touch sloppy here, only checking the first line
|
||||
### of the file.
|
||||
stream = fs.file_contents(fsroot, path)
|
||||
try:
|
||||
pathspec, eof = core.svn_stream_readline(stream, '\n')
|
||||
finally:
|
||||
core.svn_stream_close(stream)
|
||||
if pathspec[:5] != 'link ':
|
||||
return None
|
||||
return pathspec[5:]
|
||||
|
||||
|
1845
lib/viewvc.py
1845
lib/viewvc.py
File diff suppressed because it is too large
Load Diff
@@ -1,75 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import mimetypes
|
||||
import magic
|
||||
|
||||
have_chardet = 0
|
||||
try:
|
||||
import chardet
|
||||
have_chardet = 1
|
||||
except: pass
|
||||
|
||||
class ContentMagic:
|
||||
|
||||
def __init__(self, encodings):
|
||||
self.encodings = encodings.split(':')
|
||||
self.mime_magic = None
|
||||
self.errors = []
|
||||
# Try to load magic
|
||||
self.mime_magic = magic.open(magic.MAGIC_MIME_TYPE)
|
||||
self.mime_magic.load()
|
||||
|
||||
# returns MIME type
|
||||
def guess_mime(self, mime, filename, tempfile):
|
||||
if mime == 'application/octet-stream':
|
||||
mime = ''
|
||||
if not mime and filename:
|
||||
mime = mimetypes.guess_type(filename)[0]
|
||||
if not mime and tempfile and self.mime_magic:
|
||||
if type(tempfile) == type(''):
|
||||
mime = self.mime_magic.file(tempfile)
|
||||
else:
|
||||
c = tempfile.read(4096)
|
||||
mime = self.mime_magic.buffer(c)
|
||||
return mime
|
||||
|
||||
# returns (utf8_content, charset)
|
||||
def guess_charset(self, content):
|
||||
# Try UTF-8
|
||||
charset = 'utf-8'
|
||||
try: content = content.decode('utf-8')
|
||||
except: charset = None
|
||||
if charset is None and have_chardet and len(content) > 64:
|
||||
# Try to guess with chardet
|
||||
try:
|
||||
# Only detect on first 256KB if content is longer
|
||||
if len(content) > 256*1024:
|
||||
charset = chardet.detect(content[0:256*1024])
|
||||
else:
|
||||
charset = chardet.detect(content)
|
||||
if charset and charset['encoding']:
|
||||
charset = charset['encoding']
|
||||
if charset == 'MacCyrillic':
|
||||
# Silly MacCyr, try cp1251
|
||||
try:
|
||||
content = content.decode('windows-1251')
|
||||
charset = 'windows-1251'
|
||||
except: content = content.decode(charset)
|
||||
else:
|
||||
content = content.decode(charset)
|
||||
except: charset = None
|
||||
# Then try to guess primitively
|
||||
if charset is None:
|
||||
for charset in self.encodings:
|
||||
try:
|
||||
content = content.decode(charset)
|
||||
break
|
||||
except: charset = None
|
||||
return (content, charset)
|
||||
|
||||
# guess and encode return value into UTF-8
|
||||
def utf8(self, content):
|
||||
(uni, charset) = self.guess_charset(content)
|
||||
if charset:
|
||||
return uni.encode('utf-8')
|
||||
return content
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 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 @@
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
import os, sys, traceback, thread
|
||||
import os, sys, traceback, string, thread
|
||||
try:
|
||||
import win32api
|
||||
except ImportError, e:
|
||||
@@ -40,9 +40,9 @@ def CommandLine(command, args):
|
||||
"""Convert an executable path and a sequence of arguments into a command
|
||||
line that can be passed to CreateProcess"""
|
||||
|
||||
cmd = "\"" + command.replace("\"", "\"\"") + "\""
|
||||
cmd = "\"" + string.replace(command, "\"", "\"\"") + "\""
|
||||
for arg in args:
|
||||
cmd = cmd + " \"" + arg.replace("\"", "\"\"") + "\""
|
||||
cmd = cmd + " \"" + string.replace(arg, "\"", "\"\"") + "\""
|
||||
return cmd
|
||||
|
||||
def CreateProcess(cmd, hStdInput, hStdOutput, hStdError):
|
||||
@@ -109,13 +109,13 @@ def CreatePipe(readInheritable, writeInheritable):
|
||||
|
||||
def File2FileObject(pipe, mode):
|
||||
"""Make a C stdio file object out of a win32 file handle"""
|
||||
if mode.find('r') >= 0:
|
||||
if string.find(mode, 'r') >= 0:
|
||||
wmode = os.O_RDONLY
|
||||
elif mode.find('w') >= 0:
|
||||
elif string.find(mode, 'w') >= 0:
|
||||
wmode = os.O_WRONLY
|
||||
if mode.find('b') >= 0:
|
||||
if string.find(mode, 'b') >= 0:
|
||||
wmode = wmode | os.O_BINARY
|
||||
if mode.find('t') >= 0:
|
||||
if string.find(mode, 't') >= 0:
|
||||
wmode = wmode | os.O_TEXT
|
||||
return os.fdopen(msvcrt.open_osfhandle(pipe.Detach(),wmode),mode)
|
||||
|
||||
|
@@ -1,42 +1,9 @@
|
||||
RELEASE MANAGEMENT
|
||||
RELEASE MANAGEMENT
|
||||
|
||||
ViewVC rolls releases from release branches associate with each minor
|
||||
version of the software. For example, the 1.1.0 is rolled from the
|
||||
1.1.x branch. The same is true for the 1.1.1, 1.1.2, ... releases.
|
||||
|
||||
|
||||
A. Creating Release Branches
|
||||
============================
|
||||
|
||||
Primary ViewVC development occurs on the trunk, with bugfixes and
|
||||
compatible features being backported to release branches as
|
||||
appropriate. When, however, the need arises to create a new release
|
||||
branch, here's the process (M, N, X, and Y below represent integral
|
||||
major, minor, and patch version numbers, and are not literal):
|
||||
|
||||
1. Create the release branch as a copy of the trunk@HEAD (the
|
||||
lower-case "x" in the branch name is literal):
|
||||
|
||||
svn cp -m "Branch for X.Y release stabilization." . ^/branches/X.Y.x
|
||||
|
||||
2. On the trunk, update the following files to reflect the new
|
||||
version which trunk will be progressing towards:
|
||||
|
||||
CHANGES: Add stub section for new release.
|
||||
INSTALL: Update example configuration.
|
||||
lib/viewvc.py: Update "__version__" value.
|
||||
docs/upgrading-howto.html: Add stub section for new release.
|
||||
docs/template-authoring-guide.html: Update to reflect new release.
|
||||
docs/release-notes/M.N.0.html: Add a new stub file.
|
||||
|
||||
Commit these changes:
|
||||
|
||||
svn ci -m "Trunk is now progressing toward version M.N."
|
||||
|
||||
|
||||
B. Publishing Releases
|
||||
======================
|
||||
|
||||
There is a script, `tools/make-release', which creates a release
|
||||
directory and the various archive files that we distribute. All other
|
||||
steps required to get a ViewVC release out of the door require manual
|
||||
@@ -46,17 +13,16 @@ follows:
|
||||
Checkout a working copy of the release branch for the release you
|
||||
intend to roll, and in that working copy, perform the following steps
|
||||
(X, Y, and Z below represent integral major, minor, and patch version
|
||||
numbers, and are not literal):
|
||||
numbers, and not literal):
|
||||
|
||||
1. Review any open bug reports:
|
||||
|
||||
http://viewvc.tigris.org/servlets/ProjectIssues
|
||||
|
||||
2. Ensure that the file 'docs/upgrading.html' describes all user
|
||||
visible changes for users of previous releases of ViewVC. (Any
|
||||
changes here should be made on the trunk and backported to the
|
||||
branch.) NOTE: This step should not be necessary for patch
|
||||
releases.
|
||||
2. Add a new subsection to the file 'docs/upgrading.html' describing
|
||||
all user visible changes for users of previous releases of ViewVC.
|
||||
Commit any modifications. NOTE: This step should not be necessary
|
||||
for patch releases.
|
||||
|
||||
3. Verify that copyright years are correct in both the license-1.html
|
||||
file and the source code.
|
||||
@@ -147,13 +113,8 @@ numbers, and are not literal):
|
||||
http://viewvc.tigris.org/issues/editmilestones.cgi?component=viewvc&action=add
|
||||
|
||||
18. Send to the announce@ list a message explaining all the cool new
|
||||
features.
|
||||
features, and post similar announcements to other places interested
|
||||
in this sort of stuff, such as Freshmeat (http://www.freshmeat.net).
|
||||
|
||||
http://viewvc.tigris.org/ds/viewForumSummary.do?dsForumId=4253
|
||||
|
||||
19. Post a new release notification at Freecode.
|
||||
|
||||
https://freecode.com/projects/viewvc/releases/new
|
||||
|
||||
20. Merge CHANGES for this release into the CHANGES file for newer
|
||||
19. Merge CHANGES for this release into the CHANGES file for newer
|
||||
release lines and commit.
|
||||
|
@@ -1,107 +0,0 @@
|
||||
-*- text -*-
|
||||
|
||||
This document carries some design thoughts regarding the solution of
|
||||
Issue #439[1] ("allow svn repositories to reside in web-navigable
|
||||
subdirectories")
|
||||
|
||||
[1] http://viewvc.tigris.org/issues/show_bug.cgi?id=439
|
||||
|
||||
|
||||
INTRODUCTION
|
||||
============
|
||||
|
||||
Many folks have expressed the desire that ViewVC expose its configured
|
||||
roots at more or less arbitrary virtual paths below the ViewVC root
|
||||
URL. An example might explain this better.
|
||||
|
||||
Say you have a ViewVC instance configured with roots like so:
|
||||
|
||||
# path vc
|
||||
# ------------ ---
|
||||
root_parents = /var/cvs/old : cvs,
|
||||
/var/svn/dev : svn,
|
||||
/var/svn/qa : svn,
|
||||
|
||||
and say that each of these root parents has a few roots whose names
|
||||
begin with the basenames of the parent directory ("dev-tools" lives in
|
||||
"/var/svn/dev", "old-docs" lives in "/var/cvs/old", etc.)
|
||||
|
||||
Today, ViewVC will display all those roots in the "root listing" view
|
||||
as if they are siblings:
|
||||
|
||||
old-docs/
|
||||
old-src/
|
||||
dev-build/
|
||||
dev-libs/
|
||||
dev-tools/
|
||||
qa-scripts/
|
||||
qa-utils/
|
||||
|
||||
In other words, any heirarchy which might exist in the on-disk
|
||||
locations of the roots, or (in the Subversion case) in the
|
||||
version-control system itself, is flattened.
|
||||
|
||||
But sometimes you might want to preserve -- or even introduce -- some
|
||||
heirarchy in those roots, exposed to the users. For example, you
|
||||
might wish that instead of a single "root listing" view, ViewVC
|
||||
instead presented users with a navigable tree constructed from paths
|
||||
configured by the admin. For example, imagine if each root_parent
|
||||
item also carried an "exposure path" bit of configuration:
|
||||
|
||||
# path vc exposure-path
|
||||
# ------------ --- -------------
|
||||
root_parents = /var/cvs/old : cvs : old,
|
||||
/var/svn/dev : svn : current/dev,
|
||||
/var/svn/qa : svn : current/qa,
|
||||
|
||||
A visit to ViewVC's root URL would then show:
|
||||
|
||||
old/
|
||||
current/
|
||||
|
||||
Clicking into "old/", you'd see:
|
||||
|
||||
old-docs/
|
||||
old-src/
|
||||
|
||||
Alternatively, clicking into "current/" would show:
|
||||
|
||||
dev/
|
||||
qa/
|
||||
|
||||
...and so on.
|
||||
|
||||
In fact, you'd have a virtual heirarchy like so:
|
||||
|
||||
/
|
||||
old/
|
||||
old-docs/ => CVS root at /var/cvs/old/docs
|
||||
old-src/ => CVS root at /var/cvs/old/src
|
||||
current/
|
||||
dev/
|
||||
dev-build/ => SVN root at /var/svn/dev/dev-build
|
||||
dev-libs/ => SVN root at /var/svn/dev/dev-libs
|
||||
dev-tools/ => SVN root at /var/svn/dev/dev-tools
|
||||
qa/
|
||||
qa-scripts/ => SVN root at /var/svn/qa/qa-scripts
|
||||
qa-utils/ => SVN root at /var/svn/qa/qa-utils
|
||||
|
||||
|
||||
CHALLENGES
|
||||
==========
|
||||
|
||||
* Merely tacking on a new "exposure path" item in the definition of
|
||||
the root_parents, cvs_roots, and svn_roots seems clunky. It also
|
||||
limits the the granularity of control: you couldn't assign
|
||||
different locations to the various roots that live within a single
|
||||
root parent path. Finally, the current code is banking on there
|
||||
being only a single colon (:) separator (since those might legally
|
||||
appear in Windows on-disk paths). That might create a bit of a
|
||||
compatibility annoyance.
|
||||
|
||||
* What do you do about root_as_url_component=0? I guess this feature
|
||||
is just simply unavailable in that case.
|
||||
|
||||
* Do you continue to allow cvs_roots and svn_roots members to specify
|
||||
a root name, or does the root name concept go away entirely in light
|
||||
of the new exposure path concept?
|
@@ -1,5 +0,0 @@
|
||||
This directory contains ViewVC template sets contributed by their
|
||||
respective authors and expected to work against ViewVC 1.0.x. They
|
||||
are not necessarily supported by the ViewVC development community, and
|
||||
do not necessarily carry the same license or copyright as ViewVC
|
||||
itself.
|
@@ -1,7 +0,0 @@
|
||||
Template Set: newvc
|
||||
Author(s): C. Michael Pilato <cmpilato@red-bean.com>
|
||||
Compatibility: ViewVC 1.1
|
||||
|
||||
The "newvc" template set uses top navigation tabs to flip between
|
||||
various views of a file or directory, and aims for a clean-yet-modern
|
||||
look and feel.
|
@@ -1,128 +0,0 @@
|
||||
[# Setup page definitions]
|
||||
[define page_title]Diff of:[end]
|
||||
[define help_href][docroot]/help_rootview.html[end]
|
||||
[# end]
|
||||
[include "include/header.ezt" "diff"]
|
||||
|
||||
<form method="get" action="[diff_format_action]" style="display: inline;">
|
||||
<div>
|
||||
[for diff_format_hidden_values]<input type="hidden" name="[diff_format_hidden_values.name]" value="[diff_format_hidden_values.value]"/>[end]
|
||||
<select name="diff_format" onchange="submit()">
|
||||
<option value="h" [is diff_format "h"]selected="selected"[end]>Colored Diff</option>
|
||||
<option value="l" [is diff_format "l"]selected="selected"[end]>Long Colored Diff</option>
|
||||
<option value="f" [is diff_format "f"]selected="selected"[end]>Full Colored Diff</option>
|
||||
<option value="u" [is diff_format "u"]selected="selected"[end]>Unidiff</option>
|
||||
<option value="c" [is diff_format "c"]selected="selected"[end]>Context Diff</option>
|
||||
<option value="s" [is diff_format "s"]selected="selected"[end]>Side by Side</option>
|
||||
</select>
|
||||
<input type="submit" value="Show" />
|
||||
(<a href="[patch_href]">Generate patch</a>)
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div id="vc_main_body">
|
||||
<!-- ************************************************************** -->
|
||||
|
||||
[if-any raw_diff]
|
||||
<pre>[raw_diff]</pre>
|
||||
[else]
|
||||
|
||||
[define change_right][end]
|
||||
[define last_change_type][end]
|
||||
|
||||
[# these should live in stylesheets]
|
||||
|
||||
<table cellpadding="0" cellspacing="0" style="width: 100%;">
|
||||
[for changes]
|
||||
[is changes.type "change"][else]
|
||||
[if-any change_right][change_right][define change_right][end][end]
|
||||
[end]
|
||||
[is changes.type "header"]
|
||||
<tr>
|
||||
<th class="vc_header" style="width:6%;"><strong>#</strong></th>
|
||||
<th colspan="2" class="vc_header">
|
||||
<strong>Line [changes.line_info_left]</strong> |
|
||||
<strong>Line [changes.line_info_right]</strong>
|
||||
</th>
|
||||
</tr>
|
||||
[else]
|
||||
[is changes.type "add"]
|
||||
<tr>
|
||||
<td id="l[changes.line_number]">[if-any right.annotate_href]<a href="[right.annotate_href]#l[changes.line_number]">[changes.line_number]</a>[else][changes.line_number][end]</td>
|
||||
<td class="vc_diff_plusminus"><strong style="color: green;">+</strong></td>
|
||||
<td class="vc_diff_add">[changes.right]</td>
|
||||
</tr>
|
||||
[else]
|
||||
[is changes.type "remove"]
|
||||
<tr>
|
||||
<td style="text-decoration: line-through">[changes.line_number]</td>
|
||||
<td class="vc_diff_plusminus"><strong style="color: red;">–</strong></td>
|
||||
<td class="vc_diff_remove">[changes.left]</td>
|
||||
</tr>
|
||||
[else]
|
||||
[is changes.type "change"]
|
||||
[if-any changes.have_left]
|
||||
<tr>
|
||||
<td style="text-decoration: line-through">[changes.line_number]</td>
|
||||
<td class="vc_diff_plusminus"><strong style="color: yellow;"><</strong></td>
|
||||
<td class="vc_diff_changes1">[changes.left]</td>
|
||||
</tr>
|
||||
[end]
|
||||
[define change_right][change_right]
|
||||
[if-any changes.have_right]
|
||||
<tr>
|
||||
<td id="l[changes.line_number]">[if-any right.annotate_href]<a href="[right.annotate_href]#l[changes.line_number]">[changes.line_number]</a>[else][changes.line_number][end]</td>
|
||||
<td class="vc_diff_plusminus"><strong style="color: yellow;">></strong></td>
|
||||
<td class="vc_diff_changes2">[changes.right]</td>
|
||||
</tr>[end]
|
||||
[end]
|
||||
[else]
|
||||
[is changes.type "no-changes"]
|
||||
<tr><td colspan="3" style="vc_diff_nochange"><strong>- No changes -</strong></td></tr>
|
||||
[else]
|
||||
[is changes.type "binary-diff"]
|
||||
<tr><td colspan="3" class="vc_diff_binary"><strong>- Binary file revisions differ -</strong></td></tr>
|
||||
[else]
|
||||
[is changes.type "error"]
|
||||
<tr><td colspan="3" class="vc_diff_error"><strong>- ViewVC depends on rcsdiff and GNU diff
|
||||
to create this page. ViewVC cannot find GNU diff. Even if you
|
||||
have GNU diff installed, the rcsdiff program must be configured
|
||||
and compiled with the GNU diff location. -</strong></td></tr>
|
||||
[else][# a line of context]
|
||||
<tr>
|
||||
<td id="l[changes.line_number]">[if-any right.annotate_href]<a href="[right.annotate_href]#l[changes.line_number]">[changes.line_number]</a>[else][changes.line_number][end]</td>
|
||||
<td class="vc_diff_plusminus"> </td>
|
||||
<td style="font-family: monospace; white-space: pre;">[changes.right]</td>
|
||||
</tr>
|
||||
[end][end][end][end][end][end][end]
|
||||
[define last_change_type][changes.type][end]
|
||||
[end]
|
||||
[if-any change_right][change_right][end]
|
||||
</table>
|
||||
|
||||
<h3>Diff Legend</h3>
|
||||
<table class="auto" cellspacing="0">
|
||||
<tr>
|
||||
<td class="vc_diff_plusminus"><strong style="color: red;">–</strong></td>
|
||||
<td class="vc_diff_remove">Removed lines</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="vc_diff_plusminus"><strong style="color: green;">+</strong></td>
|
||||
<td class="vc_diff_add">Added lines</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="vc_diff_plusminus"><strong style="color: yellow;"><</strong></td>
|
||||
<td class="vc_diff_changes1">Changed lines</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="vc_diff_plusminus"><strong style="color: yellow;">></strong></td>
|
||||
<td class="vc_diff_changes2">Changed lines</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
[end]
|
||||
|
||||
<!-- ************************************************************** -->
|
||||
</div>
|
||||
|
||||
[include "include/footer.ezt"]
|
@@ -1,139 +0,0 @@
|
||||
[# setup page definitions]
|
||||
[define page_title]Index of:[end]
|
||||
[define help_href][docroot]/help_[if-any where]dir[else]root[end]view.html[end]
|
||||
[# end]
|
||||
[include "include/header.ezt" "directory"]
|
||||
|
||||
[if-any where][else]
|
||||
<!-- you may insert repository access instructions here -->
|
||||
[end]
|
||||
|
||||
<table class="auto">
|
||||
[is cfg.options.use_pagesize "0"][else][is picklist_len "1"][else]
|
||||
<tr>
|
||||
<td>Jump to page:</td>
|
||||
<td><form method="get" action="[dir_paging_action]">
|
||||
[for dir_paging_hidden_values]<input type="hidden" name="[dir_paging_hidden_values.name]" value="[dir_paging_hidden_values.value]"/>[end]
|
||||
<select name="dir_pagestart" onchange="submit()">
|
||||
[for picklist]
|
||||
<option [is picklist.count dir_pagestart]selected[end] value="[picklist.count]">Page [picklist.page]: [picklist.start] to [picklist.end]</option>
|
||||
[end]
|
||||
</select>
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
[end][end]
|
||||
</table>
|
||||
|
||||
<div id="vc_main_body">
|
||||
<!-- ************************************************************** -->
|
||||
|
||||
<div id="vc_togglables">
|
||||
[is roottype "svn"]
|
||||
[if-any rev]r<a href="[revision_href]">[rev]</a>[end]
|
||||
[else]
|
||||
[is num_dead "0"]
|
||||
[else]
|
||||
[if-any attic_showing]
|
||||
<a href="[hide_attic_href]">Hide
|
||||
[else]
|
||||
<a href="[show_attic_href]">Show
|
||||
[end]
|
||||
dead files</a>
|
||||
[end]
|
||||
[end]
|
||||
</div>
|
||||
|
||||
<table cellspacing="2" class="fixed" id="dirlist">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 200px" class="vc_header[is sortby "file"]_sort[end]">
|
||||
<a href="[sortby_file_href]#dirlist">File
|
||||
[is sortby "file"]
|
||||
<img class="vc_sortarrow" alt="[is sortdir "down"](rev)[end]"
|
||||
width="13" height="13"
|
||||
src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" />
|
||||
[end]
|
||||
</a>
|
||||
</th>
|
||||
<th class="vc_header[is sortby "rev"]_sort[end]">
|
||||
<a href="[sortby_rev_href]#dirlist">Last Change
|
||||
[is sortby "rev"]
|
||||
<img class="vc_sortarrow" alt="[is sortdir "down"](rev)[end]"
|
||||
width="13" height="13"
|
||||
src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" />
|
||||
[end]
|
||||
</a>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
[if-any up_href]
|
||||
<tr class="vc_row_odd">
|
||||
<td colspan="2">
|
||||
<a href="[up_href]">
|
||||
<img src="[docroot]/images/back_small.png" alt="" class="vc_icon"
|
||||
/> ../</a>
|
||||
</td>
|
||||
</tr>
|
||||
[end]
|
||||
[for entries]
|
||||
[define click_href][is entries.pathtype "dir"][entries.view_href][else][if-any entries.prefer_markup][entries.view_href][else][entries.download_href][end][end][end]
|
||||
|
||||
<tr class="vc_row_[if-index entries even]even[else]odd[end]">
|
||||
<td style="width: 200px" onclick="jumpTo('[click_href]')">
|
||||
<a name="[entries.anchor]" href="[click_href]" title="[is entries.pathtype "dir"]View Directory Contents[else][if-any entries.prefer_markup]View[else]Download[end] File Contents[end]">
|
||||
<img src="[docroot]/images/[is entries.pathtype "dir"]dir[else][is entries.state "dead"]broken[else]text[end][end].png" alt="" class="vc_icon" />
|
||||
[entries.name][is entries.pathtype "dir"]/[end]</a>
|
||||
[is entries.state "dead"](dead)[end]
|
||||
</td>
|
||||
|
||||
<td [if-any entries.log_href]onclick="jumpTo('[entries.log_href]')"[end]>
|
||||
[if-any entries.rev]
|
||||
<strong>[if-any entries.log_href]<a href="[entries.log_href]" title="Revision [entries.rev]">[entries.rev]</a>[else][entries.rev][end]</strong>
|
||||
([entries.ago] ago)
|
||||
by <em>[entries.author]</em>:
|
||||
[entries.log]
|
||||
[is entries.pathtype "dir"][is roottype "cvs"]
|
||||
<em>(from [entries.log_file]/[entries.log_rev])</em>
|
||||
[end][end]
|
||||
[end]
|
||||
</td>
|
||||
</tr>
|
||||
[end]
|
||||
</tbody>
|
||||
|
||||
</table>
|
||||
|
||||
<div id="vc_view_summary">
|
||||
[if-any search_re_form]
|
||||
<form class="inline" method="get" action="[search_re_action]">
|
||||
<div class="inline">
|
||||
[for search_re_hidden_values]<input type="hidden" name="[search_re_hidden_values.name]" value="[search_re_hidden_values.value]"/>[end]
|
||||
<input type="text" name="search" value="[search_re]" />
|
||||
<input type="submit" value="Search Files" />
|
||||
</div>
|
||||
</form>
|
||||
[if-any search_re]
|
||||
<form class="inline" method="get" action="[search_re_action]">
|
||||
<div class="inline">
|
||||
[for search_re_hidden_values]<input type="hidden" name="[search_re_hidden_values.name]" value="[search_re_hidden_values.value]"/>[end]
|
||||
<input type="submit" value="Show all files" />
|
||||
</div>
|
||||
</form>
|
||||
[end]
|
||||
|
||||
[end]
|
||||
[include "include/pathrev_form.ezt"]
|
||||
|
||||
[files_shown] file[is files_shown "1"][else]s[end] shown
|
||||
</div>
|
||||
|
||||
[include "include/props.ezt"]
|
||||
|
||||
<!-- ************************************************************** -->
|
||||
</div>
|
||||
|
||||
[include "include/footer.ezt"]
|
@@ -1,8 +0,0 @@
|
||||
/************************************/
|
||||
/*** ViewVC Help CSS Stylesheet ***/
|
||||
/************************************/
|
||||
body { margin: 0.5em; }
|
||||
img { border: none; }
|
||||
table { width: 100%; }
|
||||
td { vertical-align: top; }
|
||||
col.menu { width:12em; }
|
@@ -1,126 +0,0 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<title>ViewVC Help: Directory View</title>
|
||||
<link rel="stylesheet" href="help.css" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
<table>
|
||||
<col class="menu" />
|
||||
<col />
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<h1>ViewVC Help: Directory View</h1>
|
||||
</td>
|
||||
</tr>
|
||||
<tr><td>
|
||||
<h3>Help</h3>
|
||||
<a href="help_rootview.html">General</a><br />
|
||||
<strong>Directory View</strong><br />
|
||||
<a href="help_log.html">Log View</a><br />
|
||||
|
||||
<h3>Internet</h3>
|
||||
<a href="http://viewvc.org/index.html">Home</a><br />
|
||||
<a href="http://viewvc.org/upgrading.html">Upgrading</a><br />
|
||||
<a href="http://viewvc.org/contributing.html">Contributing</a><br />
|
||||
<a href="http://viewvc.org/license-1.html">License</a><br />
|
||||
</td><td colspan="2">
|
||||
|
||||
<p>The directory listing view should be a familiar sight to any
|
||||
computer user. It shows the path of the current directory being viewed
|
||||
at the top of the page. Below that is a table summarizing the
|
||||
directory contents, and then comes actual contents, a sortable list of
|
||||
all files and subdirectories inside the current directory.</p>
|
||||
|
||||
<p><a name="summary"></a>The summary table is made up of some or all
|
||||
of the following rows:</p>
|
||||
<ul>
|
||||
<li><a name="summary-files-shown"><strong>Files Shown</strong></a>
|
||||
- Number of files shown in the directory listing. This might be less
|
||||
than the actual number of files in the directory if a
|
||||
<a href="#option-search">regular expression search</a> is in place,
|
||||
hiding files which don't meet the search criteria. In CVS directory
|
||||
listings, this row will also have a link to toggle display of
|
||||
<a href="help_rootview.html#dead-files">dead files</a>, if any are
|
||||
present.</li>
|
||||
|
||||
<li><a name="summary-revision"><strong>Directory
|
||||
Revision</strong></a> - For Subversion directories only.
|
||||
Shown as "# of #" where the first number is the most recent
|
||||
repository revision where the directory (or a path underneath it)
|
||||
was modified. The second number is just the latest repository
|
||||
revision. Both numbers are links to
|
||||
<a href="help_rootview.html#view-rev">revision views</a></li>
|
||||
|
||||
<li><a name="summary-sticky-revision-tag"><strong>Sticky
|
||||
Revision/Tag</strong></a> - shows the current
|
||||
<a href="help_rootview.html#sticky-revision-tag">sticky revision or
|
||||
tag</a> and contains form fields to set or clear it.</li>
|
||||
|
||||
<li><a name="summary-search"><strong>Current Search</strong></a> -
|
||||
If a <a href="#option-search">regular expression search</a> is in place,
|
||||
shows the search string.</li>
|
||||
|
||||
<li><a name="summary-query"><strong>Query</strong></a> - Provides
|
||||
a link to a <a href="help_rootview.html#view-query">query form</a>
|
||||
for the directory</li>
|
||||
</ul>
|
||||
|
||||
<p><a name="list"></a>The actual directory list is a table with
|
||||
filenames and directory names in one column and information about the
|
||||
most recent revisions where each file or directory was modified in the
|
||||
other columns. Column headers can be clicked to sort the directory
|
||||
entries in order by a column, and clicked again to reverse the sort
|
||||
order.</p>
|
||||
|
||||
<p>
|
||||
<!-- If using directory.ezt template -->
|
||||
File names are links to <a href="help_log.html">log views</a>
|
||||
showing a list of revisions where a file was modified. Revision
|
||||
numbers are links to either
|
||||
<a href="help_rootview.html#view-markup">view</a>
|
||||
or <a href="help_rootview.html#view-checkout">download</a> a file
|
||||
(depending on its file type). The links are reversed for directories.
|
||||
Directory revision numbers are links to <a href="help_log.html">log
|
||||
views</a>, while directory names are links showing the contents of those
|
||||
directories.
|
||||
|
||||
<!-- If using dir_alt.ezt template -->
|
||||
<!--
|
||||
File and directory names are links to retrieve their contents.
|
||||
File links may be either
|
||||
<a href="help_rootview.html#view-markup">view</a>
|
||||
or <a href="help_rootview.html#view-download">download</a> links
|
||||
depending on the file type. Directory links go to directory
|
||||
listings. Revision numbers are links to <a href="help_log.html">log
|
||||
views</a> showing lists of revisions where a file or directory was
|
||||
modified.
|
||||
-->
|
||||
|
||||
Also, in CVS repositories with the <a
|
||||
href="help_rootview.html#view-graph">graph view</a> enabled, there
|
||||
will be small icons next to file names which are links to revision
|
||||
graphs.</p>
|
||||
|
||||
<p>Depending on how ViewVC is configured, there may be more options
|
||||
at the bottom of directory pages:</p>
|
||||
|
||||
<ul>
|
||||
<li><a name="option-search"><strong>Regular expression
|
||||
search</strong></a> - If enabled, will show a form field accepting
|
||||
a search string (a
|
||||
<a href="http://doc.python.org/lib/re-syntax.html">python regular
|
||||
expression</a>). Once submitted, only files that have at least
|
||||
one occurance of the expression will show up in directory listings.
|
||||
</li>
|
||||
<li><a name="option-tarball"><strong>Tarball download</strong></a> -
|
||||
If enabled, will show a link to download a gzipped tar archive of
|
||||
the directory contents.</li>
|
||||
</ul>
|
||||
|
||||
</td></tr></table>
|
||||
<hr />
|
||||
<address><a href="mailto:users@viewvc.tigris.org">ViewVC Users Mailinglist</a></address>
|
||||
</body>
|
||||
</html>
|
@@ -1,71 +0,0 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<title>ViewVC Help: Log View</title>
|
||||
<link rel="stylesheet" href="help.css" type="text/css" />
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
|
||||
</head>
|
||||
<body>
|
||||
<table>
|
||||
<col class="menu" />
|
||||
<col />
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<h1>ViewVC Help: Log View</h1>
|
||||
</td>
|
||||
</tr>
|
||||
<tr><td>
|
||||
<h3>Help</h3>
|
||||
<a href="help_rootview.html">General</a><br />
|
||||
<a href="help_dirview.html">Directory View</a><br />
|
||||
<strong>Log View</strong><br />
|
||||
|
||||
<h3>Internet</h3>
|
||||
<a href="http://viewvc.org/index.html">Home</a><br />
|
||||
<a href="http://viewvc.org/upgrading.html">Upgrading</a><br />
|
||||
<a href="http://viewvc.org/contributing.html">Contributing</a><br />
|
||||
<a href="http://viewvc.org/license-1.html">License</a><br />
|
||||
</td><td colspan="2">
|
||||
<p>
|
||||
The log view displays the revision history of the selected source
|
||||
file or directory. For each revision the following information is
|
||||
displayed:
|
||||
|
||||
<ul>
|
||||
<li>The revision number. In Subversion repositories, this is a
|
||||
link to the <a href="help_rootview.html#view-rev">revision
|
||||
view</a></li>
|
||||
<li>For files, links to
|
||||
<a href="help_rootview.html#view-markup">view</a>,
|
||||
<a href="help_rootview.html#view-checkout">download</a>, and
|
||||
<a href="help_rootview.html#view-annotate">annotate</a> the
|
||||
revision. For directories, a link to
|
||||
<a href="help_dirview.html">list directory contents</a></li>
|
||||
<li>A link to select the revision for diffs (see below)</li>
|
||||
<li>The date and age of the change</li>
|
||||
<li>The author of the modification</li>
|
||||
<li>The CVS branch (usually <em>MAIN</em>, if not on a branch)</li>
|
||||
<li>Possibly a list of CVS tags bound to the revision (if any)</li>
|
||||
<li>The size of the change measured in added and removed lines of
|
||||
code. (CVS only)</li>
|
||||
<li>The size of the file in bytes at the time of the revision
|
||||
(Subversion only)</li>
|
||||
<li>Links to view diffs to the previous revision or possibly to
|
||||
an arbitrary selected revision (if any, see above)</li>
|
||||
<li>If the revision is the result of a copy, the path and revision
|
||||
copied from</li>
|
||||
<li>If the revision precedes a copy or rename, the path at the
|
||||
time of the revision</li>
|
||||
<li>And last but not least, the commit log message which should tell
|
||||
about the reason for the change.</li>
|
||||
</ul>
|
||||
<p>
|
||||
At the bottom of the page you will find a form which allows
|
||||
to request diffs between arbitrary revisions.
|
||||
</p>
|
||||
</td></tr></table>
|
||||
<hr />
|
||||
<address><a href="mailto:users@viewvc.tigris.org">ViewVC Users Mailinglist</a></address>
|
||||
</body>
|
||||
</html>
|
@@ -1,66 +0,0 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<title>ViewVC Help: Query The Commit Database</title>
|
||||
<link rel="stylesheet" href="help.css" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
<table>
|
||||
<col class="menu" />
|
||||
<col />
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<h1>ViewVC Help: Query The Commit Database</h1>
|
||||
</td>
|
||||
</tr>
|
||||
<tr><td>
|
||||
<h3>Other Help:</h3>
|
||||
<a href="help_rootview.html">General</a><br />
|
||||
<a href="help_dirview.html">Directory View</a><br />
|
||||
<a href="help_log.html">Classic Log View</a><br />
|
||||
<a href="help_logtable.html">Alternative Log View</a><br />
|
||||
<strong>Query Database</strong>
|
||||
|
||||
<h3>Internet</h3>
|
||||
<a href="http://viewvc.org/index.html">Home</a><br />
|
||||
<a href="http://viewvc.org/upgrading.html">Upgrading</a><br />
|
||||
<a href="http://viewvc.org/contributing.html">Contributing</a><br />
|
||||
<a href="http://viewvc.org/license-1.html">License</a><br />
|
||||
</td><td colspan="2">
|
||||
|
||||
<p>
|
||||
Select your parameters for querying the CVS commit database in the
|
||||
form at the top of the page. You
|
||||
can search for multiple matches by typing a comma-seperated list
|
||||
into the text fields. Regular expressions, and wildcards are also
|
||||
supported. Blank text input fields are treated as wildcards.
|
||||
</p>
|
||||
<p>
|
||||
Any of the text entry fields can take a comma-seperated list of
|
||||
search arguments. For example, to search for all commits from
|
||||
authors <em>jpaint</em> and <em>gstein</em>, just type: <code>jpaint,
|
||||
gstein</code> in the <em>Author</em> input box. If you are searching
|
||||
for items containing spaces or quotes, you will need to quote your
|
||||
request. For example, the same search above with quotes is:
|
||||
<code>"jpaint", "gstein"</code>.
|
||||
</p>
|
||||
<p>
|
||||
Wildcard and regular expression searches are entered in a similar
|
||||
way to the quoted requests. You must quote any wildcard or
|
||||
regular expression request, and a command character preceeds the
|
||||
first quote. The command character <code>l</code>(lowercase L) is for wildcard
|
||||
searches, and the wildcard character is a percent (<code>%</code>). The
|
||||
command character for regular expressions is <code>r</code>, and is
|
||||
passed directly to MySQL, so you'll need to refer to the MySQL
|
||||
manual for the exact regex syntax. It is very similar to Perl. A
|
||||
wildard search for all files with a <em>.py</em> extention is:
|
||||
<code>l"%.py"</code> in the <em>File</em> input box. The same search done
|
||||
with a regular expression is: <code>r".*\.py"</code>.
|
||||
</p>
|
||||
<p>
|
||||
All search types can be mixed, as long as they are seperated by
|
||||
commas.
|
||||
</p>
|
||||
</td></tr></table>
|
||||
</body></html>
|
@@ -1,166 +0,0 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<title>ViewVC Help: General</title>
|
||||
<link rel="stylesheet" href="help.css" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
<table>
|
||||
<col class="menu" />
|
||||
<col />
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<h1>ViewVC Help: General</h1>
|
||||
</td>
|
||||
</tr>
|
||||
<tr><td>
|
||||
<h3>Help</h3>
|
||||
<strong>General</strong><br />
|
||||
<a href="help_dirview.html">Directory View</a><br />
|
||||
<a href="help_log.html">Log View</a><br />
|
||||
|
||||
<h3>Internet</h3>
|
||||
<a href="http://viewvc.org/index.html">Home</a><br />
|
||||
<a href="http://viewvc.org/upgrading.html">Upgrading</a><br />
|
||||
<a href="http://viewvc.org/contributing.html">Contributing</a><br />
|
||||
<a href="http://viewvc.org/license-1.html">License</a><br />
|
||||
</td><td colspan="2">
|
||||
|
||||
<p><em>ViewVC</em> is a WWW interface for CVS and Subversion
|
||||
repositories. It allows you to browse the files and directories in a
|
||||
repository while showing you metadata from the repository history: log
|
||||
messages, modification dates, author names, revision numbers, copy
|
||||
history, and so on. It provides several different views of repository
|
||||
data to help you find the information you are looking for:</p>
|
||||
|
||||
<ul>
|
||||
<li><a name="view-dir" href="help_dirview.html"><strong>Directory
|
||||
View</strong></a> - Shows a list of files and subdirectories in a
|
||||
directory of the repository, along with metadata like author names and
|
||||
log entries.</li>
|
||||
|
||||
<li><a name="view-log" href="help_log.html"><strong>Log
|
||||
View</strong></a> - Shows a revision by revision list of all the
|
||||
changes that have made to a file or directory in the repository, with
|
||||
metadata and links to views of each revision.</li>
|
||||
|
||||
<li><a name="view-markup"><strong>File Contents View (Markup
|
||||
View)</strong></a> - Shows the contents of a file at a particular
|
||||
revision, with revision information at the top of the page. File
|
||||
revisions which are GIF, PNG, or JPEG images are displayed inline on
|
||||
the page. Other file types are displayed as marked up text. The markup
|
||||
may be limited to turning URLs and email addresses into links, or
|
||||
configured to show colorized source code.</li>
|
||||
|
||||
<li><a name="view-checkout"><strong>File Download (Checkout
|
||||
View)</strong></a> - Retrieves the unaltered contents of a file
|
||||
revision. Browsers may try to display the file, or just save it to
|
||||
disk.</li>
|
||||
|
||||
<li><a name="view-annotate"><strong>File Annotate View</strong></a> -
|
||||
Shows the contents of a file revision and breaks it down line by line,
|
||||
showing the revision number where each one was last modified, along
|
||||
with links and other information. <em>This view is disabled in some
|
||||
ViewVC configurations</em></li>
|
||||
|
||||
<li><a name="view-diff"><strong>File Diff View</strong></a> - Shows
|
||||
the changes made between two revisions of a file</li>
|
||||
|
||||
<li><a name="view-tarball"><strong>Directory Tarball View</strong> -
|
||||
Retrieves a gzipped tar archive containing the contents of a
|
||||
directory.<em>This view is disabled in the default ViewVC
|
||||
configuration.</em></li>
|
||||
|
||||
<li><a name="view-query"><strong>Directory Query View</strong></a> -
|
||||
Shows information about changes made to all subdirectories and files
|
||||
under a parent directory, sorted and filtered by criteria you specify.
|
||||
<em>This view is disabled in the default ViewVC configuration.</em>
|
||||
</li>
|
||||
|
||||
<li><a name="view-rev"><strong>Revision View</strong> - Shows
|
||||
information about a revision including log message, author, and a list
|
||||
of changed paths. <em>For Subversion repositories only.</em></li>
|
||||
|
||||
<li><a name="view-graph"><strong>Graph View</strong></a> - Shows a
|
||||
graphical representation of a file's revisions and branches complete
|
||||
with tag and author names and links to markup and diff pages.
|
||||
<em>For CVS repositories only, and disabled in the default
|
||||
configuration.</em></li>
|
||||
</ul>
|
||||
|
||||
<h3><a name="multiple-repositories">Multiple Repositories</a></h3>
|
||||
|
||||
<p>A single installation of ViewVC is often used to provide access to
|
||||
more than one repository. In these installations, ViewVC shows a
|
||||
<em>Project Root</em> drop down box in the top right corner of every
|
||||
generated page to allow for quick access to any repository.</p>
|
||||
|
||||
<h3><a name="sticky-revision-tag">Sticky Revision and Tag</a></h3>
|
||||
|
||||
<p>By default, ViewVC will show the files and directories and revisions
|
||||
that currently exist in the repository. But it's also possible to browse
|
||||
the contents of a repository at a point in its past history by choosing
|
||||
a "sticky tag" (in CVS) or a "sticky revision" (in Subversion) from the
|
||||
forms at the top of directory and log pages. They're called sticky
|
||||
because once they're chosen, they stick around when you navigate to
|
||||
other pages, until you reset them. When they're set, directory and log
|
||||
pages only show revisions preceding the specified point in history. In
|
||||
CVS, when a tag refers to a branch or a revision on a branch, only
|
||||
revisions from the branch history are shown, including branch points and
|
||||
their preceding revisions.</p>
|
||||
|
||||
<h3><a name="dead-files">Dead Files</a></h3>
|
||||
|
||||
<p>In CVS directory listings, ViewVC can optionally display dead files.
|
||||
Dead files are files which used to be in a directory but are currently
|
||||
deleted, or files which just don't exist in the currently selected
|
||||
<a href="#sticky-revision-tag">sticky tag</a>. Dead files cannot be
|
||||
shown in Subversion repositories. The only way to see a deleted file in
|
||||
a Subversion directory is to navigate to a sticky revision where the
|
||||
file previously existed.</p>
|
||||
|
||||
<h3><a name="artificial-tags">Artificial Tags</a></h3>
|
||||
|
||||
<p>In CVS Repositories, ViewVC adds artificial tags <em>HEAD</em> and
|
||||
<em>MAIN</em> to tag listings and accepts them in place of revision
|
||||
numbers and real tag names in all URLs. <em>MAIN</em> acts like a branch
|
||||
tag pointing at the default branch, while <em>HEAD</em> acts like a
|
||||
revision tag pointing to the latest revision on the default branch. The
|
||||
default branch is usually just the trunk, but may be set to other
|
||||
branches inside individual repository files. CVS will always check out
|
||||
revisions from a file's default branch when no other branch is specified
|
||||
on the command line.</p>
|
||||
|
||||
<h3><a name="more-information">More Information</a></h3>
|
||||
|
||||
<p>More information about <em>ViewVC</em> is available from
|
||||
<a href="http://viewvc.org/">viewvc.org</a>.
|
||||
See the links below for guides to CVS and Subversion</p>
|
||||
|
||||
<h4>Documentation about CVS</h4>
|
||||
<blockquote>
|
||||
<p>
|
||||
<a href="http://cvsbook.red-bean.com/"><em>Open Source
|
||||
Development with CVS</em></a><br />
|
||||
<a href="http://www.loria.fr/~molli/cvs/doc/cvs_toc.html">CVS
|
||||
User's Guide</a><br />
|
||||
<a href="http://cellworks.washington.edu/pub/docs/cvs/tutorial/cvs_tutorial_1.html">Another CVS tutorial</a><br />
|
||||
<a href="http://www.csc.calpoly.edu/~dbutler/tutorials/winter96/cvs/">Yet another CVS tutorial (a little old, but nice)</a><br />
|
||||
<a href="http://www.cs.utah.edu/dept/old/texinfo/cvs/FAQ.txt">An old but very useful FAQ about CVS</a>
|
||||
</p>
|
||||
</blockquote>
|
||||
|
||||
<h4>Documentation about Subversion</h3>
|
||||
<blockquote>
|
||||
<p>
|
||||
<a href="http://svnbook.red-bean.com/"><em>Version Control with
|
||||
Subversion</em></a><br />
|
||||
</p>
|
||||
</blockquote>
|
||||
|
||||
</td></tr></table>
|
||||
<hr />
|
||||
<address><a href="mailto:users@viewvc.tigris.org">ViewVC Users Mailinglist</a></address>
|
||||
</body>
|
||||
</html>
|
Binary file not shown.
Before Width: | Height: | Size: 601 B |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user