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

Compare commits

..

32 Commits
1.1.2 ... 1.1.3

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

git-svn-id: http://viewvc.tigris.org/svn/viewvc/tags/1.1.3@2451 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-09-08 16:51:21 +00:00
cmpilato
a6c01c2f5b Tag the 1.1.3 final release.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/tags/1.1.3@2311 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-12-22 19:54:14 +00:00
cmpilato
d4ac97de3c Commit to a release date of today for 1.1.3. Merry Christmas.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2310 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-12-22 19:52:47 +00:00
cmpilato
3039b1f43b Merge from trunk r2308, whose log message was somethin' like this:
* conf/viewvc.conf.dist
     (docroot): Add admonishment about how this interplays with per-root
       configuration of the 'template_dir' option.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2309 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-12-08 17:13:53 +00:00
cmpilato
13597f89cd Merge from trunk r2306, whoc log message read as such:
* conf/viewvc.conf.dist
     Try to consistify the way our admonishments appear herein.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2307 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-12-08 17:06:42 +00:00
cmpilato
870ae9aecc Merge r2303 from trunk (or, the deletion half of the viewvc.org directory
move, at least).

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2304 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-12-04 21:52:11 +00:00
cmpilato
c94b0709ec Merge from trunk r2300, whose log message read thusly:
Some security-related improvements/fixes.
   
   Make per-root authorizer-related configuration work so that admins can
   feel comfortable exposing their root listing views again.
   
   Also, make the query interface require that queried roots be
   configured (explicitly or implicitly) to use either the 'forbidden'
   authorizer or none at all.  This is a security fix, since
   administrators might reasonably have thought that if they configured a
   root to use another authorizer, the query interface would have honored
   that configuration.
   
   * lib/config.py
     (Config.__init__): Now track whether root options have been overlayed.
     (Config.overlay_root_options): Assert that no root options have been
       overlayed (and then noted that they now have).
     (Config.get_authorizer_and_params_hack): New function to workaround
       our inability to un-overlay root-specific options.  It's only the
       query interface and root listing views that really need to act on
       multiple roots at once, and really only the authorization options
       that matter there anyway.
     (Config.get_authorizer_params): Remove per-root overlay hacks from
       this function.
   
   * lib/viewvc.py
     (Request.run_viewvc): Update call to setup_authorizer() (to not
       specify the rootname).
     (setup_authorizer): Make the rootname parameter option, as a flag
       for whether to consult the current configuration or instead use
       the hack which manually digs around for per-root overrides of
       authorizer stuffs.
   
   * lib/query.py
     (is_forbidden): Don't try to apply the 'forbidden' authorizer where
       some other (or no) authorizer has been configured for a given
       root.  But do complain if another authorizer has been configured,
       rather than simply letting stuff leak through that an
       administrator might reasonably expect to have been hidden.

Also:

* CHANGES
  Note these changes.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2301 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-12-03 06:09:25 +00:00
cmpilato
5632e63bcb * CHANGES
Minor typo fix.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2299 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-12-03 05:59:37 +00:00
cmpilato
ae7b1103f6 Merge from trunk r2297, whose log message read thusly:
* lib/query.py
     (run_query): Expand roots so we're playing with a full deck.

Also:

* CHANGES
  Record this change.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2298 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-12-03 05:59:15 +00:00
cmpilato
2882178be7 Merge from trunk r2291 and r2292, which can be summarized like so:
* conf/viewvc.conf.dist,
   * lib/config.py
     Take a crack at revamping the configuration subsystem to allow
     for a clear understanding of per-vhost and per-root configuration
     overrides.  This should resolve issue #411 (Can't override
     authz-* sections in virtual host).

Also:

* CHANGES
  Note this change.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2296 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-12-02 20:35:21 +00:00
