mirror of
https://github.com/vitalif/viewvc-4intranet
synced 2019-04-16 04:14:59 +03:00
Compare commits
32 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
79e6d32043 | ||
![]() |
a6c01c2f5b | ||
![]() |
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 |
17
CHANGES
17
CHANGES
@@ -1,3 +1,20 @@
|
||||
Version 1.1.3 (released 22-Dec-2009)
|
||||
|
||||
* security fix: add root listing support of per-root authz config
|
||||
* security fix: query.py requires 'forbidden' authorizer (or none) in config
|
||||
* fix URL-ification of truncated log messages (issue #3)
|
||||
* fix regexp input validation (issue #426, #427, #440)
|
||||
* add support for configurable tab-to-spaces conversion
|
||||
* fix not-a-sequence error in diff view
|
||||
* allow viewvc-install to work when templates-contrib is absent
|
||||
* minor template improvements/corrections
|
||||
* expose revision metadata in diff view (issue #431)
|
||||
* markup file/directory item property URLs and email addresses (issue #434)
|
||||
* make ViewVC cross copies in Subversion history by default
|
||||
* fix bug that caused standalone.py failure under Python 1.5.2 (issue #442)
|
||||
* fix support for per-vhost overrides of authorizer parameters (issue #411)
|
||||
* fix root name identification in query.py interface
|
||||
|
||||
Version 1.1.2 (released 11-Aug-2009)
|
||||
|
||||
* security fix: validate the 'view' parameter to avoid XSS attack
|
||||
|
67
INSTALL
67
INSTALL
@@ -168,14 +168,23 @@ checkin database working are below.
|
||||
APACHE CONFIGURATION
|
||||
--------------------
|
||||
|
||||
1) Find out where the web server configuration file is kept. Typical
|
||||
locations are /etc/httpd/httpd.conf, /etc/httpd/conf/httpd.conf,
|
||||
and /etc/apache/httpd.conf. Depending on how apache was installed,
|
||||
you may also look under /usr/local/etc or /etc/local. Use the vendor
|
||||
documentation or the find utility if in doubt.
|
||||
1) Locate your Apache configuration file(s).
|
||||
|
||||
Either METHOD A:
|
||||
2) The ScriptAlias directive is very useful for pointing
|
||||
Typical locations are /etc/httpd/httpd.conf,
|
||||
/etc/httpd/conf/httpd.conf, and /etc/apache/httpd.conf. Depending
|
||||
on how Apache was installed, you may also look under /usr/local/etc
|
||||
or /etc/local. Use the vendor documentation or the find utility if
|
||||
in doubt.
|
||||
|
||||
2) Configure Apache to expose ViewVC to users at the URL of your choice.
|
||||
|
||||
ViewVC provides several different ways to do this. Choose one of
|
||||
the following methods:
|
||||
|
||||
-----------------------------------
|
||||
METHOD A: CGI mode via ScriptAlias
|
||||
-----------------------------------
|
||||
The ScriptAlias directive is very useful for pointing
|
||||
directly to the viewvc.cgi script. Simply insert a line containing
|
||||
|
||||
ScriptAlias /viewvc <VIEWVC_INSTALLATION_DIRECTORY>/bin/cgi/viewvc.cgi
|
||||
@@ -186,20 +195,20 @@ Either METHOD A:
|
||||
ScriptAlias /viewvc /usr/local/viewvc-1.0/bin/cgi/viewvc.cgi
|
||||
ScriptAlias /query /usr/local/viewvc-1.0/bin/cgi/query.cgi
|
||||
|
||||
continue with step 3).
|
||||
|
||||
or alternatively METHOD B:
|
||||
2) Copy the CGI scripts from
|
||||
----------------------------------------
|
||||
METHOD B: CGI mode in cgi-bin directory
|
||||
----------------------------------------
|
||||
Copy the CGI scripts from
|
||||
<VIEWVC_INSTALLATION_DIRECTORY>/bin/cgi/*.cgi
|
||||
to the /cgi-bin/ directory configured in your httpd.conf file.
|
||||
|
||||
continue with step 3).
|
||||
|
||||
and then there's METHOD C:
|
||||
2) Copy the CGI scripts from
|
||||
------------------------------------------
|
||||
METHOD C: CGI mode in ExecCGI'd directory
|
||||
------------------------------------------
|
||||
Copy the CGI scripts from
|
||||
<VIEWVC_INSTALLATION_DIRECTORY>/bin/cgi/*.cgi
|
||||
to the directory of your choosing in the Document Root adding the following
|
||||
apache directives for the directory in httpd.conf or an .htaccess file:
|
||||
Apache directives for the directory in httpd.conf or an .htaccess file:
|
||||
|
||||
Options +ExecCGI
|
||||
AddHandler cgi-script .cgi
|
||||
@@ -208,12 +217,12 @@ and then there's METHOD C:
|
||||
to be effective, "AllowOverride All" or "AllowOverride Options FileInfo"
|
||||
need to have been specified for the directory.)
|
||||
|
||||
continue with step 3).
|
||||
|
||||
or if you've got Mod_Python installed you can use METHOD D:
|
||||
2) Copy the Python scripts and .htaccess file from
|
||||
------------------------------------------
|
||||
METHOD D: Using mod_python (if installed)
|
||||
------------------------------------------
|
||||
Copy the Python scripts and .htaccess file from
|
||||
<VIEWVC_INSTALLATION_DIRECTORY>/bin/mod_python/
|
||||
to a directory being served by apache.
|
||||
to a directory being served by Apache.
|
||||
|
||||
In httpd.conf, make sure that "AllowOverride All" or at least
|
||||
"AllowOverride FileInfo Options" are enabled for the directory
|
||||
@@ -223,17 +232,17 @@ or if you've got Mod_Python installed you can use METHOD D:
|
||||
feature may not work because it uses multithreading. This works fine
|
||||
under Apache 2.
|
||||
|
||||
continue with step 3).
|
||||
3) Restart Apache.
|
||||
|
||||
3) Restart apache. The commands to do this vary. "httpd -k restart" and
|
||||
"apache -k restart" are two common variants. On RedHat Linux it is
|
||||
done using the command "/sbin/service httpd restart" and on SuSE Linux
|
||||
it is done with "rcapache restart"
|
||||
The commands to do this vary. "httpd -k restart" and "apache -k
|
||||
restart" are two common variants. On RedHat Linux it is done using
|
||||
the command "/sbin/service httpd restart" and on SuSE Linux it is
|
||||
done with "rcapache restart". Other systems use "apachectl restart".
|
||||
|
||||
4) Optional: Add access control.
|
||||
4) [Optional] Add access control.
|
||||
|
||||
In your httpd.conf you can control access to certain modules by adding
|
||||
directives like this:
|
||||
In your httpd.conf you can control access to certain modules by
|
||||
adding directives like this:
|
||||
|
||||
<Location "<url to viewvc.cgi>/<modname_you_wish_to_access_ctl>">
|
||||
AllowOverride None
|
||||
|
@@ -136,7 +136,7 @@
|
||||
## virtue of being the basenames of repositories found in the
|
||||
## root_parents option locations.
|
||||
##
|
||||
## Note: This setting is ignored when root_as_url_component is enabled.
|
||||
## NOTE: This setting is ignored when root_as_url_component is enabled.
|
||||
##
|
||||
## Example:
|
||||
## default_root = cvsroot
|
||||
@@ -243,9 +243,14 @@
|
||||
## aren't installed in the executable search path, so here's where you can
|
||||
## tell ViewVC where to find them.
|
||||
##
|
||||
## NOTE: Options with a "_dir" suffix are for configuring the directories
|
||||
## in which certain programs live; otherwise, the option value should
|
||||
## point to the actual program.
|
||||
## NOTE: Options with a "_dir" suffix are for configuring the
|
||||
## directories in which certain programs live. Note that this might
|
||||
## not be the same directory into which the program's installer dumped
|
||||
## the whole program package -- we want the deepest directory in which
|
||||
## the executable program itself resides ("C:\rcstools\bin\win32"
|
||||
## rather than just "C:\rcstools", for example). The values of options
|
||||
## whose names lack the "_dir" suffix should point to the actual
|
||||
## program itself (such as "C:\Program Files\cvsnt\cvs.exe").
|
||||
|
||||
|
||||
## rcs_dir: Directory in which the RCS utilities are installed, used
|
||||
@@ -303,14 +308,16 @@
|
||||
## alternative to using the "root=" query key. If ViewVC is configured
|
||||
## with multiple repositories, this results in more natural looking
|
||||
## ViewVC URLs.
|
||||
## Note: Enabling this option will break backwards compatibility with
|
||||
##
|
||||
## NOTE: Enabling this option will break backwards compatibility with
|
||||
## any old ViewCVS URL which doesn't have an explicit "root" parameter.
|
||||
##
|
||||
#root_as_url_component = 1
|
||||
|
||||
## checkout_magic: Use checkout links with magic /*checkout*/ prefixes so
|
||||
## checked out HTML pages can have working links to other repository files
|
||||
## Note: This option is DEPRECATED and should not be used in new ViewVC
|
||||
##
|
||||
## NOTE: This option is DEPRECATED and should not be used in new ViewVC
|
||||
## installations. Setting "default_file_view = co" achieves the same effect
|
||||
##
|
||||
#checkout_magic = 0
|
||||
@@ -347,6 +354,7 @@
|
||||
## hide_cvsroot: Don't show the CVSROOT directory
|
||||
## 1 Hide CVSROOT directory
|
||||
## 0 Show CVSROOT directory
|
||||
##
|
||||
## NOTE: Someday this option may be removed in favor of letting
|
||||
## individual authorizer plugin hide the CVSROOT.
|
||||
##
|
||||
@@ -357,7 +365,8 @@
|
||||
## 0 - No mangling; markup un-mangled email addresses as hyperlinks
|
||||
## 1 - Obfuscation (using entity encoding); no hyperlinking
|
||||
## 2 - Data-dropping address truncation; no hyperlinking
|
||||
## Note: this will not effect the display of versioned file contents, only
|
||||
##
|
||||
## NOTE: this will not effect the display of versioned file contents, only
|
||||
## addresses that appear in version control metadata (e.g. log messages).
|
||||
##
|
||||
#mangle_email_addresses = 0
|
||||
@@ -368,9 +377,11 @@
|
||||
## ViewCVS URLs, but "co" has the advantage that it allows ViewVC to serve
|
||||
## static HTML pages directly from a repository with working links
|
||||
## to other repository files
|
||||
## Note: Changing this option may break compatibility with existing
|
||||
##
|
||||
## NOTE: Changing this option may break compatibility with existing
|
||||
## bookmarked URLs.
|
||||
## Also note: If you choose one of the "co" or "markup" views, be sure
|
||||
##
|
||||
## ALSO NOTE: If you choose one of the "co" or "markup" views, be sure
|
||||
## to enable it (via the allowed_views option)
|
||||
##
|
||||
#default_file_view = log
|
||||
@@ -490,7 +501,7 @@
|
||||
## allow_compress: Allow compression via gzip of output if the Browser
|
||||
## accepts it (HTTP_ACCEPT_ENCODING contains "gzip").
|
||||
##
|
||||
## Note: this relies on Python's gzip module, which has proven to be
|
||||
## NOTE: this relies on Python's gzip module, which has proven to be
|
||||
## not-so-performant. Enabling this feature should reduce the overall
|
||||
## transfer size of ViewVC's responses to the client's request, but
|
||||
## will do so with a speed penalty.
|
||||
@@ -505,7 +516,8 @@
|
||||
## this config file resides; absolute paths may be used as well. If
|
||||
## %lang% occurs in the pathname, then the selected language will be
|
||||
## substituted.
|
||||
## See Also: the [templates] configuration section, where you can
|
||||
##
|
||||
## SEE ALSO: the [templates] configuration section, where you can
|
||||
## override templates on a per-view basis.
|
||||
##
|
||||
#template_dir = templates
|
||||
@@ -517,9 +529,16 @@
|
||||
## penalty, and from the "docroot" subdirectory of the directory
|
||||
## specified by the "template_dir" option).
|
||||
##
|
||||
## NOTE: This option is evaluated outside the context of a particular
|
||||
## root. Be careful when using per-root configuration to select an
|
||||
## alternate template set as the default value for this option will
|
||||
## still be based on the global default template set per 'template_dir'
|
||||
## above, not on 'template_dir' as overridden for a given root.
|
||||
##
|
||||
#docroot =
|
||||
|
||||
## show_subdir_lastmod: Show last changelog message for CVS subdirectories
|
||||
##
|
||||
## NOTE: The current implementation makes many assumptions and may show
|
||||
## the incorrect file at some times. The main assumption is that the
|
||||
## last modified file has the newest filedate. But some CVS operations
|
||||
@@ -528,7 +547,8 @@
|
||||
## the last checkin was on the same tag as you are viewing. Enable
|
||||
## this if you like the feature, but don't rely on correct results.
|
||||
##
|
||||
## ** WARNING: Enabling this will currently leak unauthorized path names **
|
||||
## SECURITY WARNING: Enabling this will currently leak unauthorized
|
||||
## path names.
|
||||
##
|
||||
#show_subdir_lastmod = 0
|
||||
|
||||
@@ -559,6 +579,11 @@
|
||||
##
|
||||
#enable_syntax_coloration = 1
|
||||
|
||||
## 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 by the syntax coloration logic -- if enabled -- for the
|
||||
@@ -632,7 +657,7 @@
|
||||
## If %lang% occurs in the pathname, then the selected language will be
|
||||
## substituted.
|
||||
##
|
||||
## Note: the selected language is defined by the "languages" item in the
|
||||
## NOTE: the selected language is defined by the "languages" item in the
|
||||
## [general] section, and based on the request's Accept-Language
|
||||
## header.
|
||||
##
|
||||
@@ -698,7 +723,8 @@
|
||||
#port = 3306
|
||||
|
||||
## database_name: ViewVC database name.
|
||||
##database_name = ViewVC
|
||||
##
|
||||
#database_name = ViewVC
|
||||
|
||||
## user: Username of user with read/write privileges to the database
|
||||
## specified by the 'database_name' configuration option.
|
||||
@@ -783,6 +809,16 @@
|
||||
## ViewVC options typically found in the base configuration section
|
||||
## named CONFIGSECTION ("general", "option", etc.)
|
||||
##
|
||||
## NOTE: Per-vhost overrides may only be applied to the following
|
||||
## sections:
|
||||
##
|
||||
## general
|
||||
## options
|
||||
## utilities
|
||||
## templates
|
||||
## cvsdb
|
||||
## authz-*
|
||||
##
|
||||
## Here is an example:
|
||||
##
|
||||
## [vhosts]
|
||||
@@ -812,7 +848,15 @@
|
||||
## basename of a root path in root_parents. Options found in this new
|
||||
## configuration section override for this one root the corresponding
|
||||
## options found in the base configuration section CONFIGSECTION
|
||||
## ("options", "authz-*", etc.)
|
||||
## ("options", "authz-*", etc.) as interpreted after per-vhost
|
||||
## overrides (if any) have been applied.
|
||||
##
|
||||
## NOTE: Per-root overrides may only be applied to the following
|
||||
## sections:
|
||||
##
|
||||
## options
|
||||
## utilities
|
||||
## authz-*
|
||||
##
|
||||
## Here is an example showing how to enable Subversion authz-based
|
||||
## authorization for only the single root named "svnroot":
|
||||
|
@@ -726,12 +726,22 @@ 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>
|
||||
@@ -749,6 +759,11 @@ 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>
|
||||
@@ -772,6 +787,11 @@ td {
|
||||
current revision. Valid only when <var>roottype</var> is
|
||||
<tt>svn</tt>.</td>
|
||||
</tr>
|
||||
<tr class="varlevel2">
|
||||
<td class="varname">left.size</td>
|
||||
<td>String</td>
|
||||
<td>Size of the left file revision, in bytes. Subversion only.</td>
|
||||
</tr>
|
||||
<tr class="varlevel2">
|
||||
<td class="varname">left.tag</td>
|
||||
<td>String</td>
|
||||
@@ -793,12 +813,22 @@ 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>
|
||||
@@ -816,6 +846,11 @@ 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>
|
||||
@@ -839,6 +874,11 @@ td {
|
||||
current revision. Valid only when <var>roottype</var> is
|
||||
<tt>svn</tt>.</td>
|
||||
</tr>
|
||||
<tr class="varlevel2">
|
||||
<td class="varname">right.size</td>
|
||||
<td>String</td>
|
||||
<td>Size of the right file revision, in bytes. Subversion only.</td>
|
||||
</tr>
|
||||
<tr class="varlevel2">
|
||||
<td class="varname">right.tag</td>
|
||||
<td>String</td>
|
||||
|
274
lib/config.py
274
lib/config.py
@@ -24,45 +24,142 @@ import fnmatch
|
||||
#########################################################################
|
||||
#
|
||||
# CONFIGURATION
|
||||
# -------------
|
||||
#
|
||||
# There are three forms of configuration:
|
||||
#
|
||||
# 1) edit the viewvc.conf created by the viewvc-install(er)
|
||||
# 2) as (1), but delete all unchanged entries from viewvc.conf
|
||||
# 3) do not use viewvc.conf and just edit the defaults in this file
|
||||
# 1. edit the viewvc.conf created by the viewvc-install(er)
|
||||
# 2. as (1), but delete all unchanged entries from viewvc.conf
|
||||
# 3. do not use viewvc.conf and just edit the defaults in this file
|
||||
#
|
||||
# Most users will want to use (1), but there are slight speed advantages
|
||||
# to the other two options. Note that viewvc.conf values are a bit easier
|
||||
# to work with since it is raw text, rather than python literal values.
|
||||
#
|
||||
#
|
||||
# A WORD ABOUT OPTION LAYERING/OVERRIDES
|
||||
# --------------------------------------
|
||||
#
|
||||
# ViewVC has three "layers" of configuration options:
|
||||
#
|
||||
# 1. base configuration options - very basic configuration bits
|
||||
# found in sections like 'general', 'options', etc.
|
||||
# 2. vhost overrides - these options overlay/override the base
|
||||
# configuration on a per-vhost basis.
|
||||
# 3. root overrides - these options overlay/override the base
|
||||
# configuration and vhost overrides on a per-root basis.
|
||||
#
|
||||
# Here's a diagram of the valid overlays/overrides:
|
||||
#
|
||||
# PER-ROOT PER-VHOST BASE
|
||||
#
|
||||
# ,-----------. ,-----------.
|
||||
# | vhost-*/ | | |
|
||||
# | general | --> | general |
|
||||
# | | | |
|
||||
# `-----------' `-----------'
|
||||
# ,-----------. ,-----------. ,-----------.
|
||||
# | root-*/ | | vhost-*/ | | |
|
||||
# | options | --> | options | --> | options |
|
||||
# | | | | | |
|
||||
# `-----------' `-----------' `-----------'
|
||||
# ,-----------. ,-----------. ,-----------.
|
||||
# | root-*/ | | vhost-*/ | | |
|
||||
# | templates | --> | templates | --> | templates |
|
||||
# | | | | | |
|
||||
# `-----------' `-----------' `-----------'
|
||||
# ,-----------. ,-----------. ,-----------.
|
||||
# | root-*/ | | vhost-*/ | | |
|
||||
# | utilities | --> | utilities | --> | utilities |
|
||||
# | | | | | |
|
||||
# `-----------' `-----------' `-----------'
|
||||
# ,-----------. ,-----------.
|
||||
# | vhost-*/ | | |
|
||||
# | cvsdb | --> | cvsdb |
|
||||
# | | | |
|
||||
# `-----------' `-----------'
|
||||
# ,-----------. ,-----------. ,-----------.
|
||||
# | root-*/ | | vhost-*/ | | |
|
||||
# | authz-* | --> | authz-* | --> | authz-* |
|
||||
# | | | | | |
|
||||
# `-----------' `-----------' `-----------'
|
||||
# ,-----------.
|
||||
# | |
|
||||
# | vhosts |
|
||||
# | |
|
||||
# `-----------'
|
||||
#
|
||||
# ### TODO: Figure out what this all means for the 'kv' stuff.
|
||||
#
|
||||
#########################################################################
|
||||
|
||||
class Config:
|
||||
_sections = ('general', 'utilities', 'options', 'cvsdb', 'templates')
|
||||
_force_multi_value = ('cvs_roots', 'svn_roots', 'languages', 'kv_files',
|
||||
'root_parents', 'allowed_views', 'mime_types_files')
|
||||
_base_sections = (
|
||||
# Base configuration sections.
|
||||
'authz-*',
|
||||
'cvsdb',
|
||||
'general',
|
||||
'options',
|
||||
'templates',
|
||||
'utilities',
|
||||
)
|
||||
_force_multi_value = (
|
||||
# Configuration values with multiple, comma-separated values.
|
||||
'allowed_views',
|
||||
'cvs_roots',
|
||||
'kv_files',
|
||||
'languages',
|
||||
'mime_types_files',
|
||||
'root_parents',
|
||||
'svn_roots',
|
||||
)
|
||||
_allowed_overrides = {
|
||||
# Mapping of override types to allowed overridable sections.
|
||||
'vhost' : ('authz-*',
|
||||
'cvsdb',
|
||||
'general',
|
||||
'options',
|
||||
'templates',
|
||||
'utilities',
|
||||
),
|
||||
'root' : ('authz-*',
|
||||
'options',
|
||||
'templates',
|
||||
'utilities',
|
||||
)
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
for section in self._sections:
|
||||
self.root_options_overlayed = 0
|
||||
for section in self._base_sections:
|
||||
if section[-1] == '*':
|
||||
continue
|
||||
setattr(self, section, _sub_config())
|
||||
|
||||
def load_config(self, pathname, vhost=None, rootname=None):
|
||||
def load_config(self, pathname, vhost=None):
|
||||
"""Load the configuration file at PATHNAME, applying configuration
|
||||
settings there as overrides to the built-in default values. If
|
||||
VHOST is provided, also process the configuration overrides
|
||||
specific to that virtual host."""
|
||||
|
||||
self.conf_path = os.path.isfile(pathname) and pathname or None
|
||||
self.base = os.path.dirname(pathname)
|
||||
self.parser = ConfigParser.ConfigParser()
|
||||
self.parser.read(self.conf_path or [])
|
||||
|
||||
for section in self._sections:
|
||||
if self.parser.has_section(section):
|
||||
|
||||
for section in self.parser.sections():
|
||||
if self._is_allowed_section(self.parser, section,
|
||||
self._base_sections):
|
||||
self._process_section(self.parser, section, section)
|
||||
|
||||
if vhost and self.parser.has_section('vhosts'):
|
||||
self._process_vhost(self.parser, vhost)
|
||||
|
||||
if rootname:
|
||||
self._process_root_options(self.parser, rootname)
|
||||
|
||||
def load_kv_files(self, language):
|
||||
"""Process the key/value (kv) files specified in the
|
||||
configuration, merging their values into the configuration as
|
||||
dotted heirarchical items."""
|
||||
|
||||
kv = _sub_config()
|
||||
|
||||
for fname in self.general.kv_files:
|
||||
@@ -92,10 +189,12 @@ class Config:
|
||||
return kv
|
||||
|
||||
def path(self, path):
|
||||
"""Return path relative to the config file directory"""
|
||||
"""Return PATH relative to the config file directory."""
|
||||
return os.path.join(self.base, path)
|
||||
|
||||
def _process_section(self, parser, section, subcfg_name):
|
||||
if not hasattr(self, subcfg_name):
|
||||
setattr(self, subcfg_name, _sub_config())
|
||||
sc = getattr(self, subcfg_name)
|
||||
|
||||
for opt in parser.options(section):
|
||||
@@ -108,25 +207,55 @@ class Config:
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
### FIXME: This feels like unnecessary depth of knowledge for a
|
||||
### semi-generic configuration object.
|
||||
if opt == 'cvs_roots' or opt == 'svn_roots':
|
||||
value = _parse_roots(opt, value)
|
||||
|
||||
setattr(sc, opt, value)
|
||||
|
||||
def _is_allowed_section(self, parser, section, allowed_sections):
|
||||
"""Return 1 iff SECTION is an allowed section, defined as being
|
||||
explicitly present in the ALLOWED_SECTIONS list or present in the
|
||||
form 'someprefix-*' in that list."""
|
||||
|
||||
for allowed_section in allowed_sections:
|
||||
if allowed_section[-1] == '*':
|
||||
if _startswith(section, allowed_section[:-1]):
|
||||
return 1
|
||||
elif allowed_section == section:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
def _is_allowed_override(self, parser, sectype, secspec, section):
|
||||
"""Test if SECTION is an allowed override section for sections of
|
||||
type SECTYPE ('vhosts' or 'root', currently) and type-specifier
|
||||
SECSPEC (a rootname or vhostname, currently). If it is, return
|
||||
the overridden base section name. If it's not an override section
|
||||
at all, return None. And if it's an override section but not an
|
||||
allowed one, raise IllegalOverrideSection."""
|
||||
|
||||
cv = '%s-%s/' % (sectype, secspec)
|
||||
lcv = len(cv)
|
||||
if section[:lcv] != cv:
|
||||
return None
|
||||
base_section = section[lcv:]
|
||||
if self._is_allowed_section(parser, base_section,
|
||||
self._allowed_overrides[sectype]):
|
||||
return base_section
|
||||
raise IllegalOverrideSection(sectype, section)
|
||||
|
||||
def _process_vhost(self, parser, vhost):
|
||||
# find a vhost name for this vhost, if any (if not, we've nothing to do)
|
||||
# Find a vhost name for this VHOST, if any (else, we've nothing to do).
|
||||
canon_vhost = self._find_canon_vhost(parser, vhost)
|
||||
if not canon_vhost:
|
||||
return
|
||||
|
||||
# overlay any option sections associated with this vhost name
|
||||
cv = 'vhost-%s/' % (canon_vhost)
|
||||
lcv = len(cv)
|
||||
# Overlay any option sections associated with this vhost name.
|
||||
for section in parser.sections():
|
||||
if section[:lcv] == cv:
|
||||
base_section = section[lcv:]
|
||||
if base_section not in self._sections:
|
||||
raise IllegalOverrideSection('vhost', section)
|
||||
base_section = self._is_allowed_override(parser, 'vhost',
|
||||
canon_vhost, section)
|
||||
if base_section:
|
||||
self._process_section(parser, section, base_section)
|
||||
|
||||
def _find_canon_vhost(self, parser, vhost):
|
||||
@@ -141,26 +270,22 @@ class Config:
|
||||
|
||||
return None
|
||||
|
||||
def _process_root_options(self, parser, rootname):
|
||||
rn = 'root-%s/' % (rootname)
|
||||
lrn = len(rn)
|
||||
for section in parser.sections():
|
||||
if section[:lrn] == rn:
|
||||
base_section = section[lrn:]
|
||||
if base_section in self._sections:
|
||||
if base_section == 'general':
|
||||
raise IllegalOverrideSection('root', section)
|
||||
self._process_section(parser, section, base_section)
|
||||
elif _startswith(base_section, 'authz-'):
|
||||
pass
|
||||
else:
|
||||
raise IllegalOverrideSection('root', section)
|
||||
|
||||
def overlay_root_options(self, rootname):
|
||||
"Overly per-root options atop the existing option set."
|
||||
"""Overlay per-root options for ROOTNAME atop the existing option
|
||||
set. This is a destructive change to the configuration."""
|
||||
|
||||
# We can only deal with this happening once!
|
||||
assert(self.root_options_overlayed == 0)
|
||||
self.root_options_overlayed = 1
|
||||
|
||||
if not self.conf_path:
|
||||
return
|
||||
self._process_root_options(self.parser, rootname)
|
||||
|
||||
for section in self.parser.sections():
|
||||
base_section = self._is_allowed_override(self.parser, 'root',
|
||||
rootname, section)
|
||||
if base_section:
|
||||
self._process_section(self.parser, section, base_section)
|
||||
|
||||
def _get_parser_items(self, parser, section):
|
||||
"""Basically implement ConfigParser.items() for pre-Python-2.3 versions."""
|
||||
@@ -171,23 +296,67 @@ class Config:
|
||||
for option in parser.options(section):
|
||||
d[option] = parser.get(section, option)
|
||||
return d.items()
|
||||
|
||||
def get_authorizer_params(self, authorizer, rootname=None):
|
||||
if not self.conf_path:
|
||||
return {}
|
||||
|
||||
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 == authz_section:
|
||||
if section == root_authz_section:
|
||||
for key, value in self._get_parser_items(self.parser, section):
|
||||
params[key] = value
|
||||
if rootname:
|
||||
root_authz_section = 'root-%s/authz-%s' % (rootname, authorizer)
|
||||
for section in self.parser.sections():
|
||||
if section == root_authz_section:
|
||||
for key, value in self._get_parser_items(self.parser, section):
|
||||
params[key] = value
|
||||
return authorizer, params
|
||||
|
||||
def get_authorizer_params(self, authorizer=None):
|
||||
"""Return a dictionary of parameter names and values which belong
|
||||
to the configured authorizer (or AUTHORIZER, if provided)."""
|
||||
params = {}
|
||||
if authorizer is None:
|
||||
authorizer = self.options.authorizer
|
||||
if authorizer:
|
||||
authz_section = 'authz-%s' % (self.options.authorizer)
|
||||
if hasattr(self, authz_section):
|
||||
sub_config = getattr(self, authz_section)
|
||||
for attr in dir(sub_config):
|
||||
params[attr] = getattr(sub_config, attr)
|
||||
return params
|
||||
|
||||
def set_defaults(self):
|
||||
@@ -240,10 +409,11 @@ class Config:
|
||||
self.options.show_subdir_lastmod = 0
|
||||
self.options.show_logs = 1
|
||||
self.options.show_log_in_markup = 1
|
||||
self.options.cross_copies = 0
|
||||
self.options.cross_copies = 1
|
||||
self.options.use_localtime = 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"
|
||||
|
27
lib/query.py
27
lib/query.py
@@ -275,8 +275,30 @@ def prev_rev(rev):
|
||||
return string.join(r, '.')
|
||||
|
||||
def is_forbidden(cfg, cvsroot_name, module):
|
||||
auth_params = cfg.get_authorizer_params('forbidden', cvsroot_name)
|
||||
forbidden = auth_params.get('forbidden', '')
|
||||
'''Return 1 if MODULE in CVSROOT_NAME is forbidden; return 0 otherwise.'''
|
||||
|
||||
# CVSROOT_NAME might be None here if the data comes from an
|
||||
# unconfigured root. This interfaces doesn't care that the root
|
||||
# isn't configured, but if that's the case, it will consult only
|
||||
# the base and per-vhost configuration for authorizer and
|
||||
# authorizer parameters.
|
||||
if cvsroot_name:
|
||||
authorizer, params = cfg.get_authorizer_and_params_hack(cvsroot_name)
|
||||
else:
|
||||
authorizer = cfg.options.authorizer
|
||||
params = cfg.get_authorizer_params()
|
||||
|
||||
# If CVSROOT_NAME isn't configured to use an authorizer, nothing
|
||||
# is forbidden. If it's configured to use something other than
|
||||
# the 'forbidden' authorizer, complain. Otherwise, check for
|
||||
# forbiddenness per the PARAMS as expected.
|
||||
if not authorizer:
|
||||
return 0
|
||||
if authorizer != 'forbidden':
|
||||
raise Exception("The 'forbidden' authorizer is the only one supported "
|
||||
"by this interface. The '%s' root is configured to "
|
||||
"use a different one." % (cvsroot_name))
|
||||
forbidden = params.get('forbidden', '')
|
||||
forbidden = map(string.strip, filter(None, string.split(forbidden, ',')))
|
||||
default = 0
|
||||
for pat in forbidden:
|
||||
@@ -369,6 +391,7 @@ def run_query(server, cfg, form_data, viewvc_link):
|
||||
files = [ ]
|
||||
|
||||
cvsroots = {}
|
||||
viewvc.expand_root_parents(cfg)
|
||||
rootitems = cfg.general.svn_roots.items() + cfg.general.cvs_roots.items()
|
||||
for key, value in rootitems:
|
||||
cvsroots[cvsdb.CleanRepository(value)] = key
|
||||
|
397
lib/viewvc.py
397
lib/viewvc.py
@@ -14,7 +14,7 @@
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
__version__ = '1.1.2'
|
||||
__version__ = '1.1.3'
|
||||
|
||||
# this comes from our library; measure the startup time
|
||||
import debug
|
||||
@@ -234,7 +234,7 @@ class Request:
|
||||
cfg.overlay_root_options(self.rootname)
|
||||
|
||||
# Setup an Authorizer for this rootname and username
|
||||
self.auth = setup_authorizer(cfg, self.username, self.rootname)
|
||||
self.auth = setup_authorizer(cfg, self.username)
|
||||
|
||||
# Create the repository object
|
||||
try:
|
||||
@@ -633,12 +633,14 @@ def _validate_param(name, value):
|
||||
'400 Bad Request')
|
||||
|
||||
def _validate_regex(value):
|
||||
# hmm. there isn't anything that we can do here.
|
||||
|
||||
### we need to watch the flow of these parameters through the system
|
||||
### to ensure they don't hit the page unescaped. otherwise, these
|
||||
### parameters could constitute a CSS attack.
|
||||
pass
|
||||
try:
|
||||
re.compile(value)
|
||||
return True
|
||||
except:
|
||||
return None
|
||||
|
||||
def _validate_view(value):
|
||||
# Return true iff VALUE is one of our allowed views.
|
||||
@@ -689,15 +691,15 @@ _legal_params = {
|
||||
'content-type' : _validate_mimetype,
|
||||
|
||||
# for query
|
||||
'branch' : _validate_regex,
|
||||
'branch_match' : _re_validate_alpha,
|
||||
'dir' : None,
|
||||
'file' : _validate_regex,
|
||||
'file_match' : _re_validate_alpha,
|
||||
'who' : _validate_regex,
|
||||
'branch_match' : _re_validate_alpha,
|
||||
'who_match' : _re_validate_alpha,
|
||||
'comment' : _validate_regex,
|
||||
'comment_match' : _re_validate_alpha,
|
||||
'dir' : None,
|
||||
'file' : None,
|
||||
'branch' : None,
|
||||
'who' : None,
|
||||
'comment' : None,
|
||||
'querysort' : _re_validate_alpha,
|
||||
'date' : _re_validate_alpha,
|
||||
'hours' : _re_validate_number,
|
||||
@@ -791,32 +793,37 @@ def _orig_path(request, rev_param='revision', path_param=None):
|
||||
return _path_parts(request.repos.get_location(path, pathrev, rev)), rev
|
||||
return _path_parts(path), rev
|
||||
|
||||
def setup_authorizer(cfg, username, rootname):
|
||||
import imp
|
||||
def setup_authorizer(cfg, username, rootname=None):
|
||||
"""Setup the authorizer. If ROOTNAME is provided, assume that
|
||||
per-root options have not been overlayed. Otherwise, assume they
|
||||
have (and fetch the authorizer for the configured root)."""
|
||||
|
||||
if rootname is None:
|
||||
authorizer = cfg.options.authorizer
|
||||
params = cfg.get_authorizer_params()
|
||||
else:
|
||||
authorizer, params = cfg.get_authorizer_and_params_hack(rootname)
|
||||
|
||||
# No configured authorizer? No problem.
|
||||
if not cfg.options.authorizer:
|
||||
if not authorizer:
|
||||
return None
|
||||
|
||||
# First, try to load a module with the configured name.
|
||||
import imp
|
||||
fp = None
|
||||
try:
|
||||
try:
|
||||
fp, path, desc = imp.find_module("%s" % (cfg.options.authorizer),
|
||||
vcauth.__path__)
|
||||
fp, path, desc = imp.find_module("%s" % (authorizer), vcauth.__path__)
|
||||
my_auth = imp.load_module('viewvc', fp, path, desc)
|
||||
except ImportError:
|
||||
raise debug.ViewVCException(
|
||||
'Invalid authorizer (%s) specified for root "%s"' \
|
||||
% (cfg.options.authorizer, rootname),
|
||||
% (authorizer, rootname),
|
||||
'500 Internal Server Error')
|
||||
finally:
|
||||
if fp:
|
||||
fp.close()
|
||||
|
||||
# Now we'll get custom parameters for our particular root.
|
||||
params = cfg.get_authorizer_params(cfg.options.authorizer, rootname)
|
||||
|
||||
# Finally, instantiate our Authorizer.
|
||||
return my_auth.ViewVCAuthorizer(username, params)
|
||||
|
||||
@@ -1068,55 +1075,193 @@ def get_file_view_info(request, where, rev=None, mime_type=None, pathrev=-1):
|
||||
revision_href=revision_href,
|
||||
prefer_markup=ezt.boolean(prefer_markup))
|
||||
|
||||
def htmlify(html):
|
||||
return html and cgi.escape(html) or html
|
||||
|
||||
# Regular expressions for location text that looks like URLs and email
|
||||
# addresses. Note that the regexps assume the text is already HTML-encoded.
|
||||
|
||||
# Matches URLs
|
||||
_re_rewrite_url = re.compile('((http|https|ftp|file|svn|svn\+ssh)'
|
||||
'(://[-a-zA-Z0-9%.~:_/]+)((\?|\&)'
|
||||
'(://[-a-zA-Z0-9%.~:_/]+)((\?|\&)'
|
||||
'([-a-zA-Z0-9%.~:_]+)=([-a-zA-Z0-9%.~:_])+)*'
|
||||
'(#([-a-zA-Z0-9%.~:_]+)?)?)')
|
||||
# Matches email addresses
|
||||
_re_rewrite_email = re.compile('([-a-zA-Z0-9_.\+]+)@'
|
||||
'(([-a-zA-Z0-9]+\.)+[A-Za-z]{2,4})')
|
||||
|
||||
def mangle_email_addresses(text, style=0):
|
||||
# style=2: truncation mangling
|
||||
if style == 2:
|
||||
return re.sub(_re_rewrite_email, r'\1@…', text)
|
||||
|
||||
# style=1: entity-encoding and at-wrapping
|
||||
if style == 1:
|
||||
def _match_replace(matchobj):
|
||||
return string.join(map(lambda x: '&#%d;' % (ord(x)),
|
||||
matchobj.group(1)), '') \
|
||||
+ ' {at} ' + \
|
||||
string.join(map(lambda x: '&#%d;' % (ord(x)),
|
||||
matchobj.group(2)), '')
|
||||
return re.sub(_re_rewrite_email, _match_replace, text)
|
||||
class HtmlFormatter:
|
||||
"""Format a string as HTML-encoded output with customizable markup
|
||||
rules, for example turning strings that look like URLs into anchor links.
|
||||
|
||||
# otherwise, no mangling
|
||||
return text
|
||||
NOTE: While there might appear to be some unused portions of this
|
||||
interface, there is a good chance that there are consumers outside
|
||||
of ViewVC itself that make use of these things.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self._formatters = []
|
||||
|
||||
def htmlify(html, mangle_email_addrs=0):
|
||||
if not html:
|
||||
return html
|
||||
html = cgi.escape(html)
|
||||
html = re.sub(_re_rewrite_url, r'<a href="\1">\1</a>', html)
|
||||
html = mangle_email_addresses(html, mangle_email_addrs)
|
||||
return html
|
||||
def format_url(self, mobj, userdata, maxlen=0):
|
||||
"""Return a 2-tuple containing:
|
||||
- the text represented by MatchObject MOBJ, formatted as
|
||||
linkified URL, with no more than MAXLEN characters in the
|
||||
non-HTML-tag bits. If MAXLEN is 0, there is no maximum.
|
||||
- the number of non-HTML-tag characters returned.
|
||||
"""
|
||||
s = mobj.group(0)
|
||||
trunc_s = maxlen and s[:maxlen] or s
|
||||
return '<a href="%s">%s</a>' % (cgi.escape(s),
|
||||
cgi.escape(trunc_s)), \
|
||||
len(trunc_s)
|
||||
|
||||
def format_log(log, cfg, htmlize=1):
|
||||
def format_email(self, mobj, userdata, maxlen=0):
|
||||
"""Return a 2-tuple containing:
|
||||
- the text represented by MatchObject MOBJ, formatted as
|
||||
linkified email address, with no more than MAXLEN characters
|
||||
in the non-HTML-tag bits. If MAXLEN is 0, there is no maximum.
|
||||
- the number of non-HTML-tag characters returned.
|
||||
"""
|
||||
s = mobj.group(0)
|
||||
trunc_s = maxlen and s[:maxlen] or s
|
||||
return '<a href="mailto:%s">%s</a>' % (urllib.quote(s),
|
||||
self._entity_encode(trunc_s)), \
|
||||
len(trunc_s)
|
||||
|
||||
def format_email_obfuscated(self, mobj, userdata, maxlen=0):
|
||||
"""Return a 2-tuple containing:
|
||||
- the text represented by MatchObject MOBJ, formatted as an
|
||||
entity-encoded email address, with no more than MAXLEN characters
|
||||
in the non-HTML-tag bits. If MAXLEN is 0, there is no maximum.
|
||||
- the number of non-HTML-tag characters returned.
|
||||
"""
|
||||
s = mobj.group(0)
|
||||
trunc_s = maxlen and s[:maxlen] or s
|
||||
return self._entity_encode(trunc_s), len(trunc_s)
|
||||
|
||||
def format_email_truncated(self, mobj, userdata, maxlen=0):
|
||||
"""Return a 2-tuple containing:
|
||||
- the text represented by MatchObject MOBJ, formatted as an
|
||||
HTML-escaped truncated email address of no more than MAXLEN
|
||||
characters. If MAXLEN is 0, there is no maximum.
|
||||
- the number of characters returned.
|
||||
"""
|
||||
s = mobj.group(1)
|
||||
s_len = len(s)
|
||||
if (maxlen == 0) or (s_len < (maxlen - 1)):
|
||||
return self._entity_encode(s) + '@…', s_len + 2
|
||||
elif s_len < maxlen:
|
||||
return self._entity_encode(s) + '@', s_len + 1
|
||||
else:
|
||||
trunc_s = mobj.group(1)[:maxlen]
|
||||
return self._entity_encode(trunc_s), len(trunc_s)
|
||||
|
||||
def format_text(self, s, unused, maxlen=0):
|
||||
"""Return a 2-tuple containing:
|
||||
- the text S, HTML-escaped, containing no more than MAXLEN
|
||||
characters. If MAXLEN is 0, there is no maximum.
|
||||
- the number of characters returned.
|
||||
"""
|
||||
trunc_s = maxlen and s[:maxlen] or s
|
||||
return cgi.escape(trunc_s), len(trunc_s)
|
||||
|
||||
def add_formatter(self, regexp, conv, userdata=None):
|
||||
"""Register a formatter which finds instances of strings matching
|
||||
REGEXP, and using the function CONV and USERDATA to format them.
|
||||
|
||||
CONV is a function which accepts three parameters:
|
||||
- the MatchObject which holds the string portion to be formatted,
|
||||
- the USERDATA object,
|
||||
- the maximum number of characters from that string to use for
|
||||
human-readable output (or 0 to indicate no maximum).
|
||||
"""
|
||||
if type(regexp) == type(''):
|
||||
regexp = re.compile(regexp)
|
||||
self._formatters.append([regexp, conv, userdata])
|
||||
|
||||
def get_result(self, s, maxlen=0):
|
||||
"""Format S per the set of formatters registered with this object,
|
||||
and limited to MAXLEN visible characters (or unlimited if MAXLEN
|
||||
is 0). Return a 3-tuple containing the formatted result string,
|
||||
the number of visible characters in the result string, and a
|
||||
boolean flag indicating whether or not S was truncated.
|
||||
"""
|
||||
out = ''
|
||||
out_len = 0
|
||||
for token in self._tokenize_text(s):
|
||||
chunk, chunk_len = token.converter(token.match, token.userdata, maxlen)
|
||||
out = out + chunk
|
||||
out_len = out_len + chunk_len
|
||||
if maxlen:
|
||||
maxlen = maxlen - chunk_len
|
||||
if maxlen <= 0:
|
||||
return out, out_len, 1
|
||||
return out, out_len, 0
|
||||
|
||||
def _entity_encode(self, s):
|
||||
return string.join(map(lambda x: '&#%d;' % (ord(x)), s), '')
|
||||
|
||||
def _tokenize_text(self, s):
|
||||
tokens = []
|
||||
while s:
|
||||
best_match = best_conv = best_userdata = None
|
||||
for test in self._formatters:
|
||||
match = test[0].search(s)
|
||||
# If we find and match and (a) its our first one, or (b) it
|
||||
# matches text earlier than our previous best match, or (c) it
|
||||
# matches text at the same location as our previous best match
|
||||
# but extends to cover more text than that match, then this is
|
||||
# our new best match.
|
||||
#
|
||||
# Implied here is that when multiple formatters match exactly
|
||||
# the same text, the first formatter in the registration list wins.
|
||||
if match \
|
||||
and ((best_match is None) \
|
||||
or (match.start() < best_match.start())
|
||||
or ((match.start() == best_match.start()) \
|
||||
and (match.end() > best_match.end()))):
|
||||
best_match = match
|
||||
best_conv = test[1]
|
||||
best_userdata = test[2]
|
||||
# If we found a match...
|
||||
if best_match:
|
||||
# ... add any non-matching stuff first, then the matching bit.
|
||||
start = best_match.start()
|
||||
end = best_match.end()
|
||||
if start > 0:
|
||||
tokens.append(_item(match=s[:start],
|
||||
converter=self.format_text,
|
||||
userdata=None))
|
||||
tokens.append(_item(match=best_match,
|
||||
converter=best_conv,
|
||||
userdata=best_userdata))
|
||||
s = s[end:]
|
||||
else:
|
||||
# Otherwise, just add the rest of the string.
|
||||
tokens.append(_item(match=s,
|
||||
converter=self.format_text,
|
||||
userdata=None))
|
||||
s = ''
|
||||
return tokens
|
||||
|
||||
def format_log(log, cfg, maxlen=0, htmlize=1):
|
||||
if not log:
|
||||
return log
|
||||
|
||||
if htmlize:
|
||||
s = htmlify(log[:cfg.options.short_log_len],
|
||||
cfg.options.mangle_email_addresses)
|
||||
else:
|
||||
s = cgi.escape(log[:cfg.options.short_log_len])
|
||||
lf = HtmlFormatter()
|
||||
lf.add_formatter(_re_rewrite_url, lf.format_url)
|
||||
if cfg.options.mangle_email_addresses == 2:
|
||||
s = re.sub(_re_rewrite_email, r'\1@...', s)
|
||||
if len(log) > cfg.options.short_log_len:
|
||||
s = s + '...'
|
||||
return s
|
||||
lf.add_formatter(_re_rewrite_email, lf.format_email_truncated)
|
||||
elif cfg.options.mangle_email_addresses == 1:
|
||||
lf.add_formatter(_re_rewrite_email, lf.format_email_obfuscated)
|
||||
else:
|
||||
lf.add_formatter(_re_rewrite_email, lf.format_email)
|
||||
log, log_len, truncated = lf.get_result(log, maxlen)
|
||||
return log + (truncated and '…' or '')
|
||||
else:
|
||||
if cfg.options.mangle_email_addresses == 2:
|
||||
log = re.sub(_re_rewrite_email, r'\1@...', log)
|
||||
return maxlen and log[:maxlen] or log
|
||||
|
||||
_time_desc = {
|
||||
1 : 'second',
|
||||
@@ -1204,7 +1349,7 @@ def common_template_data(request, revision=None, mime_type=None):
|
||||
'rootname' : request.rootname \
|
||||
and request.server.escape(request.rootname) or None,
|
||||
'rootpath' : request.rootpath,
|
||||
'roots_href' : request.get_url(view_func=view_roots, escape=1, params={}),
|
||||
'roots_href' : None,
|
||||
'roottype' : request.roottype,
|
||||
'rss_href' : None,
|
||||
'tarball_href' : None,
|
||||
@@ -1240,6 +1385,10 @@ def common_template_data(request, revision=None, mime_type=None):
|
||||
where=dir, pathtype=vclib.DIR,
|
||||
params={}, escape=1)
|
||||
|
||||
if 'roots' in cfg.options.allowed_views:
|
||||
data['roots_href'] = request.get_url(view_func=view_roots,
|
||||
escape=1, params={})
|
||||
|
||||
if request.pathtype == vclib.FILE:
|
||||
fvi = get_file_view_info(request, request.where, data['rev'], mime_type)
|
||||
data['view_href'] = fvi.view_href
|
||||
@@ -1312,22 +1461,21 @@ def retry_read(src, reqlen=CHUNK_SIZE):
|
||||
continue
|
||||
return chunk
|
||||
|
||||
def copy_stream(src, dst, cfg, htmlize=0):
|
||||
def copy_stream(src, dst, htmlize=0):
|
||||
while 1:
|
||||
chunk = retry_read(src)
|
||||
if not chunk:
|
||||
break
|
||||
if htmlize:
|
||||
chunk = htmlify(chunk, mangle_email_addrs=0)
|
||||
chunk = htmlify(chunk)
|
||||
dst.write(chunk)
|
||||
|
||||
class MarkupPipeWrapper:
|
||||
"""An EZT callback that outputs a filepointer, plus some optional
|
||||
pre- and post- text."""
|
||||
|
||||
def __init__(self, cfg, fp, pretext=None, posttext=None, htmlize=1):
|
||||
def __init__(self, fp, pretext=None, posttext=None, htmlize=0):
|
||||
self.fp = fp
|
||||
self.cfg = cfg
|
||||
self.pretext = pretext
|
||||
self.posttext = posttext
|
||||
self.htmlize = htmlize
|
||||
@@ -1335,7 +1483,7 @@ class MarkupPipeWrapper:
|
||||
def __call__(self, ctx):
|
||||
if self.pretext:
|
||||
ctx.fp.write(self.pretext)
|
||||
copy_stream(self.fp, ctx.fp, self.cfg, self.htmlize)
|
||||
copy_stream(self.fp, ctx.fp, self.htmlize)
|
||||
self.fp.close()
|
||||
if self.posttext:
|
||||
ctx.fp.write(self.posttext)
|
||||
@@ -1376,11 +1524,13 @@ def markup_stream_pygments(request, cfg, blame_data, fp, filename, mime_type):
|
||||
try:
|
||||
lexer = get_lexer_for_mimetype(mime_type,
|
||||
encoding=encoding,
|
||||
tabsize=cfg.options.tabsize,
|
||||
stripnl=False)
|
||||
except ClassNotFound:
|
||||
try:
|
||||
lexer = get_lexer_for_filename(filename,
|
||||
encoding=encoding,
|
||||
tabsize=cfg.options.tabsize,
|
||||
stripnl=False)
|
||||
except ClassNotFound:
|
||||
use_pygments = 0
|
||||
@@ -1392,7 +1542,15 @@ def markup_stream_pygments(request, cfg, blame_data, fp, filename, mime_type):
|
||||
# one from the file contents we fetch with PATH and REV.
|
||||
if not use_pygments:
|
||||
if blame_source:
|
||||
return blame_source
|
||||
class BlameSourceTabsizeWrapper:
|
||||
def __init__(self, blame_source, tabsize):
|
||||
self.blame_source = blame_source
|
||||
self.tabsize = cfg.options.tabsize
|
||||
def __getitem__(self, idx):
|
||||
item = self.blame_source.__getitem__(idx)
|
||||
item.text = string.expandtabs(item.text, self.tabsize)
|
||||
return item
|
||||
return BlameSourceTabsizeWrapper(blame_source, cfg.options.tabsize)
|
||||
else:
|
||||
lines = []
|
||||
line_no = 0
|
||||
@@ -1401,8 +1559,8 @@ def markup_stream_pygments(request, cfg, blame_data, fp, filename, mime_type):
|
||||
if not line:
|
||||
break
|
||||
line_no = line_no + 1
|
||||
item = vclib.Annotation(cgi.escape(line), line_no,
|
||||
None, None, None, None)
|
||||
line = cgi.escape(string.expandtabs(line, cfg.options.tabsize))
|
||||
item = vclib.Annotation(line, line_no, None, None, None, None)
|
||||
item.diff_href = None
|
||||
lines.append(item)
|
||||
return lines
|
||||
@@ -1468,7 +1626,7 @@ def get_itemprops(request, path_parts, rev):
|
||||
props = []
|
||||
has_binary_props = 0
|
||||
for name in propnames:
|
||||
value = itemprops[name]
|
||||
value = format_log(itemprops[name], request.cfg)
|
||||
undisplayable = ezt.boolean(0)
|
||||
# skip non-utf8 property names
|
||||
try:
|
||||
@@ -1572,7 +1730,7 @@ def markup_or_annotate(request, is_annotate):
|
||||
data['date'] = make_time_string(entry.date, cfg)
|
||||
data['author'] = entry.author
|
||||
data['changed'] = entry.changed
|
||||
data['log'] = htmlify(entry.log, cfg.options.mangle_email_addresses)
|
||||
data['log'] = format_log(entry.log, cfg)
|
||||
data['size'] = entry.size
|
||||
|
||||
if entry.date is not None:
|
||||
@@ -1788,8 +1946,9 @@ def view_directory(request):
|
||||
row.date = make_time_string(file.date, cfg)
|
||||
row.ago = html_time(request, file.date)
|
||||
if cfg.options.show_logs:
|
||||
row.short_log = format_log(file.log, cfg)
|
||||
row.log = htmlify(file.log, cfg.options.mangle_email_addresses)
|
||||
row.log = format_log(file.log, cfg)
|
||||
row.short_log = format_log(file.log, cfg,
|
||||
maxlen=cfg.options.short_log_len)
|
||||
row.lockinfo = file.lockinfo
|
||||
row.anchor = request.server.escape(file.name)
|
||||
row.name = request.server.escape(file.name)
|
||||
@@ -1875,9 +2034,7 @@ def view_directory(request):
|
||||
'entries' : rows,
|
||||
'sortby' : sortby,
|
||||
'sortdir' : sortdir,
|
||||
'search_re' : search_re \
|
||||
and htmlify(search_re, cfg.options.mangle_email_addresses) \
|
||||
or None,
|
||||
'search_re' : htmlify(search_re),
|
||||
'dir_pagestart' : None,
|
||||
'sortby_file_href' : request.get_url(params={'sortby': 'file',
|
||||
'sortdir': None},
|
||||
@@ -2164,7 +2321,7 @@ def view_log(request):
|
||||
entry.ago = None
|
||||
if rev.date is not None:
|
||||
entry.ago = html_time(request, rev.date, 1)
|
||||
entry.log = htmlify(rev.log or "", cfg.options.mangle_email_addresses)
|
||||
entry.log = format_log(rev.log or '', cfg)
|
||||
entry.size = rev.size
|
||||
entry.lockinfo = rev.lockinfo
|
||||
entry.branch_point = None
|
||||
@@ -2426,7 +2583,7 @@ def view_checkout(request):
|
||||
or calculate_mime_type(request, path, rev) \
|
||||
or 'text/plain'
|
||||
server_fp = get_writeready_server_file(request, mime_type)
|
||||
copy_stream(fp, server_fp, cfg)
|
||||
copy_stream(fp, server_fp)
|
||||
fp.close()
|
||||
|
||||
def view_cvsgraph_image(request):
|
||||
@@ -2448,7 +2605,7 @@ def view_cvsgraph_image(request):
|
||||
"-r", request.repos.rootpath,
|
||||
rcsfile), 'rb', 0)
|
||||
|
||||
copy_stream(fp, get_writeready_server_file(request, 'image/png'), cfg)
|
||||
copy_stream(fp, get_writeready_server_file(request, 'image/png'))
|
||||
fp.close()
|
||||
|
||||
def view_cvsgraph(request):
|
||||
@@ -2556,7 +2713,7 @@ def view_doc(request):
|
||||
mime_type = 'text/css'
|
||||
else: # assume HTML:
|
||||
mime_type = None
|
||||
copy_stream(fp, get_writeready_server_file(request, mime_type), cfg)
|
||||
copy_stream(fp, get_writeready_server_file(request, mime_type))
|
||||
fp.close()
|
||||
|
||||
def rcsdiff_date_reformat(date_str, cfg):
|
||||
@@ -2605,7 +2762,7 @@ class DiffSource:
|
||||
return item
|
||||
|
||||
def _format_text(self, text):
|
||||
text = string.expandtabs(string.rstrip(text))
|
||||
text = string.expandtabs(string.rstrip(text), self.cfg.options.tabsize)
|
||||
hr_breakable = self.cfg.options.hr_breakable
|
||||
|
||||
# in the code below, "\x01" will be our stand-in for "&". We don't want
|
||||
@@ -2619,7 +2776,7 @@ class DiffSource:
|
||||
text = string.replace(text, ' ', ' \x01nbsp;')
|
||||
else:
|
||||
text = string.replace(text, ' ', '\x01nbsp;')
|
||||
text = htmlify(text, mangle_email_addrs=0)
|
||||
text = htmlify(text)
|
||||
text = string.replace(text, '\x01', '&')
|
||||
text = string.replace(text, '\x02',
|
||||
'<span style="color:red">\</span><br />')
|
||||
@@ -2901,7 +3058,7 @@ def view_patch(request):
|
||||
|
||||
server_fp = get_writeready_server_file(request, 'text/plain')
|
||||
server_fp.write(headers)
|
||||
copy_stream(fp, server_fp, cfg)
|
||||
copy_stream(fp, server_fp)
|
||||
fp.close()
|
||||
|
||||
|
||||
@@ -2919,6 +3076,15 @@ def view_diff(request):
|
||||
if check_freshness(request, None, '%s-%s' % (rev1, rev2), weak=1):
|
||||
return
|
||||
|
||||
# TODO: Is the slice necessary, or is limit enough?
|
||||
log_entry1 = request.repos.itemlog(p1, rev1, vclib.SORTBY_REV, 0, 1, {})[-1]
|
||||
log_entry2 = request.repos.itemlog(p2, rev2, vclib.SORTBY_REV, 0, 1, {})[-1]
|
||||
|
||||
ago1 = log_entry1.date is not None \
|
||||
and html_time(request, log_entry1.date, 1) or None
|
||||
ago2 = log_entry2.date is not None \
|
||||
and html_time(request, log_entry2.date, 2) or None
|
||||
|
||||
diff_type = None
|
||||
diff_options = {}
|
||||
human_readable = 0
|
||||
@@ -2979,30 +3145,31 @@ def view_diff(request):
|
||||
'to diff', '400 Bad Request')
|
||||
path_left = _path_join(p1)
|
||||
path_right = _path_join(p2)
|
||||
|
||||
date1 = date2 = raw_diff_fp = None
|
||||
changes = []
|
||||
if fp:
|
||||
date1, date2, flag, headers = diff_parse_headers(fp, diff_type,
|
||||
rev1, rev2,
|
||||
sym1, sym2)
|
||||
else:
|
||||
date1 = date2 = flag = headers = None
|
||||
|
||||
raw_diff_fp = changes = None
|
||||
if fp:
|
||||
rev1, rev2, sym1, sym2)
|
||||
if human_readable:
|
||||
if flag is not None:
|
||||
changes = [ _item(type=flag) ]
|
||||
else:
|
||||
changes = DiffSource(fp, cfg)
|
||||
else:
|
||||
raw_diff_fp = MarkupPipeWrapper(cfg, fp,
|
||||
htmlify(headers, mangle_email_addrs=0),
|
||||
None, 1)
|
||||
raw_diff_fp = MarkupPipeWrapper(fp, htmlify(headers), None, 1)
|
||||
|
||||
no_format_params = request.query_dict.copy()
|
||||
no_format_params['diff_format'] = None
|
||||
diff_format_action, diff_format_hidden_values = \
|
||||
request.get_form(params=no_format_params)
|
||||
|
||||
fvi = get_file_view_info(request, path_left, rev1)
|
||||
left = _item(date=rcsdiff_date_reformat(date1, cfg),
|
||||
left = _item(date=make_time_string(log_entry1.date, cfg),
|
||||
author=log_entry1.author,
|
||||
log=format_log(log_entry1.log, cfg),
|
||||
size=log_entry1.size,
|
||||
ago=ago1,
|
||||
path=path_left,
|
||||
rev=rev1,
|
||||
tag=sym1,
|
||||
@@ -3014,7 +3181,11 @@ def view_diff(request):
|
||||
prefer_markup=fvi.prefer_markup)
|
||||
|
||||
fvi = get_file_view_info(request, path_right, rev2)
|
||||
right = _item(date=rcsdiff_date_reformat(date2, cfg),
|
||||
right = _item(date=make_time_string(log_entry2.date, cfg),
|
||||
author=log_entry2.author,
|
||||
log=format_log(log_entry2.log, cfg),
|
||||
size=log_entry2.size,
|
||||
ago=ago2,
|
||||
path=path_right,
|
||||
rev=rev2,
|
||||
tag=sym2,
|
||||
@@ -3025,9 +3196,6 @@ def view_diff(request):
|
||||
revision_href=fvi.revision_href,
|
||||
prefer_markup=fvi.prefer_markup)
|
||||
|
||||
diff_format_action, diff_format_hidden_values = \
|
||||
request.get_form(params=no_format_params)
|
||||
|
||||
data = common_template_data(request)
|
||||
data.merge(ezt.TemplateData({
|
||||
'left' : left,
|
||||
@@ -3372,7 +3540,7 @@ def view_revision(request):
|
||||
'rev' : str(rev),
|
||||
'author' : author,
|
||||
'date' : date_str,
|
||||
'log' : msg and htmlify(msg, cfg.options.mangle_email_addresses) or None,
|
||||
'log' : format_log(msg, cfg),
|
||||
'ago' : date is not None and html_time(request, date, 1) or None,
|
||||
'changes' : changes,
|
||||
'prev_href' : prev_rev_href,
|
||||
@@ -3412,12 +3580,42 @@ def is_querydb_nonempty_for_root(request):
|
||||
return 1
|
||||
return 0
|
||||
|
||||
def validate_query_args(request):
|
||||
# Do some additional input validation of query form arguments beyond
|
||||
# what is offered by the CGI param validation loop in Request.run_viewvc().
|
||||
|
||||
for arg_base in ['branch', 'file', 'comment', 'who']:
|
||||
# First, make sure the the XXX_match args have valid values:
|
||||
arg_match = arg_base + '_match'
|
||||
arg_match_value = request.query_dict.get(arg_match, 'exact')
|
||||
if not arg_match_value in ('exact', 'like', 'glob', 'regex', 'notregex'):
|
||||
raise debug.ViewVCException(
|
||||
'An illegal value was provided for the "%s" parameter.'
|
||||
% (arg_match),
|
||||
'400 Bad Request')
|
||||
|
||||
# Now, for those args which are supposed to be regular expressions (per
|
||||
# their corresponding XXX_match values), make sure they are.
|
||||
if arg_match_value == 'regex' or arg_match_value == 'notregex':
|
||||
arg_base_value = request.query_dict.get(arg_base)
|
||||
if arg_base_value:
|
||||
try:
|
||||
re.compile(arg_base_value)
|
||||
except:
|
||||
raise debug.ViewVCException(
|
||||
'An illegal value was provided for the "%s" parameter.'
|
||||
% (arg_base),
|
||||
'400 Bad Request')
|
||||
|
||||
def view_queryform(request):
|
||||
if not is_query_supported(request):
|
||||
raise debug.ViewVCException('Can not query project root "%s" at "%s".'
|
||||
% (request.rootname, request.where),
|
||||
'403 Forbidden')
|
||||
|
||||
# Do some more precise input validation.
|
||||
validate_query_args(request)
|
||||
|
||||
query_action, query_hidden_values = \
|
||||
request.get_form(view_func=view_query, params={'limit_changes': None})
|
||||
limit_changes = \
|
||||
@@ -3503,8 +3701,7 @@ def english_query(request):
|
||||
ret.append('on all branches ')
|
||||
comment = request.query_dict.get('comment', '')
|
||||
if comment:
|
||||
ret.append('with comment <i>%s</i> '
|
||||
% htmlify(comment, mangle_email_addrs=0))
|
||||
ret.append('with comment <i>%s</i> ' % htmlify(comment))
|
||||
if who:
|
||||
ret.append('by <em>%s</em> ' % request.server.escape(who))
|
||||
date = request.query_dict.get('date', 'hours')
|
||||
@@ -3692,8 +3889,9 @@ def build_commit(request, files, max_files, dir_strip, format):
|
||||
commit.log = None
|
||||
commit.short_log = None
|
||||
else:
|
||||
commit.log = htmlify(desc)
|
||||
commit.short_log = format_log(desc, cfg, format != 'rss')
|
||||
commit.log = format_log(desc, cfg, 0, format != 'rss')
|
||||
commit.short_log = format_log(desc, cfg, cfg.options.short_log_len,
|
||||
format != 'rss')
|
||||
commit.author = request.server.escape(author)
|
||||
commit.rss_date = make_rss_time_string(date, request.cfg)
|
||||
if request.roottype == 'svn':
|
||||
@@ -3740,6 +3938,9 @@ def view_query(request):
|
||||
|
||||
cfg = request.cfg
|
||||
|
||||
# Do some more precise input validation.
|
||||
validate_query_args(request)
|
||||
|
||||
# get form data
|
||||
branch = request.query_dict.get('branch', '')
|
||||
branch_match = request.query_dict.get('branch_match', 'exact')
|
||||
@@ -4074,7 +4275,7 @@ def load_config(pathname=None, server=None):
|
||||
if cfg.general.mime_types_files:
|
||||
files = cfg.general.mime_types_files[:]
|
||||
files.reverse()
|
||||
files = map(lambda x: os.path.join(os.path.dirname(pathname), x), files)
|
||||
files = map(lambda x, y=pathname: os.path.join(os.path.dirname(y), x), files)
|
||||
mimetypes.init(files)
|
||||
|
||||
debug.t_end('load-config')
|
||||
|
@@ -21,13 +21,13 @@
|
||||
<th style="width:6%;"></th>
|
||||
<th style="width:47%; vertical-align:top;">
|
||||
[is left.path right.path][else][left.path][end]
|
||||
revision [if-any left_view_href]<a href="[left_view_href]">[end][left.rev][if-any left_view_href]</a>[end],
|
||||
revision [if-any left_view_href]<a href="[left_view_href]">[end][left.rev][if-any left_view_href]</a>[end][if-any left.author] by <em>[left.author]</em>[end],
|
||||
[left.date]
|
||||
[if-any left.tag]<br />Tag: [left.tag][end]
|
||||
</th>
|
||||
<th style="width:47%; vertical-align:top;">
|
||||
[is left.path right.path][else][right.path][end]
|
||||
revision [if-any right_view_href]<a href="[right_view_href]">[end][right.rev][if-any right_view_href]</a>[end],
|
||||
revision [if-any right_view_href]<a href="[right_view_href]">[end][right.rev][if-any right_view_href]</a>[end][if-any right.author] by <em>[right.author]</em>[end],
|
||||
[right.date]
|
||||
[if-any right.tag]<br />Tag: [right.tag][end]
|
||||
</th>
|
||||
|
@@ -71,9 +71,9 @@ Revision [if-any revision_href]<a href="[revision_href]"><strong>[rev]</strong><
|
||||
[if-any image_src_href][define hide_binary_garbage]0[end][end]
|
||||
|
||||
[is hide_binary_garbage "1"]
|
||||
<p><strong>This file's contents are not viewable. Please
|
||||
<a href="[download_href]">download</a> this version of the
|
||||
file in order to view it.</strong></p>
|
||||
<p><strong>This file's contents are not viewable.
|
||||
[if-any download_href]Please <a href="[download_href]">download</a>
|
||||
this version of the file in order to view it.[end]</strong></p>
|
||||
[else]
|
||||
|
||||
[define last_rev]0[end]
|
||||
|
@@ -75,11 +75,12 @@ if sys.platform == "win32":
|
||||
## List of directories for installation.
|
||||
## type (source path,
|
||||
## destination path,
|
||||
## boolean -- optional item?,
|
||||
## boolean -- prompt before replacing?)
|
||||
TREE_LIST = [
|
||||
("lib", "lib", 0),
|
||||
("templates", "templates", 1),
|
||||
("templates-contrib", "templates-contrib", 1),
|
||||
("lib", "lib", 0, 0),
|
||||
("templates", "templates", 0, 1),
|
||||
("templates-contrib", "templates-contrib", 1, 1),
|
||||
]
|
||||
|
||||
|
||||
@@ -275,18 +276,22 @@ LEGEND
|
||||
py_compile.compile(destdir_path, destdir_path + "c" , dst_path)
|
||||
|
||||
|
||||
def install_tree(src_path, dst_path, prompt_replace):
|
||||
def install_tree(src_path, dst_path, is_optional, prompt_replace):
|
||||
"""Install a tree whose source is at SRC_PATH (which is relative
|
||||
to the ViewVC source directory) into the location DST_PATH (which
|
||||
is relative both to the global ROOT_DIR and DESTDIR settings). If
|
||||
PROMPT_REPLACE is set (and is not overridden by global setting
|
||||
CLEAN_MODE), prompt the user for how to deal with already existing
|
||||
files that differ from the to-be-installed version."""
|
||||
files that differ from the to-be-installed version. If
|
||||
IS_OPTIONAL is set, don't fuss about a missing source item."""
|
||||
|
||||
orig_src_path = src_path
|
||||
orig_dst_path = dst_path
|
||||
src_path = _actual_src_path(src_path)
|
||||
dst_path = os.path.join(ROOT_DIR, string.replace(dst_path, '/', os.sep))
|
||||
if not os.path.isdir(src_path):
|
||||
print " skipping %s" % (dst_path)
|
||||
return
|
||||
destdir_path = os.path.join(DESTDIR + dst_path)
|
||||
|
||||
# Get a list of items in the directory.
|
||||
@@ -306,7 +311,7 @@ def install_tree(src_path, dst_path, prompt_replace):
|
||||
|
||||
# If the item is a subdirectory, recurse. Otherwise, install the file.
|
||||
if os.path.isdir(os.path.join(src_path, fname)):
|
||||
install_tree(orig_src_child, orig_dst_child, prompt_replace)
|
||||
install_tree(orig_src_child, orig_dst_child, 0, prompt_replace)
|
||||
else:
|
||||
set_paths = 0
|
||||
compile_it = fname[-3:] == '.py'
|
||||
|
@@ -1,9 +0,0 @@
|
||||
# Old documents.
|
||||
RedirectPermanent /upgrading.html http://viewvc.tigris.org/nonav/source/browse/*checkout*/viewvc/trunk/docs/upgrading-howto.html
|
||||
RedirectPermanent /template-authoring-guide.html http://viewvc.tigris.org/nonav/source/browse/*checkout*/viewvc/trunk/docs/template-authoring-guide.html
|
||||
RedirectPermanent /url-reference.html http://viewvc.tigris.org/nonav/source/browse/*checkout*/viewvc/trunk/docs/url-reference.html
|
||||
|
||||
# The license-1.html file.
|
||||
RedirectPermanent /license-1.html http://viewvc.tigris.org/nonav/source/browse/*checkout*/viewvc/trunk/LICENSE.html
|
||||
|
||||
|
@@ -1,93 +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">
|
||||
<head>
|
||||
<title>ViewVC: Contact</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<link rel="stylesheet" type="text/css" href="./styles.css"/>
|
||||
<link rel="shortcut icon" href="./favicon.ico" type="image/x-icon" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="title">
|
||||
<a href="http://www.viewvc.org/"><img
|
||||
src="./images/title.jpg" alt="ViewVC: Repository Browsing"/></a>
|
||||
</div>
|
||||
|
||||
<div id="menu">
|
||||
<p><a href="./index.html">Home</a> |
|
||||
<a href="http://viewvc.tigris.org/">Project Page</a> |
|
||||
<a href="./download.html">Download</a> |
|
||||
<a href="./contributing.html">Contributing</a> |
|
||||
<a href="./faq.html">FAQ</a> |
|
||||
<a href="http://viewvc.tigris.org/nonav/source/browse/*checkout*/viewvc/trunk/LICENSE.html">License</a> |
|
||||
<a href="./contact.html">Contact</a> |
|
||||
<a href="./who.html">About</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<table id="pagetable">
|
||||
<tr>
|
||||
<td id="pagecolumn1">
|
||||
|
||||
<h4>On this page:</h4>
|
||||
|
||||
<ul id="bookmarks">
|
||||
<li><a href="#sec-contacting-us">Contacting Us</a></li>
|
||||
</ul>
|
||||
|
||||
<p><a href="http://validator.w3.org/check?uri=referer"><img
|
||||
src="http://www.w3.org/Icons/valid-xhtml10"
|
||||
alt="Valid XHTML 1.0 Strict" height="31" width="88" /></a>
|
||||
</p>
|
||||
|
||||
</td>
|
||||
<td id="pagecolumn2">
|
||||
|
||||
<div class="section">
|
||||
|
||||
<h2 id="sec-contacting-us">Contacting Us</h2>
|
||||
|
||||
<div class="section-body">
|
||||
|
||||
<p>ViewVC is an <a href="http://www.opensource.org/">Open Source</a>
|
||||
project, and all <a href="./contributing.html">contributions</a>
|
||||
are welcome. Please send any comments, questions, or suggestions
|
||||
to the <a
|
||||
href="mailto:users@viewvc.tigris.org">ViewVC
|
||||
users mailing list</a>. There is also a <a
|
||||
href="mailto:dev@viewvc.tigris.org">mailing
|
||||
list specifically for ViewVC developers</a>. You can subscribe to
|
||||
these lists (and other project lists), as well view the list
|
||||
archives, <a
|
||||
href="http://viewvc.tigris.org/servlets/ProjectMailingListList"
|
||||
>here</a>. If you are having problems with ViewVC, consider
|
||||
checking our <a href="./faq.html">Frequently Asked Questions</a>
|
||||
page and list of <a
|
||||
href="http://viewvc.tigris.org/issues/buglist.cgi?component=viewvc&issue_status=UNCONFIRMED&issue_status=NEW&issue_status=STARTED&issue_status=REOPENED"
|
||||
>open issues</a> before mail our users@ list — perhaps the
|
||||
information you need is already available in one of those
|
||||
places.</p>
|
||||
|
||||
<p>If you prefer interaction that's a little more real-time, use your
|
||||
favorite IRC client to pop into <tt><a
|
||||
href="irc://irc.freenode.net/viewvc">#viewvc</a></tt> on
|
||||
irc.freenode.net.</p>
|
||||
|
||||
<p>So, to summarize:</p>
|
||||
|
||||
<ul>
|
||||
<li>To discuss ViewVC installation, configuration, and behavior: users@viewvc.tigris.org</li>
|
||||
<li>To discuss the development of ViewVC itself or submit patches: dev@viewvc.tigris.org</li>
|
||||
<li>To discussion anything ViewVC-related in real time: irc.freenode.net, #viewvc</li>
|
||||
</ul>
|
||||
|
||||
</div> <!-- section-body -->
|
||||
</div> <!-- section -->
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
@@ -1,241 +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">
|
||||
<head>
|
||||
<title>ViewVC: Contributing</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<link rel="stylesheet" type="text/css" href="./styles.css"/>
|
||||
<link rel="shortcut icon" href="./favicon.ico" type="image/x-icon" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="title">
|
||||
<a href="http://www.viewvc.org/"><img
|
||||
src="./images/title.jpg" alt="ViewVC: Repository Browsing"/></a>
|
||||
</div>
|
||||
|
||||
<div id="menu">
|
||||
<p><a href="./index.html">Home</a> |
|
||||
<a href="http://viewvc.tigris.org/">Project Page</a> |
|
||||
<a href="./download.html">Download</a> |
|
||||
<a href="./contributing.html">Contributing</a> |
|
||||
<a href="./faq.html">FAQ</a> |
|
||||
<a href="http://viewvc.tigris.org/nonav/source/browse/*checkout*/viewvc/trunk/LICENSE.html">License</a> |
|
||||
<a href="./contact.html">Contact</a> |
|
||||
<a href="./who.html">About</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<table id="pagetable">
|
||||
<tr>
|
||||
<td id="pagecolumn1">
|
||||
|
||||
<h4>On this page:</h4>
|
||||
|
||||
<ul id="bookmarks">
|
||||
<li><a href="#sec-getting-started">Getting Started</a></li>
|
||||
<li><a href="#sec-testing">Testing and Reporting</a></li>
|
||||
<li><a href="#sec-coding-style">Coding Style</a></li>
|
||||
<li><a href="#sec-patches">Submitting Patches</a></li>
|
||||
<li><a href="#sec-security">Security</a></li>
|
||||
<li><a href="#sec-adding-features">Adding Features</a></li>
|
||||
<li><a href="#sec-templates">Hacking on Templates</a></li>
|
||||
</ul>
|
||||
|
||||
<p><a href="http://validator.w3.org/check?uri=referer"><img
|
||||
src="http://www.w3.org/Icons/valid-xhtml10"
|
||||
alt="Valid XHTML 1.0 Strict" height="31" width="88" /></a>
|
||||
</p>
|
||||
|
||||
</td>
|
||||
<td id="pagecolumn2">
|
||||
|
||||
<div class="section">
|
||||
|
||||
<h2 id="sec-getting-started">Getting Started</h2>
|
||||
|
||||
<div class="section-body">
|
||||
|
||||
<p>Some basic knowledge about <a
|
||||
href="http://www.python.org">Python</a> and development tools like
|
||||
<code>diff</code> is required. Your best bet is to start with a
|
||||
fresh source code snapshot, which you may obtain from our
|
||||
Subversion repository (see instructions <a
|
||||
href="./download.html#sec-subversion">here</a>).</p>
|
||||
|
||||
<p>Version control history can be obtained using Subversion clients,
|
||||
but is also browsable using <a
|
||||
href="http://viewvc.tigris.org/source/browse/viewvc/trunk/"
|
||||
>tigris.org's integrated ViewVC tool</a>.</p>
|
||||
|
||||
</div> <!-- section-body -->
|
||||
</div> <!-- section -->
|
||||
|
||||
<div class="section">
|
||||
|
||||
<h2 id="sec-testing">Testing and Reporting</h2>
|
||||
|
||||
<div class="section-body">
|
||||
|
||||
<p>Testing usability and the installation process on different
|
||||
platforms is also a valuable contribution. Please report your
|
||||
results back to us developers. Bandwidth is getting cheaper daily,
|
||||
so don't be afraid — in fact, feel encouraged — to dump
|
||||
as much detail about the problems you are seeing as possible into
|
||||
your bug reports. Here are some things you definitely should
|
||||
try to include:</p>
|
||||
|
||||
<ul>
|
||||
|
||||
<li>What version of ViewVC you are using (if you are using a source
|
||||
snapshot, tell us the date of that snapshot).</li>
|
||||
|
||||
<li>What operating system your ViewVC is running on.</li>
|
||||
|
||||
<li>What version of Python you are using.</li>
|
||||
|
||||
<li>Whether you are running ViewVC standalone, or as a CGI program
|
||||
under a web server (and if so, what web server).</li>
|
||||
|
||||
<li>The URL of your ViewVC instantiation, if it is public.
|
||||
Sometimes, letting developers see the problem for themselves can
|
||||
save everyone alot of time.</li>
|
||||
|
||||
</ul>
|
||||
|
||||
</div> <!-- section-body -->
|
||||
</div> <!-- section -->
|
||||
|
||||
<div class="section">
|
||||
|
||||
<h2 id="sec-coding-style">Coding Style</h2>
|
||||
|
||||
<div class="section-body">
|
||||
|
||||
<p>Unlike its predecessor, CvsWeb, ViewVC is written in Python, so it
|
||||
doesn't suffer from the "unmaintainable code effect" that hits most
|
||||
Perl projects sooner or later:</p>
|
||||
|
||||
<blockquote>
|
||||
|
||||
<p>"[Perl] combines all the worst aspects of C and Lisp: a
|
||||
billion different sublanguages in one monolithic executable. It
|
||||
combines the power of C with the readability of
|
||||
PostScript." — Jamie Zawinski
|
||||
</p>
|
||||
|
||||
</blockquote>
|
||||
|
||||
<p>Of course, a symphony of insanity can be composed in any language,
|
||||
so we do try to stick to some basic guiding principles. Maintain
|
||||
whatever style is present in the code being modified. New code can
|
||||
use anything sane (which generally means <a
|
||||
href="http://www.python.org/dev/peps/pep-0008/">PEP 8</a>).
|
||||
Our only real peeve is if someone writes a function call as:
|
||||
<code>some_func (args)</code> — that space between the
|
||||
function name and opening parenthesis is Huge Badness. Oh, and we
|
||||
do <strong>not</strong> use Subversion keywords (such as
|
||||
<code>$</code><code>Id$</code>) within the source.</p>
|
||||
|
||||
<p>Otherwise… <em>shrug</em>.</p>
|
||||
|
||||
</div> <!-- section-body -->
|
||||
</div> <!-- section -->
|
||||
|
||||
<div class="section">
|
||||
|
||||
<h2 id="sec-patches">Submitting Patches</h2>
|
||||
|
||||
<div class="section-body">
|
||||
|
||||
<p>Nothing speaks more loudly when bugs or features are the topic than
|
||||
a patch. And quite frankly, sometimes if you want something done,
|
||||
you gotta do it yourself. So, patches are always welcome. If you
|
||||
aren't sure what exactly a "patch" is, or don't know how
|
||||
to generate one, but you've got code contributions to make, please
|
||||
don't hesitate to ask questions on the mailing lists. Patch
|
||||
generation and application are pretty easy thing to get the hang of,
|
||||
and drastically simplify code submission and review.</p>
|
||||
|
||||
<p>Please use the <a
|
||||
href="http://viewvc.tigris.org/servlets/ProjectIssues">Issue
|
||||
Tracker</a> to submit your patches. Unified contextual diffs
|
||||
against the latest development snapshot are preferred.</p>
|
||||
|
||||
<p>If you have commit access, then you should know what you're doing.
|
||||
Just make changes directly. Subscribing to the <a
|
||||
href="http://viewvc.tigris.org/servlets/ProjectMailingListList"
|
||||
><tt>dev@</tt> developer mailing list</a> is recommended in any
|
||||
case.</p>
|
||||
|
||||
</div> <!-- section-body -->
|
||||
</div> <!-- section -->
|
||||
|
||||
<div class="section">
|
||||
|
||||
<h2 id="sec-security">Security</h2>
|
||||
|
||||
<div class="section-body">
|
||||
|
||||
<p>Since ViewVC is used on the Internet, security is a major concern.
|
||||
If you need to pass data from the request into an external program,
|
||||
please don't use <code>os.system()</code> or
|
||||
<code>os.popen()</code>. Please use the module
|
||||
<code>lib/popen.py</code> that is included in the ViewVC
|
||||
distribution instead.</p>
|
||||
|
||||
</div> <!-- section-body -->
|
||||
</div> <!-- section -->
|
||||
|
||||
<div class="section">
|
||||
|
||||
<h2 id="sec-adding-features">Adding Features</h2>
|
||||
|
||||
<div class="section-body">
|
||||
|
||||
<p>If you need a new configuration option think carefully, into which
|
||||
section it belongs. Try to keep the content of
|
||||
<code>cgi/viewvc.conf.dist</code> file and the library module
|
||||
<code>lib/config.py</code> in sync.</p>
|
||||
|
||||
<p>Because ViewVC is a Web-based application, people will have ViewVC
|
||||
URLs hyperlinked from other sites, embedded in emails, bookmarked
|
||||
in their browsers, etc. It is very important to ensure that those
|
||||
URLs continue to retrieve the information they were intended to
|
||||
retrieve even if ViewVC is upgraded on the hosting server. In
|
||||
other words, as new features require modifications to the <a
|
||||
href="./url-reference.html">ViewVC URL schema</a>, make sure those
|
||||
modifications preserve the existing functionality of all ViewVC
|
||||
URLs.</p>
|
||||
|
||||
<p>The library subdirectory contains a module <code>debug.py</code>,
|
||||
which you may find useful for performance testing.</p>
|
||||
|
||||
<p>If a new file or module is added, a new line in the installer
|
||||
program <code>viewvc-install</code> is required.</p>
|
||||
|
||||
</div> <!-- section-body -->
|
||||
</div> <!-- section -->
|
||||
|
||||
<div class="section">
|
||||
|
||||
<h2 id="sec-templates">Hacking on Templates</h2>
|
||||
|
||||
<div class="section-body">
|
||||
|
||||
<p>The library module <code>ezt.py</code> contains a module docstring
|
||||
which describes the directives used in the HTML templates used by
|
||||
ViewVC. The templates themselves can be found in the
|
||||
<code>templates</code> subdirectory. We're currently developing a
|
||||
how-to guide for <a href="./template-authoring-guide.html">ViewVC
|
||||
template customization</a>.</p>
|
||||
|
||||
</div> <!-- section-body -->
|
||||
</div> <!-- section -->
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
@@ -1,90 +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">
|
||||
<head>
|
||||
<title>ViewVC: Download</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<link rel="stylesheet" type="text/css" href="./styles.css"/>
|
||||
<link rel="shortcut icon" href="./favicon.ico" type="image/x-icon" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="title">
|
||||
<a href="http://www.viewvc.org/"><img
|
||||
src="./images/title.jpg" alt="ViewVC: Repository Browsing"/></a>
|
||||
</div>
|
||||
|
||||
<div id="menu">
|
||||
<p><a href="./index.html">Home</a> |
|
||||
<a href="http://viewvc.tigris.org/">Project Page</a> |
|
||||
<a href="./download.html">Download</a> |
|
||||
<a href="./contributing.html">Contributing</a> |
|
||||
<a href="./faq.html">FAQ</a> |
|
||||
<a href="http://viewvc.tigris.org/nonav/source/browse/*checkout*/viewvc/trunk/LICENSE.html">License</a> |
|
||||
<a href="./contact.html">Contact</a> |
|
||||
<a href="./who.html">About</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<table id="pagetable">
|
||||
<tr>
|
||||
<td id="pagecolumn1">
|
||||
|
||||
<h4>On this page:</h4>
|
||||
|
||||
<ul id="bookmarks">
|
||||
<li><a href="#sec-download">Downloading</a></li>
|
||||
<li><a href="#sec-subversion">Subversion</a></li>
|
||||
</ul>
|
||||
|
||||
<p><a href="http://validator.w3.org/check?uri=referer"><img
|
||||
src="http://www.w3.org/Icons/valid-xhtml10"
|
||||
alt="Valid XHTML 1.0 Strict" height="31" width="88" /></a>
|
||||
</p>
|
||||
|
||||
</td>
|
||||
<td id="pagecolumn2">
|
||||
|
||||
<div class="section">
|
||||
|
||||
<h2 id="sec-download">Downloading</h2>
|
||||
|
||||
<div class="section-body">
|
||||
|
||||
<p>You can download the latest release of ViewVC (and the older ViewVC
|
||||
and ViewCVS releases, too) from our <a
|
||||
href="http://viewvc.tigris.org/servlets/ProjectDocumentList?folderID=6004"
|
||||
>Documents & Files</a> area. For information about what has
|
||||
changed in each release, see the <a
|
||||
href="http://viewvc.tigris.org/source/browse/viewvc/trunk/CHANGES?rev=HEAD"
|
||||
>CHANGES</a> file.</p>
|
||||
|
||||
<p>We are also making <a href="./nightly/">nightly snapshots</a>
|
||||
available in tar.gz and zip formats.</p>
|
||||
|
||||
</div> <!-- section-body -->
|
||||
</div> <!-- section -->
|
||||
|
||||
<div class="section">
|
||||
|
||||
<h2 id="sec-subversion">Subversion</h2>
|
||||
|
||||
<div class="section-body">
|
||||
|
||||
<p>The source code for ViewVC is maintained in a Subversion repository
|
||||
at Tigris.org. You can checkout the trunk of our development tree
|
||||
from <a href="http://viewvc.tigris.org/svn/viewvc/trunk/"
|
||||
>http://viewvc.tigris.org/svn/viewvc/trunk/</a>. You'll
|
||||
need to provide your Tigris.org username and password when so
|
||||
prompted, or, if you don't have a Tigris.org account, use "guest"
|
||||
as the username (with no, or an empty, password).</p>
|
||||
|
||||
</div> <!-- section-body -->
|
||||
</div> <!-- section -->
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
@@ -1,359 +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">
|
||||
<head>
|
||||
<title>ViewVC: Frequently Asked Questions (FAQ)</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<link rel="stylesheet" type="text/css" href="./styles.css"/>
|
||||
<link rel="shortcut icon" href="./favicon.ico" type="image/x-icon" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="title">
|
||||
<a href="http://www.viewvc.org/"><img
|
||||
src="./images/title.jpg" alt="ViewVC: Frequently Asked Questions (FAQ)"/></a>
|
||||
</div>
|
||||
|
||||
<div id="menu">
|
||||
<p><a href="./index.html">Home</a> |
|
||||
<a href="http://viewvc.tigris.org/">Project Page</a> |
|
||||
<a href="./download.html">Download</a> |
|
||||
<a href="./contributing.html">Contributing</a> |
|
||||
<a href="./faq.html">FAQ</a> |
|
||||
<a href="http://viewvc.tigris.org/nonav/source/browse/*checkout*/viewvc/trunk/LICENSE.html">License</a> |
|
||||
<a href="./contact.html">Contact</a> |
|
||||
<a href="./who.html">About</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<table id="pagetable">
|
||||
<tr>
|
||||
<td id="pagecolumn1">
|
||||
|
||||
<h4>On this page:</h4>
|
||||
|
||||
<ul id="bookmarks">
|
||||
<li><a href="#faq-q">Questions</a></li>
|
||||
<li><a href="#faq-a">Answers</a></li>
|
||||
</ul>
|
||||
|
||||
<p><a href="http://validator.w3.org/check?uri=referer"><img
|
||||
src="http://www.w3.org/Icons/valid-xhtml10"
|
||||
alt="Valid XHTML 1.0 Strict" height="31" width="88" /></a>
|
||||
</p>
|
||||
|
||||
</td>
|
||||
<td id="pagecolumn2">
|
||||
|
||||
<div class="section">
|
||||
|
||||
<!-- ##################################################################### -->
|
||||
<h2 id="faq-q">Questions</h2>
|
||||
<!-- ##################################################################### -->
|
||||
|
||||
<div class="section-body">
|
||||
|
||||
<!-- #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# -->
|
||||
<h3 class="faq-section" id="faq-q-general">General Usage</h3>
|
||||
<!-- #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# -->
|
||||
|
||||
<ul>
|
||||
|
||||
<li><a href="#installation">Where does the installation documentation,
|
||||
if any, live?</a></li>
|
||||
|
||||
<li><a href="#authz-support">Does ViewVC support path-based
|
||||
authorization, such as Subversion's authz-file mechanism?</a></li>
|
||||
|
||||
<li><a href="#missing-tmpdir">What causes "Error: OSError: [Errno 2] No such file
|
||||
or directory: '/tmp/tmpGc-Ztj'"?</a></li>
|
||||
|
||||
<li><a href="#standalone-only">Why does <em>SOME-FEATURE</em>
|
||||
work under standalone.py, but not under Apache (or IIS or
|
||||
…)?</a></li>
|
||||
|
||||
<li><a href="#rss-support">How do I enable ViewVC's RSS feed
|
||||
capabilities?</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
<!-- #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# -->
|
||||
<h3 class="faq-section" id="faq-q-cvs">CVS Browsing</h3>
|
||||
<!-- #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# -->
|
||||
|
||||
<ul>
|
||||
|
||||
<li><a href="#rlog-output-ended-early">What causes "Error: Rlog output ended
|
||||
early. Expected RCS file "/opt/cvs/project/file,v""?</a></li>
|
||||
|
||||
<li><a href="#comalformedoutput">What causes "Error: COMalformedOutput: Unable to
|
||||
find filename in co output stream"?</a></li>
|
||||
|
||||
<li><a href="#error-during-rlog">What causes "Error: error during
|
||||
rlog: 0x100"?</a></li>
|
||||
|
||||
<li><a href="#missing-files">Why do my directories have no files in them?</a></li>
|
||||
|
||||
<li><a href="#none-match">ViewVC doesn't show files I'm looking for,
|
||||
and instead displays the message "NOTE: There are N files, but
|
||||
none match the current selection criteria". How can I fix this?</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
<!-- #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# -->
|
||||
<h3 class="faq-section" id="faq-q-svn">Subversion Browsing</h3>
|
||||
<!-- #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# -->
|
||||
|
||||
<ul>
|
||||
|
||||
<li><a href="#no-module-named-svn">What causes "Error: ImportError: No module named
|
||||
svn"?</a></li>
|
||||
|
||||
<li><a href="#remote-svn-access">Can I use ViewVC with remote
|
||||
Subversion repositories?</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
<!-- #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
|
||||
<!-- ##################################################################### -->
|
||||
<h2 id="faq-a">Answers</h2>
|
||||
<!-- ##################################################################### -->
|
||||
|
||||
<div class="section-body">
|
||||
|
||||
<!-- #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# -->
|
||||
<h3 class="faq-section" id="faq-a-general">General Usage</h3>
|
||||
<!-- #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# -->
|
||||
|
||||
<div id="installation">
|
||||
<p class="faq-atitle">Where does the installation documentation, if
|
||||
any, live?</p>
|
||||
|
||||
<p>ViewVC's installation how-to documentation lives in the INSTALL
|
||||
file located in the root of the ViewVC source code distribution.
|
||||
The most recent version of this document (which may cover
|
||||
unreleased ViewVC versions) can be found
|
||||
at <a href="http://viewvc.tigris.org/source/browse/*checkout*/viewvc/trunk/INSTALL"
|
||||
>http://viewvc.tigris.org/source/browse/*checkout*/viewvc/trunk/INSTALL</a>.
|
||||
If you are upgrading an existing ViewVC instance, you'll also want
|
||||
to read the upgrade documentation, found
|
||||
at <a href="http://viewvc.tigris.org/source/browse/*checkout*/viewvc/trunk/docs/upgrading-howto.html"
|
||||
>http://viewvc.tigris.org/source/browse/*checkout*/viewvc/trunk/docs/upgrading-howto.html</a>.</p>
|
||||
</div>
|
||||
|
||||
<div id="authz-support">
|
||||
<p class="faq-atitle">Does ViewVC support path-based authorization,
|
||||
such as Subversion's authz-file mechanism?</p>
|
||||
|
||||
<p>There are no released versions of ViewVC which have this support
|
||||
built-in. The feature request is being tracking in <a
|
||||
href="http://viewvc.tigris.org/issues/show_bug.cgi?id=268">issue
|
||||
#268</a>, and we're working to complete it for release in ViewVC
|
||||
1.1.</p>
|
||||
|
||||
<p>In the meantime, there are some available workarounds. For
|
||||
example, if you are running ViewVC under Apache, you can use
|
||||
regular Apache configuration directives to authz-protect areas of
|
||||
your repository — see the <a
|
||||
href="http://httpd.apache.org/docs/2.2/mod/core.html#location"
|
||||
>Location</a> and <a
|
||||
href="http://httpd.apache.org/docs/2.2/mod/core.html#locationmatch"
|
||||
>LocationMatch</a> directives, as well as the <a
|
||||
href="http://httpd.apache.org/docs/2.2/howto/auth.html"
|
||||
>Authentication, Authorization and Access Control</a> portions of
|
||||
the Apache HTTP Server documentation. This works best when ViewVC
|
||||
is only serving up a single repository, or when the
|
||||
<code>root_as_url_component</code> option is enabled otherwise.
|
||||
And you might have to disable the checkout view, or at least turn
|
||||
off the use of <code>checkout_magic</code> (see
|
||||
<code>viewvc.conf</code> for details).</p>
|
||||
</div>
|
||||
|
||||
<div id="missing-tmpdir">
|
||||
<p class="faq-atitle">What causes "Error: OSError: [Errno 2] No such
|
||||
file or directory: '/tmp/tmpGc-Ztj'"?</p>
|
||||
|
||||
<p>This is tracked in <a
|
||||
href="http://viewvc.tigris.org/issues/show_bug.cgi?id=282">issue
|
||||
#282</a>. While we haven't figured out how to make the error
|
||||
message more graceful or helpful, the basic problem seems to
|
||||
generally boil down to a misconfiguration of ViewVC. Make sure
|
||||
your helper applications are in the program search path
|
||||
(<code>$PATH</code>, e.g.) on your ViewVC server, or accurately
|
||||
specified in <code>viewvc.conf</code>.</p>
|
||||
</div>
|
||||
|
||||
<div id="standalone-only">
|
||||
<p class="faq-atitle">Why does <em>SOME-FEATURE</em> work under
|
||||
standalone.py, but not under Apache (or IIS or …)?</p>
|
||||
|
||||
<p>Most of the time folks run standalone.py, they do so as a regular
|
||||
system user, from a shell running with any and all the
|
||||
environmental customizations present in their shell startup
|
||||
scripts. But most web server packages (like Apache) run as a
|
||||
different, often underprivileged, user, whose environment is
|
||||
stripped down to just the system-wide default state. This can
|
||||
cause problems for ViewVC's various helper applications, which
|
||||
might reside in a location that's included as part of your (and
|
||||
thus standalone.py's) <code>$PATH</code>, but not that of the user
|
||||
as whom the web server package runs. The solution might be as
|
||||
simple explicitly configuring the paths of the helper applications
|
||||
in <code>viewvc.conf</code>, or as complex as expanding the program
|
||||
search path used by your web server software.</p>
|
||||
</div>
|
||||
|
||||
<div id="rss-support">
|
||||
<p class="faq-atitle">How do I enable ViewVC's RSS feed capabilities?</p>
|
||||
|
||||
<p>ViewVC generates its RSS feeds from repository data mirrored in a
|
||||
database as part of its MySQL integration. Follow the steps in
|
||||
ViewVC's <code>INSTALL</code> file to setup and configure the MySQL
|
||||
integration and begin mirroring your commit metadata in the
|
||||
database — the RSS feed feature will then be enabled with no
|
||||
additional configuration.</p>
|
||||
</div>
|
||||
|
||||
<!-- #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# -->
|
||||
<h3 class="faq-section" id="faq-a-cvs">CVS Browsing</h3>
|
||||
<!-- #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# -->
|
||||
|
||||
<div id="rlog-output-ended-early">
|
||||
|
||||
<p class="faq-atitle">What causes "Error: Rlog output ended
|
||||
early. Expected RCS file "/opt/cvs/project/file,v""?</p>
|
||||
|
||||
<p>This error generally occurs when ViewVC is unable to locate the
|
||||
rlog tool, one of several in the RCS toolchain. If the system
|
||||
doesn't have the RCS toolchain installed, you'll need to install
|
||||
it. If the RCS toolchain is installed, but ViewVC simply can't
|
||||
locate the RCS programs, either ensure that the programs are
|
||||
in the system <code>$PATH</code>, or specify their location in
|
||||
the <code>viewvc.conf</code> file — the option is
|
||||
<code>utilities/rcs_dir</code> as of ViewVC 1.1,
|
||||
<code>general/rcs_path</code> in earlier versions.</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="comalformedoutput">
|
||||
|
||||
<p class="faq-atitle">What causes "Error: COMalformedOutput: Unable to
|
||||
find filename in co output stream"?</p>
|
||||
|
||||
<p>This is another symptom of the basic problem described <a
|
||||
href="#rlog-output-ended-early">here</a>.</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="error-during-rlog">
|
||||
<p class="faq-atitle">What causes "Error: error during rlog:
|
||||
0x100"?</p>
|
||||
|
||||
<p>### TODO ###</p>
|
||||
</div>
|
||||
|
||||
<div id="missing-files">
|
||||
<p class="faq-atitle">Why do my directories have no files in them?</p>
|
||||
|
||||
<p>There are a few reasons why this can happen. Here are some of the
|
||||
most common ones:</p>
|
||||
|
||||
<ul>
|
||||
<li>Some folks mistakenly point ViewVC's configuration bits to their
|
||||
CVS working copies. But ViewVC isn't a working copy browser
|
||||
— it's a <em>repository</em> browser. If you don't know
|
||||
the difference, here's a tip that might help: CVS repositories
|
||||
are directories trees filled with files that end with
|
||||
"<code>,v</code>". If your directory isn't filled with "comma
|
||||
vee" files, it probably is <em>not</em> a CVS repository.</li>
|
||||
<li>… <!-- TODO --></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div id="none-match">
|
||||
<p class="faq-atitle">ViewVC doesn't show files I'm looking for, and
|
||||
instead displays the message "NOTE: There are N files, but none
|
||||
match the current selection criteria". How can I fix this?</p>
|
||||
|
||||
<p>From time to time, CVS and CVSNT add support for new keywords to
|
||||
the RCS backend used to house repository data. If you're running a
|
||||
version of ViewVC or ViewCVS that hasn't yet learned to handle
|
||||
those new keywords, you might get the error you're seeing. The
|
||||
solution is to try to get versions of CVS/CVSNT and ViewVC which
|
||||
are better aligned, which generally means upgrading ViewVC (which
|
||||
is probably less disruptive than downgrading your version control
|
||||
system).</p>
|
||||
</div>
|
||||
|
||||
<!-- #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# -->
|
||||
<h3 class="faq-section" id="faq-a-svn">Subversion Browsing</h3>
|
||||
<!-- #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# -->
|
||||
|
||||
<div id="no-module-named-svn">
|
||||
<p class="faq-atitle">What causes "Error: ImportError: No module named
|
||||
svn"?</p>
|
||||
|
||||
<p>ViewVC uses Subversion's Python bindings to interact with and pull
|
||||
information out of your Subversion repositories. These bindings
|
||||
are not, however, generally provided as part of the ViewVC
|
||||
distribution — you have to install them yourself some other
|
||||
way. (For more information, contact the <a
|
||||
href="http://subversion.tigris.org">Subversion</a> community.) The
|
||||
error you see is Python being asked to import the Subversion Python
|
||||
bindings and being unable to do so, typically because the bindings
|
||||
modules aren't found in the Python library search path.</p>
|
||||
</div>
|
||||
|
||||
<div id="remote-svn-access">
|
||||
|
||||
<p class="faq-atitle">Can I use ViewVC with remote Subversion
|
||||
repositories?</p>
|
||||
|
||||
<p>ViewVC prefers to have direct access to the repository (and in
|
||||
fact, <em>must</em> have that for CVS repositories), but there does
|
||||
exist experimental support for remote Subversion repositories.
|
||||
Enabling this is a fairly simple — just use the URL of the
|
||||
repository where you would use its path in the
|
||||
<code>svn_roots</code> configuration options (sorry, you can't use
|
||||
<code>root_parents</code> for remote repositories because
|
||||
Subversion doesn't expose a repository-listing repository access
|
||||
API).</p>
|
||||
|
||||
<p>What you should expect:</p>
|
||||
|
||||
<ul>
|
||||
<li><p>Near parity with local-access Subversion in terms of
|
||||
functionality.</p></li>
|
||||
|
||||
<li><p>Known shortcomings:</p>
|
||||
<ul>
|
||||
<li>file sizes aren't reported everywhere</li>
|
||||
<li>the "revision" view lacks/botches some information</li>
|
||||
<li>she ain't the fastest horse on the track…</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li><p>Lack of configury for dealing with the intricacies of remote
|
||||
access. If the auth credentials and such you need to access
|
||||
the remote repository aren't cached in
|
||||
<code>~VIEWVC_USER/.subversion</code> (where
|
||||
<code>VIEWVC_USER</code> is the system user as whom ViewVC
|
||||
runs), stuff won't work.</p></li>
|
||||
</ul>
|
||||
|
||||
<!-- #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div> <!-- section -->
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
Binary file not shown.
Before Width: | Height: | Size: 1.4 KiB |
Binary file not shown.
Before Width: | Height: | Size: 1.9 KiB |
Binary file not shown.
Before Width: | Height: | Size: 16 KiB |
Binary file not shown.
@@ -1,204 +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">
|
||||
<head>
|
||||
<title>ViewVC: Repository Browsing</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<link rel="stylesheet" type="text/css" href="./styles.css"/>
|
||||
<link rel="shortcut icon" href="./favicon.ico" type="image/x-icon" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="title">
|
||||
<a href="http://www.viewvc.org/"><img
|
||||
src="./images/title.jpg" alt="ViewVC: Repository Browsing"/></a>
|
||||
</div>
|
||||
|
||||
<div id="menu">
|
||||
<p><a href="./index.html">Home</a> |
|
||||
<a href="http://viewvc.tigris.org/">Project Page</a> |
|
||||
<a href="./download.html">Download</a> |
|
||||
<a href="./contributing.html">Contributing</a> |
|
||||
<a href="./faq.html">FAQ</a> |
|
||||
<a href="http://viewvc.tigris.org/nonav/source/browse/*checkout*/viewvc/trunk/LICENSE.html">License</a> |
|
||||
<a href="./contact.html">Contact</a> |
|
||||
<a href="./who.html">About</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<table id="pagetable">
|
||||
<tr>
|
||||
<td id="pagecolumn1">
|
||||
|
||||
<h4>On this page:</h4>
|
||||
|
||||
<ul id="bookmarks">
|
||||
<li><a href="#sec-what-is-viewvc">What Is ViewVC?</a></li>
|
||||
<li><a href="#sec-requirements">Requirements</a></li>
|
||||
<li><a href="#sec-future">Future Plans</a></li>
|
||||
</ul>
|
||||
|
||||
<p><a href="http://validator.w3.org/check?uri=referer"><img
|
||||
src="http://www.w3.org/Icons/valid-xhtml10"
|
||||
alt="Valid XHTML 1.0 Strict" height="31" width="88" /></a>
|
||||
</p>
|
||||
|
||||
</td>
|
||||
<td id="pagecolumn2">
|
||||
|
||||
<div class="section">
|
||||
|
||||
<h2 id="sec-what-is-viewvc">What Is ViewVC?</h2>
|
||||
|
||||
<div class="section-body">
|
||||
|
||||
<p>ViewVC is a browser interface for CVS and Subversion version
|
||||
control repositories. It generates templatized HTML to present
|
||||
navigable directory, revision, and change log listings. It can
|
||||
display specific versions of files as well as diffs between those
|
||||
versions. Basically, ViewVC provides the bulk of the report-like
|
||||
functionality you expect out of your version control tool, but much
|
||||
more prettily than the average textual command-line program
|
||||
output.</p>
|
||||
|
||||
<p>Here are some of the additional features of ViewVC:</p>
|
||||
|
||||
<ul>
|
||||
|
||||
<li>Support for filesystem-accessible CVS and Subversion repositories.</li>
|
||||
|
||||
<li>Individually configurable virtual host support.</li>
|
||||
|
||||
<li>Line-based annotation/blame display.</li>
|
||||
|
||||
<li>Revision graph capabilities (via integration with <a
|
||||
href="http://www.akhphd.au.dk/~bertho/cvsgraph/">CvsGraph</a>)
|
||||
(<em>CVS only</em>).</li>
|
||||
|
||||
<li>Syntax highlighting support.</li>
|
||||
|
||||
<li><a href="http://www.mozilla.org/projects/bonsai/">Bonsai</a>-like
|
||||
repository query facilities.</li>
|
||||
|
||||
<li>Template-driven output generation.</li>
|
||||
|
||||
<li>Colorized, side-by-side differences.</li>
|
||||
|
||||
<li>Tarball generation (by tag/branch for CVS, by revision for
|
||||
Subversion).</li>
|
||||
|
||||
<li>I18N support based on the Accept-Language request header.</li>
|
||||
|
||||
<li>Ability to run either as CGI script or as a standalone
|
||||
server.</li>
|
||||
|
||||
<li>Regexp-based file searching.</li>
|
||||
|
||||
<li>INI-like configuration file (as opposed to requiring actual code
|
||||
tweaks).</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<p>For a complete list of changes present in each release, see
|
||||
ViewVC's <a
|
||||
href="http://viewvc.tigris.org/source/browse/viewvc/trunk/CHANGES?rev=HEAD"
|
||||
>CHANGES</a> file.</p>
|
||||
|
||||
</div> <!-- section-body -->
|
||||
</div> <!-- section -->
|
||||
|
||||
<div class="section">
|
||||
|
||||
<h2 id="sec-requirements">Requirements</h2>
|
||||
|
||||
<div class="section-body">
|
||||
|
||||
<p>The only hard software requirement for running ViewVC is <a
|
||||
href="http://www.python.org/">Python 1.5.2</a> or later. All other
|
||||
requirements depend on what you want to do with the tool.</p>
|
||||
|
||||
<p>If you plan to use ViewVC with CVS repositories, you need the
|
||||
following things:</p>
|
||||
|
||||
<ul>
|
||||
|
||||
<li><a href="http://www.cs.purdue.edu/homes/trinkle/RCS/">RCS</a>
|
||||
(Revision Control System)</li>
|
||||
|
||||
<li><a href="http://www.gnu.org/software/diffutils/diffutils.html">GNU
|
||||
diff</a></li>
|
||||
|
||||
<li>Read-only, physical access to a CVS repository.</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<p>For use with Subversion repositories, you need these things:</p>
|
||||
|
||||
<ul>
|
||||
|
||||
<li><a href="http://subversion.tigris.org/">Subversion</a> 1.2 or
|
||||
later and its SWIG Python bindings.</li>
|
||||
|
||||
<li><a href="http://www.gnu.org/software/diffutils/diffutils.html">GNU
|
||||
diff</a></li>
|
||||
|
||||
<li>Physical access to a Subversion repository (though there is
|
||||
limited, use-at-your-risk support for remote access, too).</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<p>ViewVC integrates with additional pieces of software to provide
|
||||
certain bits of optional functionality:</p>
|
||||
|
||||
<ul>
|
||||
|
||||
<li><a href="http://www.mysql.com/">MySQL</a> — Needed to use
|
||||
the commit database query functionality.</li>
|
||||
|
||||
<li><a href="http://www.codento.com/people/mtr/genscript/">GNU
|
||||
enscript</a> — Needed for syntax highlighting in versioned
|
||||
file contents displays</li>
|
||||
|
||||
<li><a href="http://www.akhphd.au.dk/~bertho/cvsgraph/">CvsGraph</a>
|
||||
— Needed for version graph displays.</li>
|
||||
|
||||
<li><a href="http://httpd.apache.org/">Apache HTTP Server</a>, or
|
||||
another server capable of running CGI programs — unless
|
||||
you just want ViewVC to run in standalone server mode.</li>
|
||||
|
||||
</ul>
|
||||
|
||||
</div> <!-- section-body -->
|
||||
</div> <!-- section -->
|
||||
|
||||
<div class="section">
|
||||
|
||||
<h2 id="sec-future">Future Plans</h2>
|
||||
|
||||
<div class="section-body">
|
||||
|
||||
<p>ViewVC is an Open Source project. So any future development
|
||||
depends on the <a href="./contributing.html">contributions</a> that
|
||||
will be made by its user community. Certainly working patches have
|
||||
a greater chance to become realized quickly than feature requests,
|
||||
but please don't hesitate to submit your suggestions to our <a
|
||||
href="http://viewvc.tigris.org/servlets/ProjectIssues">issue
|
||||
tracker</a>.</p>
|
||||
|
||||
<p>Some things we're thinking about include:</p>
|
||||
|
||||
<ul>
|
||||
<li>UI streamlining/simplification.</li>
|
||||
<li>Integration with CVS and Subversion commit mail scripts.</li>
|
||||
<li>Integration with an indexer such as LXR.</li>
|
||||
</ul>
|
||||
|
||||
</div> <!-- section-body -->
|
||||
</div> <!-- section -->
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
@@ -1,219 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
import urllib
|
||||
import shutil
|
||||
import getopt
|
||||
import tempfile
|
||||
|
||||
|
||||
class BuildError(Exception):
|
||||
def __init__(self, message):
|
||||
self.message = message
|
||||
def __str__(self):
|
||||
return self.message
|
||||
|
||||
|
||||
def make_release(branch, export_dir, publish_dir, root_url, username, password):
|
||||
|
||||
# Export the requested ViewVC source tree.
|
||||
cmd = "svn export --quiet '%s/%s' %s " \
|
||||
"--username '%s' --password '%s' --non-interactive --force" \
|
||||
% (root_url, urllib.quote(branch), export_dir, username, password)
|
||||
sys.stdout.write("Running: %s\n" % (cmd))
|
||||
os.system(cmd)
|
||||
|
||||
# Get the version number from ViewVC itself.
|
||||
sys.path.insert(0, os.path.join(export_dir, 'lib'))
|
||||
try:
|
||||
import viewvc
|
||||
except ImportError:
|
||||
raise BuildError("Unable to import viewvc module; export failed?")
|
||||
version = viewvc.__version__
|
||||
del sys.modules['viewvc']
|
||||
del viewvc
|
||||
del sys.path[0]
|
||||
|
||||
# Now, use ViewVC tools to make the distribution archives.
|
||||
localtime = time.localtime()
|
||||
date = "%4d%02d%02d" % (localtime[0], localtime[1], localtime[2])
|
||||
distversion = "viewvc-%s" % (version)
|
||||
distname = "%s-%s" % (distversion, date)
|
||||
gzip_name = distname + '.tar.gz'
|
||||
zip_name = distname + '.zip'
|
||||
curdir = os.getcwd()
|
||||
try:
|
||||
os.chdir(os.path.join(export_dir, 'tools'))
|
||||
os.system('./make-release %s' % (distname))
|
||||
finally:
|
||||
os.chdir(curdir)
|
||||
|
||||
# Finally, return the locations of the archive files we've built.
|
||||
gzip_file = os.path.join(export_dir, 'tools', gzip_name)
|
||||
if not os.path.exists(gzip_file):
|
||||
gzip_file = None
|
||||
zip_file = os.path.join(export_dir, 'tools', zip_name)
|
||||
if not os.path.exists(zip_file):
|
||||
zip_file = None
|
||||
|
||||
# Remove superceded archives.
|
||||
if gzip_file or zip_file:
|
||||
dirents = os.listdir(publish_dir)
|
||||
for dirent in dirents:
|
||||
if dirent.startswith(distversion) \
|
||||
and ((dirent.endswith('.tar.gz') and gzip_file) \
|
||||
or (dirent.endswith('.zip') and zip_file)):
|
||||
os.unlink(dirent)
|
||||
|
||||
# Install the new archives.
|
||||
if gzip_file:
|
||||
os.rename(gzip_file, os.path.join(publish_dir, gzip_name))
|
||||
if zip_file:
|
||||
os.rename(zip_file, os.path.join(publish_dir, zip_name))
|
||||
|
||||
# Return our archive names.
|
||||
return gzip_file and gzip_name or None, zip_file and zip_name or None
|
||||
|
||||
|
||||
def publish_releases(branches, publish_dir, root_url, username, password):
|
||||
new_index_contents = get_html_index_header()
|
||||
for branch in branches:
|
||||
sys.stdout.write("Beginning build for branch '%s'.\n" % (branch))
|
||||
export_dir = None
|
||||
try:
|
||||
export_dir = tempfile.mkdtemp("", "viewvc-nightly-")
|
||||
gzip_file, zip_file = make_release(branch, export_dir, publish_dir,
|
||||
root_url, username, password)
|
||||
new_index_contents = new_index_contents + """
|
||||
<p>Build of %s:</p>
|
||||
<ul>
|
||||
<li><a href="%s">%s</a></li>
|
||||
<li><a href="%s">%s</a></li>
|
||||
</ul>
|
||||
""" % (branch, urllib.quote(gzip_file), gzip_file,
|
||||
urllib.quote(zip_file), zip_file)
|
||||
finally:
|
||||
if export_dir:
|
||||
sys.stdout.write("Removing temporary directory '%s'.\n" % (export_dir))
|
||||
shutil.rmtree(export_dir)
|
||||
sys.stdout.write("Finished build for branch '%s'.\n" % (branch))
|
||||
|
||||
new_index_contents = new_index_contents + get_html_index_footer()
|
||||
open(os.path.join(publish_dir, 'index.html'), 'w').write(new_index_contents)
|
||||
|
||||
|
||||
def get_html_index_header():
|
||||
return """
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>ViewVC: Nightly Snapshots</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<link rel="stylesheet" type="text/css" href="../styles.css"/>
|
||||
<link rel="shortcut icon" href="../favicon.ico" type="image/x-icon" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="title">
|
||||
<a href="http://www.viewvc.org/"><img
|
||||
src="../images/title.jpg" alt="ViewVC: Repository Browsing"/></a>
|
||||
</div>
|
||||
|
||||
<div id="menu">
|
||||
<p><a href="../index.html">Home</a> |
|
||||
<a href="http://viewvc.tigris.org/">Project Page</a> |
|
||||
<a href="../download.html">Download</a> |
|
||||
<a href="../contributing.html">Contributing</a> |
|
||||
<a href="./faq.html">FAQ</a> |
|
||||
<a href="http://viewvc.tigris.org/nonav/source/browse/*checkout*/viewvc/trunk/LICENSE.html">License</a> |
|
||||
<a href="../contact.html">Contact</a> |
|
||||
<a href="../who.html">About</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<table id="pagetable">
|
||||
<tr>
|
||||
<td id="pagecolumn1">
|
||||
|
||||
<h4>On this page:</h4>
|
||||
|
||||
<ul id="bookmarks">
|
||||
<li><a href="%s">Snapshots</a></li>
|
||||
</ul>
|
||||
|
||||
<p><a href="http://validator.w3.org/check?uri=referer"><img
|
||||
src="http://www.w3.org/Icons/valid-xhtml10"
|
||||
alt="Valid XHTML 1.0 Strict" height="31" width="88" /></a>
|
||||
</p>
|
||||
|
||||
</td>
|
||||
<td id="pagecolumn2">
|
||||
|
||||
<div class="section">
|
||||
<h2 id="sec-snapshots">Snapshots</h2>
|
||||
<div class="section-body">
|
||||
""" % ("#sec-snapshots")
|
||||
|
||||
|
||||
def get_html_index_footer():
|
||||
return """
|
||||
</div> <!-- section-body -->
|
||||
</div> <!-- section -->
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
|
||||
def usage_and_exit(errmsg=None):
|
||||
stream = errmsg and sys.stderr or sys.stdout
|
||||
progname = os.path.basename(sys.argv[0])
|
||||
stream.write("""%s -- nightly ViewVC build generation
|
||||
|
||||
Usage: %s [OPTIONS] PUBLISH-DIR BRANCH ...
|
||||
|
||||
Build an archive from one or more BRANCH in the ViewVC source
|
||||
repository, dropping them into PUBLISH-DIR alongside an index.html
|
||||
file that name and links to them.
|
||||
|
||||
Options:
|
||||
--help (-h, -?) : Show this usage message
|
||||
--username : Username used for source export
|
||||
--password : Password used for source export
|
||||
--root-url : Alternative source repository root URL
|
||||
|
||||
""" % (progname, progname))
|
||||
if errmsg:
|
||||
stream.write("ERROR: %s\n" % (errmsg))
|
||||
sys.exit(errmsg and 1 or 0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
root_url = "http://viewvc.tigris.org/svn/viewvc"
|
||||
username = password = ""
|
||||
opts, args = getopt.getopt(sys.argv[1:], 'h?',
|
||||
['help', 'username=', 'password=', 'root-url='])
|
||||
for name, value in opts:
|
||||
if name == '-h' or name == '-?' or name == '--help':
|
||||
usage_and_exit()
|
||||
elif name == '--username':
|
||||
username = value
|
||||
elif name == '--password':
|
||||
password = value
|
||||
elif name == '--root-url':
|
||||
root_url = value
|
||||
argc = len(args)
|
||||
if argc < 2:
|
||||
usage_and_exit("Not enough arguments")
|
||||
try:
|
||||
publish_releases(args[1:], args[0], root_url, username, password)
|
||||
except BuildError, e:
|
||||
sys.stderr.write(str(e) + "\n")
|
||||
sys.exit(1)
|
@@ -1,169 +0,0 @@
|
||||
body {
|
||||
background-color: #b2d388;
|
||||
color: black;
|
||||
font-family: trebuchet ms, arial, sans-serif;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#title {
|
||||
background-color: #739c3e;
|
||||
height: 70px;
|
||||
}
|
||||
|
||||
img {
|
||||
border: none;
|
||||
}
|
||||
|
||||
hr {
|
||||
width: 95%;
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
address {
|
||||
font-size: 60%;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
h2 {
|
||||
background-color: #94bd5e;
|
||||
padding: 2px 0.5em 2px 0.5em;
|
||||
margin: 0;
|
||||
border-bottom: dotted 1px rgb(180,193,205);
|
||||
font-size: 110%;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin: 0;
|
||||
font-size: 100%;
|
||||
font-weight: bold;
|
||||
border-bottom: dotted 1px rgb(180,193,205);
|
||||
}
|
||||
|
||||
h4 {
|
||||
padding: 2px 0.5em 2px 0.5em;
|
||||
margin: 0;
|
||||
font-size: 90%;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
p, dl {
|
||||
padding: 0.5em;
|
||||
margin: 0;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
ul, ol {
|
||||
font-size: 90%;
|
||||
padding: 0.5em;
|
||||
margin: 0 0 0 0.25in;
|
||||
}
|
||||
|
||||
code {
|
||||
font-size: 105%;
|
||||
font-style: italic;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
text-align: justify;
|
||||
font-style: italic;
|
||||
margin: 0 0.5in 0 0.5in;
|
||||
padding: 5px;
|
||||
border: 1px dotted black;
|
||||
}
|
||||
|
||||
.section {
|
||||
background: white;
|
||||
border-color: rgb(24,24,24);
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
padding: 0;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
.section-body {
|
||||
padding: 0 1em;
|
||||
}
|
||||
|
||||
.section p, .section dl, .section ul, .section ol {
|
||||
padding: 0.5em 0;
|
||||
}
|
||||
|
||||
#menu {
|
||||
background: black;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#menu a, #menu a:visited {
|
||||
background: black;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#menu a:hover {
|
||||
background: black;
|
||||
color: white;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
#submenu {
|
||||
background: rgb(90%,97%,99%);
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
#pagetable tr {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
#pagetable {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#pagecolumn1 {
|
||||
width: 175px;
|
||||
padding: 1em 0 0 0;
|
||||
}
|
||||
|
||||
#pagecolumn1 a, #pagecolumn1 a:visited {
|
||||
color: rgb(0,0,164);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#pagecolumn1 a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
#pagecolumn2 {
|
||||
padding: 1em 2em;
|
||||
}
|
||||
|
||||
#pagecolumn2 a, #pagecolumn2 a:visited {
|
||||
color: rgb(0,0,164);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
#pagecolumn2 a:hover {
|
||||
background-color: rgb(180,193,205);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
#bookmarks {
|
||||
padding-left: 0.5em;
|
||||
font-size: 80%;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.notice {
|
||||
border: 1px solid black;
|
||||
margin-bottom: 0.5em;
|
||||
background: yellow;
|
||||
}
|
||||
|
||||
.faq-section {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
.faq-atitle {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
@@ -1,181 +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">
|
||||
<head>
|
||||
<title>ViewVC: About</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<link rel="stylesheet" type="text/css" href="./styles.css"/>
|
||||
<link rel="shortcut icon" href="./favicon.ico" type="image/x-icon" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="title">
|
||||
<a href="http://www.viewvc.org/"><img
|
||||
src="./images/title.jpg" alt="ViewVC: Repository Browsing"/></a>
|
||||
</div>
|
||||
|
||||
<div id="menu">
|
||||
<p><a href="./index.html">Home</a> |
|
||||
<a href="http://viewvc.tigris.org/">Project Page</a> |
|
||||
<a href="./download.html">Download</a> |
|
||||
<a href="./contributing.html">Contributing</a> |
|
||||
<a href="./faq.html">FAQ</a> |
|
||||
<a href="http://viewvc.tigris.org/nonav/source/browse/*checkout*/viewvc/trunk/LICENSE.html">License</a> |
|
||||
<a href="./contact.html">Contact</a> |
|
||||
<a href="./who.html">About</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<table id="pagetable">
|
||||
<tr>
|
||||
<td id="pagecolumn1">
|
||||
|
||||
<h4>On this page:</h4>
|
||||
|
||||
<ul id="bookmarks">
|
||||
<li><a href="#sec-history">The History of ViewVC</a></li>
|
||||
<li><a href="#sec-viewcvs-group">The ViewCVS Group</a></li>
|
||||
<li><a href="#sec-site-credits">About This Site</a></li>
|
||||
</ul>
|
||||
|
||||
<p><a href="http://validator.w3.org/check?uri=referer"><img
|
||||
src="http://www.w3.org/Icons/valid-xhtml10"
|
||||
alt="Valid XHTML 1.0 Strict" height="31" width="88" /></a>
|
||||
</p>
|
||||
|
||||
</td>
|
||||
<td id="pagecolumn2">
|
||||
|
||||
<div class="section">
|
||||
|
||||
<h2 id="sec-history">The History of ViewVC</h2>
|
||||
|
||||
<div class="section-body">
|
||||
|
||||
<p>The ViewVC software was inspired by early versions of cvsweb
|
||||
(originally written by Bill Fenner, further developed by Henner
|
||||
Zeller, and now maintained as <a
|
||||
href="http://www.freebsd.org/projects/cvsweb.html">CVSweb</a> by
|
||||
the FreeBSD community). Greg Stein wanted to make some changes and
|
||||
updates, but cvsweb was implemented in Perl. He wrote:</p>
|
||||
|
||||
<blockquote>
|
||||
|
||||
<p>"While I can manage some Perl, cvsweb was rather
|
||||
unmaintainable for me. So I undertook the task to convert the
|
||||
software to Python. As a result, I've actually been able to go
|
||||
<strong>way</strong> beyond the simple changes that I had
|
||||
envisioned."
|
||||
</p>
|
||||
|
||||
</blockquote>
|
||||
|
||||
<p>So ViewVC started out as just a port of the cvsweb script,
|
||||
originally called ViewCVS. Along the way, it has had numerous
|
||||
cleanups and other modifications, a process simplified by the
|
||||
elegance of the <a href="http://www.python.org/">Python</a>
|
||||
language.</p>
|
||||
|
||||
<p>In 2001, the ViewCVS project was moved to <a
|
||||
href="http://www.sourceforge.net">SourceForge</a>, a popular
|
||||
software collaboration environment. There the project continued to
|
||||
mature, releasing several stable-yet-pre-1.0 versions. In 2002,
|
||||
C. Michael Pilato began implementing support for Subversion in
|
||||
ViewCVS, taking advantage of a version control abstraction layer
|
||||
begun by Lucas Bruand. Along the way, Russell Yanofsky delivered
|
||||
large improvements to that abstraction, and to ViewCVS as whole.
|
||||
ViewCVS was well on its way to releasing a 1.0 version.</p>
|
||||
|
||||
<p>Of course, now that ViewCVS could browse Subversion repositories as
|
||||
easily as CVS ones, the ViewCVS name seemed inappropriate. Also,
|
||||
the active ViewCVS developers at the time were growing frustrated
|
||||
with SourceForge as a project hosting environment —
|
||||
Subversion wasn't yet available as an option for version control,
|
||||
the CVS service was always down at just the wrong moment, the bug
|
||||
trackers were painful to use, and so on. So in late 2005, the
|
||||
decision was made to rename the project to ViewVC, to convert the
|
||||
project's CVS data to Subversion, and to move the project and its
|
||||
version controlled and open issue data to <a
|
||||
href="http://www.tigris.org">Tigris.org</a>.</p>
|
||||
|
||||
<p>Today, ViewVC is being developed at <a
|
||||
href="http://viewvc.tigris.org">http://viewvc.tigris.org</a> by a
|
||||
small community of folks.</p>
|
||||
|
||||
</div> <!-- section-body -->
|
||||
</div> <!-- section -->
|
||||
|
||||
<div class="section">
|
||||
|
||||
<h2 id="sec-viewcvs-group">The ViewCVS Group</h2>
|
||||
|
||||
<div class="section-body">
|
||||
|
||||
<p>The ViewCVS Group is an informal group of people working on and
|
||||
developing the ViewVC package. The current set of members are
|
||||
listed below with some of their notable contributions:</p>
|
||||
|
||||
<dl>
|
||||
<dt><a href="http://www.lyra.org/greg/">Greg Stein</a></dt>
|
||||
<dd>original python port of Henner Zeller's cvsweb, secure popen
|
||||
implementation, configuration file implementation, rcsparse
|
||||
module, and EZT template engine</dd>
|
||||
|
||||
<dt>Jay Painter</dt>
|
||||
<dd>CVSdb query engine</dd>
|
||||
|
||||
<dt>Tanaka Akira</dt>
|
||||
<dd>enscript colorization and tarball generation</dd>
|
||||
|
||||
<dt>Tim Cera</dt>
|
||||
<dd>CvsGraph support, log_table template, regular expression search,
|
||||
and paging capability</dd>
|
||||
|
||||
<dt>Peter Funk</dt>
|
||||
<dd>standalone server, blimp logo, and numerous improvements to
|
||||
ViewVC's interfaces and documentation</dd>
|
||||
|
||||
<dt>Lucas Bruand</dt>
|
||||
<dd>C++ RCS parser (tparse) and vclib module for supporting new
|
||||
version control systems</dd>
|
||||
|
||||
<dt><a href="http://www.cmichaelpilato.com/">C. Michael Pilato</a></dt>
|
||||
<dd>Subversion support, root_as_url alternative URL scheme,
|
||||
templatization work, website design, documentation</dd>
|
||||
|
||||
<dt>Russell Yanofsky</dt>
|
||||
<dd>Windows support and the sapi module for supporting multiple web
|
||||
server interfaces, sweeping abstraction and UI improvements</dd>
|
||||
|
||||
<dt>James Henstridge</dt>
|
||||
<dd>integrated query interface, support for querying Subversion
|
||||
repositories, caching support, CSS formatting, and the EZT
|
||||
"define" directive</dd>
|
||||
|
||||
</dl>
|
||||
|
||||
</div> <!-- section-body -->
|
||||
</div> <!-- section -->
|
||||
|
||||
<div class="section">
|
||||
|
||||
<h2 id="sec-site-credits">About This Site</h2>
|
||||
|
||||
<div class="section-body">
|
||||
|
||||
<p>The ViewVC website was designed by <a
|
||||
href="http://www.cmichaelpilato.com/">C. Michael Pilato</a>. All
|
||||
HTML was hand-edited in Emacs, and the little splashes of graphical
|
||||
goodness owe their existence to Open Office and the Gimp. Textual
|
||||
content for the site is mostly the work of Greg Stein, but has been
|
||||
tweaked through the ages by various ViewVC contributors.</p>
|
||||
|
||||
</div> <!-- section-body -->
|
||||
</div> <!-- section -->
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
Reference in New Issue
Block a user