cmpilato
bfc59256ca Merge from trunk r2293, whose log message read like so:
Fix issue #442: Standalone server 1.1.2 fails to start under Python 1.5.2
   
   * lib/viewvc.py
     (load_config): Tweak lambda usage for 1.5.2-compliance.

   Patch by:  Dmitry Bulgakov <dvb{_AT_}tigris.org>

Also:

* CHANGES
  Note this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2294 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-12-02 16:04:27 +00:00
cmpilato
3abe695c3c Merge from trunk r2288, whose log message read like so:
* lib/viewvc.py
     (common_template_data): Default 'roots_href' to None, and only
       populate it if the roots listing is one of the configured allowed
       views.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2289 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-12-01 15:01:34 +00:00
cmpilato
624dfdf0f8 Merge from trunk r2286, whose log message read thusly:
Finish issue #440 (Database query by glob/exact match fails).
   
   Defer validation of certain query-view-related input whose format is
   unknown at the time of validation.  That includes all the form fields
   whose interpretation (as an exact match, a blog, a regular expression,
   etc.) changes based on the another field's value.  Instead, do that
   validation in the view funcs themselves.
   
   * lib/viewvc.py
     (_legal_params): Set the validator for the 'branch', 'file', 'who',
       and 'comment' parameters to None.
     (validate_query_args): New function for more careful query arg validation.
     (view_queryform, view_query): Call validate_query_args().

Also:

* CHANGES
  Add "#440" to the list of bugs related to this one problem.  Sheesh.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2287 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-12-01 14:56:21 +00:00
cmpilato
28bf22f279 Merge from trunk r2281, whose log message read like so:
Fix issue #438, a regression caused by a botched function signature
   change in r2244.
   
   * lib/viewvc.py
     (view_checkout, view_cvsgraph_image, view_doc, view_patch): Fix
       calls to copy_stream().


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2282 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-11-10 22:18:41 +00:00
cmpilato
830a48f88c Merge from trunk r2279, whose log message read like so:
Make ViewVC cross copies in Subversion history by default.
   
   * lib/config.py
     (Config.set_defaults): Make 'options.cross_copies' default to 1.  At
       a minimum that makes it jive with the commented out default value
       shown in viewvc.conf.dist, but I believe that it's also the better
       of the two ways to resolve that disparity in terms of user experience.
   
   Noticed by: Tom Throckmorton <throck{_AT_}gmail.com>

Also:

* CHANGES
  Note this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2280 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-11-05 16:36:29 +00:00
cmpilato
a23543d763 Merge from trunk r2277, whose log message read like so:
For issue #434, mark up URLs and email addresses in item property
   values as we do for revision property values.
   
   * lib/viewvc.py
     (get_itemprops): Pass property values through format_log().
   
   Patch by: Senthil Kumaran S <senthil@collab.net>

Also:

* CHANGES
  Note this change.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2278 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-10-25 22:19:24 +00:00
cmpilato
586a183d5c Merge from trunk r2275, whose log message read:
Finish issue #431 - make revision metadata available in diff view.

   * lib/viewvc.py
     (view_diff): Use repos.itemlog() to fetch revision metadata about
     the left- and right-hand diff objects and expose 'author', 'log',
     'ago', and 'size' to the templates.
   
   * docs/template-authoring-guide.html
     Document the newly added template data dictionary items.
   
   * templates/diff.ezt
     Display the revision authors in the diff view.

Also:

* CHANGES
  Note this change.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2276 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-10-25 20:57:16 +00:00
cmpilato
7ae02a5887 Merge from trunk r2273, whose log message read like so:
Fix a bug that caused log message in the query view to be truncated to
   a single character.
   
   * lib/viewvc.py
     (build_commit): Fix calls to format_log().  It turns out that
       parameter ordering is, you know, somewhat important.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2274 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-10-08 17:54:08 +00:00
cmpilato
4020fd2e5f Merge from trunk r2269 and r2270, which contain some INSTALL file
reformatting/clarification tweaks.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2272 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-10-08 17:46:57 +00:00
cmpilato
af871d59ce Tweak change to note additional issues.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2267 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-09-09 19:10:50 +00:00
cmpilato
b8d36c8c14 Merge from trunk r2265 whose log message read:
Fix issue #427 - regex filtering broken by bogus validator function.
   
   * lib/viewvc.py
     (_validate_regex): Try compiling the incoming regex, returning True
       on success and None otherwise.  (Simply 'pass'ing caused all
       regexs to fail validation.)
   
   Patch by: Robert Fleming <flemingr{_AT_}tigris.org>

Also:

* CHANGES
  Note this change.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2266 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-09-09 18:58:34 +00:00
cmpilato
4b5721a3d7 Merge from trunk r2263 (a typo fix in a comment).
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2264 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-09-09 16:03:50 +00:00
cmpilato
10235b8fdf Merge from trunk r2261, whose log message read like so:
* conf/viewvc.conf.dist
     (utilities): Clarify what we mean by "the directories in which
       certain programs live".
   
   Suggested by: Eric McCarty <emccarty{_AT_}ostendo.com>


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2262 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-09-09 15:58:54 +00:00
cmpilato
45332f577a Merge from trunk r2259, whose log message read:
* templates/file.ezt
     Don't point folks to the checkout view if it's disabled.

Also:

* CHANGES
  Note (generically) this change.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2260 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-09-09 14:34:55 +00:00
cmpilato
148fd7b462 Merge from trunk r2257, whose log message read like so:
* lib/viewvc.py
     (HtmlFormatter._tokenize_text): Give preference to the longer of
       overlapping matches that begin at the same point.  Add some
       comments that better explain what we're doing.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2258 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-08-31 15:04:01 +00:00
cmpilato
6f66313b51 Merge from trunk r2255, which has some HtmlFormatter improvements.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2256 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-08-27 15:18:20 +00:00
cmpilato
cf32af4816 Merge from trunk r2252, whose log read:
Make the installing of the 'templates-contrib' tree optional.
   
   * viewvc-install
     (TREE_LIST): Add a new field -- a boolean "is-optional" flag.
     (install_tree): New parameter 'is_optional', used to avoid whining when
       something in the source tree is missing.

Also:

* CHANGES
  Note this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2254 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-08-25 15:30:31 +00:00
cmpilato
df3addacdb Merge from trunk r2251, whose log message read:
* lib/viewvc.py
     (view_diff): Tweak some logic ordering, and ensure that 'changes' is
       initialized as a sequence (instead of None) as some template logic
       paths seem to care.

Also:

* CHANGES
  Note this change.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2253 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-08-25 15:28:12 +00:00
cmpilato
a1428600b7 Merge from trunk r2249, whose log message read thusly:
Add configurable support for tabstop-to-space translation.
   
   * conf/viewvc.conf.dist
     (options.tabsize): New.
   
   * lib/config.py
     (Config.set_defaults): Set default value for the 'tabsize' option.
   
   * lib/viewvc.py
     (markup_stream_pygments): Pass the 'tabsize' parameter to
       Pygments' lexers.  When not using pygments, use 'string.expandtabs'
       to do tab-to-space conversion (possibly with a new object that wraps
       BlameSource objects solely for that purpose).
     (DiffSource._format_text): Honor the tabsize configuration options when
       expanding tabs to spaces.

Also:

* CHANGES
  Note this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2250 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-08-24 15:11:05 +00:00
cmpilato
4a4a3b1d61 Merge from trunk r2247, whose log message read:
Follow-up to r2244 and r2245, supplying a way for the HtmlFormatter to
   indicate that truncation happened (so callers can tack on an ellipsis
   or somesuch).
   
   * lib/viewvc.py
     (HtmlFormatter.format_email_truncated): Fix logic to use ellipsis
       for mangling but not for truncation.  (Also fix an off-by-one
       error in the logic.)
     (HtmlFormatter.get_result): Now return the result string and a
       "was-truncated" flag.
     (format_log): Update call to HtmlFormatter.get_result, and tack on
      an ellipsis if the log was truncated.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2248 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-08-21 15:59:02 +00:00
cmpilato
8c65f1b2c5 Merge from trunk r2244 and r2245, which fixes issue #3 (where truncation of
log messages for display purposes caused marked-up URLs to also be truncated),
and which paves the way for custom bug/issue ID linkification.

* lib/viewvc.py
  See r2244 and r2245.

Also:

* CHANGES
  Note this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2246 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-08-21 15:11:56 +00:00
cmpilato
55046f2261 Begin a new release cycle.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2236 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-08-11 13:09:49 +00:00
23 changed files with 716 additions and 1772 deletions

17
CHANGES
View File

@@ -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
View File

@@ -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

View File

@@ -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":

View File

@@ -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>

View File

@@ -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"

View File

@@ -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

View File

@@ -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%.~:_/]+)((\?|\&amp;)'
'(://[-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&#64;&hellip;', 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) + '&#64;&hellip;', s_len + 2
elif s_len < maxlen:
return self._entity_encode(s) + '&#64;', 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 '&hellip;' 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')

View File

@@ -21,13 +21,13 @@
<th style="width:6%;"></th>
<th style="width:47%; vertical-align:top;">
[is left.path right.path][else][left.path][end]
revision [if-any left_view_href]<a href="[left_view_href]">[end][left.rev][if-any left_view_href]</a>[end],
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>

View File

@@ -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]

View File

@@ -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'

View File

@@ -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

View File

@@ -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:&#117;&#115;&#101;&#114;&#115;&#64;&#118;&#105;&#101;&#119;&#118;&#99;&#46;&#116;&#105;&#103;&#114;&#105;&#115;&#46;&#111;&#114;&#103;">ViewVC
users mailing list</a>. There is also a <a
href="mailto:&#100;&#101;&#118;&#64;&#118;&#105;&#101;&#119;&#118;&#99;&#46;&#116;&#105;&#103;&#114;&#105;&#115;&#46;&#111;&#114;&#103;">mailing
list specifically for ViewVC developers</a>. You can subscribe to
these lists (and other project lists), as well view the list
archives, <a
href="http://viewvc.tigris.org/servlets/ProjectMailingListList"
>here</a>. If you are having problems with ViewVC, consider
checking our <a href="./faq.html">Frequently Asked Questions</a>
page and list of <a
href="http://viewvc.tigris.org/issues/buglist.cgi?component=viewvc&amp;issue_status=UNCONFIRMED&amp;issue_status=NEW&amp;issue_status=STARTED&amp;issue_status=REOPENED"
>open issues</a> before mail our users@ list &mdash; perhaps the
information you need is already available in one of those
places.</p>
<p>If you prefer interaction that's a little more real-time, use your
favorite IRC client to pop into <tt><a
href="irc://irc.freenode.net/viewvc">#viewvc</a></tt> on
irc.freenode.net.</p>
<p>So, to summarize:</p>
<ul>
<li>To discuss ViewVC installation, configuration, and behavior: &#117;&#115;&#101;&#114;&#115;&#64;&#118;&#105;&#101;&#119;&#118;&#99;&#46;&#116;&#105;&#103;&#114;&#105;&#115;&#46;&#111;&#114;&#103;</li>
<li>To discuss the development of ViewVC itself or submit patches: &#100;&#101;&#118;&#64;&#118;&#105;&#101;&#119;&#118;&#99;&#46;&#116;&#105;&#103;&#114;&#105;&#115;&#46;&#111;&#114;&#103;</li>
<li>To discussion anything ViewVC-related in real time: irc.freenode.net, #viewvc</li>
</ul>
</div> <!-- section-body -->
</div> <!-- section -->
</td>
</tr>
</table>
</body>
</html>

View File

@@ -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 &mdash; in fact, feel encouraged &mdash; to dump
as much detail about the problems you are seeing as possible into
your bug reports. Here are some things you definitely should
try to include:</p>
<ul>
<li>What version of ViewVC you are using (if you are using a source
snapshot, tell us the date of that snapshot).</li>
<li>What operating system your ViewVC is running on.</li>
<li>What version of Python you are using.</li>
<li>Whether you are running ViewVC standalone, or as a CGI program
under a web server (and if so, what web server).</li>
<li>The URL of your ViewVC instantiation, if it is public.
Sometimes, letting developers see the problem for themselves can
save everyone alot of time.</li>
</ul>
</div> <!-- section-body -->
</div> <!-- section -->
<div class="section">
<h2 id="sec-coding-style">Coding Style</h2>
<div class="section-body">
<p>Unlike its predecessor, CvsWeb, ViewVC is written in Python, so it
doesn't suffer from the "unmaintainable code effect" that hits most
Perl projects sooner or later:</p>
<blockquote>
<p>&quot;[Perl] combines all the worst aspects of C and Lisp: a
billion different sublanguages in one monolithic executable. It
combines the power of C with the readability of
PostScript.&quot; &mdash;&nbsp;Jamie&nbsp;Zawinski
</p>
</blockquote>
<p>Of course, a symphony of insanity can be composed in any language,
so we do try to stick to some basic guiding principles. Maintain
whatever style is present in the code being modified. New code can
use anything sane (which generally means <a
href="http://www.python.org/dev/peps/pep-0008/">PEP&nbsp;8</a>).
Our only real peeve is if someone writes a function call as:
<code>some_func&nbsp;(args)</code> &mdash; that space between the
function name and opening parenthesis is Huge Badness. Oh, and we
do <strong>not</strong> use Subversion keywords (such as
<code>$</code><code>Id$</code>) within the source.</p>
<p>Otherwise&hellip; <em>shrug</em>.</p>
</div> <!-- section-body -->
</div> <!-- section -->
<div class="section">
<h2 id="sec-patches">Submitting Patches</h2>
<div class="section-body">
<p>Nothing speaks more loudly when bugs or features are the topic than
a patch. And quite frankly, sometimes if you want something done,
you gotta do it yourself. So, patches are always welcome. If you
aren't sure what exactly a &quot;patch&quot; is, or don't know how
to generate one, but you've got code contributions to make, please
don't hesitate to ask questions on the mailing lists. Patch
generation and application are pretty easy thing to get the hang of,
and drastically simplify code submission and review.</p>
<p>Please use the <a
href="http://viewvc.tigris.org/servlets/ProjectIssues">Issue
Tracker</a> to submit your patches. Unified contextual diffs
against the latest development snapshot are preferred.</p>
<p>If you have commit access, then you should know what you're doing.
Just make changes directly. Subscribing to the <a
href="http://viewvc.tigris.org/servlets/ProjectMailingListList"
><tt>dev@</tt> developer mailing list</a> is recommended in any
case.</p>
</div> <!-- section-body -->
</div> <!-- section -->
<div class="section">
<h2 id="sec-security">Security</h2>
<div class="section-body">
<p>Since ViewVC is used on the Internet, security is a major concern.
If you need to pass data from the request into an external program,
please don't use <code>os.system()</code> or
<code>os.popen()</code>. Please use the module
<code>lib/popen.py</code> that is included in the ViewVC
distribution instead.</p>
</div> <!-- section-body -->
</div> <!-- section -->
<div class="section">
<h2 id="sec-adding-features">Adding Features</h2>
<div class="section-body">
<p>If you need a new configuration option think carefully, into which
section it belongs. Try to keep the content of
<code>cgi/viewvc.conf.dist</code> file and the library module
<code>lib/config.py</code> in sync.</p>
<p>Because ViewVC is a Web-based application, people will have ViewVC
URLs hyperlinked from other sites, embedded in emails, bookmarked
in their browsers, etc. It is very important to ensure that those
URLs continue to retrieve the information they were intended to
retrieve even if ViewVC is upgraded on the hosting server. In
other words, as new features require modifications to the <a
href="./url-reference.html">ViewVC URL schema</a>, make sure those
modifications preserve the existing functionality of all ViewVC
URLs.</p>
<p>The library subdirectory contains a module <code>debug.py</code>,
which you may find useful for performance testing.</p>
<p>If a new file or module is added, a new line in the installer
program <code>viewvc-install</code> is required.</p>
</div> <!-- section-body -->
</div> <!-- section -->
<div class="section">
<h2 id="sec-templates">Hacking on Templates</h2>
<div class="section-body">
<p>The library module <code>ezt.py</code> contains a module docstring
which describes the directives used in the HTML templates used by
ViewVC. The templates themselves can be found in the
<code>templates</code> subdirectory. We're currently developing a
how-to guide for <a href="./template-authoring-guide.html">ViewVC
template customization</a>.</p>
</div> <!-- section-body -->
</div> <!-- section -->
</td>
</tr>
</table>
</body>
</html>

View File

@@ -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 &amp; Files</a> area. For information about what has
changed in each release, see the <a
href="http://viewvc.tigris.org/source/browse/viewvc/trunk/CHANGES?rev=HEAD"
>CHANGES</a> file.</p>
<p>We are also making <a href="./nightly/">nightly snapshots</a>
available in tar.gz and zip formats.</p>
</div> <!-- section-body -->
</div> <!-- section -->
<div class="section">
<h2 id="sec-subversion">Subversion</h2>
<div class="section-body">
<p>The source code for ViewVC is maintained in a Subversion repository
at Tigris.org. You can checkout the trunk of our development tree
from <a href="http://viewvc.tigris.org/svn/viewvc/trunk/"
>http://viewvc.tigris.org/svn/viewvc/trunk/</a>. You'll
need to provide your Tigris.org username and password when so
prompted, or, if you don't have a Tigris.org account, use "guest"
as the username (with no, or an empty, password).</p>
</div> <!-- section-body -->
</div> <!-- section -->
</td>
</tr>
</table>
</body>
</html>

View File

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

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.

View File

@@ -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> &mdash; Needed to use
the commit database query functionality.</li>
<li><a href="http://www.codento.com/people/mtr/genscript/">GNU
enscript</a> &mdash; Needed for syntax highlighting in versioned
file contents displays</li>
<li><a href="http://www.akhphd.au.dk/~bertho/cvsgraph/">CvsGraph</a>
&mdash; Needed for version graph displays.</li>
<li><a href="http://httpd.apache.org/">Apache HTTP Server</a>, or
another server capable of running CGI programs &mdash; unless
you just want ViewVC to run in standalone server mode.</li>
</ul>
</div> <!-- section-body -->
</div> <!-- section -->
<div class="section">
<h2 id="sec-future">Future Plans</h2>
<div class="section-body">
<p>ViewVC is an Open Source project. So any future development
depends on the <a href="./contributing.html">contributions</a> that
will be made by its user community. Certainly working patches have
a greater chance to become realized quickly than feature requests,
but please don't hesitate to submit your suggestions to our <a
href="http://viewvc.tigris.org/servlets/ProjectIssues">issue
tracker</a>.</p>
<p>Some things we're thinking about include:</p>
<ul>
<li>UI streamlining/simplification.</li>
<li>Integration with CVS and Subversion commit mail scripts.</li>
<li>Integration with an indexer such as LXR.</li>
</ul>
</div> <!-- section-body -->
</div> <!-- section -->
</td>
</tr>
</table>
</body>
</html>

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -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>&quot;While I can manage some Perl, cvsweb was rather
unmaintainable for me. So I undertook the task to convert the
software to Python. As a result, I've actually been able to go
<strong>way</strong> beyond the simple changes that I had
envisioned.&quot;
</p>
</blockquote>
<p>So ViewVC started out as just a port of the cvsweb script,
originally called ViewCVS. Along the way, it has had numerous
cleanups and other modifications, a process simplified by the
elegance of the <a href="http://www.python.org/">Python</a>
language.</p>
<p>In 2001, the ViewCVS project was moved to <a
href="http://www.sourceforge.net">SourceForge</a>, a popular
software collaboration environment. There the project continued to
mature, releasing several stable-yet-pre-1.0 versions. In 2002,
C. Michael Pilato began implementing support for Subversion in
ViewCVS, taking advantage of a version control abstraction layer
begun by Lucas Bruand. Along the way, Russell Yanofsky delivered
large improvements to that abstraction, and to ViewCVS as whole.
ViewCVS was well on its way to releasing a 1.0 version.</p>
<p>Of course, now that ViewCVS could browse Subversion repositories as
easily as CVS ones, the ViewCVS name seemed inappropriate. Also,
the active ViewCVS developers at the time were growing frustrated
with SourceForge as a project hosting environment &mdash;
Subversion wasn't yet available as an option for version control,
the CVS service was always down at just the wrong moment, the bug
trackers were painful to use, and so on. So in late 2005, the
decision was made to rename the project to ViewVC, to convert the
project's CVS data to Subversion, and to move the project and its
version controlled and open issue data to <a
href="http://www.tigris.org">Tigris.org</a>.</p>
<p>Today, ViewVC is being developed at <a
href="http://viewvc.tigris.org">http://viewvc.tigris.org</a> by a
small community of folks.</p>
</div> <!-- section-body -->
</div> <!-- section -->
<div class="section">
<h2 id="sec-viewcvs-group">The ViewCVS Group</h2>
<div class="section-body">
<p>The ViewCVS Group is an informal group of people working on and
developing the ViewVC package. The current set of members are
listed below with some of their notable contributions:</p>
<dl>
<dt><a href="http://www.lyra.org/greg/">Greg Stein</a></dt>
<dd>original python port of Henner Zeller's cvsweb, secure popen
implementation, configuration file implementation, rcsparse
module, and EZT template engine</dd>
<dt>Jay Painter</dt>
<dd>CVSdb query engine</dd>
<dt>Tanaka Akira</dt>
<dd>enscript colorization and tarball generation</dd>
<dt>Tim Cera</dt>
<dd>CvsGraph support, log_table template, regular expression search,
and paging capability</dd>
<dt>Peter Funk</dt>
<dd>standalone server, blimp logo, and numerous improvements to
ViewVC's interfaces and documentation</dd>
<dt>Lucas Bruand</dt>
<dd>C++ RCS parser (tparse) and vclib module for supporting new
version control systems</dd>
<dt><a href="http://www.cmichaelpilato.com/">C. Michael Pilato</a></dt>
<dd>Subversion support, root_as_url alternative URL scheme,
templatization work, website design, documentation</dd>
<dt>Russell Yanofsky</dt>
<dd>Windows support and the sapi module for supporting multiple web
server interfaces, sweeping abstraction and UI improvements</dd>
<dt>James Henstridge</dt>
<dd>integrated query interface, support for querying Subversion
repositories, caching support, CSS formatting, and the EZT
&quot;define&quot; directive</dd>
</dl>
</div> <!-- section-body -->
</div> <!-- section -->
<div class="section">
<h2 id="sec-site-credits">About This Site</h2>
<div class="section-body">
<p>The ViewVC website was designed by <a
href="http://www.cmichaelpilato.com/">C. Michael Pilato</a>. All
HTML was hand-edited in Emacs, and the little splashes of graphical
goodness owe their existence to Open Office and the Gimp. Textual
content for the site is mostly the work of Greg Stein, but has been
tweaked through the ages by various ViewVC contributors.</p>
</div> <!-- section-body -->
</div> <!-- section -->
</td>
</tr>
</table>
</body>
</html>